diff options
Diffstat (limited to 'tools')
554 files changed, 47730 insertions, 9234 deletions
diff --git a/tools/Godot.app/Contents/Resources/Godot.icns b/tools/Godot.app/Contents/Resources/Godot.icns Binary files differindex 18bc68d6ea..4a3dc0415a 100644 --- a/tools/Godot.app/Contents/Resources/Godot.icns +++ b/tools/Godot.app/Contents/Resources/Godot.icns diff --git a/tools/SCsub b/tools/SCsub index 875663b849..f046e9ad08 100644 --- a/tools/SCsub +++ b/tools/SCsub @@ -5,16 +5,16 @@ env.add_source_files(env.tool_sources,"*.cpp") Export('env') -SConscript('editor/SCsub'); -#SConscript('scintilla/SCsub'); -SConscript('collada/SCsub'); -SConscript('docdump/SCsub'); -SConscript('freetype/SCsub'); -SConscript('doc/SCsub'); -SConscript('pck/SCsub'); - -lib = env.Library("tool",env.tool_sources) - -env.Prepend(LIBS=[lib]) +if (env["tools"]!="no"): + SConscript('editor/SCsub'); + #SConscript('scintilla/SCsub'); + SConscript('collada/SCsub'); + SConscript('docdump/SCsub'); + SConscript('freetype/SCsub'); + SConscript('pe_bliss/SCsub'); + SConscript('doc/SCsub') + SConscript('pck/SCsub') + lib = env.Library("tool",env.tool_sources) + env.Prepend(LIBS=[lib]) diff --git a/tools/addheader/addheader.py b/tools/addheader/addheader.py index 6870a1e926..038cec96d0 100644 --- a/tools/addheader/addheader.py +++ b/tools/addheader/addheader.py @@ -6,7 +6,7 @@ header="""\ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/collada/SCsub b/tools/collada/SCsub index c8eaa596d1..34524f10ef 100644 --- a/tools/collada/SCsub +++ b/tools/collada/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.tool_sources,"*.cpp") Export('env') - - diff --git a/tools/collada/collada.cpp b/tools/collada/collada.cpp index 97e9f5c36d..deec5f60c7 100644 --- a/tools/collada/collada.cpp +++ b/tools/collada/collada.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -817,7 +817,7 @@ void Collada::_parse_camera(XMLParser& parser) { if (name=="perspective") { camera.mode=CameraData::MODE_PERSPECTIVE; - } else if (name=="orthogonal") { + } else if (name=="orthographic") { camera.mode=CameraData::MODE_ORTHOGONAL; } else if (name=="xfov") { @@ -979,7 +979,7 @@ void Collada::_parse_curve_geometry(XMLParser& parser,String p_id,String p_name) current_source=id; COLLADA_PRINT("source data: "+id); - } else if (section=="float_array" || section=="array" || section=="float_array") { + } else if (section=="float_array" || section=="array") { // create a new array and read it. if (curvedata.sources.has(current_source)) { @@ -2054,8 +2054,8 @@ void Collada::_parse_animation(XMLParser& parser) { } if (target.find("/")!=-1) { //transform component - track.target=target.get_slice("/",0); - track.param=target.get_slice("/",1); + track.target=target.get_slicec('/',0); + track.param=target.get_slicec('/',1); if (track.param.find(".")!=-1) track.component=track.param.get_slice(".",1).to_upper(); track.param=track.param.get_slice(".",0); diff --git a/tools/collada/collada.h b/tools/collada/collada.h index f523d24e02..81f51a1f3d 100644 --- a/tools/collada/collada.h +++ b/tools/collada/collada.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -337,6 +337,24 @@ public: if(normal==p_vert.normal) { if(uv==p_vert.uv) { if(uv2==p_vert.uv2) { + + if (!weights.empty() || !p_vert.weights.empty()) { + + if (weights.size()==p_vert.weights.size()) { + + for(int i=0;i<weights.size();i++) { + if (weights[i].bone_idx!=p_vert.weights[i].bone_idx) + return weights[i].bone_idx<p_vert.weights[i].bone_idx; + + if (weights[i].weight!=p_vert.weights[i].weight) + return weights[i].weight<p_vert.weights[i].weight; + } + } else { + return weights.size() < p_vert.weights.size(); + } + + } + return (color<p_vert.color); } else return (uv2<p_vert.uv2); diff --git a/tools/doc/SCsub b/tools/doc/SCsub index c8eaa596d1..34524f10ef 100644 --- a/tools/doc/SCsub +++ b/tools/doc/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.tool_sources,"*.cpp") Export('env') - - diff --git a/tools/doc/doc_data.cpp b/tools/doc/doc_data.cpp index d13c7ea73c..c1d3e5e314 100644 --- a/tools/doc/doc_data.cpp +++ b/tools/doc/doc_data.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -59,6 +59,9 @@ void DocData::merge_from(const DocData& p_data) { if (cf.methods[j].name!=m.name) continue; + if (cf.methods[j].arguments.size()!=m.arguments.size()) + continue; + const MethodDoc &mf = cf.methods[j]; m.description=mf.description; @@ -134,9 +137,9 @@ void DocData::merge_from(const DocData& p_data) { void DocData::generate(bool p_basic_types) { - List<String> classes; + List<StringName> classes; ObjectTypeDB::get_type_list(&classes); - classes.sort(); + classes.sort_custom<StringName::AlphCompare>(); while(classes.size()) { @@ -186,8 +189,12 @@ void DocData::generate(bool p_basic_types) { arginfo=E->get().return_val; if (arginfo.type==Variant::NIL) continue; - - method.return_type=(arginfo.hint==PROPERTY_HINT_RESOURCE_TYPE)?arginfo.hint_string:Variant::get_type_name(arginfo.type); +#ifdef DEBUG_METHODS_ENABLED + if (m && m->get_return_type()!=StringName()) + method.return_type=m->get_return_type(); + else +#endif + method.return_type=(arginfo.hint==PROPERTY_HINT_RESOURCE_TYPE)?arginfo.hint_string:Variant::get_type_name(arginfo.type); } else { @@ -542,7 +549,7 @@ void DocData::generate(bool p_basic_types) { Globals::Singleton &s=E->get(); pd.name=s.name; pd.type=s.ptr->get_type(); - while (ObjectTypeDB::type_inherits_from(pd.type)!="Object") + while (String(ObjectTypeDB::type_inherits_from(pd.type))!="Object") pd.type=ObjectTypeDB::type_inherits_from(pd.type); if (pd.type.begins_with("_")) pd.type=pd.type.substr(1,pd.type.length()); @@ -912,9 +919,9 @@ Error DocData::save(const String& p_path) { String qualifiers; if (m.qualifiers!="") - qualifiers+="qualifiers=\""+m.qualifiers.xml_escape()+"\""; + qualifiers+=" qualifiers=\""+m.qualifiers.xml_escape()+"\""; - _write_string(f,2,"<method name=\""+m.name+"\" "+qualifiers+" >"); + _write_string(f,2,"<method name=\""+m.name+"\""+qualifiers+">"); if (m.return_type!="") { diff --git a/tools/doc/doc_data.h b/tools/doc/doc_data.h index 018bd67aaf..b62eb21b4a 100644 --- a/tools/doc/doc_data.h +++ b/tools/doc/doc_data.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/docdump/SCsub b/tools/docdump/SCsub index c8eaa596d1..34524f10ef 100644 --- a/tools/docdump/SCsub +++ b/tools/docdump/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.tool_sources,"*.cpp") Export('env') - - diff --git a/tools/docdump/class_list.xml b/tools/docdump/class_list.xml index ab33cef7d7..3d07f84177 100644 --- a/tools/docdump/class_list.xml +++ b/tools/docdump/class_list.xml @@ -3140,7 +3140,7 @@ <constant name="MODE_OPEN_DIR" value="1"> </constant> <constant name="MODE_OPEN_FILE" value="0"> - Editor will not allow to select unexisting files. + Editor will not allow to select nonexistent files. </constant> <constant name="MODE_SAVE_FILE" value="2"> Editor will warn when a file exists. diff --git a/tools/docdump/doc_dump.cpp b/tools/docdump/doc_dump.cpp index 9fece38995..5f108ee9c8 100644 --- a/tools/docdump/doc_dump.cpp +++ b/tools/docdump/doc_dump.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -65,7 +65,7 @@ static String _escape_string(const String& p_str) { ret=ret.replace(">","<"); ret=ret.replace("'","'"); ret=ret.replace("\"","""); - for (int i=1;i<32;i++) { + for (char i=1;i<32;i++) { char chr[2]={i,0}; ret=ret.replace(chr,"&#"+String::num(i)+";"); @@ -76,10 +76,10 @@ static String _escape_string(const String& p_str) { void DocDump::dump(const String& p_file) { - List<String> class_list; + List<StringName> class_list; ObjectTypeDB::get_type_list(&class_list); - class_list.sort(); + class_list.sort_custom<StringName::AlphCompare>(); FileAccess *f = FileAccess::open(p_file,FileAccess::WRITE); diff --git a/tools/docdump/doc_dump.h b/tools/docdump/doc_dump.h index 1f2f8fb484..cb18289e31 100644 --- a/tools/docdump/doc_dump.h +++ b/tools/docdump/doc_dump.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/docdump/locales/es/LC_MESSAGES/makedocs.mo b/tools/docdump/locales/es/LC_MESSAGES/makedocs.mo Binary files differnew file mode 100644 index 0000000000..8d7ea2689e --- /dev/null +++ b/tools/docdump/locales/es/LC_MESSAGES/makedocs.mo diff --git a/tools/docdump/locales/es/LC_MESSAGES/makedocs.po b/tools/docdump/locales/es/LC_MESSAGES/makedocs.po new file mode 100644 index 0000000000..82115dd897 --- /dev/null +++ b/tools/docdump/locales/es/LC_MESSAGES/makedocs.po @@ -0,0 +1,142 @@ +# Translations template for PROJECT. +# Copyright (C) 2015 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2015. +# +msgid "" +msgstr "" +"Project-Id-Version: makedocs\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2015-10-07 11:47-0600\n" +"PO-Revision-Date: 2015-10-07 13:10-0600\n" +"Last-Translator: Jorge Araya Navarro <elcorreo@deshackra.com>\n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.0\n" +"X-Generator: Poedit 1.8.4\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: makedocs.py:74 +msgid "" +"\"<code>{gclass}</code>(Go to page of class {gclass})\":/class_{lkclass}" +msgstr "" +"\"<code>{gclass}</code>(Ir a la pagina de la clase {gclass})\":/" +"class_{lkclass}" + +#: makedocs.py:76 +msgid "" +"\"<code>{gclass}.{method}</code>(Go to page {gclass}, section {method})\":/" +"class_{lkclass}#{lkmethod}" +msgstr "" +"\"<code>{gclass}.{method}</code>(Ir a la pagina {gclass}, sección " +"{method})\":/class_{lkclass}#{lkmethod}" + +#: makedocs.py:79 +msgid "\"<code>{method}</code>(Jump to method {method})\":#{lkmethod}" +msgstr "\"<code>{method}</code>(Saltar al método {method})\":#{lkmethod}" + +#: makedocs.py:81 +msgid " \"{rtype}(Go to page of class {rtype})\":/class_{link} " +msgstr " \"{rtype}(Ir a la pagina de la clase {rtype})\":/class_{link} " + +#: makedocs.py:82 +msgid "" +"\"*{funcname}*(Jump to description for node {funcname})\":#{link} <b>(</b> " +msgstr "" +"\"*{funcname}*(Saltar a la descripción para el nodo {funcname})\":#{link} " +"<b>(</b> " + +#: makedocs.py:87 +msgid "h4. Inherits: " +msgstr "h4. Hereda de: " + +#: makedocs.py:232 +msgid "<doc>'s version attribute missing" +msgstr "El atributo version de <doc> no existe" + +#: makedocs.py:246 +msgid "|_. Index symbol |_. Class name |_. Index symbol |_. Class name |\n" +msgstr "" +"|_. Índice de símbolo |_. Nombre de la clase |_. Índice de símbolo |_. " +"Nombre de la clase |\n" + +#: makedocs.py:305 +msgid "" +"h4. Category: {}\n" +"\n" +msgstr "" +"h4. Categoría: {}\n" +"\n" + +#: makedocs.py:310 +msgid "" +"h2. Brief Description\n" +"\n" +msgstr "" +"h2. Descripción breve\n" +"\n" + +#: makedocs.py:312 +msgid "" +"\"read more\":#more\n" +"\n" +msgstr "" +"\"Leer más\":#more\n" +"\n" + +#: makedocs.py:317 +msgid "" +"\n" +"h3. Member Functions\n" +"\n" +msgstr "" +"\n" +"h3. Funciones miembro\n" +"\n" + +#: makedocs.py:323 +msgid "" +"\n" +"h3. Signals\n" +"\n" +msgstr "" +"\n" +"h3. Señales\n" +"\n" + +#: makedocs.py:331 +msgid "" +"\n" +"h3. Numeric Constants\n" +"\n" +msgstr "" +"\n" +"h3. Constantes numéricas\n" +"\n" + +#: makedocs.py:347 +msgid "" +"\n" +"h3(#more). Description\n" +"\n" +msgstr "" +"\n" +"h3(#more). Descripción\n" +"\n" + +#: makedocs.py:351 +msgid "_Nothing here, yet..._\n" +msgstr "_Aún nada por aquí..._\n" + +#: makedocs.py:355 +msgid "" +"\n" +"h3. Member Function Description\n" +"\n" +msgstr "" +"\n" +"h3. Descripción de las funciones miembro\n" +"\n" diff --git a/tools/docdump/makedocs.pot b/tools/docdump/makedocs.pot new file mode 100644 index 0000000000..be3220f686 --- /dev/null +++ b/tools/docdump/makedocs.pot @@ -0,0 +1,108 @@ +# Translations template for PROJECT. +# Copyright (C) 2015 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2015. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: makedocs 0.1\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2015-10-07 11:47-0600\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.0\n" +"X-Generator: Poedit 1.8.4\n" + +#: makedocs.py:74 +msgid "\"<code>{gclass}</code>(Go to page of class {gclass})\":/class_{lkclass}" +msgstr "" + +#: makedocs.py:76 +msgid "\"<code>{gclass}.{method}</code>(Go to page {gclass}, section {method})\":/class_{lkclass}#{lkmethod}" +msgstr "" + +#: makedocs.py:79 +msgid "\"<code>{method}</code>(Jump to method {method})\":#{lkmethod}" +msgstr "" + +#: makedocs.py:81 +msgid " \"{rtype}(Go to page of class {rtype})\":/class_{link} " +msgstr "" + +#: makedocs.py:82 +msgid "\"*{funcname}*(Jump to description for node {funcname})\":#{link} <b>(</b> " +msgstr "" + +#: makedocs.py:87 +msgid "h4. Inherits: " +msgstr "" + +#: makedocs.py:232 +msgid "<doc>'s version attribute missing" +msgstr "" + +#: makedocs.py:246 +msgid "|_. Index symbol |_. Class name |_. Index symbol |_. Class name |\n" +msgstr "" + +#: makedocs.py:305 +msgid "" +"h4. Category: {}\n" +"\n" +msgstr "" + +#: makedocs.py:310 +msgid "" +"h2. Brief Description\n" +"\n" +msgstr "" + +#: makedocs.py:312 +msgid "" +"\"read more\":#more\n" +"\n" +msgstr "" + +#: makedocs.py:317 +msgid "" +"\n" +"h3. Member Functions\n" +"\n" +msgstr "" + +#: makedocs.py:323 +msgid "" +"\n" +"h3. Signals\n" +"\n" +msgstr "" + +#: makedocs.py:331 +msgid "" +"\n" +"h3. Numeric Constants\n" +"\n" +msgstr "" + +#: makedocs.py:347 +msgid "" +"\n" +"h3(#more). Description\n" +"\n" +msgstr "" + +#: makedocs.py:351 +msgid "_Nothing here, yet..._\n" +msgstr "" + +#: makedocs.py:355 +msgid "" +"\n" +"h3. Member Function Description\n" +"\n" +msgstr "" diff --git a/tools/docdump/makedocs.py b/tools/docdump/makedocs.py new file mode 100644 index 0000000000..be57891abc --- /dev/null +++ b/tools/docdump/makedocs.py @@ -0,0 +1,382 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +# +# makedocs.py: Generate documentation for Open Project Wiki +# Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. +# Contributor: Jorge Araya Navarro <elcorreo@deshackra.com> +# + +# IMPORTANT NOTICE: +# If you are going to modify anything from this file, please be sure to follow +# the Style Guide for Python Code or often called "PEP8". To do this +# automagically just install autopep8: +# +# $ sudo pip3 install autopep8 +# +# and run: +# +# $ autopep8 makedocs.py +# +# Before committing your changes. Also be sure to delete any trailing +# whitespace you may left. +# +# TODO: +# * Refactor code. +# * Adapt this script for generating content in other markup formats like +# DokuWiki, Markdown, etc. +# +# Also check other TODO entries in this script for more information on what is +# left to do. +import argparse +import gettext +import logging +import re +from itertools import zip_longest +from os import path, listdir +from xml.etree import ElementTree + + +# add an option to change the verbosity +logging.basicConfig(level=logging.INFO) + + +def getxmlfloc(): + """ Returns the supposed location of the XML file + """ + filepath = path.dirname(path.abspath(__file__)) + return path.join(filepath, "class_list.xml") + + +def langavailable(): + """ Return a list of languages available for translation + """ + filepath = path.join( + path.dirname(path.abspath(__file__)), "locales") + files = listdir(filepath) + choices = [x for x in files] + choices.insert(0, "none") + return choices + + +desc = "Generates documentation from a XML file to different markup languages" + +parser = argparse.ArgumentParser(description=desc) +parser.add_argument("--input", dest="xmlfp", default=getxmlfloc(), + help="Input XML file, default: {}".format(getxmlfloc())) +parser.add_argument("--output-dir", dest="outputdir", required=True, + help="Output directory for generated files") +parser.add_argument("--language", choices=langavailable(), default="none", + help=("Choose the language of translation" + " for the output files. Default is English (none). " + "Note: This is NOT for the documentation itself!")) +# TODO: add an option for outputting different markup formats + +args = parser.parse_args() +# Let's check if the file and output directory exists +if not path.isfile(args.xmlfp): + logging.critical("File not found: {}".format(args.xmlfp)) + exit(1) +elif not path.isdir(args.outputdir): + logging.critical("Path does not exist: {}".format(args.outputdir)) + exit(1) + +_ = gettext.gettext +if args.language != "none": + lang = gettext.translation(domain="makedocs", + localedir="locales", + languages=[args.language]) + lang.install() + + _ = lang.gettext + +# Strings +C_LINK = _("\"<code>{gclass}</code>(Go to page of class" + " {gclass})\":/class_{lkclass}") +MC_LINK = _("\"<code>{gclass}.{method}</code>(Go " + "to page {gclass}, section {method})\"" + ":/class_{lkclass}#{lkmethod}") +TM_JUMP = _("\"<code>{method}</code>(Jump to method" + " {method})\":#{lkmethod}") +GTC_LINK = _(" \"{rtype}(Go to page of class {rtype})\":/class_{link} ") +DFN_JUMP = _("\"*{funcname}*(Jump to description for" + " node {funcname})\":#{link} <b>(</b> ") +M_ARG_DEFAULT = C_LINK + " {name}={default}" +M_ARG = C_LINK + " {name}" + +OPENPROJ_INH = _("h4. Inherits: ") + C_LINK + "\n\n" + + +def tb(string): + """ Return a byte representation of a string + """ + return bytes(string, "UTF-8") + + +def sortkey(c): + """ Symbols are first, letters second + """ + if "_" == c.attrib["name"][0]: + return "A" + else: + return c.attrib["name"] + + +def toOP(text): + """ Convert commands in text to Open Project commands + """ + # TODO: Make this capture content between [command] ... [/command] + groups = re.finditer((r'\[html (?P<command>/?\w+/?)(\]| |=)?(\]| |=)?(?P<a' + 'rg>\w+)?(\]| |=)?(?P<value>"[^"]+")?/?\]'), text) + alignstr = "" + for group in groups: + gd = group.groupdict() + if gd["command"] == "br/": + text = text.replace(group.group(0), "\n\n", 1) + elif gd["command"] == "div": + if gd["value"] == '"center"': + alignstr = ("{display:block; margin-left:auto;" + " margin-right:auto;}") + elif gd["value"] == '"left"': + alignstr = "<" + elif gd["value"] == '"right"': + alignstr = ">" + text = text.replace(group.group(0), "\n\n", 1) + elif gd["command"] == "/div": + alignstr = "" + text = text.replace(group.group(0), "\n\n", 1) + elif gd["command"] == "img": + text = text.replace(group.group(0), "!{align}{src}!".format( + align=alignstr, src=gd["value"].strip('"')), 1) + elif gd["command"] == "b" or gd["command"] == "/b": + text = text.replace(group.group(0), "*", 1) + elif gd["command"] == "i" or gd["command"] == "/i": + text = text.replace(group.group(0), "_", 1) + elif gd["command"] == "u" or gd["command"] == "/u": + text = text.replace(group.group(0), "+", 1) + # Process other non-html commands + groups = re.finditer((r'\[method ((?P<class>[aA0-zZ9_]+)(?:\.))' + r'?(?P<method>[aA0-zZ9_]+)\]'), text) + for group in groups: + gd = group.groupdict() + if gd["class"]: + replacewith = (MC_LINK.format(gclass=gd["class"], + method=gd["method"], + lkclass=gd["class"].lower(), + lkmethod=gd["method"].lower())) + else: + # The method is located in the same wiki page + replacewith = (TM_JUMP.format(method=gd["method"], + lkmethod=gd["method"].lower())) + + text = text.replace(group.group(0), replacewith, 1) + # Finally, [Classes] are around brackets, make them direct links + groups = re.finditer(r'\[(?P<class>[az0-AZ0_]+)\]', text) + for group in groups: + gd = group.groupdict() + replacewith = (C_LINK. + format(gclass=gd["class"], + lkclass=gd["class"].lower())) + text = text.replace(group.group(0), replacewith, 1) + + return text + "\n\n" + + +def mkfn(node, is_signal=False): + """ Return a string containing a unsorted item for a function + """ + finalstr = "" + name = node.attrib["name"] + rtype = node.find("return") + if rtype: + rtype = rtype.attrib["type"] + else: + rtype = "void" + # write the return type and the function name first + finalstr += "* " + # return type + if not is_signal: + if rtype != "void": + finalstr += GTC_LINK.format( + rtype=rtype, + link=rtype.lower()) + else: + finalstr += " void " + + # function name + if not is_signal: + finalstr += DFN_JUMP.format( + funcname=name, + link=name.lower()) + else: + # Signals have no description + finalstr += "*{funcname}* <b>(</b>".format(funcname=name) + # loop for the arguments of the function, if any + args = [] + for arg in sorted( + node.iter(tag="argument"), + key=lambda a: int(a.attrib["index"])): + + ntype = arg.attrib["type"] + nname = arg.attrib["name"] + + if "default" in arg.attrib: + args.insert(-1, M_ARG_DEFAULT.format( + gclass=ntype, + lkclass=ntype.lower(), + name=nname, + default=arg.attrib["default"])) + else: + # No default value present + args.insert(-1, M_ARG.format(gclass=ntype, + lkclass=ntype.lower(), name=nname)) + # join the arguments together + finalstr += ", ".join(args) + # and, close the function with a ) + finalstr += " <b>)</b>" + # write the qualifier, if any + if "qualifiers" in node.attrib: + qualifier = node.attrib["qualifiers"] + finalstr += " " + qualifier + + finalstr += "\n" + + return finalstr + +# Let's begin +tree = ElementTree.parse(args.xmlfp) +root = tree.getroot() + +# Check version attribute exists in <doc> +if "version" not in root.attrib: + logging.critical(_("<doc>'s version attribute missing")) + exit(1) + +version = root.attrib["version"] +classes = sorted(root, key=sortkey) +# first column is always longer, second column of classes should be shorter +zclasses = zip_longest(classes[:int(len(classes) / 2 + 1)], + classes[int(len(classes) / 2 + 1):], + fillvalue="") + +# We write the class_list file and also each class file at once +with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: + # Write header of table + fcl.write(tb("|^.\n")) + fcl.write(tb(_("|_. Index symbol |_. Class name " + "|_. Index symbol |_. Class name |\n"))) + fcl.write(tb("|-.\n")) + + indexletterl = "" + indexletterr = "" + for gdclassl, gdclassr in zclasses: + # write a row # + # write the index symbol column, left + if indexletterl != gdclassl.attrib["name"][0]: + indexletterl = gdclassl.attrib["name"][0] + fcl.write(tb("| *{}* |".format(indexletterl.upper()))) + else: + # empty cell + fcl.write(tb("| |")) + # write the class name column, left + fcl.write(tb(C_LINK.format( + gclass=gdclassl.attrib["name"], + lkclass=gdclassl.attrib["name"].lower()))) + + # write the index symbol column, right + if isinstance(gdclassr, ElementTree.Element): + if indexletterr != gdclassr.attrib["name"][0]: + indexletterr = gdclassr.attrib["name"][0] + fcl.write(tb("| *{}* |".format(indexletterr.upper()))) + else: + # empty cell + fcl.write(tb("| |")) + # We are dealing with an empty string + else: + # two empty cell + fcl.write(tb("| | |\n")) + # We won't get the name of the class since there is no ElementTree + # object for the right side of the tuple, so we iterate the next + # tuple instead + continue + + # write the class name column (if any), right + fcl.write(tb(C_LINK.format( + gclass=gdclassl.attrib["name"], + lkclass=gdclassl.attrib["name"].lower()) + "|\n")) + + # row written # + # now, let's write each class page for each class + for gdclass in [gdclassl, gdclassr]: + if not isinstance(gdclass, ElementTree.Element): + continue + + classname = gdclass.attrib["name"] + with open(path.join(args.outputdir, "{}.txt".format( + classname.lower())), "wb") as clsf: + # First level header with the name of the class + clsf.write(tb("h1. {}\n\n".format(classname))) + # lay the attributes + if "inherits" in gdclass.attrib: + inh = gdclass.attrib["inherits"].strip() + clsf.write(tb(OPENPROJ_INH.format(gclass=inh, + lkclass=inh.lower()))) + if "category" in gdclass.attrib: + clsf.write(tb(_("h4. Category: {}\n\n"). + format(gdclass.attrib["category"].strip()))) + # lay child nodes + briefd = gdclass.find("brief_description") + if briefd.text.strip(): + clsf.write(tb(_("h2. Brief Description\n\n"))) + clsf.write(tb(toOP(briefd.text.strip()) + + _("\"read more\":#more\n\n"))) + + # Write the list of member functions of this class + methods = gdclass.find("methods") + if methods and len(methods) > 0: + clsf.write(tb(_("\nh3. Member Functions\n\n"))) + for method in methods.iter(tag='method'): + clsf.write(tb(mkfn(method))) + + signals = gdclass.find("signals") + if signals and len(signals) > 0: + clsf.write(tb(_("\nh3. Signals\n\n"))) + for signal in signals.iter(tag='signal'): + clsf.write(tb(mkfn(signal, True))) + # TODO: <members> tag is necessary to process? it does not + # exists in class_list.xml file. + + consts = gdclass.find("constants") + if consts and len(consts) > 0: + clsf.write(tb(_("\nh3. Numeric Constants\n\n"))) + for const in sorted(consts, key=lambda k: + k.attrib["name"]): + if const.text.strip(): + clsf.write(tb("* *{name}* = *{value}* - {desc}\n". + format( + name=const.attrib["name"], + value=const.attrib["value"], + desc=const.text.strip()))) + else: + # Constant have no description + clsf.write(tb("* *{name}* = *{value}*\n". + format( + name=const.attrib["name"], + value=const.attrib["value"]))) + descrip = gdclass.find("description") + clsf.write(tb(_("\nh3(#more). Description\n\n"))) + if descrip.text: + clsf.write(tb(descrip.text.strip() + "\n")) + else: + clsf.write(tb(_("_Nothing here, yet..._\n"))) + + # and finally, the description for each method + if methods and len(methods) > 0: + clsf.write(tb(_("\nh3. Member Function Description\n\n"))) + for method in methods.iter(tag='method'): + clsf.write(tb("h4(#{n}). {name}\n\n".format( + n=method.attrib["name"].lower(), + name=method.attrib["name"]))) + clsf.write(tb(mkfn(method) + "\n")) + clsf.write(tb(toOP(method.find( + "description").text.strip()))) diff --git a/tools/docdump/makehtml.py b/tools/docdump/makehtml.py index d533ca1b8b..9b9c62f33b 100644 --- a/tools/docdump/makehtml.py +++ b/tools/docdump/makehtml.py @@ -1,5 +1,19 @@ import sys import xml.etree.ElementTree as ET +from xml.sax.saxutils import escape, unescape + +html_escape_table = { + '"': """, + "'": "'" +} + +html_unescape_table = {v:k for k, v in html_escape_table.items()} + +def html_escape(text): + return escape(text, html_escape_table) + +def html_unescape(text): + return unescape(text, html_unescape_table) input_list = [] @@ -96,7 +110,7 @@ def make_html_class_list(class_list,columns): idx=0 for n in class_list: - col = idx/col_max + col = int(idx/col_max) if (col>=columns): col=columns-1 fit_columns[col]+=[n] @@ -299,6 +313,7 @@ def make_type(p_type,p_parent): def make_text_def(class_name,parent,text): + text = html_escape(text) pos=0 while(True): pos = text.find("[",pos) @@ -598,7 +613,6 @@ def make_html_class(node): descr=node.find("description") if (descr!=None and descr.text.strip()!=""): - h4=ET.SubElement(div,"h4") h4.text="Description:" @@ -644,7 +658,6 @@ def make_html_class(node): class_names=[] classes={} - for file in input_list: tree = ET.parse(file) doc=tree.getroot() diff --git a/tools/editor/SCsub b/tools/editor/SCsub index 73ec530177..cd46ff8353 100644 --- a/tools/editor/SCsub +++ b/tools/editor/SCsub @@ -28,7 +28,7 @@ def make_doc_header(target,source,env): - + if (env["tools"]=="yes"): @@ -43,17 +43,16 @@ if (env["tools"]=="yes"): f.write(reg_exporters_inc) f.write(reg_exporters) f.close() - + env.Depends("#tools/editor/doc_data_compressed.h","#doc/base/classes.xml") env.Command("#tools/editor/doc_data_compressed.h","#doc/base/classes.xml",make_doc_header) #make_doc_header(env.File("#tools/editor/doc_data_raw.h").srcnode().abspath,env.File("#doc/base/classes.xml").srcnode().abspath,env) - + env.add_source_files(env.tool_sources,"*.cpp") - + Export('env') - SConscript('icons/SCsub'); + SConscript('icons/SCsub'); SConscript('plugins/SCsub'); SConscript('fileserver/SCsub'); SConscript('io_plugins/SCsub'); - diff --git a/tools/editor/animation_editor.cpp b/tools/editor/animation_editor.cpp index 95f9ee6509..ace6fda696 100644 --- a/tools/editor/animation_editor.cpp +++ b/tools/editor/animation_editor.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,7 @@ #include "io/resource_saver.h" #include "pair.h" #include "scene/gui/separator.h" +#include "editor_node.h" /* Missing to fix: *Set @@ -44,11 +45,207 @@ */ +class AnimationCurveEdit : public Control { + OBJ_TYPE( AnimationCurveEdit, Control ); +public: + enum Mode { + MODE_DISABLED, + MODE_SINGLE, + MODE_MULTIPLE + }; +private: + + Set<float> multiples; + float transition; + Mode mode; + + void _notification(int p_what) { + + if (p_what==NOTIFICATION_DRAW) { + + + RID ci = get_canvas_item(); + + Size2 s = get_size(); + Rect2 r(Point2(),s); + + //r=r.grow(3); + Ref<StyleBox> sb = get_stylebox("normal","LineEdit"); + sb->draw(ci,r); + r.size-=sb->get_minimum_size(); + r.pos+=sb->get_offset(); + //VisualServer::get_singleton()->canvas_item_add + + Ref<Font> f = get_font("font","Label"); + r=r.grow(-2); + Color color = get_color("font_color","Label"); + + int points = 48; + if (mode==MODE_MULTIPLE) { + + int max_draw = 16; + Color mcolor=color; + mcolor.a*=0.3; + + Set<float>::Element *E=multiples.front(); + for(int j=0;j<16;j++) { + + if (!E) + break; + + float prev=1.0; + float exp=E->get(); + bool flip=false;//hint_text=="attenuation"; + + + for(int i=1;i<=points;i++) { + + float ifl = i/float(points); + float iflp = (i-1)/float(points); + + float h = 1.0-Math::ease(ifl,exp); + + if (flip) { + ifl=1.0-ifl; + iflp=1.0-iflp; + } + + VisualServer::get_singleton()->canvas_item_add_line(ci,r.pos+Point2(iflp*r.size.width,prev*r.size.height),r.pos+Point2(ifl*r.size.width,h*r.size.height),mcolor); + prev=h; + } + + E=E->next(); + } + } + + float exp=transition; + if (mode!=MODE_DISABLED) { + + + float prev=1.0; + + bool flip=false;//hint_text=="attenuation"; + + + for(int i=1;i<=points;i++) { + + float ifl = i/float(points); + float iflp = (i-1)/float(points); + + float h = 1.0-Math::ease(ifl,exp); + + if (flip) { + ifl=1.0-ifl; + iflp=1.0-iflp; + } + + VisualServer::get_singleton()->canvas_item_add_line(ci,r.pos+Point2(iflp*r.size.width,prev*r.size.height),r.pos+Point2(ifl*r.size.width,h*r.size.height),color); + prev=h; + } + } + + String txt=String::num(exp,2); + if (mode==MODE_DISABLED) { + txt="Disabled"; + } else if (mode==MODE_MULTIPLE) { + txt+=" - All Selection"; + } + + f->draw(ci,Point2(10,10+f->get_ascent()),txt,color); + + } + } + + void _input_event(const InputEvent& p_ev) { + if (p_ev.type==InputEvent::MOUSE_MOTION && p_ev.mouse_motion.button_mask&BUTTON_MASK_LEFT) { + + if (mode==MODE_DISABLED) + return; + + float rel = p_ev.mouse_motion.relative_x; + if (rel==0) + return; + + bool flip=false; + + if (flip) + rel=-rel; + + float val = transition; + if (val==0) + return; + bool sg = val < 0; + val = Math::absf(val); + + val = Math::log(val)/Math::log(2); + //logspace + val+=rel*0.05; + // + + val = Math::pow(2,val); + if (sg) + val=-val; + + transition=val; + update(); + //emit_signal("variant_changed"); + emit_signal("transition_changed",transition); + } + } + +public: + + static void _bind_methods() { + + // ObjectTypeDB::bind_method("_update_obj",&AnimationKeyEdit::_update_obj); + ObjectTypeDB::bind_method("_input_event",&AnimationCurveEdit::_input_event); + ADD_SIGNAL(MethodInfo("transition_changed")); + } + + void set_mode(Mode p_mode) { + + mode=p_mode; + update(); + } + + void clear_multiples() { multiples.clear(); update();} + void set_multiple(float p_transition) { + + multiples.insert(p_transition); + } + + void set_transition(float p_transition) { + transition=p_transition; + update(); + } + + float get_transition() const { + return transition; + } + + void force_transition(float p_value) { + if (mode==MODE_DISABLED) + return; + transition=p_value; + emit_signal("transition_changed",p_value); + update(); + } + + AnimationCurveEdit() { + + transition=1.0; + set_default_cursor_shape(CURSOR_HSPLIT); + mode=MODE_DISABLED; + } + +}; + class AnimationKeyEdit : public Object { OBJ_TYPE(AnimationKeyEdit,Object); public: bool setting; + bool hidden; static void _bind_methods() { @@ -56,12 +253,12 @@ public: ObjectTypeDB::bind_method("_key_ofs_changed",&AnimationKeyEdit::_key_ofs_changed); } - PopupDialog *ke_dialog; + //PopupDialog *ke_dialog; void _update_obj(const Ref<Animation> &p_anim) { if (setting) return; - if (!ke_dialog->is_visible()) + if (hidden) return; if (!(animation==p_anim)) return; @@ -69,7 +266,7 @@ public: } void _key_ofs_changed(const Ref<Animation> &p_anim,float from, float to) { - if (!ke_dialog->is_visible()) + if (hidden) return; if (!(animation==p_anim)) return; @@ -408,8 +605,8 @@ public: } break; } - if (animation->track_get_type(track)!=Animation::TYPE_METHOD) - p_list->push_back( PropertyInfo( Variant::REAL, "easing", PROPERTY_HINT_EXP_EASING)); + //if (animation->track_get_type(track)!=Animation::TYPE_METHOD) + // p_list->push_back( PropertyInfo( Variant::REAL, "easing", PROPERTY_HINT_EXP_EASING)); } UndoRedo *undo_redo; @@ -425,31 +622,46 @@ public: _change_notify(); } - AnimationKeyEdit() { key_ofs=0; track=-1; setting=false; } + AnimationKeyEdit() { hidden=true; key_ofs=0; track=-1; setting=false; } }; -void AnimationKeyEditor::_menu_track(int p_type) { +void AnimationKeyEditor::_menu_add_track(int p_type) { ERR_FAIL_COND(!animation.is_valid()); - last_menu_track_opt=p_type; switch(p_type) { - case TRACK_MENU_ADD_VALUE_TRACK: - case TRACK_MENU_ADD_TRANSFORM_TRACK: - case TRACK_MENU_ADD_CALL_TRACK: { + case ADD_TRACK_MENU_ADD_CALL_TRACK: { + if (root) { + call_select->popup_centered_ratio(); + break; + } + } break; + case ADD_TRACK_MENU_ADD_VALUE_TRACK: + case ADD_TRACK_MENU_ADD_TRANSFORM_TRACK: { undo_redo->create_action("Anim Add Track"); - undo_redo->add_do_method(animation.ptr(),"add_track",p_type); + undo_redo->add_do_method(animation.ptr(),"add_track",p_type); undo_redo->add_do_method(animation.ptr(),"track_set_path",animation->get_track_count(),"."); undo_redo->add_undo_method(animation.ptr(),"remove_track",animation->get_track_count()); undo_redo->commit_action(); } break; + } +} + +void AnimationKeyEditor::_menu_track(int p_type) { + + ERR_FAIL_COND(!animation.is_valid()); + + + last_menu_track_opt=p_type; + switch(p_type) { + case TRACK_MENU_SCALE: case TRACK_MENU_SCALE_PIVOT: { @@ -610,6 +822,8 @@ void AnimationKeyEditor::_menu_track(int p_type) { selection=new_selection; track_editor->update(); + _edit_if_single_selection(); + } @@ -689,8 +903,31 @@ void AnimationKeyEditor::_menu_track(int p_type) { optimize_dialog->popup_centered(Size2(250,180)); } break; + case CURVE_SET_LINEAR: { + curve_edit->force_transition(1.0); + + } break; + case CURVE_SET_IN: { + + curve_edit->force_transition(4.0); + + } break; + case CURVE_SET_OUT: { + curve_edit->force_transition(0.25); + } break; + case CURVE_SET_INOUT: { + curve_edit->force_transition(-4); + + } break; + case CURVE_SET_OUTIN: { + + curve_edit->force_transition(-0.25); + } break; + case CURVE_SET_CONSTANT: { + curve_edit->force_transition(0); + } break; } @@ -772,19 +1009,24 @@ void AnimationKeyEditor::_track_editor_draw() { if (!animation.is_valid()) { v_scroll->hide(); h_scroll->hide(); + menu_add_track->set_disabled(true); menu_track->set_disabled(true); edit_button->set_disabled(true); + key_editor_tab->hide(); move_up_button->set_disabled(true); move_down_button->set_disabled(true); remove_button->set_disabled(true); return; } + menu_add_track->set_disabled(false); menu_track->set_disabled(false); edit_button->set_disabled(false); move_up_button->set_disabled(false); move_down_button->set_disabled(false); remove_button->set_disabled(false); + if (edit_button->is_pressed()) + key_editor_tab->show(); te_drawing=true; @@ -1000,8 +1242,13 @@ void AnimationKeyEditor::_track_editor_draw() { } } + Color sep_color=color; + color.a*=0.5; + for(int i=0;i<fit;i++) { + //this code sucks, i always forget how it works + int idx = v_scroll->get_val() + i; if (idx>=animation->get_track_count()) break; @@ -1090,6 +1337,7 @@ void AnimationKeyEditor::_track_editor_draw() { float key_hofs = -Math::floor(type_icon[tt]->get_height()/2); int kc=animation->track_get_key_count(idx); + bool first=true; for(int i=0;i<kc;i++) { @@ -1097,8 +1345,16 @@ void AnimationKeyEditor::_track_editor_draw() { float time = animation->track_get_key_time(idx,i); if (time<keys_from) continue; - if (time>keys_to) + if (time>keys_to) { + + if (first && i>0 && animation->track_get_key_value(idx,i)==animation->track_get_key_value(idx,i-1)) { + //draw whole line + te->draw_line(ofs+Vector2(name_limit,y+h/2),ofs+Point2(settings_limit,y+h/2),color); + } + break; + } + float x = key_hofs + name_limit + (time-keys_from)*zoom_scale; Ref<Texture> tex = type_icon[tt]; @@ -1116,7 +1372,22 @@ void AnimationKeyEditor::_track_editor_draw() { if (mouse_over.over==MouseOver::OVER_KEY && mouse_over.track==idx && mouse_over.over_key==i) tex=type_hover; + Variant value = animation->track_get_key_value(idx,i); + if (first && i>0 && value==animation->track_get_key_value(idx,i-1)) { + + te->draw_line(ofs+Vector2(name_limit,y+h/2),ofs+Point2(x,y+h/2),color); + } + + if (i<kc-1 && value==animation->track_get_key_value(idx,i+1)) { + float x_n = key_hofs + name_limit + (animation->track_get_key_time(idx,i+1)-keys_from)*zoom_scale; + + x_n = MIN( x_n, settings_limit); + te->draw_line(ofs+Point2(x_n,y+h/2),ofs+Point2(x,y+h/2),color); + + } + te->draw_texture(tex,ofs+Point2(x,y+key_vofs).floor()); + first=false; } } @@ -1226,7 +1497,9 @@ void AnimationKeyEditor::_clear_selection_for_anim(const Ref<Animation>& p_anim) if (!(animation==p_anim)) return; - selection.clear(); + //selection.clear(); + _clear_selection(); + } void AnimationKeyEditor::_select_at_anim(const Ref<Animation>& p_anim,int p_track,float p_pos){ @@ -1244,6 +1517,7 @@ void AnimationKeyEditor::_select_at_anim(const Ref<Animation>& p_anim,int p_trac ki.pos=p_pos; selection.insert(sk,ki); + } @@ -1283,6 +1557,83 @@ PropertyInfo AnimationKeyEditor::_find_hint_for_track(int p_idx) { } +void AnimationKeyEditor::_curve_transition_changed(float p_what) { + + if (selection.size()==0) + return; + if (selection.size()==1) + undo_redo->create_action("Edit Node Curve",true); + else + undo_redo->create_action("Edit Selection Curve",true); + + for(Map<SelectedKey,KeyInfo>::Element *E=selection.front();E;E=E->next()) { + + int track = E->key().track; + int key = E->key().key; + float prev_val = animation->track_get_key_transition(track,key); + undo_redo->add_do_method(animation.ptr(),"track_set_key_transition",track,key,p_what); + undo_redo->add_undo_method(animation.ptr(),"track_set_key_transition",track,key,prev_val); + } + + undo_redo->commit_action(); + +} + +void AnimationKeyEditor::_toggle_edit_curves() { + + if (edit_button->is_pressed()) + key_editor_tab->show(); + else + key_editor_tab->hide(); +} + + +bool AnimationKeyEditor::_edit_if_single_selection() { + + if (selection.size()!=1) { + + if (selection.size()==0) { + curve_edit->set_mode(AnimationCurveEdit::MODE_DISABLED); + print_line("disable"); + } else { + + curve_edit->set_mode(AnimationCurveEdit::MODE_MULTIPLE); + curve_edit->set_transition(1.0); + curve_edit->clear_multiples(); + //add all + for(Map<SelectedKey,KeyInfo>::Element *E=selection.front();E;E=E->next()) { + + curve_edit->set_multiple(animation->track_get_key_transition(E->key().track,E->key().key)); + } + print_line("multiple"); + + } + return false; + } + curve_edit->set_mode(AnimationCurveEdit::MODE_SINGLE); + print_line("regular"); + + int idx = selection.front()->key().track; + int key = selection.front()->key().key; + { + + key_edit->animation=animation; + key_edit->track=idx; + key_edit->key_ofs=animation->track_get_key_time(idx,key); + key_edit->hint=_find_hint_for_track(idx); + key_edit->notify_change(); + + curve_edit->set_transition(animation->track_get_key_transition(idx,key)); + + /*key_edit_dialog->set_size( Size2( 200,200) ); + key_edit_dialog->set_pos( track_editor->get_global_pos() + ofs + mpos +Point2(-100,20)); + key_edit_dialog->popup();*/ + + } + + return true; + +} void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { @@ -1364,9 +1715,12 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { undo_redo->add_undo_method(animation.ptr(),"track_insert_key",E->key().track,E->get().pos,animation->track_get_key_value(E->key().track,E->key().key),animation->track_get_key_transition(E->key().track,E->key().key)); } + undo_redo->add_do_method(this,"_clear_selection_for_anim",animation); + undo_redo->add_undo_method(this,"_clear_selection_for_anim",animation); undo_redo->commit_action(); - selection.clear(); + //selection.clear(); accept_event(); + _edit_if_single_selection(); } } else if (animation.is_valid() && animation->get_track_count()>0) { @@ -1375,7 +1729,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { if (p_input.is_action("ui_up")) selected_track--; if (v_scroll->is_visible() && p_input.is_action("ui_page_up")) - selected_track=selected_track--;; + selected_track--; if (selected_track<0) selected_track=0; @@ -1552,20 +1906,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { } - if (mb.mod.command || edit_button->is_pressed()) { - - key_edit->animation=animation; - key_edit->track=idx; - key_edit->key_ofs=animation->track_get_key_time(idx,key); - key_edit->hint=_find_hint_for_track(idx); - key_edit->notify_change(); - - key_edit_dialog->set_size( Size2( 200,200) ); - key_edit_dialog->set_pos( track_editor->get_global_pos() + ofs + mpos +Point2(-100,20)); - key_edit_dialog->popup(); - - } SelectedKey sk; sk.track=idx; @@ -1577,7 +1918,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { if (!mb.mod.shift && !selection.has(sk)) - selection.clear(); + _clear_selection(); selection.insert(sk,ki); @@ -1588,7 +1929,10 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { selected_track=idx; track_editor->update(); - + if (_edit_if_single_selection() && mb.mod.command) { + edit_button->set_pressed(true); + key_editor_tab->show(); + } } else { //button column int ofsx = size.width - mpos.x; @@ -1824,7 +2168,8 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { if (from_track>to_track) { if (!click.shift) - selection.clear(); + _clear_selection(); + _edit_if_single_selection(); break; } @@ -1842,12 +2187,13 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { if (from_track > tracks_to || to_track < tracks_from) { if (!click.shift) - selection.clear(); + _clear_selection(); + _edit_if_single_selection(); break; } if (!click.shift) - selection.clear(); + _clear_selection(); int higher_track=0x7FFFFFFF; @@ -1880,6 +2226,8 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { } + _edit_if_single_selection(); + } break; case ClickOver::CLICK_MOVE_KEYS: { @@ -1891,8 +2239,9 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { if (!click.shift) { KeyInfo ki=selection[click.selk]; - selection.clear(); + _clear_selection(); selection[click.selk]=ki; + _edit_if_single_selection(); } break; @@ -2007,6 +2356,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { } undo_redo->commit_action(); + _edit_if_single_selection(); } break; default: {} @@ -2031,7 +2381,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { te->update(); track_editor->set_tooltip(""); - if (!track_editor->has_focus() && (!get_focus_owner() || !get_focus_owner()->cast_to<LineEdit>())) + if (!track_editor->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) track_editor->call_deferred("grab_focus"); @@ -2321,12 +2671,14 @@ void AnimationKeyEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { - zoomicon->set_texture( get_icon("Zoom","EditorIcons") ); - //menu_track->set_icon(get_icon("AddTrack","EditorIcons")); - menu_track->get_popup()->add_icon_item(get_icon("KeyValue","EditorIcons"),"Add Normal Track",TRACK_MENU_ADD_VALUE_TRACK); - menu_track->get_popup()->add_icon_item(get_icon("KeyXform","EditorIcons"),"Add Transform Track",TRACK_MENU_ADD_TRANSFORM_TRACK); - menu_track->get_popup()->add_icon_item(get_icon("KeyCall","EditorIcons"),"Add Call Func Track",TRACK_MENU_ADD_CALL_TRACK); - menu_track->get_popup()->add_separator(); + zoomicon->set_texture( get_icon("Zoom","EditorIcons") ); + + menu_add_track->set_icon(get_icon("AddTrack","EditorIcons")); + menu_add_track->get_popup()->add_icon_item(get_icon("KeyValue","EditorIcons"),"Add Normal Track",ADD_TRACK_MENU_ADD_VALUE_TRACK); + menu_add_track->get_popup()->add_icon_item(get_icon("KeyXform","EditorIcons"),"Add Transform Track",ADD_TRACK_MENU_ADD_TRANSFORM_TRACK); + menu_add_track->get_popup()->add_icon_item(get_icon("KeyCall","EditorIcons"),"Add Call Func Track",ADD_TRACK_MENU_ADD_CALL_TRACK); + + menu_track->set_icon(get_icon("Tools","EditorIcons")); menu_track->get_popup()->add_item("Scale Selection",TRACK_MENU_SCALE); menu_track->get_popup()->add_item("Scale From Cursor",TRACK_MENU_SCALE_PIVOT); menu_track->get_popup()->add_separator(); @@ -2348,20 +2700,33 @@ void AnimationKeyEditor::_notification(int p_what) { optimize_dialog->connect("confirmed",this,"_animation_optimize"); menu_track->get_popup()->add_child(tpp); - menu_track->get_popup()->add_submenu_item("Set Transitions..","Transitions"); - menu_track->get_popup()->add_separator(); + //menu_track->get_popup()->add_submenu_item("Set Transitions..","Transitions"); + //menu_track->get_popup()->add_separator(); menu_track->get_popup()->add_item("Optimize Animation",TRACK_MENU_OPTIMIZE); + curve_linear->set_icon(get_icon("CurveLinear","EditorIcons")); + curve_in->set_icon(get_icon("CurveIn","EditorIcons")); + curve_out->set_icon(get_icon("CurveOut","EditorIcons")); + curve_inout->set_icon(get_icon("CurveInOut","EditorIcons")); + curve_outin->set_icon(get_icon("CurveOutIn","EditorIcons")); + curve_constant->set_icon(get_icon("CurveConstant","EditorIcons")); - + curve_linear->connect("pressed",this,"_menu_track",varray(CURVE_SET_LINEAR)); + curve_in->connect("pressed",this,"_menu_track",varray(CURVE_SET_IN)); + curve_out->connect("pressed",this,"_menu_track",varray(CURVE_SET_OUT)); + curve_inout->connect("pressed",this,"_menu_track",varray(CURVE_SET_INOUT)); + curve_outin->connect("pressed",this,"_menu_track",varray(CURVE_SET_OUTIN)); + curve_constant->connect("pressed",this,"_menu_track",varray(CURVE_SET_CONSTANT)); move_up_button->set_icon(get_icon("MoveUp","EditorIcons")); move_down_button->set_icon(get_icon("MoveDown","EditorIcons")); remove_button->set_icon(get_icon("Remove","EditorIcons")); edit_button->set_icon(get_icon("EditKey","EditorIcons")); + edit_button->connect("pressed",this,"_toggle_edit_curves"); loop->set_icon(get_icon("Loop","EditorIcons")); + curve_edit->connect("transition_changed",this,"_curve_transition_changed"); //edit_button->add_color_override("font_color",get_color("font_color","Tree")); //edit_button->add_color_override("font_color_hover",get_color("font_color","Tree")); @@ -2390,6 +2755,7 @@ void AnimationKeyEditor::_notification(int p_what) { } + call_select->connect("selected",this,"_add_call_track"); // rename_anim->set_icon( get_icon("Rename","EditorIcons") ); /* edit_anim->set_icon( get_icon("Edit","EditorIcons") ); @@ -2456,6 +2822,17 @@ void AnimationKeyEditor::_update_menu() { updating=false; } +void AnimationKeyEditor::_clear_selection() { + + selection.clear(); + key_edit->animation=Ref<Animation>(); + key_edit->track=0; + key_edit->key_ofs=0; + key_edit->hint=PropertyInfo(); + key_edit->notify_change(); + +} + void AnimationKeyEditor::set_animation(const Ref<Animation>& p_anim) { @@ -2466,11 +2843,12 @@ void AnimationKeyEditor::set_animation(const Ref<Animation>& p_anim) { animation->connect("changed",this,"_update_paths"); timeline_pos=0; - selection.clear(); + _clear_selection(); _update_paths(); _update_menu(); selected_track=-1; + _edit_if_single_selection(); } void AnimationKeyEditor::set_root(Node *p_root) { @@ -2538,7 +2916,7 @@ void AnimationKeyEditor::_query_insert(const InsertData& p_id) { insert_confirm->set_text("Create "+itos(insert_data.size())+" NEW tracks and insert keys?"); insert_confirm->get_ok()->set_text("Create"); - insert_confirm->popup_centered(Size2(300,100)); + insert_confirm->popup_centered_minsize(); insert_query=true; } else { call_deferred("_insert_delay"); @@ -2591,6 +2969,7 @@ void AnimationKeyEditor::insert_transform_key(Spatial *p_node,const String& p_su id.value=p_xform; id.type=Animation::TYPE_TRANSFORM; id.query="node '"+p_node->get_name()+"'"; + id.advance=false; //dialog insert @@ -2643,6 +3022,7 @@ void AnimationKeyEditor::insert_node_value_key(Node* p_node, const String& p_pro id.value=p_value; id.type=Animation::TYPE_VALUE; id.query="property '"+p_property+"'"; + id.advance=false; //dialog insert _query_insert(id); @@ -2650,7 +3030,7 @@ void AnimationKeyEditor::insert_node_value_key(Node* p_node, const String& p_pro } -void AnimationKeyEditor::insert_value_key(const String& p_property,const Variant& p_value) { +void AnimationKeyEditor::insert_value_key(const String& p_property,const Variant& p_value,bool p_advance) { ERR_FAIL_COND(!root); //let's build a node path @@ -2696,6 +3076,7 @@ void AnimationKeyEditor::insert_value_key(const String& p_property,const Variant id.value=p_value; id.type=Animation::TYPE_VALUE; id.query="property '"+p_property+"'"; + id.advance=p_advance; //dialog insert _query_insert(id); @@ -2909,11 +3290,11 @@ void AnimationKeyEditor::set_anim_pos(float p_pos) { void AnimationKeyEditor::_pane_drag(const Point2& p_delta) { - Size2 ecs = ec->get_minsize(); + Size2 ecs = ec->get_custom_minimum_size(); ecs.y-=p_delta.y; if (ecs.y<100) ecs.y=100; - ec->set_minsize(ecs);; + ec->set_custom_minimum_size(ecs);; } @@ -2928,20 +3309,39 @@ void AnimationKeyEditor::_insert_delay() { undo_redo->create_action("Anim Insert"); int last_track = animation->get_track_count(); + bool advance=false; while(insert_data.size()) { + if (insert_data.front()->get().advance) + advance=true; last_track=_confirm_insert(insert_data.front()->get(),last_track); insert_data.pop_front(); } undo_redo->commit_action(); + + if (advance) { + float step = animation->get_step(); + if (step==0) + step=1; + + float pos=timeline_pos; + + pos=Math::stepify(pos+step,step); + if (pos>animation->get_length()) + pos=animation->get_length(); + timeline_pos=pos; + track_pos->update(); + emit_signal("timeline_changed",pos); + } insert_queue=false; } void AnimationKeyEditor::_step_changed(float p_len) { updating=true; - animation->set_step(p_len); + if (!animation.is_null()) + animation->set_step(p_len); updating=false; } @@ -3077,6 +3477,26 @@ void AnimationKeyEditor::_scale() { } +void AnimationKeyEditor::_add_call_track(const NodePath& p_base) { + + print_line("BASE IS "+String(p_base)); + Node* base = EditorNode::get_singleton()->get_edited_scene(); + if (!base) + return; + Node* from=base->get_node(p_base); + if (!from || !root) + return; + + NodePath path = root->get_path_to(from); + + undo_redo->create_action("Anim Add Call Track"); + undo_redo->add_do_method(animation.ptr(),"add_track",Animation::TYPE_METHOD); + undo_redo->add_do_method(animation.ptr(),"track_set_path",animation->get_track_count(),path); + undo_redo->add_undo_method(animation.ptr(),"remove_track",animation->get_track_count()); + undo_redo->commit_action(); + +} + void AnimationKeyEditor::cleanup() { set_animation(Ref<Animation>()); @@ -3105,6 +3525,7 @@ void AnimationKeyEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("_track_editor_input_event"),&AnimationKeyEditor::_track_editor_input_event); ObjectTypeDB::bind_method(_MD("_track_name_changed"),&AnimationKeyEditor::_track_name_changed); ObjectTypeDB::bind_method(_MD("_track_menu_selected"),&AnimationKeyEditor::_track_menu_selected); + ObjectTypeDB::bind_method(_MD("_menu_add_track"),&AnimationKeyEditor::_menu_add_track); ObjectTypeDB::bind_method(_MD("_menu_track"),&AnimationKeyEditor::_menu_track); ObjectTypeDB::bind_method(_MD("_clear_selection_for_anim"),&AnimationKeyEditor::_clear_selection_for_anim); ObjectTypeDB::bind_method(_MD("_select_at_anim"),&AnimationKeyEditor::_select_at_anim); @@ -3122,12 +3543,16 @@ void AnimationKeyEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_animation"),&AnimationKeyEditor::set_animation); ObjectTypeDB::bind_method(_MD("_animation_optimize"),&AnimationKeyEditor::_animation_optimize); + ObjectTypeDB::bind_method(_MD("_curve_transition_changed"),&AnimationKeyEditor::_curve_transition_changed); + ObjectTypeDB::bind_method(_MD("_toggle_edit_curves"),&AnimationKeyEditor::_toggle_edit_curves); + ObjectTypeDB::bind_method(_MD("_add_call_track"),&AnimationKeyEditor::_add_call_track); ADD_SIGNAL( MethodInfo("resource_selected", PropertyInfo( Variant::OBJECT, "res"),PropertyInfo( Variant::STRING, "prop") ) ); ADD_SIGNAL( MethodInfo("keying_changed" ) ); ADD_SIGNAL( MethodInfo("timeline_changed", PropertyInfo(Variant::REAL,"pos") ) ); ADD_SIGNAL( MethodInfo("animation_len_changed", PropertyInfo(Variant::REAL,"len") ) ); + ADD_SIGNAL( MethodInfo("key_edited", PropertyInfo(Variant::INT,"track"), PropertyInfo(Variant::INT,"key") ) ); } @@ -3153,15 +3578,6 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h //menu->set_pos(Point2()); //add_child(menu); - menu_track = memnew( MenuButton ); - menu_track->set_text("Tracks"); - hb->add_child(menu_track); - menu_track->get_popup()->connect("item_pressed",this,"_menu_track"); - - - - hb->add_child( memnew( VSeparator ) ); - zoomicon = memnew( TextureFrame ); hb->add_child(zoomicon); zoomicon->set_tooltip("Animation zoom."); @@ -3219,12 +3635,10 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h hb->add_child( memnew( VSeparator ) ); - edit_button = memnew( ToolButton ); - edit_button->set_toggle_mode(true); - edit_button->set_focus_mode(FOCUS_NONE); - edit_button->set_disabled(true); - hb->add_child(edit_button); - edit_button->set_tooltip("Enable editing of individual keys by clicking them."); + menu_add_track = memnew( MenuButton ); + hb->add_child(menu_add_track); + menu_add_track->get_popup()->connect("item_pressed",this,"_menu_add_track"); + menu_add_track->set_tooltip("Add new tracks."); move_up_button = memnew( ToolButton ); hb->add_child(move_up_button); @@ -3238,7 +3652,7 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h move_down_button->connect("pressed",this,"_menu_track",make_binds(TRACK_MENU_MOVE_DOWN)); move_down_button->set_focus_mode(FOCUS_NONE); move_down_button->set_disabled(true); - move_down_button->set_tooltip("Move current track dosn."); + move_down_button->set_tooltip("Move current track down."); remove_button = memnew( ToolButton ); hb->add_child(remove_button); @@ -3247,6 +3661,20 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h remove_button->set_disabled(true); remove_button->set_tooltip("Remove selected track."); + hb->add_child(memnew( VSeparator )); + + menu_track = memnew( MenuButton ); + hb->add_child(menu_track); + menu_track->get_popup()->connect("item_pressed",this,"_menu_track"); + menu_track->set_tooltip("Track tools"); + + edit_button = memnew( ToolButton ); + edit_button->set_toggle_mode(true); + edit_button->set_focus_mode(FOCUS_NONE); + edit_button->set_disabled(true); + + hb->add_child(edit_button); + edit_button->set_tooltip("Enable editing of individual keys by clicking them."); optimize_dialog = memnew( ConfirmationDialog ); add_child(optimize_dialog); @@ -3289,15 +3717,15 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h keying->connect("pressed",this,"_keying_toggled"); */ - l = memnew( Label ); +/* l = memnew( Label ); l->set_text("Base: "); l->set_pos(Point2(0,3)); -// dr_panel->add_child(l); +// dr_panel->add_child(l);*/ // menu->get_popup()->connect("item_pressed",this,"_menu_callback"); - ec = memnew (EmptyControl); - ec->set_minsize(Size2(0,50)); + ec = memnew (Control); + ec->set_custom_minimum_size(Size2(0,150)); add_child(ec); ec->set_v_size_flags(SIZE_EXPAND_FILL); @@ -3313,6 +3741,7 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h track_editor->set_focus_mode(Control::FOCUS_ALL); track_editor->set_h_size_flags(SIZE_EXPAND_FILL); + track_pos = memnew( Control ); track_pos->set_area_as_parent_rect(); track_pos->set_ignore_mouse(true); @@ -3325,6 +3754,56 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h v_scroll->connect("value_changed",this,"_scroll_changed"); v_scroll->set_val(0); + key_editor_tab = memnew(TabContainer); + hb->add_child(key_editor_tab); + key_editor_tab->set_custom_minimum_size(Size2(200,0)); + + key_editor = memnew( PropertyEditor ); + key_editor->set_area_as_parent_rect(); + key_editor->hide_top_label(); + key_editor->set_name("Key"); + key_editor_tab->add_child(key_editor); + + key_edit = memnew( AnimationKeyEdit ); + key_edit->undo_redo=undo_redo; + //key_edit->ke_dialog=key_edit_dialog; + key_editor->edit(key_edit); + type_menu = memnew( PopupMenu ); + add_child(type_menu); + for(int i=0;i<Variant::VARIANT_MAX;i++) + type_menu->add_item(Variant::get_type_name(Variant::Type(i)),i); + type_menu->connect("item_pressed",this,"_create_value_item"); + + VBoxContainer *curve_vb = memnew( VBoxContainer ); + curve_vb->set_name("Transition"); + HBoxContainer *curve_hb = memnew( HBoxContainer ); + curve_vb->add_child(curve_hb); + + curve_linear = memnew( ToolButton ); + curve_linear->set_focus_mode(FOCUS_NONE); + curve_hb->add_child(curve_linear); + curve_in = memnew( ToolButton ); + curve_in->set_focus_mode(FOCUS_NONE); + curve_hb->add_child(curve_in); + curve_out = memnew( ToolButton ); + curve_out->set_focus_mode(FOCUS_NONE); + curve_hb->add_child(curve_out); + curve_inout = memnew( ToolButton ); + curve_inout->set_focus_mode(FOCUS_NONE); + curve_hb->add_child(curve_inout); + curve_outin = memnew( ToolButton ); + curve_outin->set_focus_mode(FOCUS_NONE); + curve_hb->add_child(curve_outin); + curve_constant = memnew( ToolButton ); + curve_constant->set_focus_mode(FOCUS_NONE); + curve_hb->add_child(curve_constant); + + + curve_edit = memnew( AnimationCurveEdit ); + curve_vb->add_child(curve_edit); + curve_edit->set_v_size_flags(SIZE_EXPAND_FILL); + key_editor_tab->add_child(curve_vb); + h_scroll = memnew( HScrollBar ); h_scroll->connect("value_changed",this,"_scroll_changed"); add_child(h_scroll); @@ -3340,7 +3819,7 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h add_child(track_menu); track_menu->connect("item_pressed",this,"_track_menu_selected"); - + key_editor_tab->hide(); last_idx =1; @@ -3359,24 +3838,6 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h timeline_pos=0; - key_edit_dialog = memnew( PopupDialog ); - key_editor = memnew( PropertyEditor ); - add_child(key_edit_dialog); - key_editor->set_area_as_parent_rect(); - key_editor->hide_top_label(); - for(int i=0;i<4;i++) - key_editor->set_margin(Margin(i),5); - key_edit_dialog->add_child(key_editor); - - key_edit = memnew( AnimationKeyEdit ); - key_edit->undo_redo=undo_redo; - key_edit->ke_dialog=key_edit_dialog; - key_editor->edit(key_edit); - type_menu = memnew( PopupMenu ); - add_child(type_menu); - for(int i=0;i<Variant::VARIANT_MAX;i++) - type_menu->add_item(Variant::get_type_name(Variant::Type(i)),i); - type_menu->connect("item_pressed",this,"_create_value_item"); keying=false; insert_frame=0; insert_query=false; @@ -3397,7 +3858,9 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h scale_dialog->connect("confirmed",this,"_scale"); add_child(scale_dialog); - + call_select = memnew( SceneTreeDialog ); + add_child(call_select); + call_select->set_title("Call Functions in Which Node?"); } diff --git a/tools/editor/animation_editor.h b/tools/editor/animation_editor.h index 9d3e692fad..629377d78e 100644 --- a/tools/editor/animation_editor.h +++ b/tools/editor/animation_editor.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,15 +37,17 @@ #include "scene/gui/scroll_bar.h" #include "scene/gui/tool_button.h" #include "scene/gui/file_dialog.h" -#include "scene/gui/empty_control.h" +#include "scene/gui/tab_container.h" + #include "scene/resources/animation.h" #include "scene/animation/animation_cache.h" #include "scene_tree_editor.h" #include "editor_data.h" #include "property_editor.h" - +#include "scene_tree_editor.h" class AnimationKeyEdit; +class AnimationCurveEdit; class AnimationKeyEditor : public VBoxContainer { @@ -68,9 +70,9 @@ class AnimationKeyEditor : public VBoxContainer { enum { - TRACK_MENU_ADD_VALUE_TRACK, - TRACK_MENU_ADD_TRANSFORM_TRACK, - TRACK_MENU_ADD_CALL_TRACK, + ADD_TRACK_MENU_ADD_VALUE_TRACK, + ADD_TRACK_MENU_ADD_TRANSFORM_TRACK, + ADD_TRACK_MENU_ADD_CALL_TRACK, TRACK_MENU_SCALE, TRACK_MENU_SCALE_PIVOT, TRACK_MENU_MOVE_UP, @@ -86,7 +88,13 @@ class AnimationKeyEditor : public VBoxContainer { TRACK_MENU_SET_ALL_TRANS_OUTIN, TRACK_MENU_NEXT_STEP, TRACK_MENU_PREV_STEP, - TRACK_MENU_OPTIMIZE + TRACK_MENU_OPTIMIZE, + CURVE_SET_LINEAR, + CURVE_SET_IN, + CURVE_SET_OUT, + CURVE_SET_INOUT, + CURVE_SET_OUTIN, + CURVE_SET_CONSTANT }; struct MouseOver { @@ -157,7 +165,7 @@ class AnimationKeyEditor : public VBoxContainer { PopupMenu *track_menu; PopupMenu *type_menu; - EmptyControl *ec; + Control *ec; TextureFrame *zoomicon; HSlider *zoom; //MenuButton *menu; @@ -169,6 +177,14 @@ class AnimationKeyEditor : public VBoxContainer { ToolButton *move_down_button; ToolButton *remove_button; + ToolButton *curve_linear; + ToolButton *curve_in; + ToolButton *curve_out; + ToolButton *curve_inout; + ToolButton *curve_outin; + ToolButton *curve_constant; + + ConfirmationDialog *optimize_dialog; SpinBox *optimize_linear_error; SpinBox *optimize_angular_error; @@ -176,6 +192,7 @@ class AnimationKeyEditor : public VBoxContainer { SpinBox *step; + MenuButton *menu_add_track; MenuButton *menu_track; HScrollBar *h_scroll; @@ -183,13 +200,15 @@ class AnimationKeyEditor : public VBoxContainer { Control *track_editor; Control *track_pos; + TabContainer *key_editor_tab; ConfirmationDialog *scale_dialog; SpinBox *scale; - PopupDialog *key_edit_dialog; PropertyEditor *key_editor; + SceneTreeDialog *call_select; + Ref<Animation> animation; void _update_paths(); @@ -203,6 +222,7 @@ class AnimationKeyEditor : public VBoxContainer { AnimationKeyEdit *key_edit; + AnimationCurveEdit *curve_edit; bool inserting; @@ -220,6 +240,7 @@ class AnimationKeyEditor : public VBoxContainer { int track_idx; Variant value; String query; + bool advance; };/* insert_data;*/ bool insert_query; @@ -254,7 +275,7 @@ class AnimationKeyEditor : public VBoxContainer { void _scale(); - + void _clear_selection(); //void _browse_path(); @@ -266,18 +287,24 @@ class AnimationKeyEditor : public VBoxContainer { void _scroll_changed(double); + void _menu_add_track(int p_type); void _menu_track(int p_type); void _clear_selection_for_anim(const Ref<Animation>& p_anim); void _select_at_anim(const Ref<Animation>& p_anim,int p_track,float p_pos); + void _curve_transition_changed(float p_what); PropertyInfo _find_hint_for_track(int p_idx); void _create_value_item(int p_type); void _pane_drag(const Point2& p_delta); + bool _edit_if_single_selection(); + void _toggle_edit_curves(); void _animation_len_update(); + void _add_call_track(const NodePath& p_base); + void _root_removed(); protected: @@ -296,7 +323,7 @@ public: void set_anim_pos(float p_pos); void insert_node_value_key(Node* p_node, const String& p_property,const Variant& p_value,bool p_only_if_exists=false); - void insert_value_key(const String& p_property,const Variant& p_value); + void insert_value_key(const String& p_property, const Variant& p_value, bool p_advance); void insert_transform_key(Spatial *p_node,const String& p_sub,const Transform& p_xform); AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_history, EditorSelection *p_selection); diff --git a/tools/editor/array_property_edit.cpp b/tools/editor/array_property_edit.cpp new file mode 100644 index 0000000000..9cd443270b --- /dev/null +++ b/tools/editor/array_property_edit.cpp @@ -0,0 +1,231 @@ +#include "array_property_edit.h" + +#include "editor_node.h" + +#define ITEMS_PER_PAGE 100 + +Variant ArrayPropertyEdit::get_array() const{ + + Object*o = ObjectDB::get_instance(obj); + if (!o) + return Array(); + Variant arr=o->get(property); + if (!arr.is_array()) { + Variant::CallError ce; + arr=Variant::construct(default_type,NULL,0,ce); + } + return arr; +} + +void ArrayPropertyEdit::_notif_change() { + _change_notify(); +} +void ArrayPropertyEdit::_notif_changev(const String& p_v) { + + _change_notify(p_v.utf8().get_data()); +} + +void ArrayPropertyEdit::_set_size(int p_size) { + + Variant arr = get_array(); + arr.call("resize",p_size); + Object*o = ObjectDB::get_instance(obj); + if (!o) + return; + + o->set(property,arr); + +} + +void ArrayPropertyEdit::_set_value(int p_idx,const Variant& p_value) { + + Variant arr = get_array(); + arr.set(p_idx,p_value); + Object*o = ObjectDB::get_instance(obj); + if (!o) + return; + + o->set(property,arr); +} + +bool ArrayPropertyEdit::_set(const StringName& p_name, const Variant& p_value){ + + String pn=p_name; + + if (pn.begins_with("array/")) { + + if (pn=="array/size") { + + Variant arr = get_array(); + int size = arr.call("size"); + + int newsize=p_value; + if (newsize==size) + return true; + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Resize Array"); + ur->add_do_method(this,"_set_size",newsize); + ur->add_undo_method(this,"_set_size",size); + if (newsize<size) { + for(int i=newsize;i<size;i++) { + ur->add_undo_method(this,"_set_value",i,arr.get(i)); + + } + } + ur->add_do_method(this,"_notif_change"); + ur->add_undo_method(this,"_notif_change"); + ur->commit_action(); + return true; + } + if (pn=="array/page") { + page=p_value; + _change_notify(); + return true; + } + } else if (pn.begins_with("indices")) { + + if (pn.find("_")!=-1) { + //type + int idx=pn.get_slicec('/',1).get_slicec('_',0).to_int(); + + int type = p_value; + + Variant arr = get_array(); + + Variant value = arr.get(idx); + if (value.get_type()!=type && type>=0 && type<Variant::VARIANT_MAX) { + Variant::CallError ce; + Variant new_value=Variant::construct(Variant::Type(type),NULL,0,ce); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + + ur->create_action("Change Array Value Type"); + ur->add_do_method(this,"_set_value",idx,new_value); + ur->add_undo_method(this,"_set_value",idx,value); + ur->add_do_method(this,"_notif_change"); + ur->add_undo_method(this,"_notif_change"); + ur->commit_action(); + + } + return true; + + } else { + int idx=pn.get_slicec('/',1).to_int(); + Variant arr = get_array(); + + Variant value = arr.get(idx); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + + ur->create_action("Change Array Value"); + ur->add_do_method(this,"_set_value",idx,p_value); + ur->add_undo_method(this,"_set_value",idx,value); + ur->add_do_method(this,"_notif_changev",p_name); + ur->add_undo_method(this,"_notif_changev",p_name); + ur->commit_action(); + return true; + } + } + + return false; +} + +bool ArrayPropertyEdit::_get(const StringName& p_name,Variant &r_ret) const { + + Variant arr = get_array(); + //int size = arr.call("size"); + + String pn=p_name; + if (pn.begins_with("array/")) { + + if (pn=="array/size") { + r_ret=arr.call("size"); + return true; + } + if (pn=="array/page") { + r_ret=page; + return true; + } + } else if (pn.begins_with("indices")) { + + if (pn.find("_")!=-1) { + //type + int idx=pn.get_slicec('/',1).get_slicec('_',0).to_int(); + bool valid; + r_ret=arr.get(idx,&valid); + if (valid) + r_ret=r_ret.get_type(); + return valid; + + } else { + int idx=pn.get_slicec('/',1).to_int(); + bool valid; + r_ret=arr.get(idx,&valid); + return valid; + } + } + + return false; +} + +void ArrayPropertyEdit::_get_property_list( List<PropertyInfo> *p_list) const{ + + Variant arr = get_array(); + int size = arr.call("size"); + + p_list->push_back( PropertyInfo(Variant::INT,"array/size",PROPERTY_HINT_RANGE,"0,100000,1") ); + int pages = size/ITEMS_PER_PAGE; + if (pages>0) + p_list->push_back( PropertyInfo(Variant::INT,"array/page",PROPERTY_HINT_RANGE,"0,"+itos(pages)+",1") ); + + int offset=page*ITEMS_PER_PAGE; + + int items=MIN(size-offset,ITEMS_PER_PAGE); + + + for(int i=0;i<items;i++) { + + Variant v=arr.get(i+offset); + if (arr.get_type()==Variant::ARRAY) { + p_list->push_back(PropertyInfo(Variant::INT,"indices/"+itos(i+offset)+"_type",PROPERTY_HINT_ENUM,vtypes)); + } + if (arr.get_type()!=Variant::ARRAY || v.get_type()!=Variant::NIL) { + PropertyInfo pi(v.get_type(),"indices/"+itos(i+offset)); + if (v.get_type()==Variant::OBJECT) { + pi.hint=PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string="Resource"; + } + p_list->push_back(pi); + } + } + +} + +void ArrayPropertyEdit::edit(Object* p_obj,const StringName& p_prop,Variant::Type p_deftype) { + + page=0; + property=p_prop; + obj=p_obj->get_instance_ID(); + default_type=p_deftype; + +} + +void ArrayPropertyEdit::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_set_size"),&ArrayPropertyEdit::_set_size); + ObjectTypeDB::bind_method(_MD("_set_value"),&ArrayPropertyEdit::_set_value); + ObjectTypeDB::bind_method(_MD("_notif_change"),&ArrayPropertyEdit::_notif_change); + ObjectTypeDB::bind_method(_MD("_notif_changev"),&ArrayPropertyEdit::_notif_changev); +} + +ArrayPropertyEdit::ArrayPropertyEdit() +{ + page=0; + for(int i=0;i<Variant::VARIANT_MAX;i++) { + + if (i>0) + vtypes+=","; + vtypes+=Variant::get_type_name( Variant::Type(i) ); + } + default_type=Variant::NIL; + +} diff --git a/tools/editor/array_property_edit.h b/tools/editor/array_property_edit.h new file mode 100644 index 0000000000..acfb8e68ed --- /dev/null +++ b/tools/editor/array_property_edit.h @@ -0,0 +1,36 @@ +#ifndef ARRAY_PROPERTY_EDIT_H +#define ARRAY_PROPERTY_EDIT_H + +#include "scene/main/node.h" + +class ArrayPropertyEdit : public Reference { + + OBJ_TYPE(ArrayPropertyEdit,Reference); + + int page; + ObjectID obj; + StringName property; + String vtypes; + Variant get_array() const; + Variant::Type default_type; + + void _notif_change(); + void _notif_changev(const String& p_v); + void _set_size(int p_size); + void _set_value(int p_idx,const Variant& p_value); + +protected: + + static void _bind_methods(); + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + +public: + + void edit(Object* p_obj, const StringName& p_prop, Variant::Type p_deftype); + + ArrayPropertyEdit(); +}; + +#endif // ARRAY_PROPERTY_EDIT_H diff --git a/tools/editor/call_dialog.cpp b/tools/editor/call_dialog.cpp index 2e4fb96a58..0e3abcf4ef 100644 --- a/tools/editor/call_dialog.cpp +++ b/tools/editor/call_dialog.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/call_dialog.h b/tools/editor/call_dialog.h index b7bf1affda..fe69847796 100644 --- a/tools/editor/call_dialog.h +++ b/tools/editor/call_dialog.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/code_editor.cpp b/tools/editor/code_editor.cpp index 242654c472..685763cadb 100644 --- a/tools/editor/code_editor.cpp +++ b/tools/editor/code_editor.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -599,14 +599,26 @@ CodeTextEditor::CodeTextEditor() { add_child(text_editor); text_editor->set_area_as_parent_rect(); text_editor->set_margin(MARGIN_BOTTOM,20); - text_editor->add_font_override("font",get_font("source","Fonts")); + + String editor_font = EDITOR_DEF("text_editor/font", ""); + bool font_overrode = false; + if (editor_font!="") { + Ref<Font> fnt = ResourceLoader::load(editor_font); + if (fnt.is_valid()) { + text_editor->add_font_override("font",fnt); + font_overrode = true; + } + } + + if (!font_overrode) + text_editor->add_font_override("font",get_font("source","Fonts")); text_editor->set_show_line_numbers(true); text_editor->set_brace_matching(true); line_col = memnew( Label ); add_child(line_col); line_col->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,135); - line_col->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,20); + line_col->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,15); line_col->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,1); line_col->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,5); //line_col->set_align(Label::ALIGN_RIGHT); @@ -625,7 +637,7 @@ CodeTextEditor::CodeTextEditor() { error = memnew( Label ); add_child(error); error->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN,5); - error->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,20); + error->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,15); error->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,1); error->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,130); error->hide(); diff --git a/tools/editor/code_editor.h b/tools/editor/code_editor.h index f82eaf5ec5..0c32aeb68f 100644 --- a/tools/editor/code_editor.h +++ b/tools/editor/code_editor.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/connections_dialog.cpp b/tools/editor/connections_dialog.cpp index d7158eb7e9..b0bacdae61 100644 --- a/tools/editor/connections_dialog.cpp +++ b/tools/editor/connections_dialog.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -172,7 +172,7 @@ void ConnectDialog::ok_pressed() { if (dst_method->get_text()=="") { error->set_text("Method in target Node must be specified!"); - error->popup_centered(Size2(300,80)); + error->popup_centered_minsize(); return; } emit_signal("connected"); @@ -530,7 +530,7 @@ void ConnectionsDialog::ok_pressed() { get_ok()->set_disabled(true); return; } - if (item->get_parent()==tree->get_root()) { + if (item->get_parent()==tree->get_root() || item->get_parent()->get_parent()==tree->get_root()) { //a signal - connect String signal=item->get_metadata(0).operator Dictionary()["name"]; String signalname=signal; @@ -607,6 +607,14 @@ void ConnectionsDialog::_remove_confirm() { } */ + +struct _ConnectionsDialogMethodInfoSort { + + _FORCE_INLINE_ bool operator()(const MethodInfo& a, const MethodInfo& b) const { + return a.name < b.name; + } +}; + void ConnectionsDialog::update_tree() { if (!is_visible()) @@ -623,74 +631,131 @@ void ConnectionsDialog::update_tree() { node->get_signal_list(&node_signals); - - for(List<MethodInfo>::Element *E=node_signals.front();E;E=E->next()) { - + //node_signals.sort_custom<_ConnectionsDialogMethodInfoSort>(); + bool did_script=false; + StringName base = node->get_type(); - MethodInfo &mi =E->get(); + while(base) { - String signaldesc; - signaldesc=mi.name+"("; - StringArray argnames; - if (mi.arguments.size()) { - signaldesc+=" "; - for(int i=0;i<mi.arguments.size();i++) { + List<MethodInfo> node_signals; + Ref<Texture> icon; + String name; - PropertyInfo &pi = mi.arguments[i]; + if (!did_script) { - if (i>0) - signaldesc+=", "; - signaldesc+=Variant::get_type_name(pi.type)+" "+(pi.name==""?String("arg "+itos(i)):pi.name); - argnames.push_back(pi.name); + Ref<Script> scr = node->get_script(); + if (scr.is_valid()) { + scr->get_script_signal_list(&node_signals); + if (scr->get_path().is_resource_file()) + name=scr->get_path().get_file(); + else + name=scr->get_type(); + if (has_icon(scr->get_type(),"EditorIcons")) { + icon=get_icon(scr->get_type(),"EditorIcons"); + } } - signaldesc+=" "; + + } else { + + ObjectTypeDB::get_signal_list(base,&node_signals,true); + if (has_icon(base,"EditorIcons")) { + icon=get_icon(base,"EditorIcons"); + } + name=base; } - signaldesc+=")"; - - TreeItem *item=tree->create_item(root); - item->set_text(0,signaldesc); - Dictionary sinfo; - sinfo["name"]=mi.name; - sinfo["args"]=argnames; - item->set_metadata(0,sinfo); - item->set_icon(0,get_icon("Signal","EditorIcons")); - List<Object::Connection> connections; - node->get_signal_connection_list(mi.name,&connections); + TreeItem *pitem = NULL; - for(List<Object::Connection>::Element *F=connections.front();F;F=F->next()) { + if (node_signals.size()) { + pitem=tree->create_item(root); + pitem->set_text(0,name); + pitem->set_icon(0,icon); + pitem->set_selectable(0,false); + pitem->set_editable(0,false); + pitem->set_custom_bg_color(0,get_color("prop_subsection","Editor")); + node_signals.sort(); + } - Object::Connection&c = F->get(); - if (!(c.flags&CONNECT_PERSIST)) - continue; + for(List<MethodInfo>::Element *E=node_signals.front();E;E=E->next()) { - Node *target = c.target->cast_to<Node>(); - if (!target) - continue; - String path = String(node->get_path_to(target))+" :: "+c.method+"()"; - if (c.flags&CONNECT_DEFERRED) - path+=" (deferred)"; - if (c.binds.size()) { + MethodInfo &mi =E->get(); - path+=" binds( "; - for(int i=0;i<c.binds.size();i++) { + String signaldesc; + signaldesc=mi.name+"("; + StringArray argnames; + if (mi.arguments.size()) { + signaldesc+=" "; + for(int i=0;i<mi.arguments.size();i++) { + + PropertyInfo &pi = mi.arguments[i]; if (i>0) - path+=", "; - path+=c.binds[i].operator String(); + signaldesc+=", "; + String tname="var"; + if (pi.type!=Variant::NIL) { + tname=Variant::get_type_name(pi.type); + } + signaldesc+=tname+" "+(pi.name==""?String("arg "+itos(i)):pi.name); + argnames.push_back(pi.name); + } - path+=" )"; + signaldesc+=" "; } - TreeItem *item2=tree->create_item(item); - item2->set_text(0,path); - item2->set_metadata(0,c); - item2->set_icon(0,get_icon("Slot","EditorIcons")); + signaldesc+=")"; + + TreeItem *item=tree->create_item(pitem); + item->set_text(0,signaldesc); + Dictionary sinfo; + sinfo["name"]=mi.name; + sinfo["args"]=argnames; + item->set_metadata(0,sinfo); + item->set_icon(0,get_icon("Signal","EditorIcons")); + + List<Object::Connection> connections; + node->get_signal_connection_list(mi.name,&connections); + + for(List<Object::Connection>::Element *F=connections.front();F;F=F->next()) { + + Object::Connection&c = F->get(); + if (!(c.flags&CONNECT_PERSIST)) + continue; + + Node *target = c.target->cast_to<Node>(); + if (!target) + continue; + + String path = String(node->get_path_to(target))+" :: "+c.method+"()"; + if (c.flags&CONNECT_DEFERRED) + path+=" (deferred)"; + if (c.binds.size()) { + path+=" binds( "; + for(int i=0;i<c.binds.size();i++) { + + if (i>0) + path+=", "; + path+=c.binds[i].operator String(); + } + path+=" )"; + } + + TreeItem *item2=tree->create_item(item); + item2->set_text(0,path); + item2->set_metadata(0,c); + item2->set_icon(0,get_icon("Slot","EditorIcons")); + + + } + } + if (!did_script) { + did_script=true; + } else { + base=ObjectTypeDB::type_inherits_from(base); } } @@ -713,7 +778,7 @@ void ConnectionsDialog::_something_selected() { get_ok()->set_text("Connect.."); get_ok()->set_disabled(true); - } else if (item->get_parent()==tree->get_root()) { + } else if (item->get_parent()==tree->get_root() || item->get_parent()->get_parent()==tree->get_root()) { //a signal - connect get_ok()->set_text("Connect.."); get_ok()->set_disabled(false); diff --git a/tools/editor/connections_dialog.h b/tools/editor/connections_dialog.h index 1e223494c8..68b13bf07a 100644 --- a/tools/editor/connections_dialog.h +++ b/tools/editor/connections_dialog.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/console.cpp b/tools/editor/console.cpp index 6b37895bc4..0c98f05706 100644 --- a/tools/editor/console.cpp +++ b/tools/editor/console.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/console.h b/tools/editor/console.h index b4ff62fa75..aff425fcde 100644 --- a/tools/editor/console.h +++ b/tools/editor/console.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/create_dialog.cpp b/tools/editor/create_dialog.cpp index f816e46bcf..a9119349c8 100644 --- a/tools/editor/create_dialog.cpp +++ b/tools/editor/create_dialog.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -128,7 +128,7 @@ void CreateDialog::_update_search() { _parse_fs(EditorFileSystem::get_singleton()->get_filesystem()); */ - List<String> type_list; + List<StringName> type_list; ObjectTypeDB::get_type_list(&type_list); HashMap<String,TreeItem*> types; @@ -137,7 +137,7 @@ void CreateDialog::_update_search() { root->set_text(0,base_type); - List<String>::Element *I=type_list.front(); + List<StringName>::Element *I=type_list.front(); TreeItem *to_select=NULL; for(;I;I=I->next()) { @@ -230,6 +230,11 @@ void CreateDialog::_notification(int p_what) { connect("confirmed",this,"_confirmed"); _update_search(); } + if (p_what==NOTIFICATION_EXIT_TREE) { + + disconnect("confirmed",this,"_confirmed"); + + } if (p_what==NOTIFICATION_VISIBILITY_CHANGED) { diff --git a/tools/editor/create_dialog.h b/tools/editor/create_dialog.h index 35f9f2a1fb..f200e1caf5 100644 --- a/tools/editor/create_dialog.h +++ b/tools/editor/create_dialog.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/default_saver.cpp b/tools/editor/default_saver.cpp index bbeda8addb..c865adb1eb 100644 --- a/tools/editor/default_saver.cpp +++ b/tools/editor/default_saver.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/default_saver.h b/tools/editor/default_saver.h index 3c5d23b134..4e11ff5592 100644 --- a/tools/editor/default_saver.h +++ b/tools/editor/default_saver.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/dependency_editor.cpp b/tools/editor/dependency_editor.cpp new file mode 100644 index 0000000000..c04e82a08a --- /dev/null +++ b/tools/editor/dependency_editor.cpp @@ -0,0 +1,512 @@ +#include "dependency_editor.h" +#include "os/file_access.h" +#include "scene/gui/margin_container.h" +#include "io/resource_loader.h" +#include "editor_node.h" + +void DependencyEditor::_notification(int p_what){ + + +} + +void DependencyEditor::_searched(const String& p_path) { + + Map<String,String> dep_rename; + dep_rename[replacing]=p_path; + + + ResourceLoader::rename_dependencies(editing,dep_rename); + + _update_list(); + _update_file(); +} + +void DependencyEditor::_load_pressed(Object* p_item,int p_cell,int p_button){ + + TreeItem *ti=p_item->cast_to<TreeItem>(); + String fname = ti->get_text(0); + replacing = ti->get_text(1); + + search->set_title("Search Replacement For: "+replacing.get_file()); + + search->clear_filters(); + List<String> ext; + ResourceLoader::get_recognized_extensions_for_type(ti->get_metadata(0),&ext); + for (List<String>::Element *E=ext.front();E;E=E->next()) { + search->add_filter("*"+E->get()); + } + search->popup_centered_ratio(); + +} + +void DependencyEditor::_fix_and_find(EditorFileSystemDirectory *efsd, Map<String,Map<String,String> >& candidates){ + + for(int i=0;i<efsd->get_subdir_count();i++) { + _fix_and_find(efsd->get_subdir(i),candidates); + } + + for(int i=0;i<efsd->get_file_count();i++) { + + String file = efsd->get_file(i); + if (!candidates.has(file)) + continue; + + String path = efsd->get_file_path(i); + Map<String,String> &ss = candidates[file]; + + + for(Map<String,String>::Element *E=candidates[file].front();E;E=E->next()) { + + if (E->get()==String()) { + E->get()=path; + continue; + } + + //must match the best, using subdirs + String existing=E->get().replace_first("res://",""); + String current=path.replace_first("res://",""); + String lost=E->key().replace_first("res://",""); + + Vector<String> existingv=existing.split("/"); + existingv.invert(); + Vector<String> currentv=current.split("/"); + currentv.invert(); + Vector<String> lostv=lost.split("/"); + lostv.invert(); + + int existing_score=0; + int current_score=0; + + for(int j=0;j<lostv.size();j++) { + + if (j<existingv.size() && lostv[j]==existingv[j]) { + existing_score++; + } + if (j<currentv.size() && lostv[j]==currentv[j]) { + current_score++; + } + } + + if (current_score > existing_score) { + + //if it was the same, could track distance to new path but.. + + E->get()=path; //replace by more accurate + } + + } + + } + +} + + +void DependencyEditor::_fix_all(){ + + if (!EditorFileSystem::get_singleton()->get_filesystem()) + return; + + Map<String,Map<String,String> > candidates; + + for (List<String>::Element *E=missing.front();E;E=E->next()) { + + String base = E->get().get_file(); + if (!candidates.has(base)) { + candidates[base]=Map<String,String>(); + } + + candidates[base][E->get()]=""; + } + + _fix_and_find(EditorFileSystem::get_singleton()->get_filesystem(),candidates); + + Map<String,String> remaps; + + for (Map<String,Map<String,String> >::Element *E=candidates.front();E;E=E->next()) { + + for (Map<String,String>::Element *F=E->get().front();F;F=F->next()) { + + if (F->get()!=String()) { + remaps[F->key()]=F->get(); + } + } + + } + + if (remaps.size()) { + + ResourceLoader::rename_dependencies(editing,remaps); + + _update_list(); + _update_file(); + } +} + +void DependencyEditor::_update_file() { + + EditorFileSystem::get_singleton()->update_file(editing); + +} + +void DependencyEditor::_update_list() { + + List<String> deps; + ResourceLoader::get_dependencies(editing,&deps,true); + + tree->clear(); + missing.clear(); + + TreeItem *root = tree->create_item(); + + Ref<Texture> folder = get_icon("folder","FileDialog"); + + bool broken=false; + + for(List<String>::Element *E=deps.front();E;E=E->next()) { + + TreeItem *item = tree->create_item(root); + + String n = E->get(); + String path; + String type; + + if (n.find("::")!=-1) { + path = n.get_slice("::",0); + type = n.get_slice("::",1); + } else { + path=n; + type="Resource"; + } + String name = path.get_file(); + + Ref<Texture> icon; + if (has_icon(type,"EditorIcons")) { + icon=get_icon(type,"EditorIcons"); + } else { + icon=get_icon("Object","EditorIcons"); + } + item->set_text(0,name); + item->set_icon(0,icon); + item->set_metadata(0,type); + item->set_text(1,path); + + if (!FileAccess::exists(path)) { + item->set_custom_color(1,Color(1,0.4,0.3)); + missing.push_back(path); + broken=true; + } + + item->add_button(1,folder,0); + } + + fixdeps->set_disabled(!broken); + +} + + + +void DependencyEditor::edit(const String& p_path) { + + + editing=p_path; + set_title("Dependencies For: "+p_path.get_file()); + + _update_list(); + popup_centered_ratio(); + + if (EditorNode::get_singleton()->is_scene_open(p_path)) { + EditorNode::get_singleton()->show_warning("Scene '"+p_path.get_file()+"' is currently being edited.\nChanges will not take effect unless reloaded."); + } else if (ResourceCache::has(p_path)) { + EditorNode::get_singleton()->show_warning("Resource '"+p_path.get_file()+"' is in use.\nChanges will take effect when reloaded."); + } +} + + +void DependencyEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_searched"),&DependencyEditor::_searched); + ObjectTypeDB::bind_method(_MD("_load_pressed"),&DependencyEditor::_load_pressed); + ObjectTypeDB::bind_method(_MD("_fix_all"),&DependencyEditor::_fix_all); + +} + +DependencyEditor::DependencyEditor() { + + VBoxContainer *vb = memnew( VBoxContainer ); + vb->set_name("Dependencies"); + add_child(vb); + set_child_rect(vb); + + tree = memnew( Tree ); + tree->set_columns(2); + tree->set_column_titles_visible(true); + tree->set_column_title(0,"Resource"); + tree->set_column_title(1,"Path"); + tree->set_hide_root(true); + tree->connect("button_pressed",this,"_load_pressed"); + + HBoxContainer *hbc = memnew( HBoxContainer ); + Label *label = memnew( Label("Dependencies:")); + hbc->add_child(label); + hbc->add_spacer(); + fixdeps = memnew( Button("Fix Broken")); + hbc->add_child(fixdeps); + fixdeps->connect("pressed",this,"_fix_all"); + + vb->add_child(hbc); + + MarginContainer *mc = memnew( MarginContainer ); + mc->set_v_size_flags(SIZE_EXPAND_FILL); + + mc->add_child(tree); + vb->add_child(mc); + + set_title("Dependency Editor"); + search = memnew( EditorFileDialog ); + search->connect("file_selected",this,"_searched"); + search->set_mode(EditorFileDialog::MODE_OPEN_FILE); + search->set_title("Search Replacement Resource:"); + add_child(search); + +} + +///////////////////////////////////// + + + +void DependencyEditorOwners::_fill_owners(EditorFileSystemDirectory *efsd) { + + if (!efsd) + return; + + for(int i=0;i<efsd->get_subdir_count();i++) { + _fill_owners(efsd->get_subdir(i)); + } + + for(int i=0;i<efsd->get_file_count();i++) { + + Vector<String> deps = efsd->get_file_deps(i); + //print_line(":::"+efsd->get_file_path(i)); + bool found=false; + for(int j=0;j<deps.size();j++) { + //print_line("\t"+deps[j]+" vs "+editing); + if (deps[j]==editing) { + //print_line("found"); + found=true; + break; + } + } + if (!found) + continue; + + Ref<Texture> icon; + String type=efsd->get_file_type(i); + if (!has_icon(type,"EditorIcons")) { + icon=get_icon("Object","EditorIcons"); + } else { + icon=get_icon(type,"EditorIcons"); + } + + owners->add_item(efsd->get_file_path(i),icon); + } + +} + +void DependencyEditorOwners::show(const String& p_path) { + + editing=p_path; + owners->clear(); + _fill_owners(EditorFileSystem::get_singleton()->get_filesystem()); + popup_centered_ratio(); + + set_title("Owners Of: "+p_path.get_file()); + +} + +DependencyEditorOwners::DependencyEditorOwners() { + + + owners = memnew( ItemList ); + add_child(owners); + set_child_rect(owners); + + +} + +/////////////////////// + + +void DependencyRemoveDialog::_fill_owners(EditorFileSystemDirectory *efsd) { + + if (!efsd) + return; + + for(int i=0;i<efsd->get_subdir_count();i++) { + _fill_owners(efsd->get_subdir(i)); + } + + for(int i=0;i<efsd->get_file_count();i++) { + + Vector<String> deps = efsd->get_file_deps(i); + //print_line(":::"+efsd->get_file_path(i)); + Set<String> met; + for(int j=0;j<deps.size();j++) { + if (files.has(deps[j])) { + met.insert(deps[j]); + } + } + if (!met.size()) + continue; + + exist=true; + + Ref<Texture> icon; + String type=efsd->get_file_type(i); + if (!has_icon(type,"EditorIcons")) { + icon=get_icon("Object","EditorIcons"); + } else { + icon=get_icon(type,"EditorIcons"); + } + + + for(Set<String>::Element *E=met.front();E;E=E->next()) { + + String which = E->get(); + if (!files[which]) { + TreeItem *ti=owners->create_item(owners->get_root()); + ti->set_text(0,which.get_file()); + files[which]=ti; + + } + TreeItem *ti=owners->create_item(files[which]); + ti->set_text(0,efsd->get_file_path(i)); + ti->set_icon(0,icon); + } + + } + +} + +void DependencyRemoveDialog::show(const Vector<String> &to_erase) { + + exist=false; + owners->clear(); + files.clear(); + TreeItem *root=owners->create_item(); + for(int i=0;i<to_erase.size();i++) { + files[to_erase[i]]=NULL; + } + + _fill_owners(EditorFileSystem::get_singleton()->get_filesystem()); + + if (exist) { + owners->show(); + text->set_text("The files being removed are required by other resources in order for them to work.\nRemove them anyway? (no undo)"); + popup_centered_minsize(Size2(500,220)); + } else { + owners->hide(); + text->set_text("Remove selected files from the project? (no undo)"); + popup_centered_minsize(Size2(400,100)); + } + +} + +void DependencyRemoveDialog::ok_pressed() { + + + DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + for (Map<String,TreeItem*>::Element *E=files.front();E;E=E->next()) { + + da->remove(E->key()); + EditorFileSystem::get_singleton()->update_file(E->key()); + } + memdelete(da); + +} + +DependencyRemoveDialog::DependencyRemoveDialog() { + + VBoxContainer *vb = memnew( VBoxContainer ); + add_child(vb); + set_child_rect(vb); + + text = memnew( Label ); + vb->add_child(text); + + owners = memnew( Tree ); + owners->set_hide_root(true); + vb->add_child(owners); + owners->set_v_size_flags(SIZE_EXPAND_FILL); + get_ok()->set_text("Remove"); +} + + +////////////// + + +void DependencyErrorDialog::show(const String& p_for_file,const Vector<String> &report) { + + + for_file=p_for_file; + set_title("Error loading: "+p_for_file.get_file()); + files->clear(); + + TreeItem *root = files->create_item(NULL); + for(int i=0;i<report.size();i++) { + + String dep; + String type="Object"; + dep=report[i].get_slice("::",0); + if (report[i].get_slice_count("::")>0) + type=report[i].get_slice("::",1); + + Ref<Texture> icon; + if (!has_icon(type,"EditorIcons")) { + icon=get_icon("Object","EditorIcons"); + } else { + icon=get_icon(type,"EditorIcons"); + } + + TreeItem *ti=files->create_item(root); + ti->set_text(0,dep); + ti->set_icon(0,icon); + + } + + popup_centered_minsize(Size2(500,220)); + +} + +void DependencyErrorDialog::ok_pressed() { + + EditorNode::get_singleton()->load_scene(for_file,true); +} + +void DependencyErrorDialog::custom_action(const String&) { + + EditorNode::get_singleton()->fix_dependencies(for_file); +} + +DependencyErrorDialog::DependencyErrorDialog() { + + VBoxContainer *vb = memnew( VBoxContainer ); + add_child(vb); + set_child_rect(vb); + + + files = memnew( Tree ); + files->set_hide_root(true); + vb->add_margin_child("Scene failed to load due to missing dependencies:",files,true); + files->set_v_size_flags(SIZE_EXPAND_FILL); + get_ok()->set_text("Open Anyway"); + + text = memnew( Label ); + vb->add_child(text); + text->set_text("Which action should be taken?"); + + + fdep=add_button("Fix Dependencies",true,"fixdeps"); + + set_title("Errors loading!"); + +} diff --git a/tools/editor/dependency_editor.h b/tools/editor/dependency_editor.h new file mode 100644 index 0000000000..1c328e7a93 --- /dev/null +++ b/tools/editor/dependency_editor.h @@ -0,0 +1,94 @@ +#ifndef DEPENDENCY_EDITOR_H +#define DEPENDENCY_EDITOR_H + +#include "scene/gui/dialogs.h" +#include "scene/gui/tree.h" +#include "scene/gui/tab_container.h" +#include "editor_file_dialog.h" + +class EditorFileSystemDirectory; + +class DependencyEditor : public AcceptDialog { + OBJ_TYPE(DependencyEditor,AcceptDialog); + + + Tree *tree; + Button *fixdeps; + + EditorFileDialog *search; + + String replacing; + String editing; + List<String> missing; + + + void _fix_and_find(EditorFileSystemDirectory *efsd, Map<String,Map<String,String> >& candidates); + + void _searched(const String& p_path); + void _load_pressed(Object* p_item,int p_cell,int p_button); + void _fix_all(); + void _update_list(); + + void _update_file(); + +protected: + + static void _bind_methods(); + void _notification(int p_what); +public: + + + void edit(const String& p_path); + DependencyEditor(); +}; + +class DependencyEditorOwners : public AcceptDialog { + OBJ_TYPE(DependencyEditorOwners,AcceptDialog); + + ItemList *owners; + String editing; + void _fill_owners(EditorFileSystemDirectory *efsd); + +public: + + void show(const String& p_path); + DependencyEditorOwners(); +}; + +class DependencyRemoveDialog : public ConfirmationDialog { + OBJ_TYPE(DependencyRemoveDialog,ConfirmationDialog); + + + Label *text; + Tree *owners; + bool exist; + Map<String,TreeItem*> files; + void _fill_owners(EditorFileSystemDirectory *efsd); + + void ok_pressed(); + +public: + + void show(const Vector<String> &to_erase); + DependencyRemoveDialog(); +}; + + +class DependencyErrorDialog : public ConfirmationDialog { + OBJ_TYPE(DependencyErrorDialog,ConfirmationDialog); + + + String for_file; + Button *fdep; + Label *text; + Tree *files; + void ok_pressed(); + void custom_action(const String&); + +public: + + void show(const String& p_for,const Vector<String> &report); + DependencyErrorDialog(); +}; + +#endif // DEPENDENCY_EDITOR_H diff --git a/tools/editor/doc_code_font.h b/tools/editor/doc_code_font.h index 470054ccd4..91f67c4a41 100644 --- a/tools/editor/doc_code_font.h +++ b/tools/editor/doc_code_font.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,7 +26,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -static const int _bi_font_doc_code_font_height=19;
-static const int _bi_font_doc_code_font_ascent=15;
-static const int _bi_font_doc_code_font_charcount=193;
-static const int _bi_font_doc_code_font_characters[]={0, 253, 53, 2, 2, -1, 14, 0, 13, 253, 56, 2, 2, -1, 14, 0, 32, 253, 59, 2, 2, -1, 14, 4, 33, 221, 29, 3, 12, 1, 3, 5, 34, 144, 77, 6, 5, 0, 3, 7, 35, 180, 55, 9, 11, -1, 4, 8, 36, 215, 0, 8, 15, 0, 2, 8, 37, 28, 45, 12, 11, -1, 4, 12, 38, 199, 16, 11, 12, 0, 3, 11, 39, 157, 77, 3, 5, 0, 3, 4, 40, 36, 16, 4, 15, 0, 3, 5, 41, 31, 16, 4, 15, 0, 3, 5, 42, 93, 79, 9, 7, -1, 3, 8, 43, 110, 68, 9, 9, -1, 5, 8, 44, 121, 78, 4, 6, -1, 12, 4, 45, 204, 76, 5, 3, -1, 9, 5, 46, 216, 75, 4, 3, 0, 12, 4, 47, 233, 0, 8, 15, -1, 2, 7, 48, 246, 29, 9, 11, -1, 4, 8, 49, 208, 54, 8, 11, 0, 4, 8, 50, 219, 42, 9, 11, -1, 4, 8, 51, 229, 41, 9, 11, -1, 4, 8, 52, 239, 41, 9, 11, -1, 4, 8, 53, 0, 57, 9, 11, -1, 4, 8, 54, 160, 55, 9, 11, -1, 4, 8, 55, 10, 57, 9, 11, -1, 4, 8, 56, 20, 57, 9, 11, -1, 4, 8, 57, 40, 57, 9, 11, -1, 4, 8, 58, 78, 80, 3, 8, 0, 7, 4, 59, 72, 68, 4, 11, -1, 7, 4, 60, 18, 69, 8, 11, 0, 4, 8, 61, 112, 78, 8, 7, 0, 6, 8, 62, 0, 69, 8, 11, 0, 4, 8, 63, 184, 30, 7, 12, 0, 3, 7, 64, 148, 16, 13, 13, 0, 4, 14, 65, 117, 44, 11, 11, -1, 4, 10, 66, 50, 57, 9, 11, 0, 4, 9, 67, 197, 42, 10, 11, -1, 4, 8, 68, 153, 43, 10, 11, 0, 4, 10, 69, 244, 53, 8, 11, 0, 4, 8, 70, 43, 69, 7, 11, 0, 4, 7, 71, 93, 44, 11, 11, -1, 4, 10, 72, 60, 56, 9, 11, 0, 4, 10, 73, 77, 68, 4, 11, 0, 4, 4, 74, 249, 41, 6, 11, -1, 4, 5, 75, 70, 56, 9, 11, 0, 4, 9, 76, 27, 69, 7, 11, 0, 4, 7, 77, 14, 45, 13, 11, 0, 4, 14, 78, 208, 42, 10, 11, 0, 4, 11, 79, 41, 45, 12, 11, -1, 4, 11, 80, 199, 54, 8, 11, 0, 4, 9, 81, 162, 16, 13, 13, -1, 4, 11, 82, 80, 56, 9, 11, 0, 4, 9, 83, 90, 56, 9, 11, -1, 4, 8, 84, 100, 56, 9, 11, -1, 4, 8, 85, 164, 43, 10, 11, 0, 4, 10, 86, 129, 44, 11, 11, -1, 4, 9, 87, 229, 29, 16, 11, -1, 4, 15, 88, 175, 43, 10, 11, -1, 4, 9, 89, 186, 43, 10, 11, -1, 4, 8, 90, 110, 56, 9, 11, -1, 4, 8, 91, 26, 16, 4, 15, 0, 3, 5, 92, 123, 0, 10, 15, -2, 2, 7, 93, 251, 0, 4, 15, 0, 3, 5, 94, 82, 79, 10, 7, -2, 4, 8, 95, 180, 76, 9, 3, -1, 15, 8, 96, 161, 77, 6, 4, -1, 3, 5, 97, 27, 81, 8, 8, -1, 7, 8, 98, 68, 31, 8, 12, 0, 3, 9, 99, 18, 81, 8, 8, -1, 7, 7, 100, 211, 16, 9, 12, -1, 3, 9, 101, 228, 66, 9, 8, -1, 7, 8, 102, 176, 30, 7, 12, -1, 3, 5, 103, 120, 56, 9, 11, -1, 7, 8, 104, 59, 31, 8, 12, 0, 3, 9, 105, 66, 68, 5, 11, -1, 4, 4, 106, 141, 16, 6, 14, -2, 4, 4, 107, 104, 31, 8, 12, 0, 3, 8, 108, 251, 16, 3, 12, 0, 3, 4, 109, 144, 68, 13, 8, 0, 7, 13, 110, 9, 81, 8, 8, 0, 7, 9, 111, 208, 66, 9, 8, -1, 7, 9, 112, 217, 54, 8, 11, 0, 7, 9, 113, 140, 56, 9, 11, -1, 7, 9, 114, 53, 81, 6, 8, 0, 7, 6, 115, 45, 81, 7, 8, -1, 7, 6, 116, 102, 68, 7, 10, -1, 5, 6, 117, 36, 81, 8, 8, 0, 7, 9, 118, 238, 65, 9, 8, -1, 7, 8, 119, 158, 68, 13, 8, -1, 7, 12, 120, 218, 66, 9, 8, -1, 7, 7, 121, 150, 56, 9, 11, -1, 7, 8, 122, 248, 65, 7, 8, -1, 7, 6, 123, 14, 16, 5, 15, 0, 3, 6, 124, 41, 16, 3, 15, 2, 3, 8, 125, 20, 16, 5, 15, 0, 3, 6, 126, 135, 78, 8, 5, -1, 4, 8, 160, 253, 62, 2, 2, -1, 14, 4, 161, 225, 29, 3, 12, 0, 5, 5, 162, 35, 69, 7, 11, 0, 4, 8, 163, 190, 55, 8, 11, 0, 4, 8, 164, 82, 68, 9, 10, -1, 5, 8, 165, 105, 44, 11, 11, -2, 4, 8, 166, 45, 16, 3, 15, 2, 3, 8, 167, 125, 16, 8, 14, 0, 3, 8, 168, 197, 76, 6, 3, 0, 4, 7, 169, 186, 16, 12, 12, 0, 3, 13, 170, 137, 68, 6, 9, 0, 4, 7, 171, 197, 67, 10, 8, -1, 6, 9, 172, 126, 78, 8, 5, -1, 8, 8, 173, 210, 75, 5, 3, -1, 9, 5, 174, 103, 79, 8, 7, 0, 3, 8, 175, 190, 76, 6, 3, 0, 4, 6, 176, 151, 77, 5, 5, 0, 3, 5, 177, 221, 16, 9, 12, -1, 3, 8, 178, 60, 81, 6, 8, -1, 2, 5, 179, 67, 80, 5, 8, -1, 2, 5, 180, 168, 77, 6, 4, -1, 3, 5, 181, 170, 55, 9, 11, 0, 7, 9, 182, 176, 16, 9, 13, 0, 4, 10, 183, 221, 75, 3, 3, 0, 9, 4, 184, 175, 76, 4, 4, 0, 14, 5, 185, 73, 80, 4, 8, 0, 2, 4, 186, 129, 68, 7, 9, 0, 4, 7, 187, 185, 67, 11, 8, -1, 6, 9, 188, 54, 44, 12, 11, -2, 4, 11, 189, 67, 44, 12, 11, -2, 4, 11, 190, 80, 44, 12, 11, -1, 4, 11, 191, 192, 29, 7, 12, -1, 5, 7, 192, 88, 0, 11, 15, -1, 0, 10, 193, 64, 0, 11, 15, -1, 0, 10, 194, 76, 0, 11, 15, -1, 0, 10, 195, 100, 0, 11, 15, -1, 0, 10, 196, 74, 16, 11, 14, -1, 1, 10, 197, 62, 16, 11, 14, -1, 1, 10, 198, 0, 45, 13, 11, -1, 4, 12, 199, 107, 16, 8, 14, 0, 4, 8, 200, 188, 0, 8, 15, 0, 0, 8, 201, 206, 0, 8, 15, 0, 0, 8, 202, 224, 0, 8, 15, 0, 0, 8, 203, 116, 16, 8, 14, 0, 1, 8, 204, 0, 16, 6, 15, -2, 0, 4, 205, 7, 16, 6, 15, -1, 0, 4, 206, 242, 0, 8, 15, -2, 0, 4, 207, 134, 16, 6, 14, -1, 1, 4, 208, 141, 44, 11, 11, -1, 4, 10, 209, 145, 0, 10, 15, 0, 0, 11, 210, 0, 0, 12, 15, -1, 0, 11, 211, 39, 0, 12, 15, -1, 0, 11, 212, 26, 0, 12, 15, -1, 0, 11, 213, 13, 0, 12, 15, -1, 0, 11, 214, 49, 16, 12, 14, -1, 1, 11, 215, 0, 81, 8, 8, 0, 6, 8, 216, 52, 0, 11, 15, 0, 2, 11, 217, 167, 0, 10, 15, 0, 0, 10, 218, 134, 0, 10, 15, 0, 0, 10, 219, 156, 0, 10, 15, 0, 0, 10, 220, 86, 16, 10, 14, 0, 1, 10, 221, 112, 0, 10, 15, -1, 0, 8, 222, 226, 54, 8, 11, 0, 4, 9, 223, 77, 31, 8, 12, 0, 3, 9, 224, 113, 31, 8, 12, -1, 3, 8, 225, 122, 31, 8, 12, -1, 3, 8, 226, 131, 31, 8, 12, -1, 3, 8, 227, 167, 30, 8, 12, -1, 3, 8, 228, 235, 53, 8, 11, -1, 4, 8, 229, 50, 31, 8, 12, -1, 3, 8, 230, 172, 67, 12, 8, 0, 7, 12, 231, 51, 69, 7, 11, 0, 7, 7, 232, 40, 32, 9, 12, -1, 3, 8, 233, 231, 16, 9, 12, -1, 3, 8, 234, 241, 16, 9, 12, -1, 3, 8, 235, 30, 57, 9, 11, -1, 4, 8, 236, 200, 29, 6, 12, -2, 3, 4, 237, 214, 29, 6, 12, -1, 3, 4, 238, 207, 29, 6, 12, -1, 3, 4, 239, 59, 69, 6, 11, -1, 4, 4, 240, 86, 31, 8, 12, 0, 3, 9, 241, 95, 31, 8, 12, 0, 3, 9, 242, 0, 32, 9, 12, -1, 3, 9, 243, 10, 32, 9, 12, -1, 3, 9, 244, 20, 32, 9, 12, -1, 3, 9, 245, 30, 32, 9, 12, -1, 3, 9, 246, 130, 56, 9, 11, -1, 4, 9, 247, 120, 68, 8, 9, -1, 5, 8, 248, 92, 68, 9, 10, 0, 6, 9, 249, 140, 31, 8, 12, 0, 3, 9, 250, 149, 30, 8, 12, 0, 3, 9, 251, 158, 30, 8, 12, 0, 3, 9, 252, 9, 69, 8, 11, 0, 4, 9, 253, 178, 0, 9, 15, -1, 3, 8, 254, 197, 0, 8, 15, 0, 3, 9, 255, 97, 16, 9, 14, -1, 4, 8};
+static const int _bi_font_doc_code_font_height=19; +static const int _bi_font_doc_code_font_ascent=15; +static const int _bi_font_doc_code_font_charcount=193; +static const int _bi_font_doc_code_font_characters[]={0, 253, 53, 2, 2, -1, 14, 0, 13, 253, 56, 2, 2, -1, 14, 0, 32, 253, 59, 2, 2, -1, 14, 4, 33, 221, 29, 3, 12, 1, 3, 5, 34, 144, 77, 6, 5, 0, 3, 7, 35, 180, 55, 9, 11, -1, 4, 8, 36, 215, 0, 8, 15, 0, 2, 8, 37, 28, 45, 12, 11, -1, 4, 12, 38, 199, 16, 11, 12, 0, 3, 11, 39, 157, 77, 3, 5, 0, 3, 4, 40, 36, 16, 4, 15, 0, 3, 5, 41, 31, 16, 4, 15, 0, 3, 5, 42, 93, 79, 9, 7, -1, 3, 8, 43, 110, 68, 9, 9, -1, 5, 8, 44, 121, 78, 4, 6, -1, 12, 4, 45, 204, 76, 5, 3, -1, 9, 5, 46, 216, 75, 4, 3, 0, 12, 4, 47, 233, 0, 8, 15, -1, 2, 7, 48, 246, 29, 9, 11, -1, 4, 8, 49, 208, 54, 8, 11, 0, 4, 8, 50, 219, 42, 9, 11, -1, 4, 8, 51, 229, 41, 9, 11, -1, 4, 8, 52, 239, 41, 9, 11, -1, 4, 8, 53, 0, 57, 9, 11, -1, 4, 8, 54, 160, 55, 9, 11, -1, 4, 8, 55, 10, 57, 9, 11, -1, 4, 8, 56, 20, 57, 9, 11, -1, 4, 8, 57, 40, 57, 9, 11, -1, 4, 8, 58, 78, 80, 3, 8, 0, 7, 4, 59, 72, 68, 4, 11, -1, 7, 4, 60, 18, 69, 8, 11, 0, 4, 8, 61, 112, 78, 8, 7, 0, 6, 8, 62, 0, 69, 8, 11, 0, 4, 8, 63, 184, 30, 7, 12, 0, 3, 7, 64, 148, 16, 13, 13, 0, 4, 14, 65, 117, 44, 11, 11, -1, 4, 10, 66, 50, 57, 9, 11, 0, 4, 9, 67, 197, 42, 10, 11, -1, 4, 8, 68, 153, 43, 10, 11, 0, 4, 10, 69, 244, 53, 8, 11, 0, 4, 8, 70, 43, 69, 7, 11, 0, 4, 7, 71, 93, 44, 11, 11, -1, 4, 10, 72, 60, 56, 9, 11, 0, 4, 10, 73, 77, 68, 4, 11, 0, 4, 4, 74, 249, 41, 6, 11, -1, 4, 5, 75, 70, 56, 9, 11, 0, 4, 9, 76, 27, 69, 7, 11, 0, 4, 7, 77, 14, 45, 13, 11, 0, 4, 14, 78, 208, 42, 10, 11, 0, 4, 11, 79, 41, 45, 12, 11, -1, 4, 11, 80, 199, 54, 8, 11, 0, 4, 9, 81, 162, 16, 13, 13, -1, 4, 11, 82, 80, 56, 9, 11, 0, 4, 9, 83, 90, 56, 9, 11, -1, 4, 8, 84, 100, 56, 9, 11, -1, 4, 8, 85, 164, 43, 10, 11, 0, 4, 10, 86, 129, 44, 11, 11, -1, 4, 9, 87, 229, 29, 16, 11, -1, 4, 15, 88, 175, 43, 10, 11, -1, 4, 9, 89, 186, 43, 10, 11, -1, 4, 8, 90, 110, 56, 9, 11, -1, 4, 8, 91, 26, 16, 4, 15, 0, 3, 5, 92, 123, 0, 10, 15, -2, 2, 7, 93, 251, 0, 4, 15, 0, 3, 5, 94, 82, 79, 10, 7, -2, 4, 8, 95, 180, 76, 9, 3, -1, 15, 8, 96, 161, 77, 6, 4, -1, 3, 5, 97, 27, 81, 8, 8, -1, 7, 8, 98, 68, 31, 8, 12, 0, 3, 9, 99, 18, 81, 8, 8, -1, 7, 7, 100, 211, 16, 9, 12, -1, 3, 9, 101, 228, 66, 9, 8, -1, 7, 8, 102, 176, 30, 7, 12, -1, 3, 5, 103, 120, 56, 9, 11, -1, 7, 8, 104, 59, 31, 8, 12, 0, 3, 9, 105, 66, 68, 5, 11, -1, 4, 4, 106, 141, 16, 6, 14, -2, 4, 4, 107, 104, 31, 8, 12, 0, 3, 8, 108, 251, 16, 3, 12, 0, 3, 4, 109, 144, 68, 13, 8, 0, 7, 13, 110, 9, 81, 8, 8, 0, 7, 9, 111, 208, 66, 9, 8, -1, 7, 9, 112, 217, 54, 8, 11, 0, 7, 9, 113, 140, 56, 9, 11, -1, 7, 9, 114, 53, 81, 6, 8, 0, 7, 6, 115, 45, 81, 7, 8, -1, 7, 6, 116, 102, 68, 7, 10, -1, 5, 6, 117, 36, 81, 8, 8, 0, 7, 9, 118, 238, 65, 9, 8, -1, 7, 8, 119, 158, 68, 13, 8, -1, 7, 12, 120, 218, 66, 9, 8, -1, 7, 7, 121, 150, 56, 9, 11, -1, 7, 8, 122, 248, 65, 7, 8, -1, 7, 6, 123, 14, 16, 5, 15, 0, 3, 6, 124, 41, 16, 3, 15, 2, 3, 8, 125, 20, 16, 5, 15, 0, 3, 6, 126, 135, 78, 8, 5, -1, 4, 8, 160, 253, 62, 2, 2, -1, 14, 4, 161, 225, 29, 3, 12, 0, 5, 5, 162, 35, 69, 7, 11, 0, 4, 8, 163, 190, 55, 8, 11, 0, 4, 8, 164, 82, 68, 9, 10, -1, 5, 8, 165, 105, 44, 11, 11, -2, 4, 8, 166, 45, 16, 3, 15, 2, 3, 8, 167, 125, 16, 8, 14, 0, 3, 8, 168, 197, 76, 6, 3, 0, 4, 7, 169, 186, 16, 12, 12, 0, 3, 13, 170, 137, 68, 6, 9, 0, 4, 7, 171, 197, 67, 10, 8, -1, 6, 9, 172, 126, 78, 8, 5, -1, 8, 8, 173, 210, 75, 5, 3, -1, 9, 5, 174, 103, 79, 8, 7, 0, 3, 8, 175, 190, 76, 6, 3, 0, 4, 6, 176, 151, 77, 5, 5, 0, 3, 5, 177, 221, 16, 9, 12, -1, 3, 8, 178, 60, 81, 6, 8, -1, 2, 5, 179, 67, 80, 5, 8, -1, 2, 5, 180, 168, 77, 6, 4, -1, 3, 5, 181, 170, 55, 9, 11, 0, 7, 9, 182, 176, 16, 9, 13, 0, 4, 10, 183, 221, 75, 3, 3, 0, 9, 4, 184, 175, 76, 4, 4, 0, 14, 5, 185, 73, 80, 4, 8, 0, 2, 4, 186, 129, 68, 7, 9, 0, 4, 7, 187, 185, 67, 11, 8, -1, 6, 9, 188, 54, 44, 12, 11, -2, 4, 11, 189, 67, 44, 12, 11, -2, 4, 11, 190, 80, 44, 12, 11, -1, 4, 11, 191, 192, 29, 7, 12, -1, 5, 7, 192, 88, 0, 11, 15, -1, 0, 10, 193, 64, 0, 11, 15, -1, 0, 10, 194, 76, 0, 11, 15, -1, 0, 10, 195, 100, 0, 11, 15, -1, 0, 10, 196, 74, 16, 11, 14, -1, 1, 10, 197, 62, 16, 11, 14, -1, 1, 10, 198, 0, 45, 13, 11, -1, 4, 12, 199, 107, 16, 8, 14, 0, 4, 8, 200, 188, 0, 8, 15, 0, 0, 8, 201, 206, 0, 8, 15, 0, 0, 8, 202, 224, 0, 8, 15, 0, 0, 8, 203, 116, 16, 8, 14, 0, 1, 8, 204, 0, 16, 6, 15, -2, 0, 4, 205, 7, 16, 6, 15, -1, 0, 4, 206, 242, 0, 8, 15, -2, 0, 4, 207, 134, 16, 6, 14, -1, 1, 4, 208, 141, 44, 11, 11, -1, 4, 10, 209, 145, 0, 10, 15, 0, 0, 11, 210, 0, 0, 12, 15, -1, 0, 11, 211, 39, 0, 12, 15, -1, 0, 11, 212, 26, 0, 12, 15, -1, 0, 11, 213, 13, 0, 12, 15, -1, 0, 11, 214, 49, 16, 12, 14, -1, 1, 11, 215, 0, 81, 8, 8, 0, 6, 8, 216, 52, 0, 11, 15, 0, 2, 11, 217, 167, 0, 10, 15, 0, 0, 10, 218, 134, 0, 10, 15, 0, 0, 10, 219, 156, 0, 10, 15, 0, 0, 10, 220, 86, 16, 10, 14, 0, 1, 10, 221, 112, 0, 10, 15, -1, 0, 8, 222, 226, 54, 8, 11, 0, 4, 9, 223, 77, 31, 8, 12, 0, 3, 9, 224, 113, 31, 8, 12, -1, 3, 8, 225, 122, 31, 8, 12, -1, 3, 8, 226, 131, 31, 8, 12, -1, 3, 8, 227, 167, 30, 8, 12, -1, 3, 8, 228, 235, 53, 8, 11, -1, 4, 8, 229, 50, 31, 8, 12, -1, 3, 8, 230, 172, 67, 12, 8, 0, 7, 12, 231, 51, 69, 7, 11, 0, 7, 7, 232, 40, 32, 9, 12, -1, 3, 8, 233, 231, 16, 9, 12, -1, 3, 8, 234, 241, 16, 9, 12, -1, 3, 8, 235, 30, 57, 9, 11, -1, 4, 8, 236, 200, 29, 6, 12, -2, 3, 4, 237, 214, 29, 6, 12, -1, 3, 4, 238, 207, 29, 6, 12, -1, 3, 4, 239, 59, 69, 6, 11, -1, 4, 4, 240, 86, 31, 8, 12, 0, 3, 9, 241, 95, 31, 8, 12, 0, 3, 9, 242, 0, 32, 9, 12, -1, 3, 9, 243, 10, 32, 9, 12, -1, 3, 9, 244, 20, 32, 9, 12, -1, 3, 9, 245, 30, 32, 9, 12, -1, 3, 9, 246, 130, 56, 9, 11, -1, 4, 9, 247, 120, 68, 8, 9, -1, 5, 8, 248, 92, 68, 9, 10, 0, 6, 9, 249, 140, 31, 8, 12, 0, 3, 9, 250, 149, 30, 8, 12, 0, 3, 9, 251, 158, 30, 8, 12, 0, 3, 9, 252, 9, 69, 8, 11, 0, 4, 9, 253, 178, 0, 9, 15, -1, 3, 8, 254, 197, 0, 8, 15, 0, 3, 9, 255, 97, 16, 9, 14, -1, 4, 8}; diff --git a/tools/editor/doc_font.h b/tools/editor/doc_font.h index fe998cfb16..f2e5e7950b 100644 --- a/tools/editor/doc_font.h +++ b/tools/editor/doc_font.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,7 +26,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -static const int _bi_font_doc_font_height=18;
-static const int _bi_font_doc_font_ascent=14;
-static const int _bi_font_doc_font_charcount=193;
-static const int _bi_font_doc_font_characters[]={0, 253, 3, 2, 2, -1, 13, 0, 13, 253, 9, 2, 2, -1, 13, 0, 32, 253, 0, 2, 2, -1, 13, 3, 33, 220, 26, 3, 11, 1, 3, 5, 34, 187, 59, 5, 5, 0, 3, 6, 35, 88, 40, 9, 10, -1, 4, 7, 36, 50, 0, 8, 14, -1, 2, 7, 37, 175, 14, 11, 11, -1, 3, 11, 38, 187, 14, 11, 11, -1, 3, 10, 39, 198, 58, 3, 5, 0, 3, 3, 40, 98, 0, 4, 14, 0, 3, 5, 41, 103, 0, 4, 14, 0, 3, 5, 42, 146, 61, 7, 6, 0, 3, 7, 43, 246, 48, 8, 8, -1, 5, 7, 44, 193, 58, 4, 5, -1, 11, 4, 45, 13, 73, 5, 2, 0, 9, 5, 46, 241, 57, 3, 3, 0, 11, 4, 47, 84, 0, 7, 14, -1, 2, 6, 48, 246, 37, 8, 10, -1, 4, 7, 49, 33, 52, 7, 10, 0, 4, 7, 50, 237, 37, 8, 10, -1, 4, 7, 51, 219, 38, 8, 10, -1, 4, 7, 52, 128, 39, 9, 10, -1, 4, 7, 53, 41, 52, 7, 10, -1, 4, 7, 54, 9, 53, 7, 10, 0, 4, 7, 55, 228, 38, 8, 10, -1, 4, 7, 56, 57, 52, 7, 10, 0, 4, 7, 57, 65, 51, 7, 10, 0, 4, 7, 58, 98, 62, 4, 8, 0, 6, 4, 59, 162, 50, 5, 10, -1, 6, 4, 60, 174, 49, 8, 9, -1, 4, 7, 61, 179, 59, 7, 5, 0, 7, 7, 62, 228, 49, 8, 8, -1, 5, 7, 63, 89, 28, 7, 11, 0, 3, 7, 64, 54, 15, 12, 12, 0, 4, 13, 65, 36, 41, 10, 10, -1, 4, 9, 66, 97, 51, 7, 10, 0, 4, 8, 67, 174, 38, 8, 10, 0, 4, 8, 68, 156, 39, 8, 10, 0, 4, 9, 69, 105, 51, 7, 10, 0, 4, 7, 70, 121, 51, 6, 10, 0, 4, 7, 71, 0, 53, 8, 10, 0, 4, 9, 72, 192, 38, 8, 10, 0, 4, 9, 73, 251, 26, 3, 10, 0, 4, 4, 74, 156, 50, 5, 10, -1, 4, 5, 75, 147, 39, 8, 10, 0, 4, 8, 76, 135, 50, 6, 10, 0, 4, 6, 77, 13, 41, 11, 10, 0, 4, 12, 78, 210, 38, 8, 10, 0, 4, 10, 79, 68, 40, 9, 10, 0, 4, 10, 80, 113, 51, 7, 10, 0, 4, 8, 81, 199, 14, 10, 11, 0, 4, 10, 82, 183, 38, 8, 10, 0, 4, 8, 83, 128, 50, 6, 10, 0, 4, 7, 84, 108, 40, 9, 10, -1, 4, 7, 85, 165, 39, 8, 10, 0, 4, 9, 86, 25, 41, 10, 10, -1, 4, 9, 87, 236, 26, 14, 10, -1, 4, 13, 88, 98, 40, 9, 10, -1, 4, 8, 89, 118, 40, 9, 10, -1, 4, 7, 90, 138, 39, 8, 10, -1, 4, 7, 91, 49, 15, 4, 13, 0, 3, 5, 92, 76, 0, 7, 14, -1, 2, 6, 93, 43, 15, 5, 13, -1, 3, 5, 94, 137, 61, 8, 6, -1, 4, 7, 95, 245, 57, 9, 2, -1, 15, 7, 96, 230, 58, 5, 3, -1, 3, 4, 97, 57, 63, 7, 8, 0, 6, 7, 98, 81, 28, 7, 11, 0, 3, 8, 99, 79, 62, 6, 8, 0, 6, 6, 100, 33, 29, 7, 11, 0, 3, 8, 101, 9, 64, 7, 8, 0, 6, 8, 102, 200, 26, 6, 11, -1, 3, 5, 103, 153, 27, 7, 11, 0, 6, 7, 104, 121, 27, 7, 11, 0, 3, 8, 105, 224, 26, 3, 11, 0, 3, 4, 106, 92, 0, 5, 14, -2, 3, 4, 107, 25, 29, 7, 11, 0, 3, 7, 108, 228, 26, 3, 11, 0, 3, 4, 109, 204, 49, 11, 8, 0, 6, 12, 110, 17, 63, 7, 8, 0, 6, 8, 111, 33, 63, 7, 8, 0, 6, 8, 112, 17, 29, 7, 11, 0, 6, 8, 113, 9, 29, 7, 11, 0, 6, 8, 114, 86, 62, 5, 8, 0, 6, 5, 115, 92, 62, 5, 8, 0, 6, 6, 116, 168, 50, 5, 10, -1, 4, 5, 117, 49, 63, 7, 8, 0, 6, 8, 118, 0, 64, 8, 8, -1, 6, 7, 119, 216, 49, 11, 8, -1, 6, 10, 120, 41, 63, 7, 8, -1, 6, 7, 121, 0, 30, 8, 11, -1, 6, 7, 122, 72, 62, 6, 8, 0, 6, 6, 123, 8, 15, 6, 13, -1, 3, 5, 124, 0, 0, 3, 15, 1, 2, 7, 125, 15, 15, 6, 13, -1, 3, 5, 126, 202, 58, 8, 4, -1, 4, 7, 160, 253, 6, 2, 2, -1, 13, 3, 161, 232, 26, 3, 11, 0, 4, 5, 162, 149, 50, 6, 10, 0, 4, 7, 163, 17, 52, 7, 10, 0, 4, 7, 164, 183, 49, 8, 9, -1, 5, 7, 165, 47, 41, 10, 10, -2, 4, 7, 166, 4, 0, 3, 15, 2, 2, 7, 167, 122, 14, 7, 12, 0, 3, 7, 168, 0, 73, 6, 2, -1, 4, 6, 169, 162, 14, 12, 11, 0, 3, 13, 170, 65, 62, 6, 8, -1, 4, 6, 171, 112, 62, 8, 7, -1, 6, 8, 172, 211, 58, 7, 4, -1, 8, 7, 173, 19, 72, 5, 2, 0, 9, 5, 174, 103, 62, 8, 7, -1, 3, 8, 175, 7, 73, 5, 2, 0, 3, 6, 176, 171, 61, 7, 5, -1, 3, 5, 177, 58, 40, 9, 10, -1, 4, 7, 178, 154, 61, 5, 6, 0, 2, 5, 179, 160, 61, 5, 6, -1, 2, 5, 180, 224, 58, 5, 3, -1, 3, 4, 181, 241, 14, 8, 11, 0, 6, 8, 182, 97, 15, 8, 12, 0, 4, 9, 183, 236, 58, 4, 3, 0, 8, 4, 184, 219, 58, 4, 4, 0, 13, 5, 185, 166, 61, 4, 6, 0, 2, 4, 186, 25, 63, 7, 8, -1, 4, 6, 187, 129, 61, 7, 7, 0, 6, 8, 188, 221, 14, 9, 11, 0, 3, 10, 189, 231, 14, 9, 11, 0, 3, 10, 190, 210, 14, 10, 11, -1, 3, 10, 191, 41, 29, 7, 11, -1, 4, 7, 192, 141, 0, 10, 13, -1, 1, 9, 193, 130, 0, 10, 13, -1, 1, 9, 194, 119, 0, 10, 13, -1, 1, 9, 195, 20, 0, 10, 14, -1, 0, 9, 196, 67, 15, 10, 12, -1, 2, 9, 197, 108, 0, 10, 13, -1, 1, 9, 198, 0, 42, 12, 10, -1, 4, 11, 199, 228, 0, 8, 13, 0, 4, 8, 200, 245, 0, 7, 13, 0, 1, 7, 201, 237, 0, 7, 13, 0, 1, 7, 202, 0, 16, 7, 13, 0, 1, 7, 203, 130, 14, 7, 12, 0, 2, 7, 204, 36, 15, 6, 13, -2, 1, 4, 205, 22, 15, 6, 13, -1, 1, 4, 206, 29, 15, 6, 13, -1, 1, 4, 207, 154, 14, 7, 12, -2, 2, 4, 208, 78, 40, 9, 10, -1, 4, 9, 209, 59, 0, 8, 14, 0, 0, 10, 210, 172, 0, 9, 13, 0, 1, 10, 211, 152, 0, 9, 13, 0, 1, 10, 212, 182, 0, 9, 13, 0, 1, 10, 213, 31, 0, 9, 14, 0, 0, 10, 214, 78, 15, 9, 12, 0, 2, 10, 215, 121, 62, 7, 7, 0, 6, 7, 216, 8, 0, 11, 14, -1, 2, 10, 217, 192, 0, 8, 13, 0, 1, 9, 218, 210, 0, 8, 13, 0, 1, 9, 219, 201, 0, 8, 13, 0, 1, 9, 220, 88, 15, 8, 12, 0, 2, 9, 221, 162, 0, 9, 13, -1, 1, 7, 222, 81, 51, 7, 10, 0, 4, 8, 223, 49, 29, 7, 11, 0, 3, 8, 224, 57, 28, 7, 11, 0, 3, 7, 225, 65, 28, 7, 11, 0, 3, 7, 226, 73, 28, 7, 11, 0, 3, 7, 227, 138, 14, 7, 12, 0, 2, 7, 228, 25, 52, 7, 10, 0, 4, 7, 229, 146, 14, 7, 12, 0, 2, 7, 230, 192, 49, 11, 8, 0, 6, 12, 231, 207, 26, 6, 11, 0, 6, 6, 232, 97, 28, 7, 11, 0, 3, 8, 233, 105, 28, 7, 11, 0, 3, 8, 234, 113, 28, 7, 11, 0, 3, 8, 235, 73, 51, 7, 10, 0, 4, 8, 236, 214, 26, 5, 11, -2, 3, 3, 237, 250, 14, 5, 11, -1, 3, 3, 238, 193, 26, 6, 11, -2, 3, 3, 239, 142, 50, 6, 10, -2, 4, 3, 240, 185, 26, 7, 11, 0, 3, 8, 241, 106, 15, 7, 12, 0, 2, 8, 242, 129, 27, 7, 11, 0, 3, 8, 243, 137, 27, 7, 11, 0, 3, 8, 244, 145, 27, 7, 11, 0, 3, 8, 245, 114, 14, 7, 12, 0, 2, 8, 246, 49, 52, 7, 10, 0, 4, 8, 247, 237, 48, 8, 8, -1, 5, 7, 248, 201, 38, 8, 10, 0, 5, 8, 249, 161, 27, 7, 11, 0, 3, 8, 250, 169, 26, 7, 11, 0, 3, 8, 251, 177, 26, 7, 11, 0, 3, 8, 252, 89, 51, 7, 10, 0, 4, 8, 253, 41, 0, 8, 14, -1, 3, 7, 254, 68, 0, 7, 14, 0, 3, 8, 255, 219, 0, 8, 13, -1, 4, 7};
+static const int _bi_font_doc_font_height=18; +static const int _bi_font_doc_font_ascent=14; +static const int _bi_font_doc_font_charcount=193; +static const int _bi_font_doc_font_characters[]={0, 253, 3, 2, 2, -1, 13, 0, 13, 253, 9, 2, 2, -1, 13, 0, 32, 253, 0, 2, 2, -1, 13, 3, 33, 220, 26, 3, 11, 1, 3, 5, 34, 187, 59, 5, 5, 0, 3, 6, 35, 88, 40, 9, 10, -1, 4, 7, 36, 50, 0, 8, 14, -1, 2, 7, 37, 175, 14, 11, 11, -1, 3, 11, 38, 187, 14, 11, 11, -1, 3, 10, 39, 198, 58, 3, 5, 0, 3, 3, 40, 98, 0, 4, 14, 0, 3, 5, 41, 103, 0, 4, 14, 0, 3, 5, 42, 146, 61, 7, 6, 0, 3, 7, 43, 246, 48, 8, 8, -1, 5, 7, 44, 193, 58, 4, 5, -1, 11, 4, 45, 13, 73, 5, 2, 0, 9, 5, 46, 241, 57, 3, 3, 0, 11, 4, 47, 84, 0, 7, 14, -1, 2, 6, 48, 246, 37, 8, 10, -1, 4, 7, 49, 33, 52, 7, 10, 0, 4, 7, 50, 237, 37, 8, 10, -1, 4, 7, 51, 219, 38, 8, 10, -1, 4, 7, 52, 128, 39, 9, 10, -1, 4, 7, 53, 41, 52, 7, 10, -1, 4, 7, 54, 9, 53, 7, 10, 0, 4, 7, 55, 228, 38, 8, 10, -1, 4, 7, 56, 57, 52, 7, 10, 0, 4, 7, 57, 65, 51, 7, 10, 0, 4, 7, 58, 98, 62, 4, 8, 0, 6, 4, 59, 162, 50, 5, 10, -1, 6, 4, 60, 174, 49, 8, 9, -1, 4, 7, 61, 179, 59, 7, 5, 0, 7, 7, 62, 228, 49, 8, 8, -1, 5, 7, 63, 89, 28, 7, 11, 0, 3, 7, 64, 54, 15, 12, 12, 0, 4, 13, 65, 36, 41, 10, 10, -1, 4, 9, 66, 97, 51, 7, 10, 0, 4, 8, 67, 174, 38, 8, 10, 0, 4, 8, 68, 156, 39, 8, 10, 0, 4, 9, 69, 105, 51, 7, 10, 0, 4, 7, 70, 121, 51, 6, 10, 0, 4, 7, 71, 0, 53, 8, 10, 0, 4, 9, 72, 192, 38, 8, 10, 0, 4, 9, 73, 251, 26, 3, 10, 0, 4, 4, 74, 156, 50, 5, 10, -1, 4, 5, 75, 147, 39, 8, 10, 0, 4, 8, 76, 135, 50, 6, 10, 0, 4, 6, 77, 13, 41, 11, 10, 0, 4, 12, 78, 210, 38, 8, 10, 0, 4, 10, 79, 68, 40, 9, 10, 0, 4, 10, 80, 113, 51, 7, 10, 0, 4, 8, 81, 199, 14, 10, 11, 0, 4, 10, 82, 183, 38, 8, 10, 0, 4, 8, 83, 128, 50, 6, 10, 0, 4, 7, 84, 108, 40, 9, 10, -1, 4, 7, 85, 165, 39, 8, 10, 0, 4, 9, 86, 25, 41, 10, 10, -1, 4, 9, 87, 236, 26, 14, 10, -1, 4, 13, 88, 98, 40, 9, 10, -1, 4, 8, 89, 118, 40, 9, 10, -1, 4, 7, 90, 138, 39, 8, 10, -1, 4, 7, 91, 49, 15, 4, 13, 0, 3, 5, 92, 76, 0, 7, 14, -1, 2, 6, 93, 43, 15, 5, 13, -1, 3, 5, 94, 137, 61, 8, 6, -1, 4, 7, 95, 245, 57, 9, 2, -1, 15, 7, 96, 230, 58, 5, 3, -1, 3, 4, 97, 57, 63, 7, 8, 0, 6, 7, 98, 81, 28, 7, 11, 0, 3, 8, 99, 79, 62, 6, 8, 0, 6, 6, 100, 33, 29, 7, 11, 0, 3, 8, 101, 9, 64, 7, 8, 0, 6, 8, 102, 200, 26, 6, 11, -1, 3, 5, 103, 153, 27, 7, 11, 0, 6, 7, 104, 121, 27, 7, 11, 0, 3, 8, 105, 224, 26, 3, 11, 0, 3, 4, 106, 92, 0, 5, 14, -2, 3, 4, 107, 25, 29, 7, 11, 0, 3, 7, 108, 228, 26, 3, 11, 0, 3, 4, 109, 204, 49, 11, 8, 0, 6, 12, 110, 17, 63, 7, 8, 0, 6, 8, 111, 33, 63, 7, 8, 0, 6, 8, 112, 17, 29, 7, 11, 0, 6, 8, 113, 9, 29, 7, 11, 0, 6, 8, 114, 86, 62, 5, 8, 0, 6, 5, 115, 92, 62, 5, 8, 0, 6, 6, 116, 168, 50, 5, 10, -1, 4, 5, 117, 49, 63, 7, 8, 0, 6, 8, 118, 0, 64, 8, 8, -1, 6, 7, 119, 216, 49, 11, 8, -1, 6, 10, 120, 41, 63, 7, 8, -1, 6, 7, 121, 0, 30, 8, 11, -1, 6, 7, 122, 72, 62, 6, 8, 0, 6, 6, 123, 8, 15, 6, 13, -1, 3, 5, 124, 0, 0, 3, 15, 1, 2, 7, 125, 15, 15, 6, 13, -1, 3, 5, 126, 202, 58, 8, 4, -1, 4, 7, 160, 253, 6, 2, 2, -1, 13, 3, 161, 232, 26, 3, 11, 0, 4, 5, 162, 149, 50, 6, 10, 0, 4, 7, 163, 17, 52, 7, 10, 0, 4, 7, 164, 183, 49, 8, 9, -1, 5, 7, 165, 47, 41, 10, 10, -2, 4, 7, 166, 4, 0, 3, 15, 2, 2, 7, 167, 122, 14, 7, 12, 0, 3, 7, 168, 0, 73, 6, 2, -1, 4, 6, 169, 162, 14, 12, 11, 0, 3, 13, 170, 65, 62, 6, 8, -1, 4, 6, 171, 112, 62, 8, 7, -1, 6, 8, 172, 211, 58, 7, 4, -1, 8, 7, 173, 19, 72, 5, 2, 0, 9, 5, 174, 103, 62, 8, 7, -1, 3, 8, 175, 7, 73, 5, 2, 0, 3, 6, 176, 171, 61, 7, 5, -1, 3, 5, 177, 58, 40, 9, 10, -1, 4, 7, 178, 154, 61, 5, 6, 0, 2, 5, 179, 160, 61, 5, 6, -1, 2, 5, 180, 224, 58, 5, 3, -1, 3, 4, 181, 241, 14, 8, 11, 0, 6, 8, 182, 97, 15, 8, 12, 0, 4, 9, 183, 236, 58, 4, 3, 0, 8, 4, 184, 219, 58, 4, 4, 0, 13, 5, 185, 166, 61, 4, 6, 0, 2, 4, 186, 25, 63, 7, 8, -1, 4, 6, 187, 129, 61, 7, 7, 0, 6, 8, 188, 221, 14, 9, 11, 0, 3, 10, 189, 231, 14, 9, 11, 0, 3, 10, 190, 210, 14, 10, 11, -1, 3, 10, 191, 41, 29, 7, 11, -1, 4, 7, 192, 141, 0, 10, 13, -1, 1, 9, 193, 130, 0, 10, 13, -1, 1, 9, 194, 119, 0, 10, 13, -1, 1, 9, 195, 20, 0, 10, 14, -1, 0, 9, 196, 67, 15, 10, 12, -1, 2, 9, 197, 108, 0, 10, 13, -1, 1, 9, 198, 0, 42, 12, 10, -1, 4, 11, 199, 228, 0, 8, 13, 0, 4, 8, 200, 245, 0, 7, 13, 0, 1, 7, 201, 237, 0, 7, 13, 0, 1, 7, 202, 0, 16, 7, 13, 0, 1, 7, 203, 130, 14, 7, 12, 0, 2, 7, 204, 36, 15, 6, 13, -2, 1, 4, 205, 22, 15, 6, 13, -1, 1, 4, 206, 29, 15, 6, 13, -1, 1, 4, 207, 154, 14, 7, 12, -2, 2, 4, 208, 78, 40, 9, 10, -1, 4, 9, 209, 59, 0, 8, 14, 0, 0, 10, 210, 172, 0, 9, 13, 0, 1, 10, 211, 152, 0, 9, 13, 0, 1, 10, 212, 182, 0, 9, 13, 0, 1, 10, 213, 31, 0, 9, 14, 0, 0, 10, 214, 78, 15, 9, 12, 0, 2, 10, 215, 121, 62, 7, 7, 0, 6, 7, 216, 8, 0, 11, 14, -1, 2, 10, 217, 192, 0, 8, 13, 0, 1, 9, 218, 210, 0, 8, 13, 0, 1, 9, 219, 201, 0, 8, 13, 0, 1, 9, 220, 88, 15, 8, 12, 0, 2, 9, 221, 162, 0, 9, 13, -1, 1, 7, 222, 81, 51, 7, 10, 0, 4, 8, 223, 49, 29, 7, 11, 0, 3, 8, 224, 57, 28, 7, 11, 0, 3, 7, 225, 65, 28, 7, 11, 0, 3, 7, 226, 73, 28, 7, 11, 0, 3, 7, 227, 138, 14, 7, 12, 0, 2, 7, 228, 25, 52, 7, 10, 0, 4, 7, 229, 146, 14, 7, 12, 0, 2, 7, 230, 192, 49, 11, 8, 0, 6, 12, 231, 207, 26, 6, 11, 0, 6, 6, 232, 97, 28, 7, 11, 0, 3, 8, 233, 105, 28, 7, 11, 0, 3, 8, 234, 113, 28, 7, 11, 0, 3, 8, 235, 73, 51, 7, 10, 0, 4, 8, 236, 214, 26, 5, 11, -2, 3, 3, 237, 250, 14, 5, 11, -1, 3, 3, 238, 193, 26, 6, 11, -2, 3, 3, 239, 142, 50, 6, 10, -2, 4, 3, 240, 185, 26, 7, 11, 0, 3, 8, 241, 106, 15, 7, 12, 0, 2, 8, 242, 129, 27, 7, 11, 0, 3, 8, 243, 137, 27, 7, 11, 0, 3, 8, 244, 145, 27, 7, 11, 0, 3, 8, 245, 114, 14, 7, 12, 0, 2, 8, 246, 49, 52, 7, 10, 0, 4, 8, 247, 237, 48, 8, 8, -1, 5, 7, 248, 201, 38, 8, 10, 0, 5, 8, 249, 161, 27, 7, 11, 0, 3, 8, 250, 169, 26, 7, 11, 0, 3, 8, 251, 177, 26, 7, 11, 0, 3, 8, 252, 89, 51, 7, 10, 0, 4, 8, 253, 41, 0, 8, 14, -1, 3, 7, 254, 68, 0, 7, 14, 0, 3, 8, 255, 219, 0, 8, 13, -1, 4, 7}; diff --git a/tools/editor/doc_title_font.h b/tools/editor/doc_title_font.h index 22bad995f8..fb6b4eaf5b 100644 --- a/tools/editor/doc_title_font.h +++ b/tools/editor/doc_title_font.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,7 +26,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -static const int _bi_font_doc_title_font_height=22;
-static const int _bi_font_doc_title_font_ascent=17;
-static const int _bi_font_doc_title_font_charcount=193;
-static const int _bi_font_doc_title_font_characters[]={0, 108, 97, 2, 2, -1, 16, 0, 13, 111, 97, 2, 2, -1, 16, 0, 32, 252, 55, 2, 2, -1, 16, 4, 33, 84, 48, 3, 13, 1, 4, 6, 34, 9, 99, 6, 6, 0, 4, 8, 35, 52, 76, 9, 12, -1, 5, 9, 36, 31, 18, 9, 16, 0, 3, 9, 37, 28, 35, 13, 13, -1, 4, 13, 38, 56, 35, 12, 13, 0, 4, 13, 39, 16, 99, 3, 6, 0, 4, 4, 40, 48, 0, 5, 17, 0, 3, 6, 41, 54, 0, 5, 17, 0, 3, 6, 42, 207, 84, 9, 8, 0, 3, 9, 43, 121, 86, 9, 9, 0, 7, 9, 44, 251, 71, 4, 6, -1, 13, 5, 45, 102, 97, 5, 3, 0, 10, 6, 46, 66, 99, 4, 4, 0, 13, 5, 47, 24, 0, 9, 17, -2, 3, 8, 48, 11, 76, 10, 12, -1, 5, 9, 49, 102, 74, 9, 12, 0, 5, 9, 50, 48, 63, 10, 12, -1, 5, 9, 51, 59, 63, 10, 12, -1, 5, 9, 52, 70, 63, 10, 12, -1, 5, 9, 53, 81, 62, 10, 12, -1, 5, 9, 54, 235, 59, 10, 12, -1, 5, 9, 55, 103, 61, 10, 12, -1, 5, 9, 56, 114, 60, 10, 12, -1, 5, 9, 57, 125, 60, 10, 12, -1, 5, 9, 58, 252, 45, 3, 9, 0, 8, 5, 59, 246, 71, 4, 11, -1, 8, 5, 60, 148, 73, 8, 12, 0, 5, 9, 61, 0, 100, 8, 7, 0, 8, 9, 62, 139, 73, 8, 12, 0, 5, 9, 63, 40, 49, 8, 13, 0, 4, 8, 64, 184, 17, 15, 14, 0, 5, 16, 65, 178, 47, 12, 12, -1, 5, 11, 66, 136, 60, 10, 12, 0, 5, 10, 67, 240, 45, 11, 12, -1, 5, 10, 68, 0, 63, 11, 12, 0, 5, 11, 69, 130, 73, 8, 12, 0, 5, 9, 70, 112, 74, 8, 12, 0, 5, 8, 71, 191, 46, 12, 12, -1, 5, 11, 72, 24, 63, 11, 12, 0, 5, 11, 73, 193, 72, 4, 12, 0, 5, 5, 74, 173, 73, 6, 12, -1, 5, 6, 75, 147, 60, 10, 12, 0, 5, 10, 76, 121, 73, 8, 12, 0, 5, 8, 77, 122, 47, 15, 12, 0, 5, 16, 78, 12, 63, 11, 12, 0, 5, 12, 79, 138, 47, 13, 12, -1, 5, 12, 80, 62, 76, 9, 12, 0, 5, 10, 81, 200, 17, 14, 14, -1, 5, 12, 82, 158, 60, 10, 12, 0, 5, 10, 83, 32, 76, 9, 12, -1, 5, 9, 84, 169, 60, 10, 12, -1, 5, 9, 85, 204, 46, 11, 12, 0, 5, 12, 86, 165, 47, 12, 12, -1, 5, 11, 87, 88, 48, 17, 12, -1, 5, 16, 88, 216, 46, 11, 12, -1, 5, 10, 89, 228, 46, 11, 12, -1, 5, 9, 90, 180, 60, 10, 12, -1, 5, 9, 91, 249, 0, 5, 16, 0, 4, 6, 92, 13, 0, 10, 17, -2, 3, 8, 93, 85, 17, 5, 16, 0, 4, 6, 94, 196, 85, 10, 8, -1, 5, 9, 95, 71, 99, 10, 3, -1, 17, 9, 96, 46, 99, 6, 4, -1, 4, 5, 97, 111, 87, 9, 9, -1, 8, 9, 98, 207, 32, 9, 13, 0, 4, 10, 99, 161, 86, 8, 9, -1, 8, 8, 100, 69, 34, 10, 13, -1, 4, 10, 101, 89, 88, 10, 9, -1, 8, 9, 102, 58, 49, 7, 13, -1, 4, 6, 103, 191, 59, 10, 12, -1, 8, 9, 104, 177, 33, 9, 13, 0, 4, 10, 105, 187, 73, 5, 12, -1, 5, 4, 106, 177, 17, 6, 15, -2, 5, 5, 107, 217, 32, 9, 13, 0, 4, 9, 108, 250, 17, 4, 13, 0, 4, 4, 109, 24, 89, 14, 9, 0, 8, 15, 110, 151, 86, 9, 9, 0, 8, 10, 111, 100, 87, 10, 9, -1, 8, 10, 112, 42, 76, 9, 12, 0, 8, 10, 113, 213, 59, 10, 12, -1, 8, 10, 114, 188, 86, 7, 9, 0, 8, 6, 115, 170, 86, 8, 9, -1, 8, 7, 116, 230, 72, 7, 11, -1, 6, 6, 117, 141, 86, 9, 9, 0, 8, 10, 118, 78, 89, 10, 9, -1, 8, 9, 119, 8, 89, 15, 9, -1, 8, 13, 120, 131, 86, 9, 9, -1, 8, 8, 121, 224, 59, 10, 12, -1, 8, 9, 122, 179, 86, 8, 9, -1, 8, 7, 123, 34, 0, 6, 17, -1, 3, 6, 124, 64, 0, 3, 17, 2, 3, 9, 125, 41, 0, 6, 17, 0, 3, 6, 126, 30, 99, 9, 5, 0, 5, 9, 160, 114, 97, 2, 2, -1, 16, 4, 161, 80, 48, 3, 13, 1, 6, 6, 162, 165, 73, 7, 12, 0, 5, 9, 163, 92, 74, 9, 12, 0, 5, 9, 164, 209, 72, 10, 11, -1, 6, 9, 165, 36, 63, 11, 12, -2, 5, 9, 166, 60, 0, 3, 17, 2, 3, 9, 167, 151, 17, 8, 15, 0, 4, 9, 168, 82, 99, 6, 3, 0, 5, 7, 169, 236, 17, 13, 13, 0, 4, 15, 170, 238, 72, 7, 11, 0, 5, 7, 171, 54, 89, 11, 9, -1, 7, 10, 172, 20, 99, 9, 5, -1, 10, 9, 173, 96, 98, 5, 3, 0, 10, 6, 174, 226, 84, 8, 8, 0, 4, 9, 175, 89, 98, 6, 3, 0, 4, 7, 176, 40, 99, 5, 5, 0, 4, 6, 177, 22, 76, 9, 12, 0, 5, 9, 178, 235, 84, 6, 8, 0, 3, 6, 179, 242, 84, 6, 8, 0, 3, 6, 180, 53, 99, 6, 4, -1, 4, 5, 181, 0, 76, 10, 12, 0, 8, 10, 182, 215, 17, 10, 14, 0, 5, 11, 183, 251, 78, 3, 4, 0, 9, 5, 184, 60, 99, 5, 4, 0, 16, 5, 185, 249, 83, 5, 8, -1, 3, 5, 186, 0, 89, 7, 10, 0, 5, 8, 187, 66, 89, 11, 9, -1, 7, 10, 188, 42, 35, 13, 13, -1, 4, 12, 189, 0, 35, 13, 13, -1, 4, 12, 190, 14, 35, 13, 13, -1, 4, 13, 191, 49, 49, 8, 13, -1, 6, 8, 192, 150, 0, 12, 16, -1, 1, 11, 193, 176, 0, 12, 16, -1, 1, 11, 194, 163, 0, 12, 16, -1, 1, 11, 195, 124, 0, 12, 16, -1, 1, 11, 196, 105, 17, 12, 15, -1, 2, 11, 197, 0, 0, 12, 17, -1, 0, 11, 198, 106, 47, 15, 12, -1, 5, 14, 199, 141, 17, 9, 15, 0, 5, 10, 200, 60, 18, 8, 16, 0, 1, 9, 201, 41, 18, 9, 16, 0, 1, 9, 202, 21, 18, 9, 16, 0, 1, 9, 203, 160, 17, 8, 15, 0, 2, 9, 204, 69, 17, 7, 16, -2, 1, 5, 205, 77, 17, 7, 16, -1, 1, 5, 206, 51, 18, 8, 16, -2, 1, 5, 207, 169, 17, 7, 15, -2, 2, 5, 208, 152, 47, 12, 12, -1, 5, 12, 209, 237, 0, 11, 16, 0, 1, 12, 210, 82, 0, 13, 16, -1, 1, 12, 211, 68, 0, 13, 16, -1, 1, 12, 212, 110, 0, 13, 16, -1, 1, 12, 213, 96, 0, 13, 16, -1, 1, 12, 214, 91, 17, 13, 15, -1, 2, 12, 215, 217, 84, 8, 8, 0, 8, 9, 216, 137, 0, 12, 16, 0, 3, 12, 217, 225, 0, 11, 16, 0, 1, 12, 218, 189, 0, 11, 16, 0, 1, 12, 219, 213, 0, 11, 16, 0, 1, 12, 220, 118, 17, 11, 15, 0, 2, 12, 221, 201, 0, 11, 16, -1, 1, 9, 222, 72, 76, 9, 12, 0, 5, 10, 223, 167, 33, 9, 13, 0, 4, 10, 224, 157, 33, 9, 13, -1, 4, 9, 225, 30, 49, 9, 13, -1, 4, 9, 226, 227, 32, 9, 13, -1, 4, 9, 227, 237, 31, 9, 13, -1, 4, 9, 228, 82, 75, 9, 12, -1, 5, 9, 229, 226, 17, 9, 14, -1, 3, 9, 230, 39, 89, 14, 9, 0, 8, 14, 231, 157, 73, 7, 12, 0, 8, 8, 232, 146, 33, 10, 13, -1, 4, 9, 233, 80, 34, 10, 13, -1, 4, 9, 234, 91, 33, 10, 13, -1, 4, 9, 235, 92, 61, 10, 12, -1, 5, 9, 236, 66, 49, 6, 13, -2, 4, 4, 237, 73, 48, 6, 13, -1, 4, 4, 238, 247, 31, 8, 13, -2, 4, 4, 239, 180, 73, 6, 12, -1, 5, 4, 240, 187, 32, 9, 13, 0, 4, 10, 241, 197, 32, 9, 13, 0, 4, 10, 242, 102, 33, 10, 13, -1, 4, 10, 243, 113, 33, 10, 13, -1, 4, 10, 244, 124, 33, 10, 13, -1, 4, 10, 245, 135, 33, 10, 13, -1, 4, 10, 246, 202, 59, 10, 12, -1, 5, 10, 247, 220, 72, 9, 11, -1, 6, 9, 248, 198, 72, 10, 11, 0, 7, 10, 249, 0, 49, 9, 13, 0, 4, 10, 250, 10, 49, 9, 13, 0, 4, 10, 251, 20, 49, 9, 13, 0, 4, 10, 252, 246, 58, 9, 12, 0, 5, 10, 253, 0, 18, 10, 16, -1, 4, 9, 254, 11, 18, 9, 16, 0, 4, 10, 255, 130, 17, 10, 15, -1, 5, 9};
+static const int _bi_font_doc_title_font_height=22; +static const int _bi_font_doc_title_font_ascent=17; +static const int _bi_font_doc_title_font_charcount=193; +static const int _bi_font_doc_title_font_characters[]={0, 108, 97, 2, 2, -1, 16, 0, 13, 111, 97, 2, 2, -1, 16, 0, 32, 252, 55, 2, 2, -1, 16, 4, 33, 84, 48, 3, 13, 1, 4, 6, 34, 9, 99, 6, 6, 0, 4, 8, 35, 52, 76, 9, 12, -1, 5, 9, 36, 31, 18, 9, 16, 0, 3, 9, 37, 28, 35, 13, 13, -1, 4, 13, 38, 56, 35, 12, 13, 0, 4, 13, 39, 16, 99, 3, 6, 0, 4, 4, 40, 48, 0, 5, 17, 0, 3, 6, 41, 54, 0, 5, 17, 0, 3, 6, 42, 207, 84, 9, 8, 0, 3, 9, 43, 121, 86, 9, 9, 0, 7, 9, 44, 251, 71, 4, 6, -1, 13, 5, 45, 102, 97, 5, 3, 0, 10, 6, 46, 66, 99, 4, 4, 0, 13, 5, 47, 24, 0, 9, 17, -2, 3, 8, 48, 11, 76, 10, 12, -1, 5, 9, 49, 102, 74, 9, 12, 0, 5, 9, 50, 48, 63, 10, 12, -1, 5, 9, 51, 59, 63, 10, 12, -1, 5, 9, 52, 70, 63, 10, 12, -1, 5, 9, 53, 81, 62, 10, 12, -1, 5, 9, 54, 235, 59, 10, 12, -1, 5, 9, 55, 103, 61, 10, 12, -1, 5, 9, 56, 114, 60, 10, 12, -1, 5, 9, 57, 125, 60, 10, 12, -1, 5, 9, 58, 252, 45, 3, 9, 0, 8, 5, 59, 246, 71, 4, 11, -1, 8, 5, 60, 148, 73, 8, 12, 0, 5, 9, 61, 0, 100, 8, 7, 0, 8, 9, 62, 139, 73, 8, 12, 0, 5, 9, 63, 40, 49, 8, 13, 0, 4, 8, 64, 184, 17, 15, 14, 0, 5, 16, 65, 178, 47, 12, 12, -1, 5, 11, 66, 136, 60, 10, 12, 0, 5, 10, 67, 240, 45, 11, 12, -1, 5, 10, 68, 0, 63, 11, 12, 0, 5, 11, 69, 130, 73, 8, 12, 0, 5, 9, 70, 112, 74, 8, 12, 0, 5, 8, 71, 191, 46, 12, 12, -1, 5, 11, 72, 24, 63, 11, 12, 0, 5, 11, 73, 193, 72, 4, 12, 0, 5, 5, 74, 173, 73, 6, 12, -1, 5, 6, 75, 147, 60, 10, 12, 0, 5, 10, 76, 121, 73, 8, 12, 0, 5, 8, 77, 122, 47, 15, 12, 0, 5, 16, 78, 12, 63, 11, 12, 0, 5, 12, 79, 138, 47, 13, 12, -1, 5, 12, 80, 62, 76, 9, 12, 0, 5, 10, 81, 200, 17, 14, 14, -1, 5, 12, 82, 158, 60, 10, 12, 0, 5, 10, 83, 32, 76, 9, 12, -1, 5, 9, 84, 169, 60, 10, 12, -1, 5, 9, 85, 204, 46, 11, 12, 0, 5, 12, 86, 165, 47, 12, 12, -1, 5, 11, 87, 88, 48, 17, 12, -1, 5, 16, 88, 216, 46, 11, 12, -1, 5, 10, 89, 228, 46, 11, 12, -1, 5, 9, 90, 180, 60, 10, 12, -1, 5, 9, 91, 249, 0, 5, 16, 0, 4, 6, 92, 13, 0, 10, 17, -2, 3, 8, 93, 85, 17, 5, 16, 0, 4, 6, 94, 196, 85, 10, 8, -1, 5, 9, 95, 71, 99, 10, 3, -1, 17, 9, 96, 46, 99, 6, 4, -1, 4, 5, 97, 111, 87, 9, 9, -1, 8, 9, 98, 207, 32, 9, 13, 0, 4, 10, 99, 161, 86, 8, 9, -1, 8, 8, 100, 69, 34, 10, 13, -1, 4, 10, 101, 89, 88, 10, 9, -1, 8, 9, 102, 58, 49, 7, 13, -1, 4, 6, 103, 191, 59, 10, 12, -1, 8, 9, 104, 177, 33, 9, 13, 0, 4, 10, 105, 187, 73, 5, 12, -1, 5, 4, 106, 177, 17, 6, 15, -2, 5, 5, 107, 217, 32, 9, 13, 0, 4, 9, 108, 250, 17, 4, 13, 0, 4, 4, 109, 24, 89, 14, 9, 0, 8, 15, 110, 151, 86, 9, 9, 0, 8, 10, 111, 100, 87, 10, 9, -1, 8, 10, 112, 42, 76, 9, 12, 0, 8, 10, 113, 213, 59, 10, 12, -1, 8, 10, 114, 188, 86, 7, 9, 0, 8, 6, 115, 170, 86, 8, 9, -1, 8, 7, 116, 230, 72, 7, 11, -1, 6, 6, 117, 141, 86, 9, 9, 0, 8, 10, 118, 78, 89, 10, 9, -1, 8, 9, 119, 8, 89, 15, 9, -1, 8, 13, 120, 131, 86, 9, 9, -1, 8, 8, 121, 224, 59, 10, 12, -1, 8, 9, 122, 179, 86, 8, 9, -1, 8, 7, 123, 34, 0, 6, 17, -1, 3, 6, 124, 64, 0, 3, 17, 2, 3, 9, 125, 41, 0, 6, 17, 0, 3, 6, 126, 30, 99, 9, 5, 0, 5, 9, 160, 114, 97, 2, 2, -1, 16, 4, 161, 80, 48, 3, 13, 1, 6, 6, 162, 165, 73, 7, 12, 0, 5, 9, 163, 92, 74, 9, 12, 0, 5, 9, 164, 209, 72, 10, 11, -1, 6, 9, 165, 36, 63, 11, 12, -2, 5, 9, 166, 60, 0, 3, 17, 2, 3, 9, 167, 151, 17, 8, 15, 0, 4, 9, 168, 82, 99, 6, 3, 0, 5, 7, 169, 236, 17, 13, 13, 0, 4, 15, 170, 238, 72, 7, 11, 0, 5, 7, 171, 54, 89, 11, 9, -1, 7, 10, 172, 20, 99, 9, 5, -1, 10, 9, 173, 96, 98, 5, 3, 0, 10, 6, 174, 226, 84, 8, 8, 0, 4, 9, 175, 89, 98, 6, 3, 0, 4, 7, 176, 40, 99, 5, 5, 0, 4, 6, 177, 22, 76, 9, 12, 0, 5, 9, 178, 235, 84, 6, 8, 0, 3, 6, 179, 242, 84, 6, 8, 0, 3, 6, 180, 53, 99, 6, 4, -1, 4, 5, 181, 0, 76, 10, 12, 0, 8, 10, 182, 215, 17, 10, 14, 0, 5, 11, 183, 251, 78, 3, 4, 0, 9, 5, 184, 60, 99, 5, 4, 0, 16, 5, 185, 249, 83, 5, 8, -1, 3, 5, 186, 0, 89, 7, 10, 0, 5, 8, 187, 66, 89, 11, 9, -1, 7, 10, 188, 42, 35, 13, 13, -1, 4, 12, 189, 0, 35, 13, 13, -1, 4, 12, 190, 14, 35, 13, 13, -1, 4, 13, 191, 49, 49, 8, 13, -1, 6, 8, 192, 150, 0, 12, 16, -1, 1, 11, 193, 176, 0, 12, 16, -1, 1, 11, 194, 163, 0, 12, 16, -1, 1, 11, 195, 124, 0, 12, 16, -1, 1, 11, 196, 105, 17, 12, 15, -1, 2, 11, 197, 0, 0, 12, 17, -1, 0, 11, 198, 106, 47, 15, 12, -1, 5, 14, 199, 141, 17, 9, 15, 0, 5, 10, 200, 60, 18, 8, 16, 0, 1, 9, 201, 41, 18, 9, 16, 0, 1, 9, 202, 21, 18, 9, 16, 0, 1, 9, 203, 160, 17, 8, 15, 0, 2, 9, 204, 69, 17, 7, 16, -2, 1, 5, 205, 77, 17, 7, 16, -1, 1, 5, 206, 51, 18, 8, 16, -2, 1, 5, 207, 169, 17, 7, 15, -2, 2, 5, 208, 152, 47, 12, 12, -1, 5, 12, 209, 237, 0, 11, 16, 0, 1, 12, 210, 82, 0, 13, 16, -1, 1, 12, 211, 68, 0, 13, 16, -1, 1, 12, 212, 110, 0, 13, 16, -1, 1, 12, 213, 96, 0, 13, 16, -1, 1, 12, 214, 91, 17, 13, 15, -1, 2, 12, 215, 217, 84, 8, 8, 0, 8, 9, 216, 137, 0, 12, 16, 0, 3, 12, 217, 225, 0, 11, 16, 0, 1, 12, 218, 189, 0, 11, 16, 0, 1, 12, 219, 213, 0, 11, 16, 0, 1, 12, 220, 118, 17, 11, 15, 0, 2, 12, 221, 201, 0, 11, 16, -1, 1, 9, 222, 72, 76, 9, 12, 0, 5, 10, 223, 167, 33, 9, 13, 0, 4, 10, 224, 157, 33, 9, 13, -1, 4, 9, 225, 30, 49, 9, 13, -1, 4, 9, 226, 227, 32, 9, 13, -1, 4, 9, 227, 237, 31, 9, 13, -1, 4, 9, 228, 82, 75, 9, 12, -1, 5, 9, 229, 226, 17, 9, 14, -1, 3, 9, 230, 39, 89, 14, 9, 0, 8, 14, 231, 157, 73, 7, 12, 0, 8, 8, 232, 146, 33, 10, 13, -1, 4, 9, 233, 80, 34, 10, 13, -1, 4, 9, 234, 91, 33, 10, 13, -1, 4, 9, 235, 92, 61, 10, 12, -1, 5, 9, 236, 66, 49, 6, 13, -2, 4, 4, 237, 73, 48, 6, 13, -1, 4, 4, 238, 247, 31, 8, 13, -2, 4, 4, 239, 180, 73, 6, 12, -1, 5, 4, 240, 187, 32, 9, 13, 0, 4, 10, 241, 197, 32, 9, 13, 0, 4, 10, 242, 102, 33, 10, 13, -1, 4, 10, 243, 113, 33, 10, 13, -1, 4, 10, 244, 124, 33, 10, 13, -1, 4, 10, 245, 135, 33, 10, 13, -1, 4, 10, 246, 202, 59, 10, 12, -1, 5, 10, 247, 220, 72, 9, 11, -1, 6, 9, 248, 198, 72, 10, 11, 0, 7, 10, 249, 0, 49, 9, 13, 0, 4, 10, 250, 10, 49, 9, 13, 0, 4, 10, 251, 20, 49, 9, 13, 0, 4, 10, 252, 246, 58, 9, 12, 0, 5, 10, 253, 0, 18, 10, 16, -1, 4, 9, 254, 11, 18, 9, 16, 0, 4, 10, 255, 130, 17, 10, 15, -1, 5, 9}; diff --git a/tools/editor/editor_data.cpp b/tools/editor/editor_data.cpp index aeca76bb29..673ee30adb 100644 --- a/tools/editor/editor_data.cpp +++ b/tools/editor/editor_data.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,7 +39,7 @@ void EditorHistory::_cleanup_history() { bool fail=false; for(int j=0;j<history[i].path.size();j++) { - if (!history[i].path[j].res.is_null()) + if (!history[i].path[j].ref.is_null()) continue; if (ObjectDB::get_instance(history[i].path[j].object)) @@ -70,10 +70,10 @@ void EditorHistory::_add_object(ObjectID p_object,const String& p_property,int p Object *obj = ObjectDB::get_instance(p_object); ERR_FAIL_COND(!obj); - Resource *r = obj->cast_to<Resource>(); + Reference *r = obj->cast_to<Reference>(); Obj o; if (r) - o.res=RES(r); + o.ref=REF(r); o.object=p_object; o.property=p_property; @@ -123,6 +123,27 @@ void EditorHistory::add_object(ObjectID p_object,int p_relevel){ _add_object(p_object,"",p_relevel); } +int EditorHistory::get_history_len() { + return history.size(); +} +int EditorHistory::get_history_pos() { + return current; +} + +ObjectID EditorHistory::get_history_obj(int p_obj) const { + ERR_FAIL_INDEX_V(p_obj,history.size(),0); + ERR_FAIL_INDEX_V(history[p_obj].level,history[p_obj].path.size(),0); + return history[p_obj].path[history[p_obj].level].object; +} + +bool EditorHistory::is_at_begining() const { + return current<=0; +} +bool EditorHistory::is_at_end() const { + + return ((current+1)>=history.size()); +} + bool EditorHistory::next() { @@ -424,9 +445,225 @@ void EditorData::remove_custom_type(const String& p_type){ } +int EditorData::add_edited_scene(int p_at_pos) { + + if (p_at_pos<0) + p_at_pos=edited_scene.size(); + EditedScene es; + es.root=NULL; + es.history_current=-1; + es.version=0; + es.live_edit_root=NodePath(String("/root")); + + if (p_at_pos==edited_scene.size()) + edited_scene.push_back(es); + else + edited_scene.insert(p_at_pos,es); + + if (current_edited_scene<0) + current_edited_scene=0; + return p_at_pos; +} + +void EditorData::move_edited_scene_index(int p_idx,int p_to_idx){ + + ERR_FAIL_INDEX(p_idx,edited_scene.size()); + ERR_FAIL_INDEX(p_to_idx,edited_scene.size()); + SWAP(edited_scene[p_idx],edited_scene[p_to_idx]); +} +void EditorData::remove_scene(int p_idx){ + ERR_FAIL_INDEX(p_idx,edited_scene.size()); + if (edited_scene[p_idx].root) + memdelete(edited_scene[p_idx].root); + + if (current_edited_scene>p_idx) + current_edited_scene--; + else if (current_edited_scene==p_idx && current_edited_scene>0) { + current_edited_scene--; + } + + edited_scene.remove(p_idx); + +} +int EditorData::get_edited_scene() const { + + return current_edited_scene; +} +void EditorData::set_edited_scene(int p_idx){ + + ERR_FAIL_INDEX(p_idx,edited_scene.size()); + current_edited_scene=p_idx; + //swap +} +Node* EditorData::get_edited_scene_root(){ + + ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),NULL); + + return edited_scene[current_edited_scene].root; +} +void EditorData::set_edited_scene_root(Node* p_root) { + + ERR_FAIL_INDEX(current_edited_scene,edited_scene.size()); + edited_scene[current_edited_scene].root=p_root; +} + +int EditorData::get_edited_scene_count() const { + + return edited_scene.size(); +} + +void EditorData::set_edited_scene_version(uint64_t version) { + ERR_FAIL_INDEX(current_edited_scene,edited_scene.size()); + edited_scene[current_edited_scene].version=version; + +} + +uint64_t EditorData::get_edited_scene_version() const{ + + ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),0); + return edited_scene[current_edited_scene].version; + +} +uint64_t EditorData::get_scene_version(int p_idx) const{ + ERR_FAIL_INDEX_V(p_idx,edited_scene.size(),false); + return edited_scene[p_idx].version; +} + +String EditorData::get_scene_type(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,edited_scene.size(),String()); + if (!edited_scene[p_idx].root) + return ""; + return edited_scene[p_idx].root->get_type(); + +} + +Ref<Script> EditorData::get_scene_root_script(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,edited_scene.size(),Ref<Script>()); + if (!edited_scene[p_idx].root) + return Ref<Script>(); + Ref<Script> s=edited_scene[p_idx].root->get_script(); + if (!s.is_valid() && edited_scene[p_idx].root->get_child_count()) { + Node *n = edited_scene[p_idx].root->get_child(0); + while(!s.is_valid() && n && n->get_filename()==String()) { + s=n->get_script(); + n=n->get_parent(); + } + } + return s; +} + +String EditorData::get_scene_title(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx,edited_scene.size(),String()); + if (!edited_scene[p_idx].root) + return "[empty]"; + if (edited_scene[p_idx].root->get_filename()=="") + return "[unsaved]"; + return edited_scene[p_idx].root->get_filename().get_file(); +} + + +String EditorData::get_scene_path(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,edited_scene.size(),String()); + + if (!edited_scene[p_idx].root) + return ""; + return edited_scene[p_idx].root->get_filename(); + +} + +void EditorData::set_edited_scene_live_edit_root(const NodePath& p_root) { + ERR_FAIL_INDEX(current_edited_scene,edited_scene.size()); + + edited_scene[current_edited_scene].live_edit_root=p_root; + +} +NodePath EditorData::get_edited_scene_live_edit_root() { + + ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),String()); + + return edited_scene[current_edited_scene].live_edit_root; + + + +} + + +void EditorData::save_edited_scene_state(EditorSelection *p_selection, EditorHistory *p_history, const Dictionary& p_custom) { + + ERR_FAIL_INDEX(current_edited_scene,edited_scene.size()); + + EditedScene &es=edited_scene[current_edited_scene]; + es.selection = p_selection->get_selected_node_list(); + es.history_current=p_history->current; + es.history_stored=p_history->history; + es.editor_states=get_editor_states(); + es.custom_state=p_custom; + +} + +Dictionary EditorData::restore_edited_scene_state(EditorSelection *p_selection, EditorHistory *p_history) { + ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),Dictionary()); + + EditedScene &es=edited_scene[current_edited_scene]; + + p_history->current=es.history_current; + p_history->history=es.history_stored; + + + p_selection->clear(); + for(List<Node*>::Element *E=es.selection.front();E;E=E->next()) { + p_selection->add_node(E->get()); + } + set_editor_states(es.editor_states); + + return es.custom_state; +} + + +void EditorData::set_edited_scene_import_metadata(Ref<ResourceImportMetadata> p_mdata) { + + ERR_FAIL_INDEX(current_edited_scene,edited_scene.size()); + edited_scene[current_edited_scene].medatata=p_mdata; + +} + +Ref<ResourceImportMetadata> EditorData::get_edited_scene_import_metadata() const{ + + ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),Ref<ResourceImportMetadata>()); + return edited_scene[current_edited_scene].medatata; +} + +void EditorData::clear_edited_scenes() { + + for(int i=0;i<edited_scene.size();i++) { + if (edited_scene[i].root) { + memdelete( edited_scene[i].root ); + } + } + edited_scene.clear(); +} + + + +void EditorData::set_plugin_window_layout(Ref<ConfigFile> p_layout) { + for(int i=0;i<editor_plugins.size();i++) { + editor_plugins[i]->set_window_layout(p_layout); + } +} + +void EditorData::get_plugin_window_layout(Ref<ConfigFile> p_layout) { + for(int i=0;i<editor_plugins.size();i++) { + editor_plugins[i]->get_window_layout(p_layout); + } +} EditorData::EditorData() { + current_edited_scene=-1; + // load_imported_scenes_from_globals(); } diff --git a/tools/editor/editor_data.h b/tools/editor/editor_data.h index 50faba88ee..c5ee83ae63 100644 --- a/tools/editor/editor_data.h +++ b/tools/editor/editor_data.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -45,7 +45,7 @@ class EditorHistory { struct Obj { - RES res; + REF ref; ObjectID object; String property; }; @@ -55,6 +55,7 @@ class EditorHistory { Vector<Obj> path; int level; }; +friend class EditorData; Vector<History> history; int current; @@ -74,10 +75,17 @@ class EditorHistory { public: + bool is_at_begining() const; + bool is_at_end() const; + void add_object(ObjectID p_object); void add_object(ObjectID p_object,const String& p_subprop); void add_object(ObjectID p_object,int p_relevel); + int get_history_len(); + int get_history_pos(); + ObjectID get_history_obj(int p_obj) const; + bool next(); bool previous(); ObjectID get_current(); @@ -91,6 +99,8 @@ public: EditorHistory(); }; +class EditorSelection; + class EditorData { public: @@ -117,6 +127,22 @@ private: void _cleanup_history(); + struct EditedScene { + Node* root; + Dictionary editor_states; + Ref<ResourceImportMetadata> medatata; + List<Node*> selection; + Vector<EditorHistory::History> history_stored; + int history_current; + Dictionary custom_state; + uint64_t version; + NodePath live_edit_root; + + + }; + + Vector<EditedScene> edited_scene; + int current_edited_scene; public: @@ -146,6 +172,36 @@ public: void remove_custom_type(const String& p_type); const Map<String,Vector<CustomType> >& get_custom_types() const { return custom_types; } + + int add_edited_scene(int p_at_pos); + void move_edited_scene_index(int p_idx,int p_to_idx); + void remove_scene(int p_idx); + void set_edited_scene(int p_idx); + void set_edited_scene_root(Node* p_root); + void set_edited_scene_import_metadata(Ref<ResourceImportMetadata> p_mdata); + Ref<ResourceImportMetadata> get_edited_scene_import_metadata() const; + int get_edited_scene() const; + Node* get_edited_scene_root(); + int get_edited_scene_count() const; + String get_scene_title(int p_idx) const; + String get_scene_path(int p_idx) const; + String get_scene_type(int p_idx) const; + Ref<Script> get_scene_root_script(int p_idx) const; + void set_edited_scene_version(uint64_t version); + uint64_t get_edited_scene_version() const; + uint64_t get_scene_version(int p_idx) const; + void clear_edited_scenes(); + void set_edited_scene_live_edit_root(const NodePath& p_root); + NodePath get_edited_scene_live_edit_root(); + + + void set_plugin_window_layout(Ref<ConfigFile> p_layout); + void get_plugin_window_layout(Ref<ConfigFile> p_layout); + + void save_edited_scene_state(EditorSelection *p_selection,EditorHistory *p_history,const Dictionary& p_custom); + Dictionary restore_edited_scene_state(EditorSelection *p_selection, EditorHistory *p_history); + + EditorData(); }; diff --git a/tools/editor/editor_dir_dialog.cpp b/tools/editor/editor_dir_dialog.cpp index eee4125e93..a8421acff8 100644 --- a/tools/editor/editor_dir_dialog.cpp +++ b/tools/editor/editor_dir_dialog.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,6 +28,9 @@ /*************************************************************************/ #include "editor_dir_dialog.h" #include "os/os.h" +#include "os/keyboard.h" +#include "tools/editor/editor_settings.h" + void EditorDirDialog::_update_dir(TreeItem* p_item) { @@ -39,17 +42,33 @@ void EditorDirDialog::_update_dir(TreeItem* p_item) { da->change_dir(cdir); da->list_dir_begin(); String p=da->get_next(); + + List<String> dirs; + bool ishidden; + bool show_hidden = EditorSettings::get_singleton()->get("file_dialog/show_hidden_files"); + while(p!="") { - if (da->current_is_dir() && !p.begins_with(".")) { - TreeItem *ti = tree->create_item(p_item); - ti->set_text(0,p); - ti->set_icon(0,get_icon("Folder","EditorIcons")); - ti->set_collapsed(true); - } + ishidden = da->current_is_hidden(); + + if (show_hidden || !ishidden) { + if (da->current_is_dir() && !p.begins_with(".")) { + dirs.push_back(p); + } + } p=da->get_next(); } + dirs.sort(); + + for(List<String>::Element *E=dirs.front();E;E=E->next()) { + TreeItem *ti = tree->create_item(p_item); + ti->set_text(0,E->get()); + ti->set_icon(0,get_icon("Folder","EditorIcons")); + ti->set_collapsed(true); + + } + memdelete(da); updating=false; diff --git a/tools/editor/editor_dir_dialog.h b/tools/editor/editor_dir_dialog.h index 060fb724ce..8ac83b86e8 100644 --- a/tools/editor/editor_dir_dialog.h +++ b/tools/editor/editor_dir_dialog.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/editor_file_dialog.cpp b/tools/editor/editor_file_dialog.cpp new file mode 100644 index 0000000000..c62347d129 --- /dev/null +++ b/tools/editor/editor_file_dialog.cpp @@ -0,0 +1,1367 @@ +#include "editor_file_dialog.h" +#include "scene/gui/label.h" +#include "scene/gui/center_container.h" +#include "print_string.h" +#include "os/keyboard.h" +#include "editor_resource_preview.h" +#include "editor_settings.h" +#include "scene/gui/margin_container.h" +EditorFileDialog::GetIconFunc EditorFileDialog::get_icon_func=NULL; +EditorFileDialog::GetIconFunc EditorFileDialog::get_large_icon_func=NULL; + +EditorFileDialog::RegisterFunc EditorFileDialog::register_func=NULL; +EditorFileDialog::RegisterFunc EditorFileDialog::unregister_func=NULL; + + +VBoxContainer *EditorFileDialog::get_vbox() { + return vbox; + +} + +void EditorFileDialog::_notification(int p_what) { + + if (p_what==NOTIFICATION_ENTER_TREE) { + + mode_thumbnails->set_icon(get_icon("FileThumbnail","EditorIcons")); + mode_list->set_icon(get_icon("FileList","EditorIcons")); + dir_prev->set_icon(get_icon("ArrowLeft","EditorIcons")); + dir_next->set_icon(get_icon("ArrowRight","EditorIcons")); + dir_up->set_icon(get_icon("ArrowUp","EditorIcons")); + favorite->set_icon(get_icon("Favorites","EditorIcons")); + + fav_up->set_icon(get_icon("MoveUp","EditorIcons")); + fav_down->set_icon(get_icon("MoveDown","EditorIcons")); + fav_rm->set_icon(get_icon("RemoveSmall","EditorIcons")); + + } + if (p_what==NOTIFICATION_PROCESS) { + + if (preview_waiting) { + preview_wheel_timeout-=get_process_delta_time(); + if (preview_wheel_timeout<=0) { + preview_wheel_index++; + if (preview_wheel_index>=8) + preview_wheel_index=0; + Ref<Texture> frame = get_icon("WaitPreview"+itos(preview_wheel_index+1),"EditorIcons"); + preview->set_texture(frame); + preview_wheel_timeout=0.1; + } + } + } + + if (p_what==NOTIFICATION_DRAW) { + + //RID ci = get_canvas_item(); + //get_stylebox("panel","PopupMenu")->draw(ci,Rect2(Point2(),get_size())); + } +} + +void EditorFileDialog::set_enable_multiple_selection(bool p_enable) { + + item_list->set_select_mode(p_enable?ItemList::SELECT_MULTI:ItemList::SELECT_SINGLE); + +}; + +Vector<String> EditorFileDialog::get_selected_files() const { + + Vector<String> list; + for(int i=0;i<item_list->get_item_count();i++) { + if (item_list->is_selected(i)) + list.push_back(item_list->get_item_text(i)); + } + return list; + +}; + +void EditorFileDialog::update_dir() { + + dir->set_text(dir_access->get_current_dir()); +} + +void EditorFileDialog::_dir_entered(String p_dir) { + + + dir_access->change_dir(p_dir); + file->set_text(""); + invalidate(); + update_dir(); + _push_history(); + + +} + +void EditorFileDialog::_file_entered(const String& p_file) { + + _action_pressed(); +} + +void EditorFileDialog::_save_confirm_pressed() { + String f=dir_access->get_current_dir().plus_file(file->get_text()); + _save_to_recent(); + emit_signal("file_selected",f); + hide(); +} + +void EditorFileDialog::_post_popup() { + + ConfirmationDialog::_post_popup(); + if (invalidated) { + update_file_list(); + invalidated=false; + } + if (mode==MODE_SAVE_FILE) + file->grab_focus(); + else + item_list->grab_focus(); + + if (is_visible() && get_current_file()!="") + _request_single_thumbnail(get_current_dir().plus_file(get_current_file())); + + if (is_visible()) { + Ref<Texture> folder = get_icon("folder","FileDialog"); + recent->clear(); + + + bool res = access==ACCESS_RESOURCES; + Vector<String> recentd = EditorSettings::get_singleton()->get_recent_dirs(); + for(int i=0;i<recentd.size();i++) { + bool cres = recentd[i].begins_with("res://"); + if (cres!=res) + continue; + String name = recentd[i]; + if (res && name=="res://") { + name="/"; + } else { + name=name.get_file()+"/"; + } + + //print_line("file: "+name); + recent->add_item(name,folder); + recent->set_item_metadata( recent->get_item_count()-1,recentd[i]); + } + + local_history.clear(); + local_history_pos=-1; + _push_history(); + + _update_favorites(); + } + +} + +void EditorFileDialog::_thumbnail_result(const String& p_path,const Ref<Texture>& p_preview, const Variant& p_udata) { + + if (display_mode==DISPLAY_LIST || p_preview.is_null()) + return; + + for(int i=0;i<item_list->get_item_count();i++) { + Dictionary d = item_list->get_item_metadata(i); + String pname = d["path"]; + if (pname==p_path) { + item_list->set_item_icon(i,p_preview); + item_list->set_item_tag_icon(i,Ref<Texture>()); + } + } +} + +void EditorFileDialog::_thumbnail_done(const String& p_path,const Ref<Texture>& p_preview, const Variant& p_udata) { + + set_process(false); + preview_waiting=false; + + if (p_preview.is_valid() && get_current_path()==p_path) { + + + preview->set_texture(p_preview); + if (display_mode==DISPLAY_THUMBNAILS) { + preview_vb->hide(); + } else { + preview_vb->show(); + } + + } else { + preview_vb->hide(); + preview->set_texture(Ref<Texture>()); + + } + +} + +void EditorFileDialog::_request_single_thumbnail(const String& p_path) { + + EditorResourcePreview::get_singleton()->queue_resource_preview(p_path,this,"_thumbnail_done",p_path); + //print_line("want file "+p_path); + set_process(true); + preview_waiting=true; + preview_wheel_timeout=0; + +} + +void EditorFileDialog::_action_pressed() { + + if (mode==MODE_OPEN_FILES) { + + + String fbase=dir_access->get_current_dir(); + + DVector<String> files; + for(int i=0;i<item_list->get_item_count();i++) { + if (item_list->is_selected(i)) + files.push_back( fbase.plus_file(item_list->get_item_text(i) )); + } + + if (files.size()) { + _save_to_recent(); + emit_signal("files_selected",files); + hide(); + } + + return; + } + + String f=dir_access->get_current_dir().plus_file(file->get_text()); + + if (mode==MODE_OPEN_FILE && dir_access->file_exists(f)) { + _save_to_recent(); + emit_signal("file_selected",f); + hide(); + } + + if (mode==MODE_OPEN_DIR) { + + + String path=dir_access->get_current_dir(); + /*if (tree->get_selected()) { + Dictionary d = tree->get_selected()->get_metadata(0); + if (d["dir"]) { + path=path+"/"+String(d["name"]); + } + }*/ + path=path.replace("\\","/"); + _save_to_recent(); + emit_signal("dir_selected",path); + hide(); + } + + if (mode==MODE_SAVE_FILE) { + + bool valid=false; + + if (filter->get_selected()==filter->get_item_count()-1) { + valid=true; //match none + } else if (filters.size()>1 && filter->get_selected()==0) { + // match all filters + for (int i=0;i<filters.size();i++) { + + String flt=filters[i].get_slice(";",0); + for (int j=0;j<flt.get_slice_count(",");j++) { + + String str = flt.get_slice(",",j).strip_edges(); + if (f.match(str)) { + valid=true; + break; + } + } + if (valid) + break; + } + } else { + int idx=filter->get_selected(); + if (filters.size()>1) + idx--; + if (idx>=0 && idx<filters.size()) { + + String flt=filters[idx].get_slice(";",0); + int filterSliceCount=flt.get_slice_count(","); + for (int j=0;j<filterSliceCount;j++) { + + String str = (flt.get_slice(",",j).strip_edges()); + if (f.match(str)) { + valid=true; + break; + } + } + + if (!valid && filterSliceCount>0) { + String str = (flt.get_slice(",",0).strip_edges()); + f+=str.substr(1, str.length()-1); + _request_single_thumbnail(get_current_dir().plus_file(f.get_file())); + file->set_text(f.get_file()); + valid=true; + } + } else { + valid=true; + } + } + + + if (!valid) { + + exterr->popup_centered_minsize(Size2(250,80)); + return; + + } + + if (dir_access->file_exists(f)) { + confirm_save->set_text("File Exists, Overwrite?"); + confirm_save->popup_centered(Size2(200,80)); + } else { + + _save_to_recent(); + emit_signal("file_selected",f); + hide(); + } + } +} + +void EditorFileDialog::_cancel_pressed() { + + file->set_text(""); + invalidate(); + hide(); +} + +void EditorFileDialog::_item_selected(int p_item) { + + int current = p_item; + if (current<0 || current>=item_list->get_item_count()) + return; + + Dictionary d=item_list->get_item_metadata(current); + + if (!d["dir"]) { + + file->set_text(d["name"]); + _request_single_thumbnail(get_current_dir().plus_file(get_current_file())); + } +} + +void EditorFileDialog::_push_history() { + + local_history.resize(local_history_pos+1); + String new_path = dir_access->get_current_dir(); + if (local_history.size()==0 || new_path!=local_history[local_history_pos]) { + local_history.push_back(new_path); + local_history_pos++; + dir_prev->set_disabled(local_history_pos==0); + dir_next->set_disabled(true); + } + +} +void EditorFileDialog::_item_dc_selected(int p_item) { + + + int current = p_item; + if (current<0 || current>=item_list->get_item_count()) + return; + + Dictionary d=item_list->get_item_metadata(current); + + if (d["dir"]) { + + //print_line("change dir: "+String(d["name"])); + dir_access->change_dir(d["name"]); + if (mode==MODE_OPEN_FILE || mode==MODE_OPEN_FILES || mode==MODE_OPEN_DIR) + file->set_text(""); + call_deferred("_update_file_list"); + call_deferred("_update_dir"); + + _push_history(); + + + } else { + + _action_pressed(); + } +} + + + +void EditorFileDialog::update_file_list() { + + + int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size"); + Ref<Texture> folder_thumbnail; + Ref<Texture> file_thumbnail; + + item_list->clear(); + + if (display_mode==DISPLAY_THUMBNAILS) { + + item_list->set_max_columns(0); + item_list->set_icon_mode(ItemList::ICON_MODE_TOP); + item_list->set_fixed_column_width(thumbnail_size*3/2); + item_list->set_max_text_lines(2); + item_list->set_min_icon_size(Size2(thumbnail_size,thumbnail_size)); + + if (!has_icon("ResizedFolder","EditorIcons")) { + Ref<ImageTexture> folder = get_icon("FolderBig","EditorIcons"); + Image img = folder->get_data(); + img.resize(thumbnail_size,thumbnail_size); + Ref<ImageTexture> resized_folder = Ref<ImageTexture>( memnew( ImageTexture)); + resized_folder->create_from_image(img,0); + Theme::get_default()->set_icon("ResizedFolder","EditorIcons",resized_folder); + } + + folder_thumbnail = get_icon("ResizedFolder","EditorIcons"); + + if (!has_icon("ResizedFile","EditorIcons")) { + Ref<ImageTexture> file = get_icon("FileBig","EditorIcons"); + Image img = file->get_data(); + img.resize(thumbnail_size,thumbnail_size); + Ref<ImageTexture> resized_file = Ref<ImageTexture>( memnew( ImageTexture)); + resized_file->create_from_image(img,0); + Theme::get_default()->set_icon("ResizedFile","EditorIcons",resized_file); + } + + file_thumbnail = get_icon("ResizedFile","EditorIcons"); + + preview_vb->hide(); + + } else { + + item_list->set_icon_mode(ItemList::ICON_MODE_LEFT); + item_list->set_max_columns(1); + item_list->set_max_text_lines(1); + item_list->set_fixed_column_width(0); + item_list->set_min_icon_size(Size2()); + if (preview->get_texture().is_valid()) + preview_vb->show(); + + } + + + dir_access->list_dir_begin(); + + + Ref<Texture> folder = get_icon("folder","FileDialog"); + List<String> files; + List<String> dirs; + + bool isdir; + bool ishidden; + bool show_hidden = show_hidden_files; + String item; + + while ((item=dir_access->get_next(&isdir))!="") { + + ishidden = dir_access->current_is_hidden(); + + if (show_hidden || !ishidden) { + if (!isdir) + files.push_back(item); + else + dirs.push_back(item); + } + } + + dirs.sort_custom<NoCaseComparator>(); + files.sort_custom<NoCaseComparator>(); + + while(!dirs.empty()) { + + if (dirs.front()->get()!=".") { + item_list->add_item(dirs.front()->get()+"/"); + if (display_mode==DISPLAY_THUMBNAILS) { + + item_list->set_item_icon(item_list->get_item_count()-1,folder_thumbnail); + } else { + + item_list->set_item_icon(item_list->get_item_count()-1,folder); + } + + Dictionary d; + d["name"]=dirs.front()->get(); + d["path"]=String(); + d["dir"]=true; + item_list->set_item_metadata( item_list->get_item_count() -1, d); + } + dirs.pop_front(); + + } + + dirs.clear(); + + List<String> patterns; + // build filter + if (filter->get_selected()==filter->get_item_count()-1) { + + // match all + } else if (filters.size()>1 && filter->get_selected()==0) { + // match all filters + for (int i=0;i<filters.size();i++) { + + String f=filters[i].get_slice(";",0); + for (int j=0;j<f.get_slice_count(",");j++) { + + patterns.push_back(f.get_slice(",",j).strip_edges()); + } + } + } else { + int idx=filter->get_selected(); + if (filters.size()>1) + idx--; + + if (idx>=0 && idx<filters.size()) { + + String f=filters[idx].get_slice(";",0); + for (int j=0;j<f.get_slice_count(",");j++) { + + patterns.push_back(f.get_slice(",",j).strip_edges()); + } + } + } + + + String base_dir = dir_access->get_current_dir(); + + + while(!files.empty()) { + + bool match=patterns.empty(); + + for(List<String>::Element *E=patterns.front();E;E=E->next()) { + + if (files.front()->get().matchn(E->get())) { + + match=true; + break; + } + } + + if (match) { + //TreeItem *ti=tree->create_item(root); + //ti->set_text(0,files.front()->get()); + item_list->add_item(files.front()->get()); + + if (get_icon_func) { + + + Ref<Texture> icon = get_icon_func(base_dir.plus_file(files.front()->get())); + //ti->set_icon(0,icon); + if (display_mode==DISPLAY_THUMBNAILS) { + + item_list->set_item_icon(item_list->get_item_count()-1,file_thumbnail); + item_list->set_item_tag_icon(item_list->get_item_count()-1,icon); + } else { + item_list->set_item_icon(item_list->get_item_count()-1,icon); + } + } + + if (mode==MODE_OPEN_DIR) { + //disabled mode? + //ti->set_custom_color(0,get_color("files_disabled")); + //ti->set_selectable(0,false); + } + Dictionary d; + d["name"]=files.front()->get(); + d["dir"]=false; + String fullpath = base_dir.plus_file(files.front()->get()); + + if (display_mode==DISPLAY_THUMBNAILS) { + EditorResourcePreview::get_singleton()->queue_resource_preview(fullpath,this,"_thumbnail_result",fullpath); + } + d["path"]=base_dir.plus_file(files.front()->get()); + //ti->set_metadata(0,d); + item_list->set_item_metadata(item_list->get_item_count()-1,d); + + if (file->get_text()==files.front()->get()) + item_list->set_current(item_list->get_item_count()-1); + } + + files.pop_front(); + } + + if (favorites->get_current()>=0) { + favorites->unselect(favorites->get_current()); + } + + favorite->set_pressed(false); + fav_up->set_disabled(true); + fav_down->set_disabled(true); + for(int i=0;i<favorites->get_item_count();i++) { + if (favorites->get_item_metadata(i)==base_dir) { + favorites->select(i); + favorite->set_pressed(true); + if (i>0) { + fav_up->set_disabled(false); + } + if (i<favorites->get_item_count()-1) { + fav_down->set_disabled(false); + } + break; + } + + } + // ?? + //if (tree->get_root() && tree->get_root()->get_children()) + // tree->get_root()->get_children()->select(0); + + files.clear(); + +} + +void EditorFileDialog::_filter_selected(int) { + + update_file_list(); +} + +void EditorFileDialog::update_filters() { + + filter->clear(); + + if (filters.size()>1) { + String all_filters; + + const int max_filters=5; + + for(int i=0;i<MIN( max_filters, filters.size()) ;i++) { + String flt=filters[i].get_slice(";",0); + if (i>0) + all_filters+=","; + all_filters+=flt; + } + + if (max_filters<filters.size()) + all_filters+=", ..."; + + filter->add_item("All Recognized ( "+all_filters+" )"); + } + for(int i=0;i<filters.size();i++) { + + String flt=filters[i].get_slice(";",0).strip_edges(); + String desc=filters[i].get_slice(";",1).strip_edges(); + if (desc.length()) + filter->add_item(desc+" ( "+flt+" )"); + else + filter->add_item("( "+flt+" )"); + } + + filter->add_item("All Files (*)"); + +} + +void EditorFileDialog::clear_filters() { + + filters.clear(); + update_filters(); + invalidate(); +} +void EditorFileDialog::add_filter(const String& p_filter) { + + filters.push_back(p_filter); + update_filters(); + invalidate(); + +} + +String EditorFileDialog::get_current_dir() const { + + return dir->get_text(); +} +String EditorFileDialog::get_current_file() const { + + return file->get_text(); +} +String EditorFileDialog::get_current_path() const { + + return dir->get_text().plus_file(file->get_text()); +} +void EditorFileDialog::set_current_dir(const String& p_dir) { + + dir_access->change_dir(p_dir); + update_dir(); + invalidate(); + //_push_history(); + + +} +void EditorFileDialog::set_current_file(const String& p_file) { + + file->set_text(p_file); + update_dir(); + invalidate(); + int lp = p_file.find_last("."); + if (lp!=-1) { + file->select(0,lp); + file->grab_focus(); + } + + if (is_visible()) + _request_single_thumbnail(get_current_dir().plus_file(get_current_file())); + + +} +void EditorFileDialog::set_current_path(const String& p_path) { + + if (!p_path.size()) + return; + int pos=MAX( p_path.find_last("/"), p_path.find_last("\\") ); + if (pos==-1) { + + set_current_file(p_path); + } else { + + String dir=p_path.substr(0,pos); + String file=p_path.substr(pos+1,p_path.length()); + set_current_dir(dir); + set_current_file(file); + } +} + + +void EditorFileDialog::set_mode(Mode p_mode) { + + mode=p_mode; + switch(mode) { + + case MODE_OPEN_FILE: get_ok()->set_text("Open"); set_title("Open a File"); makedir->hide(); break; + case MODE_OPEN_FILES: get_ok()->set_text("Open"); set_title("Open File(s)"); makedir->hide(); break; + case MODE_SAVE_FILE: get_ok()->set_text("Save"); set_title("Save a File"); makedir->show(); break; + case MODE_OPEN_DIR: get_ok()->set_text("Open"); set_title("Open a Directory"); makedir->show(); break; + } + + if (mode==MODE_OPEN_FILES) { + item_list->set_select_mode(ItemList::SELECT_MULTI); + } else { + item_list->set_select_mode(ItemList::SELECT_SINGLE); + } +} + +EditorFileDialog::Mode EditorFileDialog::get_mode() const { + + return mode; +} + +void EditorFileDialog::set_access(Access p_access) { + + ERR_FAIL_INDEX(p_access,3); + if (access==p_access) + return; + memdelete( dir_access ); + switch(p_access) { + case ACCESS_FILESYSTEM: { + + dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + } break; + case ACCESS_RESOURCES: { + + dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES); + } break; + case ACCESS_USERDATA: { + + dir_access = DirAccess::create(DirAccess::ACCESS_USERDATA); + } break; + } + access=p_access; + _update_drives(); + invalidate(); + update_filters(); + update_dir(); +} + +void EditorFileDialog::invalidate() { + + if (is_visible()) { + update_file_list(); + invalidated=false; + } else { + invalidated=true; + } + +} + +EditorFileDialog::Access EditorFileDialog::get_access() const{ + + return access; +} + +void EditorFileDialog::_make_dir_confirm() { + + + Error err = dir_access->make_dir( makedirname->get_text() ); + if (err==OK) { + dir_access->change_dir(makedirname->get_text()); + invalidate(); + update_filters(); + update_dir(); + _push_history(); + + } else { + mkdirerr->popup_centered_minsize(Size2(250,50)); + } +} + + +void EditorFileDialog::_make_dir() { + + makedialog->popup_centered_minsize(Size2(250,80)); + makedirname->grab_focus(); + +} + +void EditorFileDialog::_select_drive(int p_idx) { + + String d = drives->get_item_text(p_idx); + dir_access->change_dir(d); + file->set_text(""); + invalidate(); + update_dir(); + _push_history(); + + +} + +void EditorFileDialog::_update_drives() { + + + int dc = dir_access->get_drive_count(); + if (dc==0 || access!=ACCESS_FILESYSTEM) { + drives->hide(); + } else { + drives->clear(); + drives->show(); + + for(int i=0;i<dir_access->get_drive_count();i++) { + String d = dir_access->get_drive(i); + drives->add_item(dir_access->get_drive(i)); + } + + drives->select(dir_access->get_current_drive()); + + } +} + + +void EditorFileDialog::_favorite_selected(int p_idx) { + + Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + ERR_FAIL_INDEX(p_idx,favorited.size()); + + dir_access->change_dir(favorited[p_idx]); + file->set_text(""); + invalidate(); + update_dir(); + _push_history(); +} + +void EditorFileDialog::_favorite_move_up(){ + + int current = favorites->get_current(); + + if (current>0 && current<favorites->get_item_count()) { + Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + + int a_idx=favorited.find(String(favorites->get_item_metadata(current-1))); + int b_idx=favorited.find(String(favorites->get_item_metadata(current))); + + if (a_idx==-1 || b_idx==-1) + return; + SWAP(favorited[a_idx],favorited[b_idx]); + + EditorSettings::get_singleton()->set_favorite_dirs(favorited); + + _update_favorites(); + update_file_list(); + + } +} +void EditorFileDialog::_favorite_move_down(){ + + int current = favorites->get_current(); + + if (current>=0 && current<favorites->get_item_count()-1) { + Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + + int a_idx=favorited.find(String(favorites->get_item_metadata(current+1))); + int b_idx=favorited.find(String(favorites->get_item_metadata(current))); + + if (a_idx==-1 || b_idx==-1) + return; + SWAP(favorited[a_idx],favorited[b_idx]); + + EditorSettings::get_singleton()->set_favorite_dirs(favorited); + + _update_favorites(); + update_file_list(); + + } +} + + +void EditorFileDialog::_update_favorites() { + + bool res = access==ACCESS_RESOURCES; + + String current = get_current_dir(); + Ref<Texture> star = get_icon("Favorites","EditorIcons"); + favorites->clear(); + + favorite->set_pressed(false); + + + Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + for(int i=0;i<favorited.size();i++) { + bool cres = favorited[i].begins_with("res://"); + if (cres!=res) + continue; + String name = favorited[i]; + + bool setthis = name==current; + + if (res && name=="res://") { + name="/"; + } else { + name=name.get_file()+"/"; + } + + + //print_line("file: "+name); + favorites->add_item(name,star); + favorites->set_item_metadata( favorites->get_item_count()-1,favorited[i]); + + if (setthis) { + favorite->set_pressed(true); + favorites->set_current(favorites->get_item_count()-1); + } + } + + +} + +void EditorFileDialog::_favorite_toggled(bool p_toggle) { + bool res = access==ACCESS_RESOURCES; + + String cd = get_current_dir(); + + Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + + bool found = false; + for(int i=0;i<favorited.size();i++) { + bool cres = favorited[i].begins_with("res://"); + if (cres!=res) + continue; + + if (favorited[i]==cd) { + found=true; + break; + } + } + + if (found) { + favorited.erase(cd); + favorite->set_pressed(false); + } else { + favorited.push_back(cd); + favorite->set_pressed(true); + } + + EditorSettings::get_singleton()->set_favorite_dirs(favorited); + + _update_favorites(); + +} + +void EditorFileDialog::_recent_selected(int p_idx) { + + Vector<String> recentd = EditorSettings::get_singleton()->get_recent_dirs(); + ERR_FAIL_INDEX(p_idx,recentd.size()); + + dir_access->change_dir(recent->get_item_metadata(p_idx)); + update_file_list(); + update_dir(); + _push_history(); +} + +void EditorFileDialog::_go_up() { + + dir_access->change_dir(".."); + update_file_list(); + update_dir(); + _push_history(); + +} + +void EditorFileDialog::_go_back(){ + + if (local_history_pos<=0) { + return; + } + + local_history_pos--; + dir_access->change_dir(local_history[local_history_pos]); + update_file_list(); + update_dir(); + + dir_prev->set_disabled(local_history_pos==0); + dir_next->set_disabled(local_history_pos==local_history.size()-1); +} +void EditorFileDialog::_go_forward(){ + + if (local_history_pos==local_history.size()-1) { + return; + } + + local_history_pos++; + dir_access->change_dir(local_history[local_history_pos]); + update_file_list(); + update_dir(); + + dir_prev->set_disabled(local_history_pos==0); + dir_next->set_disabled(local_history_pos==local_history.size()-1); + +} + +bool EditorFileDialog::default_show_hidden_files=true; + +void EditorFileDialog::set_display_mode(DisplayMode p_mode) { + + if (display_mode==p_mode) + return; + if (p_mode==DISPLAY_THUMBNAILS) { + mode_list->set_pressed(false); + mode_thumbnails->set_pressed(true); + } else { + mode_thumbnails->set_pressed(false); + mode_list->set_pressed(true); + } + display_mode=p_mode; + invalidate();; +} + +EditorFileDialog::DisplayMode EditorFileDialog::get_display_mode() const{ + + return display_mode; +} + + + +void EditorFileDialog::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_item_selected"),&EditorFileDialog::_item_selected); + ObjectTypeDB::bind_method(_MD("_item_db_selected"),&EditorFileDialog::_item_dc_selected); + ObjectTypeDB::bind_method(_MD("_dir_entered"),&EditorFileDialog::_dir_entered); + ObjectTypeDB::bind_method(_MD("_file_entered"),&EditorFileDialog::_file_entered); + ObjectTypeDB::bind_method(_MD("_action_pressed"),&EditorFileDialog::_action_pressed); + ObjectTypeDB::bind_method(_MD("_cancel_pressed"),&EditorFileDialog::_cancel_pressed); + ObjectTypeDB::bind_method(_MD("_filter_selected"),&EditorFileDialog::_filter_selected); + ObjectTypeDB::bind_method(_MD("_save_confirm_pressed"),&EditorFileDialog::_save_confirm_pressed); + + ObjectTypeDB::bind_method(_MD("clear_filters"),&EditorFileDialog::clear_filters); + ObjectTypeDB::bind_method(_MD("add_filter","filter"),&EditorFileDialog::add_filter); + ObjectTypeDB::bind_method(_MD("get_current_dir"),&EditorFileDialog::get_current_dir); + ObjectTypeDB::bind_method(_MD("get_current_file"),&EditorFileDialog::get_current_file); + ObjectTypeDB::bind_method(_MD("get_current_path"),&EditorFileDialog::get_current_path); + ObjectTypeDB::bind_method(_MD("set_current_dir","dir"),&EditorFileDialog::set_current_dir); + ObjectTypeDB::bind_method(_MD("set_current_file","file"),&EditorFileDialog::set_current_file); + ObjectTypeDB::bind_method(_MD("set_current_path","path"),&EditorFileDialog::set_current_path); + ObjectTypeDB::bind_method(_MD("set_mode","mode"),&EditorFileDialog::set_mode); + ObjectTypeDB::bind_method(_MD("get_mode"),&EditorFileDialog::get_mode); + ObjectTypeDB::bind_method(_MD("get_vbox:VBoxContainer"),&EditorFileDialog::get_vbox); + ObjectTypeDB::bind_method(_MD("set_access","access"),&EditorFileDialog::set_access); + ObjectTypeDB::bind_method(_MD("get_access"),&EditorFileDialog::get_access); + ObjectTypeDB::bind_method(_MD("set_show_hidden_files"),&EditorFileDialog::set_show_hidden_files); + ObjectTypeDB::bind_method(_MD("is_showing_hidden_files"),&EditorFileDialog::is_showing_hidden_files); + ObjectTypeDB::bind_method(_MD("_select_drive"),&EditorFileDialog::_select_drive); + ObjectTypeDB::bind_method(_MD("_make_dir"),&EditorFileDialog::_make_dir); + ObjectTypeDB::bind_method(_MD("_make_dir_confirm"),&EditorFileDialog::_make_dir_confirm); + ObjectTypeDB::bind_method(_MD("_update_file_list"),&EditorFileDialog::update_file_list); + ObjectTypeDB::bind_method(_MD("_update_dir"),&EditorFileDialog::update_dir); + ObjectTypeDB::bind_method(_MD("_thumbnail_done"),&EditorFileDialog::_thumbnail_done); + ObjectTypeDB::bind_method(_MD("set_display_mode","mode"),&EditorFileDialog::set_display_mode); + ObjectTypeDB::bind_method(_MD("get_display_mode"),&EditorFileDialog::get_display_mode); + ObjectTypeDB::bind_method(_MD("_thumbnail_result"),&EditorFileDialog::_thumbnail_result); + + ObjectTypeDB::bind_method(_MD("_recent_selected"),&EditorFileDialog::_recent_selected); + ObjectTypeDB::bind_method(_MD("_go_back"),&EditorFileDialog::_go_back); + ObjectTypeDB::bind_method(_MD("_go_forward"),&EditorFileDialog::_go_forward); + ObjectTypeDB::bind_method(_MD("_go_up"),&EditorFileDialog::_go_up); + + ObjectTypeDB::bind_method(_MD("_favorite_toggled"),&EditorFileDialog::_favorite_toggled); + ObjectTypeDB::bind_method(_MD("_favorite_selected"),&EditorFileDialog::_favorite_selected); + ObjectTypeDB::bind_method(_MD("_favorite_move_up"),&EditorFileDialog::_favorite_move_up); + ObjectTypeDB::bind_method(_MD("_favorite_move_down"),&EditorFileDialog::_favorite_move_down); + + ObjectTypeDB::bind_method(_MD("invalidate"),&EditorFileDialog::invalidate); + + ADD_SIGNAL(MethodInfo("file_selected",PropertyInfo( Variant::STRING,"path"))); + ADD_SIGNAL(MethodInfo("files_selected",PropertyInfo( Variant::STRING_ARRAY,"paths"))); + ADD_SIGNAL(MethodInfo("dir_selected",PropertyInfo( Variant::STRING,"dir"))); + + BIND_CONSTANT( MODE_OPEN_FILE ); + BIND_CONSTANT( MODE_OPEN_FILES ); + BIND_CONSTANT( MODE_OPEN_DIR ); + BIND_CONSTANT( MODE_SAVE_FILE ); + + BIND_CONSTANT( ACCESS_RESOURCES ); + BIND_CONSTANT( ACCESS_USERDATA ); + BIND_CONSTANT( ACCESS_FILESYSTEM ); + +} + + +void EditorFileDialog::set_show_hidden_files(bool p_show) { + show_hidden_files=p_show; + invalidate(); +} + +bool EditorFileDialog::is_showing_hidden_files() const { + return show_hidden_files; +} + +void EditorFileDialog::set_default_show_hidden_files(bool p_show) { + default_show_hidden_files=p_show; +} + +void EditorFileDialog::_save_to_recent() { + + String dir = get_current_dir(); + Vector<String> recent = EditorSettings::get_singleton()->get_recent_dirs(); + + const int max=20; + int count=0; + bool res=dir.begins_with("res://"); + + for(int i=0;i<recent.size();i++) { + bool cres=recent[i].begins_with("res://"); + if (recent[i]==dir || (res==cres && count>max)) { + recent.remove(i); + i--; + } else { + count++; + } + } + + recent.insert(0,dir); + + EditorSettings::get_singleton()->set_recent_dirs(recent); + + +} + +EditorFileDialog::EditorFileDialog() { + + show_hidden_files=true; + display_mode=DISPLAY_THUMBNAILS; + local_history_pos=0; + + VBoxContainer *vbc = memnew( VBoxContainer ); + add_child(vbc); + set_child_rect(vbc); + + mode=MODE_SAVE_FILE; + set_title("Save a File"); + + HBoxContainer *pathhb = memnew( HBoxContainer ); + + dir_prev = memnew( ToolButton ); + dir_next = memnew( ToolButton ); + dir_up = memnew( ToolButton ); + + pathhb->add_child(dir_prev); + pathhb->add_child(dir_next); + pathhb->add_child(dir_up); + + dir_prev->connect("pressed",this,"_go_back"); + dir_next->connect("pressed",this,"_go_forward"); + dir_up->connect("pressed",this,"_go_up"); + + dir = memnew(LineEdit); + pathhb->add_child(dir); + dir->set_h_size_flags(SIZE_EXPAND_FILL); + + favorite = memnew( ToolButton ); + favorite->set_toggle_mode(true); + favorite->connect("toggled",this,"_favorite_toggled"); + pathhb->add_child(favorite); + + mode_thumbnails = memnew( ToolButton ); + mode_thumbnails->connect("pressed",this,"set_display_mode",varray(DISPLAY_THUMBNAILS)); + mode_thumbnails->set_toggle_mode(true); + mode_thumbnails->set_pressed(true); + pathhb->add_child(mode_thumbnails); + mode_list = memnew( ToolButton ); + mode_list->connect("pressed",this,"set_display_mode",varray(DISPLAY_LIST)); + mode_list->set_toggle_mode(true); + pathhb->add_child(mode_list); + + drives = memnew( OptionButton ); + pathhb->add_child(drives); + drives->connect("item_selected",this,"_select_drive"); + + makedir = memnew( Button ); + makedir->set_text("Create Folder"); + makedir->connect("pressed",this,"_make_dir"); + pathhb->add_child(makedir); + + list_hb = memnew( HBoxContainer ); + + vbc->add_margin_child("Path:",pathhb); + vbc->add_child(list_hb); + list_hb->set_v_size_flags(SIZE_EXPAND_FILL); + + VBoxContainer *fav_vb = memnew( VBoxContainer ); + list_hb->add_child(fav_vb); + HBoxContainer *fav_hb = memnew( HBoxContainer ); + fav_vb->add_child(fav_hb); + fav_hb->add_child(memnew(Label("Favorites:"))); + fav_hb->add_spacer(); + fav_up = memnew( ToolButton ); + fav_hb->add_child(fav_up); + fav_up->connect("pressed",this,"_favorite_move_up"); + fav_down = memnew( ToolButton ); + fav_hb->add_child(fav_down); + fav_down->connect("pressed",this,"_favorite_move_down"); + fav_rm = memnew( ToolButton ); + fav_hb->add_child(fav_rm); + fav_rm->hide(); // redundant + + MarginContainer *fav_mv = memnew( MarginContainer ); + fav_vb->add_child(fav_mv); + fav_mv->set_v_size_flags(SIZE_EXPAND_FILL); + favorites = memnew( ItemList ); + fav_mv->add_child(favorites); + favorites->connect("item_selected",this,"_favorite_selected"); + + recent = memnew( ItemList ); + fav_vb->add_margin_child("Recent:",recent,true); + recent->connect("item_selected",this,"_recent_selected"); + + VBoxContainer *item_vb = memnew( VBoxContainer ); + list_hb->add_child(item_vb); + item_vb->set_h_size_flags(SIZE_EXPAND_FILL); + + item_list = memnew( ItemList ); + item_list->set_v_size_flags(SIZE_EXPAND_FILL); + item_vb->add_margin_child("Directories & Files:",item_list,true); + + HBoxContainer* filter_hb = memnew( HBoxContainer ); + item_vb->add_child(filter_hb); + + VBoxContainer *filter_vb = memnew( VBoxContainer ); + filter_hb->add_child(filter_vb); + filter_vb->set_h_size_flags(SIZE_EXPAND_FILL); + + preview_vb = memnew( VBoxContainer ); + filter_hb->add_child(preview_vb); + CenterContainer *prev_cc = memnew( CenterContainer ); + preview_vb->add_margin_child("Preview:",prev_cc); + preview = memnew( TextureFrame ); + prev_cc->add_child(preview); + preview_vb->hide(); + + + file = memnew(LineEdit); + //add_child(file); + filter_vb->add_margin_child("File:",file); + + + filter = memnew( OptionButton ); + //add_child(filter); + filter_vb->add_margin_child("Filter:",filter); + filter->set_clip_text(true);//too many extensions overflow it + + dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES); + access=ACCESS_RESOURCES; + _update_drives(); + + + connect("confirmed", this,"_action_pressed"); + //cancel->connect("pressed", this,"_cancel_pressed"); + item_list->connect("item_selected", this,"_item_selected",varray(),CONNECT_DEFERRED); + item_list->connect("item_activated", this,"_item_db_selected",varray()); + dir->connect("text_entered", this,"_dir_entered"); + file->connect("text_entered", this,"_file_entered"); + filter->connect("item_selected", this,"_filter_selected"); + + + confirm_save = memnew( ConfirmationDialog ); + confirm_save->set_as_toplevel(true); + add_child(confirm_save); + + + confirm_save->connect("confirmed", this,"_save_confirm_pressed"); + + makedialog = memnew( ConfirmationDialog ); + makedialog->set_title("Create Folder"); + VBoxContainer *makevb= memnew( VBoxContainer ); + makedialog->add_child(makevb); + makedialog->set_child_rect(makevb); + makedirname = memnew( LineEdit ); + makevb->add_margin_child("Name:",makedirname); + add_child(makedialog); + makedialog->register_text_enter(makedirname); + makedialog->connect("confirmed",this,"_make_dir_confirm"); + mkdirerr = memnew( AcceptDialog ); + mkdirerr->set_text("Could not create folder."); + add_child(mkdirerr); + + exterr = memnew( AcceptDialog ); + exterr->set_text("Must use a valid extension."); + add_child(exterr); + + + //update_file_list(); + update_filters(); + update_dir(); + + set_hide_on_ok(false); + vbox=vbc; + + + invalidated=true; + if (register_func) + register_func(this); + + preview_wheel_timeout=0; + preview_wheel_index=0; + preview_waiting=false; + +} + + +EditorFileDialog::~EditorFileDialog() { + + if (unregister_func) + unregister_func(this); + memdelete(dir_access); +} + + +void EditorLineEditFileChooser::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_browse"),&EditorLineEditFileChooser::_browse); + ObjectTypeDB::bind_method(_MD("_chosen"),&EditorLineEditFileChooser::_chosen); + ObjectTypeDB::bind_method(_MD("get_button:Button"),&EditorLineEditFileChooser::get_button); + ObjectTypeDB::bind_method(_MD("get_line_edit:LineEdit"),&EditorLineEditFileChooser::get_line_edit); + ObjectTypeDB::bind_method(_MD("get_file_dialog:EditorFileDialog"),&EditorLineEditFileChooser::get_file_dialog); + +} + +void EditorLineEditFileChooser::_chosen(const String& p_text){ + + line_edit->set_text(p_text); + line_edit->emit_signal("text_entered",p_text); +} + +void EditorLineEditFileChooser::_browse() { + + dialog->popup_centered_ratio(); +} + +EditorLineEditFileChooser::EditorLineEditFileChooser() { + + line_edit = memnew( LineEdit ); + add_child(line_edit); + line_edit->set_h_size_flags(SIZE_EXPAND_FILL); + button = memnew( Button ); + button->set_text(" .. "); + add_child(button); + button->connect("pressed",this,"_browse"); + dialog = memnew( EditorFileDialog); + add_child(dialog); + dialog->connect("file_selected",this,"_chosen"); + dialog->connect("dir_selected",this,"_chosen"); + dialog->connect("files_selected",this,"_chosen"); + +} diff --git a/tools/editor/editor_file_dialog.h b/tools/editor/editor_file_dialog.h new file mode 100644 index 0000000000..6cfd970516 --- /dev/null +++ b/tools/editor/editor_file_dialog.h @@ -0,0 +1,245 @@ +/*************************************************************************/ +/* file_dialog.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + + +#ifndef EDITORFILEDIALOG_H +#define EDITORFILEDIALOG_H + +#include "scene/gui/dialogs.h" +#include "scene/gui/item_list.h" +#include "scene/gui/line_edit.h" +#include "scene/gui/option_button.h" +#include "scene/gui/dialogs.h" +#include "os/dir_access.h" +#include "scene/gui/box_container.h" +#include "scene/gui/texture_frame.h" +#include "scene/gui/tool_button.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +class EditorFileDialog : public ConfirmationDialog { + + OBJ_TYPE( EditorFileDialog, ConfirmationDialog ); + +public: + + enum DisplayMode { + DISPLAY_THUMBNAILS, + DISPLAY_LIST + }; + + + enum Access { + ACCESS_RESOURCES, + ACCESS_USERDATA, + ACCESS_FILESYSTEM + }; + + + enum Mode { + MODE_OPEN_FILE, + MODE_OPEN_FILES, + MODE_OPEN_DIR, + MODE_SAVE_FILE, + }; + + typedef Ref<Texture> (*GetIconFunc)(const String&); + typedef void (*RegisterFunc)(EditorFileDialog*); + + static GetIconFunc get_icon_func; + static GetIconFunc get_large_icon_func; + static RegisterFunc register_func; + static RegisterFunc unregister_func; + +private: + + ConfirmationDialog *makedialog; + LineEdit *makedirname; + + Button *makedir; + Access access; + //Button *action; + VBoxContainer *vbox; + Mode mode; + LineEdit *dir; + + ToolButton *dir_prev; + ToolButton *dir_next; + ToolButton *dir_up; + + OptionButton *drives; + ItemList *item_list; + TextureFrame *preview; + VBoxContainer *preview_vb; + HBoxContainer *list_hb; + LineEdit *file; + AcceptDialog *mkdirerr; + AcceptDialog *exterr; + OptionButton *filter; + DirAccess *dir_access; + ConfirmationDialog *confirm_save; + ToolButton *mode_thumbnails; + ToolButton *mode_list; + + + ToolButton *favorite; + + ToolButton *fav_up; + ToolButton *fav_down; + ToolButton *fav_rm; + + ItemList *favorites; + ItemList *recent; + + Vector<String> local_history; + int local_history_pos; + void _push_history(); + + Vector<String> filters; + + bool preview_waiting; + int preview_wheel_index; + float preview_wheel_timeout; + static bool default_show_hidden_files; + bool show_hidden_files; + DisplayMode display_mode; + + bool invalidated; + + void update_dir(); + void update_file_list(); + void update_filters(); + + void _update_favorites(); + void _favorite_toggled(bool p_toggle); + void _favorite_selected(int p_idx); + void _favorite_move_up(); + void _favorite_move_down(); + + + + void _recent_selected(int p_idx); + + void _item_selected(int p_item); + void _item_dc_selected(int p_item); + + void _select_drive(int p_idx); + void _dir_entered(String p_dir); + void _file_entered(const String& p_file); + void _action_pressed(); + void _save_confirm_pressed(); + void _cancel_pressed(); + void _filter_selected(int); + void _make_dir(); + void _make_dir_confirm(); + + void _update_drives(); + + void _go_up(); + void _go_back(); + void _go_forward(); + + virtual void _post_popup(); + + void _save_to_recent(); + //callback funtion is callback(String p_path,Ref<Texture> preview,Variant udata) preview null if could not load + + void _thumbnail_result(const String& p_path,const Ref<Texture>& p_preview, const Variant& p_udata); + void _thumbnail_done(const String& p_path,const Ref<Texture>& p_preview, const Variant& p_udata); + void _request_single_thumbnail(const String& p_path); + +protected: + + void _notification(int p_what); + static void _bind_methods(); + //bind helpers +public: + + void clear_filters(); + void add_filter(const String& p_filter); + + void set_enable_multiple_selection(bool p_enable); + Vector<String> get_selected_files() const; + + String get_current_dir() const; + String get_current_file() const; + String get_current_path() const; + void set_current_dir(const String& p_dir); + void set_current_file(const String& p_file); + void set_current_path(const String& p_path); + + void set_display_mode(DisplayMode p_mode); + DisplayMode get_display_mode() const; + + void set_mode(Mode p_mode); + Mode get_mode() const; + + VBoxContainer *get_vbox(); + LineEdit *get_line_edit() { return file; } + + void set_access(Access p_access); + Access get_access() const; + + void set_show_hidden_files(bool p_show); + bool is_showing_hidden_files() const; + + static void set_default_show_hidden_files(bool p_show); + + void invalidate(); + + EditorFileDialog(); + ~EditorFileDialog(); + +}; + +class EditorLineEditFileChooser : public HBoxContainer { + + OBJ_TYPE( EditorLineEditFileChooser, HBoxContainer ); + Button *button; + LineEdit *line_edit; + EditorFileDialog *dialog; + + void _chosen(const String& p_text); + void _browse(); +protected: + static void _bind_methods(); +public: + + Button *get_button() { return button; } + LineEdit *get_line_edit() { return line_edit; } + EditorFileDialog *get_file_dialog() { return dialog; } + + EditorLineEditFileChooser(); +}; + +VARIANT_ENUM_CAST( EditorFileDialog::Mode ); +VARIANT_ENUM_CAST( EditorFileDialog::Access ); +VARIANT_ENUM_CAST( EditorFileDialog::DisplayMode ); + +#endif // EDITORFILEDIALOG_H diff --git a/tools/editor/editor_file_system.cpp b/tools/editor/editor_file_system.cpp index 5d72928e9c..33e4a15c85 100644 --- a/tools/editor/editor_file_system.cpp +++ b/tools/editor/editor_file_system.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,7 +33,7 @@ #include "os/file_access.h" #include "editor_node.h" #include "io/resource_saver.h" - +#include "editor_settings.h" EditorFileSystem *EditorFileSystem::singleton=NULL; @@ -94,6 +94,12 @@ bool EditorFileSystemDirectory::get_file_meta(int p_idx) const { return files[p_idx].meta.enabled; } +Vector<String> EditorFileSystemDirectory::get_file_deps(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,files.size(),Vector<String>()); + return files[p_idx].meta.deps; + +} Vector<String> EditorFileSystemDirectory::get_missing_sources(int p_idx) const { ERR_FAIL_INDEX_V(p_idx,files.size(),Vector<String>()); @@ -118,7 +124,7 @@ bool EditorFileSystemDirectory::is_missing_sources(int p_idx) const { return false; } -String EditorFileSystemDirectory::get_file_type(int p_idx) const { +StringName EditorFileSystemDirectory::get_file_type(int p_idx) const { ERR_FAIL_INDEX_V(p_idx,files.size(),""); return files[p_idx].type; @@ -198,6 +204,13 @@ EditorFileSystemDirectory::ImportMeta EditorFileSystem::_get_meta(const String& } m.import_editor=imd->get_editor(); } + + List<String> deps; + ResourceLoader::get_dependencies(p_path,&deps); + for(List<String>::Element *E=deps.front();E;E=E->next()) { + m.deps.push_back(E->get()); + } + return m; } @@ -357,7 +370,9 @@ void EditorFileSystem::_scan_scenes() { String project=Globals::get_singleton()->get_resource_path(); - FileAccess *f =FileAccess::open(project+"/.fscache",FileAccess::READ); + + String fscache = EditorSettings::get_singleton()->get_project_settings_path().plus_file("filesystem_cache"); + FileAccess *f =FileAccess::open(fscache,FileAccess::READ); if (f) { //read the disk cache @@ -395,7 +410,7 @@ void EditorFileSystem::_scan_scenes() { } else { Vector<String> split = l.split("::"); - ERR_CONTINUE( split.size() != 4); + ERR_CONTINUE( split.size() != 5); String name = split[0]; String file; @@ -427,6 +442,15 @@ void EditorFileSystem::_scan_scenes() { } } + String deps = split[4].strip_edges(); + if (deps.length()) { + Vector<String> dp = deps.split("<>"); + for(int i=0;i<dp.size();i++) { + String path=dp[i]; + fc.meta.deps.push_back(path); + } + } + file_cache[name]=fc; ERR_CONTINUE(!dc); @@ -470,7 +494,9 @@ void EditorFileSystem::_scan_scenes() { //save back the findings - f=FileAccess::open(project+"/.fscache",FileAccess::WRITE); +// String fscache = EditorSettings::get_singleton()->get_project_settings_path().plus_file("file_cache"); + + f=FileAccess::open(fscache,FileAccess::WRITE); _save_type_cache_fs(scandir,f); f->close(); memdelete(f); @@ -526,6 +552,7 @@ void EditorFileSystem::scan() { thread = Thread::create(_thread_func,this,s); //tree->hide(); //progress->show(); + } @@ -794,6 +821,14 @@ void EditorFileSystem::_save_type_cache_fs(DirItem *p_dir,FileAccess *p_file) { } } + s+="::"; + for(int j=0;j<p_dir->files[i]->meta.deps.size();j++) { + + if (j>0) + s+="<>"; + s+=p_dir->files[i]->meta.deps[j]; + } + p_file->store_line(s); } @@ -940,19 +975,19 @@ String EditorFileSystem::get_file_type(const String& p_file) const { EditorFileSystemDirectory *EditorFileSystem::get_path(const String& p_path) { if (!filesystem || scanning) - return false; + return NULL; String f = Globals::get_singleton()->localize_path(p_path); if (!f.begins_with("res://")) - return false; + return NULL; f=f.substr(6,f.length()); f=f.replace("\\","/"); if (f==String()) - return filesystem; + return filesystem; if (f.ends_with("/")) f=f.substr(0,f.length()-1); @@ -960,7 +995,7 @@ EditorFileSystemDirectory *EditorFileSystem::get_path(const String& p_path) { Vector<String> path = f.split("/"); if (path.size()==0) - return false; + return NULL; EditorFileSystemDirectory *fs=filesystem; @@ -989,6 +1024,7 @@ EditorFileSystemDirectory *EditorFileSystem::get_path(const String& p_path) { void EditorFileSystem::_resource_saved(const String& p_path){ + print_line("resource saved: "+p_path); EditorFileSystem::get_singleton()->update_file(p_path); } @@ -1032,6 +1068,13 @@ void EditorFileSystem::update_file(const String& p_file) { return; } + if (!FileAccess::exists(p_file)) { + //was removed + fs->files.remove(cpos); + call_deferred("emit_signal","filesystem_changed"); //update later + return; + + } String type = ResourceLoader::get_resource_type(p_file); diff --git a/tools/editor/editor_file_system.h b/tools/editor/editor_file_system.h index 2d14f9012f..f79dd209ef 100644 --- a/tools/editor/editor_file_system.h +++ b/tools/editor/editor_file_system.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -59,13 +59,14 @@ class EditorFileSystemDirectory : public Object { Vector<Source> sources; String import_editor; + Vector<String> deps; bool enabled; }; struct FileInfo { String file; - String type; + StringName type; uint64_t modified_time; ImportMeta meta; @@ -87,10 +88,11 @@ public: int get_file_count() const; String get_file(int p_idx) const; String get_file_path(int p_idx) const; - String get_file_type(int p_idx) const; + StringName get_file_type(int p_idx) const; bool get_file_meta(int p_idx) const; bool is_missing_sources(int p_idx) const; Vector<String> get_missing_sources(int p_idx) const; + Vector<String> get_file_deps(int p_idx) const; EditorFileSystemDirectory *get_parent(); @@ -120,7 +122,7 @@ class EditorFileSystem : public Node { String path; String name; Vector<DirItem*> dirs; - Vector<SceneItem*> files; + Vector<SceneItem*> files; ~DirItem(); }; @@ -149,6 +151,7 @@ class EditorFileSystem : public Node { String type; uint64_t modification_time; EditorFileSystemDirectory::ImportMeta meta; + Vector<String> deps; }; struct DirCache { diff --git a/tools/editor/editor_fonts.cpp b/tools/editor/editor_fonts.cpp index 86f1e9283c..f145f1ddef 100644 --- a/tools/editor/editor_fonts.cpp +++ b/tools/editor/editor_fonts.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,50 +26,50 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "editor_fonts.h"
-#include "doc_font.h"
-#include "doc_title_font.h"
-#include "doc_code_font.h"
-
-static Ref<Font> make_font(int p_height,int p_ascent, int p_valign, int p_charcount, const int *p_chars,const Ref<Texture> &p_texture) {
-
-
- Ref<Font> font( memnew( Font ) );
- font->add_texture( p_texture );
-
- for (int i=0;i<p_charcount;i++) {
-
- const int *c = &p_chars[i*8];
-
- int chr=c[0];
- Rect2 frect;
- frect.pos.x=c[1];
- frect.pos.y=c[2];
- frect.size.x=c[3];
- frect.size.y=c[4];
- Point2 align( c[5], c[6]+p_valign);
- int advance=c[7];
-
-
- font->add_char( chr, 0, frect, align,advance );
-
- }
-
- font->set_height( p_height );
- font->set_ascent( p_ascent );
-
- return font;
-}
-
-
-void editor_register_fonts(Ref<Theme> p_theme) {
-
-
- Ref<Font> doc_font = make_font(_bi_font_doc_font_height,_bi_font_doc_font_ascent,0,_bi_font_doc_font_charcount,_bi_font_doc_font_characters,p_theme->get_icon("DocFont","EditorIcons"));
- Ref<Font> doc_code_font = make_font(_bi_font_doc_code_font_height,_bi_font_doc_code_font_ascent,0,_bi_font_doc_code_font_charcount,_bi_font_doc_code_font_characters,p_theme->get_icon("DocCodeFont","EditorIcons"));
- Ref<Font> doc_title_font = make_font(_bi_font_doc_title_font_height,_bi_font_doc_title_font_ascent,0,_bi_font_doc_title_font_charcount,_bi_font_doc_title_font_characters,p_theme->get_icon("DocTitleFont","EditorIcons"));
- p_theme->set_font("doc","EditorFonts",doc_font);
- p_theme->set_font("doc_code","EditorFonts",doc_code_font);
- p_theme->set_font("doc_title","EditorFonts",doc_title_font);
-
-}
+#include "editor_fonts.h" +#include "doc_font.h" +#include "doc_title_font.h" +#include "doc_code_font.h" + +static Ref<Font> make_font(int p_height,int p_ascent, int p_valign, int p_charcount, const int *p_chars,const Ref<Texture> &p_texture) { + + + Ref<Font> font( memnew( Font ) ); + font->add_texture( p_texture ); + + for (int i=0;i<p_charcount;i++) { + + const int *c = &p_chars[i*8]; + + int chr=c[0]; + Rect2 frect; + frect.pos.x=c[1]; + frect.pos.y=c[2]; + frect.size.x=c[3]; + frect.size.y=c[4]; + Point2 align( c[5], c[6]+p_valign); + int advance=c[7]; + + + font->add_char( chr, 0, frect, align,advance ); + + } + + font->set_height( p_height ); + font->set_ascent( p_ascent ); + + return font; +} + + +void editor_register_fonts(Ref<Theme> p_theme) { + + + Ref<Font> doc_font = make_font(_bi_font_doc_font_height,_bi_font_doc_font_ascent,0,_bi_font_doc_font_charcount,_bi_font_doc_font_characters,p_theme->get_icon("DocFont","EditorIcons")); + Ref<Font> doc_code_font = make_font(_bi_font_doc_code_font_height,_bi_font_doc_code_font_ascent,0,_bi_font_doc_code_font_charcount,_bi_font_doc_code_font_characters,p_theme->get_icon("DocCodeFont","EditorIcons")); + Ref<Font> doc_title_font = make_font(_bi_font_doc_title_font_height,_bi_font_doc_title_font_ascent,0,_bi_font_doc_title_font_charcount,_bi_font_doc_title_font_characters,p_theme->get_icon("DocTitleFont","EditorIcons")); + p_theme->set_font("doc","EditorFonts",doc_font); + p_theme->set_font("doc_code","EditorFonts",doc_code_font); + p_theme->set_font("doc_title","EditorFonts",doc_title_font); + +} diff --git a/tools/editor/editor_fonts.h b/tools/editor/editor_fonts.h index a3973513b3..cc990a560c 100644 --- a/tools/editor/editor_fonts.h +++ b/tools/editor/editor_fonts.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,11 +26,11 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef EDITOR_FONTS_H
-#define EDITOR_FONTS_H
-
-#include "scene/resources/theme.h"
-
-void editor_register_fonts(Ref<Theme> p_theme);
-
-#endif
+#ifndef EDITOR_FONTS_H +#define EDITOR_FONTS_H + +#include "scene/resources/theme.h" + +void editor_register_fonts(Ref<Theme> p_theme); + +#endif diff --git a/tools/editor/editor_help.cpp b/tools/editor/editor_help.cpp index 8408436a6c..321ac76240 100644 --- a/tools/editor/editor_help.cpp +++ b/tools/editor/editor_help.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -79,7 +79,7 @@ void EditorHelpSearch::_update_search() { _parse_fs(EditorFileSystem::get_singleton()->get_filesystem()); */ - List<String> type_list; + List<StringName> type_list; ObjectTypeDB::get_type_list(&type_list); DocData *doc=EditorHelp::get_doc_data(); @@ -300,9 +300,9 @@ void EditorHelpSearch::_bind_methods() { } -EditorHelpSearch::EditorHelpSearch(EditorNode *p_editor) { +EditorHelpSearch::EditorHelpSearch() { - editor=p_editor; + editor=EditorNode::get_singleton(); VBoxContainer *vbc = memnew( VBoxContainer ); add_child(vbc); set_child_rect(vbc); @@ -318,17 +318,138 @@ EditorHelpSearch::EditorHelpSearch(EditorNode *p_editor) { search_box->connect("input_event",this,"_sbox_input"); search_options = memnew( Tree ); vbc->add_margin_child("Matches:",search_options,true); - get_ok()->set_text("View"); + get_ok()->set_text("Open"); get_ok()->set_disabled(true); register_text_enter(search_box); set_hide_on_ok(false); search_options->connect("item_activated",this,"_confirmed"); set_title("Search Classes"); + // search_options->set_hide_root(true); } +///////////////////////////////// + +//////////////////////////////////// +/// ///////////////////////////////// + + + +void EditorHelpIndex::add_type(const String& p_type,HashMap<String,TreeItem*>& p_types,TreeItem *p_root) { + + if (p_types.has(p_type)) + return; +// if (!ObjectTypeDB::is_type(p_type,base) || p_type==base) +// return; + + String inherits=EditorHelp::get_doc_data()->class_list[p_type].inherits; + + TreeItem *parent=p_root; + + + if (inherits.length()) { + + if (!p_types.has(inherits)) { + + add_type(inherits,p_types,p_root); + } + + if (p_types.has(inherits) ) + parent=p_types[inherits]; + } + + TreeItem *item = class_list->create_item(parent); + item->set_metadata(0,p_type); + item->set_tooltip(0,EditorHelp::get_doc_data()->class_list[p_type].brief_description); + item->set_text(0,p_type); + + + if (has_icon(p_type,"EditorIcons")) { + + item->set_icon(0, get_icon(p_type,"EditorIcons")); + } + + p_types[p_type]=item; +} + + +void EditorHelpIndex::_tree_item_selected() { + + + TreeItem *s=class_list->get_selected(); + if (!s) + return; + + emit_signal("open_class",s->get_text(0)); + + hide(); + + //_goto_desc(s->get_text(0)); + +} + +void EditorHelpIndex::select_class(const String& p_class) { + + if (!tree_item_map.has(p_class)) + return; + tree_item_map[p_class]->select(0); + class_list->ensure_cursor_is_visible(); +} + +void EditorHelpIndex::_notification(int p_what) { + + if (p_what==NOTIFICATION_ENTER_TREE) { + + class_list->clear(); + tree_item_map.clear(); + TreeItem *root = class_list->create_item(); + class_list->set_hide_root(true); + connect("confirmed",this,"_tree_item_selected"); + + + for(Map<String,DocData::ClassDoc>::Element *E=EditorHelp::get_doc_data()->class_list.front();E;E=E->next()) { + + + add_type(E->key(),tree_item_map,root); + } + + } +} + +void EditorHelpIndex::_bind_methods() { + + ObjectTypeDB::bind_method("_tree_item_selected",&EditorHelpIndex::_tree_item_selected); + ObjectTypeDB::bind_method("select_class",&EditorHelpIndex::select_class); + ADD_SIGNAL( MethodInfo("open_class")); +} + + + +EditorHelpIndex::EditorHelpIndex() { + + + VBoxContainer *vbc = memnew( VBoxContainer ); + add_child(vbc); + set_child_rect(vbc); + + class_list = memnew( Tree ); + vbc->add_margin_child("Class List: ",class_list,true); + class_list->set_v_size_flags(SIZE_EXPAND_FILL); + + + class_list->connect("item_activated",this,"_tree_item_selected"); + + + get_ok()->set_text("Open"); +} + + +///////////////////////////////// + +//////////////////////////////////// +/// ///////////////////////////////// DocData *EditorHelp::doc=NULL; void EditorHelp::_unhandled_key_input(const InputEvent& p_ev) { @@ -339,8 +460,6 @@ void EditorHelp::_unhandled_key_input(const InputEvent& p_ev) { search->grab_focus(); search->select_all(); - } else if (p_ev.key.mod.shift && p_ev.key.scancode==KEY_F1) { - class_search->popup(); } } @@ -351,17 +470,19 @@ void EditorHelp::_search(const String&) { String stext=search->get_text(); - bool keep = prev_search==stext && class_list->get_selected() && prev_search_page==class_list->get_selected()->get_text(0); + bool keep = prev_search==stext; - class_desc->search(stext); + bool ret = class_desc->search(stext, keep); + if (!ret) { + class_desc->search(stext, false); + } prev_search=stext; - if (class_list->get_selected()) - prev_search_page=class_list->get_selected()->get_text(0); } +#if 0 void EditorHelp::_button_pressed(int p_idx) { if (p_idx==PAGE_CLASS_LIST) { @@ -399,16 +520,11 @@ void EditorHelp::_button_pressed(int p_idx) { } else if (p_idx==PAGE_SEARCH) { _search(""); - } else if (p_idx==CLASS_SEARCH) { - - class_search->popup(); } - - } - +#endif void EditorHelp::_class_list_select(const String& p_select) { @@ -417,16 +533,28 @@ void EditorHelp::_class_list_select(const String& p_select) { void EditorHelp::_class_desc_select(const String& p_select) { + + +// print_line("LINK: "+p_select); if (p_select.begins_with("#")) { - _goto_desc(p_select.substr(1,p_select.length())); + //_goto_desc(p_select.substr(1,p_select.length())); + emit_signal("go_to_help","class_name:"+p_select.substr(1,p_select.length())); return; } else if (p_select.begins_with("@")) { String m = p_select.substr(1,p_select.length()); - if (!method_line.has(m)) - return; - class_desc->scroll_to_line(method_line[m]); - return; + + if (m.find(".")!=-1) { + //must go somewhere else + + emit_signal("go_to_help","class_method:"+m.get_slice(".",0)+":"+m.get_slice(".",0)); + } else { + + if (!method_line.has(m)) + return; + class_desc->scroll_to_line(method_line[m]); + } + } @@ -449,68 +577,40 @@ void EditorHelp::_add_type(const String& p_type) { } -void EditorHelp::_update_history_buttons() { - - back->set_disabled(history_pos<2); - forward->set_disabled(history_pos>=history.size()); - -} - - void EditorHelp::_scroll_changed(double p_scroll) { if (scroll_locked) return; - int p = history_pos -1; - if (p<0 || p>=history.size()) - return; - if (class_desc->get_v_scroll()->is_hidden()) p_scroll=0; - history[p].scroll=p_scroll; + //history[p].scroll=p_scroll; } -Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_vscr) { +Error EditorHelp::_goto_desc(const String& p_class,int p_vscr) { //ERR_FAIL_COND(!doc->class_list.has(p_class)); if (!doc->class_list.has(p_class)) return ERR_DOES_NOT_EXIST; - if (tree_item_map.has(p_class)) { + //if (tree_item_map.has(p_class)) { select_locked = true; - tree_item_map[p_class]->select(0); - class_list->ensure_cursor_is_visible(); - } + //} class_desc->show(); //tabs->set_current_tab(PAGE_CLASS_DESC); - edited_class->set_pressed(true); - class_list_button->set_pressed(false); description_line=0; - if (p_class==edited_class->get_text()) + if (p_class==edited_class) return OK; //already there scroll_locked=true; - if (p_update_history) { - - history.resize(history_pos); - history_pos++; - History h; - h.c=p_class; - h.scroll=0; - history.push_back(h); - _update_history_buttons(); - class_desc->get_v_scroll()->set_val(0); - } - class_desc->clear(); method_line.clear(); - edited_class->set_text(p_class); + edited_class=p_class; //edited_class->show(); @@ -529,7 +629,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color")); class_desc->add_text("Class: "); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/base_type_color")); - class_desc->add_text(p_class); + _add_text(p_class); class_desc->pop(); class_desc->pop(); class_desc->pop(); @@ -560,7 +660,13 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v //class_desc->add_newline(); class_desc->add_newline(); + class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); + class_desc->push_font( get_font("normal","Fonts") ); + class_desc->push_indent(1); _add_text(cd.brief_description); + class_desc->pop(); + class_desc->pop(); + class_desc->pop(); class_desc->add_newline(); class_desc->add_newline(); } @@ -591,7 +697,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_meta("@"+cd.methods[i].name); } class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); - class_desc->add_text(cd.methods[i].name); + _add_text(cd.methods[i].name); class_desc->pop(); if (cd.methods[i].description!="") class_desc->pop(); @@ -603,13 +709,14 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (j>0) class_desc->add_text(", "); _add_type(cd.methods[i].arguments[j].type); - class_desc->add_text(" "+cd.methods[i].arguments[j].name); + class_desc->add_text(" "); + _add_text(cd.methods[i].arguments[j].name); if (cd.methods[i].arguments[j].default_value!="") { class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text("="); class_desc->pop(); - class_desc->add_text(cd.methods[i].arguments[j].default_value); + _add_text(cd.methods[i].arguments[j].default_value); } class_desc->pop(); @@ -621,7 +728,8 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (cd.methods[i].qualifiers!="") { class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color")); - class_desc->add_text(" "+cd.methods[i].qualifiers); + class_desc->add_text(" "); + _add_text(cd.methods[i].qualifiers); class_desc->pop(); } @@ -655,7 +763,8 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_font(doc_code_font); _add_type(cd.properties[i].type); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); - class_desc->add_text(" "+cd.properties[i].name); + class_desc->add_text(" "); + _add_text(cd.properties[i].name); class_desc->pop(); class_desc->pop(); @@ -663,7 +772,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_font(doc_font); class_desc->add_text(" "); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/comment_color")); - class_desc->add_text(cd.properties[i].description); + _add_text(cd.properties[i].description); class_desc->pop(); class_desc->pop(); @@ -698,7 +807,8 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_font(doc_code_font); _add_type(cd.theme_properties[i].type); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); - class_desc->add_text(" "+cd.theme_properties[i].name); + class_desc->add_text(" "); + _add_text(cd.theme_properties[i].name); class_desc->pop(); class_desc->pop(); @@ -706,7 +816,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_font(doc_font); class_desc->add_text(" "); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/comment_color")); - class_desc->add_text(cd.theme_properties[i].description); + _add_text(cd.theme_properties[i].description); class_desc->pop(); class_desc->pop(); @@ -736,11 +846,11 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v for(int i=0;i<cd.signals.size();i++) { signal_line[cd.signals[i].name]=class_desc->get_line_count()-2; //gets overriden if description - class_desc->push_font(doc_code_font); + class_desc->push_font(doc_code_font); // monofont //_add_type("void"); //class_desc->add_text(" "); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); - class_desc->add_text(cd.signals[i].name); + _add_text(cd.signals[i].name); class_desc->pop(); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text(cd.signals[i].arguments.size()?"( ":"("); @@ -750,13 +860,14 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (j>0) class_desc->add_text(", "); _add_type(cd.signals[i].arguments[j].type); - class_desc->add_text(" "+cd.signals[i].arguments[j].name); + class_desc->add_text(" "); + _add_text(cd.signals[i].arguments[j].name); if (cd.signals[i].arguments[j].default_value!="") { class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text("="); class_desc->pop(); - class_desc->add_text(cd.signals[i].arguments[j].default_value); + _add_text(cd.signals[i].arguments[j].default_value); } class_desc->pop(); @@ -765,14 +876,15 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text(cd.signals[i].arguments.size()?" )":")"); class_desc->pop(); + class_desc->pop(); // end monofont if (cd.signals[i].description!="") { class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/comment_color")); - class_desc->add_text(" "+cd.signals[i].description); + class_desc->add_text(" "); + _add_text(cd.signals[i].description); class_desc->pop(); } - class_desc->pop();//monofont class_desc->add_newline(); } @@ -800,20 +912,20 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v constant_line[cd.constants[i].name]=class_desc->get_line_count()-2; class_desc->push_font(doc_code_font); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/base_type_color")); - class_desc->add_text(cd.constants[i].name); + _add_text(cd.constants[i].name); class_desc->pop(); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text(" = "); class_desc->pop(); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color")); - class_desc->add_text(cd.constants[i].value); + _add_text(cd.constants[i].value); class_desc->pop(); class_desc->pop(); if (cd.constants[i].description!="") { class_desc->push_font(doc_font); class_desc->add_text(" "); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/comment_color")); - class_desc->add_text(cd.constants[i].description); + _add_text(cd.constants[i].description); class_desc->pop(); class_desc->pop(); } @@ -838,7 +950,13 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->add_newline(); class_desc->add_newline(); + class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); + class_desc->push_font( get_font("normal","Fonts") ); + class_desc->push_indent(1); _add_text(cd.description); + class_desc->pop(); + class_desc->pop(); + class_desc->pop(); class_desc->add_newline(); class_desc->add_newline(); } @@ -864,7 +982,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->add_text(" "); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); - class_desc->add_text(cd.methods[i].name); + _add_text(cd.methods[i].name); class_desc->pop(); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text(cd.methods[i].arguments.size()?"( ":"("); @@ -874,13 +992,14 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (j>0) class_desc->add_text(", "); _add_type(cd.methods[i].arguments[j].type); - class_desc->add_text(" "+cd.methods[i].arguments[j].name); + class_desc->add_text(" "); + _add_text(cd.methods[i].arguments[j].name); if (cd.methods[i].arguments[j].default_value!="") { class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text("="); class_desc->pop(); - class_desc->add_text(cd.methods[i].arguments[j].default_value); + _add_text(cd.methods[i].arguments[j].default_value); } class_desc->pop(); @@ -892,7 +1011,8 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (cd.methods[i].qualifiers!="") { class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color")); - class_desc->add_text(" "+cd.methods[i].qualifiers); + class_desc->add_text(" "); + _add_text(cd.methods[i].qualifiers); class_desc->pop(); } @@ -900,8 +1020,14 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->pop(); class_desc->add_newline(); - class_desc->add_newline(); + class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); + class_desc->push_font( get_font("normal","Fonts") ); + class_desc->push_indent(1); _add_text(cd.methods[i].description); + class_desc->pop(); + class_desc->pop(); + class_desc->pop(); + class_desc->add_newline(); class_desc->add_newline(); class_desc->add_newline(); @@ -913,10 +1039,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v } - if (!p_update_history) { - class_desc->get_v_scroll()->set_val(history[history_pos-1].scroll); - } scroll_locked=false; @@ -927,8 +1050,6 @@ void EditorHelp::_request_help(const String& p_string) { Error err = _goto_desc(p_string); if (err==OK) { editor->call("_editor_select",3); - } else { - class_search->popup(p_string); } //100 palabras } @@ -975,9 +1096,9 @@ void EditorHelp::_help_callback(const String& p_topic) { void EditorHelp::_add_text(const String& p_bbcode) { - class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); + /*class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); class_desc->push_font( get_font("normal","Fonts") ); - class_desc->push_indent(1); + class_desc->push_indent(1);*/ int pos = 0; List<String> tag_stack; @@ -1055,7 +1176,7 @@ void EditorHelp::_add_text(const String& p_bbcode) { } else if (tag=="i") { //use italics font - //class_desc->push_font(get_font("italic","Fonts")); + class_desc->push_font(get_font("italic","Fonts")); pos=brk_end+1; tag_stack.push_front(tag); } else if (tag=="code") { @@ -1191,69 +1312,18 @@ void EditorHelp::_add_text(const String& p_bbcode) { } } + /*class_desc->pop(); class_desc->pop(); - class_desc->pop(); + class_desc->pop();*/ } -void EditorHelp::add_type(const String& p_type,HashMap<String,TreeItem*>& p_types,TreeItem *p_root) { - - if (p_types.has(p_type)) - return; -// if (!ObjectTypeDB::is_type(p_type,base) || p_type==base) -// return; - - String inherits=doc->class_list[p_type].inherits; - - TreeItem *parent=p_root; - - - if (inherits.length()) { - - if (!p_types.has(inherits)) { - - add_type(inherits,p_types,p_root); - } - - if (p_types.has(inherits) ) - parent=p_types[inherits]; - } - - TreeItem *item = class_list->create_item(parent); - item->set_metadata(0,p_type); - item->set_tooltip(0,doc->class_list[p_type].brief_description); - item->set_text(0,p_type); - - - if (has_icon(p_type,"EditorIcons")) { - - item->set_icon(0, get_icon(p_type,"EditorIcons")); - } - - p_types[p_type]=item; -} - void EditorHelp::_update_doc() { - class_list->clear(); - - List<String> type_list; - - tree_item_map.clear(); - - TreeItem *root = class_list->create_item(); - class_list->set_hide_root(true); - List<String>::Element *I=type_list.front(); - - for(Map<String,DocData::ClassDoc>::Element *E=doc->class_list.front();E;E=E->next()) { - - - add_type(E->key(),tree_item_map,root); - } } @@ -1277,8 +1347,8 @@ void EditorHelp::_notification(int p_what) { case NOTIFICATION_READY: { - forward->set_icon(get_icon("Forward","EditorIcons")); - back->set_icon(get_icon("Back","EditorIcons")); +// forward->set_icon(get_icon("Forward","EditorIcons")); +// back->set_icon(get_icon("Back","EditorIcons")); _update_doc(); editor->connect("request_help",this,"_request_help"); @@ -1286,222 +1356,118 @@ void EditorHelp::_notification(int p_what) { } } -void EditorHelp::_tree_item_selected() { +void EditorHelp::go_to_help(const String& p_help) { - if (select_locked) { - select_locked = false; - return; - } - TreeItem *s=class_list->get_selected(); - if (!s) - return; - select_locked=true; - _goto_desc(s->get_text(0)); - select_locked=false; + _help_callback(p_help); } -void EditorHelp::_bind_methods() { - - ObjectTypeDB::bind_method("_class_list_select",&EditorHelp::_class_list_select); - ObjectTypeDB::bind_method("_class_desc_select",&EditorHelp::_class_desc_select); - ObjectTypeDB::bind_method("_button_pressed",&EditorHelp::_button_pressed); - ObjectTypeDB::bind_method("_scroll_changed",&EditorHelp::_scroll_changed); - ObjectTypeDB::bind_method("_request_help",&EditorHelp::_request_help); - ObjectTypeDB::bind_method("_unhandled_key_input",&EditorHelp::_unhandled_key_input); - ObjectTypeDB::bind_method("_search",&EditorHelp::_search); - ObjectTypeDB::bind_method("_tree_item_selected",&EditorHelp::_tree_item_selected); - ObjectTypeDB::bind_method("_help_callback",&EditorHelp::_help_callback); +void EditorHelp::go_to_class(const String& p_class,int p_scroll) { + _goto_desc(p_class,p_scroll); } -EditorHelp::EditorHelp(EditorNode *p_editor) { - - editor=p_editor; - - VBoxContainer *vbc = this; - - HBoxContainer *panel_hb = memnew( HBoxContainer ); - - Button *b = memnew( Button ); - b->set_text("Class List"); - panel_hb->add_child(b); - vbc->add_child(panel_hb); - b->set_toggle_mode(true); - b->set_pressed(true); - b->connect("pressed",this,"_button_pressed",make_binds(PAGE_CLASS_LIST)); - class_list_button=b; - class_list_button->hide(); - - b = memnew( Button ); - b->set_text("Class"); - panel_hb->add_child(b); - edited_class=b; - edited_class->hide(); - b->set_toggle_mode(true); - b->connect("pressed",this,"_button_pressed",make_binds(PAGE_CLASS_DESC)); - - b = memnew( Button ); - b->set_text("Search in Classes"); - panel_hb->add_child(b); - b->connect("pressed",this,"_button_pressed",make_binds(CLASS_SEARCH)); - - Control *expand = memnew( Control ); - expand->set_h_size_flags(SIZE_EXPAND_FILL); - panel_hb->add_child(expand); - - b = memnew( Button ); - panel_hb->add_child(b); - back=b; - b->connect("pressed",this,"_button_pressed",make_binds(PAGE_CLASS_PREV)); - - b = memnew( Button ); - panel_hb->add_child(b); - forward=b; - b->connect("pressed",this,"_button_pressed",make_binds(PAGE_CLASS_NEXT)); - - Separator *hs = memnew( VSeparator ); - panel_hb->add_child(hs); - EmptyControl *ec = memnew( EmptyControl ); - ec->set_minsize(Size2(200,1)); - panel_hb->add_child(ec); - search = memnew( LineEdit ); - ec->add_child(search); - search->set_area_as_parent_rect(); - search->connect("text_entered",this,"_search"); - - b = memnew( Button ); - b->set_text("Find"); - panel_hb->add_child(b); - b->connect("pressed",this,"_button_pressed",make_binds(PAGE_SEARCH)); - - hs = memnew( VSeparator ); - panel_hb->add_child(hs); - - h_split = memnew( HSplitContainer ); - h_split->set_v_size_flags(SIZE_EXPAND_FILL); +void EditorHelp::popup_search() { - vbc->add_child(h_split); - - class_list = memnew( Tree ); - h_split->add_child(class_list); - //class_list->connect("meta_clicked",this,"_class_list_select"); - //class_list->set_selection_enabled(true); - - { - PanelContainer *pc = memnew( PanelContainer ); - pc->add_style_override("panel",get_stylebox("normal","TextEdit")); - h_split->add_child(pc); - class_desc = memnew( RichTextLabel ); - pc->add_child(class_desc); - class_desc->connect("meta_clicked",this,"_class_desc_select"); - } - - class_desc->get_v_scroll()->connect("value_changed",this,"_scroll_changed"); - class_desc->set_selection_enabled(true); - editor=p_editor; - history_pos=0; - scroll_locked=false; - select_locked=false; - set_process_unhandled_key_input(true); - h_split->set_split_offset(200); - class_list->connect("cell_selected",this,"_tree_item_selected"); - class_desc->hide(); - - class_search = memnew( EditorHelpSearch(editor) ); - editor->get_gui_base()->add_child(class_search); - class_search->connect("go_to_help",this,"_help_callback"); -// prev_search_page=-1; -} - -EditorHelp::~EditorHelp() { - if (doc) - memdelete(doc); + search_dialog->popup_centered(Size2(250,80)); + search->grab_focus(); } +void EditorHelp::_search_cbk() { -void EditorHelpPlugin::edit(Object *p_object) { - - if (!p_object->cast_to<Script>()) - return; - - //editor_help->edit(p_object->cast_to<Script>()); + _search(search->get_text()); } -bool EditorHelpPlugin::handles(Object *p_object) const { +String EditorHelp::get_class_name() { - return false; + return edited_class; } -void EditorHelpPlugin::make_visible(bool p_visible) { - - if (p_visible) { - editor_help->show(); - } else { - - editor_help->hide(); - } - +void EditorHelp::search_again() { + _search(prev_search); } -void EditorHelpPlugin::selected_notify() { +int EditorHelp::get_scroll() const { - //editor_help->ensure_select_current(); + return class_desc->get_v_scroll()->get_val(); } +void EditorHelp::set_scroll(int p_scroll) { -Dictionary EditorHelpPlugin::get_state() const { - - return Dictionary(); -} -void EditorHelpPlugin::set_state(const Dictionary& p_state) { + class_desc->get_v_scroll()->set_val(p_scroll); - //editor_help->set_state(p_state); } -void EditorHelpPlugin::clear() { - //editor_help->clear(); -} +void EditorHelp::_bind_methods() { -void EditorHelpPlugin::save_external_data() { + ObjectTypeDB::bind_method("_class_list_select",&EditorHelp::_class_list_select); + ObjectTypeDB::bind_method("_class_desc_select",&EditorHelp::_class_desc_select); +// ObjectTypeDB::bind_method("_button_pressed",&EditorHelp::_button_pressed); + ObjectTypeDB::bind_method("_scroll_changed",&EditorHelp::_scroll_changed); + ObjectTypeDB::bind_method("_request_help",&EditorHelp::_request_help); + ObjectTypeDB::bind_method("_unhandled_key_input",&EditorHelp::_unhandled_key_input); + ObjectTypeDB::bind_method("_search",&EditorHelp::_search); + ObjectTypeDB::bind_method("_search_cbk",&EditorHelp::_search_cbk); - //editor_help->save_external_data(); -} + ObjectTypeDB::bind_method("_help_callback",&EditorHelp::_help_callback); -void EditorHelpPlugin::apply_changes() { + ADD_SIGNAL(MethodInfo("go_to_help")); - //editor_help->apply_helps(); } -void EditorHelpPlugin::restore_global_state() { +EditorHelp::EditorHelp() { - //if (bool(EDITOR_DEF("text_editor/restore_helps_on_load",true))) { -// editor_help->_load_files_state(); - //} + editor=EditorNode::get_singleton(); -} + VBoxContainer *vbc = this; -void EditorHelpPlugin::save_global_state() { - //if (bool(EDITOR_DEF("text_editor/restore_helps_on_load",true))) { -// editor_help->_save_files_state(); -// } + //class_list->connect("meta_clicked",this,"_class_list_select"); + //class_list->set_selection_enabled(true); -} + { + Panel *pc = memnew( Panel ); + Ref<StyleBoxFlat> style( memnew( StyleBoxFlat ) ); + style->set_bg_color( EditorSettings::get_singleton()->get("text_editor/background_color") ); + pc->set_v_size_flags(SIZE_EXPAND_FILL); + pc->add_style_override("panel", style); //get_stylebox("normal","TextEdit")); + vbc->add_child(pc); + class_desc = memnew( RichTextLabel ); + pc->add_child(class_desc); + class_desc->set_area_as_parent_rect(8); + class_desc->connect("meta_clicked",this,"_class_desc_select"); + } + class_desc->get_v_scroll()->connect("value_changed",this,"_scroll_changed"); + class_desc->set_selection_enabled(true); + + scroll_locked=false; + select_locked=false; + set_process_unhandled_key_input(true); + class_desc->hide(); -EditorHelpPlugin::EditorHelpPlugin(EditorNode *p_node) { + search_dialog = memnew( ConfirmationDialog ); + add_child(search_dialog); + VBoxContainer *search_vb = memnew( VBoxContainer ); + search_dialog->add_child(search_vb); + search_dialog->set_child_rect(search_vb); + search = memnew( LineEdit ); + search_dialog->register_text_enter(search); + search_vb->add_margin_child("Search Text",search); + search_dialog->get_ok()->set_text("Find"); + search_dialog->connect("confirmed",this,"_search_cbk"); + search_dialog->set_hide_on_ok(false); + search_dialog->set_self_opacity(0.8); - editor=p_node; - editor_help = memnew( EditorHelp(p_node) ); - editor->get_viewport()->add_child(editor_help); - editor_help->set_area_as_parent_rect(); - editor_help->hide(); + /*class_search = memnew( EditorHelpSearch(editor) ); + editor->get_gui_base()->add_child(class_search); + class_search->connect("go_to_help",this,"_help_callback");*/ +// prev_search_page=-1; } +EditorHelp::~EditorHelp() { -EditorHelpPlugin::~EditorHelpPlugin() -{ } + diff --git a/tools/editor/editor_help.h b/tools/editor/editor_help.h index 1c2b704b98..b5ee6eca6c 100644 --- a/tools/editor/editor_help.h +++ b/tools/editor/editor_help.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -70,9 +70,29 @@ public: void popup(const String& p_term=""); - EditorHelpSearch(EditorNode *p_editor); + EditorHelpSearch(); }; +class EditorHelpIndex : public ConfirmationDialog { + OBJ_TYPE( EditorHelpIndex, ConfirmationDialog ); + + + Tree *class_list; + HashMap<String,TreeItem*> tree_item_map; + + void _tree_item_selected(); + void add_type(const String& p_type,HashMap<String,TreeItem*>& p_types,TreeItem *p_root); +protected: + + void _notification(int p_what); + static void _bind_methods(); + +public: + + void select_class(const String& p_class); + + EditorHelpIndex(); +}; class EditorHelp : public VBoxContainer { @@ -91,17 +111,11 @@ class EditorHelp : public VBoxContainer { }; - struct History { - String c; - int scroll; - }; - - Vector<History> history; - int history_pos; bool select_locked; String prev_search; - String prev_search_page; + + String edited_class; EditorNode *editor; Map<String,int> method_line; @@ -111,21 +125,17 @@ class EditorHelp : public VBoxContainer { Map<String,int> constant_line; int description_line; - Tree *class_list; RichTextLabel *class_desc; HSplitContainer *h_split; static DocData *doc; - Button *class_list_button; - Button *edited_class; - Button *back; - Button *forward; + + ConfirmationDialog *search_dialog; LineEdit *search; - String base_path; - HashMap<String,TreeItem*> tree_item_map; + String base_path; void _help_callback(const String& p_topic); @@ -133,25 +143,24 @@ class EditorHelp : public VBoxContainer { void _add_text(const String& p_text); bool scroll_locked; - void _button_pressed(int p_idx); + //void _button_pressed(int p_idx); void _add_type(const String& p_type); void _scroll_changed(double p_scroll); void _class_list_select(const String& p_select); void _class_desc_select(const String& p_select); - Error _goto_desc(const String& p_class,bool p_update_history=true,int p_vscr=-1); - void _update_history_buttons(); + Error _goto_desc(const String& p_class, int p_vscr=-1); + //void _update_history_buttons(); void _update_doc(); void _request_help(const String& p_string); void _search(const String& p_str); + void _search_cbk(); void _unhandled_key_input(const InputEvent& p_ev); - void add_type(const String& p_type,HashMap<String,TreeItem*>& p_types,TreeItem *p_root); - void _tree_item_selected(); - EditorHelpSearch *class_search; + protected: @@ -163,41 +172,25 @@ public: static void generate_doc(); static DocData *get_doc_data() { return doc; } - EditorHelp(EditorNode *p_editor=NULL); - ~EditorHelp(); -}; - - + void go_to_help(const String& p_help); + void go_to_class(const String& p_class,int p_scroll=0); -class EditorHelpPlugin : public EditorPlugin { + void popup_search(); + void search_again(); - OBJ_TYPE( EditorHelpPlugin, EditorPlugin ); + String get_class_name(); - EditorHelp *editor_help; - EditorNode *editor; -public: + void set_focused() { class_desc->grab_focus(); } - virtual String get_name() const { return "Help"; } - bool has_main_screen() const { return true; } - virtual void edit(Object *p_node); - virtual bool handles(Object *p_node) const; - virtual void make_visible(bool p_visible); - virtual void selected_notify(); + int get_scroll() const; + void set_scroll(int p_scroll); - Dictionary get_state() const; - virtual void set_state(const Dictionary& p_state); - virtual void clear(); - - virtual void save_external_data(); - virtual void apply_changes(); + EditorHelp(); + ~EditorHelp(); +}; - virtual void restore_global_state(); - virtual void save_global_state(); - EditorHelpPlugin(EditorNode *p_node); - ~EditorHelpPlugin(); -}; #endif // EDITOR_HELP_H diff --git a/tools/editor/editor_icons.h b/tools/editor/editor_icons.h index 4cd08bba03..910febc895 100644 --- a/tools/editor/editor_icons.h +++ b/tools/editor/editor_icons.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/editor_import_export.cpp b/tools/editor/editor_import_export.cpp index a511e78863..f52c6e67a2 100644 --- a/tools/editor/editor_import_export.cpp +++ b/tools/editor/editor_import_export.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,6 +40,7 @@ #include "io/resource_saver.h" #include "io/md5.h" #include "io_plugins/editor_texture_import_plugin.h" +#include "tools/editor/plugins/script_editor_plugin.h" String EditorImportPlugin::validate_source_path(const String& p_path) { @@ -47,6 +48,7 @@ String EditorImportPlugin::validate_source_path(const String& p_path) { String rp = Globals::get_singleton()->get_resource_path(); if (!rp.ends_with("/")) rp+="/"; + return rp.path_to_file(gp); } @@ -253,7 +255,16 @@ static void _add_filter_to_list(Set<StringName>& r_list,const String& p_filter) } +Vector<uint8_t> EditorExportPlatform::get_exported_file_default(String& p_fname) const { + FileAccess *f = FileAccess::open(p_fname,FileAccess::READ); + ERR_FAIL_COND_V(!f,Vector<uint8_t>()); + Vector<uint8_t> ret; + ret.resize(f->get_len()); + int rbs = f->get_buffer(ret.ptr(),ret.size()); + memdelete(f); + return ret; +} Vector<uint8_t> EditorExportPlatform::get_exported_file(String& p_fname) const { @@ -268,13 +279,9 @@ Vector<uint8_t> EditorExportPlatform::get_exported_file(String& p_fname) const { } - FileAccess *f = FileAccess::open(p_fname,FileAccess::READ); - ERR_FAIL_COND_V(!f,Vector<uint8_t>()); - Vector<uint8_t> ret; - ret.resize(f->get_len()); - int rbs = f->get_buffer(ret.ptr(),ret.size()); - memdelete(f); - return ret; + return get_exported_file_default(p_fname); + + } Vector<StringName> EditorExportPlatform::get_dependencies(bool p_bundles) const { @@ -292,7 +299,11 @@ Vector<StringName> EditorExportPlatform::get_dependencies(bool p_bundles) const _add_filter_to_list(exported,"*"); } else { _add_to_list(EditorFileSystem::get_singleton()->get_filesystem(),exported); - _add_filter_to_list(exported,EditorImportExport::get_singleton()->get_export_custom_filter()); + String cf = EditorImportExport::get_singleton()->get_export_custom_filter(); + if (cf!="") + cf+=","; + cf+="*.flags"; + _add_filter_to_list(exported,cf); } @@ -361,8 +372,12 @@ Vector<StringName> EditorExportPlatform::get_dependencies(bool p_bundles) const } } } + String cf = EditorImportExport::get_singleton()->get_export_custom_filter(); + if (cf!="") + cf+=","; + cf+="*.flags"; + _add_filter_to_list(exported,cf); - _add_filter_to_list(exported,EditorImportExport::get_singleton()->get_export_custom_filter()); } @@ -547,6 +562,7 @@ Error EditorExportPlatform::export_project_files(EditorExportSaveFunction p_func group_shrink*=EditorImportExport::get_singleton()->get_export_image_shrink(); switch(EditorImportExport::get_singleton()->image_export_group_get_image_action(E->get())) { + case EditorImportExport::IMAGE_ACTION_KEEP: case EditorImportExport::IMAGE_ACTION_NONE: { switch(EditorImportExport::get_singleton()->get_export_image_action()) { @@ -571,7 +587,7 @@ Error EditorExportPlatform::export_project_files(EditorExportSaveFunction p_func } break; //use default case EditorImportExport::IMAGE_ACTION_COMPRESS_RAM: { group_format=EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_RAM; - } break; //use default + } break; //use default } String image_list_md5; @@ -815,13 +831,43 @@ Error EditorExportPlatform::export_project_files(EditorExportSaveFunction p_func StringName engine_cfg="res://engine.cfg"; + StringName boot_splash; + { + String splash=Globals::get_singleton()->get("application/boot_splash"); //avoid splash from being converted + splash=splash.strip_edges(); + if (splash!=String()) { + if (!splash.begins_with("res://")) + splash="res://"+splash; + splash=splash.simplify_path(); + boot_splash=splash; + } + } + StringName custom_cursor; + { + String splash=Globals::get_singleton()->get("display/custom_mouse_cursor"); //avoid splash from being converted + splash=splash.strip_edges(); + if (splash!=String()) { + if (!splash.begins_with("res://")) + splash="res://"+splash; + splash=splash.simplify_path(); + custom_cursor=splash; + } + } + + + for(int i=0;i<files.size();i++) { if (remap_files.has(files[i]) || files[i]==engine_cfg) //gonna be remapped (happened before!) continue; //from atlas? String src=files[i]; - Vector<uint8_t> buf = get_exported_file(src); + Vector<uint8_t> buf; + + if (src==boot_splash || src==custom_cursor) + buf = get_exported_file_default(src); //bootsplash must be kept if used + else + buf = get_exported_file(src); ERR_CONTINUE( saved.has(src) ); @@ -896,6 +942,69 @@ Error EditorExportPlatform::export_project_files(EditorExportSaveFunction p_func return OK; } +static int _get_pad(int p_alignment, int p_n) { + + int rest = p_n % p_alignment; + int pad = 0; + if (rest > 0) { + pad = p_alignment - rest; + }; + + return pad; +}; + +void EditorExportPlatform::gen_export_flags(Vector<String> &r_flags, int p_flags) { + + String host = EditorSettings::get_singleton()->get("network/debug_host"); + + if (p_flags&EXPORT_DUMB_CLIENT) { + int port = EditorSettings::get_singleton()->get("file_server/port"); + String passwd = EditorSettings::get_singleton()->get("file_server/password"); + r_flags.push_back("-rfs"); + r_flags.push_back(host+":"+itos(port)); + if (passwd!="") { + r_flags.push_back("-rfs_pass"); + r_flags.push_back(passwd); + } + } + + if (p_flags&EXPORT_REMOTE_DEBUG) { + + r_flags.push_back("-rdebug"); + r_flags.push_back(host+":"+String::num(GLOBAL_DEF("debug/debug_port", 6007))); + + List<String> breakpoints; + ScriptEditor::get_singleton()->get_breakpoints(&breakpoints); + + + if (breakpoints.size()) { + + r_flags.push_back("-bp"); + String bpoints; + for(const List<String>::Element *E=breakpoints.front();E;E=E->next()) { + + bpoints+=E->get().replace(" ","%20"); + if (E->next()) + bpoints+=","; + } + + r_flags.push_back(bpoints); + } + + } + + if (p_flags&EXPORT_VIEW_COLLISONS) { + + r_flags.push_back("-debugcol"); + } + + if (p_flags&EXPORT_VIEW_NAVIGATION) { + + r_flags.push_back("-debugnav"); + } + + +} Error EditorExportPlatform::save_pack_file(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total) { @@ -922,11 +1031,19 @@ Error EditorExportPlatform::save_pack_file(void *p_userdata,const String& p_path pd->ep->step("Storing File: "+p_path,2+p_file*100/p_total); pd->count++; pd->ftmp->store_buffer(p_data.ptr(),p_data.size()); + if (pd->alignment > 1) { + + int pad = _get_pad(pd->alignment, pd->ftmp->get_pos()); + for (int i=0; i<pad; i++) { + + pd->ftmp->store_8(0); + }; + }; return OK; } -Error EditorExportPlatform::save_pack(FileAccess *dst,bool p_make_bundles) { +Error EditorExportPlatform::save_pack(FileAccess *dst,bool p_make_bundles, int p_alignment) { EditorProgress ep("savepack","Packing",102); @@ -944,7 +1061,6 @@ Error EditorExportPlatform::save_pack(FileAccess *dst,bool p_make_bundles) { dst->store_32(0); } - size_t fcountpos = dst->get_pos(); dst->store_32(0); @@ -953,11 +1069,20 @@ Error EditorExportPlatform::save_pack(FileAccess *dst,bool p_make_bundles) { pd.f=dst; pd.ftmp=tmp; pd.count=0; + pd.alignment = p_alignment; Error err = export_project_files(save_pack_file,&pd,p_make_bundles); memdelete(tmp); if (err) return err; + if (p_alignment > 1) { + int pad = _get_pad(p_alignment, dst->get_pos()); + for (int i=0; i<pad; i++) { + + dst->store_8(0); + }; + }; + size_t ofsplus = dst->get_pos(); //append file @@ -994,7 +1119,7 @@ Error EditorExportPlatform::save_pack(FileAccess *dst,bool p_make_bundles) { return OK; } -Error EditorExportPlatformPC::export_project(const String& p_path, bool p_debug, bool p_dumb) { +Error EditorExportPlatformPC::export_project(const String& p_path, bool p_debug, int p_flags) { @@ -1084,30 +1209,42 @@ bool EditorExportPlatformPC::can_export(String *r_error) const { String exe_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; - if (!FileAccess::exists(exe_path+debug_binary32) || !FileAccess::exists(exe_path+release_binary32)) { + if (use64 && (!FileAccess::exists(exe_path+debug_binary64) || !FileAccess::exists(exe_path+release_binary64))) { valid=false; - err="No 32 bits export templates found.\nDownload and install export templates.\n"; + err="No 64 bits export templates found.\nDownload and install export templates.\n"; } - if (!FileAccess::exists(exe_path+debug_binary64) || !FileAccess::exists(exe_path+release_binary64)) { + + if (!use64 && (!FileAccess::exists(exe_path+debug_binary32) || !FileAccess::exists(exe_path+release_binary32))) { valid=false; - err="No 64 bits export templates found.\nDownload and install export templates.\n"; + err="No 32 bits export templates found.\nDownload and install export templates.\n"; + } + + if(custom_debug_binary=="" && custom_release_binary=="") { + if (r_error) *r_error=err; + return valid; } + bool dvalid = true; + bool rvalid = true; - if (custom_debug_binary!="" && !FileAccess::exists(custom_debug_binary)) { - valid=false; - err+="Custom debug binary not found.\n"; + if(!FileAccess::exists(custom_debug_binary)) { + dvalid = false; + err = "Custom debug binary not found.\n"; } - if (custom_release_binary!="" && !FileAccess::exists(custom_release_binary)) { - valid=false; - err+="Custom release binary not found.\n"; + if(!FileAccess::exists(custom_release_binary)) { + rvalid = false; + err = "Custom release binary not found.\n"; } + if (dvalid || rvalid) + valid = true; + else + valid = false; + if (r_error) *r_error=err; return valid; - } @@ -1133,10 +1270,36 @@ EditorImportExport* EditorImportExport::singleton=NULL; void EditorImportExport::add_import_plugin(const Ref<EditorImportPlugin>& p_plugin) { + // Need to make sure the name is unique if we are going to lookup by it + ERR_FAIL_COND(by_idx.has(p_plugin->get_name())); + by_idx[ p_plugin->get_name() ]=plugins.size(); plugins.push_back(p_plugin); } +void EditorImportExport::remove_import_plugin(const Ref<EditorImportPlugin>& p_plugin) { + + String plugin_name = p_plugin->get_name(); + + // Keep the indices the same + // Find the index of the target plugin + ERR_FAIL_COND(!by_idx.has(plugin_name)); + int idx = by_idx[plugin_name]; + int last_idx = plugins.size() - 1; + + // Swap the last plugin and the target one + SWAP(plugins[idx], plugins[last_idx]); + + // Update the index of the old last one + by_idx[plugins[idx]->get_name()] = idx; + + // Remove the target plugin's by_idx entry + by_idx.erase(plugin_name); + + // Erase the plugin + plugins.remove(last_idx); +} + int EditorImportExport::get_import_plugin_count() const{ return plugins.size(); @@ -1269,12 +1432,12 @@ EditorImportExport::ImageAction EditorImportExport::get_export_image_action() co return image_action; } -void EditorImportExport::set_export_image_shrink(int p_shrink) { +void EditorImportExport::set_export_image_shrink(float p_shrink) { image_shrink=p_shrink; } -int EditorImportExport::get_export_image_shrink() const{ +float EditorImportExport::get_export_image_shrink() const{ return image_shrink; } @@ -1345,12 +1508,12 @@ bool EditorImportExport::image_export_group_get_make_atlas(const StringName& p_e return image_groups[p_export_group].make_atlas; } -void EditorImportExport::image_export_group_set_shrink(const StringName& p_export_group,int p_amount){ +void EditorImportExport::image_export_group_set_shrink(const StringName& p_export_group,float p_amount){ ERR_FAIL_COND(!image_groups.has(p_export_group)); image_groups[p_export_group].shrink=p_amount; } -int EditorImportExport::image_export_group_get_shrink(const StringName& p_export_group) const{ +float EditorImportExport::image_export_group_get_shrink(const StringName& p_export_group) const{ ERR_FAIL_COND_V(!image_groups.has(p_export_group),1); return image_groups[p_export_group].shrink; @@ -1504,6 +1667,8 @@ void EditorImportExport::load_config() { g.action=IMAGE_ACTION_COMPRESS_RAM; else if (action=="compress_disk") g.action=IMAGE_ACTION_COMPRESS_DISK; + else if (action=="keep") + g.action=IMAGE_ACTION_KEEP; } if (d.has("atlas")) @@ -1553,6 +1718,17 @@ void EditorImportExport::load_config() { } } + if (cf->has_section("convert_samples")) { + + if (cf->has_section_key("convert_samples","max_hz")) + sample_action_max_hz=cf->get_value("convert_samples","max_hz"); + + if (cf->has_section_key("convert_samples","trim")) + sample_action_trim=cf->get_value("convert_samples","trim"); + } + + + } @@ -1631,6 +1807,7 @@ void EditorImportExport::save_config() { case IMAGE_ACTION_NONE: d["action"]="default"; break; case IMAGE_ACTION_COMPRESS_RAM: d["action"]="compress_ram"; break; case IMAGE_ACTION_COMPRESS_DISK: d["action"]="compress_disk"; break; + case IMAGE_ACTION_KEEP: d["action"]="keep"; break; } @@ -1662,6 +1839,14 @@ void EditorImportExport::save_config() { cf->set_value("script","encrypt_key",script_key); + switch(sample_action) { + case SAMPLE_ACTION_NONE: cf->set_value("convert_samples","action","none"); break; + case SAMPLE_ACTION_COMPRESS_RAM: cf->set_value("convert_samples","action","compress_ram"); break; + } + + cf->set_value("convert_samples","max_hz",sample_action_max_hz); + cf->set_value("convert_samples","trim",sample_action_trim); + cf->save("res://export.cfg"); } @@ -1687,6 +1872,35 @@ String EditorImportExport::script_get_encryption_key() const{ } +void EditorImportExport::sample_set_action(SampleAction p_action) { + + sample_action=p_action; +} + +EditorImportExport::SampleAction EditorImportExport::sample_get_action() const{ + + return sample_action; +} + +void EditorImportExport::sample_set_max_hz(int p_hz){ + + sample_action_max_hz=p_hz; +} +int EditorImportExport::sample_get_max_hz() const{ + + return sample_action_max_hz; +} + +void EditorImportExport::sample_set_trim(bool p_trim){ + + sample_action_trim=p_trim; +} +bool EditorImportExport::sample_get_trim() const{ + + return sample_action_trim; +} + + void EditorImportExport::_bind_methods() { ObjectTypeDB::bind_method(_MD("image_export_group_create"),&EditorImportExport::image_export_group_create); @@ -1714,8 +1928,21 @@ EditorImportExport::EditorImportExport() { image_formats.insert("png"); image_shrink=1; + script_action=SCRIPT_ACTION_COMPILE; + sample_action=SAMPLE_ACTION_NONE; + sample_action_max_hz=44100; + sample_action_trim=false; + +} + + + +EditorImportExport::~EditorImportExport() { + + + } diff --git a/tools/editor/editor_import_export.h b/tools/editor/editor_import_export.h index 8305e3c88c..1a3171e66b 100644 --- a/tools/editor/editor_import_export.h +++ b/tools/editor/editor_import_export.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -83,6 +83,7 @@ public: typedef Error (*EditorExportSaveFunction)(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total); protected: + Vector<uint8_t> get_exported_file_default(String& p_fname) const; virtual Vector<uint8_t> get_exported_file(String& p_fname) const; virtual Vector<StringName> get_dependencies(bool p_bundles) const; @@ -100,9 +101,11 @@ protected: Vector<TempData> file_ofs; EditorProgress *ep; int count; + int alignment; }; + void gen_export_flags(Vector<String> &r_flags, int p_flags); static Error save_pack_file(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total); public: @@ -118,10 +121,17 @@ public: IMAGE_COMPRESSION_ETC2, // ericsson new compression format (can handle alpha) }; + enum ExportFlags { + EXPORT_DUMB_CLIENT=1, + EXPORT_REMOTE_DEBUG=2, + EXPORT_VIEW_COLLISONS=4, + EXPORT_VIEW_NAVIGATION=8 + }; + Error export_project_files(EditorExportSaveFunction p_func, void* p_udata,bool p_make_bundles); - Error save_pack(FileAccess *p_where, bool p_make_bundles=false); + Error save_pack(FileAccess *p_where, bool p_make_bundles=false, int p_alignment = 1); virtual String get_name() const =0; virtual ImageCompression get_image_compression() const=0; virtual Ref<Texture> get_logo() const =0; @@ -130,14 +140,14 @@ public: virtual int get_device_count() const { return 0; } virtual String get_device_name(int p_device) const { return ""; } virtual String get_device_info(int p_device) const { return ""; } - virtual Error run(int p_device,bool p_dumb=false) { return OK; } + virtual Error run(int p_device,int p_flags) { return OK; } virtual bool can_export(String *r_error=NULL) const=0; virtual bool requieres_password(bool p_debug) const { return false; } virtual String get_binary_extension() const=0; - virtual Error export_project(const String& p_path,bool p_debug,bool p_dumb=false)=0; + virtual Error export_project(const String& p_path,bool p_debug,int p_flags=0)=0; EditorExportPlatform() {}; }; @@ -187,7 +197,7 @@ public: virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_BC; } virtual String get_binary_extension() const { return binary_extension; } - virtual Error export_project(const String& p_path,bool p_debug,bool p_dumb=false); + virtual Error export_project(const String& p_path, bool p_debug, int p_flags=0); virtual void set_release_binary32(const String& p_binary) { release_binary32=p_binary; } virtual void set_debug_binary32(const String& p_binary) { debug_binary32=p_binary; } virtual void set_release_binary64(const String& p_binary) { release_binary64=p_binary; } @@ -225,6 +235,8 @@ public: IMAGE_ACTION_NONE, IMAGE_ACTION_COMPRESS_DISK, IMAGE_ACTION_COMPRESS_RAM, + IMAGE_ACTION_KEEP //for group + }; enum ScriptAction { @@ -233,6 +245,12 @@ public: SCRIPT_ACTION_ENCRYPT }; + enum SampleAction { + + SAMPLE_ACTION_NONE, + SAMPLE_ACTION_COMPRESS_RAM, + }; + protected: struct ImageGroup { @@ -240,7 +258,7 @@ protected: ImageAction action; bool make_atlas; float lossy_quality; - int shrink; + float shrink; }; Vector<Ref<EditorExportPlugin> > export_plugins; @@ -248,7 +266,7 @@ protected: Map<String,int> by_idx; ImageAction image_action; float image_action_compress_quality; - int image_shrink; + float image_shrink; Set<String> image_formats; ExportFilter export_filter; @@ -262,6 +280,10 @@ protected: ScriptAction script_action; String script_key; + SampleAction sample_action; + int sample_action_max_hz; + bool sample_action_trim; + static EditorImportExport* singleton; static void _bind_methods(); @@ -270,6 +292,7 @@ public: static EditorImportExport* get_singleton() { return singleton; } void add_import_plugin(const Ref<EditorImportPlugin>& p_plugin); + void remove_import_plugin(const Ref<EditorImportPlugin>& p_plugin); int get_import_plugin_count() const; Ref<EditorImportPlugin> get_import_plugin(int p_idx) const; Ref<EditorImportPlugin> get_import_plugin_by_name(const String& p_string) const; @@ -297,8 +320,8 @@ public: void set_export_image_action(ImageAction p_action); ImageAction get_export_image_action() const; - void set_export_image_shrink(int p_shrink); - int get_export_image_shrink() const; + void set_export_image_shrink(float p_shrink); + float get_export_image_shrink() const; void set_export_image_quality(float p_quality); float get_export_image_quality() const; @@ -313,8 +336,8 @@ public: ImageAction image_export_group_get_image_action(const StringName& p_export_group) const; void image_export_group_set_make_atlas(const StringName& p_export_group,bool p_make); bool image_export_group_get_make_atlas(const StringName& p_export_group) const; - void image_export_group_set_shrink(const StringName& p_export_group,int p_amount); - int image_export_group_get_shrink(const StringName& p_export_group) const; + void image_export_group_set_shrink(const StringName& p_export_group,float p_amount); + float image_export_group_get_shrink(const StringName& p_export_group) const; void image_export_group_set_lossy_quality(const StringName& p_export_group,float p_quality); float image_export_group_get_lossy_quality(const StringName& p_export_group) const; @@ -330,10 +353,20 @@ public: void script_set_encryption_key(const String& p_key); String script_get_encryption_key() const; + void sample_set_action(SampleAction p_action); + SampleAction sample_get_action() const; + + void sample_set_max_hz(int p_hz); + int sample_get_max_hz() const; + + void sample_set_trim(bool p_trim); + bool sample_get_trim() const; + void load_config(); void save_config(); EditorImportExport(); + ~EditorImportExport(); }; VARIANT_ENUM_CAST(EditorImportExport::ImageAction); diff --git a/tools/editor/editor_log.cpp b/tools/editor/editor_log.cpp index f7f5f596d7..264117eecd 100644 --- a/tools/editor/editor_log.cpp +++ b/tools/editor/editor_log.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -81,6 +81,7 @@ void EditorLog::_notification(int p_what) { log->add_color_override("default_color",get_color("font_color","Tree")); tb->set_normal_texture( get_icon("Collapse","EditorIcons")); tb->set_hover_texture( get_icon("CollapseHl","EditorIcons")); + //button->set_icon(get_icon("Console","EditorIcons")); } @@ -104,6 +105,17 @@ void EditorLog::_close_request() { } +void EditorLog::_clear_request() { + + log->clear(); + +} + +void EditorLog::clear() { + _clear_request(); +} + + void EditorLog::add_message(const String& p_msg,bool p_error) { @@ -114,6 +126,7 @@ void EditorLog::add_message(const String& p_msg,bool p_error) { log->push_color(get_color("fg_error","Editor")); } else { button->set_icon(Ref<Texture>()); + } @@ -143,17 +156,20 @@ void EditorLog::_dragged(const Point2& p_ofs) { */ -ToolButton *EditorLog::get_button() { +Button *EditorLog::get_button() { return button; } void EditorLog::_flip_request() { - if (is_visible()) + if (is_visible()) { hide(); - else + button->show(); + } else { show(); + button->hide(); + } } void EditorLog::_undo_redo_cbk(void *p_self,const String& p_name) { @@ -167,9 +183,12 @@ void EditorLog::_bind_methods() { ObjectTypeDB::bind_method(_MD("_close_request"),&EditorLog::_close_request ); ObjectTypeDB::bind_method(_MD("_flip_request"),&EditorLog::_flip_request ); + ObjectTypeDB::bind_method(_MD("_clear_request"),&EditorLog::_clear_request ); + //ObjectTypeDB::bind_method(_MD("_dragged"),&EditorLog::_dragged ); ADD_SIGNAL( MethodInfo("close_request")); ADD_SIGNAL( MethodInfo("show_request")); + ADD_SIGNAL( MethodInfo("clear_request")); } EditorLog::EditorLog() { @@ -186,7 +205,7 @@ EditorLog::EditorLog() { hb->add_child(title); - button = memnew( ToolButton ); + button = memnew( Button ); button->set_text_align(Button::ALIGN_LEFT); button->connect("pressed",this,"_flip_request"); button->set_focus_mode(FOCUS_NONE); @@ -198,14 +217,19 @@ EditorLog::EditorLog() { //pd->connect("dragged",this,"_dragged"); //pd->set_default_cursor_shape(Control::CURSOR_MOVE); + clearbutton = memnew( Button ); + hb->add_child(clearbutton); + clearbutton->set_text("Clear"); + clearbutton->connect("pressed", this,"_clear_request"); + tb = memnew( TextureButton ); hb->add_child(tb); tb->connect("pressed",this,"_close_request"); - ec = memnew( EmptyControl); + ec = memnew( Control); vb->add_child(ec); - ec->set_minsize(Size2(0,100)); + ec->set_custom_minimum_size(Size2(0,100)); ec->set_v_size_flags(SIZE_EXPAND_FILL); @@ -219,7 +243,7 @@ EditorLog::EditorLog() { log->set_selection_enabled(true); log->set_focus_mode(FOCUS_CLICK); pc->add_child(log); - add_message(VERSION_FULL_NAME" (c) 2008-2014 Juan Linietsky, Ariel Manzur."); + add_message(VERSION_FULL_NAME" (c) 2008-2015 Juan Linietsky, Ariel Manzur."); //log->add_text("Initialization Complete.\n"); //because it looks cool. add_style_override("panel",get_stylebox("panelf","Panel")); @@ -241,8 +265,8 @@ void EditorLog::deinit() { } + EditorLog::~EditorLog() { } - diff --git a/tools/editor/editor_log.h b/tools/editor/editor_log.h index b6cd951287..93044f9a2d 100644 --- a/tools/editor/editor_log.h +++ b/tools/editor/editor_log.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,7 +33,7 @@ #include "scene/gui/label.h" #include "scene/gui/rich_text_label.h" #include "scene/gui/texture_button.h" -#include "scene/gui/empty_control.h" +//#include "scene/gui/empty_control.h" #include "scene/gui/box_container.h" #include "scene/gui/panel_container.h" #include "scene/gui/texture_frame.h" @@ -44,13 +44,14 @@ class EditorLog : public PanelContainer { OBJ_TYPE( EditorLog, PanelContainer ); - ToolButton *button; + Button *button; + Button *clearbutton; Label *title; RichTextLabel *log; TextureButton *tb; HBoxContainer *title_hb; // PaneDrag *pd; - EmptyControl *ec; + Control *ec; static void _error_handler(void *p_self, const char*p_func, const char*p_file,int p_line, const char*p_error,const char*p_errorexp,ErrorHandlerType p_type); @@ -58,10 +59,10 @@ class EditorLog : public PanelContainer { Thread::ID current; - // void _dragged(const Point2& p_ofs); void _close_request(); void _flip_request(); + void _clear_request(); static void _undo_redo_cbk(void *p_self,const String& p_name); protected: @@ -72,7 +73,8 @@ public: void add_message(const String& p_msg, bool p_error=false); void deinit(); - ToolButton *get_button(); + Button *get_button(); + void clear(); EditorLog(); ~EditorLog(); }; diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp index dbc896cbb9..4e22592880 100644 --- a/tools/editor/editor_node.cpp +++ b/tools/editor/editor_node.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -54,10 +54,11 @@ #include "register_exporters.h" #include "bind/core_bind.h" #include "io/zip_io.h" - +#include "io/config_file.h" // plugins #include "plugins/sprite_frames_editor_plugin.h" +#include "plugins/sprite_region_editor_plugin.h" #include "plugins/canvas_item_editor_plugin.h" #include "plugins/spatial_editor_plugin.h" #include "plugins/sample_editor_plugin.h" @@ -75,6 +76,7 @@ #include "plugins/tile_map_editor_plugin.h" #include "plugins/cube_grid_theme_editor_plugin.h" #include "plugins/shader_editor_plugin.h" +#include "plugins/shader_graph_editor_plugin.h" #include "plugins/path_editor_plugin.h" #include "plugins/rich_text_editor_plugin.h" #include "plugins/collision_polygon_editor_plugin.h" @@ -88,6 +90,11 @@ #include "plugins/animation_player_editor_plugin.h" #include "plugins/baked_light_editor_plugin.h" #include "plugins/polygon_2d_editor_plugin.h" +#include "plugins/navigation_polygon_editor_plugin.h" +#include "plugins/light_occluder_2d_editor_plugin.h" +#include "plugins/color_ramp_editor_plugin.h" +#include "plugins/collision_shape_2d_editor_plugin.h" +#include "main/input_default.h" // end #include "tools/editor/io_plugins/editor_texture_import_plugin.h" #include "tools/editor/io_plugins/editor_scene_import_plugin.h" @@ -96,15 +103,53 @@ #include "tools/editor/io_plugins/editor_translation_import_plugin.h" #include "tools/editor/io_plugins/editor_mesh_import_plugin.h" +#include "plugins/editor_preview_plugins.h" +#include "script_editor_debugger.h" EditorNode *EditorNode::singleton=NULL; +void EditorNode::_update_scene_tabs() { + + bool show_rb = EditorSettings::get_singleton()->get("global/show_script_in_scene_tabs"); + + scene_tabs->clear_tabs(); + Ref<Texture> script_icon = gui_base->get_icon("Script","EditorIcons"); + for(int i=0;i<editor_data.get_edited_scene_count();i++) { + + String type=editor_data.get_scene_type(i); + Ref<Texture> icon; + if (type!=String()) { + + if (!gui_base->has_icon(type,"EditorIcons")) { + type="Node"; + } + + icon=gui_base->get_icon(type,"EditorIcons"); + + } + + + + int current = editor_data.get_edited_scene(); + bool unsaved = (i==current)?saved_version!=editor_data.get_undo_redo().get_version():editor_data.get_scene_version(i)!=0; + scene_tabs->add_tab(editor_data.get_scene_title(i)+(unsaved?"(*)":""),icon); + + if (show_rb && editor_data.get_scene_root_script(i).is_valid()) { + scene_tabs->set_tab_right_button(i,script_icon); + } + + } + + scene_tabs->set_current_tab(editor_data.get_edited_scene()); + +} + void EditorNode::_update_title() { String appname = Globals::get_singleton()->get("application/name"); String title = appname.empty()?String(VERSION_FULL_NAME):String(_MKSTR(VERSION_NAME) + String(" - ") + appname); - String edited = edited_scene?edited_scene->get_filename():String(); + String edited = editor_data.get_edited_scene_root()?editor_data.get_edited_scene_root()->get_filename():String(); if (!edited.empty()) title+=" - " + String(edited.get_file()); if (unsaved_cache) @@ -120,13 +165,13 @@ void EditorNode::_unhandled_input(const InputEvent& p_event) { switch(p_event.key.scancode) { - case KEY_F1: + /*case KEY_F1: if (!p_event.key.mod.shift && !p_event.key.mod.command) _editor_select(3); - break; - case KEY_F2: _editor_select(0); break; - case KEY_F3: _editor_select(1); break; - case KEY_F4: _editor_select(2); break; + break;*/ + case KEY_F1: _editor_select(0); break; + case KEY_F2: _editor_select(1); break; + case KEY_F3: _editor_select(2); break; case KEY_F5: _menu_option_confirm((p_event.key.mod.control&&p_event.key.mod.shift)?RUN_PLAY_CUSTOM_SCENE:RUN_PLAY,true); break; case KEY_F6: _menu_option_confirm(RUN_PLAY_SCENE,true); break; case KEY_F7: _menu_option_confirm(RUN_PAUSE,true); break; @@ -141,6 +186,7 @@ void EditorNode::_unhandled_input(const InputEvent& p_event) { void EditorNode::_notification(int p_what) { if (p_what==NOTIFICATION_EXIT_TREE) { + editor_data.save_editor_external_data(); log->deinit(); // do not get messages anymore @@ -168,6 +214,11 @@ void EditorNode::_notification(int p_what) { _update_title(); } + if (last_checked_version!=editor_data.get_undo_redo().get_version()) { + _update_scene_tabs(); + last_checked_version=editor_data.get_undo_redo().get_version(); + } + //get_root_node()->set_rect(viewport->get_global_rect()); //update the circle @@ -181,7 +232,7 @@ void EditorNode::_notification(int p_what) { circle_step=0; circle_step_msec=tick; - circle_step_frame=frame+1; + circle_step_frame=frame+1; update_menu->set_icon(gui_base->get_icon("Progress"+itos(circle_step+1),"EditorIcons")); @@ -215,6 +266,7 @@ void EditorNode::_notification(int p_what) { } if (p_what==NOTIFICATION_ENTER_TREE) { + //MessageQueue::get_singleton()->push_call(this,"_get_scene_metadata"); get_tree()->set_editor_hint(true); get_tree()->get_root()->set_as_audio_listener(false); @@ -224,10 +276,19 @@ void EditorNode::_notification(int p_what) { //import_monitor->scan_changes(); } + + if (p_what==NOTIFICATION_EXIT_TREE) { + + + editor_data.clear_edited_scenes(); + + } if (p_what==NOTIFICATION_READY) { VisualServer::get_singleton()->viewport_set_hide_scenario(get_scene_root()->get_viewport(),true); VisualServer::get_singleton()->viewport_set_hide_canvas(get_scene_root()->get_viewport(),true); + VisualServer::get_singleton()->viewport_set_disable_environment(get_viewport()->get_viewport_rid(),true); + _editor_select(1); if (defer_load_scene!="") { @@ -313,6 +374,11 @@ void EditorNode::_fs_changed() { E->get()->invalidate(); } + + for(Set<EditorFileDialog*>::Element *E=editor_file_dialogs.front();E;E=E->next()) { + + E->get()->invalidate(); + } } void EditorNode::_sources_changed(bool p_exist) { @@ -335,6 +401,19 @@ void EditorNode::_vp_resized() { } +void EditorNode::_rebuild_import_menu() +{ + PopupMenu* p = import_menu->get_popup(); + p->clear(); + p->add_item("Sub-Scene", FILE_IMPORT_SUBSCENE); + p->add_separator(); + for (int i = 0; i < editor_import_export->get_import_plugin_count(); i++) { + p->add_item(editor_import_export->get_import_plugin(i)->get_visible_name(), IMPORT_PLUGIN_BASE + i); + } + p->add_separator(); + p->add_item("Re-Import..", SETTINGS_IMPORT); +} + void EditorNode::_node_renamed() { if (property_editor) @@ -366,7 +445,7 @@ void EditorNode::edit_node(Node *p_node) { void EditorNode::open_resource(const String& p_type) { - file->set_mode(FileDialog::MODE_OPEN_FILE); + file->set_mode(EditorFileDialog::MODE_OPEN_FILE); List<String> extensions; ResourceLoader::get_recognized_extensions_for_type(p_type,&extensions); @@ -383,17 +462,74 @@ void EditorNode::open_resource(const String& p_type) { current_option=RESOURCE_LOAD; } -void EditorNode::save_resource(const Ref<Resource>& p_resource) { +void EditorNode::save_resource_in_path(const Ref<Resource>& p_resource,const String& p_path) { + + editor_data.apply_changes_in_editors(); + int flg=0; + if (EditorSettings::get_singleton()->get("on_save/compress_binary_resources")) + flg|=ResourceSaver::FLAG_COMPRESS; + if (EditorSettings::get_singleton()->get("on_save/save_paths_as_relative")) + flg|=ResourceSaver::FLAG_RELATIVE_PATHS; + + String path = Globals::get_singleton()->localize_path(p_path); + Error err = ResourceSaver::save(path,p_resource,flg|ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS); + + if (err!=OK) { + accept->set_text("Error saving resource!"); + accept->popup_centered_minsize(); + return; + } +// EditorFileSystem::get_singleton()->update_file(path,p_resource->get_type()); + + ((Resource*)p_resource.ptr())->set_path(path); + emit_signal("resource_saved",p_resource); - ERR_FAIL_COND(p_resource.is_null() || p_resource->get_path()=="" || p_resource->get_path().find("::")!=-1); - resources_dock->save_resource(p_resource->get_path(),p_resource); +} + +void EditorNode::save_resource(const Ref<Resource>& p_resource) { + + if (p_resource->get_path().is_resource_file()) { + save_resource_in_path(p_resource,p_resource->get_path()); + } else { + save_resource_as(p_resource); + } } void EditorNode::save_resource_as(const Ref<Resource>& p_resource) { - resources_dock->save_resource_as(p_resource); + file->set_mode(EditorFileDialog::MODE_SAVE_FILE); + bool relpaths = (p_resource->has_meta("__editor_relpaths__") && p_resource->get_meta("__editor_relpaths__").operator bool()); + + List<String> extensions; + Ref<PackedScene> sd = memnew( PackedScene ); + ResourceSaver::get_recognized_extensions(p_resource,&extensions); + file->clear_filters(); + for(int i=0;i<extensions.size();i++) { + file->add_filter("*."+extensions[i]+" ; "+extensions[i].to_upper()); + } + + //file->set_current_path(current_path); + if (p_resource->get_path()!="") { + file->set_current_path(p_resource->get_path()); + if (extensions.size()) { + String ext=p_resource->get_path().extension().to_lower(); + if (extensions.find(ext)==NULL) { + file->set_current_path(p_resource->get_path().replacen("."+ext,"."+extensions.front()->get())); + } + } + } else { + + String existing; + if (extensions.size()) { + existing="new_"+p_resource->get_type().to_lower()+"."+extensions.front()->get().to_lower(); + } + file->set_current_path(existing); + + } + file->popup_centered_ratio(); + file->set_title("Save Resource As.."); } @@ -434,14 +570,14 @@ void EditorNode::_dialog_display_file_error(String p_file,Error p_error) { }break; } - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); } } void EditorNode::_get_scene_metadata() { - Node *scene = edited_scene; + Node *scene = editor_data.get_edited_scene_root(); if (!scene) return; @@ -468,7 +604,7 @@ void EditorNode::_get_scene_metadata() { void EditorNode::_set_scene_metadata() { - Node *scene = edited_scene; + Node *scene = editor_data.get_edited_scene_root(); if (!scene) return; @@ -590,7 +726,42 @@ static Error _fix_imported_scene_paths(Node* node, Node* root, String save_path) }; -bool EditorNode::_find_and_save_edited_subresources(Object *obj,Set<RES>& processed,int32_t flags) { +bool EditorNode::_find_and_save_resource(RES res,Map<RES,bool>& processed,int32_t flags) { + + if (res.is_null()) + return false; + + if (processed.has(res)) { + + return processed[res]; + } + + + bool changed = res->is_edited(); + res->set_edited(false); + + bool subchanged = _find_and_save_edited_subresources(res.ptr(),processed,flags); + +// print_line("checking if edited: "+res->get_type()+" :: "+res->get_name()+" :: "+res->get_path()+" :: "+itos(changed)+" :: SR "+itos(subchanged)); + + if (res->get_path().is_resource_file()) { + if (changed || subchanged) { + //save + print_line("Also saving modified external resource: "+res->get_path()); + Error err = ResourceSaver::save(res->get_path(),res,flags); + + } + processed[res]=false; //because it's a file + return false; + } else { + + + processed[res]=changed; + return changed; + } +} + +bool EditorNode::_find_and_save_edited_subresources(Object *obj,Map<RES,bool>& processed,int32_t flags) { bool ret_changed=false; List<PropertyInfo> pi; @@ -600,57 +771,45 @@ bool EditorNode::_find_and_save_edited_subresources(Object *obj,Set<RES>& proces if (!(E->get().usage&PROPERTY_USAGE_STORAGE)) continue; + + switch(E->get().type) { case Variant::OBJECT: { RES res = obj->get(E->get().name); - if (res.is_null() || processed.has(res)) - break; - - processed.insert(res); - - bool changed = res->is_edited(); - res->set_edited(false); - - bool subchanged = _find_and_save_edited_subresources(res.ptr(),processed,flags); - - if (res->get_path().is_resource_file()) { - if (changed || subchanged) { - //save - print_line("Also saving modified external resource: "+res->get_path()); - Error err = ResourceSaver::save(res->get_path(),res,flags); - - } - } else { - + if (_find_and_save_resource(res,processed,flags)) ret_changed=true; - } - } break; case Variant::ARRAY: { - /*Array varray=p_variant; + Array varray= obj->get(E->get().name); int len=varray.size(); for(int i=0;i<len;i++) { Variant v=varray.get(i); - _find_resources(v); - }*/ + RES res=v; + if (_find_and_save_resource(res,processed,flags)) + ret_changed=true; + + //_find_resources(v); + } } break; case Variant::DICTIONARY: { - /* - Dictionary d=p_variant; + + Dictionary d=obj->get(E->get().name);; List<Variant> keys; d.get_key_list(&keys); for(List<Variant>::Element *E=keys.front();E;E=E->next()) { Variant v = d[E->get()]; - _find_resources(v); - } */ + RES res=v; + if (_find_and_save_resource(res,processed,flags)) + ret_changed=true; + } } break; default: {} } @@ -661,23 +820,113 @@ bool EditorNode::_find_and_save_edited_subresources(Object *obj,Set<RES>& proces } -void EditorNode::_save_edited_subresources(Node* scene,Set<RES>& processed,int32_t flags) { +void EditorNode::_save_edited_subresources(Node* scene,Map<RES,bool>& processed,int32_t flags) { _find_and_save_edited_subresources(scene,processed,flags); for(int i=0;i<scene->get_child_count();i++) { Node *n = scene->get_child(i); - if (n->get_owner()!=edited_scene) + if (n->get_owner()!=editor_data.get_edited_scene_root()) continue; _save_edited_subresources(n,processed,flags); } } +void EditorNode::_find_node_types(Node* p_node, int&count_2d, int&count_3d) { + + if (p_node->is_type("Viewport") || (p_node!=editor_data.get_edited_scene_root() && p_node->get_owner()!=editor_data.get_edited_scene_root())) + return; + + if (p_node->is_type("CanvasItem")) + count_2d++; + else if (p_node->is_type("Spatial")) + count_3d++; + + for(int i=0;i<p_node->get_child_count();i++) + _find_node_types(p_node->get_child(i),count_2d,count_3d); + +} + + +void EditorNode::_save_scene_with_preview(String p_file) { + + int c2d=0; + int c3d=0; + + EditorProgress save("save","Saving Scene",4); + save.step("Analyzing",0); + _find_node_types(editor_data.get_edited_scene_root(),c2d,c3d); + + RID viewport; + bool is2d; + if (c3d<c2d) { + viewport=scene_root->get_viewport(); + is2d=true; + } else { + viewport=SpatialEditor::get_singleton()->get_editor_viewport(0)->get_viewport_node()->get_viewport(); + is2d=false; + + } + save.step("Creating Thumbnail",1); + //current view? + int screen =-1; + for(int i=0;i<editor_table.size();i++) { + if (editor_plugin_screen==editor_table[i]) { + screen=i; + break; + } + } + + _editor_select(is2d?0:1); + + VS::get_singleton()->viewport_queue_screen_capture(viewport); + save.step("Creating Thumbnail",2); + save.step("Creating Thumbnail",3); + Image img = VS::get_singleton()->viewport_get_screen_capture(viewport); + int preview_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");; + int width,height; + if (img.get_width() > preview_size && img.get_width() >= img.get_height()) { + + width=preview_size; + height = img.get_height() * preview_size / img.get_width(); + } else if (img.get_height() > preview_size && img.get_height() >= img.get_width()) { + + height=preview_size; + width = img.get_width() * preview_size / img.get_height(); + } else { + + width=img.get_width(); + height=img.get_height(); + } + + img.convert(Image::FORMAT_RGB); + img.resize(width,height); + + String pfile = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp/last_scene_preview.png"); + img.save_png(pfile); + Vector<uint8_t> imgdata = FileAccess::get_file_as_array(pfile); + + //print_line("img data is "+itos(imgdata.size())); + + if (editor_data.get_edited_scene_import_metadata().is_null()) + editor_data.set_edited_scene_import_metadata(Ref<ResourceImportMetadata>( memnew( ResourceImportMetadata ) ) ); + editor_data.get_edited_scene_import_metadata()->set_option("thumbnail",imgdata); + + //tamanio tel thumbnail + if (screen!=-1) { + _editor_select(screen); + } + save.step("Saving Scene",4); + _save_scene(p_file); + +} + + void EditorNode::_save_scene(String p_file) { - Node *scene = edited_scene; + Node *scene = editor_data.get_edited_scene_root(); if (!scene) { @@ -685,7 +934,7 @@ void EditorNode::_save_scene(String p_file) { //accept->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("This operation can't be done without a tree root."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); return; } @@ -707,11 +956,11 @@ void EditorNode::_save_scene(String p_file) { //accept->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("Couldn't save scene. Likely dependencies (instances) couldn't be satisfied."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); return; } - sdata->set_import_metadata(scene_import_metadata); + sdata->set_import_metadata(editor_data.get_edited_scene_import_metadata()); int flg=0; if (EditorSettings::get_singleton()->get("on_save/compress_binary_resources")) flg|=ResourceSaver::FLAG_COMPRESS; @@ -721,14 +970,15 @@ void EditorNode::_save_scene(String p_file) { err = ResourceSaver::save(p_file,sdata,flg); - Set<RES> processed; + Map<RES,bool> processed; _save_edited_subresources(scene,processed,flg); editor_data.save_editor_external_data(); if (err==OK) { scene->set_filename( Globals::get_singleton()->localize_path(p_file) ); //EditorFileSystem::get_singleton()->update_file(p_file,sdata->get_type()); - saved_version=editor_data.get_undo_redo().get_version(); + set_current_version(editor_data.get_undo_redo().get_version()); _update_title(); + _update_scene_tabs(); } else { _dialog_display_file_error(p_file,err); @@ -860,6 +1110,11 @@ void EditorNode::_dialog_action(String p_file) { push_item(res.operator->() ); + } break; + case FILE_NEW_INHERITED_SCENE: { + + + load_scene(p_file,false,true); } break; case FILE_OPEN_SCENE: { @@ -908,14 +1163,14 @@ void EditorNode::_dialog_action(String p_file) { //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("This operation requieres a single selected node."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } Node *base = selection.front()->get(); Map<Node*,Node*> reown; - reown[get_edited_scene()]=base; + reown[editor_data.get_edited_scene_root()]=base; Node *copy = base->duplicate_and_reown(reown); if (copy) { @@ -930,7 +1185,7 @@ void EditorNode::_dialog_action(String p_file) { //accept->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("Couldn't save subscene. Likely dependencies (instances) couldn't be satisfied."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); return; } @@ -948,7 +1203,7 @@ void EditorNode::_dialog_action(String p_file) { //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("Error saving scene."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } //EditorFileSystem::get_singleton()->update_file(p_file,sdata->get_type()); @@ -959,7 +1214,7 @@ void EditorNode::_dialog_action(String p_file) { //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("Error duplicating scene to save it."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } @@ -973,10 +1228,22 @@ void EditorNode::_dialog_action(String p_file) { if (file->get_mode()==FileDialog::MODE_SAVE_FILE) { - _save_scene(p_file); + //_save_scene(p_file); + _save_scene_with_preview(p_file); + } } break; + + case FILE_SAVE_AND_RUN: { + if (file->get_mode()==FileDialog::MODE_SAVE_FILE) { + + //_save_scene(p_file); + _save_scene_with_preview(p_file); + _run(false); + } + } break; + case FILE_EXPORT_MESH_LIBRARY: { Ref<MeshLibrary> ml; @@ -988,7 +1255,7 @@ void EditorNode::_dialog_action(String p_file) { //accept->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("Can't load MeshLibrary for merging!."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); return; } @@ -998,14 +1265,14 @@ void EditorNode::_dialog_action(String p_file) { ml = Ref<MeshLibrary>( memnew( MeshLibrary )); } - MeshLibraryEditor::update_library_file(edited_scene,ml,true); + MeshLibraryEditor::update_library_file(editor_data.get_edited_scene_root(),ml,true); Error err = ResourceSaver::save(p_file,ml); if (err) { accept->get_ok()->set_text("I see.."); accept->set_text("Error saving MeshLibrary!."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); return; } @@ -1022,7 +1289,7 @@ void EditorNode::_dialog_action(String p_file) { //accept->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("Can't load TileSet for merging!."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); return; } @@ -1032,14 +1299,14 @@ void EditorNode::_dialog_action(String p_file) { ml = Ref<TileSet>( memnew( TileSet )); } - TileSetEditor::update_library_file(edited_scene,ml,true); + TileSetEditor::update_library_file(editor_data.get_edited_scene_root(),ml,true); Error err = ResourceSaver::save(p_file,ml); if (err) { accept->get_ok()->set_text("I see.."); accept->set_text("Error saving TileSet!."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); return; } } break; @@ -1056,7 +1323,7 @@ void EditorNode::_dialog_action(String p_file) { //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("Can't open export templates zip."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); return; } @@ -1119,13 +1386,26 @@ void EditorNode::_dialog_action(String p_file) { unzClose(pkg); } break; + case RESOURCE_SAVE: + case RESOURCE_SAVE_AS: { + + + uint32_t current = editor_history.get_current(); + Object *current_obj = current>0 ? ObjectDB::get_instance(current) : NULL; + + ERR_FAIL_COND(!current_obj->cast_to<Resource>()) + RES current_res = RES(current_obj->cast_to<Resource>()); + + save_resource_in_path(current_res,p_file); + + } break; default: { //save scene? - if (file->get_mode()==FileDialog::MODE_SAVE_FILE) { - _save_scene(p_file); + //_save_scene(p_file); + _save_scene_with_preview(p_file); } } break; @@ -1156,6 +1436,67 @@ void EditorNode::push_item(Object *p_object,const String& p_property) { } +void EditorNode::_select_history(int p_idx) { + + //push it to the top, it is not correct, but it's more useful + ObjectID id=editor_history.get_history_obj(p_idx); + Object* obj=ObjectDB::get_instance(id); + if (!obj) + return; + push_item(obj); +} + +void EditorNode::_prepare_history() { + + int history_to = MAX(0,editor_history.get_history_len()-25); + + editor_history_menu->get_popup()->clear(); + + Ref<Texture> base_icon = gui_base->get_icon("Object","EditorIcons"); + Set<ObjectID> already; + for(int i=editor_history.get_history_len()-1;i>=history_to;i--) { + + + ObjectID id=editor_history.get_history_obj(i); + Object* obj=ObjectDB::get_instance(id); + if (!obj || already.has(id)) { + if (history_to>0) { + history_to--; + } + continue; + } + + already.insert(id); + + Ref<Texture> icon = gui_base->get_icon("Object","EditorIcons"); + if (gui_base->has_icon(obj->get_type(),"EditorIcons")) + icon=gui_base->get_icon(obj->get_type(),"EditorIcons"); + else + icon=base_icon; + + String text; + if (obj->cast_to<Resource>()) { + Resource *r=obj->cast_to<Resource>(); + if (r->get_path().is_resource_file()) + text=r->get_path().get_file(); + else if (r->get_name()!=String()) { + text=r->get_name(); + } else { + text=r->get_type(); + } + } else if (obj->cast_to<Node>()) { + text=obj->cast_to<Node>()->get_name(); + } else { + text=obj->get_type(); + } + + if (i==editor_history.get_history_pos()) { + text="["+text+"]"; + } + editor_history_menu->get_popup()->add_icon_item(icon,text,i); + } +} + void EditorNode::_property_editor_forward() { if (editor_history.next()) @@ -1172,15 +1513,15 @@ void EditorNode::_property_editor_back() { void EditorNode::_imported(Node *p_node) { - Node *scene = edited_scene; - set_edited_scene(p_node); - + Node *scene = editor_data.get_edited_scene_root(); +// add_edited_scene(p_node); +/* if (scene) { String path = scene->get_filename(); p_node->set_filename(path); memdelete(scene); } - +*/ } @@ -1200,6 +1541,9 @@ void EditorNode::_edit_current() { uint32_t current = editor_history.get_current(); Object *current_obj = current>0 ? ObjectDB::get_instance(current) : NULL; + property_back->set_disabled( editor_history.is_at_begining() ); + property_forward->set_disabled( editor_history.is_at_end() ); + this->current=current_obj; editor_path->update_path(); @@ -1214,7 +1558,10 @@ void EditorNode::_edit_current() { object_menu->set_disabled(true); - if (current_obj->is_type("Resource")) { + bool is_resource = current_obj->is_type("Resource"); + resource_save_button->set_disabled(!is_resource); + + if (is_resource) { Resource *current_res = current_obj->cast_to<Resource>(); @@ -1223,12 +1570,11 @@ void EditorNode::_edit_current() { property_editor->edit( current_res ); object_menu->set_disabled(false); - resources_dock->add_resource(Ref<Resource>(current_res)); - top_pallete->set_current_tab(1); - } + //resources_dock->add_resource(Ref<Resource>(current_res)); - if (current_obj->is_type("Node")) { + //top_pallete->set_current_tab(1); + } else if (current_obj->is_type("Node")) { Node * current_node = current_obj->cast_to<Node>(); ERR_FAIL_COND(!current_node); @@ -1241,7 +1587,13 @@ void EditorNode::_edit_current() { scene_tree_dock->set_selected(current_node); object_menu->get_popup()->clear(); - top_pallete->set_current_tab(0); + //top_pallete->set_current_tab(0); + + } else { + + property_editor->edit( current_obj ); + //scene_tree_dock->set_selected(current_node); + //object_menu->get_popup()->clear(); } @@ -1255,17 +1607,20 @@ void EditorNode::_edit_current() { if (main_plugin!=editor_plugin_screen) { // update screen main_plugin - if (editor_plugin_screen) - editor_plugin_screen->make_visible(false); - editor_plugin_screen=main_plugin; - editor_plugin_screen->edit(current_obj); - editor_plugin_screen->make_visible(true); + if (!changing_scene) { - for(int i=0;i<editor_table.size();i++) { - if (editor_table[i]==main_plugin) { - main_editor_tabs->set_current_tab(i); - break; + if (editor_plugin_screen) + editor_plugin_screen->make_visible(false); + editor_plugin_screen=main_plugin; + editor_plugin_screen->edit(current_obj); + + editor_plugin_screen->make_visible(true); + + + for(int i=0;i<editor_table.size();i++) { + + main_editor_buttons[i]->set_pressed(editor_table[i]==main_plugin); } } @@ -1310,7 +1665,13 @@ void EditorNode::_edit_current() { p->add_item("Copy Params",OBJECT_COPY_PARAMS); p->add_item("Set Params",OBJECT_PASTE_PARAMS); p->add_separator(); - p->add_item("Make Resources Unique",OBJECT_UNIQUE_RESOURCES); + p->add_item("Paste Resource",RESOURCE_PASTE); + if (is_resource) { + p->add_item("Copy Resource",RESOURCE_COPY); + p->add_item("Make Built-In",RESOURCE_UNREF); + } + p->add_separator(); + p->add_item("Make Sub-Resources Unique",OBJECT_UNIQUE_RESOURCES); p->add_separator(); p->add_icon_item(gui_base->get_icon("Help","EditorIcons"),"Class Reference",OBJECT_REQUEST_HELP); List<MethodInfo> methods; @@ -1343,6 +1704,20 @@ void EditorNode::_edit_current() { _update_keying(); } +void EditorNode::_resource_created() { + + Object *c = create_dialog->instance_selected(); + + ERR_FAIL_COND(!c); + Resource *r = c->cast_to<Resource>(); + ERR_FAIL_COND(!r); + + REF res( r ); + + push_item(c); + +} + void EditorNode::_resource_selected(const RES& p_res,const String& p_property) { @@ -1365,8 +1740,10 @@ void EditorNode::_run(bool p_current,const String& p_custom) { } play_button->set_pressed(false); - pause_button->set_pressed(false); + play_button->set_icon(gui_base->get_icon("Play","EditorIcons")); + //pause_button->set_pressed(false); play_scene_button->set_pressed(false); + play_scene_button->set_icon(gui_base->get_icon("PlayScene","EditorIcons")); String current_filename; String run_filename; @@ -1374,29 +1751,25 @@ void EditorNode::_run(bool p_current,const String& p_custom) { - if (p_current || (edited_scene && p_custom==edited_scene->get_filename())) { + if (p_current || (editor_data.get_edited_scene_root() && p_custom==editor_data.get_edited_scene_root()->get_filename())) { - Node *scene = edited_scene; + Node *scene = editor_data.get_edited_scene_root(); if (!scene) { - current_option=-1; //accept->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("No scene to run exists."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); return; } if (scene->get_filename()=="") { - - current_option=-1; //accept->get_cancel()->hide(); - accept->get_ok()->set_text("I see.."); - accept->set_text("Scene has never been saved. Save before running!"); - accept->popup_centered(Size2(300,70));; + /**/ + _menu_option_confirm(FILE_SAVE_BEFORE_RUN, false); return; } @@ -1431,7 +1804,7 @@ void EditorNode::_run(bool p_current,const String& p_custom) { //accept->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("No main scene has ever been defined.\nSelect one from \"Project Settings\" under the 'application' category."); - accept->popup_centered(Size2(300,100));; + accept->popup_centered_minsize(); return; } @@ -1442,7 +1815,7 @@ void EditorNode::_run(bool p_current,const String& p_custom) { if (unsaved_cache) { - Node *scene = edited_scene; + Node *scene = editor_data.get_edited_scene_root(); if (scene) { //only autosave if there is a scene obviously @@ -1452,17 +1825,22 @@ void EditorNode::_run(bool p_current,const String& p_custom) { //accept->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("Current scene was never saved, please save scene before running."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); return; } - _save_scene(scene->get_filename()); + //_save_scene(scene->get_filename()); + _save_scene_with_preview(scene->get_filename()); } } editor_data.save_editor_external_data(); } + if (bool(EDITOR_DEF("run/always_clear_output_on_play", true))) { + log->clear(); + } + List<String> breakpoints; editor_data.get_editor_breakpoints(&breakpoints); @@ -1475,7 +1853,7 @@ void EditorNode::_run(bool p_current,const String& p_custom) { //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("Could not start subprocess!"); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); return; } @@ -1483,8 +1861,10 @@ void EditorNode::_run(bool p_current,const String& p_custom) { emit_signal("play_pressed"); if (p_current) { play_scene_button->set_pressed(true); + play_scene_button->set_icon(gui_base->get_icon("Reload","EditorIcons")); } else { play_button->set_pressed(true); + play_button->set_icon(gui_base->get_icon("Reload","EditorIcons")); } _playing_edited=p_current; @@ -1493,8 +1873,8 @@ void EditorNode::_run(bool p_current,const String& p_custom) { void EditorNode::_cleanup_scene() { - - Node *scene = edited_scene; +#if 0 + Node *scene = editor_data.get_edited_scene_root(); editor_selection->clear(); editor_data.clear_editor_states(); editor_history.clear(); @@ -1503,7 +1883,7 @@ void EditorNode::_cleanup_scene() { property_editor->edit(NULL); resources_dock->cleanup(); scene_import_metadata.unref(); - set_edited_scene(NULL); + //set_edited_scene(NULL); if (scene) { if (scene->get_filename()!="") { previous_scenes.push_back(scene->get_filename()); @@ -1529,35 +1909,43 @@ void EditorNode::_cleanup_scene() { } _update_title(); - +#endif } void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { - current_option=(MenuOptions)p_option; + //print_line("option "+itos(p_option)+" confirm "+itos(p_confirmed)); + if (!p_confirmed) //this may be a hack.. + current_option=(MenuOptions)p_option; switch( p_option ) { case FILE_NEW_SCENE: { + /* if (!p_confirmed) { confirmation->get_ok()->set_text("Yes"); //confirmation->get_cancel()->show(); confirmation->set_text("Start a New Scene? (Current will be lost)"); - confirmation->popup_centered(Size2(300,70)); + confirmation->popup_centered_minsize(); break; - } + }*/ + + int idx = editor_data.add_edited_scene(-1); + _scene_tab_changed(idx); + editor_data.clear_editor_states(); - _cleanup_scene(); + //_cleanup_scene(); } break; + case FILE_NEW_INHERITED_SCENE: case FILE_OPEN_SCENE: { //print_tree(); - file->set_mode(FileDialog::MODE_OPEN_FILE); + file->set_mode(EditorFileDialog::MODE_OPEN_FILE); //not for now? List<String> extensions; ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions); @@ -1569,11 +1957,11 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { //file->set_current_path(current_path); - Node *scene = edited_scene; + Node *scene = editor_data.get_edited_scene_root(); if (scene) { file->set_current_path(scene->get_filename()); }; - file->set_title("Open Scene"); + file->set_title(p_option==FILE_OPEN_SCENE?"Open Scene":"Open Base Scene"); file->popup_centered_ratio(); } break; @@ -1590,6 +1978,13 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { quick_open->set_title("Quick Open Script.."); } break; + case FILE_QUICK_OPEN_FILE: { + + + quick_open->popup("Resource",false,true); + quick_open->set_title("Quick Search File.."); + + } break; case FILE_RUN_SCRIPT: { file_script->popup_centered_ratio(); @@ -1602,20 +1997,41 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { open_request(previous_scenes.back()->get()); } break; + case FILE_CLOSE: { + + if (!p_confirmed) { + confirmation->get_ok()->set_text("Yes"); + //confirmation->get_cancel()->show(); + confirmation->set_text("Close scene? (Unsaved changes will be lost)"); + confirmation->popup_centered_minsize(); + break; + } + + _remove_edited_scene(); + + + + } break; + case SCENE_TAB_CLOSE: { + _remove_scene(tab_closing); + _update_scene_tabs(); + current_option = -1; + } break; case FILE_SAVE_SCENE: { - Node *scene = edited_scene; + Node *scene = editor_data.get_edited_scene_root(); if (scene && scene->get_filename()!="") { - _save_scene(scene->get_filename()); + //_save_scene(scene->get_filename()); + _save_scene_with_preview(scene->get_filename()); return; }; // fallthrough to save_as - }; + } break; case FILE_SAVE_AS_SCENE: { - Node *scene = edited_scene; + Node *scene = editor_data.get_edited_scene_root(); if (!scene) { @@ -1623,11 +2039,11 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("This operation can't be done without a tree root."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } - file->set_mode(FileDialog::MODE_SAVE_FILE); + file->set_mode(EditorFileDialog::MODE_SAVE_FILE); bool relpaths = (scene->has_meta("__editor_relpaths__") && scene->get_meta("__editor_relpaths__").operator bool()); @@ -1663,9 +2079,21 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { } break; + case FILE_SAVE_BEFORE_RUN: { + if (!p_confirmed) { + accept->get_ok()->set_text("Yes"); + accept->set_text("This scene has never been saved. Save before running?"); + accept->popup_centered_minsize(); + break; + } + + _menu_option(FILE_SAVE_AS_SCENE); + _menu_option_confirm(FILE_SAVE_AND_RUN, true); + } break; + case FILE_DUMP_STRINGS: { - Node *scene = edited_scene; + Node *scene = editor_data.get_edited_scene_root(); if (!scene) { @@ -1673,7 +2101,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("This operation can't be done without a tree root."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } @@ -1691,14 +2119,14 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("Please save the scene first."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } bool relpaths = (scene->has_meta("__editor_relpaths__") && scene->get_meta("__editor_relpaths__").operator bool()); - file->set_mode(FileDialog::MODE_SAVE_FILE); + file->set_mode(EditorFileDialog::MODE_SAVE_FILE); file->set_current_path(cpath); file->set_title("Save Translatable Strings"); @@ -1709,7 +2137,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { case FILE_SAVE_SUBSCENE: { - Node *scene = edited_scene; + Node *scene = editor_data.get_edited_scene_root(); if (!scene) { @@ -1717,7 +2145,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("This operation can't be done without a scene."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } @@ -1730,24 +2158,24 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("This operation requieres a single selected node."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } Node *tocopy = selection.front()->get(); - if (tocopy!=edited_scene && tocopy->get_filename()!="") { + if (tocopy!=editor_data.get_edited_scene_root() && tocopy->get_filename()!="") { current_option=-1; //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("This operation can't be done on instanced scenes."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } - file->set_mode(FileDialog::MODE_SAVE_FILE); + file->set_mode(EditorFileDialog::MODE_SAVE_FILE); List<String> extensions; Ref<PackedScene> sd = memnew( PackedScene ); @@ -1770,7 +2198,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { file->set_title("Save Sub-Scene As.."); } break; case FILE_SAVE_OPTIMIZED: { - Node *scene = edited_scene; + Node *scene = editor_data.get_edited_scene_root(); #if 0 if (!scene) { @@ -1832,13 +2260,13 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { case FILE_EXPORT_MESH_LIBRARY: { - if (!edited_scene) { + if (!editor_data.get_edited_scene_root()) { current_option=-1; //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("This operation can't be done without a scene."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } @@ -1877,13 +2305,13 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { //import_subscene->popup_centered_ratio(); - if (!edited_scene) { + if (!editor_data.get_edited_scene_root()) { current_option=-1; //accept->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("This operation can't be done without a selected node."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } @@ -1893,14 +2321,17 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { case FILE_QUIT: { + if (!p_confirmed) { + confirmation->get_ok()->set_text("Quit"); //confirmation->get_cancel()->show(); confirmation->set_text("Exit the Editor?"); - confirmation->popup_centered(Size2(300,70)); + confirmation->popup_centered(Size2(180,70)); break; } + _menu_option_confirm(RUN_STOP,true); get_tree()->quit(); @@ -1912,7 +2343,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { confirmation->get_ok()->set_text("Open"); //confirmation->get_cancel()->show(); confirmation->set_text("Current scene not saved. Open anyway?"); - confirmation->popup_centered(Size2(300,70)); + confirmation->popup_centered_minsize(); break; } @@ -1948,6 +2379,25 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { log->add_message("REDO: "+action); } break; + + case EDIT_REVERT: { + + Node *scene = get_edited_scene(); + + if (!scene) + break; + + if (unsaved_cache && !p_confirmed) { + confirmation->get_ok()->set_text("Revert"); + confirmation->set_text("This action cannot be undone. Revert anyway?"); + confirmation->popup_centered_minsize(); + break; + } + + Error err = load_scene(scene->get_filename()); + + } break; + #if 0 case NODE_EXTERNAL_INSTANCE: { @@ -2002,7 +2452,70 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { } break; #endif - + case RESOURCE_NEW: { + + create_dialog->popup_centered_ratio(); + } break; + case RESOURCE_LOAD: { + + open_resource(); + } break; + case RESOURCE_SAVE: { + + + uint32_t current = editor_history.get_current(); + Object *current_obj = current>0 ? ObjectDB::get_instance(current) : NULL; + + ERR_FAIL_COND(!current_obj->cast_to<Resource>()) + + RES current_res = RES(current_obj->cast_to<Resource>()); + + save_resource(current_res); + + } break; + case RESOURCE_SAVE_AS: { + + uint32_t current = editor_history.get_current(); + Object *current_obj = current>0 ? ObjectDB::get_instance(current) : NULL; + + ERR_FAIL_COND(!current_obj->cast_to<Resource>()) + + RES current_res = RES(current_obj->cast_to<Resource>()); + + save_resource_as(current_res); + + } break; + case RESOURCE_UNREF: { + + uint32_t current = editor_history.get_current(); + Object *current_obj = current>0 ? ObjectDB::get_instance(current) : NULL; + + ERR_FAIL_COND(!current_obj->cast_to<Resource>()) + + RES current_res = RES(current_obj->cast_to<Resource>()); + current_res->set_path(""); + _edit_current(); + } break; + case RESOURCE_COPY: { + + uint32_t current = editor_history.get_current(); + Object *current_obj = current>0 ? ObjectDB::get_instance(current) : NULL; + + ERR_FAIL_COND(!current_obj->cast_to<Resource>()) + + RES current_res = RES(current_obj->cast_to<Resource>()); + + EditorSettings::get_singleton()->set_resource_clipboard(current_res); + + } break; + case RESOURCE_PASTE: { + + RES r = EditorSettings::get_singleton()->get_resource_clipboard(); + if (r.is_valid()) { + push_item(EditorSettings::get_singleton()->get_resource_clipboard().ptr(),String()); + } + + } break; case OBJECT_REQUEST_HELP: { if (current) { @@ -2060,7 +2573,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { } editor_data.get_undo_redo().clear_history(); - if (editor_plugin_screen) { //reload editor plugin + if (editor_plugin_over) { //reload editor plugin editor_plugin_over->edit(NULL); editor_plugin_over->edit(current); } @@ -2073,12 +2586,12 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { call_dialog->popup_centered_ratio(); } break; case RUN_PLAY: { - + _menu_option_confirm(RUN_STOP,true); _run(false); } break; case RUN_PLAY_CUSTOM_SCENE: { - + _menu_option_confirm(RUN_STOP,true); quick_run->popup("PackedScene",true); quick_run->set_title("Quick Run Scene.."); @@ -2095,16 +2608,25 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { editor_run.stop(); play_button->set_pressed(false); + play_button->set_icon(gui_base->get_icon("Play","EditorIcons")); play_scene_button->set_pressed(false); - pause_button->set_pressed(false); + play_scene_button->set_icon(gui_base->get_icon("PlayScene","EditorIcons")); + //pause_button->set_pressed(false); emit_signal("stop_pressed"); } break; case RUN_PLAY_SCENE: { - + _menu_option_confirm(RUN_STOP,true); _run(true); } break; + case RUN_PLAY_NATIVE: { + _menu_option_confirm(RUN_STOP,true); + emit_signal("play_pressed"); + editor_run.run_native_notify(); + + + } break; case RUN_SCENE_SETTINGS: { run_settings_dialog->popup_run_settings(); @@ -2118,7 +2640,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { if (!p_confirmed) { confirmation->get_ok()->set_text("Yes"); confirmation->set_text("Open Project Manager? \n(Unsaved changes will be lost)"); - confirmation->popup_centered(Size2(300,70)); + confirmation->popup_centered_minsize(); break; } @@ -2136,28 +2658,57 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { case RUN_FILE_SERVER: { //file_server - bool ischecked = fileserver_menu->get_popup()->is_item_checked( fileserver_menu->get_popup()->get_item_index(RUN_FILE_SERVER)); + bool ischecked = debug_button->get_popup()->is_item_checked( debug_button->get_popup()->get_item_index(RUN_FILE_SERVER)); if (ischecked) { file_server->stop(); - fileserver_menu->set_icon(gui_base->get_icon("FileServer","EditorIcons")); - fileserver_menu->get_popup()->set_item_text( fileserver_menu->get_popup()->get_item_index(RUN_FILE_SERVER),"Enable File Server"); + //debug_button->set_icon(gui_base->get_icon("FileServer","EditorIcons")); + //debug_button->get_popup()->set_item_text( debug_button->get_popup()->get_item_index(RUN_FILE_SERVER),"Enable File Server"); } else { file_server->start(); - fileserver_menu->set_icon(gui_base->get_icon("FileServerActive","EditorIcons")); - fileserver_menu->get_popup()->set_item_text( fileserver_menu->get_popup()->get_item_index(RUN_FILE_SERVER),"Disable File Server"); + //debug_button->set_icon(gui_base->get_icon("FileServerActive","EditorIcons")); + //debug_button->get_popup()->set_item_text( debug_button->get_popup()->get_item_index(RUN_FILE_SERVER),"Disable File Server"); } - fileserver_menu->get_popup()->set_item_checked( fileserver_menu->get_popup()->get_item_index(RUN_FILE_SERVER),!ischecked); + debug_button->get_popup()->set_item_checked( debug_button->get_popup()->get_item_index(RUN_FILE_SERVER),!ischecked); + + } break; + case RUN_LIVE_DEBUG: { + + bool ischecked = debug_button->get_popup()->is_item_checked( debug_button->get_popup()->get_item_index(RUN_LIVE_DEBUG)); + debug_button->get_popup()->set_item_checked( debug_button->get_popup()->get_item_index(RUN_LIVE_DEBUG),!ischecked); + ScriptEditor::get_singleton()->get_debugger()->set_live_debugging(!ischecked); } break; + case RUN_DEPLOY_DUMB_CLIENTS: { - bool ischecked = fileserver_menu->get_popup()->is_item_checked( fileserver_menu->get_popup()->get_item_index(RUN_DEPLOY_DUMB_CLIENTS)); - fileserver_menu->get_popup()->set_item_checked( fileserver_menu->get_popup()->get_item_index(RUN_DEPLOY_DUMB_CLIENTS),!ischecked); + bool ischecked = debug_button->get_popup()->is_item_checked( debug_button->get_popup()->get_item_index(RUN_DEPLOY_DUMB_CLIENTS)); + debug_button->get_popup()->set_item_checked( debug_button->get_popup()->get_item_index(RUN_DEPLOY_DUMB_CLIENTS),!ischecked); run_native->set_deploy_dumb(!ischecked); } break; + case RUN_DEPLOY_REMOTE_DEBUG: { + + bool ischecked = debug_button->get_popup()->is_item_checked( debug_button->get_popup()->get_item_index(RUN_DEPLOY_REMOTE_DEBUG)); + debug_button->get_popup()->set_item_checked( debug_button->get_popup()->get_item_index(RUN_DEPLOY_REMOTE_DEBUG),!ischecked); + run_native->set_deploy_debug_remote(!ischecked); + + } break; + case RUN_DEBUG_COLLISONS: { + + bool ischecked = debug_button->get_popup()->is_item_checked( debug_button->get_popup()->get_item_index(RUN_DEBUG_COLLISONS)); + debug_button->get_popup()->set_item_checked( debug_button->get_popup()->get_item_index(RUN_DEBUG_COLLISONS),!ischecked); + run_native->set_debug_collisions(!ischecked); + editor_run.set_debug_collisions(!ischecked); + } break; + case RUN_DEBUG_NAVIGATION: { + + bool ischecked = debug_button->get_popup()->is_item_checked( debug_button->get_popup()->get_item_index(RUN_DEBUG_NAVIGATION)); + debug_button->get_popup()->set_item_checked( debug_button->get_popup()->get_item_index(RUN_DEBUG_NAVIGATION),!ischecked); + run_native->set_debug_navigation(!ischecked); + editor_run.set_debug_navigation(!ischecked); + } break; case SETTINGS_UPDATE_ALWAYS: { update_menu->get_popup()->set_item_checked(0,true); @@ -2306,7 +2857,7 @@ Control* EditorNode::get_viewport() { void EditorNode::_editor_select(int p_which) { static bool selecting=false; - if (selecting) + if (selecting || changing_scene) return; selecting=true; @@ -2314,7 +2865,9 @@ void EditorNode::_editor_select(int p_which) { ERR_FAIL_INDEX(p_which,editor_table.size()); - main_editor_tabs->set_current_tab(p_which); + for(int i=0;i<main_editor_buttons.size();i++) { + main_editor_buttons[i]->set_pressed(i==p_which); + } selecting=false; @@ -2332,6 +2885,8 @@ void EditorNode::_editor_select(int p_which) { editor_plugin_screen=new_editor; editor_plugin_screen->make_visible(true); editor_plugin_screen->selected_notify(); + + } void EditorNode::add_editor_plugin(EditorPlugin *p_editor) { @@ -2339,7 +2894,12 @@ void EditorNode::add_editor_plugin(EditorPlugin *p_editor) { if (p_editor->has_main_screen()) { - singleton->main_editor_tabs->add_tab(p_editor->get_name()); + ToolButton *tb = memnew( ToolButton ); + tb->set_toggle_mode(true); + tb->connect("pressed",singleton,"_editor_select",varray(singleton->main_editor_buttons.size())); + tb->set_text(p_editor->get_name()); + singleton->main_editor_buttons.push_back(tb); + singleton->main_editor_button_vb->add_child(tb); singleton->editor_table.push_back(p_editor); } singleton->editor_data.add_editor_plugin( p_editor ); @@ -2351,16 +2911,18 @@ void EditorNode::remove_editor_plugin(EditorPlugin *p_editor) { if (p_editor->has_main_screen()) { - for(int i=0;i<singleton->main_editor_tabs->get_tab_count();i++) { + for(int i=0;i<singleton->main_editor_buttons.size();i++) { + + if (p_editor->get_name()==singleton->main_editor_buttons[i]->get_name()) { - if (p_editor->get_name()==singleton->main_editor_tabs->get_tab_title(i)) { + memdelete( singleton->main_editor_buttons[i] ); + singleton->main_editor_buttons.remove(i); - singleton->main_editor_tabs->remove_tab(i); break; } } - singleton->main_editor_tabs->add_tab(p_editor->get_name()); + //singleton->main_editor_tabs->add_tab(p_editor->get_name()); singleton->editor_table.erase(p_editor); } singleton->remove_child(p_editor); @@ -2369,29 +2931,80 @@ void EditorNode::remove_editor_plugin(EditorPlugin *p_editor) { } +void EditorNode::add_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import) { + + editor_import_export->add_import_plugin(p_editor_import); + _rebuild_import_menu(); +} + +void EditorNode::remove_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import) { + + editor_import_export->remove_import_plugin(p_editor_import); + _rebuild_import_menu(); +} + +void EditorNode::_remove_edited_scene() { + int new_index = editor_data.get_edited_scene(); + int old_index=new_index; + + if (new_index>0) { + new_index=new_index-1; + } else if (editor_data.get_edited_scene_count()>1) { + new_index=1; + } else { + editor_data.add_edited_scene(-1); + new_index=1; + } + + _scene_tab_changed(new_index); + editor_data.remove_scene(old_index); + editor_data.get_undo_redo().clear_history(); + _update_title(); + _update_scene_tabs(); + +// if (editor_data.get_edited_scene_count()==1) { +// //make new scene appear saved +// set_current_version(editor_data.get_undo_redo().get_version()); +// unsaved_cache=false; +// } +} + +void EditorNode::_remove_scene(int index) { +// printf("Attempting to remove scene %d (current is %d)\n", index, editor_data.get_edited_scene()); + + if (editor_data.get_edited_scene() == index) { + //Scene to remove is current scene + _remove_edited_scene(); + } + else { + // Scene to remove is not active scene + editor_data.remove_scene(index); + } +} + void EditorNode::set_edited_scene(Node *p_scene) { - if (edited_scene) { - if (edited_scene->get_parent()==scene_root) - scene_root->remove_child(edited_scene); + if (get_editor_data().get_edited_scene_root()) { + if (get_editor_data().get_edited_scene_root()->get_parent()==scene_root) + scene_root->remove_child(get_editor_data().get_edited_scene_root()); animation_editor->set_root(NULL); } - edited_scene=p_scene; - if (edited_scene && edited_scene->cast_to<Popup>()) - edited_scene->cast_to<Popup>()->show(); //show popups - scene_tree_dock->set_edited_scene(edited_scene); + get_editor_data().set_edited_scene_root(p_scene); + + if (p_scene && p_scene->cast_to<Popup>()) + p_scene->cast_to<Popup>()->show(); //show popups + scene_tree_dock->set_edited_scene(p_scene); if (get_tree()) - get_tree()->set_edited_scene_root(edited_scene); + get_tree()->set_edited_scene_root(p_scene); - if (edited_scene) { + if (p_scene) { if (p_scene->get_parent()!=scene_root) scene_root->add_child(p_scene); animation_editor->set_root(p_scene); } - - } + void EditorNode::_fetch_translatable_strings(const Object *p_object,Set<StringName>& strings) { @@ -2443,7 +3056,7 @@ Error EditorNode::save_translatable_strings(const String& p_to_file) { OS::Time time = OS::get_singleton()->get_time(); f->store_line("# Translation Strings Dump."); f->store_line("# Created By."); - f->store_line("# \t"VERSION_FULL_NAME" (c) 2008-2014 Juan Linietsky, Ariel Manzur."); + f->store_line("# \t" VERSION_FULL_NAME " (c) 2008-2015 Juan Linietsky, Ariel Manzur."); f->store_line("# From Scene: "); f->store_line("# \t"+get_edited_scene()->get_filename()); f->store_line(""); @@ -2558,7 +3171,7 @@ Error EditorNode::save_optimized_copy(const String& p_scene,const String& p_pres } } - ERR_EXPLAIN("Preset '"+p_preset+"' references unexisting saver: "+type); + ERR_EXPLAIN("Preset '"+p_preset+"' references nonexistent saver: "+type); ERR_FAIL_COND_V(saver.is_null(),ERR_INVALID_DATA); List<Variant> keys; @@ -2617,7 +3230,179 @@ Error EditorNode::save_optimized_copy(const String& p_scene,const String& p_pres return OK; } -Error EditorNode::load_scene(const String& p_scene) { + +int EditorNode::_get_current_main_editor() { + + for(int i=0;i<editor_table.size();i++) { + if (editor_table[i]==editor_plugin_screen) + return i; + } + + return 0; +} + +Dictionary EditorNode::_get_main_scene_state() { + + Dictionary state; + state["main_tab"]=_get_current_main_editor(); + state["scene_tree_offset"]=scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_val(); + state["property_edit_offset"]=get_property_editor()->get_scene_tree()->get_vscroll_bar()->get_val(); + state["saved_version"]=saved_version; + //print_line(" getting main tab: "+itos(state["main_tab"])); + return state; +} + +void EditorNode::_set_main_scene_state(Dictionary p_state) { + + //print_line("set current 7 "); + changing_scene=false; + +#if 0 + if (p_state.has("main_tab")) { + int idx = p_state["main_tab"]; + + + print_line("comes with tab: "+itos(idx)); + int current=-1; + for(int i=0;i<editor_table.size();i++) { + if (editor_plugin_screen==editor_table[i]) { + current=i; + break; + } + } + + + if (idx<2 && current<2) { + //only set tab for 2D and 3D + _editor_select(idx); + //print_line(" setting main tab: "+itos(p_state["main_tab"])); + } + } +#else + + if (get_edited_scene()) { + + int current=-1; + for(int i=0;i<editor_table.size();i++) { + if (editor_plugin_screen==editor_table[i]) { + current=i; + break; + } + } + + if (current<2) { + //use heuristic instead + + int n2d=0,n3d=0; + _find_node_types(get_edited_scene(),n2d,n3d); + if (n2d>n3d) { + _editor_select(0); + } else if (n3d>n2d) { + _editor_select(1); + + } + } + + } +#endif + + + if (p_state.has("scene_tree_offset")) + scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->set_val(p_state["scene_tree_offset"]); + if (p_state.has("property_edit_offset")) + get_property_editor()->get_scene_tree()->get_vscroll_bar()->set_val(p_state["property_edit_offset"]); + + //print_line("set current 8 "); + + //this should only happen at the very end + + //changing_scene=true; //avoid script change from opening editor + ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root(); + ScriptEditor::get_singleton()->set_scene_root_script( editor_data.get_scene_root_script(editor_data.get_edited_scene()) ); + //changing_scene=false; + +} + +void EditorNode::set_current_version(uint64_t p_version) { + + saved_version=p_version; + editor_data.set_edited_scene_version(p_version); +} + +bool EditorNode::is_changing_scene() const { + return changing_scene; +} +void EditorNode::set_current_scene(int p_idx) { + + changing_scene=true; + editor_data.save_edited_scene_state(editor_selection,&editor_history,_get_main_scene_state()); + + if (get_editor_data().get_edited_scene_root()) { + if (get_editor_data().get_edited_scene_root()->get_parent()==scene_root) + scene_root->remove_child(get_editor_data().get_edited_scene_root()); + animation_editor->set_root(NULL); + } + + //print_line("set current 2 "); + + editor_selection->clear(); + editor_data.set_edited_scene(p_idx); + + Node* new_scene = editor_data.get_edited_scene_root(); + + if (new_scene && new_scene->cast_to<Popup>()) + new_scene->cast_to<Popup>()->show(); //show popups + + //print_line("set current 3 "); + + scene_tree_dock->set_edited_scene(new_scene); + if (get_tree()) + get_tree()->set_edited_scene_root(new_scene); + + if (new_scene) { + if (new_scene->get_parent()!=scene_root) + scene_root->add_child(new_scene); + animation_editor->set_root(new_scene); + } + //print_line("set current 4 "); + + + Dictionary state = editor_data.restore_edited_scene_state(editor_selection,&editor_history); + _edit_current(); + + /*if (!unsaved) { + saved_version=editor_data.get_undo_redo().get_version(); + if (p_backwards) + saved_version--; + else + saved_version++; + print_line("was saved, updating version"); + } else { + saved_version=state["saved_version"]; + }*/ + //_set_main_scene_state(state); + + call_deferred("_set_main_scene_state",state); //do after everything else is done setting up + //print_line("set current 6 "); + + +} + +bool EditorNode::is_scene_open(const String& p_path) { + + for(int i=0;i<editor_data.get_edited_scene_count();i++) { + if (editor_data.get_scene_path(i)==p_path) + return true; + } + + return false; +} + +void EditorNode::fix_dependencies(const String& p_for_file) { + dependency_fixer->edit(p_for_file); +} + +Error EditorNode::load_scene(const String& p_scene, bool p_ignore_broken_deps,bool p_set_inherited) { if (!is_inside_tree()) { defer_load_scene = p_scene; @@ -2625,6 +3410,14 @@ Error EditorNode::load_scene(const String& p_scene) { } + for(int i=0;i<editor_data.get_edited_scene_count();i++) { + + if (editor_data.get_scene_path(i)==p_scene) { + _scene_tab_changed(i); + return OK; + } + } + load_errors->clear(); String lpath = Globals::get_singleton()->localize_path(p_scene); @@ -2635,38 +3428,97 @@ Error EditorNode::load_scene(const String& p_scene) { //accept->get_cancel()->hide(); accept->get_ok()->set_text("Ugh"); accept->set_text("Error loading scene, it must be inside the project path. Use 'Import' to open the scene, then save it inside the project path."); - accept->popup_centered(Size2(300,120)); + accept->popup_centered_minsize(); opening_prev=false; return ERR_FILE_NOT_FOUND; } - _cleanup_scene(); // i'm sorry but this MUST happen to avoid modified resources to not be reloaded. + int prev = editor_data.get_edited_scene(); + int idx = editor_data.add_edited_scene(-1); + //print_line("load scene callback"); + //set_current_scene(idx); - Ref<PackedScene> sdata = ResourceLoader::load(lpath); + if (!editor_data.get_edited_scene_root() && editor_data.get_edited_scene_count()==2) { + _remove_edited_scene(); + } else { + _scene_tab_changed(idx); + } + + + + //_cleanup_scene(); // i'm sorry but this MUST happen to avoid modified resources to not be reloaded. + + dependency_errors.clear(); + + Ref<PackedScene> sdata = ResourceLoader::load(lpath,"",true); if (!sdata.is_valid()) { current_option=-1; //accept->get_cancel()->hide(); accept->get_ok()->set_text("Ugh"); accept->set_text("Error loading scene."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); opening_prev=false; + + if (prev!=-1) { + set_current_scene(prev); + editor_data.remove_scene(idx); + } return ERR_FILE_NOT_FOUND; } + if (!p_ignore_broken_deps && dependency_errors.has(lpath)) { + + current_option=-1; + Vector<String> errors; + for(Set<String>::Element *E=dependency_errors[lpath].front();E;E=E->next()) { + + errors.push_back(E->get()); + } + dependency_error->show(lpath,errors); + opening_prev=false; + + if (prev!=-1) { + set_current_scene(prev); + editor_data.remove_scene(idx); + } + return ERR_FILE_MISSING_DEPENDENCIES; + } + + dependency_errors.erase(lpath); //at least not self path + + for (Map<String,Set<String> >::Element *E=dependency_errors.front();E;E=E->next()) { + + String txt="Scene '"+E->key()+"' has broken dependencies:\n"; + for(Set<String>::Element *F=E->get().front();F;F=F->next()) { + txt+="\t"+F->get()+"\n"; + } + add_io_error(txt); + } + + sdata->set_path(lpath,true); //take over path + Node*new_scene=sdata->instance(true); if (!new_scene) { + sdata.unref(); current_option=-1; //accept->get_cancel()->hide(); accept->get_ok()->set_text("Ugh"); accept->set_text("Error loading scene."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); opening_prev=false; + if (prev!=-1) { + set_current_scene(prev); + editor_data.remove_scene(idx); + } return ERR_FILE_NOT_FOUND; } + //guess not needed in the end? + //new_scene->clear_internal_tree_resource_paths(); //make sure no internal tree paths to internal resources exist + /* Node *old_scene = edited_scene; _hide_top_editors(); @@ -2679,16 +3531,33 @@ Error EditorNode::load_scene(const String& p_scene) { memdelete(old_scene); } */ + + if (p_set_inherited) { + Ref<SceneState> state = sdata->get_state(); + state->set_path(lpath); + new_scene->set_scene_inherited_state(state); + new_scene->set_filename(String()); + if (new_scene->get_scene_instance_state().is_valid()) + new_scene->get_scene_instance_state()->set_path(String()); + } + + set_edited_scene(new_scene); _get_scene_metadata(); + /* + editor_data.set_edited_scene_root(new_scene); + scene_tree_dock->set_selected(new_scene, true); property_editor->edit(new_scene); - scene_import_metadata = sdata->get_import_metadata(); + editor_data.set_edited_scene_root(new_scene); +*/ + editor_data.set_edited_scene_import_metadata( sdata->get_import_metadata() ); - editor_data.get_undo_redo().clear_history(); + +// editor_data.get_undo_redo().clear_history(); saved_version=editor_data.get_undo_redo().get_version(); _update_title(); - + _update_scene_tabs(); _add_to_recent_scenes(lpath); if (new_scene->has_meta("__editor_plugin_screen__")) { @@ -2707,7 +3576,9 @@ Error EditorNode::load_scene(const String& p_scene) { prev_scene->set_disabled(previous_scenes.size()==0); opening_prev=false; - top_pallete->set_current_tab(0); //always go to scene + ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root(); + + //top_pallete->set_current_tab(0); //always go to scene push_item(new_scene); @@ -2718,8 +3589,9 @@ Error EditorNode::load_scene(const String& p_scene) { void EditorNode::open_request(const String& p_path) { - external_file=p_path; - _menu_option_confirm(FILE_EXTERNAL_OPEN_SCENE,false); + load_scene(p_path); // as it will be opened in separate tab + //external_file=p_path; + //_menu_option_confirm(FILE_EXTERNAL_OPEN_SCENE,false); } @@ -2740,9 +3612,9 @@ void EditorNode::_instance_request(const String& p_path){ request_instance_scene(p_path); } -void EditorNode::_property_keyed(const String& p_keyed,const Variant& p_value) { +void EditorNode::_property_keyed(const String& p_keyed,const Variant& p_value,bool p_advance) { - animation_editor->insert_value_key(p_keyed,p_value); + animation_editor->insert_value_key(p_keyed,p_value,p_advance); } void EditorNode::_transform_keyed(Object *sp,const String& p_sub,const Transform& p_key) { @@ -2780,15 +3652,15 @@ void EditorNode::_update_keying() { void EditorNode::_close_messages() { // left_split->set_dragger_visible(false); - old_split_ofs = left_split->get_split_offset(); - left_split->set_split_offset(0); + old_split_ofs = center_split->get_split_offset(); + center_split->set_split_offset(0); // scene_root_parent->set_anchor_and_margin(MARGIN_BOTTOM,Control::ANCHOR_END,0); } void EditorNode::_show_messages() { // left_split->set_dragger_visible(true); - left_split->set_split_offset(old_split_ofs); + center_split->set_split_offset(old_split_ofs); // scene_root_parent->set_anchor_and_margin(MARGIN_BOTTOM,Control::ANCHOR_END,log->get_margin(MARGIN_TOP)); } @@ -2857,15 +3729,16 @@ void EditorNode::_open_recent_scene(int p_idx) { String path = "res://"+rc[p_idx]; - if (unsaved_cache) { + + /*if (unsaved_cache) { _recent_scene=rc[p_idx]; open_recent_confirmation->set_text("Discard current scene and open:\n'"+rc[p_idx]+"'"); open_recent_confirmation->get_label()->set_align(Label::ALIGN_CENTER); open_recent_confirmation->popup_centered(Size2(400,100)); return; - } + }*/ - load_scene(rc[p_idx]); + load_scene(path); } @@ -2909,12 +3782,6 @@ void EditorNode::_save_optimized() { #endif } -void EditorNode::_open_recent_scene_confirm() { - - load_scene(_recent_scene); - -} - void EditorNode::_update_recent_scenes() { String base="_"+Globals::get_singleton()->get_resource_path().replace("\\","::").replace("/","::"); @@ -2934,9 +3801,12 @@ void EditorNode::hide_animation_player_editors() { void EditorNode::_quick_opened(const String& p_resource) { - print_line("quick_opened"); - if (quick_open->get_base_type()=="PackedScene") { + if (current_option==FILE_QUICK_OPEN_FILE) { + scenes_dock->open(p_resource); + return; + } + if (quick_open->get_base_type()=="PackedScene") { open_request(p_resource); } else { load_resource(p_resource); @@ -2952,9 +3822,7 @@ void EditorNode::_quick_run(const String& p_resource) { void EditorNode::notify_child_process_exited() { - play_button->set_pressed(false); - play_scene_button->set_pressed(false); - pause_button->set_pressed(false); + _menu_option_confirm(RUN_STOP,false); stop_button->set_pressed(false); editor_run.stop(); @@ -3034,6 +3902,8 @@ void EditorNode::register_editor_types() { ObjectTypeDB::register_type<EditorImportPlugin>(); ObjectTypeDB::register_type<EditorScenePostImport>(); ObjectTypeDB::register_type<EditorScript>(); + ObjectTypeDB::register_type<EditorFileDialog>(); + ObjectTypeDB::register_type<UndoRedo>(); //ObjectTypeDB::register_type<EditorImporter>(); @@ -3119,11 +3989,13 @@ void EditorNode::_bind_methods() { ObjectTypeDB::bind_method("_quick_opened",&EditorNode::_quick_opened); ObjectTypeDB::bind_method("_quick_run",&EditorNode::_quick_run); + ObjectTypeDB::bind_method("_resource_created",&EditorNode::_resource_created); + ObjectTypeDB::bind_method("_import_action",&EditorNode::_import_action); //ObjectTypeDB::bind_method("_import",&EditorNode::_import); // ObjectTypeDB::bind_method("_import_conflicts_solved",&EditorNode::_import_conflicts_solved); ObjectTypeDB::bind_method("_open_recent_scene",&EditorNode::_open_recent_scene); - ObjectTypeDB::bind_method("_open_recent_scene_confirm",&EditorNode::_open_recent_scene_confirm); +// ObjectTypeDB::bind_method("_open_recent_scene_confirm",&EditorNode::_open_recent_scene_confirm); ObjectTypeDB::bind_method("_save_optimized",&EditorNode::_save_optimized); ObjectTypeDB::bind_method(_MD("animation_panel_make_visible","enable"),&EditorNode::animation_panel_make_visible); @@ -3132,7 +4004,32 @@ void EditorNode::_bind_methods() { ObjectTypeDB::bind_method("_sources_changed",&EditorNode::_sources_changed); ObjectTypeDB::bind_method("_fs_changed",&EditorNode::_fs_changed); - + ObjectTypeDB::bind_method("_dock_select_draw",&EditorNode::_dock_select_draw); + ObjectTypeDB::bind_method("_dock_select_input",&EditorNode::_dock_select_input); + ObjectTypeDB::bind_method("_dock_pre_popup",&EditorNode::_dock_pre_popup); + ObjectTypeDB::bind_method("_dock_split_dragged",&EditorNode::_dock_split_dragged); + ObjectTypeDB::bind_method("_save_docks",&EditorNode::_save_docks); + ObjectTypeDB::bind_method("_dock_popup_exit",&EditorNode::_dock_popup_exit); + ObjectTypeDB::bind_method("_dock_move_left",&EditorNode::_dock_move_left); + ObjectTypeDB::bind_method("_dock_move_right",&EditorNode::_dock_move_right); + + ObjectTypeDB::bind_method("set_current_scene",&EditorNode::set_current_scene); + ObjectTypeDB::bind_method("set_current_version",&EditorNode::set_current_version); + ObjectTypeDB::bind_method("_scene_tab_changed",&EditorNode::_scene_tab_changed); + ObjectTypeDB::bind_method("_scene_tab_closed",&EditorNode::_scene_tab_closed); + ObjectTypeDB::bind_method("_scene_tab_script_edited",&EditorNode::_scene_tab_script_edited); + ObjectTypeDB::bind_method("_set_main_scene_state",&EditorNode::_set_main_scene_state); + ObjectTypeDB::bind_method("_update_scene_tabs",&EditorNode::_update_scene_tabs); + + ObjectTypeDB::bind_method("_prepare_history",&EditorNode::_prepare_history); + ObjectTypeDB::bind_method("_select_history",&EditorNode::_select_history); + + ObjectTypeDB::bind_method("_toggle_search_bar",&EditorNode::_toggle_search_bar); + ObjectTypeDB::bind_method("_clear_search_box",&EditorNode::_clear_search_box); + + ObjectTypeDB::bind_method(_MD("add_editor_import_plugin", "plugin"), &EditorNode::add_editor_import_plugin); + ObjectTypeDB::bind_method(_MD("remove_editor_import_plugin", "plugin"), &EditorNode::remove_editor_import_plugin); + ObjectTypeDB::bind_method(_MD("get_gui_base"), &EditorNode::get_gui_base); ADD_SIGNAL( MethodInfo("play_pressed") ); ADD_SIGNAL( MethodInfo("pause_pressed") ); @@ -3181,6 +4078,16 @@ void EditorNode::_file_dialog_unregister(FileDialog *p_dialog){ singleton->file_dialogs.erase(p_dialog); } +void EditorNode::_editor_file_dialog_register(EditorFileDialog *p_dialog) { + + singleton->editor_file_dialogs.insert(p_dialog); +} + +void EditorNode::_editor_file_dialog_unregister(EditorFileDialog *p_dialog){ + + singleton->editor_file_dialogs.erase(p_dialog); +} + Vector<EditorNodeInitCallback> EditorNode::_init_callbacks; Error EditorNode::export_platform(const String& p_platform, const String& p_path, bool p_debug,const String& p_password,bool p_quit_after) { @@ -3193,13 +4100,469 @@ Error EditorNode::export_platform(const String& p_platform, const String& p_path return OK; } +void EditorNode::show_warning(const String& p_text) { + + warning->set_text(p_text); + warning->popup_centered_minsize(); +} + +void EditorNode::_dock_select_input(const InputEvent& p_input) { + + if (p_input.type==InputEvent::MOUSE_BUTTON || p_input.type==InputEvent::MOUSE_MOTION) { + + Vector2 point(p_input.mouse_motion.x,p_input.mouse_motion.y); + + int nrect = -1; + for(int i=0;i<DOCK_SLOT_MAX;i++) { + if (dock_select_rect[i].has_point(point)) { + nrect=i; + break; + } + } + + + if (nrect!=dock_select_rect_over) { + dock_select->update(); + dock_select_rect_over=nrect; + + } + + + if (nrect==-1) + return; + + if (p_input.type==InputEvent::MOUSE_BUTTON && p_input.mouse_button.button_index==1 && p_input.mouse_button.pressed && dock_popup_selected!=nrect) { + Control *dock = dock_slot[dock_popup_selected]->get_current_tab_control(); + if (dock) { + dock_slot[dock_popup_selected]->remove_child(dock); + } + if (dock_slot[dock_popup_selected]->get_tab_count()==0) { + dock_slot[dock_popup_selected]->hide(); + + } else { + + dock_slot[dock_popup_selected]->set_current_tab(0); + } + + print_line("performing reparent"); + dock_slot[nrect]->add_child(dock); + dock_popup_selected=nrect; + dock_slot[nrect]->set_current_tab(dock_slot[nrect]->get_tab_count()-1); + dock_slot[nrect]->show(); + dock_select->update(); + + VSplitContainer*splits[DOCK_SLOT_MAX/2]={ + left_l_vsplit, + left_r_vsplit, + right_l_vsplit, + right_r_vsplit, + }; + + for(int i=0;i<4;i++) { + bool in_use = dock_slot[i*2+0]->get_tab_count() || dock_slot[i*2+1]->get_tab_count(); + if (in_use) + splits[i]->show(); + else + splits[i]->hide(); + } + + _save_docks(); + } + } +} + +void EditorNode::_dock_popup_exit() { + + dock_select_rect_over=-1; + dock_select->update(); +} + +void EditorNode::_dock_pre_popup(int p_which) { + + + dock_popup_selected=p_which; +} + +void EditorNode::_dock_move_left() { + + if (dock_popup_selected<0 || dock_popup_selected>=DOCK_SLOT_MAX) + return; + Control *current = dock_slot[dock_popup_selected]->get_tab_control( dock_slot[dock_popup_selected]->get_current_tab() ); + Control *prev = dock_slot[dock_popup_selected]->get_tab_control( dock_slot[dock_popup_selected]->get_current_tab()-1 ); + if (!current || !prev) + return; + dock_slot[dock_popup_selected]->move_child(current,prev->get_index()); + dock_slot[dock_popup_selected]->set_current_tab( dock_slot[dock_popup_selected]->get_current_tab()-1 ); + dock_select->update(); + _save_docks(); + + +} + +void EditorNode::_dock_move_right() { + + Control *current = dock_slot[dock_popup_selected]->get_tab_control( dock_slot[dock_popup_selected]->get_current_tab() ); + Control *next = dock_slot[dock_popup_selected]->get_tab_control( dock_slot[dock_popup_selected]->get_current_tab()+1 ); + if (!current || !next) + return; + dock_slot[dock_popup_selected]->move_child(next,current->get_index()); + dock_slot[dock_popup_selected]->set_current_tab( dock_slot[dock_popup_selected]->get_current_tab()+1 ); + dock_select->update(); + _save_docks(); +} + +void EditorNode::_dock_select_draw(){ + Size2 s = dock_select->get_size(); + s.y/=2.0; + s.x/=6.0; + + Color used=Color(0.6,0.6,0.6,0.8); + Color used_selected=Color(0.8,0.8,0.8,0.8); + Color tab_selected=Color(1,1,1,1); + Color unused=used; + unused.a=0.4; + Color unusable=unused; + unusable.a=0.1; + + Rect2 unr(s.x*2,0,s.x*2,s.y*2); + unr.pos+=Vector2(2,5); + unr.size-=Vector2(4,7); + + dock_select->draw_rect(unr,unusable); + + dock_tab_move_left->set_disabled(true); + dock_tab_move_right->set_disabled(true); + + if (dock_popup_selected!=-1 && dock_slot[dock_popup_selected]->get_tab_count()) { + + dock_tab_move_left->set_disabled(dock_slot[dock_popup_selected]->get_current_tab()==0); + dock_tab_move_right->set_disabled(dock_slot[dock_popup_selected]->get_current_tab()>=dock_slot[dock_popup_selected]->get_tab_count()-1); + } + + for(int i=0;i<DOCK_SLOT_MAX;i++) { + + Vector2 ofs; + + switch(i) { + case DOCK_SLOT_LEFT_UL: { + + } break; + case DOCK_SLOT_LEFT_BL: { + ofs.y+=s.y; + } break; + case DOCK_SLOT_LEFT_UR: { + ofs.x+=s.x; + } break; + case DOCK_SLOT_LEFT_BR: { + ofs+=s; + } break; + case DOCK_SLOT_RIGHT_UL: { + ofs.x+=s.x*4; + } break; + case DOCK_SLOT_RIGHT_BL: { + ofs.x+=s.x*4; + ofs.y+=s.y; + + } break; + case DOCK_SLOT_RIGHT_UR: { + ofs.x+=s.x*4; + ofs.x+=s.x; + + } break; + case DOCK_SLOT_RIGHT_BR: { + ofs.x+=s.x*4; + ofs+=s; + + } break; + } + + Rect2 r(ofs,s); + dock_select_rect[i]=r; + r.pos+=Vector2(2,5); + r.size-=Vector2(4,7); + + + if (i==dock_select_rect_over) { + dock_select->draw_rect(r,used_selected); + } else if (dock_slot[i]->get_child_count()==0) { + dock_select->draw_rect(r,unused); + } else { + + dock_select->draw_rect(r,used); + } + + for(int j=0;j<MIN(3,dock_slot[i]->get_child_count());j++) { + int xofs = (r.size.width/3)*j; + Color c = used; + if (i==dock_popup_selected && (dock_slot[i]->get_current_tab()>3 || dock_slot[i]->get_current_tab()==j)) + c=tab_selected; + dock_select->draw_rect(Rect2(2+ofs.x+xofs,ofs.y,r.size.width/3-1,3),c); + } + + } +} + +void EditorNode::_save_docks() { + + Ref<ConfigFile> config; + config.instance(); + + for(int i=0;i<DOCK_SLOT_MAX;i++) { + String names; + for(int j=0;j<dock_slot[i]->get_tab_count();j++) { + String name = dock_slot[i]->get_tab_control(j)->get_name(); + if (names!="") + names+=","; + names+=name; + } + + if (names!="") { + config->set_value("docks","dock_"+itos(i+1),names); + } + } + + VSplitContainer*splits[DOCK_SLOT_MAX/2]={ + left_l_vsplit, + left_r_vsplit, + right_l_vsplit, + right_r_vsplit, + }; + + for(int i=0;i<DOCK_SLOT_MAX/2;i++) { + + if (splits[i]->is_visible()) { + config->set_value("docks","dock_split_"+itos(i+1),splits[i]->get_split_offset()); + } + } + + + HSplitContainer *h_splits[4]={ + left_l_hsplit, + left_r_hsplit, + main_hsplit, + right_hsplit, + }; + + for(int i=0;i<4;i++) { + + config->set_value("docks","dock_hsplit_"+itos(i+1),h_splits[i]->get_split_offset()); + } + + editor_data.get_plugin_window_layout(config); + + config->save(EditorSettings::get_singleton()->get_project_settings_path().plus_file("editor_layout.cfg")); + +} + +void EditorNode::save_layout() { + + dock_drag_timer->start(); +} + +void EditorNode::_dock_split_dragged(int ofs) { + + dock_drag_timer->start(); +} + +void EditorNode::_load_docks() { + + Ref<ConfigFile> config; + config.instance(); + Error err = config->load(EditorSettings::get_singleton()->get_project_settings_path().plus_file("editor_layout.cfg")); + if (err!=OK) { + return; //no config + } + + for(int i=0;i<DOCK_SLOT_MAX;i++) { + + if (!config->has_section_key("docks","dock_"+itos(i+1))) + continue; + + Vector<String> names = String(config->get_value("docks","dock_"+itos(i+1))).split(","); + + for(int j=0;j<names.size();j++) { + + String name=names[j]; + //find it, in a horribly inefficient way + int atidx=-1; + Control *node=NULL; + for(int k=0;k<DOCK_SLOT_MAX;k++) { + if (!dock_slot[k]->has_node(name)) + continue; + node=dock_slot[k]->get_node(name)->cast_to<Control>(); + if (!node) + continue; + atidx=k; + break; + } + if (atidx==-1) //well, it's not anywhere + continue; + + if (atidx==j) { + node->raise(); + continue; + } + dock_slot[atidx]->remove_child(node); + + if (dock_slot[atidx]->get_tab_count()==0) { + dock_slot[atidx]->hide(); + + } + dock_slot[i]->add_child(node); + dock_slot[i]->show(); + } + + } + + VSplitContainer*splits[DOCK_SLOT_MAX/2]={ + left_l_vsplit, + left_r_vsplit, + right_l_vsplit, + right_r_vsplit, + }; + + for(int i=0;i<DOCK_SLOT_MAX/2;i++) { + + if (!config->has_section_key("docks","dock_split_"+itos(i+1))) + continue; + + int ofs = config->get_value("docks","dock_split_"+itos(i+1)); + splits[i]->set_split_offset(ofs); + } + + HSplitContainer *h_splits[4]={ + left_l_hsplit, + left_r_hsplit, + main_hsplit, + right_hsplit, + }; + + for(int i=0;i<4;i++) { + if (!config->has_section_key("docks","dock_hsplit_"+itos(i+1))) + continue; + int ofs = config->get_value("docks","dock_hsplit_"+itos(i+1)); + h_splits[i]->set_split_offset(ofs); + } + + for(int i=0;i<DOCK_SLOT_MAX/2;i++) { + bool in_use = dock_slot[i*2+0]->get_tab_count() || dock_slot[i*2+1]->get_tab_count(); + if (in_use) + splits[i]->show(); + else + splits[i]->hide(); + } + + for(int i=0;i<DOCK_SLOT_MAX;i++) { + + if (!dock_slot[i]->is_hidden() && dock_slot[i]->get_tab_count()) { + dock_slot[i]->set_current_tab(0); + } + } + + editor_data.set_plugin_window_layout(config); + +} + + +void EditorNode::_scene_tab_script_edited(int p_tab) { + + Ref<Script> script = editor_data.get_scene_root_script(p_tab); + if (script.is_valid()) + edit_resource(script); +} + +void EditorNode::_scene_tab_closed(int p_tab) { + current_option = SCENE_TAB_CLOSE; + tab_closing = p_tab; + if (unsaved_cache) { + confirmation->get_ok()->set_text("Yes"); + //confirmation->get_cancel()->show(); + confirmation->set_text("Close scene? (Unsaved changes will be lost)"); + confirmation->popup_centered_minsize(); + } + else { + _remove_scene(p_tab); + //_update_scene_tabs(); + } + +} + + +void EditorNode::_scene_tab_changed(int p_tab) { + + + //print_line("set current 1 "); + bool unsaved = (saved_version!=editor_data.get_undo_redo().get_version()); + //print_line("version: "+itos(editor_data.get_undo_redo().get_version())+", saved "+itos(saved_version)); + + if (p_tab==editor_data.get_edited_scene()) + return; //pointless + + uint64_t next_scene_version = editor_data.get_scene_version(p_tab); + + + + //print_line("scene tab changed???"); + editor_data.get_undo_redo().create_action("Switch Scene Tab"); + editor_data.get_undo_redo().add_do_method(this,"set_current_version",unsaved?saved_version:0); + editor_data.get_undo_redo().add_do_method(this,"set_current_scene",p_tab); + editor_data.get_undo_redo().add_do_method(scene_tabs,"set_current_tab",p_tab); + editor_data.get_undo_redo().add_do_method(this,"set_current_version",next_scene_version==0?editor_data.get_undo_redo().get_version()+1:next_scene_version); + + editor_data.get_undo_redo().add_undo_method(this,"set_current_version",next_scene_version); + editor_data.get_undo_redo().add_undo_method(this,"set_current_scene",editor_data.get_edited_scene()); + editor_data.get_undo_redo().add_undo_method(scene_tabs,"set_current_tab",editor_data.get_edited_scene()); + editor_data.get_undo_redo().add_undo_method(this,"set_current_version",saved_version); + editor_data.get_undo_redo().commit_action(); + +} + +void EditorNode::_toggle_search_bar(bool p_pressed) { + + property_editor->set_use_filter(p_pressed); + + if (p_pressed) { + + search_bar->show(); + search_box->grab_focus(); + search_box->select_all(); + } else { + + search_bar->hide(); + } +} + +void EditorNode::_clear_search_box() { + + if (search_box->get_text()=="") + return; + + search_box->clear(); + property_editor->update_tree(); +} + EditorNode::EditorNode() { EditorHelp::generate_doc(); //before any editor classes are crated + SceneState::set_disable_placeholders(true); + + InputDefault *id = Input::get_singleton()->cast_to<InputDefault>(); + + if (id) { + + if (!OS::get_singleton()->has_touchscreen_ui_hint() && Input::get_singleton()) { + //only if no touchscreen ui hint, set emulation + id->set_emulate_touch(false); //just disable just in case + } + id->set_custom_mouse_cursor(RES()); + } + singleton=this; + last_checked_version=0; + changing_scene=false; FileAccess::set_backup_save(true); @@ -3210,7 +4573,9 @@ EditorNode::EditorNode() { EditorSettings::create(); ResourceLoader::set_abort_on_missing_resources(false); + FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("file_dialog/show_hidden_files")); ResourceLoader::set_error_notify_func(this,_load_error_notify); + ResourceLoader::set_dependency_error_notify_func(this,_dependency_error_report); ResourceLoader::set_timestamp_on_load(true); ResourceSaver::set_timestamp_on_save(true); @@ -3227,7 +4592,13 @@ EditorNode::EditorNode() { FileDialog::register_func=_file_dialog_register; FileDialog::unregister_func=_file_dialog_unregister; + EditorFileDialog::get_icon_func=_file_dialog_get_icon; + EditorFileDialog::register_func=_editor_file_dialog_register; + EditorFileDialog::unregister_func=_editor_file_dialog_unregister; + + editor_import_export = memnew( EditorImportExport ); + add_child(editor_import_export); register_exporters(); @@ -3245,11 +4616,14 @@ EditorNode::EditorNode() { gui_base->set_area_as_parent_rect(); - Ref<Theme> theme( memnew( Theme ) ); + theme = Ref<Theme>( memnew( Theme ) ); gui_base->set_theme( theme ); editor_register_icons(theme); editor_register_fonts(theme); + //theme->set_icon("folder","EditorFileDialog",Theme::get_default()->get_icon("folder","EditorFileDialog")); + //theme->set_color("files_disabled","EditorFileDialog",Color(0,0,0,0.7)); + String global_font = EditorSettings::get_singleton()->get("global/font"); if (global_font!="") { Ref<Font> fnt = ResourceLoader::load(global_font); @@ -3268,6 +4642,8 @@ EditorNode::EditorNode() { theme->set_stylebox("EditorFocus","EditorStyles",focus_sbt); + resource_preview = memnew( EditorResourcePreview ); + add_child(resource_preview); progress_dialog = memnew( ProgressDialog ); gui_base->add_child(progress_dialog); @@ -3275,26 +4651,166 @@ EditorNode::EditorNode() { gui_base->set_anchor( MARGIN_RIGHT, Control::ANCHOR_END ); gui_base->set_anchor( MARGIN_BOTTOM, Control::ANCHOR_END ); gui_base->set_end( Point2(0,0) ); - + main_vbox = memnew( VBoxContainer ); gui_base->add_child(main_vbox); main_vbox->set_area_as_parent_rect(8); +#if 0 + PanelContainer *top_dark_panel = memnew( PanelContainer ); + Ref<StyleBoxTexture> top_dark_sb; + top_dark_sb.instance();; + top_dark_sb->set_texture(theme->get_icon("PanelTop","EditorIcons")); + for(int i=0;i<4;i++) { + top_dark_sb->set_margin_size(Margin(i),3); + top_dark_sb->set_default_margin(Margin(i),0); + } + top_dark_sb->set_expand_margin_size(MARGIN_LEFT,20); + top_dark_sb->set_expand_margin_size(MARGIN_RIGHT,20); + + top_dark_panel->add_style_override("panel",top_dark_sb); + VBoxContainer *top_dark_vb = memnew( VBoxContainer ); + main_vbox->add_child(top_dark_panel); + top_dark_panel->add_child(top_dark_vb); +#endif + + + menu_hb = memnew( HBoxContainer ); main_vbox->add_child(menu_hb); - main_split = memnew( HSplitContainer ); - main_vbox->add_child(main_split); - main_split->set_v_size_flags(Control::SIZE_EXPAND_FILL); - +// top_dark_vb->add_child(scene_tabs); + //left + left_l_hsplit = memnew( HSplitContainer ); + main_vbox->add_child(left_l_hsplit); + + left_l_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL); + + left_l_vsplit = memnew( VSplitContainer ); + left_l_hsplit->add_child(left_l_vsplit); + dock_slot[DOCK_SLOT_LEFT_UL]=memnew( TabContainer ); + left_l_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_UL]); + dock_slot[DOCK_SLOT_LEFT_BL]=memnew( TabContainer ); + left_l_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_BL]); + left_l_vsplit->hide(); + dock_slot[DOCK_SLOT_LEFT_UL]->hide(); + dock_slot[DOCK_SLOT_LEFT_BL]->hide(); + + left_r_hsplit = memnew( HSplitContainer ); + left_l_hsplit->add_child(left_r_hsplit); + left_r_vsplit = memnew( VSplitContainer ); + left_r_hsplit->add_child(left_r_vsplit); + dock_slot[DOCK_SLOT_LEFT_UR]=memnew( TabContainer ); + left_r_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_UR]); + dock_slot[DOCK_SLOT_LEFT_BR]=memnew( TabContainer ); + left_r_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_BR]); + //left_r_vsplit->hide(); + //dock_slot[DOCK_SLOT_LEFT_UR]->hide(); + //dock_slot[DOCK_SLOT_LEFT_BR]->hide(); + + + main_hsplit = memnew( HSplitContainer ); + left_r_hsplit->add_child(main_hsplit); + //main_split->set_v_size_flags(Control::SIZE_EXPAND_FILL); + VBoxContainer * center_vb = memnew( VBoxContainer); + main_hsplit->add_child(center_vb); + center_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + + center_split = memnew( VSplitContainer ); + //main_hsplit->add_child(center_split); + center_split->set_v_size_flags(Control::SIZE_EXPAND_FILL); + center_split->set_collapsed(false); + center_vb->add_child(center_split); + + right_hsplit = memnew( HSplitContainer ); + main_hsplit->add_child(right_hsplit); + + right_l_vsplit = memnew( VSplitContainer ); + right_hsplit->add_child(right_l_vsplit); + dock_slot[DOCK_SLOT_RIGHT_UL]=memnew( TabContainer ); + right_l_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_UL]); + dock_slot[DOCK_SLOT_RIGHT_BL]=memnew( TabContainer ); + right_l_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_BL]); + //right_l_vsplit->hide(); + //dock_slot[DOCK_SLOT_RIGHT_UL]->hide(); + //dock_slot[DOCK_SLOT_RIGHT_BL]->hide(); + + right_r_vsplit = memnew( VSplitContainer ); + right_hsplit->add_child(right_r_vsplit); + dock_slot[DOCK_SLOT_RIGHT_UR]=memnew( TabContainer ); + right_r_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_UR]); + dock_slot[DOCK_SLOT_RIGHT_BR]=memnew( TabContainer ); + right_r_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_BR]); + right_r_vsplit->hide(); + dock_slot[DOCK_SLOT_RIGHT_UR]->hide(); + dock_slot[DOCK_SLOT_RIGHT_BR]->hide(); + + left_l_vsplit->connect("dragged",this,"_dock_split_dragged"); + left_r_vsplit->connect("dragged",this,"_dock_split_dragged"); + right_l_vsplit->connect("dragged",this,"_dock_split_dragged"); + right_r_vsplit->connect("dragged",this,"_dock_split_dragged"); + + left_l_hsplit->connect("dragged",this,"_dock_split_dragged"); + left_r_hsplit->connect("dragged",this,"_dock_split_dragged"); + main_hsplit->connect("dragged",this,"_dock_split_dragged"); + right_hsplit->connect("dragged",this,"_dock_split_dragged"); + + + + dock_select_popoup = memnew( PopupPanel ); + gui_base->add_child(dock_select_popoup); + VBoxContainer *dock_vb = memnew( VBoxContainer ); + dock_select_popoup->add_child(dock_vb); + + HBoxContainer *dock_hb = memnew( HBoxContainer); + dock_tab_move_left = memnew( ToolButton ); + dock_tab_move_left->set_icon(theme->get_icon("Back","EditorIcons")); + dock_tab_move_left->set_focus_mode(Control::FOCUS_NONE); + dock_tab_move_left->connect("pressed",this,"_dock_move_left"); + //dock_tab_move_left->set_h_size_flags(Control::SIZE_EXPAND_FILL); + dock_hb->add_child(dock_tab_move_left); + dock_hb->add_spacer(); + dock_tab_move_right = memnew( ToolButton ); + dock_tab_move_right->set_icon(theme->get_icon("Forward","EditorIcons")); + dock_tab_move_right->set_focus_mode(Control::FOCUS_NONE); + dock_tab_move_right->connect("pressed",this,"_dock_move_right"); + + //dock_tab_move_right->set_h_size_flags(Control::SIZE_EXPAND_FILL); + dock_hb->add_child(dock_tab_move_right); + dock_vb->add_child(dock_hb); + + dock_select = memnew( Control ); + dock_select->set_custom_minimum_size(Size2(128,64)); + dock_select->connect("input_event",this,"_dock_select_input"); + dock_select->connect("draw",this,"_dock_select_draw"); + dock_select->connect("mouse_exit",this,"_dock_popup_exit"); + dock_select->set_v_size_flags(Control::SIZE_EXPAND_FILL); + dock_vb->add_child(dock_select); + + + dock_select_popoup->set_child_rect(dock_vb); + dock_select_popoup->set_as_minsize(); + dock_select_rect_over=-1; + dock_popup_selected=-1; + //dock_select_popoup->set_(Size2(20,20)); + + for(int i=0;i<DOCK_SLOT_MAX;i++) { + dock_slot[i]->set_custom_minimum_size(Size2(230,220)); + dock_slot[i]->set_v_size_flags(Control::SIZE_EXPAND_FILL); + dock_slot[i]->set_popup(dock_select_popoup); + dock_slot[i]->connect("pre_popup_pressed",this,"_dock_pre_popup",varray(i)); + + //dock_slot[i]->set_tab_align(TabContainer::ALIGN_LEFT); + } - left_split = memnew( VSplitContainer ); - main_split->add_child(left_split); - left_split->set_h_size_flags(Control::SIZE_EXPAND_FILL); - left_split->set_collapsed(false); + dock_drag_timer = memnew( Timer ); + add_child(dock_drag_timer); + dock_drag_timer->set_wait_time(0.5); + dock_drag_timer->set_one_shot(true); + dock_drag_timer->connect("timeout",this,"_save_docks"); top_split = memnew( VSplitContainer ); - left_split->add_child(top_split); + center_split->add_child(top_split); top_split->set_v_size_flags(Control::SIZE_EXPAND_FILL); top_split->set_collapsed(true); @@ -3304,19 +4820,19 @@ EditorNode::EditorNode() { srt->add_constant_override("separation",0); - main_editor_tabs = memnew( Tabs ); +/* main_editor_tabs = memnew( Tabs ); main_editor_tabs->connect("tab_changed",this,"_editor_select"); - HBoxContainer *srth = memnew( HBoxContainer ); - srt->add_child( srth ); - EmptyControl *tec = memnew( EmptyControl ); - tec->set_minsize(Size2(100,0)); - tec->set_h_size_flags(Control::SIZE_EXPAND_FILL); - srth->add_child(tec); - srth->add_child(main_editor_tabs); - tec = memnew( EmptyControl ); - tec->set_minsize(Size2(100,0)); - srth->add_child(tec); - tec->set_h_size_flags(Control::SIZE_EXPAND_FILL); + main_editor_tabs->set_tab_close_display_policy(Tabs::SHOW_NEVER); +*/ + scene_tabs=memnew( Tabs ); + scene_tabs->add_tab("unsaved"); + scene_tabs->set_tab_align(Tabs::ALIGN_CENTER); + scene_tabs->set_tab_close_display_policy(Tabs::SHOW_ACTIVE_ONLY); + scene_tabs->connect("tab_changed",this,"_scene_tab_changed"); + scene_tabs->connect("right_button_pressed",this,"_scene_tab_script_edited"); + scene_tabs->connect("tab_close", this, "_scene_tab_closed"); + + srt->add_child(scene_tabs); scene_root_parent = memnew( Panel ); @@ -3338,6 +4854,8 @@ EditorNode::EditorNode() { scene_root = memnew( Viewport ); + + //scene_root_base->add_child(scene_root); scene_root->set_meta("_editor_disable_input",true); VisualServer::get_singleton()->viewport_set_hide_scenario(scene_root->get_viewport(),true); @@ -3363,6 +4881,7 @@ EditorNode::EditorNode() { animation_panel=pc; animation_panel->hide(); + HBoxContainer *animation_hb = memnew( HBoxContainer); animation_vb->add_child(animation_hb); @@ -3390,6 +4909,8 @@ EditorNode::EditorNode() { anim_close->set_pressed_texture( anim_close->get_icon("Close","EditorIcons")); + + PanelContainer *top_region = memnew( PanelContainer ); top_region->add_style_override("panel",gui_base->get_stylebox("hover","Button")); HBoxContainer *left_menu_hb = memnew( HBoxContainer ); @@ -3411,8 +4932,9 @@ EditorNode::EditorNode() { prev_scene->set_disabled(true); //left_menu_hb->add_child( prev_scene ); prev_scene->connect("pressed",this,"_menu_option",make_binds(FILE_OPEN_PREV)); - //gui_base->add_child(prev_scene); + gui_base->add_child(prev_scene); prev_scene->set_pos(Point2(3,24)); + prev_scene->hide(); Separator *vs=NULL; @@ -3420,15 +4942,20 @@ EditorNode::EditorNode() { file_menu->set_tooltip("Operations with scene files."); p=file_menu->get_popup(); p->add_item("New Scene",FILE_NEW_SCENE); + p->add_item("New Inherited Scene..",FILE_NEW_INHERITED_SCENE); p->add_item("Open Scene..",FILE_OPEN_SCENE,KEY_MASK_CMD+KEY_O); + p->add_separator(); p->add_item("Save Scene",FILE_SAVE_SCENE,KEY_MASK_CMD+KEY_S); p->add_item("Save Scene As..",FILE_SAVE_AS_SCENE,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_S); p->add_separator(); - p->add_item("Goto Prev. Scene",FILE_OPEN_PREV,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_P); + p->add_item("Close Scene",FILE_CLOSE,KEY_MASK_SHIFT+KEY_MASK_CTRL+KEY_W); + p->add_separator(); + p->add_item("Close Goto Prev. Scene",FILE_OPEN_PREV,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_P); p->add_submenu_item("Open Recent","RecentScenes",FILE_OPEN_RECENT); p->add_separator(); p->add_item("Quick Open Scene..",FILE_QUICK_OPEN_SCENE,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_O); p->add_item("Quick Open Script..",FILE_QUICK_OPEN_SCRIPT,KEY_MASK_ALT+KEY_MASK_CMD+KEY_O); + p->add_item("Quick Search File..",FILE_QUICK_OPEN_FILE,KEY_MASK_ALT+KEY_MASK_CMD+KEY_P); p->add_separator(); PopupMenu *pm_export = memnew(PopupMenu ); @@ -3450,6 +4977,8 @@ EditorNode::EditorNode() { p->add_separator(); p->add_item("Project Settings",RUN_SETTINGS); p->add_separator(); + p->add_item("Revert Scene",EDIT_REVERT); + p->add_separator(); p->add_item("Quit to Project List",RUN_PROJECT_MANAGER,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_Q); p->add_item("Quit",FILE_QUIT,KEY_MASK_CMD+KEY_Q); @@ -3458,6 +4987,18 @@ EditorNode::EditorNode() { p->add_child(recent_scenes); recent_scenes->connect("item_pressed",this,"_open_recent_scene"); + { + Control *sp = memnew( Control ); + sp->set_custom_minimum_size(Size2(30,0)); + menu_hb->add_child(sp); + } + + PanelContainer *editor_region = memnew( PanelContainer ); + editor_region->add_style_override("panel",gui_base->get_stylebox("hover","Button")); + main_editor_button_vb = memnew( HBoxContainer ); + editor_region->add_child(main_editor_button_vb); + menu_hb->add_child(editor_region); + //menu_hb->add_spacer(); #if 0 node_menu = memnew( MenuButton ); @@ -3494,8 +5035,6 @@ EditorNode::EditorNode() { left_menu_hb->add_child( import_menu ); p=import_menu->get_popup(); - p->add_item("Sub-Scene",FILE_IMPORT_SUBSCENE); - p->add_separator(); p->connect("item_pressed",this,"_menu_option"); export_button = memnew( ToolButton ); @@ -3533,18 +5072,18 @@ EditorNode::EditorNode() { play_button->set_icon(gui_base->get_icon("MainPlay","EditorIcons")); play_button->set_focus_mode(Control::FOCUS_NONE); play_button->connect("pressed", this,"_menu_option",make_binds(RUN_PLAY)); - play_button->set_tooltip("Start the scene (F5)."); + play_button->set_tooltip("Play the project (F5)."); - pause_button = memnew( ToolButton ); + /*pause_button = memnew( ToolButton ); //menu_panel->add_child(pause_button); - not needed for now? pause_button->set_toggle_mode(true); pause_button->set_icon(gui_base->get_icon("Pause","EditorIcons")); pause_button->set_focus_mode(Control::FOCUS_NONE); pause_button->connect("pressed", this,"_menu_option",make_binds(RUN_PAUSE)); pause_button->set_tooltip("Pause the scene (F7)."); - +*/ stop_button = memnew( ToolButton ); play_hb->add_child(stop_button); //stop_button->set_toggle_mode(true); @@ -3560,8 +5099,9 @@ EditorNode::EditorNode() { menu_hb->add_child(native_play_button); native_play_button->hide(); native_play_button->get_popup()->connect("item_pressed",this,"_run_in_device"); + run_native->connect("native_run",this,"_menu_option",varray(RUN_PLAY_NATIVE)); - VSeparator *s1 = memnew( VSeparator ); +// VSeparator *s1 = memnew( VSeparator ); // play_hb->add_child(s1); play_scene_button = memnew( ToolButton ); @@ -3580,30 +5120,34 @@ EditorNode::EditorNode() { play_custom_scene_button->connect("pressed", this,"_menu_option",make_binds(RUN_PLAY_CUSTOM_SCENE)); play_custom_scene_button->set_tooltip("Play custom scene ("+keycode_get_string(KEY_MASK_CMD|KEY_MASK_SHIFT|KEY_F5)+")."); - fileserver_menu = memnew( MenuButton ); - play_hb->add_child(fileserver_menu); - fileserver_menu->set_flat(true); - fileserver_menu->set_focus_mode(Control::FOCUS_NONE); - fileserver_menu->set_icon(gui_base->get_icon("FileServer","EditorIcons")); - //fileserver_menu->connect("pressed", this,"_menu_option",make_binds(RUN_PLAY_CUSTOM_SCENE)); - fileserver_menu->set_tooltip("Serve the project filesystem to remote clients."); - - p=fileserver_menu->get_popup(); - p->add_check_item("Enable File Server",RUN_FILE_SERVER); - p->set_item_tooltip(p->get_item_index(RUN_FILE_SERVER),"Enable/Disable the File Server."); + debug_button = memnew( MenuButton ); + debug_button->set_flat(true); + play_hb->add_child(debug_button); + //debug_button->set_toggle_mode(true); + debug_button->set_focus_mode(Control::FOCUS_NONE); + debug_button->set_icon(gui_base->get_icon("Remote","EditorIcons")); + //debug_button->connect("pressed", this,"_menu_option",make_binds(RUN_LIVE_DEBUG)); + debug_button->set_tooltip("Debug Options"); + + p=debug_button->get_popup(); + p->add_check_item("Live Editing",RUN_LIVE_DEBUG); + p->add_check_item("File Server",RUN_FILE_SERVER); + p->add_separator(); + p->add_check_item("Deploy Remote Debug",RUN_DEPLOY_REMOTE_DEBUG); + p->add_check_item("Deploy File Server Clients",RUN_DEPLOY_DUMB_CLIENTS); p->add_separator(); - p->add_check_item("Deploy Dumb Clients",RUN_DEPLOY_DUMB_CLIENTS); - //p->set_item_checked( p->get_item_index(RUN_DEPLOY_DUMB_CLIENTS),true ); - p->set_item_tooltip(p->get_item_index(RUN_DEPLOY_DUMB_CLIENTS),"Deploy dumb clients when the File Server is active."); + p->add_check_item("Visible Collision Shapes",RUN_DEBUG_COLLISONS); + p->add_check_item("Visible Navigation",RUN_DEBUG_NAVIGATION); p->connect("item_pressed",this,"_menu_option"); + /* run_settings_button = memnew( ToolButton ); //menu_hb->add_child(run_settings_button); //run_settings_button->set_toggle_mode(true); run_settings_button->set_focus_mode(Control::FOCUS_NONE); run_settings_button->set_icon(gui_base->get_icon("Run","EditorIcons")); run_settings_button->connect("pressed", this,"_menu_option",make_binds(RUN_SCENE_SETTINGS)); - +*/ /* run_settings_button = memnew( ToolButton ); @@ -3615,6 +5159,39 @@ EditorNode::EditorNode() { */ + progress_hb = memnew( BackgroundProgress ); + menu_hb->add_child(progress_hb); + + { + Control *sp = memnew( Control ); + sp->set_custom_minimum_size(Size2(30,0)); + menu_hb->add_child(sp); + } + + + PanelContainer *vu_cont = memnew( PanelContainer ); + vu_cont->add_style_override("panel",gui_base->get_stylebox("hover","Button")); + menu_hb->add_child(vu_cont); + + audio_vu = memnew( TextureProgress ); + CenterContainer *vu_cc = memnew( CenterContainer ); + vu_cc->add_child(audio_vu); + vu_cont->add_child(vu_cc); + audio_vu->set_under_texture(gui_base->get_icon("VuEmpty","EditorIcons")); + audio_vu->set_progress_texture(gui_base->get_icon("VuFull","EditorIcons")); + audio_vu->set_max(24); + audio_vu->set_min(-80); + audio_vu->set_step(0.01); + audio_vu->set_val(0); + + { + Control *sp = memnew( Control ); + sp->set_custom_minimum_size(Size2(30,0)); + menu_hb->add_child(sp); + } + + + top_region = memnew( PanelContainer ); top_region->add_style_override("panel",gui_base->get_stylebox("hover","Button")); HBoxContainer *right_menu_hb = memnew( HBoxContainer ); @@ -3646,6 +5223,15 @@ EditorNode::EditorNode() { sources_button->connect("pressed",this,"_menu_option",varray(SOURCES_REIMPORT)); sources_button->set_tooltip("Alerts when an external resource has changed."); + update_menu = memnew( MenuButton ); + update_menu->set_tooltip("Spins when the editor window repaints!"); + right_menu_hb->add_child(update_menu); + update_menu->set_icon(gui_base->get_icon("Progress1","EditorIcons")); + p=update_menu->get_popup(); + p->add_check_item("Update Always",SETTINGS_UPDATE_ALWAYS); + p->add_check_item("Update Changes",SETTINGS_UPDATE_CHANGES); + p->set_item_checked(1,true); + //sources_button->connect(); /* @@ -3657,68 +5243,84 @@ EditorNode::EditorNode() { - editor_hsplit = memnew( HSplitContainer ); - main_split->add_child(editor_hsplit); - editor_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL); + //editor_hsplit = memnew( HSplitContainer ); + //main_split->add_child(editor_hsplit); + //editor_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL); - editor_vsplit = memnew( VSplitContainer ); - editor_hsplit->add_child(editor_vsplit); + //editor_vsplit = memnew( VSplitContainer ); + //editor_hsplit->add_child(editor_vsplit); - top_pallete = memnew( TabContainer ); + //top_pallete = memnew( TabContainer ); scene_tree_dock = memnew( SceneTreeDock(this,scene_root,editor_selection,editor_data) ); scene_tree_dock->set_name("Scene"); - top_pallete->add_child(scene_tree_dock); - + //top_pallete->add_child(scene_tree_dock); + dock_slot[DOCK_SLOT_LEFT_UR]->add_child(scene_tree_dock); +#if 0 resources_dock = memnew( ResourcesDock(this) ); resources_dock->set_name("Resources"); - top_pallete->add_child(resources_dock); - top_pallete->set_v_size_flags(Control::SIZE_EXPAND_FILL); - - EmptyControl *editor_spacer = memnew( EmptyControl ); - editor_spacer->set_minsize(Size2(260,200)); + //top_pallete->add_child(resources_dock); + dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(resources_dock); + //top_pallete->set_v_size_flags(Control::SIZE_EXPAND_FILL); +#endif + dock_slot[DOCK_SLOT_RIGHT_BL]->hide(); + /*Control *editor_spacer = memnew( Control ); + editor_spacer->set_custom_minimum_size(Size2(260,200)); editor_spacer->set_v_size_flags(Control::SIZE_EXPAND_FILL); editor_vsplit->add_child( editor_spacer ); editor_spacer->add_child( top_pallete ); - top_pallete->set_area_as_parent_rect(); + top_pallete->set_area_as_parent_rect();*/ - prop_pallete = memnew( TabContainer ); + //prop_pallete = memnew( TabContainer ); - prop_pallete->set_v_size_flags(Control::SIZE_EXPAND_FILL); + //prop_pallete->set_v_size_flags(Control::SIZE_EXPAND_FILL); - editor_spacer = memnew( EmptyControl ); - editor_spacer->set_minsize(Size2(260,200)); + /*editor_spacer = memnew( Control ); + editor_spacer->set_custom_minimum_size(Size2(260,200)); editor_spacer->set_v_size_flags(Control::SIZE_EXPAND_FILL); editor_vsplit->add_child( editor_spacer ); editor_spacer->add_child( prop_pallete ); - prop_pallete->set_area_as_parent_rect(); + prop_pallete->set_area_as_parent_rect();*/ VBoxContainer *prop_editor_base = memnew( VBoxContainer ); prop_editor_base->set_name("Inspector"); // Properties? - prop_pallete->add_child(prop_editor_base); + dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(prop_editor_base); HBoxContainer *prop_editor_hb = memnew( HBoxContainer ); - prop_editor_base->add_child(prop_editor_hb); - editor_path = memnew( EditorPath(&editor_history) ); - editor_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); - prop_editor_hb->add_child(editor_path); - property_editor = memnew( PropertyEditor ); - property_editor->set_autoclear(true); - property_editor->set_show_categories(true); - property_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); - - property_editor->hide_top_label(); - - prop_editor_base->add_child( property_editor ); - property_editor->set_undo_redo(&editor_data.get_undo_redo()); + prop_editor_base->add_child(prop_editor_hb); + resource_new_button = memnew( ToolButton ); + resource_new_button->set_tooltip("Create a new resource in memory and edit it"); + resource_new_button->set_icon(gui_base->get_icon("New","EditorIcons")); + prop_editor_hb->add_child(resource_new_button); + resource_new_button->connect("pressed",this,"_menu_option",varray(RESOURCE_NEW)); + resource_new_button->set_focus_mode(Control::FOCUS_NONE); + + resource_load_button = memnew( ToolButton ); + resource_load_button->set_tooltip("Load an existing resource from disk and edit it"); + resource_load_button->set_icon(gui_base->get_icon("Load","EditorIcons")); + prop_editor_hb->add_child(resource_load_button); + resource_load_button->connect("pressed",this,"_menu_option",varray(RESOURCE_LOAD)); + resource_load_button->set_focus_mode(Control::FOCUS_NONE); + + resource_save_button = memnew( MenuButton ); + resource_save_button->set_tooltip("Save the currently edited resource"); + resource_save_button->set_icon(gui_base->get_icon("Save","EditorIcons")); + prop_editor_hb->add_child(resource_save_button); + resource_save_button->get_popup()->add_item("Save",RESOURCE_SAVE); + resource_save_button->get_popup()->add_item("Save As..",RESOURCE_SAVE_AS); + resource_save_button->get_popup()->connect("item_pressed",this,"_menu_option"); + resource_save_button->set_focus_mode(Control::FOCUS_NONE); + resource_save_button->set_disabled(true); + + prop_editor_hb->add_spacer(); - property_back = memnew( ToolButton ); property_back->set_icon( gui_base->get_icon("Back","EditorIcons") ); property_back->set_flat(true); property_back->set_tooltip("Go to the previous edited object in history."); + property_back->set_disabled(true); prop_editor_hb->add_child( property_back ); @@ -3726,28 +5328,89 @@ EditorNode::EditorNode() { property_forward->set_icon( gui_base->get_icon("Forward","EditorIcons") ); property_forward->set_flat(true); property_forward->set_tooltip("Go to the next edited object in history."); + property_forward->set_disabled(true); prop_editor_hb->add_child( property_forward ); + + editor_history_menu = memnew( MenuButton ); + editor_history_menu->set_tooltip("History of recently edited objects"); + editor_history_menu->set_icon( gui_base->get_icon("History","EditorIcons")); + prop_editor_hb->add_child(editor_history_menu); + editor_history_menu->connect("about_to_show",this,"_prepare_history"); + editor_history_menu->get_popup()->connect("item_pressed",this,"_select_history"); + + + prop_editor_hb = memnew( HBoxContainer ); //again... + + prop_editor_base->add_child(prop_editor_hb); + editor_path = memnew( EditorPath(&editor_history) ); + editor_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); + prop_editor_hb->add_child(editor_path); + + search_button = memnew( ToolButton ); + search_button->set_toggle_mode(true); + search_button->set_pressed(false); + search_button->set_icon(gui_base->get_icon("Zoom","EditorIcons")); + prop_editor_hb->add_child(search_button); + search_button->connect("toggled",this,"_toggle_search_bar"); + object_menu = memnew( MenuButton ); object_menu->set_icon(gui_base->get_icon("Tools","EditorIcons")); prop_editor_hb->add_child( object_menu ); object_menu->set_tooltip("Object properties."); + create_dialog = memnew( CreateDialog ); + gui_base->add_child(create_dialog); + create_dialog->set_base_type("Resource"); + create_dialog->connect("create",this,"_resource_created"); + + search_bar = memnew( HBoxContainer ); + search_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL); + prop_editor_base->add_child(search_bar); + search_bar->hide(); + + l = memnew( Label("Search: ") ); + search_bar->add_child(l); + + search_box = memnew( LineEdit ); + search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); + search_bar->add_child(search_box); + + ToolButton *clear_button = memnew( ToolButton ); + clear_button->set_icon(gui_base->get_icon("Close","EditorIcons")); + search_bar->add_child(clear_button); + clear_button->connect("pressed",this,"_clear_search_box"); + + property_editor = memnew( PropertyEditor ); + property_editor->set_autoclear(true); + property_editor->set_show_categories(true); + property_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); + property_editor->set_use_doc_hints(true); + + property_editor->hide_top_label(); + property_editor->register_text_enter(search_box); + + prop_editor_base->add_child( property_editor ); + property_editor->set_undo_redo(&editor_data.get_undo_redo()); + scenes_dock = memnew( ScenesDock(this) ); scenes_dock->set_name("FileSystem"); - prop_pallete->add_child(scenes_dock); + dock_slot[DOCK_SLOT_LEFT_BR]->add_child(scenes_dock); + //prop_pallete->add_child(scenes_dock); scenes_dock->connect("open",this,"open_request"); scenes_dock->connect("instance",this,"_instance_request"); log = memnew( EditorLog ); - left_split->add_child(log); + center_split->add_child(log); log->connect("close_request",this,"_close_messages"); log->connect("show_request",this,"_show_messages"); //left_split->set_dragger_visible(false); + + old_split_ofs=0; @@ -3760,42 +5423,23 @@ EditorNode::EditorNode() { animation_vb->add_child(animation_editor); - left_split->connect("resized",this,"_vp_resized"); + center_split->connect("resized",this,"_vp_resized"); animation_editor->hide(); - PanelContainer *bottom_pc = memnew( PanelContainer ); - main_vbox->add_child(bottom_pc); + /*PanelContainer *bottom_pc = memnew( PanelContainer ); + srt->add_child(bottom_pc); bottom_hb = memnew( HBoxContainer ); - bottom_pc->add_child(bottom_hb); + bottom_pc->add_child(bottom_hb);*/ - bottom_hb->add_child( log->get_button() ); + center_vb->add_child( log->get_button() ); log->get_button()->set_h_size_flags(Control::SIZE_EXPAND_FILL); - progress_hb = memnew( BackgroundProgress ); - bottom_hb->add_child(progress_hb); + //progress_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); - audio_vu = memnew( TextureProgress ); - CenterContainer *vu_cc = memnew( CenterContainer ); - vu_cc->add_child(audio_vu); - bottom_hb->add_child(vu_cc); - audio_vu->set_under_texture(gui_base->get_icon("VuEmpty","EditorIcons")); - audio_vu->set_progress_texture(gui_base->get_icon("VuFull","EditorIcons")); - audio_vu->set_max(24); - audio_vu->set_min(-80); - audio_vu->set_step(0.01); - audio_vu->set_val(0); - update_menu = memnew( MenuButton ); - update_menu->set_tooltip("Spins when the editor window repaints!"); - bottom_hb->add_child(update_menu); - update_menu->set_icon(gui_base->get_icon("Progress1","EditorIcons")); - p=update_menu->get_popup(); - p->add_check_item("Update Always",SETTINGS_UPDATE_ALWAYS); - p->add_check_item("Update Changes",SETTINGS_UPDATE_CHANGES); - p->set_item_checked(1,true); /* animation_menu = memnew( ToolButton ); @@ -3860,7 +5504,11 @@ EditorNode::EditorNode() { + dependency_error = memnew( DependencyErrorDialog ); + gui_base->add_child(dependency_error); + dependency_fixer = memnew( DependencyEditor ); + gui_base->add_child( dependency_fixer ); settings_config_dialog = memnew( EditorSettingsDialog ); gui_base->add_child(settings_config_dialog); @@ -3894,7 +5542,7 @@ EditorNode::EditorNode() { about->get_ok()->set_text("Thanks!"); about->set_hide_on_ok(true); Label *about_text = memnew( Label ); - about_text->set_text(VERSION_FULL_NAME"\n(c) 2008-2014 Juan Linietsky, Ariel Manzur.\n"); + about_text->set_text(VERSION_FULL_NAME"\n(c) 2008-2015 Juan Linietsky, Ariel Manzur.\n"); about_text->set_pos(Point2(gui_base->get_icon("Logo","EditorIcons")->get_size().width+30,20)); gui_base->add_child(about); about->add_child(about_text); @@ -3903,6 +5551,8 @@ EditorNode::EditorNode() { logo->set_pos(Point2(20,20)); logo->set_texture(gui_base->get_icon("Logo","EditorIcons") ); + warning = memnew( AcceptDialog ); + add_child(warning); @@ -3916,7 +5566,7 @@ EditorNode::EditorNode() { file_templates->add_filter("*.tpz ; Template Package"); - file = memnew( FileDialog ); + file = memnew( EditorFileDialog ); gui_base->add_child(file); file->set_current_dir("res://"); @@ -3990,8 +5640,9 @@ EditorNode::EditorNode() { editor_import_export->add_import_plugin( Ref<EditorTextureImportPlugin>( memnew(EditorTextureImportPlugin(this,EditorTextureImportPlugin::MODE_TEXTURE_2D) ))); - editor_import_export->add_import_plugin( Ref<EditorTextureImportPlugin>( memnew(EditorTextureImportPlugin(this,EditorTextureImportPlugin::MODE_TEXTURE_3D) ))); editor_import_export->add_import_plugin( Ref<EditorTextureImportPlugin>( memnew(EditorTextureImportPlugin(this,EditorTextureImportPlugin::MODE_ATLAS) ))); + editor_import_export->add_import_plugin( Ref<EditorTextureImportPlugin>( memnew(EditorTextureImportPlugin(this,EditorTextureImportPlugin::MODE_LARGE) ))); + editor_import_export->add_import_plugin( Ref<EditorTextureImportPlugin>( memnew(EditorTextureImportPlugin(this,EditorTextureImportPlugin::MODE_TEXTURE_3D) ))); Ref<EditorSceneImportPlugin> _scene_import = memnew(EditorSceneImportPlugin(this) ); Ref<EditorSceneImporterCollada> _collada_import = memnew( EditorSceneImporterCollada); _scene_import->add_importer(_collada_import); @@ -4004,19 +5655,17 @@ EditorNode::EditorNode() { editor_import_export->add_import_plugin( Ref<EditorSampleImportPlugin>( memnew(EditorSampleImportPlugin(this)))); editor_import_export->add_import_plugin( Ref<EditorTranslationImportPlugin>( memnew(EditorTranslationImportPlugin(this)))); - - for(int i=0;i<editor_import_export->get_import_plugin_count();i++) { - import_menu->get_popup()->add_item(editor_import_export->get_import_plugin(i)->get_visible_name(),IMPORT_PLUGIN_BASE+i); - } - editor_import_export->add_export_plugin( Ref<EditorTextureExportPlugin>( memnew(EditorTextureExportPlugin))); + editor_import_export->add_export_plugin( Ref<EditorSampleExportPlugin>( memnew(EditorSampleExportPlugin))); add_editor_plugin( memnew( CanvasItemEditorPlugin(this) ) ); add_editor_plugin( memnew( SpatialEditorPlugin(this) ) ); add_editor_plugin( memnew( ScriptEditorPlugin(this) ) ); - add_editor_plugin( memnew( EditorHelpPlugin(this) ) ); add_editor_plugin( memnew( AnimationPlayerEditorPlugin(this) ) ); - add_editor_plugin( memnew( ShaderEditorPlugin(this) ) ); + add_editor_plugin( memnew( ShaderGraphEditorPlugin(this,true) ) ); + add_editor_plugin( memnew( ShaderGraphEditorPlugin(this,false) ) ); + add_editor_plugin( memnew( ShaderEditorPlugin(this,true) ) ); + add_editor_plugin( memnew( ShaderEditorPlugin(this,false) ) ); add_editor_plugin( memnew( CameraEditorPlugin(this) ) ); add_editor_plugin( memnew( SampleEditorPlugin(this) ) ); add_editor_plugin( memnew( SampleLibraryEditorPlugin(this) ) ); @@ -4031,28 +5680,39 @@ EditorNode::EditorNode() { add_editor_plugin( memnew( ParticlesEditorPlugin(this) ) ); add_editor_plugin( memnew( ResourcePreloaderEditorPlugin(this) ) ); add_editor_plugin( memnew( ItemListEditorPlugin(this) ) ); - add_editor_plugin( memnew( RichTextEditorPlugin(this) ) ); + //add_editor_plugin( memnew( RichTextEditorPlugin(this) ) ); add_editor_plugin( memnew( CollisionPolygonEditorPlugin(this) ) ); add_editor_plugin( memnew( CollisionPolygon2DEditorPlugin(this) ) ); add_editor_plugin( memnew( TileSetEditorPlugin(this) ) ); add_editor_plugin( memnew( TileMapEditorPlugin(this) ) ); add_editor_plugin( memnew( SpriteFramesEditorPlugin(this) ) ); + add_editor_plugin( memnew( SpriteRegionEditorPlugin(this) ) ); add_editor_plugin( memnew( Particles2DEditorPlugin(this) ) ); add_editor_plugin( memnew( Path2DEditorPlugin(this) ) ); add_editor_plugin( memnew( PathEditorPlugin(this) ) ); add_editor_plugin( memnew( BakedLightEditorPlugin(this) ) ); add_editor_plugin( memnew( Polygon2DEditorPlugin(this) ) ); + add_editor_plugin( memnew( LightOccluder2DEditorPlugin(this) ) ); + add_editor_plugin( memnew( NavigationPolygonEditorPlugin(this) ) ); + add_editor_plugin( memnew( ColorRampEditorPlugin(this) ) ); + add_editor_plugin( memnew( CollisionShape2DEditorPlugin(this) ) ); for(int i=0;i<EditorPlugins::get_plugin_count();i++) add_editor_plugin( EditorPlugins::create(i,this) ); + + resource_preview->add_preview_generator( Ref<EditorTexturePreviewPlugin>( memnew(EditorTexturePreviewPlugin ))); + resource_preview->add_preview_generator( Ref<EditorPackedScenePreviewPlugin>( memnew(EditorPackedScenePreviewPlugin ))); + resource_preview->add_preview_generator( Ref<EditorMaterialPreviewPlugin>( memnew(EditorMaterialPreviewPlugin ))); + resource_preview->add_preview_generator( Ref<EditorScriptPreviewPlugin>( memnew(EditorScriptPreviewPlugin ))); + resource_preview->add_preview_generator( Ref<EditorSamplePreviewPlugin>( memnew(EditorSamplePreviewPlugin ))); + resource_preview->add_preview_generator( Ref<EditorMeshPreviewPlugin>( memnew(EditorMeshPreviewPlugin ))); + circle_step_msec=OS::get_singleton()->get_ticks_msec(); circle_step_frame=OS::get_singleton()->get_frames_drawn();; circle_step=0; - - import_menu->get_popup()->add_separator(); - import_menu->get_popup()->add_item("Re-Import..",SETTINGS_IMPORT); + _rebuild_import_menu(); editor_plugin_screen=NULL; editor_plugin_over=NULL; @@ -4067,9 +5727,9 @@ EditorNode::EditorNode() { Globals::get_singleton()->set("debug/indicators_enabled",true); Globals::get_singleton()->set("render/room_cull_enabled",false); - theme->set_color("prop_category","Editor",Color::hex(0x3f3945ff)); - theme->set_color("prop_section","Editor",Color::hex(0x38323dff)); - theme->set_color("prop_subsection","Editor",Color::hex(0x342e39ff)); + theme->set_color("prop_category","Editor",Color::hex(0x3f3a44ff)); + theme->set_color("prop_section","Editor",Color::hex(0x35313aff)); + theme->set_color("prop_subsection","Editor",Color::hex(0x312e37ff)); theme->set_color("fg_selected","Editor",Color::html("ffbd8e8e")); theme->set_color("fg_error","Editor",Color::html("ffbd8e8e")); @@ -4100,8 +5760,8 @@ EditorNode::EditorNode() { } - edited_scene=NULL; - saved_version=0; + //edited_scene=NULL; + saved_version=1; unsaved_cache=true; _last_instanced_scene=NULL; @@ -4155,17 +5815,28 @@ EditorNode::EditorNode() { Node::set_human_readable_collision_renaming(true); + + // Ref<ImageTexture> it = gui_base->get_icon("logo","Icons"); // OS::get_singleton()->set_icon( it->get_data() ); for(int i=0;i<_init_callbacks.size();i++) _init_callbacks[i](); + editor_data.add_edited_scene(-1); + editor_data.set_edited_scene(0); + _update_scene_tabs(); + + _load_docks(); + + } EditorNode::~EditorNode() { + + memdelete( EditorHelp::get_doc_data() ); memdelete(editor_selection); memdelete(file_server); EditorSettings::destroy(); diff --git a/tools/editor/editor_node.h b/tools/editor/editor_node.h index 7b66a7809e..7d8b97688e 100644 --- a/tools/editor/editor_node.h +++ b/tools/editor/editor_node.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -78,7 +78,7 @@ #include "tools/editor/editor_plugin.h" #include "fileserver/editor_file_server.h" - +#include "editor_resource_preview.h" @@ -94,6 +94,8 @@ typedef void (*EditorNodeInitCallback)(); + + class EditorNode : public Node { OBJ_TYPE( EditorNode, Node ); @@ -105,9 +107,12 @@ class EditorNode : public Node { enum MenuOptions { FILE_NEW_SCENE, + FILE_NEW_INHERITED_SCENE, FILE_OPEN_SCENE, FILE_SAVE_SCENE, FILE_SAVE_AS_SCENE, + FILE_SAVE_BEFORE_RUN, + FILE_SAVE_AND_RUN, FILE_IMPORT_SUBSCENE, FILE_EXPORT_PROJECT, FILE_EXPORT_MESH_LIBRARY, @@ -119,18 +124,22 @@ class EditorNode : public Node { FILE_OPEN_OLD_SCENE, FILE_QUICK_OPEN_SCENE, FILE_QUICK_OPEN_SCRIPT, + FILE_QUICK_OPEN_FILE, FILE_RUN_SCRIPT, FILE_OPEN_PREV, + FILE_CLOSE, FILE_QUIT, FILE_EXTERNAL_OPEN_SCENE, EDIT_UNDO, EDIT_REDO, + EDIT_REVERT, RESOURCE_NEW, RESOURCE_LOAD, RESOURCE_SAVE, RESOURCE_SAVE_AS, RESOURCE_UNREF, RESOURCE_COPY, + RESOURCE_PASTE, OBJECT_COPY_PARAMS, OBJECT_PASTE_PARAMS, OBJECT_UNIQUE_RESOURCES, @@ -140,12 +149,17 @@ class EditorNode : public Node { RUN_PAUSE, RUN_STOP, RUN_PLAY_SCENE, + RUN_PLAY_NATIVE, RUN_PLAY_CUSTOM_SCENE, RUN_SCENE_SETTINGS, RUN_SETTINGS, RUN_PROJECT_MANAGER, RUN_FILE_SERVER, RUN_DEPLOY_DUMB_CLIENTS, + RUN_LIVE_DEBUG, + RUN_DEBUG_COLLISONS, + RUN_DEBUG_NAVIGATION, + RUN_DEPLOY_REMOTE_DEBUG, SETTINGS_UPDATE_ALWAYS, SETTINGS_UPDATE_CHANGES, SETTINGS_IMPORT, @@ -159,25 +173,54 @@ class EditorNode : public Node { SOURCES_REIMPORT, DEPENDENCY_LOAD_CHANGED_IMAGES, DEPENDENCY_UPDATE_IMPORTED, + SCENE_TAB_CLOSE, IMPORT_PLUGIN_BASE=100, OBJECT_METHOD_BASE=500 }; + enum DockSlot { + DOCK_SLOT_LEFT_UL, + DOCK_SLOT_LEFT_BL, + DOCK_SLOT_LEFT_UR, + DOCK_SLOT_LEFT_BR, + DOCK_SLOT_RIGHT_UL, + DOCK_SLOT_RIGHT_BL, + DOCK_SLOT_RIGHT_UR, + DOCK_SLOT_RIGHT_BR, + DOCK_SLOT_MAX + }; - - Node *edited_scene; //scene being edited + //Node *edited_scene; //scene being edited Viewport *scene_root; //root of the scene being edited - Ref<ResourceImportMetadata> scene_import_metadata; + //Ref<ResourceImportMetadata> scene_import_metadata; Control* scene_root_parent; Control *gui_base; VBoxContainer *main_vbox; - HSplitContainer *main_split; - VSplitContainer *left_split; + + //split + + HSplitContainer *left_l_hsplit; + VSplitContainer *left_l_vsplit; + HSplitContainer *left_r_hsplit; + VSplitContainer *left_r_vsplit; + HSplitContainer *main_hsplit; + HSplitContainer *right_hsplit; + VSplitContainer *right_l_vsplit; + VSplitContainer *right_r_vsplit; + + VSplitContainer *center_split; + + //main tabs + + Tabs *scene_tabs; + int tab_closing; + + int old_split_ofs; VSplitContainer *top_split; HBoxContainer *bottom_hb; @@ -187,8 +230,9 @@ class EditorNode : public Node { TextureButton *anim_close; Panel *menu_panel; - HSplitContainer *editor_hsplit; - VSplitContainer *editor_vsplit; + + //HSplitContainer *editor_hsplit; + //VSplitContainer *editor_vsplit; HBoxContainer *menu_hb; Control *viewport; MenuButton *file_menu; @@ -205,35 +249,44 @@ class EditorNode : public Node { ToolButton *animation_menu; ToolButton *play_scene_button; ToolButton *play_custom_scene_button; + MenuButton *debug_button; + ToolButton *search_button; TextureProgress *audio_vu; - MenuButton *fileserver_menu; + //MenuButton *fileserver_menu; TextEdit *load_errors; AcceptDialog *load_error_dialog; Control *scene_root_base; + Ref<Theme> theme; PopupMenu *recent_scenes; Button *property_back; Button *property_forward; SceneTreeDock *scene_tree_dock; - ResourcesDock *resources_dock; + //ResourcesDock *resources_dock; PropertyEditor *property_editor; ScenesDock *scenes_dock; EditorRunNative *run_native; + HBoxContainer *search_bar; + LineEdit *search_box; + + CreateDialog *create_dialog; + CallDialog *call_dialog; ConfirmationDialog *confirmation; ConfirmationDialog *import_confirmation; ConfirmationDialog *open_recent_confirmation; AcceptDialog *accept; AcceptDialog *about; + AcceptDialog *warning; //OptimizedPresetsDialog *optimized_presets; EditorSettingsDialog *settings_config_dialog; RunSettingsDialog *run_settings_dialog; ProjectSettings *project_settings; - FileDialog *file; + EditorFileDialog *file; FileDialog *file_templates; FileDialog *file_export; FileDialog *file_export_lib; @@ -244,8 +297,8 @@ class EditorNode : public Node { String current_path; MenuButton *update_menu; ToolButton *sources_button; - TabContainer *prop_pallete; - TabContainer *top_pallete; + //TabContainer *prop_pallete; + //TabContainer *top_pallete; String defer_load_scene; String defer_translatable; String defer_optimize; @@ -258,12 +311,18 @@ class EditorNode : public Node { HBoxContainer *animation_panel_hb; VBoxContainer *animation_vb; EditorPath *editor_path; + ToolButton *resource_new_button; + ToolButton *resource_load_button; + MenuButton *resource_save_button; + MenuButton *editor_history_menu; AnimationKeyEditor *animation_editor; EditorLog *log; CenterContainer *tabs_center; EditorQuickOpen *quick_open; EditorQuickOpen *quick_run; - Tabs *main_editor_tabs; + + HBoxContainer *main_editor_button_vb; + Vector<ToolButton*> main_editor_buttons; Vector<EditorPlugin*> editor_table; EditorReImportDialog *reimport_dialog; @@ -272,6 +331,19 @@ class EditorNode : public Node { ProgressDialog *progress_dialog; BackgroundProgress *progress_hb; + DependencyErrorDialog *dependency_error; + DependencyEditor *dependency_fixer; + + TabContainer *dock_slot[DOCK_SLOT_MAX]; + Rect2 dock_select_rect[DOCK_SLOT_MAX]; + int dock_select_rect_over; + PopupPanel *dock_select_popoup; + Control *dock_select; + ToolButton *dock_tab_move_left; + ToolButton *dock_tab_move_right; + int dock_popup_selected; + Timer *dock_drag_timer; + String _tmp_import_path; EditorImportExport *editor_import_export; @@ -282,8 +354,10 @@ class EditorNode : public Node { bool reference_resource_mem; bool save_external_resources_mem; uint64_t saved_version; + uint64_t last_checked_version; bool unsaved_cache; String open_navigate; + bool changing_scene; uint32_t circle_step_msec; uint64_t circle_step_frame; @@ -299,6 +373,7 @@ class EditorNode : public Node { EditorSelection *editor_selection; ProjectExport *project_export; ProjectExportDialog *project_export_settings; + EditorResourcePreview *resource_preview; EditorFileServer *file_server; @@ -314,6 +389,7 @@ class EditorNode : public Node { int current_option; //void _animation_visibility_toggle(); + void _resource_created(); void _resource_selected(const RES& p_res,const String& p_property=""); void _menu_option(int p_option); void _menu_confirm_current(); @@ -322,6 +398,9 @@ class EditorNode : public Node { void _property_editor_forward(); void _property_editor_back(); + void _select_history(int p_idx); + void _prepare_history(); + void _fs_changed(); void _sources_changed(bool p_exist); @@ -332,16 +411,19 @@ class EditorNode : public Node { void _set_scene_metadata(); void _get_scene_metadata(); void _update_title(); + void _update_scene_tabs(); void _close_messages(); void _show_messages(); void _vp_resized(); + void _rebuild_import_menu(); + void _save_scene(String p_file); void _instance_request(const String& p_path); - void _property_keyed(const String& p_keyed,const Variant& p_value); + void _property_keyed(const String& p_keyed, const Variant& p_value, bool p_advance); void _transform_keyed(Object *sp,const String& p_sub,const Transform& p_key); void _update_keying(); @@ -357,7 +439,7 @@ class EditorNode : public Node { void _add_to_recent_scenes(const String& p_scene); void _update_recent_scenes(); void _open_recent_scene(int p_idx); - void _open_recent_scene_confirm(); + //void _open_recent_scene_confirm(); String _recent_scene; bool convert_old; @@ -374,19 +456,38 @@ class EditorNode : public Node { String import_reload_fn; Set<FileDialog*> file_dialogs; + Set<EditorFileDialog*> editor_file_dialogs; + Map<String,Ref<Texture> > icon_type_cache; static Ref<Texture> _file_dialog_get_icon(const String& p_path); static void _file_dialog_register(FileDialog *p_dialog); static void _file_dialog_unregister(FileDialog *p_dialog); + static void _editor_file_dialog_register(EditorFileDialog *p_dialog); + static void _editor_file_dialog_unregister(EditorFileDialog *p_dialog); void _cleanup_scene(); + void _remove_edited_scene(); + void _remove_scene(int index); + bool _find_and_save_resource(RES p_res,Map<RES,bool>& processed,int32_t flags); + bool _find_and_save_edited_subresources(Object *obj,Map<RES,bool>& processed,int32_t flags); + void _save_edited_subresources(Node* scene,Map<RES,bool>& processed,int32_t flags); - bool _find_and_save_edited_subresources(Object *obj,Set<RES>& processed,int32_t flags); - void _save_edited_subresources(Node* scene,Set<RES>& processed,int32_t flags); + void _find_node_types(Node* p_node, int&count_2d, int&count_3d); + void _save_scene_with_preview(String p_file); + Map<String,Set<String> > dependency_errors; + + static void _dependency_error_report(void *ud,const String& p_path,const String& p_dep,const String& p_type) { + EditorNode*en=(EditorNode*)ud; + if (!en->dependency_errors.has(p_path)) + en->dependency_errors[p_path]=Set<String>(); + en->dependency_errors[p_path].insert(p_dep+"::"+p_type); + + } + struct ExportDefer { String platform; String path; @@ -401,6 +502,27 @@ class EditorNode : public Node { bool _find_scene_in_use(Node* p_node,const String& p_path) const; + void _dock_select_input(const InputEvent& p_input); + void _dock_move_left(); + void _dock_move_right(); + void _dock_select_draw(); + void _dock_pre_popup(int p_which); + void _dock_split_dragged(int ofs); + void _dock_popup_exit(); + void _scene_tab_changed(int p_tab); + void _scene_tab_closed(int p_tab); + void _scene_tab_script_edited(int p_tab); + + Dictionary _get_main_scene_state(); + void _set_main_scene_state(Dictionary p_state); + + int _get_current_main_editor(); + + void _save_docks(); + void _load_docks(); + + void _toggle_search_bar(bool p_pressed); + void _clear_search_box(); protected: void _notification(int p_what); @@ -417,10 +539,15 @@ public: static void add_editor_plugin(EditorPlugin *p_editor); static void remove_editor_plugin(EditorPlugin *p_editor); + void add_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import); + void remove_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import); + void edit_node(Node *p_node); void edit_resource(const Ref<Resource>& p_resource); void open_resource(const String& p_type=""); + + void save_resource_in_path(const Ref<Resource>& p_resource,const String& p_path); void save_resource(const Ref<Resource>& p_resource); void save_resource_as(const Ref<Resource>& p_resource); @@ -432,7 +559,7 @@ public: void open_request(const String& p_path); - void set_edited_scene(Node *p_scene); + bool is_changing_scene() const; static EditorLog *get_log() { return singleton->log; } @@ -445,15 +572,23 @@ public: void hide_animation_player_editors(); void animation_panel_make_visible(bool p_visible); - Node *get_edited_scene() { return edited_scene; } + void set_edited_scene(Node *p_scene); + + Node *get_edited_scene() { return editor_data.get_edited_scene_root(); } Viewport *get_scene_root() { return scene_root; } //root of the scene being edited Error save_optimized_copy(const String& p_scene,const String& p_preset); + void fix_dependencies(const String& p_for_file); void clear_scene() { _cleanup_scene(); } - Error load_scene(const String& p_scene); + Error load_scene(const String& p_scene, bool p_ignore_broken_deps=false, bool p_set_inherited=false); Error load_resource(const String& p_scene); + bool is_scene_open(const String& p_path); + + void set_current_version(uint64_t p_version); + void set_current_scene(int p_idx); + static EditorData& get_editor_data() { return singleton->editor_data; } static VSplitContainer *get_top_split() { return singleton->top_split; } @@ -472,6 +607,12 @@ public: void stop_child_process(); + Ref<Theme> get_editor_theme() const { return theme; } + + + void show_warning(const String& p_text); + + Error export_platform(const String& p_platform, const String& p_path, bool p_debug,const String& p_password,bool p_quit_after=false); static void register_editor_types(); @@ -494,6 +635,9 @@ public: bool is_scene_in_use(const String& p_path); void scan_import_changes(); + + void save_layout(); + EditorNode(); ~EditorNode(); void get_singleton(const char* arg1, bool arg2); diff --git a/tools/editor/editor_path.cpp b/tools/editor/editor_path.cpp index 83ca04fcab..94e2efe346 100644 --- a/tools/editor/editor_path.cpp +++ b/tools/editor/editor_path.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -73,7 +73,11 @@ void EditorPath::_notification(int p_what) { if (obj->cast_to<Resource>()) { Resource *r = obj->cast_to<Resource>(); - name=r->get_name(); + if (r->get_path().is_resource_file()) + name=r->get_path().get_file(); + else + name=r->get_name(); + if (name=="") name=r->get_type(); } else if (obj->cast_to<Node>()) { diff --git a/tools/editor/editor_path.h b/tools/editor/editor_path.h index effa87b423..2edaeb92a3 100644 --- a/tools/editor/editor_path.h +++ b/tools/editor/editor_path.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/editor_plugin.cpp b/tools/editor/editor_plugin.cpp index 2a2ad63d32..7417d707bb 100644 --- a/tools/editor/editor_plugin.cpp +++ b/tools/editor/editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -73,6 +73,13 @@ void EditorPlugin::add_custom_control(CustomControlContainer p_location,Control } break; case CONTAINER_CANVAS_EDITOR_SIDE: { + CanvasItemEditor::get_singleton()->get_palette_split()->add_child(p_control); + CanvasItemEditor::get_singleton()->get_palette_split()->move_child(p_control,0); + + } break; + case CONTAINER_CANVAS_EDITOR_BOTTOM: { + + CanvasItemEditor::get_singleton()->get_bottom_split()->add_child(p_control); } break; @@ -191,6 +198,13 @@ bool EditorPlugin::get_remove_list(List<Node*> *p_list) { void EditorPlugin::restore_global_state() {} void EditorPlugin::save_global_state() {} +void EditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) { + +} + +void EditorPlugin::get_window_layout(Ref<ConfigFile> p_layout){ + +} void EditorPlugin::_bind_methods() { diff --git a/tools/editor/editor_plugin.h b/tools/editor/editor_plugin.h index bcde0f73fb..0f3a1e2e3c 100644 --- a/tools/editor/editor_plugin.h +++ b/tools/editor/editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,7 +32,7 @@ #include "scene/main/node.h" #include "scene/resources/texture.h" #include "undo_redo.h" - +#include "io/config_file.h" /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -66,7 +66,8 @@ public: CONTAINER_SPATIAL_EDITOR_SIDE, CONTAINER_SPATIAL_EDITOR_BOTTOM, CONTAINER_CANVAS_EDITOR_MENU, - CONTAINER_CANVAS_EDITOR_SIDE + CONTAINER_CANVAS_EDITOR_SIDE, + CONTAINER_CANVAS_EDITOR_BOTTOM }; //TODO: send a resoucre for editing to the editor node? @@ -89,6 +90,8 @@ public: virtual void apply_changes() ; // if changes are pending in editor, apply them virtual void get_breakpoints(List<String> *p_breakpoints); virtual bool get_remove_list(List<Node*> *p_list); + virtual void set_window_layout(Ref<ConfigFile> p_layout); + virtual void get_window_layout(Ref<ConfigFile> p_layout); virtual void restore_global_state(); virtual void save_global_state(); diff --git a/tools/editor/editor_reimport_dialog.cpp b/tools/editor/editor_reimport_dialog.cpp index 034ac58a8e..8842a485b3 100644 --- a/tools/editor/editor_reimport_dialog.cpp +++ b/tools/editor/editor_reimport_dialog.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,7 +33,7 @@ void EditorReImportDialog::popup_reimport() { if (EditorFileSystem::get_singleton()->is_scanning()) { error->set_text("Please wait for scan to complete"); - error->popup_centered(Size2(250,100)); + error->popup_centered_minsize(); return; } @@ -70,7 +70,7 @@ void EditorReImportDialog::popup_reimport() { if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->get_filename()=="") { error->set_text("Current scene must be saved to re-import."); - error->popup_centered(Size2(250,100)); + error->popup_centered_minsize(); get_ok()->set_text("Re-Import"); get_ok()->set_disabled(true); return; @@ -93,7 +93,7 @@ void EditorReImportDialog::ok_pressed() { if (EditorFileSystem::get_singleton()->is_scanning()) { error->set_text("Please wait for scan to complete"); - error->popup_centered(Size2(250,100)); + error->popup_centered_minsize(); return; } diff --git a/tools/editor/editor_reimport_dialog.h b/tools/editor/editor_reimport_dialog.h index a5fac262fc..9726bac805 100644 --- a/tools/editor/editor_reimport_dialog.h +++ b/tools/editor/editor_reimport_dialog.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/editor_resource_preview.cpp b/tools/editor/editor_resource_preview.cpp new file mode 100644 index 0000000000..f684b49a2c --- /dev/null +++ b/tools/editor/editor_resource_preview.cpp @@ -0,0 +1,261 @@ +#include "editor_resource_preview.h" +#include "editor_settings.h" +#include "os/file_access.h" +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "globals.h" + + +Ref<Texture> EditorResourcePreviewGenerator::generate_from_path(const String& p_path) { + + RES res = ResourceLoader::load(p_path); + if (!res.is_valid()) + return res; + return generate(res); +} + +EditorResourcePreviewGenerator::EditorResourcePreviewGenerator() { + + +} + + +EditorResourcePreview* EditorResourcePreview::singleton=NULL; + + +void EditorResourcePreview::_thread_func(void *ud) { + + EditorResourcePreview *erp=(EditorResourcePreview*)ud; + erp->_thread(); + +} + + +void EditorResourcePreview::_preview_ready(const String& p_str,const Ref<Texture>& p_texture,ObjectID id,const StringName& p_func,const Variant& p_ud) { + + //print_line("preview is ready"); + preview_mutex->lock(); + + Item item; + item.order=order++; + item.preview=p_texture; + cache[p_str]=item; + + Object *recv = ObjectDB::get_instance(id); + if (recv) { + recv->call_deferred(p_func,p_str,p_texture,p_ud); + } + + preview_mutex->unlock(); +} + +Ref<Texture> EditorResourcePreview::_generate_preview(const QueueItem& p_item,const String& cache_base) { + + String type = ResourceLoader::get_resource_type(p_item.path); + //print_line("resource type is: "+type); + + if (type=="") + return Ref<Texture>(); //could not guess type + + Ref<Texture> generated; + + for(int i=0;i<preview_generators.size();i++) { + if (!preview_generators[i]->handles(type)) + continue; + generated = preview_generators[i]->generate_from_path(p_item.path); + + break; + } + + if (generated.is_valid()) { + //print_line("was generated"); + int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size"); + //wow it generated a preview... save cache + ResourceSaver::save(cache_base+".png",generated); + FileAccess *f=FileAccess::open(cache_base+".txt",FileAccess::WRITE); + f->store_line(itos(thumbnail_size)); + f->store_line(itos(FileAccess::get_modified_time(p_item.path))); + f->store_line(FileAccess::get_md5(p_item.path)); + memdelete(f); + } else { + //print_line("was not generated"); + + } + + return generated; +} + +void EditorResourcePreview::_thread() { + + //print_line("begin thread"); + while(!exit) { + + //print_line("wait for semaphore"); + preview_sem->wait(); + preview_mutex->lock(); + + //print_line("blue team go"); + + if (queue.size()) { + + + + QueueItem item = queue.front()->get(); + queue.pop_front(); + preview_mutex->unlock(); + + Ref<Texture> texture; + + //print_line("pop from queue "+item.path); + + uint64_t modtime = FileAccess::get_modified_time(item.path); + int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size"); + + if (cache.has(item.path)) { + //already has it because someone loaded it, just let it know it's ready + call_deferred("_preview_ready",item.path,cache[item.path].preview,item.id,item.function,item.userdata); + + } else { + + + String temp_path=EditorSettings::get_singleton()->get_settings_path().plus_file("tmp"); + String cache_base = Globals::get_singleton()->globalize_path(item.path).md5_text(); + cache_base = temp_path.plus_file("resthumb-"+cache_base); + + //does not have it, try to load a cached thumbnail + + String file = cache_base+".txt"; + //print_line("cachetxt at "+file); + FileAccess *f=FileAccess::open(file,FileAccess::READ); + if (!f) { + + //print_line("generate because not cached"); + + //generate + texture=_generate_preview(item,cache_base); + } else { + + int tsize = f->get_line().to_int64(); + uint64_t last_modtime = f->get_line().to_int64(); + + bool cache_valid = true; + + if (tsize!=thumbnail_size) { + cache_valid=false; + memdelete(f); + } else if (last_modtime!=modtime) { + + String last_md5 = f->get_line(); + String md5 = FileAccess::get_md5(item.path); + memdelete(f); + + if (last_md5!=md5) { + + cache_valid=false; + } else { + //update modified time + + f=FileAccess::open(file,FileAccess::WRITE); + f->store_line(itos(modtime)); + f->store_line(md5); + memdelete(f); + } + } else { + memdelete(f); + } + + if (cache_valid) { + + texture = ResourceLoader::load(cache_base+".png","ImageTexture",true); + if (!texture.is_valid()) { + //well fuck + cache_valid=false; + } + } + + if (!cache_valid) { + + texture=_generate_preview(item,cache_base); + } + + } + + //print_line("notify of preview ready"); + call_deferred("_preview_ready",item.path,texture,item.id,item.function,item.userdata); + + } + + } else { + preview_mutex->unlock(); + } + + } +} + + + + +void EditorResourcePreview::queue_resource_preview(const String& p_path, Object* p_receiver, const StringName& p_receiver_func, const Variant& p_userdata) { + + ERR_FAIL_NULL(p_receiver); + preview_mutex->lock(); + if (cache.has(p_path)) { + cache[p_path].order=order++; + p_receiver->call_deferred(p_receiver_func,p_path,cache[p_path].preview,p_userdata); + preview_mutex->unlock(); + return; + + } + + //print_line("send to thread "+p_path); + QueueItem item; + item.function=p_receiver_func; + item.id=p_receiver->get_instance_ID(); + item.path=p_path; + item.userdata=p_userdata; + + queue.push_back(item); + preview_mutex->unlock(); + preview_sem->post(); + +} + +void EditorResourcePreview::add_preview_generator(const Ref<EditorResourcePreviewGenerator>& p_generator) { + + preview_generators.push_back(p_generator); +} + +EditorResourcePreview* EditorResourcePreview::get_singleton() { + + return singleton; +} + +void EditorResourcePreview::_bind_methods() { + + ObjectTypeDB::bind_method("_preview_ready",&EditorResourcePreview::_preview_ready); +} + +EditorResourcePreview::EditorResourcePreview() { + singleton=this; + preview_mutex = Mutex::create(); + preview_sem = Semaphore::create(); + order=0; + exit=false; + + thread = Thread::create(_thread_func,this); +} + + +EditorResourcePreview::~EditorResourcePreview() +{ + + exit=true; + preview_sem->post(); + Thread::wait_to_finish(thread); + memdelete(thread); + memdelete(preview_mutex); + memdelete(preview_sem); + + +} + diff --git a/tools/editor/editor_resource_preview.h b/tools/editor/editor_resource_preview.h new file mode 100644 index 0000000000..aadae75597 --- /dev/null +++ b/tools/editor/editor_resource_preview.h @@ -0,0 +1,95 @@ +#ifndef EDITORRESOURCEPREVIEW_H +#define EDITORRESOURCEPREVIEW_H + +#include "scene/main/node.h" +#include "os/semaphore.h" +#include "os/thread.h" +#include "scene/resources/texture.h" + +/* make previews for: +*packdscene +*wav +*image +*mesh +-font +*script +*material +-shader +-shader graph? +-navigation mesh +-collision? +-occluder polygon +-navigation polygon +-tileset +-curve and curve2D +*/ + + +class EditorResourcePreviewGenerator : public Reference { + + OBJ_TYPE(EditorResourcePreviewGenerator,Reference ); + +public: + + virtual bool handles(const String& p_type) const=0; + virtual Ref<Texture> generate(const RES& p_from)=0; + virtual Ref<Texture> generate_from_path(const String& p_path); + + EditorResourcePreviewGenerator(); +}; + + +class EditorResourcePreview : public Node { + + OBJ_TYPE(EditorResourcePreview,Node); + + + static EditorResourcePreview* singleton; + + struct QueueItem { + String path; + ObjectID id; + StringName function; + Variant userdata; + }; + + List<QueueItem> queue; + + Mutex *preview_mutex; + Semaphore *preview_sem; + Thread *thread; + bool exit; + + struct Item { + Ref<Texture> preview; + int order; + }; + + int order; + + Map<String,Item> cache; + + void _preview_ready(const String& p_str,const Ref<Texture>& p_texture, ObjectID id, const StringName &p_func, const Variant &p_ud); + Ref<Texture> _generate_preview(const QueueItem& p_item, const String &cache_base); + + static void _thread_func(void *ud); + void _thread(); + + Vector<Ref<EditorResourcePreviewGenerator> > preview_generators; +protected: + + static void _bind_methods(); +public: + + static EditorResourcePreview* get_singleton(); + + //callback funtion is callback(String p_path,Ref<Texture> preview,Variant udata) preview null if could not load + void queue_resource_preview(const String& p_path, Object* p_receiver, const StringName& p_receiver_func, const Variant& p_userdata); + + void add_preview_generator(const Ref<EditorResourcePreviewGenerator>& p_generator); + + EditorResourcePreview(); + ~EditorResourcePreview(); +}; + +#endif // EDITORRESOURCEPREVIEW_H diff --git a/tools/editor/editor_run.cpp b/tools/editor/editor_run.cpp index d545b8d8f2..b635cea84b 100644 --- a/tools/editor/editor_run.cpp +++ b/tools/editor/editor_run.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,7 +28,7 @@ /*************************************************************************/ #include "editor_run.h" #include "globals.h" - +#include "editor_settings.h" EditorRun::Status EditorRun::get_status() const { @@ -61,6 +61,77 @@ Error EditorRun::run(const String& p_scene,const String p_custom_args,const List } } + if (debug_collisions) { + args.push_back("-debugcol"); + } + + if (debug_navigation) { + args.push_back("-debugnav"); + } + + int screen = EditorSettings::get_singleton()->get("game_window_placement/screen"); + + if (screen==0) { + screen=OS::get_singleton()->get_current_screen(); + } else { + screen--; + } + + Rect2 screen_rect; + screen_rect.pos=OS::get_singleton()->get_screen_position(screen); + screen_rect.size=OS::get_singleton()->get_screen_size(screen); + + + Size2 desired_size; + + desired_size.x=Globals::get_singleton()->get("display/width"); + desired_size.y=Globals::get_singleton()->get("display/height"); + + Size2 test_size; + test_size.x=Globals::get_singleton()->get("display/test_width"); + test_size.y=Globals::get_singleton()->get("display/test_height"); + if (test_size.x>0 && test_size.y>0) { + + desired_size=test_size; + } + + + int window_placement=EditorSettings::get_singleton()->get("game_window_placement/rect"); + + switch(window_placement) { + case 0: { // default + + args.push_back("-p"); + args.push_back(itos(screen_rect.pos.x)+"x"+itos(screen_rect.pos.y)); + } break; + case 1: { // centered + Vector2 pos=screen_rect.pos+((screen_rect.size-desired_size)/2).floor(); + args.push_back("-p"); + args.push_back(itos(pos.x)+"x"+itos(pos.y)); + } break; + case 2: { // custom pos + Vector2 pos = EditorSettings::get_singleton()->get("game_window_placement/rect_custom_position"); + pos+=screen_rect.pos; + args.push_back("-p"); + args.push_back(itos(pos.x)+"x"+itos(pos.y)); + } break; + case 3: { // force maximized + Vector2 pos=screen_rect.pos; + args.push_back("-p"); + args.push_back(itos(pos.x)+"x"+itos(pos.y)); + args.push_back("-mx"); + + } break; + case 4: { // force fullscreen + + Vector2 pos=screen_rect.pos; + args.push_back("-p"); + args.push_back(itos(pos.x)+"x"+itos(pos.y)); + args.push_back("-f"); + } break; + } + + if (p_breakpoints.size()) { args.push_back("-bp"); @@ -105,7 +176,31 @@ void EditorRun::stop() { status=STATUS_STOP; } +void EditorRun::set_debug_collisions(bool p_debug) { + + debug_collisions=p_debug; +} + +bool EditorRun::get_debug_collisions() const{ + + return debug_collisions; +} + +void EditorRun::set_debug_navigation(bool p_debug) { + + debug_navigation=p_debug; +} + +bool EditorRun::get_debug_navigation() const{ + + return debug_navigation; +} + + EditorRun::EditorRun() { status=STATUS_STOP; + debug_collisions=false; + debug_navigation=false; + } diff --git a/tools/editor/editor_run.h b/tools/editor/editor_run.h index 94a0f1878f..e1b0b081c7 100644 --- a/tools/editor/editor_run.h +++ b/tools/editor/editor_run.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -43,12 +43,22 @@ public: OS::ProcessID pid; private: + bool debug_collisions; + bool debug_navigation; Status status; public: Status get_status() const; Error run(const String& p_scene,const String p_custom_args,const List<String>& p_breakpoints,const String& p_edited_scene); + void run_native_notify() { status=STATUS_PLAY; } void stop(); + + void set_debug_collisions(bool p_debug); + bool get_debug_collisions() const; + + void set_debug_navigation(bool p_debug); + bool get_debug_navigation() const; + EditorRun(); }; diff --git a/tools/editor/editor_run_native.cpp b/tools/editor/editor_run_native.cpp index 17117be188..2eedba93dc 100644 --- a/tools/editor/editor_run_native.cpp +++ b/tools/editor/editor_run_native.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -101,12 +101,28 @@ void EditorRunNative::_run_native(int p_idx,const String& p_platform) { Ref<EditorExportPlatform> eep = EditorImportExport::get_singleton()->get_export_platform(p_platform); ERR_FAIL_COND(eep.is_null()); - eep->run(p_idx,deploy_dumb); + if (deploy_debug_remote) { + emit_signal("native_run"); + + } + int flags=0; + if (deploy_debug_remote) + flags|=EditorExportPlatform::EXPORT_REMOTE_DEBUG; + if (deploy_dumb) + flags|=EditorExportPlatform::EXPORT_DUMB_CLIENT; + if (debug_collisions) + flags|=EditorExportPlatform::EXPORT_VIEW_COLLISONS; + if (debug_navigation) + flags|=EditorExportPlatform::EXPORT_VIEW_NAVIGATION; + + eep->run(p_idx,flags); } void EditorRunNative::_bind_methods() { ObjectTypeDB::bind_method("_run_native",&EditorRunNative::_run_native); + + ADD_SIGNAL(MethodInfo("native_run")); } void EditorRunNative::set_deploy_dumb(bool p_enabled) { @@ -119,10 +135,43 @@ bool EditorRunNative::is_deploy_dumb_enabled() const{ return deploy_dumb; } +void EditorRunNative::set_deploy_debug_remote(bool p_enabled) { + + deploy_debug_remote=p_enabled; +} + +bool EditorRunNative::is_deploy_debug_remote_enabled() const{ + + return deploy_debug_remote; +} + +void EditorRunNative::set_debug_collisions(bool p_debug) { + + debug_collisions=p_debug; +} + +bool EditorRunNative::get_debug_collisions() const{ + + return debug_collisions; +} + +void EditorRunNative::set_debug_navigation(bool p_debug) { + + debug_navigation=p_debug; +} + +bool EditorRunNative::get_debug_navigation() const{ + + return debug_navigation; +} EditorRunNative::EditorRunNative() { set_process(true); first=true; deploy_dumb=false; + deploy_debug_remote=false; + debug_collisions=false; + debug_navigation=false; + } diff --git a/tools/editor/editor_run_native.h b/tools/editor/editor_run_native.h index f4bda2d07d..77d6dc198e 100644 --- a/tools/editor/editor_run_native.h +++ b/tools/editor/editor_run_native.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,6 +39,9 @@ class EditorRunNative : public HBoxContainer { Map<StringName,MenuButton*> menus; bool first; bool deploy_dumb; + bool deploy_debug_remote; + bool debug_collisions; + bool debug_navigation; void _run_native(int p_idx,const String& p_platform); @@ -50,6 +53,16 @@ public: void set_deploy_dumb(bool p_enabled); bool is_deploy_dumb_enabled() const; + + void set_deploy_debug_remote(bool p_enabled); + bool is_deploy_debug_remote_enabled() const; + + void set_debug_collisions(bool p_debug); + bool get_debug_collisions() const; + + void set_debug_navigation(bool p_debug); + bool get_debug_navigation() const; + EditorRunNative(); }; diff --git a/tools/editor/editor_run_script.cpp b/tools/editor/editor_run_script.cpp index 5f8598d052..90581374f6 100644 --- a/tools/editor/editor_run_script.cpp +++ b/tools/editor/editor_run_script.cpp @@ -18,7 +18,7 @@ void EditorScript::add_root_node(Node *p_node) { return; } - editor->set_edited_scene(p_node); +// editor->set_edited_scene(p_node); } Node *EditorScript::get_scene() { diff --git a/tools/editor/editor_selection.cpp b/tools/editor/editor_selection.cpp index 890b31cd4d..d8fd8735fc 100644 --- a/tools/editor/editor_selection.cpp +++ b/tools/editor/editor_selection.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/editor_selection.h b/tools/editor/editor_selection.h index 7f726e2446..f10f313a4e 100644 --- a/tools/editor/editor_selection.h +++ b/tools/editor/editor_selection.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/editor_settings.cpp b/tools/editor/editor_settings.cpp index 8b0fd204e3..15de6e7266 100644 --- a/tools/editor/editor_settings.cpp +++ b/tools/editor/editor_settings.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -224,6 +224,28 @@ void EditorSettings::create() { dir->change_dir(".."); } + if (dir->change_dir("config")!=OK) { + dir->make_dir("config"); + } else { + + dir->change_dir(".."); + } + + dir->change_dir("config"); + + String pcp=Globals::get_singleton()->get_resource_path(); + if (pcp.ends_with("/")) + pcp=config_path.substr(0,pcp.size()-1); + pcp=pcp.get_file()+"-"+pcp.md5_text(); + + if (dir->change_dir(pcp)) { + dir->make_dir(pcp); + } else { + dir->change_dir(".."); + } + + dir->change_dir(".."); + // path at least is validated, so validate config file @@ -244,6 +266,7 @@ void EditorSettings::create() { } singleton->config_file_path=config_file_path; + singleton->project_config_path=pcp; singleton->settings_path=config_path+"/"+config_dir; if (OS::get_singleton()->is_stdout_verbose()) { @@ -251,7 +274,8 @@ void EditorSettings::create() { print_line("EditorSettings: Load OK!"); } - + singleton->setup_network(); + singleton->load_favorites(); singleton->scan_plugins(); return; @@ -264,7 +288,9 @@ void EditorSettings::create() { singleton = Ref<EditorSettings>( memnew( EditorSettings ) ); singleton->config_file_path=config_file_path; + singleton->settings_path=config_path+"/"+config_dir; singleton->_load_defaults(); + singleton->setup_network(); singleton->scan_plugins(); @@ -306,6 +332,35 @@ Error EditorSettings::_load_plugin(const String& p_path, Plugin &plugin) { return OK; } +void EditorSettings::setup_network() { + + List<IP_Address> local_ip; + IP::get_singleton()->get_local_addresses(&local_ip); + String lip; + String hint; + String current=get("network/debug_host"); + + for(List<IP_Address>::Element *E=local_ip.front();E;E=E->next()) { + + String ip = E->get(); + if (ip=="127.0.0.1") + continue; + + if (lip!="") + lip=ip; + if (ip==current) + lip=current; //so it saves + if (hint!="") + hint+=","; + hint+=ip; + + } + + set("network/debug_host",lip); + add_property_hint(PropertyInfo(Variant::STRING,"network/debug_host",PROPERTY_HINT_ENUM,hint)); + +} + void EditorSettings::scan_plugins() { Map<String,Plugin> new_plugins; @@ -392,7 +447,7 @@ void EditorSettings::_load_defaults() { hints["global/default_project_path"]=PropertyInfo(Variant::STRING,"global/default_project_path",PROPERTY_HINT_GLOBAL_DIR); set("global/default_project_export_path",""); hints["global/default_project_export_path"]=PropertyInfo(Variant::STRING,"global/default_project_export_path",PROPERTY_HINT_GLOBAL_DIR); - + set("global/show_script_in_scene_tabs",false); set("text_editor/background_color",Color::html("3b000000")); set("text_editor/text_color",Color::html("aaaaaa")); set("text_editor/text_selected_color",Color::html("000000")); @@ -404,14 +459,22 @@ void EditorSettings::_load_defaults() { set("text_editor/symbol_color",Color::html("badfff")); set("text_editor/selection_color",Color::html("7b5dbe")); set("text_editor/brace_mismatch_color",Color(1,0.2,0.2)); + set("text_editor/current_line_color",Color(0.3,0.5,0.8,0.15)); set("text_editor/idle_parse_delay",2); set("text_editor/create_signal_callbacks",true); - set("text_editor/autosave_interval_seconds",60); + set("text_editor/autosave_interval_secs",0); + set("text_editor/font",""); hints["text_editor/font"]=PropertyInfo(Variant::STRING,"text_editor/font",PROPERTY_HINT_GLOBAL_FILE,"*.fnt"); set("text_editor/auto_brace_complete", false); + set("text_editor/restore_scripts_on_load",true); + + set("scenetree_editor/duplicate_node_name_num_separator",0); + hints["scenetree_editor/duplicate_node_name_num_separator"]=PropertyInfo(Variant::INT,"scenetree_editor/duplicate_node_name_num_separator",PROPERTY_HINT_ENUM, "None,Space,Underscore,Dash"); + + set("gridmap_editor/pick_distance", 5000.0); set("3d_editor/default_fov",45.0); set("3d_editor/default_z_near",0.1); @@ -419,6 +482,8 @@ void EditorSettings::_load_defaults() { set("3d_editor/navigation_scheme",0); hints["3d_editor/navigation_scheme"]=PropertyInfo(Variant::INT,"3d_editor/navigation_scheme",PROPERTY_HINT_ENUM,"Godot,Maya,Modo"); + set("3d_editor/zoom_style",0); + hints["3d_editor/zoom_style"]=PropertyInfo(Variant::INT,"3d_editor/zoom_style",PROPERTY_HINT_ENUM,"Vertical, Horizontal"); set("3d_editor/orbit_modifier",0); hints["3d_editor/orbit_modifier"]=PropertyInfo(Variant::INT,"3d_editor/orbit_modifier",PROPERTY_HINT_ENUM,"None,Shift,Alt,Meta,Ctrl"); set("3d_editor/pan_modifier",1); @@ -432,6 +497,16 @@ void EditorSettings::_load_defaults() { set("2d_editor/bone_selected_color",Color(0.9,0.45,0.45,0.9)); set("2d_editor/bone_ik_color",Color(0.9,0.9,0.45,0.9)); + set("game_window_placement/rect",0); + hints["game_window_placement/rect"]=PropertyInfo(Variant::INT,"game_window_placement/rect",PROPERTY_HINT_ENUM,"Default,Centered,Custom Position,Force Maximized,Force Full Screen"); + String screen_hints="Default (Same as Editor)"; + for(int i=0;i<OS::get_singleton()->get_screen_count();i++) { + screen_hints+=",Monitor "+itos(i+1); + } + set("game_window_placement/rect_custom_position",Vector2()); + set("game_window_placement/screen",0); + hints["game_window_placement/screen"]=PropertyInfo(Variant::INT,"game_window_placement/screen",PROPERTY_HINT_ENUM,screen_hints); + set("on_save/compress_binary_resources",true); set("on_save/save_modified_external_resources",true); set("on_save/save_paths_as_relative",false); @@ -439,11 +514,15 @@ void EditorSettings::_load_defaults() { set("text_editor/create_signal_callbacks",true); + set("file_dialog/show_hidden_files", false); + set("file_dialog/thumbnail_size", 64); + hints["file_dialog/thumbnail_size"]=PropertyInfo(Variant::INT,"file_dialog/thumbnail_size",PROPERTY_HINT_RANGE,"32,128,16"); set("animation/autorename_animation_tracks",true); set("animation/confirm_insert_track",true); set("property_editor/texture_preview_width",48); + set("property_editor/auto_refresh_interval",0.3); set("help/doc_path",""); set("import/ask_save_before_reimport",false); @@ -636,6 +715,71 @@ void EditorSettings::set_plugin_enabled(const String& p_plugin, bool p_enabled) } +void EditorSettings::set_favorite_dirs(const Vector<String>& p_favorites) { + + favorite_dirs=p_favorites; + FileAccess *f = FileAccess::open(get_project_settings_path().plus_file("favorite_dirs"),FileAccess::WRITE); + if (f) { + for(int i=0;i<favorite_dirs.size();i++) + f->store_line(favorite_dirs[i]); + memdelete(f); + } + +} + +Vector<String> EditorSettings::get_favorite_dirs() const { + + return favorite_dirs; +} + + +void EditorSettings::set_recent_dirs(const Vector<String>& p_recent) { + + recent_dirs=p_recent; + FileAccess *f = FileAccess::open(get_project_settings_path().plus_file("recent_dirs"),FileAccess::WRITE); + if (f) { + for(int i=0;i<recent_dirs.size();i++) + f->store_line(recent_dirs[i]); + memdelete(f); + } +} + +Vector<String> EditorSettings::get_recent_dirs() const { + + return recent_dirs; +} + +String EditorSettings::get_project_settings_path() const { + + + return get_settings_path().plus_file("config").plus_file(project_config_path); +} + + +void EditorSettings::load_favorites() { + + FileAccess *f = FileAccess::open(get_project_settings_path().plus_file("favorite_dirs"),FileAccess::READ); + if (f) { + String line = f->get_line().strip_edges(); + while(line!="") { + favorite_dirs.push_back(line); + line = f->get_line().strip_edges(); + } + memdelete(f); + } + + f = FileAccess::open(get_project_settings_path().plus_file("recent_dirs"),FileAccess::READ); + if (f) { + String line = f->get_line().strip_edges(); + while(line!="") { + recent_dirs.push_back(line); + line = f->get_line().strip_edges(); + } + memdelete(f); + } + +} + void EditorSettings::_bind_methods() { diff --git a/tools/editor/editor_settings.h b/tools/editor/editor_settings.h index c44afd9593..4ba940cd1c 100644 --- a/tools/editor/editor_settings.h +++ b/tools/editor/editor_settings.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -88,6 +88,11 @@ private: void _load_defaults(); + String project_config_path; + + Vector<String> favorite_dirs; + Vector<String> recent_dirs; + protected: static void _bind_methods(); @@ -102,11 +107,13 @@ public: static EditorSettings *get_singleton(); void erase(String p_var); String get_settings_path() const; + String get_project_settings_path() const; const Map<String,Plugin>& get_plugins() const { return plugins; } void scan_plugins(); void enable_plugins(); + void setup_network(); void raise_order(const String& p_name); static void create(); @@ -125,6 +132,14 @@ public: void add_property_hint(const PropertyInfo& p_hint); + void set_favorite_dirs(const Vector<String>& p_favorite_dirs); + Vector<String> get_favorite_dirs() const; + + void set_recent_dirs(const Vector<String>& p_recent_dirs); + Vector<String> get_recent_dirs() const; + + void load_favorites(); + EditorSettings(); ~EditorSettings(); diff --git a/tools/editor/editor_sub_scene.cpp b/tools/editor/editor_sub_scene.cpp index 25a52bb2cd..2a6eba2554 100644 --- a/tools/editor/editor_sub_scene.cpp +++ b/tools/editor/editor_sub_scene.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -215,7 +215,7 @@ EditorSubScene::EditorSubScene() { tree->set_v_size_flags(SIZE_EXPAND_FILL); vb->add_margin_child("Import From Node:",tree)->set_v_size_flags(SIZE_EXPAND_FILL); - file_dialog = memnew( FileDialog ); + file_dialog = memnew( EditorFileDialog ); List<String> extensions; ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions); @@ -224,7 +224,7 @@ EditorSubScene::EditorSubScene() { file_dialog->add_filter("*."+E->get()); } - file_dialog->set_mode(FileDialog::MODE_OPEN_FILE); + file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); add_child(file_dialog); file_dialog->connect("file_selected",this,"_path_selected"); diff --git a/tools/editor/editor_sub_scene.h b/tools/editor/editor_sub_scene.h index 8f5e864eb9..dfd6c531e2 100644 --- a/tools/editor/editor_sub_scene.h +++ b/tools/editor/editor_sub_scene.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,7 +31,7 @@ #include "scene/gui/dialogs.h" #include "scene/gui/tree.h" -#include "scene/gui/file_dialog.h" +#include "tools/editor/editor_file_dialog.h" class EditorSubScene : public ConfirmationDialog { @@ -42,7 +42,7 @@ class EditorSubScene : public ConfirmationDialog { Tree *tree; Node *scene; - FileDialog *file_dialog; + EditorFileDialog *file_dialog; void _fill_tree(Node* p_node,TreeItem *p_parent); void _reown(Node* p_node,List<Node*> *p_to_reown); diff --git a/tools/editor/editor_vu.cpp b/tools/editor/editor_vu.cpp index cd0e75f22d..ac11aceb21 100644 --- a/tools/editor/editor_vu.cpp +++ b/tools/editor/editor_vu.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/editor_vu.h b/tools/editor/editor_vu.h index f8fef8b510..4c51ac31c0 100644 --- a/tools/editor/editor_vu.h +++ b/tools/editor/editor_vu.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/file_type_cache.cpp b/tools/editor/file_type_cache.cpp index f00fc8fe1a..a86400a560 100644 --- a/tools/editor/file_type_cache.cpp +++ b/tools/editor/file_type_cache.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/file_type_cache.h b/tools/editor/file_type_cache.h index bb89718a55..efc6d22b25 100644 --- a/tools/editor/file_type_cache.h +++ b/tools/editor/file_type_cache.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/fileserver/SCsub b/tools/editor/fileserver/SCsub index b525fb3f75..363a2ce4c0 100644 --- a/tools/editor/fileserver/SCsub +++ b/tools/editor/fileserver/SCsub @@ -1,7 +1,3 @@ Import('env') Export('env') env.add_source_files(env.tool_sources,"*.cpp") - - - - diff --git a/tools/editor/fileserver/editor_file_server.cpp b/tools/editor/fileserver/editor_file_server.cpp index f21d9b4ec1..ea95e4da1c 100644 --- a/tools/editor/fileserver/editor_file_server.cpp +++ b/tools/editor/fileserver/editor_file_server.cpp @@ -278,6 +278,7 @@ void EditorFileServer::_thread_start(void*s) { self->to_wait.erase(w); self->wait_mutex->unlock(); Thread::wait_to_finish(w); + memdelete(w); self->wait_mutex->lock(); } self->wait_mutex->unlock(); @@ -317,27 +318,7 @@ EditorFileServer::EditorFileServer() { cmd=CMD_NONE; thread=Thread::create(_thread_start,this); - List<IP_Address> local_ip; - IP::get_singleton()->get_local_addresses(&local_ip); EDITOR_DEF("file_server/port",6010); - String lip; - String hint; - for(List<IP_Address>::Element *E=local_ip.front();E;E=E->next()) { - - String ip = E->get(); - if (ip=="127.0.0.1") - continue; - - if (lip!="") - lip=ip; - if (hint!="") - hint+=","; - hint+=ip; - - } - - EDITOR_DEF("file_server/host",lip); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"file_server/host",PROPERTY_HINT_ENUM,hint)); EDITOR_DEF("file_server/password",""); } @@ -346,5 +327,6 @@ EditorFileServer::~EditorFileServer() { quit=true; Thread::wait_to_finish(thread); + memdelete(thread); memdelete(wait_mutex); } diff --git a/tools/editor/groups_editor.cpp b/tools/editor/groups_editor.cpp index 52db562d8a..2e82854014 100644 --- a/tools/editor/groups_editor.cpp +++ b/tools/editor/groups_editor.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,6 +39,9 @@ void GroupsEditor::_notification(int p_what) { if (p_what==NOTIFICATION_ENTER_TREE) { connect("confirmed", this,"_close"); } + if (p_what==NOTIFICATION_EXIT_TREE) { + disconnect("confirmed", this,"_close"); + } } void GroupsEditor::_close() { diff --git a/tools/editor/groups_editor.h b/tools/editor/groups_editor.h index 6357358e1d..09883a150f 100644 --- a/tools/editor/groups_editor.h +++ b/tools/editor/groups_editor.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/icons/SCsub b/tools/editor/icons/SCsub index aea053d22b..addf6879a2 100644 --- a/tools/editor/icons/SCsub +++ b/tools/editor/icons/SCsub @@ -9,30 +9,30 @@ def make_editor_icons_action(target, source, env): pixmaps = source s = cStringIO.StringIO() - + s.write("#include \"editor_icons.h\"\n\n") s.write("#include \"scene/resources/theme.h\"\n\n") for x in pixmaps: - + x=str(x) var_str=os.path.basename(x)[:-4]+"_png"; #print(var_str) - + s.write("static const unsigned char "+ var_str +"[]={\n"); - + pngf=open(x,"rb"); - + b=pngf.read(1); while(len(b)==1): s.write(hex(ord(b))) b=pngf.read(1); if (len(b)==1): s.write(",") - + s.write("\n};\n\n\n"); pngf.close(); - + s.write("static Ref<ImageTexture> make_icon(const uint8_t* p_png) {\n") s.write("\tRef<ImageTexture> texture( memnew( ImageTexture ) );\n") s.write("\ttexture->create_from_image( Image(p_png),ImageTexture::FLAG_FILTER );\n") @@ -42,14 +42,14 @@ def make_editor_icons_action(target, source, env): s.write("void editor_register_icons(Ref<Theme> p_theme) {\n\n") for x in pixmaps: - + x=os.path.basename(str(x)) type=x[5:-4].title().replace("_",""); var_str=x[:-4]+"_png"; s.write("\tp_theme->set_icon(\""+type+"\",\"EditorIcons\",make_icon("+var_str+"));\n"); s.write("\n\n}\n\n"); - + f = open(dst,"wb") f.write(s.getvalue()) f.close() @@ -63,4 +63,3 @@ env.Alias('editor_icons',[env.MakeEditorIconsBuilder('#tools/editor/editor_icons env.tool_sources.append("#tools/editor/editor_icons.cpp") Export('env') - diff --git a/tools/editor/icons/icon_add.png b/tools/editor/icons/icon_add.png Binary files differindex 1a97f356d6..26283ca67c 100644 --- a/tools/editor/icons/icon_add.png +++ b/tools/editor/icons/icon_add.png diff --git a/tools/editor/icons/icon_anchor.png b/tools/editor/icons/icon_anchor.png Binary files differnew file mode 100644 index 0000000000..1f9f9fa139 --- /dev/null +++ b/tools/editor/icons/icon_anchor.png diff --git a/tools/editor/icons/icon_animation.png b/tools/editor/icons/icon_animation.png Binary files differindex 6af126bf37..ac663c0554 100644 --- a/tools/editor/icons/icon_animation.png +++ b/tools/editor/icons/icon_animation.png diff --git a/tools/editor/icons/icon_arrow_left.png b/tools/editor/icons/icon_arrow_left.png Binary files differnew file mode 100644 index 0000000000..b72ac2a10d --- /dev/null +++ b/tools/editor/icons/icon_arrow_left.png diff --git a/tools/editor/icons/icon_arrow_left_disabled.png b/tools/editor/icons/icon_arrow_left_disabled.png Binary files differnew file mode 100644 index 0000000000..f1f9d0f988 --- /dev/null +++ b/tools/editor/icons/icon_arrow_left_disabled.png diff --git a/tools/editor/icons/icon_arrow_right.png b/tools/editor/icons/icon_arrow_right.png Binary files differnew file mode 100644 index 0000000000..d0a48b01ac --- /dev/null +++ b/tools/editor/icons/icon_arrow_right.png diff --git a/tools/editor/icons/icon_arrow_right_disabled.png b/tools/editor/icons/icon_arrow_right_disabled.png Binary files differnew file mode 100644 index 0000000000..840cd0da0a --- /dev/null +++ b/tools/editor/icons/icon_arrow_right_disabled.png diff --git a/tools/editor/icons/icon_arrow_up.png b/tools/editor/icons/icon_arrow_up.png Binary files differnew file mode 100644 index 0000000000..10aac0e0f6 --- /dev/null +++ b/tools/editor/icons/icon_arrow_up.png diff --git a/tools/editor/icons/icon_arrow_up_disabled.png b/tools/editor/icons/icon_arrow_up_disabled.png Binary files differnew file mode 100644 index 0000000000..fb46aa1373 --- /dev/null +++ b/tools/editor/icons/icon_arrow_up_disabled.png diff --git a/tools/editor/icons/icon_atlas_texture.png b/tools/editor/icons/icon_atlas_texture.png Binary files differindex 3da9f0bee4..0051b0cda4 100644 --- a/tools/editor/icons/icon_atlas_texture.png +++ b/tools/editor/icons/icon_atlas_texture.png diff --git a/tools/editor/icons/icon_audio_stream_gibberish.png b/tools/editor/icons/icon_audio_stream_gibberish.png Binary files differindex f10671e8a2..95470c298e 100644 --- a/tools/editor/icons/icon_audio_stream_gibberish.png +++ b/tools/editor/icons/icon_audio_stream_gibberish.png diff --git a/tools/editor/icons/icon_audio_stream_opus.png b/tools/editor/icons/icon_audio_stream_opus.png Binary files differnew file mode 100644 index 0000000000..69b0c83b4d --- /dev/null +++ b/tools/editor/icons/icon_audio_stream_opus.png diff --git a/tools/editor/icons/icon_auto_play.png b/tools/editor/icons/icon_auto_play.png Binary files differindex e454ca8c7b..1d5c957cc7 100644 --- a/tools/editor/icons/icon_auto_play.png +++ b/tools/editor/icons/icon_auto_play.png diff --git a/tools/editor/icons/icon_back.png b/tools/editor/icons/icon_back.png Binary files differindex d60e3b2640..f7e507d92b 100644 --- a/tools/editor/icons/icon_back.png +++ b/tools/editor/icons/icon_back.png diff --git a/tools/editor/icons/icon_back_buffer_copy.png b/tools/editor/icons/icon_back_buffer_copy.png Binary files differnew file mode 100644 index 0000000000..b27eb39108 --- /dev/null +++ b/tools/editor/icons/icon_back_buffer_copy.png diff --git a/tools/editor/icons/icon_back_disabled.png b/tools/editor/icons/icon_back_disabled.png Binary files differnew file mode 100644 index 0000000000..31aab496e2 --- /dev/null +++ b/tools/editor/icons/icon_back_disabled.png diff --git a/tools/editor/icons/icon_bake.png b/tools/editor/icons/icon_bake.png Binary files differindex b1b0f941da..ae06ce48e6 100644 --- a/tools/editor/icons/icon_bake.png +++ b/tools/editor/icons/icon_bake.png diff --git a/tools/editor/icons/icon_blend.png b/tools/editor/icons/icon_blend.png Binary files differindex 985b63d5d1..2a75f0b2f4 100644 --- a/tools/editor/icons/icon_blend.png +++ b/tools/editor/icons/icon_blend.png diff --git a/tools/editor/icons/icon_bone.png b/tools/editor/icons/icon_bone.png Binary files differindex 174b0bc167..81b6d8856e 100644 --- a/tools/editor/icons/icon_bone.png +++ b/tools/editor/icons/icon_bone.png diff --git a/tools/editor/icons/icon_bool.png b/tools/editor/icons/icon_bool.png Binary files differindex d465b7da07..3381033b00 100644 --- a/tools/editor/icons/icon_bool.png +++ b/tools/editor/icons/icon_bool.png diff --git a/tools/editor/icons/icon_canvas_item.png b/tools/editor/icons/icon_canvas_item.png Binary files differindex 99403bed21..add54ba1af 100644 --- a/tools/editor/icons/icon_canvas_item.png +++ b/tools/editor/icons/icon_canvas_item.png diff --git a/tools/editor/icons/icon_canvas_item_material.png b/tools/editor/icons/icon_canvas_item_material.png Binary files differnew file mode 100644 index 0000000000..2fe8921653 --- /dev/null +++ b/tools/editor/icons/icon_canvas_item_material.png diff --git a/tools/editor/icons/icon_canvas_item_shader.png b/tools/editor/icons/icon_canvas_item_shader.png Binary files differnew file mode 100644 index 0000000000..a5f4e7bf85 --- /dev/null +++ b/tools/editor/icons/icon_canvas_item_shader.png diff --git a/tools/editor/icons/icon_canvas_item_shader_graph.png b/tools/editor/icons/icon_canvas_item_shader_graph.png Binary files differnew file mode 100644 index 0000000000..bba966b43e --- /dev/null +++ b/tools/editor/icons/icon_canvas_item_shader_graph.png diff --git a/tools/editor/icons/icon_canvas_modulate.png b/tools/editor/icons/icon_canvas_modulate.png Binary files differnew file mode 100644 index 0000000000..2a34df7793 --- /dev/null +++ b/tools/editor/icons/icon_canvas_modulate.png diff --git a/tools/editor/icons/icon_check_box.png b/tools/editor/icons/icon_check_box.png Binary files differnew file mode 100644 index 0000000000..8a2b56cc3e --- /dev/null +++ b/tools/editor/icons/icon_check_box.png diff --git a/tools/editor/icons/icon_class_list.png b/tools/editor/icons/icon_class_list.png Binary files differnew file mode 100644 index 0000000000..fb756c0fe1 --- /dev/null +++ b/tools/editor/icons/icon_class_list.png diff --git a/tools/editor/icons/icon_close.png b/tools/editor/icons/icon_close.png Binary files differindex 11fa746271..10e56d5bb8 100644 --- a/tools/editor/icons/icon_close.png +++ b/tools/editor/icons/icon_close.png diff --git a/tools/editor/icons/icon_close_hover.png b/tools/editor/icons/icon_close_hover.png Binary files differindex efcc9e7471..cb519691e5 100644 --- a/tools/editor/icons/icon_close_hover.png +++ b/tools/editor/icons/icon_close_hover.png diff --git a/tools/editor/icons/icon_collapse.png b/tools/editor/icons/icon_collapse.png Binary files differindex bd5c9765a5..23db9e42a7 100644 --- a/tools/editor/icons/icon_collapse.png +++ b/tools/editor/icons/icon_collapse.png diff --git a/tools/editor/icons/icon_collapse_hl.png b/tools/editor/icons/icon_collapse_hl.png Binary files differindex 7ed9a5c125..0dfbc8b175 100644 --- a/tools/editor/icons/icon_collapse_hl.png +++ b/tools/editor/icons/icon_collapse_hl.png diff --git a/tools/editor/icons/icon_color_ramp.png b/tools/editor/icons/icon_color_ramp.png Binary files differnew file mode 100644 index 0000000000..9031b5ec53 --- /dev/null +++ b/tools/editor/icons/icon_color_ramp.png diff --git a/tools/editor/icons/icon_connect.png b/tools/editor/icons/icon_connect.png Binary files differindex 24258414c4..745e445a61 100644 --- a/tools/editor/icons/icon_connect.png +++ b/tools/editor/icons/icon_connect.png diff --git a/tools/editor/icons/icon_control_align_bottom_center.png b/tools/editor/icons/icon_control_align_bottom_center.png Binary files differnew file mode 100644 index 0000000000..5ce9fe5c1c --- /dev/null +++ b/tools/editor/icons/icon_control_align_bottom_center.png diff --git a/tools/editor/icons/icon_control_align_bottom_left.png b/tools/editor/icons/icon_control_align_bottom_left.png Binary files differnew file mode 100644 index 0000000000..6c5129bf95 --- /dev/null +++ b/tools/editor/icons/icon_control_align_bottom_left.png diff --git a/tools/editor/icons/icon_control_align_bottom_right.png b/tools/editor/icons/icon_control_align_bottom_right.png Binary files differnew file mode 100644 index 0000000000..8857f4e940 --- /dev/null +++ b/tools/editor/icons/icon_control_align_bottom_right.png diff --git a/tools/editor/icons/icon_control_align_bottom_wide.png b/tools/editor/icons/icon_control_align_bottom_wide.png Binary files differnew file mode 100644 index 0000000000..56f009b8e4 --- /dev/null +++ b/tools/editor/icons/icon_control_align_bottom_wide.png diff --git a/tools/editor/icons/icon_control_align_center.png b/tools/editor/icons/icon_control_align_center.png Binary files differnew file mode 100644 index 0000000000..acd42525fa --- /dev/null +++ b/tools/editor/icons/icon_control_align_center.png diff --git a/tools/editor/icons/icon_control_align_center_left.png b/tools/editor/icons/icon_control_align_center_left.png Binary files differnew file mode 100644 index 0000000000..997074b097 --- /dev/null +++ b/tools/editor/icons/icon_control_align_center_left.png diff --git a/tools/editor/icons/icon_control_align_center_right.png b/tools/editor/icons/icon_control_align_center_right.png Binary files differnew file mode 100644 index 0000000000..b5cae63f7a --- /dev/null +++ b/tools/editor/icons/icon_control_align_center_right.png diff --git a/tools/editor/icons/icon_control_align_left_center.png b/tools/editor/icons/icon_control_align_left_center.png Binary files differnew file mode 100644 index 0000000000..7bb4dfb567 --- /dev/null +++ b/tools/editor/icons/icon_control_align_left_center.png diff --git a/tools/editor/icons/icon_control_align_left_wide.png b/tools/editor/icons/icon_control_align_left_wide.png Binary files differnew file mode 100644 index 0000000000..1b0a6cff95 --- /dev/null +++ b/tools/editor/icons/icon_control_align_left_wide.png diff --git a/tools/editor/icons/icon_control_align_right_center.png b/tools/editor/icons/icon_control_align_right_center.png Binary files differnew file mode 100644 index 0000000000..cf12d44c6a --- /dev/null +++ b/tools/editor/icons/icon_control_align_right_center.png diff --git a/tools/editor/icons/icon_control_align_right_wide.png b/tools/editor/icons/icon_control_align_right_wide.png Binary files differnew file mode 100644 index 0000000000..406ed25aed --- /dev/null +++ b/tools/editor/icons/icon_control_align_right_wide.png diff --git a/tools/editor/icons/icon_control_align_top_center.png b/tools/editor/icons/icon_control_align_top_center.png Binary files differnew file mode 100644 index 0000000000..da7ede984a --- /dev/null +++ b/tools/editor/icons/icon_control_align_top_center.png diff --git a/tools/editor/icons/icon_control_align_top_left.png b/tools/editor/icons/icon_control_align_top_left.png Binary files differnew file mode 100644 index 0000000000..84a224fbbe --- /dev/null +++ b/tools/editor/icons/icon_control_align_top_left.png diff --git a/tools/editor/icons/icon_control_align_top_right.png b/tools/editor/icons/icon_control_align_top_right.png Binary files differnew file mode 100644 index 0000000000..3b58eead9c --- /dev/null +++ b/tools/editor/icons/icon_control_align_top_right.png diff --git a/tools/editor/icons/icon_control_align_top_wide.png b/tools/editor/icons/icon_control_align_top_wide.png Binary files differnew file mode 100644 index 0000000000..869ae26134 --- /dev/null +++ b/tools/editor/icons/icon_control_align_top_wide.png diff --git a/tools/editor/icons/icon_control_align_wide.png b/tools/editor/icons/icon_control_align_wide.png Binary files differnew file mode 100644 index 0000000000..57a2933b25 --- /dev/null +++ b/tools/editor/icons/icon_control_align_wide.png diff --git a/tools/editor/icons/icon_control_hcenter_wide.png b/tools/editor/icons/icon_control_hcenter_wide.png Binary files differnew file mode 100644 index 0000000000..739ea5baeb --- /dev/null +++ b/tools/editor/icons/icon_control_hcenter_wide.png diff --git a/tools/editor/icons/icon_control_vcenter_wide.png b/tools/editor/icons/icon_control_vcenter_wide.png Binary files differnew file mode 100644 index 0000000000..44cbb8f344 --- /dev/null +++ b/tools/editor/icons/icon_control_vcenter_wide.png diff --git a/tools/editor/icons/icon_curve_constant.png b/tools/editor/icons/icon_curve_constant.png Binary files differnew file mode 100644 index 0000000000..cdeac1785e --- /dev/null +++ b/tools/editor/icons/icon_curve_constant.png diff --git a/tools/editor/icons/icon_curve_in.png b/tools/editor/icons/icon_curve_in.png Binary files differnew file mode 100644 index 0000000000..2db202632e --- /dev/null +++ b/tools/editor/icons/icon_curve_in.png diff --git a/tools/editor/icons/icon_curve_in_out.png b/tools/editor/icons/icon_curve_in_out.png Binary files differnew file mode 100644 index 0000000000..f4cb593496 --- /dev/null +++ b/tools/editor/icons/icon_curve_in_out.png diff --git a/tools/editor/icons/icon_curve_linear.png b/tools/editor/icons/icon_curve_linear.png Binary files differnew file mode 100644 index 0000000000..9467dc97a3 --- /dev/null +++ b/tools/editor/icons/icon_curve_linear.png diff --git a/tools/editor/icons/icon_curve_out.png b/tools/editor/icons/icon_curve_out.png Binary files differnew file mode 100644 index 0000000000..c91ade6f4a --- /dev/null +++ b/tools/editor/icons/icon_curve_out.png diff --git a/tools/editor/icons/icon_curve_out_in.png b/tools/editor/icons/icon_curve_out_in.png Binary files differnew file mode 100644 index 0000000000..6499fa753e --- /dev/null +++ b/tools/editor/icons/icon_curve_out_in.png diff --git a/tools/editor/icons/icon_debug.png b/tools/editor/icons/icon_debug.png Binary files differnew file mode 100644 index 0000000000..03b98aa9e4 --- /dev/null +++ b/tools/editor/icons/icon_debug.png diff --git a/tools/editor/icons/icon_default_project_icon.png b/tools/editor/icons/icon_default_project_icon.png Binary files differindex 3e991fcc29..e87a49bd28 100644 --- a/tools/editor/icons/icon_default_project_icon.png +++ b/tools/editor/icons/icon_default_project_icon.png diff --git a/tools/editor/icons/icon_del.png b/tools/editor/icons/icon_del.png Binary files differindex 5349af466e..10e56d5bb8 100644 --- a/tools/editor/icons/icon_del.png +++ b/tools/editor/icons/icon_del.png diff --git a/tools/editor/icons/icon_duplicate.png b/tools/editor/icons/icon_duplicate.png Binary files differindex bae4aa2c30..f854a14fd3 100644 --- a/tools/editor/icons/icon_duplicate.png +++ b/tools/editor/icons/icon_duplicate.png diff --git a/tools/editor/icons/icon_edit.png b/tools/editor/icons/icon_edit.png Binary files differindex 012a7f5f1d..157f785b83 100644 --- a/tools/editor/icons/icon_edit.png +++ b/tools/editor/icons/icon_edit.png diff --git a/tools/editor/icons/icon_edit_key.png b/tools/editor/icons/icon_edit_key.png Binary files differindex 43a7056f38..9ab1287fc6 100644 --- a/tools/editor/icons/icon_edit_key.png +++ b/tools/editor/icons/icon_edit_key.png diff --git a/tools/editor/icons/icon_edit_resource.png b/tools/editor/icons/icon_edit_resource.png Binary files differindex de538dfe95..31d0c68fc6 100644 --- a/tools/editor/icons/icon_edit_resource.png +++ b/tools/editor/icons/icon_edit_resource.png diff --git a/tools/editor/icons/icon_editor_focus.png b/tools/editor/icons/icon_editor_focus.png Binary files differindex f21d22ebd8..40ce11f381 100644 --- a/tools/editor/icons/icon_editor_focus.png +++ b/tools/editor/icons/icon_editor_focus.png diff --git a/tools/editor/icons/icon_enum.png b/tools/editor/icons/icon_enum.png Binary files differindex 2496e1d0db..ac36c96e28 100644 --- a/tools/editor/icons/icon_enum.png +++ b/tools/editor/icons/icon_enum.png diff --git a/tools/editor/icons/icon_event_player.png b/tools/editor/icons/icon_event_player.png Binary files differindex b67f91b1b3..68646b3dfe 100644 --- a/tools/editor/icons/icon_event_player.png +++ b/tools/editor/icons/icon_event_player.png diff --git a/tools/editor/icons/icon_favorites.png b/tools/editor/icons/icon_favorites.png Binary files differindex da4713d032..4a3b575f95 100644 --- a/tools/editor/icons/icon_favorites.png +++ b/tools/editor/icons/icon_favorites.png diff --git a/tools/editor/icons/icon_file_big.png b/tools/editor/icons/icon_file_big.png Binary files differnew file mode 100644 index 0000000000..887a4ee05c --- /dev/null +++ b/tools/editor/icons/icon_file_big.png diff --git a/tools/editor/icons/icon_file_list.png b/tools/editor/icons/icon_file_list.png Binary files differnew file mode 100644 index 0000000000..ab790a85a5 --- /dev/null +++ b/tools/editor/icons/icon_file_list.png diff --git a/tools/editor/icons/icon_file_server.png b/tools/editor/icons/icon_file_server.png Binary files differindex 27c99127c3..4bd94fa8c8 100644 --- a/tools/editor/icons/icon_file_server.png +++ b/tools/editor/icons/icon_file_server.png diff --git a/tools/editor/icons/icon_file_thumbnail.png b/tools/editor/icons/icon_file_thumbnail.png Binary files differnew file mode 100644 index 0000000000..c32be5e8f8 --- /dev/null +++ b/tools/editor/icons/icon_file_thumbnail.png diff --git a/tools/editor/icons/icon_filesystem.png b/tools/editor/icons/icon_filesystem.png Binary files differnew file mode 100644 index 0000000000..6841f9a792 --- /dev/null +++ b/tools/editor/icons/icon_filesystem.png diff --git a/tools/editor/icons/icon_folder.png b/tools/editor/icons/icon_folder.png Binary files differindex 814f217edf..a450a7b297 100644 --- a/tools/editor/icons/icon_folder.png +++ b/tools/editor/icons/icon_folder.png diff --git a/tools/editor/icons/icon_folder_big.png b/tools/editor/icons/icon_folder_big.png Binary files differnew file mode 100644 index 0000000000..988e070f98 --- /dev/null +++ b/tools/editor/icons/icon_folder_big.png diff --git a/tools/editor/icons/icon_font.png b/tools/editor/icons/icon_font.png Binary files differindex d9554183c2..3ffe4f1b17 100644 --- a/tools/editor/icons/icon_font.png +++ b/tools/editor/icons/icon_font.png diff --git a/tools/editor/icons/icon_forward.png b/tools/editor/icons/icon_forward.png Binary files differindex ca6838ae9e..14e8bc9a5a 100644 --- a/tools/editor/icons/icon_forward.png +++ b/tools/editor/icons/icon_forward.png diff --git a/tools/editor/icons/icon_g_d_script.png b/tools/editor/icons/icon_g_d_script.png Binary files differindex 3b1cc98c4d..88d865356c 100644 --- a/tools/editor/icons/icon_g_d_script.png +++ b/tools/editor/icons/icon_g_d_script.png diff --git a/tools/editor/icons/icon_godot.png b/tools/editor/icons/icon_godot.png Binary files differnew file mode 100644 index 0000000000..e80820fc10 --- /dev/null +++ b/tools/editor/icons/icon_godot.png diff --git a/tools/editor/icons/icon_graph_color_ramp.png b/tools/editor/icons/icon_graph_color_ramp.png Binary files differnew file mode 100644 index 0000000000..9031b5ec53 --- /dev/null +++ b/tools/editor/icons/icon_graph_color_ramp.png diff --git a/tools/editor/icons/icon_graph_comment.png b/tools/editor/icons/icon_graph_comment.png Binary files differnew file mode 100644 index 0000000000..bf7889c510 --- /dev/null +++ b/tools/editor/icons/icon_graph_comment.png diff --git a/tools/editor/icons/icon_graph_cube_uniform.png b/tools/editor/icons/icon_graph_cube_uniform.png Binary files differnew file mode 100644 index 0000000000..d1b92b4943 --- /dev/null +++ b/tools/editor/icons/icon_graph_cube_uniform.png diff --git a/tools/editor/icons/icon_graph_curve_map.png b/tools/editor/icons/icon_graph_curve_map.png Binary files differnew file mode 100644 index 0000000000..de5c32f09e --- /dev/null +++ b/tools/editor/icons/icon_graph_curve_map.png diff --git a/tools/editor/icons/icon_graph_default_texture.png b/tools/editor/icons/icon_graph_default_texture.png Binary files differnew file mode 100644 index 0000000000..da77ec9364 --- /dev/null +++ b/tools/editor/icons/icon_graph_default_texture.png diff --git a/tools/editor/icons/icon_graph_input.png b/tools/editor/icons/icon_graph_input.png Binary files differnew file mode 100644 index 0000000000..a396bc2350 --- /dev/null +++ b/tools/editor/icons/icon_graph_input.png diff --git a/tools/editor/icons/icon_graph_rgb.png b/tools/editor/icons/icon_graph_rgb.png Binary files differnew file mode 100644 index 0000000000..abffaedd34 --- /dev/null +++ b/tools/editor/icons/icon_graph_rgb.png diff --git a/tools/editor/icons/icon_graph_rgb_op.png b/tools/editor/icons/icon_graph_rgb_op.png Binary files differnew file mode 100644 index 0000000000..642fc838c2 --- /dev/null +++ b/tools/editor/icons/icon_graph_rgb_op.png diff --git a/tools/editor/icons/icon_graph_rgb_uniform.png b/tools/editor/icons/icon_graph_rgb_uniform.png Binary files differnew file mode 100644 index 0000000000..92c79997ef --- /dev/null +++ b/tools/editor/icons/icon_graph_rgb_uniform.png diff --git a/tools/editor/icons/icon_graph_scalar.png b/tools/editor/icons/icon_graph_scalar.png Binary files differnew file mode 100644 index 0000000000..028d0e9ea4 --- /dev/null +++ b/tools/editor/icons/icon_graph_scalar.png diff --git a/tools/editor/icons/icon_graph_scalar_interp.png b/tools/editor/icons/icon_graph_scalar_interp.png Binary files differnew file mode 100644 index 0000000000..4f178a27c4 --- /dev/null +++ b/tools/editor/icons/icon_graph_scalar_interp.png diff --git a/tools/editor/icons/icon_graph_scalar_op.png b/tools/editor/icons/icon_graph_scalar_op.png Binary files differnew file mode 100644 index 0000000000..0fc4cae94c --- /dev/null +++ b/tools/editor/icons/icon_graph_scalar_op.png diff --git a/tools/editor/icons/icon_graph_scalar_uniform.png b/tools/editor/icons/icon_graph_scalar_uniform.png Binary files differnew file mode 100644 index 0000000000..fc6590a8cf --- /dev/null +++ b/tools/editor/icons/icon_graph_scalar_uniform.png diff --git a/tools/editor/icons/icon_graph_scalars_to_vec.png b/tools/editor/icons/icon_graph_scalars_to_vec.png Binary files differnew file mode 100644 index 0000000000..7ca39a2f56 --- /dev/null +++ b/tools/editor/icons/icon_graph_scalars_to_vec.png diff --git a/tools/editor/icons/icon_graph_texscreen.png b/tools/editor/icons/icon_graph_texscreen.png Binary files differnew file mode 100644 index 0000000000..e183a8fa56 --- /dev/null +++ b/tools/editor/icons/icon_graph_texscreen.png diff --git a/tools/editor/icons/icon_graph_texture_uniform.png b/tools/editor/icons/icon_graph_texture_uniform.png Binary files differnew file mode 100644 index 0000000000..7517ac1d92 --- /dev/null +++ b/tools/editor/icons/icon_graph_texture_uniform.png diff --git a/tools/editor/icons/icon_graph_time.png b/tools/editor/icons/icon_graph_time.png Binary files differnew file mode 100644 index 0000000000..b61e45589f --- /dev/null +++ b/tools/editor/icons/icon_graph_time.png diff --git a/tools/editor/icons/icon_graph_vec_dp.png b/tools/editor/icons/icon_graph_vec_dp.png Binary files differnew file mode 100644 index 0000000000..059c3025e7 --- /dev/null +++ b/tools/editor/icons/icon_graph_vec_dp.png diff --git a/tools/editor/icons/icon_graph_vec_interp.png b/tools/editor/icons/icon_graph_vec_interp.png Binary files differnew file mode 100644 index 0000000000..daf7a00203 --- /dev/null +++ b/tools/editor/icons/icon_graph_vec_interp.png diff --git a/tools/editor/icons/icon_graph_vec_length.png b/tools/editor/icons/icon_graph_vec_length.png Binary files differnew file mode 100644 index 0000000000..60ade8c90a --- /dev/null +++ b/tools/editor/icons/icon_graph_vec_length.png diff --git a/tools/editor/icons/icon_graph_vec_op.png b/tools/editor/icons/icon_graph_vec_op.png Binary files differnew file mode 100644 index 0000000000..f2a7a51123 --- /dev/null +++ b/tools/editor/icons/icon_graph_vec_op.png diff --git a/tools/editor/icons/icon_graph_vec_scalar_op.png b/tools/editor/icons/icon_graph_vec_scalar_op.png Binary files differnew file mode 100644 index 0000000000..f0f4e7a196 --- /dev/null +++ b/tools/editor/icons/icon_graph_vec_scalar_op.png diff --git a/tools/editor/icons/icon_graph_vec_to_scalars.png b/tools/editor/icons/icon_graph_vec_to_scalars.png Binary files differnew file mode 100644 index 0000000000..a677a7cc53 --- /dev/null +++ b/tools/editor/icons/icon_graph_vec_to_scalars.png diff --git a/tools/editor/icons/icon_graph_vecs_to_xform.png b/tools/editor/icons/icon_graph_vecs_to_xform.png Binary files differnew file mode 100644 index 0000000000..51216c93eb --- /dev/null +++ b/tools/editor/icons/icon_graph_vecs_to_xform.png diff --git a/tools/editor/icons/icon_graph_vector.png b/tools/editor/icons/icon_graph_vector.png Binary files differnew file mode 100644 index 0000000000..9dfe47d757 --- /dev/null +++ b/tools/editor/icons/icon_graph_vector.png diff --git a/tools/editor/icons/icon_graph_vector_uniform.png b/tools/editor/icons/icon_graph_vector_uniform.png Binary files differnew file mode 100644 index 0000000000..611539fca7 --- /dev/null +++ b/tools/editor/icons/icon_graph_vector_uniform.png diff --git a/tools/editor/icons/icon_graph_xform.png b/tools/editor/icons/icon_graph_xform.png Binary files differnew file mode 100644 index 0000000000..22df472be4 --- /dev/null +++ b/tools/editor/icons/icon_graph_xform.png diff --git a/tools/editor/icons/icon_graph_xform_mult.png b/tools/editor/icons/icon_graph_xform_mult.png Binary files differnew file mode 100644 index 0000000000..5d0ce7982d --- /dev/null +++ b/tools/editor/icons/icon_graph_xform_mult.png diff --git a/tools/editor/icons/icon_graph_xform_scalar_func.png b/tools/editor/icons/icon_graph_xform_scalar_func.png Binary files differnew file mode 100644 index 0000000000..e53f08a564 --- /dev/null +++ b/tools/editor/icons/icon_graph_xform_scalar_func.png diff --git a/tools/editor/icons/icon_graph_xform_to_vecs.png b/tools/editor/icons/icon_graph_xform_to_vecs.png Binary files differnew file mode 100644 index 0000000000..847261f726 --- /dev/null +++ b/tools/editor/icons/icon_graph_xform_to_vecs.png diff --git a/tools/editor/icons/icon_graph_xform_uniform.png b/tools/editor/icons/icon_graph_xform_uniform.png Binary files differnew file mode 100644 index 0000000000..94c9759b25 --- /dev/null +++ b/tools/editor/icons/icon_graph_xform_uniform.png diff --git a/tools/editor/icons/icon_graph_xform_vec_func.png b/tools/editor/icons/icon_graph_xform_vec_func.png Binary files differnew file mode 100644 index 0000000000..f3ba528896 --- /dev/null +++ b/tools/editor/icons/icon_graph_xform_vec_func.png diff --git a/tools/editor/icons/icon_graph_xform_vec_imult.png b/tools/editor/icons/icon_graph_xform_vec_imult.png Binary files differnew file mode 100644 index 0000000000..7e7330cb8c --- /dev/null +++ b/tools/editor/icons/icon_graph_xform_vec_imult.png diff --git a/tools/editor/icons/icon_graph_xform_vec_mult.png b/tools/editor/icons/icon_graph_xform_vec_mult.png Binary files differnew file mode 100644 index 0000000000..f80a28c80d --- /dev/null +++ b/tools/editor/icons/icon_graph_xform_vec_mult.png diff --git a/tools/editor/icons/icon_grid.png b/tools/editor/icons/icon_grid.png Binary files differnew file mode 100644 index 0000000000..dcdd86c9b5 --- /dev/null +++ b/tools/editor/icons/icon_grid.png diff --git a/tools/editor/icons/icon_group.png b/tools/editor/icons/icon_group.png Binary files differindex 577c84777e..d43b4958c9 100644 --- a/tools/editor/icons/icon_group.png +++ b/tools/editor/icons/icon_group.png diff --git a/tools/editor/icons/icon_groups.png b/tools/editor/icons/icon_groups.png Binary files differindex da4fd0d985..f4386821ed 100644 --- a/tools/editor/icons/icon_groups.png +++ b/tools/editor/icons/icon_groups.png diff --git a/tools/editor/icons/icon_help.png b/tools/editor/icons/icon_help.png Binary files differindex 3f4f8453a7..d2085589ae 100644 --- a/tools/editor/icons/icon_help.png +++ b/tools/editor/icons/icon_help.png diff --git a/tools/editor/icons/icon_hidden.png b/tools/editor/icons/icon_hidden.png Binary files differindex 45fcfc2f47..e51b9ad03a 100644 --- a/tools/editor/icons/icon_hidden.png +++ b/tools/editor/icons/icon_hidden.png diff --git a/tools/editor/icons/icon_history.png b/tools/editor/icons/icon_history.png Binary files differnew file mode 100644 index 0000000000..5c306c3ac9 --- /dev/null +++ b/tools/editor/icons/icon_history.png diff --git a/tools/editor/icons/icon_image.png b/tools/editor/icons/icon_image.png Binary files differindex a6b1fbf6c1..5919ca8c6d 100644 --- a/tools/editor/icons/icon_image.png +++ b/tools/editor/icons/icon_image.png diff --git a/tools/editor/icons/icon_image_texture.png b/tools/editor/icons/icon_image_texture.png Binary files differindex 4618d984b8..b87e284a52 100644 --- a/tools/editor/icons/icon_image_texture.png +++ b/tools/editor/icons/icon_image_texture.png diff --git a/tools/editor/icons/icon_instance_options.png b/tools/editor/icons/icon_instance_options.png Binary files differindex 2d3e98b2ea..9108448095 100644 --- a/tools/editor/icons/icon_instance_options.png +++ b/tools/editor/icons/icon_instance_options.png diff --git a/tools/editor/icons/icon_integer.png b/tools/editor/icons/icon_integer.png Binary files differindex 0e5b5abd62..32c8d9885b 100644 --- a/tools/editor/icons/icon_integer.png +++ b/tools/editor/icons/icon_integer.png diff --git a/tools/editor/icons/icon_interp_cubic.png b/tools/editor/icons/icon_interp_cubic.png Binary files differindex ab33aa7e6a..a946d70947 100644 --- a/tools/editor/icons/icon_interp_cubic.png +++ b/tools/editor/icons/icon_interp_cubic.png diff --git a/tools/editor/icons/icon_interp_linear.png b/tools/editor/icons/icon_interp_linear.png Binary files differindex bf3849ecaf..9174af39e7 100644 --- a/tools/editor/icons/icon_interp_linear.png +++ b/tools/editor/icons/icon_interp_linear.png diff --git a/tools/editor/icons/icon_interp_raw.png b/tools/editor/icons/icon_interp_raw.png Binary files differindex 48650d6e66..f12936493b 100644 --- a/tools/editor/icons/icon_interp_raw.png +++ b/tools/editor/icons/icon_interp_raw.png diff --git a/tools/editor/icons/icon_item_list.png b/tools/editor/icons/icon_item_list.png Binary files differnew file mode 100644 index 0000000000..f930e7ecaa --- /dev/null +++ b/tools/editor/icons/icon_item_list.png diff --git a/tools/editor/icons/icon_key.png b/tools/editor/icons/icon_key.png Binary files differindex d647876866..d6096ef41f 100644 --- a/tools/editor/icons/icon_key.png +++ b/tools/editor/icons/icon_key.png diff --git a/tools/editor/icons/icon_key_next.png b/tools/editor/icons/icon_key_next.png Binary files differnew file mode 100644 index 0000000000..759008d064 --- /dev/null +++ b/tools/editor/icons/icon_key_next.png diff --git a/tools/editor/icons/icon_key_selected.png b/tools/editor/icons/icon_key_selected.png Binary files differindex d916c55286..562beef98a 100644 --- a/tools/editor/icons/icon_key_selected.png +++ b/tools/editor/icons/icon_key_selected.png diff --git a/tools/editor/icons/icon_light_2d.png b/tools/editor/icons/icon_light_2d.png Binary files differnew file mode 100644 index 0000000000..9162b33090 --- /dev/null +++ b/tools/editor/icons/icon_light_2d.png diff --git a/tools/editor/icons/icon_light_map.png b/tools/editor/icons/icon_light_map.png Binary files differindex 96d3f6e11c..e0333f06ea 100644 --- a/tools/editor/icons/icon_light_map.png +++ b/tools/editor/icons/icon_light_map.png diff --git a/tools/editor/icons/icon_light_occluder_2d.png b/tools/editor/icons/icon_light_occluder_2d.png Binary files differnew file mode 100644 index 0000000000..c66dd536d3 --- /dev/null +++ b/tools/editor/icons/icon_light_occluder_2d.png diff --git a/tools/editor/icons/icon_live_debug.png b/tools/editor/icons/icon_live_debug.png Binary files differnew file mode 100644 index 0000000000..ad55646b9a --- /dev/null +++ b/tools/editor/icons/icon_live_debug.png diff --git a/tools/editor/icons/icon_load.png b/tools/editor/icons/icon_load.png Binary files differindex fdc06d38a3..a450a7b297 100644 --- a/tools/editor/icons/icon_load.png +++ b/tools/editor/icons/icon_load.png diff --git a/tools/editor/icons/icon_lock.png b/tools/editor/icons/icon_lock.png Binary files differindex 0cfd1d4ab1..995d87b6fb 100644 --- a/tools/editor/icons/icon_lock.png +++ b/tools/editor/icons/icon_lock.png diff --git a/tools/editor/icons/icon_loop.png b/tools/editor/icons/icon_loop.png Binary files differindex d75642359d..7bde451ca0 100644 --- a/tools/editor/icons/icon_loop.png +++ b/tools/editor/icons/icon_loop.png diff --git a/tools/editor/icons/icon_main_play.png b/tools/editor/icons/icon_main_play.png Binary files differindex 401708c49e..9e8cc6c4a9 100644 --- a/tools/editor/icons/icon_main_play.png +++ b/tools/editor/icons/icon_main_play.png diff --git a/tools/editor/icons/icon_main_stop.png b/tools/editor/icons/icon_main_stop.png Binary files differindex 3f54ba69c9..31a6cd601e 100644 --- a/tools/editor/icons/icon_main_stop.png +++ b/tools/editor/icons/icon_main_stop.png diff --git a/tools/editor/icons/icon_material_shader.png b/tools/editor/icons/icon_material_shader.png Binary files differnew file mode 100644 index 0000000000..0e476b2540 --- /dev/null +++ b/tools/editor/icons/icon_material_shader.png diff --git a/tools/editor/icons/icon_material_shader_graph.png b/tools/editor/icons/icon_material_shader_graph.png Binary files differnew file mode 100644 index 0000000000..68d8b4cb49 --- /dev/null +++ b/tools/editor/icons/icon_material_shader_graph.png diff --git a/tools/editor/icons/icon_mirror_x.png b/tools/editor/icons/icon_mirror_x.png Binary files differindex d20f90c1da..657e7f5458 100644 --- a/tools/editor/icons/icon_mirror_x.png +++ b/tools/editor/icons/icon_mirror_x.png diff --git a/tools/editor/icons/icon_mirror_y.png b/tools/editor/icons/icon_mirror_y.png Binary files differindex 5e2f710425..111aa5e4ae 100644 --- a/tools/editor/icons/icon_mirror_y.png +++ b/tools/editor/icons/icon_mirror_y.png diff --git a/tools/editor/icons/icon_move_down.png b/tools/editor/icons/icon_move_down.png Binary files differindex ef310e80e1..06c7246084 100644 --- a/tools/editor/icons/icon_move_down.png +++ b/tools/editor/icons/icon_move_down.png diff --git a/tools/editor/icons/icon_move_down_hl.png b/tools/editor/icons/icon_move_down_hl.png Binary files differindex dec56e8da8..f9de58a940 100644 --- a/tools/editor/icons/icon_move_down_hl.png +++ b/tools/editor/icons/icon_move_down_hl.png diff --git a/tools/editor/icons/icon_move_up.png b/tools/editor/icons/icon_move_up.png Binary files differindex d67b1aff0b..ca6c64f7a1 100644 --- a/tools/editor/icons/icon_move_up.png +++ b/tools/editor/icons/icon_move_up.png diff --git a/tools/editor/icons/icon_move_up_hl.png b/tools/editor/icons/icon_move_up_hl.png Binary files differindex 19ce8bbe27..e076c9a265 100644 --- a/tools/editor/icons/icon_move_up_hl.png +++ b/tools/editor/icons/icon_move_up_hl.png diff --git a/tools/editor/icons/icon_multi_edit.png b/tools/editor/icons/icon_multi_edit.png Binary files differnew file mode 100644 index 0000000000..70faee3d6a --- /dev/null +++ b/tools/editor/icons/icon_multi_edit.png diff --git a/tools/editor/icons/icon_multi_node_edit.png b/tools/editor/icons/icon_multi_node_edit.png Binary files differnew file mode 100644 index 0000000000..357c062cbd --- /dev/null +++ b/tools/editor/icons/icon_multi_node_edit.png diff --git a/tools/editor/icons/icon_navigation_2d.png b/tools/editor/icons/icon_navigation_2d.png Binary files differnew file mode 100644 index 0000000000..8170ecf68c --- /dev/null +++ b/tools/editor/icons/icon_navigation_2d.png diff --git a/tools/editor/icons/icon_navigation_polygon_instance.png b/tools/editor/icons/icon_navigation_polygon_instance.png Binary files differnew file mode 100644 index 0000000000..9f9c318906 --- /dev/null +++ b/tools/editor/icons/icon_navigation_polygon_instance.png diff --git a/tools/editor/icons/icon_new.png b/tools/editor/icons/icon_new.png Binary files differindex 3596d2e8ea..c04785fc3f 100644 --- a/tools/editor/icons/icon_new.png +++ b/tools/editor/icons/icon_new.png diff --git a/tools/editor/icons/icon_node.png b/tools/editor/icons/icon_node.png Binary files differindex b0f7fb01dc..d8ce1b7538 100644 --- a/tools/editor/icons/icon_node.png +++ b/tools/editor/icons/icon_node.png diff --git a/tools/editor/icons/icon_non_favorite.png b/tools/editor/icons/icon_non_favorite.png Binary files differnew file mode 100644 index 0000000000..edd806fbe8 --- /dev/null +++ b/tools/editor/icons/icon_non_favorite.png diff --git a/tools/editor/icons/icon_occluder_polygon_2d.png b/tools/editor/icons/icon_occluder_polygon_2d.png Binary files differnew file mode 100644 index 0000000000..705794b729 --- /dev/null +++ b/tools/editor/icons/icon_occluder_polygon_2d.png diff --git a/tools/editor/icons/icon_open.png b/tools/editor/icons/icon_open.png Binary files differindex 4fad5677ca..a450a7b297 100644 --- a/tools/editor/icons/icon_open.png +++ b/tools/editor/icons/icon_open.png diff --git a/tools/editor/icons/icon_p_hash_translation.png b/tools/editor/icons/icon_p_hash_translation.png Binary files differindex c0eadc3c55..e18ef6a76f 100644 --- a/tools/editor/icons/icon_p_hash_translation.png +++ b/tools/editor/icons/icon_p_hash_translation.png diff --git a/tools/editor/icons/icon_packed_scene.png b/tools/editor/icons/icon_packed_scene.png Binary files differindex 9c1e1c4fbf..c9802f2b66 100644 --- a/tools/editor/icons/icon_packed_scene.png +++ b/tools/editor/icons/icon_packed_scene.png diff --git a/tools/editor/icons/icon_panel_top.png b/tools/editor/icons/icon_panel_top.png Binary files differnew file mode 100644 index 0000000000..20e67fad1a --- /dev/null +++ b/tools/editor/icons/icon_panel_top.png diff --git a/tools/editor/icons/icon_panels_1.png b/tools/editor/icons/icon_panels_1.png Binary files differindex 501c8c9acc..546ca61c89 100644 --- a/tools/editor/icons/icon_panels_1.png +++ b/tools/editor/icons/icon_panels_1.png diff --git a/tools/editor/icons/icon_panels_2.png b/tools/editor/icons/icon_panels_2.png Binary files differindex 08f104e2b1..5a4750bda2 100644 --- a/tools/editor/icons/icon_panels_2.png +++ b/tools/editor/icons/icon_panels_2.png diff --git a/tools/editor/icons/icon_panels_3.png b/tools/editor/icons/icon_panels_3.png Binary files differindex 1d1902d8dd..13988de93a 100644 --- a/tools/editor/icons/icon_panels_3.png +++ b/tools/editor/icons/icon_panels_3.png diff --git a/tools/editor/icons/icon_panels_4.png b/tools/editor/icons/icon_panels_4.png Binary files differindex 83cc133d21..c217330d43 100644 --- a/tools/editor/icons/icon_panels_4.png +++ b/tools/editor/icons/icon_panels_4.png diff --git a/tools/editor/icons/icon_patch_9_frame.png b/tools/editor/icons/icon_patch_9_frame.png Binary files differnew file mode 100644 index 0000000000..c8f38fa61a --- /dev/null +++ b/tools/editor/icons/icon_patch_9_frame.png diff --git a/tools/editor/icons/icon_pause.png b/tools/editor/icons/icon_pause.png Binary files differindex e63661353c..7c0a57003e 100644 --- a/tools/editor/icons/icon_pause.png +++ b/tools/editor/icons/icon_pause.png diff --git a/tools/editor/icons/icon_pin.png b/tools/editor/icons/icon_pin.png Binary files differindex f34c8585f7..037352137d 100644 --- a/tools/editor/icons/icon_pin.png +++ b/tools/editor/icons/icon_pin.png diff --git a/tools/editor/icons/icon_pin_pressed.png b/tools/editor/icons/icon_pin_pressed.png Binary files differindex f151b5a590..5738e6856f 100644 --- a/tools/editor/icons/icon_pin_pressed.png +++ b/tools/editor/icons/icon_pin_pressed.png diff --git a/tools/editor/icons/icon_play.png b/tools/editor/icons/icon_play.png Binary files differindex 544b3bc5f4..d2bd9d310c 100644 --- a/tools/editor/icons/icon_play.png +++ b/tools/editor/icons/icon_play.png diff --git a/tools/editor/icons/icon_play_backwards.png b/tools/editor/icons/icon_play_backwards.png Binary files differnew file mode 100644 index 0000000000..8dff5f20e3 --- /dev/null +++ b/tools/editor/icons/icon_play_backwards.png diff --git a/tools/editor/icons/icon_play_custom.png b/tools/editor/icons/icon_play_custom.png Binary files differindex 5c98c7100b..8e8ab8c62a 100644 --- a/tools/editor/icons/icon_play_custom.png +++ b/tools/editor/icons/icon_play_custom.png diff --git a/tools/editor/icons/icon_play_scene.png b/tools/editor/icons/icon_play_scene.png Binary files differindex 7ca59fe900..7079cc9677 100644 --- a/tools/editor/icons/icon_play_scene.png +++ b/tools/editor/icons/icon_play_scene.png diff --git a/tools/editor/icons/icon_play_start.png b/tools/editor/icons/icon_play_start.png Binary files differnew file mode 100644 index 0000000000..5b085aa1ed --- /dev/null +++ b/tools/editor/icons/icon_play_start.png diff --git a/tools/editor/icons/icon_play_start_backwards.png b/tools/editor/icons/icon_play_start_backwards.png Binary files differnew file mode 100644 index 0000000000..09afac637c --- /dev/null +++ b/tools/editor/icons/icon_play_start_backwards.png diff --git a/tools/editor/icons/icon_prev_scene.png b/tools/editor/icons/icon_prev_scene.png Binary files differindex c7c180e1c4..9d8dda5180 100644 --- a/tools/editor/icons/icon_prev_scene.png +++ b/tools/editor/icons/icon_prev_scene.png diff --git a/tools/editor/icons/icon_real.png b/tools/editor/icons/icon_real.png Binary files differindex bfe5038319..80fbf7017c 100644 --- a/tools/editor/icons/icon_real.png +++ b/tools/editor/icons/icon_real.png diff --git a/tools/editor/icons/icon_region_edit.png b/tools/editor/icons/icon_region_edit.png Binary files differnew file mode 100644 index 0000000000..824607f2cc --- /dev/null +++ b/tools/editor/icons/icon_region_edit.png diff --git a/tools/editor/icons/icon_reload.png b/tools/editor/icons/icon_reload.png Binary files differindex 07f53efb56..f7c6530d77 100644 --- a/tools/editor/icons/icon_reload.png +++ b/tools/editor/icons/icon_reload.png diff --git a/tools/editor/icons/icon_remote.png b/tools/editor/icons/icon_remote.png Binary files differnew file mode 100644 index 0000000000..792d958a46 --- /dev/null +++ b/tools/editor/icons/icon_remote.png diff --git a/tools/editor/icons/icon_remove.png b/tools/editor/icons/icon_remove.png Binary files differindex 5349af466e..f0f814e304 100644 --- a/tools/editor/icons/icon_remove.png +++ b/tools/editor/icons/icon_remove.png diff --git a/tools/editor/icons/icon_remove_small.png b/tools/editor/icons/icon_remove_small.png Binary files differnew file mode 100644 index 0000000000..e0903689cf --- /dev/null +++ b/tools/editor/icons/icon_remove_small.png diff --git a/tools/editor/icons/icon_rename.png b/tools/editor/icons/icon_rename.png Binary files differindex f88da39915..7b6a10df93 100644 --- a/tools/editor/icons/icon_rename.png +++ b/tools/editor/icons/icon_rename.png diff --git a/tools/editor/icons/icon_reparent.png b/tools/editor/icons/icon_reparent.png Binary files differindex af85b17ecc..59aee5e42d 100644 --- a/tools/editor/icons/icon_reparent.png +++ b/tools/editor/icons/icon_reparent.png diff --git a/tools/editor/icons/icon_replace.png b/tools/editor/icons/icon_replace.png Binary files differindex 2ae843ae10..662a58dc93 100644 --- a/tools/editor/icons/icon_replace.png +++ b/tools/editor/icons/icon_replace.png diff --git a/tools/editor/icons/icon_resource_preloader.png b/tools/editor/icons/icon_resource_preloader.png Binary files differindex e31e5a0d59..14b8c4de3c 100644 --- a/tools/editor/icons/icon_resource_preloader.png +++ b/tools/editor/icons/icon_resource_preloader.png diff --git a/tools/editor/icons/icon_rotate_0.png b/tools/editor/icons/icon_rotate_0.png Binary files differnew file mode 100644 index 0000000000..85a4b4c420 --- /dev/null +++ b/tools/editor/icons/icon_rotate_0.png diff --git a/tools/editor/icons/icon_rotate_180.png b/tools/editor/icons/icon_rotate_180.png Binary files differnew file mode 100644 index 0000000000..c4c516cff5 --- /dev/null +++ b/tools/editor/icons/icon_rotate_180.png diff --git a/tools/editor/icons/icon_rotate_270.png b/tools/editor/icons/icon_rotate_270.png Binary files differnew file mode 100644 index 0000000000..6e0f2e62b8 --- /dev/null +++ b/tools/editor/icons/icon_rotate_270.png diff --git a/tools/editor/icons/icon_rotate_90.png b/tools/editor/icons/icon_rotate_90.png Binary files differnew file mode 100644 index 0000000000..f25b0e99a3 --- /dev/null +++ b/tools/editor/icons/icon_rotate_90.png diff --git a/tools/editor/icons/icon_sample_player.png b/tools/editor/icons/icon_sample_player.png Binary files differindex 5561769b05..92d9cc77bf 100644 --- a/tools/editor/icons/icon_sample_player.png +++ b/tools/editor/icons/icon_sample_player.png diff --git a/tools/editor/icons/icon_save.png b/tools/editor/icons/icon_save.png Binary files differindex dce274ffb1..ddef66688d 100644 --- a/tools/editor/icons/icon_save.png +++ b/tools/editor/icons/icon_save.png diff --git a/tools/editor/icons/icon_script.png b/tools/editor/icons/icon_script.png Binary files differindex 65fb3c4934..baf5927c18 100644 --- a/tools/editor/icons/icon_script.png +++ b/tools/editor/icons/icon_script.png diff --git a/tools/editor/icons/icon_script_list.png b/tools/editor/icons/icon_script_list.png Binary files differnew file mode 100644 index 0000000000..cdea1e161e --- /dev/null +++ b/tools/editor/icons/icon_script_list.png diff --git a/tools/editor/icons/icon_sound_room_params.png b/tools/editor/icons/icon_sound_room_params.png Binary files differindex 8e381d7978..2d37a4b49f 100644 --- a/tools/editor/icons/icon_sound_room_params.png +++ b/tools/editor/icons/icon_sound_room_params.png diff --git a/tools/editor/icons/icon_stop.png b/tools/editor/icons/icon_stop.png Binary files differindex 3b7562fa4a..0fd43b403a 100644 --- a/tools/editor/icons/icon_stop.png +++ b/tools/editor/icons/icon_stop.png diff --git a/tools/editor/icons/icon_stream_player.png b/tools/editor/icons/icon_stream_player.png Binary files differindex 2670a567e8..cf8fdcbaea 100644 --- a/tools/editor/icons/icon_stream_player.png +++ b/tools/editor/icons/icon_stream_player.png diff --git a/tools/editor/icons/icon_string.png b/tools/editor/icons/icon_string.png Binary files differindex 86cc8e633f..48bf753c40 100644 --- a/tools/editor/icons/icon_string.png +++ b/tools/editor/icons/icon_string.png diff --git a/tools/editor/icons/icon_tab_menu.png b/tools/editor/icons/icon_tab_menu.png Binary files differnew file mode 100644 index 0000000000..29edd02f01 --- /dev/null +++ b/tools/editor/icons/icon_tab_menu.png diff --git a/tools/editor/icons/icon_texture.png b/tools/editor/icons/icon_texture.png Binary files differindex 03d6ac7db2..bbcc54bd6e 100644 --- a/tools/editor/icons/icon_texture.png +++ b/tools/editor/icons/icon_texture.png diff --git a/tools/editor/icons/icon_timer.png b/tools/editor/icons/icon_timer.png Binary files differindex 3855683033..e8c36ae893 100644 --- a/tools/editor/icons/icon_timer.png +++ b/tools/editor/icons/icon_timer.png diff --git a/tools/editor/icons/icon_tool_move.png b/tools/editor/icons/icon_tool_move.png Binary files differindex fc611cdbb1..7257d3897b 100644 --- a/tools/editor/icons/icon_tool_move.png +++ b/tools/editor/icons/icon_tool_move.png diff --git a/tools/editor/icons/icon_tool_pan.png b/tools/editor/icons/icon_tool_pan.png Binary files differindex 5c078a7b1c..bfe6fddf45 100644 --- a/tools/editor/icons/icon_tool_pan.png +++ b/tools/editor/icons/icon_tool_pan.png diff --git a/tools/editor/icons/icon_tool_rotate.png b/tools/editor/icons/icon_tool_rotate.png Binary files differindex c833b93d6e..9575ceb54e 100644 --- a/tools/editor/icons/icon_tool_rotate.png +++ b/tools/editor/icons/icon_tool_rotate.png diff --git a/tools/editor/icons/icon_tool_scale.png b/tools/editor/icons/icon_tool_scale.png Binary files differindex 3eaeae1e99..a94a6e7c98 100644 --- a/tools/editor/icons/icon_tool_scale.png +++ b/tools/editor/icons/icon_tool_scale.png diff --git a/tools/editor/icons/icon_tool_select.png b/tools/editor/icons/icon_tool_select.png Binary files differindex eb5ff6e1da..47683228e9 100644 --- a/tools/editor/icons/icon_tool_select.png +++ b/tools/editor/icons/icon_tool_select.png diff --git a/tools/editor/icons/icon_tools.png b/tools/editor/icons/icon_tools.png Binary files differindex 927173ea0f..f02d924203 100644 --- a/tools/editor/icons/icon_tools.png +++ b/tools/editor/icons/icon_tools.png diff --git a/tools/editor/icons/icon_track_continuous.png b/tools/editor/icons/icon_track_continuous.png Binary files differindex 97e8762299..9f99891c21 100644 --- a/tools/editor/icons/icon_track_continuous.png +++ b/tools/editor/icons/icon_track_continuous.png diff --git a/tools/editor/icons/icon_track_discrete.png b/tools/editor/icons/icon_track_discrete.png Binary files differindex 57a4cd5579..4e65e49afb 100644 --- a/tools/editor/icons/icon_track_discrete.png +++ b/tools/editor/icons/icon_track_discrete.png diff --git a/tools/editor/icons/icon_translation.png b/tools/editor/icons/icon_translation.png Binary files differindex 6211ab9a1b..917c6f548a 100644 --- a/tools/editor/icons/icon_translation.png +++ b/tools/editor/icons/icon_translation.png diff --git a/tools/editor/icons/icon_transpose.png b/tools/editor/icons/icon_transpose.png Binary files differnew file mode 100644 index 0000000000..f9b78bc0fd --- /dev/null +++ b/tools/editor/icons/icon_transpose.png diff --git a/tools/editor/icons/icon_unbone.png b/tools/editor/icons/icon_unbone.png Binary files differindex 819e8a8e5d..c8cd774460 100644 --- a/tools/editor/icons/icon_unbone.png +++ b/tools/editor/icons/icon_unbone.png diff --git a/tools/editor/icons/icon_ungroup.png b/tools/editor/icons/icon_ungroup.png Binary files differindex 16511e3f1c..4ea620bf96 100644 --- a/tools/editor/icons/icon_ungroup.png +++ b/tools/editor/icons/icon_ungroup.png diff --git a/tools/editor/icons/icon_unlock.png b/tools/editor/icons/icon_unlock.png Binary files differindex b86447bf7a..f9fa31c3e0 100644 --- a/tools/editor/icons/icon_unlock.png +++ b/tools/editor/icons/icon_unlock.png diff --git a/tools/editor/icons/icon_uv.png b/tools/editor/icons/icon_uv.png Binary files differindex 4d9d198d86..39bc737a37 100644 --- a/tools/editor/icons/icon_uv.png +++ b/tools/editor/icons/icon_uv.png diff --git a/tools/editor/icons/icon_vector.png b/tools/editor/icons/icon_vector.png Binary files differindex 7826d7f7a9..0ee33ba0b7 100644 --- a/tools/editor/icons/icon_vector.png +++ b/tools/editor/icons/icon_vector.png diff --git a/tools/editor/icons/icon_vector2.png b/tools/editor/icons/icon_vector2.png Binary files differindex 44e48c36c7..5920109a55 100644 --- a/tools/editor/icons/icon_vector2.png +++ b/tools/editor/icons/icon_vector2.png diff --git a/tools/editor/icons/icon_viewport.png b/tools/editor/icons/icon_viewport.png Binary files differindex 0a0d93cf4d..3859f6c7e9 100644 --- a/tools/editor/icons/icon_viewport.png +++ b/tools/editor/icons/icon_viewport.png diff --git a/tools/editor/icons/icon_visible.png b/tools/editor/icons/icon_visible.png Binary files differindex 519898bbef..cbc44c4e30 100644 --- a/tools/editor/icons/icon_visible.png +++ b/tools/editor/icons/icon_visible.png diff --git a/tools/editor/icons/icon_wait_no_preview.png b/tools/editor/icons/icon_wait_no_preview.png Binary files differnew file mode 100644 index 0000000000..5d20cd99ec --- /dev/null +++ b/tools/editor/icons/icon_wait_no_preview.png diff --git a/tools/editor/icons/icon_wait_preview_1.png b/tools/editor/icons/icon_wait_preview_1.png Binary files differnew file mode 100644 index 0000000000..0aab42e04a --- /dev/null +++ b/tools/editor/icons/icon_wait_preview_1.png diff --git a/tools/editor/icons/icon_wait_preview_2.png b/tools/editor/icons/icon_wait_preview_2.png Binary files differnew file mode 100644 index 0000000000..f476b9ce1f --- /dev/null +++ b/tools/editor/icons/icon_wait_preview_2.png diff --git a/tools/editor/icons/icon_wait_preview_3.png b/tools/editor/icons/icon_wait_preview_3.png Binary files differnew file mode 100644 index 0000000000..2775d1ef43 --- /dev/null +++ b/tools/editor/icons/icon_wait_preview_3.png diff --git a/tools/editor/icons/icon_wait_preview_4.png b/tools/editor/icons/icon_wait_preview_4.png Binary files differnew file mode 100644 index 0000000000..2eaa86fec9 --- /dev/null +++ b/tools/editor/icons/icon_wait_preview_4.png diff --git a/tools/editor/icons/icon_wait_preview_5.png b/tools/editor/icons/icon_wait_preview_5.png Binary files differnew file mode 100644 index 0000000000..6590644bc1 --- /dev/null +++ b/tools/editor/icons/icon_wait_preview_5.png diff --git a/tools/editor/icons/icon_wait_preview_6.png b/tools/editor/icons/icon_wait_preview_6.png Binary files differnew file mode 100644 index 0000000000..307e412310 --- /dev/null +++ b/tools/editor/icons/icon_wait_preview_6.png diff --git a/tools/editor/icons/icon_wait_preview_7.png b/tools/editor/icons/icon_wait_preview_7.png Binary files differnew file mode 100644 index 0000000000..b0edc94d93 --- /dev/null +++ b/tools/editor/icons/icon_wait_preview_7.png diff --git a/tools/editor/icons/icon_wait_preview_8.png b/tools/editor/icons/icon_wait_preview_8.png Binary files differnew file mode 100644 index 0000000000..67a2f48ec3 --- /dev/null +++ b/tools/editor/icons/icon_wait_preview_8.png diff --git a/tools/editor/icons/icon_zoom.png b/tools/editor/icons/icon_zoom.png Binary files differindex cbacaaaeca..e4bbbfe7c3 100644 --- a/tools/editor/icons/icon_zoom.png +++ b/tools/editor/icons/icon_zoom.png diff --git a/tools/editor/import_settings.cpp b/tools/editor/import_settings.cpp index 63b8d65b69..36d7828be0 100644 --- a/tools/editor/import_settings.cpp +++ b/tools/editor/import_settings.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/import_settings.h b/tools/editor/import_settings.h index eb7a8b6143..31237dd8cf 100644 --- a/tools/editor/import_settings.h +++ b/tools/editor/import_settings.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/inspector_dock.cpp b/tools/editor/inspector_dock.cpp new file mode 100644 index 0000000000..57d19c3ec8 --- /dev/null +++ b/tools/editor/inspector_dock.cpp @@ -0,0 +1,22 @@ +#include "inspector_dock.h" + +#if 0 +void InspectorDock::_go_next() { + + +} + +void InspectorDock::_go_prev() { + + +} + +void InspectorDock::_bind_methods() { + +} + +InspectorDock::InspectorDock() { + + +} +#endif diff --git a/tools/editor/inspector_dock.h b/tools/editor/inspector_dock.h new file mode 100644 index 0000000000..90f043aba8 --- /dev/null +++ b/tools/editor/inspector_dock.h @@ -0,0 +1,35 @@ +#ifndef INSPECTOR_DOCK_H +#define INSPECTOR_DOCK_H + +#include "scene/gui/box_container.h" +#include "property_editor.h" + + +//this is for now bundled in EditorNode, will be moved away here eventually + +#if 0 +class InspectorDock : public VBoxContainer +{ + OBJ_TYPE(InspectorDock,VBoxContainer); + + PropertyEditor *property_editor; + + EditorHistory editor_history; + + void _go_next(); + void _go_prev(); + +protected: + + static void _bind_methods(); +public: + + EditorHistory &get_editor_history(); + + PropertyEditor *get_property_editor(); + + InspectorDock(); +}; + +#endif +#endif // INSPECTOR_DOCK_H diff --git a/tools/editor/io_plugins/SCsub b/tools/editor/io_plugins/SCsub index b525fb3f75..363a2ce4c0 100644 --- a/tools/editor/io_plugins/SCsub +++ b/tools/editor/io_plugins/SCsub @@ -1,7 +1,3 @@ Import('env') Export('env') env.add_source_files(env.tool_sources,"*.cpp") - - - - diff --git a/tools/editor/io_plugins/editor_atlas.cpp b/tools/editor/io_plugins/editor_atlas.cpp index 4c716874ba..7e9acd193d 100644 --- a/tools/editor/io_plugins/editor_atlas.cpp +++ b/tools/editor/io_plugins/editor_atlas.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,7 +27,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "editor_atlas.h" - +#include "print_string.h" struct _EditorAtlasWorkRect { @@ -83,6 +83,7 @@ void EditorAtlas::fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, S //place them int ofs=0; int limit_h=0; + for(int j=0;j<wrects.size();j++) { @@ -100,6 +101,9 @@ void EditorAtlas::fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, S wrects[j].p.x=ofs; wrects[j].p.y=from_y; + + + int end_h = from_y+wrects[j].s.height; int end_w = ofs+wrects[j].s.width; if (ofs==0) @@ -116,7 +120,7 @@ void EditorAtlas::fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, S if (end_w > max_w) max_w=end_w; - if (ofs==0 || end_h>limit_h ) //while h limit not reched, keep stacking + //if (ofs==0 || end_h>limit_h ) //while h limit not reched, keep stacking ofs+=wrects[j].s.width; } @@ -136,8 +140,8 @@ void EditorAtlas::fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, S for(int i=0;i<results.size();i++) { - float h = nearest_power_of_2(results[i].max_h); - float w = nearest_power_of_2(results[i].max_w); + float h = results[i].max_h; + float w = results[i].max_w; float aspect = h>w ? h/w : w/h; if (aspect < best_aspect) { best=i; diff --git a/tools/editor/io_plugins/editor_atlas.h b/tools/editor/io_plugins/editor_atlas.h index 685cf60c9d..716faff0c6 100644 --- a/tools/editor/io_plugins/editor_atlas.h +++ b/tools/editor/io_plugins/editor_atlas.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/io_plugins/editor_font_import_plugin.cpp b/tools/editor/io_plugins/editor_font_import_plugin.cpp index 064758f6cd..10a3877529 100644 --- a/tools/editor/io_plugins/editor_font_import_plugin.cpp +++ b/tools/editor/io_plugins/editor_font_import_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,7 +28,7 @@ /*************************************************************************/ #include "editor_font_import_plugin.h" #include "scene/gui/dialogs.h" -#include "scene/gui/file_dialog.h" +#include "tools/editor/editor_file_dialog.h" #include "tools/editor/editor_node.h" #include "os/file_access.h" #include "editor_atlas.h" @@ -47,6 +47,12 @@ class _EditorFontImportOptions : public Object { OBJ_TYPE(_EditorFontImportOptions,Object); public: + enum FontMode { + + FONT_BITMAP, + FONT_DISTANCE_FIELD + }; + enum ColorType { COLOR_WHITE, COLOR_CUSTOM, @@ -69,6 +75,9 @@ public: CHARSET_CUSTOM_LATIN }; + + FontMode font_mode; + CharacterSet character_set; String custom_file; @@ -91,7 +100,6 @@ public: bool color_use_monochrome; String gradient_image; - bool disable_filter; bool round_advance; @@ -100,7 +108,10 @@ public: bool _set(const StringName& p_name, const Variant& p_value) { String n = p_name; - if (n=="extra_space/char") + if (n=="mode/mode") { + font_mode=FontMode(int(p_value)); + _change_notify(); + } else if (n=="extra_space/char") char_extra_spacing=p_value; else if (n=="extra_space/space") space_extra_spacing=p_value; @@ -169,7 +180,9 @@ public: bool _get(const StringName& p_name,Variant &r_ret) const{ String n = p_name; - if (n=="extra_space/char") + if (n=="mode/mode") + r_ret=font_mode; + else if (n=="extra_space/char") r_ret=char_extra_spacing; else if (n=="extra_space/space") r_ret=space_extra_spacing; @@ -231,6 +244,9 @@ public: void _get_property_list( List<PropertyInfo> *p_list) const{ + + p_list->push_back(PropertyInfo(Variant::INT,"mode/mode",PROPERTY_HINT_ENUM,"Bitmap,Distance Field")); + p_list->push_back(PropertyInfo(Variant::INT,"extra_space/char",PROPERTY_HINT_RANGE,"-64,64,1")); p_list->push_back(PropertyInfo(Variant::INT,"extra_space/space",PROPERTY_HINT_RANGE,"-64,64,1")); p_list->push_back(PropertyInfo(Variant::INT,"extra_space/top",PROPERTY_HINT_RANGE,"-64,64,1")); @@ -240,35 +256,45 @@ public: if (character_set>=CHARSET_CUSTOM) p_list->push_back(PropertyInfo(Variant::STRING,"character_set/custom",PROPERTY_HINT_FILE)); - p_list->push_back(PropertyInfo(Variant::BOOL,"shadow/enabled")); - if (shadow) { - p_list->push_back(PropertyInfo(Variant::INT,"shadow/radius",PROPERTY_HINT_RANGE,"-64,64,1")); - p_list->push_back(PropertyInfo(Variant::VECTOR2,"shadow/offset")); - p_list->push_back(PropertyInfo(Variant::COLOR,"shadow/color")); - p_list->push_back(PropertyInfo(Variant::REAL,"shadow/transition",PROPERTY_HINT_EXP_EASING)); - } + int usage = PROPERTY_USAGE_DEFAULT; - p_list->push_back(PropertyInfo(Variant::BOOL,"shadow2/enabled")); - if (shadow2) { - p_list->push_back(PropertyInfo(Variant::INT,"shadow2/radius",PROPERTY_HINT_RANGE,"-64,64,1")); - p_list->push_back(PropertyInfo(Variant::VECTOR2,"shadow2/offset")); - p_list->push_back(PropertyInfo(Variant::COLOR,"shadow2/color")); - p_list->push_back(PropertyInfo(Variant::REAL,"shadow2/transition",PROPERTY_HINT_EXP_EASING)); + if (font_mode==FONT_DISTANCE_FIELD) { + usage = PROPERTY_USAGE_NOEDITOR; } - p_list->push_back(PropertyInfo(Variant::INT,"color/mode",PROPERTY_HINT_ENUM,"White,Color,Gradient,Gradient Image")); - if (color_type==COLOR_CUSTOM) { - p_list->push_back(PropertyInfo(Variant::COLOR,"color/color")); + { + p_list->push_back(PropertyInfo(Variant::BOOL,"shadow/enabled",PROPERTY_HINT_NONE,"",usage)); + if (shadow) { + p_list->push_back(PropertyInfo(Variant::INT,"shadow/radius",PROPERTY_HINT_RANGE,"-64,64,1",usage)); + p_list->push_back(PropertyInfo(Variant::VECTOR2,"shadow/offset",PROPERTY_HINT_NONE,"",usage)); + p_list->push_back(PropertyInfo(Variant::COLOR,"shadow/color",PROPERTY_HINT_NONE,"",usage)); + p_list->push_back(PropertyInfo(Variant::REAL,"shadow/transition",PROPERTY_HINT_EXP_EASING,"",usage)); + } + + p_list->push_back(PropertyInfo(Variant::BOOL,"shadow2/enabled",PROPERTY_HINT_NONE,"",usage)); + if (shadow2) { + p_list->push_back(PropertyInfo(Variant::INT,"shadow2/radius",PROPERTY_HINT_RANGE,"-64,64,1",usage)); + p_list->push_back(PropertyInfo(Variant::VECTOR2,"shadow2/offset",PROPERTY_HINT_NONE,"",usage)); + p_list->push_back(PropertyInfo(Variant::COLOR,"shadow2/color",PROPERTY_HINT_NONE,"",usage)); + p_list->push_back(PropertyInfo(Variant::REAL,"shadow2/transition",PROPERTY_HINT_EXP_EASING,"",usage)); + } + + p_list->push_back(PropertyInfo(Variant::INT,"color/mode",PROPERTY_HINT_ENUM,"White,Color,Gradient,Gradient Image",usage)); + if (color_type==COLOR_CUSTOM) { + p_list->push_back(PropertyInfo(Variant::COLOR,"color/color",PROPERTY_HINT_NONE,"",usage)); + + } + if (color_type==COLOR_GRADIENT_RANGE) { + p_list->push_back(PropertyInfo(Variant::COLOR,"color/begin",PROPERTY_HINT_NONE,"",usage)); + p_list->push_back(PropertyInfo(Variant::COLOR,"color/end",PROPERTY_HINT_NONE,"",usage)); + } + if (color_type==COLOR_GRADIENT_IMAGE) { + p_list->push_back(PropertyInfo(Variant::STRING,"color/image",PROPERTY_HINT_FILE,"",usage)); + } + p_list->push_back(PropertyInfo(Variant::BOOL,"color/monochrome",PROPERTY_HINT_NONE,"",usage)); } - if (color_type==COLOR_GRADIENT_RANGE) { - p_list->push_back(PropertyInfo(Variant::COLOR,"color/begin")); - p_list->push_back(PropertyInfo(Variant::COLOR,"color/end")); - } - if (color_type==COLOR_GRADIENT_IMAGE) { - p_list->push_back(PropertyInfo(Variant::STRING,"color/image",PROPERTY_HINT_FILE)); - } - p_list->push_back(PropertyInfo(Variant::BOOL,"color/monochrome")); + p_list->push_back(PropertyInfo(Variant::BOOL,"advanced/round_advance")); p_list->push_back(PropertyInfo(Variant::BOOL,"advanced/disable_filter")); @@ -307,6 +333,7 @@ public: gradient_end=Color(0.5,0.5,0.5,1); color_use_monochrome=false; + font_mode=FONT_BITMAP; round_advance=true; disable_filter=false; @@ -314,6 +341,8 @@ public: _EditorFontImportOptions() { + font_mode=FONT_BITMAP; + char_extra_spacing=0; top_extra_spacing=0; bottom_extra_spacing=0; @@ -350,8 +379,8 @@ class EditorFontImportDialog : public ConfirmationDialog { OBJ_TYPE(EditorFontImportDialog, ConfirmationDialog); - LineEditFileChooser *source; - LineEditFileChooser *dest; + EditorLineEditFileChooser *source; + EditorLineEditFileChooser *dest; SpinBox *font_size; LineEdit *test_string; ColorPickerButton *test_color; @@ -377,7 +406,10 @@ class EditorFontImportDialog : public ConfirmationDialog { imd->set_option(opt,v); } - imd->add_source(EditorImportPlugin::validate_source_path(source->get_line_edit()->get_text())); + String src_path = EditorImportPlugin::validate_source_path(source->get_line_edit()->get_text()); + //print_line("pre src path "+source->get_line_edit()->get_text()); + //print_line("src path "+src_path); + imd->add_source(src_path); imd->set_option("font/size",font_size->get_val()); return imd; @@ -499,7 +531,7 @@ class EditorFontImportDialog : public ConfirmationDialog { Error err = plugin->import(dest->get_line_edit()->get_text(),rimd); if (err!=OK) { - error_dialog->set_text("Could't save font."); + error_dialog->set_text("Couldn't save font."); error_dialog->popup_centered(Size2(200,100)); return; } @@ -580,9 +612,9 @@ public: hbc->add_child(vbr); vbr->set_h_size_flags(SIZE_EXPAND_FILL); - source = memnew( LineEditFileChooser ); - source->get_file_dialog()->set_access(FileDialog::ACCESS_FILESYSTEM); - source->get_file_dialog()->set_mode(FileDialog::MODE_OPEN_FILE); + source = memnew( EditorLineEditFileChooser ); + source->get_file_dialog()->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + source->get_file_dialog()->set_mode(EditorFileDialog::MODE_OPEN_FILE); source->get_file_dialog()->add_filter("*.ttf;TrueType"); source->get_file_dialog()->add_filter("*.otf;OpenType"); source->get_line_edit()->connect("text_entered",this,"_src_changed"); @@ -594,7 +626,7 @@ public: font_size->set_max(256); font_size->set_val(16); font_size->connect("value_changed",this,"_font_size_changed"); - dest = memnew( LineEditFileChooser ); + dest = memnew( EditorLineEditFileChooser ); // List<String> fl; Ref<Font> font= memnew(Font); @@ -621,6 +653,7 @@ public: vbl->add_spacer(); vbl->add_margin_child("Test: ",testhb); + /* HBoxContainer *upd_hb = memnew( HBoxContainer ); // vbl->add_child(upd_hb); upd_hb->add_spacer(); @@ -628,7 +661,7 @@ public: upd_hb->add_child(update); update->set_text("Update"); update->connect("pressed",this,"_update"); - +*/ options = memnew( _EditorFontImportOptions ); prop_edit = memnew( PropertyEditor() ); vbr->add_margin_child("Options:",prop_edit,true); @@ -706,6 +739,137 @@ struct _EditorKerningKey { }; + +static unsigned char get_SDF_radial( + unsigned char *fontmap, + int w, int h, + int x, int y, + int max_radius ) +{ + // hideous brute force method + float d2 = max_radius*max_radius+1.0; + unsigned char v = fontmap[x+y*w]; + for( int radius = 1; (radius <= max_radius) && (radius*radius < d2); ++radius ) + { + int line, lo, hi; + // north + line = y - radius; + if( (line >= 0) && (line < h) ) + { + lo = x - radius; + hi = x + radius; + if( lo < 0 ) { lo = 0; } + if( hi >= w ) { hi = w-1; } + int idx = line * w + lo; + for( int i = lo; i <= hi; ++i ) + { + // check this pixel + if( fontmap[idx] != v ) + { + float nx = i - x; + float ny = line - y; + float nd2 = nx*nx+ny*ny; + if( nd2 < d2 ) + { + d2 = nd2; + } + } + // move on + ++idx; + } + } + // south + line = y + radius; + if( (line >= 0) && (line < h) ) + { + lo = x - radius; + hi = x + radius; + if( lo < 0 ) { lo = 0; } + if( hi >= w ) { hi = w-1; } + int idx = line * w + lo; + for( int i = lo; i <= hi; ++i ) + { + // check this pixel + if( fontmap[idx] != v ) + { + float nx = i - x; + float ny = line - y; + float nd2 = nx*nx+ny*ny; + if( nd2 < d2 ) + { + d2 = nd2; + } + } + // move on + ++idx; + } + } + // west + line = x - radius; + if( (line >= 0) && (line < w) ) + { + lo = y - radius + 1; + hi = y + radius - 1; + if( lo < 0 ) { lo = 0; } + if( hi >= h ) { hi = h-1; } + int idx = lo * w + line; + for( int i = lo; i <= hi; ++i ) + { + // check this pixel + if( fontmap[idx] != v ) + { + float nx = line - x; + float ny = i - y; + float nd2 = nx*nx+ny*ny; + if( nd2 < d2 ) + { + d2 = nd2; + } + } + // move on + idx += w; + } + } + // east + line = x + radius; + if( (line >= 0) && (line < w) ) + { + lo = y - radius + 1; + hi = y + radius - 1; + if( lo < 0 ) { lo = 0; } + if( hi >= h ) { hi = h-1; } + int idx = lo * w + line; + for( int i = lo; i <= hi; ++i ) + { + // check this pixel + if( fontmap[idx] != v ) + { + float nx = line - x; + float ny = i - y; + float nd2 = nx*nx+ny*ny; + if( nd2 < d2 ) + { + d2 = nd2; + } + } + // move on + idx += w; + } + } + } + d2 = sqrtf( d2 ); + if( v==0 ) + { + d2 = -d2; + } + d2 *= 127.5 / max_radius; + d2 += 127.5; + if( d2 < 0.0 ) d2 = 0.0; + if( d2 > 255.0 ) d2 = 255.0; + return (unsigned char)(d2 + 0.5); +} + + Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata>& p_from, const String &p_existing) { Ref<ResourceImportMetadata> from = p_from; @@ -754,7 +918,11 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata } - error = FT_Set_Pixel_Sizes(face,0,size); + int font_mode = from->get_option("mode/mode"); + + int scaler=(font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD)?16:1; + + error = FT_Set_Pixel_Sizes(face,0,size*scaler); FT_GlyphSlot slot = face->glyph; @@ -820,9 +988,9 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata { bool skip=false; - error = FT_Load_Char( face, charcode, FT_LOAD_RENDER ); + error = FT_Load_Char( face, charcode, font_mode==_EditorFontImportOptions::FONT_BITMAP?FT_LOAD_RENDER:FT_LOAD_MONOCHROME ); if (error) skip=true; - else error = FT_Render_Glyph( face->glyph, ft_render_mode_normal ); + else error = FT_Render_Glyph( face->glyph, font_mode==_EditorFontImportOptions::FONT_BITMAP?ft_render_mode_normal:ft_render_mode_mono ); if (error) { skip=true; } else if (!skip) { @@ -847,28 +1015,36 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata } _EditorFontData * fdata = memnew( _EditorFontData ); - fdata->bitmap.resize( slot->bitmap.width*slot->bitmap.rows ); - fdata->width=slot->bitmap.width; - fdata->height=slot->bitmap.rows; - fdata->character=charcode; - fdata->glyph=FT_Get_Char_Index(face,charcode); - if (charcode=='x') - xsize=slot->bitmap.width; - if (charcode<127) { - if (slot->bitmap_top>max_up) { + int w = slot->bitmap.width; + int h = slot->bitmap.rows; + int p = slot->bitmap.pitch; - max_up=slot->bitmap_top; - } + //print_line("W: "+itos(w)+" P: "+itos(slot->bitmap.pitch)); + if (font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD) { - if ( (slot->bitmap_top - fdata->height)<max_down ) { + // oversize the holding buffer so I can smooth it! + int sw = w + scaler * 4; + int sh = h + scaler * 4; + // do the SDF + int sdfw = sw / scaler; + int sdfh = sh / scaler; - max_down=slot->bitmap_top - fdata->height; - } + fdata->width=sdfw; + fdata->height=sdfh; + } else { + fdata->width=w; + fdata->height=h; } + fdata->character=charcode; + fdata->glyph=FT_Get_Char_Index(face,charcode); + if (charcode=='x') + xsize=w/scaler; + + fdata->valign=slot->bitmap_top; fdata->halign=slot->bitmap_left; @@ -878,12 +1054,85 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata else fdata->advance=slot->advance.x/float(1<<6); + if (font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD) { + + fdata->halign = fdata->halign / scaler - 1.5; + fdata->valign = fdata->valign / scaler + 1.5; + fdata->advance/=scaler; + + } + fdata->advance+=font_spacing; - for (int i=0;i<slot->bitmap.width;i++) { - for (int j=0;j<slot->bitmap.rows;j++) { - fdata->bitmap[j*slot->bitmap.width+i]=slot->bitmap.buffer[j*slot->bitmap.width+i]; + if (charcode<127) { + int top = fdata->valign; + int hmax = h/scaler; + + if (top>max_up) { + + max_up=top; + } + + + if ( (top - hmax)<max_down ) { + + max_down=top - hmax; + } + } + + if (font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD) { + + + // oversize the holding buffer so I can smooth it! + int sw = w + scaler * 4; + int sh = h + scaler * 4; + + unsigned char *smooth_buf = new unsigned char[sw*sh]; + + for( int i = 0; i < sw*sh; ++i ) { + smooth_buf[i] = 0; + } + + // copy the glyph into the buffer to be smoothed + unsigned char *buf = slot->bitmap.buffer; + for( int j = 0; j < h; ++j ) { + for( int i = 0; i < w; ++i ) { + smooth_buf[scaler*2+i+(j+scaler*2)*sw] = 255 * ((buf[j*p+(i>>3)] >> (7 - (i & 7))) & 1); + } + } + + // do the SDF + int sdfw = fdata->width; + int sdfh = fdata->height; + + fdata->bitmap.resize( sdfw*sdfh ); + + for( int j = 0; j < sdfh; ++j ) { + for( int i = 0; i < sdfw; ++i ) { + int pd_idx = j*sdfw+i; + + //fdata->bitmap[j*slot->bitmap.width+i]=slot->bitmap.buffer[j*slot->bitmap.width+i]; + + fdata->bitmap[pd_idx] = + //get_SDF + get_SDF_radial + ( smooth_buf, sw, sh, + i*scaler + (scaler >>1), j*scaler + (scaler >>1), + 2*scaler ); + + } + } + + delete [] smooth_buf; + + } else { + fdata->bitmap.resize( slot->bitmap.width*slot->bitmap.rows ); + for (int i=0;i<slot->bitmap.width;i++) { + for (int j=0;j<slot->bitmap.rows;j++) { + + fdata->bitmap[j*slot->bitmap.width+i]=slot->bitmap.buffer[j*slot->bitmap.width+i]; + } } } @@ -904,9 +1153,10 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata spd->ofs_x=0; spd->ofs_y=0; - if (!FT_Load_Char( face, ' ', FT_LOAD_RENDER ) && !FT_Render_Glyph( face->glyph, ft_render_mode_normal )) { + if (!FT_Load_Char( face, ' ', FT_LOAD_RENDER ) && !FT_Render_Glyph( face->glyph, font_mode==_EditorFontImportOptions::FONT_BITMAP?ft_render_mode_normal:ft_render_mode_mono )) { spd->advance = slot->advance.x>>6; //round to nearest or store as float + spd->advance/=scaler; spd->advance+=font_spacing; } else { @@ -955,7 +1205,7 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata if (kern==0) continue; - kerning_map[kpk]=kern; + kerning_map[kpk]=kern/scaler; } } } @@ -1079,7 +1329,7 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata pixels.resize(s.x*s.y*4); DVector<uint8_t>::Write w = pixels.write(); - print_line("val: "+itos(font_data_list[i]->valign)); + //print_line("val: "+itos(font_data_list[i]->valign)); for(int y=0;y<s.height;y++) { int yc=CLAMP(y-o.y+font_data_list[i]->valign,0,height-1); @@ -1284,6 +1534,7 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata font->clear(); font->set_height(height+bottom_space+top_space); font->set_ascent(ascent+top_space); + font->set_distance_field_hint(font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD); //register texures { diff --git a/tools/editor/io_plugins/editor_font_import_plugin.h b/tools/editor/io_plugins/editor_font_import_plugin.h index ac3b4eb0fe..451f01080e 100644 --- a/tools/editor/io_plugins/editor_font_import_plugin.h +++ b/tools/editor/io_plugins/editor_font_import_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/io_plugins/editor_import_collada.cpp b/tools/editor/io_plugins/editor_import_collada.cpp index 529ed3374b..d57cff850e 100644 --- a/tools/editor/io_plugins/editor_import_collada.cpp +++ b/tools/editor/io_plugins/editor_import_collada.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,6 +39,7 @@ #include "scene/resources/packed_scene.h" #include "os/os.h" #include "tools/editor/editor_node.h" +#include <iostream> struct ColladaImport { @@ -83,7 +84,7 @@ struct ColladaImport { Error _create_scene(Collada::Node *p_node, Spatial *p_parent); Error _create_resources(Collada::Node *p_node); Error _create_material(const String& p_material); - Error _create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_data, const Collada::MorphControllerData *p_morph_data); + Error _create_mesh_surfaces(bool p_optimize,Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_data, const Collada::MorphControllerData *p_morph_data,Vector<Ref<Mesh> > p_morph_meshes=Vector<Ref<Mesh> >()); Error load(const String& p_path, int p_flags, bool p_force_make_tangents=false); void _fix_param_animation_tracks(); void create_animation(int p_clip,bool p_make_tracks_in_all_bones); @@ -285,13 +286,16 @@ Error ColladaImport::_create_scene(Collada::Node *p_node, Spatial *p_parent) { case Collada::CameraData::MODE_ORTHOGONAL: { - if (cd.orthogonal.x_mag) { + if (cd.orthogonal.y_mag) { - camera->set_orthogonal(cd.orthogonal.x_mag,cd.z_near,cd.z_far); + camera->set_keep_aspect_mode(Camera::KEEP_HEIGHT); + camera->set_orthogonal(cd.orthogonal.y_mag*2.0 ,cd.z_near,cd.z_far); - } else if (!cd.orthogonal.x_mag && cd.orthogonal.y_mag) { + } else if (!cd.orthogonal.y_mag && cd.orthogonal.x_mag) { - camera->set_orthogonal(cd.orthogonal.y_mag * cd.aspect,cd.z_near,cd.z_far); + + camera->set_keep_aspect_mode(Camera::KEEP_WIDTH); + camera->set_orthogonal(cd.orthogonal.x_mag*2.0,cd.z_near,cd.z_far); } } break; @@ -585,7 +589,7 @@ static void _generate_tangents_and_binormals(const DVector<int>& p_indices,const } } -Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *skin_controller, const Collada::MorphControllerData *p_morph_data) { +Error ColladaImport::_create_mesh_surfaces(bool p_optimize,Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *skin_controller, const Collada::MorphControllerData *p_morph_data,Vector<Ref<Mesh> > p_morph_meshes) { bool local_xform_mirror=p_local_xform.basis.determinant() < 0; @@ -706,10 +710,126 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co //find largest source.. + /************************/ + /* ADD WEIGHTS IF EXIST */ + /************************/ + + Map<int,Vector<Collada::Vertex::Weight> > pre_weights; + + bool has_weights=false; + + if (skin_controller) { + + const Collada::SkinControllerData::Source *weight_src=NULL; + int weight_ofs=0; + + if (skin_controller->weights.sources.has("WEIGHT")) { + + String weight_id = skin_controller->weights.sources["WEIGHT"].source; + weight_ofs = skin_controller->weights.sources["WEIGHT"].offset; + if (skin_controller->sources.has(weight_id)) { + + weight_src = &skin_controller->sources[weight_id]; + + } + } + + int joint_ofs=0; + + if (skin_controller->weights.sources.has("JOINT")) { + + joint_ofs = skin_controller->weights.sources["JOINT"].offset; + } + + //should be OK, given this was pre-checked. + + int index_ofs=0; + int wstride = skin_controller->weights.sources.size(); + for(int w_i=0;w_i<skin_controller->weights.sets.size();w_i++) { + + int amount = skin_controller->weights.sets[w_i]; + + Vector<Collada::Vertex::Weight> weights; + + for (int a_i=0;a_i<amount;a_i++) { + + Collada::Vertex::Weight w; + + int read_from = index_ofs+a_i*wstride; + ERR_FAIL_INDEX_V(read_from+wstride-1,skin_controller->weights.indices.size(),ERR_INVALID_DATA); + int weight_index = skin_controller->weights.indices[read_from+weight_ofs]; + ERR_FAIL_INDEX_V(weight_index,weight_src->array.size(),ERR_INVALID_DATA); + + w.weight = weight_src->array[weight_index]; + + int bone_index = skin_controller->weights.indices[read_from+joint_ofs]; + if (bone_index==-1) + continue; //ignore this weight (refers to bind shape) + ERR_FAIL_INDEX_V(bone_index,bone_remap.size(),ERR_INVALID_DATA); + + w.bone_idx=bone_remap[bone_index]; + + + weights.push_back(w); + } + + /* FIX WEIGHTS */ + + + + weights.sort(); + + if (weights.size()>4) { + //cap to 4 and make weights add up 1 + weights.resize(4); + + } + + //make sure weights allways add up to 1 + float total=0; + for(int i=0;i<weights.size();i++) + total+=weights[i].weight; + if (total) + for(int i=0;i<weights.size();i++) + weights[i].weight/=total; + + if (weights.size()==0 || total==0) { //if nothing, add a weight to bone 0 + //no weights assigned + Collada::Vertex::Weight w; + w.bone_idx=0; + w.weight=1.0; + weights.clear(); + weights.push_back(w); + + } + + pre_weights[w_i]=weights; + + /* + for(Set<int>::Element *E=vertex_map[w_i].front();E;E=E->next()) { + + int dst = E->get(); + ERR_EXPLAIN("invalid vertex index in array"); + ERR_FAIL_INDEX_V(dst,vertex_array.size(),ERR_INVALID_DATA); + vertex_array[dst].weights=weights; + + }*/ + + + + + index_ofs+=wstride*amount; + + } + + //vertices need to be localized + has_weights=true; + + } Set<Collada::Vertex> vertex_set; //vertex set will be the vertices List<int> indices_list; //indices will be the indices - Map<int,Set<int> > vertex_map; //map vertices (for setting skinning/morph) + //Map<int,Set<int> > vertex_map; //map vertices (for setting skinning/morph) /**************************/ /* CREATE PRIMITIVE ARRAY */ @@ -746,14 +866,19 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co ERR_FAIL_INDEX_V(src,p.indices.size(),ERR_INVALID_DATA); Collada::Vertex vertex; - if (p_morph_data) + if (!p_optimize) vertex.uid=vertidx++; + + int vertex_index=p.indices[src+vertex_ofs]; //used for index field (later used by controllers) int vertex_pos = (vertex_src->stride?vertex_src->stride:3) * vertex_index; ERR_FAIL_INDEX_V(vertex_pos,vertex_src->array.size(),ERR_INVALID_DATA); vertex.vertex=Vector3(vertex_src->array[vertex_pos+0],vertex_src->array[vertex_pos+1],vertex_src->array[vertex_pos+2]); + if (pre_weights.has(vertex_index)) { + vertex.weights=pre_weights[vertex_index]; + } if (normal_src) { @@ -832,9 +957,9 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co vertex_set.insert(vertex); } - if (!vertex_map.has(vertex_index)) + /* if (!vertex_map.has(vertex_index)) vertex_map[vertex_index]=Set<int>(); - vertex_map[vertex_index].insert(index); //should be outside.. + vertex_map[vertex_index].insert(index); //should be outside..*/ //build triangles if needed if (j==0) prev2[0]=index; @@ -870,120 +995,10 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co vertex_array[F->get().idx]=F->get(); } - /************************/ - /* ADD WEIGHTS IF EXIST */ - /************************/ - - - bool has_weights=false; - - if (skin_controller) { - - const Collada::SkinControllerData::Source *weight_src=NULL; - int weight_ofs=0; - if (skin_controller->weights.sources.has("WEIGHT")) { - - String weight_id = skin_controller->weights.sources["WEIGHT"].source; - weight_ofs = skin_controller->weights.sources["WEIGHT"].offset; - if (skin_controller->sources.has(weight_id)) { - - weight_src = &skin_controller->sources[weight_id]; - - } - } - - int joint_ofs=0; - - if (skin_controller->weights.sources.has("JOINT")) { - - joint_ofs = skin_controller->weights.sources["JOINT"].offset; - } - - //should be OK, given this was pre-checked. - - int index_ofs=0; - int wstride = skin_controller->weights.sources.size(); - for(int w_i=0;w_i<skin_controller->weights.sets.size();w_i++) { - - int amount = skin_controller->weights.sets[w_i]; - - if (vertex_map.has(w_i)) { //vertex may no longer be here, don't bother converting - - Vector<Collada::Vertex::Weight> weights; - - for (int a_i=0;a_i<amount;a_i++) { - - Collada::Vertex::Weight w; - - int read_from = index_ofs+a_i*wstride; - ERR_FAIL_INDEX_V(read_from+wstride-1,skin_controller->weights.indices.size(),ERR_INVALID_DATA); - int weight_index = skin_controller->weights.indices[read_from+weight_ofs]; - ERR_FAIL_INDEX_V(weight_index,weight_src->array.size(),ERR_INVALID_DATA); - - w.weight = weight_src->array[weight_index]; - - int bone_index = skin_controller->weights.indices[read_from+joint_ofs]; - if (bone_index==-1) - continue; //ignore this weight (refers to bind shape) - ERR_FAIL_INDEX_V(bone_index,bone_remap.size(),ERR_INVALID_DATA); - - w.bone_idx=bone_remap[bone_index]; - - - weights.push_back(w); - } - - /* FIX WEIGHTS */ - - - - weights.sort(); - - if (weights.size()>4) { - //cap to 4 and make weights add up 1 - weights.resize(4); - - } - - //make sure weights allways add up to 1 - float total=0; - for(int i=0;i<weights.size();i++) - total+=weights[i].weight; - if (total) - for(int i=0;i<weights.size();i++) - weights[i].weight/=total; - - if (weights.size()==0 || total==0) { //if nothing, add a weight to bone 0 - //no weights assigned - Collada::Vertex::Weight w; - w.bone_idx=0; - w.weight=1.0; - weights.clear(); - weights.push_back(w); - - } - - - for(Set<int>::Element *E=vertex_map[w_i].front();E;E=E->next()) { - - int dst = E->get(); - ERR_EXPLAIN("invalid vertex index in array"); - ERR_FAIL_INDEX_V(dst,vertex_array.size(),ERR_INVALID_DATA); - vertex_array[dst].weights=weights; - - } - - } else { - //zzprint_line("no vertex found for index "+itos(w_i)); - } - - index_ofs+=wstride*amount; - - } - - //vertices need to be localized + if (has_weights) { + //if skeleton, localize Transform local_xform = p_local_xform; for(int i=0;i<vertex_array.size();i++) { @@ -996,11 +1011,9 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co //vertex_array[i].tangent.normal*=-1.0; } } - - has_weights=true; - } + DVector<int> index_array; index_array.resize(indices_list.size()); DVector<int>::Write index_arrayw = index_array.write(); @@ -1272,7 +1285,7 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co //////////////////////////// // THEN THE MORPH TARGETS // //////////////////////////// - +#if 0 if (p_morph_data) { //add morphie target @@ -1354,8 +1367,63 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co vertw = DVector<Vector3>::Write(); DVector<Vector3> normals; DVector<float> tangents; + print_line("vertex source id: "+vertex_src_id); + if(md.vertices[vertex_src_id].sources.has("NORMAL")){ + //has normals + normals.resize(vlen); + //std::cout << "has normals" << std::endl; + String normal_src_id = md.vertices[vertex_src_id].sources["NORMAL"]; + //std::cout << "normals source: "<< normal_src_id.utf8().get_data() <<std::endl; + ERR_FAIL_COND_V(!md.sources.has(normal_src_id),ERR_INVALID_DATA); + + const Collada::MeshData::Source *m=&md.sources[normal_src_id]; + + ERR_FAIL_COND_V( m->array.size() != vertex_src->array.size(), ERR_INVALID_DATA); + int stride=m->stride; + if (stride==0) + stride=3; + + + //read normals from morph target + DVector<Vector3>::Write vertw = normals.write(); + + for(int m_i=0;m_i<m->array.size()/stride;m_i++) { + + int pos = m_i*stride; + Vector3 vtx( m->array[pos+0], m->array[pos+1], m->array[pos+2] ); + + #ifndef NO_UP_AXIS_SWAP + if (collada.state.up_axis==Vector3::AXIS_Z) { + + SWAP( vtx.z, vtx.y ); + vtx.z = -vtx.z; + + } + #endif + + Collada::Vertex vertex; + vertex.vertex=vtx; + vertex.fix_unit_scale(collada); + vtx=vertex.vertex; + + vtx = p_local_xform.xform(vtx); + - _generate_normals(index_array,vertices,normals); + if (vertex_map.has(m_i)) { //vertex may no longer be here, don't bother converting + + + for (Set<int> ::Element *E=vertex_map[m_i].front() ; E; E=E->next() ) { + + vertw[E->get()]=vtx; + } + } + } + + print_line("using built-in normals"); + }else{ + print_line("generating normals"); + _generate_normals(index_array,vertices,normals);//no normals + } if (final_tangent_array.size() && final_uv_array.size()) { _generate_tangents_and_binormals(index_array,vertices,final_uv_array,normals,tangents); @@ -1380,6 +1448,17 @@ Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Co } +#endif + for(int mi=0;mi<p_morph_meshes.size();mi++) { + + // print_line("want surface "+itos(mi)+" has "+itos(p_morph_meshes[mi]->get_surface_count())); + Array a = p_morph_meshes[mi]->surface_get_arrays(surface); + a[Mesh::ARRAY_BONES]=Variant(); + a[Mesh::ARRAY_WEIGHTS]=Variant(); + a[Mesh::ARRAY_INDEX]=Variant(); + //a.resize(Mesh::ARRAY_MAX); //no need for index + mr.push_back(a); + } p_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,d,mr); @@ -1510,17 +1589,21 @@ Error ColladaImport::_create_resources(Collada::Node *p_node) { String meshid; Transform apply_xform; Vector<int> bone_remap; + Vector<Ref<Mesh> > morphs; print_line("mesh: "+String(mi->get_name())); if (ng->controller) { print_line("has controller"); - if (collada.state.skin_controller_data_map.has(ng->source)) { + String ngsource = ng->source; - ERR_FAIL_COND_V(!collada.state.skin_controller_data_map.has(ng->source),ERR_INVALID_DATA); - skin=&collada.state.skin_controller_data_map[ng->source]; + if (collada.state.skin_controller_data_map.has(ngsource)) { + + + ERR_FAIL_COND_V(!collada.state.skin_controller_data_map.has(ngsource),ERR_INVALID_DATA); + skin=&collada.state.skin_controller_data_map[ngsource]; Vector<String> skeletons = ng->skeletons; @@ -1543,7 +1626,10 @@ Error ColladaImport::_create_resources(Collada::Node *p_node) { if (collada.state.morph_controller_data_map.has(meshid)) { //it's a morph!! morph = &collada.state.morph_controller_data_map[meshid]; + ngsource=meshid; meshid=morph->mesh; + } else { + ngsource=""; } if (apply_mesh_xform_to_vertices) { @@ -1574,15 +1660,48 @@ Error ColladaImport::_create_resources(Collada::Node *p_node) { ERR_FAIL_COND_V( !bone_remap_map.has(str), ERR_INVALID_DATA ); bone_remap[i]=bone_remap_map[str]; } - } else if (collada.state.morph_controller_data_map.has(ng->source)) { - print_line("is morph "+ng->source); + } + + if (collada.state.morph_controller_data_map.has(ngsource)) { + print_line("is morph "+ngsource); //it's a morph!! - morph = &collada.state.morph_controller_data_map[ng->source]; + morph = &collada.state.morph_controller_data_map[ngsource]; meshid=morph->mesh; printf("KKmorph: %p\n",morph); print_line("morph mshid: "+meshid); - } else { - ERR_EXPLAIN("Controller Instance Source '"+ng->source+"' is neither skin or morph!"); + + Vector<String> targets; + + morph->targets.has("MORPH_TARGET"); + String target = morph->targets["MORPH_TARGET"]; + bool valid=false; + if (morph->sources.has(target)) { + valid=true; + Vector<String> names = morph->sources[target].sarray; + for(int i=0;i<names.size();i++) { + + String meshid=names[i]; + if (collada.state.mesh_data_map.has(meshid)) { + Ref<Mesh> mesh=Ref<Mesh>(memnew( Mesh )); + const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid]; + Error err = _create_mesh_surfaces(false,mesh,ng->material_map,meshdata,apply_xform,bone_remap,skin,NULL); + ERR_FAIL_COND_V(err,err); + + morphs.push_back(mesh); + } else { + valid=false; + } + } + } + + if (!valid) + morphs.clear(); + + ngsource=""; + } + + if (ngsource!=""){ + ERR_EXPLAIN("Controller Instance Source '"+ngsource+"' is neither skin or morph!"); ERR_FAIL_V( ERR_INVALID_DATA ); } @@ -1603,7 +1722,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node) { mesh=Ref<Mesh>(memnew( Mesh )); const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid]; mesh->set_name( meshdata.name ); - Error err = _create_mesh_surfaces(mesh,ng->material_map,meshdata,apply_xform,bone_remap,skin,morph); + Error err = _create_mesh_surfaces(morphs.size()==0,mesh,ng->material_map,meshdata,apply_xform,bone_remap,skin,morph,morphs); ERR_FAIL_COND_V(err,err); mesh_cache[meshid]=mesh; diff --git a/tools/editor/io_plugins/editor_import_collada.h b/tools/editor/io_plugins/editor_import_collada.h index ae4cedeff6..243cd043a0 100644 --- a/tools/editor/io_plugins/editor_import_collada.h +++ b/tools/editor/io_plugins/editor_import_collada.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/io_plugins/editor_mesh_import_plugin.cpp b/tools/editor/io_plugins/editor_mesh_import_plugin.cpp index 7d6f400ccc..2139513025 100644 --- a/tools/editor/io_plugins/editor_mesh_import_plugin.cpp +++ b/tools/editor/io_plugins/editor_mesh_import_plugin.cpp @@ -1,6 +1,6 @@ #include "editor_mesh_import_plugin.h" -#include "scene/gui/file_dialog.h" +#include "tools/editor/editor_file_dialog.h" #include "tools/editor/editor_dir_dialog.h" #include "tools/editor/editor_node.h" #include "tools/editor/property_editor.h" @@ -105,7 +105,7 @@ public: _EditorMeshImportOptions() { generate_tangents=true; - generate_normals=true; + generate_normals=false; flip_faces=false; smooth_shading=false; weld_vertices=true; @@ -126,7 +126,7 @@ class EditorMeshImportDialog : public ConfirmationDialog { LineEdit *import_path; LineEdit *save_path; - FileDialog *file_select; + EditorFileDialog *file_select; EditorDirDialog *save_select; ConfirmationDialog *error_dialog; PropertyEditor *option_editor; @@ -300,16 +300,16 @@ public: save_choose->connect("pressed", this,"_browse_target"); - file_select = memnew(FileDialog); - file_select->set_access(FileDialog::ACCESS_FILESYSTEM); + file_select = memnew(EditorFileDialog); + file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM); add_child(file_select); - file_select->set_mode(FileDialog::MODE_OPEN_FILES); + file_select->set_mode(EditorFileDialog::MODE_OPEN_FILES); file_select->connect("files_selected", this,"_choose_files"); file_select->add_filter("*.obj ; Wavefront OBJ"); save_select = memnew( EditorDirDialog ); add_child(save_select); - // save_select->set_mode(FileDialog::MODE_OPEN_DIR); + // save_select->set_mode(EditorFileDialog::MODE_OPEN_DIR); save_select->connect("dir_selected", this,"_choose_save_dir"); get_ok()->connect("pressed", this,"_import"); diff --git a/tools/editor/io_plugins/editor_sample_import_plugin.cpp b/tools/editor/io_plugins/editor_sample_import_plugin.cpp index 377af8f179..7888246956 100644 --- a/tools/editor/io_plugins/editor_sample_import_plugin.cpp +++ b/tools/editor/io_plugins/editor_sample_import_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,7 +27,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "editor_sample_import_plugin.h" -#include "scene/gui/file_dialog.h" +#include "tools/editor/editor_file_dialog.h" #include "tools/editor/editor_dir_dialog.h" #include "tools/editor/editor_node.h" #include "tools/editor/property_editor.h" @@ -35,6 +35,7 @@ #include "io/resource_saver.h" #include "os/file_access.h" #include "io/marshalls.h" +#include "tools/editor/editor_settings.h" class _EditorSampleImportOptions : public Object { @@ -156,7 +157,7 @@ public: edit_normalize=true; edit_loop=false; - compress_mode=COMPRESS_MODE_DISABLED; + compress_mode=COMPRESS_MODE_RAM; compress_bitrate=COMPRESS_128; } @@ -171,7 +172,7 @@ class EditorSampleImportDialog : public ConfirmationDialog { LineEdit *import_path; LineEdit *save_path; - FileDialog *file_select; + EditorFileDialog *file_select; EditorDirDialog *save_select; ConfirmationDialog *error_dialog; PropertyEditor *option_editor; @@ -254,6 +255,24 @@ public: error_dialog->popup_centered(Size2(200,100)); } + if (save_path->get_text().strip_edges()=="") { + error_dialog->set_text("Target path is empty."); + error_dialog->popup_centered_minsize(); + return; + } + + if (!save_path->get_text().begins_with("res://")) { + error_dialog->set_text("Target path must be full resource path."); + error_dialog->popup_centered_minsize(); + return; + } + + if (!DirAccess::exists(save_path->get_text())) { + error_dialog->set_text("Target path must exist."); + error_dialog->popup_centered_minsize(); + return; + } + for(int i=0;i<samples.size();i++) { Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); @@ -345,16 +364,16 @@ public: save_choose->connect("pressed", this,"_browse_target"); - file_select = memnew(FileDialog); - file_select->set_access(FileDialog::ACCESS_FILESYSTEM); + file_select = memnew(EditorFileDialog); + file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM); add_child(file_select); - file_select->set_mode(FileDialog::MODE_OPEN_FILES); + file_select->set_mode(EditorFileDialog::MODE_OPEN_FILES); file_select->connect("files_selected", this,"_choose_files"); file_select->add_filter("*.wav ; MS Waveform"); save_select = memnew( EditorDirDialog ); add_child(save_select); - // save_select->set_mode(FileDialog::MODE_OPEN_DIR); + // save_select->set_mode(EditorFileDialog::MODE_OPEN_DIR); save_select->connect("dir_selected", this,"_choose_save_dir"); get_ok()->connect("pressed", this,"_import"); @@ -562,8 +581,7 @@ Error EditorSampleImportPlugin::import(const String& p_path, const Ref<ResourceI int compression = from->get_option("compress/mode"); bool force_mono = from->get_option("force/mono"); - if (compression==_EditorSampleImportOptions::COMPRESS_MODE_RAM) - force_mono=true; + if (force_mono && chans==2) { @@ -590,8 +608,47 @@ Error EditorSampleImportPlugin::import(const String& p_path, const Ref<ResourceI if ( compression == _EditorSampleImportOptions::COMPRESS_MODE_RAM) { dst_format=Sample::FORMAT_IMA_ADPCM; + if (chans==1) { + _compress_ima_adpcm(data,dst_data); + } else { + + print_line("INTERLEAAVE!"); + + + + //byte interleave + Vector<float> left; + Vector<float> right; - _compress_ima_adpcm(data,dst_data); + int tlen = data.size()/2; + left.resize(tlen); + right.resize(tlen); + + for(int i=0;i<tlen;i++) { + left[i]=data[i*2+0]; + right[i]=data[i*2+1]; + } + + DVector<uint8_t> bleft; + DVector<uint8_t> bright; + + _compress_ima_adpcm(left,bleft); + _compress_ima_adpcm(right,bright); + + int dl = bleft.size(); + dst_data.resize( dl *2 ); + + DVector<uint8_t>::Write w=dst_data.write(); + DVector<uint8_t>::Read rl=bleft.read(); + DVector<uint8_t>::Read rr=bright.read(); + + for(int i=0;i<dl;i++) { + w[i*2+0]=rl[i]; + w[i*2+1]=rr[i]; + } + } + +// print_line("compressing ima-adpcm, resulting buffersize is "+itos(dst_data.size())+" from "+itos(data.size())); } else { @@ -691,7 +748,7 @@ void EditorSampleImportPlugin::_compress_ima_adpcm(const Vector<float>& p_data,D *(out++) =0; for (i=0;i<datalen;i++) { - int step,diff,vpdiff,signed_nibble,p,mask; + int step,diff,vpdiff,mask; uint8_t nibble; int16_t xm_sample; @@ -701,8 +758,8 @@ void EditorSampleImportPlugin::_compress_ima_adpcm(const Vector<float>& p_data,D xm_sample=CLAMP(in[i]*32767.0,-32768,32767); - if (xm_sample==32767 || xm_sample==-32768) - printf("clippy!\n",xm_sample); + //if (xm_sample==32767 || xm_sample==-32768) + // printf("clippy!\n",xm_sample); } // xm_sample=xm_sample+xm_prev; @@ -737,10 +794,10 @@ void EditorSampleImportPlugin::_compress_ima_adpcm(const Vector<float>& p_data,D prev+=vpdiff ; if (prev > 32767) { - printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip up %i\n",i,xm_sample,prev,diff,vpdiff,prev); + //printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip up %i\n",i,xm_sample,prev,diff,vpdiff,prev); prev=32767; } else if (prev < -32768) { - printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip down %i\n",i,xm_sample,prev,diff,vpdiff,prev); + //printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip down %i\n",i,xm_sample,prev,diff,vpdiff,prev); prev = -32768 ; } @@ -762,9 +819,54 @@ void EditorSampleImportPlugin::_compress_ima_adpcm(const Vector<float>& p_data,D } + +EditorSampleImportPlugin* EditorSampleImportPlugin::singleton=NULL; + + + EditorSampleImportPlugin::EditorSampleImportPlugin(EditorNode* p_editor) { + singleton=this; dialog = memnew( EditorSampleImportDialog(this)); p_editor->get_gui_base()->add_child(dialog); } +Vector<uint8_t> EditorSampleExportPlugin::custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform) { + + + + if (EditorImportExport::get_singleton()->sample_get_action()==EditorImportExport::SAMPLE_ACTION_NONE || p_path.extension().to_lower()!="wav") { + + return Vector<uint8_t>(); + } + + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + + imd->add_source(EditorImportPlugin::validate_source_path(p_path)); + + imd->set_option("force/8_bit",false); + imd->set_option("force/mono",false); + imd->set_option("force/max_rate",true); + imd->set_option("force/max_rate_hz",EditorImportExport::get_singleton()->sample_get_max_hz()); + imd->set_option("edit/trim",EditorImportExport::get_singleton()->sample_get_trim()); + imd->set_option("edit/normalize",false); + imd->set_option("edit/loop",false); + imd->set_option("compress/mode",1); + + String savepath = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp/smpconv.smp"); + Error err = EditorSampleImportPlugin::singleton->import(savepath,imd); + + + ERR_FAIL_COND_V(err!=OK,Vector<uint8_t>()); + + p_path=p_path.basename()+".smp"; + return FileAccess::get_file_as_array(savepath); + +} + + +EditorSampleExportPlugin::EditorSampleExportPlugin() { + +} + + diff --git a/tools/editor/io_plugins/editor_sample_import_plugin.h b/tools/editor/io_plugins/editor_sample_import_plugin.h index 176dece0d5..89319affa0 100644 --- a/tools/editor/io_plugins/editor_sample_import_plugin.h +++ b/tools/editor/io_plugins/editor_sample_import_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -43,6 +43,8 @@ class EditorSampleImportPlugin : public EditorImportPlugin { void _compress_ima_adpcm(const Vector<float>& p_data,DVector<uint8_t>& dst_data); public: + static EditorSampleImportPlugin *singleton; + virtual String get_name() const; virtual String get_visible_name() const; virtual void import_dialog(const String& p_from=""); @@ -52,4 +54,16 @@ public: EditorSampleImportPlugin(EditorNode* p_editor); }; +class EditorSampleExportPlugin : public EditorExportPlugin { + + OBJ_TYPE( EditorSampleExportPlugin, EditorExportPlugin); + + +public: + + virtual Vector<uint8_t> custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform); + + EditorSampleExportPlugin(); +}; + #endif // EDITOR_SAMPLE_IMPORT_PLUGIN_H diff --git a/tools/editor/io_plugins/editor_scene_import_plugin.cpp b/tools/editor/io_plugins/editor_scene_import_plugin.cpp index 06780e4d8a..ca44df269b 100644 --- a/tools/editor/io_plugins/editor_scene_import_plugin.cpp +++ b/tools/editor/io_plugins/editor_scene_import_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -162,8 +162,8 @@ class EditorSceneImportDialog : public ConfirmationDialog { LineEdit *save_path; LineEdit *script_path; Tree *import_options; - FileDialog *file_select; - FileDialog *script_select; + EditorFileDialog *file_select; + EditorFileDialog *script_select; EditorDirDialog *save_select; OptionButton *texture_action; @@ -671,15 +671,28 @@ void EditorSceneImportDialog::_import(bool p_and_open) { wip_open=p_and_open; //' ImportMonitorBlock imb; - if (import_path->get_text()=="") { + + if (import_path->get_text().strip_edges()=="") { error_dialog->set_text("Source path is empty."); - error_dialog->popup_centered(Size2(200,100)); + error_dialog->popup_centered_minsize(); return; } - if (save_path->get_text()=="") { + if (save_path->get_text().strip_edges()=="") { error_dialog->set_text("Target path is empty."); - error_dialog->popup_centered(Size2(200,100)); + error_dialog->popup_centered_minsize(); + return; + } + + if (!save_path->get_text().begins_with("res://")) { + error_dialog->set_text("Target path must be full resource path."); + error_dialog->popup_centered_minsize(); + return; + } + + if (!DirAccess::exists(save_path->get_text())) { + error_dialog->set_text("Target path must exist."); + error_dialog->popup_centered_minsize(); return; } @@ -701,7 +714,8 @@ void EditorSceneImportDialog::_import(bool p_and_open) { } - Ref<EditorScenePostImport> pi; + + if (script_path->get_text()!="") { Ref<Script> scr = ResourceLoader::load(script_path->get_text()); @@ -711,7 +725,7 @@ void EditorSceneImportDialog::_import(bool p_and_open) { return; } - pi = Ref<EditorScenePostImport>( memnew( EditorScenePostImport ) ); + Ref<EditorScenePostImport> pi = Ref<EditorScenePostImport>( memnew( EditorScenePostImport ) ); pi->set_script(scr.get_ref_ptr()); if (!pi->get_script_instance()) { @@ -719,6 +733,7 @@ void EditorSceneImportDialog::_import(bool p_and_open) { error_dialog->popup_centered(Size2(200,100)); return; } + } @@ -747,7 +762,7 @@ void EditorSceneImportDialog::_import(bool p_and_open) { rim->set_option("animation_optimizer_max_angle",animation_options->get_optimize_max_angle()); rim->set_option("animation_filters",animation_options->get_filter()); rim->set_option("animation_clips",animation_options->get_clips()); - rim->set_option("post_import_script",script_path->get_text()!=String()?EditorImportPlugin::validate_source_path(script_path->get_text()):String()); + rim->set_option("post_import_script",script_path->get_text()); rim->set_option("import_this_time",this_import->get_selected()); rim->set_option("import_next_time",next_import->get_selected()); rim->set_option("reimport",true); @@ -893,6 +908,7 @@ void EditorSceneImportDialog::popup_import(const String &p_from) { if (rimd->has_option("animation_optimizer_max_angle")) animation_options->set_optimize_max_angle(rimd->get_option("animation_optimizer_max_angle")); + script_path->set_text(rimd->get_option("post_import_script")); if (rimd->has_option("import_this_time")) this_import->select(rimd->get_option("import_this_time")); @@ -1063,19 +1079,19 @@ EditorSceneImportDialog::EditorSceneImportDialog(EditorNode *p_editor, EditorSce vbc->set_v_size_flags(SIZE_EXPAND_FILL); vbc->add_margin_child("Options:",import_options,true); - file_select = memnew(FileDialog); - file_select->set_access(FileDialog::ACCESS_FILESYSTEM); + file_select = memnew(EditorFileDialog); + file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM); add_child(file_select); - file_select->set_mode(FileDialog::MODE_OPEN_FILE); + file_select->set_mode(EditorFileDialog::MODE_OPEN_FILE); file_select->connect("file_selected", this,"_choose_file"); save_select = memnew(EditorDirDialog); add_child(save_select); - //save_select->set_mode(FileDialog::MODE_SAVE_FILE); + //save_select->set_mode(EditorFileDialog::MODE_SAVE_FILE); save_select->connect("dir_selected", this,"_choose_save_file"); get_ok()->connect("pressed", this,"_import"); @@ -1124,7 +1140,7 @@ EditorSceneImportDialog::EditorSceneImportDialog(EditorNode *p_editor, EditorSce script_choose->connect("pressed", this,"_browse_script"); - script_select = memnew(FileDialog); + script_select = memnew(EditorFileDialog); add_child(script_select); for(int i=0;i<ScriptServer::get_language_count();i++) { @@ -1136,7 +1152,7 @@ EditorSceneImportDialog::EditorSceneImportDialog(EditorNode *p_editor, EditorSce } - script_select->set_mode(FileDialog::MODE_OPEN_FILE); + script_select->set_mode(EditorFileDialog::MODE_OPEN_FILE); script_select->connect("file_selected", this,"_choose_script"); @@ -1798,8 +1814,8 @@ Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh> for(int i=0;i<portal_points.size()-1;i++) { - float a = portal_points[i].atan2(); - float b = portal_points[i+1].atan2(); + float a = portal_points[i].angle(); + float b = portal_points[i+1].angle(); if (a>b) { SWAP( portal_points[i], portal_points[i+1] ); @@ -2121,7 +2137,7 @@ void EditorSceneImportPlugin::_merge_existing_node(Node *p_node,Node *p_imported } -void EditorSceneImportPlugin::_add_new_nodes(Node *p_node,Node *p_imported,Node *p_imported_scene,Set<Node*> &checked_nodes) { +void EditorSceneImportPlugin::_add_new_nodes(Node *p_node,Node *p_imported,Node *p_imported_scene,Node *p_existing_scene,Set<Node*> &checked_nodes) { for(int i=0;i<p_imported->get_child_count();i++) { @@ -2129,12 +2145,15 @@ void EditorSceneImportPlugin::_add_new_nodes(Node *p_node,Node *p_imported,Node Node *imported_node = p_imported->get_child(i); - if (imported_node->get_owner()!=p_imported_scene) + if (imported_node->get_owner()!=p_imported_scene) { + // print_line("skipping because not imported at "+String(imported_node->get_name())); continue; //end of the road + } Vector<StringName> nn; nn.push_back(imported_node->get_name()); NodePath imported_path(nn,false); + //print_line("check for: "+String(imported_path)); if (!p_node->has_node(imported_path) && !checked_nodes.has(imported_node)) { //not there, re-add it @@ -2144,8 +2163,11 @@ void EditorSceneImportPlugin::_add_new_nodes(Node *p_node,Node *p_imported,Node if (o) n=o->cast_to<Node>(); + //print_line("creating node of same type.."); + if (n) { + //print_line("copy props and add"); List<PropertyInfo> pl; imported_node->get_property_list(&pl); for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) { @@ -2155,8 +2177,11 @@ void EditorSceneImportPlugin::_add_new_nodes(Node *p_node,Node *p_imported,Node } p_node->add_child(n); + n->set_owner(p_existing_scene); } + } else { + //print_line("already exists"); } @@ -2164,7 +2189,7 @@ void EditorSceneImportPlugin::_add_new_nodes(Node *p_node,Node *p_imported,Node Node *other_node = p_node->get_node(imported_path); - _add_new_nodes(other_node,imported_node,p_imported_scene,checked_nodes); + _add_new_nodes(other_node,imported_node,p_imported_scene,p_existing_scene,checked_nodes); } @@ -2177,7 +2202,7 @@ void EditorSceneImportPlugin::_merge_scenes(Node *p_node,Node *p_imported) { Set<Ref<Resource> > checked_resources; Set<Node*> checked_nodes; _merge_existing_node(p_node,p_imported,checked_resources,checked_nodes); - _add_new_nodes(p_node,p_imported,p_imported,checked_nodes); + _add_new_nodes(p_node,p_imported,p_imported,p_node,checked_nodes); //add existing.. ? } @@ -2214,27 +2239,33 @@ void EditorSceneImportPlugin::_scan_materials(Node*p_base,Node *p_node,Map<Strin void EditorSceneImportPlugin::_apply_materials(Node*p_base,Node *p_node,Map<String,Ref<Material> > &mesh_materials,Map<String,Ref<Material> >& override_materials,Set<Ref<Mesh> >& meshes_processed) { - if (!p_base && p_node->get_owner()!=p_base) + if (p_node!=p_base && p_node->get_owner()!=p_base) return; MeshInstance *mi=p_node->cast_to<MeshInstance>(); if (mi) { + print_line("is mesh "+String(p_node->get_name())); String path = p_base->get_path_to(p_node); - if (override_materials.has(path)) + if (override_materials.has(path)) { + print_line("is in material overrides"); mi->set_material_override(override_materials[path]); + } Ref<Mesh> mesh = mi->get_mesh(); if (mesh.is_valid() && !meshes_processed.has(mesh)) { + print_line("mesh was not processed"); meshes_processed.insert(mesh); for(int i=0;i<mesh->get_surface_count();i++) { String name = mesh->get_name()+":"+mesh->surface_get_name(i); + print_line("name for surface "+itos(i)+": "+name); if (mesh_materials.has(name)) { Ref<Material> mat = mesh_materials[name]; mesh->surface_set_material(i,mat); + print_line("overriding!"); } } } @@ -2251,9 +2282,19 @@ void EditorSceneImportPlugin::_merge_materials(Node *p_node,Node *p_imported) { Map<String,Ref<Material> > override_materials; _scan_materials(p_node,p_node,mesh_materials,override_materials); + + for (Map<String,Ref<Material> >::Element *E=mesh_materials.front();E;E=E->next()) { + print_line("Mats: "+String(E->key())); + } + + for (Map<String,Ref<Material> >::Element *E=override_materials.front();E;E=E->next()) { + print_line("Overrides: "+String(E->key())); + } + Set<Ref<Mesh> > mp; _apply_materials(p_imported,p_imported,mesh_materials,override_materials,mp); + } #if 0 @@ -2597,8 +2638,11 @@ void EditorSceneImportPlugin::_filter_tracks(Node *scene, const String& p_text) for(Set<String>::Element *F=keep_local.front();F;F=F->next()) { keep.insert(F->get()); } - + print_line("FILTERING ANIM: "+String(E->get())); _filter_anim_tracks(anim->get_animation(name),keep); + } else { + print_line("NOT FILTERING ANIM: "+String(E->get())); + } } @@ -2687,7 +2731,7 @@ Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, c Ref<EditorScenePostImport> post_import_script; if (post_import_script_path!="") { - post_import_script_path = EditorImportPlugin::expand_source_path(post_import_script_path); + post_import_script_path = post_import_script_path; Ref<Script> scr = ResourceLoader::load(post_import_script_path); if (!scr.is_valid()) { EditorNode::add_io_error("Couldn't load post-import script: '"+post_import_script_path); @@ -2709,8 +2753,11 @@ Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, c EditorNode::add_io_error("Error running Post-Import script: '"+post_import_script_path); return err; } + + } + /// IMPORT IMAGES diff --git a/tools/editor/io_plugins/editor_scene_import_plugin.h b/tools/editor/io_plugins/editor_scene_import_plugin.h index fa4730f7ee..71efab9503 100644 --- a/tools/editor/io_plugins/editor_scene_import_plugin.h +++ b/tools/editor/io_plugins/editor_scene_import_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -113,7 +113,7 @@ class EditorSceneImportPlugin : public EditorImportPlugin { void _filter_tracks(Node *scene, const String& p_text); void _merge_existing_node(Node *p_node,Node *p_imported_scene,Set<Ref<Resource> >& checked_resources,Set<Node*> &checked_nodes); - void _add_new_nodes(Node *p_node,Node *p_imported,Node *p_imported_scene,Set<Node*> &checked_nodes); + void _add_new_nodes(Node *p_node,Node *p_imported,Node *p_imported_scene,Node *p_existing_scene,Set<Node*> &checked_nodes); void _optimize_animations(Node *scene, float p_max_lin_error,float p_max_ang_error,float p_max_angle); void _merge_scenes(Node *p_node, Node *p_imported); diff --git a/tools/editor/io_plugins/editor_texture_import_plugin.cpp b/tools/editor/io_plugins/editor_texture_import_plugin.cpp index b855b15b39..8d5a4f1dcf 100644 --- a/tools/editor/io_plugins/editor_texture_import_plugin.cpp +++ b/tools/editor/io_plugins/editor_texture_import_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -51,6 +51,7 @@ static const char *flag_names[]={ NULL }; +#if 0 // not used static const char *flag_short_names[]={ "Stream", "FixBorder", @@ -65,6 +66,7 @@ static const char *flag_short_names[]={ "Anisoropic", NULL }; +#endif void EditorImportTextureOptions::set_format(EditorTextureImportPlugin::ImageFormat p_format) { @@ -142,6 +144,8 @@ void EditorImportTextureOptions::_changed() { void EditorImportTextureOptions::_bind_methods() { + print_line("bind toptions"); + ObjectTypeDB::bind_method("_changed",&EditorImportTextureOptions::_changed); ObjectTypeDB::bind_method("_changedp",&EditorImportTextureOptions::_changedp); @@ -217,7 +221,6 @@ EditorImportTextureOptions::EditorImportTextureOptions() { fname++; } - add_margin_child("Texture Options",flags,true); notice_for_2d = memnew( Label ); @@ -245,17 +248,20 @@ class EditorTextureImportDialog : public ConfirmationDialog { LineEdit *import_path; LineEdit *save_path; - FileDialog *file_select; - FileDialog *save_file_select; + EditorFileDialog *file_select; + EditorFileDialog *save_file_select; EditorDirDialog *save_select; OptionButton *texture_action; ConfirmationDialog *error_dialog; CheckButton *crop_source; + SpinBox *size; bool atlas; + bool large; EditorTextureImportPlugin *plugin; void _choose_files(const Vector<String>& p_path); + void _choose_file(const String& p_path); void _choose_save_dir(const String& p_path); void _browse(); void _browse_target(); @@ -270,7 +276,7 @@ public: Error import(const String& p_from, const String& p_to, const String& p_preset); void popup_import(const String &p_from=String()); - EditorTextureImportDialog(EditorTextureImportPlugin *p_plugin=NULL,bool p_2d=false,bool p_atlas=false); + EditorTextureImportDialog(EditorTextureImportPlugin *p_plugin=NULL,bool p_2d=false,bool p_atlas=false,bool p_large=false); }; @@ -299,6 +305,15 @@ void EditorTextureImportDialog::_choose_files(const Vector<String>& p_path) { import_path->set_text(files); } + + + +void EditorTextureImportDialog::_choose_file(const String& p_path) { + + + import_path->set_text(p_path); + +} void EditorTextureImportDialog::_choose_save_dir(const String& p_path) { save_path->set_text(p_path); @@ -321,12 +336,23 @@ void EditorTextureImportDialog::_import() { String dst_path=save_path->get_text(); - if (dst_path.empty()) { + if (save_path->get_text().strip_edges()=="") { + error_dialog->set_text("Target path is empty."); + error_dialog->popup_centered_minsize(); + return; + } - error_dialog->set_text("Please specify a valid target import path!"); - error_dialog->popup_centered(Size2(200,100)); + if (!save_path->get_text().begins_with("res://")) { + error_dialog->set_text("Target path must be full resource path."); + error_dialog->popup_centered_minsize(); return; + } + + if (!atlas && !large && !DirAccess::exists(save_path->get_text())) { + error_dialog->set_text("Target path must exist."); + error_dialog->popup_centered_minsize(); + return; } if (atlas) { //atlas @@ -349,6 +375,8 @@ void EditorTextureImportDialog::_import() { imd->set_option("flags",texture_options->get_flags()); imd->set_option("quality",texture_options->get_quality()); imd->set_option("atlas",true); + imd->set_option("atlas_size",int(size->get_val())); + imd->set_option("large",false); imd->set_option("crop",crop_source->is_pressed()); Error err = plugin->import(dst_file,imd); @@ -359,7 +387,38 @@ void EditorTextureImportDialog::_import() { return; } + } else if (large) { //atlas + + if (files.size()!=1) { + + error_dialog->set_text("Only one file is required for large texture"); + error_dialog->popup_centered(Size2(200,100)); + return; + + } + String dst_file = dst_path; + //dst_file=dst_file.basename()+".tex"; + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + //imd->set_editor(); + for(int i=0;i<files.size();i++) { + imd->add_source(EditorImportPlugin::validate_source_path(files[i])); + } + imd->set_option("format",texture_options->get_format()); + imd->set_option("flags",texture_options->get_flags()); + imd->set_option("quality",texture_options->get_quality()); + imd->set_option("atlas",false); + imd->set_option("large",true); + imd->set_option("large_cell_size",int(size->get_val())); + imd->set_option("crop",crop_source->is_pressed()); + Error err = plugin->import(dst_file,imd); + if (err) { + + error_dialog->set_text("Error importing: "+dst_file.get_file()); + error_dialog->popup_centered(Size2(200,100)); + return; + + } } else { @@ -374,6 +433,8 @@ void EditorTextureImportDialog::_import() { imd->set_option("flags",texture_options->get_flags()); imd->set_option("quality",texture_options->get_quality()); imd->set_option("atlas",false); + imd->set_option("large",false); + Error err = plugin->import(dst_file,imd); if (err) { @@ -395,7 +456,7 @@ void EditorTextureImportDialog::_browse() { void EditorTextureImportDialog::_browse_target() { - if (atlas) { + if (atlas || large) { save_file_select->popup_centered_ratio(); } else { save_select->popup_centered_ratio(); @@ -411,7 +472,11 @@ void EditorTextureImportDialog::popup_import(const String& p_from) { Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_from); ERR_FAIL_COND(!rimd.is_valid()); - save_path->set_text(p_from.get_base_dir()); + if (plugin->get_mode()==EditorTextureImportPlugin::MODE_ATLAS || plugin->get_mode()==EditorTextureImportPlugin::MODE_LARGE) + save_path->set_text(p_from); + else + save_path->set_text(p_from.get_base_dir()); + texture_options->set_format(EditorTextureImportPlugin::ImageFormat(int(rimd->get_option("format")))); texture_options->set_flags(rimd->get_option("flags")); texture_options->set_quality(rimd->get_option("quality")); @@ -457,6 +522,7 @@ void EditorTextureImportDialog::_bind_methods() { ObjectTypeDB::bind_method("_choose_files",&EditorTextureImportDialog::_choose_files); + ObjectTypeDB::bind_method("_choose_file",&EditorTextureImportDialog::_choose_file); ObjectTypeDB::bind_method("_choose_save_dir",&EditorTextureImportDialog::_choose_save_dir); ObjectTypeDB::bind_method("_import",&EditorTextureImportDialog::_import); ObjectTypeDB::bind_method("_browse",&EditorTextureImportDialog::_browse); @@ -464,21 +530,25 @@ void EditorTextureImportDialog::_bind_methods() { // ADD_SIGNAL( MethodInfo("imported",PropertyInfo(Variant::OBJECT,"scene")) ); } -EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin* p_plugin, bool p_2d, bool p_atlas) { +EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin* p_plugin, bool p_2d, bool p_atlas,bool p_large) { atlas=p_atlas; + large=p_large; plugin=p_plugin; set_title("Import Textures"); - texture_options = memnew( EditorImportTextureOptions );; - VBoxContainer *vbc = texture_options; + + VBoxContainer *vbc = memnew(VBoxContainer); add_child(vbc); set_child_rect(vbc); VBoxContainer *source_vb=memnew(VBoxContainer); - vbc->add_margin_child("Source Texture(s):",source_vb); + if (large) + vbc->add_margin_child("Source Texture:",source_vb); + else + vbc->add_margin_child("Source Texture(s):",source_vb); HBoxContainer *hbc = memnew( HBoxContainer ); source_vb->add_child(hbc); @@ -493,6 +563,7 @@ EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin* if (!p_atlas) crop_source->hide(); + Button * import_choose = memnew( Button ); import_choose->set_text(" .. "); hbc->add_child(import_choose); @@ -502,6 +573,19 @@ EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin* hbc = memnew( HBoxContainer ); vbc->add_margin_child("Target Path:",hbc); + size = memnew( SpinBox ); + size->set_min(128); + size->set_max(16384); + + if (p_atlas) { + size->set_val(2048); + vbc->add_margin_child("Max Texture size:",size); + } else { + size->set_val(256); + vbc->add_margin_child("Cell Size:",size); + } + + save_path = memnew( LineEdit ); save_path->set_h_size_flags(SIZE_EXPAND_FILL); hbc->add_child(save_path); @@ -512,32 +596,39 @@ EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin* save_choose->connect("pressed", this,"_browse_target"); - file_select = memnew(FileDialog); - file_select->set_access(FileDialog::ACCESS_FILESYSTEM); + file_select = memnew(EditorFileDialog); + file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM); add_child(file_select); - file_select->set_mode(FileDialog::MODE_OPEN_FILES); + if (!large) + file_select->set_mode(EditorFileDialog::MODE_OPEN_FILES); + else + file_select->set_mode(EditorFileDialog::MODE_OPEN_FILE); file_select->connect("files_selected", this,"_choose_files"); + file_select->connect("file_selected", this,"_choose_file"); - save_file_select = memnew(FileDialog); - save_file_select->set_access(FileDialog::ACCESS_RESOURCES); + save_file_select = memnew(EditorFileDialog); + save_file_select->set_access(EditorFileDialog::ACCESS_RESOURCES); add_child(save_file_select); - save_file_select->set_mode(FileDialog::MODE_SAVE_FILE); + save_file_select->set_mode(EditorFileDialog::MODE_SAVE_FILE); save_file_select->clear_filters(); - save_file_select->add_filter("*.tex;Base Atlas Texture"); + if (large) + save_file_select->add_filter("*.ltex;Large Texture"); + else + save_file_select->add_filter("*.tex;Base Atlas Texture"); save_file_select->connect("file_selected", this,"_choose_save_dir"); save_select = memnew( EditorDirDialog ); add_child(save_select); -// save_select->set_mode(FileDialog::MODE_OPEN_DIR); +// save_select->set_mode(EditorFileDialog::MODE_OPEN_DIR); save_select->connect("dir_selected", this,"_choose_save_dir"); get_ok()->connect("pressed", this,"_import"); get_ok()->set_text("Import"); //move stuff up - for(int i=0;i<4;i++) - vbc->move_child( vbc->get_child( vbc->get_child_count() -1), 0); + //for(int i=0;i<4;i++) + // vbc->move_child( vbc->get_child( vbc->get_child_count() -1), 0); error_dialog = memnew ( ConfirmationDialog ); add_child(error_dialog); @@ -546,13 +637,24 @@ EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin* set_hide_on_ok(false); + texture_options = memnew( EditorImportTextureOptions );; + vbc->add_child(texture_options); + texture_options->set_v_size_flags(SIZE_EXPAND_FILL); + if (atlas) { texture_options->set_flags(EditorTextureImportPlugin::IMAGE_FLAG_FIX_BORDER_ALPHA|EditorTextureImportPlugin::IMAGE_FLAG_NO_MIPMAPS|EditorTextureImportPlugin::IMAGE_FLAG_FILTER); texture_options->set_quality(0.7); texture_options->set_format(EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSY); - texture_options->show_2d_notice(); + //texture_options->show_2d_notice(); set_title("Import Textures for Atlas (2D)"); + } else if (large) { + + texture_options->set_flags(EditorTextureImportPlugin::IMAGE_FLAG_FIX_BORDER_ALPHA|EditorTextureImportPlugin::IMAGE_FLAG_NO_MIPMAPS|EditorTextureImportPlugin::IMAGE_FLAG_FILTER); + texture_options->set_quality(0.7); + texture_options->set_format(EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS); + texture_options->show_2d_notice(); + set_title("Import Large Textures (2D)"); } else if (p_2d) { @@ -598,12 +700,17 @@ String EditorTextureImportPlugin::get_name() const { return "texture_atlas"; } break; + case MODE_LARGE: { + + return "texture_large"; + } break; } return ""; } + String EditorTextureImportPlugin::get_visible_name() const { switch(mode) { @@ -618,7 +725,11 @@ String EditorTextureImportPlugin::get_visible_name() const { } break; case MODE_ATLAS: { - return "Atlas Texture"; + return "2D Atlas Texture"; + } break; + case MODE_LARGE: { + + return "2D Large Texture"; } break; } @@ -716,6 +827,135 @@ Error EditorTextureImportPlugin::import(const String& p_path, const Ref<Resource return import2(p_path,p_from,EditorExportPlatform::IMAGE_COMPRESSION_BC,false); } + +Error EditorTextureImportPlugin::_process_texture_data(Ref<ImageTexture> &texture,int format, float quality,int flags,EditorExportPlatform::ImageCompression p_compr,int tex_flags,float shrink) { + + + if (format==IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS || format==IMAGE_FORMAT_COMPRESS_DISK_LOSSY) { + + Image image=texture->get_data(); + ERR_FAIL_COND_V(image.empty(),ERR_INVALID_DATA); + + bool has_alpha=image.detect_alpha(); + if (!has_alpha && image.get_format()==Image::FORMAT_RGBA) { + + image.convert(Image::FORMAT_RGB); + + } + + if (image.get_format()==Image::FORMAT_RGBA && flags&IMAGE_FLAG_FIX_BORDER_ALPHA) { + + image.fix_alpha_edges(); + } + + if (image.get_format()==Image::FORMAT_RGBA && flags&IMAGE_FLAG_PREMULT_ALPHA) { + + image.premultiply_alpha(); + } + + if (flags&IMAGE_FLAG_CONVERT_NORMAL_TO_XY) { + image.normalmap_to_xy(); + } + + //if ((image.get_format()==Image::FORMAT_RGB || image.get_format()==Image::FORMAT_RGBA) && flags&IMAGE_FLAG_CONVERT_TO_LINEAR) { + + // image.srgb_to_linear(); + //} + + if (shrink>1) { + + int orig_w=image.get_width(); + int orig_h=image.get_height(); + image.resize(orig_w/shrink,orig_h/shrink,Image::INTERPOLATE_CUBIC); + texture->create_from_image(image,tex_flags); + texture->set_size_override(Size2(orig_w,orig_h)); + + + } else { + + texture->create_from_image(image,tex_flags); + } + + + if (format==IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS) { + texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSLESS); + } else { + texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + } + + + + texture->set_lossy_storage_quality(quality); + + + } else { + + + Image image=texture->get_data(); + ERR_FAIL_COND_V(image.empty(),ERR_INVALID_DATA); + + + bool has_alpha=image.detect_alpha(); + if (!has_alpha && image.get_format()==Image::FORMAT_RGBA) { + + image.convert(Image::FORMAT_RGB); + + } + + if (image.get_format()==Image::FORMAT_RGBA && flags&IMAGE_FLAG_FIX_BORDER_ALPHA) { + + image.fix_alpha_edges(); + } + + if (image.get_format()==Image::FORMAT_RGBA && flags&IMAGE_FLAG_PREMULT_ALPHA) { + + image.premultiply_alpha(); + } + + if (flags&IMAGE_FLAG_CONVERT_NORMAL_TO_XY) { + image.normalmap_to_xy(); + } + + //if ((image.get_format()==Image::FORMAT_RGB || image.get_format()==Image::FORMAT_RGBA) && flags&IMAGE_FLAG_CONVERT_TO_LINEAR) { +// + // print_line("CONVERT BECAUSE: "+itos(flags)); + // image.srgb_to_linear(); + //} + + int orig_w=image.get_width(); + int orig_h=image.get_height(); + + if (shrink>1) { + image.resize(orig_w/shrink,orig_h/shrink,Image::INTERPOLATE_CUBIC); + texture->create_from_image(image,tex_flags); + texture->set_size_override(Size2(orig_w,orig_h)); + } + + if (!(flags&IMAGE_FLAG_NO_MIPMAPS)) { + image.generate_mipmaps(); + + } + + if (format!=IMAGE_FORMAT_UNCOMPRESSED) { + + compress_image(p_compr,image,flags&IMAGE_FLAG_COMPRESS_EXTRA); + } + + + texture->create_from_image(image,tex_flags); + + + if (shrink>1 || (format!=IMAGE_FORMAT_UNCOMPRESSED && (image.get_width()!=orig_w || image.get_height()!=orig_h))) { + texture->set_size_override(Size2(orig_w,orig_h)); + } + + //uint32_t save_flags=ResourceSaver::FLAG_COMPRESS; + } + + return OK; +} + + Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<ResourceImportMetadata>& p_from,EditorExportPlatform::ImageCompression p_compr, bool p_external){ @@ -727,8 +967,11 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc Ref<ImageTexture> texture; Vector<Ref<AtlasTexture> > atlases; bool atlas = from->get_option("atlas"); + bool large = from->get_option("large"); int flags=from->get_option("flags"); + int format=from->get_option("format"); + float quality=from->get_option("quality"); uint32_t tex_flags=0; @@ -744,20 +987,95 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc tex_flags|=Texture::FLAG_ANISOTROPIC_FILTER; print_line("path: "+p_path+" flags: "+itos(tex_flags)); - int shrink=1; + float shrink=1; if (from->has_option("shrink")) shrink=from->get_option("shrink"); - if (atlas) { + if (large) { + ERR_FAIL_COND_V(from->get_source_count()!=1,ERR_INVALID_PARAMETER); + + String src_path = EditorImportPlugin::expand_source_path(from->get_source_path(0)); + + + int cell_size=from->get_option("large_cell_size"); + ERR_FAIL_COND_V(cell_size<128 || cell_size>16384,ERR_CANT_OPEN); + + EditorProgress pg("ltex","Import Large Texture",3); + + pg.step("Load Source Image",0); + Image img; + Error err = ImageLoader::load_image(src_path,&img); + if (err) { + return err; + } + + pg.step("Slicing",1); + + Map<Vector2,Image> pieces; + for(int i=0;i<img.get_width();i+=cell_size) { + int w = MIN(img.get_width()-i,cell_size); + for(int j=0;j<img.get_height();j+=cell_size) { + int h = MIN(img.get_height()-j,cell_size); + + Image piece(w,h,0,img.get_format()); + piece.blit_rect(img,Rect2(i,j,w,h),Point2(0,0)); + if (!piece.is_invisible()) { + pieces[Vector2(i,j)]=piece; + //print_line("ADDING PIECE AT "+Vector2(i,j)); + } + } + } + + Ref<LargeTexture> existing; + if (ResourceCache::has(p_path)) { + existing = ResourceCache::get(p_path); + } + + if (existing.is_valid()) { + existing->clear(); + } else { + existing = Ref<LargeTexture>(memnew( LargeTexture )); + } + + existing->set_size(Size2(img.get_width(),img.get_height())); + pg.step("Inserting",2); + + for (Map<Vector2,Image>::Element *E=pieces.front();E;E=E->next()) { + + Ref<ImageTexture> imgtex = Ref<ImageTexture>( memnew( ImageTexture ) ); + imgtex->create_from_image(E->get(),tex_flags); + _process_texture_data(imgtex,format,quality,flags,p_compr,tex_flags,shrink); + existing->add_piece(E->key(),imgtex); + } + + if (!p_external) { + from->set_editor(get_name()); + existing->set_path(p_path); + existing->set_import_metadata(from); + } + pg.step("Saving",3); + + err = ResourceSaver::save(p_path,existing); + if (err!=OK) { + EditorNode::add_io_error("Couldn't save large texture: "+p_path); + return err; + } + + return OK; + + + } else if (atlas) { //prepare atlas! Vector< Image > sources; + Vector< Image > tsources; bool alpha=false; bool crop = from->get_option("crop"); EditorProgress ep("make_atlas","Build Atlas For: "+p_path.get_file(),from->get_source_count()+3); print_line("sources: "+itos(from->get_source_count())); + for(int i=0;i<from->get_source_count();i++) { String path = EditorImportPlugin::expand_source_path(from->get_source_path(i)); @@ -775,17 +1093,57 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc if (src.detect_alpha()) alpha=true; - sources.push_back(src); + tsources.push_back(src); } ep.step("Converting Images",sources.size()); - for(int i=0;i<sources.size();i++) { + int base_index=0; + + + Map<uint64_t,int> source_md5; + Map<int,List<int> > source_map; + + for(int i=0;i<tsources.size();i++) { + + Image src = tsources[i]; if (alpha) { - sources[i].convert(Image::FORMAT_RGBA); + src.convert(Image::FORMAT_RGBA); + } else { + src.convert(Image::FORMAT_RGB); + } + + DVector<uint8_t> data = src.get_data(); + MD5_CTX md5; + DVector<uint8_t>::Read r=data.read(); + MD5Init(&md5); + int len=data.size(); + for(int j=0;j<len;j++) { + uint8_t b = r[j]; + b>>=2; //to aid in comparing + MD5Update(&md5,(unsigned char*)&b,1); + } + MD5Final(&md5); + uint64_t *cmp = (uint64_t*)md5.digest; //less bits, but still useful for this + + tsources[i]=Image(); //clear + + if (source_md5.has(*cmp)) { + int sidx=source_md5[*cmp]; + source_map[sidx].push_back(i); + print_line("REUSING "+from->get_source_path(i)); + } else { - sources[i].convert(Image::FORMAT_RGB); + int sidx=sources.size(); + source_md5[*cmp]=sidx; + sources.push_back(src); + List<int> sm; + sm.push_back(i); + source_map[sidx]=sm; + } + + } //texturepacker is not really good for optimizing, so.. @@ -822,31 +1180,59 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc ep.step("Blitting Images",sources.size()+2); + bool blit_to_po2=tex_flags&Texture::FLAG_MIPMAPS; + int atlas_w=dst_size.width; + int atlas_h=dst_size.height; + if (blit_to_po2) { + atlas_w=nearest_power_of_2(dst_size.width); + atlas_h=nearest_power_of_2(dst_size.height); + } Image atlas; - atlas.create(nearest_power_of_2(dst_size.width),nearest_power_of_2(dst_size.height),0,alpha?Image::FORMAT_RGBA:Image::FORMAT_RGB); + atlas.create(atlas_w,atlas_h,0,alpha?Image::FORMAT_RGBA:Image::FORMAT_RGB); + + + atlases.resize(from->get_source_count()); for(int i=0;i<sources.size();i++) { int x=dst_positions[i].x; int y=dst_positions[i].y; - Ref<AtlasTexture> at = memnew( AtlasTexture ); Size2 sz = Size2(sources[i].get_width(),sources[i].get_height()); + + Rect2 region; + Rect2 margin; + if (crop && sz!=crops[i].size) { Rect2 rect = crops[i]; rect.size=sz-rect.size; - at->set_region(Rect2(x+border,y+border,crops[i].size.width,crops[i].size.height)); - at->set_margin(rect); + region=Rect2(x+border,y+border,crops[i].size.width,crops[i].size.height); + margin=rect; atlas.blit_rect(sources[i],crops[i],Point2(x+border,y+border)); } else { - at->set_region(Rect2(x+border,y+border,sz.x,sz.y)); + region=Rect2(x+border,y+border,sz.x,sz.y); atlas.blit_rect(sources[i],Rect2(0,0,sources[i].get_width(),sources[i].get_height()),Point2(x+border,y+border)); } - String apath = p_path.get_base_dir().plus_file(from->get_source_path(i).get_file().basename()+".atex"); - print_line("Atlas Tex: "+apath); - at->set_path(apath); - atlases.push_back(at); + ERR_CONTINUE( !source_map.has(i) ); + for (List<int>::Element *E=source_map[i].front();E;E=E->next()) { + + String apath = p_path.get_base_dir().plus_file(from->get_source_path(E->get()).get_file().basename()+".atex"); + + Ref<AtlasTexture> at; + + if (ResourceCache::has(apath)) { + at = Ref<AtlasTexture>( ResourceCache::get(apath)->cast_to<AtlasTexture>() ); + } else { + + at = Ref<AtlasTexture>( memnew( AtlasTexture ) ); + } + at->set_region(region); + at->set_margin(margin); + at->set_path(apath); + atlases[E->get()]=at; + print_line("Atlas Tex: "+apath); + } } if (ResourceCache::has(p_path)) { texture = Ref<ImageTexture> ( ResourceCache::get(p_path)->cast_to<ImageTexture>() ); @@ -880,8 +1266,6 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc } - int format=from->get_option("format"); - float quality=from->get_option("quality"); if (!p_external) { from->set_editor(get_name()); @@ -915,7 +1299,11 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc } } + bool compress=false; +#if 1 + _process_texture_data(texture,format,quality,flags,p_compr,tex_flags,shrink); +#else if (format==IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS || format==IMAGE_FORMAT_COMPRESS_DISK_LOSSY) { Image image=texture->get_data(); @@ -972,13 +1360,6 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc texture->set_lossy_storage_quality(quality); - Error err = ResourceSaver::save(p_path,texture); - - if (err!=OK) { - EditorNode::add_io_error("Couldn't save converted texture: "+p_path); - return err; - } - } else { @@ -1041,15 +1422,20 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<Resourc texture->set_size_override(Size2(orig_w,orig_h)); } - uint32_t save_flags=ResourceSaver::FLAG_COMPRESS; + compress=true; - Error err = ResourceSaver::save(p_path,texture,save_flags); - if (err!=OK) { - EditorNode::add_io_error("Couldn't save converted texture: "+p_path); - return err; - } } +#endif + uint32_t save_flags=0; + if (compress) + save_flags=ResourceSaver::FLAG_COMPRESS; + + Error err = ResourceSaver::save(p_path,texture,save_flags); + if (err!=OK) { + EditorNode::add_io_error("Couldn't save converted texture: "+p_path); + return err; + } return OK; } @@ -1098,6 +1484,9 @@ Vector<uint8_t> EditorTextureImportPlugin::custom_export(const String& p_path, c case EditorImportExport::IMAGE_ACTION_COMPRESS_RAM: { group_format=EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_RAM; } break; //use default + case EditorImportExport::IMAGE_ACTION_KEEP: { + return Vector<uint8_t>(); + } break; //use default } @@ -1238,14 +1627,14 @@ Vector<uint8_t> EditorTextureImportPlugin::custom_export(const String& p_path, c } -EditorTextureImportPlugin *EditorTextureImportPlugin::singleton[3]={NULL,NULL,NULL}; +EditorTextureImportPlugin *EditorTextureImportPlugin::singleton[EditorTextureImportPlugin::MODE_MAX]={NULL,NULL,NULL,NULL}; EditorTextureImportPlugin::EditorTextureImportPlugin(EditorNode *p_editor, Mode p_mode) { singleton[p_mode]=this; editor=p_editor; - mode=p_mode; - dialog = memnew( EditorTextureImportDialog(this,p_mode==MODE_TEXTURE_2D || p_mode==MODE_ATLAS,p_mode==MODE_ATLAS) ); + mode=p_mode; + dialog = memnew( EditorTextureImportDialog(this,p_mode==MODE_TEXTURE_2D || p_mode==MODE_ATLAS || p_mode==MODE_LARGE,p_mode==MODE_ATLAS,p_mode==MODE_LARGE) ); editor->get_gui_base()->add_child(dialog); } diff --git a/tools/editor/io_plugins/editor_texture_import_plugin.h b/tools/editor/io_plugins/editor_texture_import_plugin.h index d17b3c05c2..38fd671e9d 100644 --- a/tools/editor/io_plugins/editor_texture_import_plugin.h +++ b/tools/editor/io_plugins/editor_texture_import_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -56,7 +56,9 @@ public: enum Mode { MODE_TEXTURE_2D, MODE_TEXTURE_3D, - MODE_ATLAS + MODE_ATLAS, + MODE_LARGE, + MODE_MAX }; @@ -65,10 +67,10 @@ private: Mode mode; EditorNode *editor; EditorTextureImportDialog *dialog; - static EditorTextureImportPlugin *singleton[3]; + static EditorTextureImportPlugin *singleton[MODE_MAX]; //used by other importers such as mesh - + Error _process_texture_data(Ref<ImageTexture> &texture, int format, float quality, int flags,EditorExportPlatform::ImageCompression p_compr,int tex_flags,float shrink); void compress_image(EditorExportPlatform::ImageCompression p_mode,Image& image,bool p_smaller); public: @@ -98,6 +100,7 @@ public: IMAGE_FLAG_USE_ANISOTROPY=1024, //convert image to linear }; + Mode get_mode() const { return mode; } virtual String get_name() const; virtual String get_visible_name() const; virtual void import_dialog(const String& p_from=""); @@ -120,6 +123,7 @@ public: virtual Vector<uint8_t> custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform); EditorTextureExportPlugin(); }; + class EditorImportTextureOptions : public VBoxContainer { OBJ_TYPE( EditorImportTextureOptions, VBoxContainer ); diff --git a/tools/editor/io_plugins/editor_translation_import_plugin.cpp b/tools/editor/io_plugins/editor_translation_import_plugin.cpp index 9540869789..d152d71af4 100644 --- a/tools/editor/io_plugins/editor_translation_import_plugin.cpp +++ b/tools/editor/io_plugins/editor_translation_import_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -47,7 +47,7 @@ class EditorTranslationImportDialog : public ConfirmationDialog { LineEdit *import_path; LineEdit *save_path; - FileDialog *file_select; + EditorFileDialog *file_select; CheckButton *ignore_first; CheckButton *compress; CheckButton *add_to_project; @@ -347,16 +347,16 @@ public: add_to_project->set_text("Add to Project (engine.cfg)"); tcomp->add_child(add_to_project); - file_select = memnew(FileDialog); - file_select->set_access(FileDialog::ACCESS_FILESYSTEM); + file_select = memnew(EditorFileDialog); + file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM); add_child(file_select); - file_select->set_mode(FileDialog::MODE_OPEN_FILE); + file_select->set_mode(EditorFileDialog::MODE_OPEN_FILE); file_select->connect("file_selected", this,"_choose_file"); file_select->add_filter("*.csv ; Translation CSV"); save_select = memnew( EditorDirDialog ); add_child(save_select); - // save_select->set_mode(FileDialog::MODE_OPEN_DIR); + // save_select->set_mode(EditorFileDialog::MODE_OPEN_DIR); save_select->connect("dir_selected", this,"_choose_save_dir"); get_ok()->connect("pressed", this,"_import"); diff --git a/tools/editor/io_plugins/editor_translation_import_plugin.h b/tools/editor/io_plugins/editor_translation_import_plugin.h index 8ea422c244..f3c2884534 100644 --- a/tools/editor/io_plugins/editor_translation_import_plugin.h +++ b/tools/editor/io_plugins/editor_translation_import_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/multi_node_edit.cpp b/tools/editor/multi_node_edit.cpp new file mode 100644 index 0000000000..cfa998bee9 --- /dev/null +++ b/tools/editor/multi_node_edit.cpp @@ -0,0 +1,118 @@ +#include "multi_node_edit.h" +#include "editor_node.h" + +bool MultiNodeEdit::_set(const StringName& p_name, const Variant& p_value){ + + Node *es = EditorNode::get_singleton()->get_edited_scene(); + if (!es) + return false; + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + + ur->create_action("MultiNode Set "+String(p_name)); + for (const List<NodePath>::Element *E=nodes.front();E;E=E->next()) { + + if (!es->has_node(E->get())) + continue; + + Node*n=es->get_node(E->get()); + if (!n) + continue; + + ur->add_do_property(n,p_name,p_value); + ur->add_undo_property(n,p_name,n->get(p_name)); + + } + + ur->commit_action(); + return true; +} + +bool MultiNodeEdit::_get(const StringName& p_name,Variant &r_ret) const { + + Node *es = EditorNode::get_singleton()->get_edited_scene(); + if (!es) + return false; + + for (const List<NodePath>::Element *E=nodes.front();E;E=E->next()) { + + if (!es->has_node(E->get())) + continue; + + const Node*n=es->get_node(E->get()); + if (!n) + continue; + + bool found; + r_ret=n->get(p_name,&found); + if (found) + return true; + + } + + return false; +} + +void MultiNodeEdit::_get_property_list( List<PropertyInfo> *p_list) const{ + + HashMap<String,PLData> usage; + + Node *es = EditorNode::get_singleton()->get_edited_scene(); + if (!es) + return; + + int nc=0; + + List<PLData*> datas; + + for (const List<NodePath>::Element *E=nodes.front();E;E=E->next()) { + + if (!es->has_node(E->get())) + continue; + + Node*n=es->get_node(E->get()); + if (!n) + continue; + + List<PropertyInfo> plist; + n->get_property_list(&plist,true); + + for(List<PropertyInfo>::Element *F=plist.front();F;F=F->next()) { + + if (!usage.has(F->get().name)) { + PLData pld; + pld.uses=0; + pld.info=F->get(); + usage[F->get().name]=pld; + datas.push_back(usage.getptr(F->get().name)); + } + + usage[F->get().name].uses++; + } + + nc++; + } + + for (List<PLData*>::Element *E=datas.front();E;E=E->next()) { + + if (nc==E->get()->uses) { + p_list->push_back(E->get()->info); + } + } + + +} + +void MultiNodeEdit::clear_nodes() { + + nodes.clear(); +} + +void MultiNodeEdit::add_node(const NodePath& p_node){ + + nodes.push_back(p_node); +} + +MultiNodeEdit::MultiNodeEdit() +{ +} diff --git a/tools/editor/multi_node_edit.h b/tools/editor/multi_node_edit.h new file mode 100644 index 0000000000..5a0cabf4be --- /dev/null +++ b/tools/editor/multi_node_edit.h @@ -0,0 +1,32 @@ +#ifndef MULTI_NODE_EDIT_H +#define MULTI_NODE_EDIT_H + +#include "scene/main/node.h" + +class MultiNodeEdit : public Reference { + + OBJ_TYPE(MultiNodeEdit,Reference); + + List<NodePath> nodes; + struct PLData { + int uses; + PropertyInfo info; + }; + +protected: + + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + +public: + + + + void clear_nodes(); + void add_node(const NodePath& p_node); + + MultiNodeEdit(); +}; + +#endif // MULTI_NODE_EDIT_H diff --git a/tools/editor/optimized_save_dialog.cpp b/tools/editor/optimized_save_dialog.cpp index 8a28272f12..687d3675fc 100644 --- a/tools/editor/optimized_save_dialog.cpp +++ b/tools/editor/optimized_save_dialog.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/optimized_save_dialog.h b/tools/editor/optimized_save_dialog.h index a3879b7cb4..739d0e1506 100644 --- a/tools/editor/optimized_save_dialog.h +++ b/tools/editor/optimized_save_dialog.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/output_strings.cpp b/tools/editor/output_strings.cpp index ec85505484..30569d11b0 100644 --- a/tools/editor/output_strings.cpp +++ b/tools/editor/output_strings.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/output_strings.h b/tools/editor/output_strings.h index cd9caa2b71..ad893534fa 100644 --- a/tools/editor/output_strings.h +++ b/tools/editor/output_strings.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,64 +26,64 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef OUTPUT_STRINGS_H
-#define OUTPUT_STRINGS_H
-
-
-#include "scene/gui/control.h"
-#include "scene/gui/scroll_bar.h"
-#include "map.h"
-
-class OutputStrings : public Control {
-
- OBJ_TYPE( OutputStrings, Control );
-public:
-
- enum LineType {
-
- LINE_NORMAL,
- LINE_WARNING,
- LINE_ERROR,
- LINE_LINK
- };
-private:
-
- struct Line {
-
-
- LineType type;
- Variant meta;
- String text;
- };
-
-
- int font_height;
- int size_height;
-
- Size2 margin;
- typedef Map<int,Line> LineMap;
- Map<int,Line> line_map;
-
- VScrollBar *v_scroll;
- HScrollBar *h_scroll;
-
- bool following;
- int line_max_count;
- bool updating;
-
- void _vscroll_changed(float p_value);
- void _hscroll_changed(float p_value);
- void update_scrollbars();
-protected:
-
- static void _bind_methods();
- void _notification(int p_what);
-
-public:
-
- void add_line(const String& p_text, const Variant& p_meta=Variant(), const LineType p_type=LINE_NORMAL);
-
- OutputStrings();
-};
-
-#endif // OUTPUT_STRINGS_H
+#ifndef OUTPUT_STRINGS_H +#define OUTPUT_STRINGS_H + + +#include "scene/gui/control.h" +#include "scene/gui/scroll_bar.h" +#include "map.h" + +class OutputStrings : public Control { + + OBJ_TYPE( OutputStrings, Control ); +public: + + enum LineType { + + LINE_NORMAL, + LINE_WARNING, + LINE_ERROR, + LINE_LINK + }; +private: + + struct Line { + + + LineType type; + Variant meta; + String text; + }; + + + int font_height; + int size_height; + + Size2 margin; + typedef Map<int,Line> LineMap; + Map<int,Line> line_map; + + VScrollBar *v_scroll; + HScrollBar *h_scroll; + + bool following; + int line_max_count; + bool updating; + + void _vscroll_changed(float p_value); + void _hscroll_changed(float p_value); + void update_scrollbars(); +protected: + + static void _bind_methods(); + void _notification(int p_what); + +public: + + void add_line(const String& p_text, const Variant& p_meta=Variant(), const LineType p_type=LINE_NORMAL); + + OutputStrings(); +}; + +#endif // OUTPUT_STRINGS_H diff --git a/tools/editor/pane_drag.cpp b/tools/editor/pane_drag.cpp index f3a236201a..fb137de5ce 100644 --- a/tools/editor/pane_drag.cpp +++ b/tools/editor/pane_drag.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/pane_drag.h b/tools/editor/pane_drag.h index 268c940e35..a6cd9b6662 100644 --- a/tools/editor/pane_drag.h +++ b/tools/editor/pane_drag.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,26 +26,26 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef PANE_DRAG_H
-#define PANE_DRAG_H
-
-#include "scene/gui/control.h"
-
-class PaneDrag : public Control {
-
- OBJ_TYPE( PaneDrag, Control );
-
- bool mouse_over;
-
-
-protected:
-
- void _input_event(const InputEvent& p_input);
- void _notification(int p_what);
- virtual Size2 get_minimum_size() const;
- static void _bind_methods();
-public:
- PaneDrag();
-};
-
-#endif // PANE_DRAG_H
+#ifndef PANE_DRAG_H +#define PANE_DRAG_H + +#include "scene/gui/control.h" + +class PaneDrag : public Control { + + OBJ_TYPE( PaneDrag, Control ); + + bool mouse_over; + + +protected: + + void _input_event(const InputEvent& p_input); + void _notification(int p_what); + virtual Size2 get_minimum_size() const; + static void _bind_methods(); +public: + PaneDrag(); +}; + +#endif // PANE_DRAG_H diff --git a/tools/editor/plugins/SCsub b/tools/editor/plugins/SCsub index b525fb3f75..363a2ce4c0 100644 --- a/tools/editor/plugins/SCsub +++ b/tools/editor/plugins/SCsub @@ -1,7 +1,3 @@ Import('env') Export('env') env.add_source_files(env.tool_sources,"*.cpp") - - - - diff --git a/tools/editor/plugins/animation_data_editor_plugin.cpp b/tools/editor/plugins/animation_data_editor_plugin.cpp index 17f17bba7d..d8d65b875a 100644 --- a/tools/editor/plugins/animation_data_editor_plugin.cpp +++ b/tools/editor/plugins/animation_data_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,8 +26,8 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "animation_data_editor_plugin.h"
-
-AnimationDataEditorPlugin::AnimationDataEditorPlugin()
-{
-}
+#include "animation_data_editor_plugin.h" + +AnimationDataEditorPlugin::AnimationDataEditorPlugin() +{ +} diff --git a/tools/editor/plugins/animation_data_editor_plugin.h b/tools/editor/plugins/animation_data_editor_plugin.h index 2fd3d5b32a..0daa67d0a5 100644 --- a/tools/editor/plugins/animation_data_editor_plugin.h +++ b/tools/editor/plugins/animation_data_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,13 +26,13 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef ANIMATION_DATA_EDITOR_PLUGIN_H
-#define ANIMATION_DATA_EDITOR_PLUGIN_H
-
-class AnimationDataEditorPlugin
-{
-public:
- AnimationDataEditorPlugin();
-};
-
-#endif // ANIMATION_DATA_EDITOR_PLUGIN_H
+#ifndef ANIMATION_DATA_EDITOR_PLUGIN_H +#define ANIMATION_DATA_EDITOR_PLUGIN_H + +class AnimationDataEditorPlugin +{ +public: + AnimationDataEditorPlugin(); +}; + +#endif // ANIMATION_DATA_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/animation_player_editor_plugin.cpp b/tools/editor/plugins/animation_player_editor_plugin.cpp index 8bb37f1d71..6542fc8b4a 100644 --- a/tools/editor/plugins/animation_player_editor_plugin.cpp +++ b/tools/editor/plugins/animation_player_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,8 +27,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "animation_player_editor_plugin.h" +#include "globals.h" #include "io/resource_loader.h" - +#include "io/resource_saver.h" +#include "os/keyboard.h" +#include "tools/editor/editor_settings.h" void AnimationPlayerEditor::_node_removed(Node *p_node) { @@ -76,6 +79,8 @@ void AnimationPlayerEditor::_notification(int p_what) { seek->set_val(player->get_current_animation_pos()); if (edit_anim->is_pressed()) editor->get_animation_editor()->set_anim_pos(player->get_current_animation_pos()); + EditorNode::get_singleton()->get_property_editor()->refresh(); + } else if (last_active) { //need the last frame after it stopped @@ -95,15 +100,23 @@ void AnimationPlayerEditor::_notification(int p_what) { duplicate_anim->set_icon( get_icon("Duplicate","EditorIcons") ); autoplay->set_icon( get_icon("AutoPlay","EditorIcons") ); load_anim->set_icon( get_icon("Folder","EditorIcons") ); - remove_anim->set_icon( get_icon("Del","EditorIcons") ); + save_anim->set_icon(get_icon("Save", "EditorIcons")); + save_anim->get_popup()->connect("item_pressed", this, "_animation_save_menu"); + remove_anim->set_icon( get_icon("Remove","EditorIcons") ); edit_anim->set_icon( get_icon("Edit","EditorIcons") ); blend_anim->set_icon( get_icon("Blend","EditorIcons") ); - play->set_icon( get_icon("Play","EditorIcons") ); + play->set_icon( get_icon("PlayStart","EditorIcons") ); + play_from->set_icon( get_icon("Play","EditorIcons") ); + play_bw->set_icon( get_icon("PlayStartBackwards","EditorIcons") ); + play_bw_from->set_icon( get_icon("PlayBackwards","EditorIcons") ); + autoplay_icon=get_icon("AutoPlay","EditorIcons"); stop->set_icon( get_icon("Stop","EditorIcons") ); resource_edit_anim->set_icon( get_icon("EditResource","EditorIcons") ); pin->set_normal_texture(get_icon("Pin","EditorIcons") ); pin->set_pressed_texture( get_icon("PinPressed","EditorIcons") ); + tool_anim->set_icon(get_icon("Tools","EditorIcons")); + tool_anim->get_popup()->connect("item_pressed",this,"_animation_tool_menu"); blend_editor.next->connect("text_changed",this,"_blend_editor_next_changed"); @@ -178,9 +191,82 @@ void AnimationPlayerEditor::_play_pressed() { //unpause //pause->set_pressed(false); } + +void AnimationPlayerEditor::_play_from_pressed() { + + String current; + if (animation->get_selected()>=0 && animation->get_selected()<animation->get_item_count()) { + + current = animation->get_item_text( animation->get_selected() ); + } + + if (current!="") { + + float time = player->get_current_animation_pos(); + + if (current==player->get_current_animation() && player->is_playing()) { + + player->stop(); //so it wont blend with itself + } + + player->play( current ); + player->seek(time); + } + + //unstop + stop->set_pressed(false); + //unpause + //pause->set_pressed(false); +} + + +void AnimationPlayerEditor::_play_bw_pressed() { + + String current; + if (animation->get_selected()>=0 && animation->get_selected()<animation->get_item_count()) { + + current = animation->get_item_text( animation->get_selected() ); + } + + if (current!="") { + + if (current==player->get_current_animation()) + player->stop(); //so it wont blend with itself + player->play(current,-1,-1,true); + } + + //unstop + stop->set_pressed(false); + //unpause + //pause->set_pressed(false); +} + +void AnimationPlayerEditor::_play_bw_from_pressed() { + + String current; + if (animation->get_selected()>=0 && animation->get_selected()<animation->get_item_count()) { + + current = animation->get_item_text( animation->get_selected() ); + } + + if (current!="") { + + float time = player->get_current_animation_pos(); + if (current==player->get_current_animation()) + player->stop(); //so it wont blend with itself + + player->play(current,-1,-1,true); + player->seek(time); + } + + //unstop + stop->set_pressed(false); + //unpause + //pause->set_pressed(false); +} void AnimationPlayerEditor::_stop_pressed() { - player->stop(); + player->stop(false); play->set_pressed(false); stop->set_pressed(true); //pause->set_pressed(false); @@ -273,7 +359,7 @@ void AnimationPlayerEditor::_animation_rename() { } void AnimationPlayerEditor::_animation_load() { ERR_FAIL_COND(!player); - file->set_mode( FileDialog::MODE_OPEN_FILE ); + file->set_mode( EditorFileDialog::MODE_OPEN_FILE ); file->clear_filters(); List<String> extensions; @@ -285,8 +371,78 @@ void AnimationPlayerEditor::_animation_load() { } file->popup_centered_ratio(); + current_option = RESOURCE_LOAD; +} + + +void AnimationPlayerEditor::_animation_save_in_path(const Ref<Resource>& p_resource, const String& p_path) { + + int flg = 0; + if (EditorSettings::get_singleton()->get("on_save/compress_binary_resources")) + flg |= ResourceSaver::FLAG_COMPRESS; + if (EditorSettings::get_singleton()->get("on_save/save_paths_as_relative")) + flg |= ResourceSaver::FLAG_RELATIVE_PATHS; + + String path = Globals::get_singleton()->localize_path(p_path); + Error err = ResourceSaver::save(path, p_resource, flg | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS); + + if (err != OK) { + accept->set_text("Error saving resource!"); + accept->popup_centered_minsize(); + return; + } + // EditorFileSystem::get_singleton()->update_file(path,p_resource->get_type()); + + ((Resource*)p_resource.ptr())->set_path(path); + editor->emit_signal("resource_saved", p_resource); + +} +void AnimationPlayerEditor::_animation_save(const Ref<Resource>& p_resource) { + if (p_resource->get_path().is_resource_file()) { + _animation_save_in_path(p_resource, p_resource->get_path()); + } + else { + _animation_save_as(p_resource); + } +} + +void AnimationPlayerEditor::_animation_save_as(const Ref<Resource>& p_resource) { + + file->set_mode(EditorFileDialog::MODE_SAVE_FILE); + bool relpaths = (p_resource->has_meta("__editor_relpaths__") && p_resource->get_meta("__editor_relpaths__").operator bool()); + + List<String> extensions; + ResourceSaver::get_recognized_extensions(p_resource, &extensions); + file->clear_filters(); + for (int i = 0; i<extensions.size(); i++) { + + file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); + } + + //file->set_current_path(current_path); + if (p_resource->get_path() != "") { + file->set_current_path(p_resource->get_path()); + if (extensions.size()) { + String ext = p_resource->get_path().extension().to_lower(); + if (extensions.find(ext) == NULL) { + file->set_current_path(p_resource->get_path().replacen("." + ext, "." + extensions.front()->get())); + } + } + } + else { + + String existing; + if (extensions.size()) { + existing = "new_" + p_resource->get_type().to_lower() + "." + extensions.front()->get().to_lower(); + } + file->set_current_path(existing); + + } + file->popup_centered_ratio(); + file->set_title("Save Resource As.."); + current_option = RESOURCE_SAVE; } void AnimationPlayerEditor::_animation_remove() { @@ -334,7 +490,7 @@ void AnimationPlayerEditor::_animation_name_edited() { String new_name = name->get_text(); if (new_name=="" || new_name.find(":")!=-1 || new_name.find("/")!=-1) { error_dialog->set_text("ERROR: Invalid animation name!"); - error_dialog->popup_centered(Size2(300,70)); + error_dialog->popup_centered_minsize(); return; } @@ -345,7 +501,7 @@ void AnimationPlayerEditor::_animation_name_edited() { if (player->has_animation(new_name)) { error_dialog->set_text("ERROR: Animation Name Already Exists!"); - error_dialog->popup_centered(Size2(300,70)); + error_dialog->popup_centered_minsize(); return; } @@ -467,6 +623,49 @@ void AnimationPlayerEditor::ensure_visibility() { _animation_edit(); } +Dictionary AnimationPlayerEditor::get_state() const { + + Dictionary d; + + d["visible"]=is_visible(); + if (is_visible() && player) { + d["player"]=EditorNode::get_singleton()->get_edited_scene()->get_path_to(player); + d["animation"]=player->get_current_animation(); + d["editing"]=edit_anim->is_pressed(); + } + + return d; + +} +void AnimationPlayerEditor::set_state(const Dictionary& p_state) { + + if (p_state.has("visible") && p_state["visible"]) { + + Node *n = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["player"]); + if (n && n->cast_to<AnimationPlayer>()) { + player=n->cast_to<AnimationPlayer>(); + _update_player(); + show(); + set_process(true); + ensure_visibility(); + EditorNode::get_singleton()->animation_panel_make_visible(true); + + if (p_state.has("animation")) { + String anim = p_state["animation"]; + _select_anim_by_name(anim); + if (p_state.has("editing") && p_state["editing"]) { + + edit_anim->set_pressed(true); + _animation_edit(); + } + } + + } + } + +} + + void AnimationPlayerEditor::_animation_resource_edit() { if (animation->get_item_count()) { @@ -510,38 +709,55 @@ void AnimationPlayerEditor::_animation_edit() { //get_scene()->get_root_node()->call("_resource_selected",anim,""); } -void AnimationPlayerEditor::_file_selected(String p_file) { +void AnimationPlayerEditor::_dialog_action(String p_file) { - ERR_FAIL_COND(!player); + switch (current_option) { + case RESOURCE_LOAD: { + ERR_FAIL_COND(!player); - Ref<Resource> res = ResourceLoader::load(p_file,"Animation"); - ERR_FAIL_COND(res.is_null()); - ERR_FAIL_COND( !res->is_type("Animation") ); - if (p_file.find_last("/")!=-1) { + Ref<Resource> res = ResourceLoader::load(p_file, "Animation"); + ERR_FAIL_COND(res.is_null()); + ERR_FAIL_COND(!res->is_type("Animation")); + if (p_file.find_last("/") != -1) { - p_file=p_file.substr( p_file.find_last("/")+1, p_file.length() ); + p_file = p_file.substr(p_file.find_last("/") + 1, p_file.length()); - } - if (p_file.find_last("\\")!=-1) { + } + if (p_file.find_last("\\") != -1) { - p_file=p_file.substr( p_file.find_last("\\")+1, p_file.length() ); + p_file = p_file.substr(p_file.find_last("\\") + 1, p_file.length()); - } + } - if (p_file.find(".")!=-1) - p_file=p_file.substr(0,p_file.find(".")); + if (p_file.find(".") != -1) + p_file = p_file.substr(0, p_file.find(".")); - undo_redo->create_action("Load Animation"); - undo_redo->add_do_method(player,"add_animation",p_file,res); - undo_redo->add_undo_method(player,"remove_animation",p_file); - if (player->has_animation(p_file)) { - undo_redo->add_undo_method(player,"add_animation",p_file,player->get_animation(p_file)); + undo_redo->create_action("Load Animation"); + undo_redo->add_do_method(player, "add_animation", p_file, res); + undo_redo->add_undo_method(player, "remove_animation", p_file); + if (player->has_animation(p_file)) { + undo_redo->add_undo_method(player, "add_animation", p_file, player->get_animation(p_file)); - } - undo_redo->add_do_method(this,"_animation_player_changed",player); - undo_redo->add_undo_method(this,"_animation_player_changed",player); - undo_redo->commit_action(); + } + undo_redo->add_do_method(this, "_animation_player_changed", player); + undo_redo->add_undo_method(this, "_animation_player_changed", player); + undo_redo->commit_action(); + break; + } + case RESOURCE_SAVE: { + + String current = animation->get_item_text(animation->get_selected()); + if (current != "") { + Ref<Animation> anim = player->get_animation(current); + + ERR_FAIL_COND(!anim->cast_to<Resource>()) + + RES current_res = RES(anim->cast_to<Resource>()); + _animation_save_in_path(current_res, p_file); + } + } + } } void AnimationPlayerEditor::_scale_changed(const String& p_scale) { @@ -596,12 +812,17 @@ void AnimationPlayerEditor::_update_player() { stop->set_disabled(animlist.size()==0); play->set_disabled(animlist.size()==0); + play_bw->set_disabled(animlist.size()==0); + play_bw_from->set_disabled(animlist.size()==0); + play_from->set_disabled(animlist.size()==0); autoplay->set_disabled(animlist.size()==0); duplicate_anim->set_disabled(animlist.size()==0); rename_anim->set_disabled(animlist.size()==0); blend_anim->set_disabled(animlist.size()==0); remove_anim->set_disabled(animlist.size()==0); resource_edit_anim->set_disabled(animlist.size()==0); + save_anim->set_disabled(animlist.size() == 0); + int active_idx=-1; for (List<StringName>::Element *E=animlist.front();E;E=E->next()) { @@ -854,6 +1075,8 @@ void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos) { return; seek->set_val(p_pos); + EditorNode::get_singleton()->get_property_editor()->refresh(); + //seekit @@ -873,11 +1096,125 @@ void AnimationPlayerEditor::_hide_anim_editors() { } } + +void AnimationPlayerEditor::_animation_tool_menu(int p_option) { + + switch(p_option) { + + case TOOL_COPY_ANIM: { + + if (!animation->get_item_count()) { + error_dialog->set_text("ERROR: No animation to copy!"); + error_dialog->popup_centered_minsize(); + return; + } + + String current = animation->get_item_text(animation->get_selected()); + Ref<Animation> anim = player->get_animation(current); + //editor->edit_resource(anim); + EditorSettings::get_singleton()->set_resource_clipboard(anim); + + } break; + case TOOL_PASTE_ANIM: { + + Ref<Animation> anim = EditorSettings::get_singleton()->get_resource_clipboard(); + if (!anim.is_valid()) { + error_dialog->set_text("ERROR: No animation resource on clipboard!"); + error_dialog->popup_centered_minsize(); + return; + } + + String name = anim->get_name(); + if (name=="") { + name="Pasted Animation"; + } + + int idx=1; + String base = name; + while (player->has_animation(name)) { + + idx++; + name=base+" "+itos(idx); + } + + undo_redo->create_action("Paste Animation"); + undo_redo->add_do_method(player,"add_animation",name,anim); + undo_redo->add_undo_method(player,"remove_animation",name); + undo_redo->add_do_method(this,"_animation_player_changed",player); + undo_redo->add_undo_method(this,"_animation_player_changed",player); + undo_redo->commit_action(); + + _select_anim_by_name(name); + + + } break; + case TOOL_EDIT_RESOURCE: { + + if (!animation->get_item_count()) { + error_dialog->set_text("ERROR: No animation to edit!"); + error_dialog->popup_centered_minsize(); + return; + } + + String current = animation->get_item_text(animation->get_selected()); + Ref<Animation> anim = player->get_animation(current); + editor->edit_resource(anim); + + } break; + + } +} + +void AnimationPlayerEditor::_animation_save_menu(int p_option) { + + String current = animation->get_item_text(animation->get_selected()); + if (current != "") { + Ref<Animation> anim = player->get_animation(current); + + switch (p_option) { + case ANIM_SAVE: + _animation_save(anim); + break; + case ANIM_SAVE_AS: + _animation_save_as(anim); + break; + } + } +} + +void AnimationPlayerEditor::_unhandled_key_input(const InputEvent& p_ev) { + + if (is_visible() && p_ev.type==InputEvent::KEY && p_ev.key.pressed && !p_ev.key.echo && !p_ev.key.mod.alt && !p_ev.key.mod.control && !p_ev.key.mod.meta) { + + switch(p_ev.key.scancode) { + + case KEY_A: { + if (!p_ev.key.mod.shift) + _play_bw_from_pressed(); + else + _play_bw_pressed(); + } break; + case KEY_S: { + _stop_pressed(); + } break; + case KEY_D: { + if (!p_ev.key.mod.shift) + _play_from_pressed(); + else + _play_pressed(); + } break; + } + } +} + void AnimationPlayerEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("_input_event"),&AnimationPlayerEditor::_input_event); ObjectTypeDB::bind_method(_MD("_node_removed"),&AnimationPlayerEditor::_node_removed); ObjectTypeDB::bind_method(_MD("_play_pressed"),&AnimationPlayerEditor::_play_pressed); + ObjectTypeDB::bind_method(_MD("_play_from_pressed"),&AnimationPlayerEditor::_play_from_pressed); + ObjectTypeDB::bind_method(_MD("_play_bw_pressed"),&AnimationPlayerEditor::_play_bw_pressed); + ObjectTypeDB::bind_method(_MD("_play_bw_from_pressed"),&AnimationPlayerEditor::_play_bw_from_pressed); ObjectTypeDB::bind_method(_MD("_stop_pressed"),&AnimationPlayerEditor::_stop_pressed); ObjectTypeDB::bind_method(_MD("_autoplay_pressed"),&AnimationPlayerEditor::_autoplay_pressed); ObjectTypeDB::bind_method(_MD("_pause_pressed"),&AnimationPlayerEditor::_pause_pressed); @@ -890,7 +1227,7 @@ void AnimationPlayerEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("_animation_blend"),&AnimationPlayerEditor::_animation_blend); ObjectTypeDB::bind_method(_MD("_animation_edit"),&AnimationPlayerEditor::_animation_edit); ObjectTypeDB::bind_method(_MD("_animation_resource_edit"),&AnimationPlayerEditor::_animation_resource_edit); - ObjectTypeDB::bind_method(_MD("_file_selected"),&AnimationPlayerEditor::_file_selected); + ObjectTypeDB::bind_method(_MD("_dialog_action"),&AnimationPlayerEditor::_dialog_action); ObjectTypeDB::bind_method(_MD("_seek_value_changed"),&AnimationPlayerEditor::_seek_value_changed); ObjectTypeDB::bind_method(_MD("_animation_player_changed"),&AnimationPlayerEditor::_animation_player_changed); ObjectTypeDB::bind_method(_MD("_blend_edited"),&AnimationPlayerEditor::_blend_edited); @@ -904,6 +1241,9 @@ void AnimationPlayerEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("_hide_anim_editors"),&AnimationPlayerEditor::_hide_anim_editors); ObjectTypeDB::bind_method(_MD("_animation_duplicate"),&AnimationPlayerEditor::_animation_duplicate); ObjectTypeDB::bind_method(_MD("_blend_editor_next_changed"),&AnimationPlayerEditor::_blend_editor_next_changed); + ObjectTypeDB::bind_method(_MD("_unhandled_key_input"),&AnimationPlayerEditor::_unhandled_key_input); + ObjectTypeDB::bind_method(_MD("_animation_tool_menu"),&AnimationPlayerEditor::_animation_tool_menu); + ObjectTypeDB::bind_method(_MD("_animation_save_menu"), &AnimationPlayerEditor::_animation_save_menu); @@ -931,47 +1271,67 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { add_child(hb); - add_anim = memnew( Button ); + add_anim = memnew( ToolButton ); add_anim->set_tooltip("Create new animation in player."); hb->add_child(add_anim); - - load_anim = memnew( Button ); + load_anim = memnew( ToolButton ); load_anim->set_tooltip("Load an animation from disk."); hb->add_child(load_anim); - duplicate_anim = memnew( Button ); + save_anim = memnew(MenuButton); + save_anim->set_tooltip("Save the current animation"); + save_anim->get_popup()->add_item("Save", ANIM_SAVE); + save_anim->get_popup()->add_item("Save As..", ANIM_SAVE_AS); + save_anim->set_focus_mode(Control::FOCUS_NONE); + hb->add_child(save_anim); + + accept = memnew(AcceptDialog); + add_child(accept); + accept->connect("confirmed", this, "_menu_confirm_current"); + + duplicate_anim = memnew( ToolButton ); hb->add_child(duplicate_anim); duplicate_anim->set_tooltip("Duplicate Animation"); + rename_anim = memnew( ToolButton ); + hb->add_child(rename_anim); + rename_anim->set_tooltip("Rename Animation"); + + remove_anim = memnew( ToolButton ); + + hb->add_child(remove_anim); + remove_anim->set_tooltip("Remove Animation"); + + animation = memnew( OptionButton ); hb->add_child(animation); animation->set_h_size_flags(SIZE_EXPAND_FILL); animation->set_tooltip("Display list of animations in player."); - autoplay = memnew( Button ); + autoplay = memnew( ToolButton ); hb->add_child(autoplay); autoplay->set_tooltip("Autoplay On Load"); - rename_anim = memnew( Button ); - hb->add_child(rename_anim); - rename_anim->set_tooltip("Rename Animation"); - remove_anim = memnew( Button ); - - hb->add_child(remove_anim); - remove_anim->set_tooltip("Remove Animation"); - - blend_anim = memnew( Button ); + blend_anim = memnew( ToolButton ); hb->add_child(blend_anim); blend_anim->set_tooltip("Edit Target Blend Times"); + tool_anim = memnew( MenuButton); + //tool_anim->set_flat(false); + tool_anim->set_tooltip("Animation Tools"); + tool_anim->get_popup()->add_item("Copy Animation",TOOL_COPY_ANIM); + tool_anim->get_popup()->add_item("Paste Animation",TOOL_PASTE_ANIM); + //tool_anim->get_popup()->add_separator(); + //tool_anim->get_popup()->add_item("Edit Anim Resource",TOOL_PASTE_ANIM); + hb->add_child(tool_anim); - edit_anim = memnew( Button ); + edit_anim = memnew( ToolButton ); edit_anim->set_toggle_mode(true); hb->add_child(edit_anim); edit_anim->set_tooltip("Open animation editor.\nProperty editor will displays all editable keys too."); @@ -980,15 +1340,29 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { hb = memnew (HBoxContainer); add_child(hb); - play = memnew( Button ); - play->set_tooltip("Play selected animation."); + play_bw_from = memnew( ToolButton ); + play_bw_from->set_tooltip("Play backwards selected animation from current pos. (A)"); + hb->add_child(play_bw_from); - hb->add_child(play); + play_bw = memnew( ToolButton ); + play_bw->set_tooltip("Play backwards selected animation from end. (Shift+A)"); + hb->add_child(play_bw); - stop = memnew( Button ); + stop = memnew( ToolButton ); stop->set_toggle_mode(true); hb->add_child(stop); - play->set_tooltip("Stop animation playback."); + stop->set_tooltip("Stop animation playback. (S)"); + + play = memnew( ToolButton ); + play->set_tooltip("Play selected animation from start. (Shift+D)"); + hb->add_child(play); + + + play_from = memnew( ToolButton ); + play_from->set_tooltip("Play selected animation from current pos. (D)"); + hb->add_child(play_from); + + //pause = memnew( Button ); //pause->set_toggle_mode(true); @@ -1020,12 +1394,14 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { resource_edit_anim= memnew( Button ); hb->add_child(resource_edit_anim); + resource_edit_anim->hide(); - file = memnew(FileDialog); + file = memnew(EditorFileDialog); add_child(file); name_dialog = memnew( ConfirmationDialog ); + name_dialog->set_title("Create New Animation"); name_dialog->set_hide_on_ok(false); add_child(name_dialog); name = memnew( LineEdit ); @@ -1070,7 +1446,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { autoplay->connect("pressed", this,"_autoplay_pressed"); autoplay->set_toggle_mode(true); - play->connect("pressed", this,"_play_pressed"); + play->connect("pressed", this,"_play_pressed"); + play_from->connect("pressed", this,"_play_from_pressed"); + play_bw->connect("pressed", this,"_play_bw_pressed"); + play_bw_from->connect("pressed", this,"_play_bw_from_pressed"); stop->connect("pressed", this,"_stop_pressed"); //pause->connect("pressed", this,"_pause_pressed"); add_anim->connect("pressed", this,"_animation_new"); @@ -1083,7 +1462,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { remove_anim->connect("pressed", this,"_animation_remove"); animation->connect("item_selected", this,"_animation_selected",Vector<Variant>(),true); resource_edit_anim->connect("pressed", this,"_animation_resource_edit"); - file->connect("file_selected", this,"_file_selected"); + file->connect("file_selected", this,"_dialog_action"); seek->connect("value_changed", this, "_seek_value_changed",Vector<Variant>(),true); scale->connect("text_entered", this, "_scale_changed",Vector<Variant>(),true); editor->get_animation_editor()->connect("timeline_changed",this,"_animation_key_editor_seek"); @@ -1100,6 +1479,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { renaming=false; last_active=false; + + set_process_unhandled_key_input(true); } @@ -1135,6 +1516,7 @@ AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin(EditorNode *p_node) { editor=p_node; anim_editor = memnew( AnimationPlayerEditor(editor) ); + anim_editor->set_undo_redo(editor->get_undo_redo()); editor->get_animation_panel()->add_child(anim_editor); /* editor->get_viewport()->add_child(anim_editor); diff --git a/tools/editor/plugins/animation_player_editor_plugin.h b/tools/editor/plugins/animation_player_editor_plugin.h index 2c6bcae97e..ac4d1ab6ba 100644 --- a/tools/editor/plugins/animation_player_editor_plugin.h +++ b/tools/editor/plugins/animation_player_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -49,10 +49,30 @@ class AnimationPlayerEditor : public VBoxContainer { EditorNode *editor; AnimationPlayer *player; + enum { + TOOL_COPY_ANIM, + TOOL_PASTE_ANIM, + TOOL_EDIT_RESOURCE + }; + + enum { + ANIM_SAVE, + ANIM_SAVE_AS + }; + + enum { + RESOURCE_LOAD, + RESOURCE_SAVE + }; + OptionButton *animation; Button *stop; Button *play; + Button *play_from; + Button *play_bw; + Button *play_bw_from; + // Button *pause; Button *add_anim; Button *autoplay; @@ -61,8 +81,10 @@ class AnimationPlayerEditor : public VBoxContainer { Button *edit_anim; Button *resource_edit_anim; Button *load_anim; + MenuButton *save_anim; Button *blend_anim; Button *remove_anim; + MenuButton *tool_anim; TextureButton *pin; Label *nodename; SpinBox *frame; @@ -74,7 +96,9 @@ class AnimationPlayerEditor : public VBoxContainer { Ref<Texture> autoplay_icon; bool last_active; - FileDialog *file; + EditorFileDialog *file; + AcceptDialog *accept; + int current_option; struct BlendEditor { @@ -95,6 +119,9 @@ class AnimationPlayerEditor : public VBoxContainer { void _select_anim_by_name(const String& p_anim); void _play_pressed(); + void _play_from_pressed(); + void _play_bw_pressed(); + void _play_bw_from_pressed(); void _autoplay_pressed(); void _stop_pressed(); void _pause_pressed(); @@ -103,13 +130,18 @@ class AnimationPlayerEditor : public VBoxContainer { void _animation_rename(); void _animation_name_edited(); void _animation_load(); + + void _animation_save_in_path(const Ref<Resource>& p_resource, const String& p_path); + void _animation_save(const Ref<Resource>& p_resource); + void _animation_save_as(const Ref<Resource>& p_resource); + void _animation_remove(); void _animation_blend(); void _animation_edit(); void _animation_duplicate(); void _animation_resource_edit(); void _scale_changed(const String& p_scale); - void _file_selected(String p_file); + void _dialog_action(String p_file); void _seek_frame_changed(const String& p_frame); void _seek_value_changed(float p_value); void _blend_editor_next_changed(const String& p_string); @@ -126,6 +158,9 @@ class AnimationPlayerEditor : public VBoxContainer { void _animation_key_editor_seek(float p_pos); void _animation_key_editor_anim_len_changed(float p_new); + void _unhandled_key_input(const InputEvent& p_ev); + void _animation_tool_menu(int p_option); + void _animation_save_menu(int p_option); AnimationPlayerEditor(); protected: @@ -136,6 +171,10 @@ protected: static void _bind_methods(); public: + Dictionary get_state() const; + void set_state(const Dictionary& p_state); + + void ensure_visibility(); void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo=p_undo_redo; } @@ -152,6 +191,9 @@ class AnimationPlayerEditorPlugin : public EditorPlugin { public: + virtual Dictionary get_state() const { return anim_editor->get_state(); } + virtual void set_state(const Dictionary& p_state) { anim_editor->set_state(p_state); } + virtual String get_name() const { return "Anim"; } bool has_main_screen() const { return false; } virtual void edit(Object *p_node); diff --git a/tools/editor/plugins/animation_tree_editor_plugin.cpp b/tools/editor/plugins/animation_tree_editor_plugin.cpp index af15e17f50..382bc44726 100644 --- a/tools/editor/plugins/animation_tree_editor_plugin.cpp +++ b/tools/editor/plugins/animation_tree_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -1193,7 +1193,7 @@ void AnimationTreeEditor::_add_menu_item(int p_item) { } else if (p_item == MENU_IMPORT_ANIMATIONS) { file_op = MENU_IMPORT_ANIMATIONS; - file_dialog->set_mode(FileDialog::MODE_OPEN_FILE); + file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); file_dialog->popup_centered_ratio(); } else { @@ -1458,7 +1458,7 @@ AnimationTreeEditor::AnimationTreeEditor() { edit_check->hide();; edit_check->connect("pressed", this,"_edit_dialog_changed"); - file_dialog = memnew( FileDialog ); + file_dialog = memnew( EditorFileDialog ); file_dialog->set_enable_multiple_selection(true); file_dialog->set_current_dir(Globals::get_singleton()->get_resource_path()); add_child(file_dialog); diff --git a/tools/editor/plugins/animation_tree_editor_plugin.h b/tools/editor/plugins/animation_tree_editor_plugin.h index 21b31863b6..bd29530c7a 100644 --- a/tools/editor/plugins/animation_tree_editor_plugin.h +++ b/tools/editor/plugins/animation_tree_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -79,7 +79,7 @@ class AnimationTreeEditor : public Control { Button *edit_button; Button *filter_button; CheckButton *edit_check; - FileDialog* file_dialog; + EditorFileDialog* file_dialog; int file_op; void _popup_edit_dialog(); diff --git a/tools/editor/plugins/baked_light_baker.cpp b/tools/editor/plugins/baked_light_baker.cpp index 42a185b7c2..4599dbfb54 100644 --- a/tools/editor/plugins/baked_light_baker.cpp +++ b/tools/editor/plugins/baked_light_baker.cpp @@ -1233,7 +1233,7 @@ float BakedLightBaker::_throw_ray(ThreadStack& thread_stack,bool p_bake_direct,c if (dist<r) { //avoid accumulaiton of light on corners //plot_light=plot_light.linear_interpolate(Color(0,0,0,0),1.0-sd/plot_size*plot_size); - skip-true; + skip=true; } else { @@ -2127,6 +2127,7 @@ void BakedLightBaker::_stop_thread() { bake_thread_exit=true; for(int i=0;i<threads.size();i++) { Thread::wait_to_finish(threads[i]); + memdelete(threads[i]); } threads.clear(); } diff --git a/tools/editor/plugins/baked_light_editor_plugin.cpp b/tools/editor/plugins/baked_light_editor_plugin.cpp index 0f02899dc2..26524b2437 100644 --- a/tools/editor/plugins/baked_light_editor_plugin.cpp +++ b/tools/editor/plugins/baked_light_editor_plugin.cpp @@ -180,7 +180,7 @@ void BakedLightEditor::_bake_pressed() { ERR_FAIL_COND(!node); if (node->get_baked_light().is_null()) { err_dialog->set_text("BakedLightInstance does not contain a BakedLight resource."); - err_dialog->popup_centered(Size2(350,70)); + err_dialog->popup_centered_minsize(); button_bake->set_pressed(false); return; } @@ -242,7 +242,7 @@ void BakedLightEditor::_bake_lightmaps() { if (err) { err_dialog->set_text("Error baking to lightmaps!\nMake sure that a bake has just\n happened and that lightmaps are\n configured. "); - err_dialog->popup_centered(Size2(350,70)); + err_dialog->popup_centered_minsize(); return; } diff --git a/tools/editor/plugins/camera_editor_plugin.cpp b/tools/editor/plugins/camera_editor_plugin.cpp index aa7562b17e..08ed2c745d 100644 --- a/tools/editor/plugins/camera_editor_plugin.cpp +++ b/tools/editor/plugins/camera_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/plugins/camera_editor_plugin.h b/tools/editor/plugins/camera_editor_plugin.h index 5529b32e56..afb8f9415d 100644 --- a/tools/editor/plugins/camera_editor_plugin.h +++ b/tools/editor/plugins/camera_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/plugins/canvas_item_editor_plugin.cpp b/tools/editor/plugins/canvas_item_editor_plugin.cpp index 599160eb46..e3f4edf967 100644 --- a/tools/editor/plugins/canvas_item_editor_plugin.cpp +++ b/tools/editor/plugins/canvas_item_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,18 +36,185 @@ #include "globals.h" #include "os/input.h" #include "tools/editor/editor_settings.h" +#include "scene/gui/grid_container.h" + +class SnapDialog : public ConfirmationDialog { + + OBJ_TYPE(SnapDialog,ConfirmationDialog); + +friend class CanvasItemEditor; + + SpinBox *grid_offset_x; + SpinBox *grid_offset_y; + SpinBox *grid_step_x; + SpinBox *grid_step_y; + SpinBox *rotation_offset; + SpinBox *rotation_step; + +public: + SnapDialog() : ConfirmationDialog() { + const int SPIN_BOX_GRID_RANGE = 256; + const int SPIN_BOX_ROTATION_RANGE = 360; + Label *label; + VBoxContainer *container; + GridContainer *child_container; + + set_title("Configure Snap"); + get_ok()->set_text("Close"); + + container = memnew( VBoxContainer ); + add_child(container); + set_child_rect(container); + + child_container = memnew( GridContainer ); + child_container->set_columns(3); + container->add_child(child_container); + + label = memnew( Label ); + label->set_text("Grid Offset:"); + child_container->add_child(label); + label->set_h_size_flags(SIZE_EXPAND_FILL); + + grid_offset_x = memnew( SpinBox ); + grid_offset_x->set_min(-SPIN_BOX_GRID_RANGE); + grid_offset_x->set_max(SPIN_BOX_GRID_RANGE); + grid_offset_x->set_suffix("px"); + child_container->add_child(grid_offset_x); + + grid_offset_y = memnew( SpinBox ); + grid_offset_y->set_min(-SPIN_BOX_GRID_RANGE); + grid_offset_y->set_max(SPIN_BOX_GRID_RANGE); + grid_offset_y->set_suffix("px"); + child_container->add_child(grid_offset_y); + + label = memnew( Label ); + label->set_text("Grid Step:"); + child_container->add_child(label); + label->set_h_size_flags(SIZE_EXPAND_FILL); + + grid_step_x = memnew( SpinBox ); + grid_step_x->set_min(-SPIN_BOX_GRID_RANGE); + grid_step_x->set_max(SPIN_BOX_GRID_RANGE); + grid_step_x->set_suffix("px"); + child_container->add_child(grid_step_x); + + grid_step_y = memnew( SpinBox ); + grid_step_y->set_min(-SPIN_BOX_GRID_RANGE); + grid_step_y->set_max(SPIN_BOX_GRID_RANGE); + grid_step_y->set_suffix("px"); + child_container->add_child(grid_step_y); + + container->add_child( memnew( HSeparator ) ); + + child_container = memnew( GridContainer ); + child_container->set_columns(2); + container->add_child(child_container); + + label = memnew( Label ); + label->set_text("Rotation Offset:"); + child_container->add_child(label); + label->set_h_size_flags(SIZE_EXPAND_FILL); + + rotation_offset = memnew( SpinBox ); + rotation_offset->set_min(-SPIN_BOX_ROTATION_RANGE); + rotation_offset->set_max(SPIN_BOX_ROTATION_RANGE); + rotation_offset->set_suffix("deg"); + child_container->add_child(rotation_offset); + + label = memnew( Label ); + label->set_text("Rotation Step:"); + child_container->add_child(label); + label->set_h_size_flags(SIZE_EXPAND_FILL); + + rotation_step = memnew( SpinBox ); + rotation_step->set_min(-SPIN_BOX_ROTATION_RANGE); + rotation_step->set_max(SPIN_BOX_ROTATION_RANGE); + rotation_step->set_suffix("deg"); + child_container->add_child(rotation_step); + } + + void set_fields(const Point2 p_grid_offset, const Size2 p_grid_step, const float p_rotation_offset, const float p_rotation_step) { + grid_offset_x->set_val(p_grid_offset.x); + grid_offset_y->set_val(p_grid_offset.y); + grid_step_x->set_val(p_grid_step.x); + grid_step_y->set_val(p_grid_step.y); + rotation_offset->set_val(p_rotation_offset * (180 / Math_PI)); + rotation_step->set_val(p_rotation_step * (180 / Math_PI)); + } + + void get_fields(Point2 &p_grid_offset, Size2 &p_grid_step, float &p_rotation_offset, float &p_rotation_step) { + p_grid_offset.x = grid_offset_x->get_val(); + p_grid_offset.y = grid_offset_y->get_val(); + p_grid_step.x = grid_step_x->get_val(); + p_grid_step.y = grid_step_y->get_val(); + p_rotation_offset = rotation_offset->get_val() / (180 / Math_PI); + p_rotation_step = rotation_step->get_val() / (180 / Math_PI); + } +}; + void CanvasItemEditor::_unhandled_key_input(const InputEvent& p_ev) { if (!is_visible()) return; + if (p_ev.key.mod.control) + // prevent to change tool mode when control key is pressed + return; if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_Q) _tool_select(TOOL_SELECT); if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_W) _tool_select(TOOL_MOVE); if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_E) _tool_select(TOOL_ROTATE); - if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_V && drag==DRAG_ALL && can_move_pivot) - drag=DRAG_PIVOT; + if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_V && drag==DRAG_NONE && can_move_pivot) { + if (p_ev.key.mod.shift) { + //move drag pivot + drag=DRAG_PIVOT; + } else if (!Input::get_singleton()->is_mouse_button_pressed(0)) { + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + Vector2 mouse_pos = viewport->get_local_mouse_pos(); + if (selection.size() && viewport->get_rect().has_point(mouse_pos)) { + //just in case, make it work if over viewport + mouse_pos=transform.affine_inverse().xform(mouse_pos); + mouse_pos=snap_point(mouse_pos); + + undo_redo->create_action("Move Pivot"); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + Node2D *n2d = E->get()->cast_to<Node2D>(); + + if (n2d && n2d->edit_has_pivot()) { + + Vector2 offset = n2d->edit_get_pivot(); + Vector2 gpos = n2d->get_global_pos(); + + Vector2 motion_ofs = gpos-mouse_pos; + + undo_redo->add_do_method(n2d,"set_global_pos",mouse_pos); + undo_redo->add_do_method(n2d,"edit_set_pivot",offset+n2d->get_global_transform().affine_inverse().basis_xform(motion_ofs)); + undo_redo->add_undo_method(n2d,"set_global_pos",gpos); + undo_redo->add_undo_method(n2d,"edit_set_pivot",offset); + for(int i=0;i<n2d->get_child_count();i++) { + Node2D *n2dc = n2d->get_child(i)->cast_to<Node2D>(); + if (!n2dc) + continue; + + undo_redo->add_do_method(n2dc,"set_global_pos",n2dc->get_global_pos()); + undo_redo->add_undo_method(n2dc,"set_global_pos",n2dc->get_global_pos()); + + } + + } + + } + + undo_redo->commit_action(); + } + + } + } } @@ -75,9 +242,24 @@ Object *CanvasItemEditor::_get_editor_data(Object *p_what) { return memnew( CanvasItemEditorSelectedItem ); } -bool CanvasItemEditor::is_snap_active() const { +inline float _snap_scalar(float p_offset, float p_step, bool p_snap_relative, float p_target, float p_start) { + float offset = p_snap_relative ? p_start : p_offset; + return p_step != 0 ? Math::stepify(p_target - offset, p_step) + offset : p_target; +} + +Vector2 CanvasItemEditor::snap_point(Vector2 p_target, Vector2 p_start) const { + if (snap_grid) { + p_target.x = _snap_scalar(snap_offset.x, snap_step.x, snap_relative, p_target.x, p_start.x); + p_target.y = _snap_scalar(snap_offset.y, snap_step.y, snap_relative, p_target.y, p_start.y); + } + if (snap_pixel) + p_target = p_target.snapped(Size2(1, 1)); - return edit_menu->get_popup()->is_item_checked(edit_menu->get_popup()->get_item_index(SNAP_USE)); + return p_target; +} + +float CanvasItemEditor::snap_angle(float p_target, float p_start) const { + return snap_rotation ? _snap_scalar(snap_rotation_offset, snap_rotation_step, snap_relative, p_target, p_start) : p_target; } Dictionary CanvasItemEditor::get_state() const { @@ -86,9 +268,15 @@ Dictionary CanvasItemEditor::get_state() const { state["zoom"]=zoom; state["ofs"]=Point2(h_scroll->get_val(),v_scroll->get_val()); // state["ofs"]=-transform.get_origin(); - state["use_snap"]=is_snap_active(); - state["snap"]=snap; - state["pixel_snap"]=pixel_snap; + state["snap_offset"]=snap_offset; + state["snap_step"]=snap_step; + state["snap_rotation_offset"]=snap_rotation_offset; + state["snap_rotation_step"]=snap_rotation_step; + state["snap_grid"]=snap_grid; + state["snap_show_grid"]=snap_show_grid; + state["snap_rotation"]=snap_rotation; + state["snap_relative"]=snap_relative; + state["snap_pixel"]=snap_pixel; return state; } void CanvasItemEditor::set_state(const Dictionary& p_state){ @@ -105,19 +293,50 @@ void CanvasItemEditor::set_state(const Dictionary& p_state){ v_scroll->set_val(ofs.y); } - if (state.has("use_snap")) { + if (state.has("snap_step")) { + snap_step=state["snap_step"]; + } + + if (state.has("snap_offset")) { + snap_offset=state["snap_offset"]; + } + + if (state.has("snap_rotation_step")) { + snap_rotation_step=state["snap_rotation_step"]; + } + + if (state.has("snap_rotation_offset")) { + snap_rotation_offset=state["snap_rotation_offset"]; + } + + if (state.has("snap_grid")) { + snap_grid=state["snap_grid"]; int idx = edit_menu->get_popup()->get_item_index(SNAP_USE); - edit_menu->get_popup()->set_item_checked(idx,state["use_snap"]); + edit_menu->get_popup()->set_item_checked(idx,snap_grid); + } + + if (state.has("snap_show_grid")) { + snap_show_grid=state["snap_show_grid"]; + int idx = edit_menu->get_popup()->get_item_index(SNAP_SHOW_GRID); + edit_menu->get_popup()->set_item_checked(idx,snap_show_grid); } - if (state.has("snap")) { - snap=state["snap"]; + if (state.has("snap_rotation")) { + snap_rotation=state["snap_rotation"]; + int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); + edit_menu->get_popup()->set_item_checked(idx,snap_rotation); } - if (state.has("pixel_snap")) { - pixel_snap=state["pixel_snap"]; + if (state.has("snap_relative")) { + snap_relative=state["snap_relative"]; + int idx = edit_menu->get_popup()->get_item_index(SNAP_RELATIVE); + edit_menu->get_popup()->set_item_checked(idx,snap_relative); + } + + if (state.has("snap_pixel")) { + snap_pixel=state["snap_pixel"]; int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_PIXEL); - edit_menu->get_popup()->set_item_checked(idx,pixel_snap); + edit_menu->get_popup()->set_item_checked(idx,snap_pixel); } } @@ -228,6 +447,47 @@ CanvasItem* CanvasItemEditor::_select_canvas_item_at_pos(const Point2& p_pos,Nod return NULL; } +void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform, Vector<_SelectResult> &r_items) { + if (!p_node) + return; + if (p_node->cast_to<Viewport>()) + return; + + CanvasItem *c=p_node->cast_to<CanvasItem>(); + + for (int i=p_node->get_child_count()-1;i>=0;i--) { + + if (c && !c->is_set_as_toplevel()) + _find_canvas_items_at_pos(p_pos,p_node->get_child(i),p_parent_xform * c->get_transform(),p_canvas_xform, r_items); + else { + CanvasLayer *cl = p_node->cast_to<CanvasLayer>(); + if (cl) + return; + _find_canvas_items_at_pos(p_pos,p_node->get_child(i),transform ,cl ? cl->get_transform() : p_canvas_xform, r_items); //use base transform + } + } + + + if (c && c->is_visible() && !c->has_meta("_edit_lock_")) { + + Rect2 rect = c->get_item_rect(); + Point2 local_pos = (p_parent_xform * p_canvas_xform * c->get_transform()).affine_inverse().xform(p_pos); + + + if (rect.has_point(local_pos)) { + Node2D *node=c->cast_to<Node2D>(); + + _SelectResult res; + res.item=c; + res.z=node?node->get_z():0; + res.has_z=node; + r_items.push_back(res); + } + + } + + return; +} void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2& p_rect,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform,List<CanvasItem*> *r_items) { @@ -270,6 +530,96 @@ void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2& p_rect,Node* p_no } +bool CanvasItemEditor::_select(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag) { + + if (p_append) { + //additive selection + + if (!item) { + + if (p_drag) { + drag_from=transform.affine_inverse().xform(p_click_pos); + + box_selecting=true; + box_selecting_to=drag_from; + } + + return false; //nothing to add + } + + if (editor_selection->is_selected(item)) { + //already in here, erase it + editor_selection->remove_node(item); + //_remove_canvas_item(c); + + viewport->update(); + return false; + + } + _append_canvas_item(item); + viewport->update(); + + } else { + //regular selection + + if (!item) { + //clear because nothing clicked + editor_selection->clear();; + + if (p_drag) { + drag_from=transform.affine_inverse().xform(p_click_pos); + + box_selecting=true; + box_selecting_to=drag_from; + } + + viewport->update(); + return false; + } + + if (!editor_selection->is_selected(item)) { + //select a new one and clear previous selection + editor_selection->clear(); + editor_selection->add_node(item); + //reselect + if (get_tree()->is_editor_hint()) { + editor->call("edit_node",item); + } + + } + + if (p_drag) { + //prepare to move! + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item || !canvas_item->is_visible()) + continue; + CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + if (!se) + continue; + + se->undo_state=canvas_item->edit_get_state(); + if (canvas_item->cast_to<Node2D>()) + se->undo_pivot=canvas_item->cast_to<Node2D>()->edit_get_pivot(); + + } + + drag=DRAG_ALL; + drag_from=transform.affine_inverse().xform(p_click_pos); + drag_point_from=_find_topleftmost_point(); + } + + viewport->update(); + + return true; + + } +} + void CanvasItemEditor::_key_move(const Vector2& p_dir, bool p_snap, KeyMoveMODE p_move_mode) { @@ -286,9 +636,7 @@ void CanvasItemEditor::_key_move(const Vector2& p_dir, bool p_snap, KeyMoveMODE for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); if (!se) @@ -300,7 +648,7 @@ void CanvasItemEditor::_key_move(const Vector2& p_dir, bool p_snap, KeyMoveMODE Vector2 drag = p_dir; if (p_snap) - drag*=snap; + drag*=snap_step; undo_redo->add_undo_method(canvas_item,"edit_set_state",canvas_item->edit_get_state()); @@ -347,9 +695,7 @@ Point2 CanvasItemEditor::_find_topleftmost_point() { for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; @@ -377,9 +723,7 @@ int CanvasItemEditor::get_item_count() { for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; ic++; @@ -398,9 +742,7 @@ CanvasItem *CanvasItemEditor::get_single_item() { for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; if (single_item) @@ -554,6 +896,11 @@ void CanvasItemEditor::_append_canvas_item(CanvasItem *c) { } +void CanvasItemEditor::_snap_changed() { + ((SnapDialog *)snap_dialog)->get_fields(snap_offset, snap_step, snap_rotation_offset, snap_rotation_step); + viewport->update(); +} + void CanvasItemEditor::_dialog_value_changed(double) { if (updating_value_dialog) @@ -561,11 +908,6 @@ void CanvasItemEditor::_dialog_value_changed(double) { switch(last_option) { - case SNAP_CONFIGURE: { - - snap=dialog_val->get_val(); - viewport->update(); - } break; case ZOOM_SET: { zoom=dialog_val->get_val()/100.0; @@ -577,6 +919,24 @@ void CanvasItemEditor::_dialog_value_changed(double) { } } +void CanvasItemEditor::_selection_result_pressed(int p_result) { + + if (selection_results.size() <= p_result) + return; + + CanvasItem *item=selection_results[p_result].item; + + if (item) + _select(item, Point2(), additive_selection, false); +} + +void CanvasItemEditor::_selection_menu_hide() { + + selection_results.clear(); + selection_menu->clear(); + selection_menu->set_size(Vector2(0, 0)); +} + bool CanvasItemEditor::get_remove_list(List<Node*> *p_list) { @@ -639,7 +999,60 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { if (b.button_index==BUTTON_RIGHT) { + if (b.pressed && tool==TOOL_SELECT && b.mod.alt) { + + Point2 click=Point2(b.x,b.y); + + Node* scene = editor->get_edited_scene(); + if (!scene) + return; + + _find_canvas_items_at_pos(click, scene,transform,Matrix32(), selection_results); + + if (selection_results.size() == 1) { + + CanvasItem *item = selection_results[0].item; + selection_results.clear(); + + additive_selection=b.mod.shift; + if (!_select(item, click, additive_selection, false)) + return; + + } else if (!selection_results.empty()) { + selection_results.sort(); + + NodePath root_path = get_tree()->get_edited_scene_root()->get_path(); + StringName root_name = root_path.get_name(root_path.get_name_count()-1); + + for (int i = 0; i < selection_results.size(); i++) { + + CanvasItem *item=selection_results[i].item; + + Ref<Texture> icon; + if (item->has_meta("_editor_icon")) + icon=item->get_meta("_editor_icon"); + else + icon=get_icon( has_icon(item->get_type(),"EditorIcons")?item->get_type():String("Object"),"EditorIcons"); + + String node_path="/"+root_name+"/"+root_path.rel_path_to(item->get_path()); + + selection_menu->add_item(item->get_name()); + selection_menu->set_item_icon(i, icon ); + selection_menu->set_item_metadata(i, node_path); + selection_menu->set_item_tooltip(i,String(item->get_name())+ + "\nType: "+item->get_type()+"\nPath: "+node_path); + } + + additive_selection=b.mod.shift; + + selection_menu->set_global_pos(Vector2( b.global_x, b.global_y )); + selection_menu->popup(); + selection_menu->call_deferred("grab_click_focus"); + + return; + } + } if (get_item_count() > 0 && drag!=DRAG_NONE) { //cancel drag @@ -661,9 +1074,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); @@ -735,9 +1146,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); if (!se) @@ -799,13 +1208,13 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { } - List<BoneList>::Element *Cbone=NULL; //closest + Map<ObjectID,BoneList>::Element *Cbone=NULL; //closest { bone_ik_list.clear(); float closest_dist=1e20; int bone_width = EditorSettings::get_singleton()->get("2d_editor/bone_width"); - for(List<BoneList>::Element *E=bone_list.front();E;E=E->next()) { + for(Map<ObjectID,BoneList>::Element *E=bone_list.front();E;E=E->next()) { if (E->get().from == E->get().to) continue; @@ -943,9 +1352,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); if (!se) @@ -1010,90 +1417,16 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { #if 0 if ( b.pressed ) box_selection_start( click ); #endif - if (b.mod.shift) { //additive selection - - if (!c) { - - drag_from=transform.affine_inverse().xform(click); - - box_selecting=true; - box_selecting_to=drag_from; - return; //nothing to add - } - - if (editor_selection->is_selected(c)) { - //already in here, erase it - editor_selection->remove_node(c); - //_remove_canvas_item(c); - - viewport->update(); - return; - - } - _append_canvas_item(c); - viewport->update(); - } else { - //regular selection - - - - if (!c) { - //clear because nothing clicked - editor_selection->clear();; - - drag_from=transform.affine_inverse().xform(click); - - box_selecting=true; - box_selecting_to=drag_from; - viewport->update(); - return; - } - - if (!editor_selection->is_selected(c)) { - //select a new one and clear previous selection - editor_selection->clear(); - editor_selection->add_node(c); - //reselect - if (get_tree()->is_editor_hint()) { - editor->call("edit_node",c); - } - - } - - //prepare to move! - - List<Node*> &selection = editor_selection->get_selected_node_list(); - - for(List<Node*>::Element *E=selection.front();E;E=E->next()) { - - CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) - continue; - CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); - if (!se) - continue; - - se->undo_state=canvas_item->edit_get_state(); - if (canvas_item->cast_to<Node2D>()) - se->undo_pivot=canvas_item->cast_to<Node2D>()->edit_get_pivot(); - - } - - drag=DRAG_ALL; - drag_from=transform.affine_inverse().xform(click); - drag_point_from=_find_topleftmost_point(); - viewport->update(); - - } + additive_selection=b.mod.shift; + if (!_select(c, click, additive_selection)) + return; } if (p_event.type==InputEvent::MOUSE_MOTION) { - if (!viewport->has_focus()) + if (!viewport->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) viewport->call_deferred("grab_focus"); const InputEventMouseMotion &m=p_event.mouse_motion; @@ -1126,9 +1459,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); if (!se) @@ -1153,39 +1484,21 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { if (drag==DRAG_ROTATE) { Vector2 center = canvas_item->get_global_transform_with_canvas().get_origin(); - - Matrix32 rot; - rot.elements[1] = (dfrom - center).normalized(); - rot.elements[0] = rot.elements[1].tangent(); - float ang = rot.xform_inv(dto-center).atan2(); - canvas_item->edit_rotate(ang); - display_rotate_to = dto; - display_rotate_from = center; + if (Node2D *node = canvas_item->cast_to<Node2D>()) { + Matrix32 rot; + rot.elements[1] = (dfrom - center).normalized(); + rot.elements[0] = rot.elements[1].tangent(); + node->set_rot(snap_angle(rot.xform_inv(dto-center).angle(), node->get_rot())); + display_rotate_to = dto; + display_rotate_from = center; + viewport->update(); + } continue; } - if (pixel_snap || (is_snap_active() && snap>0)) { - - if (drag!=DRAG_ALL) { - dfrom=drag_point_from; - dto=snapify(dto); - } else { - - Vector2 newpos = drag_point_from + (dto-dfrom); - Vector2 disp; - if (!is_snap_active() || snap<1) { - - disp.x = Math::fposmod(newpos.x,1); - disp.y = Math::fposmod(newpos.y,1); - - } else { - disp.x = Math::fposmod(newpos.x,snap); - disp.y = Math::fposmod(newpos.y,snap); - } - dto-=disp; - } - } + dfrom = drag_point_from; + dto = snap_point(dto - (drag == DRAG_ALL ? drag_from - drag_point_from : Vector2(0, 0)), drag_point_from); Vector2 drag_vector = canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dto) - @@ -1293,8 +1606,6 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { - - if (!dragging_bone) { local_rect.pos=begin; @@ -1477,32 +1788,32 @@ void CanvasItemEditor::_viewport_draw() { _update_scrollbars(); RID ci=viewport->get_canvas_item(); - if (snap>0 && is_snap_active() && true ) { - + if (snap_show_grid) { Size2 s = viewport->get_size(); - int last_cell; Matrix32 xform = transform.affine_inverse(); - for(int i=0;i<s.width;i++) { - int cell = Math::fast_ftoi(Math::floor(xform.xform(Vector2(i,0)).x/snap)); - if (i==0) + if (snap_step.x!=0) { + for(int i=0;i<s.width;i++) { + int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(i,0)).x-snap_offset.x)/snap_step.x)); + if (i==0) + last_cell=cell; + if (last_cell!=cell) + viewport->draw_line(Point2(i,0),Point2(i,s.height),Color(0.3,0.7,1,0.3)); last_cell=cell; - if (last_cell!=cell) - viewport->draw_line(Point2(i,0),Point2(i,s.height),Color(0.3,0.7,1,0.3)); - last_cell=cell; + } } - for(int i=0;i<s.height;i++) { - - int cell = Math::fast_ftoi(Math::floor(xform.xform(Vector2(0,i)).y/snap)); - if (i==0) + if (snap_step.y!=0) { + for(int i=0;i<s.height;i++) { + int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(0,i)).y-snap_offset.y)/snap_step.y)); + if (i==0) + last_cell=cell; + if (last_cell!=cell) + viewport->draw_line(Point2(0,i),Point2(s.width,i),Color(0.3,0.7,1,0.3)); last_cell=cell; - if (last_cell!=cell) - viewport->draw_line(Point2(0,i),Point2(s.width,i),Color(0.3,0.7,1,0.3)); - last_cell=cell; + } } - } if (viewport->has_focus()) { @@ -1530,9 +1841,7 @@ void CanvasItemEditor::_viewport_draw() { CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); if (!se) @@ -1569,7 +1878,7 @@ void CanvasItemEditor::_viewport_draw() { viewport->draw_line(endpoints[i],endpoints[(i+1)%4],c,2); } - if (single && (tool==TOOL_SELECT || tool == TOOL_MOVE)) { //kind of sucks + if (single && (tool==TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_ROTATE)) { //kind of sucks if (canvas_item->cast_to<Node2D>()) { @@ -1673,7 +1982,7 @@ void CanvasItemEditor::_viewport_draw() { Color bone_ik_color = EditorSettings::get_singleton()->get("2d_editor/bone_ik_color"); Color bone_selected_color = EditorSettings::get_singleton()->get("2d_editor/bone_selected_color"); - for(List<BoneList>::Element*E=bone_list.front();E;E=E->next()) { + for(Map<ObjectID,BoneList>::Element*E=bone_list.front();E;E=E->next()) { E->get().from=Vector2(); E->get().to=Vector2(); @@ -1746,14 +2055,20 @@ void CanvasItemEditor::_notification(int p_what) { List<Node*> &selection = editor_selection->get_selected_node_list(); + bool all_control=true; + bool has_control=false; + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; + if (canvas_item->cast_to<Control>()) + has_control=true; + else + all_control=false; + CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); if (!se) continue; @@ -1770,10 +2085,19 @@ void CanvasItemEditor::_notification(int p_what) { } - for(List<BoneList>::Element *E=bone_list.front();E;E=E->next()) { + bool show_anchor = all_control && has_control; + if (show_anchor != !anchor_menu->is_hidden()) { + if (show_anchor) + anchor_menu->show(); + else + anchor_menu->hide(); + } + + for(Map<ObjectID,BoneList>::Element *E=bone_list.front();E;E=E->next()) { Object *b = ObjectDB::get_instance(E->get().bone); if (!b) { + viewport->update(); break; } @@ -1810,6 +2134,32 @@ void CanvasItemEditor::_notification(int p_what) { ungroup_button->set_icon(get_icon("Ungroup","EditorIcons")); key_insert_button->set_icon(get_icon("Key","EditorIcons")); + + //anchor_menu->add_icon_override("Align Top Left"); + anchor_menu->set_icon(get_icon("Anchor","EditorIcons")); + PopupMenu *p=anchor_menu->get_popup(); + + p->add_icon_item(get_icon("ControlAlignTopLeft","EditorIcons"),"Top Left",ANCHOR_ALIGN_TOP_LEFT); + p->add_icon_item(get_icon("ControlAlignTopRight","EditorIcons"),"Top Right",ANCHOR_ALIGN_TOP_RIGHT); + p->add_icon_item(get_icon("ControlAlignBottomRight","EditorIcons"),"Bottom Right",ANCHOR_ALIGN_BOTTOM_RIGHT); + p->add_icon_item(get_icon("ControlAlignBottomLeft","EditorIcons"),"Bottom Left",ANCHOR_ALIGN_BOTTOM_LEFT); + p->add_separator(); + p->add_icon_item(get_icon("ControlAlignLeftCenter","EditorIcons"),"Center Left",ANCHOR_ALIGN_CENTER_LEFT); + p->add_icon_item(get_icon("ControlAlignTopCenter","EditorIcons"),"Center Top",ANCHOR_ALIGN_CENTER_TOP); + p->add_icon_item(get_icon("ControlAlignRightCenter","EditorIcons"),"Center Right",ANCHOR_ALIGN_CENTER_RIGHT); + p->add_icon_item(get_icon("ControlAlignBottomCenter","EditorIcons"),"Center Bottom",ANCHOR_ALIGN_CENTER_BOTTOM); + p->add_icon_item(get_icon("ControlAlignCenter","EditorIcons"),"Center",ANCHOR_ALIGN_CENTER); + p->add_separator(); + p->add_icon_item(get_icon("ControlAlignLeftWide","EditorIcons"),"Left Wide",ANCHOR_ALIGN_LEFT_WIDE); + p->add_icon_item(get_icon("ControlAlignTopWide","EditorIcons"),"Top Wide",ANCHOR_ALIGN_TOP_WIDE); + p->add_icon_item(get_icon("ControlAlignRightWide","EditorIcons"),"Right Wide",ANCHOR_ALIGN_RIGHT_WIDE); + p->add_icon_item(get_icon("ControlAlignBottomWide","EditorIcons"),"Bottom Wide",ANCHOR_ALIGN_BOTTOM_WIDE); + p->add_icon_item(get_icon("ControlVcenterWide","EditorIcons"),"VCenter Wide ",ANCHOR_ALIGN_VCENTER_WIDE); + p->add_icon_item(get_icon("ControlHcenterWide","EditorIcons"),"HCenter Wide ",ANCHOR_ALIGN_HCENTER_WIDE); + p->add_separator(); + p->add_icon_item(get_icon("ControlAlignWide","EditorIcons"),"Full Rect",ANCHOR_ALIGN_WIDE); + + } if (p_what==NOTIFICATION_READY) { @@ -1875,9 +2225,14 @@ void CanvasItemEditor::_find_canvas_items_span(Node *p_node, Rect2& r_rect, cons if (c->has_meta("_edit_bone_")) { - BoneList bone; - bone.bone=c->get_instance_ID(); - bone_list.push_back(bone); + ObjectID id = c->get_instance_ID(); + if (!bone_list.has(id)) { + BoneList bone; + bone.bone=id; + bone_list[id]=bone; + } + + bone_list[id].last_pass=bone_last_frame; } r_rect.expand_to( xform.xform(rect.pos) ); @@ -1912,11 +2267,26 @@ void CanvasItemEditor::_update_scrollbars() { Rect2 canvas_item_rect=Rect2(Point2(),screen_rect); lock_list.clear();; - bone_list.clear();; + bone_last_frame++; + + if (editor->get_edited_scene()) _find_canvas_items_span(editor->get_edited_scene(),canvas_item_rect,Matrix32()); + List<Map<ObjectID,BoneList>::Element*> bone_to_erase; + + for(Map<ObjectID,BoneList>::Element*E=bone_list.front();E;E=E->next()) { + + if (E->get().last_pass!=bone_last_frame) { + bone_to_erase.push_back(E); + } + } + + while(bone_to_erase.size()) { + bone_list.erase(bone_to_erase.front()->get()); + bone_to_erase.pop_front(); + } //expand area so it's easier to do animations and stuff at 0,0 canvas_item_rect.size+=screen_rect*2; @@ -1995,60 +2365,62 @@ void CanvasItemEditor::_update_scroll(float) { } +void CanvasItemEditor::_set_anchor(Control::AnchorType p_left,Control::AnchorType p_top,Control::AnchorType p_right,Control::AnchorType p_bottom) { + List<Node*> &selection = editor_selection->get_selected_node_list(); -Point2 CanvasItemEditor::snapify(const Point2& p_pos) const { - - bool active=is_snap_active(); - - Vector2 pos = p_pos; - - if (!active || snap<1) { - - if (pixel_snap) { + undo_redo->create_action("Change Anchors"); + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { - pos.x=Math::stepify(pos.x,1); - pos.y=Math::stepify(pos.y,1); - } + Control *c = E->get()->cast_to<Control>(); - return pos; + undo_redo->add_do_method(c,"set_anchor",MARGIN_LEFT,p_left); + undo_redo->add_do_method(c,"set_anchor",MARGIN_TOP,p_top); + undo_redo->add_do_method(c,"set_anchor",MARGIN_RIGHT,p_right); + undo_redo->add_do_method(c,"set_anchor",MARGIN_BOTTOM,p_bottom); + undo_redo->add_undo_method(c,"set_anchor",MARGIN_LEFT,c->get_anchor(MARGIN_LEFT)); + undo_redo->add_undo_method(c,"set_anchor",MARGIN_TOP,c->get_anchor(MARGIN_TOP)); + undo_redo->add_undo_method(c,"set_anchor",MARGIN_RIGHT,c->get_anchor(MARGIN_RIGHT)); + undo_redo->add_undo_method(c,"set_anchor",MARGIN_BOTTOM,c->get_anchor(MARGIN_BOTTOM)); } - - pos.x=Math::stepify(pos.x,snap); - pos.y=Math::stepify(pos.y,snap); - return pos; - + undo_redo->commit_action(); } - void CanvasItemEditor::_popup_callback(int p_op) { last_option=MenuOption(p_op); switch(p_op) { case SNAP_USE: { - + snap_grid = !snap_grid; int idx = edit_menu->get_popup()->get_item_index(SNAP_USE); - edit_menu->get_popup()->set_item_checked( idx,!edit_menu->get_popup()->is_item_checked(0)); + edit_menu->get_popup()->set_item_checked(idx,snap_grid); + } break; + case SNAP_SHOW_GRID: { + snap_show_grid = !snap_show_grid; + int idx = edit_menu->get_popup()->get_item_index(SNAP_SHOW_GRID); + edit_menu->get_popup()->set_item_checked(idx,snap_show_grid); viewport->update(); } break; + case SNAP_USE_ROTATION: { + snap_rotation = !snap_rotation; + int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); + edit_menu->get_popup()->set_item_checked(idx,snap_rotation); + } break; + case SNAP_RELATIVE: { + snap_relative = !snap_relative; + int idx = edit_menu->get_popup()->get_item_index(SNAP_RELATIVE); + edit_menu->get_popup()->set_item_checked(idx,snap_relative); + } break; case SNAP_USE_PIXEL: { - pixel_snap = ! pixel_snap; + snap_pixel = !snap_pixel; int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_PIXEL); - edit_menu->get_popup()->set_item_checked(idx,pixel_snap); + edit_menu->get_popup()->set_item_checked(idx,snap_pixel); } break; case SNAP_CONFIGURE: { - updating_value_dialog=true; - - dialog_label->set_text("Snap (Pixels):"); - dialog_val->set_min(1); - dialog_val->set_step(1); - dialog_val->set_max(4096); - dialog_val->set_val(snap); - value_dialog->popup_centered(Size2(200,85)); - updating_value_dialog=false; - + ((SnapDialog *)snap_dialog)->set_fields(snap_offset, snap_step, snap_rotation_offset, snap_rotation_step); + snap_dialog->popup_centered(Size2(220,160)); } break; case ZOOM_IN: { zoom=zoom*(1.0/0.5); @@ -2092,9 +2464,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; canvas_item->set_meta("_edit_lock_",true); @@ -2109,9 +2479,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; @@ -2129,9 +2497,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; canvas_item->set_meta("_edit_group_",true); @@ -2146,9 +2512,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; canvas_item->set_meta("_edit_group_",Variant()); @@ -2166,9 +2530,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; @@ -2226,6 +2588,56 @@ void CanvasItemEditor::_popup_callback(int p_op) { case SPACE_VERTICAL: { //space_selected_items< proj_vector2_y, compare_items_y >(); } break; + case ANCHOR_ALIGN_TOP_LEFT: { + _set_anchor(ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_BEGIN); + } break; + case ANCHOR_ALIGN_TOP_RIGHT: { + _set_anchor(ANCHOR_END,ANCHOR_BEGIN,ANCHOR_END,ANCHOR_BEGIN); + } break; + case ANCHOR_ALIGN_BOTTOM_LEFT: { + _set_anchor(ANCHOR_BEGIN,ANCHOR_END,ANCHOR_BEGIN,ANCHOR_END); + } break; + case ANCHOR_ALIGN_BOTTOM_RIGHT: { + _set_anchor(ANCHOR_END,ANCHOR_END,ANCHOR_END,ANCHOR_END); + } break; + case ANCHOR_ALIGN_CENTER_LEFT: { + _set_anchor(ANCHOR_BEGIN,ANCHOR_CENTER,ANCHOR_BEGIN,ANCHOR_CENTER); + } break; + case ANCHOR_ALIGN_CENTER_RIGHT: { + + _set_anchor(ANCHOR_END,ANCHOR_CENTER,ANCHOR_END,ANCHOR_CENTER); + } break; + case ANCHOR_ALIGN_CENTER_TOP: { + _set_anchor(ANCHOR_CENTER,ANCHOR_BEGIN,ANCHOR_CENTER,ANCHOR_BEGIN); + } break; + case ANCHOR_ALIGN_CENTER_BOTTOM: { + _set_anchor(ANCHOR_CENTER,ANCHOR_END,ANCHOR_CENTER,ANCHOR_END); + } break; + case ANCHOR_ALIGN_CENTER: { + _set_anchor(ANCHOR_CENTER,ANCHOR_CENTER,ANCHOR_CENTER,ANCHOR_CENTER); + } break; + case ANCHOR_ALIGN_TOP_WIDE: { + _set_anchor(ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_END,ANCHOR_BEGIN); + } break; + case ANCHOR_ALIGN_LEFT_WIDE: { + _set_anchor(ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_END); + } break; + case ANCHOR_ALIGN_RIGHT_WIDE: { + _set_anchor(ANCHOR_END,ANCHOR_BEGIN,ANCHOR_END,ANCHOR_END); + } break; + case ANCHOR_ALIGN_BOTTOM_WIDE: { + _set_anchor(ANCHOR_BEGIN,ANCHOR_END,ANCHOR_END,ANCHOR_END); + } break; + case ANCHOR_ALIGN_VCENTER_WIDE: { + _set_anchor(ANCHOR_CENTER,ANCHOR_BEGIN,ANCHOR_CENTER,ANCHOR_END); + } break; + case ANCHOR_ALIGN_HCENTER_WIDE: { + _set_anchor(ANCHOR_BEGIN,ANCHOR_CENTER,ANCHOR_END,ANCHOR_CENTER); + } break; + case ANCHOR_ALIGN_WIDE: { + _set_anchor(ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_END,ANCHOR_END); + } break; + case ANIM_INSERT_KEY: case ANIM_INSERT_KEY_EXISTING: { @@ -2236,9 +2648,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; if (canvas_item->cast_to<Node2D>()) { @@ -2307,7 +2717,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { } break; case ANIM_INSERT_ROT: { - key_pos = key_rot_button->is_pressed(); + key_rot = key_rot_button->is_pressed(); } break; case ANIM_INSERT_SCALE: { @@ -2348,9 +2758,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; @@ -2400,9 +2808,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; if (canvas_item->cast_to<Node2D>()) { @@ -2528,9 +2934,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { for(List<Node*>::Element *E=selection.front();E;E=E->next()) { CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item) - continue; - if (!canvas_item->is_visible()) + if (!canvas_item || !canvas_item->is_visible()) continue; @@ -2604,6 +3008,9 @@ void CanvasItemEditor::_bind_methods() { ObjectTypeDB::bind_method("_unhandled_key_input",&CanvasItemEditor::_unhandled_key_input); ObjectTypeDB::bind_method("_viewport_draw",&CanvasItemEditor::_viewport_draw); ObjectTypeDB::bind_method("_viewport_input_event",&CanvasItemEditor::_viewport_input_event); + ObjectTypeDB::bind_method("_snap_changed",&CanvasItemEditor::_snap_changed); + ObjectTypeDB::bind_method(_MD("_selection_result_pressed"),&CanvasItemEditor::_selection_result_pressed); + ObjectTypeDB::bind_method(_MD("_selection_menu_hide"),&CanvasItemEditor::_selection_menu_hide); ADD_SIGNAL( MethodInfo("item_lock_status_changed") ); ADD_SIGNAL( MethodInfo("item_group_status_changed") ); @@ -2683,6 +3090,16 @@ void CanvasItemEditor::add_control_to_menu_panel(Control *p_control) { hb->add_child(p_control); } +HSplitContainer *CanvasItemEditor::get_palette_split() { + + return palette_split; +} + +VSplitContainer *CanvasItemEditor::get_bottom_split() { + + return bottom_split; +} + CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { tool = TOOL_SELECT; @@ -2697,15 +3114,24 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { add_child( hb ); hb->set_area_as_parent_rect(); + bottom_split = memnew( VSplitContainer ); + bottom_split->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(bottom_split); + + palette_split = memnew( HSplitContainer); + palette_split->set_v_size_flags(SIZE_EXPAND_FILL); + bottom_split->add_child(palette_split); + Control *vp_base = memnew (Control); - add_child(vp_base); vp_base->set_v_size_flags(SIZE_EXPAND_FILL); + palette_split->add_child(vp_base); Control *vp = memnew (Control); vp_base->add_child(vp); vp->set_area_as_parent_rect(); vp->add_child(p_editor->get_scene_root()); + viewport = memnew( Control ); vp_base->add_child(viewport); viewport->set_area_as_parent_rect(); @@ -2735,7 +3161,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { hb->add_child(select_button); select_button->connect("pressed",this,"_tool_select",make_binds(TOOL_SELECT)); select_button->set_pressed(true); - select_button->set_tooltip("Select Mode (Q)\n"+keycode_get_string(KEY_MASK_CMD)+"Drag: Rotate\nAlt+Drag: Move\nPress 'v' to Move Pivot (while moving)"); + select_button->set_tooltip("Select Mode (Q)\n"+keycode_get_string(KEY_MASK_CMD)+"Drag: Rotate\nAlt+Drag: Move\nPress 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving)."); move_button = memnew( ToolButton ); move_button->set_toggle_mode(true); @@ -2790,6 +3216,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { PopupMenu *p; p = edit_menu->get_popup(); p->add_check_item("Use Snap",SNAP_USE); + p->add_check_item("Show Grid",SNAP_SHOW_GRID); + p->add_check_item("Use Rotation Snap",SNAP_USE_ROTATION); + p->add_check_item("Snap Relative",SNAP_RELATIVE); p->add_item("Configure Snap..",SNAP_CONFIGURE); p->add_separator(); p->add_check_item("Use Pixel Snap",SNAP_USE_PIXEL); @@ -2829,6 +3258,14 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p->add_item("Center Selection", VIEW_CENTER_TO_SELECTION, KEY_F); p->add_item("Frame Selection", VIEW_FRAME_TO_SELECTION, KEY_MASK_CMD|KEY_F); + anchor_menu = memnew( MenuButton ); + anchor_menu->set_text("Anchor"); + hb->add_child(anchor_menu); + anchor_menu->get_popup()->connect("item_pressed", this,"_popup_callback"); + anchor_menu->hide(); + + //p = anchor_menu->get_popup(); + animation_hb = memnew( HBoxContainer ); @@ -2878,7 +3315,11 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p->add_separator(); p->add_item("Copy Pose",ANIM_COPY_POSE); p->add_item("Paste Pose",ANIM_PASTE_POSE); - p->add_item("Clear Pose",ANIM_CLEAR_POSE,KEY_MASK_ALT|KEY_K); + p->add_item("Clear Pose",ANIM_CLEAR_POSE,KEY_MASK_SHIFT|KEY_K); + + snap_dialog = memnew( SnapDialog ); + snap_dialog->connect("confirmed",this,"_snap_changed"); + add_child(snap_dialog); value_dialog = memnew( AcceptDialog ); value_dialog->set_title("Set a Value"); @@ -2899,12 +3340,25 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { dialog_val->connect("value_changed",this,"_dialog_value_changed"); select_sb = Ref<StyleBoxTexture>( memnew( StyleBoxTexture) ); + selection_menu = memnew( PopupMenu ); + add_child(selection_menu); + selection_menu->set_custom_minimum_size(Vector2(100, 0)); + selection_menu->connect("item_pressed", this, "_selection_result_pressed"); + selection_menu->connect("popup_hide", this, "_selection_menu_hide"); + key_pos=true; key_rot=true; key_scale=false; zoom=1; - snap=10; + snap_offset=Vector2(0, 0); + snap_step=Vector2(10, 10); + snap_rotation_offset=0; + snap_rotation_step=15 / (180 / Math_PI); + snap_grid=false; + snap_show_grid=false; + snap_rotation=false; + snap_pixel=false; updating_value_dialog=false; box_selecting=false; //zoom=0.5; @@ -2912,8 +3366,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { editor->get_animation_editor()->connect("keying_changed",this,"_keying_changed"); set_process_unhandled_key_input(true); can_move_pivot=false; - pixel_snap=false; drag=DRAG_NONE; + bone_last_frame=0; + additive_selection=false; } CanvasItemEditor *CanvasItemEditor::singleton=NULL; diff --git a/tools/editor/plugins/canvas_item_editor_plugin.h b/tools/editor/plugins/canvas_item_editor_plugin.h index 15ac7b1bb3..b96d36f7dc 100644 --- a/tools/editor/plugins/canvas_item_editor_plugin.h +++ b/tools/editor/plugins/canvas_item_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -75,6 +75,9 @@ class CanvasItemEditor : public VBoxContainer { enum MenuOption { SNAP_USE, + SNAP_SHOW_GRID, + SNAP_USE_ROTATION, + SNAP_RELATIVE, SNAP_CONFIGURE, SNAP_USE_PIXEL, ZOOM_IN, @@ -87,6 +90,23 @@ class CanvasItemEditor : public VBoxContainer { UNGROUP_SELECTED, ALIGN_HORIZONTAL, ALIGN_VERTICAL, + ANCHOR_ALIGN_TOP_LEFT, + ANCHOR_ALIGN_TOP_RIGHT, + ANCHOR_ALIGN_BOTTOM_LEFT, + ANCHOR_ALIGN_BOTTOM_RIGHT, + ANCHOR_ALIGN_CENTER_LEFT, + ANCHOR_ALIGN_CENTER_RIGHT, + ANCHOR_ALIGN_CENTER_TOP, + ANCHOR_ALIGN_CENTER_BOTTOM, + ANCHOR_ALIGN_CENTER, + ANCHOR_ALIGN_TOP_WIDE, + ANCHOR_ALIGN_LEFT_WIDE, + ANCHOR_ALIGN_RIGHT_WIDE, + ANCHOR_ALIGN_BOTTOM_WIDE, + ANCHOR_ALIGN_VCENTER_WIDE, + ANCHOR_ALIGN_HCENTER_WIDE, + ANCHOR_ALIGN_WIDE, + SPACE_HORIZONTAL, SPACE_VERTICAL, EXPAND_TO_PARENT, @@ -130,6 +150,7 @@ class CanvasItemEditor : public VBoxContainer { }; EditorSelection *editor_selection; + bool additive_selection; Tool tool; bool first_update; @@ -143,8 +164,15 @@ class CanvasItemEditor : public VBoxContainer { Matrix32 transform; float zoom; - int snap; - bool pixel_snap; + Vector2 snap_offset; + Vector2 snap_step; + float snap_rotation_step; + float snap_rotation_offset; + bool snap_grid; + bool snap_show_grid; + bool snap_rotation; + bool snap_relative; + bool snap_pixel; bool box_selecting; Point2 box_selecting_to; bool key_pos; @@ -156,6 +184,18 @@ class CanvasItemEditor : public VBoxContainer { MenuOption last_option; + struct _SelectResult { + + CanvasItem* item; + float z; + bool has_z; + _FORCE_INLINE_ bool operator<(const _SelectResult& p_rr) const { + return has_z && p_rr.has_z ? p_rr.z < z : p_rr.has_z; + } + }; + + Vector<_SelectResult> selection_results; + struct LockList { Point2 pos; bool lock; @@ -171,9 +211,12 @@ class CanvasItemEditor : public VBoxContainer { Vector2 from; Vector2 to; ObjectID bone; + uint64_t last_pass; }; - List<BoneList> bone_list; + uint64_t bone_last_frame; + Map<ObjectID,BoneList> bone_list; + Matrix32 bone_orig_xform; struct BoneIK { @@ -212,12 +255,15 @@ class CanvasItemEditor : public VBoxContainer { MenuButton *view_menu; HBoxContainer *animation_hb; MenuButton *animation_menu; + MenuButton *anchor_menu; Button *key_loc_button; Button *key_rot_button; Button *key_scale_button; Button *key_insert_button; + PopupMenu *selection_menu; + //PopupMenu *popup; DragType drag; Point2 drag_from; @@ -245,8 +291,13 @@ class CanvasItemEditor : public VBoxContainer { int handle_len; CanvasItem* _select_canvas_item_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform); + void _find_canvas_items_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform, Vector<_SelectResult> &r_items); void _find_canvas_items_at_rect(const Rect2& p_rect,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform,List<CanvasItem*> *r_items); + bool _select(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag=true); + + ConfirmationDialog *snap_dialog; + AcceptDialog *value_dialog; Label *dialog_label; SpinBox *dialog_val; @@ -261,7 +312,6 @@ class CanvasItemEditor : public VBoxContainer { DragType _find_drag_type(const Matrix32& p_xform, const Rect2& p_local_rect, const Point2& p_click, Vector2& r_point); - Point2 snapify(const Point2& p_pos) const; void _popup_callback(int p_op); bool updating_scroll; void _update_scroll(float); @@ -271,6 +321,10 @@ class CanvasItemEditor : public VBoxContainer { void _append_canvas_item(CanvasItem *p_item); void _dialog_value_changed(double); + void _snap_changed(); + void _selection_result_pressed(int); + void _selection_menu_hide(); + UndoRedo *undo_redo; Point2 _find_topleftmost_point(); @@ -289,6 +343,12 @@ class CanvasItemEditor : public VBoxContainer { void _viewport_input_event(const InputEvent& p_event); void _viewport_draw(); + + void _set_anchor(Control::AnchorType p_left,Control::AnchorType p_top,Control::AnchorType p_right,Control::AnchorType p_bottom); + + HSplitContainer *palette_split; + VSplitContainer *bottom_split; + friend class CanvasItemEditorPlugin; protected: @@ -330,8 +390,8 @@ protected: static CanvasItemEditor *singleton; public: - bool is_snap_active() const; - int get_snap() const { return snap; } + Vector2 snap_point(Vector2 p_target, Vector2 p_start = Vector2(0, 0)) const; + float snap_angle(float p_target, float p_start = 0) const; Matrix32 get_canvas_transform() const { return transform; } @@ -341,6 +401,9 @@ public: void add_control_to_menu_panel(Control *p_control); + HSplitContainer *get_palette_split(); + VSplitContainer *get_bottom_split(); + Control *get_viewport_control() { return viewport; } diff --git a/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp b/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp index 6bae0d2fd0..8eea723126 100644 --- a/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp +++ b/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp @@ -4,6 +4,7 @@ #include "os/file_access.h" #include "tools/editor/editor_settings.h" + void CollisionPolygon2DEditor::_notification(int p_what) { switch(p_what) { @@ -34,17 +35,6 @@ void CollisionPolygon2DEditor::_node_removed(Node *p_node) { } -Vector2 CollisionPolygon2DEditor::snap_point(const Vector2& p_point) const { - - if (canvas_item_editor->is_snap_active()) { - - return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap()); - - } else { - return p_point; - } -} - void CollisionPolygon2DEditor::_menu_option(int p_option) { switch(p_option) { @@ -98,7 +88,7 @@ bool CollisionPolygon2DEditor::forward_input_event(const InputEvent& p_event) { Vector2 gpoint = Point2(mb.x,mb.y); Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); - cpoint=snap_point(cpoint); + cpoint=canvas_item_editor->snap_point(cpoint); cpoint = node->get_global_transform().affine_inverse().xform(cpoint); Vector<Vector2> poly = node->get_polygon(); @@ -301,7 +291,7 @@ bool CollisionPolygon2DEditor::forward_input_event(const InputEvent& p_event) { Vector2 gpoint = Point2(mm.x,mm.y); Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); - cpoint=snap_point(cpoint); + cpoint=canvas_item_editor->snap_point(cpoint); edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint); canvas_item_editor->get_viewport_control()->update(); @@ -368,6 +358,7 @@ void CollisionPolygon2DEditor::edit(Node *p_collision_polygon) { wip.clear(); wip_active=false; edited_point=-1; + canvas_item_editor->get_viewport_control()->update(); } else { node=NULL; @@ -389,6 +380,7 @@ void CollisionPolygon2DEditor::_bind_methods() { CollisionPolygon2DEditor::CollisionPolygon2DEditor(EditorNode *p_editor) { + node=NULL; canvas_item_editor=NULL; editor=p_editor; undo_redo = editor->get_undo_redo(); @@ -398,11 +390,13 @@ CollisionPolygon2DEditor::CollisionPolygon2DEditor(EditorNode *p_editor) { add_child(button_create); button_create->connect("pressed",this,"_menu_option",varray(MODE_CREATE)); button_create->set_toggle_mode(true); + button_create->set_tooltip("Create a new polygon from scratch"); button_edit = memnew( ToolButton ); add_child(button_edit); button_edit->connect("pressed",this,"_menu_option",varray(MODE_EDIT)); button_edit->set_toggle_mode(true); + button_edit->set_tooltip("Edit existing polygon:\nLMB: Move Point.\nCtrl+LMB: Split Segment.\nRMB: Erase Point."); //add_constant_override("separation",0); diff --git a/tools/editor/plugins/collision_polygon_2d_editor_plugin.h b/tools/editor/plugins/collision_polygon_2d_editor_plugin.h index 052019b6c5..f34405b355 100644 --- a/tools/editor/plugins/collision_polygon_2d_editor_plugin.h +++ b/tools/editor/plugins/collision_polygon_2d_editor_plugin.h @@ -53,7 +53,6 @@ protected: static void _bind_methods(); public: - Vector2 snap_point(const Vector2& p_point) const; bool forward_input_event(const InputEvent& p_event); void edit(Node *p_collision_polygon); CollisionPolygon2DEditor(EditorNode *p_editor); diff --git a/tools/editor/plugins/collision_polygon_editor_plugin.cpp b/tools/editor/plugins/collision_polygon_editor_plugin.cpp index b92acb60f9..60683f4eda 100644 --- a/tools/editor/plugins/collision_polygon_editor_plugin.cpp +++ b/tools/editor/plugins/collision_polygon_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,6 +31,8 @@ #include "os/file_access.h" #include "tools/editor/editor_settings.h" #include "scene/3d/camera.h" +#include "canvas_item_editor_plugin.h" + void CollisionPolygonEditor::_notification(int p_what) { switch(p_what) { @@ -68,19 +70,6 @@ void CollisionPolygonEditor::_node_removed(Node *p_node) { } -Vector2 CollisionPolygonEditor::snap_point(const Vector2& p_point) const { - - return p_point; - /* - if (canvas_item_editor->is_snap_active()) { - - return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap()); - - } else { - return p_point; - } ??? */ -} - void CollisionPolygonEditor::_menu_option(int p_option) { switch(p_option) { @@ -124,6 +113,7 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const return false; Transform gt = node->get_global_transform(); + Transform gi = gt.affine_inverse(); float depth = node->get_depth()*0.5; Vector3 n = gt.basis.get_axis(2).normalized(); Plane p(gt.origin+n*depth,n); @@ -146,9 +136,11 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const if (!p.intersects_ray(ray_from,ray_dir,&spoint)) break; + spoint = gi.xform(spoint); + Vector2 cpoint(spoint.x,spoint.y); - //cpoint=snap_point(cpoint); snap? + cpoint=CanvasItemEditor::get_singleton()->snap_point(cpoint); Vector<Vector2> poly = node->get_polygon(); @@ -360,9 +352,11 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const if (!p.intersects_ray(ray_from,ray_dir,&spoint)) break; + spoint = gi.xform(spoint); + Vector2 cpoint(spoint.x,spoint.y); - //cpoint=snap_point(cpoint); + cpoint=CanvasItemEditor::get_singleton()->snap_point(cpoint); edited_point_pos = cpoint; _polygon_draw(); @@ -544,6 +538,7 @@ void CollisionPolygonEditor::_bind_methods() { CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) { + node=NULL; editor=p_editor; undo_redo = editor->get_undo_redo(); diff --git a/tools/editor/plugins/collision_polygon_editor_plugin.h b/tools/editor/plugins/collision_polygon_editor_plugin.h index 54b0706149..20a0b3c3f6 100644 --- a/tools/editor/plugins/collision_polygon_editor_plugin.h +++ b/tools/editor/plugins/collision_polygon_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -90,7 +90,6 @@ protected: static void _bind_methods(); public: - Vector2 snap_point(const Vector2& p_point) const; virtual bool forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event); void edit(Node *p_collision_polygon); CollisionPolygonEditor(EditorNode *p_editor); diff --git a/tools/editor/plugins/collision_shape_2d_editor_plugin.cpp b/tools/editor/plugins/collision_shape_2d_editor_plugin.cpp new file mode 100644 index 0000000000..7e5d52d17d --- /dev/null +++ b/tools/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -0,0 +1,573 @@ +#include "collision_shape_2d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" + +#include "scene/resources/segment_shape_2d.h" +#include "scene/resources/shape_line_2d.h" +#include "scene/resources/circle_shape_2d.h" +#include "scene/resources/rectangle_shape_2d.h" +#include "scene/resources/capsule_shape_2d.h" +#include "scene/resources/convex_polygon_shape_2d.h" +#include "scene/resources/concave_polygon_shape_2d.h" + +Variant CollisionShape2DEditor::get_handle_value(int idx) const { + + switch ( shape_type ) { + case CAPSULE_SHAPE: { + Ref<CapsuleShape2D> capsule = node->get_shape(); + + if (idx==0) { + return capsule->get_radius(); + } else if (idx==1) { + return capsule->get_height(); + } + + } break; + + case CIRCLE_SHAPE: { + Ref<CircleShape2D> circle = node->get_shape(); + + if (idx==0) { + return circle->get_radius(); + } + + } break; + + case CONCAVE_POLYGON_SHAPE: { + + } break; + + case CONVEX_POLYGON_SHAPE: { + + } break; + + case LINE_SHAPE: { + Ref<LineShape2D> line = node->get_shape(); + + if (idx==0) { + return line->get_d(); + } else { + return line->get_normal(); + } + + } break; + + case RAY_SHAPE: { + Ref<RayShape2D> ray = node->get_shape(); + + if (idx==0) { + return ray->get_length(); + } + + } break; + + case RECTANGLE_SHAPE: { + Ref<RectangleShape2D> rect = node->get_shape(); + + if (idx<2) { + return rect->get_extents().abs(); + } + + } break; + + case SEGMENT_SHAPE: { + Ref<SegmentShape2D> seg = node->get_shape(); + + if (idx==0) { + return seg->get_a(); + } else if (idx==1) { + return seg->get_b(); + } + + } break; + } + + return Variant(); +} + +void CollisionShape2DEditor::set_handle(int idx, Point2& p_point) { + + switch ( shape_type ) { + case CAPSULE_SHAPE: { + if (idx < 2) { + Ref<CapsuleShape2D> capsule = node->get_shape(); + + real_t parameter = Math::abs(p_point[idx]); + + if (idx==0) { + capsule->set_radius(parameter); + } else if (idx==1){ + capsule->set_height(parameter*2 - capsule->get_radius()*2); + } + + canvas_item_editor->get_viewport_control()->update(); + } + + } break; + + case CIRCLE_SHAPE: { + Ref<CircleShape2D> circle = node->get_shape(); + circle->set_radius(p_point.length()); + + canvas_item_editor->get_viewport_control()->update(); + + } break; + + case CONCAVE_POLYGON_SHAPE: { + + } break; + + case CONVEX_POLYGON_SHAPE: { + + } break; + + case LINE_SHAPE: { + if (idx<2) { + Ref<LineShape2D> line = node->get_shape(); + + if (idx==0){ + line->set_d(p_point.length()); + }else{ + line->set_normal(p_point.normalized()); + } + + canvas_item_editor->get_viewport_control()->update(); + } + + + } break; + + case RAY_SHAPE: { + Ref<RayShape2D> ray = node->get_shape(); + + ray->set_length(Math::abs(p_point.y)); + + canvas_item_editor->get_viewport_control()->update(); + + } break; + + case RECTANGLE_SHAPE: { + if (idx<2) { + Ref<RectangleShape2D> rect = node->get_shape(); + + Vector2 extents = rect->get_extents(); + extents[idx] = p_point[idx]; + + rect->set_extents(extents.abs()); + + canvas_item_editor->get_viewport_control()->update(); + } + + } break; + + case SEGMENT_SHAPE: { + if (edit_handle < 2) { + Ref<SegmentShape2D> seg = node->get_shape(); + + if (idx==0) { + seg->set_a(p_point); + } else if (idx==1) { + seg->set_b(p_point); + } + + canvas_item_editor->get_viewport_control()->update(); + } + + } break; + } +} + +void CollisionShape2DEditor::commit_handle(int idx, Variant& p_org) { + + Control* c = canvas_item_editor->get_viewport_control(); + undo_redo->create_action("Set Handle"); + + switch ( shape_type ) { + case CAPSULE_SHAPE: { + Ref<CapsuleShape2D> capsule = node->get_shape(); + + if (idx==0) { + undo_redo->add_do_method(capsule.ptr(),"set_radius",capsule->get_radius()); + undo_redo->add_do_method(c,"update"); + undo_redo->add_undo_method(capsule.ptr(),"set_radius",p_org); + undo_redo->add_do_method(c,"update"); + } else if (idx==1) { + undo_redo->add_do_method(capsule.ptr(),"set_height",capsule->get_height()); + undo_redo->add_do_method(c,"update"); + undo_redo->add_undo_method(capsule.ptr(),"set_height",p_org); + undo_redo->add_undo_method(c,"update"); + } + + } break; + + case CIRCLE_SHAPE: { + Ref<CircleShape2D> circle = node->get_shape(); + + undo_redo->add_do_method(circle.ptr(),"set_radius",circle->get_radius()); + undo_redo->add_do_method(c,"update"); + undo_redo->add_undo_method(circle.ptr(),"set_radius",p_org); + undo_redo->add_undo_method(c,"update"); + + } break; + + case CONCAVE_POLYGON_SHAPE: { + + } break; + + case CONVEX_POLYGON_SHAPE: { + + } break; + + case LINE_SHAPE: { + Ref<LineShape2D> line = node->get_shape(); + + if (idx==0) { + undo_redo->add_do_method(line.ptr(),"set_d",line->get_d()); + undo_redo->add_do_method(c,"update"); + undo_redo->add_undo_method(line.ptr(),"set_d",p_org); + undo_redo->add_undo_method(c,"update"); + } else { + undo_redo->add_do_method(line.ptr(),"set_normal",line->get_normal()); + undo_redo->add_do_method(c,"update"); + undo_redo->add_undo_method(line.ptr(),"set_normal",p_org); + undo_redo->add_undo_method(c,"update"); + } + + } break; + + case RAY_SHAPE: { + Ref<RayShape2D> ray = node->get_shape(); + + undo_redo->add_do_method(ray.ptr(),"set_length",ray->get_length()); + undo_redo->add_do_method(c,"update"); + undo_redo->add_undo_method(ray.ptr(),"set_length",p_org); + undo_redo->add_undo_method(c,"update"); + + } break; + + case RECTANGLE_SHAPE: { + Ref<RectangleShape2D> rect = node->get_shape(); + + undo_redo->add_do_method(rect.ptr(),"set_extents",rect->get_extents()); + undo_redo->add_do_method(c,"update"); + undo_redo->add_undo_method(rect.ptr(),"set_extents",p_org); + undo_redo->add_undo_method(c,"update"); + + } break; + + case SEGMENT_SHAPE: { + Ref<SegmentShape2D> seg = node->get_shape(); + if (idx==0) { + undo_redo->add_do_method(seg.ptr(),"set_a",seg->get_a()); + undo_redo->add_do_method(c,"update"); + undo_redo->add_undo_method(seg.ptr(),"set_a",p_org); + undo_redo->add_undo_method(c,"update"); + } else if (idx==1) { + undo_redo->add_do_method(seg.ptr(),"set_b",seg->get_b()); + undo_redo->add_do_method(c,"update"); + undo_redo->add_undo_method(seg.ptr(),"set_b",p_org); + undo_redo->add_undo_method(c,"update"); + } + + } break; + } + + undo_redo->commit_action(); +} + +bool CollisionShape2DEditor::forward_input_event(const InputEvent& p_event) { + + if (!node) { + return false; + } + + if (!node->get_shape().is_valid()) { + return false; + } + + if (shape_type == -1) { + return false; + } + + switch( p_event.type ) { + case InputEvent::MOUSE_BUTTON: { + const InputEventMouseButton& mb = p_event.mouse_button; + + Matrix32 gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + Point2 gpoint(mb.x,mb.y); + + if (mb.button_index == BUTTON_LEFT) { + if (mb.pressed) { + for (int i = 0; i < handles.size(); i++) { + if (gt.xform(handles[i]).distance_to(gpoint) < 8) { + edit_handle = i; + + break; + } + } + + if (edit_handle==-1) { + pressed = false; + + return false; + } + + original = get_handle_value(edit_handle); + pressed = true; + + return true; + + } else { + if (pressed) { + commit_handle(edit_handle, original); + + edit_handle = -1; + pressed = false; + + return true; + } + } + } + + return false; + + } break; + + case InputEvent::MOUSE_MOTION: { + const InputEventMouseMotion& mm = p_event.mouse_motion; + + if (edit_handle == -1 || !pressed) { + return false; + } + + Point2 gpoint = Point2(mm.x,mm.y); + Point2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); + cpoint = canvas_item_editor->snap_point(cpoint); + cpoint = node->get_global_transform().affine_inverse().xform(cpoint); + + set_handle(edit_handle, cpoint); + + return true; + + } break; + } + + return false; +} + +void CollisionShape2DEditor::_get_current_shape_type() { + + if (!node) { + return; + } + + Ref<Shape2D> s = node->get_shape(); + + if (!s.is_valid()) { + return; + } + + if (s->cast_to<CapsuleShape2D>()) { + shape_type = CAPSULE_SHAPE; + } else if (s->cast_to<CircleShape2D>()) { + shape_type = CIRCLE_SHAPE; + } else if (s->cast_to<ConcavePolygonShape2D>()) { + shape_type = CONCAVE_POLYGON_SHAPE; + } else if (s->cast_to<ConvexPolygonShape2D>()) { + shape_type = CONVEX_POLYGON_SHAPE; + } else if (s->cast_to<LineShape2D>()) { + shape_type = LINE_SHAPE; + } else if (s->cast_to<RayShape2D>()) { + shape_type = RAY_SHAPE; + } else if (s->cast_to<RectangleShape2D>()) { + shape_type = RECTANGLE_SHAPE; + } else if (s->cast_to<SegmentShape2D>()) { + shape_type = SEGMENT_SHAPE; + } else { + shape_type = -1; + } + + canvas_item_editor->get_viewport_control()->update(); +} + +void CollisionShape2DEditor::_canvas_draw() { + + if (!node) { + return; + } + + if (!node->get_shape().is_valid()) { + return; + } + + _get_current_shape_type(); + + if (shape_type == -1) { + return; + } + + Control *c = canvas_item_editor->get_viewport_control(); + Matrix32 gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + Ref<Texture> h = get_icon("EditorHandle","EditorIcons"); + Vector2 size = h->get_size()*0.5; + + handles.clear(); + + switch (shape_type) { + case CAPSULE_SHAPE: { + Ref<CapsuleShape2D> shape = node->get_shape(); + + handles.resize(2); + float radius = shape->get_radius(); + float height = shape->get_height()/2; + + handles[0] = Point2(radius, -height); + handles[1] = Point2(0,-(height + radius)); + + c->draw_texture(h, gt.xform(handles[0])-size); + c->draw_texture(h, gt.xform(handles[1])-size); + + } break; + + case CIRCLE_SHAPE: { + Ref<CircleShape2D> shape = node->get_shape(); + + handles.resize(1); + handles[0] = Point2(shape->get_radius(),0); + + c->draw_texture(h, gt.xform(handles[0])-size); + + } break; + + case CONCAVE_POLYGON_SHAPE: { + + } break; + + case CONVEX_POLYGON_SHAPE: { + + } break; + + case LINE_SHAPE: { + Ref<LineShape2D> shape = node->get_shape(); + + handles.resize(2); + handles[0] = shape->get_normal() * shape->get_d(); + handles[1] = shape->get_normal() * (shape->get_d() + 30.0); + + c->draw_texture(h,gt.xform(handles[0])-size); + c->draw_texture(h,gt.xform(handles[1])-size); + + } break; + + case RAY_SHAPE: { + Ref<RayShape2D> shape = node->get_shape(); + + handles.resize(1); + handles[0] = Point2(0,shape->get_length()); + + c->draw_texture(h,gt.xform(handles[0])-size); + + } break; + + case RECTANGLE_SHAPE: { + Ref<RectangleShape2D> shape = node->get_shape(); + + handles.resize(2); + Vector2 ext = shape->get_extents(); + handles[0] = Point2(ext.x,0); + handles[1] = Point2(0,-ext.y); + + c->draw_texture(h,gt.xform(handles[0])-size); + c->draw_texture(h,gt.xform(handles[1])-size); + + } break; + + case SEGMENT_SHAPE: { + Ref<SegmentShape2D> shape = node->get_shape(); + + handles.resize(2); + handles[0] = shape->get_a(); + handles[1] = shape->get_b(); + + c->draw_texture(h, gt.xform(handles[0])-size); + c->draw_texture(h, gt.xform(handles[1])-size); + + } break; + } +} + +void CollisionShape2DEditor::edit(Node* p_node) { + + if (!canvas_item_editor) { + canvas_item_editor=CanvasItemEditor::get_singleton(); + } + + if (p_node) { + node=p_node->cast_to<CollisionShape2D>(); + + if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw"); + + _get_current_shape_type(); + + } else { + edit_handle = -1; + shape_type = -1; + + if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw"); + + node=NULL; + } + + canvas_item_editor->get_viewport_control()->update(); +} + +void CollisionShape2DEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_canvas_draw",&CollisionShape2DEditor::_canvas_draw); + ObjectTypeDB::bind_method("_get_current_shape_type",&CollisionShape2DEditor::_get_current_shape_type); +} + +CollisionShape2DEditor::CollisionShape2DEditor(EditorNode* p_editor) { + + node = NULL; + canvas_item_editor = NULL; + editor = p_editor; + + undo_redo = p_editor->get_undo_redo(); + + edit_handle = -1; + pressed = false; +} + +void CollisionShape2DEditorPlugin::edit(Object* p_obj) { + + collision_shape_2d_editor->edit(p_obj->cast_to<Node>()); +} + +bool CollisionShape2DEditorPlugin::handles(Object* p_obj) const { + + return p_obj->is_type("CollisionShape2D"); +} + +void CollisionShape2DEditorPlugin::make_visible(bool visible) { + + if (!visible) { + edit(NULL); + } +} + +CollisionShape2DEditorPlugin::CollisionShape2DEditorPlugin(EditorNode* p_node) { + + editor=p_node; + + collision_shape_2d_editor = memnew( CollisionShape2DEditor(p_node) ); + p_node->get_gui_base()->add_child(collision_shape_2d_editor); +} + +CollisionShape2DEditorPlugin::~CollisionShape2DEditorPlugin() { + +} diff --git a/tools/editor/plugins/collision_shape_2d_editor_plugin.h b/tools/editor/plugins/collision_shape_2d_editor_plugin.h new file mode 100644 index 0000000000..75e9b68ea7 --- /dev/null +++ b/tools/editor/plugins/collision_shape_2d_editor_plugin.h @@ -0,0 +1,73 @@ +#ifndef COLLISION_SHAPE_2D_EDITOR_PLUGIN_H +#define COLLISION_SHAPE_2D_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" + +#include "scene/2d/collision_shape_2d.h" + +class CanvasItemEditor; + +class CollisionShape2DEditor : public Control { + OBJ_TYPE(CollisionShape2DEditor, Control); + + enum ShapeType { + CAPSULE_SHAPE, + CIRCLE_SHAPE, + CONCAVE_POLYGON_SHAPE, + CONVEX_POLYGON_SHAPE, + LINE_SHAPE, + RAY_SHAPE, + RECTANGLE_SHAPE, + SEGMENT_SHAPE + }; + + EditorNode* editor; + UndoRedo* undo_redo; + CanvasItemEditor* canvas_item_editor; + CollisionShape2D* node; + + Vector<Point2> handles; + + int shape_type; + int edit_handle; + bool pressed; + Variant original; + + Variant get_handle_value(int idx) const; + void set_handle(int idx, Point2& p_point); + void commit_handle(int idx, Variant& p_org); + + void _get_current_shape_type(); + void _canvas_draw(); + +protected: + static void _bind_methods(); + +public: + bool forward_input_event(const InputEvent& p_event); + void edit(Node* p_node); + + CollisionShape2DEditor(EditorNode* p_editor); +}; + +class CollisionShape2DEditorPlugin : public EditorPlugin { + OBJ_TYPE(CollisionShape2DEditorPlugin, EditorPlugin); + + CollisionShape2DEditor* collision_shape_2d_editor; + EditorNode* editor; + +public: + virtual bool forward_input_event(const InputEvent& p_event) { return collision_shape_2d_editor->forward_input_event(p_event); } + + virtual String get_name() const { return "CollisionShape2D"; } + bool has_main_screen() const { return false; } + virtual void edit(Object* p_obj); + virtual bool handles(Object* p_obj) const; + virtual void make_visible(bool visible); + + CollisionShape2DEditorPlugin(EditorNode* p_editor); + ~CollisionShape2DEditorPlugin(); +}; + +#endif //COLLISION_SHAPE_2D_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/color_ramp_editor_plugin.cpp b/tools/editor/plugins/color_ramp_editor_plugin.cpp new file mode 100644 index 0000000000..df50535402 --- /dev/null +++ b/tools/editor/plugins/color_ramp_editor_plugin.cpp @@ -0,0 +1,83 @@ +/* + * color_ramp_editor_plugin.cpp + */ + +#include "color_ramp_editor_plugin.h" + +ColorRampEditorPlugin::ColorRampEditorPlugin(EditorNode *p_node) { + + editor=p_node; + ramp_editor = memnew( ColorRampEdit ); + + add_custom_control(CONTAINER_CANVAS_EDITOR_BOTTOM,ramp_editor); + //add_custom_control(CONTAINER_SPATIAL_EDITOR_BOTTOM,ramp_editor); + ramp_editor->set_custom_minimum_size(Size2(100, 48)); + ramp_editor->hide(); + ramp_editor->connect("ramp_changed", this, "ramp_changed"); +} + +void ColorRampEditorPlugin::edit(Object *p_object) { + + ColorRamp* color_ramp = p_object->cast_to<ColorRamp>(); + if (!color_ramp) + return; + color_ramp_ref = Ref<ColorRamp>(color_ramp); + ramp_editor->set_points(color_ramp_ref->get_points()); +} + +bool ColorRampEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("ColorRamp"); +} + +void ColorRampEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + ramp_editor->show(); + } else { + ramp_editor->hide(); + } + +} + +void ColorRampEditorPlugin::_ramp_changed() { + + if(color_ramp_ref.is_valid()) + { + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + + //Not sure if I should convert this data to DVector + Vector<float> new_offsets=ramp_editor->get_offsets(); + Vector<Color> new_colors=ramp_editor->get_colors(); + Vector<float> old_offsets=color_ramp_ref->get_offsets(); + Vector<Color> old_colors=color_ramp_ref->get_colors(); + + if (old_offsets.size()!=new_offsets.size()) + ur->create_action("Add/Remove Color Ramp Point"); + else + ur->create_action("Modify Color Ramp",true); + ur->add_do_method(this,"undo_redo_color_ramp",new_offsets,new_colors); + ur->add_undo_method(this,"undo_redo_color_ramp",old_offsets,old_colors); + ur->commit_action(); + + //color_ramp_ref->set_points(ramp_editor->get_points()); + } +} + +void ColorRampEditorPlugin::_undo_redo_color_ramp(const Vector<float>& offsets, + const Vector<Color>& colors) { + + color_ramp_ref->set_offsets(offsets); + color_ramp_ref->set_colors(colors); + ramp_editor->set_points(color_ramp_ref->get_points()); + ramp_editor->update(); +} + +ColorRampEditorPlugin::~ColorRampEditorPlugin(){ +} + +void ColorRampEditorPlugin::_bind_methods() { + ObjectTypeDB::bind_method(_MD("ramp_changed"),&ColorRampEditorPlugin::_ramp_changed); + ObjectTypeDB::bind_method(_MD("undo_redo_color_ramp","offsets","colors"),&ColorRampEditorPlugin::_undo_redo_color_ramp); +} diff --git a/tools/editor/plugins/color_ramp_editor_plugin.h b/tools/editor/plugins/color_ramp_editor_plugin.h new file mode 100644 index 0000000000..e39a5d65fe --- /dev/null +++ b/tools/editor/plugins/color_ramp_editor_plugin.h @@ -0,0 +1,37 @@ +/* + * color_ramp_editor_plugin.h + */ + +#ifndef TOOLS_EDITOR_PLUGINS_COLOR_RAMP_EDITOR_PLUGIN_H_ +#define TOOLS_EDITOR_PLUGINS_COLOR_RAMP_EDITOR_PLUGIN_H_ + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/gui/color_ramp_edit.h" + +class ColorRampEditorPlugin : public EditorPlugin { + + OBJ_TYPE( ColorRampEditorPlugin, EditorPlugin ); + + Ref<ColorRamp> color_ramp_ref; + ColorRampEdit *ramp_editor; + EditorNode *editor; + +protected: + static void _bind_methods(); + void _ramp_changed(); + void _undo_redo_color_ramp(const Vector<float>& offsets, const Vector<Color>& colors); + +public: + virtual String get_name() const { return "ColorRamp"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_node); + virtual bool handles(Object *p_node) const; + virtual void make_visible(bool p_visible); + + ColorRampEditorPlugin(EditorNode *p_node); + ~ColorRampEditorPlugin(); + +}; + +#endif /* TOOLS_EDITOR_PLUGINS_COLOR_RAMP_EDITOR_PLUGIN_H_ */ diff --git a/tools/editor/plugins/control_editor_plugin.cpp b/tools/editor/plugins/control_editor_plugin.cpp index 8d8e107f41..7348a69665 100644 --- a/tools/editor/plugins/control_editor_plugin.cpp +++ b/tools/editor/plugins/control_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/plugins/control_editor_plugin.h b/tools/editor/plugins/control_editor_plugin.h index a229327990..074298d0df 100644 --- a/tools/editor/plugins/control_editor_plugin.h +++ b/tools/editor/plugins/control_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/plugins/cube_grid_theme_editor_plugin.cpp b/tools/editor/plugins/cube_grid_theme_editor_plugin.cpp index aad7cf2c6a..c118485083 100644 --- a/tools/editor/plugins/cube_grid_theme_editor_plugin.cpp +++ b/tools/editor/plugins/cube_grid_theme_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -269,8 +269,8 @@ void MeshLibraryEditor::_bind_methods() { MeshLibraryEditor::MeshLibraryEditor(EditorNode *p_editor) { - file = memnew( FileDialog ); - file->set_mode(FileDialog::MODE_OPEN_FILE); + file = memnew( EditorFileDialog ); + file->set_mode(EditorFileDialog::MODE_OPEN_FILE); //not for now? List<String> extensions; ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions); diff --git a/tools/editor/plugins/cube_grid_theme_editor_plugin.h b/tools/editor/plugins/cube_grid_theme_editor_plugin.h index 0dab1d12b8..583ddf6e14 100644 --- a/tools/editor/plugins/cube_grid_theme_editor_plugin.h +++ b/tools/editor/plugins/cube_grid_theme_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -42,7 +42,7 @@ class MeshLibraryEditor : public Control { EditorNode *editor; MenuButton *menu; ConfirmationDialog *cd; - FileDialog *file; + EditorFileDialog *file; int to_erase; enum { diff --git a/tools/editor/plugins/editor_preview_plugins.cpp b/tools/editor/plugins/editor_preview_plugins.cpp new file mode 100644 index 0000000000..5f52d4c3e7 --- /dev/null +++ b/tools/editor/plugins/editor_preview_plugins.cpp @@ -0,0 +1,783 @@ +#include "editor_preview_plugins.h" +#include "io/resource_loader.h" +#include "tools/editor/editor_settings.h" +#include "io/file_access_memory.h" +#include "os/os.h" +#include "scene/resources/material.h" +#include "scene/resources/sample.h" +#include "scene/resources/mesh.h" + +bool EditorTexturePreviewPlugin::handles(const String& p_type) const { + + return ObjectTypeDB::is_type(p_type,"ImageTexture"); +} + +Ref<Texture> EditorTexturePreviewPlugin::generate(const RES& p_from) { + + Ref<ImageTexture> tex =p_from; + Image img = tex->get_data(); + if (img.empty()) + return Ref<Texture>(); + + img.clear_mipmaps(); + + int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size"); + if (img.is_compressed()) { + if (img.decompress()!=OK) + return Ref<Texture>(); + } else if (img.get_format()!=Image::FORMAT_RGB && img.get_format()!=Image::FORMAT_RGBA) { + img.convert(Image::FORMAT_RGBA); + } + + int width,height; + if (img.get_width() > thumbnail_size && img.get_width() >= img.get_height()) { + + width=thumbnail_size; + height = img.get_height() * thumbnail_size / img.get_width(); + } else if (img.get_height() > thumbnail_size && img.get_height() >= img.get_width()) { + + height=thumbnail_size; + width = img.get_width() * thumbnail_size / img.get_height(); + } else { + + width=img.get_width(); + height=img.get_height(); + } + + img.resize(width,height); + + Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture )); + + ptex->create_from_image(img,0); + return ptex; + +} + +EditorTexturePreviewPlugin::EditorTexturePreviewPlugin() { + + +} + +/////////////////////////////////////////////////////////////////////////// + + +Ref<Texture> EditorPackedScenePreviewPlugin::_gen_from_imd(Ref<ResourceImportMetadata> p_imd) { + + if (p_imd.is_null()) { + return Ref<Texture>(); + } + + if (!p_imd->has_option("thumbnail")) + return Ref<Texture>(); + + Variant tn = p_imd->get_option("thumbnail"); + //print_line(Variant::get_type_name(tn.get_type())); + DVector<uint8_t> thumbnail = tn; + + int len = thumbnail.size(); + if (len==0) + return Ref<Texture>(); + + + DVector<uint8_t>::Read r = thumbnail.read(); + + Image img(r.ptr(),len); + if (img.empty()) + return Ref<Texture>(); + + Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture )); + ptex->create_from_image(img,0); + return ptex; + +} + +bool EditorPackedScenePreviewPlugin::handles(const String& p_type) const { + + return ObjectTypeDB::is_type(p_type,"PackedScene"); +} +Ref<Texture> EditorPackedScenePreviewPlugin::generate(const RES& p_from) { + + Ref<ResourceImportMetadata> imd = p_from->get_import_metadata(); + return _gen_from_imd(imd); +} + +Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String& p_path) { + + Ref<ResourceImportMetadata> imd = ResourceLoader::load_import_metadata(p_path); + return _gen_from_imd(imd); +} + +EditorPackedScenePreviewPlugin::EditorPackedScenePreviewPlugin() { + +} + +////////////////////////////////////////////////////////////////// + +bool EditorMaterialPreviewPlugin::handles(const String& p_type) const { + + return ObjectTypeDB::is_type(p_type,"Material"); //any material +} + +Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES& p_from) { + + Ref<Material> material = p_from; + ERR_FAIL_COND_V(material.is_null(),Ref<Texture>()); + + VS::get_singleton()->mesh_surface_set_material(sphere,0,material->get_rid()); + + VS::get_singleton()->viewport_queue_screen_capture(viewport); + VS::get_singleton()->viewport_set_render_target_update_mode(viewport,VS::RENDER_TARGET_UPDATE_ONCE); //once used for capture +// print_line("queue capture!"); + Image img; + + int timeout=1000; + while(timeout) { + //print_line("try capture?"); + OS::get_singleton()->delay_usec(10); + img = VS::get_singleton()->viewport_get_screen_capture(viewport); + if (!img.empty()) + break; + timeout--; + } + + //print_line("captured!"); + VS::get_singleton()->mesh_surface_set_material(sphere,0,RID()); + + int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size"); + img.resize(thumbnail_size,thumbnail_size); + + Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture )); + ptex->create_from_image(img,0); + return ptex; +} + +EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() { + + scenario = VS::get_singleton()->scenario_create(); + + viewport = VS::get_singleton()->viewport_create(); + VS::get_singleton()->viewport_set_as_render_target(viewport,true); + VS::get_singleton()->viewport_set_render_target_update_mode(viewport,VS::RENDER_TARGET_UPDATE_DISABLED); + VS::get_singleton()->viewport_set_scenario(viewport,scenario); + VS::ViewportRect vr; + vr.x=0; + vr.y=0; + vr.width=128; + vr.height=128; + VS::get_singleton()->viewport_set_rect(viewport,vr); + + camera = VS::get_singleton()->camera_create(); + VS::get_singleton()->viewport_attach_camera(viewport,camera); + VS::get_singleton()->camera_set_transform(camera,Transform(Matrix3(),Vector3(0,0,3))); + VS::get_singleton()->camera_set_perspective(camera,45,0.1,10); + + light = VS::get_singleton()->light_create(VS::LIGHT_DIRECTIONAL); + light_instance = VS::get_singleton()->instance_create2(light,scenario); + VS::get_singleton()->instance_set_transform(light_instance,Transform().looking_at(Vector3(-1,-1,-1),Vector3(0,1,0))); + + light2 = VS::get_singleton()->light_create(VS::LIGHT_DIRECTIONAL); + VS::get_singleton()->light_set_color(light2,VS::LIGHT_COLOR_DIFFUSE,Color(0.7,0.7,0.7)); + VS::get_singleton()->light_set_color(light2,VS::LIGHT_COLOR_SPECULAR,Color(0.0,0.0,0.0)); + light_instance2 = VS::get_singleton()->instance_create2(light2,scenario); + + VS::get_singleton()->instance_set_transform(light_instance2,Transform().looking_at(Vector3(0,1,0),Vector3(0,0,1))); + + sphere = VS::get_singleton()->mesh_create(); + sphere_instance = VS::get_singleton()->instance_create2(sphere,scenario); + + int lats=32; + int lons=32; + float radius=1.0; + + DVector<Vector3> vertices; + DVector<Vector3> normals; + DVector<Vector2> uvs; + DVector<float> tangents; + Matrix3 tt = Matrix3(Vector3(0,1,0),Math_PI*0.5); + + for(int i = 1; i <= lats; i++) { + double lat0 = Math_PI * (-0.5 + (double) (i - 1) / lats); + double z0 = Math::sin(lat0); + double zr0 = Math::cos(lat0); + + double lat1 = Math_PI * (-0.5 + (double) i / lats); + double z1 = Math::sin(lat1); + double zr1 = Math::cos(lat1); + + for(int j = lons; j >= 1; j--) { + + double lng0 = 2 * Math_PI * (double) (j - 1) / lons; + double x0 = Math::cos(lng0); + double y0 = Math::sin(lng0); + + double lng1 = 2 * Math_PI * (double) (j) / lons; + double x1 = Math::cos(lng1); + double y1 = Math::sin(lng1); + + + Vector3 v[4]={ + Vector3(x1 * zr0, z0, y1 *zr0), + Vector3(x1 * zr1, z1, y1 *zr1), + Vector3(x0 * zr1, z1, y0 *zr1), + Vector3(x0 * zr0, z0, y0 *zr0) + }; + +#define ADD_POINT(m_idx) \ + normals.push_back(v[m_idx]);\ + vertices.push_back(v[m_idx]*radius);\ + { Vector2 uv(Math::atan2(v[m_idx].x,v[m_idx].z),Math::atan2(-v[m_idx].y,v[m_idx].z));\ + uv/=Math_PI;\ + uv*=4.0;\ + uv=uv*0.5+Vector2(0.5,0.5);\ + uvs.push_back(uv);\ + }\ + { Vector3 t = tt.xform(v[m_idx]);\ + tangents.push_back(t.x);\ + tangents.push_back(t.y);\ + tangents.push_back(t.z);\ + tangents.push_back(1.0);\ + } + + + + ADD_POINT(0); + ADD_POINT(1); + ADD_POINT(2); + + ADD_POINT(2); + ADD_POINT(3); + ADD_POINT(0); + } + } + + Array arr; + arr.resize(VS::ARRAY_MAX); + arr[VS::ARRAY_VERTEX]=vertices; + arr[VS::ARRAY_NORMAL]=normals; + arr[VS::ARRAY_TANGENT]=tangents; + arr[VS::ARRAY_TEX_UV]=uvs; + VS::get_singleton()->mesh_add_surface(sphere,VS::PRIMITIVE_TRIANGLES,arr); + +} + +EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() { + + VS::get_singleton()->free(sphere); + VS::get_singleton()->free(sphere_instance); + VS::get_singleton()->free(viewport); + VS::get_singleton()->free(light); + VS::get_singleton()->free(light_instance); + VS::get_singleton()->free(light2); + VS::get_singleton()->free(light_instance2); + VS::get_singleton()->free(camera); + VS::get_singleton()->free(scenario); + +} + +/////////////////////////////////////////////////////////////////////////// + +static bool _is_text_char(CharType c) { + + return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; +} + +bool EditorScriptPreviewPlugin::handles(const String& p_type) const { + + return ObjectTypeDB::is_type(p_type,"Script"); +} + +Ref<Texture> EditorScriptPreviewPlugin::generate(const RES& p_from) { + + + Ref<Script> scr = p_from; + if (scr.is_null()) + return Ref<Texture>(); + + String code = scr->get_source_code().strip_edges(); + if (code=="") + return Ref<Texture>(); + + List<String> kwors; + scr->get_language()->get_reserved_words(&kwors); + + Set<String> keywords; + + for(List<String>::Element *E=kwors.front();E;E=E->next()) { + + keywords.insert(E->get()); + + } + + + int line = 0; + int col=0; + int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size"); + Image img(thumbnail_size,thumbnail_size,0,Image::FORMAT_RGBA); + + + + Color bg_color = EditorSettings::get_singleton()->get("text_editor/background_color"); + bg_color.a=1.0; + Color keyword_color = EditorSettings::get_singleton()->get("text_editor/keyword_color"); + Color text_color = EditorSettings::get_singleton()->get("text_editor/text_color"); + Color symbol_color = EditorSettings::get_singleton()->get("text_editor/symbol_color"); + Color comment_color = EditorSettings::get_singleton()->get("text_editor/comment_color"); + + + for(int i=0;i<thumbnail_size;i++) { + for(int j=0;j<thumbnail_size;j++) { + img.put_pixel(i,j,bg_color); + } + + } + + bool prev_is_text=false; + bool in_keyword=false; + for(int i=0;i<code.length();i++) { + + CharType c = code[i]; + if (c>32) { + if (col<thumbnail_size) { + Color color = text_color; + + if (c!='_' && ((c>='!' && c<='/') || (c>=':' && c<='@') || (c>='[' && c<='`') || (c>='{' && c<='~') || c=='\t')) { + //make symbol a little visible + color=symbol_color; + in_keyword=false; + } else if (!prev_is_text && _is_text_char(c)) { + int pos = i; + + while(_is_text_char(code[pos])) { + pos++; + } + ///print_line("from "+itos(i)+" to "+itos(pos)); + String word = code.substr(i,pos-i); + //print_line("found word: "+word); + if (keywords.has(word)) + in_keyword=true; + + } else if (!_is_text_char(c)) { + in_keyword=false; + } + + if (in_keyword) + color=keyword_color; + + Color ul=color; + ul.a*=0.5; + img.put_pixel(col,line*2,bg_color.blend(ul)); + img.put_pixel(col,line*2+1,color); + + prev_is_text=_is_text_char(c); + } + } else { + + prev_is_text=false; + in_keyword=false; + + if (c=='\n') { + col=0; + line++; + if (line>=thumbnail_size/2) + break; + } else if (c=='\t') { + col+=3; + } + } + col++; + } + + Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture)); + + ptex->create_from_image(img,0); + return ptex; + +} + +EditorScriptPreviewPlugin::EditorScriptPreviewPlugin() { + + +} +/////////////////////////////////////////////////////////////////// + +bool EditorSamplePreviewPlugin::handles(const String& p_type) const { + + return ObjectTypeDB::is_type(p_type,"Sample"); +} + +Ref<Texture> EditorSamplePreviewPlugin::generate(const RES& p_from) { + + Ref<Sample> smp =p_from; + ERR_FAIL_COND_V(smp.is_null(),Ref<Texture>()); + + + int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size"); + + DVector<uint8_t> img; + int w = thumbnail_size; + int h = thumbnail_size; + img.resize(w*h*3); + + DVector<uint8_t>::Write imgdata = img.write(); + uint8_t * imgw = imgdata.ptr(); + DVector<uint8_t> data = smp->get_data(); + DVector<uint8_t>::Read sampledata = data.read(); + const uint8_t *sdata=sampledata.ptr(); + + bool stereo = smp->is_stereo(); + bool _16=smp->get_format()==Sample::FORMAT_PCM16; + int len = smp->get_length(); + + if (len<1) + return Ref<Texture>(); + + if (smp->get_format()==Sample::FORMAT_IMA_ADPCM) { + + struct IMA_ADPCM_State { + + int16_t step_index; + int32_t predictor; + /* values at loop point */ + int16_t loop_step_index; + int32_t loop_predictor; + int32_t last_nibble; + int32_t loop_pos; + int32_t window_ofs; + const uint8_t *ptr; + } ima_adpcm; + + ima_adpcm.step_index=0; + ima_adpcm.predictor=0; + ima_adpcm.loop_step_index=0; + ima_adpcm.loop_predictor=0; + ima_adpcm.last_nibble=-1; + ima_adpcm.loop_pos=0x7FFFFFFF; + ima_adpcm.window_ofs=0; + ima_adpcm.ptr=NULL; + + + for(int i=0;i<w;i++) { + + float max[2]={-1e10,-1e10}; + float min[2]={1e10,1e10}; + int from = i*len/w; + int to = (i+1)*len/w; + if (to>=len) + to=len-1; + + for(int j=from;j<to;j++) { + + while(j>ima_adpcm.last_nibble) { + + static const int16_t _ima_adpcm_step_table[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + }; + + static const int8_t _ima_adpcm_index_table[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 + }; + + int16_t nibble,signed_nibble,diff,step; + + ima_adpcm.last_nibble++; + const uint8_t *src_ptr=sdata; + + int ofs = ima_adpcm.last_nibble>>1; + + if (stereo) + ofs*=2; + + + nibble = (ima_adpcm.last_nibble&1)? + (src_ptr[ofs]>>4):(src_ptr[ofs]&0xF); + step=_ima_adpcm_step_table[ima_adpcm.step_index]; + + ima_adpcm.step_index += _ima_adpcm_index_table[nibble]; + if (ima_adpcm.step_index<0) + ima_adpcm.step_index=0; + if (ima_adpcm.step_index>88) + ima_adpcm.step_index=88; + + /* + signed_nibble = (nibble&7) * ((nibble&8)?-1:1); + diff = (2 * signed_nibble + 1) * step / 4; */ + + diff = step >> 3 ; + if (nibble & 1) + diff += step >> 2 ; + if (nibble & 2) + diff += step >> 1 ; + if (nibble & 4) + diff += step ; + if (nibble & 8) + diff = -diff ; + + ima_adpcm.predictor+=diff; + if (ima_adpcm.predictor<-0x8000) + ima_adpcm.predictor=-0x8000; + else if (ima_adpcm.predictor>0x7FFF) + ima_adpcm.predictor=0x7FFF; + + + /* store loop if there */ + if (ima_adpcm.last_nibble==ima_adpcm.loop_pos) { + + ima_adpcm.loop_step_index = ima_adpcm.step_index; + ima_adpcm.loop_predictor = ima_adpcm.predictor; + } + + } + + float v=ima_adpcm.predictor/32767.0; + if (v>max[0]) + max[0]=v; + if (v<min[0]) + min[0]=v; + } + max[0]*=0.8; + min[0]*=0.8; + + for(int j=0;j<h;j++) { + float v = (j/(float)h) * 2.0 - 1.0; + uint8_t* imgofs = &imgw[(j*w+i)*3]; + if (v>min[0] && v<max[0]) { + imgofs[0]=255; + imgofs[1]=150; + imgofs[2]=80; + } else { + imgofs[0]=0; + imgofs[1]=0; + imgofs[2]=0; + } + } + } + } else { + for(int i=0;i<w;i++) { + // i trust gcc will optimize this loop + float max[2]={-1e10,-1e10}; + float min[2]={1e10,1e10}; + int c=stereo?2:1; + int from = i*len/w; + int to = (i+1)*len/w; + if (to>=len) + to=len-1; + + if (_16) { + const int16_t*src =(const int16_t*)sdata; + + for(int j=0;j<c;j++) { + + for(int k=from;k<=to;k++) { + + float v = src[k*c+j]/32768.0; + if (v>max[j]) + max[j]=v; + if (v<min[j]) + min[j]=v; + } + + } + } else { + + const int8_t*src =(const int8_t*)sdata; + + for(int j=0;j<c;j++) { + + for(int k=from;k<=to;k++) { + + float v = src[k*c+j]/128.0; + if (v>max[j]) + max[j]=v; + if (v<min[j]) + min[j]=v; + } + + } + } + + max[0]*=0.8; + max[1]*=0.8; + min[0]*=0.8; + min[1]*=0.8; + + if (!stereo) { + for(int j=0;j<h;j++) { + float v = (j/(float)h) * 2.0 - 1.0; + uint8_t* imgofs = &imgw[(j*w+i)*3]; + if (v>min[0] && v<max[0]) { + imgofs[0]=255; + imgofs[1]=150; + imgofs[2]=80; + } else { + imgofs[0]=0; + imgofs[1]=0; + imgofs[2]=0; + } + } + } else { + + for(int j=0;j<h;j++) { + + int half,ofs; + float v; + if (j<(h/2)) { + half=0; + ofs=0; + v = (j/(float)(h/2)) * 2.0 - 1.0; + } else { + half=1; + ofs=h/2; + v = ((j-(h/2))/(float)(h/2)) * 2.0 - 1.0; + } + + uint8_t* imgofs = &imgw[(j*w+i)*3]; + if (v>min[half] && v<max[half]) { + imgofs[0]=255; + imgofs[1]=150; + imgofs[2]=80; + } else { + imgofs[0]=0; + imgofs[1]=0; + imgofs[2]=0; + } + } + + } + + } + } + + imgdata = DVector<uint8_t>::Write(); + + Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture)); + ptex->create_from_image(Image(w,h,0,Image::FORMAT_RGB,img),0); + return ptex; + +} + +EditorSamplePreviewPlugin::EditorSamplePreviewPlugin() { + + +} + +/////////////////////////////////////////////////////////////////////////// + +bool EditorMeshPreviewPlugin::handles(const String& p_type) const { + + return ObjectTypeDB::is_type(p_type,"Mesh"); //any Mesh +} + +Ref<Texture> EditorMeshPreviewPlugin::generate(const RES& p_from) { + + Ref<Mesh> mesh = p_from; + ERR_FAIL_COND_V(mesh.is_null(),Ref<Texture>()); + + VS::get_singleton()->instance_set_base(mesh_instance,mesh->get_rid()); + + AABB aabb= mesh->get_aabb(); + Vector3 ofs = aabb.pos + aabb.size*0.5; + aabb.pos-=ofs; + Transform xform; + xform.basis=Matrix3().rotated(Vector3(0,1,0),Math_PI*0.125); + xform.basis = Matrix3().rotated(Vector3(1,0,0),-Math_PI*0.125)*xform.basis; + AABB rot_aabb = xform.xform(aabb); + float m = MAX(rot_aabb.size.x,rot_aabb.size.y)*0.5; + if (m==0) + return Ref<Texture>(); + m=1.0/m; + m*=0.5; + //print_line("scale: "+rtos(m)); + xform.basis.scale(Vector3(m,m,m)); + xform.origin=-xform.basis.xform(ofs); //-ofs*m; + xform.origin.z-=rot_aabb.size.z*2; + VS::get_singleton()->instance_set_transform(mesh_instance,xform); + + + + VS::get_singleton()->viewport_queue_screen_capture(viewport); + VS::get_singleton()->viewport_set_render_target_update_mode(viewport,VS::RENDER_TARGET_UPDATE_ONCE); //once used for capture +// print_line("queue capture!"); + Image img; + + int timeout=1000; + while(timeout) { + //print_line("try capture?"); + OS::get_singleton()->delay_usec(10); + img = VS::get_singleton()->viewport_get_screen_capture(viewport); + if (!img.empty()) + break; + timeout--; + } + + //print_line("captured!"); + VS::get_singleton()->instance_set_base(mesh_instance,RID()); + + int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size"); + img.resize(thumbnail_size,thumbnail_size); + + Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture )); + ptex->create_from_image(img,0); + return ptex; +} + +EditorMeshPreviewPlugin::EditorMeshPreviewPlugin() { + + scenario = VS::get_singleton()->scenario_create(); + viewport = VS::get_singleton()->viewport_create(); + VS::get_singleton()->viewport_set_as_render_target(viewport,true); + VS::get_singleton()->viewport_set_render_target_update_mode(viewport,VS::RENDER_TARGET_UPDATE_DISABLED); + VS::get_singleton()->viewport_set_scenario(viewport,scenario); + VS::ViewportRect vr; + vr.x=0; + vr.y=0; + vr.width=128; + vr.height=128; + VS::get_singleton()->viewport_set_rect(viewport,vr); + + camera = VS::get_singleton()->camera_create(); + VS::get_singleton()->viewport_attach_camera(viewport,camera); + VS::get_singleton()->camera_set_transform(camera,Transform(Matrix3(),Vector3(0,0,3))); +// VS::get_singleton()->camera_set_perspective(camera,45,0.1,10); + VS::get_singleton()->camera_set_orthogonal(camera,1.0,0.01,1000.0); + + light = VS::get_singleton()->light_create(VS::LIGHT_DIRECTIONAL); + light_instance = VS::get_singleton()->instance_create2(light,scenario); + VS::get_singleton()->instance_set_transform(light_instance,Transform().looking_at(Vector3(-1,-1,-1),Vector3(0,1,0))); + + light2 = VS::get_singleton()->light_create(VS::LIGHT_DIRECTIONAL); + VS::get_singleton()->light_set_color(light2,VS::LIGHT_COLOR_DIFFUSE,Color(0.7,0.7,0.7)); + VS::get_singleton()->light_set_color(light2,VS::LIGHT_COLOR_SPECULAR,Color(0.0,0.0,0.0)); + light_instance2 = VS::get_singleton()->instance_create2(light2,scenario); + + VS::get_singleton()->instance_set_transform(light_instance2,Transform().looking_at(Vector3(0,1,0),Vector3(0,0,1))); + +// sphere = VS::get_singleton()->mesh_create(); + mesh_instance = VS::get_singleton()->instance_create(); + VS::get_singleton()->instance_set_scenario(mesh_instance,scenario); + + + +} + +EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() { + + //VS::get_singleton()->free(sphere); + VS::get_singleton()->free(mesh_instance); + VS::get_singleton()->free(viewport); + VS::get_singleton()->free(light); + VS::get_singleton()->free(light_instance); + VS::get_singleton()->free(light2); + VS::get_singleton()->free(light_instance2); + VS::get_singleton()->free(camera); + VS::get_singleton()->free(scenario); + +} diff --git a/tools/editor/plugins/editor_preview_plugins.h b/tools/editor/plugins/editor_preview_plugins.h new file mode 100644 index 0000000000..98071e2a0e --- /dev/null +++ b/tools/editor/plugins/editor_preview_plugins.h @@ -0,0 +1,88 @@ +#ifndef EDITORPREVIEWPLUGINS_H +#define EDITORPREVIEWPLUGINS_H + +#include "tools/editor/editor_resource_preview.h" + +class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { +public: + + virtual bool handles(const String& p_type) const; + virtual Ref<Texture> generate(const RES& p_from); + + EditorTexturePreviewPlugin(); +}; + + +class EditorPackedScenePreviewPlugin : public EditorResourcePreviewGenerator { + + Ref<Texture> _gen_from_imd(Ref<ResourceImportMetadata> p_imd); +public: + + virtual bool handles(const String& p_type) const; + virtual Ref<Texture> generate(const RES& p_from); + virtual Ref<Texture> generate_from_path(const String& p_path); + + EditorPackedScenePreviewPlugin(); +}; + +class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator { + + RID scenario; + RID sphere; + RID sphere_instance; + RID viewport; + RID light; + RID light_instance; + RID light2; + RID light_instance2; + RID camera; +public: + + virtual bool handles(const String& p_type) const; + virtual Ref<Texture> generate(const RES& p_from); + + EditorMaterialPreviewPlugin(); + ~EditorMaterialPreviewPlugin(); +}; + +class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator { +public: + + virtual bool handles(const String& p_type) const; + virtual Ref<Texture> generate(const RES& p_from); + + EditorScriptPreviewPlugin(); +}; + + +class EditorSamplePreviewPlugin : public EditorResourcePreviewGenerator { +public: + + virtual bool handles(const String& p_type) const; + virtual Ref<Texture> generate(const RES& p_from); + + EditorSamplePreviewPlugin(); +}; + + +class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator { + + RID scenario; + RID mesh_instance; + RID viewport; + RID light; + RID light_instance; + RID light2; + RID light_instance2; + RID camera; +public: + + virtual bool handles(const String& p_type) const; + virtual Ref<Texture> generate(const RES& p_from); + + EditorMeshPreviewPlugin(); + ~EditorMeshPreviewPlugin(); +}; + + +#endif // EDITORPREVIEWPLUGINS_H diff --git a/tools/editor/plugins/item_list_editor_plugin.cpp b/tools/editor/plugins/item_list_editor_plugin.cpp index eb7ab69987..fa261edea3 100644 --- a/tools/editor/plugins/item_list_editor_plugin.cpp +++ b/tools/editor/plugins/item_list_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -210,6 +210,7 @@ void ItemListEditor::_bind_methods() { } bool ItemListEditor::handles(Object *p_object) const { + return false; for(int i=0;i<item_plugins.size();i++) { if (item_plugins[i]->handles(p_object)) { return true; diff --git a/tools/editor/plugins/item_list_editor_plugin.h b/tools/editor/plugins/item_list_editor_plugin.h index 6b4d26fb45..351dbb800d 100644 --- a/tools/editor/plugins/item_list_editor_plugin.h +++ b/tools/editor/plugins/item_list_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/plugins/light_occluder_2d_editor_plugin.cpp b/tools/editor/plugins/light_occluder_2d_editor_plugin.cpp new file mode 100644 index 0000000000..757b5327dd --- /dev/null +++ b/tools/editor/plugins/light_occluder_2d_editor_plugin.cpp @@ -0,0 +1,492 @@ +#include "light_occluder_2d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "os/file_access.h" +#include "tools/editor/editor_settings.h" + +void LightOccluder2DEditor::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_READY: { + + button_create->set_icon( get_icon("Edit","EditorIcons")); + button_edit->set_icon( get_icon("MovePoint","EditorIcons")); + button_edit->set_pressed(true); + get_tree()->connect("node_removed",this,"_node_removed"); + create_poly->connect("confirmed",this,"_create_poly"); + + } break; + case NOTIFICATION_FIXED_PROCESS: { + + + } break; + } + +} +void LightOccluder2DEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + canvas_item_editor->get_viewport_control()->update(); + } + +} + + +void LightOccluder2DEditor::_menu_option(int p_option) { + + switch(p_option) { + + case MODE_CREATE: { + + mode=MODE_CREATE; + button_create->set_pressed(true); + button_edit->set_pressed(false); + } break; + case MODE_EDIT: { + + mode=MODE_EDIT; + button_create->set_pressed(false); + button_edit->set_pressed(true); + } break; + + } +} + +void LightOccluder2DEditor::_wip_close(bool p_closed) { + + undo_redo->create_action("Create Poly"); + undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",node->get_occluder_polygon()->get_polygon()); + undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",wip); + undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_closed",node->get_occluder_polygon()->is_closed()); + undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_closed",p_closed); + + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + wip.clear(); + wip_active=false; + mode=MODE_EDIT; + button_edit->set_pressed(true); + button_create->set_pressed(false); + edited_point=-1; +} + +bool LightOccluder2DEditor::forward_input_event(const InputEvent& p_event) { + + + if (!node) + return false; + + if (node->get_occluder_polygon().is_null()) { + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) { + create_poly->set_text("No OccluderPolygon2D resource on this node.\nCreate and assign one?"); + create_poly->popup_centered_minsize(); + } + return (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1); + } + switch(p_event.type) { + + case InputEvent::MOUSE_BUTTON: { + + const InputEventMouseButton &mb=p_event.mouse_button; + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + + Vector2 gpoint = Point2(mb.x,mb.y); + Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); + cpoint=canvas_item_editor->snap_point(cpoint); + cpoint = node->get_global_transform().affine_inverse().xform(cpoint); + + Vector<Vector2> poly = Variant(node->get_occluder_polygon()->get_polygon()); + + //first check if a point is to be added (segment split) + real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8); + + switch(mode) { + + + case MODE_CREATE: { + + if (mb.button_index==BUTTON_LEFT && mb.pressed) { + + + if (!wip_active) { + + wip.clear(); + wip.push_back( cpoint ); + wip_active=true; + edited_point_pos=cpoint; + canvas_item_editor->get_viewport_control()->update(); + edited_point=1; + return true; + } else { + + + if (wip.size()>1 && xform.xform(wip[0]).distance_to(gpoint)<grab_treshold) { + //wip closed + _wip_close(true); + + return true; + } else if (wip.size()>1 && xform.xform(wip[wip.size()-1]).distance_to(gpoint)<grab_treshold) { + //wip closed + _wip_close(false); + return true; + + } else { + + wip.push_back( cpoint ); + edited_point=wip.size(); + canvas_item_editor->get_viewport_control()->update(); + return true; + + //add wip point + } + } + } else if (mb.button_index==BUTTON_RIGHT && mb.pressed && wip_active) { + _wip_close(true); + } + + + + } break; + + case MODE_EDIT: { + + if (mb.button_index==BUTTON_LEFT) { + if (mb.pressed) { + + if (mb.mod.control) { + + + if (poly.size() < 3) { + + undo_redo->create_action("Edit Poly"); + undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",poly); + poly.push_back(cpoint); + undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",poly); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + return true; + } + + //search edges + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + for(int i=0;i<poly.size();i++) { + + Vector2 points[2] ={ xform.xform(poly[i]), + xform.xform(poly[(i+1)%poly.size()]) }; + + Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points); + if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2) + continue; //not valid to reuse point + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_pos=cp; + closest_idx=i; + } + + + } + + if (closest_idx>=0) { + + pre_move_edit=poly; + poly.insert(closest_idx+1,xform.affine_inverse().xform(closest_pos)); + edited_point=closest_idx+1; + edited_point_pos=xform.affine_inverse().xform(closest_pos); + node->get_occluder_polygon()->set_polygon(Variant(poly)); + canvas_item_editor->get_viewport_control()->update(); + return true; + } + } else { + + //look for points to move + + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + for(int i=0;i<poly.size();i++) { + + Vector2 cp =xform.xform(poly[i]); + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_pos=cp; + closest_idx=i; + } + + } + + if (closest_idx>=0) { + + pre_move_edit=poly; + edited_point=closest_idx; + edited_point_pos=xform.affine_inverse().xform(closest_pos); + canvas_item_editor->get_viewport_control()->update(); + return true; + } + } + } else { + + if (edited_point!=-1) { + + //apply + + ERR_FAIL_INDEX_V(edited_point,poly.size(),false); + poly[edited_point]=edited_point_pos; + undo_redo->create_action("Edit Poly"); + undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",poly); + undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",pre_move_edit); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + + edited_point=-1; + return true; + } + } + } if (mb.button_index==BUTTON_RIGHT && mb.pressed && edited_point==-1) { + + + + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + for(int i=0;i<poly.size();i++) { + + Vector2 cp =xform.xform(poly[i]); + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_pos=cp; + closest_idx=i; + } + + } + + if (closest_idx>=0) { + + + undo_redo->create_action("Edit Poly (Remove Point)"); + undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",poly); + poly.remove(closest_idx); + undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",poly); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + return true; + } + + } + + + + } break; + } + + + + } break; + case InputEvent::MOUSE_MOTION: { + + const InputEventMouseMotion &mm=p_event.mouse_motion; + + if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) { + + Vector2 gpoint = Point2(mm.x,mm.y); + Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); + cpoint=canvas_item_editor->snap_point(cpoint); + edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint); + + canvas_item_editor->get_viewport_control()->update(); + + } + + } break; + } + + return false; +} +void LightOccluder2DEditor::_canvas_draw() { + + if (!node || !node->get_occluder_polygon().is_valid()) + return; + + Control *vpc = canvas_item_editor->get_viewport_control(); + + Vector<Vector2> poly; + + if (wip_active) + poly=wip; + else + poly=Variant(node->get_occluder_polygon()->get_polygon()); + + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + Ref<Texture> handle= get_icon("EditorHandle","EditorIcons"); + + int len = poly.size(); + + for(int i=0;i<poly.size();i++) { + + + Vector2 p,p2; + p = i==edited_point ? edited_point_pos : poly[i]; + if ((wip_active && i==poly.size()-1) || (((i+1)%poly.size())==edited_point)) + p2=edited_point_pos; + else + p2 = poly[(i+1)%poly.size()]; + + Vector2 point = xform.xform(p); + Vector2 next_point = xform.xform(p2); + + Color col=Color(1,0.3,0.1,0.8); + + if (i==poly.size()-1 && (!node->get_occluder_polygon()->is_closed() || wip_active)) { + + } else { + vpc->draw_line(point,next_point,col,2); + } + vpc->draw_texture(handle,point-handle->get_size()*0.5); + } +} + + + +void LightOccluder2DEditor::edit(Node *p_collision_polygon) { + + if (!canvas_item_editor) { + canvas_item_editor=CanvasItemEditor::get_singleton(); + } + + if (p_collision_polygon) { + + node=p_collision_polygon->cast_to<LightOccluder2D>(); + if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw"); + wip.clear(); + wip_active=false; + edited_point=-1; + canvas_item_editor->get_viewport_control()->update(); + } else { + node=NULL; + + if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw"); + + } + +} + +void LightOccluder2DEditor::_create_poly() { + + if (!node) + return; + undo_redo->create_action("Create Occluder Polygon"); + undo_redo->add_do_method(node,"set_occluder_polygon",Ref<OccluderPolygon2D>(memnew( OccluderPolygon2D))); + undo_redo->add_undo_method(node,"set_occluder_polygon",Variant(REF())); + undo_redo->commit_action(); +} + +void LightOccluder2DEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_menu_option"),&LightOccluder2DEditor::_menu_option); + ObjectTypeDB::bind_method(_MD("_canvas_draw"),&LightOccluder2DEditor::_canvas_draw); + ObjectTypeDB::bind_method(_MD("_node_removed"),&LightOccluder2DEditor::_node_removed); + ObjectTypeDB::bind_method(_MD("_create_poly"),&LightOccluder2DEditor::_create_poly); + +} + + +LightOccluder2DEditor::LightOccluder2DEditor(EditorNode *p_editor) { + + node=NULL; + canvas_item_editor=NULL; + editor=p_editor; + undo_redo = editor->get_undo_redo(); + + add_child( memnew( VSeparator )); + button_create = memnew( ToolButton ); + add_child(button_create); + button_create->connect("pressed",this,"_menu_option",varray(MODE_CREATE)); + button_create->set_toggle_mode(true); + button_create->set_tooltip("Create a new polygon from scratch"); + + button_edit = memnew( ToolButton ); + add_child(button_edit); + button_edit->connect("pressed",this,"_menu_option",varray(MODE_EDIT)); + button_edit->set_toggle_mode(true); + button_edit->set_tooltip("Edit existing polygon:\nLMB: Move Point.\nCtrl+LMB: Split Segment.\nRMB: Erase Point."); + + create_poly = memnew( ConfirmationDialog ); + add_child(create_poly); + create_poly->get_ok()->set_text("Create"); + + + //add_constant_override("separation",0); + +#if 0 + options = memnew( MenuButton ); + add_child(options); + options->set_area_as_parent_rect(); + options->set_text("Polygon"); + //options->get_popup()->add_item("Parse BBCODE",PARSE_BBCODE); + options->get_popup()->connect("item_pressed", this,"_menu_option"); +#endif + + mode = MODE_EDIT; + wip_active=false; + +} + + +void LightOccluder2DEditorPlugin::edit(Object *p_object) { + + collision_polygon_editor->edit(p_object->cast_to<Node>()); +} + +bool LightOccluder2DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("LightOccluder2D"); +} + +void LightOccluder2DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + collision_polygon_editor->show(); + } else { + + collision_polygon_editor->hide(); + collision_polygon_editor->edit(NULL); + } + +} + +LightOccluder2DEditorPlugin::LightOccluder2DEditorPlugin(EditorNode *p_node) { + + editor=p_node; + collision_polygon_editor = memnew( LightOccluder2DEditor(p_node) ); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor); + + collision_polygon_editor->hide(); + + + +} + + +LightOccluder2DEditorPlugin::~LightOccluder2DEditorPlugin() +{ +} + diff --git a/tools/editor/plugins/light_occluder_2d_editor_plugin.h b/tools/editor/plugins/light_occluder_2d_editor_plugin.h new file mode 100644 index 0000000000..5fb5631d05 --- /dev/null +++ b/tools/editor/plugins/light_occluder_2d_editor_plugin.h @@ -0,0 +1,87 @@ +#ifndef LIGHT_OCCLUDER_2D_EDITOR_PLUGIN_H +#define LIGHT_OCCLUDER_2D_EDITOR_PLUGIN_H + + + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/2d/light_occluder_2d.h" +#include "scene/gui/tool_button.h" +#include "scene/gui/button_group.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +class CanvasItemEditor; + +class LightOccluder2DEditor : public HBoxContainer { + + OBJ_TYPE(LightOccluder2DEditor, HBoxContainer ); + + UndoRedo *undo_redo; + enum Mode { + + MODE_CREATE, + MODE_EDIT, + + }; + + Mode mode; + + ToolButton *button_create; + ToolButton *button_edit; + + CanvasItemEditor *canvas_item_editor; + EditorNode *editor; + Panel *panel; + LightOccluder2D *node; + MenuButton *options; + + int edited_point; + Vector2 edited_point_pos; + Vector<Vector2> pre_move_edit; + Vector<Vector2> wip; + bool wip_active; + + ConfirmationDialog *create_poly; + + void _wip_close(bool p_closed); + void _canvas_draw(); + void _menu_option(int p_option); + void _create_poly(); + +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + Vector2 snap_point(const Vector2& p_point) const; + bool forward_input_event(const InputEvent& p_event); + void edit(Node *p_collision_polygon); + LightOccluder2DEditor(EditorNode *p_editor); +}; + +class LightOccluder2DEditorPlugin : public EditorPlugin { + + OBJ_TYPE( LightOccluder2DEditorPlugin, EditorPlugin ); + + LightOccluder2DEditor *collision_polygon_editor; + EditorNode *editor; + +public: + + virtual bool forward_input_event(const InputEvent& p_event) { return collision_polygon_editor->forward_input_event(p_event); } + + virtual String get_name() const { return "LightOccluder2D"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_node); + virtual bool handles(Object *p_node) const; + virtual void make_visible(bool p_visible); + + LightOccluder2DEditorPlugin(EditorNode *p_node); + ~LightOccluder2DEditorPlugin(); + +}; + +#endif // LIGHT_OCCLUDER_2D_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/mesh_editor_plugin.cpp b/tools/editor/plugins/mesh_editor_plugin.cpp index a3884f9be4..cea774f94b 100644 --- a/tools/editor/plugins/mesh_editor_plugin.cpp +++ b/tools/editor/plugins/mesh_editor_plugin.cpp @@ -33,7 +33,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { Ref<Mesh> mesh = node->get_mesh(); if (mesh.is_null()) { err_dialog->set_text("Mesh is empty!"); - err_dialog->popup_centered(Size2(100,50)); + err_dialog->popup_centered_minsize(); return; } @@ -85,7 +85,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { if (node==get_tree()->get_edited_scene_root()) { err_dialog->set_text("This doesn't work on scene root!"); - err_dialog->popup_centered(Size2(100,50)); + err_dialog->popup_centered_minsize(); return; } Ref<Shape> shape = mesh->create_trimesh_shape(); @@ -111,7 +111,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { if (node==get_tree()->get_edited_scene_root()) { err_dialog->set_text("This doesn't work on scene root!"); - err_dialog->popup_centered(Size2(100,50)); + err_dialog->popup_centered_minsize(); return; } Ref<Shape> shape = mesh->create_convex_shape(); @@ -160,7 +160,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { } break; case MENU_OPTION_CREATE_OUTLINE_MESH: { - outline_dialog->popup_centered(Size2(200,80)); + outline_dialog->popup_centered(Vector2(200, 90)); } break; } @@ -171,7 +171,7 @@ void MeshInstanceEditor::_create_outline_mesh() { Ref<Mesh> mesh = node->get_mesh(); if (mesh.is_null()) { err_dialog->set_text("MeshInstance lacks a Mesh!"); - err_dialog->popup_centered(Size2(100,50)); + err_dialog->popup_centered_minsize(); return; } @@ -179,7 +179,7 @@ void MeshInstanceEditor::_create_outline_mesh() { if (mesho.is_null()) { err_dialog->set_text("Could not create outline!"); - err_dialog->popup_centered(Size2(100,50)); + err_dialog->popup_centered_minsize(); return; } @@ -212,10 +212,11 @@ MeshInstanceEditor::MeshInstanceEditor() { options = memnew( MenuButton ); - //add_child(options); SpatialEditor::get_singleton()->add_control_to_menu_panel(options); options->set_text("Mesh"); + options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MeshInstance","EditorIcons")); + options->get_popup()->add_item("Create Trimesh Static Body",MENU_OPTION_CREATE_STATIC_TRIMESH_BODY); options->get_popup()->add_item("Create Convex Static Body",MENU_OPTION_CREATE_STATIC_CONVEX_BODY); options->get_popup()->add_separator(); @@ -229,17 +230,26 @@ MeshInstanceEditor::MeshInstanceEditor() { options->get_popup()->connect("item_pressed", this,"_menu_option"); outline_dialog = memnew( ConfirmationDialog ); - outline_dialog->set_title("Outline Size: "); + outline_dialog->set_title("Create Outline Mesh"); + outline_dialog->get_ok()->set_text("Create"); + + VBoxContainer *outline_dialog_vbc = memnew( VBoxContainer ); + outline_dialog->add_child(outline_dialog_vbc); + outline_dialog->set_child_rect(outline_dialog_vbc); + outline_size = memnew( SpinBox ); outline_size->set_min(0.001); outline_size->set_max(1024); outline_size->set_step(0.001); outline_size->set_val(0.05); - outline_dialog->add_child(outline_size); - outline_dialog->set_child_rect(outline_size); + outline_dialog_vbc->add_margin_child("Outline Size:",outline_size); + add_child(outline_dialog); outline_dialog->connect("confirmed",this,"_create_outline_mesh"); + err_dialog = memnew( AcceptDialog ); + add_child(err_dialog); + } diff --git a/tools/editor/plugins/multimesh_editor_plugin.cpp b/tools/editor/plugins/multimesh_editor_plugin.cpp index b2b6cbe9b7..a5c823f8bd 100644 --- a/tools/editor/plugins/multimesh_editor_plugin.cpp +++ b/tools/editor/plugins/multimesh_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,8 +29,7 @@ #include "multimesh_editor_plugin.h" #include "scene/gui/box_container.h" #include "scene/3d/mesh_instance.h" - - +#include "spatial_editor_plugin.h" void MultiMeshEditor::_node_removed(Node *p_node) { @@ -57,13 +56,13 @@ void MultiMeshEditor::_populate() { if (multimesh.is_null()) { err_dialog->set_text("No mesh source specified (and no MultiMesh set in node)."); - err_dialog->popup_centered(Size2(300,100)); + err_dialog->popup_centered_minsize(); return; } if (multimesh->get_mesh().is_null()) { err_dialog->set_text("No mesh source specified (and MultiMesh contains no Mesh)."); - err_dialog->popup_centered(Size2(300,100)); + err_dialog->popup_centered_minsize(); return; } @@ -75,7 +74,7 @@ void MultiMeshEditor::_populate() { if (!ms_node) { err_dialog->set_text("Mesh source is invalid (Invalid Path)."); - err_dialog->popup_centered(Size2(300,100)); + err_dialog->popup_centered_minsize(); return; } @@ -84,7 +83,7 @@ void MultiMeshEditor::_populate() { if (!ms_instance) { err_dialog->set_text("Mesh source is invalid (Not a MeshInstance)."); - err_dialog->popup_centered(Size2(300,100)); + err_dialog->popup_centered_minsize(); return; } @@ -93,7 +92,7 @@ void MultiMeshEditor::_populate() { if (mesh.is_null()) { err_dialog->set_text("Mesh source is invalid (Contains no Mesh resource)."); - err_dialog->popup_centered(Size2(300,100)); + err_dialog->popup_centered_minsize(); return; } @@ -102,7 +101,7 @@ void MultiMeshEditor::_populate() { if (surface_source->get_text()=="") { err_dialog->set_text("No surface source specified."); - err_dialog->popup_centered(Size2(300,100)); + err_dialog->popup_centered_minsize(); return; } @@ -111,7 +110,7 @@ void MultiMeshEditor::_populate() { if (!ss_node) { err_dialog->set_text("Surface source is invalid (Invalid Path)."); - err_dialog->popup_centered(Size2(300,100)); + err_dialog->popup_centered_minsize(); return; } @@ -120,7 +119,7 @@ void MultiMeshEditor::_populate() { if (!ss_instance) { err_dialog->set_text("Surface source is invalid (Not Geometry)."); - err_dialog->popup_centered(Size2(300,100)); + err_dialog->popup_centered_minsize(); return; } @@ -131,7 +130,7 @@ void MultiMeshEditor::_populate() { if (geometry.size()==0) { err_dialog->set_text("Surface source is invalid (No Faces)."); - err_dialog->popup_centered(Size2(300,100)); + err_dialog->popup_centered_minsize(); return; } @@ -290,7 +289,7 @@ void MultiMeshEditor::_menu_option(int p_option) { _last_pp_node=node; } - populate_dialog->popup_centered(Size2(250,395)); + populate_dialog->popup_centered(Size2(250,380)); } break; } @@ -299,14 +298,14 @@ void MultiMeshEditor::_menu_option(int p_option) { void MultiMeshEditor::edit(MultiMeshInstance *p_multimesh) { - node=p_multimesh; + node=p_multimesh; } void MultiMeshEditor::_browse(bool p_source) { browsing_source=p_source; - std->get_tree()->set_marked(node,false); + std->get_scene_tree()->set_marked(node,false); std->popup_centered_ratio(); if (p_source) std->set_title("Select a Source Mesh:"); @@ -326,10 +325,11 @@ MultiMeshEditor::MultiMeshEditor() { options = memnew( MenuButton ); - add_child(options); - options->set_area_as_parent_rect(); - + SpatialEditor::get_singleton()->add_control_to_menu_panel(options); + options->set_text("MultiMesh"); + options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MultiMeshInstance","EditorIcons")); + options->get_popup()->add_item("Populate Surface"); options->get_popup()->connect("item_pressed", this,"_menu_option"); @@ -341,7 +341,6 @@ MultiMeshEditor::MultiMeshEditor() { populate_dialog->add_child(vbc); populate_dialog->set_child_rect(vbc); - HBoxContainer *hbc = memnew( HBoxContainer ); surface_source = memnew( LineEdit ); @@ -372,12 +371,12 @@ MultiMeshEditor::MultiMeshEditor() { populate_axis->select(2); vbc->add_margin_child("Mesh Up Axis:",populate_axis); - populate_rotate_random = memnew( HScrollBar ); + populate_rotate_random = memnew( HSlider ); populate_rotate_random->set_max(1); populate_rotate_random->set_step(0.01); vbc->add_margin_child("Random Rotation:",populate_rotate_random); - populate_tilt_random = memnew( HScrollBar ); + populate_tilt_random = memnew( HSlider ); populate_tilt_random->set_max(1); populate_tilt_random->set_step(0.01); vbc->add_margin_child("Random Tilt:",populate_tilt_random); @@ -415,8 +414,7 @@ MultiMeshEditor::MultiMeshEditor() { std->connect("selected",this,"_browsed"); _last_pp_node=NULL; - //options->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); - //options->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); + err_dialog = memnew( AcceptDialog ); add_child(err_dialog); } @@ -435,10 +433,10 @@ bool MultiMeshEditorPlugin::handles(Object *p_object) const { void MultiMeshEditorPlugin::make_visible(bool p_visible) { if (p_visible) { - multimesh_editor->show(); + multimesh_editor->options->show(); } else { - multimesh_editor->hide(); + multimesh_editor->options->hide(); multimesh_editor->edit(NULL); } @@ -450,16 +448,7 @@ MultiMeshEditorPlugin::MultiMeshEditorPlugin(EditorNode *p_node) { multimesh_editor = memnew( MultiMeshEditor ); editor->get_viewport()->add_child(multimesh_editor); -// multimesh_editor->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); -// multimesh_editor->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); - multimesh_editor->set_margin(MARGIN_LEFT,253); - multimesh_editor->set_margin(MARGIN_RIGHT,310); - multimesh_editor->set_margin(MARGIN_TOP,0); - multimesh_editor->set_margin(MARGIN_BOTTOM,10); - - - - multimesh_editor->hide(); + multimesh_editor->options->hide(); } diff --git a/tools/editor/plugins/multimesh_editor_plugin.h b/tools/editor/plugins/multimesh_editor_plugin.h index a4d5f9bd30..edc3dfd55f 100644 --- a/tools/editor/plugins/multimesh_editor_plugin.h +++ b/tools/editor/plugins/multimesh_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -42,14 +42,14 @@ class MultiMeshEditor : public Control { OBJ_TYPE(MultiMeshEditor, Control ); +friend class MultiMeshEditorPlugin; AcceptDialog *err_dialog; - + MenuButton * options; MultiMeshInstance *_last_pp_node; bool browsing_source; Panel *panel; - MenuButton * options; MultiMeshInstance *node; LineEdit *surface_source; @@ -59,8 +59,8 @@ class MultiMeshEditor : public Control { ConfirmationDialog *populate_dialog; OptionButton *populate_axis; - HScrollBar *populate_rotate_random; - HScrollBar *populate_tilt_random; + HSlider *populate_rotate_random; + HSlider *populate_tilt_random; SpinBox *populate_scale_random; SpinBox *populate_scale; SpinBox *populate_amount; diff --git a/tools/editor/plugins/navigation_polygon_editor_plugin.cpp b/tools/editor/plugins/navigation_polygon_editor_plugin.cpp new file mode 100644 index 0000000000..fa1f614413 --- /dev/null +++ b/tools/editor/plugins/navigation_polygon_editor_plugin.cpp @@ -0,0 +1,540 @@ +#include "navigation_polygon_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "os/file_access.h" +#include "tools/editor/editor_settings.h" + +void NavigationPolygonEditor::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_READY: { + + button_create->set_icon( get_icon("Edit","EditorIcons")); + button_edit->set_icon( get_icon("MovePoint","EditorIcons")); + button_edit->set_pressed(true); + get_tree()->connect("node_removed",this,"_node_removed"); + create_nav->connect("confirmed",this,"_create_nav"); + + } break; + case NOTIFICATION_FIXED_PROCESS: { + + + } break; + } + +} +void NavigationPolygonEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + canvas_item_editor->get_viewport_control()->update(); + } + +} + +void NavigationPolygonEditor::_create_nav() { + + if (!node) + return; + + undo_redo->create_action("Create Navigation Polygon"); + undo_redo->add_do_method(node,"set_navigation_polygon",Ref<NavigationPolygon>(memnew( NavigationPolygon))); + undo_redo->add_undo_method(node,"set_navigation_polygon",Variant(REF())); + undo_redo->commit_action(); +} + +void NavigationPolygonEditor::_menu_option(int p_option) { + + switch(p_option) { + + case MODE_CREATE: { + + mode=MODE_CREATE; + button_create->set_pressed(true); + button_edit->set_pressed(false); + } break; + case MODE_EDIT: { + + mode=MODE_EDIT; + button_create->set_pressed(false); + button_edit->set_pressed(true); + } break; + + } +} + +void NavigationPolygonEditor::_wip_close() { + + + if (wip.size()>=3) { + + undo_redo->create_action("Create Poly"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"remove_outline",node->get_navigation_polygon()->get_outline_count()); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"add_outline",wip); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + mode=MODE_EDIT; + button_edit->set_pressed(true); + button_create->set_pressed(false); + } + + wip.clear(); + wip_active=false; + edited_point=-1; +} + +bool NavigationPolygonEditor::forward_input_event(const InputEvent& p_event) { + + + if (!node) + return false; + + if (node->get_navigation_polygon().is_null()) { + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) { + create_nav->set_text("No NavigationPolygon resource on this node.\nCreate and assign one?"); + create_nav->popup_centered_minsize(); + } + return (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1);; + } + + + switch(p_event.type) { + + case InputEvent::MOUSE_BUTTON: { + + const InputEventMouseButton &mb=p_event.mouse_button; + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + + Vector2 gpoint = Point2(mb.x,mb.y); + Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); + cpoint=canvas_item_editor->snap_point(cpoint); + cpoint = node->get_global_transform().affine_inverse().xform(cpoint); + + + + //first check if a point is to be added (segment split) + real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8); + + switch(mode) { + + + case MODE_CREATE: { + + if (mb.button_index==BUTTON_LEFT && mb.pressed) { + + + if (!wip_active) { + + wip.clear(); + wip.push_back( cpoint ); + wip_active=true; + edited_point_pos=cpoint; + edited_outline=-1; + canvas_item_editor->get_viewport_control()->update(); + edited_point=1; + return true; + } else { + + + if (wip.size()>1 && xform.xform(wip[0]).distance_to(gpoint)<grab_treshold) { + //wip closed + _wip_close(); + + return true; + } else { + + wip.push_back( cpoint ); + edited_point=wip.size(); + canvas_item_editor->get_viewport_control()->update(); + return true; + + //add wip point + } + } + } else if (mb.button_index==BUTTON_RIGHT && mb.pressed && wip_active) { + _wip_close(); + } + + + + } break; + + case MODE_EDIT: { + + if (mb.button_index==BUTTON_LEFT) { + if (mb.pressed) { + + if (mb.mod.control) { + + + //search edges + int closest_outline=-1; + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + + for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) { + + + DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j); + + int pc=points.size(); + DVector<Vector2>::Read poly=points.read(); + + for(int i=0;i<pc;i++) { + + Vector2 points[2] ={ xform.xform(poly[i]), + xform.xform(poly[(i+1)%pc]) }; + + Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points); + if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2) + continue; //not valid to reuse point + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_outline=j; + closest_pos=cp; + closest_idx=i; + } + + + } + } + + if (closest_idx>=0) { + + pre_move_edit=node->get_navigation_polygon()->get_outline(closest_outline); + DVector<Point2> poly = pre_move_edit; + poly.insert(closest_idx+1,xform.affine_inverse().xform(closest_pos)); + edited_point=closest_idx+1; + edited_outline=closest_outline; + edited_point_pos=xform.affine_inverse().xform(closest_pos); + node->get_navigation_polygon()->set_outline(closest_outline,poly); + canvas_item_editor->get_viewport_control()->update(); + return true; + } + } else { + + //look for points to move + int closest_outline=-1; + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + + for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) { + + + DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j); + + int pc=points.size(); + DVector<Vector2>::Read poly=points.read(); + + for(int i=0;i<pc;i++) { + + + Vector2 cp =xform.xform(poly[i]); + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_pos=cp; + closest_outline=j; + closest_idx=i; + } + } + } + + if (closest_idx>=0) { + + pre_move_edit=node->get_navigation_polygon()->get_outline(closest_outline); + edited_point=closest_idx; + edited_outline=closest_outline; + edited_point_pos=xform.affine_inverse().xform(closest_pos); + canvas_item_editor->get_viewport_control()->update(); + return true; + } + } + } else { + + if (edited_point!=-1) { + + //apply + + DVector<Vector2> poly = node->get_navigation_polygon()->get_outline(edited_outline); + ERR_FAIL_INDEX_V(edited_point,poly.size(),false); + poly.set(edited_point,edited_point_pos); + undo_redo->create_action("Edit Poly"); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"set_outline",edited_outline,poly); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"set_outline",edited_outline,pre_move_edit); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + + edited_point=-1; + return true; + } + } + } if (mb.button_index==BUTTON_RIGHT && mb.pressed && edited_point==-1) { + + int closest_outline=-1; + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + + for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) { + + + DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j); + + int pc=points.size(); + DVector<Vector2>::Read poly=points.read(); + + for(int i=0;i<pc;i++) { + + + Vector2 cp =xform.xform(poly[i]); + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_pos=cp; + closest_outline=j; + closest_idx=i; + } + } + } + + if (closest_idx>=0) { + + + DVector<Vector2> poly = node->get_navigation_polygon()->get_outline(closest_outline); + + if (poly.size()>3) { + undo_redo->create_action("Edit Poly (Remove Point)"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"set_outline",closest_outline,poly); + poly.remove(closest_idx); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"set_outline",closest_outline,poly); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + } else { + + undo_redo->create_action("Remove Poly And Point"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"add_outline_at_index",poly,closest_outline); + poly.remove(closest_idx); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"remove_outline",closest_outline); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + + } + return true; + } + } + + + + } break; + } + + + + } break; + case InputEvent::MOUSE_MOTION: { + + const InputEventMouseMotion &mm=p_event.mouse_motion; + + if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) { + + Vector2 gpoint = Point2(mm.x,mm.y); + Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); + cpoint=canvas_item_editor->snap_point(cpoint); + edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint); + + canvas_item_editor->get_viewport_control()->update(); + + } + + } break; + } + + return false; +} +void NavigationPolygonEditor::_canvas_draw() { + + if (!node) + return; + + Control *vpc = canvas_item_editor->get_viewport_control(); + if (node->get_navigation_polygon().is_null()) + return; + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + Ref<Texture> handle= get_icon("EditorHandle","EditorIcons"); + + + + for(int j=-1;j<node->get_navigation_polygon()->get_outline_count();j++) { + Vector<Vector2> poly; + + if (wip_active && j==edited_outline) { + poly=wip; + } else { + if (j==-1) + continue; + poly = Variant(node->get_navigation_polygon()->get_outline(j)); + } + + int len = poly.size(); + + for(int i=0;i<poly.size();i++) { + + + Vector2 p,p2; + p = (j==edited_outline && i==edited_point) ? edited_point_pos : poly[i]; + if (j==edited_outline && ((wip_active && i==poly.size()-1) || (((i+1)%poly.size())==edited_point))) + p2=edited_point_pos; + else + p2 = poly[(i+1)%poly.size()]; + + Vector2 point = xform.xform(p); + Vector2 next_point = xform.xform(p2); + + Color col=Color(1,0.3,0.1,0.8); + vpc->draw_line(point,next_point,col,2); + vpc->draw_texture(handle,point-handle->get_size()*0.5); + } + } +} + + + +void NavigationPolygonEditor::edit(Node *p_collision_polygon) { + + if (!canvas_item_editor) { + canvas_item_editor=CanvasItemEditor::get_singleton(); + } + + if (p_collision_polygon) { + + node=p_collision_polygon->cast_to<NavigationPolygonInstance>(); + if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw"); + wip.clear(); + wip_active=false; + edited_point=-1; + canvas_item_editor->get_viewport_control()->update(); + + } else { + node=NULL; + + if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw"); + + } + +} + +void NavigationPolygonEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_menu_option"),&NavigationPolygonEditor::_menu_option); + ObjectTypeDB::bind_method(_MD("_canvas_draw"),&NavigationPolygonEditor::_canvas_draw); + ObjectTypeDB::bind_method(_MD("_node_removed"),&NavigationPolygonEditor::_node_removed); + ObjectTypeDB::bind_method(_MD("_create_nav"),&NavigationPolygonEditor::_create_nav); + +} + +NavigationPolygonEditor::NavigationPolygonEditor(EditorNode *p_editor) { + node=NULL; + canvas_item_editor=NULL; + editor=p_editor; + undo_redo = editor->get_undo_redo(); + + add_child( memnew( VSeparator )); + button_create = memnew( ToolButton ); + add_child(button_create); + button_create->connect("pressed",this,"_menu_option",varray(MODE_CREATE)); + button_create->set_toggle_mode(true); + button_create->set_tooltip("Create a new polygon from scratch"); + + button_edit = memnew( ToolButton ); + add_child(button_edit); + button_edit->connect("pressed",this,"_menu_option",varray(MODE_EDIT)); + button_edit->set_toggle_mode(true); + button_edit->set_tooltip("Edit existing polygon:\nLMB: Move Point.\nCtrl+LMB: Split Segment.\nRMB: Erase Point."); + create_nav = memnew( ConfirmationDialog ); + add_child(create_nav); + create_nav->get_ok()->set_text("Create"); + + + //add_constant_override("separation",0); + +#if 0 + options = memnew( MenuButton ); + add_child(options); + options->set_area_as_parent_rect(); + options->set_text("Polygon"); + //options->get_popup()->add_item("Parse BBCODE",PARSE_BBCODE); + options->get_popup()->connect("item_pressed", this,"_menu_option"); +#endif + + mode = MODE_EDIT; + wip_active=false; + edited_outline=-1; + +} + + +void NavigationPolygonEditorPlugin::edit(Object *p_object) { + + collision_polygon_editor->edit(p_object->cast_to<Node>()); +} + +bool NavigationPolygonEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("NavigationPolygonInstance"); +} + +void NavigationPolygonEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + collision_polygon_editor->show(); + } else { + + collision_polygon_editor->hide(); + collision_polygon_editor->edit(NULL); + } + +} + +NavigationPolygonEditorPlugin::NavigationPolygonEditorPlugin(EditorNode *p_node) { + + editor=p_node; + collision_polygon_editor = memnew( NavigationPolygonEditor(p_node) ); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor); + + collision_polygon_editor->hide(); + + + +} + + +NavigationPolygonEditorPlugin::~NavigationPolygonEditorPlugin() +{ +} + diff --git a/tools/editor/plugins/navigation_polygon_editor_plugin.h b/tools/editor/plugins/navigation_polygon_editor_plugin.h new file mode 100644 index 0000000000..f742cb011d --- /dev/null +++ b/tools/editor/plugins/navigation_polygon_editor_plugin.h @@ -0,0 +1,90 @@ +#ifndef NAVIGATIONPOLYGONEDITORPLUGIN_H +#define NAVIGATIONPOLYGONEDITORPLUGIN_H + + + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/2d/navigation_polygon.h" +#include "scene/gui/tool_button.h" +#include "scene/gui/button_group.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +class CanvasItemEditor; + +class NavigationPolygonEditor : public HBoxContainer { + + OBJ_TYPE(NavigationPolygonEditor, HBoxContainer ); + + UndoRedo *undo_redo; + enum Mode { + + MODE_CREATE, + MODE_EDIT, + + }; + + Mode mode; + + ToolButton *button_create; + ToolButton *button_edit; + + ConfirmationDialog *create_nav; + + CanvasItemEditor *canvas_item_editor; + EditorNode *editor; + Panel *panel; + NavigationPolygonInstance *node; + MenuButton *options; + + int edited_outline; + int edited_point; + Vector2 edited_point_pos; + DVector<Vector2> pre_move_edit; + Vector<Vector2> wip; + bool wip_active; + + + void _wip_close(); + void _canvas_draw(); + void _create_nav(); + + void _menu_option(int p_option); + +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + bool forward_input_event(const InputEvent& p_event); + void edit(Node *p_collision_polygon); + NavigationPolygonEditor(EditorNode *p_editor); +}; + +class NavigationPolygonEditorPlugin : public EditorPlugin { + + OBJ_TYPE( NavigationPolygonEditorPlugin, EditorPlugin ); + + NavigationPolygonEditor *collision_polygon_editor; + EditorNode *editor; + +public: + + virtual bool forward_input_event(const InputEvent& p_event) { return collision_polygon_editor->forward_input_event(p_event); } + + virtual String get_name() const { return "NavigationPolygonInstance"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_node); + virtual bool handles(Object *p_node) const; + virtual void make_visible(bool p_visible); + + NavigationPolygonEditorPlugin(EditorNode *p_node); + ~NavigationPolygonEditorPlugin(); + +}; + + +#endif // NAVIGATIONPOLYGONEDITORPLUGIN_H diff --git a/tools/editor/plugins/particles_2d_editor_plugin.cpp b/tools/editor/plugins/particles_2d_editor_plugin.cpp index a7adfcd172..dadfa8bfdc 100644 --- a/tools/editor/plugins/particles_2d_editor_plugin.cpp +++ b/tools/editor/plugins/particles_2d_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -146,6 +146,7 @@ void Particles2DEditorPlugin::_notification(int p_what) { if (p_what==NOTIFICATION_ENTER_TREE) { menu->get_popup()->connect("item_pressed",this,"_menu_callback"); + menu->set_icon(menu->get_popup()->get_icon("Particles2D","EditorIcons")); file->connect("file_selected",this,"_file_selected"); } } @@ -172,14 +173,14 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) { menu->get_popup()->add_item("Clear Emission Mask",MENU_CLEAR_EMISSION_MASK); menu->set_text("Particles"); - file = memnew(FileDialog); + file = memnew(EditorFileDialog); add_child(file); List<String> ext; ImageLoader::get_recognized_extensions(&ext); for(List<String>::Element *E=ext.front();E;E=E->next()) { file->add_filter("*."+E->get()+"; "+E->get().to_upper()); } - file->set_mode(FileDialog::MODE_OPEN_FILE); + file->set_mode(EditorFileDialog::MODE_OPEN_FILE); CanvasItemEditor::get_singleton()->add_control_to_menu_panel(menu); epoints = memnew( SpinBox ); epoints->set_min(1); diff --git a/tools/editor/plugins/particles_2d_editor_plugin.h b/tools/editor/plugins/particles_2d_editor_plugin.h index b824774d0e..dba0bb4dae 100644 --- a/tools/editor/plugins/particles_2d_editor_plugin.h +++ b/tools/editor/plugins/particles_2d_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,57 +26,57 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef PARTICLES_2D_EDITOR_PLUGIN_H
-#define PARTICLES_2D_EDITOR_PLUGIN_H
-
-#include "tools/editor/editor_plugin.h"
-#include "tools/editor/editor_node.h"
-#include "scene/2d/collision_polygon_2d.h"
-
-#include "scene/gui/separator.h"
-#include "scene/gui/file_dialog.h"
-#include "scene/2d/particles_2d.h"
-
-class Particles2DEditorPlugin : public EditorPlugin {
-
- OBJ_TYPE( Particles2DEditorPlugin, EditorPlugin );
-
- enum {
-
- MENU_LOAD_EMISSION_MASK,
- MENU_CLEAR_EMISSION_MASK
- };
-
-
- FileDialog *file;
- EditorNode *editor;
-
- MenuButton *menu;
-
- VSeparator *sep;
- Particles2D *particles;
- SpinBox *epoints;
-
- UndoRedo *undo_redo;
- void _file_selected(const String& p_file);
- void _menu_callback(int p_idx);
-protected:
- void _notification(int p_what);
- static void _bind_methods();
-
-public:
-
-
- virtual String get_name() const { return "Particles2D"; }
- bool has_main_screen() const { return false; }
- virtual void edit(Object *p_node);
- virtual bool handles(Object *p_node) const;
- virtual void make_visible(bool p_visible);
-
- Particles2DEditorPlugin(EditorNode *p_node);
- ~Particles2DEditorPlugin();
-
-};
-
-
-#endif // PARTICLES_2D_EDITOR_PLUGIN_H
+#ifndef PARTICLES_2D_EDITOR_PLUGIN_H +#define PARTICLES_2D_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/2d/collision_polygon_2d.h" + +#include "scene/gui/separator.h" +#include "scene/gui/file_dialog.h" +#include "scene/2d/particles_2d.h" + +class Particles2DEditorPlugin : public EditorPlugin { + + OBJ_TYPE( Particles2DEditorPlugin, EditorPlugin ); + + enum { + + MENU_LOAD_EMISSION_MASK, + MENU_CLEAR_EMISSION_MASK + }; + + + EditorFileDialog *file; + EditorNode *editor; + + MenuButton *menu; + + VSeparator *sep; + Particles2D *particles; + SpinBox *epoints; + + UndoRedo *undo_redo; + void _file_selected(const String& p_file); + void _menu_callback(int p_idx); +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + + + virtual String get_name() const { return "Particles2D"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_node); + virtual bool handles(Object *p_node) const; + virtual void make_visible(bool p_visible); + + Particles2DEditorPlugin(EditorNode *p_node); + ~Particles2DEditorPlugin(); + +}; + + +#endif // PARTICLES_2D_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/particles_editor_plugin.cpp b/tools/editor/plugins/particles_editor_plugin.cpp index ebb45bc316..5c84d9a86a 100644 --- a/tools/editor/plugins/particles_editor_plugin.cpp +++ b/tools/editor/plugins/particles_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -58,7 +58,7 @@ void ParticlesEditor::_node_selected(const NodePath& p_path){ if (!vi) { err_dialog->set_text("Node does not contain geometry."); - err_dialog->popup_centered(Size2(300,100)); + err_dialog->popup_centered_minsize(); return; } @@ -67,7 +67,7 @@ void ParticlesEditor::_node_selected(const NodePath& p_path){ if (geometry.size()==0) { err_dialog->set_text("Node does not contain geometry (faces)."); - err_dialog->popup_centered(Size2(300,100)); + err_dialog->popup_centered_minsize(); return; } @@ -111,6 +111,7 @@ void ParticlesEditor::_populate() { void ParticlesEditor::_notification(int p_notification) { if (p_notification==NOTIFICATION_ENTER_TREE) { + options->set_icon(options->get_popup()->get_icon("Particles","EditorIcons")); } } @@ -219,7 +220,7 @@ void ParticlesEditor::_generate_emission_points() { if (!triangle_area_map.size() || area_accum==0) { err_dialog->set_text("Faces contain no area!"); - err_dialog->popup_centered(Size2(300,100)); + err_dialog->popup_centered_minsize(); return; } @@ -249,7 +250,7 @@ void ParticlesEditor::_generate_emission_points() { if (gcount==0) { err_dialog->set_text("No Faces!"); - err_dialog->popup_centered(Size2(300,100)); + err_dialog->popup_centered_minsize(); return; } @@ -394,7 +395,7 @@ ParticlesEditor::ParticlesEditor() { add_child(err_dialog); - emission_file_dialog = memnew( FileDialog ); + emission_file_dialog = memnew( EditorFileDialog ); add_child(emission_file_dialog); emission_file_dialog->connect("file_selected",this,"_resource_seleted"); emission_tree_dialog = memnew( SceneTreeDialog ); @@ -410,7 +411,7 @@ ParticlesEditor::ParticlesEditor() { emission_file_dialog->add_filter("*."+extensions[i]+" ; "+extensions[i].to_upper()); } - emission_file_dialog->set_mode(FileDialog::MODE_OPEN_FILE); + emission_file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); //options->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); //options->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); diff --git a/tools/editor/plugins/particles_editor_plugin.h b/tools/editor/plugins/particles_editor_plugin.h index 3e4b0f73aa..92756af1f6 100644 --- a/tools/editor/plugins/particles_editor_plugin.h +++ b/tools/editor/plugins/particles_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -48,7 +48,7 @@ class ParticlesEditor : public Control { Particles *node; - FileDialog *emission_file_dialog; + EditorFileDialog *emission_file_dialog; SceneTreeDialog *emission_tree_dialog; ConfirmationDialog *err_dialog; diff --git a/tools/editor/plugins/path_2d_editor_plugin.cpp b/tools/editor/plugins/path_2d_editor_plugin.cpp index 33ea5f3588..d037adc555 100644 --- a/tools/editor/plugins/path_2d_editor_plugin.cpp +++ b/tools/editor/plugins/path_2d_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -62,17 +62,6 @@ void Path2DEditor::_node_removed(Node *p_node) { } -Vector2 Path2DEditor::snap_point(const Vector2& p_point) const { - - if (canvas_item_editor->is_snap_active()) { - - return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap()); - - } else { - return p_point; - } -} - bool Path2DEditor::forward_input_event(const InputEvent& p_event) { if (!node) @@ -93,8 +82,8 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) { Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); Vector2 gpoint = Point2(mb.x,mb.y); - Vector2 cpoint = !mb.mod.alt? snap_point(xform.affine_inverse().xform(gpoint)) - : node->get_global_transform().affine_inverse().xform( snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) ); + Vector2 cpoint = !mb.mod.alt? canvas_item_editor->snap_point(xform.affine_inverse().xform(gpoint)) + : node->get_global_transform().affine_inverse().xform( canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) ); //first check if a point is to be added (segment split) real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8); @@ -195,7 +184,7 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) { Ref<Curve2D> curve = node->get_curve(); - Vector2 new_pos = moving_from + xform.basis_xform( gpoint - moving_screen_from ); + Vector2 new_pos = moving_from + xform.affine_inverse().basis_xform(gpoint - moving_screen_from); switch(action) { case ACTION_MOVING_POINT: { @@ -250,9 +239,9 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) { if (!wip_active) { wip.clear(); - wip.push_back( snap_point(cpoint) ); + wip.push_back( canvas_item_editor->snap_point(cpoint) ); wip_active=true; - edited_point_pos=snap_point(cpoint); + edited_point_pos=canvas_item_editor->snap_point(cpoint); canvas_item_editor->update(); edited_point=1; return true; @@ -265,7 +254,7 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) { return true; } else { - wip.push_back( snap_point(cpoint) ); + wip.push_back( canvas_item_editor->snap_point(cpoint) ); edited_point=wip.size(); canvas_item_editor->update(); return true; @@ -327,9 +316,9 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) { if (closest_idx>=0) { pre_move_edit=poly; - poly.insert(closest_idx+1,snap_point(xform.affine_inverse().xform(closest_pos))); + poly.insert(closest_idx+1,canvas_item_editor->snap_point(xform.affine_inverse().xform(closest_pos))); edited_point=closest_idx+1; - edited_point_pos=snap_point(xform.affine_inverse().xform(closest_pos)); + edited_point_pos=canvas_item_editor->snap_point(xform.affine_inverse().xform(closest_pos)); node->set_polygon(poly); canvas_item_editor->update(); return true; @@ -434,12 +423,12 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) { Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); Vector2 gpoint = Point2(mm.x,mm.y); - Vector2 cpoint = !mm.mod.alt? snap_point(xform.affine_inverse().xform(gpoint)) - : node->get_global_transform().affine_inverse().xform( snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) ); + Vector2 cpoint = !mm.mod.alt? canvas_item_editor->snap_point(xform.affine_inverse().xform(gpoint)) + : node->get_global_transform().affine_inverse().xform( canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) ); Ref<Curve2D> curve = node->get_curve(); - Vector2 new_pos = moving_from + xform.basis_xform( gpoint - moving_screen_from ); + Vector2 new_pos = moving_from + xform.affine_inverse().basis_xform(gpoint - moving_screen_from); switch(action) { @@ -471,7 +460,7 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) { Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); Vector2 gpoint = Point2(mm.x,mm.y); - edited_point_pos = snap_point(xform.affine_inverse().xform(gpoint)); + edited_point_pos = canvas_item_editor->snap_point(xform.affine_inverse().xform(gpoint)); canvas_item_editor->update(); } diff --git a/tools/editor/plugins/path_2d_editor_plugin.h b/tools/editor/plugins/path_2d_editor_plugin.h index 73de2cc838..9f15c0669f 100644 --- a/tools/editor/plugins/path_2d_editor_plugin.h +++ b/tools/editor/plugins/path_2d_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -94,7 +94,6 @@ protected: static void _bind_methods(); public: - Vector2 snap_point(const Vector2& p_point) const; bool forward_input_event(const InputEvent& p_event); void edit(Node *p_path2d); Path2DEditor(EditorNode *p_editor); diff --git a/tools/editor/plugins/path_editor_plugin.cpp b/tools/editor/plugins/path_editor_plugin.cpp index 3f540a3bf4..f4bdf50fe9 100644 --- a/tools/editor/plugins/path_editor_plugin.cpp +++ b/tools/editor/plugins/path_editor_plugin.cpp @@ -1,597 +1,597 @@ -/*************************************************************************/
-/* path_editor_plugin.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* http://www.godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
-/* */
-/* 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, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* 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.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-#include "path_editor_plugin.h"
-#include "spatial_editor_plugin.h"
-#include "scene/resources/curve.h"
-#include "os/keyboard.h"
-
-String PathSpatialGizmo::get_handle_name(int p_idx) const {
-
- Ref<Curve3D> c = path->get_curve();
- if (c.is_null())
- return "";
-
- if (p_idx<c->get_point_count()) {
-
- return "Curve Point #"+itos(p_idx);
- }
-
- p_idx=p_idx-c->get_point_count()+1;
-
- int idx=p_idx/2;
- int t=p_idx%2;
- String n = "Curve Point #"+itos(idx);
- if (t==0)
- n+=" In";
- else
- n+=" Out";
-
- return n;
-
-
-}
-Variant PathSpatialGizmo::get_handle_value(int p_idx) const{
-
- Ref<Curve3D> c = path->get_curve();
- if (c.is_null())
- return Variant();
-
- if (p_idx<c->get_point_count()) {
-
- original=c->get_point_pos(p_idx);
- return original;
- }
-
- p_idx=p_idx-c->get_point_count()+1;
-
- int idx=p_idx/2;
- int t=p_idx%2;
-
- Vector3 ofs;
- if (t==0)
- ofs=c->get_point_in(idx);
- else
- ofs= c->get_point_out(idx);
-
- original=ofs+c->get_point_pos(idx);
-
- return ofs;
-
-}
-void PathSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){
-
- Ref<Curve3D> c = path->get_curve();
- if (c.is_null())
- return;
-
- Transform gt = path->get_global_transform();
- Transform gi = gt.affine_inverse();
- Vector3 ray_from = p_camera->project_ray_origin(p_point);
- Vector3 ray_dir = p_camera->project_ray_normal(p_point);
-
- if (p_idx<c->get_point_count()) {
-
- Plane p(gt.xform(original),p_camera->get_transform().basis.get_axis(2));
-
- Vector3 inters;
-
- if (p.intersects_ray(ray_from,ray_dir,&inters)) {
-
- Vector3 local = gi.xform(inters);
- c->set_point_pos(p_idx,local);
- }
-
- return;
- }
-
- p_idx=p_idx-c->get_point_count()+1;
-
- int idx=p_idx/2;
- int t=p_idx%2;
-
- Vector3 base = c->get_point_pos(idx);
-
- Plane p(gt.xform(original),p_camera->get_transform().basis.get_axis(2));
-
- Vector3 inters;
-
- if (p.intersects_ray(ray_from,ray_dir,&inters)) {
-
- Vector3 local = gi.xform(inters)-base;
- if (t==0) {
- c->set_point_in(idx,local);
- } else {
- c->set_point_out(idx,local);
- }
- }
-
-}
-
-void PathSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
-
- Ref<Curve3D> c = path->get_curve();
- if (c.is_null())
- return;
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
-
- if (p_idx<c->get_point_count()) {
-
- if (p_cancel) {
-
- c->set_point_pos(p_idx,p_restore);
- return;
- }
- ur->create_action("Set Curve Point Pos");
- ur->add_do_method(c.ptr(),"set_point_pos",p_idx,c->get_point_pos(p_idx));
- ur->add_undo_method(c.ptr(),"set_point_pos",p_idx,p_restore);
- ur->commit_action();
-
- return;
- }
-
- p_idx=p_idx-c->get_point_count()+1;
-
- int idx=p_idx/2;
- int t=p_idx%2;
-
- Vector3 ofs;
-
- if (p_cancel) {
-
-
-
- return;
- }
-
-
-
- if (t==0) {
-
- if (p_cancel) {
-
- c->set_point_in(p_idx,p_restore);
- return;
- }
- ur->create_action("Set Curve In Pos");
- ur->add_do_method(c.ptr(),"set_point_in",idx,c->get_point_in(idx));
- ur->add_undo_method(c.ptr(),"set_point_in",idx,p_restore);
- ur->commit_action();
-
-
- } else {
- if (p_cancel) {
-
- c->set_point_out(idx,p_restore);
- return;
- }
- ur->create_action("Set Curve Out Pos");
- ur->add_do_method(c.ptr(),"set_point_out",idx,c->get_point_out(idx));
- ur->add_undo_method(c.ptr(),"set_point_out",idx,p_restore);
- ur->commit_action();
-
- }
-
-}
-
-
-void PathSpatialGizmo::redraw(){
-
- clear();
-
- Ref<Curve3D> c = path->get_curve();
- if (c.is_null())
- return;
-
- Vector3Array v3a=c->tesselate();
- //Vector3Array v3a=c->get_baked_points();
-
- int v3s = v3a.size();
- if (v3s==0)
- return;
- Vector<Vector3> v3p;
- Vector3Array::Read r = v3a.read();
-
- for(int i=0;i<v3s-1;i++) {
-
- v3p.push_back(r[i]);
- v3p.push_back(r[i+1]);
- //v3p.push_back(r[i]);
- //v3p.push_back(r[i]+Vector3(0,0.2,0));
- }
-
- add_lines(v3p,PathEditorPlugin::singleton->path_material);
- add_collision_segments(v3p);
-
- if (PathEditorPlugin::singleton->get_edited_path()==path) {
- v3p.clear();
- Vector<Vector3> handles;
- Vector<Vector3> sec_handles;
-
- for(int i=0;i<c->get_point_count();i++) {
-
- Vector3 p = c->get_point_pos(i);
- handles.push_back(p);
- if (i>0) {
- v3p.push_back(p);
- v3p.push_back(p+c->get_point_in(i));
- sec_handles.push_back(p+c->get_point_in(i));
- }
-
- if (i<c->get_point_count()-1) {
- v3p.push_back(p);
- v3p.push_back(p+c->get_point_out(i));
- sec_handles.push_back(p+c->get_point_out(i));
- }
- }
-
- add_lines(v3p,PathEditorPlugin::singleton->path_thin_material);
- add_handles(handles);
- add_handles(sec_handles,false,true);
- }
-
-}
-
-PathSpatialGizmo::PathSpatialGizmo(Path* p_path){
-
- path=p_path;
- set_spatial_node(p_path);
-
-
-
-}
-
-bool PathEditorPlugin::create_spatial_gizmo(Spatial* p_spatial) {
-
- if (p_spatial->cast_to<Path>()) {
-
-
- Ref<PathSpatialGizmo> psg = memnew( PathSpatialGizmo(p_spatial->cast_to<Path>()));
- p_spatial->set_gizmo(psg);
- return true;
- }
-
- return false;
-}
-
-bool PathEditorPlugin::forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) {
-
- if (!path)
- return false;
- Ref<Curve3D> c=path->get_curve();
- if (c.is_null())
- return false;
- Transform gt = path->get_global_transform();
- Transform it = gt.affine_inverse();
-
- static const int click_dist = 10; //should make global
-
-
- if (p_event.type==InputEvent::MOUSE_BUTTON) {
-
- const InputEventMouseButton &mb=p_event.mouse_button;
- Point2 mbpos(mb.x,mb.y);
-
- if (mb.pressed && mb.button_index==BUTTON_LEFT && (curve_create->is_pressed() || (curve_edit->is_pressed() && mb.mod.control))) {
- //click into curve, break it down
- Vector3Array v3a = c->tesselate();
- int idx=0;
- int rc=v3a.size();
- int closest_seg=-1;
- Vector3 closest_seg_point;
- float closest_d=1e20;
-
- if (rc>=2) {
- Vector3Array::Read r = v3a.read();
-
- if (p_camera->unproject_position(gt.xform(c->get_point_pos(0))).distance_to(mbpos)<click_dist)
- return false; //nope, existing
-
-
- for(int i=0;i<c->get_point_count()-1;i++) {
- //find the offset and point index of the place to break up
- int j=idx;
- if (p_camera->unproject_position(gt.xform(c->get_point_pos(i+1))).distance_to(mbpos)<click_dist)
- return false; //nope, existing
-
-
- while(j<rc && c->get_point_pos(i+1)!=r[j]) {
-
- Vector3 from =r[j];
- Vector3 to =r[j+1];
- real_t cdist = from.distance_to(to);
- from=gt.xform(from);
- to=gt.xform(to);
- if (cdist>0) {
- Vector2 s[2];
- s[0] = p_camera->unproject_position(from);
- s[1] = p_camera->unproject_position(to);
- Vector2 inters = Geometry::get_closest_point_to_segment_2d(mbpos,s);
- float d = inters.distance_to(mbpos);
-
- if (d<10 && d<closest_d) {
-
-
- closest_d=d;
- closest_seg=i;
- Vector3 ray_from=p_camera->project_ray_origin(mbpos);
- Vector3 ray_dir=p_camera->project_ray_normal(mbpos);
-
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(ray_from,ray_from+ray_dir*4096,from,to,ra,rb);
-
- closest_seg_point=it.xform(rb);
- }
-
- }
- j++;
-
- }
- if (idx==j)
- idx++; //force next
- else
- idx=j; //swap
-
-
- if (j==rc)
- break;
- }
- }
-
- UndoRedo *ur = editor->get_undo_redo();
- if (closest_seg!=-1) {
- //subdivide
-
- ur->create_action("Split Path");
- ur->add_do_method(c.ptr(),"add_point",closest_seg_point,Vector3(),Vector3(),closest_seg+1);
- ur->add_undo_method(c.ptr(),"remove_point",closest_seg+1);
- ur->commit_action();;
- return true;
-
- } else {
-
- Vector3 org;
- if (c->get_point_count()==0)
- org=path->get_transform().get_origin();
- else
- org=gt.xform(c->get_point_pos(c->get_point_count()));
- Plane p(org,p_camera->get_transform().basis.get_axis(2));
- Vector3 ray_from=p_camera->project_ray_origin(mbpos);
- Vector3 ray_dir=p_camera->project_ray_normal(mbpos);
-
- Vector3 inters;
- if (p.intersects_ray(ray_from,ray_dir,&inters)) {
-
- ur->create_action("Add Point to Curve");
- ur->add_do_method(c.ptr(),"add_point",it.xform(inters),Vector3(),Vector3(),-1);
- ur->add_undo_method(c.ptr(),"remove_point",c->get_point_count());
- ur->commit_action();;
- return true;
- }
-
- //add new at pos
- }
-
- } else if (mb.pressed && ((mb.button_index==BUTTON_LEFT && curve_del->is_pressed()) || (mb.button_index==BUTTON_RIGHT && curve_edit->is_pressed()))) {
-
- int erase_idx=-1;
- for(int i=0;i<c->get_point_count();i++) {
- //find the offset and point index of the place to break up
- if (p_camera->unproject_position(gt.xform(c->get_point_pos(i))).distance_to(mbpos)<click_dist) {
-
- erase_idx=i;
- break;
- }
- }
-
- if (erase_idx!=-1) {
-
- UndoRedo *ur = editor->get_undo_redo();
- ur->create_action("Remove Path Point");
- ur->add_do_method(c.ptr(),"remove_point",erase_idx);
- ur->add_undo_method(c.ptr(),"add_point",c->get_point_pos(erase_idx),c->get_point_in(erase_idx),c->get_point_out(erase_idx),erase_idx);
- ur->commit_action();
- return true;
- }
- }
-
- }
-
- return false;
-}
-
-
-void PathEditorPlugin::edit(Object *p_object) {
-
- if (p_object) {
- path=p_object->cast_to<Path>();
- if (path) {
-
- if (path->get_curve().is_valid()) {
- path->get_curve()->emit_signal("changed");
- }
- }
- } else {
- Path *pre=path;
- path=NULL;
- if (pre) {
- pre->get_curve()->emit_signal("changed");
- }
- }
-// collision_polygon_editor->edit(p_object->cast_to<Node>());
-}
-
-bool PathEditorPlugin::handles(Object *p_object) const {
-
- return p_object->is_type("Path");
-}
-
-void PathEditorPlugin::make_visible(bool p_visible) {
-
- if (p_visible) {
-
- curve_create->show();
- curve_edit->show();
- curve_del->show();
- curve_close->show();
- sep->show();
- } else {
-
- curve_create->hide();
- curve_edit->hide();
- curve_del->hide();
- curve_close->hide();
- sep->hide();
-
- {
- Path *pre=path;
- path=NULL;
- if (pre && pre->get_curve().is_valid()) {
- pre->get_curve()->emit_signal("changed");
- }
- }
- }
-
-}
-
-void PathEditorPlugin::_mode_changed(int p_idx) {
-
- curve_create->set_pressed(p_idx==0);
- curve_edit->set_pressed(p_idx==1);
- curve_del->set_pressed(p_idx==2);
-}
-
-void PathEditorPlugin::_close_curve() {
-
- Ref<Curve3D> c = path->get_curve();
- if (c.is_null())
- return ;
- if (c->get_point_count()<2)
- return;
- c->add_point(c->get_point_pos(0),c->get_point_in(0),c->get_point_out(0));
-
-}
-
-void PathEditorPlugin::_notification(int p_what) {
-
- if (p_what==NOTIFICATION_ENTER_TREE) {
-
- curve_create->connect("pressed",this,"_mode_changed",make_binds(0));
- curve_edit->connect("pressed",this,"_mode_changed",make_binds(1));
- curve_del->connect("pressed",this,"_mode_changed",make_binds(2));
- curve_close->connect("pressed",this,"_close_curve");
- }
-}
-
-void PathEditorPlugin::_bind_methods() {
-
- ObjectTypeDB::bind_method(_MD("_mode_changed"),&PathEditorPlugin::_mode_changed);
- ObjectTypeDB::bind_method(_MD("_close_curve"),&PathEditorPlugin::_close_curve);
-}
-
-PathEditorPlugin* PathEditorPlugin::singleton=NULL;
-
-
-PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) {
-
- path=NULL;
- editor=p_node;
- singleton=this;
-
- path_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
- path_material->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.5,0.5,1.0,0.8) );
- path_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- path_material->set_line_width(3);
- path_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
- path_material->set_flag(Material::FLAG_UNSHADED,true);
-
- path_thin_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
- path_thin_material->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.5,0.5,1.0,0.4) );
- path_thin_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- path_thin_material->set_line_width(1);
- path_thin_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
- path_thin_material->set_flag(Material::FLAG_UNSHADED,true);
-
- SpatialEditor::get_singleton()->add_gizmo_plugin(this);
-
- sep = memnew( VSeparator);
- sep->hide();
- SpatialEditor::get_singleton()->add_control_to_menu_panel(sep);
- curve_edit = memnew( ToolButton );
- curve_edit->set_icon(SpatialEditor::get_singleton()->get_icon("CurveEdit","EditorIcons"));
- curve_edit->set_toggle_mode(true);
- curve_edit->hide();
- curve_edit->set_focus_mode(Control::FOCUS_NONE);
- curve_edit->set_tooltip("Select Points\nShift+Drag: Select Control Points\n"+keycode_get_string(KEY_MASK_CMD)+"Click: Add Point\nRight Click: Delete Point.");
- SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_edit);
- curve_create = memnew( ToolButton );
- curve_create->set_icon(SpatialEditor::get_singleton()->get_icon("CurveCreate","EditorIcons"));
- curve_create->set_toggle_mode(true);
- curve_create->hide();
- curve_create->set_focus_mode(Control::FOCUS_NONE);
- curve_create->set_tooltip("Add Point (in empty space)\nSplit Segment (in curve).");
- SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_create);
- curve_del = memnew( ToolButton );
- curve_del->set_icon(SpatialEditor::get_singleton()->get_icon("CurveDelete","EditorIcons"));
- curve_del->set_toggle_mode(true);
- curve_del->hide();
- curve_del->set_focus_mode(Control::FOCUS_NONE);
- curve_del->set_tooltip("Delete Point.");
- SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_del);
- curve_close = memnew( ToolButton );
- curve_close->set_icon(SpatialEditor::get_singleton()->get_icon("CurveClose","EditorIcons"));
- curve_close->hide();
- curve_close->set_focus_mode(Control::FOCUS_NONE);
- curve_close->set_tooltip("Close Curve");
- SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_close);
-
-
-
- curve_edit->set_pressed(true);
- /*
- collision_polygon_editor = memnew( PathEditor(p_node) );
- editor->get_viewport()->add_child(collision_polygon_editor);
-
- collision_polygon_editor->set_margin(MARGIN_LEFT,200);
- collision_polygon_editor->set_margin(MARGIN_RIGHT,230);
- collision_polygon_editor->set_margin(MARGIN_TOP,0);
- collision_polygon_editor->set_margin(MARGIN_BOTTOM,10);
-
-
- collision_polygon_editor->hide();
- */
-
-
-}
-
-
-PathEditorPlugin::~PathEditorPlugin()
-{
-}
-
+/*************************************************************************/ +/* path_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "path_editor_plugin.h" +#include "spatial_editor_plugin.h" +#include "scene/resources/curve.h" +#include "os/keyboard.h" + +String PathSpatialGizmo::get_handle_name(int p_idx) const { + + Ref<Curve3D> c = path->get_curve(); + if (c.is_null()) + return ""; + + if (p_idx<c->get_point_count()) { + + return "Curve Point #"+itos(p_idx); + } + + p_idx=p_idx-c->get_point_count()+1; + + int idx=p_idx/2; + int t=p_idx%2; + String n = "Curve Point #"+itos(idx); + if (t==0) + n+=" In"; + else + n+=" Out"; + + return n; + + +} +Variant PathSpatialGizmo::get_handle_value(int p_idx) const{ + + Ref<Curve3D> c = path->get_curve(); + if (c.is_null()) + return Variant(); + + if (p_idx<c->get_point_count()) { + + original=c->get_point_pos(p_idx); + return original; + } + + p_idx=p_idx-c->get_point_count()+1; + + int idx=p_idx/2; + int t=p_idx%2; + + Vector3 ofs; + if (t==0) + ofs=c->get_point_in(idx); + else + ofs= c->get_point_out(idx); + + original=ofs+c->get_point_pos(idx); + + return ofs; + +} +void PathSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){ + + Ref<Curve3D> c = path->get_curve(); + if (c.is_null()) + return; + + Transform gt = path->get_global_transform(); + Transform gi = gt.affine_inverse(); + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + if (p_idx<c->get_point_count()) { + + Plane p(gt.xform(original),p_camera->get_transform().basis.get_axis(2)); + + Vector3 inters; + + if (p.intersects_ray(ray_from,ray_dir,&inters)) { + + Vector3 local = gi.xform(inters); + c->set_point_pos(p_idx,local); + } + + return; + } + + p_idx=p_idx-c->get_point_count()+1; + + int idx=p_idx/2; + int t=p_idx%2; + + Vector3 base = c->get_point_pos(idx); + + Plane p(gt.xform(original),p_camera->get_transform().basis.get_axis(2)); + + Vector3 inters; + + if (p.intersects_ray(ray_from,ray_dir,&inters)) { + + Vector3 local = gi.xform(inters)-base; + if (t==0) { + c->set_point_in(idx,local); + } else { + c->set_point_out(idx,local); + } + } + +} + +void PathSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){ + + Ref<Curve3D> c = path->get_curve(); + if (c.is_null()) + return; + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + + if (p_idx<c->get_point_count()) { + + if (p_cancel) { + + c->set_point_pos(p_idx,p_restore); + return; + } + ur->create_action("Set Curve Point Pos"); + ur->add_do_method(c.ptr(),"set_point_pos",p_idx,c->get_point_pos(p_idx)); + ur->add_undo_method(c.ptr(),"set_point_pos",p_idx,p_restore); + ur->commit_action(); + + return; + } + + p_idx=p_idx-c->get_point_count()+1; + + int idx=p_idx/2; + int t=p_idx%2; + + Vector3 ofs; + + if (p_cancel) { + + + + return; + } + + + + if (t==0) { + + if (p_cancel) { + + c->set_point_in(p_idx,p_restore); + return; + } + ur->create_action("Set Curve In Pos"); + ur->add_do_method(c.ptr(),"set_point_in",idx,c->get_point_in(idx)); + ur->add_undo_method(c.ptr(),"set_point_in",idx,p_restore); + ur->commit_action(); + + + } else { + if (p_cancel) { + + c->set_point_out(idx,p_restore); + return; + } + ur->create_action("Set Curve Out Pos"); + ur->add_do_method(c.ptr(),"set_point_out",idx,c->get_point_out(idx)); + ur->add_undo_method(c.ptr(),"set_point_out",idx,p_restore); + ur->commit_action(); + + } + +} + + +void PathSpatialGizmo::redraw(){ + + clear(); + + Ref<Curve3D> c = path->get_curve(); + if (c.is_null()) + return; + + Vector3Array v3a=c->tesselate(); + //Vector3Array v3a=c->get_baked_points(); + + int v3s = v3a.size(); + if (v3s==0) + return; + Vector<Vector3> v3p; + Vector3Array::Read r = v3a.read(); + + for(int i=0;i<v3s-1;i++) { + + v3p.push_back(r[i]); + v3p.push_back(r[i+1]); + //v3p.push_back(r[i]); + //v3p.push_back(r[i]+Vector3(0,0.2,0)); + } + + add_lines(v3p,PathEditorPlugin::singleton->path_material); + add_collision_segments(v3p); + + if (PathEditorPlugin::singleton->get_edited_path()==path) { + v3p.clear(); + Vector<Vector3> handles; + Vector<Vector3> sec_handles; + + for(int i=0;i<c->get_point_count();i++) { + + Vector3 p = c->get_point_pos(i); + handles.push_back(p); + if (i>0) { + v3p.push_back(p); + v3p.push_back(p+c->get_point_in(i)); + sec_handles.push_back(p+c->get_point_in(i)); + } + + if (i<c->get_point_count()-1) { + v3p.push_back(p); + v3p.push_back(p+c->get_point_out(i)); + sec_handles.push_back(p+c->get_point_out(i)); + } + } + + add_lines(v3p,PathEditorPlugin::singleton->path_thin_material); + add_handles(handles); + add_handles(sec_handles,false,true); + } + +} + +PathSpatialGizmo::PathSpatialGizmo(Path* p_path){ + + path=p_path; + set_spatial_node(p_path); + + + +} + +bool PathEditorPlugin::create_spatial_gizmo(Spatial* p_spatial) { + + if (p_spatial->cast_to<Path>()) { + + + Ref<PathSpatialGizmo> psg = memnew( PathSpatialGizmo(p_spatial->cast_to<Path>())); + p_spatial->set_gizmo(psg); + return true; + } + + return false; +} + +bool PathEditorPlugin::forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) { + + if (!path) + return false; + Ref<Curve3D> c=path->get_curve(); + if (c.is_null()) + return false; + Transform gt = path->get_global_transform(); + Transform it = gt.affine_inverse(); + + static const int click_dist = 10; //should make global + + + if (p_event.type==InputEvent::MOUSE_BUTTON) { + + const InputEventMouseButton &mb=p_event.mouse_button; + Point2 mbpos(mb.x,mb.y); + + if (mb.pressed && mb.button_index==BUTTON_LEFT && (curve_create->is_pressed() || (curve_edit->is_pressed() && mb.mod.control))) { + //click into curve, break it down + Vector3Array v3a = c->tesselate(); + int idx=0; + int rc=v3a.size(); + int closest_seg=-1; + Vector3 closest_seg_point; + float closest_d=1e20; + + if (rc>=2) { + Vector3Array::Read r = v3a.read(); + + if (p_camera->unproject_position(gt.xform(c->get_point_pos(0))).distance_to(mbpos)<click_dist) + return false; //nope, existing + + + for(int i=0;i<c->get_point_count()-1;i++) { + //find the offset and point index of the place to break up + int j=idx; + if (p_camera->unproject_position(gt.xform(c->get_point_pos(i+1))).distance_to(mbpos)<click_dist) + return false; //nope, existing + + + while(j<rc && c->get_point_pos(i+1)!=r[j]) { + + Vector3 from =r[j]; + Vector3 to =r[j+1]; + real_t cdist = from.distance_to(to); + from=gt.xform(from); + to=gt.xform(to); + if (cdist>0) { + Vector2 s[2]; + s[0] = p_camera->unproject_position(from); + s[1] = p_camera->unproject_position(to); + Vector2 inters = Geometry::get_closest_point_to_segment_2d(mbpos,s); + float d = inters.distance_to(mbpos); + + if (d<10 && d<closest_d) { + + + closest_d=d; + closest_seg=i; + Vector3 ray_from=p_camera->project_ray_origin(mbpos); + Vector3 ray_dir=p_camera->project_ray_normal(mbpos); + + Vector3 ra,rb; + Geometry::get_closest_points_between_segments(ray_from,ray_from+ray_dir*4096,from,to,ra,rb); + + closest_seg_point=it.xform(rb); + } + + } + j++; + + } + if (idx==j) + idx++; //force next + else + idx=j; //swap + + + if (j==rc) + break; + } + } + + UndoRedo *ur = editor->get_undo_redo(); + if (closest_seg!=-1) { + //subdivide + + ur->create_action("Split Path"); + ur->add_do_method(c.ptr(),"add_point",closest_seg_point,Vector3(),Vector3(),closest_seg+1); + ur->add_undo_method(c.ptr(),"remove_point",closest_seg+1); + ur->commit_action();; + return true; + + } else { + + Vector3 org; + if (c->get_point_count()==0) + org=path->get_transform().get_origin(); + else + org=gt.xform(c->get_point_pos(c->get_point_count())); + Plane p(org,p_camera->get_transform().basis.get_axis(2)); + Vector3 ray_from=p_camera->project_ray_origin(mbpos); + Vector3 ray_dir=p_camera->project_ray_normal(mbpos); + + Vector3 inters; + if (p.intersects_ray(ray_from,ray_dir,&inters)) { + + ur->create_action("Add Point to Curve"); + ur->add_do_method(c.ptr(),"add_point",it.xform(inters),Vector3(),Vector3(),-1); + ur->add_undo_method(c.ptr(),"remove_point",c->get_point_count()); + ur->commit_action();; + return true; + } + + //add new at pos + } + + } else if (mb.pressed && ((mb.button_index==BUTTON_LEFT && curve_del->is_pressed()) || (mb.button_index==BUTTON_RIGHT && curve_edit->is_pressed()))) { + + int erase_idx=-1; + for(int i=0;i<c->get_point_count();i++) { + //find the offset and point index of the place to break up + if (p_camera->unproject_position(gt.xform(c->get_point_pos(i))).distance_to(mbpos)<click_dist) { + + erase_idx=i; + break; + } + } + + if (erase_idx!=-1) { + + UndoRedo *ur = editor->get_undo_redo(); + ur->create_action("Remove Path Point"); + ur->add_do_method(c.ptr(),"remove_point",erase_idx); + ur->add_undo_method(c.ptr(),"add_point",c->get_point_pos(erase_idx),c->get_point_in(erase_idx),c->get_point_out(erase_idx),erase_idx); + ur->commit_action(); + return true; + } + } + + } + + return false; +} + + +void PathEditorPlugin::edit(Object *p_object) { + + if (p_object) { + path=p_object->cast_to<Path>(); + if (path) { + + if (path->get_curve().is_valid()) { + path->get_curve()->emit_signal("changed"); + } + } + } else { + Path *pre=path; + path=NULL; + if (pre) { + pre->get_curve()->emit_signal("changed"); + } + } +// collision_polygon_editor->edit(p_object->cast_to<Node>()); +} + +bool PathEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("Path"); +} + +void PathEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + + curve_create->show(); + curve_edit->show(); + curve_del->show(); + curve_close->show(); + sep->show(); + } else { + + curve_create->hide(); + curve_edit->hide(); + curve_del->hide(); + curve_close->hide(); + sep->hide(); + + { + Path *pre=path; + path=NULL; + if (pre && pre->get_curve().is_valid()) { + pre->get_curve()->emit_signal("changed"); + } + } + } + +} + +void PathEditorPlugin::_mode_changed(int p_idx) { + + curve_create->set_pressed(p_idx==0); + curve_edit->set_pressed(p_idx==1); + curve_del->set_pressed(p_idx==2); +} + +void PathEditorPlugin::_close_curve() { + + Ref<Curve3D> c = path->get_curve(); + if (c.is_null()) + return ; + if (c->get_point_count()<2) + return; + c->add_point(c->get_point_pos(0),c->get_point_in(0),c->get_point_out(0)); + +} + +void PathEditorPlugin::_notification(int p_what) { + + if (p_what==NOTIFICATION_ENTER_TREE) { + + curve_create->connect("pressed",this,"_mode_changed",make_binds(0)); + curve_edit->connect("pressed",this,"_mode_changed",make_binds(1)); + curve_del->connect("pressed",this,"_mode_changed",make_binds(2)); + curve_close->connect("pressed",this,"_close_curve"); + } +} + +void PathEditorPlugin::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_mode_changed"),&PathEditorPlugin::_mode_changed); + ObjectTypeDB::bind_method(_MD("_close_curve"),&PathEditorPlugin::_close_curve); +} + +PathEditorPlugin* PathEditorPlugin::singleton=NULL; + + +PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) { + + path=NULL; + editor=p_node; + singleton=this; + + path_material = Ref<FixedMaterial>( memnew( FixedMaterial )); + path_material->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.5,0.5,1.0,0.8) ); + path_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + path_material->set_line_width(3); + path_material->set_flag(Material::FLAG_DOUBLE_SIDED,true); + path_material->set_flag(Material::FLAG_UNSHADED,true); + + path_thin_material = Ref<FixedMaterial>( memnew( FixedMaterial )); + path_thin_material->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.5,0.5,1.0,0.4) ); + path_thin_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + path_thin_material->set_line_width(1); + path_thin_material->set_flag(Material::FLAG_DOUBLE_SIDED,true); + path_thin_material->set_flag(Material::FLAG_UNSHADED,true); + + SpatialEditor::get_singleton()->add_gizmo_plugin(this); + + sep = memnew( VSeparator); + sep->hide(); + SpatialEditor::get_singleton()->add_control_to_menu_panel(sep); + curve_edit = memnew( ToolButton ); + curve_edit->set_icon(SpatialEditor::get_singleton()->get_icon("CurveEdit","EditorIcons")); + curve_edit->set_toggle_mode(true); + curve_edit->hide(); + curve_edit->set_focus_mode(Control::FOCUS_NONE); + curve_edit->set_tooltip("Select Points\nShift+Drag: Select Control Points\n"+keycode_get_string(KEY_MASK_CMD)+"Click: Add Point\nRight Click: Delete Point."); + SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_edit); + curve_create = memnew( ToolButton ); + curve_create->set_icon(SpatialEditor::get_singleton()->get_icon("CurveCreate","EditorIcons")); + curve_create->set_toggle_mode(true); + curve_create->hide(); + curve_create->set_focus_mode(Control::FOCUS_NONE); + curve_create->set_tooltip("Add Point (in empty space)\nSplit Segment (in curve)."); + SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_create); + curve_del = memnew( ToolButton ); + curve_del->set_icon(SpatialEditor::get_singleton()->get_icon("CurveDelete","EditorIcons")); + curve_del->set_toggle_mode(true); + curve_del->hide(); + curve_del->set_focus_mode(Control::FOCUS_NONE); + curve_del->set_tooltip("Delete Point."); + SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_del); + curve_close = memnew( ToolButton ); + curve_close->set_icon(SpatialEditor::get_singleton()->get_icon("CurveClose","EditorIcons")); + curve_close->hide(); + curve_close->set_focus_mode(Control::FOCUS_NONE); + curve_close->set_tooltip("Close Curve"); + SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_close); + + + + curve_edit->set_pressed(true); + /* + collision_polygon_editor = memnew( PathEditor(p_node) ); + editor->get_viewport()->add_child(collision_polygon_editor); + + collision_polygon_editor->set_margin(MARGIN_LEFT,200); + collision_polygon_editor->set_margin(MARGIN_RIGHT,230); + collision_polygon_editor->set_margin(MARGIN_TOP,0); + collision_polygon_editor->set_margin(MARGIN_BOTTOM,10); + + + collision_polygon_editor->hide(); + */ + + +} + + +PathEditorPlugin::~PathEditorPlugin() +{ +} + diff --git a/tools/editor/plugins/path_editor_plugin.h b/tools/editor/plugins/path_editor_plugin.h index d730d33551..fcd4241e59 100644 --- a/tools/editor/plugins/path_editor_plugin.h +++ b/tools/editor/plugins/path_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/plugins/polygon_2d_editor_plugin.cpp b/tools/editor/plugins/polygon_2d_editor_plugin.cpp index 27e539d50b..cd82297365 100644 --- a/tools/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/tools/editor/plugins/polygon_2d_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -50,6 +50,11 @@ void Polygon2DEditor::_notification(int p_what) { uv_button[UV_MODE_ROTATE]->set_icon(get_icon("ToolRotate","EditorIcons")); uv_button[UV_MODE_SCALE]->set_icon(get_icon("ToolScale","EditorIcons")); + b_snap_grid->set_icon( get_icon("Grid", "EditorIcons")); + b_snap_enable->set_icon( get_icon("Snap", "EditorIcons")); + uv_icon_zoom->set_texture( get_icon("Zoom", "EditorIcons")); + + get_tree()->connect("node_removed", this, "_node_removed"); } break; case NOTIFICATION_FIXED_PROCESS: { @@ -62,24 +67,15 @@ void Polygon2DEditor::_notification(int p_what) { void Polygon2DEditor::_node_removed(Node *p_node) { if(p_node==node) { - node=NULL; + edit(NULL); hide(); + + canvas_item_editor->get_viewport_control()->update(); } } -Vector2 Polygon2DEditor::snap_point(const Vector2& p_point) const { - - if (canvas_item_editor->is_snap_active()) { - - return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap()); - - } else { - return p_point; - } -} - void Polygon2DEditor::_menu_option(int p_option) { switch(p_option) { @@ -101,7 +97,7 @@ void Polygon2DEditor::_menu_option(int p_option) { if (node->get_texture().is_null()) { error->set_text("No texture in this polygon.\nSet a texture to be able to edit UV."); - error->popup_centered_minsize(Size2(300,70)); + error->popup_centered_minsize(); return; } @@ -169,6 +165,41 @@ void Polygon2DEditor::_menu_option(int p_option) { } } +void Polygon2DEditor::_set_use_snap(bool p_use) +{ + use_snap=p_use; +} + +void Polygon2DEditor::_set_show_grid(bool p_show) +{ + snap_show_grid=p_show; + uv_edit_draw->update(); +} + +void Polygon2DEditor::_set_snap_off_x(float p_val) +{ + snap_offset.x=p_val; + uv_edit_draw->update(); +} + +void Polygon2DEditor::_set_snap_off_y(float p_val) +{ + snap_offset.y=p_val; + uv_edit_draw->update(); +} + +void Polygon2DEditor::_set_snap_step_x(float p_val) +{ + snap_step.x=p_val; + uv_edit_draw->update(); +} + +void Polygon2DEditor::_set_snap_step_y(float p_val) +{ + snap_step.y=p_val; + uv_edit_draw->update(); +} + void Polygon2DEditor::_wip_close() { undo_redo->create_action("Create Poly"); @@ -201,7 +232,7 @@ bool Polygon2DEditor::forward_input_event(const InputEvent& p_event) { Vector2 gpoint = Point2(mb.x,mb.y); Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); - cpoint=snap_point(cpoint); + cpoint=canvas_item_editor->snap_point(cpoint); cpoint = node->get_global_transform().affine_inverse().xform(cpoint); @@ -405,7 +436,7 @@ bool Polygon2DEditor::forward_input_event(const InputEvent& p_event) { Vector2 gpoint = Point2(mm.x,mm.y); Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); - cpoint=snap_point(cpoint); + cpoint=canvas_item_editor->snap_point(cpoint); edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint); canvas_item_editor->get_viewport_control()->update(); @@ -505,7 +536,7 @@ void Polygon2DEditor::_uv_input(const InputEvent& p_input) { Vector2 tuv=mtx.xform(uv_prev[i]); if (tuv.distance_to(Vector2(mb.x,mb.y))<8) { - + uv_drag_from=tuv; uv_drag_index=i; } } @@ -556,7 +587,7 @@ void Polygon2DEditor::_uv_input(const InputEvent& p_input) { } else if (uv_drag) { - Vector2 uv_drag_to(mm.x,mm.y); + Vector2 uv_drag_to=snap_point(Vector2(mm.x,mm.y)); Vector2 drag = mtx.affine_inverse().xform(uv_drag_to) - mtx.affine_inverse().xform(uv_drag_from); @@ -660,6 +691,33 @@ void Polygon2DEditor::_uv_draw() { uv_edit_draw->draw_texture(base_tex,Point2()); VS::get_singleton()->canvas_item_add_set_transform(uv_edit_draw->get_canvas_item(),Matrix32()); + if (snap_show_grid) { + Size2 s = uv_edit_draw->get_size(); + int last_cell; + + if (snap_step.x!=0) { + for(int i=0;i<s.width;i++) { + int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(i,0)).x-snap_offset.x)/snap_step.x)); + if (i==0) + last_cell=cell; + if (last_cell!=cell) + uv_edit_draw->draw_line(Point2(i,0),Point2(i,s.height),Color(0.3,0.7,1,0.3)); + last_cell=cell; + } + } + + if (snap_step.y!=0) { + for(int i=0;i<s.height;i++) { + int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(0,i)).y-snap_offset.y)/snap_step.y)); + if (i==0) + last_cell=cell; + if (last_cell!=cell) + uv_edit_draw->draw_line(Point2(0,i),Point2(s.width,i),Color(0.3,0.7,1,0.3)); + last_cell=cell; + } + } + } + DVector<Vector2> uvs = node->get_uv(); Ref<Texture> handle = get_icon("EditorHandle","EditorIcons"); @@ -703,16 +761,13 @@ void Polygon2DEditor::edit(Node *p_collision_polygon) { node=p_collision_polygon->cast_to<Polygon2D>(); if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw"); - node->connect("exit_tree",this,"_node_removed",varray(),CONNECT_ONESHOT); + wip.clear(); wip_active=false; edited_point=-1; } else { - if (node) - node->disconnect("exit_tree",this,"_node_removed"); - node=NULL; if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) @@ -731,8 +786,27 @@ void Polygon2DEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("_uv_input"),&Polygon2DEditor::_uv_input); ObjectTypeDB::bind_method(_MD("_uv_scroll_changed"),&Polygon2DEditor::_uv_scroll_changed); ObjectTypeDB::bind_method(_MD("_node_removed"),&Polygon2DEditor::_node_removed); + ObjectTypeDB::bind_method(_MD("_set_use_snap"),&Polygon2DEditor::_set_use_snap); + ObjectTypeDB::bind_method(_MD("_set_show_grid"),&Polygon2DEditor::_set_show_grid); + ObjectTypeDB::bind_method(_MD("_set_snap_off_x"),&Polygon2DEditor::_set_snap_off_x); + ObjectTypeDB::bind_method(_MD("_set_snap_off_y"),&Polygon2DEditor::_set_snap_off_y); + ObjectTypeDB::bind_method(_MD("_set_snap_step_x"),&Polygon2DEditor::_set_snap_step_x); + ObjectTypeDB::bind_method(_MD("_set_snap_step_y"),&Polygon2DEditor::_set_snap_step_y); + +} +inline float _snap_scalar(float p_offset, float p_step, float p_target) { + return p_step != 0 ? Math::stepify(p_target - p_offset, p_step) + p_offset : p_target; +} + +Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const { + if (use_snap) { + p_target.x = _snap_scalar(snap_offset.x*uv_draw_zoom-uv_draw_ofs.x, snap_step.x*uv_draw_zoom, p_target.x); + p_target.y = _snap_scalar(snap_offset.y*uv_draw_zoom-uv_draw_ofs.y, snap_step.y*uv_draw_zoom, p_target.y); + } + + return p_target; } Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) { @@ -742,6 +816,10 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) { editor=p_editor; undo_redo = editor->get_undo_redo(); + snap_step=Vector2(10,10); + use_snap=false; + snap_show_grid=false; + add_child( memnew( VSeparator )); button_create = memnew( ToolButton ); add_child(button_create); @@ -811,9 +889,72 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) { uv_menu->get_popup()->add_separator(); uv_menu->get_popup()->add_item("Clear UV",UVEDIT_UV_CLEAR); uv_menu->get_popup()->connect("item_pressed",this,"_menu_option"); + + uv_mode_hb->add_child( memnew( VSeparator )); + + b_snap_enable = memnew( ToolButton ); + uv_mode_hb->add_child(b_snap_enable); + b_snap_enable->set_text("Snap"); + b_snap_enable->set_focus_mode(FOCUS_NONE); + b_snap_enable->set_toggle_mode(true); + b_snap_enable->set_pressed(use_snap); + b_snap_enable->set_tooltip("Enable Snap"); + b_snap_enable->connect("toggled",this,"_set_use_snap"); + + b_snap_grid = memnew( ToolButton ); + uv_mode_hb->add_child(b_snap_grid); + b_snap_grid->set_text("Grid"); + b_snap_grid->set_focus_mode(FOCUS_NONE); + b_snap_grid->set_toggle_mode(true); + b_snap_grid->set_pressed(snap_show_grid); + b_snap_grid->set_tooltip("Show Grid"); + b_snap_grid->connect("toggled",this,"_set_show_grid"); + + uv_mode_hb->add_child( memnew( VSeparator )); + uv_mode_hb->add_child( memnew( Label("Grid Offset:") ) ); + + SpinBox *sb_off_x = memnew( SpinBox ); + sb_off_x->set_min(-256); + sb_off_x->set_max(256); + sb_off_x->set_step(1); + sb_off_x->set_val(snap_offset.x); + sb_off_x->set_suffix("px"); + sb_off_x->connect("value_changed", this, "_set_snap_off_x"); + uv_mode_hb->add_child(sb_off_x); + + SpinBox *sb_off_y = memnew( SpinBox ); + sb_off_y->set_min(-256); + sb_off_y->set_max(256); + sb_off_y->set_step(1); + sb_off_y->set_val(snap_offset.y); + sb_off_y->set_suffix("px"); + sb_off_y->connect("value_changed", this, "_set_snap_off_y"); + uv_mode_hb->add_child(sb_off_y); + + uv_mode_hb->add_child( memnew( VSeparator )); + uv_mode_hb->add_child( memnew( Label("Grid Step:") ) ); + + SpinBox *sb_step_x = memnew( SpinBox ); + sb_step_x->set_min(-256); + sb_step_x->set_max(256); + sb_step_x->set_step(1); + sb_step_x->set_val(snap_step.x); + sb_step_x->set_suffix("px"); + sb_step_x->connect("value_changed", this, "_set_snap_step_x"); + uv_mode_hb->add_child(sb_step_x); + + SpinBox *sb_step_y = memnew( SpinBox ); + sb_step_y->set_min(-256); + sb_step_y->set_max(256); + sb_step_y->set_step(1); + sb_step_y->set_val(snap_step.y); + sb_step_y->set_suffix("px"); + sb_step_y->connect("value_changed", this, "_set_snap_step_y"); + uv_mode_hb->add_child(sb_step_y); + uv_mode_hb->add_child( memnew( VSeparator )); uv_icon_zoom = memnew( TextureFrame ); - uv_main_hb->add_child( uv_icon_zoom ); + uv_mode_hb->add_child( uv_icon_zoom ); uv_zoom = memnew( HSlider ); uv_zoom->set_min(0.01); uv_zoom->set_max(4); diff --git a/tools/editor/plugins/polygon_2d_editor_plugin.h b/tools/editor/plugins/polygon_2d_editor_plugin.h index 88d1c20493..0939c44264 100644 --- a/tools/editor/plugins/polygon_2d_editor_plugin.h +++ b/tools/editor/plugins/polygon_2d_editor_plugin.h @@ -41,6 +41,8 @@ class Polygon2DEditor : public HBoxContainer { UVMode uv_mode; AcceptDialog *uv_edit; ToolButton *uv_button[4]; + ToolButton *b_snap_enable; + ToolButton *b_snap_grid; Control *uv_edit_draw; HSlider *uv_zoom; SpinBox *uv_zoom_value; @@ -78,6 +80,11 @@ class Polygon2DEditor : public HBoxContainer { Vector<Vector2> wip; bool wip_active; + bool use_snap; + bool snap_show_grid; + Vector2 snap_offset; + Vector2 snap_step; + void _uv_scroll_changed(float); void _uv_input(const InputEvent& p_input); void _uv_draw(); @@ -86,13 +93,22 @@ class Polygon2DEditor : public HBoxContainer { void _canvas_draw(); void _menu_option(int p_option); + void _set_use_snap(bool p_use); + void _set_show_grid(bool p_show); + void _set_snap_off_x(float p_val); + void _set_snap_off_y(float p_val); + void _set_snap_step_x(float p_val); + void _set_snap_step_y(float p_val); + protected: void _notification(int p_what); void _node_removed(Node *p_node); static void _bind_methods(); + + Vector2 snap_point(Vector2 p_target) const; + public: - Vector2 snap_point(const Vector2& p_point) const; bool forward_input_event(const InputEvent& p_event); void edit(Node *p_collision_polygon); Polygon2DEditor(EditorNode *p_editor); diff --git a/tools/editor/plugins/resource_preloader_editor_plugin.cpp b/tools/editor/plugins/resource_preloader_editor_plugin.cpp index d9726cac21..9cd20ac53a 100644 --- a/tools/editor/plugins/resource_preloader_editor_plugin.cpp +++ b/tools/editor/plugins/resource_preloader_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -72,7 +72,7 @@ void ResourcePreloaderEditor::_file_load_request(const String& p_path) { dialog->set_title("Error!"); //dialog->get_cancel()->set_text("Close"); dialog->get_ok()->set_text("Close"); - dialog->popup_centered(Size2(300,60)); + dialog->popup_centered_minsize(); return; ///beh should show an error i guess } @@ -102,7 +102,7 @@ void ResourcePreloaderEditor::_load_pressed() { for(int i=0;i<extensions.size();i++) file->add_filter("*."+extensions[i]); - file->set_mode(FileDialog::MODE_OPEN_FILE); + file->set_mode(EditorFileDialog::MODE_OPEN_FILE); file->popup_centered_ratio(); @@ -167,7 +167,7 @@ void ResourcePreloaderEditor::_paste_pressed() { dialog->set_title("Error!"); //dialog->get_cancel()->set_text("Close"); dialog->get_ok()->set_text("Close"); - dialog->popup_centered(Size2(300,60)); + dialog->popup_centered_minsize(); return; ///beh should show an error i guess } @@ -310,7 +310,7 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() { paste->set_text("Paste"); hbc->add_child(paste); - file = memnew( FileDialog ); + file = memnew( EditorFileDialog ); add_child(file); diff --git a/tools/editor/plugins/resource_preloader_editor_plugin.h b/tools/editor/plugins/resource_preloader_editor_plugin.h index e3178bc8ff..88272bc603 100644 --- a/tools/editor/plugins/resource_preloader_editor_plugin.h +++ b/tools/editor/plugins/resource_preloader_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -49,7 +49,7 @@ class ResourcePreloaderEditor : public PanelContainer { bool loading_scene; - FileDialog *file; + EditorFileDialog *file; AcceptDialog *dialog; diff --git a/tools/editor/plugins/rich_text_editor_plugin.cpp b/tools/editor/plugins/rich_text_editor_plugin.cpp index 58b3636dcc..a0daad854f 100644 --- a/tools/editor/plugins/rich_text_editor_plugin.cpp +++ b/tools/editor/plugins/rich_text_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,6 +28,8 @@ /*************************************************************************/ #include "rich_text_editor_plugin.h" #include "os/file_access.h" +#include "canvas_item_editor_plugin.h" + void RichTextEditor::_notification(int p_what) { switch(p_what) { @@ -100,7 +102,8 @@ void RichTextEditor::edit(Node *p_rich_text) { RichTextEditor::RichTextEditor() { options = memnew( MenuButton ); - add_child(options); + //add_child(options); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(options); options->set_area_as_parent_rect(); options->set_text("RichText"); @@ -108,10 +111,10 @@ RichTextEditor::RichTextEditor() { options->get_popup()->add_item("Clear",CLEAR); options->get_popup()->connect("item_pressed", this,"_menu_option"); - file_dialog = memnew( FileDialog ); + file_dialog = memnew( EditorFileDialog ); add_child(file_dialog); file_dialog->add_filter("*.txt"); - file_dialog->set_mode(FileDialog::MODE_OPEN_FILE); + file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); file_dialog->connect("file_selected",this,"_file_selected"); } @@ -129,10 +132,10 @@ bool RichTextEditorPlugin::handles(Object *p_object) const { void RichTextEditorPlugin::make_visible(bool p_visible) { if (p_visible) { - rich_text_editor->show(); + rich_text_editor->options->show(); } else { - rich_text_editor->hide(); + rich_text_editor->options->hide(); rich_text_editor->edit(NULL); } @@ -149,10 +152,7 @@ RichTextEditorPlugin::RichTextEditorPlugin(EditorNode *p_node) { rich_text_editor->set_margin(MARGIN_TOP,0); rich_text_editor->set_margin(MARGIN_BOTTOM,10); - - rich_text_editor->hide(); - - + rich_text_editor->options->hide(); } diff --git a/tools/editor/plugins/rich_text_editor_plugin.h b/tools/editor/plugins/rich_text_editor_plugin.h index e51e0653b9..478dc0d308 100644 --- a/tools/editor/plugins/rich_text_editor_plugin.h +++ b/tools/editor/plugins/rich_text_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -42,6 +42,8 @@ class RichTextEditor : public Control { OBJ_TYPE(RichTextEditor, Control ); + friend class RichTextEditorPlugin; + enum { PARSE_BBCODE, @@ -49,9 +51,9 @@ class RichTextEditor : public Control { }; Panel *panel; + MenuButton *options; RichTextLabel *node; - MenuButton *options; - FileDialog *file_dialog; + EditorFileDialog *file_dialog; void _file_selected(const String& p_path); void _menu_option(int p_option); diff --git a/tools/editor/plugins/sample_editor_plugin.cpp b/tools/editor/plugins/sample_editor_plugin.cpp index 83adeee789..d88f2adc73 100644 --- a/tools/editor/plugins/sample_editor_plugin.cpp +++ b/tools/editor/plugins/sample_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -98,6 +98,131 @@ void SampleEditor::generate_preview_texture(const Ref<Sample>& p_sample,Ref<Imag if (p_sample->get_format()==Sample::FORMAT_IMA_ADPCM) { + struct IMA_ADPCM_State { + + int16_t step_index; + int32_t predictor; + /* values at loop point */ + int16_t loop_step_index; + int32_t loop_predictor; + int32_t last_nibble; + int32_t loop_pos; + int32_t window_ofs; + const uint8_t *ptr; + } ima_adpcm; + + ima_adpcm.step_index=0; + ima_adpcm.predictor=0; + ima_adpcm.loop_step_index=0; + ima_adpcm.loop_predictor=0; + ima_adpcm.last_nibble=-1; + ima_adpcm.loop_pos=0x7FFFFFFF; + ima_adpcm.window_ofs=0; + ima_adpcm.ptr=NULL; + + + for(int i=0;i<w;i++) { + + float max[2]={-1e10,-1e10}; + float min[2]={1e10,1e10}; + int from = i*len/w; + int to = (i+1)*len/w; + if (to>=len) + to=len-1; + + for(int j=from;j<to;j++) { + + while(j>ima_adpcm.last_nibble) { + + static const int16_t _ima_adpcm_step_table[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + }; + + static const int8_t _ima_adpcm_index_table[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 + }; + + int16_t nibble,signed_nibble,diff,step; + + ima_adpcm.last_nibble++; + const uint8_t *src_ptr=sdata; + + int ofs = ima_adpcm.last_nibble>>1; + + if (stereo) + ofs*=2; + + nibble = (ima_adpcm.last_nibble&1)? + (src_ptr[ofs]>>4):(src_ptr[ofs]&0xF); + + step=_ima_adpcm_step_table[ima_adpcm.step_index]; + + ima_adpcm.step_index += _ima_adpcm_index_table[nibble]; + if (ima_adpcm.step_index<0) + ima_adpcm.step_index=0; + if (ima_adpcm.step_index>88) + ima_adpcm.step_index=88; + + /* + signed_nibble = (nibble&7) * ((nibble&8)?-1:1); + diff = (2 * signed_nibble + 1) * step / 4; */ + + diff = step >> 3 ; + if (nibble & 1) + diff += step >> 2 ; + if (nibble & 2) + diff += step >> 1 ; + if (nibble & 4) + diff += step ; + if (nibble & 8) + diff = -diff ; + + ima_adpcm.predictor+=diff; + if (ima_adpcm.predictor<-0x8000) + ima_adpcm.predictor=-0x8000; + else if (ima_adpcm.predictor>0x7FFF) + ima_adpcm.predictor=0x7FFF; + + + /* store loop if there */ + if (ima_adpcm.last_nibble==ima_adpcm.loop_pos) { + + ima_adpcm.loop_step_index = ima_adpcm.step_index; + ima_adpcm.loop_predictor = ima_adpcm.predictor; + } + + } + + float v=ima_adpcm.predictor/32767.0; + if (v>max[0]) + max[0]=v; + if (v<min[0]) + min[0]=v; + } + + for(int j=0;j<h;j++) { + float v = (j/(float)h) * 2.0 - 1.0; + uint8_t* imgofs = &imgw[(j*w+i)*3]; + if (v>min[0] && v<max[0]) { + imgofs[0]=255; + imgofs[1]=150; + imgofs[2]=80; + } else { + imgofs[0]=0; + imgofs[1]=0; + imgofs[2]=0; + } + } + } } else { for(int i=0;i<w;i++) { // i trust gcc will optimize this loop diff --git a/tools/editor/plugins/sample_editor_plugin.h b/tools/editor/plugins/sample_editor_plugin.h index 78d5ed401c..e615667914 100644 --- a/tools/editor/plugins/sample_editor_plugin.h +++ b/tools/editor/plugins/sample_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/plugins/sample_library_editor_plugin.cpp b/tools/editor/plugins/sample_library_editor_plugin.cpp index 41c84f6e2c..b497458a2a 100644 --- a/tools/editor/plugins/sample_library_editor_plugin.cpp +++ b/tools/editor/plugins/sample_library_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -49,9 +49,13 @@ void SampleLibraryEditor::_notification(int p_what) { if (p_what==NOTIFICATION_ENTER_TREE) { play->set_icon( get_icon("Play","EditorIcons") ); + play->set_tooltip("Play Sample"); stop->set_icon( get_icon("Stop","EditorIcons") ); + stop->set_tooltip("Stop Sample"); load->set_icon( get_icon("Folder","EditorIcons") ); + load->set_tooltip("Open Sample File(s)"); _delete->set_icon( get_icon("Del","EditorIcons") ); + _delete->set_tooltip("Remove Sample"); } if (p_what==NOTIFICATION_READY) { @@ -93,7 +97,7 @@ void SampleLibraryEditor::_file_load_request(const DVector<String>& p_path) { dialog->set_title("Error!"); //dialog->get_cancel()->set_text("Close"); dialog->get_ok()->set_text("Close"); - dialog->popup_centered(Size2(300,60)); + dialog->popup_centered_minsize(); return; ///beh should show an error i guess } String basename = path.get_file().basename(); @@ -235,6 +239,7 @@ void SampleLibraryEditor::_update_library() { List<StringName> names; sample_library->get_sample_list(&names); + names.sort_custom<StringName::AlphCompare>(); for(List<StringName>::Element *E=names.front();E;E=E->next()) { @@ -331,7 +336,8 @@ SampleLibraryEditor::SampleLibraryEditor() { play->set_pos(Point2( 5, 5 )); play->set_size( Size2(1,1 ) ); play->set_toggle_mode(true); - //add_child(play); + add_child(play); + play->hide(); stop = memnew( Button ); @@ -348,13 +354,13 @@ SampleLibraryEditor::SampleLibraryEditor() { _delete = memnew( Button ); - file = memnew( FileDialog ); + file = memnew( EditorFileDialog ); add_child(file); List<String> extensions; ResourceLoader::get_recognized_extensions_for_type("Sample",&extensions); for(int i=0;i<extensions.size();i++) file->add_filter("*."+extensions[i]); - file->set_mode(FileDialog::MODE_OPEN_FILES); + file->set_mode(EditorFileDialog::MODE_OPEN_FILES); _delete->set_pos(Point2( 65, 5 )); _delete->set_size( Size2(1,1 ) ); diff --git a/tools/editor/plugins/sample_library_editor_plugin.h b/tools/editor/plugins/sample_library_editor_plugin.h index a6ce764b9c..2770ca2d9a 100644 --- a/tools/editor/plugins/sample_library_editor_plugin.h +++ b/tools/editor/plugins/sample_library_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -54,7 +54,7 @@ class SampleLibraryEditor : public Panel { Button *_delete; Tree *tree; - FileDialog *file; + EditorFileDialog *file; ConfirmationDialog *dialog; diff --git a/tools/editor/plugins/sample_player_editor_plugin.cpp b/tools/editor/plugins/sample_player_editor_plugin.cpp index 405107889c..f1c7ca8c98 100644 --- a/tools/editor/plugins/sample_player_editor_plugin.cpp +++ b/tools/editor/plugins/sample_player_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -94,6 +94,7 @@ void SamplePlayerEditor::_update_sample_library() { List<StringName> samplenames; sl->get_sample_list(&samplenames); + samplenames.sort_custom<StringName::AlphCompare>(); for(List<StringName>::Element *E=samplenames.front();E;E=E->next()) { samples->add_item(E->get()); } diff --git a/tools/editor/plugins/sample_player_editor_plugin.h b/tools/editor/plugins/sample_player_editor_plugin.h index 4e35e4d8bb..cdd1a99c17 100644 --- a/tools/editor/plugins/sample_player_editor_plugin.h +++ b/tools/editor/plugins/sample_player_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/plugins/script_editor_plugin.cpp b/tools/editor/plugins/script_editor_plugin.cpp index 2e5f267d5c..65ed420a51 100644 --- a/tools/editor/plugins/script_editor_plugin.cpp +++ b/tools/editor/plugins/script_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,10 +38,89 @@ #include "os/file_access.h" #include "scene/main/viewport.h" #include "os/keyboard.h" +#include "os/input.h" + /*** SCRIPT EDITOR ****/ +class EditorScriptCodeCompletionCache : public ScriptCodeCompletionCache { + + + struct Cache { + uint64_t time_loaded; + RES cache; + }; + + Map<String,Cache> cached; + + +public: + + uint64_t max_time_cache; + int max_cache_size; + + void cleanup() { + + List< Map<String,Cache>::Element * > to_clean; + + + Map<String,Cache>::Element *I=cached.front(); + while(I) { + if ((OS::get_singleton()->get_ticks_msec()-I->get().time_loaded)>max_time_cache) { + to_clean.push_back(I); + } + I=I->next(); + } + + while(to_clean.front()) { + cached.erase(to_clean.front()->get()); + to_clean.pop_front(); + } + } + + RES get_cached_resource(const String& p_path) { + + Map<String,Cache>::Element *E=cached.find(p_path); + if (!E) { + + Cache c; + c.cache=ResourceLoader::load(p_path); + E=cached.insert(p_path,c); + } + + E->get().time_loaded=OS::get_singleton()->get_ticks_msec(); + + if (cached.size()>max_cache_size) { + uint64_t older; + Map<String,Cache>::Element *O=cached.front(); + older=O->get().time_loaded; + Map<String,Cache>::Element *I=O; + while(I) { + if (I->get().time_loaded<older) { + older = I->get().time_loaded; + O=I; + } + I=I->next(); + } + if (O!=E) {//should never heppane.. + cached.erase(O); + } + } + + return E->get().cache; + } + + + EditorScriptCodeCompletionCache() { + + max_cache_size=128; + max_time_cache=5*60*1000; //minutes, five + } + +}; + +#define SORT_SCRIPT_LIST void ScriptEditorQuickOpen::popup(const Vector<String>& p_functions, bool p_dontclear) { @@ -118,6 +197,8 @@ void ScriptEditorQuickOpen::_notification(int p_what) { if (p_what==NOTIFICATION_ENTER_TREE) { connect("confirmed",this,"_confirmed"); + + } } @@ -188,7 +269,7 @@ void ScriptTextEditor::apply_code() { if (script.is_null()) return; - print_line("applying code"); +// print_line("applying code"); script->set_source_code(get_text_edit()->get_text()); script->update_exports(); } @@ -210,6 +291,7 @@ void ScriptTextEditor::_load_theme_settings() { get_text_edit()->add_color_override("font_selected_color",EDITOR_DEF("text_editor/text_selected_color",Color(1,1,1))); get_text_edit()->add_color_override("selection_color",EDITOR_DEF("text_editor/selection_color",Color(0.2,0.2,1))); get_text_edit()->add_color_override("brace_mismatch_color",EDITOR_DEF("text_editor/brace_mismatch_color",Color(1,0.2,0.2))); + get_text_edit()->add_color_override("current_line_color",EDITOR_DEF("text_editor/current_line_color",Color(0.3,0.5,0.8,0.15))); Color keyword_color= EDITOR_DEF("text_editor/keyword_color",Color(0.5,0.0,0.2)); @@ -238,10 +320,10 @@ void ScriptTextEditor::_load_theme_settings() { //colorize engine types Color type_color= EDITOR_DEF("text_editor/engine_type_color",Color(0.0,0.2,0.4)); - List<String> types; + List<StringName> types; ObjectTypeDB::get_type_list(&types); - for(List<String>::Element *E=types.front();E;E=E->next()) { + for(List<StringName>::Element *E=types.front();E;E=E->next()) { get_text_edit()->add_keyword_color(E->get(),type_color); } @@ -284,8 +366,19 @@ void ScriptTextEditor::reload_text() { ERR_FAIL_COND(script.is_null()) ; - get_text_edit()->set_text(script->get_source_code()); - get_text_edit()->clear_undo_history(); + TextEdit *te = get_text_edit(); + int column = te->cursor_get_column(); + int row = te->cursor_get_line(); + int h = te->get_h_scroll(); + int v = te->get_v_scroll(); + + te->set_text(script->get_source_code()); + te->clear_undo_history(); + te->cursor_set_line(row); + te->cursor_set_column(column); + te->set_h_scroll(h); + te->set_v_scroll(v); + _line_col_changed(); } @@ -294,12 +387,11 @@ void ScriptTextEditor::_notification(int p_what) { if (p_what==NOTIFICATION_READY) { - _update_name(); + //emit_signal("name_changed"); } } -void ScriptTextEditor::_update_name() { - +String ScriptTextEditor::get_name() { String name; if (script->get_path().find("local://")==-1 && script->get_path().find("::")==-1) { @@ -312,21 +404,20 @@ void ScriptTextEditor::_update_name() { else name=script->get_type()+"("+itos(script->get_instance_ID())+")"; + return name; - if (name!=String(get_name())) { +} - set_name(name); +Ref<Texture> ScriptTextEditor::get_icon() { + if (get_parent_control() && get_parent_control()->has_icon(script->get_type(),"EditorIcons")) { + return get_parent_control()->get_icon(script->get_type(),"EditorIcons"); } - if (!has_meta("_tab_icon")) { - if (get_parent_control() && get_parent_control()->has_icon(script->get_type(),"EditorIcons")) { - set_meta("_tab_icon",get_parent_control()->get_icon(script->get_type(),"EditorIcons")); - } - } + return Ref<Texture>(); +} -} void ScriptTextEditor::set_edited_script(const Ref<Script>& p_script) { @@ -342,8 +433,7 @@ void ScriptTextEditor::set_edited_script(const Ref<Script>& p_script) { get_text_edit()->tag_saved_version(); - _update_name(); - + emit_signal("name_changed"); _line_col_changed(); } @@ -382,7 +472,7 @@ void ScriptTextEditor::_validate_script() { te->set_line_as_marked(i,line==i); } - _update_name(); + emit_signal("name_changed"); } @@ -416,6 +506,10 @@ void ScriptTextEditor::_code_complete_script(const String& p_code, List<String>* } } +void ScriptTextEditor::_bind_methods() { + + ADD_SIGNAL(MethodInfo("name_changed")); +} ScriptTextEditor::ScriptTextEditor() { @@ -452,6 +546,10 @@ void ScriptEditor::_show_debugger(bool p_show) { } +void ScriptEditor::_script_created(Ref<Script> p_script) { + editor->push_item(p_script.operator->()); +} + void ScriptEditor::_goto_script_line2(int p_line) { int selected = tab_container->get_current_tab(); @@ -474,27 +572,122 @@ void ScriptEditor::_goto_script_line(REF p_script,int p_line) { } + +void ScriptEditor::_update_history_arrows() { + + script_back->set_disabled( history_pos<=0 ); + script_forward->set_disabled( history_pos>=history.size()-1 ); +} + + +void ScriptEditor::_go_to_tab(int p_idx) { + + Node *cn = tab_container->get_child(p_idx); + if (!cn) + return; + Control *c = cn->cast_to<Control>(); + if (!c) + return; + + if (history_pos>=0 && history_pos<history.size() && history[history_pos].control==tab_container->get_current_tab_control()) { + + Node *n = tab_container->get_current_tab_control(); + + if (n->cast_to<ScriptTextEditor>()) { + + history[history_pos].scroll_pos=n->cast_to<ScriptTextEditor>()->get_text_edit()->get_v_scroll(); + history[history_pos].cursor_column=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_column(); + history[history_pos].cursor_row=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_line(); + } + if (n->cast_to<EditorHelp>()) { + + history[history_pos].scroll_pos=n->cast_to<EditorHelp>()->get_scroll(); + } + } + + history.resize(history_pos+1); + ScriptHistory sh; + sh.control=c; + sh.scroll_pos=0; + + history.push_back(sh); + history_pos++; + + + tab_container->set_current_tab(p_idx); + + c = tab_container->get_current_tab_control(); + + if (c->cast_to<ScriptTextEditor>()) { + + script_name_label->set_text(c->cast_to<ScriptTextEditor>()->get_name()); + script_icon->set_texture(c->cast_to<ScriptTextEditor>()->get_icon()); + if (is_visible()) + c->cast_to<ScriptTextEditor>()->get_text_edit()->grab_focus(); + } + if (c->cast_to<EditorHelp>()) { + + script_name_label->set_text(c->cast_to<EditorHelp>()->get_class_name()); + script_icon->set_texture(get_icon("Help","EditorIcons")); + if (is_visible()) + c->cast_to<EditorHelp>()->set_focused(); + } + + + + c->set_meta("__editor_pass",++edit_pass); + _update_history_arrows(); + _update_script_colors(); +} + void ScriptEditor::_close_current_tab() { int selected = tab_container->get_current_tab(); if (selected<0 || selected>=tab_container->get_child_count()) return; - + + Node *tselected = tab_container->get_child(selected); ScriptTextEditor *current = tab_container->get_child(selected)->cast_to<ScriptTextEditor>(); - if (!current) - return; + if (current) { + apply_scripts(); + } - apply_scripts(); + //remove from history + history.resize(history_pos+1); + + for(int i=0;i<history.size();i++) { + if (history[i].control==tselected) { + history.remove(i); + i--; + history_pos--; + } + } + + if (history_pos>=history.size()) { + history_pos=history.size()-1; + } int idx = tab_container->get_current_tab(); - memdelete(current); + memdelete(tselected); if (idx>=tab_container->get_child_count()) idx=tab_container->get_child_count()-1; - if (idx>=0) + if (idx>=0) { + + if (history_pos>=0) { + idx = history[history_pos].control->get_index(); + } tab_container->set_current_tab(idx); - _update_window_menu(); - _save_files_state(); + //script_list->select(idx); + } + + + _update_history_arrows(); + + + + _update_script_names(); + EditorNode::get_singleton()->save_layout(); } @@ -585,10 +778,10 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource>& p_res) { ste->get_text_edit()->tag_saved_version(); } - ste->_update_name(); - } + _update_script_names(); + } bool ScriptEditor::_test_script_times_on_disk() { @@ -629,8 +822,13 @@ bool ScriptEditor::_test_script_times_on_disk() { - if (!all_ok) - disk_changed->call_deferred("popup_centered_ratio",0.5); + if (!all_ok) { + if (bool(EDITOR_DEF("text_editor/auto_reload_changed_scripts",false))) { + script_editor->_reload_scripts(); + } else { + disk_changed->call_deferred("popup_centered_ratio",0.5); + } + } return all_ok; } @@ -648,31 +846,15 @@ void ScriptEditor::swap_lines(TextEdit *tx, int line1, int line2) void ScriptEditor::_menu_option(int p_option) { - if (p_option==FILE_OPEN) { - - editor->open_resource("Script"); - return; - } - int selected = tab_container->get_current_tab(); - if (selected<0 || selected>=tab_container->get_child_count()) - return; - - ScriptTextEditor *current = tab_container->get_child(selected)->cast_to<ScriptTextEditor>(); - if (!current) - return; - switch(p_option) { - case FILE_SAVE: { - - if (!_test_script_times_on_disk()) - return; - editor->save_resource( current->get_edited_script() ); - + case FILE_NEW: { + script_create_dialog->config("Node", ".gd"); + script_create_dialog->popup_centered(Size2(300, 300)); } break; - case FILE_SAVE_AS: { - - editor->save_resource_as( current->get_edited_script() ); + case FILE_OPEN: { + editor->open_resource("Script"); + return; } break; case FILE_SAVE_ALL: { @@ -697,373 +879,470 @@ void ScriptEditor::_menu_option(int p_option) { } break; - case EDIT_UNDO: { - current->get_text_edit()->undo(); - } break; - case EDIT_REDO: { - current->get_text_edit()->redo(); - } break; - case EDIT_CUT: { - - current->get_text_edit()->cut(); - } break; - case EDIT_COPY: { - current->get_text_edit()->copy(); - - } break; - case EDIT_PASTE: { - current->get_text_edit()->paste(); + case SEARCH_HELP: { + help_search_dialog->popup("current"); } break; - case EDIT_SELECT_ALL: { - - current->get_text_edit()->select_all(); + case SEARCH_CLASSES: { - } break; - case EDIT_MOVE_LINE_UP: { - - TextEdit *tx = current->get_text_edit(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - - if (tx->is_selection_active()) - { - int from_line = tx->get_selection_from_line(); - int from_col = tx->get_selection_from_column(); - int to_line = tx->get_selection_to_line(); - int to_column = tx->get_selection_to_column(); - - for (int i = from_line; i <= to_line; i++) - { - int line_id = i; - int next_id = i - 1; - - if (line_id == 0 || next_id < 0) - return; - - swap_lines(tx, line_id, next_id); - } - int from_line_up = from_line > 0 ? from_line-1 : from_line; - int to_line_up = to_line > 0 ? to_line-1 : to_line; - tx->select(from_line_up, from_col, to_line_up, to_column); - } - else - { - int line_id = tx->cursor_get_line(); - int next_id = line_id - 1; - - if (line_id == 0 || next_id < 0) - return; - - swap_lines(tx, line_id, next_id); - } - tx->update(); - - } break; - case EDIT_MOVE_LINE_DOWN: { - - TextEdit *tx = current->get_text_edit(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - - if (tx->is_selection_active()) - { - int from_line = tx->get_selection_from_line(); - int from_col = tx->get_selection_from_column(); - int to_line = tx->get_selection_to_line(); - int to_column = tx->get_selection_to_column(); - - for (int i = to_line; i >= from_line; i--) - { - int line_id = i; - int next_id = i + 1; - - if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count()) - return; - - swap_lines(tx, line_id, next_id); - } - int from_line_down = from_line < tx->get_line_count() ? from_line+1 : from_line; - int to_line_down = to_line < tx->get_line_count() ? to_line+1 : to_line; - tx->select(from_line_down, from_col, to_line_down, to_column); - } - else - { - int line_id = tx->cursor_get_line(); - int next_id = line_id + 1; - - if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count()) - return; - - swap_lines(tx, line_id, next_id); - } - tx->update(); - - } break; - case EDIT_INDENT_LEFT: { - - TextEdit *tx = current->get_text_edit(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - - int begin, end; - begin = tx->get_selection_from_line(); - if (tx->is_selection_active()) - { - end = tx->get_selection_to_line(); - for (int i = begin; i <= end; i++) - { - String line_text = tx->get_line(i); - // begins with tab - if (line_text.begins_with("\t")) - { - line_text = line_text.substr(1, line_text.length()); - tx->set_line(i, line_text); - } - // begins with 4 spaces - else if (line_text.begins_with(" ")) - { - line_text = line_text.substr(4, line_text.length()); - tx->set_line(i, line_text); - } - } - } - else - { - begin = tx->cursor_get_line(); - String line_text = tx->get_line(begin); - // begins with tab - if (line_text.begins_with("\t")) - { - line_text = line_text.substr(1, line_text.length()); - tx->set_line(begin, line_text); - } - // begins with 4 spaces - else if (line_text.begins_with(" ")) - { - line_text = line_text.substr(4, line_text.length()); - tx->set_line(begin, line_text); - } - } - tx->update(); - //tx->deselect(); - - } break; - case EDIT_INDENT_RIGHT: { - - TextEdit *tx = current->get_text_edit(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - - int begin, end; - begin = tx->get_selection_from_line(); - if (tx->is_selection_active()) - { - end = tx->get_selection_to_line(); - for (int i = begin; i <= end; i++) - { - String line_text = tx->get_line(i); - line_text = '\t' + line_text; - tx->set_line(i, line_text); - } - } - else - { - begin = tx->cursor_get_line(); - String line_text = tx->get_line(begin); - line_text = '\t' + line_text; - tx->set_line(begin, line_text); - } - tx->update(); - //tx->deselect(); - - } break; - case EDIT_CLONE_DOWN: { - - TextEdit *tx = current->get_text_edit(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - int line = tx->cursor_get_line(); - int next_line = line + 1; - - if (line == tx->get_line_count() || next_line > tx->get_line_count()) - return; - - String line_clone = tx->get_line(line); - tx->insert_at(line_clone, next_line); - tx->update(); - - } break; - case EDIT_TOGGLE_COMMENT: { - - TextEdit *tx = current->get_text_edit(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - - int begin, end; - begin = tx->get_selection_from_line(); - if (tx->is_selection_active()) - { - end = tx->get_selection_to_line(); - for (int i = begin; i <= end; i++) - { - String line_text = tx->get_line(i); - - if (line_text.begins_with("#")) - line_text = line_text.strip_edges().substr(1, line_text.length()); - else - line_text = "#" + line_text; - tx->set_line(i, line_text); - } - } - else - { - begin = tx->cursor_get_line(); - String line_text = tx->get_line(begin); - - if (line_text.begins_with("#")) - line_text = line_text.strip_edges().substr(1, line_text.length()); - else - line_text = "#" + line_text; - tx->set_line(begin, line_text); - } - tx->update(); - //tx->deselect(); - - } break; - case EDIT_COMPLETE: { - - current->get_text_edit()->query_code_comple(); + if (tab_container->get_tab_count()==0) + break; - } break; - case EDIT_AUTO_INDENT: { + String current; - TextEdit *te = current->get_text_edit(); - String text = te->get_text(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - int begin,end; - if (te->is_selection_active()) { - begin=te->get_selection_from_line(); - end=te->get_selection_to_line(); - } else { - begin=0; - end=te->get_line_count()-1; + EditorHelp *eh = tab_container->get_child( tab_container->get_current_tab() )->cast_to<EditorHelp>(); + if (eh) { + current=eh->get_class_name(); } - scr->get_language()->auto_indent_code(text,begin,end); - te->set_text(text); + help_index->popup_centered_ratio(0.6); + if (current!="") { + help_index->call_deferred("select_class",current); + } } break; - case SEARCH_FIND: { - - find_replace_dialog->set_text_edit(current->get_text_edit()); - find_replace_dialog->popup_search(); - } break; - case SEARCH_FIND_NEXT: { - - find_replace_dialog->set_text_edit(current->get_text_edit()); - find_replace_dialog->search_next(); - } break; - case SEARCH_REPLACE: { + case SEARCH_WEBSITE: { - find_replace_dialog->set_text_edit(current->get_text_edit()); - find_replace_dialog->popup_replace(); + OS::get_singleton()->shell_open("http://www.godotengine.org/projects/godot-engine/wiki/Documentation#Tutorials"); } break; - case SEARCH_LOCATE_FUNCTION: { - if (!current) - return; - quick_open->popup(current->get_functions()); - } break; - case SEARCH_GOTO_LINE: { + case WINDOW_NEXT: { - goto_line_dialog->popup_find_line(current->get_text_edit()); + _history_forward(); } break; - case DEBUG_TOGGLE_BREAKPOINT: { - int line=current->get_text_edit()->cursor_get_line(); - bool dobreak = !current->get_text_edit()->is_line_set_as_breakpoint(line); - current->get_text_edit()->set_line_as_breakpoint(line,dobreak); + case WINDOW_PREV: { + _history_back(); } break; - case DEBUG_NEXT: { - if (debugger) - debugger->debug_next(); - } break; - case DEBUG_STEP: { - - if (debugger) - debugger->debug_step(); - - } break; - case DEBUG_BREAK: { - - if (debugger) - debugger->debug_break(); + } - } break; - case DEBUG_CONTINUE: { - if (debugger) - debugger->debug_continue(); + int selected = tab_container->get_current_tab(); + if (selected<0 || selected>=tab_container->get_child_count()) + return; - } break; - case DEBUG_SHOW: { - if (debugger) { - bool visible = debug_menu->get_popup()->is_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW) ); - debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), !visible); - if (visible) - debugger->hide(); + ScriptTextEditor *current = tab_container->get_child(selected)->cast_to<ScriptTextEditor>(); + if (current) { + + switch(p_option) { + case FILE_NEW: { + script_create_dialog->config("Node", ".gd"); + script_create_dialog->popup_centered(Size2(300, 300)); + } break; + case FILE_SAVE: { + if (!_test_script_times_on_disk()) + return; + editor->save_resource( current->get_edited_script() ); + + } break; + case FILE_SAVE_AS: { + + editor->save_resource_as( current->get_edited_script() ); + + } break; + case EDIT_UNDO: { + current->get_text_edit()->undo(); + current->get_text_edit()->call_deferred("grab_focus"); + } break; + case EDIT_REDO: { + current->get_text_edit()->redo(); + current->get_text_edit()->call_deferred("grab_focus"); + } break; + case EDIT_CUT: { + + current->get_text_edit()->cut(); + current->get_text_edit()->call_deferred("grab_focus"); + } break; + case EDIT_COPY: { + current->get_text_edit()->copy(); + current->get_text_edit()->call_deferred("grab_focus"); + + } break; + case EDIT_PASTE: { + current->get_text_edit()->paste(); + current->get_text_edit()->call_deferred("grab_focus"); + + } break; + case EDIT_SELECT_ALL: { + + current->get_text_edit()->select_all(); + current->get_text_edit()->call_deferred("grab_focus"); + + } break; + case EDIT_MOVE_LINE_UP: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + if (tx->is_selection_active()) + { + int from_line = tx->get_selection_from_line(); + int from_col = tx->get_selection_from_column(); + int to_line = tx->get_selection_to_line(); + int to_column = tx->get_selection_to_column(); + + for (int i = from_line; i <= to_line; i++) + { + int line_id = i; + int next_id = i - 1; + + if (line_id == 0 || next_id < 0) + return; + + swap_lines(tx, line_id, next_id); + } + int from_line_up = from_line > 0 ? from_line-1 : from_line; + int to_line_up = to_line > 0 ? to_line-1 : to_line; + tx->select(from_line_up, from_col, to_line_up, to_column); + } else - debugger->show(); - } - } break; - case HELP_CONTEXTUAL: { - String text = current->get_text_edit()->get_selection_text(); - if (text == "") - text = current->get_text_edit()->get_word_under_cursor(); - if (text != "") - editor->emit_signal("request_help", text); - } break; - case WINDOW_CLOSE: { - - erase_tab_confirm->set_text("Close Tab?:\n\""+current->get_name()+"\""); - erase_tab_confirm->popup_centered(Point2(250,80)); - } break; - case WINDOW_MOVE_LEFT: { - - if (tab_container->get_current_tab()>0) { - tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()-1); - tab_container->move_child(current,tab_container->get_current_tab()-1); - _update_window_menu(); + { + int line_id = tx->cursor_get_line(); + int next_id = line_id - 1; + + if (line_id == 0 || next_id < 0) + return; + + swap_lines(tx, line_id, next_id); + } + tx->update(); + + } break; + case EDIT_MOVE_LINE_DOWN: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + if (tx->is_selection_active()) + { + int from_line = tx->get_selection_from_line(); + int from_col = tx->get_selection_from_column(); + int to_line = tx->get_selection_to_line(); + int to_column = tx->get_selection_to_column(); + + for (int i = to_line; i >= from_line; i--) + { + int line_id = i; + int next_id = i + 1; + + if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count()) + return; + + swap_lines(tx, line_id, next_id); + } + int from_line_down = from_line < tx->get_line_count() ? from_line+1 : from_line; + int to_line_down = to_line < tx->get_line_count() ? to_line+1 : to_line; + tx->select(from_line_down, from_col, to_line_down, to_column); + } + else + { + int line_id = tx->cursor_get_line(); + int next_id = line_id + 1; + + if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count()) + return; + + swap_lines(tx, line_id, next_id); + } + tx->update(); + + } break; + case EDIT_INDENT_LEFT: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + + if (tx->is_selection_active()) + { + int begin = tx->get_selection_from_line(); + int end = tx->get_selection_to_line(); + for (int i = begin; i <= end; i++) + { + String line_text = tx->get_line(i); + // begins with tab + if (line_text.begins_with("\t")) + { + line_text = line_text.substr(1, line_text.length()); + tx->set_line(i, line_text); + } + // begins with 4 spaces + else if (line_text.begins_with(" ")) + { + line_text = line_text.substr(4, line_text.length()); + tx->set_line(i, line_text); + } + } + } + else + { + int begin = tx->cursor_get_line(); + String line_text = tx->get_line(begin); + // begins with tab + if (line_text.begins_with("\t")) + { + line_text = line_text.substr(1, line_text.length()); + tx->set_line(begin, line_text); + } + // begins with 4 spaces + else if (line_text.begins_with(" ")) + { + line_text = line_text.substr(4, line_text.length()); + tx->set_line(begin, line_text); + } + } + tx->update(); + //tx->deselect(); + + } break; + case EDIT_INDENT_RIGHT: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + if (tx->is_selection_active()) + { + int begin = tx->get_selection_from_line(); + int end = tx->get_selection_to_line(); + for (int i = begin; i <= end; i++) + { + String line_text = tx->get_line(i); + line_text = '\t' + line_text; + tx->set_line(i, line_text); + } + } + else + { + int begin = tx->cursor_get_line(); + String line_text = tx->get_line(begin); + line_text = '\t' + line_text; + tx->set_line(begin, line_text); + } + tx->update(); + //tx->deselect(); + + } break; + case EDIT_CLONE_DOWN: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + int line = tx->cursor_get_line(); + int next_line = line + 1; + + if (line == tx->get_line_count() || next_line > tx->get_line_count()) + return; + + String line_clone = tx->get_line(line); + tx->insert_at(line_clone, next_line); + tx->update(); + + } break; + case EDIT_TOGGLE_COMMENT: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + + + if (tx->is_selection_active()) + { + int begin = tx->get_selection_from_line(); + int end = tx->get_selection_to_line(); + for (int i = begin; i <= end; i++) + { + String line_text = tx->get_line(i); + + if (line_text.begins_with("#")) + line_text = line_text.substr(1, line_text.length()); + else + line_text = "#" + line_text; + tx->set_line(i, line_text); + } + } + else + { + int begin = tx->cursor_get_line(); + String line_text = tx->get_line(begin); + + if (line_text.begins_with("#")) + line_text = line_text.substr(1, line_text.length()); + else + line_text = "#" + line_text; + tx->set_line(begin, line_text); + } + tx->update(); + //tx->deselect(); + + } break; + case EDIT_COMPLETE: { + + current->get_text_edit()->query_code_comple(); + + } break; + case EDIT_AUTO_INDENT: { + + TextEdit *te = current->get_text_edit(); + String text = te->get_text(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + int begin,end; + if (te->is_selection_active()) { + begin=te->get_selection_from_line(); + end=te->get_selection_to_line(); + } else { + begin=0; + end=te->get_line_count()-1; + } + scr->get_language()->auto_indent_code(text,begin,end); + te->set_text(text); + + + } break; + case SEARCH_FIND: { + + find_replace_dialog->set_text_edit(current->get_text_edit()); + find_replace_dialog->popup_search(); + } break; + case SEARCH_FIND_NEXT: { + + find_replace_dialog->set_text_edit(current->get_text_edit()); + find_replace_dialog->search_next(); + } break; + case SEARCH_REPLACE: { + + find_replace_dialog->set_text_edit(current->get_text_edit()); + find_replace_dialog->popup_replace(); + } break; + case SEARCH_LOCATE_FUNCTION: { + + if (!current) + return; + quick_open->popup(current->get_functions()); + } break; + case SEARCH_GOTO_LINE: { + + goto_line_dialog->popup_find_line(current->get_text_edit()); + } break; + case DEBUG_TOGGLE_BREAKPOINT: { + int line=current->get_text_edit()->cursor_get_line(); + bool dobreak = !current->get_text_edit()->is_line_set_as_breakpoint(line); + current->get_text_edit()->set_line_as_breakpoint(line,dobreak); + get_debugger()->set_breakpoint(current->get_edited_script()->get_path(),line+1,dobreak); + } break; + case DEBUG_NEXT: { + + if (debugger) + debugger->debug_next(); + } break; + case DEBUG_STEP: { + + if (debugger) + debugger->debug_step(); + + } break; + case DEBUG_BREAK: { + + if (debugger) + debugger->debug_break(); + + } break; + case DEBUG_CONTINUE: { + + if (debugger) + debugger->debug_continue(); + + } break; + case DEBUG_SHOW: { + if (debugger) { + bool visible = debug_menu->get_popup()->is_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW) ); + debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), !visible); + if (visible) + debugger->hide(); + else + debugger->show(); + } + } break; + case HELP_CONTEXTUAL: { + String text = current->get_text_edit()->get_selection_text(); + if (text == "") + text = current->get_text_edit()->get_word_under_cursor(); + if (text != "") + help_search_dialog->popup(text); + } break; + case FILE_CLOSE: { + if (current->get_text_edit()->get_version()!=current->get_text_edit()->get_saved_version()) { + erase_tab_confirm->set_text("Close and save changes?\n\""+current->get_name()+"\""); + erase_tab_confirm->popup_centered_minsize(); + } else { + _close_current_tab(); + } + } break; + case WINDOW_MOVE_LEFT: { + + if (tab_container->get_current_tab()>0) { + tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()-1); + script_list->call_deferred("select",tab_container->get_current_tab()-1); + tab_container->move_child(current,tab_container->get_current_tab()-1); + _update_script_names(); + } + } break; + case WINDOW_MOVE_RIGHT: { + + if (tab_container->get_current_tab()<tab_container->get_child_count()-1) { + tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()+1); + script_list->call_deferred("select",tab_container->get_current_tab()+1); + tab_container->move_child(current,tab_container->get_current_tab()+1); + _update_script_names(); + } + + + } break; + + default: { + + if (p_option>=WINDOW_SELECT_BASE) { + + tab_container->set_current_tab(p_option-WINDOW_SELECT_BASE); + script_list->select(p_option-WINDOW_SELECT_BASE); + + } } - } break; - case WINDOW_MOVE_RIGHT: { + } + } - if (tab_container->get_current_tab()<tab_container->get_child_count()-1) { - tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()+1); - tab_container->move_child(current,tab_container->get_current_tab()+1); - _update_window_menu(); - } + EditorHelp *help = tab_container->get_child(selected)->cast_to<EditorHelp>(); + if (help) { + switch(p_option) { - } break; - default: { + case SEARCH_FIND: { + help->popup_search(); + } break; + case SEARCH_FIND_NEXT: { + help->search_again(); + } break; + case FILE_CLOSE: { + _close_current_tab(); + } break; - if (p_option>=WINDOW_SELECT_BASE) { - tab_container->set_current_tab(p_option-WINDOW_SELECT_BASE); - } } } + } void ScriptEditor::_tab_changed(int p_which) { @@ -1080,12 +1359,35 @@ void ScriptEditor::_notification(int p_what) { editor->connect("stop_pressed",this,"_editor_stop"); editor->connect("script_add_function_request",this,"_add_callback"); editor->connect("resource_saved",this,"_res_saved_callback"); + script_list->connect("item_selected",this,"_script_selected"); + script_split->connect("dragged",this,"_script_split_dragged"); + autosave_timer->connect("timeout",this,"_autosave_scripts"); + { + float autosave_time = EditorSettings::get_singleton()->get("text_editor/autosave_interval_secs"); + if (autosave_time>0) { + autosave_timer->set_wait_time(autosave_time); + autosave_timer->start(); + } else { + autosave_timer->stop(); + } + } + + EditorSettings::get_singleton()->connect("settings_changed",this,"_editor_settings_changed"); + help_search->set_icon(get_icon("Help","EditorIcons")); + site_search->set_icon(get_icon("Godot","EditorIcons")); + class_search->set_icon(get_icon("ClassList","EditorIcons")); + + script_forward->set_icon(get_icon("Forward","EditorIcons")); + script_back->set_icon(get_icon("Back","EditorIcons")); + + } if (p_what==NOTIFICATION_READY) { - _update_window_menu(); + + get_tree()->connect("tree_changed",this,"_tree_changed"); } if (p_what==NOTIFICATION_EXIT_TREE) { @@ -1125,10 +1427,11 @@ static const Node * _find_node_with_script(const Node* p_node, const RefPtr & p_ Dictionary ScriptEditor::get_state() const { - apply_scripts(); - Dictionary state; +// apply_scripts(); + Dictionary state; +#if 0 Array paths; int open=-1; @@ -1161,12 +1464,12 @@ Dictionary ScriptEditor::get_state() const { if (open!=-1) state["current"]=open; - +#endif return state; } void ScriptEditor::set_state(const Dictionary& p_state) { - +#if 0 print_line("attempt set state: "+String(Variant(p_state))); if (!p_state.has("sources")) @@ -1203,10 +1506,11 @@ void ScriptEditor::set_state(const Dictionary& p_state) { if (p_state.has("current")) { tab_container->set_current_tab(p_state["current"]); } +#endif } void ScriptEditor::clear() { - +#if 0 List<ScriptTextEditor*> stes; for(int i=0;i<tab_container->get_child_count();i++) { @@ -1226,24 +1530,18 @@ void ScriptEditor::clear() { int idx = tab_container->get_current_tab(); if (idx>=tab_container->get_child_count()) idx=tab_container->get_child_count()-1; - if (idx>=0) + if (idx>=0) { tab_container->set_current_tab(idx); + script_list->select( script_list->find_metadata(idx) ); + } - _update_window_menu(); +#endif } -void ScriptEditor::_save_files_state() { - - return; //no thank you - - String rpath="_open_scripts_"+Globals::get_singleton()->get_resource_path(); - rpath=rpath.replace("\\","_-_"); - rpath=rpath.replace("/","_-_"); - rpath=rpath.replace(":","_"); - Vector<String> scripts; +void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) { for(int i=0;i<tab_container->get_child_count();i++) { @@ -1251,114 +1549,236 @@ void ScriptEditor::_save_files_state() { if (!ste) continue; + List<int> bpoints; + ste->get_text_edit()->get_breakpoints(&bpoints); Ref<Script> script = ste->get_edited_script(); - if (script->get_path()!="" && script->get_path().find("local://")==-1 && script->get_path().find("::")==-1) { + String base = script->get_path(); + ERR_CONTINUE( base.begins_with("local://") || base=="" ); + for(List<int>::Element *E=bpoints.front();E;E=E->next()) { - scripts.push_back(script->get_path()); + p_breakpoints->push_back(base+":"+itos(E->get()+1)); } } +} + + + - EditorSettings::get_singleton()->set(rpath,scripts); - EditorSettings::get_singleton()->save(); +void ScriptEditor::ensure_focus_current() { + + if (!is_inside_tree()) + return; + + int cidx = tab_container->get_current_tab(); + if (cidx<0 || cidx>=tab_container->get_tab_count()); + Control *c = tab_container->get_child(cidx)->cast_to<Control>(); + if (!c) + return; + ScriptTextEditor *ste = c->cast_to<ScriptTextEditor>(); + if (!ste) + return; + ste->get_text_edit()->grab_focus(); } -void ScriptEditor::_load_files_state() { - return; +void ScriptEditor::_script_selected(int p_idx) { - String rpath="_open_scripts_"+Globals::get_singleton()->get_resource_path(); - rpath=rpath.replace("\\","_-_"); - rpath=rpath.replace("/","_-_"); - rpath=rpath.replace(":","_"); + grab_focus_block = !Input::get_singleton()->is_mouse_button_pressed(1); //amazing hack, simply amazing - if (EditorSettings::get_singleton()->has(rpath)) { + _go_to_tab(script_list->get_item_metadata(p_idx)); + grab_focus_block=false; +} + +void ScriptEditor::ensure_select_current() { + + + if (tab_container->get_child_count() && tab_container->get_current_tab()>=0) { + + Node *current = tab_container->get_child(tab_container->get_current_tab()); - Vector<String> open_files=EditorSettings::get_singleton()->get("rpath"); - for(int i=0;i<open_files.size();i++) { - Ref<Script> scr = ResourceLoader::load(open_files[i]); - if (!scr.is_valid()) - continue; - editor->edit_resource(scr); + ScriptTextEditor *ste = current->cast_to<ScriptTextEditor>(); + if (ste) { + + Ref<Script> script = ste->get_edited_script(); + + if (!grab_focus_block && is_inside_tree()) + ste->get_text_edit()->grab_focus(); + + edit_menu->show(); + search_menu->show(); + script_search_menu->hide(); + + + } + + EditorHelp *eh = current->cast_to<EditorHelp>(); + + if (eh) { + edit_menu->hide(); + search_menu->hide(); + script_search_menu->show(); + } } -} -void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) { - for(int i=0;i<tab_container->get_child_count();i++) { - ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); - if (!ste) - continue; - List<int> bpoints; - ste->get_text_edit()->get_breakpoints(&bpoints); +} - Ref<Script> script = ste->get_edited_script(); - String base = script->get_path(); - ERR_CONTINUE( base.begins_with("local://") || base=="" ); +void ScriptEditor::_find_scripts(Node* p_base, Node* p_current, Set<Ref<Script> > &used) { + if (p_current!=p_base && p_current->get_owner()!=p_base) + return; - for(List<int>::Element *E=bpoints.front();E;E=E->next()) { + if (p_current->get_script_instance()) { + Ref<Script> scr = p_current->get_script(); + if (scr.is_valid()) + used.insert(scr); + } - p_breakpoints->push_back(base+":"+itos(E->get()+1)); - } + for(int i=0;i<p_current->get_child_count();i++) { + _find_scripts(p_base,p_current->get_child(i),used); } } +struct _ScriptEditorItemData { + String name; + Ref<Texture> icon; + int index; + String tooltip; + bool used; + int category; -void ScriptEditor::_bind_methods() { - ObjectTypeDB::bind_method("_tab_changed",&ScriptEditor::_tab_changed); - ObjectTypeDB::bind_method("_menu_option",&ScriptEditor::_menu_option); - ObjectTypeDB::bind_method("_close_current_tab",&ScriptEditor::_close_current_tab); - ObjectTypeDB::bind_method("_editor_play",&ScriptEditor::_editor_play); - ObjectTypeDB::bind_method("_editor_pause",&ScriptEditor::_editor_pause); - ObjectTypeDB::bind_method("_editor_stop",&ScriptEditor::_editor_stop); - ObjectTypeDB::bind_method("_add_callback",&ScriptEditor::_add_callback); - ObjectTypeDB::bind_method("_reload_scripts",&ScriptEditor::_reload_scripts); - ObjectTypeDB::bind_method("_resave_scripts",&ScriptEditor::_resave_scripts); - ObjectTypeDB::bind_method("_res_saved_callback",&ScriptEditor::_res_saved_callback); - ObjectTypeDB::bind_method("_goto_script_line",&ScriptEditor::_goto_script_line); - ObjectTypeDB::bind_method("_goto_script_line2",&ScriptEditor::_goto_script_line2); - ObjectTypeDB::bind_method("_breaked",&ScriptEditor::_breaked); - ObjectTypeDB::bind_method("_show_debugger",&ScriptEditor::_show_debugger); - ObjectTypeDB::bind_method("_get_debug_tooltip",&ScriptEditor::_get_debug_tooltip); + bool operator<(const _ScriptEditorItemData& id) const { -} + return category==id.category?name.nocasecmp_to(id.name)<0:category<id.category; + } +}; -void ScriptEditor::ensure_focus_current() { - int cidx = tab_container->get_current_tab(); - if (cidx<0 || cidx>=tab_container->get_tab_count()); - Control *c = tab_container->get_child(cidx)->cast_to<Control>(); - if (!c) - return; - ScriptTextEditor *ste = c->cast_to<ScriptTextEditor>(); - if (!ste) +void ScriptEditor::_update_script_colors() { + + bool enabled = EditorSettings::get_singleton()->get("text_editor/script_temperature_enabled"); + if (!enabled) return; - ste->get_text_edit()->grab_focus(); + + int hist_size = EditorSettings::get_singleton()->get("text_editor/script_temperature_history_size"); + Color hot_color=EditorSettings::get_singleton()->get("text_editor/script_temperature_hot_color"); + Color cold_color=EditorSettings::get_singleton()->get("text_editor/script_temperature_cold_color"); + + for(int i=0;i<script_list->get_item_count();i++) { + + int c = script_list->get_item_metadata(i); + Node *n = tab_container->get_child(c); + if (!n) + continue; + + script_list->set_item_custom_bg_color(i,Color(0,0,0,0)); + if (!n->has_meta("__editor_pass")) { + continue; + } + + int pass=n->get_meta("__editor_pass"); + int h = edit_pass - pass; + if (h>hist_size) { + continue; + } + float v = Math::ease((edit_pass-pass)/float_t(hist_size),0.4); + + + script_list->set_item_custom_bg_color(i,hot_color.linear_interpolate(cold_color,v)); + } } -void ScriptEditor::ensure_select_current() { +void ScriptEditor::_update_script_names() { + waiting_update_names=false; + Set<Ref<Script> > used; + Node* edited = EditorNode::get_singleton()->get_edited_scene(); + if (edited) { + _find_scripts(edited,edited,used); + } - if (tab_container->get_child_count() && tab_container->get_current_tab()>=0) { + script_list->clear(); + bool split_script_help = EditorSettings::get_singleton()->get("text_editor/group_help_pages"); - ScriptTextEditor *ste = tab_container->get_child(tab_container->get_current_tab())->cast_to<ScriptTextEditor>(); - if (!ste) - return; - Ref<Script> script = ste->get_edited_script(); + Vector<_ScriptEditorItemData> sedata; + + for(int i=0;i<tab_container->get_child_count();i++) { + + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (ste) { + + String name = ste->get_name(); + Ref<Texture> icon = ste->get_icon(); + String tooltip = ste->get_edited_script()->get_path(); + + _ScriptEditorItemData sd; + sd.icon=icon; + sd.name=name; + sd.tooltip=tooltip; + sd.index=i; + sd.used=used.has(ste->get_edited_script()); + sd.category=0; + + sedata.push_back(sd); + } + + EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>(); + if (eh) { + + String name = eh->get_class_name(); + Ref<Texture> icon = get_icon("Help","EditorIcons"); + String tooltip = name+" Class Reference"; + + _ScriptEditorItemData sd; + sd.icon=icon; + sd.name=name; + sd.tooltip=tooltip; + sd.index=i; + sd.used=false; + sd.category=split_script_help?1:0; + sedata.push_back(sd); + + } - ste->get_text_edit()->grab_focus(); } + + sedata.sort(); + + for(int i=0;i<sedata.size();i++) { + + script_list->add_item(sedata[i].name,sedata[i].icon); + int index = script_list->get_item_count()-1; + script_list->set_item_tooltip(index,sedata[i].tooltip); + script_list->set_item_metadata(index,sedata[i].index); + if (sedata[i].used) { + + script_list->set_item_custom_bg_color(index,Color(88/255.0,88/255.0,60/255.0)); + } + if (tab_container->get_current_tab()==sedata[i].index) { + script_list->select(index); + script_name_label->set_text(sedata[i].name); + script_icon->set_texture(sedata[i].icon); + + } + } + + _update_script_colors(); + + + + } void ScriptEditor::edit(const Ref<Script>& p_script) { @@ -1368,6 +1788,8 @@ void ScriptEditor::edit(const Ref<Script>& p_script) { // see if already has it + bool open_dominant = EditorSettings::get_singleton()->get("text_editor/open_dominant_script_on_scene_change"); + if (p_script->get_path().is_resource_file() && bool(EditorSettings::get_singleton()->get("external_editor/use_external_editor"))) { String path = EditorSettings::get_singleton()->get("external_editor/exec_path"); @@ -1396,9 +1818,14 @@ void ScriptEditor::edit(const Ref<Script>& p_script) { if (ste->get_edited_script()==p_script) { - if (tab_container->get_current_tab()!=i) - tab_container->set_current_tab(i); - ste->get_text_edit()->grab_focus(); + if (open_dominant || !EditorNode::get_singleton()->is_changing_scene()) { + if (tab_container->get_current_tab()!=i) { + _go_to_tab(i); + script_list->select( script_list->find_metadata(i) ); + } + if (is_visible()) + ste->get_text_edit()->grab_focus(); + } return; } } @@ -1409,11 +1836,16 @@ void ScriptEditor::edit(const Ref<Script>& p_script) { ste->set_edited_script(p_script); ste->get_text_edit()->set_tooltip_request_func(this,"_get_debug_tooltip",ste); tab_container->add_child(ste); - tab_container->set_current_tab(tab_container->get_tab_count()-1); + _go_to_tab(tab_container->get_tab_count()-1); - _update_window_menu(); - _save_files_state(); + + + _update_script_names(); + ste->connect("name_changed",this,"_update_script_names"); + if (!restoring_layout) { + EditorNode::get_singleton()->save_layout(); + } } void ScriptEditor::save_external_data() { @@ -1473,51 +1905,6 @@ void ScriptEditor::_editor_stop() { debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true ); } -void ScriptEditor::_update_window_menu() { - - int idx=0; - for(int i=0;i<tab_container->get_child_count();i++) { - - ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); - if (!ste) - continue; - idx++; - } - - if (idx==0) { - window_menu->set_disabled(true); - edit_menu->set_disabled(true); - search_menu->set_disabled(true); - return; - } else { - - window_menu->set_disabled(false); - edit_menu->set_disabled(false); - search_menu->set_disabled(false); - } - - window_menu->get_popup()->clear(); - window_menu->get_popup()->add_item("Close",WINDOW_CLOSE,KEY_MASK_CMD|KEY_W); - window_menu->get_popup()->add_separator(); - window_menu->get_popup()->add_item("Move Left",WINDOW_MOVE_LEFT,KEY_MASK_CMD|KEY_LEFT); - window_menu->get_popup()->add_item("Move Right",WINDOW_MOVE_RIGHT,KEY_MASK_CMD|KEY_RIGHT); - window_menu->get_popup()->add_separator(); - - idx=0; - for(int i=0;i<tab_container->get_child_count();i++) { - - ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); - if (!ste) - continue; - String n = ste->get_name(); - uint32_t accel=0; - if (idx<9) { - accel=KEY_MASK_ALT|(KEY_1+idx); - } - window_menu->get_popup()->add_item(n,WINDOW_SELECT_BASE+idx,accel); - idx++; - } -} void ScriptEditor::_add_callback(Object *p_obj, const String& p_function, const StringArray& p_args) { @@ -1549,18 +1936,284 @@ void ScriptEditor::_add_callback(Object *p_obj, const String& p_function, const ste->get_text_edit()->insert_text_at_cursor("\n\n"+func); } - tab_container->set_current_tab(i); + _go_to_tab(i); ste->get_text_edit()->cursor_set_line(pos); ste->get_text_edit()->cursor_set_column(1); + script_list->select( script_list->find_metadata(i) ); + break; } } +void ScriptEditor::_editor_settings_changed() { + + print_line("settings changed"); + float autosave_time = EditorSettings::get_singleton()->get("text_editor/autosave_interval_secs"); + if (autosave_time>0) { + autosave_timer->set_wait_time(autosave_time); + autosave_timer->start(); + } else { + autosave_timer->stop(); + } + +} + +void ScriptEditor::_autosave_scripts() { + + print_line("autosaving"); + save_external_data(); +} + +void ScriptEditor::_tree_changed() { + + if (waiting_update_names) + return; + + waiting_update_names=true; + call_deferred("_update_script_names"); +} + +void ScriptEditor::_script_split_dragged(float) { + + EditorNode::get_singleton()->save_layout(); +} + +void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) { + + if (!bool(EDITOR_DEF("text_editor/restore_scripts_on_load",true))) { + return; + } + + if (!p_layout->has_section_key("ScriptEditor","open_scripts") && !p_layout->has_section_key("ScriptEditor","open_help")) + return; + + Array scripts = p_layout->get_value("ScriptEditor","open_scripts"); + Array helps; + if (p_layout->has_section_key("ScriptEditor","open_help")) + helps=p_layout->get_value("ScriptEditor","open_help"); + + restoring_layout=true; + + for(int i=0;i<scripts.size();i++) { + + String path = scripts[i]; + Ref<Script> scr = ResourceLoader::load(path); + if (scr.is_valid()) { + edit(scr); + } + } + + + for(int i=0;i<helps.size();i++) { + + String path = helps[i]; + _help_class_open(path); + } + + for(int i=0;i<tab_container->get_child_count();i++) { + tab_container->get_child(i)->set_meta("__editor_pass",Variant()); + } + + + if (p_layout->has_section_key("ScriptEditor","split_offset")) { + script_split->set_split_offset(p_layout->get_value("ScriptEditor","split_offset")); + } + + + restoring_layout=false; + +} + +void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) { + + Array scripts; + Array helps; + + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (ste) { + + String path = ste->get_edited_script()->get_path(); + if (!path.is_resource_file()) + continue; + + scripts.push_back(path); + } + + EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>(); + + if (eh) { + + helps.push_back(eh->get_class_name()); + } + + + } + + p_layout->set_value("ScriptEditor","open_scripts",scripts); + p_layout->set_value("ScriptEditor","open_help",helps); + p_layout->set_value("ScriptEditor","split_offset",script_split->get_split_offset()); + +} + + +void ScriptEditor::_help_class_open(const String& p_class) { + + + for(int i=0;i<tab_container->get_child_count();i++) { + + EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>(); + + if (eh && eh->get_class_name()==p_class) { + + _go_to_tab(i); + _update_script_names(); + return; + } + } + + EditorHelp * eh = memnew( EditorHelp ); + + + eh->set_name(p_class); + tab_container->add_child(eh); + _go_to_tab(tab_container->get_tab_count()-1); + eh->go_to_class(p_class,0); + eh->connect("go_to_help",this,"_help_class_goto"); + _update_script_names(); + +} + +void ScriptEditor::_help_class_goto(const String& p_desc) { + + + String cname=p_desc.get_slice(":",1); + + for(int i=0;i<tab_container->get_child_count();i++) { + + EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>(); + + if (eh && eh->get_class_name()==cname) { + + _go_to_tab(i); + eh->go_to_help(p_desc); + _update_script_names(); + return; + } + } + + EditorHelp * eh = memnew( EditorHelp ); + + eh->set_name(cname); + tab_container->add_child(eh); + _go_to_tab(tab_container->get_tab_count()-1); + eh->go_to_help(p_desc); + eh->connect("go_to_help",this,"_help_class_goto"); + _update_script_names(); + +} + +void ScriptEditor::_update_history_pos(int p_new_pos) { + + Node *n = tab_container->get_current_tab_control(); + + if (n->cast_to<ScriptTextEditor>()) { + + history[history_pos].scroll_pos=n->cast_to<ScriptTextEditor>()->get_text_edit()->get_v_scroll(); + history[history_pos].cursor_column=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_column(); + history[history_pos].cursor_row=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_line(); + } + if (n->cast_to<EditorHelp>()) { + + history[history_pos].scroll_pos=n->cast_to<EditorHelp>()->get_scroll(); + } + + history_pos=p_new_pos; + tab_container->set_current_tab(history[history_pos].control->get_index()); + + n = history[history_pos].control; + + if (n->cast_to<ScriptTextEditor>()) { + + n->cast_to<ScriptTextEditor>()->get_text_edit()->set_v_scroll(history[history_pos].scroll_pos); + n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_set_column( history[history_pos].cursor_column ); + n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_set_line( history[history_pos].cursor_row ); + n->cast_to<ScriptTextEditor>()->get_text_edit()->grab_focus(); + } + + if (n->cast_to<EditorHelp>()) { + + n->cast_to<EditorHelp>()->set_scroll(history[history_pos].scroll_pos); + n->cast_to<EditorHelp>()->set_focused(); + } + + n->set_meta("__editor_pass",++edit_pass); + _update_script_names(); + _update_history_arrows(); + +} + +void ScriptEditor::_history_forward() { + + if (history_pos<history.size()-1) { + _update_history_pos(history_pos+1); + } +} + +void ScriptEditor::_history_back(){ + + if (history_pos>0) { + _update_history_pos(history_pos-1); + } + +} +void ScriptEditor::set_scene_root_script( Ref<Script> p_script ) { + + bool open_dominant = EditorSettings::get_singleton()->get("text_editor/open_dominant_script_on_scene_change"); + if (open_dominant && p_script.is_valid()) { + edit(p_script); + } +} + +void ScriptEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_tab_changed",&ScriptEditor::_tab_changed); + ObjectTypeDB::bind_method("_menu_option",&ScriptEditor::_menu_option); + ObjectTypeDB::bind_method("_close_current_tab",&ScriptEditor::_close_current_tab); + ObjectTypeDB::bind_method("_editor_play",&ScriptEditor::_editor_play); + ObjectTypeDB::bind_method("_editor_pause",&ScriptEditor::_editor_pause); + ObjectTypeDB::bind_method("_editor_stop",&ScriptEditor::_editor_stop); + ObjectTypeDB::bind_method("_add_callback",&ScriptEditor::_add_callback); + ObjectTypeDB::bind_method("_reload_scripts",&ScriptEditor::_reload_scripts); + ObjectTypeDB::bind_method("_resave_scripts",&ScriptEditor::_resave_scripts); + ObjectTypeDB::bind_method("_res_saved_callback",&ScriptEditor::_res_saved_callback); + ObjectTypeDB::bind_method("_goto_script_line",&ScriptEditor::_goto_script_line); + ObjectTypeDB::bind_method("_goto_script_line2",&ScriptEditor::_goto_script_line2); + ObjectTypeDB::bind_method("_breaked",&ScriptEditor::_breaked); + ObjectTypeDB::bind_method("_show_debugger",&ScriptEditor::_show_debugger); + ObjectTypeDB::bind_method("_get_debug_tooltip",&ScriptEditor::_get_debug_tooltip); + ObjectTypeDB::bind_method("_autosave_scripts",&ScriptEditor::_autosave_scripts); + ObjectTypeDB::bind_method("_editor_settings_changed",&ScriptEditor::_editor_settings_changed); + ObjectTypeDB::bind_method("_update_script_names",&ScriptEditor::_update_script_names); + ObjectTypeDB::bind_method("_tree_changed",&ScriptEditor::_tree_changed); + ObjectTypeDB::bind_method("_script_selected",&ScriptEditor::_script_selected); + ObjectTypeDB::bind_method("_script_created",&ScriptEditor::_script_created); + ObjectTypeDB::bind_method("_script_split_dragged",&ScriptEditor::_script_split_dragged); + ObjectTypeDB::bind_method("_help_class_open",&ScriptEditor::_help_class_open); + ObjectTypeDB::bind_method("_help_class_goto",&ScriptEditor::_help_class_goto); + ObjectTypeDB::bind_method("_history_forward",&ScriptEditor::_history_forward); + ObjectTypeDB::bind_method("_history_back",&ScriptEditor::_history_back); +} + ScriptEditor::ScriptEditor(EditorNode *p_editor) { + completion_cache = memnew( EditorScriptCodeCompletionCache ); + restoring_layout=false; + waiting_update_names=false; editor=p_editor; menu_hb = memnew( HBoxContainer ); @@ -1570,17 +2223,36 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { add_child(v_split); v_split->set_v_size_flags(SIZE_EXPAND_FILL); + script_split = memnew( HSplitContainer ); + v_split->add_child(script_split); + script_split->set_v_size_flags(SIZE_EXPAND_FILL); + + script_list = memnew( ItemList ); + script_split->add_child(script_list); + script_list->set_custom_minimum_size(Size2(70,0)); + script_split->set_split_offset(70); + tab_container = memnew( TabContainer ); - v_split->add_child(tab_container); - tab_container->set_v_size_flags(SIZE_EXPAND_FILL); + tab_container->set_tabs_visible(false); + script_split->add_child(tab_container); + + + tab_container->set_h_size_flags(SIZE_EXPAND_FILL); file_menu = memnew( MenuButton ); menu_hb->add_child(file_menu); file_menu->set_text("File"); + file_menu->get_popup()->add_item("New",FILE_NEW); file_menu->get_popup()->add_item("Open",FILE_OPEN); - file_menu->get_popup()->add_item("Save",FILE_SAVE,KEY_MASK_ALT|KEY_S); + file_menu->get_popup()->add_separator(); + file_menu->get_popup()->add_item("Save",FILE_SAVE,KEY_MASK_ALT|KEY_MASK_CMD|KEY_S); file_menu->get_popup()->add_item("Save As..",FILE_SAVE_AS); file_menu->get_popup()->add_item("Save All",FILE_SAVE_ALL,KEY_MASK_CMD|KEY_MASK_SHIFT|KEY_S); + file_menu->get_popup()->add_separator(); + file_menu->get_popup()->add_item("History Prev",WINDOW_PREV,KEY_MASK_CTRL|KEY_MASK_ALT|KEY_LEFT); + file_menu->get_popup()->add_item("History Next",WINDOW_NEXT,KEY_MASK_CTRL|KEY_MASK_ALT|KEY_RIGHT); + file_menu->get_popup()->add_separator(); + file_menu->get_popup()->add_item("Close",FILE_CLOSE,KEY_MASK_CMD|KEY_W); file_menu->get_popup()->connect("item_pressed", this,"_menu_option"); edit_menu = memnew( MenuButton ); @@ -1602,7 +2274,11 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { edit_menu->get_popup()->add_item("Toggle Comment",EDIT_TOGGLE_COMMENT,KEY_MASK_CMD|KEY_K); edit_menu->get_popup()->add_item("Clone Down",EDIT_CLONE_DOWN,KEY_MASK_CMD|KEY_B); edit_menu->get_popup()->add_separator(); +#ifdef OSX_ENABLED + edit_menu->get_popup()->add_item("Complete Symbol",EDIT_COMPLETE,KEY_MASK_CTRL|KEY_SPACE); +#else edit_menu->get_popup()->add_item("Complete Symbol",EDIT_COMPLETE,KEY_MASK_CMD|KEY_SPACE); +#endif edit_menu->get_popup()->add_item("Auto Indent",EDIT_AUTO_INDENT,KEY_MASK_CMD|KEY_I); edit_menu->get_popup()->connect("item_pressed", this,"_menu_option"); @@ -1611,13 +2287,22 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { menu_hb->add_child(search_menu); search_menu->set_text("Search"); search_menu->get_popup()->add_item("Find..",SEARCH_FIND,KEY_MASK_CMD|KEY_F); - search_menu->get_popup()->add_item("Find Next",SEARCH_FIND_NEXT,KEY_MASK_CMD|KEY_G); + search_menu->get_popup()->add_item("Find Next",SEARCH_FIND_NEXT,KEY_F3); search_menu->get_popup()->add_item("Replace..",SEARCH_REPLACE,KEY_MASK_CMD|KEY_R); search_menu->get_popup()->add_separator(); search_menu->get_popup()->add_item("Goto Function..",SEARCH_LOCATE_FUNCTION,KEY_MASK_SHIFT|KEY_MASK_CMD|KEY_F); search_menu->get_popup()->add_item("Goto Line..",SEARCH_GOTO_LINE,KEY_MASK_CMD|KEY_L); search_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + script_search_menu = memnew( MenuButton ); + menu_hb->add_child(script_search_menu); + script_search_menu->set_text("Search"); + script_search_menu->get_popup()->add_item("Find..",SEARCH_FIND,KEY_MASK_CMD|KEY_F); + script_search_menu->get_popup()->add_item("Find Next",SEARCH_FIND_NEXT,KEY_F3); + script_search_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + script_search_menu->hide(); + + debug_menu = memnew( MenuButton ); menu_hb->add_child(debug_menu); debug_menu->set_text("Debug"); @@ -1638,6 +2323,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true ); +#if 0 window_menu = memnew( MenuButton ); menu_hb->add_child(window_menu); window_menu->set_text("Window"); @@ -1648,12 +2334,61 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { window_menu->get_popup()->add_separator(); window_menu->get_popup()->connect("item_pressed", this,"_menu_option"); +#endif + help_menu = memnew( MenuButton ); menu_hb->add_child(help_menu); help_menu->set_text("Help"); help_menu->get_popup()->add_item("Contextual", HELP_CONTEXTUAL, KEY_MASK_SHIFT|KEY_F1); help_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + menu_hb->add_spacer(); + + + script_icon = memnew( TextureFrame ); + menu_hb->add_child(script_icon); + script_name_label = memnew( Label ); + menu_hb->add_child(script_name_label); + + script_icon->hide(); + script_name_label->hide(); + + menu_hb->add_spacer(); + + site_search = memnew( ToolButton ); + site_search->set_text("Tutorials"); + site_search->connect("pressed",this,"_menu_option",varray(SEARCH_WEBSITE)); + menu_hb->add_child(site_search); + site_search->set_tooltip("Open http://www.godotengine.org at tutorials section."); + + class_search = memnew( ToolButton ); + class_search->set_text("Classes"); + class_search->connect("pressed",this,"_menu_option",varray(SEARCH_CLASSES)); + menu_hb->add_child(class_search); + class_search->set_tooltip("Search the class hierarchy."); + + help_search = memnew( ToolButton ); + help_search->set_text("Search Help"); + help_search->connect("pressed",this,"_menu_option",varray(SEARCH_HELP)); + menu_hb->add_child(help_search); + help_search->set_tooltip("Search the reference documentation."); + + menu_hb->add_child( memnew( VSeparator) ); + + script_back = memnew( ToolButton ); + script_back->connect("pressed",this,"_history_back"); + menu_hb->add_child(script_back); + script_back->set_disabled(true); + help_search->set_tooltip("Go to previous edited document."); + + script_forward = memnew( ToolButton ); + script_forward->connect("pressed",this,"_history_forward"); + menu_hb->add_child(script_forward); + script_forward->set_disabled(true); + help_search->set_tooltip("Go to next edited document."); + + + tab_container->connect("tab_changed", this,"_tab_changed"); find_replace_dialog = memnew(FindReplaceDialog); @@ -1663,6 +2398,10 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { add_child(erase_tab_confirm); erase_tab_confirm->connect("confirmed", this,"_close_current_tab"); + script_create_dialog = memnew(ScriptCreateDialog); + script_create_dialog->set_title("Create Script"); + add_child(script_create_dialog); + script_create_dialog->connect("script_created", this, "_script_created"); goto_line_dialog = memnew(GotoLineDialog); add_child(goto_line_dialog); @@ -1705,11 +2444,35 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { v_split->add_child(debugger); debugger->connect("breaked",this,"_breaked"); + + autosave_timer = memnew( Timer ); + autosave_timer->set_one_shot(false); + add_child(autosave_timer); + + grab_focus_block=false; + + help_search_dialog = memnew( EditorHelpSearch ); + add_child(help_search_dialog); + help_search_dialog->connect("go_to_help",this,"_help_class_goto"); + + + help_index = memnew( EditorHelpIndex ); + add_child(help_index); + help_index->connect("open_class",this,"_help_class_open"); + + history_pos=-1; // debugger_gui->hide(); + edit_pass=0; + } +ScriptEditor::~ScriptEditor() { + + memdelete(completion_cache); +} + void ScriptEditorPlugin::edit(Object *p_object) { if (!p_object->cast_to<Script>()) @@ -1769,20 +2532,24 @@ void ScriptEditorPlugin::apply_changes() { void ScriptEditorPlugin::restore_global_state() { - if (bool(EDITOR_DEF("text_editor/restore_scripts_on_load",true))) { - script_editor->_load_files_state(); - } } void ScriptEditorPlugin::save_global_state() { - if (bool(EDITOR_DEF("text_editor/restore_scripts_on_load",true))) { - script_editor->_save_files_state(); - } +} + +void ScriptEditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) { + script_editor->set_window_layout(p_layout); } +void ScriptEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout){ + + script_editor->get_window_layout(p_layout); +} + + void ScriptEditorPlugin::get_breakpoints(List<String> *p_breakpoints) { @@ -1798,8 +2565,15 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) { script_editor->hide(); + EDITOR_DEF("text_editor/auto_reload_changed_scripts",false); + EDITOR_DEF("text_editor/open_dominant_script_on_scene_change",true); EDITOR_DEF("external_editor/use_external_editor",false); EDITOR_DEF("external_editor/exec_path",""); + EDITOR_DEF("text_editor/script_temperature_enabled",true); + EDITOR_DEF("text_editor/script_temperature_history_size",15); + EDITOR_DEF("text_editor/script_temperature_hot_color",Color(1,0,0,0.3)); + EDITOR_DEF("text_editor/script_temperature_cold_color",Color(0,0,1,0.3)); + EDITOR_DEF("text_editor/group_help_pages",false); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"external_editor/exec_path",PROPERTY_HINT_GLOBAL_FILE)); EDITOR_DEF("external_editor/exec_flags",""); diff --git a/tools/editor/plugins/script_editor_plugin.h b/tools/editor/plugins/script_editor_plugin.h index 136d966587..e755f570ef 100644 --- a/tools/editor/plugins/script_editor_plugin.h +++ b/tools/editor/plugins/script_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,15 +30,18 @@ #define SCRIPT_EDITOR_PLUGIN_H #include "tools/editor/editor_plugin.h" +#include "tools/editor/script_create_dialog.h" #include "scene/gui/tab_container.h" #include "scene/gui/text_edit.h" #include "scene/gui/menu_button.h" +#include "scene/gui/tool_button.h" #include "scene/gui/tree.h" #include "scene/main/timer.h" #include "script_language.h" #include "tools/editor/code_editor.h" #include "scene/gui/split_container.h" - +#include "scene/gui/item_list.h" +#include "tools/editor/editor_help.h" class ScriptEditorQuickOpen : public ConfirmationDialog { @@ -88,6 +91,7 @@ protected: virtual void _code_complete_script(const String& p_code, List<String>* r_options); virtual void _load_theme_settings(); void _notification(int p_what); + static void _bind_methods(); public: @@ -97,12 +101,15 @@ public: Vector<String> get_functions() ; void set_edited_script(const Ref<Script>& p_script); void reload_text(); - void _update_name(); + String get_name() ; + Ref<Texture> get_icon() ; ScriptTextEditor(); }; +class EditorScriptCodeCompletionCache; + class ScriptEditor : public VBoxContainer { OBJ_TYPE(ScriptEditor, VBoxContainer ); @@ -110,11 +117,12 @@ class ScriptEditor : public VBoxContainer { EditorNode *editor; enum { - + FILE_NEW, FILE_OPEN, FILE_SAVE, FILE_SAVE_AS, FILE_SAVE_ALL, + FILE_CLOSE, EDIT_UNDO, EDIT_REDO, EDIT_CUT, @@ -123,27 +131,31 @@ class ScriptEditor : public VBoxContainer { EDIT_SELECT_ALL, EDIT_COMPLETE, EDIT_AUTO_INDENT, - EDIT_TOGGLE_COMMENT, - EDIT_MOVE_LINE_UP, - EDIT_MOVE_LINE_DOWN, - EDIT_INDENT_RIGHT, - EDIT_INDENT_LEFT, - EDIT_CLONE_DOWN, + EDIT_TOGGLE_COMMENT, + EDIT_MOVE_LINE_UP, + EDIT_MOVE_LINE_DOWN, + EDIT_INDENT_RIGHT, + EDIT_INDENT_LEFT, + EDIT_CLONE_DOWN, SEARCH_FIND, SEARCH_FIND_NEXT, SEARCH_REPLACE, SEARCH_LOCATE_FUNCTION, SEARCH_GOTO_LINE, + SEARCH_HELP, + SEARCH_CLASSES, + SEARCH_WEBSITE, DEBUG_TOGGLE_BREAKPOINT, DEBUG_NEXT, DEBUG_STEP, DEBUG_BREAK, DEBUG_CONTINUE, DEBUG_SHOW, - HELP_CONTEXTUAL, - WINDOW_CLOSE, + HELP_CONTEXTUAL, WINDOW_MOVE_LEFT, WINDOW_MOVE_RIGHT, + WINDOW_NEXT, + WINDOW_PREV, WINDOW_SELECT_BASE=100 }; @@ -151,16 +163,47 @@ class ScriptEditor : public VBoxContainer { MenuButton *file_menu; MenuButton *edit_menu; MenuButton *search_menu; - MenuButton *window_menu; + MenuButton *script_search_menu; MenuButton *debug_menu; MenuButton *help_menu; + Timer *autosave_timer; uint64_t idle; + Button *help_search; + Button *site_search; + Button *class_search; + EditorHelpSearch *help_search_dialog; + + ItemList *script_list; + HSplitContainer *script_split; TabContainer *tab_container; FindReplaceDialog *find_replace_dialog; GotoLineDialog *goto_line_dialog; ConfirmationDialog *erase_tab_confirm; + ScriptCreateDialog *script_create_dialog; ScriptEditorDebugger* debugger; + ToolButton *scripts_visible; + + TextureFrame *script_icon; + Label *script_name_label; + + ToolButton *script_back; + ToolButton *script_forward; + + + struct ScriptHistory { + + Control *control; + int scroll_pos; + int cursor_column; + int cursor_row; + }; + + Vector<ScriptHistory> history; + int history_pos; + + + EditorHelpIndex *help_index; void _tab_changed(int p_which); void _menu_option(int p_optin); @@ -170,6 +213,8 @@ class ScriptEditor : public VBoxContainer { VSplitContainer *v_split; + bool restoring_layout; + String _get_debug_tooltip(const String&p_text,Node *_ste); void _resave_scripts(const String& p_str); @@ -179,13 +224,18 @@ class ScriptEditor : public VBoxContainer { void _close_current_tab(); + bool grab_focus_block; + ScriptEditorQuickOpen *quick_open; + EditorScriptCodeCompletionCache *completion_cache; void _editor_play(); void _editor_pause(); void _editor_stop(); + int edit_pass; + void _add_callback(Object *p_obj, const String& p_function, const StringArray& p_args); void _res_saved_callback(const Ref<Resource>& p_res); @@ -194,6 +244,34 @@ class ScriptEditor : public VBoxContainer { void _breaked(bool p_breaked,bool p_can_debug); void _show_debugger(bool p_show); void _update_window_menu(); + void _script_created(Ref<Script> p_script); + + void _editor_settings_changed(); + void _autosave_scripts(); + + void _update_script_names(); + + void _script_selected(int p_idx); + + void _find_scripts(Node* p_base, Node* p_current,Set<Ref<Script> >& used); + + void _tree_changed(); + + void _script_split_dragged(float); + + + void _history_forward(); + void _history_back(); + + bool waiting_update_names; + + void _help_class_open(const String& p_class); + void _help_class_goto(const String& p_desc); + void _update_history_arrows(); + void _go_to_tab(int p_idx); + void _update_history_pos(int p_new_pos); + void _update_script_colors(); + static ScriptEditor *script_editor; protected: @@ -202,9 +280,6 @@ protected: public: static ScriptEditor *get_singleton() { return script_editor; } - void _save_files_state(); - void _load_files_state(); - void ensure_focus_current(); void apply_scripts() const; @@ -218,11 +293,19 @@ public: void get_breakpoints(List<String> *p_breakpoints); - void swap_lines(TextEdit *tx, int line1, int line2); + void swap_lines(TextEdit *tx, int line1, int line2); void save_external_data(); + void set_window_layout(Ref<ConfigFile> p_layout); + void get_window_layout(Ref<ConfigFile> p_layout); + + void set_scene_root_script( Ref<Script> p_script ); + + ScriptEditorDebugger *get_debugger() { return debugger; } + ScriptEditor(EditorNode *p_editor); + ~ScriptEditor(); }; class ScriptEditorPlugin : public EditorPlugin { @@ -250,6 +333,9 @@ public: virtual void restore_global_state(); virtual void save_global_state(); + virtual void set_window_layout(Ref<ConfigFile> p_layout); + virtual void get_window_layout(Ref<ConfigFile> p_layout); + virtual void get_breakpoints(List<String> *p_breakpoints); diff --git a/tools/editor/plugins/shader_editor_plugin.cpp b/tools/editor/plugins/shader_editor_plugin.cpp index 3166383fc8..a182d57742 100644 --- a/tools/editor/plugins/shader_editor_plugin.cpp +++ b/tools/editor/plugins/shader_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -57,9 +57,9 @@ void ShaderTextEditor::set_edited_shader(const Ref<Shader>& p_shader,ShaderLangu _load_theme_settings(); - if (p_type==ShaderLanguage::SHADER_MATERIAL_LIGHT) + if (p_type==ShaderLanguage::SHADER_MATERIAL_LIGHT || p_type==ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT) get_text_edit()->set_text(shader->get_light_code()); - else if (p_type==ShaderLanguage::SHADER_MATERIAL_VERTEX) + else if (p_type==ShaderLanguage::SHADER_MATERIAL_VERTEX || p_type==ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX) get_text_edit()->set_text(shader->get_vertex_code()); else get_text_edit()->set_text(shader->get_fragment_code()); @@ -81,6 +81,7 @@ void ShaderTextEditor::_load_theme_settings() { get_text_edit()->add_color_override("font_selected_color",EDITOR_DEF("text_editor/text_selected_color",Color(1,1,1))); get_text_edit()->add_color_override("selection_color",EDITOR_DEF("text_editor/selection_color",Color(0.2,0.2,1))); get_text_edit()->add_color_override("brace_mismatch_color",EDITOR_DEF("text_editor/brace_mismatch_color",Color(1,0.2,0.2))); + get_text_edit()->add_color_override("current_line_color",EDITOR_DEF("text_editor/current_line_color",Color(0.3,0.5,0.8,0.15))); Color keyword_color= EDITOR_DEF("text_editor/keyword_color",Color(0.5,0.0,0.2)); @@ -131,17 +132,12 @@ void ShaderTextEditor::_validate_script() { String errortxt; int line,col; - String code; - if (type==ShaderLanguage::SHADER_MATERIAL_LIGHT) - code=get_text_edit()->get_text(); - else if (type==ShaderLanguage::SHADER_MATERIAL_VERTEX) - code=get_text_edit()->get_text(); - else - code=get_text_edit()->get_text(); - + String code=get_text_edit()->get_text(); //List<StringName> params; //shader->get_param_list(¶ms); + print_line("compile: type: "+itos(type)+" code:\n"+code); + Error err = ShaderLanguage::compile(code,type,NULL,NULL,&errortxt,&line,&col); if (err!=OK) { @@ -233,25 +229,7 @@ void ShaderEditor::_menu_option(int p_option) { goto_line_dialog->popup_find_line(current->get_text_edit()); } break; - case SHADER_POST_PROCESS_MODE:{ - - fragment_editor->set_edited_shader(shader,ShaderLanguage::SHADER_POST_PROCESS); - fragment_editor->_validate_script(); - apply_shaders(); - settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE), false); - settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_POST_PROCESS_MODE), true); - - - } break; - case SHADER_MATERIAL_MODE: { - fragment_editor->set_edited_shader(shader,ShaderLanguage::SHADER_MATERIAL_FRAGMENT); - fragment_editor->_validate_script(); - apply_shaders(); - settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE), true); - settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_POST_PROCESS_MODE), false); - - } break; } } @@ -408,18 +386,17 @@ void ShaderEditor::edit(const Ref<Shader>& p_shader) { shader=p_shader; if (shader->get_mode()==Shader::MODE_MATERIAL) { + vertex_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_MATERIAL_VERTEX); fragment_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_MATERIAL_FRAGMENT); light_editor->set_edited_shader(shader,ShaderLanguage::SHADER_MATERIAL_LIGHT); - settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE), true); - settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_POST_PROCESS_MODE), false); - } else { + } else if (shader->get_mode()==Shader::MODE_CANVAS_ITEM) { - fragment_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_POST_PROCESS); - settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE), false); - settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_POST_PROCESS_MODE), true); + vertex_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX); + fragment_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT); + light_editor->set_edited_shader(shader,ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT); } - vertex_editor->set_edited_shader(shader,ShaderLanguage::SHADER_MATERIAL_VERTEX); + //vertex_editor->set_edited_shader(shader,ShaderLanguage::SHADER_MATERIAL_VERTEX); // see if already has it @@ -495,15 +472,6 @@ ShaderEditor::ShaderEditor() { search_menu->get_popup()->add_item("Goto Line..",SEARCH_GOTO_LINE,KEY_MASK_CMD|KEY_G); search_menu->get_popup()->connect("item_pressed", this,"_menu_option"); - settings_menu = memnew( MenuButton ); - add_child(settings_menu); - settings_menu->set_pos(Point2(90,-1)); - settings_menu->set_text("Shader"); - settings_menu->get_popup()->add_check_item("Material Mode",SHADER_MATERIAL_MODE); - settings_menu->get_popup()->set_item_checked(settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE),true); - settings_menu->get_popup()->add_check_item("Post Process Mode",SHADER_POST_PROCESS_MODE); - - settings_menu->get_popup()->connect("item_pressed", this,"_menu_option"); tab_container->connect("tab_changed", this,"_tab_changed"); @@ -550,7 +518,13 @@ void ShaderEditorPlugin::edit(Object *p_object) { bool ShaderEditorPlugin::handles(Object *p_object) const { - return p_object->is_type("Shader"); + Shader *shader=p_object->cast_to<Shader>(); + if (!shader) + return false; + if (_2d) + return shader->get_mode()==Shader::MODE_CANVAS_ITEM; + else + return shader->get_mode()==Shader::MODE_MATERIAL; } void ShaderEditorPlugin::make_visible(bool p_visible) { @@ -596,12 +570,15 @@ void ShaderEditorPlugin::apply_changes() { shader_editor->apply_shaders(); } -ShaderEditorPlugin::ShaderEditorPlugin(EditorNode *p_node) { +ShaderEditorPlugin::ShaderEditorPlugin(EditorNode *p_node, bool p_2d) { editor=p_node; shader_editor = memnew( ShaderEditor ); - - SpatialEditor::get_singleton()->get_shader_split()->add_child(shader_editor); + _2d=p_2d; + if (p_2d) + add_custom_control(CONTAINER_CANVAS_EDITOR_BOTTOM,shader_editor); + else + add_custom_control(CONTAINER_SPATIAL_EDITOR_BOTTOM,shader_editor); // editor->get_viewport()->add_child(shader_editor); // shader_editor->set_area_as_parent_rect(); diff --git a/tools/editor/plugins/shader_editor_plugin.h b/tools/editor/plugins/shader_editor_plugin.h index 49caee5da6..4ead2ba94e 100644 --- a/tools/editor/plugins/shader_editor_plugin.h +++ b/tools/editor/plugins/shader_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -79,9 +79,6 @@ class ShaderEditor : public Control { SEARCH_REPLACE, //SEARCH_LOCATE_SYMBOL, SEARCH_GOTO_LINE, - SHADER_MATERIAL_MODE, - SHADER_POST_PROCESS_MODE, - SHADER_SHADE_MODEL_MODE, }; @@ -134,6 +131,7 @@ class ShaderEditorPlugin : public EditorPlugin { OBJ_TYPE( ShaderEditorPlugin, EditorPlugin ); + bool _2d; ShaderEditor *shader_editor; EditorNode *editor; public: @@ -152,7 +150,7 @@ public: virtual void save_external_data(); virtual void apply_changes(); - ShaderEditorPlugin(EditorNode *p_node); + ShaderEditorPlugin(EditorNode *p_node,bool p_2d); ~ShaderEditorPlugin(); }; diff --git a/tools/editor/plugins/shader_graph_editor_plugin.cpp b/tools/editor/plugins/shader_graph_editor_plugin.cpp index 2686ca895e..3a7dc26466 100644 --- a/tools/editor/plugins/shader_graph_editor_plugin.cpp +++ b/tools/editor/plugins/shader_graph_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,1082 +28,2807 @@ /*************************************************************************/ #include "shader_graph_editor_plugin.h" -#if 0 + +#include "scene/gui/check_box.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" +#include "spatial_editor_plugin.h" +#include "os/keyboard.h" +#include "canvas_item_editor_plugin.h" -class _ShaderTester : public ShaderCodeGenerator { -public: +void GraphColorRampEdit::_input_event(const InputEvent& p_event) { - Set<int> *_set; + if (p_event.type==InputEvent::KEY && p_event.key.pressed && p_event.key.scancode==KEY_DELETE && grabbed!=-1) { - virtual void begin() {} - virtual Error add_node(VS::ShaderNodeType p_type,int p_node_pos,int p_id,const Variant& p_param,const Vector<int>& p_in_connections,const Vector<int>& p_out_connections,const Vector<int>& p_out_connection_outputs) { if (_set) _set->insert(p_id); return OK; } - virtual void end() {} + points.remove(grabbed); + grabbed=-1; + update(); + emit_signal("ramp_changed"); + accept_event(); + } - _ShaderTester() { _set=NULL; } -}; + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) { + + update(); + int x = p_event.mouse_button.x; + int total_w = get_size().width-get_size().height-3; + if (x>total_w+3) { + + if (grabbed==-1) + return; + Size2 ms = Size2(350, picker->get_combined_minimum_size().height+10); + picker->set_color(points[grabbed].color); + popup->set_pos(get_global_pos()-Size2(0,ms.height)); + popup->set_size(ms); + popup->popup(); + return; + } + float ofs = CLAMP(x/float(total_w),0,1); -void ShaderEditor::edit(Ref<Shader> p_shader) { + grabbed=-1; + grabbing=true; + int pos=-1; + for(int i=0;i<points.size();i++) { + if (ABS(x-points[i].offset*total_w)<4) { + grabbed=i; + } + if (points[i].offset<ofs) + pos=i; + } - shader=p_shader; + grabbed_at=ofs; + //grab or select + if (grabbed!=-1) { + return; + } + //insert - if (shader.is_null()) - hide(); - else { - _read_shader_graph(); - } -} + Point p; + p.offset=ofs; -Size2 ShaderEditor::_get_maximum_size() { + Point prev; + Point next; - Size2 max; + if (pos==-1) { - for(List<int>::Element *E=order.front();E;E=E->next()) { + prev.color=Color(0,0,0); + prev.offset=0; + if (points.size()) { + next=points[0]; + } else { + next.color=Color(1,1,1); + next.offset=1.0; + } + } else { - Point2 pos = Point2( shader_graph.node_get_pos_x(E->get()), shader_graph.node_get_pos_y(E->get()) ); + if (pos==points.size()-1) { + next.color=Color(1,1,1); + next.offset=1.0; + } else { + next=points[pos+1]; + } + prev=points[pos]; - if (click_type==CLICK_NODE && click_node==E->get()) { + } + + p.color=prev.color.linear_interpolate(next.color,(p.offset-prev.offset)/(next.offset-prev.offset)); - pos+=click_motion-click_pos; + points.push_back(p); + points.sort(); + for(int i=0;i<points.size();i++) { + if (points[i].offset==ofs) { + grabbed=i; + break; + } } - pos+=get_node_size(E->get()); - if (pos.x>max.x) - max.x=pos.x; - if (pos.y>max.y) - max.y=pos.y; + + emit_signal("ramp_changed"); } - return max; -} + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && !p_event.mouse_button.pressed) { -Size2 ShaderEditor::get_node_size(int p_node) const { + if (grabbing) { + grabbing=false; + emit_signal("ramp_changed"); + } + update(); + } - VisualServer::ShaderNodeType type=shader_graph.node_get_type(p_node); - Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); - Ref<Font> font = get_font("font","PopupMenu"); - Color font_color = get_color("font_color","PopupMenu"); + if (p_event.type==InputEvent::MOUSE_MOTION && grabbing) { - Size2 size = style->get_minimum_size(); + int total_w = get_size().width-get_size().height-3; - int count=1; // title - count += VisualServer::shader_get_input_count( type) + VisualServer::shader_get_output_count( type); + int x = p_event.mouse_motion.x; + float newofs = CLAMP(x/float(total_w),0,1); - float max_w=font->get_string_size( VisualServer::shader_node_get_type_info(type).name ).width; + bool valid=true; + for(int i=0;i<points.size();i++) { - for(int i=0;i<VisualServer::shader_get_input_count(type);i++) - max_w = MAX( max_w, font->get_string_size( VisualServer::shader_get_input_name(type,i) ).width ); + if (points[i].offset==newofs && i!=grabbed) { + valid=false; + } + } + if (!valid) + return; - for(int i=0;i<VisualServer::shader_get_output_count(type);i++) - max_w = MAX( max_w, font->get_string_size( VisualServer::shader_get_output_name(type,i) ).width ); + points[grabbed].offset=newofs; + points.sort(); + for(int i=0;i<points.size();i++) { + if (points[i].offset==newofs) { + grabbed=i; + break; + } + } + emit_signal("ramp_changed"); + update(); + } +} - switch(type) { +void GraphColorRampEdit::_notification(int p_what){ + + if (p_what==NOTIFICATION_ENTER_TREE) { + picker->connect("color_changed",this,"_color_changed"); + } + if (p_what==NOTIFICATION_DRAW) { - case VS::NODE_IN: - case VS::NODE_OUT: - case VS::NODE_VEC_IN: - case VS::NODE_VEC_OUT: - case VS::NODE_PARAMETER: - case VS::NODE_VEC_PARAMETER: - case VS::NODE_COLOR_PARAMETER: - case VS::NODE_TEXTURE_PARAMETER: - case VS::NODE_TEXTURE_2D_PARAMETER: - case VS::NODE_TEXTURE_CUBE_PARAMETER: - case VS::NODE_TRANSFORM_PARAMETER: - case VS::NODE_LABEL: { - max_w=MAX( max_w, font->get_string_size( shader_graph.node_get_param(p_node) ).width ); - count++; - } break; - case VS::NODE_TIME: - case VS::NODE_CONSTANT: - case VS::NODE_VEC_CONSTANT: - case VS::NODE_COLOR_CONSTANT: - case VS::NODE_TRANSFORM_CONSTANT: { - count++; - } break; - case VS::NODE_TEXTURE: - case VS::NODE_VEC_TEXTURE_2D: - case VS::NODE_VEC_TEXTURE_CUBE: { + Point prev; + prev.offset=0; + prev.color=Color(0,0,0); + int w = get_size().x; + int h = get_size().y; - RefPtr res = shader_graph.node_get_param(p_node); - Ref<Texture> texture = res; - if (texture.is_null() || texture->get_width()==0) { + int total_w = get_size().width-get_size().height-3; - size.y+=max_w; + for(int i=-1;i<points.size();i++) { + + Point next; + if (i+1==points.size()) { + next.color=Color(1,1,1); + next.offset=1; } else { + next=points[i+1]; + } - size.y+=max_w * texture->get_height() / texture->get_width(); + if (prev.offset==next.offset) { + prev=next; + continue; } - } break; - default: {} + + Vector<Vector2> points; + Vector<Color> colors; + points.push_back(Vector2(prev.offset*total_w,h)); + points.push_back(Vector2(prev.offset*total_w,0)); + points.push_back(Vector2(next.offset*total_w,0)); + points.push_back(Vector2(next.offset*total_w,h)); + colors.push_back(prev.color); + colors.push_back(prev.color); + colors.push_back(next.color); + colors.push_back(next.color); + draw_primitive(points,colors,Vector<Point2>()); + prev=next; + } + + for(int i=0;i<points.size();i++) { + + Color col=i==grabbed?Color(1,0.0,0.0,0.9):Color(1,1,1,0.8); + + draw_line(Vector2(points[i].offset*total_w,0),Vector2(points[i].offset*total_w,h-1),Color(0,0,0,0.7)); + draw_line(Vector2(points[i].offset*total_w-1,h/2),Vector2(points[i].offset*total_w-1,h-1),col); + draw_line(Vector2(points[i].offset*total_w+1,h/2),Vector2(points[i].offset*total_w+1,h-1),col); + draw_line(Vector2(points[i].offset*total_w-1,h/2),Vector2(points[i].offset*total_w+1,h/2),col); + draw_line(Vector2(points[i].offset*total_w-1,h-1),Vector2(points[i].offset*total_w+1,h-1),col); + + } + + if (grabbed!=-1) { + + draw_rect(Rect2(total_w+3,0,h,h),points[grabbed].color); + } + + if (has_focus()) { + + draw_line(Vector2(-1,-1),Vector2(total_w+1,-1),Color(1,1,1,0.6)); + draw_line(Vector2(total_w+1,-1),Vector2(total_w+1,h+1),Color(1,1,1,0.6)); + draw_line(Vector2(total_w+1,h+1),Vector2(-1,h+1),Color(1,1,1,0.6)); + draw_line(Vector2(-1,-1),Vector2(-1,h+1),Color(1,1,1,0.6)); + } } +} - size.x+=max_w; - size.y+=count*(font->get_height()+get_constant("vseparation","PopupMenu")); +Size2 GraphColorRampEdit::get_minimum_size() const { - return size; + return Vector2(0,16); } -Error ShaderEditor::validate_graph() { +void GraphColorRampEdit::_color_changed(const Color& p_color) { + + if (grabbed==-1) + return; + points[grabbed].color=p_color; + update(); + emit_signal("ramp_changed"); - _ShaderTester st; - active_nodes.clear(); - st._set=&active_nodes; - return shader_graph.generate(&st); } -void ShaderEditor::_draw_node(int p_node) { +void GraphColorRampEdit::set_ramp(const Vector<float>& p_offsets,const Vector<Color>& p_colors) { + + ERR_FAIL_COND(p_offsets.size()!=p_colors.size()); + points.clear(); + for(int i=0;i<p_offsets.size();i++) { + Point p; + p.offset=p_offsets[i]; + p.color=p_colors[i]; + points.push_back(p); + } - VisualServer::ShaderNodeType type=shader_graph.node_get_type(p_node); - Ref<StyleBox> style = active_nodes.has(p_node)?get_stylebox("panel","PopupMenu"):get_stylebox("panel_disabled","PopupMenu"); - Ref<Font> font = get_font("font","PopupMenu"); - Color font_color = get_color("font_color","PopupMenu"); - Color font_color_title = get_color("font_color_hover","PopupMenu"); - Size2 size=get_node_size(p_node); - Point2 pos = Point2( shader_graph.node_get_pos_x(p_node), shader_graph.node_get_pos_y(p_node) )-offset; + points.sort(); + update(); +} - if (click_type==CLICK_NODE && click_node==p_node) { +Vector<float> GraphColorRampEdit::get_offsets() const{ + Vector<float> ret; + for(int i=0;i<points.size();i++) + ret.push_back(points[i].offset); + return ret; +} +Vector<Color> GraphColorRampEdit::get_colors() const{ - pos+=click_motion-click_pos; + Vector<Color> ret; + for(int i=0;i<points.size();i++) + ret.push_back(points[i].color); + return ret; +} + + +void GraphColorRampEdit::_bind_methods(){ + + ObjectTypeDB::bind_method(_MD("_input_event"),&GraphColorRampEdit::_input_event); + ObjectTypeDB::bind_method(_MD("_color_changed"),&GraphColorRampEdit::_color_changed); + ADD_SIGNAL(MethodInfo("ramp_changed")); +} + +GraphColorRampEdit::GraphColorRampEdit(){ + + grabbed=-1; + grabbing=false; + set_focus_mode(FOCUS_ALL); + + popup = memnew( PopupPanel ); + picker = memnew( ColorPicker ); + popup->add_child(picker); + popup->set_child_rect(picker); + add_child(popup); + +} +//////////// + +void GraphCurveMapEdit::_input_event(const InputEvent& p_event) { + + if (p_event.type==InputEvent::KEY && p_event.key.pressed && p_event.key.scancode==KEY_DELETE && grabbed!=-1) { + + points.remove(grabbed); + grabbed=-1; + update(); + emit_signal("curve_changed"); + accept_event(); } - RID ci = get_canvas_item(); - style->draw(ci,Rect2(pos,size)); + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) { + + update(); + Point2 p = Vector2(p_event.mouse_button.x,p_event.mouse_button.y)/get_size(); + p.y=1.0-p.y; + grabbed=-1; + grabbing=true; - Point2 ofs=style->get_offset()+pos; - Point2 ascent=Point2(0,font->get_ascent()); - float w = size.width-style->get_minimum_size().width; - float h = font->get_height()+get_constant("vseparation","PopupMenu"); + for(int i=0;i<points.size();i++) { - font->draw_halign( ci, ofs+ascent, HALIGN_CENTER,w, VisualServer::shader_node_get_type_info(type).name,font_color_title); - ofs.y+=h; + Vector2 ps = p*get_size(); + Vector2 pt = Vector2(points[i].offset,points[i].height)*get_size(); + if (ps.distance_to(pt)<4) { + grabbed=i; + } - Ref<Texture> vec_icon = get_icon("NodeVecSlot","EditorIcons"); - Ref<Texture> real_icon = get_icon("NodeRealSlot","EditorIcons"); - float icon_h_ofs = Math::floor(( font->get_height()-vec_icon->get_height())/2.0 )+1; + } + + + //grab or select + if (grabbed!=-1) { + return; + } + //insert - for(int i=0;i<VisualServer::shader_get_input_count(type);i++) { + Point np; + np.offset=p.x; + np.height=p.y; + + points.push_back(np); + points.sort(); + for(int i=0;i<points.size();i++) { + if (points[i].offset==p.x && points[i].height==p.y) { + grabbed=i; + break; + } + } + + emit_signal("curve_changed"); - String name = VisualServer::shader_get_input_name(type,i); - font->draw_halign( ci, ofs+ascent, HALIGN_LEFT,w, name,font_color); - Ref<Texture> icon = VisualServer::shader_is_input_vector(type,i)?vec_icon:real_icon; - icon->draw(ci,ofs+Point2(-real_icon->get_width(),icon_h_ofs)); - ofs.y+=h; } - for(int i=0;i<VisualServer::shader_get_output_count(type);i++) { + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && !p_event.mouse_button.pressed) { - String name = VisualServer::shader_get_output_name(type,i); - font->draw_halign( ci, ofs+ascent, HALIGN_RIGHT,w, name,font_color); - Ref<Texture> icon = VisualServer::shader_is_output_vector(type,i)?vec_icon:real_icon; - icon->draw(ci,ofs+Point2(w,icon_h_ofs)); - ofs.y+=h; + if (grabbing) { + grabbing=false; + emit_signal("curve_changed"); + } + update(); } - switch(type) { - - case VS::NODE_IN: - case VS::NODE_OUT: - case VS::NODE_PARAMETER: - case VS::NODE_VEC_IN: - case VS::NODE_COLOR_PARAMETER: - case VS::NODE_VEC_OUT: - case VS::NODE_TEXTURE_PARAMETER: - case VS::NODE_TEXTURE_2D_PARAMETER: - case VS::NODE_TEXTURE_CUBE_PARAMETER: - case VS::NODE_TRANSFORM_CONSTANT: - case VS::NODE_TRANSFORM_PARAMETER: - case VS::NODE_VEC_PARAMETER: - case VS::NODE_LABEL: { - String text = shader_graph.node_get_param(p_node); - font->draw_halign( ci, ofs+ascent, HALIGN_CENTER,w, text,font_color); - } break; - case VS::NODE_TIME: - case VS::NODE_CONSTANT: { - String text = rtos(shader_graph.node_get_param(p_node)); - font->draw_halign( ci, ofs+ascent, HALIGN_CENTER,w, text,font_color); - - } break; - case VS::NODE_VEC_CONSTANT: { - String text = Vector3(shader_graph.node_get_param(p_node)); - font->draw_halign( ci, ofs+ascent, HALIGN_CENTER,w, text,font_color); - } break; - case VS::NODE_COLOR_CONSTANT: { - - Color color = shader_graph.node_get_param(p_node); - Rect2 r(ofs,Size2(w,h)); - VisualServer::get_singleton()->canvas_item_add_rect(ci,r,color); - } break; - case VS::NODE_TEXTURE: - case VS::NODE_VEC_TEXTURE_2D: - case VS::NODE_VEC_TEXTURE_CUBE: { - - Rect2 r(ofs,Size2(w,(pos.y+size.y-style->get_margin(MARGIN_BOTTOM))-ofs.y)); - Vector<Point2> points; - Vector<Point2> uvs; - points.resize(4); - uvs.resize(4); - points[0]=r.pos; - points[1]=r.pos+Point2(r.size.x,0); - points[2]=r.pos+r.size; - points[3]=r.pos+Point2(0,r.size.y); - uvs[0]=Point2(0,0); - uvs[1]=Point2(1,0); - uvs[2]=Point2(1,1); - uvs[3]=Point2(0,1); - - Ref<Texture> texture = shader_graph.node_get_param(p_node).operator RefPtr(); - if (texture.is_null() || texture->get_width()==0) { - texture=get_icon("Click2Edit","EditorIcons"); + if (p_event.type==InputEvent::MOUSE_MOTION && grabbing && grabbed != -1) { + + Point2 p = Vector2(p_event.mouse_button.x,p_event.mouse_button.y)/get_size(); + p.y=1.0-p.y; + + p.x = CLAMP(p.x,0.0,1.0); + p.y = CLAMP(p.y,0.0,1.0); + + bool valid=true; + + for(int i=0;i<points.size();i++) { + + if (points[i].offset==p.x && points[i].height==p.y && i!=grabbed) { + valid=false; } + } + + if (!valid) + return; + + points[grabbed].offset=p.x; + points[grabbed].height=p.y; - VisualServer::get_singleton()->canvas_item_add_primitive(ci,points,Vector<Color>(),uvs,texture->get_rid()); - } break; - default: {} + points.sort(); + for(int i=0;i<points.size();i++) { + if (points[i].offset==p.x && points[i].height==p.y) { + grabbed=i; + break; + } + } + + emit_signal("curve_changed"); + + update(); } } -void ShaderEditor::_node_param_changed() { +void GraphCurveMapEdit::_plot_curve(const Vector2& p_a,const Vector2& p_b,const Vector2& p_c,const Vector2& p_d) { + + float geometry[4][4]; + float tmp1[4][4]; + float tmp2[4][4]; + float deltas[4][4]; + double x, dx, dx2, dx3; + double y, dy, dy2, dy3; + double d, d2, d3; + int lastx, lasty; + int newx, newy; + int ntimes; + int i,j; + + int xmax=get_size().x; + int ymax=get_size().y; + + /* construct the geometry matrix from the segment */ + for (i = 0; i < 4; i++) { + geometry[i][2] = 0; + geometry[i][3] = 0; + } - shader_graph.node_set_param( click_node,property_editor->get_variant() ); - update(); - _write_shader_graph(); -} + geometry[0][0] = (p_a[0] * xmax); + geometry[1][0] = (p_b[0] * xmax); + geometry[2][0] = (p_c[0] * xmax); + geometry[3][0] = (p_d[0] * xmax); + + geometry[0][1] = (p_a[1] * ymax); + geometry[1][1] = (p_b[1] * ymax); + geometry[2][1] = (p_c[1] * ymax); + geometry[3][1] = (p_d[1] * ymax); + + /* subdivide the curve ntimes (1000) times */ + ntimes = 4 * xmax; + /* ntimes can be adjusted to give a finer or coarser curve */ + d = 1.0 / ntimes; + d2 = d * d; + d3 = d * d * d; + + /* construct a temporary matrix for determining the forward differencing deltas */ + tmp2[0][0] = 0; tmp2[0][1] = 0; tmp2[0][2] = 0; tmp2[0][3] = 1; + tmp2[1][0] = d3; tmp2[1][1] = d2; tmp2[1][2] = d; tmp2[1][3] = 0; + tmp2[2][0] = 6*d3; tmp2[2][1] = 2*d2; tmp2[2][2] = 0; tmp2[2][3] = 0; + tmp2[3][0] = 6*d3; tmp2[3][1] = 0; tmp2[3][2] = 0; tmp2[3][3] = 0; + + /* compose the basis and geometry matrices */ + + static const float CR_basis[4][4] = + { + { -0.5, 1.5, -1.5, 0.5 }, + { 1.0, -2.5, 2.0, -0.5 }, + { -0.5, 0.0, 0.5, 0.0 }, + { 0.0, 1.0, 0.0, 0.0 }, + }; + + for (i = 0; i < 4; i++) + { + for (j = 0; j < 4; j++) + { + tmp1[i][j] = (CR_basis[i][0] * geometry[0][j] + + CR_basis[i][1] * geometry[1][j] + + CR_basis[i][2] * geometry[2][j] + + CR_basis[i][3] * geometry[3][j]); + } + } + /* compose the above results to get the deltas matrix */ + + for (i = 0; i < 4; i++) + { + for (j = 0; j < 4; j++) + { + deltas[i][j] = (tmp2[i][0] * tmp1[0][j] + + tmp2[i][1] * tmp1[1][j] + + tmp2[i][2] * tmp1[2][j] + + tmp2[i][3] * tmp1[3][j]); + } + } -ShaderEditor::ClickType ShaderEditor::_locate_click(const Point2& p_click,int *p_node_id,int *p_slot_index) const { - Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); - Ref<Texture> real_icon = get_icon("NodeRealSlot","EditorIcons"); - Ref<Font> font = get_font("font","PopupMenu"); - float h = font->get_height()+get_constant("vseparation","PopupMenu"); - float extra_left=MAX( real_icon->get_width()-style->get_margin(MARGIN_LEFT), 0 ); - float extra_right=MAX( real_icon->get_width()-style->get_margin(MARGIN_RIGHT), 0 ); + /* extract the x deltas */ + x = deltas[0][0]; + dx = deltas[1][0]; + dx2 = deltas[2][0]; + dx3 = deltas[3][0]; + /* extract the y deltas */ + y = deltas[0][1]; + dy = deltas[1][1]; + dy2 = deltas[2][1]; + dy3 = deltas[3][1]; - for(const List<int>::Element *E=order.back();E;E=E->prev()) { - Size2 size=get_node_size(E->get()); - size.width+=extra_left+extra_right; - Point2 pos = Point2( shader_graph.node_get_pos_x(E->get()), shader_graph.node_get_pos_y(E->get()) )-offset; - pos.x-=extra_left; + lastx = CLAMP (x, 0, xmax); + lasty = CLAMP (y, 0, ymax); - Rect2 rect( pos, size ); - if (!rect.has_point(p_click)) - continue; - VisualServer::ShaderNodeType type=shader_graph.node_get_type(E->get()); - if (p_node_id) - *p_node_id=E->get(); - float y=p_click.y-(pos.y+style->get_margin(MARGIN_TOP)); - if (y<h) - return CLICK_NODE; - y-=h; - - for(int i=0;i<VisualServer::shader_get_input_count(type);i++) { - - if (y<h) { - if (p_slot_index) - *p_slot_index=i; - return CLICK_INPUT_SLOT; + /* if (fix255) + { + cd->curve[cd->outline][lastx] = lasty; + } + else + { + cd->curve_ptr[cd->outline][lastx] = lasty; + if(gb_debug) printf("bender_plot_curve xmax:%d ymax:%d\n", (int)xmax, (int)ymax); + } +*/ + /* loop over the curve */ + for (i = 0; i < ntimes; i++) + { + /* increment the x values */ + x += dx; + dx += dx2; + dx2 += dx3; + + /* increment the y values */ + y += dy; + dy += dy2; + dy2 += dy3; + + newx = CLAMP ((Math::round (x)), 0, xmax); + newy = CLAMP ((Math::round (y)), 0, ymax); + + /* if this point is different than the last one...then draw it */ + if ((lastx != newx) || (lasty != newy)) + { +#if 0 + if(fix255) + { + /* use fixed array size (for the curve graph) */ + cd->curve[cd->outline][newx] = newy; } - y-=h; + else + { + /* use dynamic allocated curve_ptr (for the real curve) */ + cd->curve_ptr[cd->outline][newx] = newy; + + if(gb_debug) printf("outline: %d cX: %d cY: %d\n", (int)cd->outline, (int)newx, (int)newy); + } +#endif + draw_line(Vector2(lastx,ymax-lasty),Vector2(newx,ymax-newy),Color(0.8,0.8,0.8,0.8),2.0); } - for(int i=0;i<VisualServer::shader_get_output_count(type);i++) { + lastx = newx; + lasty = newy; + } +} + + +void GraphCurveMapEdit::_notification(int p_what){ + + if (p_what==NOTIFICATION_DRAW) { + + draw_style_box(get_stylebox("bg","Tree"),Rect2(Point2(),get_size())); + + int w = get_size().x; + int h = get_size().y; - if (y<h) { - if (p_slot_index) - *p_slot_index=i; - return CLICK_OUTPUT_SLOT; + Vector2 prev=Vector2(0,0); + Vector2 prev2=Vector2(0,0); + + for(int i=-1;i<points.size();i++) { + + Vector2 next; + Vector2 next2; + if (i+1>=points.size()) { + next=Vector2(1,1); + } else { + next=Vector2(points[i+1].offset,points[i+1].height); } - y-=h; + + if (i+2>=points.size()) { + next2=Vector2(1,1); + } else { + next2=Vector2(points[i+2].offset,points[i+2].height); + } + + /*if (i==-1 && prev.offset==next.offset) { + prev=next; + continue; + }*/ + + _plot_curve(prev2,prev,next,next2); + + prev2=prev; + prev=next; } - if (p_click.y<(rect.pos.y+rect.size.height-style->get_margin(MARGIN_BOTTOM))) - return CLICK_PARAMETER; - else - return CLICK_NODE; + for(int i=0;i<points.size();i++) { + + Color col=i==grabbed?Color(1,0.0,0.0,0.9):Color(1,1,1,0.8); + + + draw_rect(Rect2( Vector2(points[i].offset,1.0-points[i].height)*get_size()-Vector2(2,2),Vector2(5,5)),col); + } + + /* if (grabbed!=-1) { + + draw_rect(Rect2(total_w+3,0,h,h),points[grabbed].color); + } +*/ + if (has_focus()) { + + draw_line(Vector2(-1,-1),Vector2(w+1,-1),Color(1,1,1,0.6)); + draw_line(Vector2(w+1,-1),Vector2(w+1,h+1),Color(1,1,1,0.6)); + draw_line(Vector2(w+1,h+1),Vector2(-1,h+1),Color(1,1,1,0.6)); + draw_line(Vector2(-1,-1),Vector2(-1,h+1),Color(1,1,1,0.6)); + } } +} - return CLICK_NONE; +Size2 GraphCurveMapEdit::get_minimum_size() const { + return Vector2(64,64); } -Point2 ShaderEditor::_get_slot_pos(int p_node_id,bool p_input,int p_slot) { - Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); - float w = get_node_size(p_node_id).width; - Ref<Font> font = get_font("font","PopupMenu"); - float h = font->get_height()+get_constant("vseparation","PopupMenu"); - Ref<Texture> vec_icon = get_icon("NodeVecSlot","EditorIcons"); - Point2 pos = Point2( shader_graph.node_get_pos_x(p_node_id), shader_graph.node_get_pos_y(p_node_id) )-offset; - pos+=style->get_offset(); - pos.y+=h; - if(p_input) { +void GraphCurveMapEdit::set_points(const Vector<Vector2>& p_points) { - pos.y+=p_slot*h; - pos+=Point2( -vec_icon->get_width()/2.0, h/2.0).floor(); - return pos; - } else { - pos.y+=VisualServer::shader_get_input_count( shader_graph.node_get_type(p_node_id ) )*h; + points.clear(); + for(int i=0;i<p_points.size();i++) { + Point p; + p.offset=p_points[i].x; + p.height=p_points[i].y; + points.push_back(p); } - pos.y+=p_slot*h; - pos+=Point2( w-style->get_minimum_size().width+vec_icon->get_width()/2.0, h/2.0).floor(); - - return pos; + points.sort(); + update(); +} +Vector<Vector2> GraphCurveMapEdit::get_points() const { + Vector<Vector2> ret; + for(int i=0;i<points.size();i++) + ret.push_back(Vector2(points[i].offset,points[i].height)); + return ret; } -void ShaderEditor::_node_edit_property(int p_node) { +void GraphCurveMapEdit::_bind_methods(){ - Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); - Size2 size = get_node_size(p_node); - Point2 pos = Point2( shader_graph.node_get_pos_x(p_node), shader_graph.node_get_pos_y(p_node) )-offset; + ObjectTypeDB::bind_method(_MD("_input_event"),&GraphCurveMapEdit::_input_event); + ADD_SIGNAL(MethodInfo("curve_changed")); +} - VisualServer::ShaderNodeType type=shader_graph.node_get_type(p_node); +GraphCurveMapEdit::GraphCurveMapEdit(){ - PropertyInfo ph = VisualServer::get_singleton()->shader_node_get_type_info(type); - if (ph.type==Variant::NIL) - return; - if (ph.type==Variant::_RID) - ph.type=Variant::OBJECT; + grabbed=-1; + grabbing=false; + set_focus_mode(FOCUS_ALL); - property_editor->edit(NULL,ph.name,ph.type,shader_graph.node_get_param(p_node),ph.hint,ph.hint_string); +} - Point2 popup_pos=Point2( pos.x+(size.width-property_editor->get_size().width)/2.0,pos.y+(size.y-style->get_margin(MARGIN_BOTTOM))).floor(); - popup_pos+=get_global_pos(); - property_editor->set_pos(popup_pos); - property_editor->popup(); +////cbacks +/// +void ShaderGraphView::_scalar_const_changed(double p_value,int p_id) { + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change Scalar Constant",true); + ur->add_do_method(graph.ptr(),"scalar_const_node_set_value",type,p_id,p_value); + ur->add_undo_method(graph.ptr(),"scalar_const_node_set_value",type,p_id,graph->scalar_const_node_get_value(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; } -bool ShaderEditor::has_point(const Point2& p_point) const { +void ShaderGraphView::_vec_const_changed(double p_value, int p_id,Array p_arr){ + + Vector3 val; + for(int i=0;i<p_arr.size();i++) { + val[i]=p_arr[i].call("get_val"); + } - int n,si; + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change Vec Constant",true); + ur->add_do_method(graph.ptr(),"vec_const_node_set_value",type,p_id,val); + ur->add_undo_method(graph.ptr(),"vec_const_node_set_value",type,p_id,graph->vec_const_node_get_value(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; - return _locate_click(p_point,&n,&si)!=CLICK_NONE; } +void ShaderGraphView::_rgb_const_changed(const Color& p_color, int p_id){ + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change RGB Constant",true); + ur->add_do_method(graph.ptr(),"rgb_const_node_set_value",type,p_id,p_color); + ur->add_undo_method(graph.ptr(),"rgb_const_node_set_value",type,p_id,graph->rgb_const_node_get_value(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; -void ShaderEditor::_input_event(InputEvent p_event) { +} +void ShaderGraphView::_scalar_op_changed(int p_op, int p_id){ + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change Scalar Operator"); + ur->add_do_method(graph.ptr(),"scalar_op_node_set_op",type,p_id,p_op); + ur->add_undo_method(graph.ptr(),"scalar_op_node_set_op",type,p_id,graph->scalar_op_node_get_op(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; - switch(p_event.type) { +} +void ShaderGraphView::_vec_op_changed(int p_op, int p_id){ + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change Vec Operator"); + ur->add_do_method(graph.ptr(),"vec_op_node_set_op",type,p_id,p_op); + ur->add_undo_method(graph.ptr(),"vec_op_node_set_op",type,p_id,graph->vec_op_node_get_op(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; +} +void ShaderGraphView::_vec_scalar_op_changed(int p_op, int p_id){ + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change VecxScalar Operator"); + ur->add_do_method(graph.ptr(),"vec_scalar_op_node_set_op",type,p_id,p_op); + ur->add_undo_method(graph.ptr(),"vec_scalar_op_node_set_op",type,p_id,graph->vec_scalar_op_node_get_op(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; - case InputEvent::MOUSE_BUTTON: { +} +void ShaderGraphView::_rgb_op_changed(int p_op, int p_id){ + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change RGB Operator"); + ur->add_do_method(graph.ptr(),"rgb_op_node_set_op",type,p_id,p_op); + ur->add_undo_method(graph.ptr(),"rgb_op_node_set_op",type,p_id,graph->rgb_op_node_get_op(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; +} +void ShaderGraphView::_xform_inv_rev_changed(bool p_enabled, int p_id){ + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Toggle Rot Only"); + ur->add_do_method(graph.ptr(),"xform_vec_mult_node_set_no_translation",type,p_id,p_enabled); + ur->add_undo_method(graph.ptr(),"xform_vec_mult_node_set_no_translation",type,p_id,graph->xform_vec_mult_node_get_no_translation(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; +} +void ShaderGraphView::_scalar_func_changed(int p_func, int p_id){ + + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change Scalar Function"); + ur->add_do_method(graph.ptr(),"scalar_func_node_set_function",type,p_id,p_func); + ur->add_undo_method(graph.ptr(),"scalar_func_node_set_function",type,p_id,graph->scalar_func_node_get_function(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; +} +void ShaderGraphView::_vec_func_changed(int p_func, int p_id){ + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change Vec Function"); + ur->add_do_method(graph.ptr(),"vec_func_node_set_function",type,p_id,p_func); + ur->add_undo_method(graph.ptr(),"vec_func_node_set_function",type,p_id,graph->vec_func_node_get_function(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; - if (p_event.mouse_button.pressed) { +} +void ShaderGraphView::_scalar_input_changed(double p_value,int p_id){ + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change Scalar Uniform",true); + ur->add_do_method(graph.ptr(),"scalar_input_node_set_value",type,p_id,p_value); + ur->add_undo_method(graph.ptr(),"scalar_input_node_set_value",type,p_id,graph->scalar_input_node_get_value(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; +} +void ShaderGraphView::_vec_input_changed(double p_value, int p_id,Array p_arr){ - if (p_event.mouse_button.button_index==1) { - click_pos=Point2(p_event.mouse_button.x,p_event.mouse_button.y); - click_motion=click_pos; - click_type = _locate_click(click_pos,&click_node,&click_slot); - if( click_type!=CLICK_NONE) { + Vector3 val; + for(int i=0;i<p_arr.size();i++) { + val[i]=p_arr[i].call("get_val"); + } - order.erase(click_node); - order.push_back(click_node); - update(); - } - switch(click_type) { - case CLICK_INPUT_SLOT: { - click_pos=_get_slot_pos(click_node,true,click_slot); - } break; - case CLICK_OUTPUT_SLOT: { - click_pos=_get_slot_pos(click_node,false,click_slot); - } break; - case CLICK_PARAMETER: { - //open editor - _node_edit_property(click_node); - } break; - } - } - if (p_event.mouse_button.button_index==2) { + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change Vec Uniform",true); + ur->add_do_method(graph.ptr(),"vec_input_node_set_value",type,p_id,val); + ur->add_undo_method(graph.ptr(),"vec_input_node_set_value",type,p_id,graph->vec_input_node_get_value(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; - if (click_type!=CLICK_NONE) { - click_type=CLICK_NONE; - update(); - } else { - // try to disconnect/remove +} +void ShaderGraphView::_xform_input_changed(int p_id, Node *p_button){ - Point2 rclick_pos=Point2(p_event.mouse_button.x,p_event.mouse_button.y); - rclick_type = _locate_click(rclick_pos,&rclick_node,&rclick_slot); - if (rclick_type==CLICK_INPUT_SLOT || rclick_type==CLICK_OUTPUT_SLOT) { - node_popup->clear(); - node_popup->add_item("Disconnect",NODE_DISCONNECT); - node_popup->set_pos(rclick_pos); - node_popup->popup(); + ToolButton *tb = p_button->cast_to<ToolButton>(); + ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height)); + ped_popup->set_size(tb->get_size()); + edited_id=p_id; + edited_def=-1; + ped_popup->edit(NULL,"",Variant::TRANSFORM,graph->xform_input_node_get_value(type,p_id),PROPERTY_HINT_NONE,""); + ped_popup->popup(); - } +} +void ShaderGraphView::_xform_const_changed(int p_id, Node *p_button){ - if (rclick_type==CLICK_NODE) { - node_popup->clear(); - node_popup->add_item("Remove",NODE_ERASE); - node_popup->set_pos(rclick_pos); - node_popup->popup(); - } + ToolButton *tb = p_button->cast_to<ToolButton>(); + ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height)); + ped_popup->set_size(tb->get_size()); + edited_id=p_id; + edited_def=-1; + ped_popup->edit(NULL,"",Variant::TRANSFORM,graph->xform_const_node_get_value(type,p_id),PROPERTY_HINT_NONE,""); + ped_popup->popup(); +} - } - } - } else { +void ShaderGraphView::_rgb_input_changed(const Color& p_color, int p_id){ - if (p_event.mouse_button.button_index==1 && click_type!=CLICK_NONE) { - switch(click_type) { - case CLICK_INPUT_SLOT: - case CLICK_OUTPUT_SLOT: { + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change RGB Uniform",true); + ur->add_do_method(graph.ptr(),"rgb_input_node_set_value",type,p_id,p_color); + ur->add_undo_method(graph.ptr(),"rgb_input_node_set_value",type,p_id,graph->rgb_input_node_get_value(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; +} +void ShaderGraphView::_tex_input_change(int p_id, Node *p_button){ - Point2 dst_click_pos=Point2(p_event.mouse_button.x,p_event.mouse_button.y); - int id; - int slot; - ClickType dst_click_type = _locate_click(dst_click_pos,&id,&slot); - if (dst_click_type==CLICK_INPUT_SLOT && click_type==CLICK_OUTPUT_SLOT) { - shader_graph.connect(click_node,click_slot,id,slot); +} +void ShaderGraphView::_cube_input_change(int p_id){ - Error err = validate_graph(); - if (err==ERR_CYCLIC_LINK) - shader_graph.disconnect(click_node,click_slot,id,slot); - _write_shader_graph(); - } - if (click_type==CLICK_INPUT_SLOT && dst_click_type==CLICK_OUTPUT_SLOT) { +} - shader_graph.connect(id,slot,click_node,click_slot); +void ShaderGraphView::_variant_edited() { + + if (edited_def != -1) { + + Variant v = ped_popup->get_variant(); + Variant v2 = graph->default_get_value(type,edited_id,edited_def); + if (v2.get_type() == Variant::NIL) + switch (v.get_type()) { + case Variant::VECTOR3: + v2=Vector3(); + break; + case Variant::REAL: + v2=0.0; + break; + case Variant::TRANSFORM: + v2=Transform(); + break; + case Variant::COLOR: + v2=Color(); + break; + } + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change default value"); + ur->add_do_method(graph.ptr(),"default_set_value",type,edited_id,edited_def, v); + ur->add_undo_method(graph.ptr(),"default_set_value",type,edited_id,edited_def, v2); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + ur->commit_action(); + return; + } - Error err = validate_graph(); - if (err==ERR_CYCLIC_LINK) - shader_graph.disconnect(id,slot,click_node,click_slot); - _write_shader_graph(); - } + if (graph->node_get_type(type,edited_id)==ShaderGraph::NODE_XFORM_CONST) { - } break; - case CLICK_NODE: { - int new_x=shader_graph.node_get_pos_x(click_node)+(click_motion.x-click_pos.x); - int new_y=shader_graph.node_get_pos_y(click_node)+(click_motion.y-click_pos.y); - shader_graph.node_set_pos(click_node,new_x,new_y); - _write_shader_graph(); + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change XForm Uniform"); + ur->add_do_method(graph.ptr(),"xform_const_node_set_value",type,edited_id,ped_popup->get_variant()); + ur->add_undo_method(graph.ptr(),"xform_const_node_set_value",type,edited_id,graph->xform_const_node_get_value(type,edited_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + ur->commit_action(); + } - } break; - } - click_type=CLICK_NONE; - update(); - } - } + if (graph->node_get_type(type,edited_id)==ShaderGraph::NODE_XFORM_INPUT) { - } + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change XForm Uniform"); + ur->add_do_method(graph.ptr(),"xform_input_node_set_value",type,edited_id,ped_popup->get_variant()); + ur->add_undo_method(graph.ptr(),"xform_input_node_set_value",type,edited_id,graph->xform_input_node_get_value(type,edited_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + ur->commit_action(); + } - case InputEvent::MOUSE_MOTION: { + if (graph->node_get_type(type,edited_id)==ShaderGraph::NODE_TEXTURE_INPUT) { - if (p_event.mouse_motion.button_mask&1 && click_type!=CLICK_NONE) { + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change Texture Uniform"); + ur->add_do_method(graph.ptr(),"texture_input_node_set_value",type,edited_id,ped_popup->get_variant()); + ur->add_undo_method(graph.ptr(),"texture_input_node_set_value",type,edited_id,graph->texture_input_node_get_value(type,edited_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + ur->commit_action(); + } - click_motion=Point2(p_event.mouse_button.x,p_event.mouse_button.y); - update(); - } + if (graph->node_get_type(type,edited_id)==ShaderGraph::NODE_CUBEMAP_INPUT) { - } break; + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change Cubemap Uniform"); + ur->add_do_method(graph.ptr(),"cubemap_input_node_set_value",type,edited_id,ped_popup->get_variant()); + ur->add_undo_method(graph.ptr(),"cubemap_input_node_set_value",type,edited_id,graph->cubemap_input_node_get_value(type,edited_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + ur->commit_action(); } + } -void ShaderEditor::_notification(int p_what) { +void ShaderGraphView::_comment_edited(int p_id,Node* p_button) { + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + TextEdit *te=p_button->cast_to<TextEdit>(); + ur->create_action("Change Comment",true); + ur->add_do_method(graph.ptr(),"comment_node_set_text",type,p_id,te->get_text()); + ur->add_undo_method(graph.ptr(),"comment_node_set_text",type,p_id,graph->comment_node_get_text(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; - switch(p_what) { +} - case NOTIFICATION_DRAW: { +void ShaderGraphView::_color_ramp_changed(int p_id,Node* p_ramp) { - _update_scrollbars(); - //VisualServer::get_singleton()->canvas_item_add_rect(get_canvas_item(),Rect2(Point2(),get_size()),Color(0,0,0,1)); + GraphColorRampEdit *cr=p_ramp->cast_to<GraphColorRampEdit>(); - for(List<int>::Element *E=order.front();E;E=E->next()) { + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); - _draw_node(E->get()); - } - if (click_type==CLICK_INPUT_SLOT || click_type==CLICK_OUTPUT_SLOT) { + Vector<float> offsets=cr->get_offsets(); + Vector<Color> colors=cr->get_colors(); - VisualServer::get_singleton()->canvas_item_add_line(get_canvas_item(),click_pos,click_motion,Color(0.5,1,0.5,0.8),2); - } + DVector<float> new_offsets; + DVector<Color> new_colors; + { + new_offsets.resize(offsets.size()); + new_colors.resize(colors.size()); + DVector<float>::Write ow=new_offsets.write(); + DVector<Color>::Write cw=new_colors.write(); + for(int i=0;i<new_offsets.size();i++) { + ow[i]=offsets[i]; + cw[i]=colors[i]; + } - List<ShaderGraph::Connection> connections = shader_graph.get_connection_list(); - for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) { + } - const ShaderGraph::Connection &c=E->get(); - Point2 source = _get_slot_pos(c.src_id,false,c.src_slot); - Point2 dest = _get_slot_pos(c.dst_id,true,c.dst_slot); - bool vec = VisualServer::shader_is_input_vector( shader_graph.node_get_type(c.dst_id), c.dst_slot ); - Color col = vec?Color(1,0.5,0.5,0.8):Color(1,1,0.5,0.8); - if (click_type==CLICK_NODE && click_node==c.src_id) { + DVector<float> old_offsets=graph->color_ramp_node_get_offsets(type,p_id); + DVector<Color> old_colors=graph->color_ramp_node_get_colors(type,p_id); - source+=click_motion-click_pos; - } + if (old_offsets.size()!=new_offsets.size()) + ur->create_action("Add/Remove to Color Ramp"); + else + ur->create_action("Modify Color Ramp",true); - if (click_type==CLICK_NODE && click_node==c.dst_id) { + ur->add_do_method(graph.ptr(),"color_ramp_node_set_ramp",type,p_id,new_colors,new_offsets); + ur->add_undo_method(graph.ptr(),"color_ramp_node_set_ramp",type,p_id,old_colors,old_offsets); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; +} - dest+=click_motion-click_pos; - } +void ShaderGraphView::_curve_changed(int p_id,Node* p_curve) { - VisualServer::get_singleton()->canvas_item_add_line(get_canvas_item(),source,dest,col,2); + GraphCurveMapEdit *cr=p_curve->cast_to<GraphCurveMapEdit>(); + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + + + Vector<Point2> points=cr->get_points(); + + DVector<Vector2> new_points; + { + new_points.resize(points.size()); + DVector<Vector2>::Write ow=new_points.write(); + for(int i=0;i<new_points.size();i++) { + ow[i]=points[i]; + } - } - } break; } + + DVector<Vector2> old_points=graph->curve_map_node_get_points(type,p_id); + + if (old_points.size()!=new_points.size()) + ur->create_action("Add/Remove to Curve Map"); + else + ur->create_action("Modify Curve Map",true); + + ur->add_do_method(graph.ptr(),"curve_map_node_set_points",type,p_id,new_points); + ur->add_undo_method(graph.ptr(),"curve_map_node_set_points",type,p_id,old_points); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; } -void ShaderEditor::_update_scrollbars() { - Size2 size = get_size(); - Size2 hmin = h_scroll->get_minimum_size(); - Size2 vmin = v_scroll->get_minimum_size(); +void ShaderGraphView::_input_name_changed(const String& p_name, int p_id, Node *p_line_edit) { - v_scroll->set_begin( Point2(size.width - vmin.width, 0) ); - v_scroll->set_end( Point2(size.width, size.height) ); + LineEdit *le=p_line_edit->cast_to<LineEdit>(); + ERR_FAIL_COND(!le); - h_scroll->set_begin( Point2( 0, size.height - hmin.height) ); - h_scroll->set_end( Point2(size.width-vmin.width, size.height) ); + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Change Input Name"); + ur->add_do_method(graph.ptr(),"input_node_set_name",type,p_id,p_name); + ur->add_undo_method(graph.ptr(),"input_node_set_name",type,p_id,graph->input_node_get_name(type,p_id)); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; + le->set_text(graph->input_node_get_name(type,p_id)); +} +void ShaderGraphView::_tex_edited(int p_id,Node* p_button) { - Size2 min = _get_maximum_size(); + ToolButton *tb = p_button->cast_to<ToolButton>(); + ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height)); + ped_popup->set_size(tb->get_size()); + edited_id=p_id; + edited_def=-1; + ped_popup->edit(NULL,"",Variant::OBJECT,graph->texture_input_node_get_value(type,p_id),PROPERTY_HINT_RESOURCE_TYPE,"Texture"); +} - if (min.height < size.height - hmin.height) { +void ShaderGraphView::_cube_edited(int p_id,Node* p_button) { - v_scroll->hide(); - offset.y=0; - } else { + ToolButton *tb = p_button->cast_to<ToolButton>(); + ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height)); + ped_popup->set_size(tb->get_size()); + edited_id=p_id; + edited_def=-1; + ped_popup->edit(NULL,"",Variant::OBJECT,graph->cubemap_input_node_get_value(type,p_id),PROPERTY_HINT_RESOURCE_TYPE,"CubeMap"); +} + + +//////////////view///////////// + + +void ShaderGraphView::_connection_request(const String& p_from, int p_from_slot,const String& p_to,int p_to_slot) { - v_scroll->show(); - v_scroll->set_max(min.height); - v_scroll->set_page(size.height - hmin.height); - offset.y=v_scroll->get_val(); + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + + int from_idx=-1; + int to_idx=-1; + for (Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) { + + if (p_from==E->get()->get_name()) + from_idx=E->key(); + if (p_to==E->get()->get_name()) + to_idx=E->key(); } - if (min.width < size.width - vmin.width) { + ERR_FAIL_COND(from_idx==-1); + ERR_FAIL_COND(to_idx==-1); - h_scroll->hide(); - offset.x=0; - } else { + ur->create_action("Connect Graph Nodes"); + + List<ShaderGraph::Connection> conns; - h_scroll->show(); - h_scroll->set_max(min.width); - h_scroll->set_page(size.width - vmin.width); - offset.x=h_scroll->get_val(); + graph->get_node_connections(type,&conns); + //disconnect/reconnect dependencies + ur->add_undo_method(graph.ptr(),"disconnect_node",type,from_idx,p_from_slot,to_idx,p_to_slot); + for(List<ShaderGraph::Connection>::Element *E=conns.front();E;E=E->next()) { + + if (E->get().dst_id==to_idx && E->get().dst_slot==p_to_slot) { + ur->add_do_method(graph.ptr(),"disconnect_node",type,E->get().src_id,E->get().src_slot,E->get().dst_id,E->get().dst_slot); + ur->add_undo_method(graph.ptr(),"connect_node",type,E->get().src_id,E->get().src_slot,E->get().dst_id,E->get().dst_slot); + } } -} + ur->add_do_method(graph.ptr(),"connect_node",type,from_idx,p_from_slot,to_idx,p_to_slot); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + ur->commit_action(); -void ShaderEditor::_scroll_moved() { - offset.x=h_scroll->get_val(); - offset.y=v_scroll->get_val(); - update(); } -void ShaderEditor::_bind_methods() { +void ShaderGraphView::_disconnection_request(const String& p_from, int p_from_slot,const String& p_to,int p_to_slot) { + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + + int from_idx=-1; + int to_idx=-1; + for (Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) { + + if (p_from==E->get()->get_name()) + from_idx=E->key(); + if (p_to==E->get()->get_name()) + to_idx=E->key(); + } + + ERR_FAIL_COND(from_idx==-1); + ERR_FAIL_COND(to_idx==-1); + + if (!graph->is_node_connected(type,from_idx,p_from_slot,to_idx,p_to_slot)) + return; //nothing to disconnect + + ur->create_action("Disconnect Graph Nodes"); + + List<ShaderGraph::Connection> conns; + + graph->get_node_connections(type,&conns); + //disconnect/reconnect dependencies + ur->add_do_method(graph.ptr(),"disconnect_node",type,from_idx,p_from_slot,to_idx,p_to_slot); + ur->add_undo_method(graph.ptr(),"connect_node",type,from_idx,p_from_slot,to_idx,p_to_slot); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + ur->commit_action(); + - ObjectTypeDB::bind_method( "_node_menu_item", &ShaderEditor::_node_menu_item ); - ObjectTypeDB::bind_method( "_node_add_callback", &ShaderEditor::_node_add_callback ); - ObjectTypeDB::bind_method( "_input_event", &ShaderEditor::_input_event ); - ObjectTypeDB::bind_method( "_node_param_changed", &ShaderEditor::_node_param_changed ); - ObjectTypeDB::bind_method( "_scroll_moved", &ShaderEditor::_scroll_moved ); - ObjectTypeDB::bind_method( "_vertex_item", &ShaderEditor::_vertex_item ); - ObjectTypeDB::bind_method( "_fragment_item", &ShaderEditor::_fragment_item ); - ObjectTypeDB::bind_method( "_post_item", &ShaderEditor::_post_item ); } -void ShaderEditor::_read_shader_graph() { +void ShaderGraphView::_node_removed(int p_id) { + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Remove Shader Graph Node"); - shader_graph.clear();; - order.clear(); - List<int> nodes; - shader->get_node_list(&nodes); - int larger_id=0; - for(List<int>::Element *E=nodes.front();E;E=E->next()) { + ur->add_do_method(graph.ptr(),"node_remove",type,p_id); + ur->add_undo_method(graph.ptr(),"node_add",type,graph->node_get_type(type,p_id),p_id); + ur->add_undo_method(graph.ptr(),"node_set_state",type,p_id,graph->node_get_state(type,p_id)); + List<ShaderGraph::Connection> conns; - if (E->get() > larger_id) - larger_id = E->get(); + graph->get_node_connections(type,&conns); + for(List<ShaderGraph::Connection>::Element *E=conns.front();E;E=E->next()) { - shader_graph.node_add( (VS::ShaderNodeType)shader->node_get_type(E->get()), E->get() ); - shader_graph.node_set_param( E->get(), shader->node_get_param( E->get() ) ); - Point2 pos = shader->node_get_pos(E->get()); - shader_graph.node_set_pos( E->get(), pos.x,pos.y ); - order.push_back(E->get()); + if (E->get().dst_id==p_id || E->get().src_id==p_id) { + ur->add_undo_method(graph.ptr(),"connect_node",type,E->get().src_id,E->get().src_slot,E->get().dst_id,E->get().dst_slot); + } } + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + ur->commit_action(); - last_id=larger_id+1; +} - List<Shader::Connection> connections; - shader->get_connections(&connections); +void ShaderGraphView::_begin_node_move() +{ + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Move Shader Graph Node"); +} - for(List<Shader::Connection>::Element *E=connections.front();E;E=E->next()) { +void ShaderGraphView::_node_moved(const Vector2& p_from, const Vector2& p_to,int p_id) { - Shader::Connection &c=E->get(); - shader_graph.connect(c.src_id,c.src_slot,c.dst_id,c.dst_slot); - } - validate_graph(); - update(); + ERR_FAIL_COND(!node_map.has(p_id)); + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->add_do_method(this,"_move_node",p_id,p_to); + ur->add_undo_method(this,"_move_node",p_id,p_from); } -void ShaderEditor::_write_shader_graph() { +void ShaderGraphView::_end_node_move() +{ + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->commit_action(); +} - shader->clear(); - List<int> nodes; - shader_graph.get_node_list(&nodes); - for(List<int>::Element *E=nodes.front();E;E=E->next()) { +void ShaderGraphView::_move_node(int p_id,const Vector2& p_to) { - shader->node_add((Shader::NodeType)shader_graph.node_get_type(E->get()),E->get()); - shader->node_set_param(E->get(),shader_graph.node_get_param(E->get())); - shader->node_set_pos(E->get(),Point2( shader_graph.node_get_pos_x(E->get()),shader_graph.node_get_pos_y(E->get()) ) ); - } + ERR_FAIL_COND(!node_map.has(p_id)); + node_map[p_id]->set_offset(p_to); + graph->node_set_pos(type,p_id,p_to); +} - List<ShaderGraph::Connection> connections = shader_graph.get_connection_list(); - for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) { +void ShaderGraphView::_duplicate_nodes_request() +{ + Array s_id; - const ShaderGraph::Connection &c=E->get(); - shader->connect(c.src_id,c.src_slot,c.dst_id,c.dst_slot); + for(Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) { + ShaderGraph::NodeType t=graph->node_get_type(type, E->key()); + if (t==ShaderGraph::NODE_OUTPUT || t==ShaderGraph::NODE_INPUT) + continue; + GraphNode *gn = E->get(); + if (gn && gn->is_selected()) + s_id.push_back(E->key()); } + + if (s_id.size()==0) + return; + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Duplicate Graph Node(s)"); + ur->add_do_method(this,"_duplicate_nodes",s_id); + List<int> n_ids = graph->generate_ids(type, s_id.size()); + for (List<int>::Element *E=n_ids.front();E;E=E->next()) + ur->add_undo_method(graph.ptr(),"node_remove",type,E->get()); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + ur->commit_action(); + } -void ShaderEditor::_add_node_from_text(const String& p_text) { +void ShaderGraphView::_duplicate_nodes(const Array &p_nodes) +{ + List<int> n = List<int>(); + for (int i=0; i<p_nodes.size();i++) + n.push_back(p_nodes.get(i)); + graph->duplicate_nodes(type, n); + call_deferred("_update_graph"); +} - ERR_FAIL_COND( p_text.get_slice_count(" ") != 3 ); - bool input = p_text.get_slice(" ",0)=="In:"; - String name = p_text.get_slice(" ",1); - bool vec = p_text.get_slice(" ",2)=="(vec3)"; +void ShaderGraphView::_delete_nodes_request() +{ + List<int> s_id=List<int>(); - _node_add( input? - ( vec? VisualServer::NODE_VEC_IN : VisualServer::NODE_IN ) : - ( vec? VisualServer::NODE_VEC_OUT : VisualServer::NODE_OUT ) ); + for(Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) { + ShaderGraph::NodeType t=graph->node_get_type(type, E->key()); + if (t==ShaderGraph::NODE_OUTPUT) + continue; + GraphNode *gn = E->get(); + if (gn && gn->is_selected()) + s_id.push_back(E->key()); + } + + if (s_id.size()==0) + return; + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Delete Shader Graph Node(s)"); + + for (List<int>::Element *N=s_id.front();N;N=N->next()) { + ur->add_do_method(graph.ptr(),"node_remove",type,N->get()); + ur->add_undo_method(graph.ptr(),"node_add",type,graph->node_get_type(type,N->get()),N->get()); + ur->add_undo_method(graph.ptr(),"node_set_state",type,N->get(),graph->node_get_state(type,N->get())); + List<ShaderGraph::Connection> conns; + + graph->get_node_connections(type,&conns); + for(List<ShaderGraph::Connection>::Element *E=conns.front();E;E=E->next()) { + + if (E->get().dst_id==N->get() || E->get().src_id==N->get()) { + ur->add_undo_method(graph.ptr(),"connect_node",type,E->get().src_id,E->get().src_slot,E->get().dst_id,E->get().dst_slot); + } + } + } + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + ur->commit_action(); - shader_graph.node_set_param( last_id-1,name ); - _write_shader_graph(); } -void ShaderEditor::_vertex_item(int p_item) { +void ShaderGraphView::_default_changed(int p_id, Node *p_button, int p_param, int v_type, String p_hint) +{ + ToolButton *tb = p_button->cast_to<ToolButton>(); + ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height)); + ped_popup->set_size(tb->get_size()); + edited_id=p_id; + edited_def=p_param; + Variant::Type vt = (Variant::Type)v_type; + Variant v = graph->default_get_value(type,p_id,edited_def); + int h=PROPERTY_HINT_NONE; + if (v.get_type() == Variant::NIL) + switch (vt) { + case Variant::VECTOR3: + v=Vector3(); + break; + case Variant::REAL: + h=PROPERTY_HINT_RANGE; + v=0.0; + break; + case Variant::TRANSFORM: + v=Transform(); + break; + case Variant::COLOR: + h=PROPERTY_HINT_COLOR_NO_ALPHA; + v=Color(); + break; + } + + ped_popup->edit(NULL,"",vt,v,h,p_hint); - _add_node_from_text(vertex_popup->get_item_text(p_item)); + ped_popup->popup(); } -void ShaderEditor::_fragment_item(int p_item) { - _add_node_from_text(fragment_popup->get_item_text(p_item)); +ToolButton *ShaderGraphView::make_label(String text, Variant::Type v_type) { + ToolButton *l = memnew( ToolButton ); + l->set_text(text); + l->set_text_align(ToolButton::ALIGN_LEFT); + l->add_style_override("hover", l->get_stylebox("normal", "ToolButton")); + l->add_style_override("pressed", l->get_stylebox("normal", "ToolButton")); + l->add_style_override("focus", l->get_stylebox("normal", "ToolButton")); + switch (v_type) { + case Variant::REAL: + l->set_icon(ped_popup->get_icon("Real", "EditorIcons")); + break; + case Variant::VECTOR3: + l->set_icon(ped_popup->get_icon("Vector", "EditorIcons")); + break; + case Variant::TRANSFORM: + l->set_icon(ped_popup->get_icon("Matrix", "EditorIcons")); + break; + case Variant::COLOR: + l->set_icon(ped_popup->get_icon("Color", "EditorIcons")); + } + return l; } -void ShaderEditor::_post_item(int p_item) { - _add_node_from_text(post_popup->get_item_text(p_item)); +ToolButton *ShaderGraphView::make_editor(String text,GraphNode* gn,int p_id,int param,Variant::Type v_type, String p_hint) { + ToolButton *edit = memnew( ToolButton ); + edit->set_text(text); + edit->set_text_align(ToolButton::ALIGN_LEFT); + edit->set_flat(false); + edit->add_style_override("normal", gn->get_stylebox("defaultframe", "GraphNode")); + edit->add_style_override("hover", gn->get_stylebox("defaultframe", "GraphNode")); + edit->add_style_override("pressed", gn->get_stylebox("defaultframe", "GraphNode")); + edit->add_style_override("focus", gn->get_stylebox("defaultfocus", "GraphNode")); + edit->connect("pressed",this,"_default_changed",varray(p_id,edit,param,v_type,p_hint)); + + switch (v_type) { + case Variant::REAL: + edit->set_icon(ped_popup->get_icon("Real", "EditorIcons")); + break; + case Variant::VECTOR3: + edit->set_icon(ped_popup->get_icon("Vector", "EditorIcons")); + break; + case Variant::TRANSFORM: + edit->set_icon(ped_popup->get_icon("Matrix", "EditorIcons")); + break; + case Variant::COLOR: + Image icon_color = Image(15,15,false,Image::FORMAT_RGB); + Color c = graph->default_get_value(type,p_id,param); + for (int x=1;x<14;x++) + for (int y=1;y<14;y++) + icon_color.put_pixel(x,y,c); + Ref<ImageTexture> t; + t.instance(); + t->create_from_image(icon_color); + edit->set_icon(t); + break; + } + return edit; } +void ShaderGraphView::_create_node(int p_id) { -void ShaderEditor::_node_menu_item(int p_item) { - switch(p_item) { + GraphNode *gn = memnew( GraphNode ); + gn->set_show_close_button(true); + Color typecol[4]={ + Color(0.9,0.4,1), + Color(0.8,1,0.2), + Color(1,0.2,0.2), + Color(0,1,1) + }; - case GRAPH_ADD_NODE: { - add_popup->popup_centered_ratio(); - validate_graph(); - } break; - case NODE_DISCONNECT: { + const String hint_spin = "-65536,65535,0.001"; + const String hint_slider = "0.0,1.0,0.01,slider"; - if (rclick_type==CLICK_INPUT_SLOT) { - List<ShaderGraph::Connection> connections = shader_graph.get_connection_list(); - for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) { + switch(graph->node_get_type(type,p_id)) { - const ShaderGraph::Connection &c=E->get(); - if( c.dst_id==rclick_node && c.dst_slot==rclick_slot) { + case ShaderGraph::NODE_INPUT: { - shader_graph.disconnect(c.src_id,c.src_slot,c.dst_id,c.dst_slot); - } - } - update(); - _write_shader_graph(); - validate_graph(); + gn->set_title("Input"); + + List<ShaderGraph::SlotInfo> si; + ShaderGraph::get_input_output_node_slot_info(graph->get_mode(),type,&si); + + int idx=0; + for (List<ShaderGraph::SlotInfo>::Element *E=si.front();E;E=E->next()) { + ShaderGraph::SlotInfo& s=E->get(); + if (s.dir==ShaderGraph::SLOT_IN) { + + Label *l= memnew( Label ); + l->set_text(s.name); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child(l); + gn->set_slot(idx,false,0,Color(),true,s.type,typecol[s.type]); + idx++; } + } - if (rclick_type==CLICK_OUTPUT_SLOT) { + } break; // all inputs (case Shader type dependent) + case ShaderGraph::NODE_SCALAR_CONST: { + gn->set_title("Scalar"); + SpinBox *sb = memnew( SpinBox ); + sb->set_min(-100000); + sb->set_max(100000); + sb->set_step(0.001); + sb->set_val(graph->scalar_const_node_get_value(type,p_id)); + sb->connect("value_changed",this,"_scalar_const_changed",varray(p_id)); + gn->add_child(sb); + gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + } break; //scalar constant + case ShaderGraph::NODE_VEC_CONST: { + + gn->set_title("Vector"); + Array v3p(true); + for(int i=0;i<3;i++) { + HBoxContainer *hbc = memnew( HBoxContainer ); + Label *l = memnew( Label ); + l->set_text(String::chr('X'+i)); + hbc->add_child(l); + SpinBox *sb = memnew( SpinBox ); + sb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + sb->set_min(-100000); + sb->set_max(100000); + sb->set_step(0.001); + sb->set_val(graph->vec_const_node_get_value(type,p_id)[i]); + sb->connect("value_changed",this,"_vec_const_changed",varray(p_id,v3p)); + v3p.push_back(sb); + hbc->add_child(sb); + gn->add_child(hbc); + } + gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + + } break; //vec3 constant + case ShaderGraph::NODE_RGB_CONST: { + + gn->set_title("Color"); + ColorPickerButton *cpb = memnew( ColorPickerButton ); + cpb->set_color(graph->rgb_const_node_get_value(type,p_id)); + cpb->connect("color_changed",this,"_rgb_const_changed",varray(p_id)); + gn->add_child(cpb); + Label *l = memnew( Label ); + l->set_text("RGB"); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child(l); + l = memnew( Label ); + l->set_text("Alpha"); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child(l); + + gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(2,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + } break; //rgb constant (shows a color picker instead) + case ShaderGraph::NODE_XFORM_CONST: { + gn->set_title("XForm"); + ToolButton *edit = memnew( ToolButton ); + edit->set_text("edit.."); + edit->connect("pressed",this,"_xform_const_changed",varray(p_id,edit)); + gn->add_child(edit); + gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]); + + } break; // 4x4 matrix constant + case ShaderGraph::NODE_TIME: { + + gn->set_title("Time"); + Label *l = memnew( Label ); + l->set_text("(s)"); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child(l); + gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + } break; // time in seconds + case ShaderGraph::NODE_SCREEN_TEX: { + + gn->set_title("ScreenTex"); + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (!graph->is_slot_connected(type,p_id,0)) { + Vector3 v = graph->default_get_value(type, p_id, 0); + hbc->add_child(make_editor("UV: " + v,gn,p_id,0,Variant::VECTOR3)); + } else { + hbc->add_child(make_label("UV",Variant::VECTOR3)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("RGB"))); + gn->add_child(hbc); + gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + + } break; // screen texture sampler (takes UV) (only usable in fragment case Shader) + case ShaderGraph::NODE_SCALAR_OP: { + + gn->set_title("ScalarOp"); + static const char* op_name[ShaderGraph::SCALAR_MAX_OP]={ + "Add", + "Sub", + "Mul", + "Div", + "Mod", + "Pow", + "Max", + "Min", + "Atan2" + }; + + OptionButton *ob = memnew( OptionButton ); + for(int i=0;i<ShaderGraph::SCALAR_MAX_OP;i++) { + + ob->add_item(op_name[i],i); + } - List<ShaderGraph::Connection> connections = shader_graph.get_connection_list(); - for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) { + ob->select(graph->scalar_op_node_get_op(type,p_id)); + ob->connect("item_selected",this,"_scalar_op_changed",varray(p_id)); + gn->add_child(ob); + + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("a",Variant::REAL)); + } else { + float v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("a: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_spin)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("out"))); + gn->add_child(hbc); + if (graph->is_slot_connected(type, p_id, 1)) { + gn->add_child(make_label("b",Variant::REAL)); + } else { + float v = graph->default_get_value(type,p_id,1); + gn->add_child(make_editor(String("b: ")+Variant(v),gn,p_id,1,Variant::REAL,hint_spin)); + } - const ShaderGraph::Connection &c=E->get(); - if( c.src_id==rclick_node && c.src_slot==rclick_slot) { + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color()); - shader_graph.disconnect(c.src_id,c.src_slot,c.dst_id,c.dst_slot); - } - } - update(); - _write_shader_graph(); - validate_graph(); + + } break; // scalar vs scalar op (mul: { } break; add: { } break; div: { } break; etc) + case ShaderGraph::NODE_VEC_OP: { + + gn->set_title("VecOp"); + static const char* op_name[ShaderGraph::VEC_MAX_OP]={ + "Add", + "Sub", + "Mul", + "Div", + "Mod", + "Pow", + "Max", + "Min", + "Cross" + }; + + OptionButton *ob = memnew( OptionButton ); + for(int i=0;i<ShaderGraph::VEC_MAX_OP;i++) { + + ob->add_item(op_name[i],i); + } + + ob->select(graph->vec_op_node_get_op(type,p_id)); + ob->connect("item_selected",this,"_vec_op_changed",varray(p_id)); + gn->add_child(ob); + + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("a",Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("out"))); + gn->add_child(hbc); + if (graph->is_slot_connected(type, p_id, 1)) { + gn->add_child(make_label("b",Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,1); + gn->add_child(make_editor(String("b: ")+v,gn,p_id,1,Variant::VECTOR3)); + } + + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color()); + + + } break; // vec3 vs vec3 op (mul: { } break;ad: { } break;div: { } break;crossprod: { } break;etc) + case ShaderGraph::NODE_VEC_SCALAR_OP: { + + gn->set_title("VecScalarOp"); + static const char* op_name[ShaderGraph::VEC_SCALAR_MAX_OP]={ + "Mul", + "Div", + "Pow", + }; + + OptionButton *ob = memnew( OptionButton ); + for(int i=0;i<ShaderGraph::VEC_SCALAR_MAX_OP;i++) { + + ob->add_item(op_name[i],i); + } + + ob->select(graph->vec_scalar_op_node_get_op(type,p_id)); + ob->connect("item_selected",this,"_vec_scalar_op_changed",varray(p_id)); + gn->add_child(ob); + + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("a",Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("out"))); + gn->add_child(hbc); + + if (graph->is_slot_connected(type, p_id, 1)) { + gn->add_child(make_label("b",Variant::REAL)); + } else { + float v = graph->default_get_value(type,p_id,1); + gn->add_child(make_editor(String("b: ")+Variant(v),gn,p_id,1,Variant::REAL,hint_spin)); + } + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color()); + + + } break; // vec3 vs scalar op (mul: { } break; add: { } break; div: { } break; etc) + case ShaderGraph::NODE_RGB_OP: { + + gn->set_title("RGB Op"); + static const char* op_name[ShaderGraph::RGB_MAX_OP]={ + "Screen", + "Difference", + "Darken", + "Lighten", + "Overlay", + "Dodge", + "Burn", + "SoftLight", + "HardLight" + }; + + OptionButton *ob = memnew( OptionButton ); + for(int i=0;i<ShaderGraph::RGB_MAX_OP;i++) { + + ob->add_item(op_name[i],i); + } + + ob->select(graph->rgb_op_node_get_op(type,p_id)); + ob->connect("item_selected",this,"_rgb_op_changed",varray(p_id)); + gn->add_child(ob); + + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("a",Variant::COLOR)); + } else { + hbc->add_child(make_editor(String("a: "),gn,p_id,0,Variant::COLOR)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("out"))); + gn->add_child(hbc); + if (graph->is_slot_connected(type, p_id, 1)) { + gn->add_child(make_label("b",Variant::COLOR)); + } else { + gn->add_child(make_editor(String("b: "),gn,p_id,1,Variant::COLOR)); + } + + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color()); + + } break; // vec3 vs vec3 rgb op (with scalar amount): { } break; like brighten: { } break; darken: { } break; burn: { } break; dodge: { } break; multiply: { } break; etc. + case ShaderGraph::NODE_XFORM_MULT: { + + gn->set_title("XFMult"); + HBoxContainer *hbc = memnew( HBoxContainer ); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("a",Variant::TRANSFORM)); + } else { + hbc->add_child(make_editor(String("a: edit..."),gn,p_id,0,Variant::TRANSFORM)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("out"))); + gn->add_child(hbc); + if (graph->is_slot_connected(type, p_id, 1)) { + gn->add_child(make_label("b",Variant::TRANSFORM)); + } else { + gn->add_child(make_editor(String("b: edit..."),gn,p_id,1,Variant::TRANSFORM)); + } + + gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]); + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],false,0,Color()); + + + } break; // mat4 x mat4 + case ShaderGraph::NODE_XFORM_VEC_MULT: { + + gn->set_title("XFVecMult"); + + CheckBox *button = memnew (CheckBox("RotOnly")); + button->set_pressed(graph->xform_vec_mult_node_get_no_translation(type,p_id)); + button->connect("toggled",this,"_xform_inv_rev_changed",varray(p_id)); + + gn->add_child(button); + + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("xf",Variant::TRANSFORM)); + } else { + hbc->add_child(make_editor(String("xf: edit..."),gn,p_id,0,Variant::TRANSFORM)); + } + hbc->add_spacer(); + Label *l = memnew(Label("out")); + l->set_align(Label::ALIGN_RIGHT); + hbc->add_child( l); + gn->add_child(hbc); + if (graph->is_slot_connected(type, p_id, 1)) { + gn->add_child(make_label("a",Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,1); + gn->add_child(make_editor(String("a: ")+v,gn,p_id,1,Variant::VECTOR3)); + } + + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color()); + + } break; + case ShaderGraph::NODE_XFORM_VEC_INV_MULT: { + + gn->set_title("XFVecInvMult"); + + + CheckBox *button = memnew( CheckBox("RotOnly")); + button->set_pressed(graph->xform_vec_mult_node_get_no_translation(type,p_id)); + button->connect("toggled",this,"_xform_inv_rev_changed",varray(p_id)); + + gn->add_child(button); + + if (graph->is_slot_connected(type, p_id, 0)) { + gn->add_child(make_label("a",Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,0); + gn->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3)); + } + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 1)) { + hbc->add_child(make_label("xf", Variant::TRANSFORM)); + } else { + hbc->add_child(make_editor(String("xf: edit..."),gn,p_id,1,Variant::TRANSFORM)); + } + hbc->add_spacer(); + Label *l = memnew(Label("out")); + l->set_align(Label::ALIGN_RIGHT); + hbc->add_child( l); + gn->add_child(hbc); + + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color()); + gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + + + } break; // mat4 x vec3 inverse mult (with no-translation option) + case ShaderGraph::NODE_SCALAR_FUNC: { + + gn->set_title("ScalarFunc"); + static const char* func_name[ShaderGraph::SCALAR_MAX_FUNC]={ + "Sin", + "Cos", + "Tan", + "ASin", + "ACos", + "ATan", + "SinH", + "CosH", + "TanH", + "Log", + "Exp", + "Sqrt", + "Abs", + "Sign", + "Floor", + "Round", + "Ceil", + "Frac", + "Satr", + "Neg" + }; + + OptionButton *ob = memnew( OptionButton ); + for(int i=0;i<ShaderGraph::SCALAR_MAX_FUNC;i++) { + + ob->add_item(func_name[i],i); + } + + ob->select(graph->scalar_func_node_get_function(type,p_id)); + ob->connect("item_selected",this,"_scalar_func_changed",varray(p_id)); + gn->add_child(ob); + + HBoxContainer *hbc = memnew( HBoxContainer ); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("in", Variant::REAL)); + } else { + float v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("in: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_spin)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("out"))); + gn->add_child(hbc); + + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + + } break; // scalar function (sin: { } break; cos: { } break; etc) + case ShaderGraph::NODE_VEC_FUNC: { + + + + gn->set_title("VecFunc"); + static const char* func_name[ShaderGraph::VEC_MAX_FUNC]={ + "Normalize", + "Saturate", + "Negate", + "Reciprocal", + "RGB to HSV", + "HSV to RGB", + }; + + OptionButton *ob = memnew( OptionButton ); + for(int i=0;i<ShaderGraph::VEC_MAX_FUNC;i++) { + + ob->add_item(func_name[i],i); + } + + ob->select(graph->vec_func_node_get_function(type,p_id)); + ob->connect("item_selected",this,"_vec_func_changed",varray(p_id)); + gn->add_child(ob); + + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("in", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("in: ")+v,gn,p_id,0,Variant::VECTOR3)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("out"))); + gn->add_child(hbc); + + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + + } break; // vector function (normalize: { } break; negate: { } break; reciprocal: { } break; rgb2hsv: { } break; hsv2rgb: { } break; etc: { } break; etc) + case ShaderGraph::NODE_VEC_LEN: { + gn->set_title("VecLength"); + HBoxContainer *hbc = memnew( HBoxContainer ); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("in", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("in: ")+v,gn,p_id,0,Variant::VECTOR3)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("len"))); + gn->add_child(hbc); + + gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + } break; // vec3 length + case ShaderGraph::NODE_DOT_PROD: { + + gn->set_title("DotProduct"); + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("a", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("dp"))); + gn->add_child(hbc); + if (graph->is_slot_connected(type, p_id, 1)) { + gn->add_child(make_label("b", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,1); + gn->add_child(make_editor(String("b: ")+v,gn,p_id,1,Variant::VECTOR3)); + } + + gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color()); + + } break; // vec3 . vec3 (dot product -> scalar output) + case ShaderGraph::NODE_VEC_TO_SCALAR: { + + gn->set_title("Vec2Scalar"); + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("vec", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("vec: ")+v,gn,p_id,0,Variant::VECTOR3)); + } + hbc->add_spacer(); + Label *l=memnew(Label("x")); + l->set_align(Label::ALIGN_RIGHT); + hbc->add_child( l); + gn->add_child(hbc); + l=memnew(Label("y")); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child( l ); + l=memnew(Label("z")); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child( l); + + gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + gn->set_slot(2,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + + + + } break; // 1 vec3 input: { } break; 3 scalar outputs + case ShaderGraph::NODE_SCALAR_TO_VEC: { + + gn->set_title("Scalar2Vec"); + HBoxContainer *hbc = memnew( HBoxContainer ); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("x", Variant::REAL)); + } else { + float v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("x: ")+Variant(v),gn,p_id,0,Variant::REAL)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("vec"))); + gn->add_child(hbc); + if (graph->is_slot_connected(type, p_id, 1)) { + gn->add_child(make_label("y", Variant::REAL)); + } else { + float v = graph->default_get_value(type,p_id,1); + gn->add_child(make_editor(String("y: ")+Variant(v),gn,p_id,1,Variant::REAL)); + } + if (graph->is_slot_connected(type, p_id, 2)) { + gn->add_child(make_label("in", Variant::REAL)); + } else { + float v = graph->default_get_value(type,p_id,2); + gn->add_child(make_editor(String("in: ")+Variant(v),gn,p_id,2,Variant::REAL)); + } + + gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color()); + gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color()); + + } break; // 3 scalar input: { } break; 1 vec3 output + case ShaderGraph::NODE_VEC_TO_XFORM: { + + gn->set_title("Vec2XForm"); + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("x", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("x: ")+v,gn,p_id,0,Variant::VECTOR3)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("xf"))); + gn->add_child(hbc); + if (graph->is_slot_connected(type, p_id, 1)) { + gn->add_child(make_label("y", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,1); + gn->add_child(make_editor(String("y: ")+v,gn,p_id,1,Variant::VECTOR3)); + } + if (graph->is_slot_connected(type, p_id, 2)) { + gn->add_child(make_label("z", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,2); + gn->add_child(make_editor(String("z: ")+v,gn,p_id,2,Variant::VECTOR3)); + } + if (graph->is_slot_connected(type, p_id, 3)) { + gn->add_child(make_label("ofs", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,3); + gn->add_child(make_editor(String("ofs: ")+v,gn,p_id,3,Variant::VECTOR3)); + } + + gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]); + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color()); + gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color()); + gn->set_slot(3,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color()); + + } break; // 3 vec input: { } break; 1 xform output + case ShaderGraph::NODE_XFORM_TO_VEC: { + + gn->set_title("XForm2Vec"); + + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("fx", Variant::TRANSFORM)); + } else { + hbc->add_child(make_editor(String("fx: edit..."),gn,p_id,0,Variant::TRANSFORM)); + } + hbc->add_spacer(); + Label *l=memnew(Label("x")); + l->set_align(Label::ALIGN_RIGHT); + hbc->add_child( l); + gn->add_child(hbc); + l=memnew(Label("y")); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child( l ); + l=memnew(Label("z")); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child( l); + l=memnew(Label("ofs")); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child( l); + + gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(2,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(3,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + + } break; // 3 vec input: { } break; 1 xform output + case ShaderGraph::NODE_SCALAR_INTERP: { + + gn->set_title("ScalarInterp"); + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("a", Variant::REAL)); + } else { + float v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("a: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_spin)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("interp"))); + gn->add_child(hbc); + if (graph->is_slot_connected(type, p_id, 1)) { + gn->add_child(make_label("b", Variant::REAL)); + } else { + float v = graph->default_get_value(type,p_id,1); + gn->add_child(make_editor(String("b: ")+Variant(v),gn,p_id,1,Variant::REAL,hint_spin)); + } + if (graph->is_slot_connected(type, p_id, 2)) { + gn->add_child(make_label("c", Variant::REAL)); + } else { + float v = graph->default_get_value(type,p_id,2); + gn->add_child(make_editor(String("c: ")+Variant(v),gn,p_id,2,Variant::REAL,hint_slider)); + } + + gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color()); + gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color()); + + + } break; // scalar interpolation (with optional curve) + case ShaderGraph::NODE_VEC_INTERP: { + + gn->set_title("VecInterp"); + HBoxContainer *hbc = memnew( HBoxContainer ); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("a", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3)); + } + hbc->add_spacer(); + hbc->add_child( memnew(Label("interp"))); + gn->add_child(hbc); + if (graph->is_slot_connected(type, p_id, 1)) { + gn->add_child(make_label("b", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,1); + gn->add_child(make_editor(String("b: ")+v,gn,p_id,1,Variant::VECTOR3)); + } + if (graph->is_slot_connected(type, p_id, 2)) { + gn->add_child(make_label("c", Variant::REAL)); + } else { + float v = graph->default_get_value(type,p_id,2); + gn->add_child(make_editor(String("c: ")+Variant(v),gn,p_id,2,Variant::REAL,hint_slider)); + } + + gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color()); + gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color()); + + } break; // vec3 interpolation (with optional curve) + case ShaderGraph::NODE_COLOR_RAMP: { + + gn->set_title("ColorRamp"); + GraphColorRampEdit * ramp = memnew( GraphColorRampEdit ); + + DVector<real_t> offsets = graph->color_ramp_node_get_offsets(type,p_id); + DVector<Color> colors = graph->color_ramp_node_get_colors(type,p_id); + + int oc = offsets.size(); + + if (oc) { + DVector<real_t>::Read rofs = offsets.read(); + DVector<Color>::Read rcol = colors.read(); + + Vector<float> ofsv; + Vector<Color> colorv; + for(int i=0;i<oc;i++) { + ofsv.push_back(rofs[i]); + colorv.push_back(rcol[i]); } - } break; - case NODE_ERASE: { - - order.erase(rclick_node); - shader_graph.node_remove(rclick_node); - update(); - _write_shader_graph(); - validate_graph(); - } break; - case GRAPH_CLEAR: { - - order.clear(); - shader_graph.clear(); - last_id=1; - last_x=20; - last_y=20; - update(); - _write_shader_graph(); - validate_graph(); - - } break; - } -} + ramp->set_ramp(ofsv,colorv); -void ShaderEditor::_node_add(VisualServer::ShaderNodeType p_type) { + } - shader_graph.node_add(p_type,last_id ); - shader_graph.node_set_pos(last_id ,last_x,last_y); - String test_param; + ramp->connect("ramp_changed",this,"_color_ramp_changed",varray(p_id,ramp)); + ramp->set_custom_minimum_size(Size2(128,1)); + gn->add_child(ramp); - switch(p_type) { - case VS::NODE_PARAMETER: { - test_param="param"; - } break; - case VS::NODE_VEC_PARAMETER: { + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("c", Variant::REAL)); + } else { + float v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("c: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_slider)); + } + hbc->add_spacer(); + Label *l=memnew(Label("rgb")); + l->set_align(Label::ALIGN_RIGHT); + hbc->add_child( l); + gn->add_child(hbc); + l=memnew(Label("alpha")); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child( l); - test_param="vec"; - } break; - case VS::NODE_COLOR_PARAMETER: { - test_param="color"; - } break; - case VS::NODE_TEXTURE_PARAMETER: { + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(2,false,ShaderGraph::SLOT_MAX,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); - test_param="tex"; - } break; - case VS::NODE_TEXTURE_2D_PARAMETER: { - test_param="tex2D"; - } break; - case VS::NODE_TEXTURE_CUBE_PARAMETER: { + } break; // scalar interpolation (with optional curve) + case ShaderGraph::NODE_CURVE_MAP: { - test_param="cubemap"; - } break; - case VS::NODE_TRANSFORM_PARAMETER: { - test_param="xform"; - } break; - case VS::NODE_LABEL: { + gn->set_title("CurveMap"); + GraphCurveMapEdit * map = memnew( GraphCurveMapEdit ); - test_param="label"; - } break; - } + DVector<Vector2> points = graph->curve_map_node_get_points(type,p_id); - if(test_param!="") { + int oc = points.size(); - int iter=0; - List<int> l; + if (oc) { + DVector<Vector2>::Read rofs = points.read(); - shader_graph.get_node_list(&l); - bool found; - String test; - do { - iter++; - test=test_param; - if (iter>1) - test+="_"+itos(iter); - found=false; - for(List<int>::Element *E=l.front();E;E=E->next()) { + Vector<Vector2> ofsv; + for(int i=0;i<oc;i++) { + ofsv.push_back(rofs[i]); + } + map->set_points(ofsv); - String param = shader_graph.node_get_param( E->get() ); - if (param==test) { - found=true; - break; - } + } + map->connect("curve_changed",this,"_curve_changed",varray(p_id,map)); + + //map->connect("map_changed",this,"_curve_map_changed",varray(p_id,map)); + map->set_custom_minimum_size(Size2(128,64)); + gn->add_child(map); + + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("c", Variant::REAL)); + } else { + float v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("c: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_slider)); + } + hbc->add_spacer(); + Label *l=memnew(Label("cmap")); + l->set_align(Label::ALIGN_RIGHT); + hbc->add_child( l); + gn->add_child(hbc); + + + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + + } break; // scalar interpolation (with optional curve) + + case ShaderGraph::NODE_SCALAR_INPUT: { + + gn->set_title("ScalarUniform"); + LineEdit *le = memnew( LineEdit ); + gn->add_child(le); + le->set_text(graph->input_node_get_name(type,p_id)); + le->connect("text_entered",this,"_input_name_changed",varray(p_id,le)); + SpinBox *sb = memnew( SpinBox ); + sb->set_min(-100000); + sb->set_max(100000); + sb->set_step(0.001); + sb->set_val(graph->scalar_input_node_get_value(type,p_id)); + sb->connect("value_changed",this,"_scalar_input_changed",varray(p_id)); + gn->add_child(sb); + gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + } break; // scalar uniform (assignable in material) + case ShaderGraph::NODE_VEC_INPUT: { + + gn->set_title("VectorUniform"); + LineEdit *le = memnew( LineEdit ); + gn->add_child(le); + le->set_text(graph->input_node_get_name(type,p_id)); + le->connect("text_entered",this,"_input_name_changed",varray(p_id,le)); + Array v3p(true); + for(int i=0;i<3;i++) { + HBoxContainer *hbc = memnew( HBoxContainer ); + Label *l = memnew( Label ); + l->set_text(String::chr('X'+i)); + hbc->add_child(l); + SpinBox *sb = memnew( SpinBox ); + sb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + sb->set_min(-100000); + sb->set_max(100000); + sb->set_step(0.001); + sb->set_val(graph->vec_input_node_get_value(type,p_id)[i]); + sb->connect("value_changed",this,"_vec_input_changed",varray(p_id,v3p)); + v3p.push_back(sb); + hbc->add_child(sb); + gn->add_child(hbc); + } + gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + + } break; // vec3 uniform (assignable in material) + case ShaderGraph::NODE_RGB_INPUT: { + + gn->set_title("ColorUniform"); + LineEdit *le = memnew( LineEdit ); + gn->add_child(le); + le->set_text(graph->input_node_get_name(type,p_id)); + le->connect("text_entered",this,"_input_name_changed",varray(p_id,le)); + ColorPickerButton *cpb = memnew( ColorPickerButton ); + cpb->set_color(graph->rgb_input_node_get_value(type,p_id)); + cpb->connect("color_changed",this,"_rgb_input_changed",varray(p_id)); + gn->add_child(cpb); + Label *l = memnew( Label ); + l->set_text("RGB"); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child(l); + l = memnew( Label ); + l->set_text("Alpha"); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child(l); + + gn->set_slot(2,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(3,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + + } break; // color uniform (assignable in material) + case ShaderGraph::NODE_XFORM_INPUT: { + gn->set_title("XFUniform"); + LineEdit *le = memnew( LineEdit ); + gn->add_child(le); + le->set_text(graph->input_node_get_name(type,p_id)); + le->connect("text_entered",this,"_input_name_changed",varray(p_id,le)); + ToolButton *edit = memnew( ToolButton ); + edit->set_text("edit.."); + edit->connect("pressed",this,"_xform_input_changed",varray(p_id,edit)); + gn->add_child(edit); + gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]); + + } break; // mat4 uniform (assignable in material) + case ShaderGraph::NODE_TEXTURE_INPUT: { + + gn->set_title("TexUniform"); + LineEdit *le = memnew( LineEdit ); + gn->add_child(le); + le->set_text(graph->input_node_get_name(type,p_id)); + le->connect("text_entered",this,"_input_name_changed",varray(p_id,le)); + TextureFrame *tex = memnew( TextureFrame ); + tex->set_expand(true); + tex->set_custom_minimum_size(Size2(80,80)); + gn->add_child(tex); + tex->set_texture(graph->texture_input_node_get_value(type,p_id)); + ToolButton *edit = memnew( ToolButton ); + edit->set_text("edit.."); + edit->connect("pressed",this,"_tex_edited",varray(p_id,edit)); + gn->add_child(edit); + + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("UV", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("UV: ")+v,gn,p_id,0,Variant::VECTOR3)); + } + hbc->add_spacer(); + Label *l=memnew(Label("RGB")); + l->set_align(Label::ALIGN_RIGHT); + hbc->add_child(l); + gn->add_child(hbc); + l = memnew( Label ); + l->set_text("Alpha"); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child(l); + + gn->set_slot(3,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(4,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + } break; // texture input (assignable in material) + case ShaderGraph::NODE_CUBEMAP_INPUT: { + + gn->set_title("TexUniform"); + LineEdit *le = memnew( LineEdit ); + gn->add_child(le); + le->set_text(graph->input_node_get_name(type,p_id)); + le->connect("text_entered",this,"_input_name_changed",varray(p_id,le)); + + ToolButton *edit = memnew( ToolButton ); + edit->set_text("edit.."); + edit->connect("pressed",this,"_cube_edited",varray(p_id,edit)); + gn->add_child(edit); + + + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("UV", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("UV: ")+v,gn,p_id,0,Variant::VECTOR3)); + } + hbc->add_spacer(); + Label *l=memnew(Label("RGB")); + l->set_align(Label::ALIGN_RIGHT); + hbc->add_child(l); + gn->add_child(hbc); + l = memnew( Label ); + l->set_text("Alpha"); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child(l); + + gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(3,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + } break; // cubemap input (assignable in material) + case ShaderGraph::NODE_DEFAULT_TEXTURE: { + + gn->set_title("CanvasItemTex"); + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + if (graph->is_slot_connected(type, p_id, 0)) { + hbc->add_child(make_label("UV", Variant::VECTOR3)); + } else { + Vector3 v = graph->default_get_value(type,p_id,0); + hbc->add_child(make_editor(String("UV: ")+v,gn,p_id,0,Variant::VECTOR3)); + } + hbc->add_spacer(); + Label *l=memnew(Label("RGB")); + l->set_align(Label::ALIGN_RIGHT); + hbc->add_child(l); + gn->add_child(hbc); + l = memnew( Label ); + l->set_text("Alpha"); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child(l); + + gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + + } break; // screen texture sampler (takes UV) (only usable in fragment case Shader) + + case ShaderGraph::NODE_OUTPUT: { + gn->set_title("Output"); + gn->set_show_close_button(false); + + List<ShaderGraph::SlotInfo> si; + ShaderGraph::get_input_output_node_slot_info(graph->get_mode(),type,&si); + + Array colors; + colors.push_back("Color"); + colors.push_back("LightColor"); + colors.push_back("Light"); + colors.push_back("Diffuse"); + colors.push_back("Specular"); + colors.push_back("Emmision"); + Array reals; + reals.push_back("Alpha"); + reals.push_back("DiffuseAlpha"); + reals.push_back("NormalMapDepth"); + reals.push_back("SpecExp"); + reals.push_back("Glow"); + reals.push_back("ShadeParam"); + reals.push_back("SpecularExp"); + reals.push_back("LightAlpha"); + reals.push_back("PointSize"); + reals.push_back("Discard"); + + int idx=0; + for (List<ShaderGraph::SlotInfo>::Element *E=si.front();E;E=E->next()) { + ShaderGraph::SlotInfo& s=E->get(); + if (s.dir==ShaderGraph::SLOT_OUT) { + Variant::Type v; + if (colors.find(s.name)>=0) + v=Variant::COLOR; + else if (reals.find(s.name)>=0) + v=Variant::REAL; + else + v=Variant::VECTOR3; + gn->add_child(make_label(s.name, v)); + gn->set_slot(idx,true,s.type,typecol[s.type],false,0,Color()); + idx++; } + } - } while (found); + } break; // output (case Shader type dependent) + case ShaderGraph::NODE_COMMENT: { + gn->set_title("Comment"); + TextEdit *te = memnew(TextEdit); + te->set_custom_minimum_size(Size2(100,100)); + gn->add_child(te); + te->set_text(graph->comment_node_get_text(type,p_id)); + te->connect("text_changed",this,"_comment_edited",varray(p_id,te)); + + } break; // comment - shader_graph.node_set_param(last_id,test); } - order.push_back(last_id); - last_x+=10; - last_y+=10; - last_id++; - last_x=last_x % (int)get_size().width; - last_y=last_y % (int)get_size().height; - update(); - add_popup->hide();; - _write_shader_graph(); -} + gn->connect("dragged",this,"_node_moved",varray(p_id)); + gn->connect("close_request",this,"_node_removed",varray(p_id),CONNECT_DEFERRED); + graph_edit->add_child(gn); + node_map[p_id]=gn; + gn->set_offset(graph->node_get_pos(type,p_id)); -void ShaderEditor::_node_add_callback() { - TreeItem * item = add_types->get_selected(); - ERR_FAIL_COND(!item); - _node_add((VisualServer::ShaderNodeType)(int)item->get_metadata(0)); - add_popup->hide() ; } -ShaderEditor::ShaderEditor() { +void ShaderGraphView::_update_graph() { - set_focus_mode(FOCUS_ALL); - Panel* menu_panel = memnew( Panel ); - menu_panel->set_anchor( MARGIN_RIGHT, Control::ANCHOR_END ); - menu_panel->set_end( Point2(0,22) ); + if (block_update) + return; + + for (Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) { - add_child( menu_panel ); + memdelete(E->get()); + } - PopupMenu *p; - List<PropertyInfo> defaults; + node_map.clear(); - MenuButton* node_menu = memnew( MenuButton ); - node_menu->set_text("Graph"); - node_menu->set_pos( Point2( 5,0) ); - menu_panel->add_child( node_menu ); + if (!graph.is_valid()) + return; - p=node_menu->get_popup(); - p->add_item("Add Node",GRAPH_ADD_NODE); - p->add_separator(); - p->add_item("Clear",GRAPH_CLEAR); - p->connect("item_pressed", this,"_node_menu_item"); - MenuButton* vertex_menu = memnew( MenuButton ); - vertex_menu->set_text("Vertex"); - vertex_menu->set_pos( Point2( 49,0) ); - menu_panel->add_child( vertex_menu ); + List<int> nl; + graph->get_node_list(type,&nl); - p=vertex_menu->get_popup(); - defaults.clear(); - VisualServer::shader_get_default_input_nodes(VisualServer::SHADER_VERTEX,&defaults); + for(List<int>::Element *E=nl.front();E;E=E->next()) { - int id=0; - for(int i=0;i<defaults.size();i++) { + _create_node(E->get()); + } + graph_edit->clear_connections(); - p->add_item("In: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++); + List<ShaderGraph::Connection> connections; + graph->get_node_connections(type,&connections); + for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) { + + ERR_CONTINUE(!node_map.has(E->get().src_id) || !node_map.has(E->get().dst_id)); + graph_edit->connect_node(node_map[E->get().src_id]->get_name(),E->get().src_slot,node_map[E->get().dst_id]->get_name(),E->get().dst_slot); } - p->add_separator(); - id++; - defaults.clear(); - VisualServer::shader_get_default_output_nodes(VisualServer::SHADER_VERTEX,&defaults); +} - for(int i=0;i<defaults.size();i++) { +void ShaderGraphView::_sg_updated() { - p->add_item("Out: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++); + if (!graph.is_valid()) + return; + switch(graph->get_graph_error(type)) { + case ShaderGraph::GRAPH_OK: status->set_text(""); break; + case ShaderGraph::GRAPH_ERROR_CYCLIC: status->set_text("Error: Cyclic Connection Link"); break; + case ShaderGraph::GRAPH_ERROR_MISSING_CONNECTIONS: status->set_text("Error: Missing Input Connections"); break; } +} - vertex_popup=p; - vertex_popup->connect("item_pressed", this,"_vertex_item"); - MenuButton* fragment_menu = memnew( MenuButton ); - fragment_menu->set_text("Fragment"); - fragment_menu->set_pos( Point2( 95 ,0) ); - menu_panel->add_child( fragment_menu ); +void ShaderGraphView::set_graph(Ref<ShaderGraph> p_graph){ - p=fragment_menu->get_popup(); - defaults.clear(); - VisualServer::shader_get_default_input_nodes(VisualServer::SHADER_FRAGMENT,&defaults); - id=0; - for(int i=0;i<defaults.size();i++) { - p->add_item("In: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++); + if (graph.is_valid()) { + graph->disconnect("updated",this,"_sg_updated"); + } + graph=p_graph; + if (graph.is_valid()) { + graph->connect("updated",this,"_sg_updated"); } - p->add_separator(); - id++; - defaults.clear(); - VisualServer::shader_get_default_output_nodes(VisualServer::SHADER_FRAGMENT,&defaults); + _update_graph(); + _sg_updated(); + +} - for(int i=0;i<defaults.size();i++) { +void ShaderGraphView::_notification(int p_what) { - p->add_item("Out: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++); - } + if (p_what==NOTIFICATION_ENTER_TREE) { - fragment_popup=p; - fragment_popup->connect("item_pressed", this,"_fragment_item"); + ped_popup->connect("variant_changed",this,"_variant_edited"); + } +} - MenuButton* post_menu = memnew( MenuButton ); - post_menu->set_text("Post"); - post_menu->set_pos( Point2( 161,0) ); - menu_panel->add_child( post_menu ); +void ShaderGraphView::add_node(int p_type, const Vector2 &location) { - p=post_menu->get_popup(); - defaults.clear(); - VisualServer::shader_get_default_input_nodes(VisualServer::SHADER_POST_PROCESS,&defaults); - id=0; - for(int i=0;i<defaults.size();i++) { + if (p_type==ShaderGraph::NODE_INPUT && graph->node_count(type, p_type)>0) + return; - p->add_item("In: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++); + List<int> existing; + graph->get_node_list(type,&existing); + existing.sort(); + int newid=1; + for(List<int>::Element *E=existing.front();E;E=E->next()) { + if (!E->next() || (E->get()+1!=E->next()->get())){ + newid=E->get()+1; + break; + } } - p->add_separator(); - id++; - defaults.clear(); - VisualServer::shader_get_default_output_nodes(VisualServer::SHADER_POST_PROCESS,&defaults); + Vector2 init_ofs = location; + while(true) { + bool valid=true; + for(List<int>::Element *E=existing.front();E;E=E->next()) { + Vector2 pos = graph->node_get_pos(type,E->get()); + if (init_ofs==pos) { + init_ofs+=Vector2(20,20); + valid=false; + break; - for(int i=0;i<defaults.size();i++) { + } + } - p->add_item("Out: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++); + if (valid) + break; } + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("Add Shader Graph Node"); + ur->add_do_method(graph.ptr(),"node_add",type,p_type,newid); + ur->add_do_method(graph.ptr(),"node_set_pos",type,newid,init_ofs); + ur->add_undo_method(graph.ptr(),"node_remove",type,newid); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + ur->commit_action(); - post_popup=p; - post_popup->connect("item_pressed", this,"_post_item"); +} +void ShaderGraphView::_bind_methods() { + + ObjectTypeDB::bind_method("_update_graph",&ShaderGraphView::_update_graph); + ObjectTypeDB::bind_method("_begin_node_move", &ShaderGraphView::_begin_node_move); + ObjectTypeDB::bind_method("_node_moved",&ShaderGraphView::_node_moved); + ObjectTypeDB::bind_method("_end_node_move", &ShaderGraphView::_end_node_move); + ObjectTypeDB::bind_method("_move_node",&ShaderGraphView::_move_node); + ObjectTypeDB::bind_method("_node_removed",&ShaderGraphView::_node_removed); + ObjectTypeDB::bind_method("_connection_request",&ShaderGraphView::_connection_request); + ObjectTypeDB::bind_method("_disconnection_request",&ShaderGraphView::_disconnection_request); + ObjectTypeDB::bind_method("_duplicate_nodes_request", &ShaderGraphView::_duplicate_nodes_request); + ObjectTypeDB::bind_method("_duplicate_nodes", &ShaderGraphView::_duplicate_nodes); + ObjectTypeDB::bind_method("_delete_nodes_request", &ShaderGraphView::_delete_nodes_request); + + ObjectTypeDB::bind_method("_default_changed",&ShaderGraphView::_default_changed); + ObjectTypeDB::bind_method("_scalar_const_changed",&ShaderGraphView::_scalar_const_changed); + ObjectTypeDB::bind_method("_vec_const_changed",&ShaderGraphView::_vec_const_changed); + ObjectTypeDB::bind_method("_rgb_const_changed",&ShaderGraphView::_rgb_const_changed); + ObjectTypeDB::bind_method("_xform_const_changed",&ShaderGraphView::_xform_const_changed); + ObjectTypeDB::bind_method("_scalar_op_changed",&ShaderGraphView::_scalar_op_changed); + ObjectTypeDB::bind_method("_vec_op_changed",&ShaderGraphView::_vec_op_changed); + ObjectTypeDB::bind_method("_vec_scalar_op_changed",&ShaderGraphView::_vec_scalar_op_changed); + ObjectTypeDB::bind_method("_rgb_op_changed",&ShaderGraphView::_rgb_op_changed); + ObjectTypeDB::bind_method("_xform_inv_rev_changed",&ShaderGraphView::_xform_inv_rev_changed); + ObjectTypeDB::bind_method("_scalar_func_changed",&ShaderGraphView::_scalar_func_changed); + ObjectTypeDB::bind_method("_vec_func_changed",&ShaderGraphView::_vec_func_changed); + ObjectTypeDB::bind_method("_scalar_input_changed",&ShaderGraphView::_scalar_input_changed); + ObjectTypeDB::bind_method("_vec_input_changed",&ShaderGraphView::_vec_input_changed); + ObjectTypeDB::bind_method("_xform_input_changed",&ShaderGraphView::_xform_input_changed); + ObjectTypeDB::bind_method("_rgb_input_changed",&ShaderGraphView::_rgb_input_changed); + ObjectTypeDB::bind_method("_tex_input_change",&ShaderGraphView::_tex_input_change); + ObjectTypeDB::bind_method("_cube_input_change",&ShaderGraphView::_cube_input_change); + ObjectTypeDB::bind_method("_input_name_changed",&ShaderGraphView::_input_name_changed); + ObjectTypeDB::bind_method("_tex_edited",&ShaderGraphView::_tex_edited); + ObjectTypeDB::bind_method("_variant_edited",&ShaderGraphView::_variant_edited); + ObjectTypeDB::bind_method("_cube_edited",&ShaderGraphView::_cube_edited); + ObjectTypeDB::bind_method("_comment_edited",&ShaderGraphView::_comment_edited); + ObjectTypeDB::bind_method("_color_ramp_changed",&ShaderGraphView::_color_ramp_changed); + ObjectTypeDB::bind_method("_curve_changed",&ShaderGraphView::_curve_changed); + + ObjectTypeDB::bind_method("_sg_updated",&ShaderGraphView::_sg_updated); +} - /* add popup */ +ShaderGraphView::ShaderGraphView(ShaderGraph::ShaderType p_type) { + + type=p_type; + graph_edit = memnew( GraphEdit ); + block_update=false; + ped_popup = memnew( CustomPropertyEditor ); + graph_edit->add_child(ped_popup); + status = memnew( Label ); + graph_edit->get_top_layer()->add_child(status); + graph_edit->connect("_begin_node_move", this, "_begin_node_move"); + graph_edit->connect("_end_node_move", this, "_end_node_move"); + status->set_pos(Vector2(5,5)); + status->add_color_override("font_color_shadow",Color(0,0,0)); + status->add_color_override("font_color",Color(1,0.4,0.3)); + status->add_constant_override("shadow_as_outline",1); + status->add_constant_override("shadow_offset_x",2); + status->add_constant_override("shadow_offset_y",2); + status->set_text(""); +} + + +//////////////edit////////////// +void ShaderGraphEditor::edit(Ref<ShaderGraph> p_shader) { + + for(int i=0;i<ShaderGraph::SHADER_TYPE_MAX;i++) { + graph_edits[i]->set_graph(p_shader); + } +} - add_popup = memnew( Popup ); - add_child(add_popup); - add_popup->set_as_toplevel(true); - Panel *add_panel = memnew( Panel ); - add_popup->add_child(add_panel); - add_panel->set_area_as_parent_rect(); +void ShaderGraphEditor::_add_node(int p_type) { - Label *add_label = memnew (Label ); - add_label->set_pos(Point2(5,5)); - add_label->set_text("Available Nodes:"); - add_panel->add_child(add_label); + ShaderGraph::ShaderType shader_type=ShaderGraph::ShaderType(tabs->get_current_tab()); + graph_edits[shader_type]->add_node(p_type, next_location); +} - add_types = memnew( Tree ); - add_types->set_anchor( MARGIN_RIGHT, ANCHOR_END ); - add_types->set_anchor( MARGIN_BOTTOM, ANCHOR_END ); - add_types->set_begin( Point2( 20,25 ) ); - add_types->set_end( Point2( 10, 30 ) ); - add_types->set_hide_root(true); - add_types->set_columns(4); - add_types->set_select_mode(Tree::SELECT_ROW); +void ShaderGraphEditor::_popup_requested(const Vector2 &p_position) +{ + next_location = get_local_mouse_pos(); + popup->set_global_pos(p_position); + popup->set_size( Size2( 200, 0) ); + popup->popup(); + popup->call_deferred("grab_click_focus"); + popup->set_invalidate_click_until_motion(); +} +void ShaderGraphEditor::_notification(int p_what) { + if (p_what==NOTIFICATION_ENTER_TREE) { - TreeItem *add_types_root = add_types->create_item(NULL); - TreeItem *info_item = add_types->create_item(add_types_root); + for(int i=0;i<ShaderGraph::NODE_TYPE_MAX;i++) { - for(int i=0;i<VisualServer::NODE_TYPE_MAX;i++) { + if (i==ShaderGraph::NODE_OUTPUT) + continue; + if (!_2d && i==ShaderGraph::NODE_DEFAULT_TEXTURE) + continue; - TreeItem *item = add_types->create_item(add_types_root); - PropertyInfo prop = VisualServer::shader_node_get_type_info((VisualServer::ShaderNodeType)i); - item->set_text(0,prop.name); - item->set_text(1,itos(VisualServer::shader_get_input_count((VisualServer::ShaderNodeType)i))); - item->set_text(2,itos(VisualServer::shader_get_output_count((VisualServer::ShaderNodeType)i))); - String hint = (prop.type==Variant::_RID)?prop.hint_string:Variant::get_type_name(prop.type); - item->set_text(3,hint); - item->set_metadata(0,i); - } - info_item->set_text(0,"::NODE::"); - info_item->set_custom_color(0,Color(0.6,0.1,0.1)); - info_item->set_text(1,"::INPUTS::"); - info_item->set_custom_color(1,Color(0.6,0.1,0.1)); - info_item->set_text(2,"::OUTPUTS::"); - info_item->set_custom_color(2,Color(0.6,0.1,0.1)); - info_item->set_text(3,"::PARAM::"); - info_item->set_custom_color(3,Color(0.6,0.1,0.1)); - info_item->set_selectable(0,false); - info_item->set_selectable(1,false); - info_item->set_selectable(2,false); - info_item->set_selectable(3,false); + String nn = node_names[i]; + String ic = nn.get_slice(":",0); + String v = nn.get_slice(":",1); + bool addsep=false; + if (nn.ends_with(":")) { + addsep=true; + } + popup->add_icon_item(get_icon(ic,"EditorIcons"),v,i); + if (addsep) + popup->add_separator(); + } + popup->connect("item_pressed",this,"_add_node"); - add_panel->add_child(add_types); - add_confirm = memnew( Button ); - add_confirm->set_anchor( MARGIN_LEFT, ANCHOR_END ); - add_confirm->set_anchor( MARGIN_TOP, ANCHOR_END ); - add_confirm->set_anchor( MARGIN_RIGHT, ANCHOR_END ); - add_confirm->set_anchor( MARGIN_BOTTOM, ANCHOR_END ); - add_confirm->set_begin( Point2( 75, 29 ) ); - add_confirm->set_end( Point2( 10, 15 ) ); - add_confirm->set_text("Add"); - add_panel->add_child(add_confirm); - add_confirm->connect("pressed", this,"_node_add_callback"); + } +} - last_id=1; - last_x=20; - last_y=20; +void ShaderGraphEditor::_bind_methods() { - property_editor = memnew( CustomPropertyEditor ); - add_child(property_editor); - property_editor->connect("variant_changed", this,"_node_param_changed"); + ObjectTypeDB::bind_method("_add_node",&ShaderGraphEditor::_add_node); + ObjectTypeDB::bind_method("_popup_requested",&ShaderGraphEditor::_popup_requested); +} - h_scroll = memnew( HScrollBar ); - v_scroll = memnew( VScrollBar ); - add_child(h_scroll); - add_child(v_scroll); +const char* ShaderGraphEditor::node_names[ShaderGraph::NODE_TYPE_MAX]={ + "GraphInput:Input", // all inputs (shader type dependent) + "GraphScalar:Scalar Constant", //scalar constant + "GraphVector:Vector Constant", //vec3 constant + "GraphRgb:RGB Constant", //rgb constant (shows a color picker instead) + "GraphXform:XForm Constant", // 4x4 matrix constant + "GraphTime:Time:", // time in seconds + "GraphTexscreen:Screen Sample", // screen texture sampler (takes uv) (only usable in fragment shader) + "GraphScalarOp:Scalar Operator", // scalar vs scalar op (mul", add", div", etc) + "GraphVecOp:Vector Operator", // vec3 vs vec3 op (mul",ad",div",crossprod",etc) + "GraphVecScalarOp:Scalar+Vector Operator", // vec3 vs scalar op (mul", add", div", etc) + "GraphRgbOp:RGB Operator:", // vec3 vs vec3 rgb op (with scalar amount)", like brighten", darken", burn", dodge", multiply", etc. + "GraphXformMult:XForm Multiply", // mat4 x mat4 + "GraphXformVecMult:XForm+Vector Multiply", // mat4 x vec3 mult (with no-translation option) + "GraphXformVecImult:Form+Vector InvMultiply:", // mat4 x vec3 inverse mult (with no-translation option) + "GraphXformScalarFunc:Scalar Function", // scalar function (sin", cos", etc) + "GraphXformVecFunc:Vector Function", // vector function (normalize", negate", reciprocal", rgb2hsv", hsv2rgb", etc", etc) + "GraphVecLength:Vector Length", // vec3 length + "GraphVecDp:Dot Product:", // vec3 . vec3 (dot product -> scalar output) + "GraphVecToScalars:Vector -> Scalars", // 1 vec3 input", 3 scalar outputs + "GraphScalarsToVec:Scalars -> Vector", // 3 scalar input", 1 vec3 output + "GraphXformToVecs:XForm -> Vectors", // 3 vec input", 1 xform output + "GraphVecsToXform:Vectors -> XForm:", // 3 vec input", 1 xform output + "GraphScalarInterp:Scalar Interpolate", // scalar interpolation (with optional curve) + "GraphVecInterp:Vector Interpolate:", // vec3 interpolation (with optional curve) + "GraphColorRamp:Color Ramp", // vec3 interpolation (with optional curve) + "GraphCurveMap:Curve Remap:", // vec3 interpolation (with optional curve) + "GraphScalarUniform:Scalar Uniform", // scalar uniform (assignable in material) + "GraphVectorUniform:Vector Uniform", // vec3 uniform (assignable in material) + "GraphRgbUniform:RGB Uniform", // color uniform (assignable in material) + "GraphXformUniform:XForm Uniform", // mat4 uniform (assignable in material) + "GraphTextureUniform:Texture Uniform", // texture input (assignable in material) + "GraphCubeUniform:CubeMap Uniform:", // cubemap input (assignable in material) + "GraphDefaultTexture:CanvasItem Texture:", // cubemap input (assignable in material) + "Output", // output (shader type dependent) + "GraphComment:Comment", // comment - h_scroll->connect("value_changed", this,"_scroll_moved"); - v_scroll->connect("value_changed", this,"_scroll_moved"); - node_popup= memnew(PopupMenu ); - add_child(node_popup); - node_popup->set_as_toplevel(true); +}; +ShaderGraphEditor::ShaderGraphEditor(bool p_2d) { + _2d=p_2d; + + popup = memnew( PopupMenu ); + add_child(popup); + + + tabs = memnew(TabContainer); + tabs->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(tabs); + const char* sname[ShaderGraph::SHADER_TYPE_MAX]={ + "Vertex", + "Fragment", + "Light" + }; + for(int i=0;i<ShaderGraph::SHADER_TYPE_MAX;i++) { + + graph_edits[i]= memnew( ShaderGraphView(ShaderGraph::ShaderType(i)) ); + add_child(graph_edits[i]); + graph_edits[i]->get_graph_edit()->set_name(sname[i]); + tabs->add_child(graph_edits[i]->get_graph_edit()); + graph_edits[i]->get_graph_edit()->connect("connection_request",graph_edits[i],"_connection_request"); + graph_edits[i]->get_graph_edit()->connect("disconnection_request",graph_edits[i],"_disconnection_request"); + graph_edits[i]->get_graph_edit()->connect("duplicate_nodes_request", graph_edits[i], "_duplicate_nodes_request"); + graph_edits[i]->get_graph_edit()->connect("popup_request",this,"_popup_requested"); + graph_edits[i]->get_graph_edit()->connect("delete_nodes_request",graph_edits[i],"_delete_nodes_request"); + graph_edits[i]->get_graph_edit()->set_right_disconnects(true); + } - node_popup->connect("item_pressed", this,"_node_menu_item"); + tabs->set_current_tab(1); + set_custom_minimum_size(Size2(100,300)); } -void ShaderEditorPlugin::edit(Object *p_object) { +void ShaderGraphEditorPlugin::edit(Object *p_object) { - shader_editor->edit(p_object->cast_to<Shader>()); + shader_editor->edit(p_object->cast_to<ShaderGraph>()); } -bool ShaderEditorPlugin::handles(Object *p_object) const { +bool ShaderGraphEditorPlugin::handles(Object *p_object) const { - return p_object->is_type("Shader"); + ShaderGraph *shader=p_object->cast_to<ShaderGraph>(); + if (!shader) + return false; + if (_2d) + return shader->get_mode()==Shader::MODE_CANVAS_ITEM; + else + return shader->get_mode()==Shader::MODE_MATERIAL; } -void ShaderEditorPlugin::make_visible(bool p_visible) { +void ShaderGraphEditorPlugin::make_visible(bool p_visible) { if (p_visible) { shader_editor->show(); - shader_editor->set_process(true); } else { shader_editor->hide(); - shader_editor->set_process(false); } } -ShaderEditorPlugin::ShaderEditorPlugin(EditorNode *p_node) { +ShaderGraphEditorPlugin::ShaderGraphEditorPlugin(EditorNode *p_node, bool p_2d) { + _2d=p_2d; editor=p_node; - shader_editor = memnew( ShaderEditor ); - editor->get_viewport()->add_child(shader_editor); - shader_editor->set_area_as_parent_rect(); + shader_editor = memnew( ShaderGraphEditor(p_2d) ); shader_editor->hide(); + if (p_2d) + CanvasItemEditor::get_singleton()->get_bottom_split()->add_child(shader_editor); + else + SpatialEditor::get_singleton()->get_shader_split()->add_child(shader_editor); + // editor->get_viewport()->add_child(shader_editor); + // shader_editor->set_area_as_parent_rect(); + // shader_editor->hide(); } -ShaderEditorPlugin::~ShaderEditorPlugin() +ShaderGraphEditorPlugin::~ShaderGraphEditorPlugin() { } -#endif + diff --git a/tools/editor/plugins/shader_graph_editor_plugin.h b/tools/editor/plugins/shader_graph_editor_plugin.h index 5b0767dc82..39e9b29d45 100644 --- a/tools/editor/plugins/shader_graph_editor_plugin.h +++ b/tools/editor/plugins/shader_graph_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,122 +29,211 @@ #ifndef SHADER_GRAPH_EDITOR_PLUGIN_H #define SHADER_GRAPH_EDITOR_PLUGIN_H -#if 0 + #include "tools/editor/editor_plugin.h" #include "tools/editor/editor_node.h" #include "scene/resources/shader.h" #include "servers/visual/shader_graph.h" #include "scene/gui/tree.h" #include "scene/gui/button.h" +#include "scene/gui/graph_edit.h" #include "scene/gui/popup.h" #include "tools/editor/property_editor.h" +#include "scene/resources/shader_graph.h" /** @author Juan Linietsky <reduzio@gmail.com> */ -class ShaderEditor : public Control { - OBJ_TYPE(ShaderEditor, Control ); +class GraphColorRampEdit : public Control { + + OBJ_TYPE(GraphColorRampEdit,Control); - enum MenuAction { - GRAPH_ADD_NODE, - GRAPH_CLEAR, - NODE_DISCONNECT, - NODE_ERASE, + struct Point { + float offset; + Color color; + bool operator<(const Point& p_ponit) const { + return offset<p_ponit.offset; + } }; - enum ClickType { - CLICK_NONE, - CLICK_NODE, - CLICK_INPUT_SLOT, - CLICK_OUTPUT_SLOT, - CLICK_PARAMETER + PopupPanel *popup; + ColorPicker *picker; + + + bool grabbing; + int grabbed; + float grabbed_at; + Vector<Point> points; + + void _color_changed(const Color& p_color); + +protected: + void _input_event(const InputEvent& p_event); + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_ramp(const Vector<float>& p_offsets,const Vector<Color>& p_colors); + Vector<float> get_offsets() const; + Vector<Color> get_colors() const; + virtual Size2 get_minimum_size() const; + GraphColorRampEdit(); +}; + + +class GraphCurveMapEdit : public Control { + + OBJ_TYPE(GraphCurveMapEdit,Control); + + + struct Point { + + float offset; + float height; + bool operator<(const Point& p_ponit) const { + return offset<p_ponit.offset; + } }; - PopupMenu *node_popup; - Popup *add_popup; - PopupMenu *vertex_popup; - PopupMenu *fragment_popup; - PopupMenu *post_popup; - Tree *add_types; - Button *add_confirm; - HScrollBar *h_scroll; - VScrollBar *v_scroll; - - Ref<Shader> shader; - List<int> order; - Set<int> active_nodes; - ShaderGraph shader_graph; - int last_x,last_y; - uint32_t last_id; - - CustomPropertyEditor *property_editor; - - Point2 offset; - ClickType click_type; - Point2 click_pos; - int click_node; - int click_slot; - Point2 click_motion; - ClickType rclick_type; - int rclick_node; - int rclick_slot; - - Size2 _get_maximum_size(); - Size2 get_node_size(int p_node) const; - void _draw_node(int p_node); - - void _add_node_from_text(const String& p_text); - void _update_scrollbars(); - void _scroll_moved(); - void _node_param_changed(); - void _node_add_callback(); - void _node_add(VisualServer::ShaderNodeType p_type); - void _node_edit_property(int p_node); - void _node_menu_item(int p_item); - void _vertex_item(int p_item); - void _fragment_item(int p_item); - void _post_item(int p_item); - - ClickType _locate_click(const Point2& p_click,int *p_node_id,int *p_slot_index) const; - Point2 _get_slot_pos(int p_node_id,bool p_input,int p_slot); - - Error validate_graph(); - - void _read_shader_graph(); - void _write_shader_graph(); - - virtual bool has_point(const Point2& p_point) const; + + bool grabbing; + int grabbed; + Vector<Point> points; + + void _plot_curve(const Vector2& p_a,const Vector2& p_b,const Vector2& p_c,const Vector2& p_d); protected: + void _input_event(const InputEvent& p_event); void _notification(int p_what); - void _input_event(InputEvent p_event); static void _bind_methods(); public: - void edit(Ref<Shader> p_shader); - ShaderEditor(); + void set_points(const Vector<Vector2>& p_points); + Vector<Vector2> get_points() const; + virtual Size2 get_minimum_size() const; + GraphCurveMapEdit(); }; -class ShaderEditorPlugin : public EditorPlugin { +class ShaderGraphView : public Node { + + OBJ_TYPE(ShaderGraphView,Node); + + + + CustomPropertyEditor *ped_popup; + bool block_update; + + Label *status; + GraphEdit *graph_edit; + Ref<ShaderGraph> graph; + int edited_id; + int edited_def; + + ShaderGraph::ShaderType type; + + void _update_graph(); + void _create_node(int p_id); + + + ToolButton *make_label(String text, Variant::Type v_type = Variant::NIL); + ToolButton *make_editor(String text, GraphNode* gn, int p_id, int param, Variant::Type type, String p_hint=""); + + void _connection_request(const String& p_from, int p_from_slot,const String& p_to,int p_to_slot); + void _disconnection_request(const String& p_from, int p_from_slot,const String& p_to,int p_to_slot); + + void _node_removed(int p_id); + void _begin_node_move(); + void _node_moved(const Vector2& p_from, const Vector2& p_to,int p_id); + void _end_node_move(); + void _move_node(int p_id,const Vector2& p_to); + void _duplicate_nodes_request(); + void _duplicate_nodes(const Array &p_nodes); + void _delete_nodes_request(); + + + void _default_changed(int p_id, Node* p_button, int p_param, int v_type, String p_hint); + + void _scalar_const_changed(double p_value,int p_id); + void _vec_const_changed(double p_value, int p_id, Array p_arr); + void _rgb_const_changed(const Color& p_color, int p_id); + void _xform_const_changed(int p_id,Node* p_button); + void _scalar_op_changed(int p_op, int p_id); + void _vec_op_changed(int p_op, int p_id); + void _vec_scalar_op_changed(int p_op, int p_id); + void _rgb_op_changed(int p_op, int p_id); + void _xform_inv_rev_changed(bool p_enabled, int p_id); + void _scalar_func_changed(int p_func, int p_id); + void _vec_func_changed(int p_func, int p_id); + void _scalar_input_changed(double p_value,int p_id); + void _vec_input_changed(double p_value, int p_id, Array p_arr); + void _xform_input_changed(int p_id,Node* p_button); + void _rgb_input_changed(const Color& p_color, int p_id); + void _tex_input_change(int p_id,Node* p_button); + void _cube_input_change(int p_id); + void _input_name_changed(const String& p_name,int p_id,Node* p_line_edit); + void _tex_edited(int p_id,Node* p_button); + void _cube_edited(int p_id,Node* p_button); + void _variant_edited(); + void _comment_edited(int p_id,Node* p_button); + void _color_ramp_changed(int p_id,Node* p_ramp); + void _curve_changed(int p_id,Node* p_curve); + void _sg_updated(); + Map<int,GraphNode*> node_map; +protected: + void _notification(int p_what); + static void _bind_methods(); +public: + + void add_node(int p_type, const Vector2 &location); + GraphEdit *get_graph_edit() { return graph_edit; } + void set_graph(Ref<ShaderGraph> p_graph); - OBJ_TYPE( ShaderEditorPlugin, EditorPlugin ); + ShaderGraphView(ShaderGraph::ShaderType p_type=ShaderGraph::SHADER_TYPE_FRAGMENT); +}; + +class ShaderGraphEditor : public VBoxContainer { + + OBJ_TYPE(ShaderGraphEditor,VBoxContainer); - ShaderEditor *shader_editor; + PopupMenu *popup; + TabContainer *tabs; + ShaderGraphView *graph_edits[ShaderGraph::SHADER_TYPE_MAX]; + static const char* node_names[ShaderGraph::NODE_TYPE_MAX]; + Vector2 next_location; + + bool _2d; + void _add_node(int p_type); + void _popup_requested(const Vector2 &p_position); +protected: + void _notification(int p_what); + static void _bind_methods(); +public: + + void edit(Ref<ShaderGraph> p_shader); + ShaderGraphEditor(bool p_2d); +}; + +class ShaderGraphEditorPlugin : public EditorPlugin { + + OBJ_TYPE( ShaderGraphEditorPlugin, EditorPlugin ); + + bool _2d; + ShaderGraphEditor *shader_editor; EditorNode *editor; public: - virtual String get_name() const { return "Shader"; } + virtual String get_name() const { return "ShaderGraph"; } bool has_main_screen() const { return false; } virtual void edit(Object *p_node); virtual bool handles(Object *p_node) const; virtual void make_visible(bool p_visible); - ShaderEditorPlugin(EditorNode *p_node); - ~ShaderEditorPlugin(); + ShaderGraphEditorPlugin(EditorNode *p_node,bool p_2d); + ~ShaderGraphEditorPlugin(); }; #endif -#endif // SHADER_GRAPH_EDITOR_PLUGIN_H + diff --git a/tools/editor/plugins/spatial_editor_plugin.cpp b/tools/editor/plugins/spatial_editor_plugin.cpp index a1f1ccf5e3..7816efe89f 100644 --- a/tools/editor/plugins/spatial_editor_plugin.cpp +++ b/tools/editor/plugins/spatial_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -232,15 +232,6 @@ void SpatialEditorViewport::_select(Spatial *p_node, bool p_append,bool p_single } - -struct _RayResult { - - Spatial* item; - float depth; - int handle; - _FORCE_INLINE_ bool operator<(const _RayResult& p_rr) const { return depth<p_rr.depth; } -}; - ObjectID SpatialEditorViewport::_select_ray(const Point2& p_pos, bool p_append,bool &r_includes_current,int *r_gizmo_handle,bool p_alt_select) { if (r_gizmo_handle) @@ -379,6 +370,70 @@ ObjectID SpatialEditorViewport::_select_ray(const Point2& p_pos, bool p_append,b } +void SpatialEditorViewport::_find_items_at_pos(const Point2& p_pos,bool &r_includes_current,Vector<_RayResult> &results,bool p_alt_select) { + + Vector3 ray=_get_ray(p_pos); + Vector3 pos=_get_ray_pos(p_pos); + + Vector<RID> instances=VisualServer::get_singleton()->instances_cull_ray(pos,ray,get_tree()->get_root()->get_world()->get_scenario() ); + Set<Ref<SpatialEditorGizmo> > found_gizmos; + + r_includes_current=false; + + for (int i=0;i<instances.size();i++) { + + uint32_t id=VisualServer::get_singleton()->instance_get_object_instance_ID(instances[i]); + Object *obj=ObjectDB::get_instance(id); + if (!obj) + continue; + + Spatial *spat=obj->cast_to<Spatial>(); + + if (!spat) + continue; + + Ref<SpatialEditorGizmo> seg = spat->get_gizmo(); + + if (!seg.is_valid()) + continue; + + if (found_gizmos.has(seg)) + continue; + + found_gizmos.insert(seg); + Vector3 point; + Vector3 normal; + + int handle=-1; + bool inters = seg->intersect_ray(camera,p_pos,point,normal,NULL,p_alt_select); + + if (!inters) + continue; + + float dist = pos.distance_to(point); + + if (dist<0) + continue; + + + + if (editor_selection->is_selected(spat)) + r_includes_current=true; + + _RayResult res; + res.item=spat; + res.depth=dist; + res.handle=handle; + results.push_back(res); + } + + + if (results.empty()) + return; + + results.sort(); +} + Vector3 SpatialEditorViewport::_get_screen_to_space(const Vector3& p_pos) { @@ -477,6 +532,16 @@ void SpatialEditorViewport::_select_region() { } +void SpatialEditorViewport::_update_name() { + + String ortho = orthogonal?"Orthogonal":"Perspective"; + + if (name!="") + view_menu->set_text("[ "+name+" "+ortho+" ]"); + else + view_menu->set_text("[ "+ortho+" ]"); +} + void SpatialEditorViewport::_compute_edit(const Point2& p_point) { @@ -535,6 +600,14 @@ SpatialEditorViewport::NavigationScheme SpatialEditorViewport::_get_navigation_s return NAVIGATION_GODOT; } +SpatialEditorViewport::NavigationZoomStyle SpatialEditorViewport::_get_navigation_zoom_style(const String& p_property) { + switch(EditorSettings::get_singleton()->get(p_property).operator int()) { + case 0: return NAVIGATION_ZOOM_VERTICAL; + case 1: return NAVIGATION_ZOOM_HORIZONTAL; + } + return NAVIGATION_ZOOM_VERTICAL; +} + bool SpatialEditorViewport::_gizmo_select(const Vector2& p_screenpos,bool p_hilite_only) { if (!spatial_editor->is_gizmo_visible()) @@ -659,7 +732,8 @@ bool SpatialEditorViewport::_gizmo_select(const Vector2& p_screenpos,bool p_hili void SpatialEditorViewport::_smouseenter() { - surface->grab_focus(); + if (!surface->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) + surface->grab_focus(); } void SpatialEditorViewport::_sinput(const InputEvent &p_event) { @@ -705,6 +779,7 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { } break; case BUTTON_RIGHT: { + NavigationScheme nav_scheme = _get_navigation_schema("3d_editor/navigation_scheme"); if (b.pressed && _edit.gizmo.is_valid()) { //restore @@ -787,6 +862,57 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { //VisualServer::get_singleton()->instance_set_transform(cursor_instance,Transform(Matrix3(),cursor.cursor_pos)); } } + + if (b.mod.alt) { + + if (nav_scheme == NAVIGATION_MAYA) + break; + + _find_items_at_pos(Vector2( b.x, b.y ),clicked_includes_current,selection_results,b.mod.shift); + + clicked_wants_append=b.mod.shift; + + if (selection_results.size() == 1) { + + clicked=selection_results[0].item->get_instance_ID(); + selection_results.clear(); + + if (clicked) { + _select_clicked(clicked_wants_append,true); + clicked=0; + } + + } else if (!selection_results.empty()) { + + NodePath root_path = get_tree()->get_edited_scene_root()->get_path(); + StringName root_name = root_path.get_name(root_path.get_name_count()-1); + + for (int i = 0; i < selection_results.size(); i++) { + + Spatial *spat=selection_results[i].item; + + Ref<Texture> icon; + if (spat->has_meta("_editor_icon")) + icon=spat->get_meta("_editor_icon"); + else + icon=get_icon( has_icon(spat->get_type(),"EditorIcons")?spat->get_type():String("Object"),"EditorIcons"); + + String node_path="/"+root_name+"/"+root_path.rel_path_to(spat->get_path()); + + selection_menu->add_item(spat->get_name()); + selection_menu->set_item_icon(i, icon ); + selection_menu->set_item_metadata(i, node_path); + selection_menu->set_item_tooltip(i,String(spat->get_name())+ + "\nType: "+spat->get_type()+"\nPath: "+node_path); + } + + selection_menu->set_global_pos(Vector2( b.global_x, b.global_y )); + selection_menu->popup(); + selection_menu->call_deferred("grab_click_focus"); + + break; + } + } } if (_edit.mode!=TRANSFORM_NONE && b.pressed) { @@ -824,6 +950,8 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { _edit.plane=TRANSFORM_X_AXIS; set_message("View Plane Transform.",2); + name=""; + _update_name(); } break; case TRANSFORM_X_AXIS: { @@ -1429,10 +1557,19 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { if (nav_scheme==NAVIGATION_MAYA && m.mod.shift) zoom_speed *= zoom_speed_modifier; - if ( m.relative_y > 0) - cursor.distance*=1+m.relative_y*zoom_speed; - else if (m.relative_y < 0) - cursor.distance/=1-m.relative_y*zoom_speed; + NavigationZoomStyle zoom_style = _get_navigation_zoom_style("3d_editor/zoom_style"); + if (zoom_style == NAVIGATION_ZOOM_HORIZONTAL) { + if ( m.relative_x > 0) + cursor.distance*=1-m.relative_x*zoom_speed; + else if (m.relative_x < 0) + cursor.distance/=1+m.relative_x*zoom_speed; + } + else { + if ( m.relative_y > 0) + cursor.distance*=1+m.relative_y*zoom_speed; + else if (m.relative_y < 0) + cursor.distance/=1-m.relative_y*zoom_speed; + } } break; @@ -1443,6 +1580,8 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { cursor.x_rot=Math_PI/2.0; if (cursor.x_rot<-Math_PI/2.0) cursor.x_rot=-Math_PI/2.0; + name=""; + _update_name(); } break; default: {} @@ -1467,9 +1606,14 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { if (k.mod.shift) { cursor.x_rot=-Math_PI/2.0; set_message("Bottom View.",2); + name="Bottom"; + _update_name(); + } else { cursor.x_rot=Math_PI/2.0; set_message("Top View.",2); + name="Top"; + _update_name(); } } break; case KEY_KP_1: { @@ -1478,10 +1622,14 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { if (k.mod.shift) { cursor.y_rot=Math_PI; set_message("Rear View.",2); + name="Rear"; + _update_name(); } else { cursor.y_rot=0; set_message("Front View.",2); + name="Front"; + _update_name(); } } break; @@ -1491,9 +1639,13 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { if (k.mod.shift) { cursor.y_rot=Math_PI/2.0; set_message("Left View.",2); + name="Left"; + _update_name(); } else { cursor.y_rot=-Math_PI/2.0; set_message("Right View.",2); + name="Right"; + _update_name(); } } break; @@ -1501,6 +1653,7 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { orthogonal = !orthogonal; _menu_option(orthogonal?VIEW_PERSPECTIVE:VIEW_ORTHOGONAL); + _update_name(); } break; @@ -1720,6 +1873,12 @@ void SpatialEditorViewport::_notification(int p_what) { _init_gizmo_instance(index); } + if (p_what==NOTIFICATION_EXIT_TREE) { + + + _finish_gizmo_instances(); + + } if (p_what==NOTIFICATION_MOUSE_ENTER) { @@ -1814,35 +1973,47 @@ void SpatialEditorViewport::_menu_option(int p_option) { cursor.x_rot=Math_PI/2.0; cursor.y_rot=0; + name="Top"; + _update_name(); } break; case VIEW_BOTTOM: { cursor.x_rot=-Math_PI/2.0; cursor.y_rot=0; + name="Bottom"; + _update_name(); } break; case VIEW_LEFT: { cursor.y_rot=Math_PI/2.0; cursor.x_rot=0; + name="Left"; + _update_name(); } break; case VIEW_RIGHT: { cursor.y_rot=-Math_PI/2.0; cursor.x_rot=0; + name="Right"; + _update_name(); } break; case VIEW_FRONT: { cursor.y_rot=0; cursor.x_rot=0; + name="Front"; + _update_name(); } break; case VIEW_REAR: { cursor.y_rot=Math_PI; cursor.x_rot=0; + name="Rear"; + _update_name(); } break; case VIEW_CENTER_TO_SELECTION: { @@ -1893,11 +2064,11 @@ void SpatialEditorViewport::_menu_option(int p_option) { if (!se) continue; - Vector3 original_scale = sp->get_scale(); - sp->set_global_transform(camera_transform); - sp->set_scale(original_scale); - undo_redo->add_do_method(sp,"set_global_transform",sp->get_global_transform()); - undo_redo->add_undo_method(sp,"set_global_transform",se->original); + Transform xform = camera_transform; + xform.scale_basis(sp->get_scale()); + + undo_redo->add_do_method(sp,"set_global_transform",xform); + undo_redo->add_undo_method(sp,"set_global_transform",sp->get_global_transform()); } undo_redo->commit_action(); } break; @@ -1924,6 +2095,7 @@ void SpatialEditorViewport::_menu_option(int p_option) { view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), false ); orthogonal=false; call_deferred("update_transform_gizmo_view"); + _update_name(); } break; case VIEW_ORTHOGONAL: { @@ -1932,6 +2104,7 @@ void SpatialEditorViewport::_menu_option(int p_option) { view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), true ); orthogonal=true; call_deferred("update_transform_gizmo_view"); + _update_name(); } break; case VIEW_AUDIO_LISTENER: { @@ -1993,6 +2166,16 @@ void SpatialEditorViewport::_init_gizmo_instance(int p_idx) { } + +void SpatialEditorViewport::_finish_gizmo_instances() { + + + for(int i=0;i<3;i++) { + VS::get_singleton()->free(move_gizmo_instance[i]); + VS::get_singleton()->free(rotate_gizmo_instance[i]); + } + +} void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) { @@ -2020,6 +2203,26 @@ void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) { } } +void SpatialEditorViewport::_selection_result_pressed(int p_result) { + + if (selection_results.size() <= p_result) + return; + + clicked=selection_results[p_result].item->get_instance_ID(); + + if (clicked) { + _select_clicked(clicked_wants_append,true); + clicked=0; + } +} + +void SpatialEditorViewport::_selection_menu_hide() { + + selection_results.clear(); + selection_menu->clear(); + selection_menu->set_size(Vector2(0, 0)); +} + void SpatialEditorViewport::set_can_preview(Camera* p_preview) { preview=p_preview; @@ -2093,7 +2296,18 @@ void SpatialEditorViewport::set_state(const Dictionary& p_state) { view_menu->get_popup()->set_item_checked( idx, listener ); } - + if (p_state.has("previewing")) { + Node *pv = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["previewing"]); + if (pv && pv->cast_to<Camera>()) { + previewing=pv->cast_to<Camera>(); + previewing->connect("exit_tree",this,"_preview_exited_scene"); + VS::get_singleton()->viewport_attach_camera( viewport->get_viewport(), previewing->get_camera() ); //replace + view_menu->hide(); + surface->update(); + preview_camera->set_pressed(true); + preview_camera->show(); + } + } } Dictionary SpatialEditorViewport::get_state() const { @@ -2106,6 +2320,10 @@ Dictionary SpatialEditorViewport::get_state() const { d["use_environment"]=camera->get_environment().is_valid(); d["use_orthogonal"]=camera->get_projection()==Camera::PROJECTION_ORTHOGONAL; d["listener"]=viewport->is_audio_listener(); + if (previewing) { + d["previewing"]=EditorNode::get_singleton()->get_edited_scene()->get_path_to(previewing); + } + return d; } @@ -2119,6 +2337,8 @@ void SpatialEditorViewport::_bind_methods(){ ObjectTypeDB::bind_method(_MD("_toggle_camera_preview"),&SpatialEditorViewport::_toggle_camera_preview); ObjectTypeDB::bind_method(_MD("_preview_exited_scene"),&SpatialEditorViewport::_preview_exited_scene); ObjectTypeDB::bind_method(_MD("update_transform_gizmo_view"),&SpatialEditorViewport::update_transform_gizmo_view); + ObjectTypeDB::bind_method(_MD("_selection_result_pressed"),&SpatialEditorViewport::_selection_result_pressed); + ObjectTypeDB::bind_method(_MD("_selection_menu_hide"),&SpatialEditorViewport::_selection_menu_hide); ADD_SIGNAL( MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport")) ); } @@ -2130,18 +2350,24 @@ void SpatialEditorViewport::reset() { message_time=0; message=""; last_message=""; + name="Top"; cursor.x_rot=0; cursor.y_rot=0; cursor.distance=4; cursor.region_select=false; + _update_name(); +} +SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, EditorNode *p_editor, int p_index) { + _edit.mode=TRANSFORM_NONE; + _edit.plane=TRANSFORM_VIEW; + _edit.edited_gizmo=0; + _edit.snap=1; + _edit.gizmo_handle=0; -} - -SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, EditorNode *p_editor, int p_index) { index=p_index; editor=p_editor; @@ -2172,18 +2398,17 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed view_menu = memnew( MenuButton ); surface->add_child(view_menu); view_menu->set_pos( Point2(4,4)); - view_menu->set_text("[view]"); view_menu->set_self_opacity(0.5); - view_menu->get_popup()->add_item("Top",VIEW_TOP); - view_menu->get_popup()->add_item("Bottom",VIEW_BOTTOM); - view_menu->get_popup()->add_item("Left",VIEW_LEFT); - view_menu->get_popup()->add_item("Right",VIEW_RIGHT); - view_menu->get_popup()->add_item("Front",VIEW_FRONT); - view_menu->get_popup()->add_item("Rear",VIEW_REAR); + view_menu->get_popup()->add_item("Top (Num7)",VIEW_TOP); + view_menu->get_popup()->add_item("Bottom (Shift+Num7)",VIEW_BOTTOM); + view_menu->get_popup()->add_item("Left (Num3)",VIEW_LEFT); + view_menu->get_popup()->add_item("Right (Shift+Num3)",VIEW_RIGHT); + view_menu->get_popup()->add_item("Front (Num1)",VIEW_FRONT); + view_menu->get_popup()->add_item("Rear (Shift+Num1)",VIEW_REAR); view_menu->get_popup()->add_separator(); - view_menu->get_popup()->add_check_item("Perspective",VIEW_PERSPECTIVE); - view_menu->get_popup()->add_check_item("Orthogonal",VIEW_ORTHOGONAL); + view_menu->get_popup()->add_check_item("Perspective (Num5)",VIEW_PERSPECTIVE); + view_menu->get_popup()->add_check_item("Orthogonal (Num5)",VIEW_ORTHOGONAL); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE),true); view_menu->get_popup()->add_separator(); view_menu->get_popup()->add_check_item("Environment",VIEW_ENVIRONMENT); @@ -2211,11 +2436,21 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed preview=NULL; gizmo_scale=1.0; + selection_menu = memnew( PopupMenu ); + add_child(selection_menu); + selection_menu->set_custom_minimum_size(Vector2(100, 0)); + selection_menu->connect("item_pressed", this, "_selection_result_pressed"); + selection_menu->connect("popup_hide", this, "_selection_menu_hide"); + if (p_index==0) { view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER),true); viewport->set_as_audio_listener(true); } + + name="Top"; + _update_name(); + EditorSettings::get_singleton()->connect("settings_changed",this,"update_transform_gizmo_view"); } @@ -2625,7 +2860,7 @@ void SpatialEditor::_menu_item_pressed(int p_option) { } break; case MENU_TRANSFORM_CONFIGURE_SNAP: { - snap_dialog->popup_centered(Size2(200,160)); + snap_dialog->popup_centered(Size2(200,180)); } break; case MENU_TRANSFORM_LOCAL_COORDS: { @@ -2911,14 +3146,14 @@ void SpatialEditor::_init_indicators() { VisualServer::get_singleton()->instance_set_transform(light_instance,light_transform); - RID mat = VisualServer::get_singleton()->fixed_material_create(); - VisualServer::get_singleton()->fixed_material_set_flag(mat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true); - VisualServer::get_singleton()->fixed_material_set_flag(mat, VisualServer::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true); + //RID mat = VisualServer::get_singleton()->fixed_material_create(); + ///VisualServer::get_singleton()->fixed_material_set_flag(mat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true); + //VisualServer::get_singleton()->fixed_material_set_flag(mat, VisualServer::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true); { - RID indicator_mat = VisualServer::get_singleton()->fixed_material_create(); + indicator_mat = VisualServer::get_singleton()->fixed_material_create(); VisualServer::get_singleton()->material_set_flag( indicator_mat, VisualServer::MATERIAL_FLAG_UNSHADED, true ); VisualServer::get_singleton()->material_set_flag( indicator_mat, VisualServer::MATERIAL_FLAG_ONTOP, false ); VisualServer::get_singleton()->fixed_material_set_flag(indicator_mat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true); @@ -2982,7 +3217,7 @@ void SpatialEditor::_init_indicators() { d[VisualServer::ARRAY_COLOR]=origin_colors; VisualServer::get_singleton()->mesh_add_surface(origin,VisualServer::PRIMITIVE_LINES,d); - VisualServer::get_singleton()->mesh_surface_set_material(origin,0,indicator_mat,true); + VisualServer::get_singleton()->mesh_surface_set_material(origin,0,indicator_mat); // origin = VisualServer::get_singleton()->poly_create(); @@ -3013,17 +3248,17 @@ void SpatialEditor::_init_indicators() { cursor_points.push_back(Vector3(0,-cs,0)); cursor_points.push_back(Vector3(0,0,+cs)); cursor_points.push_back(Vector3(0,0,-cs)); - RID cmat=VisualServer::get_singleton()->fixed_material_create(); - VisualServer::get_singleton()->fixed_material_set_param(cmat,VS::FIXED_MATERIAL_PARAM_DIFFUSE,Color(0,1,1)); - VisualServer::get_singleton()->material_set_flag( cmat, VisualServer::MATERIAL_FLAG_UNSHADED, true ); - VisualServer::get_singleton()->fixed_material_set_flag(cmat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true); - VisualServer::get_singleton()->fixed_material_set_flag(cmat, VisualServer::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true); + cursor_material=VisualServer::get_singleton()->fixed_material_create(); + VisualServer::get_singleton()->fixed_material_set_param(cursor_material,VS::FIXED_MATERIAL_PARAM_DIFFUSE,Color(0,1,1)); + VisualServer::get_singleton()->material_set_flag( cursor_material, VisualServer::MATERIAL_FLAG_UNSHADED, true ); + VisualServer::get_singleton()->fixed_material_set_flag(cursor_material, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true); + VisualServer::get_singleton()->fixed_material_set_flag(cursor_material, VisualServer::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true); Array d; d.resize(VS::ARRAY_MAX); d[VS::ARRAY_VERTEX]=cursor_points; VisualServer::get_singleton()->mesh_add_surface(cursor_mesh,VS::PRIMITIVE_LINES,d); - VisualServer::get_singleton()->mesh_surface_set_material(cursor_mesh,0,cmat,true); + VisualServer::get_singleton()->mesh_surface_set_material(cursor_mesh,0,cursor_material); cursor_instance = VisualServer::get_singleton()->instance_create2(cursor_mesh,get_tree()->get_root()->get_world()->get_scenario()); VS::get_singleton()->instance_set_layer_mask(cursor_instance,1<<SpatialEditorViewport::GIZMO_GRID_LAYER); @@ -3096,11 +3331,11 @@ void SpatialEditor::_init_indicators() { int arrow_sides=6; - for(int i = 0; i < 7 ; i++) { + for(int k = 0; k < 7 ; k++) { - Matrix3 ma(ivec,Math_PI*2*float(i)/arrow_sides); - Matrix3 mb(ivec,Math_PI*2*float(i+1)/arrow_sides); + Matrix3 ma(ivec,Math_PI*2*float(k)/arrow_sides); + Matrix3 mb(ivec,Math_PI*2*float(k+1)/arrow_sides); for(int j=0;j<arrow_points-1;j++) { @@ -3192,7 +3427,6 @@ void SpatialEditor::_init_indicators() { void SpatialEditor::_finish_indicators() { - VisualServer::get_singleton()->free(origin_instance); VisualServer::get_singleton()->free(origin); for(int i=0;i<3;i++) { @@ -3207,6 +3441,8 @@ void SpatialEditor::_finish_indicators() { VisualServer::get_singleton()->free(cursor_instance); VisualServer::get_singleton()->free(cursor_mesh); + VisualServer::get_singleton()->free(indicator_mat); + VisualServer::get_singleton()->free(cursor_material); } void SpatialEditor::_instance_scene() { @@ -3295,6 +3531,7 @@ void SpatialEditor::_notification(int p_what) { tool_button[SpatialEditor::TOOL_MODE_ROTATE]->set_icon( get_icon("ToolRotate","EditorIcons") ); tool_button[SpatialEditor::TOOL_MODE_SCALE]->set_icon( get_icon("ToolScale","EditorIcons") ); instance_button->set_icon( get_icon("SpatialAdd","EditorIcons") ); + instance_button->hide(); view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT),get_icon("Panels1","EditorIcons")); @@ -3537,6 +3774,8 @@ void SpatialEditor::_default_light_angle_input(const InputEvent& p_event) { SpatialEditor::SpatialEditor(EditorNode *p_editor) { + gizmo.visible=true; + gizmo.scale=1.0; viewport_environment = Ref<Environment>( memnew( Environment ) ); undo_redo=p_editor->get_undo_redo(); @@ -3633,12 +3872,12 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { p->add_check_item("Use Default sRGB",MENU_VIEW_USE_DEFAULT_SRGB); p->add_separator(); - p->add_check_item("1 Viewport",MENU_VIEW_USE_1_VIEWPORT,KEY_MASK_ALT+KEY_1); - p->add_check_item("2 Viewports",MENU_VIEW_USE_2_VIEWPORTS,KEY_MASK_ALT+KEY_2); - p->add_check_item("2 Viewports (Alt)",MENU_VIEW_USE_2_VIEWPORTS_ALT,KEY_MASK_SHIFT+KEY_MASK_ALT+KEY_2); - p->add_check_item("3 Viewports",MENU_VIEW_USE_3_VIEWPORTS,KEY_MASK_ALT+KEY_3); - p->add_check_item("3 Viewports (Alt)",MENU_VIEW_USE_3_VIEWPORTS_ALT,KEY_MASK_SHIFT+KEY_MASK_ALT+KEY_3); - p->add_check_item("4 Viewports",MENU_VIEW_USE_4_VIEWPORTS,KEY_MASK_ALT+KEY_4); + p->add_check_item("1 Viewport",MENU_VIEW_USE_1_VIEWPORT,KEY_MASK_CMD+KEY_1); + p->add_check_item("2 Viewports",MENU_VIEW_USE_2_VIEWPORTS,KEY_MASK_CMD+KEY_2); + p->add_check_item("2 Viewports (Alt)",MENU_VIEW_USE_2_VIEWPORTS_ALT,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_2); + p->add_check_item("3 Viewports",MENU_VIEW_USE_3_VIEWPORTS,KEY_MASK_CMD+KEY_3); + p->add_check_item("3 Viewports (Alt)",MENU_VIEW_USE_3_VIEWPORTS_ALT,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_3); + p->add_check_item("4 Viewports",MENU_VIEW_USE_4_VIEWPORTS,KEY_MASK_CMD+KEY_4); p->add_separator(); p->add_check_item("Display Normal",MENU_VIEW_DISPLAY_NORMAL); @@ -3649,7 +3888,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { p->add_check_item("View Origin",MENU_VIEW_ORIGIN); p->add_check_item("View Grid",MENU_VIEW_GRID); p->add_separator(); - p->add_check_item("Settings",MENU_VIEW_CAMERA_SETTINGS ); + p->add_item("Settings",MENU_VIEW_CAMERA_SETTINGS); p->set_item_checked( p->get_item_index(MENU_VIEW_USE_DEFAULT_LIGHT), true ); @@ -3689,46 +3928,24 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { snap_dialog = memnew( ConfirmationDialog ); snap_dialog->set_title("Snap Settings"); add_child(snap_dialog); - Label *l = memnew(Label); - l->set_text("Translate Snap:"); - l->set_pos(Point2(5,5)); - snap_dialog->add_child(l); + + VBoxContainer *snap_dialog_vbc = memnew( VBoxContainer ); + snap_dialog->add_child(snap_dialog_vbc); + snap_dialog->set_child_rect(snap_dialog_vbc); snap_translate = memnew( LineEdit ); - snap_translate->set_anchor( MARGIN_RIGHT, ANCHOR_END ); - snap_translate->set_begin( Point2(15,22) ); - snap_translate->set_end( Point2(15,35) ); snap_translate->set_text("1"); - snap_dialog->add_child(snap_translate); - - l = memnew(Label); - l->set_text("Rotate Snap (deg.):"); - l->set_pos(Point2(5,45)); - snap_dialog->add_child(l); + snap_dialog_vbc->add_margin_child("Translate Snap:",snap_translate); snap_rotate = memnew( LineEdit ); - snap_rotate->set_anchor( MARGIN_RIGHT, ANCHOR_END ); - snap_rotate->set_begin( Point2(15,62) ); - snap_rotate->set_end( Point2(15,75) ); snap_rotate->set_text("5"); - snap_dialog->add_child(snap_rotate); - - - l = memnew(Label); - l->set_text("Scale Snap (%):"); - l->set_pos(Point2(5,85)); - snap_dialog->add_child(l); + snap_dialog_vbc->add_margin_child("Rotate Snap (deg.):",snap_rotate); snap_scale = memnew( LineEdit ); - snap_scale->set_anchor( MARGIN_RIGHT, ANCHOR_END ); - snap_scale->set_begin( Point2(15,102) ); - snap_scale->set_end( Point2(15,115) ); snap_scale->set_text("5"); - snap_dialog->add_child(snap_scale); + snap_dialog_vbc->add_margin_child("Scale Snap (%):",snap_scale); - //snap_dialog->get_cancel()->hide(); - - /* SNAP DIALOG */ + /* SETTINGS DIALOG */ settings_dialog = memnew( ConfirmationDialog ); settings_dialog->set_title("Viewport Settings"); @@ -3802,7 +4019,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { xform_dialog = memnew( ConfirmationDialog ); xform_dialog->set_title("Transform Change"); add_child(xform_dialog); - l = memnew(Label); + Label *l = memnew(Label); l->set_text("Translate:"); l->set_pos(Point2(5,5)); xform_dialog->add_child(l); diff --git a/tools/editor/plugins/spatial_editor_plugin.h b/tools/editor/plugins/spatial_editor_plugin.h index 1fdc97c49d..ebd3f77fe7 100644 --- a/tools/editor/plugins/spatial_editor_plugin.h +++ b/tools/editor/plugins/spatial_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -92,6 +92,7 @@ public: }; private: int index; + String name; void _menu_option(int p_option); Size2 prev_size; @@ -110,11 +111,21 @@ private: bool orthogonal; float gizmo_scale; + struct _RayResult { + + Spatial* item; + float depth; + int handle; + _FORCE_INLINE_ bool operator<(const _RayResult& p_rr) const { return depth<p_rr.depth; } + }; + + void _update_name(); void _compute_edit(const Point2& p_point); void _clear_selected(); void _select_clicked(bool p_append,bool p_single); void _select(Spatial *p_node, bool p_append,bool p_single); ObjectID _select_ray(const Point2& p_pos, bool p_append,bool &r_includes_current,int *r_gizmo_handle=NULL,bool p_alt_select=false); + void _find_items_at_pos(const Point2& p_pos,bool &r_includes_current,Vector<_RayResult> &results,bool p_alt_select=false); Vector3 _get_ray_pos(const Vector2& p_pos) const; Vector3 _get_ray(const Vector2& p_pos); Point2 _point_to_screen(const Vector3& p_point); @@ -134,9 +145,12 @@ private: float get_fov() const; ObjectID clicked; + Vector<_RayResult> selection_results; bool clicked_includes_current; bool clicked_wants_append; + PopupMenu *selection_menu; + enum NavigationScheme { NAVIGATION_GODOT, NAVIGATION_MAYA, @@ -144,6 +158,12 @@ private: }; NavigationScheme _get_navigation_schema(const String& p_property); + enum NavigationZoomStyle { + NAVIGATION_ZOOM_VERTICAL, + NAVIGATION_ZOOM_HORIZONTAL + }; + NavigationZoomStyle _get_navigation_zoom_style(const String& p_property); + enum NavigationMode { NAVIGATION_NONE, NAVIGATION_PAN, @@ -216,6 +236,9 @@ private: void _preview_exited_scene(); void _toggle_camera_preview(bool); void _init_gizmo_instance(int p_idx); + void _finish_gizmo_instances(); + void _selection_result_pressed(int); + void _selection_menu_hide(); protected: @@ -230,7 +253,7 @@ public: void set_state(const Dictionary& p_state); Dictionary get_state() const; void reset(); - + Viewport *get_viewport_node() { return viewport; } SpatialEditorViewport(SpatialEditor *p_spatial_editor,EditorNode *p_editor,int p_index); @@ -316,6 +339,8 @@ private: RID indicators_instance; RID cursor_mesh; RID cursor_instance; + RID indicator_mat; + RID cursor_material; /* struct Selected { @@ -411,6 +436,7 @@ private: HBoxContainer *hbc_menu; + // // void _generate_selection_box(); @@ -503,6 +529,11 @@ public: void set_can_preview(Camera* p_preview); + SpatialEditorViewport *get_editor_viewport(int p_idx) { + ERR_FAIL_INDEX_V(p_idx,4,NULL); + return viewports[p_idx]; + } + Camera *get_camera() { return NULL; } void edit(Spatial *p_spatial); void clear(); diff --git a/tools/editor/plugins/sprite_frames_editor_plugin.cpp b/tools/editor/plugins/sprite_frames_editor_plugin.cpp index e04d9dfddb..e90087efda 100644 --- a/tools/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/tools/editor/plugins/sprite_frames_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -76,7 +76,7 @@ void SpriteFramesEditor::_file_load_request(const DVector<String>& p_path) { dialog->set_title("Error!"); //dialog->get_cancel()->set_text("Close"); dialog->get_ok()->set_text("Close"); - dialog->popup_centered(Size2(300,60)); + dialog->popup_centered_minsize(); return; ///beh should show an error i guess } @@ -115,7 +115,7 @@ void SpriteFramesEditor::_load_pressed() { for(int i=0;i<extensions.size();i++) file->add_filter("*."+extensions[i]); - file->set_mode(FileDialog::MODE_OPEN_FILES); + file->set_mode(EditorFileDialog::MODE_OPEN_FILES); file->popup_centered_ratio(); @@ -188,7 +188,7 @@ void SpriteFramesEditor::_paste_pressed() { dialog->set_title("Error!"); //dialog->get_cancel()->set_text("Close"); dialog->get_ok()->set_text("Close"); - dialog->popup_centered(Size2(300,60)); + dialog->popup_centered_minsize(); return; ///beh should show an error i guess } @@ -229,6 +229,33 @@ void SpriteFramesEditor::_empty_pressed() { } +void SpriteFramesEditor::_empty2_pressed() { + + + int from=-1; + + if (tree->get_selected()) { + + from = tree->get_selected()->get_metadata(0); + sel=from; + + } else { + from=frames->get_frame_count(); + } + + + + Ref<Texture> r; + + undo_redo->create_action("Add Empty"); + undo_redo->add_do_method(frames,"add_frame",r,from+1); + undo_redo->add_undo_method(frames,"remove_frame",from+1); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); + +} + void SpriteFramesEditor::_up_pressed() { if (!tree->get_selected()) @@ -322,6 +349,8 @@ void SpriteFramesEditor::_update_library() { ti->set_text(0,"Frame "+itos(i)); ti->set_icon(0,frames->get_frame(i)); } + if (frames->get_frame(i).is_valid()) + ti->set_tooltip(0,frames->get_frame(i)->get_path()); ti->set_metadata(0,i); ti->set_icon_max_width(0,96); if (sel==i) @@ -355,6 +384,7 @@ void SpriteFramesEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("_input_event"),&SpriteFramesEditor::_input_event); ObjectTypeDB::bind_method(_MD("_load_pressed"),&SpriteFramesEditor::_load_pressed); ObjectTypeDB::bind_method(_MD("_empty_pressed"),&SpriteFramesEditor::_empty_pressed); + ObjectTypeDB::bind_method(_MD("_empty2_pressed"),&SpriteFramesEditor::_empty2_pressed); ObjectTypeDB::bind_method(_MD("_item_edited"),&SpriteFramesEditor::_item_edited); ObjectTypeDB::bind_method(_MD("_delete_pressed"),&SpriteFramesEditor::_delete_pressed); ObjectTypeDB::bind_method(_MD("_paste_pressed"),&SpriteFramesEditor::_paste_pressed); @@ -387,9 +417,13 @@ SpriteFramesEditor::SpriteFramesEditor() { hbc->add_child(paste); empty = memnew( Button ); - empty->set_text("Insert Empty"); + empty->set_text("Insert Empty (Before)"); hbc->add_child(empty); + empty2 = memnew( Button ); + empty2->set_text("Insert Empty (After)"); + hbc->add_child(empty2); + move_up = memnew( Button ); move_up->set_text("Up"); hbc->add_child(move_up); @@ -401,7 +435,7 @@ SpriteFramesEditor::SpriteFramesEditor() { _delete = memnew( Button ); hbc->add_child(_delete); - file = memnew( FileDialog ); + file = memnew( EditorFileDialog ); add_child(file); @@ -422,6 +456,7 @@ SpriteFramesEditor::SpriteFramesEditor() { _delete->connect("pressed", this,"_delete_pressed"); paste->connect("pressed", this,"_paste_pressed"); empty->connect("pressed", this,"_empty_pressed"); + empty2->connect("pressed", this,"_empty2_pressed"); move_up->connect("pressed", this,"_up_pressed"); move_down->connect("pressed", this,"_down_pressed"); file->connect("files_selected", this,"_file_load_request"); diff --git a/tools/editor/plugins/sprite_frames_editor_plugin.h b/tools/editor/plugins/sprite_frames_editor_plugin.h index 99c6ad486e..969d7b1ce3 100644 --- a/tools/editor/plugins/sprite_frames_editor_plugin.h +++ b/tools/editor/plugins/sprite_frames_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -46,6 +46,7 @@ class SpriteFramesEditor : public PanelContainer { Button *_delete; Button *paste; Button *empty; + Button *empty2; Button *move_up; Button *move_down; Tree *tree; @@ -53,7 +54,7 @@ class SpriteFramesEditor : public PanelContainer { int sel; - FileDialog *file; + EditorFileDialog *file; AcceptDialog *dialog; @@ -65,6 +66,7 @@ class SpriteFramesEditor : public PanelContainer { void _file_load_request(const DVector<String>& p_path); void _paste_pressed(); void _empty_pressed(); + void _empty2_pressed(); void _delete_pressed(); void _delete_confirm_pressed(); void _up_pressed(); diff --git a/tools/editor/plugins/sprite_region_editor_plugin.cpp b/tools/editor/plugins/sprite_region_editor_plugin.cpp new file mode 100644 index 0000000000..35c53cf562 --- /dev/null +++ b/tools/editor/plugins/sprite_region_editor_plugin.cpp @@ -0,0 +1,565 @@ +/*************************************************************************/ +/* sprite_region_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Author: Mariano Suligoy */ +/* */ +/* 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, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "sprite_region_editor_plugin.h" +#include "scene/gui/check_box.h" +#include "os/input.h" +#include "os/keyboard.h" + +void SpriteRegionEditor::_region_draw() +{ + Ref<Texture> base_tex = node->get_texture(); + if (base_tex.is_null()) + return; + + Matrix32 mtx; + mtx.elements[2]=-draw_ofs; + mtx.scale_basis(Vector2(draw_zoom,draw_zoom)); + + VS::get_singleton()->canvas_item_set_clip(edit_draw->get_canvas_item(),true); + VS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(),mtx); + edit_draw->draw_texture(base_tex,Point2()); + VS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(),Matrix32()); + + if (snap_show_grid) { + Size2 s = edit_draw->get_size(); + int last_cell; + + if (snap_step.x!=0) { + for(int i=0;i<s.width;i++) { + int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(i,0)).x-snap_offset.x)/snap_step.x)); + if (i==0) + last_cell=cell; + if (last_cell!=cell) + edit_draw->draw_line(Point2(i,0),Point2(i,s.height),Color(0.3,0.7,1,0.3)); + last_cell=cell; + } + } + + if (snap_step.y!=0) { + for(int i=0;i<s.height;i++) { + int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(0,i)).y-snap_offset.y)/snap_step.y)); + if (i==0) + last_cell=cell; + if (last_cell!=cell) + edit_draw->draw_line(Point2(0,i),Point2(s.width,i),Color(0.3,0.7,1,0.3)); + last_cell=cell; + } + } + } + + Ref<Texture> select_handle = get_icon("EditorHandle","EditorIcons"); + + Rect2 scroll_rect(Point2(),mtx.basis_xform(base_tex->get_size())); + scroll_rect.expand_to(mtx.basis_xform(edit_draw->get_size())); + + Vector2 endpoints[4]={ + mtx.basis_xform(rect.pos), + mtx.basis_xform(rect.pos+Vector2(rect.size.x,0)), + mtx.basis_xform(rect.pos+rect.size), + mtx.basis_xform(rect.pos+Vector2(0,rect.size.y)) + }; + + for(int i=0;i<4;i++) { + + int prev = (i+3)%4; + int next = (i+1)%4; + + Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized(); + ofs*=1.4144*(select_handle->get_size().width/2); + + edit_draw->draw_line(endpoints[i]-draw_ofs, endpoints[next]-draw_ofs, Color(0.9,0.5,0.5), 2); + + edit_draw->draw_texture(select_handle,(endpoints[i]+ofs-(select_handle->get_size()/2)).floor()-draw_ofs); + + ofs = (endpoints[next]-endpoints[i])/2; + ofs += (endpoints[next]-endpoints[i]).tangent().normalized()*(select_handle->get_size().width/2); + + edit_draw->draw_texture(select_handle,(endpoints[i]+ofs-(select_handle->get_size()/2)).floor()-draw_ofs); + + scroll_rect.expand_to(endpoints[i]); + } + + scroll_rect=scroll_rect.grow(200); + updating_scroll=true; + hscroll->set_min(scroll_rect.pos.x); + hscroll->set_max(scroll_rect.pos.x+scroll_rect.size.x); + hscroll->set_page(edit_draw->get_size().x); + hscroll->set_val(draw_ofs.x); + hscroll->set_step(0.001); + + vscroll->set_min(scroll_rect.pos.y); + vscroll->set_max(scroll_rect.pos.y+scroll_rect.size.y); + vscroll->set_page(edit_draw->get_size().y); + vscroll->set_val(draw_ofs.y); + vscroll->set_step(0.001); + updating_scroll=false; +} + +void SpriteRegionEditor::_region_input(const InputEvent& p_input) +{ + Matrix32 mtx; + mtx.elements[2]=-draw_ofs; + mtx.scale_basis(Vector2(draw_zoom,draw_zoom)); + + Vector2 endpoints[8]={ + mtx.xform(rect.pos)+Vector2(-4,-4), + mtx.xform(rect.pos+Vector2(rect.size.x/2,0))+Vector2(0,-4), + mtx.xform(rect.pos+Vector2(rect.size.x,0))+Vector2(4,-4), + mtx.xform(rect.pos+Vector2(rect.size.x,rect.size.y/2))+Vector2(4,0), + mtx.xform(rect.pos+rect.size)+Vector2(4,4), + mtx.xform(rect.pos+Vector2(rect.size.x/2,rect.size.y))+Vector2(0,4), + mtx.xform(rect.pos+Vector2(0,rect.size.y))+Vector2(-4,4), + mtx.xform(rect.pos+Vector2(0,rect.size.y/2))+Vector2(-4,0) + }; + + if (p_input.type==InputEvent::MOUSE_BUTTON) { + + + const InputEventMouseButton &mb=p_input.mouse_button; + + if (mb.button_index==BUTTON_LEFT) { + + + if (mb.pressed) { + + drag_from=mtx.affine_inverse().xform(Vector2(mb.x,mb.y)); + drag_from=snap_point(drag_from); + drag=true; + rect_prev=node->get_region_rect(); + + drag_index=-1; + for(int i=0;i<8;i++) { + + Vector2 tuv=endpoints[i]; + if (tuv.distance_to(Vector2(mb.x,mb.y))<8) { + drag_index=i; + creating = false; + } + } + + if (drag_index==-1) { + creating = true; + rect = Rect2(drag_from,Size2()); + } + + } else if (drag) { + + undo_redo->create_action("Set region_rect"); + undo_redo->add_do_method(node,"set_region_rect",node->get_region_rect()); + undo_redo->add_undo_method(node,"set_region_rect",rect_prev); + undo_redo->add_do_method(edit_draw,"update"); + undo_redo->add_undo_method(edit_draw,"update"); + undo_redo->commit_action(); + + drag=false; + } + + } else if (mb.button_index==BUTTON_RIGHT && mb.pressed) { + + if (drag) { + drag=false; + node->set_region_rect(rect_prev); + rect=rect_prev; + edit_draw->update(); + } + + } else if (mb.button_index==BUTTON_WHEEL_UP && mb.pressed) { + + zoom->set_val( zoom->get_val()/0.9 ); + } else if (mb.button_index==BUTTON_WHEEL_DOWN && mb.pressed) { + + zoom->set_val( zoom->get_val()*0.9); + } + + } else if (p_input.type==InputEvent::MOUSE_MOTION) { + + const InputEventMouseMotion &mm=p_input.mouse_motion; + + if (mm.button_mask&BUTTON_MASK_MIDDLE || Input::get_singleton()->is_key_pressed(KEY_SPACE)) { + + Vector2 draged(mm.relative_x,mm.relative_y); + hscroll->set_val( hscroll->get_val()-draged.x ); + vscroll->set_val( vscroll->get_val()-draged.y ); + + } else if (drag) { + + Vector2 new_pos = mtx.affine_inverse().xform(Vector2(mm.x,mm.y)); + new_pos = snap_point(new_pos); + + if (creating) { + rect = Rect2(drag_from,Size2()); + rect.expand_to(new_pos); + node->set_region_rect(rect); + edit_draw->update(); + return; + } + + switch(drag_index) { + case 0: { + Vector2 p=rect_prev.pos+rect_prev.size; + rect = Rect2(p,Size2()); + rect.expand_to(new_pos); + node->set_region_rect(rect); + } break; + case 1: { + Vector2 p=rect_prev.pos+Vector2(0,rect_prev.size.y); + rect = Rect2(p,Size2(rect_prev.size.x,0)); + rect.expand_to(new_pos); + node->set_region_rect(rect); + } break; + case 2: { + Vector2 p=rect_prev.pos+Vector2(0,rect_prev.size.y); + rect = Rect2(p,Size2()); + rect.expand_to(new_pos); + node->set_region_rect(rect); + } break; + case 3: { + Vector2 p=rect_prev.pos; + rect = Rect2(p,Size2(0,rect_prev.size.y)); + rect.expand_to(new_pos); + node->set_region_rect(rect); + } break; + case 4: { + Vector2 p=rect_prev.pos; + rect = Rect2(p,Size2()); + rect.expand_to(new_pos); + node->set_region_rect(rect); + } break; + case 5: { + Vector2 p=rect_prev.pos; + rect = Rect2(p,Size2(rect_prev.size.x,0)); + rect.expand_to(new_pos); + node->set_region_rect(rect); + } break; + case 6: { + Vector2 p=rect_prev.pos+Vector2(rect_prev.size.x,0); + rect = Rect2(p,Size2()); + rect.expand_to(new_pos); + node->set_region_rect(rect); + } break; + case 7: { + Vector2 p=rect_prev.pos+Vector2(rect_prev.size.x,0); + rect = Rect2(p,Size2(0,rect_prev.size.y)); + rect.expand_to(new_pos); + node->set_region_rect(rect); + } break; + + } + edit_draw->update(); + } + + } +} + +void SpriteRegionEditor::_scroll_changed(float) +{ + if (updating_scroll) + return; + + draw_ofs.x=hscroll->get_val(); + draw_ofs.y=vscroll->get_val(); + draw_zoom=zoom->get_val(); + print_line("_scroll_changed"); + edit_draw->update(); +} + +void SpriteRegionEditor::_set_use_snap(bool p_use) +{ + use_snap=p_use; +} + +void SpriteRegionEditor::_set_show_grid(bool p_show) +{ + snap_show_grid=p_show; + edit_draw->update(); +} + +void SpriteRegionEditor::_set_snap_off_x(float p_val) +{ + snap_offset.x=p_val; + edit_draw->update(); +} + +void SpriteRegionEditor::_set_snap_off_y(float p_val) +{ + snap_offset.y=p_val; + edit_draw->update(); +} + +void SpriteRegionEditor::_set_snap_step_x(float p_val) +{ + snap_step.x=p_val; + edit_draw->update(); +} + +void SpriteRegionEditor::_set_snap_step_y(float p_val) +{ + snap_step.y=p_val; + edit_draw->update(); +} + +void SpriteRegionEditor::_notification(int p_what) +{ + switch(p_what) { + + case NOTIFICATION_READY: { + edit_node->set_icon( get_icon("RegionEdit","EditorIcons")); + b_snap_grid->set_icon( get_icon("Grid", "EditorIcons")); + b_snap_enable->set_icon( get_icon("Snap", "EditorIcons")); + icon_zoom->set_texture( get_icon("Zoom", "EditorIcons")); + } break; + } +} + +void SpriteRegionEditor::_node_removed(Node *p_node) +{ + if(p_node==node) { + node=NULL; + hide(); + } +} + +void SpriteRegionEditor::_bind_methods() +{ + ObjectTypeDB::bind_method(_MD("_edit_node"),&SpriteRegionEditor::_edit_node); + ObjectTypeDB::bind_method(_MD("_region_draw"),&SpriteRegionEditor::_region_draw); + ObjectTypeDB::bind_method(_MD("_region_input"),&SpriteRegionEditor::_region_input); + ObjectTypeDB::bind_method(_MD("_scroll_changed"),&SpriteRegionEditor::_scroll_changed); + ObjectTypeDB::bind_method(_MD("_node_removed"),&SpriteRegionEditor::_node_removed); + ObjectTypeDB::bind_method(_MD("_set_use_snap"),&SpriteRegionEditor::_set_use_snap); + ObjectTypeDB::bind_method(_MD("_set_show_grid"),&SpriteRegionEditor::_set_show_grid); + ObjectTypeDB::bind_method(_MD("_set_snap_off_x"),&SpriteRegionEditor::_set_snap_off_x); + ObjectTypeDB::bind_method(_MD("_set_snap_off_y"),&SpriteRegionEditor::_set_snap_off_y); + ObjectTypeDB::bind_method(_MD("_set_snap_step_x"),&SpriteRegionEditor::_set_snap_step_x); + ObjectTypeDB::bind_method(_MD("_set_snap_step_y"),&SpriteRegionEditor::_set_snap_step_y); +} + +void SpriteRegionEditor::edit(Node *p_sprite) +{ + if (p_sprite) { + node=p_sprite->cast_to<Sprite>(); + node->connect("exit_tree",this,"_node_removed",varray(),CONNECT_ONESHOT); + } else { + if (node) + node->disconnect("exit_tree",this,"_node_removed"); + node=NULL; + } + +} +void SpriteRegionEditor::_edit_node() +{ + if (node->get_texture().is_null()) { + + error->set_text("No texture in this sprite.\nSet a texture to be able to edit Region."); + error->popup_centered_minsize(); + return; + } + + rect=node->get_region_rect(); + dlg_editor->popup_centered_ratio(0.85); +} + +inline float _snap_scalar(float p_offset, float p_step, float p_target) { + return p_step != 0 ? Math::stepify(p_target - p_offset, p_step) + p_offset : p_target; +} + +Vector2 SpriteRegionEditor::snap_point(Vector2 p_target) const { + if (use_snap) { + p_target.x = _snap_scalar(snap_offset.x, snap_step.x, p_target.x); + p_target.y = _snap_scalar(snap_offset.y, snap_step.y, p_target.y); + } + p_target = p_target.snapped(Size2(1, 1)); + + return p_target; +} + +SpriteRegionEditor::SpriteRegionEditor(EditorNode* p_editor) +{ + node=NULL; + editor=p_editor; + undo_redo = editor->get_undo_redo(); + + snap_step=Vector2(10,10); + use_snap=false; + snap_show_grid=false; + + add_child( memnew( VSeparator )); + edit_node = memnew( ToolButton ); + add_child(edit_node); + edit_node->connect("pressed",this,"_edit_node"); + + dlg_editor = memnew( AcceptDialog ); + add_child(dlg_editor); + dlg_editor->set_title("Sprite Region Editor"); + dlg_editor->set_self_opacity(0.9); + + VBoxContainer *main_vb = memnew( VBoxContainer ); + dlg_editor->add_child(main_vb); + dlg_editor->set_child_rect(main_vb); + HBoxContainer *hb_tools = memnew( HBoxContainer ); + main_vb->add_child(hb_tools); + + b_snap_enable = memnew( ToolButton ); + hb_tools->add_child(b_snap_enable); + b_snap_enable->set_text("Snap"); + b_snap_enable->set_focus_mode(FOCUS_NONE); + b_snap_enable->set_toggle_mode(true); + b_snap_enable->set_pressed(use_snap); + b_snap_enable->set_tooltip("Enable Snap"); + b_snap_enable->connect("toggled",this,"_set_use_snap"); + + b_snap_grid = memnew( ToolButton ); + hb_tools->add_child(b_snap_grid); + b_snap_grid->set_text("Grid"); + b_snap_grid->set_focus_mode(FOCUS_NONE); + b_snap_grid->set_toggle_mode(true); + b_snap_grid->set_pressed(snap_show_grid); + b_snap_grid->set_tooltip("Show Grid"); + b_snap_grid->connect("toggled",this,"_set_show_grid"); + + hb_tools->add_child( memnew( VSeparator )); + hb_tools->add_child( memnew( Label("Grid Offset:") ) ); + + SpinBox *sb_off_x = memnew( SpinBox ); + sb_off_x->set_min(-256); + sb_off_x->set_max(256); + sb_off_x->set_step(1); + sb_off_x->set_val(snap_offset.x); + sb_off_x->set_suffix("px"); + sb_off_x->connect("value_changed", this, "_set_snap_off_x"); + hb_tools->add_child(sb_off_x); + + SpinBox *sb_off_y = memnew( SpinBox ); + sb_off_y->set_min(-256); + sb_off_y->set_max(256); + sb_off_y->set_step(1); + sb_off_y->set_val(snap_offset.y); + sb_off_y->set_suffix("px"); + sb_off_y->connect("value_changed", this, "_set_snap_off_y"); + hb_tools->add_child(sb_off_y); + + hb_tools->add_child( memnew( VSeparator )); + hb_tools->add_child( memnew( Label("Grid Step:") ) ); + + SpinBox *sb_step_x = memnew( SpinBox ); + sb_step_x->set_min(-256); + sb_step_x->set_max(256); + sb_step_x->set_step(1); + sb_step_x->set_val(snap_step.x); + sb_step_x->set_suffix("px"); + sb_step_x->connect("value_changed", this, "_set_snap_step_x"); + hb_tools->add_child(sb_step_x); + + SpinBox *sb_step_y = memnew( SpinBox ); + sb_step_y->set_min(-256); + sb_step_y->set_max(256); + sb_step_y->set_step(1); + sb_step_y->set_val(snap_step.y); + sb_step_y->set_suffix("px"); + sb_step_y->connect("value_changed", this, "_set_snap_step_y"); + hb_tools->add_child(sb_step_y); + +// MARIANOGNU::TODO: Add more tools? + + HBoxContainer *main_hb = memnew( HBoxContainer ); + main_vb->add_child(main_hb); + edit_draw = memnew( Control ); + main_hb->add_child(edit_draw); + main_hb->set_v_size_flags(SIZE_EXPAND_FILL); + edit_draw->set_h_size_flags(SIZE_EXPAND_FILL); + + + hb_tools->add_child( memnew( VSeparator )); + icon_zoom = memnew( TextureFrame ); + hb_tools->add_child(icon_zoom); + + zoom = memnew( HSlider ); + zoom->set_min(0.01); + zoom->set_max(4); + zoom->set_val(1); + zoom->set_step(0.01); + hb_tools->add_child(zoom); + zoom->set_custom_minimum_size(Size2(200,0)); + zoom_value = memnew( SpinBox ); + zoom->share(zoom_value); + zoom_value->set_custom_minimum_size(Size2(50,0)); + hb_tools->add_child(zoom_value); + zoom->connect("value_changed",this,"_scroll_changed"); + + + + vscroll = memnew( VScrollBar); + main_hb->add_child(vscroll); + vscroll->connect("value_changed",this,"_scroll_changed"); + hscroll = memnew( HScrollBar ); + main_vb->add_child(hscroll); + hscroll->connect("value_changed",this,"_scroll_changed"); + + edit_draw->connect("draw",this,"_region_draw"); + edit_draw->connect("input_event",this,"_region_input"); + draw_zoom=1.0; + updating_scroll=false; + + error = memnew( AcceptDialog); + add_child(error); + +} + +void SpriteRegionEditorPlugin::edit(Object *p_node) +{ + region_editor->edit(p_node->cast_to<Node>()); +} + +bool SpriteRegionEditorPlugin::handles(Object *p_node) const +{ + return p_node->is_type("Sprite"); +} + +void SpriteRegionEditorPlugin::make_visible(bool p_visible) +{ + if (p_visible) { + region_editor->show(); + } else { + region_editor->hide(); + region_editor->edit(NULL); + } +} + +SpriteRegionEditorPlugin::SpriteRegionEditorPlugin(EditorNode *p_node) +{ + editor = p_node; + region_editor= memnew ( SpriteRegionEditor(p_node) ); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(region_editor); + + region_editor->hide(); +} + diff --git a/tools/editor/plugins/sprite_region_editor_plugin.h b/tools/editor/plugins/sprite_region_editor_plugin.h new file mode 100644 index 0000000000..cf69395f40 --- /dev/null +++ b/tools/editor/plugins/sprite_region_editor_plugin.h @@ -0,0 +1,125 @@ +/*************************************************************************/ +/* sprite_region_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Author: Mariano Suligoy */ +/* */ +/* 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, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SPRITE_REGION_EDITOR_PLUGIN_H +#define SPRITE_REGION_EDITOR_PLUGIN_H + +#include "canvas_item_editor_plugin.h" +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/2d/sprite.h" + +class SpriteRegionEditor : public HBoxContainer { + + OBJ_TYPE(SpriteRegionEditor, HBoxContainer ); + + ToolButton *edit_node; +// Button *use_region; + ToolButton *b_snap_enable; + ToolButton *b_snap_grid; + TextureFrame *icon_zoom; + HSlider *zoom; + SpinBox *zoom_value; + Control *edit_draw; + + VScrollBar *vscroll; + HScrollBar *hscroll; + + Sprite *node; + EditorNode *editor; + AcceptDialog *dlg_editor; + UndoRedo* undo_redo; + + Vector2 draw_ofs; + float draw_zoom; + bool updating_scroll; + + bool use_snap; + bool snap_show_grid; + Vector2 snap_offset; + Vector2 snap_step; + + Rect2 rect; + Rect2 rect_prev; + bool drag; + bool creating; + Vector2 drag_from; + int drag_index; + + AcceptDialog *error; + + void _set_use_snap(bool p_use); + void _set_show_grid(bool p_show); + void _set_snap_off_x(float p_val); + void _set_snap_off_y(float p_val); + void _set_snap_step_x(float p_val); + void _set_snap_step_y(float p_val); + +protected: + + void _notification(int p_what); + void _node_removed(Node *p_node); + static void _bind_methods(); + + Vector2 snap_point(Vector2 p_target) const; + +public: + + void edit(); + void _edit_node(); + void _region_draw(); + void _region_input(const InputEvent &p_input); + void _scroll_changed(float); + + void edit(Node *p_sprite); + SpriteRegionEditor(EditorNode* p_editor); + +}; + +class SpriteRegionEditorPlugin : public EditorPlugin +{ + + OBJ_TYPE( SpriteRegionEditorPlugin, EditorPlugin ); + + SpriteRegionEditor *region_editor; + EditorNode *editor; +public: + + virtual String get_name() const { return "Sprite"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_node); + virtual bool handles(Object *p_node) const; + virtual void make_visible(bool p_visible); + + SpriteRegionEditorPlugin(EditorNode *p_node); +}; + +#endif // SPRITE_REGION_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/stream_editor_plugin.cpp b/tools/editor/plugins/stream_editor_plugin.cpp index 6477cce47c..81db7f2846 100644 --- a/tools/editor/plugins/stream_editor_plugin.cpp +++ b/tools/editor/plugins/stream_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/plugins/stream_editor_plugin.h b/tools/editor/plugins/stream_editor_plugin.h index d49d15b765..7378bfad0c 100644 --- a/tools/editor/plugins/stream_editor_plugin.h +++ b/tools/editor/plugins/stream_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/plugins/style_box_editor_plugin.cpp b/tools/editor/plugins/style_box_editor_plugin.cpp index 3b537fb5c4..898c69e1e0 100644 --- a/tools/editor/plugins/style_box_editor_plugin.cpp +++ b/tools/editor/plugins/style_box_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/plugins/style_box_editor_plugin.h b/tools/editor/plugins/style_box_editor_plugin.h index 87f72b3cc8..00b0871572 100644 --- a/tools/editor/plugins/style_box_editor_plugin.h +++ b/tools/editor/plugins/style_box_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/plugins/theme_editor_plugin.cpp b/tools/editor/plugins/theme_editor_plugin.cpp index ccbd923118..63ba57bfc0 100644 --- a/tools/editor/plugins/theme_editor_plugin.cpp +++ b/tools/editor/plugins/theme_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -408,7 +408,7 @@ void ThemeEditor::_theme_menu_cbk(int p_option) { if (p_option==POPUP_CREATE_TEMPLATE) { - file_dialog->set_mode(FileDialog::MODE_SAVE_FILE); + file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); file_dialog->set_current_path("custom.theme"); file_dialog->popup_centered_ratio(); return; @@ -568,6 +568,24 @@ ThemeEditor::ThemeEditor() { CheckButton *cb = memnew( CheckButton ); cb->set_text("CheckButton"); first_vb->add_child(cb ); + CheckBox *cbx = memnew( CheckBox ); + cbx->set_text("CheckBox"); + first_vb->add_child(cbx ); + + + ButtonGroup *bg = memnew( ButtonGroup ); + bg->set_v_size_flags(SIZE_EXPAND_FILL); + VBoxContainer *gbvb = memnew( VBoxContainer ); + gbvb->set_v_size_flags(SIZE_EXPAND_FILL); + CheckBox *rbx1 = memnew( CheckBox ); + rbx1->set_text("CheckBox Radio1"); + rbx1->set_pressed(true); + gbvb->add_child(rbx1); + CheckBox *rbx2 = memnew( CheckBox ); + rbx2->set_text("CheckBox Radio2"); + gbvb->add_child(rbx2); + bg->add_child(gbvb); + first_vb->add_child(bg); MenuButton* test_menu_button = memnew( MenuButton ); test_menu_button->set_text("MenuButton"); @@ -714,7 +732,7 @@ ThemeEditor::ThemeEditor() { fd_button->set_text("Open File Dialog"); panel->add_child(fd_button); - test_file_dialog = memnew( FileDialog ); + test_file_dialog = memnew( EditorFileDialog ); panel->add_child(test_file_dialog); fd_button->connect("pressed", this,"_open_file_dialog"); @@ -784,7 +802,7 @@ ThemeEditor::ThemeEditor() { add_del_dialog->get_ok()->connect("pressed", this,"_dialog_cbk"); - file_dialog = memnew( FileDialog ); + file_dialog = memnew( EditorFileDialog ); file_dialog->add_filter("*.theme ; Theme File"); add_child(file_dialog); file_dialog->connect("file_selected",this,"_save_template_cbk"); diff --git a/tools/editor/plugins/theme_editor_plugin.h b/tools/editor/plugins/theme_editor_plugin.h index 98156422ee..40c7ad8186 100644 --- a/tools/editor/plugins/theme_editor_plugin.h +++ b/tools/editor/plugins/theme_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,8 @@ #include "scene/gui/texture_frame.h" #include "scene/gui/option_button.h" #include "scene/gui/file_dialog.h" +#include "scene/gui/check_box.h" +#include "scene/gui/button_group.h" #include "tools/editor/editor_node.h" @@ -46,7 +48,7 @@ class ThemeEditor : public Control { VBoxContainer *main_vb; Ref<Theme> theme; - FileDialog *file_dialog; + EditorFileDialog *file_dialog; double time_left; diff --git a/tools/editor/plugins/tile_map_editor_plugin.cpp b/tools/editor/plugins/tile_map_editor_plugin.cpp index a25997108b..66c7a39096 100644 --- a/tools/editor/plugins/tile_map_editor_plugin.cpp +++ b/tools/editor/plugins/tile_map_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,7 +34,7 @@ #include "os/file_access.h" #include "tools/editor/editor_settings.h" #include "os/input.h" - +#include "method_bind_ext.inc" void TileMapEditor::_notification(int p_what) { @@ -42,9 +42,13 @@ void TileMapEditor::_notification(int p_what) { case NOTIFICATION_READY: { - pane_drag->connect("dragged", this,"_pane_drag"); + transpose->set_icon( get_icon("Transpose","EditorIcons")); mirror_x->set_icon( get_icon("MirrorX","EditorIcons")); mirror_y->set_icon( get_icon("MirrorY","EditorIcons")); + rotate_0->set_icon( get_icon("Rotate0","EditorIcons")); + rotate_90->set_icon( get_icon("Rotate90","EditorIcons")); + rotate_180->set_icon( get_icon("Rotate180","EditorIcons")); + rotate_270->set_icon( get_icon("Rotate270","EditorIcons")); } break; } @@ -67,34 +71,71 @@ void TileMapEditor::_canvas_mouse_exit() { } int TileMapEditor::get_selected_tile() const { - - TreeItem *item = palette->get_selected(); - if (!item) + int item = palette->get_current(); + if (item==-1) return TileMap::INVALID_CELL; - return item->get_metadata(0); + return palette->get_item_metadata(item); +} + +void TileMapEditor::set_selected_tile(int p_tile) { + for (int i = 0; i < palette->get_item_count(); i++) { + if (palette->get_item_metadata(i).operator int() == p_tile) { + palette->select(i,true); + palette->ensure_current_is_visible(); + break; + } + } } -void TileMapEditor::_set_cell(const Point2i& p_pos,int p_value,bool p_flip_h, bool p_flip_v,bool p_with_undo) { +// Wrapper to workaround five arg limit of undo/redo methods +void TileMapEditor::_set_cell_shortened(const Point2& p_pos,int p_value,bool p_flip_h, bool p_flip_v, bool p_transpose) { + ERR_FAIL_COND(!node); + node->set_cell(floor(p_pos.x), floor(p_pos.y), p_value, p_flip_h, p_flip_v, p_transpose); +} + +void TileMapEditor::_set_cell(const Point2i& p_pos,int p_value,bool p_flip_h, bool p_flip_v, bool p_transpose,bool p_with_undo) { ERR_FAIL_COND(!node); bool prev_flip_h=node->is_cell_x_flipped(p_pos.x,p_pos.y); bool prev_flip_v=node->is_cell_y_flipped(p_pos.x,p_pos.y); + bool prev_transpose=node->is_cell_transposed(p_pos.x,p_pos.y); int prev_val=node->get_cell(p_pos.x,p_pos.y); - if (p_value==prev_val && p_flip_h==prev_flip_h && p_flip_v==prev_flip_v) + if (p_value==prev_val && p_flip_h==prev_flip_h && p_flip_v==prev_flip_v && p_transpose==prev_transpose) return; //check that it's actually different if (p_with_undo) { - undo_redo->add_do_method(node,"set_cell",p_pos.x,p_pos.y,p_value,p_flip_h,p_flip_v); - undo_redo->add_undo_method(node,"set_cell",p_pos.x,p_pos.y,prev_val,prev_flip_h,prev_flip_v); + undo_redo->add_do_method(node,"set_cellv",Point2(p_pos),p_value,p_flip_h,p_flip_v,p_transpose); + undo_redo->add_undo_method(node,"set_cellv",Point2(p_pos),prev_val,prev_flip_h,prev_flip_v,prev_transpose); } else { - node->set_cell(p_pos.x,p_pos.y,p_value,p_flip_h,p_flip_v); + node->set_cell(p_pos.x,p_pos.y,p_value,p_flip_h,p_flip_v,p_transpose); + + } + +} + +void TileMapEditor::_set_display_mode(int p_mode) { + if (display_mode == p_mode) { + return; + } + switch (p_mode) { + case DISPLAY_THUMBNAIL: { + button_thumbnail->set_pressed(true); + button_list->set_pressed(false); + } break; + case DISPLAY_LIST: { + button_thumbnail->set_pressed(false); + button_list->set_pressed(true); + } break; } + display_mode = p_mode; + + _update_palette(); } void TileMapEditor::_update_palette() { @@ -102,37 +143,52 @@ void TileMapEditor::_update_palette() { if (!node) return; - palette->clear();; + palette->clear(); Ref<TileSet> tileset=node->get_tileset(); if (!tileset.is_valid()) return; - - TreeItem *root = palette->create_item(); - palette->set_hide_root(true); List<int> tiles; tileset->get_tile_list(&tiles); - for(List<int>::Element *E=tiles.front();E;E=E->next()) { + if (display_mode == DISPLAY_THUMBNAIL) { + palette->set_max_columns(0); + palette->set_icon_mode(ItemList::ICON_MODE_TOP); + } else if (display_mode == DISPLAY_LIST) { + palette->set_max_columns(1); + palette->set_icon_mode(ItemList::ICON_MODE_LEFT); + } - TreeItem *tile = palette->create_item(root); + palette->set_max_text_lines(2); + + for(List<int>::Element *E=tiles.front();E;E=E->next()) { + palette->add_item(""); - tile->set_icon_max_width(0,64); Ref<Texture> tex = tileset->tile_get_texture(E->get()); + if (tex.is_valid()) { - tile->set_icon(0,tex); Rect2 region = tileset->tile_get_region(E->get()); - if (region!=Rect2()) - tile->set_icon_region(0,region); - } else if (tileset->tile_get_name(E->get())!="") - tile->set_text(0,tileset->tile_get_name(E->get())); - else - tile->set_text(0,"#"+itos(E->get())); + if (!region.has_no_area()) { + Image data = VS::get_singleton()->texture_get_data(tex->get_rid()); + + Ref<ImageTexture> img = memnew( ImageTexture ); + img->create_from_image(data.get_rect(region)); + + palette->set_item_icon(palette->get_item_count()-1, img); + } else { + palette->set_item_icon(palette->get_item_count()-1,tex); + } + } - tile->set_metadata(0,E->get()); + if (tileset->tile_get_name(E->get())!="") { + palette->set_item_text(palette->get_item_count()-1, tileset->tile_get_name(E->get())); + } else { + palette->set_item_text(palette->get_item_count()-1, "#"+itos(E->get())); + } + palette->set_item_metadata(palette->get_item_count()-1, E->get()); } } @@ -157,6 +213,7 @@ struct _TileMapEditorCopyData { int cell; bool flip_h; bool flip_v; + bool transpose; }; bool TileMapEditor::forward_input_event(const InputEvent& p_event) { @@ -193,6 +250,7 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { tcd.cell=node->get_cell(j,i); tcd.flip_h=node->is_cell_x_flipped(j,i); tcd.flip_v=node->is_cell_y_flipped(j,i); + tcd.transpose=node->is_cell_transposed(j,i); dupdata.push_back(tcd); @@ -203,7 +261,7 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { for (List<_TileMapEditorCopyData>::Element *E=dupdata.front();E;E=E->next()) { - _set_cell(E->get().pos+ofs,E->get().cell,E->get().flip_h,E->get().flip_v,true); + _set_cell(E->get().pos+ofs,E->get().cell,E->get().flip_h,E->get().flip_v,E->get().transpose,true); } undo_redo->commit_action(); @@ -225,28 +283,29 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { canvas_item_editor->update(); return true; + } else if (mb.mod.control) { + tool=TOOL_PICKING; + set_selected_tile(node->get_cell(over_tile.x, over_tile.y)); + mirror_x->set_pressed(node->is_cell_x_flipped(over_tile.x, over_tile.y)); + mirror_y->set_pressed(node->is_cell_y_flipped(over_tile.x, over_tile.y)); + transpose->set_pressed(node->is_cell_transposed(over_tile.x, over_tile.y)); + _update_transform_buttons(); + canvas_item_editor->update(); + return true; } else { int id = get_selected_tile(); if (id!=TileMap::INVALID_CELL) { tool=TOOL_PAINTING; Point2i local =node->world_to_map((xform_inv.xform(Point2(mb.x,mb.y)))); paint_undo.clear(); - CellOp op; - op.idx = node->get_cell(local.x,local.y); - if (op.idx>=0) { - if (node->is_cell_x_flipped(local.x,local.y)) - op.xf=true; - if (node->is_cell_y_flipped(local.x,local.y)) - op.yf=true; - } - paint_undo[local]=op; - node->set_cell(local.x,local.y,id,mirror_x->is_pressed(),mirror_y->is_pressed()); + paint_undo[local]=_get_op_from_cell(local); + node->set_cell(local.x,local.y,id,mirror_x->is_pressed(),mirror_y->is_pressed(),transpose->is_pressed()); return true; } } } else { - if (tool==TOOL_PAINTING || tool == TOOL_SELECTING) { + if (tool==TOOL_PAINTING || tool == TOOL_SELECTING || tool == TOOL_PICKING) { if (tool==TOOL_PAINTING) { @@ -255,8 +314,8 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { for(Map<Point2i,CellOp>::Element *E=paint_undo.front();E;E=E->next()) { Point2i p=E->key(); - undo_redo->add_do_method(node,"set_cell",p.x,p.y,node->get_cell(p.x,p.y),node->is_cell_x_flipped(p.x,p.y),node->is_cell_y_flipped(p.x,p.y)); - undo_redo->add_undo_method(node,"set_cell",p.x,p.y,E->get().idx,E->get().xf,E->get().yf); + undo_redo->add_do_method(node,"set_cellv",Point2(p),node->get_cell(p.x,p.y),node->is_cell_x_flipped(p.x,p.y),node->is_cell_y_flipped(p.x,p.y),node->is_cell_transposed(p.x,p.y)); + undo_redo->add_undo_method(node,"set_cellv",Point2(p),E->get().idx,E->get().xf,E->get().yf,E->get().tr); } undo_redo->commit_action(); @@ -280,20 +339,12 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { tool=TOOL_ERASING; Point2i local =node->world_to_map(xform_inv.xform(Point2(mb.x,mb.y))); paint_undo.clear(); - CellOp op; - op.idx = node->get_cell(local.x,local.y); - if (op.idx>=0) { - if (node->is_cell_x_flipped(local.x,local.y)) - op.xf=true; - if (node->is_cell_y_flipped(local.x,local.y)) - op.yf=true; - } - paint_undo[local]=op; - //node->set_cell(local.x,local.y,id,mirror_x->is_pressed(),mirror_y->is_pressed()); + paint_undo[local]=_get_op_from_cell(local); + //node->set_cell(local.x,local.y,id,mirror_x->is_pressed(),mirror_y->is_pressed(),transpose->is_pressed()); //return true; _set_cell(local,TileMap::INVALID_CELL); return true; - } else { + } else if (!mb.pressed) { if (tool==TOOL_ERASING) { @@ -302,9 +353,10 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { for(Map<Point2i,CellOp>::Element *E=paint_undo.front();E;E=E->next()) { Point2i p=E->key(); - //undo_redo->add_do_method(node,"set_cell",p.x,p.y,node->get_cell(p.x,p.y),node->is_cell_x_flipped(p.x,p.y),node->is_cell_y_flipped(p.x,p.y)); - _set_cell(p,TileMap::INVALID_CELL,false,false,true); - undo_redo->add_undo_method(node,"set_cell",p.x,p.y,E->get().idx,E->get().xf,E->get().yf); + //undo_redo->add_do_method(node,"set_cell",p,node->get_cell(p.x,p.y),node->is_cell_x_flipped(p.x,p.y),node->is_cell_y_flipped(p.x,p.y),node->is_cell_transposed(p.x,p.y)); + //_set_cell(p,TileMap::INVALID_CELL,false,false,false,true); + undo_redo->add_do_method(node,"set_cellv",Point2(p),TileMap::INVALID_CELL,false,false,false); + undo_redo->add_undo_method(node,"set_cellv",Point2(p),E->get().idx,E->get().xf,E->get().yf,E->get().tr); } undo_redo->commit_action(); @@ -338,17 +390,9 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { if (!paint_undo.has(over_tile)) { - CellOp op; - op.idx = node->get_cell(over_tile.x,over_tile.y); - if (op.idx>=0) { - if (node->is_cell_x_flipped(over_tile.x,over_tile.y)) - op.xf=true; - if (node->is_cell_y_flipped(over_tile.x,over_tile.y)) - op.yf=true; - } - paint_undo[over_tile]=op; + paint_undo[over_tile]=_get_op_from_cell(over_tile); } - node->set_cell(over_tile.x,over_tile.y,id,mirror_x->is_pressed(),mirror_y->is_pressed()); + node->set_cell(over_tile.x,over_tile.y,id,mirror_x->is_pressed(),mirror_y->is_pressed(),transpose->is_pressed()); return true; } @@ -375,25 +419,26 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { return true; } + if (tool==TOOL_ERASING) { - Point2i local =over_tile; + Point2i local =over_tile; if (!paint_undo.has(over_tile)) { - - CellOp op; - op.idx = node->get_cell(over_tile.x,over_tile.y); - if (op.idx>=0) { - if (node->is_cell_x_flipped(over_tile.x,over_tile.y)) - op.xf=true; - if (node->is_cell_y_flipped(over_tile.x,over_tile.y)) - op.yf=true; - } - paint_undo[over_tile]=op; + paint_undo[over_tile]=_get_op_from_cell(over_tile); } - //node->set_cell(over_tile.x,over_tile.y,id,mirror_x->is_pressed(),mirror_y->is_pressed()); + //node->set_cell(over_tile.x,over_tile.y,id,mirror_x->is_pressed(),mirror_y->is_pressed(),transpose->is_pressed()); _set_cell(local,TileMap::INVALID_CELL); return true; } + if (tool==TOOL_PICKING) { + set_selected_tile(node->get_cell(over_tile.x, over_tile.y)); + mirror_x->set_pressed(node->is_cell_x_flipped(over_tile.x, over_tile.y)); + mirror_y->set_pressed(node->is_cell_y_flipped(over_tile.x, over_tile.y)); + transpose->set_pressed(node->is_cell_transposed(over_tile.x, over_tile.y)); + _update_transform_buttons(); + canvas_item_editor->update(); + return true; + } } break; case InputEvent::KEY: { @@ -629,19 +674,53 @@ void TileMapEditor::_canvas_draw() { Ref<Texture> t = ts->tile_get_texture(st); if (t.is_valid()) { - Vector2 from = xform.xform(ts->tile_get_texture_offset(st)+node->map_to_world(over_tile)+node->get_cell_draw_offset()); - Rect2 r = ts->tile_get_region(st); + Vector2 from = node->map_to_world(over_tile)+node->get_cell_draw_offset(); + Rect2 r = ts->tile_get_region(st); Size2 sc = xform.get_scale(); if (mirror_x->is_pressed()) sc.x*=-1.0; if (mirror_y->is_pressed()) sc.y*=-1.0; + + Rect2 rect; + if (r==Rect2()) { + rect=Rect2(from,t->get_size()); + } else { + + rect=Rect2(from,r.get_size()); + } + + + if (node->get_tile_origin()==TileMap::TILE_ORIGIN_TOP_LEFT) { + rect.pos+=ts->tile_get_texture_offset(st); + + } else if (node->get_tile_origin()==TileMap::TILE_ORIGIN_CENTER) { + rect.pos+=node->get_cell_size()/2; + Vector2 s = r.size; + + Vector2 center = (s/2) - ts->tile_get_texture_offset(st); + + + if (mirror_x->is_pressed()) + rect.pos.x-=s.x-center.x; + else + rect.pos.x-=center.x; + + if (mirror_y->is_pressed()) + rect.pos.y-=s.y-center.y; + else + rect.pos.y-=center.y; + } + + rect.pos=xform.xform(rect.pos); + rect.size*=sc; + if (r==Rect2()) { - canvas_item_editor->draw_texture_rect(t,Rect2(from,t->get_size()*sc),false,Color(1,1,1,0.5)); + canvas_item_editor->draw_texture_rect(t,rect,false,Color(1,1,1,0.5),transpose->is_pressed()); } else { - canvas_item_editor->draw_texture_rect_region(t,Rect2(from,r.get_size()*sc),r,Color(1,1,1,0.5)); + canvas_item_editor->draw_texture_rect_region(t,rect,r,Color(1,1,1,0.5),transpose->is_pressed()); } } } @@ -701,27 +780,79 @@ void TileMapEditor::_tileset_settings_changed() { canvas_item_editor->update(); } -void TileMapEditor::_pane_drag(const Point2& p_to) { - - int x = theme_panel->get_margin(MARGIN_RIGHT); - - x+=p_to.x; - if (x<10) - x=10; - if (x>300) - x=300; - theme_panel->set_margin(MARGIN_RIGHT,x); -} - void TileMapEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("_menu_option"),&TileMapEditor::_menu_option); ObjectTypeDB::bind_method(_MD("_canvas_draw"),&TileMapEditor::_canvas_draw); - ObjectTypeDB::bind_method(_MD("_pane_drag"),&TileMapEditor::_pane_drag); ObjectTypeDB::bind_method(_MD("_canvas_mouse_enter"),&TileMapEditor::_canvas_mouse_enter); ObjectTypeDB::bind_method(_MD("_canvas_mouse_exit"),&TileMapEditor::_canvas_mouse_exit); ObjectTypeDB::bind_method(_MD("_tileset_settings_changed"),&TileMapEditor::_tileset_settings_changed); + ObjectTypeDB::bind_method(_MD("_update_transform_buttons"),&TileMapEditor::_update_transform_buttons); + ObjectTypeDB::bind_method(_MD("_set_cell_shortened","pos","tile","flip_x","flip_y","transpose"),&TileMapEditor::_set_cell_shortened,DEFVAL(false),DEFVAL(false),DEFVAL(false)); + ObjectTypeDB::bind_method(_MD("_set_display_mode","mode"),&TileMapEditor::_set_display_mode); +} + +TileMapEditor::CellOp TileMapEditor::_get_op_from_cell(const Point2i& p_pos) +{ + CellOp op; + op.idx = node->get_cell(p_pos.x,p_pos.y); + if (op.idx>=0) { + if (node->is_cell_x_flipped(p_pos.x,p_pos.y)) + op.xf=true; + if (node->is_cell_y_flipped(p_pos.x,p_pos.y)) + op.yf=true; + if (node->is_cell_transposed(p_pos.x,p_pos.y)) + op.tr=true; + } + return op; +} + +void TileMapEditor::_update_transform_buttons(Object *p_button) { + //ERR_FAIL_NULL(p_button); + ToolButton *b=p_button->cast_to<ToolButton>(); + //ERR_FAIL_COND(!b); + + mirror_x->set_block_signals(true); + mirror_y->set_block_signals(true); + transpose->set_block_signals(true); + rotate_0->set_block_signals(true); + rotate_90->set_block_signals(true); + rotate_180->set_block_signals(true); + rotate_270->set_block_signals(true); + + if (b == rotate_0) { + mirror_x->set_pressed(false); + mirror_y->set_pressed(false); + transpose->set_pressed(false); + } + else if (b == rotate_90) { + mirror_x->set_pressed(true); + mirror_y->set_pressed(false); + transpose->set_pressed(true); + } + else if (b == rotate_180) { + mirror_x->set_pressed(true); + mirror_y->set_pressed(true); + transpose->set_pressed(false); + } + else if (b == rotate_270) { + mirror_x->set_pressed(false); + mirror_y->set_pressed(true); + transpose->set_pressed(true); + } + rotate_0->set_pressed(!mirror_x->is_pressed() && !mirror_y->is_pressed() && !transpose->is_pressed()); + rotate_90->set_pressed(mirror_x->is_pressed() && !mirror_y->is_pressed() && transpose->is_pressed()); + rotate_180->set_pressed(mirror_x->is_pressed() && mirror_y->is_pressed() && !transpose->is_pressed()); + rotate_270->set_pressed(!mirror_x->is_pressed() && mirror_y->is_pressed() && transpose->is_pressed()); + + mirror_x->set_block_signals(false); + mirror_y->set_block_signals(false); + transpose->set_block_signals(false); + rotate_0->set_block_signals(false); + rotate_90->set_block_signals(false); + rotate_180->set_block_signals(false); + rotate_270->set_block_signals(false); } TileMapEditor::TileMapEditor(EditorNode *p_editor) { @@ -731,37 +862,85 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { editor=p_editor; undo_redo = editor->get_undo_redo(); - theme_panel = memnew( Panel ); - theme_panel->set_anchor(MARGIN_BOTTOM,ANCHOR_END); - theme_panel->set_begin( Point2(0,26)); - theme_panel->set_end( Point2(100,0) ); - p_editor->get_viewport()->add_child(theme_panel); - theme_panel->hide(); - - palette = memnew( Tree ); - palette->set_area_as_parent_rect(4); - palette->set_margin(MARGIN_TOP,25);; - theme_panel->add_child(palette); - - pane_drag = memnew( PaneDrag ) ; - pane_drag->set_anchor(MARGIN_LEFT,ANCHOR_END); - pane_drag->set_begin(Point2(16,4)); - theme_panel->add_child(pane_drag); - - add_child( memnew( VSeparator )); - + int mw = EDITOR_DEF("tile_map/palette_min_width",80); + Control *ec = memnew( Control); + ec->set_custom_minimum_size(Size2(mw,0)); + add_child(ec); + + HBoxContainer *hb = memnew( HBoxContainer ); + add_child(hb); + hb->set_h_size_flags(SIZE_EXPAND_FILL); + hb->add_spacer(true); + + button_thumbnail = memnew( ToolButton ); + button_thumbnail->set_toggle_mode(true); + button_thumbnail->set_pressed(true); + button_thumbnail->set_icon(p_editor->get_gui_base()->get_icon("FileThumbnail","EditorIcons")); + hb->add_child(button_thumbnail); + button_thumbnail->connect("pressed", this, "_set_display_mode", varray(DISPLAY_THUMBNAIL)); + + button_list = memnew( ToolButton ); + button_list->set_toggle_mode(true); + button_list->set_pressed(false); + button_list->set_icon(p_editor->get_gui_base()->get_icon("FileList","EditorIcons")); + hb->add_child(button_list); + button_list->connect("pressed", this, "_set_display_mode", varray(DISPLAY_LIST)); + + // Add tile palette + palette = memnew( ItemList ); + palette->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(palette); + + // Add menu items + canvas_item_editor_hb = memnew( HBoxContainer ); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(canvas_item_editor_hb); + canvas_item_editor_hb->add_child( memnew( VSeparator )); + transpose = memnew( ToolButton ); + transpose->set_toggle_mode(true); + transpose->set_tooltip("Transpose"); + transpose->set_focus_mode(FOCUS_NONE); + transpose->connect("pressed", this, "_update_transform_buttons", make_binds(transpose)); + canvas_item_editor_hb->add_child(transpose); mirror_x = memnew( ToolButton ); mirror_x->set_toggle_mode(true); mirror_x->set_tooltip("Mirror X (A)"); mirror_x->set_focus_mode(FOCUS_NONE); - add_child(mirror_x); + mirror_x->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_x)); + canvas_item_editor_hb->add_child(mirror_x); mirror_y = memnew( ToolButton ); mirror_y->set_toggle_mode(true); mirror_y->set_tooltip("Mirror Y (S)"); mirror_y->set_focus_mode(FOCUS_NONE); - add_child(mirror_y); - - + mirror_y->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_y)); + canvas_item_editor_hb->add_child(mirror_y); + canvas_item_editor_hb->add_child(memnew(VSeparator)); + rotate_0 = memnew( ToolButton ); + rotate_0->set_toggle_mode(true); + rotate_0->set_tooltip("Rotate 0 degrees"); + rotate_0->set_focus_mode(FOCUS_NONE); + rotate_0->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_0)); + canvas_item_editor_hb->add_child(rotate_0); + rotate_90 = memnew( ToolButton ); + rotate_90->set_toggle_mode(true); + rotate_90->set_tooltip("Rotate 90 degrees"); + rotate_90->set_focus_mode(FOCUS_NONE); + rotate_90->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_90)); + canvas_item_editor_hb->add_child(rotate_90); + rotate_180 = memnew( ToolButton ); + rotate_180->set_toggle_mode(true); + rotate_180->set_tooltip("Rotate 180 degrees"); + rotate_180->set_focus_mode(FOCUS_NONE); + rotate_180->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_180)); + canvas_item_editor_hb->add_child(rotate_180); + rotate_270 = memnew( ToolButton ); + rotate_270->set_toggle_mode(true); + rotate_270->set_tooltip("Rotate 270 degrees"); + rotate_270->set_focus_mode(FOCUS_NONE); + rotate_270->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_270)); + canvas_item_editor_hb->add_child(rotate_270); + canvas_item_editor_hb->hide(); + + rotate_0->set_pressed(true); tool=TOOL_NONE; selection_active=false; mouse_over=false; @@ -782,12 +961,12 @@ void TileMapEditorPlugin::make_visible(bool p_visible) { if (p_visible) { tile_map_editor->show(); - tile_map_editor->theme_panel->show(); + tile_map_editor->get_canvas_item_editor_hb()->show(); } else { tile_map_editor->hide(); - tile_map_editor->theme_panel->hide(); + tile_map_editor->get_canvas_item_editor_hb()->hide(); tile_map_editor->edit(NULL); } @@ -797,7 +976,8 @@ TileMapEditorPlugin::TileMapEditorPlugin(EditorNode *p_node) { editor=p_node; tile_map_editor = memnew( TileMapEditor(p_node) ); - CanvasItemEditor::get_singleton()->add_control_to_menu_panel(tile_map_editor); + CanvasItemEditor::get_singleton()->get_palette_split()->add_child(tile_map_editor); + CanvasItemEditor::get_singleton()->get_palette_split()->move_child(tile_map_editor,0); tile_map_editor->hide(); diff --git a/tools/editor/plugins/tile_map_editor_plugin.h b/tools/editor/plugins/tile_map_editor_plugin.h index 2336507f1b..74d1573d0f 100644 --- a/tools/editor/plugins/tile_map_editor_plugin.h +++ b/tools/editor/plugins/tile_map_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,15 +34,14 @@ #include "scene/2d/tile_map.h" #include "scene/gui/tool_button.h" #include "scene/gui/button_group.h" -#include "tools/editor/pane_drag.h" /** @author Juan Linietsky <reduzio@gmail.com> */ class CanvasItemEditor; -class TileMapEditor : public HBoxContainer { +class TileMapEditor : public VBoxContainer { - OBJ_TYPE(TileMapEditor, BoxContainer ); + OBJ_TYPE(TileMapEditor, VBoxContainer ); UndoRedo *undo_redo; @@ -52,18 +51,26 @@ class TileMapEditor : public HBoxContainer { TOOL_PAINTING, TOOL_SELECTING, TOOL_ERASING, - TOOL_DUPLICATING + TOOL_DUPLICATING, + TOOL_PICKING + }; + + enum DisplayMode { + DISPLAY_THUMBNAIL, + DISPLAY_LIST }; Tool tool; Control *canvas_item_editor; - Tree *palette; + int display_mode; + ItemList *palette; + ToolButton *button_thumbnail; + ToolButton *button_list; EditorNode *editor; Panel *panel; TileMap *node; MenuButton *options; - PaneDrag *pane_drag; bool selection_active; Point2i selection_begin; @@ -72,42 +79,52 @@ class TileMapEditor : public HBoxContainer { bool mouse_over; Label *mirror_label; + ToolButton *transpose; ToolButton *mirror_x; ToolButton *mirror_y; + ToolButton *rotate_0; + ToolButton *rotate_90; + ToolButton *rotate_180; + ToolButton *rotate_270; + + HBoxContainer *canvas_item_editor_hb; struct CellOp { int idx; bool xf; bool yf; - CellOp() { idx=-1; xf=false; yf=false; } + bool tr; + CellOp() { idx=-1; xf=false; yf=false; tr=false; } }; Map<Point2i,CellOp> paint_undo; int get_selected_tile() const; + void set_selected_tile(int p_tile); + void _set_display_mode(int p_mode); void _update_palette(); - void _pane_drag(const Point2& p_to); void _canvas_draw(); void _menu_option(int p_option); - void _set_cell(const Point2i& p_pos, int p_value, bool p_flip_h=false, bool p_flip_v=false, bool p_with_undo=false); + void _set_cell(const Point2i& p_pos, int p_value, bool p_flip_h=false, bool p_flip_v=false, bool p_transpose=false, bool p_with_undo=false); + void _set_cell_shortened(const Point2& p_pos, int p_value, bool p_flip_h=false, bool p_flip_v=false, bool p_transpose=false); void _canvas_mouse_enter(); void _canvas_mouse_exit(); void _tileset_settings_changed(); -friend class TileMapEditorPlugin; - Panel *theme_panel; protected: void _notification(int p_what); void _node_removed(Node *p_node); static void _bind_methods(); + CellOp _get_op_from_cell(const Point2i& p_pos); + void _update_transform_buttons(Object *p_button=0); public: - Vector2 snap_point(const Vector2& p_point) const; + HBoxContainer *get_canvas_item_editor_hb() const { return canvas_item_editor_hb; } bool forward_input_event(const InputEvent& p_event); void edit(Node *p_tile_map); TileMapEditor(EditorNode *p_editor); @@ -120,6 +137,7 @@ class TileMapEditorPlugin : public EditorPlugin { TileMapEditor *tile_map_editor; EditorNode *editor; + public: virtual bool forward_input_event(const InputEvent& p_event) { return tile_map_editor->forward_input_event(p_event); } diff --git a/tools/editor/plugins/tile_set_editor_plugin.cpp b/tools/editor/plugins/tile_set_editor_plugin.cpp index a51caf7d54..09115472a8 100644 --- a/tools/editor/plugins/tile_set_editor_plugin.cpp +++ b/tools/editor/plugins/tile_set_editor_plugin.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,83 +37,107 @@ void TileSetEditor::edit(const Ref<TileSet>& p_tileset) { void TileSetEditor::_import_scene(Node *scene, Ref<TileSet> p_library, bool p_merge) { if (!p_merge) - p_library->clear(); + p_library->clear(); for(int i=0;i<scene->get_child_count();i++) { - Node *child = scene->get_child(i); + Node *child = scene->get_child(i); - if (!child->cast_to<Sprite>()) { - if (child->get_child_count()>0) { - child=child->get_child(0); - if (!child->cast_to<Sprite>()) { - continue; - } + if (!child->cast_to<Sprite>()) { + if (child->get_child_count()>0) { + child=child->get_child(0); + if (!child->cast_to<Sprite>()) { + continue; + } - } else - continue; + } else + continue; - } + } - Sprite *mi = child->cast_to<Sprite>(); - Ref<Texture> texture=mi->get_texture(); - if (texture.is_null()) + Sprite *mi = child->cast_to<Sprite>(); + Ref<Texture> texture=mi->get_texture(); + Ref<CanvasItemMaterial> material=mi->get_material(); + + if (texture.is_null()) continue; - int id = p_library->find_tile_by_name(mi->get_name()); - if (id<0) { - - id=p_library->get_last_unused_tile_id(); - p_library->create_tile(id); - p_library->tile_set_name(id,mi->get_name()); - } - - - p_library->tile_set_texture(id,texture); - Vector2 phys_offset; - - if (mi->is_centered()) { - Size2 s; - if (mi->is_region()) { - s=mi->get_region_rect().size; - } else { - s=texture->get_size(); - } - phys_offset+=-s/2; - } - if (mi->is_region()) { - p_library->tile_set_region(id,mi->get_region_rect()); - } - - Vector<Ref<Shape2D> >collisions; - - for(int j=0;j<mi->get_child_count();j++) { - - Node *child2 = mi->get_child(j); - if (!child2->cast_to<StaticBody2D>()) - continue; - StaticBody2D *sb = child2->cast_to<StaticBody2D>(); - if (sb->get_shape_count()==0) - continue; - Ref<Shape2D> collision=sb->get_shape(0); - if (collision.is_valid()) { - collisions.push_back(collision); + int id = p_library->find_tile_by_name(mi->get_name()); + if (id<0) { + + id=p_library->get_last_unused_tile_id(); + p_library->create_tile(id); + p_library->tile_set_name(id,mi->get_name()); + } + + + + p_library->tile_set_texture(id,texture); + p_library->tile_set_material(id,material); + + Vector2 phys_offset; + + if (mi->is_centered()) { + Size2 s; + if (mi->is_region()) { + s=mi->get_region_rect().size; + } else { + s=texture->get_size(); + } + phys_offset+=-s/2; + } + if (mi->is_region()) { + p_library->tile_set_region(id,mi->get_region_rect()); + } + + Vector<Ref<Shape2D> >collisions; + Ref<NavigationPolygon> nav_poly; + Ref<OccluderPolygon2D> occluder; + + for(int j=0;j<mi->get_child_count();j++) { + + Node *child2 = mi->get_child(j); + + if (child2->cast_to<NavigationPolygonInstance>()) + nav_poly=child2->cast_to<NavigationPolygonInstance>()->get_navigation_polygon(); + + if (child2->cast_to<LightOccluder2D>()) + occluder=child2->cast_to<LightOccluder2D>()->get_occluder_polygon(); + + if (!child2->cast_to<StaticBody2D>()) + continue; + StaticBody2D *sb = child2->cast_to<StaticBody2D>(); + int shape_count = sb->get_shape_count(); + if (shape_count==0) + continue; + for (int shape_index=0; shape_index<shape_count; ++shape_index) + { + Ref<Shape2D> collision=sb->get_shape(shape_index); + if (collision.is_valid()) { + collisions.push_back(collision); + } } - } + } - if (collisions.size()) { + if (collisions.size()) { - p_library->tile_set_shapes(id,collisions); - p_library->tile_set_shape_offset(id,-phys_offset); - } else { - p_library->tile_set_shape_offset(id,Vector2()); + p_library->tile_set_shapes(id,collisions); + p_library->tile_set_shape_offset(id,-phys_offset); + } else { + p_library->tile_set_shape_offset(id,Vector2()); + + } + + p_library->tile_set_texture_offset(id,mi->get_offset()); + p_library->tile_set_navigation_polygon(id,nav_poly); + p_library->tile_set_light_occluder(id,occluder); + p_library->tile_set_occluder_offset(id,-phys_offset); + p_library->tile_set_navigation_polygon_offset(id,-phys_offset); - } - p_library->tile_set_texture_offset(id,mi->get_offset()); } } @@ -121,23 +145,23 @@ void TileSetEditor::_menu_confirm() { switch(option) { - case MENU_OPTION_REMOVE_ITEM: { + case MENU_OPTION_REMOVE_ITEM: { - tileset->remove_tile(to_erase); - } break; - case MENU_OPTION_MERGE_FROM_SCENE: - case MENU_OPTION_CREATE_FROM_SCENE: { + tileset->remove_tile(to_erase); + } break; + case MENU_OPTION_MERGE_FROM_SCENE: + case MENU_OPTION_CREATE_FROM_SCENE: { - EditorNode *en = editor; - Node * scene = en->get_edited_scene(); - if (!scene) - break; + EditorNode *en = editor; + Node * scene = en->get_edited_scene(); + if (!scene) + break; - _import_scene(scene,tileset,option==MENU_OPTION_MERGE_FROM_SCENE); + _import_scene(scene,tileset,option==MENU_OPTION_MERGE_FROM_SCENE); - } break; + } break; } } @@ -165,11 +189,11 @@ void TileSetEditor::_menu_cbk(int p_option) { cd->set_text("Create from scene?"); cd->popup_centered(Size2(300,60)); } break; - case MENU_OPTION_MERGE_FROM_SCENE: { + case MENU_OPTION_MERGE_FROM_SCENE: { - cd->set_text("Merge from scene?"); - cd->popup_centered(Size2(300,60)); - } break; + cd->set_text("Merge from scene?"); + cd->popup_centered(Size2(300,60)); + } break; } } diff --git a/tools/editor/plugins/tile_set_editor_plugin.h b/tools/editor/plugins/tile_set_editor_plugin.h index 2531ec55bf..df82df6993 100644 --- a/tools/editor/plugins/tile_set_editor_plugin.h +++ b/tools/editor/plugins/tile_set_editor_plugin.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -56,7 +56,7 @@ class TileSetEditor : public Control { int option; void _menu_cbk(int p_option); - void _menu_confirm(); + void _menu_confirm(); static void _import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge); diff --git a/tools/editor/progress_dialog.cpp b/tools/editor/progress_dialog.cpp index ac54796c64..c8b87486c0 100644 --- a/tools/editor/progress_dialog.cpp +++ b/tools/editor/progress_dialog.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,7 +29,7 @@ #include "progress_dialog.h" #include "main/main.h" #include "message_queue.h" -#include "scene/gui/empty_control.h" + void BackgroundProgress::_add_task(const String& p_task,const String& p_label, int p_steps) { @@ -43,12 +43,12 @@ void BackgroundProgress::_add_task(const String& p_task,const String& p_label, i t.progress = memnew( ProgressBar ); t.progress->set_max(p_steps); t.progress->set_val(p_steps); - EmptyControl *ec = memnew( EmptyControl ); + Control *ec = memnew( Control ); ec->set_h_size_flags(SIZE_EXPAND_FILL); ec->set_v_size_flags(SIZE_EXPAND_FILL); t.progress->set_area_as_parent_rect(); ec->add_child(t.progress); - ec->set_minsize(Size2(80,5)); + ec->set_custom_minimum_size(Size2(80,5)); t.hb->add_child(ec); add_child(t.hb); diff --git a/tools/editor/progress_dialog.h b/tools/editor/progress_dialog.h index a9d4938efe..7f1cc4cb2d 100644 --- a/tools/editor/progress_dialog.h +++ b/tools/editor/progress_dialog.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/project_export.cpp b/tools/editor/project_export.cpp index 3a0f1e76b5..cd6dc06f75 100644 --- a/tools/editor/project_export.cpp +++ b/tools/editor/project_export.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -80,7 +80,7 @@ bool ProjectExportDialog::_create_tree(TreeItem *p_parent,EditorFileSystemDirect String path = p_dir->get_file_path(i); fitem->set_tooltip(0,path); fitem->set_metadata(0,path); - Ref<Texture> icon = get_icon( (has_icon(p_dir->get_file_type(i),"EditorIcons")?p_dir->get_file_type(i):String("Object")),"EditorIcons"); + Ref<Texture> icon = get_icon( (has_icon(p_dir->get_file_type(i),ei)?p_dir->get_file_type(i):ot),ei); fitem->set_icon(0,icon); fitem->set_cell_mode(1,TreeItem::CELL_MODE_RANGE); @@ -193,6 +193,8 @@ void ProjectExportDialog::_prop_edited(String what) { _save_export_cfg(); + _validate_platform(); + } void ProjectExportDialog::_filters_edited(String what) { @@ -252,6 +254,13 @@ void ProjectExportDialog::_script_edited(Variant v) { } +void ProjectExportDialog::_sample_convert_edited(int what) { + EditorImportExport::get_singleton()->sample_set_action( EditorImportExport::SampleAction(sample_mode->get_selected())); + EditorImportExport::get_singleton()->sample_set_max_hz( sample_max_hz->get_val() ); + EditorImportExport::get_singleton()->sample_set_trim( sample_trim->is_pressed() ); + +} + void ProjectExportDialog::_notification(int p_what) { switch(p_what) { @@ -319,6 +328,15 @@ void ProjectExportDialog::_notification(int p_what) { _update_group(); _update_group_tree(); + sample_mode->select( EditorImportExport::get_singleton()->sample_get_action() ); + sample_max_hz->set_val( EditorImportExport::get_singleton()->sample_get_max_hz() ); + sample_trim->set_pressed( EditorImportExport::get_singleton()->sample_get_trim() ); + + sample_mode->connect("item_selected",this,"_sample_convert_edited"); + sample_max_hz->connect("value_changed",this,"_sample_convert_edited"); + sample_trim->connect("toggled",this,"_sample_convert_edited"); + + } break; case NOTIFICATION_EXIT_TREE: { @@ -416,7 +434,7 @@ void ProjectExportDialog::_export_action(const String& p_file) { if (FileAccess::exists(location.plus_file("engine.cfg"))) { error->set_text("Please export outside the project folder!"); - error->popup_centered(Size2(300,70));; + error->popup_centered_minsize(); return; } String nl = (location+"/..").simplify_path(); @@ -434,7 +452,7 @@ void ProjectExportDialog::_export_action(const String& p_file) { Error err = export_platform(platform,p_file,file_export_check->is_pressed(),file_export_password->get_text(),false); if (err!=OK) { error->set_text("Error exporting project!"); - error->popup_centered(Size2(300,70));; + error->popup_centered_minsize(); } } @@ -453,7 +471,7 @@ void ProjectExportDialog::_export_action_pck(const String& p_file) { FileAccess *f = FileAccess::open(p_file,FileAccess::WRITE); if (!f) { error->set_text("Error exporting project PCK! Can't write"); - error->popup_centered(Size2(300,70));; + error->popup_centered_minsize(); } ERR_FAIL_COND(!f); @@ -462,7 +480,7 @@ void ProjectExportDialog::_export_action_pck(const String& p_file) { if (err!=OK) { error->set_text("Error exporting project!"); - error->popup_centered(Size2(300,70));; + error->popup_centered_minsize(); return; } } @@ -478,7 +496,12 @@ Error ProjectExportDialog::export_platform(const String& p_platform, const Strin Error err = exporter->export_project(p_path,p_debug); if (err!=OK) { error->set_text("Error exporting project!"); - error->popup_centered(Size2(300,70));; + error->popup_centered_minsize(); + ERR_PRINT("Exporting failed!"); + if (p_quit_after) { + OS::get_singleton()->set_exit_code(255); + get_tree()->quit(); + } return ERR_CANT_CREATE; } else { if (p_quit_after) { @@ -507,7 +530,7 @@ void ProjectExportDialog::custom_action(const String&) { if (exporter.is_null()) { error->set_text("No exporter for platform '"+platform+"' yet."); - error->popup_centered(Size2(300,70));; + error->popup_centered_minsize(); return; } @@ -558,7 +581,7 @@ void ProjectExportDialog::_update_group_list() { TreeItem *ti = groups->create_item(r); ti->set_text(0,E->get()); - ti->add_button(0,get_icon("Del","EditorIcons")); + ti->add_button(0,get_icon("Remove","EditorIcons")); if (E->get()==current) { ti->select(0); } @@ -1040,6 +1063,7 @@ void ProjectExportDialog::_bind_methods() { ObjectTypeDB::bind_method(_MD("_group_select_none"),&ProjectExportDialog::_group_select_none); ObjectTypeDB::bind_method(_MD("_script_edited"),&ProjectExportDialog::_script_edited); ObjectTypeDB::bind_method(_MD("_update_script"),&ProjectExportDialog::_update_script); + ObjectTypeDB::bind_method(_MD("_sample_convert_edited"),&ProjectExportDialog::_sample_convert_edited); ObjectTypeDB::bind_method(_MD("export_platform"),&ProjectExportDialog::export_platform); @@ -1126,7 +1150,7 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) { tree->set_column_min_width(1,90); filters = memnew( LineEdit ); - vb->add_margin_child("Filters for Non-Resources (Comma Separated):",filters); + vb->add_margin_child("Filters to export non-resource files (Comma Separated, ie: *.json, *.txt):",filters); filters->connect("text_changed",this,"_filters_edited"); @@ -1151,7 +1175,7 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) { image_shrink = memnew( SpinBox ); image_shrink->set_min(1); image_shrink->set_max(8); - image_shrink->set_step(1); + image_shrink->set_step(0.1); image_vb->add_margin_child("Shrink All Images:",image_shrink); sections->add_child(image_vb); @@ -1211,6 +1235,7 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) { group_image_action->add_item("Default"); group_image_action->add_item("Compress Disk"); group_image_action->add_item("Compress RAM"); + group_image_action->add_item("Keep Original"); group_options->add_margin_child("Compress Mode:",group_image_action); group_image_action->connect("item_selected",this,"_group_changed"); @@ -1231,7 +1256,7 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) { group_shrink->set_min(1); group_shrink->set_max(8); group_shrink->set_val(1); - group_shrink->set_step(1); + group_shrink->set_step(0.001); group_options->add_margin_child("Shrink By:",group_shrink); group_shrink->connect("value_changed",this,"_group_changed"); @@ -1240,8 +1265,8 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) { group_options->add_child(atlas_preview); atlas_preview->show(); atlas_preview->connect("pressed",this,"_group_atlas_preview"); - EmptyControl *ec = memnew(EmptyControl ); - ec->set_minsize(Size2(150,1)); + Control *ec = memnew(Control ); + ec->set_custom_minimum_size(Size2(150,1)); gvb->add_child(ec); VBoxContainer *group_vb_right = memnew( VBoxContainer ); @@ -1310,6 +1335,22 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) { hbc->add_child(button_reload); */ + + sample_vbox = memnew( VBoxContainer ); + sample_vbox->set_name("Samples"); + sections->add_child(sample_vbox); + sample_mode = memnew( OptionButton ); + sample_vbox->add_margin_child("Sample Conversion Mode: (.wav files):",sample_mode); + sample_mode->add_item("Keep"); + sample_mode->add_item("Compress (RAM - IMA-ADPCM)"); + sample_max_hz = memnew( SpinBox ); + sample_max_hz->set_max(192000); + sample_max_hz->set_min(8000); + sample_vbox->add_margin_child("Sampling Rate Limit: (hz)",sample_max_hz); + sample_trim = memnew( CheckButton ); + sample_trim->set_text("Trim"); + sample_vbox->add_margin_child("Trailing Silence:",sample_trim); + script_vbox = memnew( VBoxContainer ); script_vbox->set_name("Script"); sections->add_child(script_vbox); @@ -1337,9 +1378,9 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) { expopt="--,Export,Bundle"; - file_export = memnew( FileDialog ); + file_export = memnew( EditorFileDialog ); add_child(file_export); - file_export->set_access(FileDialog::ACCESS_FILESYSTEM); + file_export->set_access(EditorFileDialog::ACCESS_FILESYSTEM); file_export->set_current_dir( EditorSettings::get_singleton()->get("global/default_project_export_path") ); file_export->set_title("Export Project"); @@ -1356,8 +1397,8 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) { file_export_password->set_editable(false); file_export->get_vbox()->add_margin_child("Password:",file_export_password); - pck_export = memnew( FileDialog ); - pck_export->set_access(FileDialog::ACCESS_FILESYSTEM); + pck_export = memnew( EditorFileDialog ); + pck_export->set_access(EditorFileDialog::ACCESS_FILESYSTEM); pck_export->set_current_dir( EditorSettings::get_singleton()->get("global/default_project_export_path") ); pck_export->set_title("Export Project PCK"); pck_export->connect("file_selected", this,"_export_action_pck"); @@ -1367,6 +1408,8 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) { button_export = add_button("Export..",!OS::get_singleton()->get_swap_ok_cancel(),"export_pck"); updating_script=false; + ei="EditorIcons"; + ot="Object"; } @@ -1648,7 +1691,7 @@ Error ProjectExport::export_project(const String& p_preset) { if (saver.is_null()) { memdelete(d); - ERR_EXPLAIN("Preset '"+preset+"' references unexisting saver: "+type); + ERR_EXPLAIN("Preset '"+preset+"' references nonexistent saver: "+type); ERR_FAIL_COND_V(saver.is_null(),ERR_INVALID_DATA); } diff --git a/tools/editor/project_export.h b/tools/editor/project_export.h index dfe7a2d900..2f824e5ff7 100644 --- a/tools/editor/project_export.h +++ b/tools/editor/project_export.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,14 +33,14 @@ #include "scene/gui/control.h" #include "scene/gui/tree.h" #include "scene/gui/label.h" -#include "scene/gui/file_dialog.h" +#include "tools/editor/editor_file_dialog.h" #include "scene/gui/button.h" #include "scene/gui/dialogs.h" #include "scene/gui/tab_container.h" #include "os/dir_access.h" #include "os/thread.h" #include "scene/gui/option_button.h" -#include "scene/gui/empty_control.h" + #include "scene/gui/slider.h" #include "tools/editor/editor_file_system.h" #include "property_editor.h" @@ -78,10 +78,13 @@ private: HBoxContainer *plat_errors; Label *platform_error_string; + StringName ei; + StringName ot; + Tree * tree; - FileDialog *pck_export; - FileDialog *file_export; + EditorFileDialog *pck_export; + EditorFileDialog *file_export; CheckButton *file_export_check; LineEdit *file_export_password; @@ -136,6 +139,10 @@ private: OptionButton *script_mode; LineEdit *script_key; + VBoxContainer *sample_vbox; + OptionButton *sample_mode; + SpinBox *sample_max_hz; + CheckButton *sample_trim; void _export_mode_changed(int p_idx); @@ -159,6 +166,8 @@ private: void _image_export_edited(int what); void _shrink_edited(float what); + void _sample_convert_edited(int what); + void _update_group_list(); void _select_group(const String& p_by_name); diff --git a/tools/editor/project_manager.cpp b/tools/editor/project_manager.cpp index 4eadd980f4..893df04709 100644 --- a/tools/editor/project_manager.cpp +++ b/tools/editor/project_manager.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,7 +39,7 @@ #include "scene/gui/line_edit.h" #include "scene/gui/panel_container.h" -#include "scene/gui/empty_control.h" + #include "scene/gui/texture_frame.h" #include "scene/gui/margin_container.h" #include "io/resource_saver.h" @@ -65,7 +65,7 @@ class NewProjectDialog : public ConfirmationDialog { error->set_text(""); get_ok()->set_disabled(true); DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - if (d->change_dir(project_path->get_text())!=OK) { + if (project_path->get_text() != "" && d->change_dir(project_path->get_text())!=OK) { error->set_text("Invalid Path for Project, Path Must Exist!"); memdelete(d); return false; @@ -82,7 +82,7 @@ class NewProjectDialog : public ConfirmationDialog { } else { - if (!d->file_exists("engine.cfg")) { + if (project_path->get_text() != "" && !d->file_exists("engine.cfg")) { error->set_text("Invalid Project Path (engine.cfg must exist)."); memdelete(d); @@ -144,7 +144,7 @@ class NewProjectDialog : public ConfirmationDialog { fdialog->set_mode(FileDialog::MODE_OPEN_FILE); fdialog->clear_filters(); - fdialog->add_filter("engine.cfg ; "_MKSTR(VERSION_NAME)" Project"); + fdialog->add_filter("engine.cfg ; " _MKSTR(VERSION_NAME) " Project"); } else { fdialog->set_mode(FileDialog::MODE_OPEN_DIR); } @@ -193,7 +193,7 @@ class NewProjectDialog : public ConfirmationDialog { f->store_line("\n"); f->store_line("[application]"); f->store_line("name=\""+project_name->get_text()+"\""); - f->store_line("icon=\"icon.png\""); + f->store_line("icon=\"res://icon.png\""); memdelete(f); @@ -245,7 +245,8 @@ public: project_name->clear(); if (import_mode) { - set_title("Import Existing Project:"); + set_title("Import Existing Project"); + get_ok()->set_text("Import"); pp->set_text("Project Path: (Must exist)"); pn->set_text("Project Name:"); pn->hide(); @@ -254,7 +255,8 @@ public: popup_centered(Size2(500,125)); } else { - set_title("Create New Project:"); + set_title("Create New Project"); + get_ok()->set_text("Create"); pp->set_text("Project Path:"); pn->set_text("Project Name:"); pn->show(); @@ -313,7 +315,6 @@ public: l->add_color_override("font_color",Color(1,0.4,0.3,0.8)); l->set_align(Label::ALIGN_CENTER); - get_ok()->set_text("Create"); DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); project_path->set_text(d->get_current_dir()); memdelete(d); @@ -479,20 +480,25 @@ void ProjectManager::_load_recent_projects() { bool favorite = (_name.begins_with("favorite_projects/"))?true:false; uint64_t last_modified = 0; - if (FileAccess::exists(conf)) + if (FileAccess::exists(conf)) { last_modified = FileAccess::get_modified_time(conf); - String fscache = path.plus_file(".fscache"); - if (FileAccess::exists(fscache)) { - uint64_t cache_modified = FileAccess::get_modified_time(fscache); - if ( cache_modified > last_modified ) - last_modified = cache_modified; - } - ProjectItem item(project, path, conf, last_modified, favorite); - if (favorite) - favorite_projects.push_back(item); - else - projects.push_back(item); + String fscache = path.plus_file(".fscache"); + if (FileAccess::exists(fscache)) { + uint64_t cache_modified = FileAccess::get_modified_time(fscache); + if ( cache_modified > last_modified ) + last_modified = cache_modified; + } + + ProjectItem item(project, path, conf, last_modified, favorite); + if (favorite) + favorite_projects.push_back(item); + else + projects.push_back(item); + } else { + //project doesn't exist on disk but it's in the XML settings file + EditorSettings::get_singleton()->erase(_name); //remove it + } } projects.sort(); @@ -580,8 +586,8 @@ void ProjectManager::_load_recent_projects() { VBoxContainer *vb = memnew(VBoxContainer); hb->add_child(vb); - EmptyControl *ec = memnew( EmptyControl ); - ec->set_minsize(Size2(0,1)); + Control *ec = memnew( Control ); + ec->set_custom_minimum_size(Size2(0,1)); vb->add_child(ec); Label *title = memnew( Label(project_name) ); title->add_font_override("font",get_font("large","Fonts")); @@ -600,6 +606,8 @@ void ProjectManager::_load_recent_projects() { erase_btn->set_disabled(selected_list.size()<1); open_btn->set_disabled(selected_list.size()<1); run_btn->set_disabled(selected_list.size()<1 || (selected_list.size()==1 && single_selected_main=="")); + + EditorSettings::get_singleton()->save(); } void ProjectManager::_open_project_confirm() { @@ -639,7 +647,7 @@ void ProjectManager::_open_project() { if (selected_list.size()>1) { multi_open_ask->set_text("Are you sure to open more than one projects?"); - multi_open_ask->popup_centered(Size2(300,100)); + multi_open_ask->popup_centered_minsize(); } else { _open_project_confirm(); } @@ -679,7 +687,7 @@ void ProjectManager::_run_project() { if (selected_list.size()>1) { multi_run_ask->set_text("Are you sure to run more than one projects?"); - multi_run_ask->popup_centered(Size2(300,100)); + multi_run_ask->popup_centered_minsize(); } else { _run_project_confirm(); } @@ -779,7 +787,7 @@ void ProjectManager::_erase_project() { erase_ask->set_text("Erase project from list?? (Folder contents will not be modified)"); - erase_ask->popup_centered(Size2(300,100)); + erase_ask->popup_centered_minsize(); } @@ -819,6 +827,7 @@ ProjectManager::ProjectManager() { if (!EditorSettings::get_singleton()) EditorSettings::create(); + FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("file_dialog/show_hidden_files")); set_area_as_parent_rect(); Panel *panel = memnew( Panel ); @@ -837,7 +846,7 @@ ProjectManager::ProjectManager() { l->set_align(Label::ALIGN_CENTER); vb->add_child(l); l = memnew( Label ); - l->set_text("v"VERSION_MKSTRING); + l->set_text("v" VERSION_MKSTRING); //l->add_font_override("font",get_font("bold","Fonts")); l->set_align(Label::ALIGN_CENTER); vb->add_child(l); @@ -940,7 +949,7 @@ ProjectManager::ProjectManager() { String cp; cp.push_back(0xA9); cp.push_back(0); - l->set_text(cp+" 2008-2014 Juan Linietsky, Ariel Manzur."); + l->set_text(cp+" 2008-2015 Juan Linietsky, Ariel Manzur."); l->set_align(Label::ALIGN_CENTER); vb->add_child(l); diff --git a/tools/editor/project_manager.h b/tools/editor/project_manager.h index c51a885d7d..1e6ea9c1c9 100644 --- a/tools/editor/project_manager.h +++ b/tools/editor/project_manager.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/project_settings.cpp b/tools/editor/project_settings.cpp index 304fb76d1c..2fd8b37753 100644 --- a/tools/editor/project_settings.cpp +++ b/tools/editor/project_settings.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -59,6 +59,9 @@ void ProjectSettings::_notification(int p_what) { if (p_what==NOTIFICATION_ENTER_TREE) { + search_button->set_icon(get_icon("Zoom","EditorIcons")); + clear_button->set_icon(get_icon("Close","EditorIcons")); + translation_list->connect("button_pressed",this,"_translation_delete"); _update_actions(); popup_add->add_icon_item(get_icon("Keyboard","EditorIcons"),"Key",InputEvent::KEY); @@ -415,7 +418,7 @@ void ProjectSettings::_update_actions() { item->set_cell_mode(0,TreeItem::CELL_MODE_CHECK); item->set_text(0,name); item->add_button(0,get_icon("Add","EditorIcons"),1); - item->add_button(0,get_icon("Del","EditorIcons"),2); + item->add_button(0,get_icon("Remove","EditorIcons"),2); item->set_custom_bg_color(0,get_color("prop_subsection","Editor")); item->set_editable(0,true); item->set_checked(0,pi.usage&PROPERTY_USAGE_CHECKED); @@ -483,7 +486,7 @@ void ProjectSettings::_update_actions() { action->set_icon(0,get_icon("JoyAxis","EditorIcons")); } break; } - action->add_button(0,get_icon("Del","EditorIcons"),2); + action->add_button(0,get_icon("Remove","EditorIcons"),2); action->set_metadata(0,i); } } @@ -771,18 +774,47 @@ void ProjectSettings::_autoload_add() { void ProjectSettings::_autoload_delete(Object *p_item,int p_column, int p_button) { + TreeItem *ti=p_item->cast_to<TreeItem>(); String name = "autoload/"+ti->get_text(0); - undo_redo->create_action("Remove Autoload"); - undo_redo->add_do_property(Globals::get_singleton(),name,Variant()); - undo_redo->add_undo_property(Globals::get_singleton(),name,Globals::get_singleton()->get(name)); - undo_redo->add_undo_method(Globals::get_singleton(),"set_persisting",name,true); - undo_redo->add_do_method(this,"_update_autoload"); - undo_redo->add_undo_method(this,"_update_autoload"); - undo_redo->add_do_method(this,"_settings_changed"); - undo_redo->add_undo_method(this,"_settings_changed"); - undo_redo->commit_action(); + if (p_button==0) { + //delete + undo_redo->create_action("Remove Autoload"); + undo_redo->add_do_property(Globals::get_singleton(),name,Variant()); + undo_redo->add_undo_property(Globals::get_singleton(),name,Globals::get_singleton()->get(name)); + undo_redo->add_undo_method(Globals::get_singleton(),"set_persisting",name,true); + undo_redo->add_do_method(this,"_update_autoload"); + undo_redo->add_undo_method(this,"_update_autoload"); + undo_redo->add_do_method(this,"_settings_changed"); + undo_redo->add_undo_method(this,"_settings_changed"); + undo_redo->commit_action(); + } else { + + TreeItem *swap; + + if (p_button==1) { + swap=ti->get_prev(); + } else if (p_button==2) { + swap=ti->get_next(); + } + if (!swap) + return; + + String swap_name= "autoload/"+swap->get_text(0); + + undo_redo->create_action("Move Autoload"); + undo_redo->add_do_method(Globals::get_singleton(),"set_order",swap_name,Globals::get_singleton()->get_order(name)); + undo_redo->add_do_method(Globals::get_singleton(),"set_order",name,Globals::get_singleton()->get_order(swap_name)); + undo_redo->add_undo_method(Globals::get_singleton(),"set_order",swap_name,Globals::get_singleton()->get_order(swap_name)); + undo_redo->add_undo_method(Globals::get_singleton(),"set_order",name,Globals::get_singleton()->get_order(name)); + undo_redo->add_do_method(this,"_update_autoload"); + undo_redo->add_undo_method(this,"_update_autoload"); + undo_redo->add_do_method(this,"_settings_changed"); + undo_redo->add_undo_method(this,"_settings_changed"); + undo_redo->commit_action(); + + } } @@ -916,8 +948,8 @@ void ProjectSettings::_translation_res_option_changed() { ERR_FAIL_COND(!remaps.has(key)); - StringArray r = remaps[key]; - ERR_FAIL_INDEX(idx,remaps.size()); + StringArray r = remaps[key]; + ERR_FAIL_INDEX(idx,r.size()); r.set(idx,path+":"+langs[which]); remaps[key]=r; @@ -1095,6 +1127,7 @@ void ProjectSettings::_update_translations() { t2->set_editable(1,true); t2->set_metadata(1,path); int idx = langs.find(locale); + print_line("find "+locale+" at "+itos(idx)); if (idx<0) idx=0; @@ -1133,12 +1166,39 @@ void ProjectSettings::_update_autoload() { TreeItem *t = autoload_list->create_item(root); t->set_text(0,name); t->set_text(1,Globals::get_singleton()->get(pi.name)); + t->add_button(1,get_icon("MoveUp","EditorIcons"),1); + t->add_button(1,get_icon("MoveDown","EditorIcons"),2); t->add_button(1,get_icon("Del","EditorIcons"),0); } } +void ProjectSettings::_toggle_search_bar(bool p_pressed) { + + globals_editor->set_use_filter(p_pressed); + + if (p_pressed) { + + search_bar->show(); + add_prop_bar->hide(); + search_box->grab_focus(); + search_box->select_all(); + } else { + + search_bar->hide(); + add_prop_bar->show(); + } +} + +void ProjectSettings::_clear_search_box() { + + if (search_box->get_text()=="") + return; + + search_box->clear(); + globals_editor->update_tree(); +} void ProjectSettings::_bind_methods() { @@ -1180,6 +1240,9 @@ void ProjectSettings::_bind_methods() { ObjectTypeDB::bind_method(_MD("_update_autoload"),&ProjectSettings::_update_autoload); ObjectTypeDB::bind_method(_MD("_autoload_delete"),&ProjectSettings::_autoload_delete); + ObjectTypeDB::bind_method(_MD("_clear_search_box"),&ProjectSettings::_clear_search_box); + ObjectTypeDB::bind_method(_MD("_toggle_search_bar"),&ProjectSettings::_toggle_search_bar); + } ProjectSettings::ProjectSettings(EditorData *p_data) { @@ -1200,88 +1263,96 @@ ProjectSettings::ProjectSettings(EditorData *p_data) { //tab_container->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN, 15 ); //tab_container->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END, 35 ); - Control *props_base = memnew( Control ); + VBoxContainer *props_base = memnew( VBoxContainer ); + props_base->set_alignment(BoxContainer::ALIGN_BEGIN); + props_base->set_v_size_flags(Control::SIZE_EXPAND_FILL); tab_container->add_child(props_base); props_base->set_name("General"); - globals_editor = memnew( PropertyEditor ); - props_base->add_child(globals_editor); - globals_editor->set_area_as_parent_rect(); - globals_editor->hide_top_label(); - globals_editor->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN, 55 ); - globals_editor->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END, 35 ); - globals_editor->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN, 5 ); - globals_editor->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END, 5 ); - globals_editor->set_capitalize_paths(false); - globals_editor->get_scene_tree()->connect("cell_selected",this,"_item_selected"); - globals_editor->connect("property_toggled",this,"_item_checked"); - globals_editor->connect("property_edited",this,"_settings_prop_edited"); + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); + props_base->add_child(hbc); - Label *l = memnew( Label ); - props_base->add_child(l); - l->set_pos(Point2(6,5)); - l->set_text("Category:"); + search_button = memnew( ToolButton ); + search_button->set_toggle_mode(true); + search_button->set_pressed(false); + search_button->set_text("Search"); + hbc->add_child(search_button); + search_button->connect("toggled",this,"_toggle_search_bar"); + hbc->add_child( memnew( VSeparator ) ); - l = memnew( Label ); - l->set_anchor(MARGIN_LEFT,ANCHOR_RATIO); - props_base->add_child(l); - l->set_begin(Point2(0.21,5)); - l->set_text("Property:"); + add_prop_bar = memnew( HBoxContainer ); + add_prop_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hbc->add_child(add_prop_bar); - l = memnew( Label ); - l->set_anchor(MARGIN_LEFT,ANCHOR_RATIO); - props_base->add_child(l); - l->set_begin(Point2(0.51,5)); - l->set_text("Type:"); + Label *l = memnew( Label ); + add_prop_bar->add_child(l); + l->set_text("Category:"); category = memnew( LineEdit ); - props_base->add_child(category); - category->set_anchor(MARGIN_RIGHT,ANCHOR_RATIO); - category->set_begin( Point2(5,25) ); - category->set_end( Point2(0.20,26) ); + category->set_h_size_flags(Control::SIZE_EXPAND_FILL); + add_prop_bar->add_child(category); category->connect("text_entered",this,"_item_adds"); + l = memnew( Label ); + add_prop_bar->add_child(l); + l->set_text("Property:"); + property = memnew( LineEdit ); - props_base->add_child(property); - property->set_anchor(MARGIN_LEFT,ANCHOR_RATIO); - property->set_anchor(MARGIN_RIGHT,ANCHOR_RATIO); - property->set_begin( Point2(0.21,25) ); - property->set_end( Point2(0.50,26) ); + property->set_h_size_flags(Control::SIZE_EXPAND_FILL); + add_prop_bar->add_child(property); property->connect("text_entered",this,"_item_adds"); + l = memnew( Label ); + add_prop_bar->add_child(l); + l->set_text("Type:"); type = memnew( OptionButton ); - props_base->add_child(type); - type->set_anchor(MARGIN_LEFT,ANCHOR_RATIO); - type->set_anchor(MARGIN_RIGHT,ANCHOR_RATIO); - type->set_begin( Point2(0.51,25) ); - type->set_end( Point2(0.70,26) ); + type->set_h_size_flags(Control::SIZE_EXPAND_FILL); + add_prop_bar->add_child(type); type->add_item("bool"); type->add_item("int"); type->add_item("float"); type->add_item("string"); Button *add = memnew( Button ); - props_base->add_child(add); - add->set_anchor(MARGIN_LEFT,ANCHOR_RATIO); - add->set_anchor(MARGIN_RIGHT,ANCHOR_RATIO); - add->set_begin( Point2(0.71,25) ); - add->set_end( Point2(0.85,26) ); + add_prop_bar->add_child(add); add->set_text("Add"); add->connect("pressed",this,"_item_add"); Button *del = memnew( Button ); - props_base->add_child(del); - del->set_anchor(MARGIN_LEFT,ANCHOR_RATIO); - del->set_anchor(MARGIN_RIGHT,ANCHOR_END); - del->set_begin( Point2(0.86,25) ); - del->set_end( Point2(5,26) ); + add_prop_bar->add_child(del); del->set_text("Del"); del->connect("pressed",this,"_item_del"); + search_bar = memnew( HBoxContainer ); + search_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hbc->add_child(search_bar); + search_bar->hide(); + + search_box = memnew( LineEdit ); + search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); + search_bar->add_child(search_box); + + clear_button = memnew( ToolButton ); + search_bar->add_child(clear_button); + clear_button->connect("pressed",this,"_clear_search_box"); + + globals_editor = memnew( PropertyEditor ); + props_base->add_child(globals_editor); + globals_editor->hide_top_label(); + globals_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); + globals_editor->register_text_enter(search_box); + globals_editor->set_capitalize_paths(false); + globals_editor->get_scene_tree()->connect("cell_selected",this,"_item_selected"); + globals_editor->connect("property_toggled",this,"_item_checked"); + globals_editor->connect("property_edited",this,"_settings_prop_edited"); + +/* Button *save = memnew( Button ); - //props_base->add_child(save); + props_base->add_child(save); + save->set_anchor(MARGIN_LEFT,ANCHOR_END); save->set_anchor(MARGIN_RIGHT,ANCHOR_END); save->set_anchor(MARGIN_TOP,ANCHOR_END); @@ -1290,18 +1361,17 @@ ProjectSettings::ProjectSettings(EditorData *p_data) { save->set_end( Point2(10,20) ); save->set_text("Save"); save->connect("pressed",this,"_save"); +*/ + + hbc = memnew( HBoxContainer ); + props_base->add_child(hbc); popup_platform = memnew( MenuButton ); popup_platform->set_text("Copy To Platform.."); popup_platform->set_disabled(true); - props_base->add_child(popup_platform); + hbc->add_child(popup_platform); - popup_platform->set_anchor(MARGIN_LEFT,ANCHOR_BEGIN); - popup_platform->set_anchor(MARGIN_RIGHT,ANCHOR_BEGIN); - popup_platform->set_anchor(MARGIN_TOP,ANCHOR_END); - popup_platform->set_anchor(MARGIN_BOTTOM,ANCHOR_END); - popup_platform->set_begin( Point2(10,28) ); - popup_platform->set_end( Point2(150,20) ); + hbc->add_spacer(); List<StringName> ep; EditorImportExport::get_singleton()->get_export_platforms(&ep); @@ -1409,6 +1479,7 @@ ProjectSettings::ProjectSettings(EditorData *p_data) { device_input->add_child(device_index); + /* save = memnew( Button ); input_base->add_child(save); save->set_anchor(MARGIN_LEFT,ANCHOR_END); @@ -1419,7 +1490,7 @@ ProjectSettings::ProjectSettings(EditorData *p_data) { save->set_end( Point2(10,20) ); save->set_text("Save"); save->connect("pressed",this,"_save"); - +*/ setting=false; //translations @@ -1446,9 +1517,9 @@ ProjectSettings::ProjectSettings(EditorData *p_data) { translation_list->set_v_size_flags(SIZE_EXPAND_FILL); tmc->add_child(translation_list); - translation_file_open=memnew( FileDialog ); + translation_file_open=memnew( EditorFileDialog ); add_child(translation_file_open); - translation_file_open->set_mode(FileDialog::MODE_OPEN_FILE); + translation_file_open->set_mode(EditorFileDialog::MODE_OPEN_FILE); translation_file_open->connect("file_selected",this,"_translation_add"); } @@ -1473,9 +1544,9 @@ ProjectSettings::ProjectSettings(EditorData *p_data) { tmc->add_child(translation_remap); translation_remap->connect("button_pressed",this,"_translation_res_delete"); - translation_res_file_open=memnew( FileDialog ); + translation_res_file_open=memnew( EditorFileDialog ); add_child(translation_res_file_open); - translation_res_file_open->set_mode(FileDialog::MODE_OPEN_FILE); + translation_res_file_open->set_mode(EditorFileDialog::MODE_OPEN_FILE); translation_res_file_open->connect("file_selected",this,"_translation_res_add"); thb = memnew( HBoxContainer); @@ -1503,9 +1574,9 @@ ProjectSettings::ProjectSettings(EditorData *p_data) { translation_remap_options->connect("item_edited",this,"_translation_res_option_changed"); translation_remap_options->connect("button_pressed",this,"_translation_res_option_delete"); - translation_res_option_file_open=memnew( FileDialog ); + translation_res_option_file_open=memnew( EditorFileDialog ); add_child(translation_res_option_file_open); - translation_res_option_file_open->set_mode(FileDialog::MODE_OPEN_FILE); + translation_res_option_file_open->set_mode(EditorFileDialog::MODE_OPEN_FILE); translation_res_option_file_open->connect("file_selected",this,"_translation_res_option_add"); } @@ -1544,9 +1615,9 @@ ProjectSettings::ProjectSettings(EditorData *p_data) { autoload_list->set_v_size_flags(SIZE_EXPAND_FILL); avb->add_margin_child("List:",autoload_list,true); - autoload_file_open=memnew( FileDialog ); + autoload_file_open=memnew( EditorFileDialog ); add_child(autoload_file_open); - autoload_file_open->set_mode(FileDialog::MODE_OPEN_FILE); + autoload_file_open->set_mode(EditorFileDialog::MODE_OPEN_FILE); autoload_file_open->connect("file_selected",this,"_autoload_file_callback"); autoload_list->set_columns(2); diff --git a/tools/editor/project_settings.h b/tools/editor/project_settings.h index 51f49a46ff..b122609e52 100644 --- a/tools/editor/project_settings.h +++ b/tools/editor/project_settings.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -47,6 +47,12 @@ class ProjectSettings : public AcceptDialog { UndoRedo *undo_redo; PropertyEditor *globals_editor; + HBoxContainer *search_bar; + ToolButton *search_button; + LineEdit *search_box; + ToolButton *clear_button; + + HBoxContainer *add_prop_bar; ConfirmationDialog *message; LineEdit *category; LineEdit *property; @@ -67,19 +73,19 @@ class ProjectSettings : public AcceptDialog { InputEvent last_wait_for_key; - FileDialog *translation_file_open; + EditorFileDialog *translation_file_open; Tree *translation_list; Button *translation_res_option_add_button; - FileDialog *translation_res_file_open; - FileDialog *translation_res_option_file_open; + EditorFileDialog *translation_res_file_open; + EditorFileDialog *translation_res_option_file_open; Tree *translation_remap; Tree *translation_remap_options; Tree *autoload_list; - FileDialog *autoload_file_open; + EditorFileDialog *autoload_file_open; LineEdit *autoload_add_name; LineEdit *autoload_add_path; @@ -130,6 +136,9 @@ class ProjectSettings : public AcceptDialog { void _translation_res_option_changed(); void _translation_res_option_delete(Object *p_item,int p_column, int p_button); + void _toggle_search_bar(bool p_pressed); + void _clear_search_box(); + ProjectSettings(); diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp index 777694481b..31393ebcbc 100644 --- a/tools/editor/property_editor.cpp +++ b/tools/editor/property_editor.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,17 +39,22 @@ #include "editor_settings.h" #include "editor_import_export.h" #include "editor_node.h" +#include "multi_node_edit.h" +#include "array_property_edit.h" +#include "editor_help.h" +#include "scene/resources/packed_scene.h" + void CustomPropertyEditor::_notification(int p_what) { - + if (p_what==NOTIFICATION_DRAW) { - + RID ci = get_canvas_item(); - get_stylebox("panel","PopupMenu")->draw(ci,Rect2(Point2(),get_size())); + get_stylebox("panel","PopupMenu")->draw(ci,Rect2(Point2(),get_size())); /* if (v.get_type()==Variant::COLOR) { - + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2( 10,10,60, get_size().height-20 ), v ); }*/ } @@ -83,7 +88,7 @@ void CustomPropertyEditor::_menu_option(int p_which) { switch(p_which) { case OBJ_MENU_LOAD: { - file->set_mode(FileDialog::MODE_OPEN_FILE); + file->set_mode(EditorFileDialog::MODE_OPEN_FILE); List<String> extensions; String type=(hint==PROPERTY_HINT_RESOURCE_TYPE)?hint_text:String(); @@ -142,7 +147,7 @@ void CustomPropertyEditor::_menu_option(int p_which) { } String orig_type = res_orig->get_type(); - print_line("orig type: "+orig_type); + Object *inst = ObjectTypeDB::instance( orig_type ); Ref<Resource> res = Ref<Resource>( inst->cast_to<Resource>() ); @@ -187,6 +192,7 @@ void CustomPropertyEditor::_menu_option(int p_which) { ERR_FAIL_COND( inheritors_array.empty() ); + String intype=inheritors_array[p_which-TYPE_BASE_ID]; Object *obj = ObjectTypeDB::instance(intype); @@ -209,11 +215,11 @@ void CustomPropertyEditor::_menu_option(int p_which) { } Variant CustomPropertyEditor::get_variant() const { - - return v; + + return v; } String CustomPropertyEditor::get_name() const { - + return name; } @@ -231,9 +237,11 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty inheritors_array.clear(); text_edit->hide(); easing_draw->hide(); - + spinbox->hide(); + slider->hide(); + for (int i=0;i<MAX_VALUE_EDITORS;i++) { - + value_editor[i]->hide(); value_label[i]->hide(); if (i<4) @@ -241,8 +249,8 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty } for (int i=0;i<MAX_ACTION_BUTTONS;i++) { - - action_buttons[i]->hide(); + + action_buttons[i]->hide(); } for(int i=0;i<20;i++) @@ -252,11 +260,49 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty switch(type) { - + case Variant::INT: case Variant::REAL: { - if (hint==PROPERTY_HINT_ALL_FLAGS) { + if (hint==PROPERTY_HINT_RANGE) { + + int c = hint_text.get_slice_count(","); + float min=0,max=100,step=1; + if (c>=1) { + + if (!hint_text.get_slice(",",0).empty()) + min=hint_text.get_slice(",",0).to_double(); + } + if (c>=2) { + + if (!hint_text.get_slice(",",1).empty()) + max=hint_text.get_slice(",",1).to_double(); + } + + if (type==Variant::REAL && c>=3) { + + if (!hint_text.get_slice(",",2).empty()) + step= hint_text.get_slice(",",2).to_double(); + } + + if (c>=4 && hint_text.get_slice(",",3)=="slider") { + slider->set_min(min); + slider->set_max(max); + slider->set_step((type==Variant::REAL) ? step : 1); + slider->set_val(v); + slider->show(); + set_size(Size2(110,30)); + } else { + spinbox->set_min(min); + spinbox->set_max(max); + spinbox->set_step((type==Variant::REAL) ? step : 1); + spinbox->set_val(v); + spinbox->show(); + set_size(Size2(70,35)); + } + + + } else if (hint==PROPERTY_HINT_ALL_FLAGS) { uint32_t flgs = v; for(int i=0;i<2;i++) { @@ -404,7 +450,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty value_editor[3]->set_text( String::num( r.size.y) ); } break; case Variant::VECTOR3: { - + List<String> names; names.push_back("x"); names.push_back("y"); @@ -416,7 +462,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty value_editor[2]->set_text( String::num( vec.z) ); } break; case Variant::PLANE: { - + List<String> names; names.push_back("x"); names.push_back("y"); @@ -428,10 +474,10 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty value_editor[1]->set_text( String::num( plane.normal.y ) ); value_editor[2]->set_text( String::num( plane.normal.z ) ); value_editor[3]->set_text( String::num( plane.d ) ); - + } break; case Variant::QUAT: { - + List<String> names; names.push_back("x"); names.push_back("y"); @@ -446,7 +492,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty } break; case Variant::_AABB: { - + List<String> names; names.push_back("px"); names.push_back("py"); @@ -455,7 +501,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty names.push_back("sy"); names.push_back("sz"); config_value_editors(6,3,16,names); - + AABB aabb=v; value_editor[0]->set_text( String::num( aabb.pos.x ) ); value_editor[1]->set_text( String::num( aabb.pos.y ) ); @@ -463,7 +509,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty value_editor[3]->set_text( String::num( aabb.size.x ) ); value_editor[4]->set_text( String::num( aabb.size.y ) ); value_editor[5]->set_text( String::num( aabb.size.z ) ); - + } break; case Variant::MATRIX32: { @@ -484,7 +530,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty } break; case Variant::MATRIX3: { - + List<String> names; names.push_back("xx"); names.push_back("xy"); @@ -496,17 +542,17 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty names.push_back("zy"); names.push_back("zz"); config_value_editors(9,3,16,names); - + Matrix3 basis=v; for(int i=0;i<9;i++) { - + value_editor[i]->set_text( String::num( basis.elements[i/3][i%3] ) ); } - + } break; case Variant::TRANSFORM: { - - + + List<String> names; names.push_back("xx"); names.push_back("xy"); @@ -521,20 +567,20 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty names.push_back("zz"); names.push_back("zo"); config_value_editors(12,4,16,names); - + Transform tr=v; for(int i=0;i<9;i++) { - + value_editor[(i/3)*4+i%3]->set_text( String::num( tr.basis.elements[i/3][i%3] ) ); } - + value_editor[3]->set_text( String::num( tr.origin.x ) ); value_editor[7]->set_text( String::num( tr.origin.y ) ); value_editor[11]->set_text( String::num( tr.origin.z ) ); - + } break; case Variant::COLOR: { - + color_picker->show(); color_picker->set_edit_alpha(hint!=PROPERTY_HINT_COLOR_NO_ALPHA); @@ -548,13 +594,13 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty float values[4]={c.r,c.g,c.b,c.a}; for (int i=0;i<4;i++) { int y=m+i*h; - + value_editor[i]->show(); - value_label[i]->show(); + value_label[i]->show(); value_label[i]->set_pos(Point2(ofs,y)); scroll[i]->set_min(0); scroll[i]->set_max(1.0); - scroll[i]->set_page(0); + scroll[i]->set_page(0); scroll[i]->set_pos(Point2(ofs+15,y+Math::floor((h-scroll[i]->get_minimum_size().height)/2.0))); scroll[i]->set_val(values[i]); scroll[i]->set_size(Size2(120,1)); @@ -562,30 +608,30 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty value_editor[i]->set_pos(Point2(ofs+140,y)); value_editor[i]->set_size(Size2(40,h)); value_editor[i]->set_text( String::num(values[i],2 )); - + } - + value_label[0]->set_text("R"); value_label[1]->set_text("G"); value_label[2]->set_text("B"); value_label[3]->set_text("A"); - + Size2 new_size = value_editor[3]->get_pos() + value_editor[3]->get_size() + Point2(10,10); set_size( new_size ); */ - + } break; case Variant::IMAGE: { - + List<String> names; names.push_back("New"); names.push_back("Load"); names.push_back("Clear"); config_action_buttons(names); - + } break; case Variant::NODE_PATH: { - + List<String> names; names.push_back("Assign"); names.push_back("Clear"); @@ -593,7 +639,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty } break; case Variant::OBJECT: { - + if (hint!=PROPERTY_HINT_RESOURCE_TYPE) break; @@ -603,6 +649,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty if (hint_text!="") { + int idx=0; for(int i=0;i<hint_text.get_slice_count(",");i++) { @@ -612,27 +659,27 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty Set<String> valid_inheritors; valid_inheritors.insert(base); - List<String> inheritors; + List<StringName> inheritors; ObjectTypeDB::get_inheriters_from(base.strip_edges(),&inheritors); - List<String>::Element *E=inheritors.front(); + List<StringName>::Element *E=inheritors.front(); while(E) { valid_inheritors.insert(E->get()); E=E->next(); } - int idx=0; for(Set<String>::Element *E=valid_inheritors.front();E;E=E->next()) { String t = E->get(); if (!ObjectTypeDB::can_instance(t)) continue; inheritors_array.push_back(t); + int id = TYPE_BASE_ID+idx; if (has_icon(t,"EditorIcons")) { - menu->add_icon_item(get_icon(t,"EditorIcons"),"New "+t,TYPE_BASE_ID+idx); + menu->add_icon_item(get_icon(t,"EditorIcons"),"New "+t,id); } else { - menu->add_item("New "+t,TYPE_BASE_ID+idx); + menu->add_item("New "+t,id); } idx++; @@ -649,6 +696,8 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty if (!RES(v).is_null()) { + + menu->add_icon_item(get_icon("EditResource","EditorIcons"),"Edit",OBJ_MENU_EDIT); menu->add_icon_item(get_icon("Del","EditorIcons"),"Clear",OBJ_MENU_CLEAR); menu->add_icon_item(get_icon("Duplicate","EditorIcons"),"Make Unique",OBJ_MENU_MAKE_UNIQUE); @@ -657,6 +706,13 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty menu->add_separator(); menu->add_icon_item(get_icon("Reload","EditorIcons"),"Re-Import",OBJ_MENU_REIMPORT); } + /*if (r.is_valid() && r->get_path().is_resource_file()) { + menu->set_item_tooltip(1,r->get_path()); + } else if (r.is_valid()) { + menu->set_item_tooltip(1,r->get_name()+" ("+r->get_type()+")"); + }*/ + } else { + } @@ -689,40 +745,40 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty } break; case Variant::INPUT_EVENT: { - - + + } break; case Variant::DICTIONARY: { - - + + } break; case Variant::RAW_ARRAY: { - - + + } break; case Variant::INT_ARRAY: { - - + + } break; case Variant::REAL_ARRAY: { - - + + } break; case Variant::STRING_ARRAY: { - - + + } break; case Variant::VECTOR3_ARRAY: { - - + + } break; case Variant::COLOR_ARRAY: { - - + + } break; default: {} - } - + } + updating=false; return true; } @@ -730,7 +786,7 @@ bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Ty void CustomPropertyEditor::_file_selected(String p_file) { switch(type) { - + case Variant::STRING: { if (hint==PROPERTY_HINT_FILE || hint==PROPERTY_HINT_DIR) { @@ -755,7 +811,7 @@ void CustomPropertyEditor::_file_selected(String p_file) { RES res = ResourceLoader::load(p_file,type); if (res.is_null()) { error->set_text("Error loading file: Not a resource!"); - error->popup_centered(Size2(300,80)); + error->popup_centered_minsize(); break; } v=res.get_ref_ptr(); @@ -868,9 +924,9 @@ void CustomPropertyEditor::_node_path_selected(NodePath p_path) { void CustomPropertyEditor::_action_pressed(int p_which) { - if (updating) + if (updating) return; - + switch(type) { case Variant::INT: { @@ -897,11 +953,11 @@ void CustomPropertyEditor::_action_pressed(int p_which) { if (p_which==0) { if (hint==PROPERTY_HINT_FILE) - file->set_access(FileDialog::ACCESS_RESOURCES); + file->set_access(EditorFileDialog::ACCESS_RESOURCES); else - file->set_access(FileDialog::ACCESS_FILESYSTEM); + file->set_access(EditorFileDialog::ACCESS_FILESYSTEM); - file->set_mode(FileDialog::MODE_OPEN_FILE); + file->set_mode(EditorFileDialog::MODE_OPEN_FILE); file->clear_filters(); file->clear_filters(); @@ -935,10 +991,10 @@ void CustomPropertyEditor::_action_pressed(int p_which) { if (p_which==0) { if (hint==PROPERTY_HINT_DIR) - file->set_access(FileDialog::ACCESS_RESOURCES); + file->set_access(EditorFileDialog::ACCESS_RESOURCES); else - file->set_access(FileDialog::ACCESS_FILESYSTEM); - file->set_mode(FileDialog::MODE_OPEN_DIR); + file->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + file->set_mode(EditorFileDialog::MODE_OPEN_DIR); file->clear_filters(); file->popup_centered_ratio(); } else { @@ -953,7 +1009,7 @@ void CustomPropertyEditor::_action_pressed(int p_which) { } break; case Variant::NODE_PATH: { - + if (p_which==0) { @@ -964,58 +1020,60 @@ void CustomPropertyEditor::_action_pressed(int p_which) { v=NodePath(); emit_signal("variant_changed"); - } + } } break; case Variant::OBJECT: { - + if (p_which==0) { - + + ERR_FAIL_COND( inheritors_array.empty() ); String intype=inheritors_array[0]; - + + if (hint==PROPERTY_HINT_RESOURCE_TYPE) { - + Object *obj = ObjectTypeDB::instance(intype); ERR_BREAK( !obj ); Resource *res=obj->cast_to<Resource>(); ERR_BREAK( !res ); - + v=Ref<Resource>(res).get_ref_ptr(); emit_signal("variant_changed"); hide(); - + } } else if (p_which==1) { - - file->set_access(FileDialog::ACCESS_RESOURCES); - file->set_mode(FileDialog::MODE_OPEN_FILE); + + file->set_access(EditorFileDialog::ACCESS_RESOURCES); + file->set_mode(EditorFileDialog::MODE_OPEN_FILE); List<String> extensions; String type=(hint==PROPERTY_HINT_RESOURCE_TYPE)?hint_text:String(); - + ResourceLoader::get_recognized_extensions_for_type(type,&extensions); file->clear_filters(); for (List<String>::Element *E=extensions.front();E;E=E->next()) { - + file->add_filter("*."+E->get()+" ; "+E->get().to_upper() ); - + } - + file->popup_centered_ratio(); - + } else if (p_which==2) { - + RefPtr RefPtr=v; if (!RefPtr.is_null()) { emit_signal("resource_edit_request"); - hide(); + hide(); } - + } else if (p_which==3) { - - + + v=Variant(); emit_signal("variant_changed"); hide(); @@ -1058,7 +1116,7 @@ void CustomPropertyEditor::_action_pressed(int p_which) { emit_signal("variant_changed"); hide(); } - + } break; case Variant::IMAGE: { @@ -1068,8 +1126,8 @@ void CustomPropertyEditor::_action_pressed(int p_which) { } else if (p_which==1) { - file->set_access(FileDialog::ACCESS_RESOURCES); - file->set_mode(FileDialog::MODE_OPEN_FILE); + file->set_access(EditorFileDialog::ACCESS_RESOURCES); + file->set_mode(EditorFileDialog::MODE_OPEN_FILE); List<String> extensions; ImageLoader::get_recognized_extensions(&extensions); @@ -1097,16 +1155,16 @@ void CustomPropertyEditor::_action_pressed(int p_which) { } void CustomPropertyEditor::_scroll_modified(double p_value) { - - if (updating) + + if (updating) return; /* switch(type) { - + case Variant::COLOR: { - + for (int i=0;i<4;i++) { - + value_editor[i]->set_text( String::num(scroll[i]->get_val(),2) ); } Color c; @@ -1131,7 +1189,7 @@ void CustomPropertyEditor::_drag_easing(const InputEvent& p_ev) { float rel = p_ev.mouse_motion.relative_x; if (rel==0) - return; + return; bool flip=hint_text=="attenuation"; @@ -1214,7 +1272,7 @@ void CustomPropertyEditor::_text_edit_changed() { } void CustomPropertyEditor::_modified(String p_string) { - + if (updating) return; updating=true; @@ -1255,17 +1313,17 @@ void CustomPropertyEditor::_modified(String p_string) { } break; case Variant::VECTOR3: { - + Vector3 vec; vec.x=value_editor[0]->get_text().to_double(); vec.y=value_editor[1]->get_text().to_double(); vec.z=value_editor[2]->get_text().to_double(); v=vec; emit_signal("variant_changed"); - + } break; case Variant::PLANE: { - + Plane pl; pl.normal.x=value_editor[0]->get_text().to_double(); pl.normal.y=value_editor[1]->get_text().to_double(); @@ -1273,10 +1331,10 @@ void CustomPropertyEditor::_modified(String p_string) { pl.d=value_editor[3]->get_text().to_double(); v=pl; emit_signal("variant_changed"); - + } break; case Variant::QUAT: { - + Quat q; q.x=value_editor[0]->get_text().to_double(); q.y=value_editor[1]->get_text().to_double(); @@ -1284,10 +1342,10 @@ void CustomPropertyEditor::_modified(String p_string) { q.w=value_editor[3]->get_text().to_double(); v=q; emit_signal("variant_changed"); - + } break; case Variant::_AABB: { - + Vector3 pos; pos.x=value_editor[0]->get_text().to_double(); pos.y=value_editor[1]->get_text().to_double(); @@ -1296,10 +1354,10 @@ void CustomPropertyEditor::_modified(String p_string) { size.x=value_editor[3]->get_text().to_double(); size.y=value_editor[4]->get_text().to_double(); size.z=value_editor[5]->get_text().to_double(); - + v=AABB(pos,size); emit_signal("variant_changed"); - + } break; case Variant::MATRIX32: { @@ -1314,39 +1372,39 @@ void CustomPropertyEditor::_modified(String p_string) { } break; case Variant::MATRIX3: { - + Matrix3 m; for(int i=0;i<9;i++) { - + m.elements[i/3][i%3]=value_editor[i]->get_text().to_double(); } - + v=m; emit_signal("variant_changed"); - + } break; case Variant::TRANSFORM: { - + Matrix3 basis; for(int i=0;i<9;i++) { - + basis.elements[i/3][i%3]=value_editor[(i/3)*4+i%3]->get_text().to_double(); } - + Vector3 origin; origin.x=value_editor[3]->get_text().to_double(); origin.y=value_editor[7]->get_text().to_double(); origin.z=value_editor[11]->get_text().to_double(); - + v=Transform(basis,origin); emit_signal("variant_changed"); - - + + } break; case Variant::COLOR: { /* for (int i=0;i<4;i++) { - + scroll[i]->set_val( value_editor[i]->get_text().to_double() ); } Color c; @@ -1360,51 +1418,57 @@ void CustomPropertyEditor::_modified(String p_string) { */ } break; case Variant::IMAGE: { - - + + } break; case Variant::NODE_PATH: { - - - } break; + + + } break; case Variant::INPUT_EVENT: { - - + + } break; case Variant::DICTIONARY: { - - + + } break; case Variant::RAW_ARRAY: { - - + + } break; case Variant::INT_ARRAY: { - - + + } break; case Variant::REAL_ARRAY: { - - + + } break; case Variant::STRING_ARRAY: { - - + + } break; case Variant::VECTOR3_ARRAY: { - - + + } break; case Variant::COLOR_ARRAY: { - - + + } break; default: {} - } - + } + updating=false; } +void CustomPropertyEditor::_range_modified(double p_value) +{ + v=p_value; + emit_signal("variant_changed"); +} + void CustomPropertyEditor::_focus_enter() { switch(type) { case Variant::REAL: @@ -1461,10 +1525,10 @@ void CustomPropertyEditor::config_action_buttons(const List<String>& p_strings) set_size( Size2( w, m*2+(h+m)*p_strings.size() ) ); for (int i=0;i<MAX_ACTION_BUTTONS;i++) { - + if (i<p_strings.size()) { action_buttons[i]->show(); - action_buttons[i]->set_text(p_strings[i]); + action_buttons[i]->set_text(p_strings[i]); action_buttons[i]->set_pos( Point2( m, m+i*(h+m) )); action_buttons[i]->set_size( Size2( w-m*2, h ) ); action_buttons[i]->set_flat(true); @@ -1472,29 +1536,29 @@ void CustomPropertyEditor::config_action_buttons(const List<String>& p_strings) action_buttons[i]->hide(); } } - - + + } void CustomPropertyEditor::config_value_editors(int p_amount, int p_columns,int p_label_w,const List<String>& p_strings) { - + int w=80; int h=20; int m=10; - + int rows=((p_amount-1)/p_columns)+1; - + set_size( Size2( m*(1+p_columns)+(w+p_label_w)*p_columns, m*(1+rows)+h*rows ) ); - + for (int i=0;i<MAX_VALUE_EDITORS;i++) { - + int c=i%p_columns; int r=i/p_columns; - + if (i<p_amount) { value_editor[i]->show(); value_label[i]->show(); - value_label[i]->set_text(i<p_strings.size()?p_strings[i]:String("")); + value_label[i]->set_text(i<p_strings.size()?p_strings[i]:String("")); value_editor[i]->set_pos( Point2( m+p_label_w+c*(w+m+p_label_w), m+r*(h+m) )); value_editor[i]->set_size( Size2( w, h ) ); value_label[i]->set_pos( Point2( m+c*(w+m+p_label_w), m+r*(h+m) ) ); @@ -1504,16 +1568,17 @@ void CustomPropertyEditor::config_value_editors(int p_amount, int p_columns,int value_label[i]->hide(); } } - - - + + + } void CustomPropertyEditor::_bind_methods() { - + ObjectTypeDB::bind_method("_focus_enter", &CustomPropertyEditor::_focus_enter); ObjectTypeDB::bind_method("_focus_exit", &CustomPropertyEditor::_focus_exit); ObjectTypeDB::bind_method("_modified",&CustomPropertyEditor::_modified); + ObjectTypeDB::bind_method("_range_modified", &CustomPropertyEditor::_range_modified); ObjectTypeDB::bind_method("_scroll_modified",&CustomPropertyEditor::_scroll_modified); ObjectTypeDB::bind_method("_action_pressed",&CustomPropertyEditor::_action_pressed); ObjectTypeDB::bind_method("_file_selected",&CustomPropertyEditor::_file_selected); @@ -1530,13 +1595,13 @@ void CustomPropertyEditor::_bind_methods() { ADD_SIGNAL( MethodInfo("resource_edit_request") ); } CustomPropertyEditor::CustomPropertyEditor() { - - + + read_only=false; updating=false; - + for (int i=0;i<MAX_VALUE_EDITORS;i++) { - + value_editor[i]=memnew( LineEdit ); add_child( value_editor[i] ); value_label[i]=memnew( Label ); @@ -1547,17 +1612,17 @@ CustomPropertyEditor::CustomPropertyEditor() { value_editor[i]->connect("focus_enter", this, "_focus_enter"); value_editor[i]->connect("focus_exit", this, "_focus_exit"); } - + for(int i=0;i<4;i++) { - + scroll[i] = memnew( HScrollBar ); scroll[i]->hide(); scroll[i]->set_min(0); scroll[i]->set_max(1.0); - scroll[i]->set_step(0.01); + scroll[i]->set_step(0.01); add_child(scroll[i]); scroll[i]->connect("value_changed", this,"_scroll_modified"); - + } for(int i=0;i<20;i++) { @@ -1581,15 +1646,15 @@ CustomPropertyEditor::CustomPropertyEditor() { text_edit->connect("text_changed",this,"_text_edit_changed"); for (int i=0;i<MAX_ACTION_BUTTONS;i++) { - + action_buttons[i]=memnew(Button); - action_buttons[i]->hide(); + action_buttons[i]->hide(); add_child(action_buttons[i]); Vector<Variant> binds; binds.push_back(i); action_buttons[i]->connect("pressed", this,"_action_pressed",binds); } - + color_picker = memnew( ColorPicker ); add_child(color_picker); color_picker->hide(); @@ -1599,10 +1664,10 @@ CustomPropertyEditor::CustomPropertyEditor() { color_picker->connect("color_changed",this,"_color_changed"); set_as_toplevel(true); - file = memnew ( FileDialog ); + file = memnew ( EditorFileDialog ); add_child(file); file->hide(); - + file->connect("file_selected", this,"_file_selected"); file->connect("dir_selected", this,"_file_selected"); @@ -1610,17 +1675,17 @@ CustomPropertyEditor::CustomPropertyEditor() { error->set_title("Error!"); add_child(error); //error->get_cancel()->hide(); - + type_button = memnew( MenuButton ); add_child(type_button); type_button->hide(); type_button->get_popup()->connect("item_pressed", this,"_type_create_selected"); - + scene_tree = memnew( SceneTreeDialog ); add_child(scene_tree); scene_tree->connect("selected", this,"_node_path_selected"); - scene_tree->get_tree()->set_show_enabled_subscene(true); + scene_tree->get_scene_tree()->set_show_enabled_subscene(true); texture_preview = memnew( TextureFrame ); add_child( texture_preview); @@ -1637,52 +1702,182 @@ CustomPropertyEditor::CustomPropertyEditor() { menu = memnew(PopupMenu); add_child(menu); menu->connect("item_pressed",this,"_menu_option"); -} + spinbox = memnew ( SpinBox ); + add_child(spinbox); + spinbox->set_area_as_parent_rect(5); + spinbox->connect("value_changed",this,"_range_modified"); + slider = memnew ( HSlider ); + add_child(slider); + slider->set_area_as_parent_rect(5); + slider->connect("value_changed",this,"_range_modified"); +} -Node *PropertyEditor::get_instanced_node() { +bool PropertyEditor::_might_be_in_instance() { - //this sucks badly if (!obj) return NULL; Node *node = obj->cast_to<Node>(); + + Node* edited_scene =EditorNode::get_singleton()->get_edited_scene(); + + bool might_be=false; + + while(node) { + + if (node->get_scene_instance_state().is_valid()) { + might_be=true; + break; + } + if (node==edited_scene) { + if (node->get_scene_inherited_state().is_valid()) { + might_be=true; + break; + } + might_be=false; + break; + } + node=node->get_owner(); + } + + return might_be; + +} + +bool PropertyEditor::_get_instanced_node_original_property(const StringName& p_prop, Variant& value) { + + Node *node = obj->cast_to<Node>(); + if (!node) - return NULL; + return false; - if (node->get_filename()=="") - return NULL; + Node *orig=node; + + Node* edited_scene =EditorNode::get_singleton()->get_edited_scene(); + + bool found=false; + +// print_line("for prop - "+String(p_prop)); - if (!node->get_owner()) - return NULL; //scene root i guess - return node; + while(node) { + + Ref<SceneState> ss; + + if (node==edited_scene) { + ss=node->get_scene_inherited_state(); + + } else { + ss=node->get_scene_instance_state(); + } + // print_line("at - "+String(edited_scene->get_path_to(node))); + + if (ss.is_valid()) { + + NodePath np = node->get_path_to(orig); + int node_idx = ss->find_node_by_path(np); + // print_line("\t valid, nodeidx "+itos(node_idx)); + if (node_idx>=0) { + bool lfound=false; + Variant lvar; + lvar=ss->get_property_value(node_idx,p_prop,lfound); + if (lfound) { + + found=true; + value=lvar; + // print_line("\t found value "+String(value)); + } + } + } + if (node==edited_scene) { + //just in case + break; + } + node=node->get_owner(); + + } + + return found; +} + +bool PropertyEditor::_is_property_different(const Variant& p_current, const Variant& p_orig,int p_usage) { + + + { + Node *node = obj->cast_to<Node>(); + if (!node) + return false; + + Node* edited_scene =EditorNode::get_singleton()->get_edited_scene(); + bool found_state=false; + + // print_line("for prop - "+String(p_prop)); + + + while(node) { + + Ref<SceneState> ss; + + if (node==edited_scene) { + ss=node->get_scene_inherited_state(); + + } else { + ss=node->get_scene_instance_state(); + } + + if (ss.is_valid()) { + found_state=true; + } + if (node==edited_scene) { + //just in case + break; + } + node=node->get_owner(); + } + + if (!found_state) + return false; //pointless to check if we are not comparing against anything. + } + + if (p_orig.get_type()==Variant::NIL) { + + + + //special cases + if (p_current.is_zero() && p_usage&PROPERTY_USAGE_STORE_IF_NONZERO) + return false; + if (p_current.is_one() && p_usage&PROPERTY_USAGE_STORE_IF_NONONE) + return false; + } + + return bool(Variant::evaluate(Variant::OP_NOT_EQUAL,p_current,p_orig)); } TreeItem *PropertyEditor::find_item(TreeItem *p_item,const String& p_name) { - - + + if (!p_item) return NULL; - + String name = p_item->get_metadata(1); - + if (name==p_name) { - + return p_item; } - + TreeItem *c=p_item->get_children(); - + while (c) { - + TreeItem *found = find_item(c,p_name); if (found) return found; c=c->get_next(); } - + return NULL; } @@ -1705,9 +1900,9 @@ void PropertyEditor::_changed_callbacks(Object *p_changed,const String& p_prop) if (p_prop==String()) update_tree_pending=true; else { - + pending[p_prop]=p_prop; - + } } @@ -1719,7 +1914,7 @@ void PropertyEditor::update_property(const String& p_prop) { void PropertyEditor::set_item_text(TreeItem *p_item, int p_type, const String& p_name, int p_hint, const String& p_hint_text) { - + switch( p_type ) { case Variant::BOOL: { @@ -1812,7 +2007,7 @@ void PropertyEditor::set_item_text(TreeItem *p_item, int p_type, const String& p } break; case Variant::IMAGE: { - + Image img = obj->get( p_name ); if (img.empty()) p_item->set_text(1,"[Image (empty)]"); @@ -1853,14 +2048,49 @@ void PropertyEditor::set_item_text(TreeItem *p_item, int p_type, const String& p } else { p_item->set_text(1,"<"+res->get_type()+">"); }; + + + if (res.is_valid() && res->get_path().is_resource_file()) { + p_item->set_tooltip(1,res->get_path()); + } else if (res.is_valid()) { + p_item->set_tooltip(1,res->get_name()+" ("+res->get_type()+")"); + } + + + if (has_icon(res->get_type(),"EditorIcons")) { + + p_item->set_icon(0,get_icon(res->get_type(),"EditorIcons")); + } else { + + Dictionary d = p_item->get_metadata(0); + int hint=d.has("hint")?d["hint"].operator int():-1; + String hint_text=d.has("hint_text")?d["hint_text"]:""; + if (hint==PROPERTY_HINT_RESOURCE_TYPE) { + + if (has_icon(hint_text,"EditorIcons")) { + + p_item->set_icon(0,get_icon(hint_text,"EditorIcons")); + + } else { + p_item->set_icon(0,get_icon("Object","EditorIcons")); + + } + } + } + + + } + } break; default: {}; } - + } + + void PropertyEditor::_notification(int p_what) { if (p_what==NOTIFICATION_ENTER_TREE) { @@ -1869,35 +2099,48 @@ void PropertyEditor::_notification(int p_what) { } if (p_what==NOTIFICATION_EXIT_TREE) { + get_tree()->disconnect("node_removed",this,"_node_removed"); edit(NULL); } - + if (p_what==NOTIFICATION_FIXED_PROCESS) { - - + + + if (refresh_countdown>0) { + refresh_countdown-=get_fixed_process_delta_time(); + if (refresh_countdown<=0) { + TreeItem *root = tree->get_root(); + _refresh_item(root); + } + } + changing=true; - + if (update_tree_pending) { update_tree(); update_tree_pending=false; - + } else { - - const String *k=NULL; + + const String *k=NULL; while ((k=pending.next(k))) { - + TreeItem * item = find_item(tree->get_root(),*k); if (!item) continue; - - if (get_instanced_node()) { - Dictionary d = get_instanced_node()->get_instance_state(); - if (d.has(*k)) { + if (_might_be_in_instance()) { + + + Variant vorig; + Dictionary d=item->get_metadata(0); + int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0; + + + if (_get_instanced_node_original_property(*k,vorig) || usage) { Variant v = obj->get(*k); - Variant vorig = d[*k]; int found=-1; for(int i=0;i<item->get_button_count(1);i++) { @@ -1908,7 +2151,7 @@ void PropertyEditor::_notification(int p_what) { } } - bool changed = ! (v==vorig); + bool changed = _is_property_different(v,vorig,usage); if ((found!=-1)!=changed) { @@ -1925,15 +2168,15 @@ void PropertyEditor::_notification(int p_what) { } } - Dictionary d=item->get_metadata(0); + Dictionary d=item->get_metadata(0); set_item_text(item,d["type"],d["name"],d["hint"],d["hint_text"]); - } + } } - + pending.clear(); - + changing=false; - + } } @@ -1982,7 +2225,73 @@ TreeItem *PropertyEditor::get_parent_node(String p_path,HashMap<String,TreeItem* } +void PropertyEditor::_refresh_item(TreeItem *p_item) { + + if (!p_item) + return; + + String name = p_item->get_metadata(1); + + if (name!=String()) { + + if (_might_be_in_instance()) { + + Variant vorig; + Dictionary d=p_item->get_metadata(0); + int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0; + + if (_get_instanced_node_original_property(name,vorig) || usage) { + Variant v = obj->get(name); + + int found=-1; + for(int i=0;i<p_item->get_button_count(1);i++) { + + if (p_item->get_button_id(1,i)==3) { + found=i; + break; + } + } + + bool changed = _is_property_different(v,vorig,usage); + + if ((found!=-1)!=changed) { + + if (changed) { + + p_item->add_button(1,get_icon("Reload","EditorIcons"),3); + } else { + + p_item->erase_button(1,found); + } + + } + + } + + } + + Dictionary d=p_item->get_metadata(0); + set_item_text(p_item,d["type"],d["name"],d["hint"],d["hint_text"]); + } + + TreeItem *c=p_item->get_children(); + + while (c) { + + _refresh_item(c); + + c=c->get_next(); + } + +} + +void PropertyEditor::refresh() { + + if (refresh_countdown>0) + return; + refresh_countdown=EditorSettings::get_singleton()->get("property_editor/auto_refresh_interval"); +} void PropertyEditor::update_tree() { @@ -2054,6 +2363,8 @@ void PropertyEditor::update_tree() { TreeItem * current_category=NULL; + String filter = search_box ? search_box->get_text() : ""; + for (List<PropertyInfo>::Element *I=plist.front() ; I ; I=I->next()) { PropertyInfo& p = I->get(); @@ -2093,6 +2404,23 @@ void PropertyEditor::update_tree() { sep->set_selectable(1,false); sep->set_custom_bg_color(0,get_color("prop_category","Editor")); sep->set_custom_bg_color(1,get_color("prop_category","Editor")); + + if (use_doc_hints) { + StringName type=p.name; + if (!class_descr_cache.has(type)) { + + String descr; + DocData *dd=EditorHelp::get_doc_data(); + Map<String,DocData::ClassDoc>::Element *E=dd->class_list.find(type); + if (E) { + descr=E->get().brief_description; + } + class_descr_cache[type]=descr.world_wrap(80); + + } + + sep->set_tooltip(0,"Class: "+p.name+":\n\n"+class_descr_cache[type]); + } //sep->set_custom_color(0,Color(1,1,1)); @@ -2100,7 +2428,24 @@ void PropertyEditor::update_tree() { } else if ( ! (p.usage&PROPERTY_USAGE_EDITOR ) ) continue; + String name = (p.name.find("/")!=-1)?p.name.right( p.name.find_last("/")+1 ):p.name; + + if (capitalize_paths) + name = name.camelcase_to_underscore().capitalize(); + String path=p.name.left( p.name.find_last("/") ) ; + + if (use_filter && filter!="") { + + String cat = path; + + if (capitalize_paths) + cat = cat.capitalize(); + + if (cat.findn(filter)==-1 && name.findn(filter)==-1) + continue; + } + //printf("property %s\n",p.name.ascii().get_data()); TreeItem * parent = get_parent_node(path,item_path,current_category?current_category:root ); //if (parent->get_parent()==root) @@ -2122,8 +2467,6 @@ void PropertyEditor::update_tree() { TreeItem * item = tree->create_item( parent ); - String name = (p.name.find("/")!=-1)?p.name.right( p.name.find_last("/")+1 ):p.name; - if (level>0) { item->set_custom_bg_color(0,col); //item->set_custom_bg_color(1,col); @@ -2139,32 +2482,65 @@ void PropertyEditor::update_tree() { item->set_checked(0,p.usage&PROPERTY_USAGE_CHECKED); } - if (capitalize_paths) - item->set_text( 0, name.capitalize() ); - else - item->set_text( 0, name ); - + item->set_text(0, name); item->set_tooltip(0, p.name); + if (use_doc_hints) { + StringName setter; + StringName type; + if (ObjectTypeDB::get_setter_and_type_for_property(obj->get_type_name(),p.name,type,setter)) { + + String descr; + bool found=false; + Map<StringName,Map<StringName,String> >::Element *E=descr_cache.find(type); + if (E) { + + Map<StringName,String>::Element *F=E->get().find(setter); + if (F) { + found=true; + descr=F->get(); + } + } + if (!found) { + + DocData *dd=EditorHelp::get_doc_data(); + Map<String,DocData::ClassDoc>::Element *E=dd->class_list.find(type); + if (E) { + for(int i=0;i<E->get().methods.size();i++) { + if (E->get().methods[i].name==setter.operator String()) { + descr=E->get().methods[i].description.strip_edges().world_wrap(80); + } + } + } + + descr_cache[type][setter]=descr; + } + + item->set_tooltip(0, "Property: "+p.name+"\n\n"+descr); + } + } + //EditorHelp::get_doc_data(); + Dictionary d; d["name"]=p.name; d["type"]=(int)p.type; d["hint"]=(int)p.hint; d["hint_text"]=p.hint_string; - + d["usage"]=(int)p.usage; + item->set_metadata( 0, d ); item->set_metadata( 1, p.name ); if (draw_red) item->set_custom_color(0,Color(0.8,0.4,0.20)); - + if (p.name==selected_property) { item->select(1); } - + //printf("property %s type %i\n",p.name.ascii().get_data(),p.type); switch( p.type ) { @@ -2228,8 +2604,10 @@ void PropertyEditor::update_tree() { item->set_cell_mode( 1, TreeItem::CELL_MODE_RANGE ); + if (p.hint==PROPERTY_HINT_SPRITE_FRAME) { + item->set_range_config(1,0,99999,1); - if (p.hint==PROPERTY_HINT_RANGE || p.hint==PROPERTY_HINT_EXP_RANGE) { + } else if (p.hint==PROPERTY_HINT_RANGE || p.hint==PROPERTY_HINT_EXP_RANGE) { int c = p.hint_string.get_slice_count(","); float min=0,max=100,step=1; @@ -2245,8 +2623,8 @@ void PropertyEditor::update_tree() { if (p.type==Variant::REAL && c>=3) { step= p.hint_string.get_slice(",",2).to_double(); - } - + } + item->set_range_config(1,min,max,step,p.hint==PROPERTY_HINT_EXP_RANGE); } else if (p.hint==PROPERTY_HINT_ENUM) { @@ -2261,7 +2639,7 @@ void PropertyEditor::update_tree() { } else { if (p.type == Variant::REAL) { - item->set_range_config(1, -65536, 65535, 0.01); + item->set_range_config(1, -65536, 65535, 0.001); } else { item->set_range_config(1, -65536, 65535, 1); @@ -2330,11 +2708,32 @@ void PropertyEditor::update_tree() { } } break; + case Variant::ARRAY: { + + item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM ); + item->add_button(1,get_icon("EditResource","EditorIcons")); + + + Variant v = obj->get(p.name); + if (v.is_array()) + item->set_text(1,"Array["+itos(v.call("size"))+"]"); + else + item->set_text(1,"Array[]"); + item->set_icon( 0, get_icon("ArrayData","EditorIcons") ); + + + } break; + case Variant::INT_ARRAY: { item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM ); - item->set_editable( 1, !read_only ); - item->set_text(1,"[IntArray]"); + item->add_button(1,get_icon("EditResource","EditorIcons")); + + Variant v = obj->get(p.name); + if (v.is_array()) + item->set_text(1,"IntArray["+itos(v.call("size"))+"]"); + else + item->set_text(1,"IntArray[]"); item->set_icon( 0, get_icon("ArrayInt","EditorIcons") ); @@ -2342,26 +2741,86 @@ void PropertyEditor::update_tree() { case Variant::REAL_ARRAY: { item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM ); - item->set_editable( 1, !read_only ); - item->set_text(1,"[RealArray]"); + item->add_button(1,get_icon("EditResource","EditorIcons")); + + Variant v = obj->get(p.name); + if (v.is_array()) + item->set_text(1,"FloatArray["+itos(v.call("size"))+"]"); + else + item->set_text(1,"FloatArray[]"); item->set_icon( 0, get_icon("ArrayReal","EditorIcons") ); + } break; case Variant::STRING_ARRAY: { item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM ); - item->set_editable( 1, !read_only ); - item->set_text(1,"[StringArray]"); + item->add_button(1,get_icon("EditResource","EditorIcons")); + + Variant v = obj->get(p.name); + if (v.is_array()) + item->set_text(1,"String["+itos(v.call("size"))+"]"); + else + item->set_text(1,"String[]"); item->set_icon( 0, get_icon("ArrayString","EditorIcons") ); + } break; case Variant::RAW_ARRAY: { item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM ); - item->set_editable( 1, !read_only ); - item->set_text(1,"[Raw Data]"); + item->add_button(1,get_icon("EditResource","EditorIcons")); + + Variant v = obj->get(p.name); + if (v.is_array()) + item->set_text(1,"Byte["+itos(v.call("size"))+"]"); + else + item->set_text(1,"Byte[]"); item->set_icon( 0, get_icon("ArrayData","EditorIcons") ); + + } break; + case Variant::VECTOR2_ARRAY: { + + item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM ); + item->add_button(1,get_icon("EditResource","EditorIcons")); + + Variant v = obj->get(p.name); + if (v.is_array()) + item->set_text(1,"Vector2["+itos(v.call("size"))+"]"); + else + item->set_text(1,"Vector2[]"); + item->set_icon( 0, get_icon("Vector2","EditorIcons") ); + + + } break; + case Variant::VECTOR3_ARRAY: { + + item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM ); + item->add_button(1,get_icon("EditResource","EditorIcons")); + + Variant v = obj->get(p.name); + if (v.is_array()) + item->set_text(1,"Vector3["+itos(v.call("size"))+"]"); + else + item->set_text(1,"Vector3[]"); + item->set_icon( 0, get_icon("Vector","EditorIcons") ); + + + } break; + case Variant::COLOR_ARRAY: { + + item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM ); + item->add_button(1,get_icon("EditResource","EditorIcons")); + + Variant v = obj->get(p.name); + if (v.is_array()) + item->set_text(1,"Color["+itos(v.call("size"))+"]"); + else + item->set_text(1,"Color[]"); + item->set_icon( 0, get_icon("Color","EditorIcons") ); + + } break; case Variant::VECTOR2: { @@ -2429,7 +2888,7 @@ void PropertyEditor::update_tree() { } break; case Variant::IMAGE: { - + item->set_cell_mode( 1, TreeItem::CELL_MODE_CUSTOM ); item->set_editable( 1, !read_only ); Image img = obj->get( p.name ); @@ -2438,7 +2897,7 @@ void PropertyEditor::update_tree() { else item->set_text(1,"[Image "+itos(img.get_width())+"x"+itos(img.get_height())+"]"); item->set_icon( 0,get_icon("Image","EditorIcons") ); - + } break; case Variant::NODE_PATH: { @@ -2453,7 +2912,10 @@ void PropertyEditor::update_tree() { item->set_editable( 1, !read_only ); item->add_button(1,get_icon("EditResource","EditorIcons")); String type; + if (p.hint==PROPERTY_HINT_RESOURCE_TYPE) + type=p.hint_string; bool notnil=false; + if (obj->get( p.name ).get_type() == Variant::NIL || obj->get( p.name ).operator RefPtr().is_null()) { item->set_text(1,"<null>"); @@ -2477,12 +2939,25 @@ void PropertyEditor::update_tree() { }; notnil=true; + if (has_icon(res->get_type(),"EditorIcons")) { + type=res->get_type(); + } + + if (res.is_valid() && res->get_path().is_resource_file()) { + item->set_tooltip(1,res->get_path()); + } else if (res.is_valid()) { + item->set_tooltip(1,res->get_name()+" ("+res->get_type()+")"); + } + } - if (p.hint==PROPERTY_HINT_RESOURCE_TYPE) { + + if (type!=String()) { + if (type.find(",")!=-1) + type=type.get_slice(",",0); //printf("prop %s , type %s\n",p.name.ascii().get_data(),p.hint_string.ascii().get_data()); - if (has_icon(p.hint_string,"EditorIcons")) - item->set_icon( 0, get_icon(p.hint_string,"EditorIcons") ); + if (has_icon(type,"EditorIcons")) + item->set_icon( 0, get_icon(type,"EditorIcons") ); else item->set_icon( 0, get_icon("Object","EditorIcons") ); } @@ -2495,17 +2970,25 @@ void PropertyEditor::update_tree() { if (keying) { - item->add_button(1,get_icon("Key","EditorIcons"),2); + if (p.hint==PROPERTY_HINT_SPRITE_FRAME) { + + item->add_button(1,get_icon("KeyNext","EditorIcons"),5); + } else { + item->add_button(1,get_icon("Key","EditorIcons"),2); + } } - if (get_instanced_node()) { + if (_might_be_in_instance()) { - Dictionary d = get_instanced_node()->get_instance_state(); - if (d.has(p.name)) { + Variant vorig; + Dictionary d=item->get_metadata(0); + int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0; + if (_get_instanced_node_original_property(p.name,vorig) || usage) { Variant v = obj->get(p.name); - Variant vorig = d[p.name]; - if (! (v==vorig)) { + + if (_is_property_different(v,vorig,usage)) { + //print_line("FOR "+String(p.name)+" RELOAD WITH: "+String(v)+"("+Variant::get_type_name(v.get_type())+")=="+String(vorig)+"("+Variant::get_type_name(vorig.get_type())+")"); item->add_button(1,get_icon("Reload","EditorIcons"),3); } } @@ -2536,7 +3019,7 @@ void PropertyEditor::_edit_set(const String& p_name, const Variant& p_value) { } } - if (!undo_redo) { + if (!undo_redo || obj->cast_to<MultiNodeEdit>() || obj->cast_to<ArrayPropertyEdit>()) { //kind of hacky obj->set(p_name,p_value); _changed_callbacks(obj,p_name); @@ -2568,10 +3051,10 @@ void PropertyEditor::_edit_set(const String& p_name, const Variant& p_value) { void PropertyEditor::_item_edited() { - - TreeItem * item = tree->get_edited(); + + TreeItem * item = tree->get_edited(); Dictionary d = item->get_metadata(0); - + String name=d["name"]; if (tree->get_edited_column()==0) { @@ -2600,12 +3083,12 @@ void PropertyEditor::_item_edited() { int hint= d["hint"]; String hint_text=d["hint_text"]; switch(type) { - + case Variant::NIL: { - - } break; + + } break; case Variant::BOOL: { - + _edit_set(name,item->is_checked(1)); } break; case Variant::INT: @@ -2624,7 +3107,7 @@ void PropertyEditor::_item_edited() { _edit_set(name,item->get_range(1)); } break; case Variant::STRING: { - + if (hint==PROPERTY_HINT_ENUM) { int idx= item->get_range(1); @@ -2642,64 +3125,64 @@ void PropertyEditor::_item_edited() { } } break; // math types - + case Variant::VECTOR3: { - + } break; case Variant::PLANE: { - + } break; case Variant::QUAT: { - + } break; case Variant::_AABB: { - + } break; case Variant::MATRIX3: { - + } break; case Variant::TRANSFORM: { - + } break; - + case Variant::COLOR: { //_edit_set(name,item->get_custom_bg_color(0)); } break; case Variant::IMAGE: { - + } break; case Variant::NODE_PATH: { - + } break; case Variant::INPUT_EVENT: { - + } break; case Variant::DICTIONARY: { - + } break; - + // arrays case Variant::RAW_ARRAY: { - + } break; case Variant::INT_ARRAY: { - + } break; case Variant::REAL_ARRAY: { - + } break; case Variant::STRING_ARRAY: { - + } break; case Variant::VECTOR3_ARRAY: { - + } break; case Variant::COLOR_ARRAY: { - + } break; - - + + }; } @@ -2750,24 +3233,34 @@ void PropertyEditor::_custom_editor_request(bool p_arrow) { } void PropertyEditor::edit(Object* p_object) { - + if (obj==p_object) return; if (obj) { - + obj->remove_change_receptor(this); } obj=p_object; update_tree(); - + if (obj) { - + obj->add_change_receptor(this); } - - + + +} + +void PropertyEditor::_set_range_def(Object *p_item, String prop,float p_frame) { + + TreeItem *ti = p_item->cast_to<TreeItem>(); + ERR_FAIL_COND(!ti); + + ti->call_deferred("set_range",1, p_frame); + obj->call_deferred("set",prop,p_frame); + } void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) { @@ -2781,20 +3274,29 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) { if (!d.has("name")) return; String prop=d["name"]; - emit_signal("property_keyed",prop,obj->get(prop)); + emit_signal("property_keyed",prop,obj->get(prop),false); + } else if (p_button==5) { + print_line("PB5"); + if (!d.has("name")) + return; + String prop=d["name"]; + emit_signal("property_keyed",prop,obj->get(prop),true); + //set_range(p_column, ti->get_range(p_column)+1.0 ); + call_deferred("_set_range_def",ti,prop,ti->get_range(p_column)+1.0); } else if (p_button==3) { - if (!get_instanced_node()) + if (!_might_be_in_instance()) return; if (!d.has("name")) return; String prop=d["name"]; - Dictionary d2 = get_instanced_node()->get_instance_state(); - if (d2.has(prop)) { + Variant vorig; + + if (_get_instanced_node_original_property(prop,vorig)) { - _edit_set(prop,d2[prop]); + _edit_set(prop,vorig); } } else { @@ -2838,15 +3340,28 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) { emit_signal("resource_selected",r,n); } + } else if (t==Variant::ARRAY || t==Variant::INT_ARRAY || t==Variant::REAL_ARRAY || t==Variant::STRING_ARRAY || t==Variant::VECTOR2_ARRAY || t==Variant::VECTOR3_ARRAY || t==Variant::COLOR_ARRAY || t==Variant::RAW_ARRAY) { + + Variant v = obj->get(n); + + if (v.get_type()!=t) { + Variant::CallError ce; + v=Variant::construct(Variant::Type(t),NULL,0,ce); + } + + Ref<ArrayPropertyEdit> ape = memnew( ArrayPropertyEdit ); + ape->edit(obj,n,Variant::Type(t)); + + EditorNode::get_singleton()->push_item(ape.ptr()); } } } void PropertyEditor::_node_removed(Node *p_node) { - + if (p_node==obj) { - + edit(NULL); } } @@ -2901,17 +3416,24 @@ void PropertyEditor::_draw_flags(Object *t,const Rect2& p_rect) { } +void PropertyEditor::_filter_changed(const String& p_text) { + + update_tree(); +} + void PropertyEditor::_bind_methods() { ObjectTypeDB::bind_method( "_item_edited",&PropertyEditor::_item_edited); ObjectTypeDB::bind_method( "_item_selected",&PropertyEditor::_item_selected); ObjectTypeDB::bind_method( "_custom_editor_request",&PropertyEditor::_custom_editor_request); - ObjectTypeDB::bind_method( "_custom_editor_edited",&PropertyEditor::_custom_editor_edited); - ObjectTypeDB::bind_method( "_resource_edit_request",&PropertyEditor::_resource_edit_request); - ObjectTypeDB::bind_method( "_node_removed",&PropertyEditor::_node_removed); + ObjectTypeDB::bind_method( "_custom_editor_edited",&PropertyEditor::_custom_editor_edited); + ObjectTypeDB::bind_method( "_resource_edit_request",&PropertyEditor::_resource_edit_request); + ObjectTypeDB::bind_method( "_node_removed",&PropertyEditor::_node_removed); ObjectTypeDB::bind_method( "_edit_button",&PropertyEditor::_edit_button); ObjectTypeDB::bind_method( "_changed_callback",&PropertyEditor::_changed_callbacks); ObjectTypeDB::bind_method( "_draw_flags",&PropertyEditor::_draw_flags); + ObjectTypeDB::bind_method( "_set_range_def",&PropertyEditor::_set_range_def); + ObjectTypeDB::bind_method( "_filter_changed",&PropertyEditor::_filter_changed); ADD_SIGNAL( MethodInfo("property_toggled",PropertyInfo( Variant::STRING, "property"),PropertyInfo( Variant::BOOL, "value"))); ADD_SIGNAL( MethodInfo("resource_selected", PropertyInfo( Variant::OBJECT, "res"),PropertyInfo( Variant::STRING, "prop") ) ); @@ -2966,30 +3488,50 @@ void PropertyEditor::set_show_categories(bool p_show) { update_tree(); } +void PropertyEditor::set_use_filter(bool p_use) { + + if (p_use==use_filter) + return; + + use_filter=p_use; + update_tree(); +} + +void PropertyEditor::register_text_enter(Node* p_line_edit) { + + ERR_FAIL_NULL(p_line_edit); + search_box=p_line_edit->cast_to<LineEdit>(); + + if (search_box) + search_box->connect("text_changed",this,"_filter_changed"); + +} + PropertyEditor::PropertyEditor() { - + _prop_edited="property_edited"; _prop_edited_name.push_back(String()); undo_redo=NULL; obj=NULL; + search_box=NULL; changing=false; update_tree_pending=false; - + top_label = memnew( Label ); top_label->set_text("Properties:"); top_label->set_anchor( MARGIN_RIGHT, ANCHOR_END ); top_label->set_begin( Point2( 10,0) ); - top_label->set_end( Point2( 0,12) ); - - add_child(top_label); + top_label->set_end( Point2( 0,12) ); + + add_child(top_label); + - tree = memnew( Tree ); tree->set_anchor( MARGIN_RIGHT, ANCHOR_END ); - tree->set_anchor( MARGIN_BOTTOM, ANCHOR_END ); + tree->set_anchor( MARGIN_BOTTOM, ANCHOR_END ); tree->set_begin( Point2(0,19 )); tree->set_end( Point2(0,0 )); - + tree->set_columns(2); tree->set_column_expand(0,true); tree->set_column_min_width(0,30); @@ -2997,21 +3539,22 @@ PropertyEditor::PropertyEditor() { tree->set_column_min_width(1,18); //tree->set_hide_root(true); - add_child( tree ); - + add_child( tree ); + tree->connect("item_edited", this,"_item_edited",varray(),CONNECT_DEFERRED); tree->connect("cell_selected", this,"_item_selected"); - + set_fixed_process(true); - + custom_editor = memnew( CustomPropertyEditor ); add_child(custom_editor); - + tree->connect("custom_popup_edited", this,"_custom_editor_request"); tree->connect("button_pressed", this,"_edit_button"); custom_editor->connect("variant_changed", this,"_custom_editor_edited"); custom_editor->connect("resource_edit_request", this,"_resource_edit_request",make_binds(),CONNECT_DEFERRED); - + tree->set_hide_folding(true); + capitalize_paths=true; autoclear=false; tree->set_column_title(0,"Property"); @@ -3021,7 +3564,11 @@ PropertyEditor::PropertyEditor() { keying=false; read_only=false; show_categories=false; - + refresh_countdown=0; + use_doc_hints=false; + + use_filter=false; + } diff --git a/tools/editor/property_editor.h b/tools/editor/property_editor.h index 08435ad75d..5fb8386b1b 100644 --- a/tools/editor/property_editor.h +++ b/tools/editor/property_editor.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,7 +32,7 @@ #include "scene/gui/tree.h" #include "scene/gui/button.h" #include "scene/gui/label.h" -#include "scene/gui/file_dialog.h" +#include "tools/editor/editor_file_dialog.h" #include "scene/gui/dialogs.h" #include "scene/gui/color_picker.h" #include "scene/gui/menu_button.h" @@ -46,9 +46,9 @@ */ class CustomPropertyEditor : public Popup { - + OBJ_TYPE( CustomPropertyEditor, Popup ); - + enum { MAX_VALUE_EDITORS=12, MAX_ACTION_BUTTONS=5, @@ -75,7 +75,7 @@ class CustomPropertyEditor : public Popup { PopupMenu *menu; SceneTreeDialog *scene_tree; - FileDialog *file; + EditorFileDialog *file; ConfirmationDialog *error; String name; Variant::Type type; @@ -93,6 +93,8 @@ class CustomPropertyEditor : public Popup { TextEdit *text_edit; bool read_only; Button *checks20[20]; + SpinBox *spinbox; + HSlider *slider; Control *easing_draw; @@ -105,6 +107,7 @@ class CustomPropertyEditor : public Popup { void _file_selected(String p_file); void _scroll_modified(double p_value); void _modified(String p_string); + void _range_modified(double p_value); void _focus_enter(); void _focus_exit(); void _action_pressed(int p_which); @@ -113,36 +116,38 @@ class CustomPropertyEditor : public Popup { void _color_changed(const Color& p_color); void _draw_easing(); void _menu_option(int p_which); - + void _drag_easing(const InputEvent& p_ev); void _node_path_selected(NodePath p_path); void show_value_editors(int p_amount); void config_value_editors(int p_amount, int p_columns,int p_label_w,const List<String>& p_strings); void config_action_buttons(const List<String>& p_strings); + protected: void _notification(int p_what); - static void _bind_methods(); - -public: + static void _bind_methods(); + +public: Variant get_variant() const; String get_name() const; - + void set_read_only(bool p_read_only) { read_only=p_read_only; } bool edit(Object* p_owner,const String& p_name,Variant::Type p_type, const Variant& p_variant,int p_hint,String p_hint_text); - + CustomPropertyEditor(); }; class PropertyEditor : public Control { - + OBJ_TYPE( PropertyEditor, Control ); - + Tree *tree; Label *top_label; //Object *object; + LineEdit *search_box; Object* obj; @@ -156,39 +161,54 @@ class PropertyEditor : public Control { bool keying; bool read_only; bool show_categories; + float refresh_countdown; + bool use_doc_hints; + + bool use_filter; HashMap<String,String> pending; String selected_property; + + Map<StringName,Map<StringName,String> > descr_cache; + Map<StringName,String > class_descr_cache; CustomPropertyEditor *custom_editor; - + void _resource_edit_request(); void _custom_editor_edited(); void _custom_editor_request(bool p_arrow); - + void _item_selected(); void _item_edited(); TreeItem *get_parent_node(String p_path,HashMap<String,TreeItem*>& item_paths,TreeItem *root); - + void set_item_text(TreeItem *p_item, int p_type, const String& p_name, int p_hint=PROPERTY_HINT_NONE, const String& p_hint_text=""); - + TreeItem *find_item(TreeItem *p_item,const String& p_name); - - + + virtual void _changed_callback(Object *p_changed,const char * p_what); virtual void _changed_callbacks(Object *p_changed,const String& p_callback); + void _edit_button(Object *p_item, int p_column, int p_button); - + void _node_removed(Node *p_node); void _edit_set(const String& p_name, const Variant& p_value); void _draw_flags(Object *ti,const Rect2& p_rect); - Node *get_instanced_node(); + bool _might_be_in_instance(); + bool _get_instanced_node_original_property(const StringName& p_prop,Variant& value); + bool _is_property_different(const Variant& p_current, const Variant& p_orig,int p_usage=0); + + void _refresh_item(TreeItem *p_item); + void _set_range_def(Object *p_item, String prop, float p_frame); + + void _filter_changed(const String& p_text); UndoRedo *undo_redo; protected: - + void _notification(int p_what); static void _bind_methods(); public: @@ -203,6 +223,8 @@ public: void update_tree(); void update_property(const String& p_prop); + void refresh(); + void edit(Object* p_object); void set_keying(bool p_active); @@ -212,7 +234,11 @@ public: void set_autoclear(bool p_enable); void set_show_categories(bool p_show); - + void set_use_doc_hints(bool p_enable) { use_doc_hints=p_enable; } + + void set_use_filter(bool p_use); + void register_text_enter(Node *p_line_edit); + PropertyEditor(); ~PropertyEditor(); diff --git a/tools/editor/pvrtc_compress.cpp b/tools/editor/pvrtc_compress.cpp index 9511d6a26d..a2f98adbe0 100644 --- a/tools/editor/pvrtc_compress.cpp +++ b/tools/editor/pvrtc_compress.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,11 +32,34 @@ #include "io/resource_saver.h" #include "io/resource_loader.h" #include "os/os.h" - +#include "os/file_access.h" +static void (*_base_image_compress_pvrtc2_func)(Image *)=NULL; +static void (*_base_image_compress_pvrtc4_func)(Image *)=NULL; static void _compress_image(Image::CompressMode p_mode,Image *p_image) { String ttpath = EditorSettings::get_singleton()->get("PVRTC/texture_tool"); + + if (ttpath.strip_edges()=="" || !FileAccess::exists(ttpath)) { + switch(p_mode) { + + case Image::COMPRESS_PVRTC2: + if (_base_image_compress_pvrtc2_func) + _base_image_compress_pvrtc2_func(p_image); + else if (_base_image_compress_pvrtc4_func) + _base_image_compress_pvrtc4_func(p_image); + + break; + case Image::COMPRESS_PVRTC4: + if (_base_image_compress_pvrtc4_func) + _base_image_compress_pvrtc4_func(p_image); + + break; + default: ERR_FAIL(); + + } + return; + } String spath = EditorSettings::get_singleton()->get_settings_path(); @@ -100,6 +123,9 @@ static void _compress_etc(Image *p_image) { void _pvrtc_register_compressors() { + _base_image_compress_pvrtc2_func=Image::_image_compress_pvrtc2_func; + _base_image_compress_pvrtc4_func=Image::_image_compress_pvrtc4_func; + Image::_image_compress_pvrtc2_func=_compress_pvrtc2; Image::_image_compress_pvrtc4_func=_compress_pvrtc4; //Image::_image_compress_etc_func=_compress_etc; //use the built in one for ETC diff --git a/tools/editor/pvrtc_compress.h b/tools/editor/pvrtc_compress.h index e683ed7804..c4fb0bacb5 100644 --- a/tools/editor/pvrtc_compress.h +++ b/tools/editor/pvrtc_compress.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/quick_open.cpp b/tools/editor/quick_open.cpp index 129c637ab0..6135a4ab64 100644 --- a/tools/editor/quick_open.cpp +++ b/tools/editor/quick_open.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,8 +30,9 @@ #include "os/keyboard.h" -void EditorQuickOpen::popup(const String& p_base, bool p_dontclear) { +void EditorQuickOpen::popup(const StringName &p_base, bool p_dontclear, bool p_add_dirs) { + add_directories=p_add_dirs; popup_centered_ratio(0.6); if (p_dontclear) search_box->select_all(); @@ -66,27 +67,53 @@ void EditorQuickOpen::_sbox_input(const InputEvent& p_ie) { void EditorQuickOpen::_parse_fs(EditorFileSystemDirectory *efsd) { - for(int i=0;i<efsd->get_subdir_count();i++) { + if (!add_directories) { + for(int i=0;i<efsd->get_subdir_count();i++) { - _parse_fs(efsd->get_subdir(i)); + _parse_fs(efsd->get_subdir(i)); + } } + TreeItem *root = search_options->get_root(); + + if (add_directories) { + String path = efsd->get_path(); + if (!path.ends_with("/")) + path+="/"; + if (path!="res://") { + path=path.substr(6,path.length()); + if (path.findn(search_box->get_text())!=-1) { + TreeItem *ti = search_options->create_item(root); + ti->set_text(0,path); + Ref<Texture> icon = get_icon("folder","FileDialog"); + ti->set_icon(0,icon); + } + } + } for(int i=0;i<efsd->get_file_count();i++) { String file = efsd->get_file_path(i); file=file.substr(6,file.length()); if (ObjectTypeDB::is_type(efsd->get_file_type(i),base_type) && (search_box->get_text()=="" || file.findn(search_box->get_text())!=-1)) { - TreeItem *root = search_options->get_root(); TreeItem *ti = search_options->create_item(root); ti->set_text(0,file); - Ref<Texture> icon = get_icon( (has_icon(efsd->get_file_type(i),"EditorIcons")?efsd->get_file_type(i):String("Object")),"EditorIcons"); + Ref<Texture> icon = get_icon( (has_icon(efsd->get_file_type(i),ei)?efsd->get_file_type(i):ot),ei); ti->set_icon(0,icon); if (root->get_children()==ti) ti->select(0); } } + + + if (add_directories) { + for(int i=0;i<efsd->get_subdir_count();i++) { + + _parse_fs(efsd->get_subdir(i)); + } + } + } void EditorQuickOpen::_update_search() { @@ -118,7 +145,7 @@ void EditorQuickOpen::_notification(int p_what) { } -String EditorQuickOpen::get_base_type() const { +StringName EditorQuickOpen::get_base_type() const { return base_type; } @@ -152,4 +179,7 @@ EditorQuickOpen::EditorQuickOpen() { set_hide_on_ok(false); search_options->connect("item_activated",this,"_confirmed"); search_options->set_hide_root(true); + ei="EditorIcons"; + ot="Object"; + add_directories=false; } diff --git a/tools/editor/quick_open.h b/tools/editor/quick_open.h index 9d8a7a1abd..8b38256d39 100644 --- a/tools/editor/quick_open.h +++ b/tools/editor/quick_open.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,7 +38,11 @@ class EditorQuickOpen : public ConfirmationDialog { LineEdit *search_box; Tree *search_options; - String base_type; + StringName base_type; + StringName ei; + StringName ot; + bool add_directories; + void _update_search(); @@ -55,9 +59,9 @@ protected: static void _bind_methods(); public: - String get_base_type() const; + StringName get_base_type() const; - void popup(const String& p_base,bool p_dontclear=false); + void popup(const StringName& p_base,bool p_dontclear=false,bool p_add_dirs=false); EditorQuickOpen(); }; diff --git a/tools/editor/register_exporters.h b/tools/editor/register_exporters.h index 559f0a9200..0e1ad2ca46 100644 --- a/tools/editor/register_exporters.h +++ b/tools/editor/register_exporters.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/reparent_dialog.cpp b/tools/editor/reparent_dialog.cpp index 5a5566c756..78ba47d54b 100644 --- a/tools/editor/reparent_dialog.cpp +++ b/tools/editor/reparent_dialog.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -41,6 +41,11 @@ void ReparentDialog::_notification(int p_what) { connect("confirmed", this,"_reparent"); } + if (p_what==NOTIFICATION_EXIT_TREE) { + + disconnect("confirmed", this,"_reparent"); + } + if (p_what==NOTIFICATION_DRAW) { //RID ci = get_canvas_item(); @@ -98,6 +103,7 @@ ReparentDialog::ReparentDialog() { add_child(node_only); node_only->hide(); + tree->set_show_enabled_subscene(true); //vbc->add_margin_child("Options:",node_only);; diff --git a/tools/editor/reparent_dialog.h b/tools/editor/reparent_dialog.h index 52a2192d7c..78c0df9285 100644 --- a/tools/editor/reparent_dialog.h +++ b/tools/editor/reparent_dialog.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/resources_dock.cpp b/tools/editor/resources_dock.cpp index 4614c4945d..b69eec4a51 100644 --- a/tools/editor/resources_dock.cpp +++ b/tools/editor/resources_dock.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -134,7 +134,7 @@ void ResourcesDock::save_resource(const String& p_path,const Ref<Resource>& p_re if (err!=OK) { accept->set_text("Error saving resource!"); - accept->popup_centered(Size2(300,100)); + accept->popup_centered_minsize(); return; } // EditorFileSystem::get_singleton()->update_file(path,p_resource->get_type()); @@ -152,7 +152,7 @@ void ResourcesDock::save_resource_as(const Ref<Resource>& p_resource) { List<String> extensions; ResourceSaver::get_recognized_extensions(res,&extensions); - file->set_mode(FileDialog::MODE_SAVE_FILE); + file->set_mode(EditorFileDialog::MODE_SAVE_FILE); if (p_resource->get_path()!="" && p_resource->get_path().find("::")==-1) { @@ -396,7 +396,7 @@ ResourcesDock::ResourcesDock(EditorNode *p_editor) { accept = memnew (AcceptDialog); add_child(accept); - file = memnew( FileDialog ); + file = memnew( EditorFileDialog ); add_child(file); file->connect("file_selected",this,"_file_action"); diff --git a/tools/editor/resources_dock.h b/tools/editor/resources_dock.h index edf769b157..933b457b29 100644 --- a/tools/editor/resources_dock.h +++ b/tools/editor/resources_dock.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,6 +38,7 @@ #include "scene/gui/menu_button.h" #include "scene/gui/file_dialog.h" #include "create_dialog.h" +#include "editor_file_dialog.h" class EditorNode; @@ -68,7 +69,7 @@ class ResourcesDock : public VBoxContainer { CreateDialog *create_dialog; AcceptDialog *accept; - FileDialog *file; + EditorFileDialog *file; Tree *resources; bool block_add; int current_action; diff --git a/tools/editor/run_settings_dialog.cpp b/tools/editor/run_settings_dialog.cpp index efe9c8f05f..e883c69939 100644 --- a/tools/editor/run_settings_dialog.cpp +++ b/tools/editor/run_settings_dialog.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/run_settings_dialog.h b/tools/editor/run_settings_dialog.h index da7c818263..fdb8857f6b 100644 --- a/tools/editor/run_settings_dialog.h +++ b/tools/editor/run_settings_dialog.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/scene_tree_dock.cpp b/tools/editor/scene_tree_dock.cpp index f5d9e83bf8..0cafe7459b 100644 --- a/tools/editor/scene_tree_dock.cpp +++ b/tools/editor/scene_tree_dock.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,11 +33,12 @@ #include "scene/resources/packed_scene.h" #include "editor_settings.h" #include "tools/editor/plugins/canvas_item_editor_plugin.h" - +#include "script_editor_debugger.h" +#include "tools/editor/plugins/script_editor_plugin.h" +#include "multi_node_edit.h" void SceneTreeDock::_unhandled_key_input(InputEvent p_event) { - uint32_t sc = p_event.key.get_scancode_with_modifiers(); if (!p_event.key.pressed || p_event.key.echo) return; @@ -61,7 +62,7 @@ Node* SceneTreeDock::instance(const String& p_file) { //accept->get_cancel()->hide(); accept->get_ok()->set_text("Ok :( "); accept->set_text("No parent to instance a child at."); - accept->popup_centered(Size2(300,70)); + accept->popup_centered_minsize(); return NULL; }; @@ -70,7 +71,7 @@ Node* SceneTreeDock::instance(const String& p_file) { Node*instanced_scene=NULL; Ref<PackedScene> sdata = ResourceLoader::load(p_file); if (sdata.is_valid()) - instanced_scene=sdata->instance(); + instanced_scene=sdata->instance(true); if (!instanced_scene) { @@ -79,11 +80,23 @@ Node* SceneTreeDock::instance(const String& p_file) { //accept->get_cancel()->hide(); accept->get_ok()->set_text("Ugh"); accept->set_text(String("Error loading scene from ")+p_file); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); return NULL; } - instanced_scene->generate_instance_state(); + // If the scene hasn't been saved yet a cyclical dependency cannot exist. + if (edited_scene->get_filename()!="") { + + if (_cyclical_dependency_exists(edited_scene->get_filename(), instanced_scene)) { + + accept->get_ok()->set_text("Ok"); + accept->set_text(String("Cannot instance the scene '")+p_file+String("' because the current scene exists within one of its' nodes.")); + accept->popup_centered_minsize(); + return NULL; + } + } + + //instanced_scene->generate_instance_state(); instanced_scene->set_filename( Globals::get_singleton()->localize_path(p_file) ); editor_data->get_undo_redo().create_action("Instance Scene"); @@ -93,6 +106,13 @@ Node* SceneTreeDock::instance(const String& p_file) { editor_data->get_undo_redo().add_do_method(editor_selection,"add_node",instanced_scene); editor_data->get_undo_redo().add_do_reference(instanced_scene); editor_data->get_undo_redo().add_undo_method(parent,"remove_child",instanced_scene); + + + String new_name = parent->validate_child_name(instanced_scene->get_name()); + ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); + editor_data->get_undo_redo().add_do_method(sed,"live_debug_instance_node",edited_scene->get_path_to(parent),p_file,new_name); + editor_data->get_undo_redo().add_undo_method(sed,"live_debug_remove_node",NodePath(String(edited_scene->get_path_to(parent))+"/"+new_name)); + editor_data->get_undo_redo().commit_action(); @@ -100,6 +120,35 @@ Node* SceneTreeDock::instance(const String& p_file) { } +bool SceneTreeDock::_cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node) { + int childCount = p_desired_node->get_child_count(); + + if (p_desired_node->get_filename()==p_target_scene_path) { + return true; + } + + for (int i=0;i<childCount;i++) { + Node* child=p_desired_node->get_child(i); + + if(_cyclical_dependency_exists(p_target_scene_path,child)) { + return true; + } + } + + return false; +} + + +static String _get_name_num_separator() { + switch(EditorSettings::get_singleton()->get("scenetree_editor/duplicate_node_name_num_separator").operator int()) { + case 0: return ""; + case 1: return " "; + case 2: return "_"; + case 3: return "-"; + } + return " "; +} + void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { current_option=p_tool; @@ -109,8 +158,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { case TOOL_NEW: { - if (!_validate_no_foreign()) - break; + //if (!_validate_no_foreign()) + // break; create_dialog->popup_centered_ratio(); } break; case TOOL_INSTANCE: { @@ -123,14 +172,14 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("This operation can't be done without a tree root."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } - if (!_validate_no_foreign()) - break; + //if (!_validate_no_foreign()) + // break; - file->set_mode(FileDialog::MODE_OPEN_FILE); + file->set_mode(EditorFileDialog::MODE_OPEN_FILE); List<String> extensions; ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions); file->clear_filters(); @@ -153,8 +202,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (!current) break; - if (!_validate_no_foreign()) - break; + //if (!_validate_no_foreign()) + // break; connect_dialog->popup_centered_ratio(); connect_dialog->set_node(current); @@ -164,8 +213,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { Node *current = scene_tree->get_selected(); if (!current) break; - if (!_validate_no_foreign()) - break; + //if (!_validate_no_foreign()) + // break; groups_editor->set_current(current); groups_editor->popup_centered_ratio(); } break; @@ -175,8 +224,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (!selected) break; - if (!_validate_no_foreign()) - break; + //if (!_validate_no_foreign()) + // break; Ref<Script> existing = selected->get_script(); if (existing.is_valid()) @@ -204,7 +253,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { //accept->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("This operation can't be done on the tree root."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } @@ -272,7 +321,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { //accept->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("This operation can't be done on the tree root."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } @@ -318,17 +367,21 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } int num=nums.to_int(); - if (num<2) - num=2; + if (num<1) + num=1; else num++; - name=name.substr(0,name.length()-nums.length()).strip_edges(); - String attempt=name+" "+itos(num); + String nnsep = _get_name_num_separator(); + name = name.substr(0,name.length()-nums.length()).strip_edges(); + if ( name.substr(name.length()-nnsep.length(),nnsep.length()) == nnsep) { + name = name.substr(0,name.length()-nnsep.length()); + } + String attempt = (name + nnsep + itos(num)).strip_edges(); while(parent->has_node(attempt)) { num++; - attempt=name+" "+itos(num); + attempt = (name + nnsep + itos(num)).strip_edges(); } dup->set_name(attempt); @@ -344,9 +397,14 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { editor_data->get_undo_redo().add_do_method(d,"set_owner",node->get_owner()); } editor_data->get_undo_redo().add_do_method(editor_selection,"add_node",dup); - editor_data->get_undo_redo().add_undo_method(parent,"remove_child",dup); + editor_data->get_undo_redo().add_undo_method(parent,"remove_child",dup); editor_data->get_undo_redo().add_do_reference(dup); + ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); + + editor_data->get_undo_redo().add_do_method(sed,"live_debug_duplicate_node",edited_scene->get_path_to(node),attempt); + editor_data->get_undo_redo().add_undo_method(sed,"live_debug_remove_node",NodePath(String(edited_scene->get_path_to(parent))+"/"+attempt)); + //parent->add_child(dup); //reselect.push_back(dup); } @@ -375,7 +433,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { //confirmation->get_cancel()->hide(); accept->get_ok()->set_text("I see.."); accept->set_text("This operation can't be done on the tree root."); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); break; } @@ -392,6 +450,19 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { reparent_dialog->set_current( nodeset ); } break; + case TOOL_MULTI_EDIT: { + + Node*root=EditorNode::get_singleton()->get_edited_scene(); + if (!root) + break; + Ref<MultiNodeEdit> mne = memnew( MultiNodeEdit ); + for (const Map<Node*,Object*>::Element *E=EditorNode::get_singleton()->get_editor_selection()->get_selection().front();E;E=E->next()) { + mne->add_node(root->get_path_to(E->key())); + } + + EditorNode::get_singleton()->push_item(mne.ptr()); + + } break; case TOOL_ERASE: { List<Node*> remove_list = editor_selection->get_selected_node_list(); @@ -411,7 +482,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } else { delete_dialog->set_text("Delete Node(s)?"); - delete_dialog->popup_centered(Size2(200,80)); + delete_dialog->popup_centered_minsize(); } @@ -426,8 +497,18 @@ void SceneTreeDock::_notification(int p_what) { switch(p_what) { - case NOTIFICATION_ENTER_TREE: { + case NOTIFICATION_READY: { + + if (!first_enter) + break; + first_enter=false; + CanvasItemEditorPlugin *canvas_item_plugin = editor_data->get_editor("2D")->cast_to<CanvasItemEditorPlugin>(); + if (canvas_item_plugin) { + canvas_item_plugin->get_canvas_item_editor()->connect("item_lock_status_changed", scene_tree, "_update_tree"); + canvas_item_plugin->get_canvas_item_editor()->connect("item_group_status_changed", scene_tree, "_update_tree"); + scene_tree->connect("node_changed", canvas_item_plugin->get_canvas_item_editor()->get_viewport_control(), "update"); + } static const char* button_names[TOOL_BUTTON_MAX]={ "New", "Add", @@ -439,21 +520,17 @@ void SceneTreeDock::_notification(int p_what) { "MoveDown", "Duplicate", "Reparent", - "Del", + "MultiNodeEdit", + "Remove", }; + + for(int i=0;i<TOOL_BUTTON_MAX;i++) tool_buttons[i]->set_icon(get_icon(button_names[i],"EditorIcons")); - } break; - case NOTIFICATION_READY: { + EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed",this,"_selection_changed"); - CanvasItemEditorPlugin *canvas_item_plugin = editor_data->get_editor("2D")->cast_to<CanvasItemEditorPlugin>(); - if (canvas_item_plugin) { - canvas_item_plugin->get_canvas_item_editor()->connect("item_lock_status_changed", scene_tree, "_update_tree"); - canvas_item_plugin->get_canvas_item_editor()->connect("item_group_status_changed", scene_tree, "_update_tree"); - scene_tree->connect("node_changed", canvas_item_plugin->get_canvas_item_editor()->get_viewport_control(), "update"); - } } break; } } @@ -496,9 +573,9 @@ Node *SceneTreeDock::_duplicate(Node *p_node, Map<Node*,Node*> &duplimap) { Ref<PackedScene> sd = ResourceLoader::load( p_node->get_filename() ); ERR_FAIL_COND_V(!sd.is_valid(),NULL); - node = sd->instance(); + node = sd->instance(true); ERR_FAIL_COND_V(!node,NULL); - node->generate_instance_state(); + //node->generate_instance_state(); } else { Object *obj = ObjectTypeDB::instance(p_node->get_type()); ERR_FAIL_COND_V(!obj,NULL); @@ -793,10 +870,20 @@ bool SceneTreeDock::_validate_no_foreign() { accept->get_ok()->set_text("Makes Sense!"); accept->set_text("Can't operate on nodes from a foreign scene!"); - accept->popup_centered(Size2(300,70));; + accept->popup_centered_minsize(); + return false; + + } + + if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(E->get()))>=0) { + + accept->get_ok()->set_text("Makes Sense!"); + accept->set_text("Can't operate on nodes the current scene inherits from!"); + accept->popup_centered_minsize(); return false; } + } return true; @@ -855,6 +942,13 @@ void SceneTreeDock::_node_reparent(NodePath p_path,bool p_node_only) { editor_data->get_undo_redo().add_do_method(node->get_parent(),"remove_child",node); editor_data->get_undo_redo().add_do_method(new_parent,"add_child",node); + + ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); + String new_name = new_parent->validate_child_name(node->get_name()); + editor_data->get_undo_redo().add_do_method(sed,"live_debug_reparent_node",edited_scene->get_path_to(node),edited_scene->get_path_to(new_parent),new_name,-1); + editor_data->get_undo_redo().add_undo_method(sed,"live_debug_reparent_node",NodePath(String(edited_scene->get_path_to(new_parent))+"/"+new_name),edited_scene->get_path_to(node->get_parent()),node->get_name(),node->get_index()); + + editor_data->get_undo_redo().add_do_method(this,"_set_owners",edited_scene,owners); if (editor->get_animation_editor()->get_root()==node) @@ -977,6 +1071,11 @@ void SceneTreeDock::_delete_confirm() { editor_data->get_undo_redo().add_undo_method(this,"_set_owners",edited_scene,owners); //editor_data->get_undo_redo().add_undo_method(n,"set_owner",n->get_owner()); editor_data->get_undo_redo().add_undo_reference(n); + + ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); + editor_data->get_undo_redo().add_do_method(sed,"live_debug_remove_and_keep_node",edited_scene->get_path_to(n),n->get_instance_ID()); + editor_data->get_undo_redo().add_undo_method(sed,"live_debug_restore_node",n->get_instance_ID(),edited_scene->get_path_to(n->get_parent()),n->get_index()); + } @@ -990,21 +1089,33 @@ void SceneTreeDock::_update_tool_buttons() { Node *sel = scene_tree->get_selected(); bool disable = !sel || (sel!=edited_scene && sel->get_owner()!=edited_scene); + disable = disable || (edited_scene->get_scene_inherited_state().is_valid() && edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(sel))>=0); bool disable_root = disable || sel->get_parent()==scene_root; + bool disable_edit = !sel; - tool_buttons[TOOL_INSTANCE]->set_disabled(disable); + tool_buttons[TOOL_INSTANCE]->set_disabled(disable_edit); tool_buttons[TOOL_REPLACE]->set_disabled(disable); - tool_buttons[TOOL_CONNECT]->set_disabled(disable); - tool_buttons[TOOL_GROUP]->set_disabled(disable); - tool_buttons[TOOL_SCRIPT]->set_disabled(disable); + tool_buttons[TOOL_CONNECT]->set_disabled(disable_edit); + tool_buttons[TOOL_GROUP]->set_disabled(disable_edit); + tool_buttons[TOOL_SCRIPT]->set_disabled(disable_edit); tool_buttons[TOOL_MOVE_UP]->set_disabled(disable_root); tool_buttons[TOOL_MOVE_DOWN]->set_disabled(disable_root); tool_buttons[TOOL_DUPLICATE]->set_disabled(disable_root); tool_buttons[TOOL_REPARENT]->set_disabled(disable_root); tool_buttons[TOOL_ERASE]->set_disabled(disable); + tool_buttons[TOOL_MULTI_EDIT]->set_disabled(EditorNode::get_singleton()->get_editor_selection()->get_selection().size()<2); + } + +void SceneTreeDock::_selection_changed() { + + tool_buttons[TOOL_MULTI_EDIT]->set_disabled(EditorNode::get_singleton()->get_editor_selection()->get_selection().size()<2); + +} + + void SceneTreeDock::_create() { @@ -1014,14 +1125,15 @@ void SceneTreeDock::_create() { if (edited_scene) { - + // If root exists in edited scene parent = scene_tree->get_selected(); - ERR_FAIL_COND(!parent); - } else { + if( !parent ) + parent = edited_scene; + } else { + // If no root exist in edited scene parent = scene_root; ERR_FAIL_COND(!parent); - } Object *c = create_dialog->instance_selected(); @@ -1033,12 +1145,20 @@ void SceneTreeDock::_create() { editor_data->get_undo_redo().create_action("Create Node"); if (edited_scene) { + editor_data->get_undo_redo().add_do_method(parent,"add_child",child); editor_data->get_undo_redo().add_do_method(child,"set_owner",edited_scene); editor_data->get_undo_redo().add_do_method(editor_selection,"clear"); editor_data->get_undo_redo().add_do_method(editor_selection,"add_node",child); editor_data->get_undo_redo().add_do_reference(child); editor_data->get_undo_redo().add_undo_method(parent,"remove_child",child); + + + String new_name = parent->validate_child_name(child->get_type()); + ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); + editor_data->get_undo_redo().add_do_method(sed,"live_debug_create_node",edited_scene->get_path_to(parent),child->get_type(),new_name); + editor_data->get_undo_redo().add_undo_method(sed,"live_debug_remove_node",NodePath(String(edited_scene->get_path_to(parent))+"/"+new_name)); + } else { editor_data->get_undo_redo().add_do_method(editor,"set_edited_scene",child); @@ -1103,24 +1223,21 @@ void SceneTreeDock::_create() { } - String newname=n->get_name(); n->replace_by(newnode,true); - if (n==edited_scene) { edited_scene=newnode; editor->set_edited_scene(newnode); } - - - editor_data->get_undo_redo().clear_history(); - memdelete(n); newnode->set_name(newname); + editor->push_item(newnode); + memdelete(n); + _update_tool_buttons(); } @@ -1180,6 +1297,7 @@ void SceneTreeDock::_bind_methods() { ObjectTypeDB::bind_method(_MD("_delete_confirm"),&SceneTreeDock::_delete_confirm); ObjectTypeDB::bind_method(_MD("_node_prerenamed"),&SceneTreeDock::_node_prerenamed); ObjectTypeDB::bind_method(_MD("_import_subscene"),&SceneTreeDock::_import_subscene); + ObjectTypeDB::bind_method(_MD("_selection_changed"),&SceneTreeDock::_selection_changed); ObjectTypeDB::bind_method(_MD("instance"),&SceneTreeDock::instance); } @@ -1209,7 +1327,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec tb = memnew( ToolButton ); tb->connect("pressed",this,"_tool_selected",make_binds(TOOL_INSTANCE, false)); - tb->set_tooltip("Instance a Node from scene file."); + tb->set_tooltip("Instance a scene file as a Node."); hbc_top->add_child(tb); tool_buttons[TOOL_INSTANCE]=tb; @@ -1283,6 +1401,12 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec hbc_bottom->add_spacer(); tb = memnew( ToolButton ); + tb->connect("pressed",this,"_tool_selected",make_binds(TOOL_MULTI_EDIT, false)); + tb->set_tooltip("Multi-Edit Selected Nodes"); + hbc_bottom->add_child(tb); + tool_buttons[TOOL_MULTI_EDIT]=tb; + + tb = memnew( ToolButton ); tb->connect("pressed",this,"_tool_selected",make_binds(TOOL_ERASE, false)); tb->set_tooltip("Erase Selected Node(s)"); hbc_bottom->add_child(tb); @@ -1309,7 +1433,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec accept = memnew( AcceptDialog ); add_child(accept); - file = memnew( FileDialog ); + file = memnew( EditorFileDialog ); add_child(file); file->connect("file_selected",this,"instance"); set_process_unhandled_key_input(true); @@ -1321,7 +1445,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec add_child(import_subscene_dialog); import_subscene_dialog->connect("subscene_selected",this,"_import_subscene"); - + first_enter=true; } diff --git a/tools/editor/scene_tree_dock.h b/tools/editor/scene_tree_dock.h index ac5391f3b9..b1c53d2ff9 100644 --- a/tools/editor/scene_tree_dock.h +++ b/tools/editor/scene_tree_dock.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -62,6 +62,7 @@ class SceneTreeDock : public VBoxContainer { TOOL_MOVE_DOWN, TOOL_DUPLICATE, TOOL_REPARENT, + TOOL_MULTI_EDIT, TOOL_ERASE, TOOL_BUTTON_MAX }; @@ -87,9 +88,10 @@ class SceneTreeDock : public VBoxContainer { ConfirmationDialog *delete_dialog; ReparentDialog *reparent_dialog; - FileDialog *file; + EditorFileDialog *file; EditorSubScene *import_subscene_dialog; + bool first_enter; void _create(); Node *scene_root; @@ -102,6 +104,7 @@ class SceneTreeDock : public VBoxContainer { void _load_request(const String& p_path); void _script_open_request(const Ref<Script>& p_script); + bool _cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node); void _node_selected(); void _node_renamed(); @@ -117,6 +120,7 @@ class SceneTreeDock : public VBoxContainer { void _import_subscene(); bool _validate_no_foreign(); + void _selection_changed(); void _fill_path_renames(Vector<StringName> base_path,Vector<StringName> new_base_path,Node * p_node, List<Pair<NodePath,NodePath> > *p_renames); @@ -132,6 +136,7 @@ public: void set_selected(Node *p_node, bool p_emit_selected=false); void fill_path_renames(Node* p_node, Node *p_new_parent, List<Pair<NodePath,NodePath> > *p_renames); void perform_node_renames(Node* p_base,List<Pair<NodePath,NodePath> > *p_renames, Map<Ref<Animation>, Set<int> > *r_rem_anims=NULL); + SceneTreeEditor *get_tree_editor() { return scene_tree; } SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelection *p_editor_selection,EditorData &p_editor_data); }; diff --git a/tools/editor/scene_tree_editor.cpp b/tools/editor/scene_tree_editor.cpp index e9ec0199d0..ac2f76acdc 100644 --- a/tools/editor/scene_tree_editor.cpp +++ b/tools/editor/scene_tree_editor.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,6 +34,8 @@ #include "scene/main/viewport.h" #include "tools/editor/plugins/canvas_item_editor_plugin.h" +#include "scene/resources/packed_scene.h" + Node *SceneTreeEditor::get_scene_node() { ERR_FAIL_COND_V(!is_inside_tree(),NULL); @@ -57,22 +59,58 @@ void SceneTreeEditor::_subscene_option(int p_idx) { switch(p_idx) { - case SCENE_MENU_SHOW_CHILDREN: { + case SCENE_MENU_EDITABLE_CHILDREN: { - if (node->has_meta("__editor_show_subtree")) { - instance_menu->set_item_checked(0,true); - node->set_meta("__editor_show_subtree",Variant()); - _update_tree(); - } else { - node->set_meta("__editor_show_subtree",true); - _update_tree(); + bool editable = EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(node); + editable = !editable; + + //node->set_instance_children_editable(editable); + EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node,editable); + instance_menu->set_item_checked(0,editable); + if (editable) { + node->set_scene_instance_load_placeholder(false); + instance_menu->set_item_checked(1,false); + } + + _update_tree(); + + } break; + case SCENE_MENU_USE_PLACEHOLDER: { + + bool placeholder = node->get_scene_instance_load_placeholder(); + placeholder = !placeholder; + + //node->set_instance_children_editable(editable); + if (placeholder) { + EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node,false); } + node->set_scene_instance_load_placeholder(placeholder); + instance_menu->set_item_checked(0,false); + instance_menu->set_item_checked(1,placeholder); + + _update_tree(); } break; case SCENE_MENU_OPEN: { emit_signal("open",node->get_filename()); } break; + case SCENE_MENU_CLEAR_INHERITANCE: { + clear_inherit_confirm->popup_centered_minsize(); + } break; + case SCENE_MENU_OPEN_INHERITED: { + if (node && node->get_scene_inherited_state().is_valid()) { + emit_signal("open",node->get_scene_inherited_state()->get_path()); + } + } break; + case SCENE_MENU_CLEAR_INHERITANCE_CONFIRM: { + if (node && node->get_scene_inherited_state().is_valid()) { + node->set_scene_inherited_state(Ref<SceneState>()); + update_tree(); + EditorNode::get_singleton()->get_property_editor()->update_tree(); + } + + } break; } @@ -94,15 +132,33 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item,int p_column,int p_id) Rect2 item_rect = tree->get_item_rect(item,0); item_rect.pos.y-=tree->get_scroll().y; item_rect.pos+=tree->get_global_pos(); - instance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y)); - instance_menu->set_size(Vector2(item_rect.size.x,0)); - if (n->has_meta("__editor_show_subtree")) - instance_menu->set_item_checked(0,true); - else - instance_menu->set_item_checked(0,false); - instance_menu->popup(); - instance_node=n->get_instance_ID(); + if (n==get_scene_node()) { + inheritance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y)); + inheritance_menu->set_size(Vector2(item_rect.size.x,0)); + inheritance_menu->popup(); + instance_node=n->get_instance_ID(); + + } else { + instance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y)); + instance_menu->set_size(Vector2(item_rect.size.x,0)); + if (EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(n)) + instance_menu->set_item_checked(0,true); + else + instance_menu->set_item_checked(0,false); + + if (n->get_owner()==get_scene_node()) { + instance_menu->set_item_checked(1,n->get_scene_instance_load_placeholder()); + instance_menu->set_item_disabled(1,false); + } else { + + instance_menu->set_item_checked(1,false); + instance_menu->set_item_disabled(1,true); + } + + instance_menu->popup(); + instance_node=n->get_instance_ID(); + } //emit_signal("open",n->get_filename()); } else if (p_id==BUTTON_SCRIPT) { RefPtr script=n->get_script(); @@ -117,7 +173,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item,int p_column,int p_id) Spatial *ci = n->cast_to<Spatial>(); if (!ci->is_visible() && ci->get_parent_spatial() && !ci->get_parent_spatial()->is_visible()) { error->set_text("This item cannot be made visible because the parent is hidden. Unhide the parent first."); - error->popup_centered_minsize(Size2(400,80)); + error->popup_centered_minsize(); return; } @@ -131,7 +187,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item,int p_column,int p_id) CanvasItem *ci = n->cast_to<CanvasItem>(); if (!ci->is_visible() && ci->get_parent_item() && !ci->get_parent_item()->is_visible()) { error->set_text("This item cannot be made visible because the parent is hidden. Unhide the parent first."); - error->popup_centered_minsize(Size2(400,80)); + error->popup_centered_minsize(); return; } bool v = !bool(n->call("is_hidden")); @@ -168,20 +224,22 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) { bool part_of_subscene=false; - if (!display_foreign && p_node->get_owner()!=get_scene_node() && p_node!=get_scene_node()) { + if (!display_foreign && p_node->get_owner()!=get_scene_node() && p_node!=get_scene_node()) { - if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && p_node->get_owner()->get_owner()==get_scene_node() && p_node->get_owner()->has_meta("__editor_show_subtree")) { + if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && (get_scene_node()->is_editable_instance(p_node->get_owner()))) { part_of_subscene=true; //allow } else { return; } + } else { + part_of_subscene = get_scene_node()->get_scene_inherited_state().is_valid() && get_scene_node()->get_scene_inherited_state()->find_node_by_path(get_scene_node()->get_path_to(p_node))>=0; } TreeItem *item = tree->create_item(p_parent); item->set_text(0, p_node->get_name() ); - if (can_rename && (p_node->get_owner() == get_scene_node() || p_node==get_scene_node())) + if (can_rename && !part_of_subscene /*(p_node->get_owner() == get_scene_node() || p_node==get_scene_node())*/) item->set_editable(0, true); item->set_selectable(0,true); @@ -199,6 +257,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) { icon=get_icon( (has_icon(p_node->get_type(),"EditorIcons")?p_node->get_type():String("Object")),"EditorIcons"); item->set_icon(0, icon ); item->set_metadata( 0,p_node->get_path() ); + if (part_of_subscene) { //item->set_selectable(0,marked_selectable); @@ -221,7 +280,10 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) { } } - if (p_node!=get_scene_node() && p_node->get_filename()!="" && can_open_instance) { + if (p_node==get_scene_node() && p_node->get_scene_inherited_state().is_valid()) { + item->add_button(0,get_icon("InstanceOptions","EditorIcons"),BUTTON_SUBSCENE); + item->set_tooltip(0,"Inherits: "+p_node->get_scene_inherited_state()->get_path()+"\nType: "+p_node->get_type()); + } else if (p_node!=get_scene_node() && p_node->get_filename()!="" && can_open_instance) { item->add_button(0,get_icon("InstanceOptions","EditorIcons"),BUTTON_SUBSCENE); item->set_tooltip(0,"Instance: "+p_node->get_filename()+"\nType: "+p_node->get_type()); @@ -487,8 +549,11 @@ void SceneTreeEditor::_notification(int p_what) { get_tree()->connect("tree_changed",this,"_tree_changed"); get_tree()->connect("node_removed",this,"_node_removed"); - instance_menu->set_item_icon(2,get_icon("Load","EditorIcons")); + instance_menu->set_item_icon(3,get_icon("Load","EditorIcons")); tree->connect("item_collapsed",this,"_cell_collapsed"); + inheritance_menu->set_item_icon(2,get_icon("Load","EditorIcons")); + clear_inherit_confirm->connect("confirmed",this,"_subscene_option",varray(SCENE_MENU_CLEAR_INHERITANCE_CONFIRM)); + // get_scene()->connect("tree_changed",this,"_tree_changed",Vector<Variant>(),CONNECT_DEFERRED); // get_scene()->connect("node_removed",this,"_node_removed",Vector<Variant>(),CONNECT_DEFERRED); @@ -498,6 +563,8 @@ void SceneTreeEditor::_notification(int p_what) { get_tree()->disconnect("tree_changed",this,"_tree_changed"); get_tree()->disconnect("node_removed",this,"_node_removed"); + tree->disconnect("item_collapsed",this,"_cell_collapsed"); + clear_inherit_confirm->disconnect("confirmed",this,"_subscene_option"); _update_tree(); } @@ -582,20 +649,28 @@ void SceneTreeEditor::_rename_node(ObjectID p_node,const String& p_name) { void SceneTreeEditor::_renamed() { TreeItem *which=tree->get_edited(); - + ERR_FAIL_COND(!which); NodePath np = which->get_metadata(0); Node *n=get_node(np); ERR_FAIL_COND(!n); + String new_name=which->get_text(0); + if (new_name.find(".") != -1 || new_name.find("/") != -1) { + + error->set_text("Invalid node name, the following characters are not allowed:\n \".\", \"/\""); + error->popup_centered_minsize(); + new_name=n->get_name(); + } + if (!undo_redo) { - n->set_name( which->get_text(0) ); + n->set_name( new_name ); which->set_metadata(0,n->get_path()); emit_signal("node_renamed"); } else { undo_redo->create_action("Rename Node"); - emit_signal("node_prerename",n,which->get_text(0)); - undo_redo->add_do_method(this,"_rename_node",n->get_instance_ID(),which->get_text(0)); + emit_signal("node_prerename",n,new_name); + undo_redo->add_do_method(this,"_rename_node",n->get_instance_ID(),new_name); undo_redo->add_undo_method(this,"_rename_node",n->get_instance_ID(),n->get_name()); undo_redo->commit_action(); } @@ -787,12 +862,27 @@ SceneTreeEditor::SceneTreeEditor(bool p_label,bool p_can_rename, bool p_can_open blocked=0; instance_menu = memnew( PopupMenu ); - instance_menu->add_check_item("Show Children",SCENE_MENU_SHOW_CHILDREN); + instance_menu->add_check_item("Editable Children",SCENE_MENU_EDITABLE_CHILDREN); + instance_menu->add_check_item("Load As Placeholder",SCENE_MENU_USE_PLACEHOLDER); instance_menu->add_separator(); instance_menu->add_item("Open in Editor",SCENE_MENU_OPEN); instance_menu->connect("item_pressed",this,"_subscene_option"); add_child(instance_menu); + inheritance_menu = memnew( PopupMenu ); + inheritance_menu->add_item("Clear Inheritance",SCENE_MENU_CLEAR_INHERITANCE); + inheritance_menu->add_separator(); + inheritance_menu->add_item("Open in Editor",SCENE_MENU_OPEN_INHERITED); + inheritance_menu->connect("item_pressed",this,"_subscene_option"); + + add_child(inheritance_menu); + + clear_inherit_confirm = memnew( ConfirmationDialog ); + clear_inherit_confirm->set_text("Clear Inheritance? (No Undo!)"); + clear_inherit_confirm->get_ok()->set_text("Clear!"); + add_child(clear_inherit_confirm); + + } @@ -810,6 +900,11 @@ void SceneTreeDialog::_notification(int p_what) { connect("confirmed", this,"_select"); } + + if (p_what==NOTIFICATION_EXIT_TREE) { + disconnect("confirmed", this,"_select"); + + } if (p_what==NOTIFICATION_DRAW) { RID ci = get_canvas_item(); diff --git a/tools/editor/scene_tree_editor.h b/tools/editor/scene_tree_editor.h index 5e88c5a41d..50cca4e24b 100644 --- a/tools/editor/scene_tree_editor.h +++ b/tools/editor/scene_tree_editor.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -52,16 +52,22 @@ class SceneTreeEditor : public Control { }; enum { - SCENE_MENU_SHOW_CHILDREN, + SCENE_MENU_EDITABLE_CHILDREN, + SCENE_MENU_USE_PLACEHOLDER, SCENE_MENU_OPEN, + SCENE_MENU_CLEAR_INHERITANCE, + SCENE_MENU_OPEN_INHERITED, + SCENE_MENU_CLEAR_INHERITANCE_CONFIRM, }; Tree *tree; Node *selected; PopupMenu *instance_menu; + PopupMenu *inheritance_menu; ObjectID instance_node; AcceptDialog *error; + ConfirmationDialog *clear_inherit_confirm; int blocked; @@ -125,6 +131,9 @@ public: void update_tree() { _update_tree(); } + + Tree* get_scene_tree() { return tree; } + SceneTreeEditor(bool p_label=true,bool p_can_rename=false, bool p_can_open_instance=false); ~SceneTreeEditor(); @@ -150,7 +159,7 @@ protected: static void _bind_methods(); public: - SceneTreeEditor *get_tree() { return tree; } + SceneTreeEditor *get_scene_tree() { return tree; } SceneTreeDialog(); ~SceneTreeDialog(); diff --git a/tools/editor/scenes.cpp b/tools/editor/scenes.cpp index 7860c04870..ada5751b5a 100644 --- a/tools/editor/scenes.cpp +++ b/tools/editor/scenes.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/scenes.h b/tools/editor/scenes.h index 59e7b728e2..463c3b5e18 100644 --- a/tools/editor/scenes.h +++ b/tools/editor/scenes.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tools/editor/scenes_dock.cpp b/tools/editor/scenes_dock.cpp index 516cb5930d..c9b376ebec 100644 --- a/tools/editor/scenes_dock.cpp +++ b/tools/editor/scenes_dock.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,15 +35,30 @@ #include "os/os.h" #include "editor_node.h" +#include "editor_settings.h" + bool ScenesDock::_create_tree(TreeItem *p_parent,EditorFileSystemDirectory *p_dir) { - String search_term = tree_filter->get_search_term(); - ScenesDockFilter::FilterOption file_filter = tree_filter->get_file_filter(); TreeItem *item = tree->create_item(p_parent); - item->set_text(0,p_dir->get_name()+"/"); + String dname=p_dir->get_name(); + if (dname=="") + dname="res://"; + + item->set_text(0,dname); item->set_icon(0,get_icon("Folder","EditorIcons")); - item->set_custom_bg_color(0,get_color("prop_subsection","Editor")); + item->set_selectable(0,true); + String lpath = p_dir->get_path(); + if (lpath!="res://" && lpath.ends_with("/")) { + lpath=lpath.substr(0,lpath.length()-1); + } + item->set_metadata(0,lpath); + if (lpath==path) { + item->select(0); + } + + + //item->set_custom_bg_color(0,get_color("prop_subsection","Editor")); bool has_items=false; @@ -52,7 +67,7 @@ bool ScenesDock::_create_tree(TreeItem *p_parent,EditorFileSystemDirectory *p_di if (_create_tree(item,p_dir->get_subdir(i))) has_items=true; } - +#if 0 for (int i=0;i<p_dir->get_file_count();i++) { String file_name = p_dir->get_file(i); @@ -89,13 +104,13 @@ bool ScenesDock::_create_tree(TreeItem *p_parent,EditorFileSystemDirectory *p_di has_items=true; } - - if (!has_items) { +#endif + /*if (!has_items) { memdelete(item); return false; - } + }*/ return true; } @@ -105,7 +120,28 @@ void ScenesDock::_update_tree() { tree->clear(); updating_tree=true; - _create_tree(NULL,EditorFileSystem::get_singleton()->get_filesystem()); + TreeItem *root = tree->create_item(); + TreeItem *favorites = tree->create_item(root); + favorites->set_icon(0, get_icon("Favorites","EditorIcons") ); + favorites->set_text(0,"Favorites:"); + favorites->set_selectable(0,false); + Vector<String> faves = EditorSettings::get_singleton()->get_favorite_dirs(); + for(int i=0;i<faves.size();i++) { + if (!faves[i].begins_with("res://")) + continue; + + TreeItem *ti = tree->create_item(favorites); + String fv = faves[i]; + if (fv=="res://") + ti->set_text(0,"/"); + else + ti->set_text(0,faves[i].get_file()); + ti->set_icon(0,get_icon("Folder","EditorIcons")); + ti->set_selectable(0,true); + ti->set_metadata(0,faves[i]); + } + + _create_tree(root,EditorFileSystem::get_singleton()->get_filesystem()); updating_tree=false; } @@ -117,68 +153,192 @@ void ScenesDock::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { + if (initialized) + return; + initialized=true; - EditorFileSystem::get_singleton()->connect("filesystem_changed",this,"_update_tree"); + EditorFileSystem::get_singleton()->connect("filesystem_changed",this,"_fs_changed"); button_reload->set_icon( get_icon("Reload","EditorIcons")); button_favorite->set_icon( get_icon("Favorites","EditorIcons")); + button_fav_up->set_icon( get_icon("MoveUp","EditorIcons")); + button_fav_down->set_icon( get_icon("MoveDown","EditorIcons")); button_instance->set_icon( get_icon("Add","EditorIcons")); button_open->set_icon( get_icon("Folder","EditorIcons")); + button_back->set_icon( get_icon("Filesystem","EditorIcons")); + display_mode->set_icon( get_icon("FileList","EditorIcons")); + display_mode->connect("pressed",this,"_change_file_display"); + file_options->set_icon( get_icon("Tools","EditorIcons")); + files->connect("item_activated",this,"_select_file"); + button_hist_next->connect("pressed",this,"_fw_history"); + button_hist_prev->connect("pressed",this,"_bw_history"); - String path = Globals::get_singleton()->get_resource_path()+"/favorites.cfg"; - FileAccess *f=FileAccess::open(path,FileAccess::READ); - if (f) { - - String l = f->get_line(); + button_hist_next->set_icon( get_icon("Forward","EditorIcons")); + button_hist_prev->set_icon( get_icon("Back","EditorIcons")); + file_options->get_popup()->connect("item_pressed",this,"_file_option"); - while(l!="") { - favorites.insert(l); - l = f->get_line(); - } + button_back->connect("pressed",this,"_go_to_tree",varray(),CONNECT_DEFERRED); + current_path->connect("text_entered",this,"_go_to_dir"); + _update_tree(); //maybe it finished already - f->close(); - memdelete(f); + if (EditorFileSystem::get_singleton()->is_scanning()) { + _set_scannig_mode(); } - - - _update_tree(); //maybe it finished already + } break; + case NOTIFICATION_PROCESS: { + if (EditorFileSystem::get_singleton()->is_scanning()) { + scanning_progress->set_val(EditorFileSystem::get_singleton()->get_scanning_progress()*100); + } } break; case NOTIFICATION_EXIT_TREE: { } break; - case NOTIFICATION_PROCESS: { - } break; } } -void ScenesDock::_favorite_toggled() { - if (updating_tree) + + +void ScenesDock::_dir_selected() { + + TreeItem *ti = tree->get_selected(); + if (!ti) return; + String dir = ti->get_metadata(0); + bool found=false; + Vector<String> favorites = EditorSettings::get_singleton()->get_favorite_dirs(); + for(int i=0;i<favorites.size();i++) { + + if (favorites[i]==dir) { + found=true; + break; + } + } + + + button_favorite->set_pressed(found); + if (ti->get_parent() && ti->get_parent()->get_parent()==tree->get_root() && !ti->get_parent()->get_prev()) { + + //a favorite!!! + button_fav_up->set_disabled(!ti->get_prev()); + button_fav_down->set_disabled(!ti->get_next()); + } else { + button_fav_up->set_disabled(true); + button_fav_down->set_disabled(true); + + } + +} +void ScenesDock::_fav_up_pressed() { TreeItem *sel = tree->get_selected(); if (!sel) - return; //? + return ; - bool faved = sel->is_checked(0); - String path = sel->get_metadata(0); - if (faved) - favorites.insert(path); - else - favorites.erase(path); + if (!sel->get_prev()) + return; + + String sw = sel->get_prev()->get_metadata(0); + String txt = sel->get_metadata(0); + + Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + + int a_idx=favorited.find(sw); + int b_idx=favorited.find(txt); - timer->start(); + if (a_idx==-1 || b_idx==-1) + return; + SWAP(favorited[a_idx],favorited[b_idx]); + + EditorSettings::get_singleton()->set_favorite_dirs(favorited); + + _update_tree(); + + if (!tree->get_root() || !tree->get_root()->get_children() || !tree->get_root()->get_children()->get_children()) + return; + sel = tree->get_root()->get_children()->get_children(); + while(sel) { + String t = sel->get_metadata(0); + if (t==txt) { + sel->select(0); + return; + } + sel=sel->get_next(); + } } -void ScenesDock::_favorites_toggled(bool p_toggled) { +void ScenesDock::_fav_down_pressed() { + + TreeItem *sel = tree->get_selected(); + if (!sel) + return ; + + if (!sel->get_next()) + return; + + String sw = sel->get_next()->get_metadata(0); + String txt = sel->get_metadata(0); + + Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + + int a_idx=favorited.find(sw); + int b_idx=favorited.find(txt); + + if (a_idx==-1 || b_idx==-1) + return; + SWAP(favorited[a_idx],favorited[b_idx]); + + EditorSettings::get_singleton()->set_favorite_dirs(favorited); _update_tree(); + + if (!tree->get_root() || !tree->get_root()->get_children() || !tree->get_root()->get_children()->get_children()) + return; + sel = tree->get_root()->get_children()->get_children(); + while(sel) { + + String t = sel->get_metadata(0); + if (t==txt) { + sel->select(0); + return; + } + sel=sel->get_next(); + } +} + +void ScenesDock::_favorites_pressed() { + + TreeItem *sel = tree->get_selected(); + if (!sel) + return ; + String dir = sel->get_metadata(0); + + int idx = -1; + Vector<String> favorites = EditorSettings::get_singleton()->get_favorite_dirs(); + for(int i=0;i<favorites.size();i++) { + + if (favorites[i]==dir) { + idx=i; + break; + } + } + + if (button_favorite->is_pressed() && idx==-1) { + favorites.push_back(dir); + EditorSettings::get_singleton()->set_favorite_dirs(favorites); + _update_tree(); + } else if (!button_favorite->is_pressed() && idx!=-1) { + favorites.remove(idx); + EditorSettings::get_singleton()->set_favorite_dirs(favorites); + _update_tree(); + } + } String ScenesDock::get_selected_path() const { @@ -192,72 +352,739 @@ String ScenesDock::get_selected_path() const { void ScenesDock::_instance_pressed() { - TreeItem *sel = tree->get_selected(); - if (!sel) - return; - String path = sel->get_metadata(0); + if (tree_mode) + { + TreeItem *sel = tree->get_selected(); + if (!sel) + return; + String path = sel->get_metadata(0); + } + else + { + int idx = -1; + for (int i = 0; i<files->get_item_count(); i++) { + if (files->is_selected(i)) + { + idx = i; + break; + } + } + + if (idx<0) + return; + + path = files->get_item_metadata(idx); + } + emit_signal("instance",path); } -void ScenesDock::_open_pressed(){ +void ScenesDock::_thumbnail_done(const String& p_path,const Ref<Texture>& p_preview, const Variant& p_udata) { - TreeItem *sel = tree->get_selected(); - if (!sel) { + if (p_preview.is_valid() && path==p_path.get_base_dir()) { + + int idx=p_udata; + if (idx>=files->get_item_count()) + return; + String fpath = files->get_item_metadata(idx); + if (fpath!=p_path) + return; + files->set_item_icon(idx,p_preview); + + } + +} + +void ScenesDock::_change_file_display() { + + if (display_mode->is_pressed()) { + display_mode->set_icon( get_icon("FileThumbnail","EditorIcons")); + + } else { + display_mode->set_icon( get_icon("FileList","EditorIcons")); + } + + _update_files(true); +} + +void ScenesDock::_update_files(bool p_keep_selection) { + + Set<String> cselection; + + if (p_keep_selection) { + + for(int i=0;i<files->get_item_count();i++) { + + if (files->is_selected(i)) + cselection.insert(files->get_item_text(i)); + } + } + + files->clear(); + + EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->get_path(path); + if (!efd) return; + + int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size"); + Ref<Texture> folder_thumbnail; + Ref<Texture> file_thumbnail; + + bool use_thumbnails=!display_mode->is_pressed(); + + if (use_thumbnails) { //thumbnails + + files->set_max_columns(0); + files->set_icon_mode(ItemList::ICON_MODE_TOP); + files->set_fixed_column_width(thumbnail_size*3/2); + files->set_max_text_lines(2); + files->set_min_icon_size(Size2(thumbnail_size,thumbnail_size)); + + if (!has_icon("ResizedFolder","EditorIcons")) { + Ref<ImageTexture> folder = get_icon("FolderBig","EditorIcons"); + Image img = folder->get_data(); + img.resize(thumbnail_size,thumbnail_size); + Ref<ImageTexture> resized_folder = Ref<ImageTexture>( memnew( ImageTexture)); + resized_folder->create_from_image(img,0); + Theme::get_default()->set_icon("ResizedFolder","EditorIcons",resized_folder); + } + + folder_thumbnail = get_icon("ResizedFolder","EditorIcons"); + + if (!has_icon("ResizedFile","EditorIcons")) { + Ref<ImageTexture> file = get_icon("FileBig","EditorIcons"); + Image img = file->get_data(); + img.resize(thumbnail_size,thumbnail_size); + Ref<ImageTexture> resized_file = Ref<ImageTexture>( memnew( ImageTexture)); + resized_file->create_from_image(img,0); + Theme::get_default()->set_icon("ResizedFile","EditorIcons",resized_file); + } + + file_thumbnail = get_icon("ResizedFile","EditorIcons"); + + } else { + + files->set_icon_mode(ItemList::ICON_MODE_LEFT); + files->set_max_columns(1); + files->set_max_text_lines(1); + files->set_fixed_column_width(0); + files->set_min_icon_size(Size2()); + } - String path = sel->get_metadata(0); - if (ResourceLoader::get_resource_type(path)=="PackedScene") { - editor->open_request(path); + if (path!="res://") { + + if (use_thumbnails) { + files->add_item("..",folder_thumbnail,true); + } else { + files->add_item("..",get_icon("folder","FileDialog"),true); + } + + String bd = path.get_base_dir(); + if (bd!="res://" && !bd.ends_with("/")) + bd+="/"; + + files->set_item_metadata(files->get_item_count()-1,bd); + } + + for(int i=0;i<efd->get_subdir_count();i++) { + + String dname=efd->get_subdir(i)->get_name(); + + + if (use_thumbnails) { + files->add_item(dname,folder_thumbnail,true); + } else { + files->add_item(dname,get_icon("folder","FileDialog"),true); + } + + files->set_item_metadata(files->get_item_count()-1,path.plus_file(dname)+"/"); + + if (cselection.has(dname)) + files->select(files->get_item_count()-1,false); + } + + for(int i=0;i<efd->get_file_count();i++) { + + String fname=efd->get_file(i); + String fp = path.plus_file(fname); + + + String type = efd->get_file_type(i); + Ref<Texture> type_icon; + + if (has_icon(type,"EditorIcons")) { + type_icon=get_icon(type,"EditorIcons"); + } else { + type_icon=get_icon("Object","EditorIcons"); + } + + if (use_thumbnails) { + files->add_item(fname,file_thumbnail,true); + files->set_item_metadata(files->get_item_count()-1,fp); + files->set_item_tag_icon(files->get_item_count()-1,type_icon); + EditorResourcePreview::get_singleton()->queue_resource_preview(fp,this,"_thumbnail_done",files->get_item_count()-1); + } else { + files->add_item(fname,type_icon,true); + files->set_item_metadata(files->get_item_count()-1,fp); + + } + + if (cselection.has(fname)) + files->select(files->get_item_count()-1,false); + + } + + +} + +void ScenesDock::_select_file(int p_idx) { + + files->select(p_idx,true); + _open_pressed(); +} + +void ScenesDock::_go_to_tree() { + + tree->show(); + files->hide(); + path_hb->hide(); + _update_tree(); + tree->grab_focus(); + tree->ensure_cursor_is_visible(); + button_favorite->show(); + button_fav_up->show(); + button_fav_down->show(); + button_open->hide(); + button_instance->hide(); + button_open->hide(); + file_options->hide(); + tree_mode=true; +} + +void ScenesDock::_go_to_dir(const String& p_dir){ + + DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (da->change_dir(p_dir)==OK) { + path=da->get_current_dir(); + _update_files(false); + } + current_path->set_text(path); + memdelete(da); + + +} +void ScenesDock::_fs_changed() { + + button_hist_prev->set_disabled(history_pos==0); + button_hist_next->set_disabled(history_pos+1==history.size()); + scanning_vb->hide(); + + if (tree_mode) { + + tree->show(); + button_favorite->show(); + button_fav_up->show(); + button_fav_down->show(); + _update_tree(); + } else { + files->show(); + path_hb->show(); + button_instance->show(); + button_open->show(); + file_options->show(); + _update_files(true); + } + + set_process(false); +} + +void ScenesDock::_set_scannig_mode() { + + tree->hide(); + button_favorite->hide(); + button_fav_up->hide(); + button_fav_down->hide(); + button_instance->hide(); + button_open->hide(); + file_options->hide(); + button_hist_prev->set_disabled(true); + button_hist_next->set_disabled(true); + scanning_vb->show(); + path_hb->hide(); + files->hide(); + set_process(true); + if (EditorFileSystem::get_singleton()->is_scanning()) { + scanning_progress->set_val(EditorFileSystem::get_singleton()->get_scanning_progress()*100); + } else { + scanning_progress->set_val(0); + } + +} + +void ScenesDock::_fw_history() { + + if (history_pos<history.size()-1) + history_pos++; + + path=history[history_pos]; + + if (tree_mode) { + _update_tree(); + tree->grab_focus(); + tree->ensure_cursor_is_visible(); } else { + _update_files(false); + current_path->set_text(path); + } - /* + button_hist_prev->set_disabled(history_pos==0); + button_hist_next->set_disabled(history_pos+1==history.size()); - RES res = ResourceLoader::load(path); - if (res.is_null()) { +} + +void ScenesDock::_bw_history() { + + if (history_pos>0) + history_pos--; + path=history[history_pos]; + + if (tree_mode) { + _update_tree(); + tree->grab_focus(); + tree->ensure_cursor_is_visible(); + } else { + _update_files(false); + current_path->set_text(path); + } + button_hist_prev->set_disabled(history_pos==0); + button_hist_next->set_disabled(history_pos+1==history.size()); + +} + +void ScenesDock::_push_to_history() { + + history.resize(history_pos+1); + if (history[history_pos]!=path) { + history.push_back(path); + history_pos++; + } + + button_hist_prev->set_disabled(history_pos==0); + button_hist_next->set_disabled(history_pos+1==history.size()); + +} + + +void ScenesDock::_find_inside_move_files(EditorFileSystemDirectory *efsd,Vector<String>& files) { + + for(int i=0;i<efsd->get_subdir_count();i++) { + _find_inside_move_files(efsd->get_subdir(i),files); + } + for(int i=0;i<efsd->get_file_count();i++) { + files.push_back(efsd->get_file_path(i)); + } + +} + +void ScenesDock::_find_remaps(EditorFileSystemDirectory *efsd,Map<String,String> &renames,List<String>& to_remaps) { + + for(int i=0;i<efsd->get_subdir_count();i++) { + _find_remaps(efsd->get_subdir(i),renames,to_remaps); + } + for(int i=0;i<efsd->get_file_count();i++) { + Vector<String> deps=efsd->get_file_deps(i); + for(int j=0;j<deps.size();j++) { + if (renames.has(deps[j])) { + to_remaps.push_back(efsd->get_file_path(i)); + break; + } + } + } +} + + +void ScenesDock::_rename_operation(const String& p_to_path) { + + if (move_files[0]==p_to_path) { + EditorNode::get_singleton()->show_warning("Same source and destination files, doing nothing."); + return; + } + if (FileAccess::exists(p_to_path)) { + EditorNode::get_singleton()->show_warning("Target file exists, can't overwrite. Delete first."); + return; + } + + Map<String,String> renames; + renames[move_files[0]]=p_to_path; + + List<String> remap; + + _find_remaps(EditorFileSystem::get_singleton()->get_filesystem(),renames,remap); + print_line("found files to remap: "+itos(remap.size())); + + //perform remaps + for(List<String>::Element *E=remap.front();E;E=E->next()) { + + Error err = ResourceLoader::rename_dependencies(E->get(),renames); + print_line("remapping: "+E->get()); + + if (err!=OK) { + EditorNode::get_singleton()->add_io_error("Can't rename deps for:\n"+E->get()+"\n"); + } + } + + //finally, perform moves + + DirAccess *da=DirAccess::create(DirAccess::ACCESS_RESOURCES); + + Error err = da->rename(move_files[0],p_to_path); + print_line("moving file "+move_files[0]+" to "+p_to_path); + if (err!=OK) { + EditorNode::get_singleton()->add_io_error("Error moving file:\n"+move_files[0]+"\n"); + } + + //rescan everything + memdelete(da); + print_line("call rescan!"); + _rescan(); +} + + +void ScenesDock::_move_operation(const String& p_to_path) { + + if (p_to_path==path) { + EditorNode::get_singleton()->show_warning("Same source and destination paths, doing nothing."); + return; + } + + //find files inside dirs to be moved + + Vector<String> inside_files; + + for(int i=0;i<move_dirs.size();i++) { + if (p_to_path.begins_with(move_dirs[i])) { + EditorNode::get_singleton()->show_warning("Can't move directories to within themselves"); return; - }*/ + } - editor->load_resource(path); + EditorFileSystemDirectory *efsd=EditorFileSystem::get_singleton()->get_path(move_dirs[i]); + if (!efsd) + continue; + _find_inside_move_files(efsd,inside_files); } -// emit_signal("open",path); + //make list of remaps + Map<String,String> renames; + String repfrom=path=="res://"?path:String(path+"/"); + String repto=p_to_path=="res://"?p_to_path:String(p_to_path+"/"); + + for(int i=0;i<move_files.size();i++) { + renames[move_files[i]]=move_files[i].replace_first(repfrom,repto); + print_line("move file "+move_files[i]+" -> "+renames[move_files[i]]); + } + for(int i=0;i<inside_files.size();i++) { + renames[inside_files[i]]=inside_files[i].replace_first(repfrom,repto); + print_line("inside file "+inside_files[i]+" -> "+renames[inside_files[i]]); + } + + //make list of files that will be run the remapping + List<String> remap; + + _find_remaps(EditorFileSystem::get_singleton()->get_filesystem(),renames,remap); + print_line("found files to remap: "+itos(remap.size())); + + //perform remaps + for(List<String>::Element *E=remap.front();E;E=E->next()) { + + Error err = ResourceLoader::rename_dependencies(E->get(),renames); + print_line("remapping: "+E->get()); + + if (err!=OK) { + EditorNode::get_singleton()->add_io_error("Can't rename deps for:\n"+E->get()+"\n"); + } + } + + //finally, perform moves + + DirAccess *da=DirAccess::create(DirAccess::ACCESS_RESOURCES); + + for(int i=0;i<move_files.size();i++) { + + String to = move_files[i].replace_first(repfrom,repto); + Error err = da->rename(move_files[i],to); + print_line("moving file "+move_files[i]+" to "+to); + if (err!=OK) { + EditorNode::get_singleton()->add_io_error("Error moving file:\n"+move_files[i]+"\n"); + } + } + + for(int i=0;i<move_dirs.size();i++) { + + String to = p_to_path.plus_file(move_dirs[i].get_file()); + Error err = da->rename(move_dirs[i],to); + print_line("moving dir "+move_dirs[i]+" to "+to); + if (err!=OK) { + EditorNode::get_singleton()->add_io_error("Error moving dir:\n"+move_dirs[i]+"\n"); + } + } + + memdelete(da); + //rescan everything + print_line("call rescan!"); + _rescan(); } -void ScenesDock::_save_favorites() { +void ScenesDock::_file_option(int p_option) { + + switch(p_option) { + + case FILE_DEPENDENCIES: { + + int idx = files->get_current(); + if (idx<0 || idx>=files->get_item_count()) + break; + String path = files->get_item_metadata(idx); + deps_editor->edit(path); + } break; + case FILE_OWNERS: { + + int idx = files->get_current(); + if (idx<0 || idx>=files->get_item_count()) + break; + String path = files->get_item_metadata(idx); + owners_editor->show(path); + } break; + case FILE_MOVE: { + + move_dirs.clear();; + move_files.clear(); + + for(int i=0;i<files->get_item_count();i++) { + + String path = files->get_item_metadata(i); + if (!files->is_selected(i)) + continue; + + if (files->get_item_text(i)=="..") { + EditorNode::get_singleton()->show_warning("Can't operate on '..'"); + return; + } + + if (path.ends_with("/")) { + move_dirs.push_back(path.substr(0,path.length()-1)); + } else { + move_files.push_back(path); + } + } + + + if (move_dirs.empty() && move_files.size()==1) { + + rename_dialog->clear_filters(); + rename_dialog->add_filter("*."+move_files[0].extension()); + rename_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); + rename_dialog->set_current_path(move_files[0]); + rename_dialog->popup_centered_ratio(); + rename_dialog->set_title("Pick New Name and Location For: "+move_files[0].get_file()); + + + } else { + //just move + move_dialog->popup_centered_ratio(); + } + + + } break; + case FILE_REMOVE: { - String path = Globals::get_singleton()->get_resource_path()+"/favorites.cfg"; - FileAccess *f=FileAccess::open(path,FileAccess::WRITE); - ERR_FAIL_COND(!f); - for(Set<String>::Element *E=favorites.front();E;E=E->next() ) { + Vector<String> torem; + + for(int i=0;i<files->get_item_count();i++) { + + String path = files->get_item_metadata(i); + if (path.ends_with("/") || !files->is_selected(i)) + continue; + torem.push_back(path); + } + + if (torem.empty()) { + EditorNode::get_singleton()->show_warning("No files selected!"); + break; + } + + remove_dialog->show(torem); + //1) find if used + //2) warn + + } break; + case FILE_INFO: { + + } break; - CharString utf8f = E->get().utf8(); - f->store_buffer((const uint8_t*)utf8f.get_data(),utf8f.length()); - f->store_8('\n'); } +} + +void ScenesDock::_open_pressed(){ + + + if (tree_mode) { + + TreeItem *sel = tree->get_selected(); + if (!sel) { + return; + } + path = sel->get_metadata(0); + /*if (path!="res://" && path.ends_with("/")) { + path=path.substr(0,path.length()-1); + }*/ + + tree_mode=false; + + tree->hide(); + files->show(); + path_hb->show(); + button_favorite->hide(); + button_fav_up->hide(); + button_fav_down->hide(); + button_instance->show(); + button_open->show(); + file_options->show(); + + _update_files(false); + + current_path->set_text(path); + + _push_to_history(); + + + } else { + + int idx=-1; + for(int i=0;i<files->get_item_count();i++) { + if (files->is_selected(i)) { + idx=i; + break; + } + } + + if (idx<0) + return; + + + + String path = files->get_item_metadata(idx); + + if (path.ends_with("/")) { + if (path!="res://") { + path=path.substr(0,path.length()-1); + } + this->path=path; + _update_files(false); + current_path->set_text(path); + _push_to_history(); + } else { + + if (ResourceLoader::get_resource_type(path)=="PackedScene") { + + editor->open_request(path); + } else { + + editor->load_resource(path); + } + } + } + +// emit_signal("open",path); - f->close(); - memdelete(f); } + void ScenesDock::_rescan() { + _set_scannig_mode(); EditorFileSystem::get_singleton()->scan(); + +} + +void ScenesDock::fix_dependencies(const String& p_for_file) { + deps_editor->edit(p_for_file); +} + +void ScenesDock::open(const String& p_path) { + + + String npath; + String nfile; + + if (p_path.ends_with("/")) { + + if (p_path!="res://") + npath=p_path.substr(0,p_path.length()-1); + else + npath="res://"; + } else { + nfile=p_path.get_file(); + npath=p_path.get_base_dir(); + } + + path=npath; + + if (tree_mode && nfile=="") { + _update_tree(); + tree->grab_focus(); + tree->call_deferred("ensure_cursor_is_visible"); + _push_to_history(); + return; + } else if (tree_mode){ + _update_tree(); + tree->grab_focus(); + tree->ensure_cursor_is_visible(); + _open_pressed(); + current_path->set_text(path); + } else { + _update_files(false); + _push_to_history(); + } + + for(int i=0;i<files->get_item_count();i++) { + + String md = files->get_item_metadata(i); + if (md==p_path) { + files->select(i,true); + files->ensure_current_is_visible(); + break; + } + } + } void ScenesDock::_bind_methods() { ObjectTypeDB::bind_method(_MD("_update_tree"),&ScenesDock::_update_tree); ObjectTypeDB::bind_method(_MD("_rescan"),&ScenesDock::_rescan); - ObjectTypeDB::bind_method(_MD("_favorites_toggled"),&ScenesDock::_favorites_toggled); - ObjectTypeDB::bind_method(_MD("_favorite_toggled"),&ScenesDock::_favorite_toggled); + ObjectTypeDB::bind_method(_MD("_favorites_pressed"),&ScenesDock::_favorites_pressed); ObjectTypeDB::bind_method(_MD("_instance_pressed"),&ScenesDock::_instance_pressed); ObjectTypeDB::bind_method(_MD("_open_pressed"),&ScenesDock::_open_pressed); - ObjectTypeDB::bind_method(_MD("_save_favorites"),&ScenesDock::_save_favorites); + + ObjectTypeDB::bind_method(_MD("_thumbnail_done"),&ScenesDock::_thumbnail_done); + ObjectTypeDB::bind_method(_MD("_select_file"), &ScenesDock::_select_file); + ObjectTypeDB::bind_method(_MD("_go_to_tree"), &ScenesDock::_go_to_tree); + ObjectTypeDB::bind_method(_MD("_go_to_dir"), &ScenesDock::_go_to_dir); + ObjectTypeDB::bind_method(_MD("_change_file_display"), &ScenesDock::_change_file_display); + ObjectTypeDB::bind_method(_MD("_fw_history"), &ScenesDock::_fw_history); + ObjectTypeDB::bind_method(_MD("_bw_history"), &ScenesDock::_bw_history); + ObjectTypeDB::bind_method(_MD("_fs_changed"), &ScenesDock::_fs_changed); + ObjectTypeDB::bind_method(_MD("_dir_selected"), &ScenesDock::_dir_selected); + ObjectTypeDB::bind_method(_MD("_fav_up_pressed"), &ScenesDock::_fav_up_pressed); + ObjectTypeDB::bind_method(_MD("_fav_down_pressed"), &ScenesDock::_fav_down_pressed); + ObjectTypeDB::bind_method(_MD("_file_option"), &ScenesDock::_file_option); + ObjectTypeDB::bind_method(_MD("_move_operation"), &ScenesDock::_move_operation); + ObjectTypeDB::bind_method(_MD("_rename_operation"), &ScenesDock::_rename_operation); ADD_SIGNAL(MethodInfo("instance")); ADD_SIGNAL(MethodInfo("open")); @@ -271,153 +1098,156 @@ ScenesDock::ScenesDock(EditorNode *p_editor) { HBoxContainer *toolbar_hbc = memnew( HBoxContainer ); add_child(toolbar_hbc); + button_hist_prev = memnew( ToolButton ); + toolbar_hbc->add_child(button_hist_prev); + button_hist_prev->set_disabled(true); + button_hist_prev->set_tooltip("Previous Directory"); + + button_hist_next = memnew( ToolButton ); + toolbar_hbc->add_child(button_hist_next); + button_hist_next->set_disabled(true); + button_hist_prev->set_focus_mode(FOCUS_NONE); + button_hist_next->set_focus_mode(FOCUS_NONE); + button_hist_next->set_tooltip("Next Directory"); + button_reload = memnew( Button ); button_reload->set_flat(true); button_reload->connect("pressed",this,"_rescan"); toolbar_hbc->add_child(button_reload); + button_reload->set_focus_mode(FOCUS_NONE); + button_reload->set_tooltip("Re-Scan Filesystem"); + + toolbar_hbc->add_spacer(); + + button_fav_up = memnew( ToolButton ); + button_fav_up->set_flat(true); + toolbar_hbc->add_child(button_fav_up); + button_fav_up->set_disabled(true); + button_fav_up->connect("pressed",this,"_fav_up_pressed"); + button_fav_up->set_tooltip("Move Favorite Up"); + + button_fav_down = memnew( ToolButton ); + button_fav_down->set_flat(true); + toolbar_hbc->add_child(button_fav_down); + button_fav_down->set_disabled(true); + button_fav_down->connect("pressed",this,"_fav_down_pressed"); + button_fav_down->set_tooltip("Move Favorite Down"); button_favorite = memnew( Button ); button_favorite->set_flat(true); button_favorite->set_toggle_mode(true); - button_favorite->connect("toggled",this,"_favorites_toggled"); + button_favorite->connect("pressed",this,"_favorites_pressed"); toolbar_hbc->add_child(button_favorite); + button_favorite->set_tooltip("Toggle folder status as Favorite"); + + button_favorite->set_focus_mode(FOCUS_NONE); + button_fav_up->set_focus_mode(FOCUS_NONE); + button_fav_down->set_focus_mode(FOCUS_NONE); - toolbar_hbc->add_spacer(); button_open = memnew( Button ); button_open->set_flat(true); button_open->connect("pressed",this,"_open_pressed"); toolbar_hbc->add_child(button_open); + button_open->hide(); + button_open->set_focus_mode(FOCUS_NONE); + button_open->set_tooltip("Open the selected file.\nOpen as scene if a scene, or as resource otherwise."); + button_instance = memnew( Button ); button_instance->set_flat(true); button_instance->connect("pressed",this,"_instance_pressed"); toolbar_hbc->add_child(button_instance); + button_instance->hide(); + button_instance->set_focus_mode(FOCUS_NONE); + button_instance->set_tooltip("Instance the selected scene(s) as child of the selected node."); + + + file_options = memnew( MenuButton ); + toolbar_hbc->add_child(file_options); + file_options->get_popup()->add_item("Rename or Move",FILE_MOVE); + file_options->get_popup()->add_item("Delete",FILE_REMOVE); + file_options->get_popup()->add_separator(); + file_options->get_popup()->add_item("Edit Dependencies",FILE_DEPENDENCIES); + file_options->get_popup()->add_item("View Owners",FILE_OWNERS); + //file_options->get_popup()->add_item("Info",FILE_INFO); + file_options->hide(); + file_options->set_focus_mode(FOCUS_NONE); + file_options->set_tooltip("Miscenaneous options related to resources on disk."); tree = memnew( Tree ); - tree_filter=memnew( ScenesDockFilter() ); - tree_filter->connect("filter_changed", this, "_update_tree"); - add_child(tree_filter); + + tree->set_hide_root(true); add_child(tree); + tree->set_v_size_flags(SIZE_EXPAND_FILL); tree->connect("item_edited",this,"_favorite_toggled"); tree->connect("item_activated",this,"_open_pressed"); - - timer = memnew( Timer ); - timer->set_one_shot(true); - timer->set_wait_time(2); - timer->connect("timeout",this,"_save_favorites"); - add_child(timer); - + tree->connect("cell_selected",this,"_dir_selected"); + + files = memnew( ItemList ); + files->set_v_size_flags(SIZE_EXPAND_FILL); + files->set_select_mode(ItemList::SELECT_MULTI); + + path_hb = memnew( HBoxContainer ); + button_back = memnew( ToolButton ); + path_hb->add_child(button_back); + current_path=memnew( LineEdit ); + current_path->set_h_size_flags(SIZE_EXPAND_FILL); + path_hb->add_child(current_path); + display_mode = memnew( ToolButton ); + path_hb->add_child(display_mode); + display_mode->set_toggle_mode(true); + add_child(path_hb); + path_hb->hide(); + + + add_child(files); + files->hide(); + + scanning_vb = memnew( VBoxContainer ); + Label *slabel = memnew( Label ); + slabel->set_text("Scanning Files,\nPlease Wait.."); + slabel->set_align(Label::ALIGN_CENTER); + scanning_vb->add_child(slabel); + scanning_progress = memnew( ProgressBar ); + scanning_vb->add_child(scanning_progress); + add_child(scanning_vb); + scanning_vb->hide(); + + + + deps_editor = memnew( DependencyEditor ); + add_child(deps_editor); + + owners_editor = memnew( DependencyEditorOwners); + add_child(owners_editor); + + remove_dialog = memnew( DependencyRemoveDialog); + add_child(remove_dialog); + + move_dialog = memnew( EditorDirDialog ); + add_child(move_dialog); + move_dialog->connect("dir_selected",this,"_move_operation"); + move_dialog->get_ok()->set_text("Move"); + + rename_dialog = memnew( EditorFileDialog ); + rename_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); + rename_dialog->connect("file_selected",this,"_rename_operation"); + add_child(rename_dialog); updating_tree=false; + initialized=false; -} - -ScenesDock::~ScenesDock() { - -} - -void ScenesDockFilter::_setup_filters() { - - filter_option->clear(); - filter_option->add_item("Path"); - filter_option->add_item("Name"); - filter_option->add_item("Folder"); -#if 0 - List<String> extensions; - ResourceLoader::get_recognized_extensions_for_type("",&extensions); - - file_filter->add_item("All Files (*)"); - filters.push_back("*"); - - List<String> filter_texts; - for(int i=0;i<extensions.size();i++) { - filter_texts.push_back("*."+extensions[i]+" ; "+extensions[i].to_upper()); - filters.push_back(extensions[i]); - } - for(int i=0;i<filter_texts.size();i++) { - - String flt=filter_texts[i].get_slice(";",0).strip_edges(); - String desc=filter_texts[i].get_slice(";",1).strip_edges(); - if (desc.length()) - file_filter->add_item(desc+" ( "+flt+" )"); - else - file_filter->add_item("( "+flt+" )"); - } -#endif -} - -void ScenesDockFilter::_command(int p_command) { - switch (p_command) { - - case CMD_CLEAR_FILTER: { - if (search_box->get_text()!="") { - search_box->clear(); - emit_signal("filter_changed"); - } - }break; - } -} + history.push_back("res://"); + history_pos=0; + tree_mode=true; -void ScenesDockFilter::_search_text_changed(const String &p_newtext) { - emit_signal("filter_changed"); -} -String ScenesDockFilter::get_search_term() { - return search_box->get_text().strip_edges(); } -ScenesDockFilter::FilterOption ScenesDockFilter::get_file_filter() { - return _current_filter; -} - -void ScenesDockFilter::_file_filter_selected(int p_idx) { - FilterOption selected = (FilterOption)(filter_option->get_selected()); - if (_current_filter != selected ) { - _current_filter = selected; - emit_signal("filter_changed"); - } -} - -void ScenesDockFilter::_notification(int p_what) { - switch(p_what) { - case NOTIFICATION_ENTER_TREE: { - clear_search_button->set_icon(get_icon("CloseHover","EditorIcons")); - } break; - } -} - -void ScenesDockFilter::_bind_methods() { - - ObjectTypeDB::bind_method(_MD("_command"),&ScenesDockFilter::_command); - ObjectTypeDB::bind_method(_MD("_search_text_changed"), &ScenesDockFilter::_search_text_changed); - ObjectTypeDB::bind_method(_MD("_file_filter_selected"), &ScenesDockFilter::_file_filter_selected); - - ADD_SIGNAL( MethodInfo("filter_changed") ); -} - -ScenesDockFilter::ScenesDockFilter() { - - _current_filter = FILTER_PATH; - - filter_option = memnew( OptionButton ); - filter_option->set_custom_minimum_size(Size2(60,10)); - filter_option->set_clip_text(true); - filter_option->connect("item_selected", this, "_file_filter_selected"); - add_child(filter_option); - - _setup_filters(); - - search_box = memnew( LineEdit ); - search_box->connect("text_changed",this,"_search_text_changed"); - search_box->set_h_size_flags(SIZE_EXPAND_FILL); - add_child(search_box); - - clear_search_button = memnew( ToolButton ); - clear_search_button->connect("pressed",this,"_command",make_binds(CMD_CLEAR_FILTER)); - add_child(clear_search_button); +ScenesDock::~ScenesDock() { } diff --git a/tools/editor/scenes_dock.h b/tools/editor/scenes_dock.h index 9849f5ace0..d045124bf7 100644 --- a/tools/editor/scenes_dock.h +++ b/tools/editor/scenes_dock.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,40 +36,112 @@ #include "scene/gui/tool_button.h" #include "scene/gui/option_button.h" #include "scene/gui/box_container.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/item_list.h" +#include "scene/gui/progress_bar.h" + #include "os/dir_access.h" #include "os/thread.h" #include "editor_file_system.h" - +#include "editor_dir_dialog.h" +#include "dependency_editor.h" class EditorNode; -class ScenesDockFilter; + class ScenesDock : public VBoxContainer { OBJ_TYPE( ScenesDock, VBoxContainer ); + enum FileMenu { + FILE_DEPENDENCIES, + FILE_OWNERS, + FILE_MOVE, + FILE_REMOVE, + FILE_REIMPORT, + FILE_INFO + }; + + + VBoxContainer *scanning_vb; + ProgressBar *scanning_progress; + EditorNode *editor; Set<String> favorites; Button *button_reload; Button *button_instance; Button *button_favorite; + Button *button_fav_up; + Button *button_fav_down; Button *button_open; - Timer *timer; + Button *button_back; + Button *display_mode; + Button *button_hist_next; + Button *button_hist_prev; + LineEdit *current_path; + HBoxContainer *path_hb; + + MenuButton *file_options; + + + DependencyEditor *deps_editor; + DependencyEditorOwners *owners_editor; + DependencyRemoveDialog *remove_dialog; + + EditorDirDialog *move_dialog; + EditorFileDialog *rename_dialog; - ScenesDockFilter *tree_filter; + Vector<String> move_dirs; + Vector<String> move_files; + + + Vector<String> history; + int history_pos; + + String path; + + bool initialized; bool updating_tree; - Tree * tree; + Tree * tree; //directories + ItemList *files; + + bool tree_mode; + + void _go_to_tree(); + void _go_to_dir(const String& p_dir); + void _select_file(int p_idx); + bool _create_tree(TreeItem *p_parent,EditorFileSystemDirectory *p_dir); + void _thumbnail_done(const String& p_path,const Ref<Texture>& p_preview, const Variant& p_udata); + void _find_inside_move_files(EditorFileSystemDirectory *efsd,Vector<String>& files); + void _find_remaps(EditorFileSystemDirectory *efsd,Map<String,String> &renames,List<String>& to_remaps); + + void _rename_operation(const String& p_to_path); + void _move_operation(const String& p_to_path); + + + void _file_option(int p_option); + void _update_files(bool p_keep_selection); + void _change_file_display(); + void _fs_changed(); + void _fw_history(); + void _bw_history(); + void _push_to_history(); + + void _fav_up_pressed(); + void _fav_down_pressed(); + void _dir_selected(); void _update_tree(); void _rescan(); - void _favorites_toggled(bool); - void _favorite_toggled(); + void _set_scannig_mode(); + + void _favorites_pressed(); void _instance_pressed(); void _open_pressed(); - void _save_favorites(); + protected: void _notification(int p_what); @@ -77,48 +149,14 @@ protected: public: String get_selected_path() const; + void open(const String& p_path); + + void fix_dependencies(const String& p_for_file); + ScenesDock(EditorNode *p_editor); ~ScenesDock(); }; -class ScenesDockFilter : public HBoxContainer { - - OBJ_TYPE( ScenesDockFilter, HBoxContainer ); - -private: - friend class ScenesDock; - - enum Command { - CMD_CLEAR_FILTER, - }; - - Tree *tree; - OptionButton *filter_option; - LineEdit *search_box; - ToolButton *clear_search_button; - - enum FilterOption { - FILTER_PATH, // NAME or Folder - FILTER_NAME, - FILTER_FOLDER, - }; - FilterOption _current_filter; - //Vector<String> filters; - - void _command(int p_command); - void _search_text_changed(const String& p_newtext); - void _setup_filters(); - void _file_filter_selected(int p_idx); - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - String get_search_term(); - FilterOption get_file_filter(); - ScenesDockFilter(); -}; #endif // SCENES_DOCK_H diff --git a/tools/editor/script_create_dialog.cpp b/tools/editor/script_create_dialog.cpp index 89c0bd6ab4..622150ab68 100644 --- a/tools/editor/script_create_dialog.cpp +++ b/tools/editor/script_create_dialog.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -74,17 +74,17 @@ bool ScriptCreateDialog::_validate(const String& p_string) { void ScriptCreateDialog::_class_name_changed(const String& p_name) { if (!_validate(parent_name->get_text())) { - error_label->set_text("INVALID PARENT CLASS NAME"); + error_label->set_text("Invaild parent class name"); error_label->add_color_override("font_color",Color(1,0.4,0.0,0.8)); } else if (class_name->is_editable()) { if (class_name->get_text()=="") { error_label->set_text("Valid Chars: a-z A-Z 0-9 _"); error_label->add_color_override("font_color",Color(1,1,1,0.6)); } else if (!_validate(class_name->get_text())) { - error_label->set_text("INVALID CLASS NAME"); + error_label->set_text("Invalid class name"); error_label->add_color_override("font_color",Color(1,0.2,0.2,0.8)); } else { - error_label->set_text("Name is Valid"); + error_label->set_text("Valid Name"); error_label->add_color_override("font_color",Color(0,1.0,0.8,0.8)); } } else { @@ -99,12 +99,12 @@ void ScriptCreateDialog::ok_pressed() { if (class_name->is_editable() && !_validate(class_name->get_text())) { alert->set_text("Class Name is Invalid!"); - alert->popup_centered(Size2(200,60)); + alert->popup_centered_minsize(); return; } if (!_validate(parent_name->get_text())) { alert->set_text("Parent Class Name is Invalid!"); - alert->popup_centered(Size2(200,60)); + alert->popup_centered_minsize(); return; @@ -134,7 +134,7 @@ void ScriptCreateDialog::ok_pressed() { if (!path_valid) { alert->set_text("Path is Invalid!"); - alert->popup_centered(Size2(200,60)); + alert->popup_centered_minsize(); return; } @@ -142,7 +142,7 @@ void ScriptCreateDialog::ok_pressed() { if (err!=OK) { alert->set_text("Could not create script in filesystem: "+String("")); - alert->popup_centered(Size2(200,60)); + alert->popup_centered_minsize(); return; } scr->set_path(lpath); @@ -184,7 +184,7 @@ void ScriptCreateDialog::_built_in_pressed() { void ScriptCreateDialog::_browse_path() { - file_browse->set_mode(FileDialog::MODE_SAVE_FILE); + file_browse->set_mode(EditorFileDialog::MODE_SAVE_FILE); file_browse->clear_filters(); List<String> extensions; @@ -363,9 +363,9 @@ ScriptCreateDialog::ScriptCreateDialog() { set_size(Size2(200,150)); set_hide_on_ok(false); - set_title("Create Script for Node..");; + set_title("Create Script for Node"); - file_browse = memnew( FileDialog ); + file_browse = memnew( EditorFileDialog ); file_browse->connect("file_selected",this,"_file_selected"); add_child(file_browse); get_ok()->set_text("Create"); diff --git a/tools/editor/script_create_dialog.h b/tools/editor/script_create_dialog.h index 42fa9d68c3..59fde8fbd5 100644 --- a/tools/editor/script_create_dialog.h +++ b/tools/editor/script_create_dialog.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,7 +32,7 @@ #include "scene/gui/dialogs.h" #include "scene/gui/line_edit.h" #include "scene/gui/option_button.h" -#include "scene/gui/file_dialog.h" +#include "tools/editor/editor_file_dialog.h" #include "scene/gui/check_button.h" class ScriptCreateDialog : public ConfirmationDialog { @@ -44,7 +44,7 @@ class ScriptCreateDialog : public ConfirmationDialog { LineEdit *parent_name; OptionButton *language_menu; LineEdit *file_path; - FileDialog *file_browse; + EditorFileDialog *file_browse; CheckButton *internal; VBoxContainer *path_vb; AcceptDialog *alert; diff --git a/tools/editor/script_editor_debugger.cpp b/tools/editor/script_editor_debugger.cpp index 024377ad18..60f2afa2c2 100644 --- a/tools/editor/script_editor_debugger.cpp +++ b/tools/editor/script_editor_debugger.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -169,6 +169,17 @@ void ScriptEditorDebugger::_scene_tree_request() { } +void ScriptEditorDebugger::_video_mem_request() { + + ERR_FAIL_COND(connection.is_null()); + ERR_FAIL_COND(!connection->is_connected()); + + Array msg; + msg.push_back("request_video_mem"); + ppeer->put_var(msg); + +} + Size2 ScriptEditorDebugger::get_minimum_size() const { Size2 ms = Control::get_minimum_size(); @@ -241,6 +252,33 @@ void ScriptEditorDebugger::_parse_message(const String& p_msg,const Array& p_dat lv[level]=it; } + le_clear->set_disabled(false); + le_set->set_disabled(false); + + } else if (p_msg=="message:video_mem") { + + vmem_tree->clear(); + TreeItem* root=vmem_tree->create_item(); + + int total=0; + + for(int i=0;i<p_data.size();i+=4) { + + TreeItem *it = vmem_tree->create_item(root); + String type=p_data[i+1]; + int bytes=p_data[i+3].operator int(); + it->set_text(0,p_data[i+0]); //path + it->set_text(1,type); //type + it->set_text(2,p_data[i+2]); //type + it->set_text(3,String::humanize_size(bytes)); //type + total+=bytes; + + if (has_icon(type,"EditorIcons")) + it->set_icon(0,get_icon(type,"EditorIcons")); + } + + vmem_total->set_tooltip("Bytes: "+itos(total)); + vmem_total->set_text(String::humanize_size(total)); } else if (p_msg=="stack_dump") { @@ -342,6 +380,63 @@ void ScriptEditorDebugger::_parse_message(const String& p_msg,const Array& p_dat perf_history.push_front(p); perf_draw->update(); + } else if (p_msg=="error") { + + Array err = p_data[0]; + + Array vals; + vals.push_back(err[0]); + vals.push_back(err[1]); + vals.push_back(err[2]); + vals.push_back(err[3]); + + bool warning = err[9]; + bool e; + String time = String("%d:%02d:%02d:%04d").sprintf(vals,&e); + String txt=time+" - "+String(err[8]); + + String tooltip="Type:"+String(warning?"Warning":"Error"); + tooltip+="\nDescription: "+String(err[8]); + tooltip+="\nTime: "+time; + tooltip+="\nC Error: "+String(err[7]); + tooltip+="\nC Source: "+String(err[5])+":"+String(err[6]); + tooltip+="\nC Function: "+String(err[4]); + + + + error_list->add_item(txt,EditorNode::get_singleton()->get_gui_base()->get_icon(warning?"Warning":"Error","EditorIcons")); + error_list->set_item_tooltip( error_list->get_item_count() -1,tooltip ); + + int scc = p_data[1]; + + Array stack; + stack.resize(scc); + for(int i=0;i<scc;i++) { + stack[i]=p_data[2+i]; + } + + error_list->set_item_metadata( error_list->get_item_count() -1,stack ); + + error_count++; + /* + int count = p_data[1]; + + Array cstack; + + OutputError oe = errors.front()->get(); + + packet_peer_stream->put_var(oe.hr); + packet_peer_stream->put_var(oe.min); + packet_peer_stream->put_var(oe.sec); + packet_peer_stream->put_var(oe.msec); + packet_peer_stream->put_var(oe.source_func); + packet_peer_stream->put_var(oe.source_file); + packet_peer_stream->put_var(oe.source_line); + packet_peer_stream->put_var(oe.error); + packet_peer_stream->put_var(oe.error_descr); + packet_peer_stream->put_var(oe.warning); + packet_peer_stream->put_var(oe.callstack); + */ } else if (p_msg=="kill_me") { editor->call_deferred("stop_child_process"); @@ -443,10 +538,24 @@ void ScriptEditorDebugger::_notification(int p_what) { tb->set_hover_texture( get_icon("CloseHover","EditorIcons")); tb->set_pressed_texture( get_icon("Close","EditorIcons")); scene_tree_refresh->set_icon( get_icon("Reload","EditorIcons")); + le_set->connect("pressed",this,"_live_edit_set"); + le_clear->connect("pressed",this,"_live_edit_clear"); + error_list->connect("item_selected",this,"_error_selected"); + error_stack->connect("item_selected",this,"_error_stack_selected"); + vmem_refresh->set_icon( get_icon("Reload","EditorIcons")); } break; case NOTIFICATION_PROCESS: { + if (error_count!=last_error_count) { + + if (error_count==0) { + error_split->set_name("Errors"); + } else { + error_split->set_name("Errors ("+itos(error_count)+")"); + } + last_error_count=error_count; + } if (connection.is_null()) { if (server->is_connection_available()) { @@ -468,6 +577,15 @@ void ScriptEditorDebugger::_notification(int p_what) { emit_signal("show_debugger",true); reason->set_text("Child Process Connected"); reason->set_tooltip("Child Process Connected"); + scene_tree->clear(); + le_set->set_disabled(true); + le_clear->set_disabled(false); + error_list->clear(); + error_stack->clear(); + error_count=0; + //live_edit_root->set_text("/root"); + + update_live_edit_root(); } else { @@ -478,8 +596,6 @@ void ScriptEditorDebugger::_notification(int p_what) { if (!connection->is_connected()) { stop(); editor->notify_child_process_exited(); //somehow, exited - msgdialog->set_text("Process being debugged exited."); - msgdialog->popup_centered(Size2(250,100)); break; }; @@ -615,6 +731,10 @@ void ScriptEditorDebugger::stop(){ log_forced_visible=false; } + node_path_cache.clear(); + res_path_cache.clear(); + le_clear->set_disabled(false); + le_set->set_disabled(true); hide(); @@ -666,6 +786,381 @@ String ScriptEditorDebugger::get_var_value(const String& p_var) const { return variables->get_var_value(p_var); } +int ScriptEditorDebugger::_get_node_path_cache(const NodePath& p_path) { + + const int *r = node_path_cache.getptr(p_path); + if (r) + return *r; + + last_path_id++; + + node_path_cache[p_path]=last_path_id; + Array msg; + msg.push_back("live_node_path"); + msg.push_back(p_path); + msg.push_back(last_path_id); + ppeer->put_var(msg); + + + return last_path_id; +} + +int ScriptEditorDebugger::_get_res_path_cache(const String& p_path) { + + Map<String,int>::Element *E=res_path_cache.find(p_path); + + if (E) + return E->get(); + + last_path_id++; + + res_path_cache[p_path]=last_path_id; + Array msg; + msg.push_back("live_res_path"); + msg.push_back(p_path); + msg.push_back(last_path_id); + ppeer->put_var(msg); + + + return last_path_id; +} + +void ScriptEditorDebugger::_method_changed(Object*p_base,const StringName& p_name,VARIANT_ARG_DECLARE) { + + if (!p_base || !live_debug || !connection.is_valid() || !editor->get_edited_scene()) + return; + + Node *node = p_base->cast_to<Node>(); + + VARIANT_ARGPTRS + + for(int i=0;i<VARIANT_ARG_MAX;i++) { + //no pointers, sorry + if (argptr[i] && (argptr[i]->get_type()==Variant::OBJECT || argptr[i]->get_type()==Variant::_RID)) + return; + } + + if (node) { + + NodePath path = editor->get_edited_scene()->get_path_to(node); + int pathid = _get_node_path_cache(path); + + + + Array msg; + msg.push_back("live_node_call"); + msg.push_back(pathid); + msg.push_back(p_name); + for(int i=0;i<VARIANT_ARG_MAX;i++) { + //no pointers, sorry + msg.push_back(*argptr[i]); + } + ppeer->put_var(msg); + + return; + + } + + Resource *res = p_base->cast_to<Resource>(); + + if (res && res->get_path()!=String()) { + + String respath = res->get_path(); + int pathid = _get_res_path_cache(respath); + + Array msg; + msg.push_back("live_res_call"); + msg.push_back(pathid); + msg.push_back(p_name); + for(int i=0;i<VARIANT_ARG_MAX;i++) { + //no pointers, sorry + msg.push_back(*argptr[i]); + } + ppeer->put_var(msg); + + return; + } + + //print_line("method"); +} + +void ScriptEditorDebugger::_property_changed(Object*p_base,const StringName& p_property,const Variant& p_value){ + + if (!p_base || !live_debug || !connection.is_valid() || !editor->get_edited_scene()) + return; + + Node *node = p_base->cast_to<Node>(); + + if (node) { + + NodePath path = editor->get_edited_scene()->get_path_to(node); + int pathid = _get_node_path_cache(path); + + + if (p_value.is_ref()) { + Ref<Resource> res = p_value; + if (res.is_valid() && res->get_path()!=String()) { + + Array msg; + msg.push_back("live_node_prop_res"); + msg.push_back(pathid); + msg.push_back(p_property); + msg.push_back(res->get_path()); + ppeer->put_var(msg); + } + } else { + + Array msg; + msg.push_back("live_node_prop"); + msg.push_back(pathid); + msg.push_back(p_property); + msg.push_back(p_value); + ppeer->put_var(msg); + } + + + return; + + } + + Resource *res = p_base->cast_to<Resource>(); + + if (res && res->get_path()!=String()) { + + String respath = res->get_path(); + int pathid = _get_res_path_cache(respath); + + + if (p_value.is_ref()) { + Ref<Resource> res = p_value; + if (res.is_valid() && res->get_path()!=String()) { + + Array msg; + msg.push_back("live_res_prop_res"); + msg.push_back(pathid); + msg.push_back(p_property); + msg.push_back(res->get_path()); + ppeer->put_var(msg); + } + } else { + + Array msg; + msg.push_back("live_res_prop"); + msg.push_back(pathid); + msg.push_back(p_property); + msg.push_back(p_value); + ppeer->put_var(msg); + } + + + return; + } + + + //print_line("prop"); +} + +void ScriptEditorDebugger::_method_changeds(void *p_ud,Object*p_base,const StringName& p_name,VARIANT_ARG_DECLARE) { + + ScriptEditorDebugger *sed = (ScriptEditorDebugger*)p_ud; + sed->_method_changed(p_base,p_name,VARIANT_ARG_PASS); + + +} + +void ScriptEditorDebugger::_property_changeds(void *p_ud,Object*p_base,const StringName& p_property,const Variant& p_value){ + + ScriptEditorDebugger *sed = (ScriptEditorDebugger*)p_ud; + sed->_property_changed(p_base,p_property,p_value); + +} + +void ScriptEditorDebugger::set_live_debugging(bool p_enable) { + + live_debug=p_enable; +} + +void ScriptEditorDebugger::_live_edit_set() { + + if (!connection.is_valid()) + return; + + TreeItem* ti = scene_tree->get_selected(); + if (!ti) + return; + String path; + + while(ti) { + String lp=ti->get_text(0); + path="/"+lp+path; + ti=ti->get_parent(); + + } + + NodePath np = path; + + editor->get_editor_data().set_edited_scene_live_edit_root(np); + + update_live_edit_root(); + + +} + +void ScriptEditorDebugger::_live_edit_clear() { + + NodePath np = NodePath("/root"); + editor->get_editor_data().set_edited_scene_live_edit_root(np); + + update_live_edit_root(); + +} + +void ScriptEditorDebugger::update_live_edit_root() { + + NodePath np = editor->get_editor_data().get_edited_scene_live_edit_root(); + + if (connection.is_valid()) { + Array msg; + msg.push_back("live_set_root"); + msg.push_back(np); + if (editor->get_edited_scene()) + msg.push_back(editor->get_edited_scene()->get_filename()); + else + msg.push_back(""); + ppeer->put_var(msg); + } + live_edit_root->set_text(np); + +} + +void ScriptEditorDebugger::live_debug_create_node(const NodePath& p_parent,const String& p_type,const String& p_name) { + + if (live_debug && connection.is_valid()) { + Array msg; + msg.push_back("live_create_node"); + msg.push_back(p_parent); + msg.push_back(p_type); + msg.push_back(p_name); + ppeer->put_var(msg); + } +} + +void ScriptEditorDebugger::live_debug_instance_node(const NodePath& p_parent,const String& p_path,const String& p_name){ + + if (live_debug && connection.is_valid()) { + Array msg; + msg.push_back("live_instance_node"); + msg.push_back(p_parent); + msg.push_back(p_path); + msg.push_back(p_name); + ppeer->put_var(msg); + } + +} +void ScriptEditorDebugger::live_debug_remove_node(const NodePath& p_at){ + + if (live_debug && connection.is_valid()) { + Array msg; + msg.push_back("live_remove_node"); + msg.push_back(p_at); + ppeer->put_var(msg); + } + +} +void ScriptEditorDebugger::live_debug_remove_and_keep_node(const NodePath& p_at,ObjectID p_keep_id) { + + if (live_debug && connection.is_valid()) { + Array msg; + msg.push_back("live_remove_and_keep_node"); + msg.push_back(p_at); + msg.push_back(p_keep_id); + ppeer->put_var(msg); + } + +} +void ScriptEditorDebugger::live_debug_restore_node(ObjectID p_id, const NodePath& p_at, int p_at_pos){ + + if (live_debug && connection.is_valid()) { + Array msg; + msg.push_back("live_restore_node"); + msg.push_back(p_id); + msg.push_back(p_at); + msg.push_back(p_at_pos); + ppeer->put_var(msg); + } + +} +void ScriptEditorDebugger::live_debug_duplicate_node(const NodePath& p_at,const String& p_new_name){ + + if (live_debug && connection.is_valid()) { + Array msg; + msg.push_back("live_duplicate_node"); + msg.push_back(p_at); + msg.push_back(p_new_name); + ppeer->put_var(msg); + } + +} +void ScriptEditorDebugger::live_debug_reparent_node(const NodePath& p_at, const NodePath& p_new_place, const String &p_new_name, int p_at_pos){ + + if (live_debug && connection.is_valid()) { + Array msg; + msg.push_back("live_reparent_node"); + msg.push_back(p_at); + msg.push_back(p_new_place); + msg.push_back(p_new_name); + msg.push_back(p_at_pos); + ppeer->put_var(msg); + } + +} + +void ScriptEditorDebugger::set_breakpoint(const String& p_path,int p_line,bool p_enabled) { + + if (connection.is_valid()) { + Array msg; + msg.push_back("breakpoint"); + msg.push_back(p_path); + msg.push_back(p_line); + msg.push_back(p_enabled); + ppeer->put_var(msg); + } +} + + +void ScriptEditorDebugger::_error_selected(int p_idx) { + + error_stack->clear(); + Array st=error_list->get_item_metadata(p_idx); + for(int i=0;i<st.size();i+=2) { + + String script=st[i]; + int line=st[i+1]; + Array md; + md.push_back(st[i]); + md.push_back(st[i+1]); + + String str = script.get_file()+":"+itos(line); + + error_stack->add_item(str); + error_stack->set_item_metadata(error_stack->get_item_count()-1,md); + error_stack->set_item_tooltip(error_stack->get_item_count()-1,"File: "+String(st[i])+"\nLine: "+itos(line)); + } +} + +void ScriptEditorDebugger:: _error_stack_selected(int p_idx){ + + Array arr = error_stack->get_item_metadata(p_idx); + if (arr.size()!=2) + return; + + + Ref<Script> s = ResourceLoader::load(arr[0]); + emit_signal("goto_script_line",s,int(arr[1])-1); + +} + + void ScriptEditorDebugger::_bind_methods() { ObjectTypeDB::bind_method(_MD("_stack_dump_frame_selected"),&ScriptEditorDebugger::_stack_dump_frame_selected); @@ -678,6 +1173,20 @@ void ScriptEditorDebugger::_bind_methods() { ObjectTypeDB::bind_method(_MD("_performance_draw"),&ScriptEditorDebugger::_performance_draw); ObjectTypeDB::bind_method(_MD("_performance_select"),&ScriptEditorDebugger::_performance_select); ObjectTypeDB::bind_method(_MD("_scene_tree_request"),&ScriptEditorDebugger::_scene_tree_request); + ObjectTypeDB::bind_method(_MD("_video_mem_request"),&ScriptEditorDebugger::_video_mem_request); + ObjectTypeDB::bind_method(_MD("_live_edit_set"),&ScriptEditorDebugger::_live_edit_set); + ObjectTypeDB::bind_method(_MD("_live_edit_clear"),&ScriptEditorDebugger::_live_edit_clear); + + ObjectTypeDB::bind_method(_MD("_error_selected"),&ScriptEditorDebugger::_error_selected); + ObjectTypeDB::bind_method(_MD("_error_stack_selected"),&ScriptEditorDebugger::_error_stack_selected); + + ObjectTypeDB::bind_method(_MD("live_debug_create_node"),&ScriptEditorDebugger::live_debug_create_node); + ObjectTypeDB::bind_method(_MD("live_debug_instance_node"),&ScriptEditorDebugger::live_debug_instance_node); + ObjectTypeDB::bind_method(_MD("live_debug_remove_node"),&ScriptEditorDebugger::live_debug_remove_node); + ObjectTypeDB::bind_method(_MD("live_debug_remove_and_keep_node"),&ScriptEditorDebugger::live_debug_remove_and_keep_node); + ObjectTypeDB::bind_method(_MD("live_debug_restore_node"),&ScriptEditorDebugger::live_debug_restore_node); + ObjectTypeDB::bind_method(_MD("live_debug_duplicate_node"),&ScriptEditorDebugger::live_debug_duplicate_node); + ObjectTypeDB::bind_method(_MD("live_debug_reparent_node"),&ScriptEditorDebugger::live_debug_reparent_node); ADD_SIGNAL(MethodInfo("goto_script_line")); ADD_SIGNAL(MethodInfo("breaked",PropertyInfo(Variant::BOOL,"reallydid"))); @@ -791,6 +1300,23 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor){ vbc->add_child(hbc); + error_split = memnew( HSplitContainer ); + VBoxContainer *errvb = memnew( VBoxContainer ); + errvb->set_h_size_flags(SIZE_EXPAND_FILL); + error_list = memnew( ItemList ); + errvb->add_margin_child("Errors:",error_list,true); + error_split->add_child(errvb); + + errvb = memnew( VBoxContainer ); + errvb->set_h_size_flags(SIZE_EXPAND_FILL); + error_stack = memnew( ItemList ); + errvb->add_margin_child("Stack Trace (if applies):",error_stack,true); + error_split->add_child(errvb); + + error_split->set_name("Errors"); + tabs->add_child(error_split); + + HSplitContainer *hsp = memnew( HSplitContainer ); perf_monitors = memnew(Tree); @@ -834,6 +1360,47 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor){ } + VBoxContainer *vmem_vb = memnew( VBoxContainer ); + HBoxContainer *vmem_hb = memnew( HBoxContainer ); + Label *vmlb = memnew(Label("List of Video Memory Usage by Resource: ") ); + vmlb->set_h_size_flags(SIZE_EXPAND_FILL); + vmem_hb->add_child( vmlb ); + vmem_hb->add_child( memnew(Label("Total: ")) ); + vmem_total = memnew( LineEdit ); + vmem_total->set_editable(false); + vmem_total->set_custom_minimum_size(Size2(100,1)); + vmem_hb->add_child(vmem_total); + vmem_refresh = memnew( Button ); + vmem_hb->add_child(vmem_refresh); + vmem_vb->add_child(vmem_hb); + vmem_refresh->connect("pressed",this,"_video_mem_request"); + + MarginContainer *vmmc = memnew( MarginContainer ); + vmem_tree = memnew( Tree ); + vmem_tree->set_v_size_flags(SIZE_EXPAND_FILL); + vmem_tree->set_h_size_flags(SIZE_EXPAND_FILL); + vmmc->add_child(vmem_tree); + vmmc->set_v_size_flags(SIZE_EXPAND_FILL); + vmem_vb->add_child(vmmc); + + vmem_vb->set_name("Video Mem"); + vmem_tree->set_columns(4); + vmem_tree->set_column_titles_visible(true); + vmem_tree->set_column_title(0,"Resource Path"); + vmem_tree->set_column_expand(0,true); + vmem_tree->set_column_expand(1,false); + vmem_tree->set_column_title(1,"Type"); + vmem_tree->set_column_min_width(1,100); + vmem_tree->set_column_expand(2,false); + vmem_tree->set_column_title(2,"Format"); + vmem_tree->set_column_min_width(2,150); + vmem_tree->set_column_expand(3,false); + vmem_tree->set_column_title(3,"Usage"); + vmem_tree->set_column_min_width(3,80); + vmem_tree->set_hide_root(true); + + tabs->add_child(vmem_vb); + info = memnew( HSplitContainer ); info->set_name("Info"); tabs->add_child(info); @@ -845,6 +1412,26 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor){ info_left->add_margin_child("Clicked Control:",clicked_ctrl); clicked_ctrl_type = memnew( LineEdit ); info_left->add_margin_child("Clicked Control Type:",clicked_ctrl_type); + + live_edit_root = memnew( LineEdit ); + + { + HBoxContainer *lehb = memnew( HBoxContainer ); + Label *l = memnew( Label("Live Edit Root:") ); + lehb->add_child(l); + l->set_h_size_flags(SIZE_EXPAND_FILL); + le_set = memnew( Button("Set From Tree") ); + lehb->add_child(le_set); + le_clear = memnew( Button("Clear") ); + lehb->add_child(le_clear); + info_left->add_child(lehb); + MarginContainer *mc = memnew( MarginContainer ); + mc->add_child(live_edit_root); + info_left->add_child(mc); + le_set->set_disabled(true); + le_clear->set_disabled(true); + } + VBoxContainer *info_right = memnew(VBoxContainer); info_right->set_h_size_flags(SIZE_EXPAND_FILL); info->add_child(info_right); @@ -870,6 +1457,14 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor){ hide(); log_forced_visible=false; + p_editor->get_undo_redo()->set_method_notify_callback(_method_changeds,this); + p_editor->get_undo_redo()->set_property_notify_callback(_property_changeds,this); + live_debug=false; + last_path_id=false; + error_count=0; + last_error_count=0; + + } ScriptEditorDebugger::~ScriptEditorDebugger() { diff --git a/tools/editor/script_editor_debugger.h b/tools/editor/script_editor_debugger.h index 5127a329aa..6b66a62dd5 100644 --- a/tools/editor/script_editor_debugger.h +++ b/tools/editor/script_editor_debugger.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,110 +26,166 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SCRIPT_EDITOR_DEBUGGER_H
-#define SCRIPT_EDITOR_DEBUGGER_H
-
-#include "scene/gui/box_container.h"
-#include "scene/gui/button.h"
-#include "core/io/tcp_server.h"
-#include "core/io/packet_peer.h"
-
-class Tree;
-class PropertyEditor;
-class EditorNode;
-class ScriptEditorDebuggerVariables;
-class LineEdit;
-class TabContainer;
-class RichTextLabel;
-class TextureButton;
-class AcceptDialog;
-class TreeItem;
-class HSplitContainer;
-
-class ScriptEditorDebugger : public Control {
-
- OBJ_TYPE( ScriptEditorDebugger, Control );
-
- AcceptDialog *msgdialog;
-
-
-
- LineEdit *clicked_ctrl;
- LineEdit *clicked_ctrl_type;
- Tree *scene_tree;
- HSplitContainer *info;
- Button *scene_tree_refresh;
-
- TextureButton *tb;
-
-
- TabContainer *tabs;
-
- Label *reason;
- bool log_forced_visible;
- ScriptEditorDebuggerVariables *variables;
-
- Button *step;
- Button *next;
- Button *back;
- Button *forward;
- Button *dobreak;
- Button *docontinue;
-
- List<Vector<float> > perf_history;
- Vector<float> perf_max;
- Vector<TreeItem*> perf_items;
-
- Tree *perf_monitors;
- Control *perf_draw;
-
- Tree *stack_dump;
- PropertyEditor *inspector;
-
- Ref<TCP_Server> server;
- Ref<StreamPeerTCP> connection;
- Ref<PacketPeerStream> ppeer;
-
- String message_type;
- Array message;
- int pending_in_queue;
-
-
- EditorNode *editor;
-
- bool breaked;
-
- void _performance_draw();
- void _performance_select(Object *, int, bool);
- void _stack_dump_frame_selected();
- void _output_clear();
- void _hide_request();
-
- void _scene_tree_request();
- void _parse_message(const String& p_msg,const Array& p_data);
-
-protected:
-
- void _notification(int p_what);
- static void _bind_methods();
-
-public:
-
- void start();
- void pause();
- void unpause();
- void stop();
-
- void debug_next();
- void debug_step();
- void debug_break();
- void debug_continue();
-
- String get_var_value(const String& p_var) const;
-
- virtual Size2 get_minimum_size() const;
- ScriptEditorDebugger(EditorNode *p_editor=NULL);
- ~ScriptEditorDebugger();
-};
-
-#endif // SCRIPT_EDITOR_DEBUGGER_H
+#ifndef SCRIPT_EDITOR_DEBUGGER_H +#define SCRIPT_EDITOR_DEBUGGER_H + +#include "scene/gui/box_container.h" +#include "scene/gui/button.h" +#include "core/io/tcp_server.h" +#include "core/io/packet_peer.h" + +class Tree; +class PropertyEditor; +class EditorNode; +class ScriptEditorDebuggerVariables; +class LineEdit; +class TabContainer; +class RichTextLabel; +class TextureButton; +class AcceptDialog; +class TreeItem; +class HSplitContainer; +class ItemList; + +class ScriptEditorDebugger : public Control { + + OBJ_TYPE( ScriptEditorDebugger, Control ); + + AcceptDialog *msgdialog; + + + + LineEdit *clicked_ctrl; + LineEdit *clicked_ctrl_type; + LineEdit *live_edit_root; + Tree *scene_tree; + HSplitContainer *info; + Button *scene_tree_refresh; + Button *le_set; + Button *le_clear; + + HSplitContainer *error_split; + ItemList *error_list; + ItemList *error_stack; + + int error_count; + int last_error_count; + + + + TextureButton *tb; + + + TabContainer *tabs; + + Label *reason; + bool log_forced_visible; + ScriptEditorDebuggerVariables *variables; + + Button *step; + Button *next; + Button *back; + Button *forward; + Button *dobreak; + Button *docontinue; + + List<Vector<float> > perf_history; + Vector<float> perf_max; + Vector<TreeItem*> perf_items; + + Tree *perf_monitors; + Control *perf_draw; + + Tree *vmem_tree; + Button *vmem_refresh; + LineEdit *vmem_total; + + Tree *stack_dump; + PropertyEditor *inspector; + + Ref<TCP_Server> server; + Ref<StreamPeerTCP> connection; + Ref<PacketPeerStream> ppeer; + + String message_type; + Array message; + int pending_in_queue; + + HashMap<NodePath,int> node_path_cache; + int last_path_id; + Map<String,int> res_path_cache; + + + EditorNode *editor; + + bool breaked; + + bool live_debug; + + void _performance_draw(); + void _performance_select(Object *, int, bool); + void _stack_dump_frame_selected(); + void _output_clear(); + void _hide_request(); + + void _scene_tree_request(); + void _parse_message(const String& p_msg,const Array& p_data); + + void _video_mem_request(); + + int _get_node_path_cache(const NodePath& p_path); + + int _get_res_path_cache(const String& p_path); + + void _live_edit_set(); + void _live_edit_clear(); + + void _method_changed(Object*p_base,const StringName& p_name,VARIANT_ARG_DECLARE); + void _property_changed(Object*p_base,const StringName& p_property,const Variant& p_value); + + static void _method_changeds(void *p_ud,Object*p_base,const StringName& p_name,VARIANT_ARG_DECLARE); + static void _property_changeds(void *p_ud,Object*p_base,const StringName& p_property,const Variant& p_value); + + void _error_selected(int p_idx); + void _error_stack_selected(int p_idx); + +protected: + + void _notification(int p_what); + static void _bind_methods(); + +public: + + void start(); + void pause(); + void unpause(); + void stop(); + + void debug_next(); + void debug_step(); + void debug_break(); + void debug_continue(); + + String get_var_value(const String& p_var) const; + + void set_live_debugging(bool p_enable); + + void live_debug_create_node(const NodePath& p_parent,const String& p_type,const String& p_name); + void live_debug_instance_node(const NodePath& p_parent,const String& p_path,const String& p_name); + void live_debug_remove_node(const NodePath& p_at); + void live_debug_remove_and_keep_node(const NodePath& p_at,ObjectID p_keep_id); + void live_debug_restore_node(ObjectID p_id,const NodePath& p_at,int p_at_pos); + void live_debug_duplicate_node(const NodePath& p_at,const String& p_new_name); + void live_debug_reparent_node(const NodePath& p_at,const NodePath& p_new_place,const String& p_new_name,int p_at_pos); + + void set_breakpoint(const String& p_path,int p_line,bool p_enabled); + + void update_live_edit_root(); + + + virtual Size2 get_minimum_size() const; + ScriptEditorDebugger(EditorNode *p_editor=NULL); + ~ScriptEditorDebugger(); +}; + +#endif // SCRIPT_EDITOR_DEBUGGER_H diff --git a/tools/editor/settings_config_dialog.cpp b/tools/editor/settings_config_dialog.cpp index 2310df4ffb..6d8f849427 100644 --- a/tools/editor/settings_config_dialog.cpp +++ b/tools/editor/settings_config_dialog.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -72,6 +72,10 @@ void EditorSettingsDialog::popup_edit_settings() { property_editor->edit(EditorSettings::get_singleton()); property_editor->update_tree(); + + search_box->select_all(); + search_box->grab_focus(); + popup_centered_ratio(0.7); } @@ -244,11 +248,21 @@ void EditorSettingsDialog::_update_plugins() { } +void EditorSettingsDialog::_clear_search_box() { + + if (search_box->get_text()=="") + return; + + search_box->clear(); + property_editor->update_tree(); +} + void EditorSettingsDialog::_notification(int p_what) { if (p_what==NOTIFICATION_ENTER_TREE) { rescan_plugins->set_icon(get_icon("Reload","EditorIcons")); + clear_button->set_icon(get_icon("Close","EditorIcons")); _update_plugins(); } } @@ -261,6 +275,7 @@ void EditorSettingsDialog::_bind_methods() { ObjectTypeDB::bind_method(_MD("_plugin_settings"),&EditorSettingsDialog::_plugin_settings); ObjectTypeDB::bind_method(_MD("_plugin_edited"),&EditorSettingsDialog::_plugin_edited); ObjectTypeDB::bind_method(_MD("_plugin_install"),&EditorSettingsDialog::_plugin_install); + ObjectTypeDB::bind_method(_MD("_clear_search_box"),&EditorSettingsDialog::_clear_search_box); } EditorSettingsDialog::EditorSettingsDialog() { @@ -271,16 +286,38 @@ EditorSettingsDialog::EditorSettingsDialog() { add_child(tabs); set_child_rect(tabs); + VBoxContainer *vbc = memnew( VBoxContainer ); + tabs->add_child(vbc); + vbc->set_name("General"); + + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); + vbc->add_child(hbc); + + Label *l = memnew( Label ); + l->set_text("Search: "); + hbc->add_child(l); + + search_box = memnew( LineEdit ); + search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hbc->add_child(search_box); + + clear_button = memnew( ToolButton ); + hbc->add_child(clear_button); + clear_button->connect("pressed",this,"_clear_search_box"); + property_editor = memnew( PropertyEditor ); property_editor->hide_top_label(); - tabs->add_child(property_editor); - property_editor->set_name("General"); + property_editor->set_use_filter(true); + property_editor->register_text_enter(search_box); + property_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); + vbc->add_child(property_editor); - VBoxContainer *vbc = memnew( VBoxContainer ); + vbc = memnew( VBoxContainer ); tabs->add_child(vbc); vbc->set_name("Plugins"); - HBoxContainer *hbc = memnew( HBoxContainer ); + hbc = memnew( HBoxContainer ); vbc->add_child(hbc); hbc->add_child( memnew( Label("Plugin List: "))); hbc->add_spacer(); diff --git a/tools/editor/settings_config_dialog.h b/tools/editor/settings_config_dialog.h index a3e4a87faf..50159cf488 100644 --- a/tools/editor/settings_config_dialog.h +++ b/tools/editor/settings_config_dialog.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -51,6 +51,8 @@ class EditorSettingsDialog : public AcceptDialog { Button *rescan_plugins; Tree *plugins; + LineEdit *search_box; + ToolButton *clear_button; PropertyEditor *property_editor; Timer *timer; @@ -71,6 +73,8 @@ class EditorSettingsDialog : public AcceptDialog { void _rescan_plugins(); void _update_plugins(); + void _clear_search_box(); + protected: static void _bind_methods(); diff --git a/tools/editor/spatial_editor_gizmos.cpp b/tools/editor/spatial_editor_gizmos.cpp index d48a4ce813..5efca44c7d 100644 --- a/tools/editor/spatial_editor_gizmos.cpp +++ b/tools/editor/spatial_editor_gizmos.cpp @@ -1,3190 +1,3191 @@ -/*************************************************************************/
-/* spatial_editor_gizmos.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* http://www.godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
-/* */
-/* 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, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* 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.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-#include "spatial_editor_gizmos.h"
-#include "geometry.h"
-#include "scene/3d/camera.h"
-#include "scene/resources/surface_tool.h"
-#include "scene/resources/sphere_shape.h"
-#include "scene/resources/box_shape.h"
-#include "scene/resources/capsule_shape.h"
-#include "scene/resources/ray_shape.h"
-#include "scene/resources/convex_polygon_shape.h"
-#include "scene/resources/plane_shape.h"
-#include "quick_hull.h"
-
-// Keep small children away from this file.
-// It's so ugly it will eat them alive
-
-#define HANDLE_HALF_SIZE 0.05
-
-void SpatialGizmoTool::clear() {
-
- for(int i=0;i<instances.size();i++) {
-
- if (instances[i].instance.is_valid())
- VS::get_singleton()->free(instances[i].instance);
-
-
- }
-
- billboard_handle=false;
- collision_segments.clear();
- collision_mesh=Ref<TriangleMesh>();
- instances.clear();
- handles.clear();
- secondary_handles.clear();
-}
-
-void SpatialGizmoTool::Instance::create_instance(Spatial *p_base) {
-
- instance = VS::get_singleton()->instance_create2(mesh->get_rid(),p_base->get_world()->get_scenario());
- VS::get_singleton()->instance_attach_object_instance_ID(instance,p_base->get_instance_ID());
- if (billboard)
- VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_BILLBOARD,true);
- if (unscaled)
- VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_DEPH_SCALE,true);
- if (skeleton.is_valid())
- VS::get_singleton()->instance_attach_skeleton(instance,skeleton);
- if (extra_margin)
- VS::get_singleton()->instance_set_extra_visibility_margin(instance,1);
- VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_CAST_SHADOW,false);
- VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_RECEIVE_SHADOWS,false);
- VS::get_singleton()->instance_set_layer_mask(instance,1<<SpatialEditorViewport::GIZMO_EDIT_LAYER); //gizmos are 26
-}
-
-
-
-void SpatialGizmoTool::add_mesh(const Ref<Mesh>& p_mesh,bool p_billboard, const RID &p_skeleton) {
-
- ERR_FAIL_COND(!spatial_node);
- Instance ins;
-
- ins.billboard=p_billboard;
- ins.mesh=p_mesh;
- ins.skeleton=p_skeleton;
- if (valid) {
- ins.create_instance(spatial_node);
- VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform());
- }
-
- instances.push_back(ins);
-
-}
-
-void SpatialGizmoTool::add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material,bool p_billboard){
-
- ERR_FAIL_COND(!spatial_node);
- Instance ins;
-
- Ref<Mesh> mesh = memnew( Mesh );
- Array a;
- a.resize(Mesh::ARRAY_MAX);
-
- a[Mesh::ARRAY_VERTEX]=p_lines;
-
- DVector<Color> color;
- color.resize(p_lines.size());
- {
- DVector<Color>::Write w = color.write();
- for(int i=0;i<p_lines.size();i++) {
- if (is_selected())
- w[i]=Color(1,1,1,0.6);
- else
- w[i]=Color(1,1,1,0.25);
- }
-
- }
-
- a[Mesh::ARRAY_COLOR]=color;
-
-
- mesh->add_surface(Mesh::PRIMITIVE_LINES,a);
- mesh->surface_set_material(0,p_material);
-
- if (p_billboard) {
- float md=0;
- for(int i=0;i<p_lines.size();i++) {
-
- md=MAX(0,p_lines[i].length());
-
- }
- if (md) {
- mesh->set_custom_aabb(AABB(Vector3(-md,-md,-md),Vector3(md,md,md)*2.0));
- }
- }
-
- ins.billboard=p_billboard;
- ins.mesh=mesh;
- if (valid) {
- ins.create_instance(spatial_node);
- VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform());
- }
-
- instances.push_back(ins);
-
-}
-
-void SpatialGizmoTool::add_unscaled_billboard(const Ref<Material>& p_material,float p_scale) {
-
- ERR_FAIL_COND(!spatial_node);
- Instance ins;
-
- Vector<Vector3 > vs;
- Vector<Vector2 > uv;
-
- vs.push_back(Vector3(-p_scale,p_scale,0));
- vs.push_back(Vector3(p_scale,p_scale,0));
- vs.push_back(Vector3(p_scale,-p_scale,0));
- vs.push_back(Vector3(-p_scale,-p_scale,0));
-
- uv.push_back(Vector2(1,0));
- uv.push_back(Vector2(0,0));
- uv.push_back(Vector2(0,1));
- uv.push_back(Vector2(1,1));
-
- Ref<Mesh> mesh = memnew( Mesh );
- Array a;
- a.resize(Mesh::ARRAY_MAX);
- a[Mesh::ARRAY_VERTEX]=vs;
- a[Mesh::ARRAY_TEX_UV]=uv;
- mesh->add_surface(Mesh::PRIMITIVE_TRIANGLE_FAN,a);
- mesh->surface_set_material(0,p_material);
-
- if (true) {
- float md=0;
- for(int i=0;i<vs.size();i++) {
-
- md=MAX(0,vs[i].length());
-
- }
- if (md) {
- mesh->set_custom_aabb(AABB(Vector3(-md,-md,-md),Vector3(md,md,md)*2.0));
- }
- }
-
- ins.mesh=mesh;
- ins.unscaled=true;
- ins.billboard=true;
- if (valid) {
- ins.create_instance(spatial_node);
- VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform());
- }
-
- instances.push_back(ins);
-
-
-}
-
-void SpatialGizmoTool::add_collision_triangles(const Ref<TriangleMesh>& p_tmesh) {
-
- collision_mesh=p_tmesh;
-}
-
-void SpatialGizmoTool::add_collision_segments(const Vector<Vector3> &p_lines) {
-
- int from=collision_segments.size();
- collision_segments.resize(from+p_lines.size());
- for(int i=0;i<p_lines.size();i++) {
-
- collision_segments[from+i]=p_lines[i];
- }
-}
-
-
-void SpatialGizmoTool::add_handles(const Vector<Vector3> &p_handles, bool p_billboard,bool p_secondary){
-
- billboard_handle=p_billboard;
-
- if (!is_selected())
- return;
-
- ERR_FAIL_COND(!spatial_node);
-
- ERR_FAIL_COND(!spatial_node);
- Instance ins;
-
-
- Ref<Mesh> mesh = memnew( Mesh );
-#if 1
-
- Array a;
- a.resize(VS::ARRAY_MAX);
- a[VS::ARRAY_VERTEX]=p_handles;
- DVector<Color> colors;
- {
- colors.resize(p_handles.size());
- DVector<Color>::Write w=colors.write();
- for(int i=0;i<p_handles.size();i++) {
-
- Color col(1,1,1,1);
- if (SpatialEditor::get_singleton()->get_over_gizmo_handle()!=i)
- col=Color(0.9,0.9,0.9,0.9);
- w[i]=col;
- }
-
- }
- a[VS::ARRAY_COLOR]=colors;
- mesh->add_surface(Mesh::PRIMITIVE_POINTS,a);
- mesh->surface_set_material(0,SpatialEditorGizmos::singleton->handle2_material);
-
- if (p_billboard) {
- float md=0;
- for(int i=0;i<p_handles.size();i++) {
-
- md=MAX(0,p_handles[i].length());
-
- }
- if (md) {
- mesh->set_custom_aabb(AABB(Vector3(-md,-md,-md),Vector3(md,md,md)*2.0));
- }
- }
-
-
-
-#else
- for(int ih=0;ih<p_handles.size();ih++) {
-
-
- Vector<Vector3> vertices;
- Vector<Vector3> normals;
-
- int vtx_idx=0;
-#define ADD_VTX(m_idx);\
- vertices.push_back( (face_points[m_idx]*HANDLE_HALF_SIZE+p_handles[ih]) );\
- normals.push_back( normal_points[m_idx] );\
- vtx_idx++;\
-
- for (int i=0;i<6;i++) {
-
-
- Vector3 face_points[4];
- Vector3 normal_points[4];
- float uv_points[8]={0,0,0,1,1,1,1,0};
-
- for (int j=0;j<4;j++) {
-
- float v[3];
- v[0]=1.0;
- v[1]=1-2*((j>>1)&1);
- v[2]=v[1]*(1-2*(j&1));
-
- for (int k=0;k<3;k++) {
-
- if (i<3)
- face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
- else
- face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
- }
- normal_points[j]=Vector3();
- normal_points[j][i%3]=(i>=3?-1:1);
- }
- //tri 1
- ADD_VTX(0);
- ADD_VTX(1);
- ADD_VTX(2);
- //tri 2
- ADD_VTX(2);
- ADD_VTX(3);
- ADD_VTX(0);
-
- }
-
-
- Array d;
- d.resize(VS::ARRAY_MAX);
- d[VisualServer::ARRAY_NORMAL]= normals ;
- d[VisualServer::ARRAY_VERTEX]= vertices ;
-
- mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,d);
- mesh->surface_set_material(ih,SpatialEditorGizmos::singleton->handle_material);
-
-
- }
-#endif
- ins.mesh=mesh;
- ins.billboard=p_billboard;
- ins.extra_margin=true;
- if (valid) {
- ins.create_instance(spatial_node);
- VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform());
- }
- instances.push_back(ins);
- if (!p_secondary) {
- int chs=handles.size();
- handles.resize(chs+p_handles.size());
- for(int i=0;i<p_handles.size();i++) {
- handles[i+chs]=p_handles[i];
- }
- } else {
-
- int chs=secondary_handles.size();
- secondary_handles.resize(chs+p_handles.size());
- for(int i=0;i<p_handles.size();i++) {
- secondary_handles[i+chs]=p_handles[i];
- }
-
- }
-
-}
-
-
-void SpatialGizmoTool::set_spatial_node(Spatial *p_node){
-
- spatial_node=p_node;
-
-}
-
-bool SpatialGizmoTool::intersect_frustum(const Camera *p_camera,const Vector<Plane> &p_frustum) {
-
- ERR_FAIL_COND_V(!spatial_node,false);
- ERR_FAIL_COND_V(!valid,false);
-
- if (collision_segments.size()) {
-
- const Plane *p=p_frustum.ptr();
- int fc=p_frustum.size();
-
- int vc=collision_segments.size();
- const Vector3* vptr=collision_segments.ptr();
- Transform t = spatial_node->get_global_transform();
-
- for(int i=0;i<vc/2;i++) {
-
-
- Vector3 a=t.xform(vptr[i*2+0]);
- Vector3 b=t.xform(vptr[i*2+1]);
-
- bool any_out=false;
- for(int j=0;j<fc;j++) {
-
- if (p[j].distance_to(a) > 0 && p[j].distance_to(b) >0) {
-
- any_out=true;
- break;
- }
- }
-
- if (!any_out)
- return true;
- }
-
- return false;
- }
-
- return false;
-}
-
-
-bool SpatialGizmoTool::intersect_ray(const Camera *p_camera,const Point2& p_point, Vector3& r_pos, Vector3& r_normal,int *r_gizmo_handle,bool p_sec_first) {
-
- ERR_FAIL_COND_V(!spatial_node,false);
- ERR_FAIL_COND_V(!valid,false);
-
- if (r_gizmo_handle) {
-
- Transform t = spatial_node->get_global_transform();
- t.orthonormalize();
- if (billboard_handle) {
- t.set_look_at(t.origin,t.origin+p_camera->get_transform().basis.get_axis(2),p_camera->get_transform().basis.get_axis(1));
- }
- Transform ti=t.affine_inverse();
-
- Vector3 ray_from=ti.xform(p_camera->project_ray_origin(p_point));
- Vector3 ray_dir=t.basis.xform_inv(p_camera->project_ray_normal(p_point)).normalized();
- Vector3 ray_to = ray_from+ray_dir*4096;
-
- float min_d=1e20;
- int idx=-1;
-
- for(int i=0;i<secondary_handles.size();i++) {
-#if 1
-
-
- Vector3 hpos = t.xform(secondary_handles[i]);
- Vector2 p = p_camera->unproject_position(hpos);
- if (p.distance_to(p_point)<SpatialEditorGizmos::singleton->handle_t->get_width()*0.6) {
-
-
- real_t dp = p_camera->get_transform().origin.distance_to(hpos);
- if (dp<min_d) {
-
- r_pos=t.xform(hpos);
- r_normal=p_camera->get_transform().basis.get_axis(2);
- min_d=dp;
- idx=i+handles.size();
-
- }
-
- }
-
-#else
- AABB aabb;
- aabb.pos=Vector3(-1,-1,-1)*HANDLE_HALF_SIZE;
- aabb.size=aabb.pos*-2;
- aabb.pos+=secondary_handles[i];
-
-
- Vector3 rpos,rnorm;
-
- if (aabb.intersects_segment(ray_from,ray_to,&rpos,&rnorm)) {
-
- real_t dp = ray_dir.dot(rpos);
- if (dp<min_d) {
-
- r_pos=t.xform(rpos);
- r_normal=ti.basis.xform_inv(rnorm).normalized();
- min_d=dp;
- idx=i+handles.size();
-
- }
- }
-#endif
- }
-
- if (p_sec_first && idx!=-1) {
-
- *r_gizmo_handle=idx;
- return true;
- }
-
- min_d=1e20;
-
- for(int i=0;i<handles.size();i++) {
-
-#if 1
-
-
- Vector3 hpos = t.xform(handles[i]);
- Vector2 p = p_camera->unproject_position(hpos);
- if (p.distance_to(p_point)<SpatialEditorGizmos::singleton->handle_t->get_width()*0.6) {
-
-
- real_t dp = p_camera->get_transform().origin.distance_to(hpos);
- if (dp<min_d) {
-
- r_pos=t.xform(hpos);
- r_normal=p_camera->get_transform().basis.get_axis(2);
- min_d=dp;
- idx=i;
-
- }
-
- }
-
-#else
-
- AABB aabb;
- aabb.pos=Vector3(-1,-1,-1)*HANDLE_HALF_SIZE;
- aabb.size=aabb.pos*-2;
- aabb.pos+=handles[i];
-
-
- Vector3 rpos,rnorm;
-
- if (aabb.intersects_segment(ray_from,ray_to,&rpos,&rnorm)) {
-
- real_t dp = ray_dir.dot(rpos);
- if (dp<min_d) {
-
- r_pos=t.xform(rpos);
- r_normal=ti.basis.xform_inv(rnorm).normalized();
- min_d=dp;
- idx=i;
-
- }
- }
-#endif
- }
-
- if (idx>=0) {
- *r_gizmo_handle=idx;
- return true;
- }
-
-
- }
-
- if (collision_segments.size()) {
-
- Plane camp(p_camera->get_transform().origin,(-p_camera->get_transform().basis.get_axis(2)).normalized());
-
- int vc=collision_segments.size();
- const Vector3* vptr=collision_segments.ptr();
- Transform t = spatial_node->get_global_transform();
- if (billboard_handle) {
- t.set_look_at(t.origin,t.origin+p_camera->get_transform().basis.get_axis(2),p_camera->get_transform().basis.get_axis(1));
- }
-
- Vector3 cp;
- float cpd=1e20;
-
- for(int i=0;i<vc/2;i++) {
-
-
- Vector3 a=t.xform(vptr[i*2+0]);
- Vector3 b=t.xform(vptr[i*2+1]);
- Vector2 s[2];
- s[0] = p_camera->unproject_position(a);
- s[1] = p_camera->unproject_position(b);
-
-
- Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point,s);
-
- float pd = p.distance_to(p_point);
-
- if (pd<cpd) {
-
-
- float d = s[0].distance_to(s[1]);
- Vector3 tcp;
- if (d>0) {
-
- float d2=s[0].distance_to(p)/d;
- tcp = a+(b-a)*d2;
-
- } else {
- tcp=a;
-
- }
-
- if (camp.distance_to(tcp)<p_camera->get_znear())
- continue;
- cp=tcp;
- cpd=pd;
- }
- }
-
- if (cpd<8) {
-
- r_pos=cp;
- r_normal=-p_camera->project_ray_normal(p_point);
- return true;
- }
-
- return false;
- }
-
-
- if (collision_mesh.is_valid()) {
- Transform gt = spatial_node->get_global_transform();
-
- if (billboard_handle) {
- gt.set_look_at(gt.origin,gt.origin+p_camera->get_transform().basis.get_axis(2),p_camera->get_transform().basis.get_axis(1));
- }
-
- Transform ai=gt.affine_inverse();
- Vector3 ray_from = ai.xform(p_camera->project_ray_origin(p_point));
- Vector3 ray_dir=ai.basis.xform(p_camera->project_ray_normal(p_point)).normalized();
- Vector3 rpos,rnorm;
-
-#if 1
-
-
-
- if (collision_mesh->intersect_ray(ray_from,ray_dir,rpos,rnorm)) {
-
- r_pos=gt.xform(rpos);
- r_normal=gt.basis.xform(rnorm).normalized();
- return true;
- }
-#else
-
- if (collision_mesh->intersect_segment(ray_from,ray_from+ray_dir*4906.0,rpos,rnorm)) {
-
- r_pos=gt.xform(rpos);
- r_normal=gt.basis.xform(rnorm).normalized();
- return true;
- }
-
-#endif
- }
-
- return false;
-
-}
-
-
-
-void SpatialGizmoTool::create() {
-
- ERR_FAIL_COND(!spatial_node);
- ERR_FAIL_COND(valid);
- valid=true;
-
- for(int i=0;i<instances.size();i++) {
-
- instances[i].create_instance(spatial_node);
- }
-
- transform();
-
-}
-
-void SpatialGizmoTool::transform(){
-
- ERR_FAIL_COND(!spatial_node);
- ERR_FAIL_COND(!valid);
- for(int i=0;i<instances.size();i++) {
- VS::get_singleton()->instance_set_transform(instances[i].instance,spatial_node->get_global_transform());
- }
-
-}
-
-
-void SpatialGizmoTool::free(){
-
- ERR_FAIL_COND(!spatial_node);
- ERR_FAIL_COND(!valid);
-
- for(int i=0;i<instances.size();i++) {
-
- if (instances[i].instance.is_valid())
- VS::get_singleton()->free(instances[i].instance);
- instances[i].instance=RID();
- }
-
- valid=false;
-
-
-}
-
-
-
-SpatialGizmoTool::SpatialGizmoTool() {
- valid=false;
- billboard_handle=false;
-
-}
-
-SpatialGizmoTool::~SpatialGizmoTool(){
-
- clear();
-}
-
-Vector3 SpatialGizmoTool::get_handle_pos(int p_idx) const {
-
- ERR_FAIL_INDEX_V(p_idx,handles.size(),Vector3());
-
- return handles[p_idx];
-
-}
-
-//// light gizmo
-
-
-String LightSpatialGizmo::get_handle_name(int p_idx) const {
-
- if (p_idx==0)
- return "Radius";
- else
- return "Aperture";
-}
-
-
-Variant LightSpatialGizmo::get_handle_value(int p_idx) const{
-
- if (p_idx==0)
- return light->get_parameter(Light::PARAM_RADIUS);
- if (p_idx==1)
- return light->get_parameter(Light::PARAM_SPOT_ANGLE);
-
- return Variant();
-}
-
-
-static float _find_closest_angle_to_half_pi_arc(const Vector3& p_from, const Vector3& p_to, float p_arc_radius,const Transform& p_arc_xform) {
-
- //bleh, discrete is simpler
- static const int arc_test_points=64;
- float min_d = 1e20;
- Vector3 min_p;
-
-
- for(int i=0;i<arc_test_points;i++) {
-
- float a = i*Math_PI*0.5/arc_test_points;
- float an = (i+1)*Math_PI*0.5/arc_test_points;
- Vector3 p=Vector3( Math::cos(a), 0, -Math::sin(a) )*p_arc_radius;
- Vector3 n=Vector3( Math::cos(an), 0,- Math::sin(an) )*p_arc_radius;
-
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(p,n,p_from,p_to,ra,rb);
-
- float d = ra.distance_to(rb);
- if (d<min_d) {
- min_d=d;
- min_p=ra;
- }
-
- }
-
- //min_p = p_arc_xform.affine_inverse().xform(min_p);
- float a = Vector2(min_p.x,-min_p.z).atan2();
- return a*180.0/Math_PI;
-}
-
-
-void LightSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point) {
-
- Transform gt = light->get_global_transform();
- gt.orthonormalize();
- Transform gi = gt.affine_inverse();
-
- Vector3 ray_from = p_camera->project_ray_origin(p_point);
- Vector3 ray_dir = p_camera->project_ray_normal(p_point);
-
- Vector3 s[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)};
- if (p_idx==0) {
-
- if (light->cast_to<SpotLight>()) {
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(Vector3(),Vector3(0,0,-4096),s[0],s[1],ra,rb);
-
- float d = -ra.z;
- if (d<0)
- d=0;
-
- light->set_parameter(Light::PARAM_RADIUS,d);
- } else if (light->cast_to<OmniLight>()) {
-
- Plane cp=Plane( gt.origin, p_camera->get_transform().basis.get_axis(2));
-
- Vector3 inters;
- if (cp.intersects_ray(ray_from,ray_dir,&inters)) {
-
- float r = inters.distance_to(gt.origin);
- light->set_parameter(Light::PARAM_RADIUS,r);
- }
-
- }
-
- } else if (p_idx==1) {
-
- float a = _find_closest_angle_to_half_pi_arc(s[0],s[1],light->get_parameter(Light::PARAM_RADIUS),gt);
- light->set_parameter(Light::PARAM_SPOT_ANGLE,CLAMP(a,0.01,89.99));
- }
-}
-
-void LightSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
-
- if (p_cancel) {
-
- light->set_parameter(p_idx==0?Light::PARAM_RADIUS:Light::PARAM_SPOT_ANGLE,p_restore);
-
- } else if (p_idx==0) {
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Light Radius");
- ur->add_do_method(light,"set_parameter",Light::PARAM_RADIUS,light->get_parameter(Light::PARAM_RADIUS));
- ur->add_undo_method(light,"set_parameter",Light::PARAM_RADIUS,p_restore);
- ur->commit_action();
- } else if (p_idx==1) {
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Light Radius");
- ur->add_do_method(light,"set_parameter",Light::PARAM_SPOT_ANGLE,light->get_parameter(Light::PARAM_SPOT_ANGLE));
- ur->add_undo_method(light,"set_parameter",Light::PARAM_SPOT_ANGLE,p_restore);
- ur->commit_action();
-
- }
-}
-
-
-
-void LightSpatialGizmo::redraw() {
-
-
- if (light->cast_to<DirectionalLight>()) {
-
-
-
- const int arrow_points=5;
- Vector3 arrow[arrow_points]={
- Vector3(0,0,2),
- Vector3(1,1,2),
- Vector3(1,1,-1),
- Vector3(2,2,-1),
- Vector3(0,0,-3)
- };
-
- int arrow_sides=4;
-
- Vector<Vector3> lines;
-
-
- for(int i = 0; i < arrow_sides ; i++) {
-
-
- Matrix3 ma(Vector3(0,0,1),Math_PI*2*float(i)/arrow_sides);
- Matrix3 mb(Vector3(0,0,1),Math_PI*2*float(i+1)/arrow_sides);
-
-
- for(int j=1;j<arrow_points-1;j++) {
-
- if (j!=2) {
- lines.push_back(ma.xform(arrow[j]));
- lines.push_back(ma.xform(arrow[j+1]));
- }
- if (j<arrow_points-1) {
- lines.push_back(ma.xform(arrow[j]));
- lines.push_back(mb.xform(arrow[j]));
- }
-
- }
- }
-
- add_lines(lines,SpatialEditorGizmos::singleton->light_material);
- add_collision_segments(lines);
- add_unscaled_billboard(SpatialEditorGizmos::singleton->light_material_directional_icon,0.05);
-
- }
-
- if (light->cast_to<OmniLight>()) {
-
- clear();
-
-
- OmniLight *on = light->cast_to<OmniLight>();
-
- float r = on->get_parameter(Light::PARAM_RADIUS);
-
- Vector<Vector3> points;
-
- for(int i=0;i<=360;i++) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+1);
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
-
- /*points.push_back(Vector3(a.x,0,a.y));
- points.push_back(Vector3(b.x,0,b.y));
- points.push_back(Vector3(0,a.x,a.y));
- points.push_back(Vector3(0,b.x,b.y));*/
- points.push_back(Vector3(a.x,a.y,0));
- points.push_back(Vector3(b.x,b.y,0));
-
- }
-
- add_lines(points,SpatialEditorGizmos::singleton->light_material,true);
- add_collision_segments(points);
-
- add_unscaled_billboard(SpatialEditorGizmos::singleton->light_material_omni_icon,0.05);
-
- Vector<Vector3> handles;
- handles.push_back(Vector3(r,0,0));
- add_handles(handles,true);
-
-
- }
-
-
- if (light->cast_to<SpotLight>()) {
-
- clear();
-
- Vector<Vector3> points;
- SpotLight *on = light->cast_to<SpotLight>();
-
- float r = on->get_parameter(Light::PARAM_RADIUS);
- float w = r*Math::sin(Math::deg2rad(on->get_parameter(Light::PARAM_SPOT_ANGLE)));
- float d = r*Math::cos(Math::deg2rad(on->get_parameter(Light::PARAM_SPOT_ANGLE)));
-
-
-
- for(int i=0;i<360;i++) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+1);
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w;
-
- /*points.push_back(Vector3(a.x,0,a.y));
- points.push_back(Vector3(b.x,0,b.y));
- points.push_back(Vector3(0,a.x,a.y));
- points.push_back(Vector3(0,b.x,b.y));*/
- points.push_back(Vector3(a.x,a.y,-d));
- points.push_back(Vector3(b.x,b.y,-d));
-
- if (i%90==0) {
-
- points.push_back(Vector3(a.x,a.y,-d));
- points.push_back(Vector3());
-
- }
-
-
- }
-
- points.push_back(Vector3(0,0,-r));
- points.push_back(Vector3());
-
- add_lines(points,SpatialEditorGizmos::singleton->light_material);
-
- Vector<Vector3> handles;
- handles.push_back(Vector3(0,0,-r));
-
- Vector<Vector3> collision_segments;
-
- for(int i=0;i<64;i++) {
-
- float ra=i*Math_PI*2.0/64.0;
- float rb=(i+1)*Math_PI*2.0/64.0;
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w;
-
- collision_segments.push_back(Vector3(a.x,a.y,-d));
- collision_segments.push_back(Vector3(b.x,b.y,-d));
-
- if (i%16==0) {
-
- collision_segments.push_back(Vector3(a.x,a.y,-d));
- collision_segments.push_back(Vector3());
-
- }
-
- if (i==16) {
-
- handles.push_back(Vector3(a.x,a.y,-d));
- }
-
- }
-
- collision_segments.push_back(Vector3(0,0,-r));
- collision_segments.push_back(Vector3());
-
-
- add_handles(handles);
- add_collision_segments(collision_segments);
- add_unscaled_billboard(SpatialEditorGizmos::singleton->light_material_omni_icon,0.05);
-
- }
-
-}
-
-LightSpatialGizmo::LightSpatialGizmo(Light* p_light){
-
- light=p_light;
- set_spatial_node(p_light);
-
-}
-
-//////
-
-String CameraSpatialGizmo::get_handle_name(int p_idx) const {
-
- if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) {
- return "FOV";
- } else {
- return "Size";
- }
-}
-Variant CameraSpatialGizmo::get_handle_value(int p_idx) const{
-
- if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) {
- return camera->get_fov();
- } else {
-
- return camera->get_size();
- }
-}
-void CameraSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){
-
- Transform gt = camera->get_global_transform();
- gt.orthonormalize();
- Transform gi = gt.affine_inverse();
-
- Vector3 ray_from = p_camera->project_ray_origin(p_point);
- Vector3 ray_dir = p_camera->project_ray_normal(p_point);
-
- Vector3 s[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)};
-
- if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) {
- Transform gt=camera->get_global_transform();
- float a = _find_closest_angle_to_half_pi_arc(s[0],s[1],1.0,gt);
- camera->set("fov",a);
- } else {
-
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(Vector3(0,0,-1),Vector3(4096,0,-1),s[0],s[1],ra,rb);
- float d = ra.x * 2.0;
- if (d<0)
- d=0;
-
- camera->set("size",d);
- }
-
-}
-void CameraSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
-
- if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) {
-
- if (p_cancel) {
-
- camera->set("fov",p_restore);
- } else {
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Camera FOV");
- ur->add_do_property(camera,"fov",camera->get_fov());
- ur->add_undo_property(camera,"fov",p_restore);
- ur->commit_action();
- }
-
- } else {
-
- if (p_cancel) {
-
- camera->set("size",p_restore);
- } else {
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Camera Size");
- ur->add_do_property(camera,"size",camera->get_size());
- ur->add_undo_property(camera,"size",p_restore);
- ur->commit_action();
- }
-
- }
-
-}
-
-void CameraSpatialGizmo::redraw(){
-
- clear();
-
- Vector<Vector3> lines;
- Vector<Vector3> handles;
-
-
- switch(camera->get_projection()) {
-
- case Camera::PROJECTION_PERSPECTIVE: {
-
- float fov = camera->get_fov();
-
- Vector3 side=Vector3( Math::sin(Math::deg2rad(fov)), 0, -Math::cos(Math::deg2rad(fov)) );
- Vector3 nside=side;
- nside.x=-nside.x;
- Vector3 up=Vector3(0,side.x,0);
-
-
-#define ADD_TRIANGLE( m_a, m_b, m_c)\
-{\
- lines.push_back(m_a);\
- lines.push_back(m_b);\
- lines.push_back(m_b);\
- lines.push_back(m_c);\
- lines.push_back(m_c);\
- lines.push_back(m_a);\
-}
-
- ADD_TRIANGLE( Vector3(), side+up, side-up );
- ADD_TRIANGLE( Vector3(), nside+up, nside-up );
- ADD_TRIANGLE( Vector3(), side+up, nside+up );
- ADD_TRIANGLE( Vector3(), side-up, nside-up );
-
- handles.push_back(side);
- side.x*=0.25;
- nside.x*=0.25;
- Vector3 tup( 0, up.y*3/2,side.z);
- ADD_TRIANGLE( tup, side+up, nside+up );
-
- } break;
- case Camera::PROJECTION_ORTHOGONAL: {
-
-#define ADD_QUAD( m_a, m_b, m_c, m_d)\
-{\
- lines.push_back(m_a);\
- lines.push_back(m_b);\
- lines.push_back(m_b);\
- lines.push_back(m_c);\
- lines.push_back(m_c);\
- lines.push_back(m_d);\
- lines.push_back(m_d);\
- lines.push_back(m_a);\
-}
- float size = camera->get_size();
-
- float hsize=size*0.5;
- Vector3 right(hsize,0,0);
- Vector3 up(0,hsize,0);
- Vector3 back(0,0,-1.0);
- Vector3 front(0,0,0);
-
- ADD_QUAD( -up-right,-up+right,up+right,up-right);
- ADD_QUAD( -up-right+back,-up+right+back,up+right+back,up-right+back);
- ADD_QUAD( up+right,up+right+back,up-right+back,up-right);
- ADD_QUAD( -up+right,-up+right+back,-up-right+back,-up-right);
- handles.push_back(right+back);
-
- right.x*=0.25;
- Vector3 tup( 0, up.y*3/2,back.z );
- ADD_TRIANGLE( tup, right+up+back, -right+up+back );
-
- } break;
-
- }
-
- add_lines(lines,SpatialEditorGizmos::singleton->camera_material);
- add_collision_segments(lines);
- add_handles(handles);
-}
-
-
-CameraSpatialGizmo::CameraSpatialGizmo(Camera* p_camera){
-
- camera=p_camera;
- set_spatial_node(camera);
-}
-
-
-
-
-//////
-
-void MeshInstanceSpatialGizmo::redraw() {
-
- Ref<Mesh> m = mesh->get_mesh();
- if (!m.is_valid())
- return; //none
-
- Ref<TriangleMesh> tm = m->generate_triangle_mesh();
- if (tm.is_valid())
- add_collision_triangles(tm);
-}
-
-MeshInstanceSpatialGizmo::MeshInstanceSpatialGizmo(MeshInstance* p_mesh) {
-
- mesh=p_mesh;
- set_spatial_node(p_mesh);
-}
-
-/////
-
-
-void Position3DSpatialGizmo::redraw() {
-
- clear();
- add_mesh(SpatialEditorGizmos::singleton->pos3d_mesh);
- Vector<Vector3> cursor_points;
- float cs = 0.25;
- cursor_points.push_back(Vector3(+cs,0,0));
- cursor_points.push_back(Vector3(-cs,0,0));
- cursor_points.push_back(Vector3(0,+cs,0));
- cursor_points.push_back(Vector3(0,-cs,0));
- cursor_points.push_back(Vector3(0,0,+cs));
- cursor_points.push_back(Vector3(0,0,-cs));
- add_collision_segments(cursor_points);
-
-}
-
-
-Position3DSpatialGizmo::Position3DSpatialGizmo(Position3D* p_p3d) {
-
- p3d=p_p3d;
- set_spatial_node(p3d);
-}
-
-
-/////
-
-void SkeletonSpatialGizmo::redraw() {
-
- clear();
-
- Ref<SurfaceTool> surface_tool( memnew( SurfaceTool ));
-
-
- surface_tool->begin(Mesh::PRIMITIVE_LINES);
- surface_tool->set_material(SpatialEditorGizmos::singleton->skeleton_material);
- Vector<Transform> grests;
- grests.resize(skel->get_bone_count());
-
- Vector<int> bones;
- Vector<float> weights;
- bones.resize(4);
- weights.resize(4);
-
- for(int i=0;i<4;i++) {
- bones[i]=0;
- weights[i]=0;
- }
-
- weights[0]=1;
-
-
- AABB aabb;
-
- Color bonecolor = Color(1.0,0.4,0.4,0.3);
- Color rootcolor = Color(0.4,1.0,0.4,0.1);
-
- for (int i=0;i<skel->get_bone_count();i++) {
-
- int parent = skel->get_bone_parent(i);
-
- if (parent>=0) {
- grests[i]=grests[parent] * skel->get_bone_rest(i);
-
- Vector3 v0 = grests[parent].origin;
- Vector3 v1 = grests[i].origin;
- Vector3 d = (v1-v0).normalized();
- float dist = v0.distance_to(v1);
-
- //find closest axis
- int closest=-1;
- float closest_d;
- for(int j=0;j<3;j++) {
- float dp = Math::abs(grests[parent].basis[j].normalized().dot(d));
- if (j==0 || dp>closest_d)
- closest=j;
- }
-
- //find closest other
- Vector3 first;
- Vector3 points[4];
- int pointidx=0;
- for(int j=0;j<3;j++) {
-
- bones[0]=parent;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(rootcolor);
- surface_tool->add_vertex(v0-grests[parent].basis[j].normalized()*dist*0.05);
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(rootcolor);
- surface_tool->add_vertex(v0+grests[parent].basis[j].normalized()*dist*0.05);
-
- if (j==closest)
- continue;
-
- Vector3 axis;
- if (first==Vector3()) {
- axis = d.cross(d.cross(grests[parent].basis[j])).normalized();
- first=axis;
- } else {
- axis = d.cross(first).normalized();
- }
-
- for(int k=0;k<2;k++) {
-
- if (k==1)
- axis=-axis;
- Vector3 point = v0+d*dist*0.2;
- point+=axis*dist*0.1;
-
-
- bones[0]=parent;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
- surface_tool->add_vertex(v0);
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
- surface_tool->add_vertex(point);
-
- bones[0]=parent;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
- surface_tool->add_vertex(point);
- bones[0]=i;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
- surface_tool->add_vertex(v1);
- points[pointidx++]=point;
-
- }
-
- }
-
- SWAP( points[1],points[2] );
- for(int j=0;j<4;j++) {
-
-
- bones[0]=parent;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
- surface_tool->add_vertex(points[j]);
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
- surface_tool->add_vertex(points[(j+1)%4]);
- }
-
-
-/*
- bones[0]=parent;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(Color(0.4,1,0.4,0.4));
- surface_tool->add_vertex(v0);
- bones[0]=i;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(Color(0.4,1,0.4,0.4));
- surface_tool->add_vertex(v1);
-*/
- } else {
-
- grests[i]=skel->get_bone_rest(i);
- bones[0]=i;
- }
-/*
- Transform t = grests[i];
- t.orthonormalize();
-
- for (int i=0;i<6;i++) {
-
-
- Vector3 face_points[4];
-
- for (int j=0;j<4;j++) {
-
- float v[3];
- v[0]=1.0;
- v[1]=1-2*((j>>1)&1);
- v[2]=v[1]*(1-2*(j&1));
-
- for (int k=0;k<3;k++) {
-
- if (i<3)
- face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
- else
- face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
- }
- }
-
- for(int j=0;j<4;j++) {
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(Color(1.0,0.4,0.4,0.4));
- surface_tool->add_vertex(t.xform(face_points[j]*0.04));
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(Color(1.0,0.4,0.4,0.4));
- surface_tool->add_vertex(t.xform(face_points[(j+1)%4]*0.04));
- }
-
- }
- */
- }
-
- Ref<Mesh> m = surface_tool->commit();
- add_mesh(m,false,skel->get_skeleton());
-
-}
-
-SkeletonSpatialGizmo::SkeletonSpatialGizmo(Skeleton* p_skel) {
-
- skel=p_skel;
- set_spatial_node(p_skel);
-}
-
-/////
-
-
-void SpatialPlayerSpatialGizmo::redraw() {
-
- clear();
- if (splayer->cast_to<SpatialStreamPlayer>()) {
-
- add_unscaled_billboard(SpatialEditorGizmos::singleton->stream_player_icon,0.05);
-
- } else if (splayer->cast_to<SpatialSamplePlayer>()) {
-
- add_unscaled_billboard(SpatialEditorGizmos::singleton->sample_player_icon,0.05);
-
- }
-
-}
-
-SpatialPlayerSpatialGizmo::SpatialPlayerSpatialGizmo(SpatialPlayer* p_splayer){
-
- set_spatial_node(p_splayer);
- splayer=p_splayer;
-}
-
-
-/////
-
-
-void RoomSpatialGizmo::redraw() {
-
- clear();
- Ref<RoomBounds> roomie = room->get_room();
- if (roomie.is_null())
- return;
- DVector<Face3> faces = roomie->get_geometry_hint();
-
- Vector<Vector3> lines;
- int fc=faces.size();
- DVector<Face3>::Read r =faces.read();
-
- Map<_EdgeKey,Vector3> edge_map;
-
- for(int i=0;i<fc;i++) {
-
- Vector3 fn = r[i].get_plane().normal;
-
- for(int j=0;j<3;j++) {
-
- _EdgeKey ek;
- ek.from=r[i].vertex[j].snapped(CMP_EPSILON);
- ek.to=r[i].vertex[(j+1)%3].snapped(CMP_EPSILON);
- if (ek.from<ek.to)
- SWAP(ek.from,ek.to);
-
- Map<_EdgeKey,Vector3>::Element *E=edge_map.find(ek);
-
- if (E) {
-
- if (E->get().dot(fn) >0.9) {
-
- E->get()=Vector3();
- }
-
- } else {
-
- edge_map[ek]=fn;
- }
-
- }
- }
-
- for(Map<_EdgeKey,Vector3>::Element *E=edge_map.front();E;E=E->next()) {
-
- if (E->get()!=Vector3()) {
- lines.push_back(E->key().from);
- lines.push_back(E->key().to);
- }
- }
-
- add_lines(lines,SpatialEditorGizmos::singleton->room_material);
- add_collision_segments(lines);
-
-}
-
-RoomSpatialGizmo::RoomSpatialGizmo(Room* p_room){
-
- set_spatial_node(p_room);
- room=p_room;
-}
-
-/////
-
-
-void PortalSpatialGizmo::redraw() {
-
- clear();
-
- Vector<Point2> points = portal->get_shape();
- if (points.size()==0) {
- return;
- }
-
- Vector<Vector3> lines;
-
- Vector3 center;
- for(int i=0;i<points.size();i++) {
-
- Vector3 f;
- f.x=points[i].x;
- f.y=points[i].y;
- Vector3 fn;
- fn.x=points[(i+1)%points.size()].x;
- fn.y=points[(i+1)%points.size()].y;
- center+=f;
-
- lines.push_back(f);
- lines.push_back(fn);
- }
-
- center/=points.size();
- lines.push_back(center);
- lines.push_back(center+Vector3(0,0,1));
-
- add_lines(lines,SpatialEditorGizmos::singleton->portal_material);
- add_collision_segments(lines);
-
-}
-
-PortalSpatialGizmo::PortalSpatialGizmo(Portal* p_portal){
-
- set_spatial_node(p_portal);
- portal=p_portal;
-}
-
-/////
-
-
-void RayCastSpatialGizmo::redraw() {
-
- clear();
-
-
- Vector<Vector3> lines;
-
- lines.push_back(Vector3());
- lines.push_back(raycast->get_cast_to());
-
- add_lines(lines,SpatialEditorGizmos::singleton->raycast_material);
- add_collision_segments(lines);
-
-}
-
-RayCastSpatialGizmo::RayCastSpatialGizmo(RayCast* p_raycast){
-
- set_spatial_node(p_raycast);
- raycast=p_raycast;
-}
-
-
-
-/////
-
-
-void VehicleWheelSpatialGizmo::redraw() {
-
- clear();
-
-
- Vector<Vector3> points;
-
- float r = car_wheel->get_radius();
- const int skip=10;
- for(int i=0;i<=360;i+=skip) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+skip);
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
-
- points.push_back(Vector3(0,a.x,a.y));
- points.push_back(Vector3(0,b.x,b.y));
-
- const int springsec=4;
-
- for(int j=0;j<springsec;j++) {
- float t = car_wheel->get_suspension_rest_length()*5;
- points.push_back(Vector3(a.x,i/360.0*t/springsec+j*(t/springsec),a.y)*0.2);
- points.push_back(Vector3(b.x,(i+skip)/360.0*t/springsec+j*(t/springsec),b.y)*0.2);
- }
-
-
- }
-
- //travel
- points.push_back(Vector3(0,0,0));
- points.push_back(Vector3(0,car_wheel->get_suspension_rest_length(),0));
-
- //axis
- points.push_back(Vector3(r*0.2,car_wheel->get_suspension_rest_length(),0));
- points.push_back(Vector3(-r*0.2,car_wheel->get_suspension_rest_length(),0));
- //axis
- points.push_back(Vector3(r*0.2,0,0));
- points.push_back(Vector3(-r*0.2,0,0));
-
- //forward line
- points.push_back(Vector3(0,-r,0));
- points.push_back(Vector3(0,-r,r*2));
- points.push_back(Vector3(0,-r,r*2));
- points.push_back(Vector3(r*2*0.2,-r,r*2*0.8));
- points.push_back(Vector3(0,-r,r*2));
- points.push_back(Vector3(-r*2*0.2,-r,r*2*0.8));
-
- add_lines(points,SpatialEditorGizmos::singleton->car_wheel_material);
- add_collision_segments(points);
-
-}
-
-VehicleWheelSpatialGizmo::VehicleWheelSpatialGizmo(VehicleWheel* p_car_wheel){
-
- set_spatial_node(p_car_wheel);
- car_wheel=p_car_wheel;
-}
-
-
-
-///
-
-void TestCubeSpatialGizmo::redraw() {
-
- clear();
- add_collision_triangles(SpatialEditorGizmos::singleton->test_cube_tm);
-}
-
-TestCubeSpatialGizmo::TestCubeSpatialGizmo(TestCube* p_tc) {
-
- tc=p_tc;
- set_spatial_node(p_tc);
-}
-
-
-///////////
-
-
-
-
-
-
-String CollisionShapeSpatialGizmo::get_handle_name(int p_idx) const {
-
- Ref<Shape> s = cs->get_shape();
- if (s.is_null())
- return "";
-
- if (s->cast_to<SphereShape>()) {
-
- return "Radius";
- }
-
- if (s->cast_to<BoxShape>()) {
-
- return "Extents";
- }
-
- if (s->cast_to<CapsuleShape>()) {
-
- return p_idx==0?"Radius":"Height";
- }
-
- if (s->cast_to<RayShape>()) {
-
- return "Length";
- }
-
- return "";
-}
-Variant CollisionShapeSpatialGizmo::get_handle_value(int p_idx) const{
-
- Ref<Shape> s = cs->get_shape();
- if (s.is_null())
- return Variant();
-
- if (s->cast_to<SphereShape>()) {
-
- Ref<SphereShape> ss = s;
- return ss->get_radius();
- }
-
- if (s->cast_to<BoxShape>()) {
-
- Ref<BoxShape> bs = s;
- return bs->get_extents();
- }
-
- if (s->cast_to<CapsuleShape>()) {
-
- Ref<CapsuleShape> cs = s;
- return p_idx==0?cs->get_radius():cs->get_height();
- }
-
- if (s->cast_to<RayShape>()) {
-
- Ref<RayShape> cs = s;
- return cs->get_length();
- }
-
- return Variant();
-}
-void CollisionShapeSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){
- Ref<Shape> s = cs->get_shape();
- if (s.is_null())
- return;
-
- Transform gt = cs->get_global_transform();
- gt.orthonormalize();
- Transform gi = gt.affine_inverse();
-
- Vector3 ray_from = p_camera->project_ray_origin(p_point);
- Vector3 ray_dir = p_camera->project_ray_normal(p_point);
-
- Vector3 sg[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)};
-
- if (s->cast_to<SphereShape>()) {
-
- Ref<SphereShape> ss = s;
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(Vector3(),Vector3(4096,0,0),sg[0],sg[1],ra,rb);
- float d = ra.x;
- if (d<0.001)
- d=0.001;
-
- ss->set_radius(d);
- }
-
- if (s->cast_to<RayShape>()) {
-
- Ref<RayShape> rs = s;
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(Vector3(),Vector3(0,0,4096),sg[0],sg[1],ra,rb);
- float d = ra.z;
- if (d<0.001)
- d=0.001;
-
- rs->set_length(d);
- }
-
-
- if (s->cast_to<BoxShape>()) {
-
- Vector3 axis;
- axis[p_idx]=1.0;
- Ref<BoxShape> bs = s;
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(Vector3(),axis*4096,sg[0],sg[1],ra,rb);
- float d = ra[p_idx];
- if (d<0.001)
- d=0.001;
-
- Vector3 he = bs->get_extents();
- he[p_idx]=d;
- bs->set_extents(he);
-
- }
-
- if (s->cast_to<CapsuleShape>()) {
-
- Vector3 axis;
- axis[p_idx==0?0:2]=1.0;
- Ref<CapsuleShape> cs = s;
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(Vector3(),axis*4096,sg[0],sg[1],ra,rb);
- float d = axis.dot(ra);
- if (p_idx==1)
- d-=cs->get_radius();
- if (d<0.001)
- d=0.001;
-
- if (p_idx==0)
- cs->set_radius(d);
- else if (p_idx==1)
- cs->set_height(d*2.0);
-
- }
-
-}
-void CollisionShapeSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
- Ref<Shape> s = cs->get_shape();
- if (s.is_null())
- return;
-
- if (s->cast_to<SphereShape>()) {
-
- Ref<SphereShape> ss=s;
- if (p_cancel) {
- ss->set_radius(p_restore);
- return;
- }
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Sphere Shape Radius");
- ur->add_do_method(ss.ptr(),"set_radius",ss->get_radius());
- ur->add_undo_method(ss.ptr(),"set_radius",p_restore);
- ur->commit_action();
-
- }
-
- if (s->cast_to<BoxShape>()) {
-
- Ref<BoxShape> ss=s;
- if (p_cancel) {
- ss->set_extents(p_restore);
- return;
- }
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Box Shape Extents");
- ur->add_do_method(ss.ptr(),"set_extents",ss->get_extents());
- ur->add_undo_method(ss.ptr(),"set_extents",p_restore);
- ur->commit_action();
- }
-
- if (s->cast_to<CapsuleShape>()) {
-
- Ref<CapsuleShape> ss=s;
- if (p_cancel) {
- if (p_idx==0)
- ss->set_radius(p_restore);
- else
- ss->set_height(p_restore);
- return;
- }
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- if (p_idx==0) {
- ur->create_action("Change Capsule Shape Radius");
- ur->add_do_method(ss.ptr(),"set_radius",ss->get_radius());
- ur->add_undo_method(ss.ptr(),"set_radius",p_restore);
- } else {
- ur->create_action("Change Capsule Shape Height");
- ur->add_do_method(ss.ptr(),"set_height",ss->get_height());
- ur->add_undo_method(ss.ptr(),"set_height",p_restore);
-
- }
-
- ur->commit_action();
-
- }
-
- if (s->cast_to<RayShape>()) {
-
- Ref<RayShape> ss=s;
- if (p_cancel) {
- ss->set_length(p_restore);
- return;
- }
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Ray Shape Length");
- ur->add_do_method(ss.ptr(),"set_length",ss->get_length());
- ur->add_undo_method(ss.ptr(),"set_length",p_restore);
- ur->commit_action();
-
- }
-
-}
-void CollisionShapeSpatialGizmo::redraw(){
-
- clear();
-
- Ref<Shape> s = cs->get_shape();
- if (s.is_null())
- return;
-
- if (s->cast_to<SphereShape>()) {
-
- Ref<SphereShape> sp= s;
- float r=sp->get_radius();
-
- Vector<Vector3> points;
-
- for(int i=0;i<=360;i++) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+1);
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
-
- points.push_back(Vector3(a.x,0,a.y));
- points.push_back(Vector3(b.x,0,b.y));
- points.push_back(Vector3(0,a.x,a.y));
- points.push_back(Vector3(0,b.x,b.y));
- points.push_back(Vector3(a.x,a.y,0));
- points.push_back(Vector3(b.x,b.y,0));
-
- }
-
- Vector<Vector3> collision_segments;
-
- for(int i=0;i<64;i++) {
-
- float ra=i*Math_PI*2.0/64.0;
- float rb=(i+1)*Math_PI*2.0/64.0;
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r;
-
- collision_segments.push_back(Vector3(a.x,0,a.y));
- collision_segments.push_back(Vector3(b.x,0,b.y));
- collision_segments.push_back(Vector3(0,a.x,a.y));
- collision_segments.push_back(Vector3(0,b.x,b.y));
- collision_segments.push_back(Vector3(a.x,a.y,0));
- collision_segments.push_back(Vector3(b.x,b.y,0));
- }
-
- add_lines(points,SpatialEditorGizmos::singleton->shape_material);
- add_collision_segments(collision_segments);
- Vector<Vector3> handles;
- handles.push_back(Vector3(r,0,0));
- add_handles(handles);
-
- }
-
- if (s->cast_to<BoxShape>()) {
-
- Ref<BoxShape> bs=s;
- Vector<Vector3> lines;
- AABB aabb;
- aabb.pos=-bs->get_extents();
- aabb.size=aabb.pos*-2;
-
- for(int i=0;i<12;i++) {
- Vector3 a,b;
- aabb.get_edge(i,a,b);
- lines.push_back(a);
- lines.push_back(b);
- }
-
- Vector<Vector3> handles;
-
- for(int i=0;i<3;i++) {
-
- Vector3 ax;
- ax[i]=bs->get_extents()[i];
- handles.push_back(ax);
- }
-
- add_lines(lines,SpatialEditorGizmos::singleton->shape_material);
- add_collision_segments(lines);
- add_handles(handles);
-
- }
-
- if (s->cast_to<CapsuleShape>()) {
-
- Ref<CapsuleShape> cs=s;
- float radius = cs->get_radius();
- float height = cs->get_height();
-
-
- Vector<Vector3> points;
-
- Vector3 d(0,0,height*0.5);
- for(int i=0;i<360;i++) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+1);
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*radius;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*radius;
-
- points.push_back(Vector3(a.x,a.y,0)+d);
- points.push_back(Vector3(b.x,b.y,0)+d);
-
- points.push_back(Vector3(a.x,a.y,0)-d);
- points.push_back(Vector3(b.x,b.y,0)-d);
-
- if (i%90==0) {
-
- points.push_back(Vector3(a.x,a.y,0)+d);
- points.push_back(Vector3(a.x,a.y,0)-d);
- }
-
- Vector3 dud = i<180?d:-d;
-
- points.push_back(Vector3(0,a.y,a.x)+dud);
- points.push_back(Vector3(0,b.y,b.x)+dud);
- points.push_back(Vector3(a.y,0,a.x)+dud);
- points.push_back(Vector3(b.y,0,b.x)+dud);
-
- }
-
- add_lines(points,SpatialEditorGizmos::singleton->shape_material);
-
- Vector<Vector3> collision_segments;
-
- for(int i=0;i<64;i++) {
-
- float ra=i*Math_PI*2.0/64.0;
- float rb=(i+1)*Math_PI*2.0/64.0;
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*radius;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*radius;
-
- collision_segments.push_back(Vector3(a.x,a.y,0)+d);
- collision_segments.push_back(Vector3(b.x,b.y,0)+d);
-
- collision_segments.push_back(Vector3(a.x,a.y,0)-d);
- collision_segments.push_back(Vector3(b.x,b.y,0)-d);
-
- if (i%16==0) {
-
- collision_segments.push_back(Vector3(a.x,a.y,0)+d);
- collision_segments.push_back(Vector3(a.x,a.y,0)-d);
- }
-
- Vector3 dud = i<32?d:-d;
-
- collision_segments.push_back(Vector3(0,a.y,a.x)+dud);
- collision_segments.push_back(Vector3(0,b.y,b.x)+dud);
- collision_segments.push_back(Vector3(a.y,0,a.x)+dud);
- collision_segments.push_back(Vector3(b.y,0,b.x)+dud);
-
- }
-
- add_collision_segments(collision_segments);
-
- Vector<Vector3> handles;
- handles.push_back(Vector3(cs->get_radius(),0,0));
- handles.push_back(Vector3(0,0,cs->get_height()*0.5+cs->get_radius()));
- add_handles(handles);
-
-
- }
-
- if (s->cast_to<PlaneShape>()) {
-
- Ref<PlaneShape> ps=s;
- Plane p = ps->get_plane();
- Vector<Vector3> points;
-
- Vector3 n1 = p.get_any_perpendicular_normal();
- Vector3 n2 = p.normal.cross(n1).normalized();
-
- Vector3 pface[4]={
- p.normal*p.d+n1*10.0+n2*10.0,
- p.normal*p.d+n1*10.0+n2*-10.0,
- p.normal*p.d+n1*-10.0+n2*-10.0,
- p.normal*p.d+n1*-10.0+n2*10.0,
- };
-
- points.push_back(pface[0]);
- points.push_back(pface[1]);
- points.push_back(pface[1]);
- points.push_back(pface[2]);
- points.push_back(pface[2]);
- points.push_back(pface[3]);
- points.push_back(pface[3]);
- points.push_back(pface[0]);
- points.push_back(p.normal*p.d);
- points.push_back(p.normal*p.d+p.normal*3);
-
- add_lines(points,SpatialEditorGizmos::singleton->shape_material);
- add_collision_segments(points);
-
- }
-
-
- if (s->cast_to<ConvexPolygonShape>()) {
-
- DVector<Vector3> points = s->cast_to<ConvexPolygonShape>()->get_points();
-
- if (points.size()>3) {
-
- QuickHull qh;
- Vector<Vector3> varr = Variant(points);
- Geometry::MeshData md;
- Error err = qh.build(varr,md);
- if (err==OK) {
- Vector<Vector3> points;
- points.resize(md.edges.size()*2);
- for(int i=0;i<md.edges.size();i++) {
- points[i*2+0]=md.vertices[md.edges[i].a];
- points[i*2+1]=md.vertices[md.edges[i].b];
- }
-
-
- add_lines(points,SpatialEditorGizmos::singleton->shape_material);
- add_collision_segments(points);
-
- }
- }
-
- }
-
-
- if (s->cast_to<RayShape>()) {
-
- Ref<RayShape> rs=s;
-
- Vector<Vector3> points;
- points.push_back(Vector3());
- points.push_back(Vector3(0,0,rs->get_length()));
- add_lines(points,SpatialEditorGizmos::singleton->shape_material);
- add_collision_segments(points);
- Vector<Vector3> handles;
- handles.push_back(Vector3(0,0,rs->get_length()));
- add_handles(handles);
-
-
- }
-
-}
-CollisionShapeSpatialGizmo::CollisionShapeSpatialGizmo(CollisionShape* p_cs) {
-
- cs=p_cs;
- set_spatial_node(p_cs);
-}
-
-
-
-/////
-
-
-void CollisionPolygonSpatialGizmo::redraw() {
-
- clear();
-
- Vector<Vector2> points = polygon->get_polygon();
- float depth = polygon->get_depth()*0.5;
-
- Vector<Vector3> lines;
- for(int i=0;i<points.size();i++) {
-
- int n = (i+1)%points.size();
- lines.push_back(Vector3(points[i].x,points[i].y,depth));
- lines.push_back(Vector3(points[n].x,points[n].y,depth));
- lines.push_back(Vector3(points[i].x,points[i].y,-depth));
- lines.push_back(Vector3(points[n].x,points[n].y,-depth));
- lines.push_back(Vector3(points[i].x,points[i].y,depth));
- lines.push_back(Vector3(points[i].x,points[i].y,-depth));
-
- }
-
- add_lines(lines,SpatialEditorGizmos::singleton->shape_material);
- add_collision_segments(lines);
-}
-
-CollisionPolygonSpatialGizmo::CollisionPolygonSpatialGizmo(CollisionPolygon* p_polygon){
-
- set_spatial_node(p_polygon);
- polygon=p_polygon;
-}
-///
-
-
-String VisibilityNotifierGizmo::get_handle_name(int p_idx) const {
-
- switch(p_idx) {
- case 0: return "X";
- case 1: return "Y";
- case 2: return "Z";
- }
-
- return "";
-}
-Variant VisibilityNotifierGizmo::get_handle_value(int p_idx) const{
-
- return notifier->get_aabb();
-}
-void VisibilityNotifierGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){
-
-
- Transform gt = notifier->get_global_transform();
- //gt.orthonormalize();
- Transform gi = gt.affine_inverse();
-
- AABB aabb = notifier->get_aabb();
- Vector3 ray_from = p_camera->project_ray_origin(p_point);
- Vector3 ray_dir = p_camera->project_ray_normal(p_point);
-
- Vector3 sg[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)};
- Vector3 ofs = aabb.pos+aabb.size*0.5;;
-
- Vector3 axis;
- axis[p_idx]=1.0;
-
- Vector3 ra,rb;
- Geometry::get_closest_points_between_segments(ofs,ofs+axis*4096,sg[0],sg[1],ra,rb);
- float d = ra[p_idx];
- if (d<0.001)
- d=0.001;
-
- Vector3 he = aabb.size;
- aabb.pos[p_idx]=(aabb.pos[p_idx]+aabb.size[p_idx]*0.5)-d;
- aabb.size[p_idx]=d*2;
- notifier->set_aabb(aabb);
-}
-
-void VisibilityNotifierGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
-
-
- if (p_cancel) {
- notifier->set_aabb(p_restore);
- return;
- }
-
- UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
- ur->create_action("Change Notifier Extents");
- ur->add_do_method(notifier,"set_aabb",notifier->get_aabb());
- ur->add_undo_method(notifier,"set_aabb",p_restore);
- ur->commit_action();
-
-}
-
-void VisibilityNotifierGizmo::redraw(){
-
- clear();
-
- Vector<Vector3> lines;
- AABB aabb = notifier->get_aabb();
-
- for(int i=0;i<12;i++) {
- Vector3 a,b;
- aabb.get_edge(i,a,b);
- lines.push_back(a);
- lines.push_back(b);
- }
-
- Vector<Vector3> handles;
-
-
- for(int i=0;i<3;i++) {
-
- Vector3 ax;
- ax[i]=aabb.pos[i]+aabb.size[i];
- handles.push_back(ax);
- }
-
- add_lines(lines,SpatialEditorGizmos::singleton->visibility_notifier_material);
- //add_unscaled_billboard(SpatialEditorGizmos::singleton->visi,0.05);
- add_collision_segments(lines);
- add_handles(handles);
-
-}
-VisibilityNotifierGizmo::VisibilityNotifierGizmo(VisibilityNotifier* p_notifier){
-
- notifier=p_notifier;
- set_spatial_node(p_notifier);
-}
-
-////////
-
-
-
-void NavigationMeshSpatialGizmo::redraw() {
-
- clear();
- Ref<NavigationMesh> navmeshie = navmesh->get_navigation_mesh();
- if (navmeshie.is_null())
- return;
-
- DVector<Vector3> vertices = navmeshie->get_vertices();
- DVector<Vector3>::Read vr=vertices.read();
- List<Face3> faces;
- for(int i=0;i<navmeshie->get_polygon_count();i++) {
- Vector<int> p = navmeshie->get_polygon(i);
-
- for(int j=2;j<p.size();j++) {
- Face3 f;
- f.vertex[0]=vr[p[0]];
- f.vertex[1]=vr[p[j-1]];
- f.vertex[2]=vr[p[j]];
-
- faces.push_back(f);
- }
- }
-
-
- Map<_EdgeKey,bool> edge_map;
- DVector<Vector3> tmeshfaces;
- tmeshfaces.resize(faces.size()*3);
-
- {
- DVector<Vector3>::Write tw=tmeshfaces.write();
- int tidx=0;
-
-
- for(List<Face3>::Element *E=faces.front();E;E=E->next()) {
-
- const Face3 &f = E->get();
-
- for(int j=0;j<3;j++) {
-
- tw[tidx++]=f.vertex[j];
- _EdgeKey ek;
- ek.from=f.vertex[j].snapped(CMP_EPSILON);
- ek.to=f.vertex[(j+1)%3].snapped(CMP_EPSILON);
- if (ek.from<ek.to)
- SWAP(ek.from,ek.to);
-
- Map<_EdgeKey,bool>::Element *E=edge_map.find(ek);
-
- if (E) {
-
- E->get()=false;
-
- } else {
-
- edge_map[ek]=true;
- }
-
- }
- }
- }
- Vector<Vector3> lines;
-
- for(Map<_EdgeKey,bool>::Element *E=edge_map.front();E;E=E->next()) {
-
- if (E->get()) {
- lines.push_back(E->key().from);
- lines.push_back(E->key().to);
- }
- }
-
- Ref<TriangleMesh> tmesh = memnew( TriangleMesh);
- tmesh->create(tmeshfaces);
-
- if (lines.size())
- add_lines(lines,navmesh->is_enabled()?SpatialEditorGizmos::singleton->navmesh_edge_material:SpatialEditorGizmos::singleton->navmesh_edge_material_disabled);
- add_collision_triangles(tmesh);
- Ref<Mesh> m = memnew( Mesh );
- Array a;
- a.resize(Mesh::ARRAY_MAX);
- a[0]=tmeshfaces;
- m->add_surface(Mesh::PRIMITIVE_TRIANGLES,a);
- m->surface_set_material(0,navmesh->is_enabled()?SpatialEditorGizmos::singleton->navmesh_solid_material:SpatialEditorGizmos::singleton->navmesh_solid_material_disabled);
- add_mesh(m);
- add_collision_segments(lines);
-
-}
-
-NavigationMeshSpatialGizmo::NavigationMeshSpatialGizmo(NavigationMeshInstance *p_navmesh){
-
- set_spatial_node(p_navmesh);
- navmesh=p_navmesh;
-}
-
-//////
-///
-///
-
-
-
-void PinJointSpatialGizmo::redraw() {
-
- clear();
- Vector<Vector3> cursor_points;
- float cs = 0.25;
- cursor_points.push_back(Vector3(+cs,0,0));
- cursor_points.push_back(Vector3(-cs,0,0));
- cursor_points.push_back(Vector3(0,+cs,0));
- cursor_points.push_back(Vector3(0,-cs,0));
- cursor_points.push_back(Vector3(0,0,+cs));
- cursor_points.push_back(Vector3(0,0,-cs));
- add_collision_segments(cursor_points);
- add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
-
-}
-
-
-PinJointSpatialGizmo::PinJointSpatialGizmo(PinJoint* p_p3d) {
-
- p3d=p_p3d;
- set_spatial_node(p3d);
-}
-
-////
-
-void HingeJointSpatialGizmo::redraw() {
-
- clear();
- Vector<Vector3> cursor_points;
- float cs = 0.25;
- /*cursor_points.push_back(Vector3(+cs,0,0));
- cursor_points.push_back(Vector3(-cs,0,0));
- cursor_points.push_back(Vector3(0,+cs,0));
- cursor_points.push_back(Vector3(0,-cs,0));*/
- cursor_points.push_back(Vector3(0,0,+cs*2));
- cursor_points.push_back(Vector3(0,0,-cs*2));
-
- float ll = p3d->get_param(HingeJoint::PARAM_LIMIT_LOWER);
- float ul = p3d->get_param(HingeJoint::PARAM_LIMIT_UPPER);
-
- if (p3d->get_flag(HingeJoint::FLAG_USE_LIMIT) && ll<ul) {
-
- const int points = 32;
- float step = (ul-ll)/points;
-
-
- for(int i=0;i<points;i++) {
-
- float s = ll+i*(ul-ll)/points;
- float n = ll+(i+1)*(ul-ll)/points;
-
- Vector3 from=Vector3( -Math::sin(s),Math::cos(s), 0 )*cs;
- Vector3 to=Vector3( -Math::sin(n),Math::cos(n), 0 )*cs;
-
- if (i==points-1) {
- cursor_points.push_back(to);
- cursor_points.push_back(Vector3());
- }
- if (i==0) {
- cursor_points.push_back(from);
- cursor_points.push_back(Vector3());
- }
-
-
- cursor_points.push_back(from);
- cursor_points.push_back(to);
-
-
- }
-
- cursor_points.push_back(Vector3(0,cs*1.5,0));
- cursor_points.push_back(Vector3());
-
- } else {
-
-
- const int points = 32;
-
- for(int i=0;i<points;i++) {
-
- float s = ll+i*(Math_PI*2.0)/points;
- float n = ll+(i+1)*(Math_PI*2.0)/points;
-
- Vector3 from=Vector3( -Math::sin(s),Math::cos(s), 0 )*cs;
- Vector3 to=Vector3( -Math::sin(n),Math::cos(n), 0 )*cs;
-
- cursor_points.push_back(from);
- cursor_points.push_back(to);
-
- }
-
- }
- add_collision_segments(cursor_points);
- add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
-
-}
-
-
-HingeJointSpatialGizmo::HingeJointSpatialGizmo(HingeJoint* p_p3d) {
-
- p3d=p_p3d;
- set_spatial_node(p3d);
-}
-
-///////
-///
-////
-
-void SliderJointSpatialGizmo::redraw() {
-
- clear();
- Vector<Vector3> cursor_points;
- float cs = 0.25;
- /*cursor_points.push_back(Vector3(+cs,0,0));
- cursor_points.push_back(Vector3(-cs,0,0));
- cursor_points.push_back(Vector3(0,+cs,0));
- cursor_points.push_back(Vector3(0,-cs,0));*/
- cursor_points.push_back(Vector3(0,0,+cs*2));
- cursor_points.push_back(Vector3(0,0,-cs*2));
-
- float ll = p3d->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_LOWER);
- float ul = p3d->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_UPPER);
- float lll = -p3d->get_param(SliderJoint::PARAM_LINEAR_LIMIT_LOWER);
- float lul = -p3d->get_param(SliderJoint::PARAM_LINEAR_LIMIT_UPPER);
-
- if (lll>lul) {
-
- cursor_points.push_back(Vector3(lul,0,0));
- cursor_points.push_back(Vector3(lll,0,0));
-
- cursor_points.push_back(Vector3(lul,-cs,-cs));
- cursor_points.push_back(Vector3(lul,-cs,cs));
- cursor_points.push_back(Vector3(lul,-cs,cs));
- cursor_points.push_back(Vector3(lul,cs,cs));
- cursor_points.push_back(Vector3(lul,cs,cs));
- cursor_points.push_back(Vector3(lul,cs,-cs));
- cursor_points.push_back(Vector3(lul,cs,-cs));
- cursor_points.push_back(Vector3(lul,-cs,-cs));
-
-
- cursor_points.push_back(Vector3(lll,-cs,-cs));
- cursor_points.push_back(Vector3(lll,-cs,cs));
- cursor_points.push_back(Vector3(lll,-cs,cs));
- cursor_points.push_back(Vector3(lll,cs,cs));
- cursor_points.push_back(Vector3(lll,cs,cs));
- cursor_points.push_back(Vector3(lll,cs,-cs));
- cursor_points.push_back(Vector3(lll,cs,-cs));
- cursor_points.push_back(Vector3(lll,-cs,-cs));
-
-
- } else {
-
- cursor_points.push_back(Vector3(+cs*2,0,0));
- cursor_points.push_back(Vector3(-cs*2,0,0));
-
- }
-
- if (ll<ul) {
-
- const int points = 32;
- float step = (ul-ll)/points;
-
-
- for(int i=0;i<points;i++) {
-
- float s = ll+i*(ul-ll)/points;
- float n = ll+(i+1)*(ul-ll)/points;
-
- Vector3 from=Vector3(0, Math::cos(s), -Math::sin(s) )*cs;
- Vector3 to=Vector3(0,Math::cos(n), -Math::sin(n) )*cs;
-
- if (i==points-1) {
- cursor_points.push_back(to);
- cursor_points.push_back(Vector3());
- }
- if (i==0) {
- cursor_points.push_back(from);
- cursor_points.push_back(Vector3());
- }
-
-
- cursor_points.push_back(from);
- cursor_points.push_back(to);
-
-
- }
-
- cursor_points.push_back(Vector3(0,cs*1.5,0));
- cursor_points.push_back(Vector3());
-
- } else {
-
-
- const int points = 32;
-
- for(int i=0;i<points;i++) {
-
- float s = ll+i*(Math_PI*2.0)/points;
- float n = ll+(i+1)*(Math_PI*2.0)/points;
-
- Vector3 from=Vector3(0,Math::cos(s),-Math::sin(s) )*cs;
- Vector3 to=Vector3( 0,Math::cos(n),-Math::sin(n) )*cs;
-
- cursor_points.push_back(from);
- cursor_points.push_back(to);
-
- }
-
- }
- add_collision_segments(cursor_points);
- add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
-
-}
-
-
-SliderJointSpatialGizmo::SliderJointSpatialGizmo(SliderJoint* p_p3d) {
-
- p3d=p_p3d;
- set_spatial_node(p3d);
-}
-
-///////
-///
-////
-
-void ConeTwistJointSpatialGizmo::redraw() {
-
- clear();
- float cs = 0.25;
- Vector<Vector3> points;
-
- float r = 1.0;
- float w = r*Math::sin(p3d->get_param(ConeTwistJoint::PARAM_SWING_SPAN));
- float d = r*Math::cos(p3d->get_param(ConeTwistJoint::PARAM_SWING_SPAN));
-
-
- //swing
- for(int i=0;i<360;i+=10) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+10);
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w;
-
- /*points.push_back(Vector3(a.x,0,a.y));
- points.push_back(Vector3(b.x,0,b.y));
- points.push_back(Vector3(0,a.x,a.y));
- points.push_back(Vector3(0,b.x,b.y));*/
- points.push_back(Vector3(d,a.x,a.y));
- points.push_back(Vector3(d,b.x,b.y));
-
- if (i%90==0) {
-
- points.push_back(Vector3(d,a.x,a.y));
- points.push_back(Vector3());
-
- }
- }
-
- points.push_back(Vector3());
- points.push_back(Vector3(1,0,0));
-
- //twist
- /*
- */
- float ts=Math::rad2deg(p3d->get_param(ConeTwistJoint::PARAM_TWIST_SPAN));
- ts=MIN(ts,720);
-
-
- for(int i=0;i<int(ts);i+=5) {
-
- float ra=Math::deg2rad(i);
- float rb=Math::deg2rad(i+5);
- float c = i/720.0;
- float cn = (i+5)/720.0;
- Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w*c;
- Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w*cn;
-
- /*points.push_back(Vector3(a.x,0,a.y));
- points.push_back(Vector3(b.x,0,b.y));
- points.push_back(Vector3(0,a.x,a.y));
- points.push_back(Vector3(0,b.x,b.y));*/
-
- points.push_back(Vector3(c,a.x,a.y));
- points.push_back(Vector3(cn,b.x,b.y));
-
- }
-
-
- add_collision_segments(points);
- add_lines(points,SpatialEditorGizmos::singleton->joint_material);
-
-}
-
-
-ConeTwistJointSpatialGizmo::ConeTwistJointSpatialGizmo(ConeTwistJoint* p_p3d) {
-
- p3d=p_p3d;
- set_spatial_node(p3d);
-}
-
-////////
-/// \brief SpatialEditorGizmos::singleton
-///
-///////
-///
-////
-
-void Generic6DOFJointSpatialGizmo::redraw() {
-
- clear();
- Vector<Vector3> cursor_points;
- float cs = 0.25;
-
- for(int ax=0;ax<3;ax++) {
- /*cursor_points.push_back(Vector3(+cs,0,0));
- cursor_points.push_back(Vector3(-cs,0,0));
- cursor_points.push_back(Vector3(0,+cs,0));
- cursor_points.push_back(Vector3(0,-cs,0));
- cursor_points.push_back(Vector3(0,0,+cs*2));
- cursor_points.push_back(Vector3(0,0,-cs*2)); */
-
- float ll;
- float ul;
- float lll;
- float lul;
-
- int a1,a2,a3;
- bool enable_ang;
- bool enable_lin;
-
- switch(ax) {
- case 0:
- ll = p3d->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT);
- ul = p3d->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT);
- lll = -p3d->get_param_x(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT);
- lul = -p3d->get_param_x(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT);
- enable_ang = p3d->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT);
- enable_lin = p3d->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT);
- a1=0;
- a2=1;
- a3=2;
- break;
- case 1:
- ll = p3d->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT);
- ul = p3d->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT);
- lll = -p3d->get_param_y(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT);
- lul = -p3d->get_param_y(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT);
- enable_ang = p3d->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT);
- enable_lin = p3d->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT);
- a1=2;
- a2=0;
- a3=1;
- break;
- case 2:
- ll = p3d->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT);
- ul = p3d->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT);
- lll = -p3d->get_param_z(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT);
- lul = -p3d->get_param_z(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT);
- enable_ang = p3d->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT);
- enable_lin = p3d->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT);
-
- a1=1;
- a2=2;
- a3=0;
- break;
- }
-
-#define ADD_VTX(x,y,z)\
- {\
- Vector3 v;\
- v[a1]=(x);\
- v[a2]=(y);\
- v[a3]=(z);\
- cursor_points.push_back(v);\
- }
-
-#define SET_VTX(what,x,y,z)\
- {\
- Vector3 v;\
- v[a1]=(x);\
- v[a2]=(y);\
- v[a3]=(z);\
- what=v;\
- }
-
-
-
-
- if (enable_lin && lll>=lul) {
-
- ADD_VTX(lul,0,0);
- ADD_VTX(lll,0,0);
-
- ADD_VTX(lul,-cs,-cs);
- ADD_VTX(lul,-cs,cs);
- ADD_VTX(lul,-cs,cs);
- ADD_VTX(lul,cs,cs);
- ADD_VTX(lul,cs,cs);
- ADD_VTX(lul,cs,-cs);
- ADD_VTX(lul,cs,-cs);
- ADD_VTX(lul,-cs,-cs);
-
-
- ADD_VTX(lll,-cs,-cs);
- ADD_VTX(lll,-cs,cs);
- ADD_VTX(lll,-cs,cs);
- ADD_VTX(lll,cs,cs);
- ADD_VTX(lll,cs,cs);
- ADD_VTX(lll,cs,-cs);
- ADD_VTX(lll,cs,-cs);
- ADD_VTX(lll,-cs,-cs);
-
-
- } else {
-
- ADD_VTX(+cs*2,0,0);
- ADD_VTX(-cs*2,0,0);
-
- }
-
- if (enable_ang && ll<=ul) {
-
- const int points = 32;
- float step = (ul-ll)/points;
-
-
- for(int i=0;i<points;i++) {
-
- float s = ll+i*(ul-ll)/points;
- float n = ll+(i+1)*(ul-ll)/points;
-
- Vector3 from;
- SET_VTX(from,0, Math::cos(s), -Math::sin(s) );
- from*=cs;
- Vector3 to;
- SET_VTX(to,0,Math::cos(n), -Math::sin(n));
- to*=cs;
-
- if (i==points-1) {
- cursor_points.push_back(to);
- cursor_points.push_back(Vector3());
- }
- if (i==0) {
- cursor_points.push_back(from);
- cursor_points.push_back(Vector3());
- }
-
-
- cursor_points.push_back(from);
- cursor_points.push_back(to);
-
-
- }
-
- ADD_VTX(0,cs*1.5,0);
- cursor_points.push_back(Vector3());
-
- } else {
-
-
- const int points = 32;
-
- for(int i=0;i<points;i++) {
-
- float s = ll+i*(Math_PI*2.0)/points;
- float n = ll+(i+1)*(Math_PI*2.0)/points;
-
-// Vector3 from=Vector3(0,Math::cos(s),-Math::sin(s) )*cs;
-// Vector3 to=Vector3( 0,Math::cos(n),-Math::sin(n) )*cs;
-
- Vector3 from;
- SET_VTX(from,0, Math::cos(s), -Math::sin(s) );
- from*=cs;
- Vector3 to;
- SET_VTX(to,0,Math::cos(n), -Math::sin(n));
- to*=cs;
-
- cursor_points.push_back(from);
- cursor_points.push_back(to);
-
- }
-
- }
- }
-
-#undef ADD_VTX
-#undef SET_VTX
-
- add_collision_segments(cursor_points);
- add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material);
-
-}
-
-
-Generic6DOFJointSpatialGizmo::Generic6DOFJointSpatialGizmo(Generic6DOFJoint* p_p3d) {
-
- p3d=p_p3d;
- set_spatial_node(p3d);
-}
-
-///////
-///
-////
-
-
-SpatialEditorGizmos *SpatialEditorGizmos::singleton=NULL;
-
-Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
-
- if (p_spatial->cast_to<Light>()) {
-
- Ref<LightSpatialGizmo> lsg = memnew( LightSpatialGizmo(p_spatial->cast_to<Light>()) );
- return lsg;
- }
-
- if (p_spatial->cast_to<Camera>()) {
-
- Ref<CameraSpatialGizmo> lsg = memnew( CameraSpatialGizmo(p_spatial->cast_to<Camera>()) );
- return lsg;
- }
-
- if (p_spatial->cast_to<Skeleton>()) {
-
- Ref<SkeletonSpatialGizmo> lsg = memnew( SkeletonSpatialGizmo(p_spatial->cast_to<Skeleton>()) );
- return lsg;
- }
-
-
- if (p_spatial->cast_to<Position3D>()) {
-
- Ref<Position3DSpatialGizmo> lsg = memnew( Position3DSpatialGizmo(p_spatial->cast_to<Position3D>()) );
- return lsg;
- }
-
- if (p_spatial->cast_to<MeshInstance>()) {
-
- Ref<MeshInstanceSpatialGizmo> misg = memnew( MeshInstanceSpatialGizmo(p_spatial->cast_to<MeshInstance>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<Room>()) {
-
- Ref<RoomSpatialGizmo> misg = memnew( RoomSpatialGizmo(p_spatial->cast_to<Room>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<NavigationMeshInstance>()) {
-
- Ref<NavigationMeshSpatialGizmo> misg = memnew( NavigationMeshSpatialGizmo(p_spatial->cast_to<NavigationMeshInstance>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<RayCast>()) {
-
- Ref<RayCastSpatialGizmo> misg = memnew( RayCastSpatialGizmo(p_spatial->cast_to<RayCast>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<Portal>()) {
-
- Ref<PortalSpatialGizmo> misg = memnew( PortalSpatialGizmo(p_spatial->cast_to<Portal>()) );
- return misg;
- }
-
-
- if (p_spatial->cast_to<TestCube>()) {
-
- Ref<TestCubeSpatialGizmo> misg = memnew( TestCubeSpatialGizmo(p_spatial->cast_to<TestCube>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<SpatialPlayer>()) {
-
- Ref<SpatialPlayerSpatialGizmo> misg = memnew( SpatialPlayerSpatialGizmo(p_spatial->cast_to<SpatialPlayer>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<CollisionShape>()) {
-
- Ref<CollisionShapeSpatialGizmo> misg = memnew( CollisionShapeSpatialGizmo(p_spatial->cast_to<CollisionShape>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<VisibilityNotifier>()) {
-
- Ref<VisibilityNotifierGizmo> misg = memnew( VisibilityNotifierGizmo(p_spatial->cast_to<VisibilityNotifier>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<VehicleWheel>()) {
-
- Ref<VehicleWheelSpatialGizmo> misg = memnew( VehicleWheelSpatialGizmo(p_spatial->cast_to<VehicleWheel>()) );
- return misg;
- }
- if (p_spatial->cast_to<PinJoint>()) {
-
- Ref<PinJointSpatialGizmo> misg = memnew( PinJointSpatialGizmo(p_spatial->cast_to<PinJoint>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<HingeJoint>()) {
-
- Ref<HingeJointSpatialGizmo> misg = memnew( HingeJointSpatialGizmo(p_spatial->cast_to<HingeJoint>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<SliderJoint>()) {
-
- Ref<SliderJointSpatialGizmo> misg = memnew( SliderJointSpatialGizmo(p_spatial->cast_to<SliderJoint>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<ConeTwistJoint>()) {
-
- Ref<ConeTwistJointSpatialGizmo> misg = memnew( ConeTwistJointSpatialGizmo(p_spatial->cast_to<ConeTwistJoint>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<Generic6DOFJoint>()) {
-
- Ref<Generic6DOFJointSpatialGizmo> misg = memnew( Generic6DOFJointSpatialGizmo(p_spatial->cast_to<Generic6DOFJoint>()) );
- return misg;
- }
-
- if (p_spatial->cast_to<CollisionPolygon>()) {
-
- Ref<CollisionPolygonSpatialGizmo> misg = memnew( CollisionPolygonSpatialGizmo(p_spatial->cast_to<CollisionPolygon>()) );
- return misg;
- }
-
-
- return Ref<SpatialEditorGizmo>();
-}
-
-
-Ref<FixedMaterial> SpatialEditorGizmos::create_line_material(const Color& p_base_color) {
-
- Ref<FixedMaterial> line_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
- line_material->set_flag(Material::FLAG_UNSHADED, true);
- line_material->set_line_width(3.0);
- line_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- line_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, true);
- line_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,p_base_color);
-
- return line_material;
-
-}
-
-Ref<FixedMaterial> SpatialEditorGizmos::create_solid_material(const Color& p_base_color) {
-
- Ref<FixedMaterial> line_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
- line_material->set_flag(Material::FLAG_UNSHADED, true);
- line_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- line_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,p_base_color);
-
- return line_material;
-
-}
-
-SpatialEditorGizmos::SpatialEditorGizmos() {
-
- singleton=this;
-
- handle_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
- handle_material->set_flag(Material::FLAG_UNSHADED, true);
- handle_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(0.8,0.8,0.8));
-
- handle2_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
- handle2_material->set_flag(Material::FLAG_UNSHADED, true);
- handle2_material->set_fixed_flag(FixedMaterial::FLAG_USE_POINT_SIZE, true);
- handle_t = SpatialEditor::get_singleton()->get_icon("Editor3DHandle","EditorIcons");
- handle2_material->set_point_size(handle_t->get_width());
- handle2_material->set_texture(FixedMaterial::PARAM_DIFFUSE,handle_t);
- handle2_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1));
- handle2_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- handle2_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, true);
-
- light_material = create_line_material(Color(1,1,0.2));
-
- light_material_omni_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
- light_material_omni_icon->set_flag(Material::FLAG_UNSHADED, true);
- light_material_omni_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
- light_material_omni_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
- light_material_omni_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- light_material_omni_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
- light_material_omni_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoLight","EditorIcons"));
-
-
- light_material_directional_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
- light_material_directional_icon->set_flag(Material::FLAG_UNSHADED, true);
- light_material_directional_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
- light_material_directional_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
- light_material_directional_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- light_material_directional_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
- light_material_directional_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoDirectionalLight","EditorIcons"));
-
- camera_material = create_line_material(Color(1.0,0.5,1.0));
-
-
- navmesh_edge_material = create_line_material(Color(0.1,0.8,1.0));
- navmesh_solid_material = create_solid_material(Color(0.1,0.8,1.0,0.4));
- navmesh_edge_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, false);
- navmesh_solid_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
-
- navmesh_edge_material_disabled = create_line_material(Color(1.0,0.8,0.1));
- navmesh_solid_material_disabled = create_solid_material(Color(1.0,0.8,0.1,0.4));
- navmesh_edge_material_disabled->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, false);
- navmesh_solid_material_disabled->set_flag(Material::FLAG_DOUBLE_SIDED,true);
-
- skeleton_material = create_line_material(Color(0.6,1.0,0.3));
- skeleton_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
- skeleton_material->set_flag(Material::FLAG_UNSHADED,true);
- skeleton_material->set_flag(Material::FLAG_ONTOP,true);
- skeleton_material->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
-
- //position 3D Shared mesh
-
- pos3d_mesh = Ref<Mesh>( memnew( Mesh ) );
- {
-
- DVector<Vector3> cursor_points;
- DVector<Color> cursor_colors;
- float cs = 0.25;
- cursor_points.push_back(Vector3(+cs,0,0));
- cursor_points.push_back(Vector3(-cs,0,0));
- cursor_points.push_back(Vector3(0,+cs,0));
- cursor_points.push_back(Vector3(0,-cs,0));
- cursor_points.push_back(Vector3(0,0,+cs));
- cursor_points.push_back(Vector3(0,0,-cs));
- cursor_colors.push_back(Color(1,0.5,0.5,0.7));
- cursor_colors.push_back(Color(1,0.5,0.5,0.7));
- cursor_colors.push_back(Color(0.5,1,0.5,0.7));
- cursor_colors.push_back(Color(0.5,1,0.5,0.7));
- cursor_colors.push_back(Color(0.5,0.5,1,0.7));
- cursor_colors.push_back(Color(0.5,0.5,1,0.7));
-
- Ref<FixedMaterial> mat = memnew( FixedMaterial );
- mat->set_flag(Material::FLAG_UNSHADED,true);
- mat->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true);
- mat->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true);
- mat->set_line_width(3);
- Array d;
- d.resize(VS::ARRAY_MAX);
- d[Mesh::ARRAY_VERTEX]=cursor_points;
- d[Mesh::ARRAY_COLOR]=cursor_colors;
- pos3d_mesh->add_surface(Mesh::PRIMITIVE_LINES,d);
- pos3d_mesh->surface_set_material(0,mat);
- }
-
-
- sample_player_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
- sample_player_icon->set_flag(Material::FLAG_UNSHADED, true);
- sample_player_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
- sample_player_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
- sample_player_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- sample_player_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
- sample_player_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoSpatialSamplePlayer","EditorIcons"));
-
- room_material = create_line_material(Color(1.0,0.6,0.9));
- portal_material = create_line_material(Color(1.0,0.8,0.6));
- raycast_material = create_line_material(Color(1.0,0.8,0.6));
- car_wheel_material = create_line_material(Color(0.6,0.8,1.0));
- visibility_notifier_material = create_line_material(Color(1.0,0.5,1.0));
- joint_material = create_line_material(Color(0.6,0.8,1.0));
-
- stream_player_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
- stream_player_icon->set_flag(Material::FLAG_UNSHADED, true);
- stream_player_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
- stream_player_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
- stream_player_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- stream_player_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
- stream_player_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoSpatialStreamPlayer","EditorIcons"));
-
- visibility_notifier_icon = Ref<FixedMaterial>( memnew( FixedMaterial ));
- visibility_notifier_icon->set_flag(Material::FLAG_UNSHADED, true);
- visibility_notifier_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true);
- visibility_notifier_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
- visibility_notifier_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
- visibility_notifier_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9));
- visibility_notifier_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("Visible","EditorIcons"));
-
- {
-
- DVector<Vector3> vertices;
-
-#undef ADD_VTX
-#define ADD_VTX(m_idx);\
- vertices.push_back( face_points[m_idx] );
-
- for (int i=0;i<6;i++) {
-
-
- Vector3 face_points[4];
-
- for (int j=0;j<4;j++) {
-
- float v[3];
- v[0]=1.0;
- v[1]=1-2*((j>>1)&1);
- v[2]=v[1]*(1-2*(j&1));
-
- for (int k=0;k<3;k++) {
-
- if (i<3)
- face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
- else
- face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
- }
- }
- //tri 1
- ADD_VTX(0);
- ADD_VTX(1);
- ADD_VTX(2);
- //tri 2
- ADD_VTX(2);
- ADD_VTX(3);
- ADD_VTX(0);
-
- }
-
- test_cube_tm = Ref<TriangleMesh>( memnew( TriangleMesh ) );
- test_cube_tm->create(vertices);
- }
-
- shape_material = create_line_material(Color(0.2,1,1.0));
-
-
-}
-
+/*************************************************************************/ +/* spatial_editor_gizmos.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "spatial_editor_gizmos.h" +#include "geometry.h" +#include "scene/3d/camera.h" +#include "scene/resources/surface_tool.h" +#include "scene/resources/sphere_shape.h" +#include "scene/resources/box_shape.h" +#include "scene/resources/capsule_shape.h" +#include "scene/resources/ray_shape.h" +#include "scene/resources/convex_polygon_shape.h" +#include "scene/resources/plane_shape.h" +#include "quick_hull.h" + +// Keep small children away from this file. +// It's so ugly it will eat them alive + +#define HANDLE_HALF_SIZE 0.05 + +void SpatialGizmoTool::clear() { + + for(int i=0;i<instances.size();i++) { + + if (instances[i].instance.is_valid()) + VS::get_singleton()->free(instances[i].instance); + + + } + + billboard_handle=false; + collision_segments.clear(); + collision_mesh=Ref<TriangleMesh>(); + instances.clear(); + handles.clear(); + secondary_handles.clear(); +} + +void SpatialGizmoTool::Instance::create_instance(Spatial *p_base) { + + instance = VS::get_singleton()->instance_create2(mesh->get_rid(),p_base->get_world()->get_scenario()); + VS::get_singleton()->instance_attach_object_instance_ID(instance,p_base->get_instance_ID()); + if (billboard) + VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_BILLBOARD,true); + if (unscaled) + VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_DEPH_SCALE,true); + if (skeleton.is_valid()) + VS::get_singleton()->instance_attach_skeleton(instance,skeleton); + if (extra_margin) + VS::get_singleton()->instance_set_extra_visibility_margin(instance,1); + VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_CAST_SHADOW,false); + VS::get_singleton()->instance_geometry_set_flag(instance,VS::INSTANCE_FLAG_RECEIVE_SHADOWS,false); + VS::get_singleton()->instance_set_layer_mask(instance,1<<SpatialEditorViewport::GIZMO_EDIT_LAYER); //gizmos are 26 +} + + + +void SpatialGizmoTool::add_mesh(const Ref<Mesh>& p_mesh,bool p_billboard, const RID &p_skeleton) { + + ERR_FAIL_COND(!spatial_node); + Instance ins; + + ins.billboard=p_billboard; + ins.mesh=p_mesh; + ins.skeleton=p_skeleton; + if (valid) { + ins.create_instance(spatial_node); + VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform()); + } + + instances.push_back(ins); + +} + +void SpatialGizmoTool::add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material,bool p_billboard){ + + ERR_FAIL_COND(!spatial_node); + Instance ins; + + Ref<Mesh> mesh = memnew( Mesh ); + Array a; + a.resize(Mesh::ARRAY_MAX); + + a[Mesh::ARRAY_VERTEX]=p_lines; + + DVector<Color> color; + color.resize(p_lines.size()); + { + DVector<Color>::Write w = color.write(); + for(int i=0;i<p_lines.size();i++) { + if (is_selected()) + w[i]=Color(1,1,1,0.6); + else + w[i]=Color(1,1,1,0.25); + } + + } + + a[Mesh::ARRAY_COLOR]=color; + + + mesh->add_surface(Mesh::PRIMITIVE_LINES,a); + mesh->surface_set_material(0,p_material); + + if (p_billboard) { + float md=0; + for(int i=0;i<p_lines.size();i++) { + + md=MAX(0,p_lines[i].length()); + + } + if (md) { + mesh->set_custom_aabb(AABB(Vector3(-md,-md,-md),Vector3(md,md,md)*2.0)); + } + } + + ins.billboard=p_billboard; + ins.mesh=mesh; + if (valid) { + ins.create_instance(spatial_node); + VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform()); + } + + instances.push_back(ins); + +} + +void SpatialGizmoTool::add_unscaled_billboard(const Ref<Material>& p_material,float p_scale) { + + ERR_FAIL_COND(!spatial_node); + Instance ins; + + Vector<Vector3 > vs; + Vector<Vector2 > uv; + + vs.push_back(Vector3(-p_scale,p_scale,0)); + vs.push_back(Vector3(p_scale,p_scale,0)); + vs.push_back(Vector3(p_scale,-p_scale,0)); + vs.push_back(Vector3(-p_scale,-p_scale,0)); + + uv.push_back(Vector2(1,0)); + uv.push_back(Vector2(0,0)); + uv.push_back(Vector2(0,1)); + uv.push_back(Vector2(1,1)); + + Ref<Mesh> mesh = memnew( Mesh ); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX]=vs; + a[Mesh::ARRAY_TEX_UV]=uv; + mesh->add_surface(Mesh::PRIMITIVE_TRIANGLE_FAN,a); + mesh->surface_set_material(0,p_material); + + if (true) { + float md=0; + for(int i=0;i<vs.size();i++) { + + md=MAX(0,vs[i].length()); + + } + if (md) { + mesh->set_custom_aabb(AABB(Vector3(-md,-md,-md),Vector3(md,md,md)*2.0)); + } + } + + ins.mesh=mesh; + ins.unscaled=true; + ins.billboard=true; + if (valid) { + ins.create_instance(spatial_node); + VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform()); + } + + instances.push_back(ins); + + +} + +void SpatialGizmoTool::add_collision_triangles(const Ref<TriangleMesh>& p_tmesh) { + + collision_mesh=p_tmesh; +} + +void SpatialGizmoTool::add_collision_segments(const Vector<Vector3> &p_lines) { + + int from=collision_segments.size(); + collision_segments.resize(from+p_lines.size()); + for(int i=0;i<p_lines.size();i++) { + + collision_segments[from+i]=p_lines[i]; + } +} + + +void SpatialGizmoTool::add_handles(const Vector<Vector3> &p_handles, bool p_billboard,bool p_secondary){ + + billboard_handle=p_billboard; + + if (!is_selected()) + return; + + ERR_FAIL_COND(!spatial_node); + + ERR_FAIL_COND(!spatial_node); + Instance ins; + + + Ref<Mesh> mesh = memnew( Mesh ); +#if 1 + + Array a; + a.resize(VS::ARRAY_MAX); + a[VS::ARRAY_VERTEX]=p_handles; + DVector<Color> colors; + { + colors.resize(p_handles.size()); + DVector<Color>::Write w=colors.write(); + for(int i=0;i<p_handles.size();i++) { + + Color col(1,1,1,1); + if (SpatialEditor::get_singleton()->get_over_gizmo_handle()!=i) + col=Color(0.9,0.9,0.9,0.9); + w[i]=col; + } + + } + a[VS::ARRAY_COLOR]=colors; + mesh->add_surface(Mesh::PRIMITIVE_POINTS,a); + mesh->surface_set_material(0,SpatialEditorGizmos::singleton->handle2_material); + + if (p_billboard) { + float md=0; + for(int i=0;i<p_handles.size();i++) { + + md=MAX(0,p_handles[i].length()); + + } + if (md) { + mesh->set_custom_aabb(AABB(Vector3(-md,-md,-md),Vector3(md,md,md)*2.0)); + } + } + + + +#else + for(int ih=0;ih<p_handles.size();ih++) { + + + Vector<Vector3> vertices; + Vector<Vector3> normals; + + int vtx_idx=0; +#define ADD_VTX(m_idx);\ + vertices.push_back( (face_points[m_idx]*HANDLE_HALF_SIZE+p_handles[ih]) );\ + normals.push_back( normal_points[m_idx] );\ + vtx_idx++;\ + + for (int i=0;i<6;i++) { + + + Vector3 face_points[4]; + Vector3 normal_points[4]; + float uv_points[8]={0,0,0,1,1,1,1,0}; + + for (int j=0;j<4;j++) { + + float v[3]; + v[0]=1.0; + v[1]=1-2*((j>>1)&1); + v[2]=v[1]*(1-2*(j&1)); + + for (int k=0;k<3;k++) { + + if (i<3) + face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1); + else + face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1); + } + normal_points[j]=Vector3(); + normal_points[j][i%3]=(i>=3?-1:1); + } + //tri 1 + ADD_VTX(0); + ADD_VTX(1); + ADD_VTX(2); + //tri 2 + ADD_VTX(2); + ADD_VTX(3); + ADD_VTX(0); + + } + + + Array d; + d.resize(VS::ARRAY_MAX); + d[VisualServer::ARRAY_NORMAL]= normals ; + d[VisualServer::ARRAY_VERTEX]= vertices ; + + mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,d); + mesh->surface_set_material(ih,SpatialEditorGizmos::singleton->handle_material); + + + } +#endif + ins.mesh=mesh; + ins.billboard=p_billboard; + ins.extra_margin=true; + if (valid) { + ins.create_instance(spatial_node); + VS::get_singleton()->instance_set_transform(ins.instance,spatial_node->get_global_transform()); + } + instances.push_back(ins); + if (!p_secondary) { + int chs=handles.size(); + handles.resize(chs+p_handles.size()); + for(int i=0;i<p_handles.size();i++) { + handles[i+chs]=p_handles[i]; + } + } else { + + int chs=secondary_handles.size(); + secondary_handles.resize(chs+p_handles.size()); + for(int i=0;i<p_handles.size();i++) { + secondary_handles[i+chs]=p_handles[i]; + } + + } + +} + + +void SpatialGizmoTool::set_spatial_node(Spatial *p_node){ + + spatial_node=p_node; + +} + +bool SpatialGizmoTool::intersect_frustum(const Camera *p_camera,const Vector<Plane> &p_frustum) { + + ERR_FAIL_COND_V(!spatial_node,false); + ERR_FAIL_COND_V(!valid,false); + + if (collision_segments.size()) { + + const Plane *p=p_frustum.ptr(); + int fc=p_frustum.size(); + + int vc=collision_segments.size(); + const Vector3* vptr=collision_segments.ptr(); + Transform t = spatial_node->get_global_transform(); + + for(int i=0;i<vc/2;i++) { + + + Vector3 a=t.xform(vptr[i*2+0]); + Vector3 b=t.xform(vptr[i*2+1]); + + bool any_out=false; + for(int j=0;j<fc;j++) { + + if (p[j].distance_to(a) > 0 && p[j].distance_to(b) >0) { + + any_out=true; + break; + } + } + + if (!any_out) + return true; + } + + return false; + } + + return false; +} + + +bool SpatialGizmoTool::intersect_ray(const Camera *p_camera,const Point2& p_point, Vector3& r_pos, Vector3& r_normal,int *r_gizmo_handle,bool p_sec_first) { + + ERR_FAIL_COND_V(!spatial_node,false); + ERR_FAIL_COND_V(!valid,false); + + if (r_gizmo_handle) { + + Transform t = spatial_node->get_global_transform(); + t.orthonormalize(); + if (billboard_handle) { + t.set_look_at(t.origin,t.origin+p_camera->get_transform().basis.get_axis(2),p_camera->get_transform().basis.get_axis(1)); + } + Transform ti=t.affine_inverse(); + + Vector3 ray_from=ti.xform(p_camera->project_ray_origin(p_point)); + Vector3 ray_dir=t.basis.xform_inv(p_camera->project_ray_normal(p_point)).normalized(); + Vector3 ray_to = ray_from+ray_dir*4096; + + float min_d=1e20; + int idx=-1; + + for(int i=0;i<secondary_handles.size();i++) { +#if 1 + + + Vector3 hpos = t.xform(secondary_handles[i]); + Vector2 p = p_camera->unproject_position(hpos); + if (p.distance_to(p_point)<SpatialEditorGizmos::singleton->handle_t->get_width()*0.6) { + + + real_t dp = p_camera->get_transform().origin.distance_to(hpos); + if (dp<min_d) { + + r_pos=t.xform(hpos); + r_normal=p_camera->get_transform().basis.get_axis(2); + min_d=dp; + idx=i+handles.size(); + + } + + } + +#else + AABB aabb; + aabb.pos=Vector3(-1,-1,-1)*HANDLE_HALF_SIZE; + aabb.size=aabb.pos*-2; + aabb.pos+=secondary_handles[i]; + + + Vector3 rpos,rnorm; + + if (aabb.intersects_segment(ray_from,ray_to,&rpos,&rnorm)) { + + real_t dp = ray_dir.dot(rpos); + if (dp<min_d) { + + r_pos=t.xform(rpos); + r_normal=ti.basis.xform_inv(rnorm).normalized(); + min_d=dp; + idx=i+handles.size(); + + } + } +#endif + } + + if (p_sec_first && idx!=-1) { + + *r_gizmo_handle=idx; + return true; + } + + min_d=1e20; + + for(int i=0;i<handles.size();i++) { + +#if 1 + + + Vector3 hpos = t.xform(handles[i]); + Vector2 p = p_camera->unproject_position(hpos); + if (p.distance_to(p_point)<SpatialEditorGizmos::singleton->handle_t->get_width()*0.6) { + + + real_t dp = p_camera->get_transform().origin.distance_to(hpos); + if (dp<min_d) { + + r_pos=t.xform(hpos); + r_normal=p_camera->get_transform().basis.get_axis(2); + min_d=dp; + idx=i; + + } + + } + +#else + + AABB aabb; + aabb.pos=Vector3(-1,-1,-1)*HANDLE_HALF_SIZE; + aabb.size=aabb.pos*-2; + aabb.pos+=handles[i]; + + + Vector3 rpos,rnorm; + + if (aabb.intersects_segment(ray_from,ray_to,&rpos,&rnorm)) { + + real_t dp = ray_dir.dot(rpos); + if (dp<min_d) { + + r_pos=t.xform(rpos); + r_normal=ti.basis.xform_inv(rnorm).normalized(); + min_d=dp; + idx=i; + + } + } +#endif + } + + if (idx>=0) { + *r_gizmo_handle=idx; + return true; + } + + + } + + if (collision_segments.size()) { + + Plane camp(p_camera->get_transform().origin,(-p_camera->get_transform().basis.get_axis(2)).normalized()); + + int vc=collision_segments.size(); + const Vector3* vptr=collision_segments.ptr(); + Transform t = spatial_node->get_global_transform(); + if (billboard_handle) { + t.set_look_at(t.origin,t.origin+p_camera->get_transform().basis.get_axis(2),p_camera->get_transform().basis.get_axis(1)); + } + + Vector3 cp; + float cpd=1e20; + + for(int i=0;i<vc/2;i++) { + + + Vector3 a=t.xform(vptr[i*2+0]); + Vector3 b=t.xform(vptr[i*2+1]); + Vector2 s[2]; + s[0] = p_camera->unproject_position(a); + s[1] = p_camera->unproject_position(b); + + + Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point,s); + + float pd = p.distance_to(p_point); + + if (pd<cpd) { + + + float d = s[0].distance_to(s[1]); + Vector3 tcp; + if (d>0) { + + float d2=s[0].distance_to(p)/d; + tcp = a+(b-a)*d2; + + } else { + tcp=a; + + } + + if (camp.distance_to(tcp)<p_camera->get_znear()) + continue; + cp=tcp; + cpd=pd; + } + } + + if (cpd<8) { + + r_pos=cp; + r_normal=-p_camera->project_ray_normal(p_point); + return true; + } + + return false; + } + + + if (collision_mesh.is_valid()) { + Transform gt = spatial_node->get_global_transform(); + + if (billboard_handle) { + gt.set_look_at(gt.origin,gt.origin+p_camera->get_transform().basis.get_axis(2),p_camera->get_transform().basis.get_axis(1)); + } + + Transform ai=gt.affine_inverse(); + Vector3 ray_from = ai.xform(p_camera->project_ray_origin(p_point)); + Vector3 ray_dir=ai.basis.xform(p_camera->project_ray_normal(p_point)).normalized(); + Vector3 rpos,rnorm; + +#if 1 + + + + if (collision_mesh->intersect_ray(ray_from,ray_dir,rpos,rnorm)) { + + r_pos=gt.xform(rpos); + r_normal=gt.basis.xform(rnorm).normalized(); + return true; + } +#else + + if (collision_mesh->intersect_segment(ray_from,ray_from+ray_dir*4906.0,rpos,rnorm)) { + + r_pos=gt.xform(rpos); + r_normal=gt.basis.xform(rnorm).normalized(); + return true; + } + +#endif + } + + return false; + +} + + + +void SpatialGizmoTool::create() { + + ERR_FAIL_COND(!spatial_node); + ERR_FAIL_COND(valid); + valid=true; + + for(int i=0;i<instances.size();i++) { + + instances[i].create_instance(spatial_node); + } + + transform(); + +} + +void SpatialGizmoTool::transform(){ + + ERR_FAIL_COND(!spatial_node); + ERR_FAIL_COND(!valid); + for(int i=0;i<instances.size();i++) { + VS::get_singleton()->instance_set_transform(instances[i].instance,spatial_node->get_global_transform()); + } + +} + + +void SpatialGizmoTool::free(){ + + ERR_FAIL_COND(!spatial_node); + ERR_FAIL_COND(!valid); + + for(int i=0;i<instances.size();i++) { + + if (instances[i].instance.is_valid()) + VS::get_singleton()->free(instances[i].instance); + instances[i].instance=RID(); + } + + valid=false; + + +} + + + +SpatialGizmoTool::SpatialGizmoTool() { + valid=false; + billboard_handle=false; + +} + +SpatialGizmoTool::~SpatialGizmoTool(){ + + clear(); +} + +Vector3 SpatialGizmoTool::get_handle_pos(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,handles.size(),Vector3()); + + return handles[p_idx]; + +} + +//// light gizmo + + +String LightSpatialGizmo::get_handle_name(int p_idx) const { + + if (p_idx==0) + return "Radius"; + else + return "Aperture"; +} + + +Variant LightSpatialGizmo::get_handle_value(int p_idx) const{ + + if (p_idx==0) + return light->get_parameter(Light::PARAM_RADIUS); + if (p_idx==1) + return light->get_parameter(Light::PARAM_SPOT_ANGLE); + + return Variant(); +} + + +static float _find_closest_angle_to_half_pi_arc(const Vector3& p_from, const Vector3& p_to, float p_arc_radius,const Transform& p_arc_xform) { + + //bleh, discrete is simpler + static const int arc_test_points=64; + float min_d = 1e20; + Vector3 min_p; + + + for(int i=0;i<arc_test_points;i++) { + + float a = i*Math_PI*0.5/arc_test_points; + float an = (i+1)*Math_PI*0.5/arc_test_points; + Vector3 p=Vector3( Math::cos(a), 0, -Math::sin(a) )*p_arc_radius; + Vector3 n=Vector3( Math::cos(an), 0,- Math::sin(an) )*p_arc_radius; + + Vector3 ra,rb; + Geometry::get_closest_points_between_segments(p,n,p_from,p_to,ra,rb); + + float d = ra.distance_to(rb); + if (d<min_d) { + min_d=d; + min_p=ra; + } + + } + + //min_p = p_arc_xform.affine_inverse().xform(min_p); + float a = Vector2(min_p.x,-min_p.z).angle(); + return a*180.0/Math_PI; +} + + +void LightSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point) { + + Transform gt = light->get_global_transform(); + gt.orthonormalize(); + Transform gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 s[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)}; + if (p_idx==0) { + + if (light->cast_to<SpotLight>()) { + Vector3 ra,rb; + Geometry::get_closest_points_between_segments(Vector3(),Vector3(0,0,-4096),s[0],s[1],ra,rb); + + float d = -ra.z; + if (d<0) + d=0; + + light->set_parameter(Light::PARAM_RADIUS,d); + } else if (light->cast_to<OmniLight>()) { + + Plane cp=Plane( gt.origin, p_camera->get_transform().basis.get_axis(2)); + + Vector3 inters; + if (cp.intersects_ray(ray_from,ray_dir,&inters)) { + + float r = inters.distance_to(gt.origin); + light->set_parameter(Light::PARAM_RADIUS,r); + } + + } + + } else if (p_idx==1) { + + float a = _find_closest_angle_to_half_pi_arc(s[0],s[1],light->get_parameter(Light::PARAM_RADIUS),gt); + light->set_parameter(Light::PARAM_SPOT_ANGLE,CLAMP(a,0.01,89.99)); + } +} + +void LightSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){ + + if (p_cancel) { + + light->set_parameter(p_idx==0?Light::PARAM_RADIUS:Light::PARAM_SPOT_ANGLE,p_restore); + + } else if (p_idx==0) { + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action("Change Light Radius"); + ur->add_do_method(light,"set_parameter",Light::PARAM_RADIUS,light->get_parameter(Light::PARAM_RADIUS)); + ur->add_undo_method(light,"set_parameter",Light::PARAM_RADIUS,p_restore); + ur->commit_action(); + } else if (p_idx==1) { + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action("Change Light Radius"); + ur->add_do_method(light,"set_parameter",Light::PARAM_SPOT_ANGLE,light->get_parameter(Light::PARAM_SPOT_ANGLE)); + ur->add_undo_method(light,"set_parameter",Light::PARAM_SPOT_ANGLE,p_restore); + ur->commit_action(); + + } +} + + + +void LightSpatialGizmo::redraw() { + + + if (light->cast_to<DirectionalLight>()) { + + + + const int arrow_points=5; + Vector3 arrow[arrow_points]={ + Vector3(0,0,2), + Vector3(1,1,2), + Vector3(1,1,-1), + Vector3(2,2,-1), + Vector3(0,0,-3) + }; + + int arrow_sides=4; + + Vector<Vector3> lines; + + + for(int i = 0; i < arrow_sides ; i++) { + + + Matrix3 ma(Vector3(0,0,1),Math_PI*2*float(i)/arrow_sides); + Matrix3 mb(Vector3(0,0,1),Math_PI*2*float(i+1)/arrow_sides); + + + for(int j=1;j<arrow_points-1;j++) { + + if (j!=2) { + lines.push_back(ma.xform(arrow[j])); + lines.push_back(ma.xform(arrow[j+1])); + } + if (j<arrow_points-1) { + lines.push_back(ma.xform(arrow[j])); + lines.push_back(mb.xform(arrow[j])); + } + + } + } + + add_lines(lines,SpatialEditorGizmos::singleton->light_material); + add_collision_segments(lines); + add_unscaled_billboard(SpatialEditorGizmos::singleton->light_material_directional_icon,0.05); + + } + + if (light->cast_to<OmniLight>()) { + + clear(); + + + OmniLight *on = light->cast_to<OmniLight>(); + + float r = on->get_parameter(Light::PARAM_RADIUS); + + Vector<Vector3> points; + + for(int i=0;i<=360;i++) { + + float ra=Math::deg2rad(i); + float rb=Math::deg2rad(i+1); + Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r; + Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r; + + /*points.push_back(Vector3(a.x,0,a.y)); + points.push_back(Vector3(b.x,0,b.y)); + points.push_back(Vector3(0,a.x,a.y)); + points.push_back(Vector3(0,b.x,b.y));*/ + points.push_back(Vector3(a.x,a.y,0)); + points.push_back(Vector3(b.x,b.y,0)); + + } + + add_lines(points,SpatialEditorGizmos::singleton->light_material,true); + add_collision_segments(points); + + add_unscaled_billboard(SpatialEditorGizmos::singleton->light_material_omni_icon,0.05); + + Vector<Vector3> handles; + handles.push_back(Vector3(r,0,0)); + add_handles(handles,true); + + + } + + + if (light->cast_to<SpotLight>()) { + + clear(); + + Vector<Vector3> points; + SpotLight *on = light->cast_to<SpotLight>(); + + float r = on->get_parameter(Light::PARAM_RADIUS); + float w = r*Math::sin(Math::deg2rad(on->get_parameter(Light::PARAM_SPOT_ANGLE))); + float d = r*Math::cos(Math::deg2rad(on->get_parameter(Light::PARAM_SPOT_ANGLE))); + + + + for(int i=0;i<360;i++) { + + float ra=Math::deg2rad(i); + float rb=Math::deg2rad(i+1); + Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w; + Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w; + + /*points.push_back(Vector3(a.x,0,a.y)); + points.push_back(Vector3(b.x,0,b.y)); + points.push_back(Vector3(0,a.x,a.y)); + points.push_back(Vector3(0,b.x,b.y));*/ + points.push_back(Vector3(a.x,a.y,-d)); + points.push_back(Vector3(b.x,b.y,-d)); + + if (i%90==0) { + + points.push_back(Vector3(a.x,a.y,-d)); + points.push_back(Vector3()); + + } + + + } + + points.push_back(Vector3(0,0,-r)); + points.push_back(Vector3()); + + add_lines(points,SpatialEditorGizmos::singleton->light_material); + + Vector<Vector3> handles; + handles.push_back(Vector3(0,0,-r)); + + Vector<Vector3> collision_segments; + + for(int i=0;i<64;i++) { + + float ra=i*Math_PI*2.0/64.0; + float rb=(i+1)*Math_PI*2.0/64.0; + Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w; + Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w; + + collision_segments.push_back(Vector3(a.x,a.y,-d)); + collision_segments.push_back(Vector3(b.x,b.y,-d)); + + if (i%16==0) { + + collision_segments.push_back(Vector3(a.x,a.y,-d)); + collision_segments.push_back(Vector3()); + + } + + if (i==16) { + + handles.push_back(Vector3(a.x,a.y,-d)); + } + + } + + collision_segments.push_back(Vector3(0,0,-r)); + collision_segments.push_back(Vector3()); + + + add_handles(handles); + add_collision_segments(collision_segments); + add_unscaled_billboard(SpatialEditorGizmos::singleton->light_material_omni_icon,0.05); + + } + +} + +LightSpatialGizmo::LightSpatialGizmo(Light* p_light){ + + light=p_light; + set_spatial_node(p_light); + +} + +////// + +String CameraSpatialGizmo::get_handle_name(int p_idx) const { + + if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) { + return "FOV"; + } else { + return "Size"; + } +} +Variant CameraSpatialGizmo::get_handle_value(int p_idx) const{ + + if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) { + return camera->get_fov(); + } else { + + return camera->get_size(); + } +} +void CameraSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){ + + Transform gt = camera->get_global_transform(); + gt.orthonormalize(); + Transform gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 s[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)}; + + if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) { + Transform gt=camera->get_global_transform(); + float a = _find_closest_angle_to_half_pi_arc(s[0],s[1],1.0,gt); + camera->set("fov",a); + } else { + + Vector3 ra,rb; + Geometry::get_closest_points_between_segments(Vector3(0,0,-1),Vector3(4096,0,-1),s[0],s[1],ra,rb); + float d = ra.x * 2.0; + if (d<0) + d=0; + + camera->set("size",d); + } + +} +void CameraSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){ + + if (camera->get_projection()==Camera::PROJECTION_PERSPECTIVE) { + + if (p_cancel) { + + camera->set("fov",p_restore); + } else { + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action("Change Camera FOV"); + ur->add_do_property(camera,"fov",camera->get_fov()); + ur->add_undo_property(camera,"fov",p_restore); + ur->commit_action(); + } + + } else { + + if (p_cancel) { + + camera->set("size",p_restore); + } else { + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action("Change Camera Size"); + ur->add_do_property(camera,"size",camera->get_size()); + ur->add_undo_property(camera,"size",p_restore); + ur->commit_action(); + } + + } + +} + +void CameraSpatialGizmo::redraw(){ + + clear(); + + Vector<Vector3> lines; + Vector<Vector3> handles; + + + switch(camera->get_projection()) { + + case Camera::PROJECTION_PERSPECTIVE: { + + float fov = camera->get_fov(); + + Vector3 side=Vector3( Math::sin(Math::deg2rad(fov)), 0, -Math::cos(Math::deg2rad(fov)) ); + Vector3 nside=side; + nside.x=-nside.x; + Vector3 up=Vector3(0,side.x,0); + + +#define ADD_TRIANGLE( m_a, m_b, m_c)\ +{\ + lines.push_back(m_a);\ + lines.push_back(m_b);\ + lines.push_back(m_b);\ + lines.push_back(m_c);\ + lines.push_back(m_c);\ + lines.push_back(m_a);\ +} + + ADD_TRIANGLE( Vector3(), side+up, side-up ); + ADD_TRIANGLE( Vector3(), nside+up, nside-up ); + ADD_TRIANGLE( Vector3(), side+up, nside+up ); + ADD_TRIANGLE( Vector3(), side-up, nside-up ); + + handles.push_back(side); + side.x*=0.25; + nside.x*=0.25; + Vector3 tup( 0, up.y*3/2,side.z); + ADD_TRIANGLE( tup, side+up, nside+up ); + + } break; + case Camera::PROJECTION_ORTHOGONAL: { + +#define ADD_QUAD( m_a, m_b, m_c, m_d)\ +{\ + lines.push_back(m_a);\ + lines.push_back(m_b);\ + lines.push_back(m_b);\ + lines.push_back(m_c);\ + lines.push_back(m_c);\ + lines.push_back(m_d);\ + lines.push_back(m_d);\ + lines.push_back(m_a);\ +} + float size = camera->get_size(); + + float hsize=size*0.5; + Vector3 right(hsize,0,0); + Vector3 up(0,hsize,0); + Vector3 back(0,0,-1.0); + Vector3 front(0,0,0); + + ADD_QUAD( -up-right,-up+right,up+right,up-right); + ADD_QUAD( -up-right+back,-up+right+back,up+right+back,up-right+back); + ADD_QUAD( up+right,up+right+back,up-right+back,up-right); + ADD_QUAD( -up+right,-up+right+back,-up-right+back,-up-right); + handles.push_back(right+back); + + right.x*=0.25; + Vector3 tup( 0, up.y*3/2,back.z ); + ADD_TRIANGLE( tup, right+up+back, -right+up+back ); + + } break; + + } + + add_lines(lines,SpatialEditorGizmos::singleton->camera_material); + add_collision_segments(lines); + add_handles(handles); +} + + +CameraSpatialGizmo::CameraSpatialGizmo(Camera* p_camera){ + + camera=p_camera; + set_spatial_node(camera); +} + + + + +////// + +void MeshInstanceSpatialGizmo::redraw() { + + Ref<Mesh> m = mesh->get_mesh(); + if (!m.is_valid()) + return; //none + + Ref<TriangleMesh> tm = m->generate_triangle_mesh(); + if (tm.is_valid()) + add_collision_triangles(tm); +} + +MeshInstanceSpatialGizmo::MeshInstanceSpatialGizmo(MeshInstance* p_mesh) { + + mesh=p_mesh; + set_spatial_node(p_mesh); +} + +///// + + +void Position3DSpatialGizmo::redraw() { + + clear(); + add_mesh(SpatialEditorGizmos::singleton->pos3d_mesh); + Vector<Vector3> cursor_points; + float cs = 0.25; + cursor_points.push_back(Vector3(+cs,0,0)); + cursor_points.push_back(Vector3(-cs,0,0)); + cursor_points.push_back(Vector3(0,+cs,0)); + cursor_points.push_back(Vector3(0,-cs,0)); + cursor_points.push_back(Vector3(0,0,+cs)); + cursor_points.push_back(Vector3(0,0,-cs)); + add_collision_segments(cursor_points); + +} + + +Position3DSpatialGizmo::Position3DSpatialGizmo(Position3D* p_p3d) { + + p3d=p_p3d; + set_spatial_node(p3d); +} + + +///// + +void SkeletonSpatialGizmo::redraw() { + + clear(); + + Ref<SurfaceTool> surface_tool( memnew( SurfaceTool )); + + + surface_tool->begin(Mesh::PRIMITIVE_LINES); + surface_tool->set_material(SpatialEditorGizmos::singleton->skeleton_material); + Vector<Transform> grests; + grests.resize(skel->get_bone_count()); + + Vector<int> bones; + Vector<float> weights; + bones.resize(4); + weights.resize(4); + + for(int i=0;i<4;i++) { + bones[i]=0; + weights[i]=0; + } + + weights[0]=1; + + + AABB aabb; + + Color bonecolor = Color(1.0,0.4,0.4,0.3); + Color rootcolor = Color(0.4,1.0,0.4,0.1); + + for (int i=0;i<skel->get_bone_count();i++) { + + int parent = skel->get_bone_parent(i); + + if (parent>=0) { + grests[i]=grests[parent] * skel->get_bone_rest(i); + + Vector3 v0 = grests[parent].origin; + Vector3 v1 = grests[i].origin; + Vector3 d = (v1-v0).normalized(); + float dist = v0.distance_to(v1); + + //find closest axis + int closest=-1; + float closest_d = 0.0; + + for(int j=0;j<3;j++) { + float dp = Math::abs(grests[parent].basis[j].normalized().dot(d)); + if (j==0 || dp>closest_d) + closest=j; + } + + //find closest other + Vector3 first; + Vector3 points[4]; + int pointidx=0; + for(int j=0;j<3;j++) { + + bones[0]=parent; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(rootcolor); + surface_tool->add_vertex(v0-grests[parent].basis[j].normalized()*dist*0.05); + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(rootcolor); + surface_tool->add_vertex(v0+grests[parent].basis[j].normalized()*dist*0.05); + + if (j==closest) + continue; + + Vector3 axis; + if (first==Vector3()) { + axis = d.cross(d.cross(grests[parent].basis[j])).normalized(); + first=axis; + } else { + axis = d.cross(first).normalized(); + } + + for(int k=0;k<2;k++) { + + if (k==1) + axis=-axis; + Vector3 point = v0+d*dist*0.2; + point+=axis*dist*0.1; + + + bones[0]=parent; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(bonecolor); + surface_tool->add_vertex(v0); + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(bonecolor); + surface_tool->add_vertex(point); + + bones[0]=parent; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(bonecolor); + surface_tool->add_vertex(point); + bones[0]=i; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(bonecolor); + surface_tool->add_vertex(v1); + points[pointidx++]=point; + + } + + } + + SWAP( points[1],points[2] ); + for(int j=0;j<4;j++) { + + + bones[0]=parent; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(bonecolor); + surface_tool->add_vertex(points[j]); + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(bonecolor); + surface_tool->add_vertex(points[(j+1)%4]); + } + + +/* + bones[0]=parent; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(Color(0.4,1,0.4,0.4)); + surface_tool->add_vertex(v0); + bones[0]=i; + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(Color(0.4,1,0.4,0.4)); + surface_tool->add_vertex(v1); +*/ + } else { + + grests[i]=skel->get_bone_rest(i); + bones[0]=i; + } +/* + Transform t = grests[i]; + t.orthonormalize(); + + for (int i=0;i<6;i++) { + + + Vector3 face_points[4]; + + for (int j=0;j<4;j++) { + + float v[3]; + v[0]=1.0; + v[1]=1-2*((j>>1)&1); + v[2]=v[1]*(1-2*(j&1)); + + for (int k=0;k<3;k++) { + + if (i<3) + face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1); + else + face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1); + } + } + + for(int j=0;j<4;j++) { + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(Color(1.0,0.4,0.4,0.4)); + surface_tool->add_vertex(t.xform(face_points[j]*0.04)); + surface_tool->add_bones(bones); + surface_tool->add_weights(weights); + surface_tool->add_color(Color(1.0,0.4,0.4,0.4)); + surface_tool->add_vertex(t.xform(face_points[(j+1)%4]*0.04)); + } + + } + */ + } + + Ref<Mesh> m = surface_tool->commit(); + add_mesh(m,false,skel->get_skeleton()); + +} + +SkeletonSpatialGizmo::SkeletonSpatialGizmo(Skeleton* p_skel) { + + skel=p_skel; + set_spatial_node(p_skel); +} + +///// + + +void SpatialPlayerSpatialGizmo::redraw() { + + clear(); + if (splayer->cast_to<SpatialStreamPlayer>()) { + + add_unscaled_billboard(SpatialEditorGizmos::singleton->stream_player_icon,0.05); + + } else if (splayer->cast_to<SpatialSamplePlayer>()) { + + add_unscaled_billboard(SpatialEditorGizmos::singleton->sample_player_icon,0.05); + + } + +} + +SpatialPlayerSpatialGizmo::SpatialPlayerSpatialGizmo(SpatialPlayer* p_splayer){ + + set_spatial_node(p_splayer); + splayer=p_splayer; +} + + +///// + + +void RoomSpatialGizmo::redraw() { + + clear(); + Ref<RoomBounds> roomie = room->get_room(); + if (roomie.is_null()) + return; + DVector<Face3> faces = roomie->get_geometry_hint(); + + Vector<Vector3> lines; + int fc=faces.size(); + DVector<Face3>::Read r =faces.read(); + + Map<_EdgeKey,Vector3> edge_map; + + for(int i=0;i<fc;i++) { + + Vector3 fn = r[i].get_plane().normal; + + for(int j=0;j<3;j++) { + + _EdgeKey ek; + ek.from=r[i].vertex[j].snapped(CMP_EPSILON); + ek.to=r[i].vertex[(j+1)%3].snapped(CMP_EPSILON); + if (ek.from<ek.to) + SWAP(ek.from,ek.to); + + Map<_EdgeKey,Vector3>::Element *E=edge_map.find(ek); + + if (E) { + + if (E->get().dot(fn) >0.9) { + + E->get()=Vector3(); + } + + } else { + + edge_map[ek]=fn; + } + + } + } + + for(Map<_EdgeKey,Vector3>::Element *E=edge_map.front();E;E=E->next()) { + + if (E->get()!=Vector3()) { + lines.push_back(E->key().from); + lines.push_back(E->key().to); + } + } + + add_lines(lines,SpatialEditorGizmos::singleton->room_material); + add_collision_segments(lines); + +} + +RoomSpatialGizmo::RoomSpatialGizmo(Room* p_room){ + + set_spatial_node(p_room); + room=p_room; +} + +///// + + +void PortalSpatialGizmo::redraw() { + + clear(); + + Vector<Point2> points = portal->get_shape(); + if (points.size()==0) { + return; + } + + Vector<Vector3> lines; + + Vector3 center; + for(int i=0;i<points.size();i++) { + + Vector3 f; + f.x=points[i].x; + f.y=points[i].y; + Vector3 fn; + fn.x=points[(i+1)%points.size()].x; + fn.y=points[(i+1)%points.size()].y; + center+=f; + + lines.push_back(f); + lines.push_back(fn); + } + + center/=points.size(); + lines.push_back(center); + lines.push_back(center+Vector3(0,0,1)); + + add_lines(lines,SpatialEditorGizmos::singleton->portal_material); + add_collision_segments(lines); + +} + +PortalSpatialGizmo::PortalSpatialGizmo(Portal* p_portal){ + + set_spatial_node(p_portal); + portal=p_portal; +} + +///// + + +void RayCastSpatialGizmo::redraw() { + + clear(); + + + Vector<Vector3> lines; + + lines.push_back(Vector3()); + lines.push_back(raycast->get_cast_to()); + + add_lines(lines,SpatialEditorGizmos::singleton->raycast_material); + add_collision_segments(lines); + +} + +RayCastSpatialGizmo::RayCastSpatialGizmo(RayCast* p_raycast){ + + set_spatial_node(p_raycast); + raycast=p_raycast; +} + + + +///// + + +void VehicleWheelSpatialGizmo::redraw() { + + clear(); + + + Vector<Vector3> points; + + float r = car_wheel->get_radius(); + const int skip=10; + for(int i=0;i<=360;i+=skip) { + + float ra=Math::deg2rad(i); + float rb=Math::deg2rad(i+skip); + Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r; + Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r; + + points.push_back(Vector3(0,a.x,a.y)); + points.push_back(Vector3(0,b.x,b.y)); + + const int springsec=4; + + for(int j=0;j<springsec;j++) { + float t = car_wheel->get_suspension_rest_length()*5; + points.push_back(Vector3(a.x,i/360.0*t/springsec+j*(t/springsec),a.y)*0.2); + points.push_back(Vector3(b.x,(i+skip)/360.0*t/springsec+j*(t/springsec),b.y)*0.2); + } + + + } + + //travel + points.push_back(Vector3(0,0,0)); + points.push_back(Vector3(0,car_wheel->get_suspension_rest_length(),0)); + + //axis + points.push_back(Vector3(r*0.2,car_wheel->get_suspension_rest_length(),0)); + points.push_back(Vector3(-r*0.2,car_wheel->get_suspension_rest_length(),0)); + //axis + points.push_back(Vector3(r*0.2,0,0)); + points.push_back(Vector3(-r*0.2,0,0)); + + //forward line + points.push_back(Vector3(0,-r,0)); + points.push_back(Vector3(0,-r,r*2)); + points.push_back(Vector3(0,-r,r*2)); + points.push_back(Vector3(r*2*0.2,-r,r*2*0.8)); + points.push_back(Vector3(0,-r,r*2)); + points.push_back(Vector3(-r*2*0.2,-r,r*2*0.8)); + + add_lines(points,SpatialEditorGizmos::singleton->car_wheel_material); + add_collision_segments(points); + +} + +VehicleWheelSpatialGizmo::VehicleWheelSpatialGizmo(VehicleWheel* p_car_wheel){ + + set_spatial_node(p_car_wheel); + car_wheel=p_car_wheel; +} + + + +/// + +void TestCubeSpatialGizmo::redraw() { + + clear(); + add_collision_triangles(SpatialEditorGizmos::singleton->test_cube_tm); +} + +TestCubeSpatialGizmo::TestCubeSpatialGizmo(TestCube* p_tc) { + + tc=p_tc; + set_spatial_node(p_tc); +} + + +/////////// + + + + + + +String CollisionShapeSpatialGizmo::get_handle_name(int p_idx) const { + + Ref<Shape> s = cs->get_shape(); + if (s.is_null()) + return ""; + + if (s->cast_to<SphereShape>()) { + + return "Radius"; + } + + if (s->cast_to<BoxShape>()) { + + return "Extents"; + } + + if (s->cast_to<CapsuleShape>()) { + + return p_idx==0?"Radius":"Height"; + } + + if (s->cast_to<RayShape>()) { + + return "Length"; + } + + return ""; +} +Variant CollisionShapeSpatialGizmo::get_handle_value(int p_idx) const{ + + Ref<Shape> s = cs->get_shape(); + if (s.is_null()) + return Variant(); + + if (s->cast_to<SphereShape>()) { + + Ref<SphereShape> ss = s; + return ss->get_radius(); + } + + if (s->cast_to<BoxShape>()) { + + Ref<BoxShape> bs = s; + return bs->get_extents(); + } + + if (s->cast_to<CapsuleShape>()) { + + Ref<CapsuleShape> cs = s; + return p_idx==0?cs->get_radius():cs->get_height(); + } + + if (s->cast_to<RayShape>()) { + + Ref<RayShape> cs = s; + return cs->get_length(); + } + + return Variant(); +} +void CollisionShapeSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){ + Ref<Shape> s = cs->get_shape(); + if (s.is_null()) + return; + + Transform gt = cs->get_global_transform(); + gt.orthonormalize(); + Transform gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)}; + + if (s->cast_to<SphereShape>()) { + + Ref<SphereShape> ss = s; + Vector3 ra,rb; + Geometry::get_closest_points_between_segments(Vector3(),Vector3(4096,0,0),sg[0],sg[1],ra,rb); + float d = ra.x; + if (d<0.001) + d=0.001; + + ss->set_radius(d); + } + + if (s->cast_to<RayShape>()) { + + Ref<RayShape> rs = s; + Vector3 ra,rb; + Geometry::get_closest_points_between_segments(Vector3(),Vector3(0,0,4096),sg[0],sg[1],ra,rb); + float d = ra.z; + if (d<0.001) + d=0.001; + + rs->set_length(d); + } + + + if (s->cast_to<BoxShape>()) { + + Vector3 axis; + axis[p_idx]=1.0; + Ref<BoxShape> bs = s; + Vector3 ra,rb; + Geometry::get_closest_points_between_segments(Vector3(),axis*4096,sg[0],sg[1],ra,rb); + float d = ra[p_idx]; + if (d<0.001) + d=0.001; + + Vector3 he = bs->get_extents(); + he[p_idx]=d; + bs->set_extents(he); + + } + + if (s->cast_to<CapsuleShape>()) { + + Vector3 axis; + axis[p_idx==0?0:2]=1.0; + Ref<CapsuleShape> cs = s; + Vector3 ra,rb; + Geometry::get_closest_points_between_segments(Vector3(),axis*4096,sg[0],sg[1],ra,rb); + float d = axis.dot(ra); + if (p_idx==1) + d-=cs->get_radius(); + if (d<0.001) + d=0.001; + + if (p_idx==0) + cs->set_radius(d); + else if (p_idx==1) + cs->set_height(d*2.0); + + } + +} +void CollisionShapeSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){ + Ref<Shape> s = cs->get_shape(); + if (s.is_null()) + return; + + if (s->cast_to<SphereShape>()) { + + Ref<SphereShape> ss=s; + if (p_cancel) { + ss->set_radius(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action("Change Sphere Shape Radius"); + ur->add_do_method(ss.ptr(),"set_radius",ss->get_radius()); + ur->add_undo_method(ss.ptr(),"set_radius",p_restore); + ur->commit_action(); + + } + + if (s->cast_to<BoxShape>()) { + + Ref<BoxShape> ss=s; + if (p_cancel) { + ss->set_extents(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action("Change Box Shape Extents"); + ur->add_do_method(ss.ptr(),"set_extents",ss->get_extents()); + ur->add_undo_method(ss.ptr(),"set_extents",p_restore); + ur->commit_action(); + } + + if (s->cast_to<CapsuleShape>()) { + + Ref<CapsuleShape> ss=s; + if (p_cancel) { + if (p_idx==0) + ss->set_radius(p_restore); + else + ss->set_height(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + if (p_idx==0) { + ur->create_action("Change Capsule Shape Radius"); + ur->add_do_method(ss.ptr(),"set_radius",ss->get_radius()); + ur->add_undo_method(ss.ptr(),"set_radius",p_restore); + } else { + ur->create_action("Change Capsule Shape Height"); + ur->add_do_method(ss.ptr(),"set_height",ss->get_height()); + ur->add_undo_method(ss.ptr(),"set_height",p_restore); + + } + + ur->commit_action(); + + } + + if (s->cast_to<RayShape>()) { + + Ref<RayShape> ss=s; + if (p_cancel) { + ss->set_length(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action("Change Ray Shape Length"); + ur->add_do_method(ss.ptr(),"set_length",ss->get_length()); + ur->add_undo_method(ss.ptr(),"set_length",p_restore); + ur->commit_action(); + + } + +} +void CollisionShapeSpatialGizmo::redraw(){ + + clear(); + + Ref<Shape> s = cs->get_shape(); + if (s.is_null()) + return; + + if (s->cast_to<SphereShape>()) { + + Ref<SphereShape> sp= s; + float r=sp->get_radius(); + + Vector<Vector3> points; + + for(int i=0;i<=360;i++) { + + float ra=Math::deg2rad(i); + float rb=Math::deg2rad(i+1); + Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r; + Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r; + + points.push_back(Vector3(a.x,0,a.y)); + points.push_back(Vector3(b.x,0,b.y)); + points.push_back(Vector3(0,a.x,a.y)); + points.push_back(Vector3(0,b.x,b.y)); + points.push_back(Vector3(a.x,a.y,0)); + points.push_back(Vector3(b.x,b.y,0)); + + } + + Vector<Vector3> collision_segments; + + for(int i=0;i<64;i++) { + + float ra=i*Math_PI*2.0/64.0; + float rb=(i+1)*Math_PI*2.0/64.0; + Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*r; + Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*r; + + collision_segments.push_back(Vector3(a.x,0,a.y)); + collision_segments.push_back(Vector3(b.x,0,b.y)); + collision_segments.push_back(Vector3(0,a.x,a.y)); + collision_segments.push_back(Vector3(0,b.x,b.y)); + collision_segments.push_back(Vector3(a.x,a.y,0)); + collision_segments.push_back(Vector3(b.x,b.y,0)); + } + + add_lines(points,SpatialEditorGizmos::singleton->shape_material); + add_collision_segments(collision_segments); + Vector<Vector3> handles; + handles.push_back(Vector3(r,0,0)); + add_handles(handles); + + } + + if (s->cast_to<BoxShape>()) { + + Ref<BoxShape> bs=s; + Vector<Vector3> lines; + AABB aabb; + aabb.pos=-bs->get_extents(); + aabb.size=aabb.pos*-2; + + for(int i=0;i<12;i++) { + Vector3 a,b; + aabb.get_edge(i,a,b); + lines.push_back(a); + lines.push_back(b); + } + + Vector<Vector3> handles; + + for(int i=0;i<3;i++) { + + Vector3 ax; + ax[i]=bs->get_extents()[i]; + handles.push_back(ax); + } + + add_lines(lines,SpatialEditorGizmos::singleton->shape_material); + add_collision_segments(lines); + add_handles(handles); + + } + + if (s->cast_to<CapsuleShape>()) { + + Ref<CapsuleShape> cs=s; + float radius = cs->get_radius(); + float height = cs->get_height(); + + + Vector<Vector3> points; + + Vector3 d(0,0,height*0.5); + for(int i=0;i<360;i++) { + + float ra=Math::deg2rad(i); + float rb=Math::deg2rad(i+1); + Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*radius; + Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*radius; + + points.push_back(Vector3(a.x,a.y,0)+d); + points.push_back(Vector3(b.x,b.y,0)+d); + + points.push_back(Vector3(a.x,a.y,0)-d); + points.push_back(Vector3(b.x,b.y,0)-d); + + if (i%90==0) { + + points.push_back(Vector3(a.x,a.y,0)+d); + points.push_back(Vector3(a.x,a.y,0)-d); + } + + Vector3 dud = i<180?d:-d; + + points.push_back(Vector3(0,a.y,a.x)+dud); + points.push_back(Vector3(0,b.y,b.x)+dud); + points.push_back(Vector3(a.y,0,a.x)+dud); + points.push_back(Vector3(b.y,0,b.x)+dud); + + } + + add_lines(points,SpatialEditorGizmos::singleton->shape_material); + + Vector<Vector3> collision_segments; + + for(int i=0;i<64;i++) { + + float ra=i*Math_PI*2.0/64.0; + float rb=(i+1)*Math_PI*2.0/64.0; + Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*radius; + Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*radius; + + collision_segments.push_back(Vector3(a.x,a.y,0)+d); + collision_segments.push_back(Vector3(b.x,b.y,0)+d); + + collision_segments.push_back(Vector3(a.x,a.y,0)-d); + collision_segments.push_back(Vector3(b.x,b.y,0)-d); + + if (i%16==0) { + + collision_segments.push_back(Vector3(a.x,a.y,0)+d); + collision_segments.push_back(Vector3(a.x,a.y,0)-d); + } + + Vector3 dud = i<32?d:-d; + + collision_segments.push_back(Vector3(0,a.y,a.x)+dud); + collision_segments.push_back(Vector3(0,b.y,b.x)+dud); + collision_segments.push_back(Vector3(a.y,0,a.x)+dud); + collision_segments.push_back(Vector3(b.y,0,b.x)+dud); + + } + + add_collision_segments(collision_segments); + + Vector<Vector3> handles; + handles.push_back(Vector3(cs->get_radius(),0,0)); + handles.push_back(Vector3(0,0,cs->get_height()*0.5+cs->get_radius())); + add_handles(handles); + + + } + + if (s->cast_to<PlaneShape>()) { + + Ref<PlaneShape> ps=s; + Plane p = ps->get_plane(); + Vector<Vector3> points; + + Vector3 n1 = p.get_any_perpendicular_normal(); + Vector3 n2 = p.normal.cross(n1).normalized(); + + Vector3 pface[4]={ + p.normal*p.d+n1*10.0+n2*10.0, + p.normal*p.d+n1*10.0+n2*-10.0, + p.normal*p.d+n1*-10.0+n2*-10.0, + p.normal*p.d+n1*-10.0+n2*10.0, + }; + + points.push_back(pface[0]); + points.push_back(pface[1]); + points.push_back(pface[1]); + points.push_back(pface[2]); + points.push_back(pface[2]); + points.push_back(pface[3]); + points.push_back(pface[3]); + points.push_back(pface[0]); + points.push_back(p.normal*p.d); + points.push_back(p.normal*p.d+p.normal*3); + + add_lines(points,SpatialEditorGizmos::singleton->shape_material); + add_collision_segments(points); + + } + + + if (s->cast_to<ConvexPolygonShape>()) { + + DVector<Vector3> points = s->cast_to<ConvexPolygonShape>()->get_points(); + + if (points.size()>3) { + + QuickHull qh; + Vector<Vector3> varr = Variant(points); + Geometry::MeshData md; + Error err = qh.build(varr,md); + if (err==OK) { + Vector<Vector3> points; + points.resize(md.edges.size()*2); + for(int i=0;i<md.edges.size();i++) { + points[i*2+0]=md.vertices[md.edges[i].a]; + points[i*2+1]=md.vertices[md.edges[i].b]; + } + + + add_lines(points,SpatialEditorGizmos::singleton->shape_material); + add_collision_segments(points); + + } + } + + } + + + if (s->cast_to<RayShape>()) { + + Ref<RayShape> rs=s; + + Vector<Vector3> points; + points.push_back(Vector3()); + points.push_back(Vector3(0,0,rs->get_length())); + add_lines(points,SpatialEditorGizmos::singleton->shape_material); + add_collision_segments(points); + Vector<Vector3> handles; + handles.push_back(Vector3(0,0,rs->get_length())); + add_handles(handles); + + + } + +} +CollisionShapeSpatialGizmo::CollisionShapeSpatialGizmo(CollisionShape* p_cs) { + + cs=p_cs; + set_spatial_node(p_cs); +} + + + +///// + + +void CollisionPolygonSpatialGizmo::redraw() { + + clear(); + + Vector<Vector2> points = polygon->get_polygon(); + float depth = polygon->get_depth()*0.5; + + Vector<Vector3> lines; + for(int i=0;i<points.size();i++) { + + int n = (i+1)%points.size(); + lines.push_back(Vector3(points[i].x,points[i].y,depth)); + lines.push_back(Vector3(points[n].x,points[n].y,depth)); + lines.push_back(Vector3(points[i].x,points[i].y,-depth)); + lines.push_back(Vector3(points[n].x,points[n].y,-depth)); + lines.push_back(Vector3(points[i].x,points[i].y,depth)); + lines.push_back(Vector3(points[i].x,points[i].y,-depth)); + + } + + add_lines(lines,SpatialEditorGizmos::singleton->shape_material); + add_collision_segments(lines); +} + +CollisionPolygonSpatialGizmo::CollisionPolygonSpatialGizmo(CollisionPolygon* p_polygon){ + + set_spatial_node(p_polygon); + polygon=p_polygon; +} +/// + + +String VisibilityNotifierGizmo::get_handle_name(int p_idx) const { + + switch(p_idx) { + case 0: return "X"; + case 1: return "Y"; + case 2: return "Z"; + } + + return ""; +} +Variant VisibilityNotifierGizmo::get_handle_value(int p_idx) const{ + + return notifier->get_aabb(); +} +void VisibilityNotifierGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){ + + + Transform gt = notifier->get_global_transform(); + //gt.orthonormalize(); + Transform gi = gt.affine_inverse(); + + AABB aabb = notifier->get_aabb(); + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2]={gi.xform(ray_from),gi.xform(ray_from+ray_dir*4096)}; + Vector3 ofs = aabb.pos+aabb.size*0.5;; + + Vector3 axis; + axis[p_idx]=1.0; + + Vector3 ra,rb; + Geometry::get_closest_points_between_segments(ofs,ofs+axis*4096,sg[0],sg[1],ra,rb); + float d = ra[p_idx]; + if (d<0.001) + d=0.001; + + Vector3 he = aabb.size; + aabb.pos[p_idx]=(aabb.pos[p_idx]+aabb.size[p_idx]*0.5)-d; + aabb.size[p_idx]=d*2; + notifier->set_aabb(aabb); +} + +void VisibilityNotifierGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){ + + + if (p_cancel) { + notifier->set_aabb(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action("Change Notifier Extents"); + ur->add_do_method(notifier,"set_aabb",notifier->get_aabb()); + ur->add_undo_method(notifier,"set_aabb",p_restore); + ur->commit_action(); + +} + +void VisibilityNotifierGizmo::redraw(){ + + clear(); + + Vector<Vector3> lines; + AABB aabb = notifier->get_aabb(); + + for(int i=0;i<12;i++) { + Vector3 a,b; + aabb.get_edge(i,a,b); + lines.push_back(a); + lines.push_back(b); + } + + Vector<Vector3> handles; + + + for(int i=0;i<3;i++) { + + Vector3 ax; + ax[i]=aabb.pos[i]+aabb.size[i]; + handles.push_back(ax); + } + + add_lines(lines,SpatialEditorGizmos::singleton->visibility_notifier_material); + //add_unscaled_billboard(SpatialEditorGizmos::singleton->visi,0.05); + add_collision_segments(lines); + add_handles(handles); + +} +VisibilityNotifierGizmo::VisibilityNotifierGizmo(VisibilityNotifier* p_notifier){ + + notifier=p_notifier; + set_spatial_node(p_notifier); +} + +//////// + + + +void NavigationMeshSpatialGizmo::redraw() { + + clear(); + Ref<NavigationMesh> navmeshie = navmesh->get_navigation_mesh(); + if (navmeshie.is_null()) + return; + + DVector<Vector3> vertices = navmeshie->get_vertices(); + DVector<Vector3>::Read vr=vertices.read(); + List<Face3> faces; + for(int i=0;i<navmeshie->get_polygon_count();i++) { + Vector<int> p = navmeshie->get_polygon(i); + + for(int j=2;j<p.size();j++) { + Face3 f; + f.vertex[0]=vr[p[0]]; + f.vertex[1]=vr[p[j-1]]; + f.vertex[2]=vr[p[j]]; + + faces.push_back(f); + } + } + + + Map<_EdgeKey,bool> edge_map; + DVector<Vector3> tmeshfaces; + tmeshfaces.resize(faces.size()*3); + + { + DVector<Vector3>::Write tw=tmeshfaces.write(); + int tidx=0; + + + for(List<Face3>::Element *E=faces.front();E;E=E->next()) { + + const Face3 &f = E->get(); + + for(int j=0;j<3;j++) { + + tw[tidx++]=f.vertex[j]; + _EdgeKey ek; + ek.from=f.vertex[j].snapped(CMP_EPSILON); + ek.to=f.vertex[(j+1)%3].snapped(CMP_EPSILON); + if (ek.from<ek.to) + SWAP(ek.from,ek.to); + + Map<_EdgeKey,bool>::Element *E=edge_map.find(ek); + + if (E) { + + E->get()=false; + + } else { + + edge_map[ek]=true; + } + + } + } + } + Vector<Vector3> lines; + + for(Map<_EdgeKey,bool>::Element *E=edge_map.front();E;E=E->next()) { + + if (E->get()) { + lines.push_back(E->key().from); + lines.push_back(E->key().to); + } + } + + Ref<TriangleMesh> tmesh = memnew( TriangleMesh); + tmesh->create(tmeshfaces); + + if (lines.size()) + add_lines(lines,navmesh->is_enabled()?SpatialEditorGizmos::singleton->navmesh_edge_material:SpatialEditorGizmos::singleton->navmesh_edge_material_disabled); + add_collision_triangles(tmesh); + Ref<Mesh> m = memnew( Mesh ); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[0]=tmeshfaces; + m->add_surface(Mesh::PRIMITIVE_TRIANGLES,a); + m->surface_set_material(0,navmesh->is_enabled()?SpatialEditorGizmos::singleton->navmesh_solid_material:SpatialEditorGizmos::singleton->navmesh_solid_material_disabled); + add_mesh(m); + add_collision_segments(lines); + +} + +NavigationMeshSpatialGizmo::NavigationMeshSpatialGizmo(NavigationMeshInstance *p_navmesh){ + + set_spatial_node(p_navmesh); + navmesh=p_navmesh; +} + +////// +/// +/// + + + +void PinJointSpatialGizmo::redraw() { + + clear(); + Vector<Vector3> cursor_points; + float cs = 0.25; + cursor_points.push_back(Vector3(+cs,0,0)); + cursor_points.push_back(Vector3(-cs,0,0)); + cursor_points.push_back(Vector3(0,+cs,0)); + cursor_points.push_back(Vector3(0,-cs,0)); + cursor_points.push_back(Vector3(0,0,+cs)); + cursor_points.push_back(Vector3(0,0,-cs)); + add_collision_segments(cursor_points); + add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material); + +} + + +PinJointSpatialGizmo::PinJointSpatialGizmo(PinJoint* p_p3d) { + + p3d=p_p3d; + set_spatial_node(p3d); +} + +//// + +void HingeJointSpatialGizmo::redraw() { + + clear(); + Vector<Vector3> cursor_points; + float cs = 0.25; + /*cursor_points.push_back(Vector3(+cs,0,0)); + cursor_points.push_back(Vector3(-cs,0,0)); + cursor_points.push_back(Vector3(0,+cs,0)); + cursor_points.push_back(Vector3(0,-cs,0));*/ + cursor_points.push_back(Vector3(0,0,+cs*2)); + cursor_points.push_back(Vector3(0,0,-cs*2)); + + float ll = p3d->get_param(HingeJoint::PARAM_LIMIT_LOWER); + float ul = p3d->get_param(HingeJoint::PARAM_LIMIT_UPPER); + + if (p3d->get_flag(HingeJoint::FLAG_USE_LIMIT) && ll<ul) { + + const int points = 32; + float step = (ul-ll)/points; + + + for(int i=0;i<points;i++) { + + float s = ll+i*(ul-ll)/points; + float n = ll+(i+1)*(ul-ll)/points; + + Vector3 from=Vector3( -Math::sin(s),Math::cos(s), 0 )*cs; + Vector3 to=Vector3( -Math::sin(n),Math::cos(n), 0 )*cs; + + if (i==points-1) { + cursor_points.push_back(to); + cursor_points.push_back(Vector3()); + } + if (i==0) { + cursor_points.push_back(from); + cursor_points.push_back(Vector3()); + } + + + cursor_points.push_back(from); + cursor_points.push_back(to); + + + } + + cursor_points.push_back(Vector3(0,cs*1.5,0)); + cursor_points.push_back(Vector3()); + + } else { + + + const int points = 32; + + for(int i=0;i<points;i++) { + + float s = ll+i*(Math_PI*2.0)/points; + float n = ll+(i+1)*(Math_PI*2.0)/points; + + Vector3 from=Vector3( -Math::sin(s),Math::cos(s), 0 )*cs; + Vector3 to=Vector3( -Math::sin(n),Math::cos(n), 0 )*cs; + + cursor_points.push_back(from); + cursor_points.push_back(to); + + } + + } + add_collision_segments(cursor_points); + add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material); + +} + + +HingeJointSpatialGizmo::HingeJointSpatialGizmo(HingeJoint* p_p3d) { + + p3d=p_p3d; + set_spatial_node(p3d); +} + +/////// +/// +//// + +void SliderJointSpatialGizmo::redraw() { + + clear(); + Vector<Vector3> cursor_points; + float cs = 0.25; + /*cursor_points.push_back(Vector3(+cs,0,0)); + cursor_points.push_back(Vector3(-cs,0,0)); + cursor_points.push_back(Vector3(0,+cs,0)); + cursor_points.push_back(Vector3(0,-cs,0));*/ + cursor_points.push_back(Vector3(0,0,+cs*2)); + cursor_points.push_back(Vector3(0,0,-cs*2)); + + float ll = p3d->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_LOWER); + float ul = p3d->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_UPPER); + float lll = -p3d->get_param(SliderJoint::PARAM_LINEAR_LIMIT_LOWER); + float lul = -p3d->get_param(SliderJoint::PARAM_LINEAR_LIMIT_UPPER); + + if (lll>lul) { + + cursor_points.push_back(Vector3(lul,0,0)); + cursor_points.push_back(Vector3(lll,0,0)); + + cursor_points.push_back(Vector3(lul,-cs,-cs)); + cursor_points.push_back(Vector3(lul,-cs,cs)); + cursor_points.push_back(Vector3(lul,-cs,cs)); + cursor_points.push_back(Vector3(lul,cs,cs)); + cursor_points.push_back(Vector3(lul,cs,cs)); + cursor_points.push_back(Vector3(lul,cs,-cs)); + cursor_points.push_back(Vector3(lul,cs,-cs)); + cursor_points.push_back(Vector3(lul,-cs,-cs)); + + + cursor_points.push_back(Vector3(lll,-cs,-cs)); + cursor_points.push_back(Vector3(lll,-cs,cs)); + cursor_points.push_back(Vector3(lll,-cs,cs)); + cursor_points.push_back(Vector3(lll,cs,cs)); + cursor_points.push_back(Vector3(lll,cs,cs)); + cursor_points.push_back(Vector3(lll,cs,-cs)); + cursor_points.push_back(Vector3(lll,cs,-cs)); + cursor_points.push_back(Vector3(lll,-cs,-cs)); + + + } else { + + cursor_points.push_back(Vector3(+cs*2,0,0)); + cursor_points.push_back(Vector3(-cs*2,0,0)); + + } + + if (ll<ul) { + + const int points = 32; + float step = (ul-ll)/points; + + + for(int i=0;i<points;i++) { + + float s = ll+i*(ul-ll)/points; + float n = ll+(i+1)*(ul-ll)/points; + + Vector3 from=Vector3(0, Math::cos(s), -Math::sin(s) )*cs; + Vector3 to=Vector3(0,Math::cos(n), -Math::sin(n) )*cs; + + if (i==points-1) { + cursor_points.push_back(to); + cursor_points.push_back(Vector3()); + } + if (i==0) { + cursor_points.push_back(from); + cursor_points.push_back(Vector3()); + } + + + cursor_points.push_back(from); + cursor_points.push_back(to); + + + } + + cursor_points.push_back(Vector3(0,cs*1.5,0)); + cursor_points.push_back(Vector3()); + + } else { + + + const int points = 32; + + for(int i=0;i<points;i++) { + + float s = ll+i*(Math_PI*2.0)/points; + float n = ll+(i+1)*(Math_PI*2.0)/points; + + Vector3 from=Vector3(0,Math::cos(s),-Math::sin(s) )*cs; + Vector3 to=Vector3( 0,Math::cos(n),-Math::sin(n) )*cs; + + cursor_points.push_back(from); + cursor_points.push_back(to); + + } + + } + add_collision_segments(cursor_points); + add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material); + +} + + +SliderJointSpatialGizmo::SliderJointSpatialGizmo(SliderJoint* p_p3d) { + + p3d=p_p3d; + set_spatial_node(p3d); +} + +/////// +/// +//// + +void ConeTwistJointSpatialGizmo::redraw() { + + clear(); + float cs = 0.25; + Vector<Vector3> points; + + float r = 1.0; + float w = r*Math::sin(p3d->get_param(ConeTwistJoint::PARAM_SWING_SPAN)); + float d = r*Math::cos(p3d->get_param(ConeTwistJoint::PARAM_SWING_SPAN)); + + + //swing + for(int i=0;i<360;i+=10) { + + float ra=Math::deg2rad(i); + float rb=Math::deg2rad(i+10); + Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w; + Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w; + + /*points.push_back(Vector3(a.x,0,a.y)); + points.push_back(Vector3(b.x,0,b.y)); + points.push_back(Vector3(0,a.x,a.y)); + points.push_back(Vector3(0,b.x,b.y));*/ + points.push_back(Vector3(d,a.x,a.y)); + points.push_back(Vector3(d,b.x,b.y)); + + if (i%90==0) { + + points.push_back(Vector3(d,a.x,a.y)); + points.push_back(Vector3()); + + } + } + + points.push_back(Vector3()); + points.push_back(Vector3(1,0,0)); + + //twist + /* + */ + float ts=Math::rad2deg(p3d->get_param(ConeTwistJoint::PARAM_TWIST_SPAN)); + ts=MIN(ts,720); + + + for(int i=0;i<int(ts);i+=5) { + + float ra=Math::deg2rad(i); + float rb=Math::deg2rad(i+5); + float c = i/720.0; + float cn = (i+5)/720.0; + Point2 a = Vector2(Math::sin(ra),Math::cos(ra))*w*c; + Point2 b = Vector2(Math::sin(rb),Math::cos(rb))*w*cn; + + /*points.push_back(Vector3(a.x,0,a.y)); + points.push_back(Vector3(b.x,0,b.y)); + points.push_back(Vector3(0,a.x,a.y)); + points.push_back(Vector3(0,b.x,b.y));*/ + + points.push_back(Vector3(c,a.x,a.y)); + points.push_back(Vector3(cn,b.x,b.y)); + + } + + + add_collision_segments(points); + add_lines(points,SpatialEditorGizmos::singleton->joint_material); + +} + + +ConeTwistJointSpatialGizmo::ConeTwistJointSpatialGizmo(ConeTwistJoint* p_p3d) { + + p3d=p_p3d; + set_spatial_node(p3d); +} + +//////// +/// \brief SpatialEditorGizmos::singleton +/// +/////// +/// +//// + +void Generic6DOFJointSpatialGizmo::redraw() { + + clear(); + Vector<Vector3> cursor_points; + float cs = 0.25; + + for(int ax=0;ax<3;ax++) { + /*cursor_points.push_back(Vector3(+cs,0,0)); + cursor_points.push_back(Vector3(-cs,0,0)); + cursor_points.push_back(Vector3(0,+cs,0)); + cursor_points.push_back(Vector3(0,-cs,0)); + cursor_points.push_back(Vector3(0,0,+cs*2)); + cursor_points.push_back(Vector3(0,0,-cs*2)); */ + + float ll; + float ul; + float lll; + float lul; + + int a1,a2,a3; + bool enable_ang; + bool enable_lin; + + switch(ax) { + case 0: + ll = p3d->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT); + ul = p3d->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT); + lll = -p3d->get_param_x(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT); + lul = -p3d->get_param_x(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT); + enable_ang = p3d->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT); + enable_lin = p3d->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT); + a1=0; + a2=1; + a3=2; + break; + case 1: + ll = p3d->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT); + ul = p3d->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT); + lll = -p3d->get_param_y(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT); + lul = -p3d->get_param_y(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT); + enable_ang = p3d->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT); + enable_lin = p3d->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT); + a1=2; + a2=0; + a3=1; + break; + case 2: + ll = p3d->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT); + ul = p3d->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT); + lll = -p3d->get_param_z(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT); + lul = -p3d->get_param_z(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT); + enable_ang = p3d->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT); + enable_lin = p3d->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT); + + a1=1; + a2=2; + a3=0; + break; + } + +#define ADD_VTX(x,y,z)\ + {\ + Vector3 v;\ + v[a1]=(x);\ + v[a2]=(y);\ + v[a3]=(z);\ + cursor_points.push_back(v);\ + } + +#define SET_VTX(what,x,y,z)\ + {\ + Vector3 v;\ + v[a1]=(x);\ + v[a2]=(y);\ + v[a3]=(z);\ + what=v;\ + } + + + + + if (enable_lin && lll>=lul) { + + ADD_VTX(lul,0,0); + ADD_VTX(lll,0,0); + + ADD_VTX(lul,-cs,-cs); + ADD_VTX(lul,-cs,cs); + ADD_VTX(lul,-cs,cs); + ADD_VTX(lul,cs,cs); + ADD_VTX(lul,cs,cs); + ADD_VTX(lul,cs,-cs); + ADD_VTX(lul,cs,-cs); + ADD_VTX(lul,-cs,-cs); + + + ADD_VTX(lll,-cs,-cs); + ADD_VTX(lll,-cs,cs); + ADD_VTX(lll,-cs,cs); + ADD_VTX(lll,cs,cs); + ADD_VTX(lll,cs,cs); + ADD_VTX(lll,cs,-cs); + ADD_VTX(lll,cs,-cs); + ADD_VTX(lll,-cs,-cs); + + + } else { + + ADD_VTX(+cs*2,0,0); + ADD_VTX(-cs*2,0,0); + + } + + if (enable_ang && ll<=ul) { + + const int points = 32; + float step = (ul-ll)/points; + + + for(int i=0;i<points;i++) { + + float s = ll+i*(ul-ll)/points; + float n = ll+(i+1)*(ul-ll)/points; + + Vector3 from; + SET_VTX(from,0, Math::cos(s), -Math::sin(s) ); + from*=cs; + Vector3 to; + SET_VTX(to,0,Math::cos(n), -Math::sin(n)); + to*=cs; + + if (i==points-1) { + cursor_points.push_back(to); + cursor_points.push_back(Vector3()); + } + if (i==0) { + cursor_points.push_back(from); + cursor_points.push_back(Vector3()); + } + + + cursor_points.push_back(from); + cursor_points.push_back(to); + + + } + + ADD_VTX(0,cs*1.5,0); + cursor_points.push_back(Vector3()); + + } else { + + + const int points = 32; + + for(int i=0;i<points;i++) { + + float s = ll+i*(Math_PI*2.0)/points; + float n = ll+(i+1)*(Math_PI*2.0)/points; + +// Vector3 from=Vector3(0,Math::cos(s),-Math::sin(s) )*cs; +// Vector3 to=Vector3( 0,Math::cos(n),-Math::sin(n) )*cs; + + Vector3 from; + SET_VTX(from,0, Math::cos(s), -Math::sin(s) ); + from*=cs; + Vector3 to; + SET_VTX(to,0,Math::cos(n), -Math::sin(n)); + to*=cs; + + cursor_points.push_back(from); + cursor_points.push_back(to); + + } + + } + } + +#undef ADD_VTX +#undef SET_VTX + + add_collision_segments(cursor_points); + add_lines(cursor_points,SpatialEditorGizmos::singleton->joint_material); + +} + + +Generic6DOFJointSpatialGizmo::Generic6DOFJointSpatialGizmo(Generic6DOFJoint* p_p3d) { + + p3d=p_p3d; + set_spatial_node(p3d); +} + +/////// +/// +//// + + +SpatialEditorGizmos *SpatialEditorGizmos::singleton=NULL; + +Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) { + + if (p_spatial->cast_to<Light>()) { + + Ref<LightSpatialGizmo> lsg = memnew( LightSpatialGizmo(p_spatial->cast_to<Light>()) ); + return lsg; + } + + if (p_spatial->cast_to<Camera>()) { + + Ref<CameraSpatialGizmo> lsg = memnew( CameraSpatialGizmo(p_spatial->cast_to<Camera>()) ); + return lsg; + } + + if (p_spatial->cast_to<Skeleton>()) { + + Ref<SkeletonSpatialGizmo> lsg = memnew( SkeletonSpatialGizmo(p_spatial->cast_to<Skeleton>()) ); + return lsg; + } + + + if (p_spatial->cast_to<Position3D>()) { + + Ref<Position3DSpatialGizmo> lsg = memnew( Position3DSpatialGizmo(p_spatial->cast_to<Position3D>()) ); + return lsg; + } + + if (p_spatial->cast_to<MeshInstance>()) { + + Ref<MeshInstanceSpatialGizmo> misg = memnew( MeshInstanceSpatialGizmo(p_spatial->cast_to<MeshInstance>()) ); + return misg; + } + + if (p_spatial->cast_to<Room>()) { + + Ref<RoomSpatialGizmo> misg = memnew( RoomSpatialGizmo(p_spatial->cast_to<Room>()) ); + return misg; + } + + if (p_spatial->cast_to<NavigationMeshInstance>()) { + + Ref<NavigationMeshSpatialGizmo> misg = memnew( NavigationMeshSpatialGizmo(p_spatial->cast_to<NavigationMeshInstance>()) ); + return misg; + } + + if (p_spatial->cast_to<RayCast>()) { + + Ref<RayCastSpatialGizmo> misg = memnew( RayCastSpatialGizmo(p_spatial->cast_to<RayCast>()) ); + return misg; + } + + if (p_spatial->cast_to<Portal>()) { + + Ref<PortalSpatialGizmo> misg = memnew( PortalSpatialGizmo(p_spatial->cast_to<Portal>()) ); + return misg; + } + + + if (p_spatial->cast_to<TestCube>()) { + + Ref<TestCubeSpatialGizmo> misg = memnew( TestCubeSpatialGizmo(p_spatial->cast_to<TestCube>()) ); + return misg; + } + + if (p_spatial->cast_to<SpatialPlayer>()) { + + Ref<SpatialPlayerSpatialGizmo> misg = memnew( SpatialPlayerSpatialGizmo(p_spatial->cast_to<SpatialPlayer>()) ); + return misg; + } + + if (p_spatial->cast_to<CollisionShape>()) { + + Ref<CollisionShapeSpatialGizmo> misg = memnew( CollisionShapeSpatialGizmo(p_spatial->cast_to<CollisionShape>()) ); + return misg; + } + + if (p_spatial->cast_to<VisibilityNotifier>()) { + + Ref<VisibilityNotifierGizmo> misg = memnew( VisibilityNotifierGizmo(p_spatial->cast_to<VisibilityNotifier>()) ); + return misg; + } + + if (p_spatial->cast_to<VehicleWheel>()) { + + Ref<VehicleWheelSpatialGizmo> misg = memnew( VehicleWheelSpatialGizmo(p_spatial->cast_to<VehicleWheel>()) ); + return misg; + } + if (p_spatial->cast_to<PinJoint>()) { + + Ref<PinJointSpatialGizmo> misg = memnew( PinJointSpatialGizmo(p_spatial->cast_to<PinJoint>()) ); + return misg; + } + + if (p_spatial->cast_to<HingeJoint>()) { + + Ref<HingeJointSpatialGizmo> misg = memnew( HingeJointSpatialGizmo(p_spatial->cast_to<HingeJoint>()) ); + return misg; + } + + if (p_spatial->cast_to<SliderJoint>()) { + + Ref<SliderJointSpatialGizmo> misg = memnew( SliderJointSpatialGizmo(p_spatial->cast_to<SliderJoint>()) ); + return misg; + } + + if (p_spatial->cast_to<ConeTwistJoint>()) { + + Ref<ConeTwistJointSpatialGizmo> misg = memnew( ConeTwistJointSpatialGizmo(p_spatial->cast_to<ConeTwistJoint>()) ); + return misg; + } + + if (p_spatial->cast_to<Generic6DOFJoint>()) { + + Ref<Generic6DOFJointSpatialGizmo> misg = memnew( Generic6DOFJointSpatialGizmo(p_spatial->cast_to<Generic6DOFJoint>()) ); + return misg; + } + + if (p_spatial->cast_to<CollisionPolygon>()) { + + Ref<CollisionPolygonSpatialGizmo> misg = memnew( CollisionPolygonSpatialGizmo(p_spatial->cast_to<CollisionPolygon>()) ); + return misg; + } + + + return Ref<SpatialEditorGizmo>(); +} + + +Ref<FixedMaterial> SpatialEditorGizmos::create_line_material(const Color& p_base_color) { + + Ref<FixedMaterial> line_material = Ref<FixedMaterial>( memnew( FixedMaterial )); + line_material->set_flag(Material::FLAG_UNSHADED, true); + line_material->set_line_width(3.0); + line_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + line_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, true); + line_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,p_base_color); + + return line_material; + +} + +Ref<FixedMaterial> SpatialEditorGizmos::create_solid_material(const Color& p_base_color) { + + Ref<FixedMaterial> line_material = Ref<FixedMaterial>( memnew( FixedMaterial )); + line_material->set_flag(Material::FLAG_UNSHADED, true); + line_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + line_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,p_base_color); + + return line_material; + +} + +SpatialEditorGizmos::SpatialEditorGizmos() { + + singleton=this; + + handle_material = Ref<FixedMaterial>( memnew( FixedMaterial )); + handle_material->set_flag(Material::FLAG_UNSHADED, true); + handle_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(0.8,0.8,0.8)); + + handle2_material = Ref<FixedMaterial>( memnew( FixedMaterial )); + handle2_material->set_flag(Material::FLAG_UNSHADED, true); + handle2_material->set_fixed_flag(FixedMaterial::FLAG_USE_POINT_SIZE, true); + handle_t = SpatialEditor::get_singleton()->get_icon("Editor3DHandle","EditorIcons"); + handle2_material->set_point_size(handle_t->get_width()); + handle2_material->set_texture(FixedMaterial::PARAM_DIFFUSE,handle_t); + handle2_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1)); + handle2_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + handle2_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, true); + + light_material = create_line_material(Color(1,1,0.2)); + + light_material_omni_icon = Ref<FixedMaterial>( memnew( FixedMaterial )); + light_material_omni_icon->set_flag(Material::FLAG_UNSHADED, true); + light_material_omni_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true); + light_material_omni_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER); + light_material_omni_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + light_material_omni_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9)); + light_material_omni_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoLight","EditorIcons")); + + + light_material_directional_icon = Ref<FixedMaterial>( memnew( FixedMaterial )); + light_material_directional_icon->set_flag(Material::FLAG_UNSHADED, true); + light_material_directional_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true); + light_material_directional_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER); + light_material_directional_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + light_material_directional_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9)); + light_material_directional_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoDirectionalLight","EditorIcons")); + + camera_material = create_line_material(Color(1.0,0.5,1.0)); + + + navmesh_edge_material = create_line_material(Color(0.1,0.8,1.0)); + navmesh_solid_material = create_solid_material(Color(0.1,0.8,1.0,0.4)); + navmesh_edge_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, false); + navmesh_solid_material->set_flag(Material::FLAG_DOUBLE_SIDED,true); + + navmesh_edge_material_disabled = create_line_material(Color(1.0,0.8,0.1)); + navmesh_solid_material_disabled = create_solid_material(Color(1.0,0.8,0.1,0.4)); + navmesh_edge_material_disabled->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, false); + navmesh_solid_material_disabled->set_flag(Material::FLAG_DOUBLE_SIDED,true); + + skeleton_material = create_line_material(Color(0.6,1.0,0.3)); + skeleton_material->set_flag(Material::FLAG_DOUBLE_SIDED,true); + skeleton_material->set_flag(Material::FLAG_UNSHADED,true); + skeleton_material->set_flag(Material::FLAG_ONTOP,true); + skeleton_material->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER); + + //position 3D Shared mesh + + pos3d_mesh = Ref<Mesh>( memnew( Mesh ) ); + { + + DVector<Vector3> cursor_points; + DVector<Color> cursor_colors; + float cs = 0.25; + cursor_points.push_back(Vector3(+cs,0,0)); + cursor_points.push_back(Vector3(-cs,0,0)); + cursor_points.push_back(Vector3(0,+cs,0)); + cursor_points.push_back(Vector3(0,-cs,0)); + cursor_points.push_back(Vector3(0,0,+cs)); + cursor_points.push_back(Vector3(0,0,-cs)); + cursor_colors.push_back(Color(1,0.5,0.5,0.7)); + cursor_colors.push_back(Color(1,0.5,0.5,0.7)); + cursor_colors.push_back(Color(0.5,1,0.5,0.7)); + cursor_colors.push_back(Color(0.5,1,0.5,0.7)); + cursor_colors.push_back(Color(0.5,0.5,1,0.7)); + cursor_colors.push_back(Color(0.5,0.5,1,0.7)); + + Ref<FixedMaterial> mat = memnew( FixedMaterial ); + mat->set_flag(Material::FLAG_UNSHADED,true); + mat->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true); + mat->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true); + mat->set_line_width(3); + Array d; + d.resize(VS::ARRAY_MAX); + d[Mesh::ARRAY_VERTEX]=cursor_points; + d[Mesh::ARRAY_COLOR]=cursor_colors; + pos3d_mesh->add_surface(Mesh::PRIMITIVE_LINES,d); + pos3d_mesh->surface_set_material(0,mat); + } + + + sample_player_icon = Ref<FixedMaterial>( memnew( FixedMaterial )); + sample_player_icon->set_flag(Material::FLAG_UNSHADED, true); + sample_player_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true); + sample_player_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER); + sample_player_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + sample_player_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9)); + sample_player_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoSpatialSamplePlayer","EditorIcons")); + + room_material = create_line_material(Color(1.0,0.6,0.9)); + portal_material = create_line_material(Color(1.0,0.8,0.6)); + raycast_material = create_line_material(Color(1.0,0.8,0.6)); + car_wheel_material = create_line_material(Color(0.6,0.8,1.0)); + visibility_notifier_material = create_line_material(Color(1.0,0.5,1.0)); + joint_material = create_line_material(Color(0.6,0.8,1.0)); + + stream_player_icon = Ref<FixedMaterial>( memnew( FixedMaterial )); + stream_player_icon->set_flag(Material::FLAG_UNSHADED, true); + stream_player_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true); + stream_player_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER); + stream_player_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + stream_player_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9)); + stream_player_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("GizmoSpatialStreamPlayer","EditorIcons")); + + visibility_notifier_icon = Ref<FixedMaterial>( memnew( FixedMaterial )); + visibility_notifier_icon->set_flag(Material::FLAG_UNSHADED, true); + visibility_notifier_icon->set_flag(Material::FLAG_DOUBLE_SIDED, true); + visibility_notifier_icon->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER); + visibility_notifier_icon->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + visibility_notifier_icon->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.9)); + visibility_notifier_icon->set_texture(FixedMaterial::PARAM_DIFFUSE,SpatialEditor::get_singleton()->get_icon("Visible","EditorIcons")); + + { + + DVector<Vector3> vertices; + +#undef ADD_VTX +#define ADD_VTX(m_idx);\ + vertices.push_back( face_points[m_idx] ); + + for (int i=0;i<6;i++) { + + + Vector3 face_points[4]; + + for (int j=0;j<4;j++) { + + float v[3]; + v[0]=1.0; + v[1]=1-2*((j>>1)&1); + v[2]=v[1]*(1-2*(j&1)); + + for (int k=0;k<3;k++) { + + if (i<3) + face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1); + else + face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1); + } + } + //tri 1 + ADD_VTX(0); + ADD_VTX(1); + ADD_VTX(2); + //tri 2 + ADD_VTX(2); + ADD_VTX(3); + ADD_VTX(0); + + } + + test_cube_tm = Ref<TriangleMesh>( memnew( TriangleMesh ) ); + test_cube_tm->create(vertices); + } + + shape_material = create_line_material(Color(0.2,1,1.0)); + + +} + diff --git a/tools/editor/spatial_editor_gizmos.h b/tools/editor/spatial_editor_gizmos.h index 7f39b648d7..bc7e8ad21d 100644 --- a/tools/editor/spatial_editor_gizmos.h +++ b/tools/editor/spatial_editor_gizmos.h @@ -1,487 +1,491 @@ -/*************************************************************************/
-/* spatial_editor_gizmos.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* http://www.godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
-/* */
-/* 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, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* 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.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-#ifndef SPATIAL_EDITOR_GIZMOS_H
-#define SPATIAL_EDITOR_GIZMOS_H
-
-
-#include "tools/editor/plugins/spatial_editor_plugin.h"
-#include "scene/3d/light.h"
-#include "scene/3d/camera.h"
-#include "scene/3d/position_3d.h"
-#include "scene/3d/spatial_sample_player.h"
-#include "scene/3d/spatial_stream_player.h"
-#include "scene/3d/test_cube.h"
-#include "scene/3d/mesh_instance.h"
-#include "scene/3d/body_shape.h"
-#include "scene/3d/room_instance.h"
-#include "scene/3d/visibility_notifier.h"
-#include "scene/3d/portal.h"
-#include "scene/3d/ray_cast.h"
-#include "scene/3d/navigation_mesh.h"
-
-#include "scene/3d/vehicle_body.h"
-#include "scene/3d/collision_polygon.h"
-#include "scene/3d/physics_joint.h"
-
-
-class Camera;
-
-class SpatialGizmoTool : public SpatialEditorGizmo {
-
- OBJ_TYPE(SpatialGizmoTool,SpatialGizmo);
-
- struct Instance{
-
- RID instance;
- Ref<Mesh> mesh;
- RID skeleton;
- bool billboard;
- bool unscaled;
- bool can_intersect;
- bool extra_margin;
- Instance() {
-
- billboard=false;
- unscaled=false;
- can_intersect=false;
- extra_margin=false;
- }
-
- void create_instance(Spatial *p_base);
-
- };
-
- Vector<Vector3> collision_segments;
- Ref<TriangleMesh> collision_mesh;
-
- struct Handle {
- Vector3 pos;
- bool billboard;
- };
-
- Vector<Vector3> handles;
- Vector<Vector3> secondary_handles;
- bool billboard_handle;
-
- bool valid;
- Spatial *base;
- Vector<Instance> instances;
- Spatial *spatial_node;
-protected:
- void add_lines(const Vector<Vector3> &p_lines,const Ref<Material>& p_material,bool p_billboard=false);
- void add_mesh(const Ref<Mesh>& p_mesh,bool p_billboard=false,const RID& p_skeleton=RID());
- void add_collision_segments(const Vector<Vector3> &p_lines);
- void add_collision_triangles(const Ref<TriangleMesh>& p_tmesh);
- void add_unscaled_billboard(const Ref<Material>& p_material,float p_scale=1);
- void add_handles(const Vector<Vector3> &p_handles,bool p_billboard=false,bool p_secondary=false);
-
- void set_spatial_node(Spatial *p_node);
-
-public:
-
- virtual Vector3 get_handle_pos(int p_idx) const;
- virtual bool intersect_frustum(const Camera *p_camera,const Vector<Plane> &p_frustum);
- virtual bool intersect_ray(const Camera *p_camera,const Point2& p_point, Vector3& r_pos, Vector3& r_normal,int *r_gizmo_handle=NULL,bool p_sec_first=false);
-
- void clear();
- void create();
- void transform();
- //void redraw();
- void free();
-
- SpatialGizmoTool();
- ~SpatialGizmoTool();
-};
-
-
-
-class LightSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(LightSpatialGizmo,SpatialGizmoTool);
-
- Light* light;
-
-public:
-
-
- virtual String get_handle_name(int p_idx) const;
- virtual Variant get_handle_value(int p_idx) const;
- virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point);
- virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false);
-
- void redraw();
- LightSpatialGizmo(Light* p_light=NULL);
-
-};
-
-class CameraSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(CameraSpatialGizmo,SpatialGizmoTool);
-
- Camera* camera;
-
-public:
-
-
- virtual String get_handle_name(int p_idx) const;
- virtual Variant get_handle_value(int p_idx) const;
- virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point);
- virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false);
-
- void redraw();
- CameraSpatialGizmo(Camera* p_camera=NULL);
-
-};
-
-
-
-class MeshInstanceSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(MeshInstanceSpatialGizmo,SpatialGizmoTool);
-
- MeshInstance* mesh;
-
-public:
-
- void redraw();
- MeshInstanceSpatialGizmo(MeshInstance* p_mesh=NULL);
-
-};
-
-class Position3DSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(Position3DSpatialGizmo,SpatialGizmoTool);
-
- Position3D* p3d;
-
-public:
-
- void redraw();
- Position3DSpatialGizmo(Position3D* p_p3d=NULL);
-
-};
-
-class SkeletonSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(SkeletonSpatialGizmo,SpatialGizmoTool);
-
- Skeleton* skel;
-
-public:
-
- void redraw();
- SkeletonSpatialGizmo(Skeleton* p_skel=NULL);
-
-};
-
-
-class SpatialPlayerSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(SpatialPlayerSpatialGizmo,SpatialGizmoTool);
-
- SpatialPlayer* splayer;
-
-public:
-
- void redraw();
- SpatialPlayerSpatialGizmo(SpatialPlayer* p_splayer=NULL);
-
-};
-
-class TestCubeSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(TestCubeSpatialGizmo,SpatialGizmoTool);
-
- TestCube* tc;
-
-public:
- void redraw();
- TestCubeSpatialGizmo(TestCube* p_tc=NULL);
-
-};
-
-
-class RoomSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(RoomSpatialGizmo,SpatialGizmoTool);
-
-
- struct _EdgeKey {
-
- Vector3 from;
- Vector3 to;
-
- bool operator<(const _EdgeKey& p_with) const { return from==p_with.from ? to < p_with.to : from < p_with.from; }
- };
-
-
-
- Room* room;
-
-public:
-
- void redraw();
- RoomSpatialGizmo(Room* p_room=NULL);
-
-};
-
-
-class PortalSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(PortalSpatialGizmo,SpatialGizmoTool);
-
- Portal* portal;
-
-public:
-
- void redraw();
- PortalSpatialGizmo(Portal* p_portal=NULL);
-
-};
-
-
-class VisibilityNotifierGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(VisibilityNotifierGizmo ,SpatialGizmoTool);
-
-
- VisibilityNotifier* notifier;
-
-public:
-
- virtual String get_handle_name(int p_idx) const;
- virtual Variant get_handle_value(int p_idx) const;
- virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point);
- virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false);
-
- void redraw();
- VisibilityNotifierGizmo(VisibilityNotifier* p_notifier=NULL);
-
-};
-
-
-
-class CollisionShapeSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(CollisionShapeSpatialGizmo,SpatialGizmoTool);
-
- CollisionShape* cs;
-
-public:
- virtual String get_handle_name(int p_idx) const;
- virtual Variant get_handle_value(int p_idx) const;
- virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point);
- virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false);
- void redraw();
- CollisionShapeSpatialGizmo(CollisionShape* p_cs=NULL);
-
-};
-
-
-class CollisionPolygonSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(CollisionPolygonSpatialGizmo,SpatialGizmoTool);
-
- CollisionPolygon* polygon;
-
-public:
-
- void redraw();
- CollisionPolygonSpatialGizmo(CollisionPolygon* p_polygon=NULL);
-
-};
-
-
-class RayCastSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(RayCastSpatialGizmo,SpatialGizmoTool);
-
- RayCast* raycast;
-
-public:
-
- void redraw();
- RayCastSpatialGizmo(RayCast* p_raycast=NULL);
-
-};
-
-
-
-class VehicleWheelSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(VehicleWheelSpatialGizmo,SpatialGizmoTool);
-
- VehicleWheel* car_wheel;
-
-public:
-
- void redraw();
- VehicleWheelSpatialGizmo(VehicleWheel* p_car_wheel=NULL);
-
-};
-
-
-class NavigationMeshSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(NavigationMeshSpatialGizmo,SpatialGizmoTool);
-
-
- struct _EdgeKey {
-
- Vector3 from;
- Vector3 to;
-
- bool operator<(const _EdgeKey& p_with) const { return from==p_with.from ? to < p_with.to : from < p_with.from; }
- };
-
-
-
- NavigationMeshInstance* navmesh;
-
-public:
-
- void redraw();
- NavigationMeshSpatialGizmo(NavigationMeshInstance* p_navmesh=NULL);
-
-};
-
-
-class PinJointSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(PinJointSpatialGizmo,SpatialGizmoTool);
-
- PinJoint* p3d;
-
-public:
-
- void redraw();
- PinJointSpatialGizmo(PinJoint* p_p3d=NULL);
-
-};
-
-
-class HingeJointSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(HingeJointSpatialGizmo,SpatialGizmoTool);
-
- HingeJoint* p3d;
-
-public:
-
- void redraw();
- HingeJointSpatialGizmo(HingeJoint* p_p3d=NULL);
-
-};
-
-class SliderJointSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(SliderJointSpatialGizmo,SpatialGizmoTool);
-
- SliderJoint* p3d;
-
-public:
-
- void redraw();
- SliderJointSpatialGizmo(SliderJoint* p_p3d=NULL);
-
-};
-
-class ConeTwistJointSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(ConeTwistJointSpatialGizmo,SpatialGizmoTool);
-
- ConeTwistJoint* p3d;
-
-public:
-
- void redraw();
- ConeTwistJointSpatialGizmo(ConeTwistJoint* p_p3d=NULL);
-
-};
-
-
-class Generic6DOFJointSpatialGizmo : public SpatialGizmoTool {
-
- OBJ_TYPE(Generic6DOFJointSpatialGizmo,SpatialGizmoTool);
-
- Generic6DOFJoint* p3d;
-
-public:
-
- void redraw();
- Generic6DOFJointSpatialGizmo(Generic6DOFJoint* p_p3d=NULL);
-
-};
-
-
-class SpatialEditorGizmos {
-public:
-
- Ref<FixedMaterial> create_line_material(const Color& p_base_color);
- Ref<FixedMaterial> create_solid_material(const Color& p_base_color);
- Ref<FixedMaterial> handle2_material;
- Ref<FixedMaterial> handle_material;
- Ref<FixedMaterial> light_material;
- Ref<FixedMaterial> light_material_omni_icon;
- Ref<FixedMaterial> light_material_directional_icon;
- Ref<FixedMaterial> camera_material;
- Ref<FixedMaterial> skeleton_material;
- Ref<FixedMaterial> room_material;
- Ref<FixedMaterial> portal_material;
- Ref<FixedMaterial> raycast_material;
- Ref<FixedMaterial> visibility_notifier_material;
- Ref<FixedMaterial> car_wheel_material;
- Ref<FixedMaterial> joint_material;
-
- Ref<FixedMaterial> navmesh_edge_material;
- Ref<FixedMaterial> navmesh_solid_material;
- Ref<FixedMaterial> navmesh_edge_material_disabled;
- Ref<FixedMaterial> navmesh_solid_material_disabled;
-
-
- Ref<FixedMaterial> sample_player_icon;
- Ref<FixedMaterial> stream_player_icon;
- Ref<FixedMaterial> visibility_notifier_icon;
-
- Ref<FixedMaterial> shape_material;
- Ref<Texture> handle_t;
-
- Ref<Mesh> pos3d_mesh;
- static SpatialEditorGizmos *singleton;
-
- Ref<TriangleMesh> test_cube_tm;
-
-
- Ref<SpatialEditorGizmo> get_gizmo(Spatial *p_spatial);
-
- SpatialEditorGizmos();
-};
-
-#endif // SPATIAL_EDITOR_GIZMOS_H
-
+/*************************************************************************/ +/* spatial_editor_gizmos.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef SPATIAL_EDITOR_GIZMOS_H +#define SPATIAL_EDITOR_GIZMOS_H + + +#include "tools/editor/plugins/spatial_editor_plugin.h" +#include "scene/3d/light.h" +#include "scene/3d/camera.h" +#include "scene/3d/position_3d.h" +#include "scene/3d/spatial_sample_player.h" +#include "scene/3d/spatial_stream_player.h" +#include "scene/3d/test_cube.h" +#include "scene/3d/mesh_instance.h" +#include "scene/3d/body_shape.h" +#include "scene/3d/room_instance.h" +#include "scene/3d/visibility_notifier.h" +#include "scene/3d/portal.h" +#include "scene/3d/ray_cast.h" +#include "scene/3d/navigation_mesh.h" + +#include "scene/3d/vehicle_body.h" +#include "scene/3d/collision_polygon.h" +#include "scene/3d/physics_joint.h" + + +class Camera; + +class SpatialGizmoTool : public SpatialEditorGizmo { + + OBJ_TYPE(SpatialGizmoTool,SpatialGizmo); + + struct Instance{ + + RID instance; + Ref<Mesh> mesh; + RID skeleton; + bool billboard; + bool unscaled; + bool can_intersect; + bool extra_margin; + Instance() { + + billboard=false; + unscaled=false; + can_intersect=false; + extra_margin=false; + } + + void create_instance(Spatial *p_base); + + }; + + Vector<Vector3> collision_segments; + Ref<TriangleMesh> collision_mesh; + + struct Handle { + Vector3 pos; + bool billboard; + }; + + Vector<Vector3> handles; + Vector<Vector3> secondary_handles; + bool billboard_handle; + + bool valid; + Spatial *base; + Vector<Instance> instances; + Spatial *spatial_node; +protected: + void add_lines(const Vector<Vector3> &p_lines,const Ref<Material>& p_material,bool p_billboard=false); + void add_mesh(const Ref<Mesh>& p_mesh,bool p_billboard=false,const RID& p_skeleton=RID()); + void add_collision_segments(const Vector<Vector3> &p_lines); + void add_collision_triangles(const Ref<TriangleMesh>& p_tmesh); + void add_unscaled_billboard(const Ref<Material>& p_material,float p_scale=1); + void add_handles(const Vector<Vector3> &p_handles,bool p_billboard=false,bool p_secondary=false); + + void set_spatial_node(Spatial *p_node); + +public: + + virtual Vector3 get_handle_pos(int p_idx) const; + virtual bool intersect_frustum(const Camera *p_camera,const Vector<Plane> &p_frustum); + virtual bool intersect_ray(const Camera *p_camera,const Point2& p_point, Vector3& r_pos, Vector3& r_normal,int *r_gizmo_handle=NULL,bool p_sec_first=false); + + void clear(); + void create(); + void transform(); + //void redraw(); + void free(); + + SpatialGizmoTool(); + ~SpatialGizmoTool(); +}; + + + +class LightSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(LightSpatialGizmo,SpatialGizmoTool); + + Light* light; + +public: + + + virtual String get_handle_name(int p_idx) const; + virtual Variant get_handle_value(int p_idx) const; + virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point); + virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false); + + void redraw(); + LightSpatialGizmo(Light* p_light=NULL); + +}; + +class CameraSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(CameraSpatialGizmo,SpatialGizmoTool); + + Camera* camera; + +public: + + + virtual String get_handle_name(int p_idx) const; + virtual Variant get_handle_value(int p_idx) const; + virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point); + virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false); + + void redraw(); + CameraSpatialGizmo(Camera* p_camera=NULL); + +}; + + + +class MeshInstanceSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(MeshInstanceSpatialGizmo,SpatialGizmoTool); + + MeshInstance* mesh; + +public: + + void redraw(); + MeshInstanceSpatialGizmo(MeshInstance* p_mesh=NULL); + +}; + +class Position3DSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(Position3DSpatialGizmo,SpatialGizmoTool); + + Position3D* p3d; + +public: + + void redraw(); + Position3DSpatialGizmo(Position3D* p_p3d=NULL); + +}; + +class SkeletonSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(SkeletonSpatialGizmo,SpatialGizmoTool); + + Skeleton* skel; + +public: + + void redraw(); + SkeletonSpatialGizmo(Skeleton* p_skel=NULL); + +}; + + + + +class SpatialPlayerSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(SpatialPlayerSpatialGizmo,SpatialGizmoTool); + + SpatialPlayer* splayer; + +public: + + void redraw(); + SpatialPlayerSpatialGizmo(SpatialPlayer* p_splayer=NULL); + +}; + + + +class TestCubeSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(TestCubeSpatialGizmo,SpatialGizmoTool); + + TestCube* tc; + +public: + void redraw(); + TestCubeSpatialGizmo(TestCube* p_tc=NULL); + +}; + + +class RoomSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(RoomSpatialGizmo,SpatialGizmoTool); + + + struct _EdgeKey { + + Vector3 from; + Vector3 to; + + bool operator<(const _EdgeKey& p_with) const { return from==p_with.from ? to < p_with.to : from < p_with.from; } + }; + + + + Room* room; + +public: + + void redraw(); + RoomSpatialGizmo(Room* p_room=NULL); + +}; + + +class PortalSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(PortalSpatialGizmo,SpatialGizmoTool); + + Portal* portal; + +public: + + void redraw(); + PortalSpatialGizmo(Portal* p_portal=NULL); + +}; + + +class VisibilityNotifierGizmo : public SpatialGizmoTool { + + OBJ_TYPE(VisibilityNotifierGizmo ,SpatialGizmoTool); + + + VisibilityNotifier* notifier; + +public: + + virtual String get_handle_name(int p_idx) const; + virtual Variant get_handle_value(int p_idx) const; + virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point); + virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false); + + void redraw(); + VisibilityNotifierGizmo(VisibilityNotifier* p_notifier=NULL); + +}; + + + +class CollisionShapeSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(CollisionShapeSpatialGizmo,SpatialGizmoTool); + + CollisionShape* cs; + +public: + virtual String get_handle_name(int p_idx) const; + virtual Variant get_handle_value(int p_idx) const; + virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point); + virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false); + void redraw(); + CollisionShapeSpatialGizmo(CollisionShape* p_cs=NULL); + +}; + + +class CollisionPolygonSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(CollisionPolygonSpatialGizmo,SpatialGizmoTool); + + CollisionPolygon* polygon; + +public: + + void redraw(); + CollisionPolygonSpatialGizmo(CollisionPolygon* p_polygon=NULL); + +}; + + +class RayCastSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(RayCastSpatialGizmo,SpatialGizmoTool); + + RayCast* raycast; + +public: + + void redraw(); + RayCastSpatialGizmo(RayCast* p_raycast=NULL); + +}; + + + +class VehicleWheelSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(VehicleWheelSpatialGizmo,SpatialGizmoTool); + + VehicleWheel* car_wheel; + +public: + + void redraw(); + VehicleWheelSpatialGizmo(VehicleWheel* p_car_wheel=NULL); + +}; + + +class NavigationMeshSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(NavigationMeshSpatialGizmo,SpatialGizmoTool); + + + struct _EdgeKey { + + Vector3 from; + Vector3 to; + + bool operator<(const _EdgeKey& p_with) const { return from==p_with.from ? to < p_with.to : from < p_with.from; } + }; + + + + NavigationMeshInstance* navmesh; + +public: + + void redraw(); + NavigationMeshSpatialGizmo(NavigationMeshInstance* p_navmesh=NULL); + +}; + + +class PinJointSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(PinJointSpatialGizmo,SpatialGizmoTool); + + PinJoint* p3d; + +public: + + void redraw(); + PinJointSpatialGizmo(PinJoint* p_p3d=NULL); + +}; + + +class HingeJointSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(HingeJointSpatialGizmo,SpatialGizmoTool); + + HingeJoint* p3d; + +public: + + void redraw(); + HingeJointSpatialGizmo(HingeJoint* p_p3d=NULL); + +}; + +class SliderJointSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(SliderJointSpatialGizmo,SpatialGizmoTool); + + SliderJoint* p3d; + +public: + + void redraw(); + SliderJointSpatialGizmo(SliderJoint* p_p3d=NULL); + +}; + +class ConeTwistJointSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(ConeTwistJointSpatialGizmo,SpatialGizmoTool); + + ConeTwistJoint* p3d; + +public: + + void redraw(); + ConeTwistJointSpatialGizmo(ConeTwistJoint* p_p3d=NULL); + +}; + + +class Generic6DOFJointSpatialGizmo : public SpatialGizmoTool { + + OBJ_TYPE(Generic6DOFJointSpatialGizmo,SpatialGizmoTool); + + Generic6DOFJoint* p3d; + +public: + + void redraw(); + Generic6DOFJointSpatialGizmo(Generic6DOFJoint* p_p3d=NULL); + +}; + + +class SpatialEditorGizmos { +public: + + Ref<FixedMaterial> create_line_material(const Color& p_base_color); + Ref<FixedMaterial> create_solid_material(const Color& p_base_color); + Ref<FixedMaterial> handle2_material; + Ref<FixedMaterial> handle_material; + Ref<FixedMaterial> light_material; + Ref<FixedMaterial> light_material_omni_icon; + Ref<FixedMaterial> light_material_directional_icon; + Ref<FixedMaterial> camera_material; + Ref<FixedMaterial> skeleton_material; + Ref<FixedMaterial> room_material; + Ref<FixedMaterial> portal_material; + Ref<FixedMaterial> raycast_material; + Ref<FixedMaterial> visibility_notifier_material; + Ref<FixedMaterial> car_wheel_material; + Ref<FixedMaterial> joint_material; + + Ref<FixedMaterial> navmesh_edge_material; + Ref<FixedMaterial> navmesh_solid_material; + Ref<FixedMaterial> navmesh_edge_material_disabled; + Ref<FixedMaterial> navmesh_solid_material_disabled; + + + Ref<FixedMaterial> sample_player_icon; + Ref<FixedMaterial> stream_player_icon; + Ref<FixedMaterial> visibility_notifier_icon; + + Ref<FixedMaterial> shape_material; + Ref<Texture> handle_t; + + Ref<Mesh> pos3d_mesh; + static SpatialEditorGizmos *singleton; + + Ref<TriangleMesh> test_cube_tm; + + + Ref<SpatialEditorGizmo> get_gizmo(Spatial *p_spatial); + + SpatialEditorGizmos(); +}; + +#endif // SPATIAL_EDITOR_GIZMOS_H + diff --git a/tools/export/blender25/godot_export_manager.py b/tools/export/blender25/godot_export_manager.py new file mode 100644 index 0000000000..582d76f94f --- /dev/null +++ b/tools/export/blender25/godot_export_manager.py @@ -0,0 +1,472 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Script copyright (c) Andreas Esau + +bl_info = { + "name": "Godot Export Manager", + "author": "Andreas Esau", + "version": (1, 0), + "blender": (2, 7, 0), + "location": "Scene Properties > Godot Export Manager", + "description": "Godot Export Manager uses the Better Collada Exporter to manage Export Groups and automatically export the objects groups to Collada Files.", + "warning": "", + "wiki_url": ("http://www.godotengine.org"), + "tracker_url": "", + "category": "Import-Export"} + +import bpy +from bpy.props import StringProperty, BoolProperty, EnumProperty, FloatProperty, FloatVectorProperty, IntProperty, CollectionProperty, PointerProperty +import os +from bpy.app.handlers import persistent +from mathutils import Vector, Matrix + +class godot_export_manager(bpy.types.Panel): + bl_label = "Godot Export Manager" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "scene" + + bpy.types.Scene.godot_export_on_save = BoolProperty(default=False) + + ### draw function for all ui elements + def draw(self, context): + layout = self.layout + split = self.layout.split() + scene = bpy.data.scenes[0] + ob = context.object + scene = context.scene + + row = layout.row() + col = row.column() + col.prop(scene,"godot_export_on_save",text="Export Groups on save") + + row = layout.row() + col = row.column(align=True) + op = col.operator("scene.godot_add_objects_to_group",text="Add selected objects to Group",icon="COPYDOWN") + + op = col.operator("scene.godot_delete_objects_from_group",text="Delete selected objects from Group",icon="PASTEDOWN") + + + + row = layout.row() + col = row.column() + col.label(text="Export Groups:") + + + row = layout.row() + col = row.column() + + col.template_list("UI_List_Godot","dummy",scene, "godot_export_groups", scene, "godot_export_groups_index",rows=1,maxrows=10,type='DEFAULT') + + col = row.column(align=True) + col.operator("scene.godot_add_export_group",text="",icon="ZOOMIN") + col.operator("scene.godot_delete_export_group",text="",icon="ZOOMOUT") + col.operator("scene.godot_export_all_groups",text="",icon="EXPORT") + + if len(scene.godot_export_groups) > 0: + row = layout.row() + col = row.column() + group = scene.godot_export_groups[scene.godot_export_groups_index] + col.prop(group,"name",text="Group Name") + col.prop(group,"export_name",text="Export Name") + col.prop(group,"export_path",text="Export Filepath") + + row = layout.row() + col = row.column() + row = layout.row() + col = row.column() + col.label(text="Export Settings:") + + col = col.row(align=True) + col.prop(group,"apply_loc",toggle=True,icon="MAN_TRANS") + col.prop(group,"apply_rot",toggle=True,icon="MAN_ROT") + col.prop(group,"apply_scale",toggle=True,icon="MAN_SCALE") + + row = layout.row() + col = row.column() + + col.prop(group,"use_include_particle_duplicates") + col.prop(group,"use_mesh_modifiers") + col.prop(group,"use_tangent_arrays") + col.prop(group,"use_triangles") + col.prop(group,"use_copy_images") + col.prop(group,"use_active_layers") + col.prop(group,"use_anim") + col.prop(group,"use_anim_action_all") + col.prop(group,"use_anim_skip_noexp") + col.prop(group,"use_anim_optimize") + col.prop(group,"anim_optimize_precision") + col.prop(group,"use_metadata") + +### Custom template_list look +class UI_List_Godot(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + ob = data + slot = item + col = layout.row(align=True) + + col.label(text=item.name,icon="GROUP") + col.prop(item,"active",text="") + + op = col.operator("scene.godot_select_group_objects",text="",emboss=False,icon="RESTRICT_SELECT_OFF") + op.idx = index + op = col.operator("scene.godot_export_group",text="",emboss=False,icon="EXPORT") + op.idx = index + +class add_objects_to_group(bpy.types.Operator): + bl_idname = "scene.godot_add_objects_to_group" + bl_label = "Add Objects to Group" + bl_description = "Adds the selected Objects to the active group below." + + undo = BoolProperty(default=True) + + def execute(self,context): + scene = context.scene + + objects_str = "" + if len(scene.godot_export_groups) > 0: + for i,object in enumerate(context.selected_objects): + if object.name not in scene.godot_export_groups[scene.godot_export_groups_index].nodes: + node = scene.godot_export_groups[scene.godot_export_groups_index].nodes.add() + node.name = object.name + if i == 0: + objects_str += object.name + else: + objects_str += ", "+object.name + + + self.report({'INFO'}, objects_str + " added to group." ) + if self.undo: + bpy.ops.ed.undo_push(message="Objects added to group") + else: + self.report({'WARNING'}, "Create a group first." ) + return{'FINISHED'} + +class del_objects_from_group(bpy.types.Operator): + bl_idname = "scene.godot_delete_objects_from_group" + bl_label = "Delete Objects from Group" + bl_description = "Delets the selected Objects from the active group below." + + def execute(self,context): + scene = context.scene + + if len(scene.godot_export_groups) > 0: + + selected_objects = [] + for object in context.selected_objects: + selected_objects.append(object.name) + + objects_str = "" + j = 0 + for i,node in enumerate(scene.godot_export_groups[scene.godot_export_groups_index].nodes): + if node.name in selected_objects: + scene.godot_export_groups[scene.godot_export_groups_index].nodes.remove(i) + + + if j == 0: + objects_str += object.name + else: + objects_str += ", "+object.name + j+=1 + + + self.report({'INFO'}, objects_str + " deleted from group." ) + bpy.ops.ed.undo_push(message="Objects deleted from group") + else: + self.report({'WARNING'}, "There is no group to delete from." ) + return{'FINISHED'} + +class select_group_objects(bpy.types.Operator): + bl_idname = "scene.godot_select_group_objects" + bl_label = "Select Group Objects" + bl_description = "Will select all group Objects in the scene." + + idx = IntProperty() + + def execute(self,context): + scene = context.scene + for object in context.scene.objects: + object.select = False + for node in scene.godot_export_groups[self.idx].nodes: + if node.name in bpy.data.objects: + bpy.data.objects[node.name].select = True + context.scene.objects.active = bpy.data.objects[node.name] + return{'FINISHED'} + +class export_groups_autosave(bpy.types.Operator): + bl_idname = "scene.godot_export_groups_autosave" + bl_label = "Export All Groups" + bl_description = "Exports all groups to Collada." + + def execute(self,context): + scene = context.scene + if scene.godot_export_on_save: + for i in range(len(scene.godot_export_groups)): + if scene.godot_export_groups[i].active: + bpy.ops.scene.godot_export_group(idx=i) + self.report({'INFO'}, "All Groups exported." ) + bpy.ops.ed.undo_push(message="Export all Groups") + return{'FINISHED'} + +class export_all_groups(bpy.types.Operator): + bl_idname = "scene.godot_export_all_groups" + bl_label = "Export All Groups" + bl_description = "Exports all groups to Collada." + + def execute(self,context): + scene = context.scene + + for i in range(0,len(scene.godot_export_groups)): + bpy.ops.scene.godot_export_group(idx=i,export_all=True) + + self.report({'INFO'}, "All Groups exported." ) + return{'FINISHED'} + + +class export_group(bpy.types.Operator): + bl_idname = "scene.godot_export_group" + bl_label = "Export Group" + bl_description = "Exports the active group to destination folder as Collada file." + + idx = IntProperty(default=0) + export_all = BoolProperty(default=False) + + + def copy_object_recursive(self,ob,parent,single_user = True): + new_ob = bpy.data.objects[ob.name].copy() + if single_user or ob.type=="ARMATURE": + new_mesh_data = new_ob.data.copy() + new_ob.data = new_mesh_data + bpy.context.scene.objects.link(new_ob) + + if ob != parent: + new_ob.parent = parent + else: + new_ob.parent = None + + for child in ob.children: + self.copy_object_recursive(child,new_ob,single_user) + new_ob.select = True + return new_ob + + def delete_object(self,ob): + if ob != None: + for child in ob.children: + self.delete_object(child) + bpy.context.scene.objects.unlink(ob) + bpy.data.objects.remove(ob) + + def convert_group_to_node(self,group): + if group.dupli_group != None: + for object in group.dupli_group.objects: + if object.parent == None: + object = self.copy_object_recursive(object,object,True) + matrix = Matrix(object.matrix_local) + object.matrix_local = Matrix() + object.matrix_local *= group.matrix_local + object.matrix_local *= matrix + + self.delete_object(group) + + def execute(self,context): + + scene = context.scene + group = context.scene.godot_export_groups + + if not group[self.idx].active and self.export_all: + return{'FINISHED'} + + for i,object in enumerate(group[self.idx].nodes): + if object.name in bpy.data.objects: + pass + else: + group[self.idx].nodes.remove(i) + bpy.ops.ed.undo_push(message="Clear not existent Group Nodes.") + + path = group[self.idx].export_path + if (path.find("//")==0 or path.find("\\\\")==0): + #if relative, convert to absolute + path = bpy.path.abspath(path) + path = path.replace("\\","/") + + ### if path exists and group export name is set the group will be exported + if os.path.exists(path) and group[self.idx].export_name != "": + + context.scene.layers = [True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True] + + + if group[self.idx].export_name.endswith(".dae"): + path = os.path.join(path,group[self.idx].export_name) + else: + path = os.path.join(path,group[self.idx].export_name+".dae") + + hide_select = [] + for object in context.scene.objects: + hide_select.append(object.hide_select) + object.hide_select = False + object.select = False + context.scene.objects.active = None + + ### make particle duplicates, parent and select them + nodes_to_be_added = [] + if group[self.idx].use_include_particle_duplicates: + for i,object in enumerate(group[self.idx].nodes): + if bpy.data.objects[object.name].type != "EMPTY": + context.scene.objects.active = bpy.data.objects[object.name] + bpy.data.objects[object.name].select = True + bpy.ops.object.duplicates_make_real() + for object in context.selected_objects: + nodes_to_be_added.append(object) + bpy.ops.object.parent_set(type="OBJECT", keep_transform=False) + + for object in context.selected_objects: + object.select = False + bpy.data.objects[object.name].select = False + context.scene.objects.active = None + for object in nodes_to_be_added: + object.select = True + + ### select all other nodes from the group + for i,object in enumerate(group[self.idx].nodes): + if bpy.data.objects[object.name].type == "EMPTY": + self.convert_group_to_node(bpy.data.objects[object.name]) + else: + bpy.data.objects[object.name].select = True + + bpy.ops.object.transform_apply(location=group[self.idx].apply_loc, rotation=group[self.idx].apply_rot, scale=group[self.idx].apply_scale) + bpy.ops.export_scene.dae(check_existing=True, filepath=path, filter_glob="*.dae", object_types=group[self.idx].object_types, use_export_selected=group[self.idx].use_export_selected, use_mesh_modifiers=group[self.idx].use_mesh_modifiers, use_tangent_arrays=group[self.idx].use_tangent_arrays, use_triangles=group[self.idx].use_triangles, use_copy_images=group[self.idx].use_copy_images, use_active_layers=group[self.idx].use_active_layers, use_anim=group[self.idx].use_anim, use_anim_action_all=group[self.idx].use_anim_action_all, use_anim_skip_noexp=group[self.idx].use_anim_skip_noexp, use_anim_optimize=group[self.idx].use_anim_optimize, anim_optimize_precision=group[self.idx].anim_optimize_precision, use_metadata=group[self.idx].use_metadata) + + self.report({'INFO'}, '"'+group[self.idx].name+'"' + " Group exported." ) + msg = "Export Group "+group[self.idx].name + + bpy.ops.ed.undo_push(message="") + bpy.ops.ed.undo() + bpy.ops.ed.undo_push(message=msg) + + else: + self.report({'INFO'}, "Define Export Name and Export Path." ) + return{'FINISHED'} + +class add_export_group(bpy.types.Operator): + bl_idname = "scene.godot_add_export_group" + bl_label = "Adds a new export Group" + bl_description = "Creates a new Export Group with the selected Objects assigned to it." + + def execute(self,context): + scene = context.scene + + item = scene.godot_export_groups.add() + item.name = "New Group" + for object in context.selected_objects: + node = item.nodes.add() + node.name = object.name + scene.godot_export_groups_index = len(scene.godot_export_groups)-1 + bpy.ops.ed.undo_push(message="Create New Export Group") + return{'FINISHED'} + +class del_export_group(bpy.types.Operator): + bl_idname = "scene.godot_delete_export_group" + bl_label = "Delets the selected export Group" + bl_description = "Delets the active Export Group." + + def invoke(self, context, event): + wm = context.window_manager + return wm.invoke_confirm(self,event) + + def execute(self,context): + scene = context.scene + + scene.godot_export_groups.remove(scene.godot_export_groups_index) + if scene.godot_export_groups_index > 0: + scene.godot_export_groups_index -= 1 + bpy.ops.ed.undo_push(message="Delete Export Group") + return{'FINISHED'} + +class godot_node_list(bpy.types.PropertyGroup): + name = StringProperty() + +class godot_export_groups(bpy.types.PropertyGroup): + name = StringProperty(name="Group Name") + export_name = StringProperty(name="scene_name") + nodes = CollectionProperty(type=godot_node_list) + export_path = StringProperty(subtype="DIR_PATH") + active = BoolProperty(default=True,description="Export Group") + + object_types = EnumProperty(name="Object Types",options={'ENUM_FLAG'},items=(('EMPTY', "Empty", ""),('CAMERA', "Camera", ""),('LAMP', "Lamp", ""),('ARMATURE', "Armature", ""),('MESH', "Mesh", ""),('CURVE', "Curve", ""),),default={'EMPTY', 'CAMERA', 'LAMP', 'ARMATURE', 'MESH','CURVE'}) + + apply_scale = BoolProperty(name="Apply Scale",description="Apply Scale before export.",default=False) + apply_rot = BoolProperty(name="Apply Rotation",description="Apply Rotation before export.",default=False) + apply_loc = BoolProperty(name="Apply Location",description="Apply Location before export.",default=False) + + use_export_selected = BoolProperty(name="Selected Objects",description="Export only selected objects (and visible in active layers if that applies).",default=True) + use_mesh_modifiers = BoolProperty(name="Apply Modifiers",description="Apply modifiers to mesh objects (on a copy!).",default=True) + use_tangent_arrays = BoolProperty(name="Tangent Arrays",description="Export Tangent and Binormal arrays (for normalmapping).",default=False) + use_triangles = BoolProperty(name="Triangulate",description="Export Triangles instead of Polygons.",default=False) + + use_copy_images = BoolProperty(name="Copy Images",description="Copy Images (create images/ subfolder)",default=False) + use_active_layers = BoolProperty(name="Active Layers",description="Export only objects on the active layers.",default=True) + use_anim = BoolProperty(name="Export Animation",description="Export keyframe animation",default=False) + use_anim_action_all = BoolProperty(name="All Actions",description=("Export all actions for the first armature found in separate DAE files"),default=False) + use_anim_skip_noexp = BoolProperty(name="Skip (-noexp) Actions",description="Skip exporting of actions whose name end in (-noexp). Useful to skip control animations.",default=True) + use_anim_optimize = BoolProperty(name="Optimize Keyframes",description="Remove double keyframes",default=True) + + anim_optimize_precision = FloatProperty(name="Precision",description=("Tolerence for comparing double keyframes (higher for greater accuracy)"),min=1, max=16,soft_min=1, soft_max=16,default=6.0) + + use_metadata = BoolProperty(name="Use Metadata",default=True,options={'HIDDEN'}) + use_include_particle_duplicates = BoolProperty(name="Include Particle Duplicates",default=True) + +def register(): + bpy.utils.register_class(godot_export_manager) + bpy.utils.register_class(godot_node_list) + bpy.utils.register_class(godot_export_groups) + bpy.utils.register_class(add_export_group) + bpy.utils.register_class(del_export_group) + bpy.utils.register_class(export_all_groups) + bpy.utils.register_class(export_groups_autosave) + bpy.utils.register_class(export_group) + bpy.utils.register_class(add_objects_to_group) + bpy.utils.register_class(del_objects_from_group) + bpy.utils.register_class(select_group_objects) + bpy.utils.register_class(UI_List_Godot) + + bpy.types.Scene.godot_export_groups = CollectionProperty(type=godot_export_groups) + bpy.types.Scene.godot_export_groups_index = IntProperty(default=0,min=0) + +def unregister(): + bpy.utils.unregister_class(godot_export_manager) + bpy.utils.unregister_class(godot_node_list) + bpy.utils.unregister_class(godot_export_groups) + bpy.utils.unregister_class(export_groups_autosave) + bpy.utils.unregister_class(add_export_group) + bpy.utils.unregister_class(del_export_group) + bpy.utils.unregister_class(export_all_groups) + bpy.utils.unregister_class(export_group) + bpy.utils.unregister_class(add_objects_to_group) + bpy.utils.unregister_class(del_objects_from_group) + bpy.utils.unregister_class(select_group_objects) + bpy.utils.unregister_class(UI_List_Godot) + +@persistent +def auto_export(dummy): + bpy.ops.scene.godot_export_groups_autosave() + +bpy.app.handlers.save_post.append(auto_export) + +if __name__ == "__main__": + register() diff --git a/tools/export/blender25/io_scene_dae/__init__.py b/tools/export/blender25/io_scene_dae/__init__.py index b3e3f70cf0..182ec21e63 100644 --- a/tools/export/blender25/io_scene_dae/__init__.py +++ b/tools/export/blender25/io_scene_dae/__init__.py @@ -81,7 +81,7 @@ class ExportDAE(bpy.types.Operator, ExportHelper): use_mesh_modifiers = BoolProperty( name="Apply Modifiers", description="Apply modifiers to mesh objects (on a copy!).", - default=True, + default=False, ) use_tangent_arrays = BoolProperty( name="Tangent Arrays", @@ -104,11 +104,6 @@ class ExportDAE(bpy.types.Operator, ExportHelper): description="Export only objects on the active layers.", default=True, ) - use_exclude_ctrl_bones = BoolProperty( - name="Exclude Control Bones", - description="Exclude skeleton bones with names that begin with 'ctrl'.", - default=True, - ) use_anim = BoolProperty( name="Export Animation", description="Export keyframe animation", diff --git a/tools/export/blender25/io_scene_dae/export_dae.py b/tools/export/blender25/io_scene_dae/export_dae.py index 4e1635429b..b846f0e2d8 100644 --- a/tools/export/blender25/io_scene_dae/export_dae.py +++ b/tools/export/blender25/io_scene_dae/export_dae.py @@ -87,6 +87,15 @@ def numarr(a,mult=1.0): s+=" " return s +def numarr_alpha(a,mult=1.0): + s=" " + for x in a: + s+=" "+str(x*mult) + if len(a) == 3: + s+=" 1.0" + s+=" " + return s + def strarr(arr): s=" " for x in arr: @@ -94,8 +103,6 @@ def strarr(arr): s+=" " return s - - class DaeExporter: def validate_id(self,d): @@ -132,10 +139,10 @@ class DaeExporter: tup = tup + (self.tangent.x,self.tangent.y,self.tangent.z) if (self.bitangent!=None): tup = tup + (self.bitangent.x,self.bitangent.y,self.bitangent.z) - #for t in self.bones: - # tup = tup + (t) - #for t in self.weights: - # tup = tup + (t) + for t in self.bones: + tup = tup + (float(t),) + for t in self.weights: + tup = tup + (float(t),) return tup @@ -162,39 +169,66 @@ class DaeExporter: def export_image(self,image): - if (image in self.image_cache): return self.image_cache[image] - + imgpath = image.filepath if (imgpath.find("//")==0 or imgpath.find("\\\\")==0): #if relative, convert to absolute imgpath = bpy.path.abspath(imgpath) #path is absolute, now do something! - + if (self.config["use_copy_images"]): #copy image basedir = os.path.dirname(self.path)+"/images" if (not os.path.isdir(basedir)): os.makedirs(basedir) - dstfile=basedir+"/"+os.path.basename(imgpath) - if (not os.path.isfile(dstfile)): - shutil.copy(imgpath,dstfile) - imgpath="images/"+os.path.basename(imgpath) + + if os.path.isfile(imgpath): + dstfile=basedir+"/"+os.path.basename(imgpath) + + if (not os.path.isfile(dstfile)): + shutil.copy(imgpath,dstfile) + imgpath="images/"+os.path.basename(imgpath) + else: + ### if file is not found save it as png file in the destination folder + img_tmp_path = image.filepath + if img_tmp_path.endswith((".bmp",".rgb",".png",".jpeg",".jpg",".jp2",".tga",".cin",".dpx",".exr",".hdr",".tif")): + image.filepath = basedir+"/"+os.path.basename(img_tmp_path) + else: + image.filepath = basedir+"/"+image.name+".png" + + dstfile=basedir+"/"+os.path.basename(image.filepath) + + if (not os.path.isfile(dstfile)): + + image.save() + imgpath="images/"+os.path.basename(image.filepath) + image.filepath = img_tmp_path else: #export relative, always, no one wants absolute paths. try: imgpath = os.path.relpath(imgpath,os.path.dirname(self.path)).replace("\\","/") # export unix compatible always + except: pass #fails sometimes, not sure why + + imgid = self.new_id("image") + print("FOR: "+imgpath) + +# if (not os.path.isfile(imgpath)): +# print("NOT FILE?") +# if imgpath.endswith((".bmp",".rgb",".png",".jpeg",".jpg",".jp2",".tga",".cin",".dpx",".exr",".hdr",".tif")): +# imgpath="images/"+os.path.basename(imgpath) +# else: +# imgpath="images/"+image.name+".png" - imgid = self.new_id("image") self.writel(S_IMGS,1,'<image id="'+imgid+'" name="'+image.name+'">') - self.writel(S_IMGS,2,'<init_from>'+imgpath+'</init_from>"/>') + self.writel(S_IMGS,2,'<init_from>'+imgpath+'</init_from>') self.writel(S_IMGS,1,'</image>') self.image_cache[image]=imgid return imgid @@ -266,25 +300,25 @@ class DaeExporter: if (emission_tex!=None): self.writel(S_FX,6,'<texture texture="'+emission_tex+'" texcoord="CHANNEL1"/>') else: - self.writel(S_FX,6,'<color>'+numarr(material.diffuse_color,material.emit)+' </color>') # not totally right but good enough + self.writel(S_FX,6,'<color>'+numarr_alpha(material.diffuse_color,material.emit)+' </color>') # not totally right but good enough self.writel(S_FX,5,'</emission>') self.writel(S_FX,5,'<ambient>') - self.writel(S_FX,6,'<color>'+numarr(self.scene.world.ambient_color,material.ambient)+' </color>') + self.writel(S_FX,6,'<color>'+numarr_alpha(self.scene.world.ambient_color,material.ambient)+' </color>') self.writel(S_FX,5,'</ambient>') self.writel(S_FX,5,'<diffuse>') if (diffuse_tex!=None): self.writel(S_FX,6,'<texture texture="'+diffuse_tex+'" texcoord="CHANNEL1"/>') else: - self.writel(S_FX,6,'<color>'+numarr(material.diffuse_color,material.diffuse_intensity)+'</color>') + self.writel(S_FX,6,'<color>'+numarr_alpha(material.diffuse_color,material.diffuse_intensity)+'</color>') self.writel(S_FX,5,'</diffuse>') self.writel(S_FX,5,'<specular>') if (specular_tex!=None): self.writel(S_FX,6,'<texture texture="'+specular_tex+'" texcoord="CHANNEL1"/>') else: - self.writel(S_FX,6,'<color>'+numarr(material.specular_color,material.specular_intensity)+'</color>') + self.writel(S_FX,6,'<color>'+numarr_alpha(material.specular_color,material.specular_intensity)+'</color>') self.writel(S_FX,5,'</specular>') self.writel(S_FX,5,'<shininess>') @@ -292,7 +326,7 @@ class DaeExporter: self.writel(S_FX,5,'</shininess>') self.writel(S_FX,5,'<reflective>') - self.writel(S_FX,6,'<color>'+strarr(material.mirror_color)+'</color>') + self.writel(S_FX,6,'<color>'+numarr_alpha(material.mirror_color)+'</color>') self.writel(S_FX,5,'</reflective>') if (material.use_transparency): @@ -300,10 +334,11 @@ class DaeExporter: self.writel(S_FX,6,'<float>'+str(material.alpha)+'</float>') self.writel(S_FX,5,'</transparency>') - + self.writel(S_FX,5,'<index_of_refraction>') + self.writel(S_FX,6,'<float>'+str(material.specular_ior)+'</float>') + self.writel(S_FX,5,'</index_of_refraction>') self.writel(S_FX,4,'</'+shtype+'>') - self.writel(S_FX,4,'<index_of_refraction>'+str(material.specular_ior)+'</index_of_refraction>') self.writel(S_FX,4,'<extra>') self.writel(S_FX,5,'<technique profile="FCOLLADA">') @@ -457,6 +492,11 @@ class DaeExporter: apply_modifiers = len(node.modifiers) and self.config["use_mesh_modifiers"] + name_to_use = mesh.name + #print("name to use: "+mesh.name) + if (custom_name!=None and custom_name!=""): + name_to_use=custom_name + mesh=node.to_mesh(self.scene,apply_modifiers,"RENDER") #is this allright? triangulate=self.config["use_triangles"] @@ -488,12 +528,12 @@ class DaeExporter: mat_assign=[] uv_layer_count=len(mesh.uv_textures) - if (len(mesh.uv_textures)): + if (has_tangents and len(mesh.uv_textures)): try: mesh.calc_tangents() except: - print("Warning, blender API is fucked up, not exporting UVs for this object.") - uv_layer_count=0 + self.operator.report({'WARNING'},'CalcTangets failed for mesh "'+mesh.name+'", no tangets will be exported.') + #uv_layer_count=0 mesh.calc_normals_split() has_tangents=False @@ -507,8 +547,8 @@ class DaeExporter: if (not (f.material_index in surface_indices)): surface_indices[f.material_index]=[] - print("Type: "+str(type(f.material_index))) - print("IDX: "+str(f.material_index)+"/"+str(len(mesh.materials))) + #print("Type: "+str(type(f.material_index))) + #print("IDX: "+str(f.material_index)+"/"+str(len(mesh.materials))) try: #Bizarre blender behavior i don't understand, so catching exception @@ -567,16 +607,30 @@ class DaeExporter: if (armature!=None): wsum=0.0 + zero_bones=[] + for vg in mv.groups: if vg.group >= len(node.vertex_groups): continue; name = node.vertex_groups[vg.group].name + if (name in si["bone_index"]): #could still put the weight as 0.0001 maybe if (vg.weight>0.001): #blender has a lot of zero weight stuff v.bones.append(si["bone_index"][name]) v.weights.append(vg.weight) wsum+=vg.weight + if (wsum==0.0): + if not self.wrongvtx_report: + self.operator.report({'WARNING'},'Mesh for object "'+node.name+'" has unassigned weights. This may look wrong in exported model.') + self.wrongvtx_report=True + + #blender can have bones assigned that weight zero so they remain local + #this is the best it can be done? + v.bones.append(0) + v.weights.append(1) + + tup = v.get_tup() @@ -596,10 +650,7 @@ class DaeExporter: meshid = self.new_id("mesh") - if (custom_name!=None): - self.writel(S_GEOM,1,'<geometry id="'+meshid+'" name="'+custom_name+'">') - else: - self.writel(S_GEOM,1,'<geometry id="'+meshid+'" name="'+mesh.name+'">') + self.writel(S_GEOM,1,'<geometry id="'+meshid+'" name="'+name_to_use+'">') self.writel(S_GEOM,2,'<mesh>') @@ -861,22 +912,35 @@ class DaeExporter: if (node.data==None): return armature=None + armcount=0 + for n in node.modifiers: + if (n.type=="ARMATURE"): + armcount+=1 if (node.parent!=None): if (node.parent.type=="ARMATURE"): armature=node.parent + if (armcount>1): + self.operator.report({'WARNING'},'Object "'+node.name+'" refers to more than one armature! This is unsopported.') + if (armcount==0): + self.operator.report({'WARNING'},'Object "'+node.name+'" is child of an armature, but has no armature modifier.') + + + if (armcount>0 and not armature): + self.operator.report({'WARNING'},'Object "'+node.name+'" has armature modifier, but is not a child of an armature. This is unsupported.') + if (node.data.shape_keys!=None): sk = node.data.shape_keys if (sk.animation_data): - print("HAS ANIM") - print("DRIVERS: "+str(len(sk.animation_data.drivers))) + #print("HAS ANIM") + #print("DRIVERS: "+str(len(sk.animation_data.drivers))) for d in sk.animation_data.drivers: if (d.driver): for v in d.driver.variables: for t in v.targets: if (t.id!=None and t.id.name in self.scene.objects): - print("LINKING "+str(node)+" WITH "+str(t.id.name)) + #print("LINKING "+str(node)+" WITH "+str(t.id.name)) self.armature_for_morph[node]=self.scene.objects[t.id.name] @@ -916,6 +980,12 @@ class DaeExporter: boneidx = si["bone_count"] si["bone_count"]+=1 bonesid = si["id"]+"-"+str(boneidx) + if (bone.name in self.used_bones): + if (self.config["use_anim_action_all"]): + self.operator.report({'WARNING'},'Bone name "'+bone.name+'" used in more than one skeleton. Actions might export wrong.') + else: + self.used_bones.append(bone.name) + si["bone_index"][bone.name]=boneidx si["bone_ids"][bone]=boneid si["bone_names"].append(bonesid) @@ -978,12 +1048,12 @@ class DaeExporter: self.writel(S_CAMS,5,'<zfar> '+str(camera.clip_end)+' </zfar>') self.writel(S_CAMS,4,'</perspective>') else: - self.writel(S_CAMS,4,'<orthografic>') - self.writel(S_CAMS,5,'<xmag> '+str(camera.ortho_scale)+' </xmag>') # I think? + self.writel(S_CAMS,4,'<orthographic>') + self.writel(S_CAMS,5,'<xmag> '+str(camera.ortho_scale*0.5)+' </xmag>') # I think? self.writel(S_CAMS,5,'<aspect_ratio> '+str(self.scene.render.resolution_x / self.scene.render.resolution_y)+' </aspect_ratio>') self.writel(S_CAMS,5,'<znear> '+str(camera.clip_start)+' </znear>') self.writel(S_CAMS,5,'<zfar> '+str(camera.clip_end)+' </zfar>') - self.writel(S_CAMS,4,'</orthografic>') + self.writel(S_CAMS,4,'</orthographic>') self.writel(S_CAMS,3,'</technique_common>') self.writel(S_CAMS,2,'</optics>') @@ -1176,13 +1246,14 @@ class DaeExporter: def export_node(self,node,il): if (not node in self.valid_nodes): return + prev_node = bpy.context.scene.objects.active bpy.context.scene.objects.active = node self.writel(S_NODES,il,'<node id="'+self.validate_id(node.name)+'" name="'+node.name+'" type="NODE">') il+=1 self.writel(S_NODES,il,'<matrix sid="transform">'+strmtx(node.matrix_local)+'</matrix>') - print("NODE TYPE: "+node.type+" NAME: "+node.name) + #print("NODE TYPE: "+node.type+" NAME: "+node.name) if (node.type=="MESH"): self.export_mesh_node(node,il) elif (node.type=="CURVE"): @@ -1199,13 +1270,14 @@ class DaeExporter: il-=1 self.writel(S_NODES,il,'</node>') + bpy.context.scene.objects.active = prev_node #make previous node active again def is_node_valid(self,node): if (not node.type in self.config["object_types"]): return False if (self.config["use_active_layers"]): valid=False - print("NAME: "+node.name) + #print("NAME: "+node.name) for i in range(20): if (node.layers[i] and self.scene.layers[i]): valid=True @@ -1355,7 +1427,7 @@ class DaeExporter: # Change frames first, export objects last # This improves performance enormously - print("anim from: "+str(start)+" to "+str(end)+" allowed: "+str(allowed)) + #print("anim from: "+str(start)+" to "+str(end)+" allowed: "+str(allowed)) for t in range(start,end+1): self.scene.frame_set(t) key = t * frame_len - frame_sub @@ -1409,7 +1481,7 @@ class DaeExporter: bone_name=self.skeleton_info[node]["bone_ids"][bone] if (not (bone_name in xform_cache)): - print("has bone: "+bone_name) + #print("has bone: "+bone_name) xform_cache[bone_name]=[] posebone = node.pose.bones[bone.name] @@ -1441,12 +1513,13 @@ class DaeExporter: return tcn def export_animations(self): - tmp_mat = [] # workaround by ndee - for s in self.skeletons: # workaround by ndee - tmp_bone_mat = [] # workaround by ndee - for bone in s.pose.bones: # workaround by ndee - tmp_bone_mat.append(Matrix(bone.matrix_basis)) # workaround by ndee - tmp_mat.append([Matrix(s.matrix_local),tmp_bone_mat]) # workaround by ndee -> stores skeleton and bone transformations + tmp_mat = [] + for s in self.skeletons: + tmp_bone_mat = [] + for bone in s.pose.bones: + tmp_bone_mat.append(Matrix(bone.matrix_basis)) + bone.matrix_basis = Matrix() + tmp_mat.append([Matrix(s.matrix_local),tmp_bone_mat]) self.writel(S_ANIM,0,'<library_animations>') @@ -1481,7 +1554,7 @@ class DaeExporter: bones.append(dp) allowed_skeletons=[] - for i,y in enumerate(self.skeletons): # workaround by ndee + for i,y in enumerate(self.skeletons): if (y.animation_data): for z in y.pose.bones: if (z.bone.name in bones): @@ -1489,37 +1562,42 @@ class DaeExporter: allowed_skeletons.append(y) y.animation_data.action=x; - y.matrix_local = tmp_mat[i][0] # workaround by ndee -> resets the skeleton transformation. - for j,bone in enumerate(s.pose.bones): # workaround by ndee - bone.matrix_basis = Matrix() # workaround by ndee -> resets the bone transformations. Important if bones in follwing actions miss keyframes + y.matrix_local = tmp_mat[i][0] + for j,bone in enumerate(s.pose.bones): + bone.matrix_basis = Matrix() - print("allowed skeletons "+str(allowed_skeletons)) + #print("allowed skeletons "+str(allowed_skeletons)) - print(str(x)) + #print(str(x)) tcn = self.export_animation(int(x.frame_range[0]),int(x.frame_range[1]+0.5),allowed_skeletons) framelen=(1.0/self.scene.render.fps) start = x.frame_range[0]*framelen end = x.frame_range[1]*framelen - print("Export anim: "+x.name) + #print("Export anim: "+x.name) self.writel(S_ANIM_CLIPS,1,'<animation_clip name="'+x.name+'" start="'+str(start)+'" end="'+str(end)+'">') for z in tcn: self.writel(S_ANIM_CLIPS,2,'<instance_animation url="#'+z+'"/>') self.writel(S_ANIM_CLIPS,1,'</animation_clip>') + if (len(tcn)==0): + self.operator.report({'WARNING'},'Animation clip "'+x.name+'" contains no tracks.') + self.writel(S_ANIM_CLIPS,0,'</library_animation_clips>') - for i,s in enumerate(self.skeletons): # workaround by ndee + + for i,s in enumerate(self.skeletons): if (s.animation_data==None): continue if s in cached_actions: s.animation_data.action = bpy.data.actions[cached_actions[s]] else: s.animation_data.action = None - for j,bone in enumerate(s.pose.bones): # workaround by ndee - bone.matrix_basis = tmp_mat[i][1][j] # workaround by ndee -> resets the bone transformation to what they were before exporting. + for j,bone in enumerate(s.pose.bones): + bone.matrix_basis = tmp_mat[i][1][j] + else: self.export_animation(self.scene.frame_start,self.scene.frame_end) @@ -1590,7 +1668,8 @@ class DaeExporter: f.write(bytes('</COLLADA>\n',"UTF-8")) return True - def __init__(self,path,kwargs): + def __init__(self,path,kwargs,operator): + self.operator=operator self.scene=bpy.context.scene self.last_id=0 self.scene_name=self.new_id("scene") @@ -1604,6 +1683,10 @@ class DaeExporter: self.config=kwargs self.valid_nodes=[] self.armature_for_morph={} + self.used_bones=[] + self.wrongvtx_report=False + + @@ -1615,9 +1698,11 @@ def save(operator, context, **kwargs ): - exp = DaeExporter(filepath,kwargs) + exp = DaeExporter(filepath,kwargs,operator) exp.export() + + return {'FINISHED'} # so the script wont run after we have batch exported. diff --git a/tools/freetype/SCsub b/tools/freetype/SCsub index a31b8c4602..65b4827f9c 100644 --- a/tools/freetype/SCsub +++ b/tools/freetype/SCsub @@ -65,7 +65,5 @@ if (env["freetype"]=="builtin"): # lib = env.Library("freetype_builtin",ft_sources) # env.Prepend(LIBS=[lib]) - -Export('env') - +Export('env') diff --git a/tools/html_fs/godot.html b/tools/html_fs/godot.html new file mode 100644 index 0000000000..36761deb90 --- /dev/null +++ b/tools/html_fs/godot.html @@ -0,0 +1,1317 @@ +<!doctype html> +<html lang="en-us"> + <head> + <meta charset="utf-8"> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <title>Emscripten-Generated Code</title> + <style> + body { + font-family: arial; + margin: 0; + padding: none; + } + + .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; } + div.emscripten { text-align: center; } + div.emscripten_border { border: 1px solid black; } + /* the canvas *must not* have any border or padding, or mouse coords will be wrong */ + canvas.emscripten { border: 0px none; } + + #emscripten_logo { + display: inline-block; + margin: 0; + } + + .spinner { + height: 30px; + width: 30px; + margin: 0; + margin-top: 20px; + margin-left: 20px; + display: inline-block; + vertical-align: top; + + -webkit-animation: rotation .8s linear infinite; + -moz-animation: rotation .8s linear infinite; + -o-animation: rotation .8s linear infinite; + animation: rotation 0.8s linear infinite; + + border-left: 5px solid rgb(235, 235, 235); + border-right: 5px solid rgb(235, 235, 235); + border-bottom: 5px solid rgb(235, 235, 235); + border-top: 5px solid rgb(120, 120, 120); + + border-radius: 100%; + background-color: rgb(189, 215, 46); + } + + @-webkit-keyframes rotation { + from {-webkit-transform: rotate(0deg);} + to {-webkit-transform: rotate(360deg);} + } + @-moz-keyframes rotation { + from {-moz-transform: rotate(0deg);} + to {-moz-transform: rotate(360deg);} + } + @-o-keyframes rotation { + from {-o-transform: rotate(0deg);} + to {-o-transform: rotate(360deg);} + } + @keyframes rotation { + from {transform: rotate(0deg);} + to {transform: rotate(360deg);} + } + + #status { + display: inline-block; + vertical-align: top; + margin-top: 30px; + margin-left: 20px; + font-weight: bold; + color: rgb(120, 120, 120); + } + + #progress { + height: 20px; + width: 30px; + } + + #controls { + display: inline-block; + float: right; + vertical-align: top; + margin-top: 30px; + margin-right: 20px; + } + + #output { + width: 100%; + height: 200px; + margin: 0 auto; + margin-top: 10px; + display: block; + background-color: black; + color: white; + font-family: 'Lucida Console', Monaco, monospace; + outline: none; + } + </style> + </head> + <body> + <a href="http://emscripten.org"> + <?xml version="1.0" encoding="UTF-8" standalone="no"?><svg + version="1.1" + id="Layer_1" + x="0px" + y="0px" + width="296px" + height="78px" + viewBox="420 120 100 170" + enable-background="new 0 0 900 400" + xml:space="preserve" + inkscape:version="0.48.4 r9939" + sodipodi:docname="emscripten_powered_by_logo.svg"><metadata + id="metadata345"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs343"><linearGradient + y2="247.6265" + x2="225.1929" + y1="152.499" + x1="225.1929" + gradientUnits="userSpaceOnUse" + id="linearGradient5104"><stop + id="stop5106" + style="stop-color:#C1D72F" + offset="0.3227531" /><stop + id="stop5108" + style="stop-color:#BCD631" + offset="0.45119295" /><stop + id="stop5110" + style="stop-color:#AFD136" + offset="0.64491969" /><stop + id="stop5112" + style="stop-color:#ABD037" + offset="1" /><a:midPointStop + style="stop-color:#C1D72F" + offset="0.0123" /><a:midPointStop + style="stop-color:#C1D72F" + offset="0.3086" /><a:midPointStop + style="stop-color:#ABD037" + offset="1" /></linearGradient><linearGradient + inkscape:collect="always" + xlink:href="#SVGID_2_" + id="linearGradient5120" + x1="397.56918" + y1="128.12726" + x2="397.56918" + y2="166.25996" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.103059,0,0,1.103059,-38.997823,3.1312145)" /><filter + inkscape:collect="always" + id="filter5126"><feGaussianBlur + inkscape:collect="always" + stdDeviation="0.56377237" + id="feGaussianBlur5128" /></filter><linearGradient + inkscape:collect="always" + xlink:href="#SVGID_2_" + id="linearGradient5134" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.103059,0,0,1.103059,-38.997823,3.1312145)" + x1="397.56918" + y1="128.12726" + x2="397.56918" + y2="166.25996" /></defs><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1440" + inkscape:window-height="838" + id="namedview341" + showgrid="false" + inkscape:zoom="0.63555556" + inkscape:cx="224.82424" + inkscape:cy="-52.085109" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" + inkscape:current-layer="Layer_1" /><g + id="g5130" + transform="matrix(0.91591318,0,0,0.91591318,28.176953,14.143571)"><path + transform="matrix(1.103059,0,0,1.103059,-35.073492,-16.03923)" + id="path5122" + style="fill:#383838;fill-opacity:0.34705882;stroke:none;filter:url(#filter5126)" + d="m 494.39333,173.6323 c 0.57407,0.28703 1.87073,1.00226 2.89426,1.02855 0.55732,0.0143 1.14006,-0.1672 1.60262,-0.4784 1.20466,-0.81046 2.23561,-2.03031 2.72683,-3.39661 0.19424,-0.54027 0.0238,-1.72222 0.0238,-1.72222 l -3.82713,-14.06478 -1.98533,0 0.50231,-2.67891 6.36261,0 2.55939,12.22285 4.78392,-9.68746 -2.00924,0 0,-2.65498 7.19979,0 -11.00301,22.38875 -1.69829,1.91358 -2.29628,1.3395 -2.46371,0.26312 -2.29628,-0.21528 -2.79859,-1.36342 z m -12.0637,-14.56445 c -0.93698,1.88565 -1.70261,4.35262 -0.81842,6.26333 0.36549,0.78976 1.35098,1.19428 2.192,1.41737 0.60934,0.16133 1.29167,0.0999 1.88775,-0.10468 0.48126,-0.1655 0.8829,-0.5224 1.255,-0.8697 0.40341,-0.3768 0.77723,-0.80461 1.03505,-1.29262 0.21864,-0.41395 0.40236,-0.84786 0.49325,-1.30698 0.20667,-1.0485 0.35879,-2.1079 0.33583,-3.17631 -0.0184,-0.87403 -0.0789,-1.87107 -0.47711,-2.64959 -0.26344,-0.51379 -0.77017,-0.71849 -1.33113,-0.85633 -0.42395,-0.10479 -0.81432,-0.0626 -1.21773,0.10517 -0.65479,0.27273 -1.2544,0.5311 -1.82112,0.95764 -0.57331,0.4317 -1.21403,0.86959 -1.53337,1.5127 z m 0.65588,-4.31208 c 0,0 2.19341,-1.80738 3.45549,-2.27082 0.71718,-0.26365 3.45363,-0.65258 4.15,-0.3378 1.47292,0.66633 2.26103,1.57529 2.7222,2.60001 0.46118,1.02472 0.69944,2.59956 0.79701,3.73627 0.13278,1.55027 -0.13682,3.77629 -0.53404,5.74843 -0.30079,1.49256 -1.01883,2.74423 -1.83478,3.92156 -1.06526,1.5373 -1.82382,2.15116 -3.66756,2.46594 -0.98864,0.16889 -1.93845,0.46787 -3.25466,0.0928 -1.4384,-0.40963 -2.35273,-0.81244 -3.39599,-1.63337 -0.72524,-0.57054 -1.16043,-1.54043 -1.16043,-1.54043 l 0,2.82636 -4.8903,0 3.39872,-23.01602 -1.92242,-0.85888 0.0403,-2.38127 7.25847,0.0534 z m -23.77803,2.20447 c 0.29175,1.49273 0.0813,4.83252 -0.86111,6.69751 -0.3062,0.60617 -0.94813,1.32967 -1.55479,1.6983 -1.01515,0.61713 -2.21688,1.21322 -3.3966,1.07639 -0.47944,-0.0541 -0.97036,-0.34348 -1.24383,-0.74151 -0.47686,-0.69328 -0.43621,-1.55032 -0.45448,-2.39198 -0.024,-1.06873 0.13137,-2.23775 0.38272,-3.277 0.18705,-0.7744 0.4229,-1.58254 0.86111,-2.24844 0.39037,-0.59323 0.92628,-1.12617 1.55478,-1.45909 0.54854,-0.29014 1.19695,-0.38467 1.81791,-0.40664 0.63637,-0.0231 1.3031,0.0385 1.88966,0.28704 0.3875,0.16453 0.92361,0.3524 1.00463,0.76542 z m 1.29312,-9.69052 -0.64254,6.12262 c 0,0 -1.68393,-0.96858 -2.605,-1.25148 -0.73032,-0.22434 -1.50312,-0.36654 -2.26624,-0.33838 -0.97069,0.0345 -1.91182,0.22099 -2.81751,0.57088 -0.9185,0.35497 -1.78344,0.94565 -2.49338,1.62792 -0.88025,0.84538 -1.51404,1.90455 -2.02977,3.0106 -0.39653,0.84993 -0.69517,1.75284 -0.87975,2.67232 -0.22875,1.14241 -0.44415,2.38719 -0.43937,3.55197 0.01,1.44865 0.0623,2.89489 0.54092,4.26214 0.25525,0.72907 0.71643,1.40578 1.28572,1.9283 0.56835,0.52207 1.29566,0.87604 2.02935,1.11621 0.41072,0.13491 0.85346,0.17274 1.28579,0.16935 1.00285,-0.01 2.03715,-0.0883 2.97671,-0.43999 0.66497,-0.2489 1.21759,-0.73399 1.79298,-1.1502 0.75304,-0.54475 2.16476,-1.86006 2.16476,-1.86006 l 0,1.62374 -0.5751,0 0,1.48807 6.86709,0 0,-2.84135 -1.92841,0 3.21374,-23.57782 -7.37422,0 0,2.33412 z m -93.60062,7.55781 2.33363,15.57933 6.23084,0 4.04243,-11.34169 1.62654,11.34169 5.88425,0 7.05633,-16.38872 0,-2.0141 -6.1713,0 0,2.82349 1.88966,0 -4.04243,10.16973 -0.74151,0 -1.29167,-12.55773 -5.38194,0 -4.7361,12.50989 -1.55478,-12.94538 -6.86496,0 0,2.82349 z m -12.15,0.72146 c -0.56264,0.0892 -1.03524,0.17358 -1.53086,0.45447 -0.737,0.41808 -1.46132,0.95771 -1.91357,1.67437 -0.44123,0.70048 -0.53204,1.57581 -0.66975,2.39196 -0.1751,1.04003 -0.20064,2.10306 -0.19136,3.15741 0.01,0.81614 -0.0138,1.66577 0.35879,2.39197 0.1904,0.37315 0.52874,0.80945 0.88503,1.02855 0.56015,0.34453 1.06632,0.55494 1.72222,0.598 0.72597,0.0483 1.48801,-0.18852 2.10493,-0.57408 0.59422,-0.37072 1.03334,-0.97401 1.38735,-1.5787 0.46117,-0.78744 0.70905,-1.69257 0.90895,-2.58334 0.20377,-0.90704 0.33579,-1.84565 0.28703,-2.77468 -0.0491,-0.92714 -0.18211,-1.88434 -0.57407,-2.72684 -0.2728,-0.58681 -0.70954,-1.00753 -1.29166,-1.29165 -0.44403,-0.21628 -0.99455,-0.24402 -1.48303,-0.16744 z m -6.62442,-0.73581 c 0.65404,-0.6664 1.4072,-1.25479 2.23273,-1.69161 1.0305,-0.54505 2.16429,-0.92749 3.31518,-1.11604 1.51307,-0.24806 3.09342,-0.2847 4.60036,0 0.88055,0.16632 1.78322,0.44742 2.50307,0.98113 0.77409,0.57312 1.35279,1.40936 1.79291,2.26639 0.42901,0.83457 0.6828,1.77223 0.77798,2.70605 0.16564,1.61985 0.024,3.29135 -0.37201,4.87103 -0.33328,1.33759 -0.88436,2.64754 -1.65745,3.78889 -0.67549,0.99679 -1.52894,1.91262 -2.53721,2.5709 -0.89957,0.58746 -1.9718,0.87641 -3.01035,1.15006 -0.87153,0.22963 -1.77166,0.4095 -2.67235,0.40576 -1.21068,-0.01 -2.47998,-0.0817 -3.58589,-0.57511 -1.09854,-0.48896 -1.89728,-1.32739 -2.60455,-2.30013 -0.61123,-0.83995 -1.02561,-1.59975 -1.31932,-2.87516 -0.2125,-0.9233 -0.40006,-2.19912 -0.37215,-3.14592 0.0335,-1.16537 0.3568,-2.74121 0.83416,-3.80434 0.52547,-1.17098 1.17609,-2.3161 2.07489,-3.2319 z m 94.95184,13.82318 c -2.20516,1.01761 -4.61429,1.69636 -7.02343,1.69636 -5.32726,0 -7.22678,-3.12145 -7.22678,-7.22678 0,-7.1251 4.54685,-11.19645 10.0772,-11.19645 3.7324,0 5.56453,1.69625 5.56453,4.47856 0,4.85189 -5.12329,6.27735 -10.41633,6.82001 0.10168,1.73076 0.81446,3.32485 3.3592,3.32485 1.2218,0 2.88401,-0.37315 4.91982,-1.22099 z m -3.22292,-11.77374 c 0,-0.81423 -0.57695,-1.28891 -1.62876,-1.28891 -1.89988,0 -3.46041,1.66212 -3.96978,4.34287 1.45897,-0.20368 5.59854,-0.91613 5.59854,-3.05396 z m -30.33408,11.77374 c -2.2054,1.01761 -4.61457,1.69636 -7.02371,1.69636 -5.32653,0 -7.22671,-3.12145 -7.22671,-7.22678 0,-7.1251 4.54679,-11.19645 10.07785,-11.19645 3.73175,0 5.56382,1.69625 5.56382,4.47856 0,4.85189 -5.12273,6.27735 -10.41568,6.82001 0.10142,1.73076 0.81422,3.32485 3.35884,3.32485 1.22158,0 2.8842,-0.37315 4.91994,-1.22099 z m -3.22305,-11.77374 c 0,-0.81423 -0.57638,-1.28891 -1.62883,-1.28891 -1.89959,0 -3.46023,1.66212 -3.96971,4.34287 1.4591,-0.20368 5.59854,-0.91613 5.59854,-3.05396 z m -82.36051,20.5268 -0.0679,-0.13571 0.98406,-5.66614 2.10303,-15.16698 c 0.0687,-0.40664 -0.0332,-0.61046 -0.30522,-0.71214 l -1.66259,-0.61111 0.37379,-2.57855 6.78556,0 -0.40663,2.71427 0.10142,0.0335 c 2.0016,-1.86631 4.10566,-3.08743 6.24306,-3.08743 2.91821,0 4.95366,1.86577 4.95366,6.78561 0,4.68241 -1.83206,11.6379 -8.14271,11.6379 -2.20534,0 -3.42694,-0.84825 -4.68256,-1.73039 l -0.74621,5.08917 c -0.0341,0.37361 0.0326,0.50898 0.47457,0.54273 l 3.42697,0.33969 -0.37385,2.5447 -9.0589,0 z m 6.78613,-12.04485 c 0.84787,0.71258 1.96788,1.32305 3.22348,1.32305 2.74798,0 3.76601,-3.86811 3.76601,-6.85368 0,-2.002 -0.47476,-3.32542 -1.76432,-3.32542 -1.35696,0 -3.08763,1.4591 -4.30913,2.54506 z m 81.08934,4.85147 0.33969,-2.54464 1.56064,-0.2038 c 0.47498,-0.0683 0.5429,-0.1695 0.61084,-0.67837 l 1.42466,-10.34864 c 0.0335,-0.37315 -0.0335,-0.61046 -0.33914,-0.71214 l -1.69691,-0.61111 0.37365,-2.57855 6.71797,0 -0.44097,3.05395 0.10191,0.0679 c 1.32326,-1.89982 3.22359,-3.46042 5.39485,-3.46042 0.7463,0 2.0359,0.13582 2.61295,0.30538 l -0.84863,6.17508 -3.96972,-0.13582 -0.10157,-1.76443 c -0.0335,-0.30537 -0.10223,-0.40701 -0.37391,-0.40701 -0.64452,0 -1.69636,0.78027 -2.64651,1.76455 l -1.18674,8.61817 c -0.0687,0.54303 -0.0334,0.64474 0.47477,0.67874 l 3.22351,0.27142 -0.37384,2.51081 -10.8575,0 z" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cssscccccccccccccccccssssssssccssscssssscsssccccccccsssssssssccsccsssssssssscsscccccccccccccccccccccccccccccccsssscsssssscscsssssssscsssssssssscsssscsccsscscsssscsccsscsccccccccccsssccccccccssscccccccccccccsccccsccccccc" /><path + sodipodi:nodetypes="cssscccccccccccccccccssssssssccssscssssscsssccccccccsssssssssccsccsssssssssscsscccccccccccccccccccccccccccccccsssscsssssscscsssssssscsssssssssscsssscsccsscscsssscsccsscsccccccccccsssccccccccssscccccccccccccsccccsccccccc" + inkscape:connector-curvature="0" + d="m 509.55935,174.26011 c 0.63327,0.31663 2.06355,1.10555 3.19256,1.13455 0.61476,0.0158 1.25757,-0.18443 1.76781,-0.5277 1.3288,-0.89397 2.46618,-2.23946 3.00784,-3.74661 0.21419,-0.59598 0.0258,-1.89972 0.0258,-1.89972 l -4.22153,-15.51428 -2.18993,0 0.55406,-2.95501 7.01835,0 2.82313,13.48255 5.27696,-10.68586 -2.21631,0 0,-2.92858 7.94179,0 -12.13698,24.69605 -1.87332,2.11078 -2.5329,1.4776 -2.71762,0.29022 -2.53295,-0.23748 -3.08699,-1.50392 z m -13.30698,-16.06545 c -1.0335,2.08005 -1.87803,4.80122 -0.90274,6.90883 0.4032,0.87116 1.49018,1.31738 2.4179,1.56347 0.67214,0.17793 1.42477,0.1102 2.08233,-0.11548 0.53084,-0.1826 0.97383,-0.5762 1.38432,-0.9593 0.44502,-0.4157 0.85733,-0.8875 1.14176,-1.42582 0.24113,-0.45665 0.44375,-0.93526 0.54404,-1.44168 0.22797,-1.1566 0.3958,-2.3252 0.37043,-3.50371 -0.0204,-0.96413 -0.0869,-2.06387 -0.52631,-2.92259 -0.29054,-0.56679 -0.84946,-0.79259 -1.46826,-0.94463 -0.46761,-0.11559 -0.89829,-0.0686 -1.34322,0.11597 -0.72226,0.30083 -1.38368,0.5859 -2.00879,1.05634 -0.63242,0.4762 -1.33915,0.9593 -1.69146,1.6686 z m 0.72346,-4.75648 c 0,0 2.41951,-1.99358 3.81169,-2.50482 0.79109,-0.29085 3.80953,-0.71977 4.57766,-0.3726 1.6247,0.73503 2.49408,1.73759 3.00274,2.86791 0.50868,1.13043 0.77154,2.86756 0.87911,4.12137 0.14648,1.71007 -0.15092,4.16549 -0.58904,6.34083 -0.33179,1.64636 -1.12383,3.02703 -2.02388,4.32576 -1.17506,1.6957 -2.01178,2.37286 -4.04556,2.72004 -1.09051,0.18629 -2.13814,0.51607 -3.59006,0.10268 -1.5866,-0.45183 -2.59522,-0.89615 -3.74599,-1.8017 -0.79994,-0.62933 -1.28003,-1.6992 -1.28003,-1.6992 l 0,3.11766 -5.39426,0 3.74898,-25.38802 -2.12052,-0.94738 0.0443,-2.62669 8.00657,0.0587 z m -26.22853,2.43167 c 0.32185,1.64663 0.0893,5.33062 -0.9498,7.38781 -0.33781,0.66857 -1.04588,1.46667 -1.7151,1.8733 -1.11975,0.68073 -2.44527,1.33822 -3.7466,1.18729 -0.52883,-0.0601 -1.07036,-0.37888 -1.37203,-0.81791 -0.52601,-0.76478 -0.48121,-1.71012 -0.50128,-2.63848 -0.0263,-1.17893 0.14487,-2.46835 0.42212,-3.6147 0.20635,-0.8543 0.4665,-1.74564 0.94981,-2.48024 0.43067,-0.65433 1.02178,-1.24217 1.71508,-1.60939 0.60504,-0.32004 1.32025,-0.42437 2.00521,-0.44854 0.70197,-0.0251 1.4374,0.0425 2.08446,0.31654 0.4274,0.18153 1.01882,0.3888 1.10813,0.84432 z m 1.42642,-10.68922 -0.70874,6.75362 c 0,0 -1.85753,-1.06838 -2.8735,-1.38048 -0.80562,-0.24744 -1.65802,-0.40424 -2.49984,-0.37318 -1.07069,0.0382 -2.10882,0.24369 -3.1078,0.62968 -1.01321,0.39157 -1.96724,1.04315 -2.75039,1.79572 -0.97095,0.93248 -1.67003,2.10085 -2.23897,3.3208 -0.43738,0.93753 -0.76677,1.93354 -0.9704,2.94777 -0.2523,1.26016 -0.4899,2.63324 -0.48461,3.91802 0.011,1.59795 0.0683,3.19329 0.59661,4.70144 0.28155,0.80417 0.79028,1.55058 1.41822,2.127 0.62695,0.57587 1.4292,0.96634 2.23856,1.23121 0.45301,0.14881 0.94135,0.19054 1.41828,0.18685 1.10615,-0.011 2.24705,-0.0973 3.28346,-0.48539 0.73352,-0.2745 1.34304,-0.80959 1.97773,-1.2687 0.83064,-0.60085 2.38786,-2.05176 2.38786,-2.05176 l 0,1.79104 -0.63429,0 0,1.64147 7.57478,0 0,-3.13415 -2.12721,0 3.54494,-26.00772 -8.13411,0 0,2.57462 z m -103.24702,8.33671 2.57413,17.18493 6.87304,0 4.45903,-12.51049 1.79414,12.51049 6.49065,0 7.78353,-18.07772 0,-2.2217 -6.8073,0 0,3.11449 2.08446,0 -4.45903,11.21783 -0.8179,0 -1.42488,-13.85193 -5.93654,0 -5.2242,13.79919 -1.71497,-14.27958 -7.57246,0 0,3.11449 z m -13.4021,0.79586 c -0.62064,0.0982 -1.14194,0.19148 -1.68866,0.50127 -0.813,0.46118 -1.61192,1.05641 -2.11077,1.84697 -0.48673,0.77268 -0.58683,1.73821 -0.73875,2.63846 -0.1932,1.14723 -0.22134,2.31976 -0.21116,3.48281 0.011,0.90024 -0.0148,1.83747 0.39579,2.63847 0.21,0.41165 0.58324,0.89285 0.97623,1.13455 0.61796,0.38003 1.17622,0.61214 1.89972,0.6596 0.80077,0.0533 1.64141,-0.20792 2.32189,-0.63318 0.65546,-0.40892 1.13978,-1.07441 1.53029,-1.7414 0.50878,-0.86864 0.78215,-1.86707 1.00265,-2.84964 0.22477,-1.00044 0.37039,-2.03585 0.31663,-3.06058 -0.0541,-1.02274 -0.20091,-2.07854 -0.63327,-3.00784 -0.3009,-0.64731 -0.78264,-1.11143 -1.42476,-1.42485 -0.48983,-0.23858 -1.09705,-0.26912 -1.63583,-0.18464 z m -7.30711,-0.81171 c 0.72143,-0.735 1.55219,-1.38409 2.46282,-1.86591 1.1367,-0.60125 2.38729,-1.02309 3.65678,-1.23104 1.66908,-0.27366 3.41222,-0.314 5.07446,0 0.97135,0.18342 1.96702,0.49352 2.76107,1.08223 0.85389,0.63222 1.49219,1.55466 1.97771,2.49999 0.47321,0.92057 0.7531,1.95483 0.85808,2.98495 0.18274,1.78675 0.0263,3.63055 -0.41031,5.37303 -0.36757,1.47539 -0.97545,2.92034 -1.82825,4.17929 -0.74509,1.09959 -1.68654,2.10982 -2.79871,2.8359 -0.99227,0.64796 -2.175,0.96671 -3.32055,1.26856 -0.96139,0.25333 -1.95426,0.4517 -2.94774,0.44756 -1.33549,-0.011 -2.73559,-0.0897 -3.9555,-0.63431 -1.21174,-0.53936 -2.09278,-1.46419 -2.87295,-2.53723 -0.67423,-0.92645 -1.13131,-1.76457 -1.45532,-3.17146 -0.2344,-1.0184 -0.44126,-2.42572 -0.41044,-3.47012 0.0365,-1.28547 0.39349,-3.02371 0.92005,-4.19644 0.57967,-1.29168 1.29729,-2.5548 2.2888,-3.565 z m 104.73744,15.24778 c -2.43247,1.12251 -5.0899,1.87126 -7.74734,1.87126 -5.87626,0 -7.97147,-3.44315 -7.97147,-7.97158 0,-7.8594 5.0154,-12.35035 11.11569,-12.35035 4.11711,0 6.13803,1.87105 6.13803,4.94016 0,5.35189 -5.65129,6.92425 -11.48983,7.52281 0.11219,1.90916 0.89836,3.66755 3.7054,3.66755 1.3477,0 3.18121,-0.41165 5.42682,-1.34689 z m -3.55513,-12.98704 c 0,-0.89823 -0.63635,-1.42181 -1.79655,-1.42181 -2.09568,0 -3.81712,1.83342 -4.37899,4.79047 1.60937,-0.22468 6.17554,-1.01053 6.17554,-3.36866 z m -33.46028,12.98704 c -2.4327,1.12251 -5.09006,1.87126 -7.74751,1.87126 -5.87553,0 -7.97151,-3.44315 -7.97151,-7.97158 0,-7.8594 5.01539,-12.35035 11.11645,-12.35035 4.11635,0 6.13722,1.87105 6.13722,4.94016 0,5.35189 -5.65062,6.92425 -11.48908,7.52281 0.11182,1.90916 0.89812,3.66755 3.70494,3.66755 1.34748,0 3.1815,-0.41165 5.42704,-1.34689 z m -3.55514,-12.98704 c 0,-0.89823 -0.63578,-1.42181 -1.79674,-1.42181 -2.09539,0 -3.81683,1.83342 -4.37881,4.79047 1.60951,-0.22468 6.17555,-1.01053 6.17555,-3.36866 z m -90.84852,22.6422 -0.0749,-0.14971 1.08546,-6.25004 2.31984,-16.73008 c 0.0757,-0.44854 -0.0367,-0.67336 -0.33673,-0.78554 l -1.83388,-0.67411 0.41228,-2.84425 7.48486,0 -0.44853,2.99397 0.11182,0.0371 c 2.2079,-2.05871 4.52887,-3.40563 6.88646,-3.40563 3.21901,0 5.46427,2.05807 5.46427,7.48491 0,5.16501 -2.02094,12.8373 -8.98192,12.8373 -2.43264,0 -3.78014,-0.93565 -5.16516,-1.90869 l -0.82311,5.61357 c -0.0376,0.41212 0.0356,0.56148 0.52347,0.59873 l 3.78017,0.37469 -0.41234,2.8069 -9.9925,0 z m 7.48553,-13.28615 c 0.93528,0.78598 2.17068,1.45946 3.55568,1.45946 3.03118,0 4.15411,-4.26682 4.15411,-7.56009 0,-2.2083 -0.52366,-3.66812 -1.94612,-3.66812 -1.49686,0 -3.40583,1.6095 -4.75323,2.80736 z m 89.44624,5.35147 0.37469,-2.80694 1.72154,-0.2248 c 0.52388,-0.0753 0.5988,-0.1869 0.67374,-0.74827 l 1.57152,-11.41514 c 0.0365,-0.41155 -0.0368,-0.67336 -0.3741,-0.78554 l -1.87181,-0.67411 0.41215,-2.84425 7.41037,0 -0.48647,3.36865 0.11241,0.0749 c 1.45966,-2.09562 3.55581,-3.81702 5.95085,-3.81702 0.8232,0 2.2457,0.14982 2.88225,0.33688 l -0.93613,6.81148 -4.37882,-0.14982 -0.11196,-1.94633 c -0.0371,-0.33677 -0.11284,-0.44891 -0.41252,-0.44891 -0.71092,0 -1.87116,0.86067 -2.91921,1.94635 l -1.30904,9.50637 c -0.0757,0.59903 -0.0368,0.71124 0.52367,0.74874 l 3.55571,0.29932 -0.41234,2.76961 -11.9765,0 z" + style="fill:url(#linearGradient5134);fill-opacity:1;stroke:none" + id="path5080" /></g><path + fill="#E2E2E2" + d="M256.023,135.437H196.36c-16.432,0-29.8,13.368-29.8,29.8v73.527c0,16.432,13.368,29.8,29.8,29.8h59.663 c16.433,0,29.801-13.368,29.801-29.8v-73.527C285.824,148.805,272.456,135.437,256.023,135.437z M191.561,165.236 c0-2.646,2.153-4.8,4.8-4.8h59.663c2.647,0,4.801,2.153,4.801,4.8v73.527c0,2.646-2.153,4.8-4.801,4.8H196.36 c-2.646,0-4.8-2.153-4.8-4.8V165.236z" + id="path3" /><path + d="m 531.664,250.155 h 18.498 l -2.809,18.064 h 5.59 37.586 l 2.6,-17.718 c 4.98,-1.091 9.133,-3.455 12.512,-6.693 3.084,4.075 8.566,7.37 18.252,7.37 6.338,0 12.775,-1.807 17.174,-3.687 4.254,2.399 9.463,3.687 15.459,3.687 3.088,0 6.236,-0.355 9.426,-1.023 h 67.135 l 3.354,-24.827 -5.445,-0.764 1.879,-13.356 c 0.371,-2.386 0.449,-4.66 0.449,-6.156 l -0.008,-0.375 c -0.457,-12.191 -8.139,-19.765 -20.045,-19.765 -2.404,0 -4.623,0.314 -6.676,0.852 h -34.189 l -0.035,0.244 c -2.527,-0.701 -5.41,-1.096 -8.686,-1.096 -3.801,0 -7.406,0.555 -10.76,1.598 l 0.105,-0.746 h -12.467 l 1.826,-12.951 H 615.08 l -1.846,7.658 c -1.373,5.704 -2.213,5.793 -4.453,6.03 l -4.508,0.477 c -3.049,-1.424 -6.357,-2.065 -9.602,-2.065 -2.135,0 -4.275,0.284 -6.416,0.852 h -19.291 c 0.502,-1.772 0.775,-3.674 0.775,-5.678 0,-9.601 -6.846,-16.305 -16.646,-16.305 -11.055,0 -18.775,7.721 -18.775,18.776 0,0.951 0.082,1.869 0.219,2.764 -2.135,-0.288 -4.277,-0.409 -5.553,-0.409 -2.053,0 -4.072,0.288 -6.045,0.852 h -31.342 c -2.74,-0.553 -5.641,-0.852 -8.537,-0.852 -7.138,0 -13.492,1.674 -18.808,4.723 l -3.451,-1.461 c -3.711,-1.571 -11.232,-3.262 -18.979,-3.262 -8.933,0 -16.383,2.56 -21.576,7.016 -3.265,-4.473 -8.523,-7.016 -15.228,-7.016 -4.822,0 -9.021,1.477 -12.572,3.44 -2.996,-2.204 -6.796,-3.44 -11.115,-3.44 -2.327,0 -4.48,0.315 -6.476,0.852 h -33.963 l -0.035,0.245 c -2.526,-0.702 -5.41,-1.097 -8.687,-1.097 -20.458,0 -35.307,16.031 -35.307,38.117 0,17.363 10.785,28.149 28.148,28.149 3.087,0 6.236,-0.356 9.426,-1.023 h 88.816 c 3.706,0.676 7.669,1.023 11.154,1.023 8.907,0 16.278,-2.375 21.51,-6.593 4.872,4.252 11.585,6.593 19.728,6.593 3.053,0 6.206,-0.368 9.286,-1.023 h 44.664 2.069 z" + id="path5" + inkscape:connector-curvature="0" + style="fill:#e2e2e2" /><path + fill="#F5F5F5" + d="M255.023,133.437H195.36c-16.432,0-29.8,13.368-29.8,29.8v73.527c0,16.432,13.368,29.8,29.8,29.8h59.663 c16.433,0,29.801-13.368,29.801-29.8v-73.527C284.824,146.805,271.456,133.437,255.023,133.437z M190.561,163.236 c0-2.646,2.153-4.8,4.8-4.8h59.663c2.647,0,4.801,2.153,4.801,4.8v73.527c0,2.646-2.153,4.8-4.801,4.8H195.36 c-2.646,0-4.8-2.153-4.8-4.8V163.236z" + id="path7" /><g + id="g9"><g + id="g11"><path + fill="#FBFDF8" + d="M195.361,251.626c-8.161,0-14.8-6.64-14.8-14.8v-73.527c0-8.161,6.639-14.8,14.8-14.8h59.663 c8.161,0,14.8,6.639,14.8,14.8v73.527c0,8.16-6.639,14.8-14.8,14.8H195.361z" + id="path13" /><path + fill="#F0F4E1" + d="M255.024,152.499c5.964,0,10.8,4.835,10.8,10.8v73.527c0,5.965-4.835,10.8-10.8,10.8h-59.663 c-5.964,0-10.8-4.835-10.8-10.8v-73.527c0-5.964,4.835-10.8,10.8-10.8H255.024 M255.024,144.499h-59.663 c-10.366,0-18.8,8.434-18.8,18.8v73.527c0,10.366,8.434,18.8,18.8,18.8h59.663c10.366,0,18.8-8.434,18.8-18.8v-73.527 C273.824,152.933,265.391,144.499,255.024,144.499L255.024,144.499z" + id="path15" /></g><defs + id="defs17"><filter + id="Adobe_OpacityMaskFilter" + filterUnits="userSpaceOnUse" + x="176.562" + y="144.499" + width="97.263" + height="111.127"><feColorMatrix + type="matrix" + values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" + color-interpolation-filters="sRGB" + result="source" + id="feColorMatrix20" /></filter></defs><mask + maskUnits="userSpaceOnUse" + x="176.562" + y="144.499" + width="97.263" + height="111.127" + id="SVGID_1_"><g + filter="url(#Adobe_OpacityMaskFilter)" + id="g23"><image + overflow="visible" + width="422" + height="480" + xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAgEBLAEsAAD/7AARRHVja3kAAQAEAAAAHgAA/+4AIUFkb2JlAGTAAAAAAQMA EAMCAwYAAAg2AAAQ4QAAF1b/2wCEABALCwsMCxAMDBAXDw0PFxsUEBAUGx8XFxcXFx8eFxoaGhoX Hh4jJSclIx4vLzMzLy9AQEBAQEBAQEBAQEBAQEABEQ8PERMRFRISFRQRFBEUGhQWFhQaJhoaHBoa JjAjHh4eHiMwKy4nJycuKzU1MDA1NUBAP0BAQEBAQEBAQEBAQP/CABEIAeMBqQMBIgACEQEDEQH/ xACjAAEAAgMBAQAAAAAAAAAAAAAABQYBAwQHAgEBAQAAAAAAAAAAAAAAAAAAAAEQAAEDAQQKAwAC AwEAAAAAAAABAwQCMRMUBRBQEjMVJQYWNgcgESEwI5AiMkARAAEBAwsEAQIFAwUBAAAAAAABMQID EFAycqOz0wQ0RaURIXGRIEFRMGEiExRAgRKh0SMzQxUSAQAAAAAAAAAAAAAAAAAAAJD/2gAMAwEA AhEDEQAAANUJsrZYFfFgV8WBXxYEL0ki5fo6GjJuaRuaRuaRuaRuaRuaRuaRuaRuaRuaRuaRuaRu aRuaMHQ5dR3ojnJ9XxYFfFgV8WD0jxf2AodbslbAD6mDhlpLvI/qkuiovZL7CGzNfRCJwQacEGnB Bp0QSdEEnRBJ0QSdEEnRBJ3BBpwQacEHidwQXzPfBA6bBqK5w2nlKVH3iJitt+gAeweP+wFDrdkr Y+vmaN02k6+e3d2Gjo6N0c2zoyaM7xozuGluGluGluGluGluGluGluGluGluGluGluGnG8c/z1YO PVIfJF80xoIGPsfBVVrl6hIrD7+B7B4/7AUOt2StnXaYyxHTJ6ZKvrqb4x9MgAAAAAAAAAAAAAAA DGR8692Dh4pbkIKJscTVNiLdVY1+weP+wFDgJ+JLJORs3XbIc3dGz6ZAAAAAAAAAAAAAAAAAAAPn R0ayMi5uLqv1S51eIT2Dx/2AofB38Ra5uIm6kOzm6o+gAAAAAAAAAAAAAAAAAAAPj7+TkjJWNIOt 2et1WfYPH/YIofH2cZcJyEnKkenn6IyAAAAAAAAAAAAAAAAAAABjODmjZONIWt2WtVWPYPH/AGCK Hx9nIXGcg5ypLfo3xkAAAAAAAAAAAAAAAAAAADGcHPGyUaQ1astaqseweP8AsEUPk6+QuM7BTtSW 7TujIAAAAAAAAAAAAAAAAAAAGM4OeOkY4hqzZqzVY9g8f9gih8nXyFxnYKdqS3ad0ZAAAAAAAAAA AAAAAAAAAAxnBzx0jHENWbNWarHsHj/sEUPk6+QuM7BTtSW7TujIAAAAAAAAAAAAAAAAAAAGM4Oe OkY4hqzZqzVY9g8f9gih8nXyFxnYKdqS3ad0ZAAAAAAAAAAAAAAAAAAAAxnBzx0jHENWbNWarHsH j/sEUPk6+QuM7BTtSW7TujIAAAAAAAAAAAAAAAAAAAGM4OeOkY4hqzZqzVY9g8f9gih8nXyFxnYK dqS3ad0ZAAAAAAAAAAAAAAAAAAAAxnBzx0jHENWbNWarHsHj/sEUPk6+QuM7BTtSW7TujIAAAAAA AAAAAAAAAAAAAGM4OeOkY4hqzZqzVY9g8f8AYIofJ18hcZ2Cnakt2ndGQAAAAAAAAAAAAAAAAAAA MZwc8dIxxDVmzVmqx7B4/wCwRQ+Tr5C4zsFO1JbtO6MgAAAAAAAAAAAAAAAAAAAYzg546RjiGrNm rNVj2Dx/2CKHydfIXGdgp2pLdp3RkAAAAAAAAAAAAAAAAAAADGcHPHSMcQ1Zs1ZqseweP+wRQ+Tr 5C4zsFO1JbtO6MgAAAAAAAAAAAAAAAAAAAYzg546RjiGrNmrNVj2Dx/2CKHydfIXGdgp2pLdp3Rk AAAAAAAAAAAAAAAAAAADGcHPHSMcQ1Zs1ZqseweP+wRQ+Tr5C4zsFO1JbtO6MgAAAAAAAAAAAAAA AAAAAYzg546RjiGrNmrNVj2Dx/2CKHydfIXGdgp2pLdp3RkAAAAAAAAAAAAAAAAAAADGcHPHSMcQ 1Zs1ZqseweP+wRQ+Tr4y5TkHOVJb9G+MgAAAAAAAAAAAAAAAAAAAYzg542SjSGrVlrVVj2Dx/wBg ih8fZxlxnIKcqT6ObpjIAAAAAAAAAAAAAAAAAAAGM4OeNkY0h61Za1VY9g8f9gih8Xbwlxm4GbqW 6uLrj7AAAAAAAAAAAAAAAAAAAA+fr5OaNkI0ia1Y61Vb9g8f9gihxknCl1m65N1OdsZ3x0ZxkAAA AAAAAAAAAAAAAAAAx8fek5ozui6jazYKsRPsHj/sEUOu2Ktlqn6XZ6scjBSRLbOPpjYxkAAAAAAA AAAAAAAAAAYfJjm+uM0xXVE1xVOZr0Y9g8f9gKHW7JWz7s1W6i9SdYlasXXB9pLbI7fHY5/s3NeT 7fGT6fI+nyPp8j6fI+nyPp8j6fI+nyPp8j6fI+nyPp8D7x8fJtxp1m7Tp5jbw/MfWIjbXTk5SHsH j/sBQ63ZK2AdthqO8vXbUZWrJ0V/oJ7ZB7Sa+ofJMIkS6IySyJRLIkSyJEsiRLIkSyJEsiRLIkSy JVLYiRLYicEr8xfwSemN0kjy8PIdkfxQp0xWEAPYPH/YCh1uyVsAAz08ome2si37qZkumaULspIu 2aRkuyki7KSLspIuyki7KSLspIuyki7KSLtilC6qSLtilC6fNNFu5qz8k7wcI+vkAAHsHj/sBWoQ AAAAAAAAAAAAAAAAAAAAAAAAAHpAf//aAAgBAgABBQD/ACi//9oACAEDAAEFAP8AKL//2gAIAQEA AQUA6w6rz/LM+776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvv qs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qz vvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++qzvvqs776rO++ qzvvqs776rMfLPYHlHyRFUbivuDeUv1FOSVKJkRwE4CcAOAHADgBwA4AcAOAHADgBwA4AcAOAHAD gBwA4AcAOAHADgBwA4AcAOAnARciFyRUK8ndQcgyGxaaqf4fYHlHwRFUjZe68RsqbpGoKIUQkKYY kISEYJDAmBMCYEwJgTAmBMCYEwJgTAmBMCYEwJgTAmBMCYEwJgTAmCQWELCKoSFcNByEhIyxusk5 VVQV0VUL8vYHlGltupyqDlaIMREQaijcUoilMUSKgkZDDIYZDDIYZDDIYZDDIYZDDIYZDDIYZDDI YZDDIYZDDIYZDDIYZDDIYZDDIYZDDIYZDDILGQWKVRSuKORR2KPRCZltDiSYrjFXx9geUaG26nKs vy9KEjxhmONRxuOUMFLAjIjJdF0XRdIXSF0hdF0XRdF0XRdF0XRdF0XRdF0XRdIXSF0hdIXRdCsi sisFTBXHHY49GH4xMhU10y4tTFfw9geUCJ9rlcL6SNHGGBlgbZKGilsShDZQ+kPr/wBX0fSGygtC CtoVNDjI6wPsElgzCGjlLrdTden2B5QZfGvnYbCIkdkYZGmihsSn61ItJXQOtD7JIZJTBm0X6+Hs DyhP1cpjbLcVojtDLY3QIn1qZU+yugebJDRKaJ7CVUvtq27o9geURaLx6C19JFbI7Y1QU0/WqFQd oH6CS2TG/wAzZrZd0ewPKMqo2n4VH5FoGKBunVTifj9JJpJdBnVH+mj2B5RkqfdcOki0jFJQn5qm pPx5CTSS6TOKf6tHsDyjI0/2hIRU/GUKbNU1DyfklCWhm6f06PYHlGRf9QkIqfjKCWapWx4kkszj daPYHlGQ2wrItjImqlseJJMM43Wj2B5RkNsEjWNarUeJJMM43Oj2B5RkNsEjWNarUeJJMM43Oj2B 5RkNsGyLY1qtR6ySTDONzo9geUZDbBsi2NarUesk2TDON1o9geUZDbBsjWNarUesk2TDON1o9geU ZDbBsjWNarUeskkwzjdaPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDb BsjWNarUeskkwzjdaPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDbBsj WNarUeskkwzjdaPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDbBsjWNa rUeskkwzjdaPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDbBsjWNarUe skkwzjdaPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDbBsjWNarUeskk wzjdaPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDbBsjWNarUeskkwzj daPYHlGQ2wbI1jWq1HrJJMM43Wj2B5RkNsGyNY1qtR6ySTDON1o9geUZDbBsjWNarUeskkwzjdaP YHlGQ2wbI1jWq1HrJNkwzjdaPYHlGQ2wbItjWq1HrJNkwzjdaPYHlGQ2wbItjWq1HrJJMM43Oj2B 5RkNsGyNY1qtR4kkwzjc6PYHlGQ2wSNY1qtR4kkwzjc6PYHlGQ2wrItjImqlseJJMM43Wj2B5RkV sJSKv4yolmqVseJJLM43Wj2B5Rkf/UJSKv4ypTZqmoeX8kqS1M43Oj2B5Rki/wC0Koi1DKlC/mqa h5SSpLUzdf6tHsDyjJ6/p2HURaxiobX81TWv4/USaiXUZy59N6PYHlGXubEiE5+RaxisaqEXVCjl Q/WSayXX+Zy59ro9geUUVbNeXPpVRFdI7gzWUVfeqK6h2skOElwmu/ST3bx/R7A8oMpk/SxHiM8M OjThTX9iLqWqr6HHB50kOkp4zSVsUVKqro9geUDLit15fLSumM+MPjTw26UuCVH2moPsWoqcK3R1 4feJD5MkIiTpKvO6fYHlGiFLViuHLSpGJAzIGnyh8peKXRHEEcQ20NtDbQ2kNpDaQ2kNpDaQ2kNp DaQ2kNpDaQ2kNpDaQ2kNpDaQ2kNpDaQ20NtDbQVxBXEFdKnit8cfHpA/IJMn6TMp+0vw9geUaYU+ pmqJNprRmUNSRuSUSSmQgkgSQI+X5fl+X5fl+X5fl+X5fl+X5fl+X5fl+X5fl+X5fl+X4r4sgWQV SCuSOSR2SPSiRLREzDMlUVVVfh7A8o+EeW4wsTNKKxmYijcsollMspliSxJZjDGIYxDGIYxDGIYx DGIYxDGIYxDGIYxDGIYxDGIYxDGIYxDGIYxDGIYxDGIYxBZYssqllUsrljksdmISsxooSVmLjyqq r8vYHlHxRVRWZ77QznNI3mzSlGZUKU5hSJmKHEUOIocRQ4jScRQ4ihxFDiKHEUOIocRQ4ihxFDiK HEUOIocRQ4ihxFDiKHEUOIocRpOIocRQ4ihxFBcxQXMEKsxpHM1aQezmhB/M3nCquqtfn7A8o/hS utC9dL50vnS/eL94v3i/eL94v3i/eL94v3i/eL94v3i/eL94v3i/eL94v3i/eL94v3i/eL94v3i/ eL50vnS9dLytT7X+PrDhvHuTHJjkxyY5McmOTHJjkxyY5McmOTHJjkxyY5McmOTHJjkxyY5McmOT HJjkxyY5McmOTHJjkxyY5McmOTHJjkxyY5McmOTHJjkxyY5McmOTHJjkxyY/pP/aAAgBAgIGPwBR f//aAAgBAwIGPwBRf//aAAgBAQEGPwCPk8jmv2su47DV1z9uE90V5xHl7vuKrTXWMHDNdYwcM11j BwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHD NdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11 jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMH DNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDNdYwcM11jBwzXWMHDP+z/AEd/2MzUhXbvz7H6XVO/ Y7vFJSkpSUpKUlKSlJSkpSUpKUlKSlJSkpSUpKUlKSlJSkpSUpKUlKSlJSkpSUpKUlKSlJSkp+le p3d6ndOn4OZqQrt349EOqp0QT9PVfzGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGC9X RVh9/wAjo8nRfnmakK7d+H+LqdVEefTqonYYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBgwYMGDBg wYMGDBeqd/uKip2+/wAszUhXbsqOutURVT9SidhgwYMlZ/XsGDBgvYVFQVOnb6fHM1IV27J0QR95 O6idhOwyaWC9hU6d/oK6v0+GZqQrt2RFVOyCdhBJrUU/cRO6N+GZqQrt06CL07qIJNiijydGjzsu ZqQrt0dd/MRBBJsUUU/y+8uZqQrt06/YQQSbFFFOv2WXM1IV26KIJNyij0uZqQrt0e8iCTcoo/Lm akK7dHvIggk2KKKPy5mpCu3R7yIJNyij8uZqQrt0e8iCTcoo/LmakK7dHvIgk3KKPy5mpCu3R7yI JNyij8uZqQrt0e8iCTaooo/LmakK7dHvIgk3KKPy5mpCu3R7yIJNyij8uZqQrt0e8iCCTaoo/Lma kK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu3R7y IIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCCTaoo /LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu 3R7yIIJNqij8uZqQrt0e8iCCTaoo/LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCC Taoo/LmakK7dHvIggk2qKPy5mpCu3R7yIIJNqij8uZqQrt0e8iCTcoo/LmakK7dHvIgk2qKKPy5m pCu3R7yIJNyij8uZqQrt0e8iCTcoo/LmakK7dHvIgk3KKPy5mpCu3R7yIJNyij8uZqQrt0e8iCCT aoo/LmakK7dHvIgk3KKPy5mpCu3R4QSblFHpczUhXboqfcQQSbVFFT7y5mpCu3RPzEEEmxRRRHZc zUhXbojyfRR1RBJsUUUX7JLmakK7dk/bVfAgk2KKL37qwVV+suZqQrt2RHk+giook2L3F7i9GJ8M zUhXbsqItFRFRRO40aNmVo0aL3FhuL5+OZqQrt34I69REVFGjRo0aNGjf6po0aNGjRo0XuK5DXv9 zqrV+OZqQrt349UXt9hEVeijRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0Xq9/YVH V6OnVflmakK7d+XVOw3qh0e7FM7PJ7KQ0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRpSKaH6V6nR 3sh1eXr+BmakK7d/C7KqFJfZTX2U19lNfZTX2U19lN72U3vZTe9lN72U3vZTe9lN72U3vZTe9lN7 2U3vZTe9lN72U3vZTe9lN72U3vZTe9lN72U19lNfZTX2U19lNfZSX2d3l/Ej/wAj/wCf+7/jD6/y f5/7tBOnX+L/AMfr+5tPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPK m08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptP Km08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKm08qbTyptPKn/jan//Z" + transform="matrix(0.24 0 0 0.24 174.5615 142.499)" + id="image25"></image></g></mask><g + opacity="0.09" + mask="url(#SVGID_1_)" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + id="g27"><path + fill="#1D2915" + a:adobe-blending-mode="normal" + a:adobe-opacity-share="0" + d="M195.361,251.626 c-8.161,0-14.8-6.64-14.8-14.8v-73.527c0-8.161,6.639-14.8,14.8-14.8h59.663c8.161,0,14.8,6.639,14.8,14.8v73.527 c0,8.16-6.639,14.8-14.8,14.8H195.361z" + id="path29" /><path + fill="#1D2915" + a:adobe-blending-mode="normal" + a:adobe-opacity-share="0" + d="M255.024,152.499 c5.964,0,10.8,4.835,10.8,10.8v73.527c0,5.965-4.835,10.8-10.8,10.8h-59.663c-5.964,0-10.8-4.835-10.8-10.8v-73.527 c0-5.964,4.835-10.8,10.8-10.8H255.024 M255.024,144.499h-59.663c-10.366,0-18.8,8.434-18.8,18.8v73.527 c0,10.366,8.434,18.8,18.8,18.8h59.663c10.366,0,18.8-8.434,18.8-18.8v-73.527C273.824,152.933,265.391,144.499,255.024,144.499 L255.024,144.499z" + id="path31" /></g></g><g + id="g33"><g + id="g35"><linearGradient + id="SVGID_2_" + gradientUnits="userSpaceOnUse" + x1="225.1929" + y1="152.499" + x2="225.1929" + y2="247.6265"><stop + offset="0.0123" + style="stop-color:#C1D72F" + id="stop38" /><stop + offset="0.1394" + style="stop-color:#BCD631" + id="stop40" /><stop + offset="0.5859" + style="stop-color:#AFD136" + id="stop42" /><stop + offset="1" + style="stop-color:#ABD037" + id="stop44" /><a:midPointStop + offset="0.0123" + style="stop-color:#C1D72F" /><a:midPointStop + offset="0.3086" + style="stop-color:#C1D72F" /><a:midPointStop + offset="1" + style="stop-color:#ABD037" /></linearGradient><path + d="M184.562,236.826c0,5.965,4.835,10.8,10.8,10.8h59.663c5.964,0,10.8-4.835,10.8-10.8v-73.527 c0-5.964-4.835-10.8-10.8-10.8h-59.663c-5.964,0-10.8,4.835-10.8,10.8V236.826z" + id="path46" + fill="url(#SVGID_2_)" /></g><defs + id="defs48"><filter + id="Adobe_OpacityMaskFilter_1_" + filterUnits="userSpaceOnUse" + x="184.562" + y="152.499" + width="81.263" + height="95.127"><feColorMatrix + type="matrix" + values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" + color-interpolation-filters="sRGB" + result="source" + id="feColorMatrix51" /></filter></defs><mask + maskUnits="userSpaceOnUse" + x="184.562" + y="152.499" + width="81.263" + height="95.127" + id="SVGID_3_"><g + filter="url(#Adobe_OpacityMaskFilter_1_)" + id="g54"><image + overflow="visible" + width="356" + height="414" + xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAgEBLAEsAAD/7AARRHVja3kAAQAEAAAAHgAA/+4AIUFkb2JlAGTAAAAAAQMA EAMCAwYAAAXBAAALIQAAEOP/2wCEABALCwsMCxAMDBAXDw0PFxsUEBAUGx8XFxcXFx8eFxoaGhoX Hh4jJSclIx4vLzMzLy9AQEBAQEBAQEBAQEBAQEABEQ8PERMRFRISFRQRFBEUGhQWFhQaJhoaHBoa JjAjHh4eHiMwKy4nJycuKzU1MDA1NUBAP0BAQEBAQEBAQEBAQP/CABEIAaEBawMBIgACEQEDEQH/ xACYAAEAAgMBAQAAAAAAAAAAAAAABAcBBQYDAgEBAAAAAAAAAAAAAAAAAAAAABAAAAMIAwEAAgMB AAAAAAAAAAIGATIDBBQFFjZQMwcRECKQMRMSEQABAgQEBgEBBwQDAQAAAAAAAQIxcgMEEFCRsyGC M6PTNBFBIGFxEiIyE1GB0UKhscFiEgEAAAAAAAAAAAAAAAAAAACQ/9oADAMBAAIRAxEAAADy0npz Z0Dnx0DS7Q9kr0IKcIKeICeICeICeICeICeICeICeICeICeICeICeICfggp2CElQD1aXxOgc+O1s um7kKj5vpObG6d2Q9zspRA9JmSGmCHmWIiWIiWIiWIiWIiWIiWIiWIiWIiWIiWIiWIaYIeJo1sPe 4OK5C2tCVS3OmN5clN3IVHod9EOv6zWb0zkAAAAAAAAAAAAAAAAAMRJnwcVXltVuetyU3chUfp5+ 5YexhTgAAAAAAAAAAAAAAAAABjODUVxZNbnjclN3IVHIjyCx5sKaAAAAAAAAAAAAAAAAAAMZwaut rJrY8bkpu5Co5EeQWPNhTQAAAAAAAAAAAAAAAAABjODV1tZNbHjclN3IVHIjyCx5sKaAAAAAAAAA AAAAAAAAAMZwautrJrY8bkpu5Co5EeQWPNhTQAAAAAAAAAAAAAAAAABjODV1tZNbHjclN3IVHIjy Cx5sKaAAAAAAAAAAAAAAAAAAMZwautrJrY8bkpu5Co5EeQWPNhTQAAAAAAAAAAAAAAAAABjODV1t ZNbHjclN3IVHIjyCx5sKaAAAAAAAAAAAAAAAAAAMZwautrJrY8bkpu5Co5EeQWPNhTQAAAAAAAAA AAAAAAAABjODV1tZNbHjclN3IVHIjyCx5sKaAAAAAAAAAAAAAAAAAAMZwautrJrY8bkpu5Co5EeQ WPNhTQAAAAAAAAAAAAAAAAABjODV1tZNbHjclN3IVHIjyCx5sKaAAAAAAAAAAAAAAAAAAMZwautr JrY8bkpu5Co5EeQWPNhTQAAAAAAAAAAAAAAAAABjODV1tZNbHjclN3IVHIjyCx5sKaAAAAAAAAAA AAAAAAAAMZwautrJrY8bkpu5Co5EeQWPNhTQAAAAAAAAAAAAAAAAABjODV1tZNbHjclN3IVHIjyC x5sKaAAAAAAAAAAAAAAAAAAMZwautrJrY8bkpu5Co5EeQWPNhTQAAAAAAAAAAAAAAAAABjODV1tZ NbHjclN3IVHIjyCx5sKaAAAAAAAAAAAAAAAAAAMZwautrJrY8bkpu5Co5EeQWPNhTQAAAAAAAAAA AAAAAAABjODV1tZNbHjclN3IVH7+HqWTO1uxMgAAAAAAAAAAAAAAAAAYzg1Vb2NXB5XJTdyFRx5G jLc3XG9SS2MgAAAAAAAAAAAAAAAAD4+ohqq47GvTa3JTdyFR830nNm/7qp+gLVk8fuDcZgehLRBL RBLRBLRBLRBLRBLRBLRBLRBLRBLRBLRBLRBLRBLRBKQohP0MbkT40OcG8uSm7kKj5vpObAJm45sd n98SO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3cQO3xxI7PX84JcQAN5 clN3IAAAAAAAAAAAAAAAAAAAAAAAf//aAAgBAgABBQD+G3//2gAIAQMAAQUA/ht//9oACAEBAAEF AFgq7/bL9narGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxn arGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qxnarGdqsZ2qx narGdqsZ2qxnarCYVyhn78PQNo/MCUmJhssm48QEScNrGJGEMQgjEIIxCCMQgjEIIxCCMQgjEIIx CCMQgjEIIxCCMQgjEIIxCCMQgjEIIxCCMQgjEIIxCCMQgjEIIxCCMQgjEIIxCCMQgjEIQakYQOlI bGTCajw2R5SPLm/KK2gegbR+LVYzzDZGzFKyBaysYS3FYGSBBQkFCQUJBQkFCQUJBQkFCQUJBQkF CQUJBQkFCQUJBQkFCQUJBQkFCQUJBQkFCQUJBQkFCQUJBQkFCQNkCA9uK0R7WVrJ+ykMy6WI8BrW NY0IraB6BtAsdqbMHtttYxkvKFKxhCs4JpCtExKFMy5W5jWX22NgRAitoHoG0SsBsePZZFhSSkuw peFm5dhi3qRKYk1BbAjoraB6BtCcl/8ASYtUBjCkL8Lwp2fS3WCxpVDA/wA5lFbQPQNoShGNLbif CcM3+roX9VQz4ZFbQPQNoSLP0t7P04e5uql5FbQPQNoSPXIOcPc3VU8itoHoG0JHrkHOHubqqeRW 0D0DaEj1yDnD3N1VPIraB6BtCR65Bzh7m6qnkVtA9A2hI9cg5w9zdVTyK2gegbQkeuQc4e5uqp5F bQPQNoSPXIOcPc3VU8itoHoG0JHrkHOHubqqeRW0D0DaEj1yDnD3N1VPIraB6BtCR65Bzh7m6qnk VtA9A2hI9cg5w9zdVTyK2gegbQkeuQc4e5uqp5FbQPQNoSPXIOcPc3VU8itoHoG0JHrkHOHubqqe RW0D0DaEj1yDnD3N1VPIraB6BtCR65Bzh7m6qnkVtA9A2hI9cg5w9zdVTyK2gegbQkeuQc4e5uqp 5FbQPQNoSPXIOcPc3VU8itoHoG0JHrkHOHubqqeRW0D0DaEj1yDnD3N1VPIraB6BtCR65Bzh7m6q nkVtA9A2hI9cg5w9zdVTyK2gegbQkeuQc4e5uqp5FbQPQNoSPXIOcPc3VU8itoHoG0JHrkHOHubq qeRW0D0DaEj1yDnD3N1VPIraB6BtCR65Bzh7m6qnkVtA9A2hI9cg5w9zdVTyK2gegbQkeuQc4e5u qp5FbQPQNoSPXIOcPc3VU8itoHoG0JHrkHOHubqqeRW0D0DaEj1yDnD3N1VPIraB6BtCR65Bzh7m 6qnkVtA9A2hI9cg5w9zdVTyK2gegbQkeuQc4e5uqp5FbQPQNoSLlvb+nD3N1UvIraB6BtCTN8Jbj /ScM3+rob9VQ36ZFbQPQNoTUx/xGtcdjSlb9ZwsRvwt1jMYVRR/+5hFbQPQNokZinmbPOsaWVjsM XhZuOwpbxOsYWcjtjzCK2gegbQLDdv8ANtuuDGsgTJTMYZjeCaZjBHmSlZcbgxjL9dGxDBFbQPQN oDGtK2z31pBJXYrWQLmVrCz5Whk8QVpBWkFaQVpBWkFaQVpBWkFaQVpBWkFaQVpBWkFaQVpBWkFa QVpBWkFaQVpBWkFaQVpBWkFaQVpBWkDZ4gNPkYI9zKxk7dysZdr80zTGaZoRW0D0DaPzK3Oalmyy oYwEVEv8yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGV S4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXGVS4yqXB1RL/JlUMMyauU1Mt/KK2gegbRxaK2jj/wD/ 2gAIAQICBj8AG3//2gAIAQMCBj8AG3//2gAIAQEBBj8Ar2djdfxW7G01az+Ok74VzEcvF7FWJ73Z o+M97s0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+M97s0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+ M97s0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+M97s0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+M9 7s0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+M97s0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+M97s 0fGe92aPjPe7NHxnvdmj4z3uzR8Z73Zo+Ms7O7u/5Leq5yVGfx0m/KIxzotYixTC5kpbbfsfFJir 9/0EWo74+5qHH8y/3IO1Ug7VSDtVIO1Ug7VSDtVIO1Ug7VSDtVIO1Ug7VSDtVIO1Ug7VSDtVIO1U g7VSDtVIO1Ug7VSDtVIO1Ug7VSDtVIO1Ug7VSDtVIO1Ug7VSDtVIO1U4fmT+5803fP3Kn+D4qsVP v+n2LCd22/C5kpbbcUqVkX4+jf8AIiI34QTgQIECBAgQIECBAgQIECBAgQIECBAgQIECBAgQIEBe AqK1FRfuFqUE/SkWf4PhY4WE7tt+FzJS224JWqJw+f0ov/YnATgcMi4i8BeAtdifpX9yf+4WE7tt +FzJS22jKSfVeP4DUROCCcMmXgORU4KPpL/qvD8CwndtvwuZKW20dUVIcEE4Hxkyi8BHon7uC/2L Cd22/C5kpbbT5/8AoTKFG/iWE7tt+FzJS22nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttp zKJlCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZRMoUbMWE7tt+Fz JS22nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzKJlCjZiwndtvwuZKW205lEyhRsxYTu 234XMlLbacyiZQo2YsJ3bb8LmSlttOZRMoUbMWE7tt+FzJS22nMomUKNmLCd22/C5kpbbTmUTKFG zFhO7bfhcyUttpzKJlCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZ RMoUbMWE7tt+FzJS22nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzKJlCjZiwndtvwuZK W205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZRMoUbMWE7tt+FzJS22nMomUKNmLCd22 /C5kpbbTmUTKFGzFhO7bfhcyUttpzKJlCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2Y sJ3bb8LmSlttOZRMoUbMWE7tt+FzJS22nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzKJ lCjZiwndtvwuZKW205lEyhRsxYTu234XMlLbacyiZQo2YsJ3bb8LmSlttOZRMoUbMWE7tt+FzJS2 2nMomUKNmLCd22/C5kpbbTmUTKFGzFhO7bfhcyUttpzCZQo38SwndtvwuZKW20dTVfvQQ+cmUXiI xFhxUsJ3bb8LmSlttGVPp8/C/go1fkTjky8RyqsB9T6KvD8CwndtvwuZKW23BKNR3wqftX+qCcRO JwyLiLxF4i0Ka8V/cuFhO7bfhcyUttuCKi/CpBRtOs74cnBF/qJ+oiRIkSJEiRIkSJEiRIkSJEiR IkSJEiRIkSJEiRIkReIv6hadFfl31d9EFc5flViuFhO7bfhcyUttv2ERrvzNT/VT4qIrf+TqIdVD qodVDqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTqpqdVNTq pqdVNTqpqdVNTqpqdVDqodVDqC/xorl0F/O74av+qfYsJ3bb8LmSlttyywndtvzD/9k=" + transform="matrix(0.24 0 0 0.24 182.5615 150.499)" + id="image56"></image></g></mask><g + opacity="0.35" + mask="url(#SVGID_3_)" + a:adobe-opacity-share="1" + id="g58"><path + a:adobe-opacity-share="0" + d="M184.562,236.826c0,5.965,4.835,10.8,10.8,10.8h59.663 c5.964,0,10.8-4.835,10.8-10.8v-73.527c0-5.964-4.835-10.8-10.8-10.8h-59.663c-5.964,0-10.8,4.835-10.8,10.8V236.826z" + id="path60" + fill="#1D2915" /></g></g><linearGradient + id="SVGID_4_" + gradientUnits="userSpaceOnUse" + x1="226.1924" + y1="159.7139" + x2="226.1924" + y2="200"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop63" /><stop + offset="0.3788" + style="stop-color:#F8FBF3" + id="stop65" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop67" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.4383" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></linearGradient><polygon + fill="url(#SVGID_4_)" + points="221.189,159.714 214.142,180.951 224.048,180.951 214.142,200 238.243,173.61 227.655,173.61 236.978,159.714 " + id="polygon69" /><g + id="g71"><g + id="g73"><g + id="g75"><image + overflow="visible" + opacity="0.75" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="392" + height="242" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYwAAAD2CAYAAADF97BZAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAHohJREFUeNrsnYlu40gSBZMU5Z75 /4+dbUsiFwtY2JrqvIqHSEoRACFZPtqk3BV8WVcvAAAACXouAQAAIAwAAEAYAADwWgYuwSp0XAKA wzJxCRAGIgCANf8fIxaE8RIxIBaA8yeMDokgjLkNfLfyzwOAfWTRNX49EkEY5h9N6+sIAuC8/++n mXKZjJ/5UfIYPuyPRXut9WOEAXDuxOGJYEqKZPpEebyjMOYKoTO+BmEAfIYwSjG0JJFaHm8rjncS RkYE2uutwkAgAO8liezzTPqY3jl1vIMwooa+fi0SRvQzshIBgHMKYwpez5Su3jJ1nFUYLWWlznne KpHW5AEAx5fF5Aijfi0jDy91nFocZxRGJgnUMvDkkf3arDiQCMD+YshIwxKE93xy5CGJ1HFqcZxJ GEsE4b0WHa3JA2EAHEcYU8PjlJREdESp47TiOIMwIlG0yqEPPl4iDmQBcCxpZMtOmWNs+Fy2n6M7 kzSOLIy5oogk0SsfZwWCNADeSxhZMYzGYy2I8uslIY5TpY2jCiMzsikjCO2xDz4fiaNFGggDYD9h LJGF9uh9rq8+7hxxWLI4vDiOJozsKCdPFJoc5ry2JG0gDYDzJouxOrTXLJHUsuiKz4uROurS1GHL VEcShpcqNGFoZSZLBBfla6zXO0MeGXGQMgCOkyyyopgCMfzveDifs44yRdQlLKtEdei0cRRhaLKI UkXZoFtCeH7uUn3dJfF9UV8HwgA4tzDG6nktjYfy/OF8zhJLZ0hjEns01SHTxt7C8EpQmdKTlhQu lSguxmu1TLSfNac8hTQAji+MMZEsahHUx6g8743v1aQxOudzSGnsKYyoBOUlCk0StQQGRRrW4aUO bWRVpj8DYQDsI4xaFJEwPFk8FGHcDYGUR1/Jon7uzf84bIlqL2FkS1BWP8XFSRKDIozBEclgiMPq CJ8rDSQCsI0cWmWRTRaWDO7BYy2OXhFH2SE+BudymLSxhzAsWbSkCk8S3vNBkUdGGJE06pTUIgTE AbCOKLRylCYNqxyVTRV347gYX9sr4nhUotDKVPXw292l8WphtMrCSxWeEOrjqghDE0hUlrKkYaUL ZACwj1S8lBGli1oYWpL433Eznl8qWdxFL2trfRsiev/GIaTxSmFkJ99po5+0RBHJoXy8Bt+j9WmU /743qU/E7vxGGgD7JBBrCRCvs9sqRd2NZHErRDEY4iiTxV1p72ppiPx/IqDVr7GbNF4ljBZZaKnC E0UtBu3jq5E0PGFcgpJUH8gCcQDsJworXWQTRiSMWyWM5/Oheu3ZltzFLmk/lPbhkCOoXiEMreHM 9FVYsrhWz8vjS3lNE8dglKiyHd9ewqAsBbB/KcoThjbvwhpCG6WL8vgu2pRb0XbdnGpFKY26vbCG 3u4mja2Fkemz8EpQZWOulZq+gkdNIlFZaq4wBGkAHFYWIm2d3lG6uFeiKNPFt9Ku3ESfEOyJY3TS xi7S2FIYc2ThpQpLFF/Bx6U4auFckiWpFlkgCoBjiCPb8T01Jow6WVx/JHEtZPEt+oCaaD6XRUYa b1OSapXFEJSdsodVporKUZlRUpEsEAbAcaWhpYx6WK02lPamSKOWxVBIo1UY3QJpbC6PrYTROhLK SxW1LH5VUvhlvG5JQ0sXWv/FReYNp0UaAPvKYq4wxoQwvIQRlbojYayRNDaVxhbCaFmKXJNFPcrp q5LFL0UUGWlE6UJ7Y7MT9hAGwDmFEaUMb/5F3X/hjb70hulnZVFL4/Sd3l2iLNUHsrBKUL+M48v4 2EoX0ZvqlaIEYQCcWhalNKwNkrz5GPeftuRWtCk30ed8aUsYzU0WdZoo25ZaHJsJZMuSVMs8i6iv opbEX4FAnt+jdXZnZaEtby6IAuBtUsac/oxaGoPy6A2mWZoupiJldK8uTa0pjLmlKK2DOxLFX8br WllKG5Lbsn4UO+0BII1B/uzLqG9Av+XPKQHZNsU7H22IcF2e0qRx2ISRkYU1g3sISlB/KY9/OUlD 67vIyOKSKEO1JAzEAbCfKFqF4c0CL1PGUKSNm9hr1LXchHYJWYjoy4RYZSnZQhxbz8OIRDEYwvhl SOIvQxh/KenC6+hu7eSOpIEwAM4hjCXSuBRp47lW1F1p16wtoFvShSYLa++MUV7Un7GGMFpKUV66 qPssaln8bUijFoY1u9vbOGnp8NkucX0A4LWyEGmbCT6KvnzIWMjiUcgjsyGbdQOa+V0nyW/p2r2i NLVmSWqJLLR0Ycnib6ck5U3S85JFy8KCSzq5kQfA9pJokUX52AdJo6/EkVmwdM6ci2nG0TmyWE0c S4URDaPN9ltkZPG3U5bS5l1kFhecu2w55SeAfcl0FncJgXh9G30hivqxXIE2Gl3ZO6KIymPZo0xI m/VjrFWSmjsqanBkYfVd/K2kC6uj25JFZ7yxraJAEADHF8pUvTYFjbZUd+5T0UY8hfEQfapAZnRl lCpG0eeHaB/XKcOS5CriGFZ6Qyx5aPMuhiBZ/FJkoaUMTRblMNpoFvfS3fOQBcB55DEp/3e9DuJa GE9ZWO1bpvRkNeZWp7u1Z0emNDUp579YGsOCNyBKF9HIqEFp7K1U4Q2lteZcWOu4tM6xoAQF8H7S EKN0Uz9qd/B90Xh3ku+r6IxUMTpHuWWsdvRGacrr09g9YWTnW7RM0rPKU9oyIN7CgpYs1twxD3EA HJcp+f9UuxPvFGnMFUUXpANLGJ5Qyu+t/+1Ddnp3iXKUNu/iy0gYvyQ/Qa8cGaUt+5FJFiJtI5+Q A8D50kVGIpNy0zgtkIYY/0b5PLOnuHZcqpTRK0nIK0+9TBjZLVfnzOrOJI0vQxbafAuvzyJbfmK4 LMB7JAzv/+zU8H+9lEZL2zApopgMcURHVJrqRO+72a0Pw0oX0YZIVsKIylDWkuVav0UpK2upj7mi QA4A504YnkCmoLpQfm/r3hbWarmRHJ5rV3kpwytNaalqljiGhRc+U4qKNkb6ctJFnSa0uRaaLC7J ZEEZCgCBRCOoLHFMxd19JuVEndyeMLIpo98yZcwtSWWXL5/bf/El9kioL7H3tYhmW1rpYm4pCgDe RyCt4pCigRaxZ297w2fHIFWU6aJ8HOTf61uVbd5mKWOYcVFb08Wc/bm1RQTrVFH3WViy6INUgSgA oEUcWn9HL7kFEOtS1BiIojy+5P97cdSlqUfVDnspYzZLh9Vq+3Rn5mBcE6KwtlgtReEli16YiAcA 64vDayt65XszQ2ejhFFu3FTuxfFQksYo+kitXfowOrFHSnmlqGgLVi9daEt9ZCbmibAzHgBsK46u kIFUlY3pp416CmNIlqNulShuRVtYbuB0r26aR6MsNYm/d8aqwpi7DEhm74urU36K9rTQ1p23Fg9E FgCwpjjqmeFdlTK8ctS1eNT2DP+qZPFVSOMm+grcUV/G4s7v1j6MOcuYe3MwNGl8KV8b7cFd/w4i /pR8RAEAc8QRSUOqlPH8+jpljEVJqZbGl5Iq6qPc7e9eScPry5AlKWONeRjeUNpLUI7SEsUg9sxt bwZ3L/RVAMBr04bXCT5Wpam6XF+WpK7y7z6Ka5EqynQxiL2DaC/xaKnNJ+5Fayi1jpQagtKTNXN7 SKSLaClhZAEAa6cNSxrlXX5fpYyxaNdqadyVdnBuyli187ufeaG0foJMyhgMMdSlp0wZykoXIu3b qAIAzE0b2nNtBGl2YdbBaRsHJ2W0rMg9q23sGy5My2S9PnExhsTFyG6BmHkDAQBeLRFNGpeqNOXt RJppG7Wb6i6Qxiz6hpP3RNIHCSNj0swF6WaUopAFALwyZWRvri1xXIL2sWVqQbR67qrCaEkaLUNr 6wtxCWRh7ZVryQFJAMAe0ojazWe7dWlsI6/JhKG1l6KUoma1lf3Ci2RdiGyyuIg+CspbRLA3TD7n jQQA2EIe0Y21Nw1hUB6z0sgkDC8dLRZGNlVkR0hZpSdLFpfkBVhkTgCADVJGZoM5qyLjyaN1o7hV +jH6mTHL2gcjugjWBfHKUN1WJw8AsFG6iGSRLeNn2spoFOlqbWXfeDG6IHK1ysI76cyOeaQLADhr maqfIY2L5Pt5rQ7vzYfVZspUfXC0CsLq5LbGEgMAnC1laP0Z1giqls7ubBl/k07vaOiYtp6TdjKa JKJJJ9n5FqQLADhj2vCG20Y33NlSVNfwu62SMLqkNb2E0SviyMzgXrUOBwDw4pThrY6R6QPOVmo2 7fvNTNzrgs9F9bhIHH3ihC07C+kCAE4mE00ctUCiakymhN+vfZPdz4gm2fHFXSALK0V409pFWPID AM6XNrwUklnANSpZzRlS29x+zllLqiVpZBKHNwoqE6OQBgAcXRTeIoCd0x564siW8K2RUs0MMy9E 9tBOwNv4KFtjQxwA8C5C8drOLlmlya4h5a21Fy513q948llbdo48okglQn8FAJxLCt68Ma1Bt9pD 7fW1O7q7LYQRxausLaPaGivPAsC7yyTbZnZiTznIyENk4UipfuZJtp68VXLyxJGZoEfaAIAzSaJl TtuaCWOVdrJ18UHrJCNZRFErU3ZCCgDwjglDa+u6GQkjszjr5sLIJI2oA8dLGtKYMBAHAJxVDJ4s Mmv1ZWURlaNeKgxJnmBmT9kueZFFKEEBwGdJJjui9CU7j/YrnJTX6Gcn3m1SbwMAOEnyyDT4SxNF tGrtLGF4nc3euN7MbMKsGDIlKMQCAO8kjeyNdIs0Vm0vt+jDiGQS2TVbtgIAOKMkoopMa5uaEc4q 9C+8EF4UmyMpAIB3Tx4tfcGb32T3G52sVzN7iQkBAA4ogJYbbetjbxe9zFp8s8v8/Y4XCQAAkeiN uwSJQiQ3qbn1Jn/zhNHNuDCR5RAKAHyKLDKfjxJG9t/YpdNbpH1/7ZY4BgDw6TKJSvWtW0Espt/g ROes/eQtxYtQAAAOsJFc/6KTmxPJAADAF8RL29F+5xMHAEAGfz5fvHfFFu1tf8ILCgDwbrLIrAi+ xs/+CGEAACCX/FpTm9x4IwwAgPMkka2+/jDCoJwEAPAGbSoJAwDgwxr+owuDlAEAcHJIGAAAgDAA AD6At5jpDQAAJAwAAPikdIEwAAAAYQAAAMIAAACEAQAACAMAABAGAAAgDAAAAIQBAAAIAwAAEAYA ACAMAABAGAAAgDAAAABhAAAAIAwAAEAYAACAMAAAAGEAAADCAAAAhAEAAAgDAAAAYQAAAMIAAACE AQAACAMAABAGAAAgDAAAQBgAAAAIAwAAEAYAACAMAABAGAAAgDAAAABhAAAAwgAAAEAYAACAMAAA AGEAAADCAAAAhAEAAAgDAAAQBgAAAMIAAACEAQDwMUwIAwAASBgAAHDOlIEwAADgUMKYuNQAAOdu F0kYAABwGGGQLgAA1mtHd2tTSRgAAOeThvX58vhYYZBSAOBTJDAl0sXU0EauJhESBgDA/tKoG/U1 GvnVk0a/08UhNQAArJtENqff4KQydbTpyBcFAODA0titA7xf+MtPM09YuwDT1nEKAOCEaSLqw5he 1Wb2K51c5hedErYkUQDAJ0ohalen4KZ9esXN9h4zvefIBQDgE6QxSVyJmSRXllq9xN9vdBEiM2ai FwDAp0hjMm6sWxKGN9oqandTbW+/0clnkkXr8DFkAgBnl8KcG+dJ/AFFU9DGZqUQ3uT3G1yUKGJ5 1pyEkVIA8BkCySSOKHVMiTZ2tfazn3liU/IEopPPXBhkAQDvKIu5N9JTcGx2o903xpLopDIXyJNB JIgp+XsCAJxBHNkb6czN+ZT8Ppl7Q96vcNItJxHZUoKTRBAA8K4CiYbIPp+PDW1t5qb+ZcKQwIxZ C3oJA2kAwLumi0w7OiqSGKWtI3yV9rNPntyc2KQdkRk9cQAAvIscJidZWG3mqHy89IZ804SR6Zix RDFWJzlK23Axz4zIBQDOJBCRuDQ/OqLItKOrDx5asw/DkoRmxFH5mlH8OhzDbQHg3WQRtZuZhDE6 clky9201YURlqDFIGGODGT0rAwCcVRxRKX9MtJ8tJarF7WffcIJZY3mmtKJVFK+s+AYAcBYxRJ+L Sk5jcMzp12iq2myVMFpO0CpXtdoRgQDAGSQSdW5bCePRII1R8h3tL1lLaq4kyhN/SNwhHtkaUQDA UdOFVRXxkoUmh0fQfmZK/SILy/t94mS9dUsiI3on6J209jNF4o5w5AEAZxFHNM/iURxLZbFKGT+7 ltSUSBjRCKjoRB/BBaQjHADOKAmvKqP1UTyM9jFqQz2BaL/D6sJoPenROdHoKC+EVdfLCAJ5AMCR xJGpzHg32I9EW9pSltosYXiiECNWRXW4R0PUmrOoFgDAEdOFJpEoSTyPe/U4VxpzfvdFCSNKGi1W fF6Au7T1a7SsagsAsHe6EMmVo7yb63tSFF5ZalHq6Gc2utnRUZEkshdgSpw8ogCAI6YLWUEUd+fj pQkj3W6uMXEvEkdWEnfR63abxSsAgBeki7k32Hfj0BJH3W5uMlqqbzh5TyCtUSpzEVpKU6QMADhi uvCE8VBuqj1R3IMb72iY7WLmrlabnXuRsWXGng/xZ4KTMgDgiOlCGiowLdKIOr6jzetm7ZGx1bDa TN/F87glLkhLR44IczQAYL90kZnYbI2EqtvEW4M8Mqt/L2of+4YLEfVfaOb0TvJWXIybcWE8e86Z owEAsJUsrOkGmVLUXZFG1D5mb7BXm4vRz7worQkjEsVNsanVqROtzLjYogAAM2URdXA/GtrIW0Ic L524Nyy4UJm+jEdwEerj+vM4/DxeiuN/P6P7kdz487z7ed4rF6P7ea0rfueOv3EA2KAMJZLbEygr iqjNbE0X2u/YfEM9JC9MV/3gLlGWsmpz1vH98/uUx70QRl8cXXFIJY5IGoI4AGBFWbR0cFtTDeo2 8ltpH7W+jUyHt7fH92YJY3JEkZFFfVG+fxLFd5EsbkXCGKqEEQmjThmlLOp0QdoAgLVkUYujbrSt AUC3QBLWa9mEYW0V8fKSVH1xygbbGjJWlppuijQG5Yhk0SmJoa+k4EmDtAEAS0RRPnorz1ojoer2 sD40aUQDgzJbts5KGUPDxeoco0bjiy9KuhgUWVyLz1+MhOEJoyt+v1H5Gk0SpA0AyIoiksUYVFse jizqR00ac6YeTMnzWj1haHfpVsdOb0SwoUgadbK4FsmiTBnZhNFVKUNDEwdpAwCyohCZtyzSwyhD 1cdvI2lEKcOTxSpTEJb0YZSNb1mailLGUxpRGWqoRKHJonMa+k7aO8ERBwCiiEShVVesEaLlTbM1 2Oe3IwpLGLdkyvDKaZsnDE0cXSGLTMrISKNMFhdHFpYwpkIWnZM4InEgDwBEYcnCWnVWW+LDE0Ut jUgcmc7uVdeQWiIMcS5iJmXcqpLTt/w5IqpFFlGjXs/b6BrEQeoAQBTRpLxphiwsUXji8EpSWv+F yEqd3XOFMTl34V7KuFelpUtwaGWovlEW5UXqg5SRKU1NhmQA4NyCyIpCjGShDZ3V5lV8B3L4bXzu OyhFeSOkWs5/s4RhDVEt7+QfRUNfSqNMGV6qqKXRBymjlsTFKFPV3zsVH7eUpqagzAUAxxRDNmFk k8XDSBfaCKhaCv/5OTxpRB3eXt/FquvtrdGHUd+p18t2PIqGXytNXZTk4U3Sy7zxtTiespjEHmk1 JctVmWQDAOcSibXQamYDJGvobCmL34njP+L3YWRLUasOpS25zPy+srPZKhFpX+Md3pDZqA+j5Y9k 6UXrFn4eAPZPGNnyU3borCaLm1KG+k+VLLzDG17rSWOTdLFEGCJ+B7IllEgCEryWbaCjWZlrxVlE AXA+cSyRxST6sFlNFjdHFnU5yhPGb0cW1gipTSofwwpvRl3S0dZ+19JEn0waljCiIW7Px6EqS12K z2n9IyL+pEBGTQGcUxaZ5T2iDm5v8yNtUp7VZ/FPQ7Lw9gpqWdJ8t+XNPVlIUhqZklSXSBGj2JNn roU0xh9ZjIU4ns9HRxzWo9dBjlAAjlOGmgJJiCzbz8KSxbfofRSRLH47Zaho7oXIhpvKrTUPo1N+ wbFoOLPSyAgjMw66vJClLMpjlD9HYdXikMSjJocu+SYhFYBlMmhJFa2y0EZCZWRxE33IbC2Hf6rH WhatI6M230RurZKUNcy2FkerNLw33lvw6/mmXos3tlyj6iH6aKw+WRaLZIEgAPYTSKs0rEUEvdFQ 1uZH2qQ8r5/iH4k7uVtGRmlltdVYM2FYb9RYNbgZaXiNq/amWtseatJ4iD9JcKk0WjrnAWB7aXhr QXk3oJP4o6G0mdzakh9WZ/c/Ys/DiFanrTdM8q7BoUpSUWmqbOSz0rB+flSGeiSkcRF7rw1vhrlI bhgx0gDYVxhT0GjWd+FjsmJxF31TuGg2dzSk9rdRjlpj7+7DJYxsaapMG1oDG02Es5ZR90RRvsHP pdOfW79mpZFdUh1hABxfGN5EvKws6r6Let8Kq/8imt3tLWWe2fNis1LUFgnDeyNHpeF8JGThJYxo 8kx5J3AtJHFVUoa1LIkmjH6mMJAFwPbCyHZyjzOqFlay0Pa1aJnR7Y2KinbV82SxujS2Kklpo4Qm RRwtPzsShZUqvooL/0wX9QZN1gq5njhE2kZSIQ2A7WQxNT5qZai6P3SU3G559RIgVsqwEoW1wGA0 jFacEtQpEoYnjXLOQ7bxzAyh9UYtfBXiuMq/d/UbnKShiWOuNJAFwHGkYQnj0ZAublU5yts5L1qy /DuQRVSCispzpyhJlfLwImGLLKw+DC8yluIYKnFo0uiN8hTSAHgfYYwSz+HKlqIyW61qaULbqzsq Q2X7LTaTx7DRm9gF4sjKQpKi8IRx/XkjalnUfRmeNCxhRP0ZCAPgGMLwZDGJP4imlsXdKEdF+3Pf xO+rqDu4DyWLLRNG1J8RScMaAjc69UUrXVyrhOGVpYbGlIEwAM6TLsZkwshULW4N0rgpj9oM7nr4 bKss3qIkNVcaIvl16LWRUc9SVJkwhiphWH0ZLSlj7dngANAuCi1R1M+z6WIUe85FnTK+FWnclBRR J4rspLwWWWwujuGFb3KLNDL9F1Z0/DJKUbUwWstSnjSQBcDxpRHJIprRHaWMmyEIL1FEqSLb0X36 Tu9SFFlpTOKvSZ8dVntVRKEJo+78tvYWvyQSBsIAOFZJKhKG1p6MTtXCGimlPY9E8RB9BvdDkdok L1qN9ggJo0Ua2T0vvIRxlT/7MKwSVDS8NprINzdlIA6AdUQRJYy6HOUtLGi1KZnSlCWSjChaS1C7 yGKPklQkDS1teEnjUr3JQ/H4nKh3q4RxUaRxCRJGZngtHd8Ax0oYmXJUZq+LaB0p77WHxP0UD4nX htqlz2IvYWSkMUnbHhhjII5aCjcjTXjlqOxcjEgaiAJge3FkN0NqmYORKU9ZcmhJFNYM7sPI4tXC mFue0t703hFH+ca2pIkoXWRkkU0ZHogFoK1BbNk9L+rH8OZ5ZYRgPc+Iwis/7S6LPYThSUOTxyh/ 7hNei6N+oy/y7z0v7skk4fVdZIWxRBaIAmC+OLKyyHZ+eyth3wOpRENkWzc/OoQs9hJGNmlMSmNc v+F9lTZ6+fduehdFHPXn+oQoMsIQsffKQAwA24ukRRqZlOF1hkevWf0To1IWkzPIYk9hlCffGc8l SBudkjaejXmdNHrjMRLEnHSBMAD2k4WIP2CmRRqePEbxl/Cw0kRUftp1nsWRhdFaotI6xbW00RWl qmfi0NJDS5pAGADnF4ZIbk0pSxjWx1lJjOL3URwyVRxNGFrasGRRp43668dKFmVD/0gKwtqiNdrn m/kXAPsJQyS3rPmkNN7185bDks00s/R0WFkcSRiiJAxLIJNxh1+nkzFICr3zemY01JzlzZEGwD7C kERpKtv4j4mUUm9L3ZImpqNe+OHAfwzRwoWROLoqcXTiL1MepYmlu+0hCoDXlaZapZFJHlMghslJ FJnf7dCyOKoworSREYcYAukqeWTkQKoAeO+kIQl5eK+PkptDccpUcRZhiPgjqTKd4p2TRGSGIJYI A2kAbC+LrDBE8qOpWo45SWI6y0UfTvbHEZWp6mSSafQzH4vkJ+chDIB9hRHdxWdGKUWL/0Wd2G8l irMJQ5OBKOnDk4bX6GdSw1qLCyINgNcKo0UakUhE5o1yOrUozioMcWTRkjqyKSGbJOjgBjiuSLyG u6V/YU5fxFuI4szC0N6MOaljmiGDTOkJUQAcM31MM59PC37GW4jiXYShiUMkP7JK+16SBMBnp45s w9/6McI48B9GlDrq2eNTQjgIA+D9hLH11yCME6cOCdJHy89AGADnFEbm89PCr0UYb5A6ZIFAsn8o SATguIJo+fppxX8HYZz8D6n75DcfAGE0ff3HtxEDf1ipdNDyh9LxhwVwOmkgB4Sx6h9Kxx8aAGJB GLDmHxb9FwCIAGEAf6gA8Ln0XAIAAEAYAACAMAAAAGEAAADCAACAs/JfAQYAL3iXmIlSiu4AAAAA SUVORK5CYII=" + transform="matrix(0.24 0 0 0.24 179.2061 198.1514)" + id="image77"></image><g + id="g79"><radialGradient + id="SVGID_5_" + cx="225.1929" + cy="226.1387" + r="30.8299" + gradientTransform="matrix(1 0 0 0.75 0 56.5347)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop82" /><stop + offset="0.4828" + style="stop-color:#FDFEFB" + id="stop84" /><stop + offset="0.7611" + style="stop-color:#F8FBF3" + id="stop86" /><stop + offset="0.989" + style="stop-color:#F2F8E8" + id="stop88" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop90" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.8025" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><path + fill="url(#SVGID_5_)" + d="M186.706,235.825c0,5.965,4.835,10.801,10.799,10.801h55.374c5.965,0,10.801-4.836,10.801-10.801 v-19.373c0-5.965-4.836-10.801-10.801-10.801h-55.374c-5.964,0-10.799,4.836-10.799,10.801V235.825z" + id="path92" /><path + fill="none" + stroke="#EDF5E5" + stroke-width="5" + stroke-miterlimit="10" + d="M186.706,235.825 c0,5.965,4.835,10.801,10.799,10.801h55.374c5.965,0,10.801-4.836,10.801-10.801v-19.373c0-5.965-4.836-10.801-10.801-10.801 h-55.374c-5.964,0-10.799,4.836-10.799,10.801V235.825z" + id="path94" /></g></g><path + opacity="0.74" + fill="#FFFFFF" + a:adobe-blending-mode="lighten" + d="M263.623,229.595c0.037-0.364,0.057-0.734,0.057-1.107 v-13.375c0-5.965-4.836-10.799-10.801-10.799h-55.374c-5.964,0-10.799,4.834-10.799,10.799v7.324 c7.545-1.012,15.699-1.566,24.213-1.566C231.959,220.87,250.812,224.252,263.623,229.595z" + id="path96" /><linearGradient + id="SVGID_6_" + gradientUnits="userSpaceOnUse" + x1="225.1929" + y1="204.3135" + x2="225.1929" + y2="246.626"><stop + offset="0.0123" + style="stop-color:#FFFFFF;stop-opacity:0" + id="stop99" /><stop + offset="0.0141" + style="stop-color:#FDFDFC;stop-opacity:2.231669e-04" + id="stop101" /><stop + offset="0.1344" + style="stop-color:#BEBEAF;stop-opacity:0.0148" + id="stop103" /><stop + offset="0.2565" + style="stop-color:#94957C;stop-opacity:0.0297" + id="stop105" /><stop + offset="0.3796" + style="stop-color:#747759;stop-opacity:0.0446" + id="stop107" /><stop + offset="0.5029" + style="stop-color:#5D633F;stop-opacity:0.0596" + id="stop109" /><stop + offset="0.6263" + style="stop-color:#4D552E;stop-opacity:0.0746" + id="stop111" /><stop + offset="0.75" + style="stop-color:#414B23;stop-opacity:0.0896" + id="stop113" /><stop + offset="0.8742" + style="stop-color:#3B461E;stop-opacity:0.1047" + id="stop115" /><stop + offset="1" + style="stop-color:#38441C;stop-opacity:0.12" + id="stop117" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF;stop-opacity:0" /><a:midPointStop + offset="0.2901" + style="stop-color:#FFFFFF;stop-opacity:0" /><a:midPointStop + offset="1" + style="stop-color:#38441C;stop-opacity:0.12" /></linearGradient><path + fill="url(#SVGID_6_)" + a:adobe-blending-mode="darken" + d="M263.68,221.954v13.871c0,5.965-4.836,10.801-10.801,10.801 h-55.374c-5.964,0-10.799-4.836-10.799-10.801v-13.871l0.038-7.704c0,0,0.923-9.937,11.173-9.937h54.962 c0,0,10.063,0.328,10.801,10.799V221.954z" + id="path119" /></g><g + id="g121"><g + id="g123"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuJJREFUeNrsl9trE0EUxjO7m5vW tKFN1RqLCmqlIvjgkz5I/cOFIqLggw9KsRHxUo1IdEtactG9+A1+A8dxNrsxK/rgwI9lt5ueb875 ZuZspfJ//Bhqjvc0AfCIHClIQEzSMkUoBqyCJbAKWrxXQoBmBL6AQzChmGQREWbmNQY/DS6Aa6AL mtZvdcDPoEcOQEgxUV5mVMYzH5wCZ8FFcJ0CLoN1UHeIGII34AV4BvbBW4qbzsqKctzruq+ALXAL 3ABXwAafNyjS9sQ3cAwG4BXYA0/AU/AejLOE+I4MtME22AH3wE2wyedNivSFQT3eB/y79kwHnGE2 v4IjinCaNrBEtJiBu2SLs686VkRWGRt8/wTL5jFwxIxMbSGB+Ac1qtcluEMBbWslFDV7QBFdlmBE bwxZtthVDn1dpgF3WIIOhakF9iCf2ajQK32W5hcRJgvnmYHb9ECzQAnyhif8o7PxkWImsiQeRSyJ fWCjJAGy5G2usKtgzc6wx5dWxT6wYhm2jKNBm/UcV90m/aLsdLVoonX+QJV8RvmcXNflNVOOKktQ Fz4p+6AMrBg/GUeeFWUHd51HyuXevz7+GRELNSRzjMwYnmhI5Laa/gEBYxEjskVE7Ih67AeOi3ZE BYc55j+xxzjgpBMpImZL1mNDMuDxm5aYBT2x1+wx+vZJ6lt94kl2Ux1uWl4JWZhy9g/AQ/DOPjt8 q0ULuLebhiRYYO8wPUTIdm+X1zDrKE/FKjH95TL3eP83MiIF7FHAY2ZkYpfadxhoRE80WJ66EKIK BE9YAiPgPkW8dPUSFUfDGnMpHVmKvQJCEoofcsamBLs0fOgSUMnomo2QQ66UAbMTi4+hmOk2mGZW B39OE+rgj5iBcNb3h5qxk9boDb1SLrEh2c75+NlnCfT1A4OP8nZiVeAT0IhZY0Ni+gHP8oEpQ59Z HHP2uRtfkeUnxTj7AWHqMU0ZiRVX2ld5kZ4jnSewHN8FGACSOOKkAlOGAAAAAABJRU5ErkJggg==" + transform="matrix(0.24 0 0 0.24 199.0298 216.5547)" + id="image125"></image><g + id="g127"><radialGradient + id="SVGID_7_" + cx="202.6289" + cy="219.7041" + r="2.9995" + gradientTransform="matrix(1 0 0 0.75 0 54.926)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop130" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop132" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop134" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_7_)" + cx="202.629" + cy="219.704" + r="2.999" + id="circle136" /></g></g><g + id="g138"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAtFJREFUeNrsmP1LFEEYx292Ts3V 9ujFrCiwFyPShH4I+imoiPqbhYKIoKigN0W8SulNIrOU63S921u/A9+BYdm9mbndg4IGPiynuzOf eea52WeuVvvf/Joo8VwA6rxm+0lBD3R5TYctpu6XYBRE4DiYzMhpqRbYBDtgHyQ+gsIzQmMUOg3O gzkwzciZTUXqO1gCH8E3CsauERSOUiOgAc6AC2ABXAYXwZECsV/gPVgBb8AH8AVsg45NTjhKTYGr 4Aa4AmYZqYjLmpdj+4ySilwTLIOn4C34YZOTjlLXwH1wC8xzKSMureQym0g+O85ITxP1uU3hPS6r t5j63zFK3QM3uYwNQ0g45KUSPMSJNLjsLS71blHUZJ9Ox5lDSuo2k32SHYsBvslKcAIc5jJvUK7r I1ZntK6Du8yryBJhW9P73hi3jg2ym7ek0hKtO0z4E5xx2RYYOany7DPYyotaUPCwyoVL3KemKpLS TX+h5jhGI88jT0x9/U9yrzoHwoL7ykQtZN8LHGvURUxy05xhntWH8I7WOTzDsaSLmOAMQl6DIYgF mTGEi5iWEyWqD9dtpHCMoPaXtn9KrHSR59CsYxSJ6SKv1e9FW6L1MmM4iXWMIu8ri7u04mjF7HuJ Y3VcxLqciaqfVlnYJRWKJexzlWNs5r2SZMGMNOp1cRYc5atEVBCtPVazD8AzHzHdQUKZUyzywoIT kY9Uh9XrC4o1WTimPmI9ouv9iAXfIHKm1GtKvSyKlq2C1Una5sMTLBRHPN4MOvIxpV6BRfCEJU/s W8Ganf4xzoaCf5dGaS36JHnMKnWNUg/BY35uD1rza7ku5bY4658cMDHkEt6nUZP4TQG1dI/Ic/CJ /SVVHHglS2J94pnluXLecuB9x3Nlk5+3jUlV9hOBMAQjCpn1lMikgFrCdQrtGEKp62CDlCtaMLQc eNu+QmV/7XGp2cyN2rsdCDAAoyXZx8WJpTUAAAAASUVORK5CYII=" + transform="matrix(0.24 0 0 0.24 213.9448 216.5547)" + id="image140"></image><g + id="g142"><radialGradient + id="SVGID_8_" + cx="217.5439" + cy="219.7041" + r="2.9995" + gradientTransform="matrix(1 0 0 0.75 0 54.926)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop145" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop147" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop149" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_8_)" + cx="217.544" + cy="219.704" + r="2.999" + id="circle151" /></g></g><g + id="g153"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAttJREFUeNrsmO9r00Acxptc1m6d Fn9M125sU4RVpyjiSwXB/9wXulciiE4dhpUMpwzFUa02XdP4HDwHR7hcLk0mCh58WOnI9548973k uTYa/0e54c15nQ8E8TJ1UpKQ2Z8QJgUtgDa4CC6AliZOiYrBCfgGfoLTsgK9OQStgE2wDa6DDv+v hhQwBAdgH0TgS1mBLsIEBV0F18BNcJvC1sCyQdgIHFHYG/AODMAxBSZVhQXgHJ15AO5T2Aa4TMHC 0GMJBXwFhxT2Erygkz/AtGhim1NS1A3wCDwGO+AKBS3QKS+nrlp6eQProKe5G4LvNucCS0+1uXQP wRNwj6JaFkH6SgitNxeJ0BwNueSzMsIC3ulduiX/roJmpp9cWkVQ1CrrqB17ws+TPGdM3y3Rftnk fTpVVlS2ZpN1+qy7znl8V2HSrUvcdbKnutryVRk+63RZd5vzBC7ClFvyMXALbHEDiJreNGpDbbH+ Wp5rJmEd7sQ+n13NCq8uU881WbfPeTquwtq0u1ezW1nXepyn7SJM9dgSCc4oPBTO4Rus9jKJwatZ lGeZy+rYXzH+GWEpXxEq5Kl8VecwBcnURVjMgCcZn5GwsTZH7CJMD3khL5zWLGzKuiHnGZpe5CZh I6bO9wx7v+bN7YYxY70j1o/yEoZpKSdMmntMoMq1tIYlVG7ts/4x50tddmXCEDfgxRETZ1JRWMI6 EesObGFRWCyfaa+oDl8jQd4DscApJUr21S54Dj7wu1JBUW2Ct1rybDDRntditUtPnWor8Aw8Zd2h rXdFQdGYd6WfbPTYnOeeEiSv/cTDyC5FvbL1luspSSXPFUYUmaHu8KS0yfjdMpySYp6QIop6TZdC njEnRTvdpVc8Lt0yBW4wS+04HHj3+Fg4pKARnUxdJnVNBL7hSNal4OxPBFLAZ/CRzumn8NR1wrKR xdfy1KLlwDvmw3RaRlDVX3s8h8dGWiUE/BZgAMf82R9IYLF+AAAAAElFTkSuQmCC" + transform="matrix(0.24 0 0 0.24 228.8599 216.5547)" + id="image155"></image><g + id="g157"><radialGradient + id="SVGID_9_" + cx="232.459" + cy="219.7041" + r="2.9995" + gradientTransform="matrix(1 0 0 0.75 0 54.926)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop160" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop162" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop164" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_9_)" + cx="232.459" + cy="219.704" + r="2.999" + id="circle166" /></g></g><g + id="g168"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAt9JREFUeNrsl91rE0EUxTOzm69a 05YmVWsUFdRKRfDBJ32Q+ocLRUTBBx+UYiOitlqRaEos2UT3w3P1jIzrbHZNVuiDAz9CNru5Z+69 M3O2Uvk/fg414zOCDzSxRwJiEJGkTBGKAatgEayCFr8rS4AwAp/BIRhTTDyPCDPzGoOfAhfANdAF zdT/SMBPoEf2wYBiQldmVAEBHjgJzoCL4DoFXAZroO4QMQRvwAvwDOyCtxQ3SWdF5QiQui+DDXAL 3ABXwDqvNygy3RPfwBHog1dgBzwBT8E7ENhCvJwMrIBNsAXugZvgPK83KdKzGlTzu8/fpWc64DSz +RV8oYhfTetPEdFiBu6SDc6+6lgRWZNo8P4Flk0zcMiMSGkSP+MPalQvJbhDASuplVB0RfkU0WUJ RuyNIcsWucoh15bYgFssQYfC1Bz7kcdsVNgrByzNHyJMFs4xA7fZA80CJcgb2uofycYHihlrh4hF ax9YL0mAGT7LKivsKmjLpLXjplVrH1ie0ryzlkWa9SxXnWR5QTv6ocUmWuMDqlLu8Di5ruk1Vzmq LEGdD5QtQln7yI8YespZUXbwzBi6cgzGsRTx14ZkxvFbDJeI9Laa/AMBgRUjTIsI6Yh69ANH0xzR DMMc8x/pMcTwjNIiIlqyHg1Jn8dvUmIWZGKv6THk/Jh4GWqFE3RTHW5auoQsTDj7B+Ah2JOzI8vU RNxQ2pYh8efYO4yHGNDubfNzkHWUJ9YqMf5yiZ7AmyEjtoAdCnjMjIj5TbycBhqxJxosT90SogoE j1kCI+A+Rbw0XmKaxzQlCXjz2GpOXUBITPFDztiUYJsNPzAC8kQklpBDrpQ+sxNZL0MR020wZlaC P2cTSvBHzMAg/f6hCu6qNfaGrJRLNCSbOS8/uyyBfL5n8JFrJy7a7Solpk1DYrynTvWBKcMBsxhw 9nEZL8S2GNtzuJo6YFOG1oor7a28iOdI8gLb47sAAwCDFN6m03jgxgAAAABJRU5ErkJggg==" + transform="matrix(0.24 0 0 0.24 243.7749 216.5547)" + id="image170"></image><g + id="g172"><radialGradient + id="SVGID_10_" + cx="247.374" + cy="219.7041" + r="2.9995" + gradientTransform="matrix(1 0 0 0.75 0 54.926)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop175" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop177" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop179" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_10_)" + cx="247.374" + cy="219.704" + r="2.999" + id="circle181" /></g></g><g + id="g183"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAn9JREFUeNrsl+lrE0EYxvdKjSZW YxEPxBsVrNdHQTxA/KMFBRGPDwoVVIpoq3jUeJUG25qk2fVZ+A28WTabxG4lHzrwgxw78z7zzMw7 73reBDR/jOcCUREhn21LRCy6osfn0kT4BN0h9oiDoiGmTN8Efoum+CHWEBRvRoSbeZXgR8QZcVEc F7syfTcI/kq8Fgviu1jlv3hcET627xXHxFlxWZwXJ8RMxgmPIKkTn8UbMSdeinfiq1hnmUYS4QTs F5fENQSc4rfUgahgT7TFivgk5sVj8VQs4kqukGiAgCviDiKOijr/BUOWL7t/9uGaVyTEighYggsI uI79NQYfdY9FPF8x/WL2xiJLk9hOoelcJehtcYslqI8hYNDGrvP9G5t1PbtRQ+NIg/W/KWaxNNpk DnK5JZ35TzbuCq70ibAu3BBXxWHW1i8hGbpc0+akNLNuBDAtTopz4kBJAuxEC8cPzIlI88BpOoQl Xw1TuDtLsqvZkxbwUA2FjZxEVJYbdROj4mWOpVuzCMoWYGPlxggm4SrfFmFFJOTzDUi2KFY8KIYT sUoSWRadLRDSM0XPMgVPn4guOT0tSN6KVtHd/w8tYWJfqDHeM+m+jBkTeIEaoEmKTUp0oXD80Kjt kU4PkVSqJWTOhIDpFf5APBFLOOPliYhxZgYR00MKmVEEuKWeQ8Q8ruRe5Xb3po7s5CqvDSjnxhVw XzzjFu3k5XTbuuziNYLvZolCk+KHBU8n8QcBL8Rd8VB8yCto8kTEDNBCTIdBg4wQvyD4L6rsdOb3 xKNhhW44IKm4wZaghSAnoIdrHWhz/m3wlOfiI86OXPJPzMvPxLwG/tcX4u3m2l8BBgBQ/dU5d1Za tAAAAABJRU5ErkJggg==" + transform="matrix(0.24 0 0 0.24 199.0298 230.2217)" + id="image185"></image><g + id="g187"><radialGradient + id="SVGID_11_" + cx="202.6289" + cy="233.3711" + r="2.999" + gradientTransform="matrix(1 0 0 0.75 0 58.3428)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop190" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop192" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop194" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_11_)" + cx="202.629" + cy="233.37" + r="2.999" + id="circle196" /></g></g><g + id="g198"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNrsmM9r1EAUx3cz2XW1 il2wLLagIh5aeilUBC967EX/XA/1It5aUaiC2JNY/EWp0lZkG3c3id+Bz0AIaXayibKHDnzIJfPm s29eZl/Sas3paNeYZ0SHaz5OKmIx5pr+azF7fyh6oi+WxXURZGJZiUScim/iWERiUkWwXTFDlxG6 I1bFBnKd3P1jpPbEvviE4JlvBtueUl1xQ9wVa2ITsdtkzOTmxGTsALE34oP4KH6I0TS5tofUJXFT PBCPxToZ67OlZTUWkSmbsffipXglvos/ZXLGI1N2qx6Kp+KRuFcgVYRh/oJYEgNxle08EUNqsbJY SDAnZa8r1FlYoQzc02sFF5n/W/wskzMlAReopydkaoVtDWocL1bqGtv8Bblx0YSgJFt9xNbJXLfG uZctjQEx11gj9M2YDXCFiVviflmAGeQCYtlt/MxTOvbJmEFkFZqSyu9GNr7xEXNP4gbnVK/mFhZl rUdsd0B3fcQCDs3lcw7PJobJrRH4iE37g24qa6VrBK05HRdiTYjVbvI8xtQ1isSyTd4pE5secW6N xEdslGnyDmhd0oazFRF7j7VGPmIxPdQ+HNMWNzUmBfFj3+7C1YDtMG7RT3UaONMS+jErtC1e05tV EksyPdkAyToHbsqW2e51R7ygs42qNooJaU/oPF2TF8wgl2SkdsVz8Y5sJVXFUvryX6TfNXld5HwE U37cWUbqGdfDsto1njVxQjscQ8jc8+SckO25jqipHTK1i+Ro1peR7FM6pKH7StAhC7uam7CQI+J+ J7RNTb0lU7Vf3+b6hXeuPxH8948qF6Pq+CvAAGGezDColMK7AAAAAElFTkSuQmCC" + transform="matrix(0.24 0 0 0.24 213.9448 230.2217)" + id="image200"></image><g + id="g202"><radialGradient + id="SVGID_12_" + cx="217.5439" + cy="233.3711" + r="2.999" + gradientTransform="matrix(1 0 0 0.75 0 58.3428)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop205" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop207" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop209" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_12_)" + cx="217.544" + cy="233.37" + r="2.999" + id="circle211" /></g></g><g + id="g213"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAhCAYAAAC1ONkWAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlRJREFUeNrsmM9LG0EUx5PdjTTW otDYWmoOQm2M/YGXnrxI/3IvnnoRW7TagocotqjQtLYp5pffgc/CECc7u+uGpuDAB2Y32Tff9+bN 7Jstlaa0lXM+E4gIAsd/BqIHpj+cpLBY0Ix4JBbhwYg4I+SvOIdf4jqrwHIOQUvihXgtVsWCQ9gP 8UV8El/Ft6wCyyl+DxH0XLwUb8UbxD0V1RE7ZtCO+I6oj2JPHIlTBPZ94sqe3yIi0hCb4h1Reibm iOK4HDPRuRJnRO+D2BGHRLSXJC7yRGpeNMV7sYXAeQSFCU6FRHIGB56IGveMI/uIGxu5JGFzTN0W wpqIijIsmpDFYZ55hah4xRpxP7MIM4NWWHEbTGGDKY1ybi8VKyVM/l2wYjtM+S1xrvww9x6KulgX K0QqvOOeGafGCnbrjOPScOtmnPA1Hm7Sr+TcjF2Rq2F3nb4zNVzCZsWyWMOr2XFe5WgB9urYX+ba KywkF1bJh8d4WWSrYLdhbdBhmohVrddNtYApLOUZI0jxgp6EMO8YQWlK272wuwobFlHkeVqqMVzC OlaR15mQMO8Yo8L6VpFnypNL0S1YWBe7h4wTVxneiP0RJ+KzaHE9KEjUAHst7J9wnWoqe7z9TVly QL9bwJQOsXOB3X36vbTVhfHqN16Zh49F2xXujK2PnWPsthhnkLYeiz0ziblrVZ55CkV7Ftrk1Q52 z5NmIkowdsUBIrIqz7SltR2la0vUNhxhP3PNP7RCf4CouPIs4jDS9p2U/svj21QfeKf6E8E/+ahy 37K2GwEGAJb/2mQI89WQAAAAAElFTkSuQmCC" + transform="matrix(0.24 0 0 0.24 228.8599 230.2217)" + id="image215"></image><g + id="g217"><radialGradient + id="SVGID_13_" + cx="232.459" + cy="233.3711" + r="2.999" + gradientTransform="matrix(1 0 0 0.75 0 58.3428)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop220" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop222" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop224" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_13_)" + cx="232.459" + cy="233.37" + r="2.999" + id="circle226" /></g></g><g + id="g228"><image + overflow="visible" + opacity="0.25" + a:adobe-blending-mode="multiply" + a:adobe-opacity-share="1" + width="30" + height="30" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhCAYAAABX5MJvAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNrsl2lrE1EUhmdLjSZ2 sYgL4lqsYN0+CuIC4o8WFERcPii0YEsQtYpL3cVgW7PMjO+F55ZhmMlMzATyoRceSDKZc957zplz zzjOBCz3P+/xRE34fE6uWESiJ0I+VybCxekeMSMOizkxlbATwx/xRXwXWwiKRhFhd17H+TFxVlwU J8W+lJ0+zlfFmngjvolNrkXDinAJ+6w4IRbFZXFenBLzqUg4ODGR+CheimXxQrwWn8U2aSolwgo4 KC6Jawg4w28mAsGAmuiI3+KDaInH4qlYJyo7QoISAq6IO4g4Lppc8wrSl66fA0TNSQvJE+GRggsI uE74GxgvW8gB/68l7ouojXVSE/s5N9dxelvcIgXNIQTkFXaT718pViMiyjIa8OiZ/N8US4Q0GLEf 2d5iauYHhWtqpu8PiMINcVUcJbduBY3R9poOT4rpJdteRi1Mi9PinDhUkQC7/Cz7aRH2iTB9YIEb /IqPiSmiu0Sza3gZf2qgcC6jEVUVjWbCR83LOR8CqFpAMu07PrxJOMp3ReSJiOnnfYjH5DdK+sgS sUkT+SW6YxASJoYe46OXFtGjp5uB5JVop8/+EVfMxj4xY7w1m/YywtRmGmqhtlNhNMIs+36O2pB2 fYSmUq+gc8ZsyBzhD8QTsWEikyciomjnETFdMMiUEWBTvYyIFlGJ/ILqNRHZy1HeyBnnhhVwXzzj FO06BSHuUcVbON9Piuy7hlvCudnEXwSsiLvioXhnB5oiEREG2ojpYtRLCXEHOP/JlG12fk88yhp0 /RJNxRrbgDaCrICQqHWhw/OfdG54Lt4T2dIj/8S8/EzMa+DYX4h3l13/BBgABM7SO70ZkkMAAAAA SUVORK5CYII=" + transform="matrix(0.24 0 0 0.24 243.7749 230.2217)" + id="image230"></image><g + id="g232"><radialGradient + id="SVGID_14_" + cx="247.374" + cy="233.3711" + r="2.999" + gradientTransform="matrix(1 0 0 0.75 0 58.3428)" + gradientUnits="userSpaceOnUse"><stop + offset="0.0123" + style="stop-color:#FFFFFF" + id="stop235" /><stop + offset="0.4235" + style="stop-color:#FAFCF6" + id="stop237" /><stop + offset="1" + style="stop-color:#F2F7E8" + id="stop239" /><a:midPointStop + offset="0.0123" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="0.6235" + style="stop-color:#FFFFFF" /><a:midPointStop + offset="1" + style="stop-color:#F2F7E8" /></radialGradient><circle + fill="url(#SVGID_14_)" + cx="247.374" + cy="233.37" + r="2.999" + id="circle241" /></g></g></g></g><path + d="m 529.664,248.155 h 18.498 l -2.809,18.064 h 5.59 37.586 l 2.6,-17.718 c 4.98,-1.091 9.133,-3.455 12.512,-6.693 3.084,4.075 8.566,7.37 18.252,7.37 6.338,0 12.775,-1.807 17.174,-3.687 4.254,2.399 9.463,3.687 15.459,3.687 3.088,0 6.236,-0.355 9.426,-1.023 h 67.135 l 3.354,-24.827 -5.445,-0.764 1.879,-13.356 c 0.371,-2.386 0.449,-4.66 0.449,-6.156 l -0.008,-0.375 c -0.457,-12.191 -8.139,-19.765 -20.045,-19.765 -2.404,0 -4.623,0.314 -6.676,0.852 h -34.189 l -0.035,0.244 c -2.527,-0.701 -5.41,-1.096 -8.686,-1.096 -3.801,0 -7.406,0.555 -10.76,1.598 l 0.105,-0.746 h -12.467 l 1.826,-12.951 H 613.08 l -1.846,7.658 c -1.373,5.704 -2.213,5.793 -4.453,6.03 l -4.508,0.477 c -3.049,-1.424 -6.357,-2.065 -9.602,-2.065 -2.135,0 -4.275,0.284 -6.416,0.852 h -19.291 c 0.502,-1.772 0.775,-3.674 0.775,-5.678 0,-9.601 -6.846,-16.305 -16.646,-16.305 -11.055,0 -18.775,7.721 -18.775,18.776 0,0.951 0.082,1.869 0.219,2.764 -2.135,-0.288 -4.277,-0.409 -5.553,-0.409 -2.053,0 -4.072,0.288 -6.045,0.852 h -31.342 c -2.74,-0.553 -5.641,-0.852 -8.537,-0.852 -7.138,0 -13.492,1.674 -18.808,4.723 l -3.451,-1.461 c -3.711,-1.571 -11.232,-3.262 -18.979,-3.262 -8.933,0 -16.383,2.56 -21.576,7.016 -3.265,-4.473 -8.523,-7.016 -15.228,-7.016 -4.822,0 -9.021,1.477 -12.572,3.44 -2.996,-2.204 -6.796,-3.44 -11.115,-3.44 -2.327,0 -4.48,0.315 -6.476,0.852 h -33.963 l -0.035,0.245 c -2.526,-0.702 -5.41,-1.097 -8.687,-1.097 -20.458,0 -35.307,16.031 -35.307,38.117 0,17.363 10.785,28.149 28.148,28.149 3.087,0 6.236,-0.356 9.426,-1.023 h 88.816 c 3.706,0.676 7.669,1.023 11.154,1.023 8.907,0 16.278,-2.375 21.51,-6.593 4.872,4.252 11.585,6.593 19.728,6.593 3.053,0 6.206,-0.368 9.286,-1.023 h 44.664 2.069 z" + id="path243" + inkscape:connector-curvature="0" + style="fill:#f5f5f5" /><g + id="g245" + transform="translate(0,16)"><g + id="g247"><path + d="m 340.308,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.095,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z" + id="path249" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 394.07,221.7 -0.171,-0.255 1.789,-10.055 2.642,-18.063 c 0.512,-3.749 0.341,-5.623 -1.96,-5.623 -2.642,0 -5.794,2.727 -9.372,5.879 l -2.727,19.512 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 -0.852,6.305 h -18.404 l -0.171,-0.341 1.875,-10.82 2.471,-17.212 c 0.512,-3.237 0.682,-5.453 -1.789,-5.453 -3.238,0 -7.413,3.664 -9.714,5.709 l -2.642,19.512 c -0.17,1.363 -0.17,1.534 1.108,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.767,-1.789 l -4.176,-1.534 0.938,-6.476 h 16.871 l -0.938,6.987 0.256,0.085 c 4.43,-3.749 9.116,-7.924 15.592,-7.924 4.687,0 7.839,2.641 8.18,7.753 l 0.256,0.086 c 4.175,-3.664 9.202,-7.839 15.252,-7.839 6.22,0 8.775,3.152 8.946,9.202 0,1.618 -0.171,3.493 -0.426,5.538 l -3.067,21.897 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.175,0.597 -0.852,6.305 H 394.07 z" + id="path251" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 443.995,190.771 -0.17,-4.431 c 0,-0.682 -0.085,-1.108 -1.022,-1.363 -1.022,-0.256 -2.642,-0.427 -4.771,-0.427 -3.579,0 -6.391,1.108 -6.391,4.09 0,2.727 2.982,3.749 6.731,5.027 6.05,2.045 13.888,4.431 13.888,13.463 0,11.076 -9.372,15.592 -20.193,15.592 -8.009,0 -14.91,-1.959 -16.273,-2.981 l 1.618,-12.355 8.691,0.512 0.255,4.941 c 0,0.597 0.171,1.108 0.938,1.363 1.278,0.427 3.238,0.768 6.05,0.768 4.687,0 7.327,-1.79 7.327,-4.687 0,-3.408 -3.152,-4.175 -8.009,-5.624 -6.135,-1.874 -12.78,-4.26 -12.78,-13.206 0,-10.48 9.116,-14.996 19.597,-14.996 6.646,0 12.866,1.533 15.081,2.471 l -1.704,12.354 -8.863,-0.511 z" + id="path253" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 489.748,218.548 c -4.175,2.386 -10.395,4.175 -16.444,4.175 -13.036,0 -18.575,-7.583 -18.575,-18.574 0,-18.83 11.588,-27.691 25.988,-27.691 6.475,0 11.843,1.874 14.229,3.578 l -1.874,13.377 -8.691,-0.426 -0.255,-5.794 c 0,-0.597 -0.086,-0.938 -0.597,-1.192 -1.022,-0.427 -2.557,-0.597 -4.175,-0.597 -5.624,0 -11.418,4.601 -11.418,17.382 0,7.839 3.493,10.395 8.436,10.395 4.346,0 8.436,-1.448 11.247,-2.556 l 2.129,7.923 z" + id="path255" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 491.364,221.7 0.853,-6.39 3.919,-0.512 c 1.193,-0.17 1.363,-0.426 1.534,-1.704 l 3.578,-25.987 c 0.086,-0.938 -0.085,-1.534 -0.852,-1.789 l -4.261,-1.534 0.938,-6.476 h 16.87 l -1.107,7.669 0.256,0.17 c 3.323,-4.771 8.095,-8.69 13.548,-8.69 1.874,0 5.112,0.341 6.561,0.767 l -2.13,15.507 -9.969,-0.341 -0.256,-4.431 c -0.086,-0.767 -0.256,-1.022 -0.938,-1.022 -1.619,0 -4.26,1.96 -6.646,4.431 l -2.981,21.643 c -0.171,1.363 -0.085,1.619 1.192,1.704 l 8.095,0.682 -0.938,6.305 h -27.266 z" + id="path257" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 536.094,221.7 -0.17,-0.426 2.045,-11.503 3.152,-22.749 c 0.17,-0.938 -0.086,-1.534 -0.853,-1.79 l -4.175,-1.448 0.852,-6.476 h 18.149 l -5.027,35.786 c -0.171,1.363 -0.085,1.534 1.192,1.704 l 4.09,0.597 -0.852,6.305 h -18.403 z m 5.879,-57.598 c 0,-5.453 3.238,-8.775 8.776,-8.775 4.175,0 6.646,2.215 6.646,6.305 0,5.368 -3.322,8.861 -8.861,8.861 -4.176,-0.001 -6.561,-2.387 -6.561,-6.391 z" + id="path259" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 556.796,239.764 -0.17,-0.341 2.471,-14.229 5.282,-38.087 c 0.171,-1.022 -0.085,-1.534 -0.767,-1.789 l -4.175,-1.534 0.938,-6.476 h 17.041 l -1.022,6.816 0.255,0.085 c 5.027,-4.686 10.311,-7.753 15.678,-7.753 7.328,0 12.44,4.686 12.44,17.041 0,11.758 -4.601,29.225 -20.449,29.225 -5.538,0 -8.605,-2.13 -11.759,-4.345 l -1.874,12.78 c -0.085,0.938 0.085,1.278 1.192,1.363 l 8.606,0.853 -0.938,6.39 h -22.749 z m 17.041,-30.247 c 2.13,1.789 4.942,3.322 8.095,3.322 6.901,0 9.458,-9.713 9.458,-17.211 0,-5.027 -1.193,-8.351 -4.431,-8.351 -3.408,0 -7.754,3.664 -10.821,6.391 l -2.301,15.849 z" + id="path261" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 635.777,219.4 c -3.749,1.789 -9.458,3.322 -14.229,3.322 -8.521,0 -12.099,-2.981 -12.099,-9.969 0,-1.107 0.085,-2.386 0.256,-3.749 l 3.066,-22.323 c 0.086,-0.512 0.086,-0.853 -0.511,-0.853 h -5.879 l 1.107,-7.839 c 7.242,-0.767 10.906,-4.431 13.122,-13.633 h 7.924 l -1.704,12.1 c -0.085,0.596 -0.085,0.852 0.597,0.852 h 11.758 l -1.193,8.521 h -12.439 l -2.812,20.364 c -0.171,1.107 -0.256,1.96 -0.256,2.727 0,2.982 1.278,4.26 4.942,4.26 2.385,0 4.771,-0.596 6.816,-1.363 l 1.534,7.583 z" + id="path263" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 671.817,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.094,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z" + id="path265" + inkscape:connector-curvature="0" + style="fill:#383838" /><path + d="m 703.596,221.7 -0.17,-0.255 1.874,-10.396 2.471,-17.723 c 0.512,-3.578 0.341,-5.879 -2.215,-5.879 -3.664,0 -8.18,3.578 -11.077,6.135 l -2.641,19.512 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.768,-1.789 l -4.175,-1.534 0.938,-6.476 h 16.87 l -0.937,6.987 0.255,0.085 c 4.771,-4.09 9.373,-7.924 16.02,-7.924 6.475,0 9.798,3.322 10.054,10.139 0,1.363 -0.085,3.067 -0.341,4.687 l -3.067,21.812 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 L 722,221.7 h -18.404 z" + id="path267" + inkscape:connector-curvature="0" + style="fill:#383838" /></g><g + id="g269"><linearGradient + id="SVGID_15_" + gradientUnits="userSpaceOnUse" + x1="324.1611" + y1="239.7637" + x2="324.1611" + y2="155.3275"><stop + offset="0" + style="stop-color:#000000" + id="stop272" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop274" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 340.308,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.095,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z" + id="path276" + style="fill:url(#SVGID_15_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_16_" + gradientUnits="userSpaceOnUse" + x1="377.45459" + y1="239.7637" + x2="377.45459" + y2="155.3277"><stop + offset="0" + style="stop-color:#000000" + id="stop279" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop281" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 394.07,221.7 -0.171,-0.255 1.789,-10.055 2.642,-18.063 c 0.512,-3.749 0.341,-5.623 -1.96,-5.623 -2.642,0 -5.794,2.727 -9.372,5.879 l -2.727,19.512 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 -0.852,6.305 h -18.404 l -0.171,-0.341 1.875,-10.82 2.471,-17.212 c 0.512,-3.237 0.682,-5.453 -1.789,-5.453 -3.238,0 -7.413,3.664 -9.714,5.709 l -2.642,19.512 c -0.17,1.363 -0.17,1.534 1.108,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.767,-1.789 l -4.176,-1.534 0.938,-6.476 h 16.871 l -0.938,6.987 0.256,0.085 c 4.43,-3.749 9.116,-7.924 15.592,-7.924 4.687,0 7.839,2.641 8.18,7.753 l 0.256,0.086 c 4.175,-3.664 9.202,-7.839 15.252,-7.839 6.22,0 8.775,3.152 8.946,9.202 0,1.618 -0.171,3.493 -0.426,5.538 l -3.067,21.897 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.175,0.597 -0.852,6.305 H 394.07 z" + id="path283" + style="fill:url(#SVGID_16_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_17_" + gradientUnits="userSpaceOnUse" + x1="435.17719" + y1="239.7637" + x2="435.17719" + y2="155.3275"><stop + offset="0" + style="stop-color:#000000" + id="stop286" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop288" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 443.995,190.771 -0.17,-4.431 c 0,-0.682 -0.085,-1.108 -1.022,-1.363 -1.022,-0.256 -2.642,-0.427 -4.771,-0.427 -3.579,0 -6.391,1.108 -6.391,4.09 0,2.727 2.982,3.749 6.731,5.027 6.05,2.045 13.888,4.431 13.888,13.463 0,11.076 -9.372,15.592 -20.193,15.592 -8.009,0 -14.91,-1.959 -16.273,-2.981 l 1.618,-12.355 8.691,0.512 0.255,4.941 c 0,0.597 0.171,1.108 0.938,1.363 1.278,0.427 3.238,0.768 6.05,0.768 4.687,0 7.327,-1.79 7.327,-4.687 0,-3.408 -3.152,-4.175 -8.009,-5.624 -6.135,-1.874 -12.78,-4.26 -12.78,-13.206 0,-10.48 9.116,-14.996 19.597,-14.996 6.646,0 12.866,1.533 15.081,2.471 l -1.704,12.354 -8.863,-0.511 z" + id="path290" + style="fill:url(#SVGID_17_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_18_" + gradientUnits="userSpaceOnUse" + x1="474.83691" + y1="239.7637" + x2="474.83691" + y2="155.3275"><stop + offset="0" + style="stop-color:#000000" + id="stop293" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop295" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 489.748,218.548 c -4.175,2.386 -10.395,4.175 -16.444,4.175 -13.036,0 -18.575,-7.583 -18.575,-18.574 0,-18.83 11.588,-27.691 25.988,-27.691 6.475,0 11.843,1.874 14.229,3.578 l -1.874,13.377 -8.691,-0.426 -0.255,-5.794 c 0,-0.597 -0.086,-0.938 -0.597,-1.192 -1.022,-0.427 -2.557,-0.597 -4.175,-0.597 -5.624,0 -11.418,4.601 -11.418,17.382 0,7.839 3.493,10.395 8.436,10.395 4.346,0 8.436,-1.448 11.247,-2.556 l 2.129,7.923 z" + id="path297" + style="fill:url(#SVGID_18_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_19_" + gradientUnits="userSpaceOnUse" + x1="512.28223" + y1="239.7637" + x2="512.28223" + y2="155.3277"><stop + offset="0" + style="stop-color:#000000" + id="stop300" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop302" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 491.364,221.7 0.853,-6.39 3.919,-0.512 c 1.193,-0.17 1.363,-0.426 1.534,-1.704 l 3.578,-25.987 c 0.086,-0.938 -0.085,-1.534 -0.852,-1.789 l -4.261,-1.534 0.938,-6.476 h 16.87 l -1.107,7.669 0.256,0.17 c 3.323,-4.771 8.095,-8.69 13.548,-8.69 1.874,0 5.112,0.341 6.561,0.767 l -2.13,15.507 -9.969,-0.341 -0.256,-4.431 c -0.086,-0.767 -0.256,-1.022 -0.938,-1.022 -1.619,0 -4.26,1.96 -6.646,4.431 l -2.981,21.643 c -0.171,1.363 -0.085,1.619 1.192,1.704 l 8.095,0.682 -0.938,6.305 h -27.266 z" + id="path304" + style="fill:url(#SVGID_19_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_20_" + gradientUnits="userSpaceOnUse" + x1="546.65918" + y1="239.7637" + x2="546.65918" + y2="155.32719"><stop + offset="0" + style="stop-color:#000000" + id="stop307" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop309" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 536.094,221.7 -0.17,-0.426 2.045,-11.503 3.152,-22.749 c 0.17,-0.938 -0.086,-1.534 -0.853,-1.79 l -4.175,-1.448 0.852,-6.476 h 18.149 l -5.027,35.786 c -0.171,1.363 -0.085,1.534 1.192,1.704 l 4.09,0.597 -0.852,6.305 h -18.403 z m 5.879,-57.598 c 0,-5.453 3.238,-8.775 8.776,-8.775 4.175,0 6.646,2.215 6.646,6.305 0,5.368 -3.322,8.861 -8.861,8.861 -4.176,-0.001 -6.561,-2.387 -6.561,-6.391 z" + id="path311" + style="fill:url(#SVGID_20_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_21_" + gradientUnits="userSpaceOnUse" + x1="580.69629" + y1="239.7637" + x2="580.69629" + y2="155.32719"><stop + offset="0" + style="stop-color:#000000" + id="stop314" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop316" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 556.796,239.764 -0.17,-0.341 2.471,-14.229 5.282,-38.087 c 0.171,-1.022 -0.085,-1.534 -0.767,-1.789 l -4.175,-1.534 0.938,-6.476 h 17.041 l -1.022,6.816 0.255,0.085 c 5.027,-4.686 10.311,-7.753 15.678,-7.753 7.328,0 12.44,4.686 12.44,17.041 0,11.758 -4.601,29.225 -20.449,29.225 -5.538,0 -8.605,-2.13 -11.759,-4.345 l -1.874,12.78 c -0.085,0.938 0.085,1.278 1.192,1.363 l 8.606,0.853 -0.938,6.39 h -22.749 z m 17.041,-30.247 c 2.13,1.789 4.942,3.322 8.095,3.322 6.901,0 9.458,-9.713 9.458,-17.211 0,-5.027 -1.193,-8.351 -4.431,-8.351 -3.408,0 -7.754,3.664 -10.821,6.391 l -2.301,15.849 z" + id="path318" + style="fill:url(#SVGID_21_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_22_" + gradientUnits="userSpaceOnUse" + x1="622.7832" + y1="239.7637" + x2="622.7832" + y2="155.3268"><stop + offset="0" + style="stop-color:#000000" + id="stop321" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop323" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 635.777,219.4 c -3.749,1.789 -9.458,3.322 -14.229,3.322 -8.521,0 -12.099,-2.981 -12.099,-9.969 0,-1.107 0.085,-2.386 0.256,-3.749 l 3.066,-22.323 c 0.086,-0.512 0.086,-0.853 -0.511,-0.853 h -5.879 l 1.107,-7.839 c 7.242,-0.767 10.906,-4.431 13.122,-13.633 h 7.924 l -1.704,12.1 c -0.085,0.596 -0.085,0.852 0.597,0.852 h 11.758 l -1.193,8.521 h -12.439 l -2.812,20.364 c -0.171,1.107 -0.256,1.96 -0.256,2.727 0,2.982 1.278,4.26 4.942,4.26 2.385,0 4.771,-0.596 6.816,-1.363 l 1.534,7.583 z" + id="path325" + style="fill:url(#SVGID_22_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_23_" + gradientUnits="userSpaceOnUse" + x1="655.6709" + y1="239.7637" + x2="655.6709" + y2="155.3275"><stop + offset="0" + style="stop-color:#000000" + id="stop328" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop330" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 671.817,218.463 c -5.538,2.556 -11.588,4.26 -17.638,4.26 -13.377,0 -18.148,-7.839 -18.148,-18.148 0,-17.893 11.418,-28.117 25.307,-28.117 9.372,0 13.973,4.26 13.973,11.247 0,12.184 -12.865,15.763 -26.157,17.126 0.255,4.346 2.045,8.35 8.435,8.35 3.068,0 7.243,-0.937 12.355,-3.067 l 1.873,8.349 z m -8.094,-29.567 c 0,-2.045 -1.448,-3.237 -4.09,-3.237 -4.771,0 -8.69,4.175 -9.969,10.906 3.664,-0.511 14.059,-2.3 14.059,-7.669 z" + id="path332" + style="fill:url(#SVGID_23_)" + inkscape:connector-curvature="0" /><linearGradient + id="SVGID_24_" + gradientUnits="userSpaceOnUse" + x1="697.92969" + y1="239.7637" + x2="697.92969" + y2="155.3277"><stop + offset="0" + style="stop-color:#000000" + id="stop335" /><stop + offset="1" + style="stop-color:#000000;stop-opacity:0" + id="stop337" /><a:midPointStop + offset="0" + style="stop-color:#000000" /><a:midPointStop + offset="0.6933" + style="stop-color:#000000" /><a:midPointStop + offset="1" + style="stop-color:#000000;stop-opacity:0" /></linearGradient><path + d="m 703.596,221.7 -0.17,-0.255 1.874,-10.396 2.471,-17.723 c 0.512,-3.578 0.341,-5.879 -2.215,-5.879 -3.664,0 -8.18,3.578 -11.077,6.135 l -2.641,19.512 c -0.171,1.363 -0.171,1.534 1.107,1.704 l 4.26,0.597 -0.852,6.305 h -23.347 l 0.853,-6.39 3.749,-0.512 c 1.107,-0.17 1.363,-0.426 1.533,-1.704 l 3.579,-25.987 c 0.17,-0.938 0,-1.534 -0.768,-1.789 l -4.175,-1.534 0.938,-6.476 h 16.87 l -0.937,6.987 0.255,0.085 c 4.771,-4.09 9.373,-7.924 16.02,-7.924 6.475,0 9.798,3.322 10.054,10.139 0,1.363 -0.085,3.067 -0.341,4.687 l -3.067,21.812 c -0.171,1.363 -0.171,1.534 1.022,1.704 l 4.26,0.597 L 722,221.7 h -18.404 z" + id="path339" + style="fill:url(#SVGID_24_)" + inkscape:connector-curvature="0" /></g></g><g + id="g4141" + transform="matrix(0.81856441,0,0,0.81856441,79.234731,-94.128741)"><g + id="g4143"></g><g + id="g4165"><linearGradient + y2="155.3275" + x2="324.1611" + y1="239.7637" + x1="324.1611" + gradientUnits="userSpaceOnUse" + id="linearGradient4167"><stop + id="stop4169" + style="stop-color:#000000" + offset="0" /><stop + id="stop4171" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3277" + x2="377.45459" + y1="239.7637" + x1="377.45459" + gradientUnits="userSpaceOnUse" + id="linearGradient4175"><stop + id="stop4177" + style="stop-color:#000000" + offset="0" /><stop + id="stop4179" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3275" + x2="435.17719" + y1="239.7637" + x1="435.17719" + gradientUnits="userSpaceOnUse" + id="linearGradient4183"><stop + id="stop4185" + style="stop-color:#000000" + offset="0" /><stop + id="stop4187" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3275" + x2="474.83691" + y1="239.7637" + x1="474.83691" + gradientUnits="userSpaceOnUse" + id="linearGradient4191"><stop + id="stop4193" + style="stop-color:#000000" + offset="0" /><stop + id="stop4195" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3277" + x2="512.28223" + y1="239.7637" + x1="512.28223" + gradientUnits="userSpaceOnUse" + id="linearGradient4199"><stop + id="stop4201" + style="stop-color:#000000" + offset="0" /><stop + id="stop4203" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.32719" + x2="546.65918" + y1="239.7637" + x1="546.65918" + gradientUnits="userSpaceOnUse" + id="linearGradient4207"><stop + id="stop4209" + style="stop-color:#000000" + offset="0" /><stop + id="stop4211" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.32719" + x2="580.69629" + y1="239.7637" + x1="580.69629" + gradientUnits="userSpaceOnUse" + id="linearGradient4215"><stop + id="stop4217" + style="stop-color:#000000" + offset="0" /><stop + id="stop4219" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3268" + x2="622.7832" + y1="239.7637" + x1="622.7832" + gradientUnits="userSpaceOnUse" + id="linearGradient4223"><stop + id="stop4225" + style="stop-color:#000000" + offset="0" /><stop + id="stop4227" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3275" + x2="655.6709" + y1="239.7637" + x1="655.6709" + gradientUnits="userSpaceOnUse" + id="linearGradient4231"><stop + id="stop4233" + style="stop-color:#000000" + offset="0" /><stop + id="stop4235" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient><linearGradient + y2="155.3277" + x2="697.92969" + y1="239.7637" + x1="697.92969" + gradientUnits="userSpaceOnUse" + id="linearGradient4239"><stop + id="stop4241" + style="stop-color:#000000" + offset="0" /><stop + id="stop4243" + style="stop-color:#000000;stop-opacity:0" + offset="1" /><a:midPointStop + style="stop-color:#000000" + offset="0" /><a:midPointStop + style="stop-color:#000000" + offset="0.6933" /><a:midPointStop + style="stop-color:#000000;stop-opacity:0" + offset="1" /></linearGradient></g></g></svg> + </a> + + <div class="spinner" id='spinner'></div> + <div class="emscripten" id="status">Downloading...</div> + +<span id='controls'> + <span><input type="checkbox" id="resize">Resize canvas</span> + <span><input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer </span> + <span><input type="button" value="Fullscreen" onclick="Module.requestFullScreen(document.getElementById('pointerLock').checked, + document.getElementById('resize').checked)"> + </span> +</span> + + <div class="emscripten"> + <progress value="0" max="100" id="progress" hidden=1></progress> + </div> + + + <div class="emscripten_border"> + <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas> + </div> + <textarea id="output" rows="8"></textarea> + + <script type='text/javascript'> + var statusElement = document.getElementById('status'); + var progressElement = document.getElementById('progress'); + var spinnerElement = document.getElementById('spinner'); + + var Module = { + TOTAL_MEMORY: $GODOTTMEM, + preRun: [], + postRun: [], + print: (function() { + var element = document.getElementById('output'); + if (element) element.value = ''; // clear browser cache + return function(text) { + if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' '); + // These replacements are necessary if you render to raw HTML + //text = text.replace(/&/g, "&"); + //text = text.replace(/</g, "<"); + //text = text.replace(/>/g, ">"); + //text = text.replace('\n', '<br>', 'g'); + console.log(text); + if (element) { + element.value += text + "\n"; + element.scrollTop = element.scrollHeight; // focus on bottom + } + }; + })(), + printErr: function(text) { + if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' '); + if (0) { // XXX disabled for safety typeof dump == 'function') { + dump(text + '\n'); // fast, straight to the real console + } else { + console.error(text); + } + }, + canvas: (function() { + var canvas = document.getElementById('canvas'); + + // As a default initial behavior, pop up an alert when webgl context is lost. To make your + // application robust, you may want to override this behavior before shipping! + // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2 + canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false); + + return canvas; + })(), + setStatus: function(text) { + if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' }; + if (text === Module.setStatus.text) return; + var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); + var now = Date.now(); + if (m && now - Date.now() < 30) return; // if this is a progress update, skip it if too soon + if (m) { + text = m[1]; + progressElement.value = parseInt(m[2])*100; + progressElement.max = parseInt(m[4])*100; + progressElement.hidden = false; + spinnerElement.hidden = false; + } else { + progressElement.value = null; + progressElement.max = null; + progressElement.hidden = true; + if (!text) spinnerElement.style.display = 'none'; + } + statusElement.innerHTML = text; + }, + totalDependencies: 0, + monitorRunDependencies: function(left) { + this.totalDependencies = Math.max(this.totalDependencies, left); + Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.'); + } + }; + Module.setStatus('Downloading...'); + window.onerror = function(event) { + // TODO: do not warn on ok events like simulating an infinite loop or exitStatus + Module.setStatus('Exception thrown, see JavaScript console'); + spinnerElement.style.display = 'none'; + Module.setStatus = function(text) { + if (text) Module.printErr('[post-exception status] ' + text); + }; + }; + </script> + <script type="text/javascript" src="$GODOTFS"></script> + <script> + + (function() { + var memoryInitializer = '$GODOTMEM'; + if (typeof Module['locateFile'] === 'function') { + memoryInitializer = Module['locateFile'](memoryInitializer); + } else if (Module['memoryInitializerPrefixURL']) { + memoryInitializer = Module['memoryInitializerPrefixURL'] + memoryInitializer; + } + var xhr = Module['memoryInitializerRequest'] = new XMLHttpRequest(); + xhr.open('GET', memoryInitializer, true); + xhr.responseType = 'arraybuffer'; + xhr.send(null); + })(); + + var script = document.createElement('script'); + script.src = "$GODOTJS"; + document.body.appendChild(script); + +</script> + </body> +</html> diff --git a/tools/html_fs/filesystem.js b/tools/html_fs/godotfs.js index 93cc30556b..93cc30556b 100644 --- a/tools/html_fs/filesystem.js +++ b/tools/html_fs/godotfs.js diff --git a/tools/osx_template.app/Contents/Resources/icon.icns b/tools/osx_template.app/Contents/Resources/icon.icns Binary files differindex 18bc68d6ea..4a3dc0415a 100644 --- a/tools/osx_template.app/Contents/Resources/icon.icns +++ b/tools/osx_template.app/Contents/Resources/icon.icns diff --git a/tools/pck/SCsub b/tools/pck/SCsub index b1fed9a472..cf98ae145d 100644 --- a/tools/pck/SCsub +++ b/tools/pck/SCsub @@ -2,4 +2,3 @@ Import('env') if env["tools"] == "yes": env.add_source_files(env.tool_sources, "*.cpp") - diff --git a/tools/pck/pck_packer.cpp b/tools/pck/pck_packer.cpp index 09611b3a93..d398fefb5f 100644 --- a/tools/pck/pck_packer.cpp +++ b/tools/pck/pck_packer.cpp @@ -136,7 +136,7 @@ Error PCKPacker::flush(bool p_verbose) { count += 1; if (p_verbose) { if (count % 100 == 0) { - printf("%i/%i (%.2f\%)\r", count, files.size(), float(count) / files.size() * 100); + printf("%i/%i (%.2f)\r", count, files.size(), float(count) / files.size() * 100); fflush(stdout); }; }; diff --git a/tools/pck/pck_packer.h b/tools/pck/pck_packer.h index 76752a6170..2bb51128e9 100644 --- a/tools/pck/pck_packer.h +++ b/tools/pck/pck_packer.h @@ -1,10 +1,10 @@ -#include "core/object.h" +#include "core/reference.h" class FileAccess; -class PCKPacker : public Object { +class PCKPacker : public Reference { - OBJ_TYPE(PCKPacker, Object); + OBJ_TYPE(PCKPacker, Reference); FileAccess* file; int alignment; diff --git a/tools/pe_bliss/README b/tools/pe_bliss/README new file mode 100644 index 0000000000..d5d1355444 --- /dev/null +++ b/tools/pe_bliss/README @@ -0,0 +1,84 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + + +Открытая бесплатная библиотека для работы с PE-файлами PE Bliss. +Бесплатна к использованию, модификации и распространению. +Автор: DX +(c) DX 2011-2012, kaimi.ru + +Совместимость: Windows, Linux + +Возможности: +[+] Создание PE или PE+ файла с нуля +[+] Чтение 32-разрядных и 64-разрядных PE-файлов (PE, PE+) и единообразная работа с ними +[+] Пересборка 32-разрядных и 64-разрядных PE-файлов +[+] Работа с директориями и заголовками +[+] Конвертирование адресов +[+] Чтение и редактирование секций PE-файла +[+] Чтение и редактирование таблицы импортов +[+] Чтение и редактирование таблицы экспортов +[+] Чтение и редактирование таблиц релокаций +[+] Чтение и редактирование ресурсов +[+] Чтение и редактирование TLS +[+] Чтение и редактирование конфигурации образа (image config) +[+] Чтение базовой информации .NET +[+] Чтение и редактирование информации о привязанном импорте +[+] Чтение директории исключений (только PE+) +[+] Чтение отладочной директории с расширенной информацией +[+] Вычисление энтропии +[+] Изменение файлового выравнивания +[+] Изменение базового адреса загрузки +[+] Работа с DOS Stub'ом и Rich overlay +[+] Высокоуровневое чтение ресурсов: картинки, иконки, курсоры, информация о версии, строковые таблицы, таблицы сообщений +[+] Высокоуровневое редактирование ресурсов: картинки, иконки, курсоры, информация о версии + +[English] +Open a free library for working with PE-file PE Bliss. +Free to use, modify, and distribute. +Author: DX +(c) DX 2011-2012, kaimi.ru +Compatibility: Windows, Linux + +### Capabilities: +[+] Creation of PE or PE + file from scratch +[+] Reading the 32-bit and 64-bit PE-file (PE, PE +) and uniform working with them +[+] Rebuild 32-bit and 64-bit PE-files +[+] Working with the directors and titles +[+] Converting addresses +[+] Reading and editing sections of PE-file +[+] Reading and editing the import table +[+] Reading and editing tables exports +[+] Reading and editing tables relocations +[+] Reading and editing resources +[+] Reading and editing TLS +[+] Reading and editing the configuration of the image (image config) +[+] Reading data base .NET +[+] Reading and editing information about tethered import +[+] Read the directory exceptions (only PE +) +[+] Read debug directories with extended information +[+] The calculation of entropy +[+] Changing file alignment +[+] Change the base load address +[+] Support of DOS Stub'om and Rich overlay +[+] High-level reading resources: images, icons, cursors, version information, string tables, message table +[+] High-level editing resources: images, icons, cursors, version information
\ No newline at end of file diff --git a/tools/pe_bliss/SCsub b/tools/pe_bliss/SCsub new file mode 100644 index 0000000000..34524f10ef --- /dev/null +++ b/tools/pe_bliss/SCsub @@ -0,0 +1,5 @@ +Import('env') + +env.add_source_files(env.tool_sources,"*.cpp") + +Export('env') diff --git a/tools/pe_bliss/entropy.cpp b/tools/pe_bliss/entropy.cpp new file mode 100644 index 0000000000..acefa63e83 --- /dev/null +++ b/tools/pe_bliss/entropy.cpp @@ -0,0 +1,111 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <cmath> +#include "entropy.h" +#include "utils.h" + +namespace pe_bliss +{ +//Calculates entropy for PE image section +double entropy_calculator::calculate_entropy(const section& s) +{ + if(s.get_raw_data().empty()) //Don't count entropy for empty sections + throw pe_exception("Section is empty", pe_exception::section_is_empty); + + return calculate_entropy(s.get_raw_data().data(), s.get_raw_data().length()); +} + +//Calculates entropy for istream (from current position of stream) +double entropy_calculator::calculate_entropy(std::istream& file) +{ + uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes + + if(file.bad()) + throw pe_exception("Stream is bad", pe_exception::stream_is_bad); + + std::streamoff pos = file.tellg(); + + std::streamoff length = pe_utils::get_file_size(file); + length -= file.tellg(); + + if(!length) //Don't calculate entropy for empty buffers + throw pe_exception("Data length is zero", pe_exception::data_is_empty); + + //Count bytes + for(std::streamoff i = 0; i != length; ++i) + ++byte_count[static_cast<unsigned char>(file.get())]; + + file.seekg(pos); + + return calculate_entropy(byte_count, length); +} + +//Calculates entropy for data block +double entropy_calculator::calculate_entropy(const char* data, size_t length) +{ + uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes + + if(!length) //Don't calculate entropy for empty buffers + throw pe_exception("Data length is zero", pe_exception::data_is_empty); + + //Count bytes + for(size_t i = 0; i != length; ++i) + ++byte_count[static_cast<unsigned char>(data[i])]; + + return calculate_entropy(byte_count, length); +} + +//Calculates entropy for this PE file (only section data) +double entropy_calculator::calculate_entropy(const pe_base& pe) +{ + uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes + + size_t total_data_length = 0; + + //Count bytes for each section + for(section_list::const_iterator it = pe.get_image_sections().begin(); it != pe.get_image_sections().end(); ++it) + { + const std::string& data = (*it).get_raw_data(); + size_t length = data.length(); + total_data_length += length; + for(size_t i = 0; i != length; ++i) + ++byte_count[static_cast<unsigned char>(data[i])]; + } + + return calculate_entropy(byte_count, total_data_length); +} + +//Calculates entropy from bytes count +double entropy_calculator::calculate_entropy(const uint32_t byte_count[256], std::streamoff total_length) +{ + double entropy = 0.; //Entropy result value + //Calculate entropy + for(uint32_t i = 0; i < 256; ++i) + { + double temp = static_cast<double>(byte_count[i]) / total_length; + if(temp > 0.) + entropy += std::abs(temp * (std::log(temp) * pe_utils::log_2)); + } + + return entropy; +} +} diff --git a/tools/pe_bliss/entropy.h b/tools/pe_bliss/entropy.h new file mode 100644 index 0000000000..7d225a3e32 --- /dev/null +++ b/tools/pe_bliss/entropy.h @@ -0,0 +1,51 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <istream> +#include "pe_base.h" + +namespace pe_bliss +{ +class entropy_calculator +{ +public: + //Calculates entropy for PE image section + static double calculate_entropy(const section& s); + + //Calculates entropy for istream (from current position of stream) + static double calculate_entropy(std::istream& file); + + //Calculates entropy for data block + static double calculate_entropy(const char* data, size_t length); + + //Calculates entropy for this PE file (only section data) + static double calculate_entropy(const pe_base& pe); + +private: + entropy_calculator(); + entropy_calculator(const entropy_calculator&); + entropy_calculator& operator=(const entropy_calculator&); + + //Calculates entropy from bytes count + static double calculate_entropy(const uint32_t byte_count[256], std::streamoff total_length); +}; +} diff --git a/tools/pe_bliss/file_version_info.cpp b/tools/pe_bliss/file_version_info.cpp new file mode 100644 index 0000000000..3f2ba454b4 --- /dev/null +++ b/tools/pe_bliss/file_version_info.cpp @@ -0,0 +1,440 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "file_version_info.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Default constructor +file_version_info::file_version_info() + :file_version_ms_(0), file_version_ls_(0), + product_version_ms_(0), product_version_ls_(0), + file_flags_(0), + file_os_(0), + file_type_(0), file_subtype_(0), + file_date_ms_(0), file_date_ls_(0) +{} + +//Constructor from Windows fixed version info structure +file_version_info::file_version_info(const vs_fixedfileinfo& info) + :file_version_ms_(info.dwFileVersionMS), file_version_ls_(info.dwFileVersionLS), + product_version_ms_(info.dwProductVersionMS), product_version_ls_(info.dwProductVersionLS), + file_flags_(info.dwFileFlags), + file_os_(info.dwFileOS), + file_type_(info.dwFileType), file_subtype_(info.dwFileSubtype), + file_date_ms_(info.dwFileDateMS), file_date_ls_(info.dwFileDateLS) +{} + +//Returns true if file is debug-built +bool file_version_info::is_debug() const +{ + return file_flags_ & vs_ff_debug ? true : false; +} + +//Returns true if file is release-built +bool file_version_info::is_prerelease() const +{ + return file_flags_ & vs_ff_prerelease ? true : false; +} + +//Returns true if file is patched +bool file_version_info::is_patched() const +{ + return file_flags_ & vs_ff_patched ? true : false; +} + +//Returns true if private build +bool file_version_info::is_private_build() const +{ + return file_flags_ & vs_ff_privatebuild ? true : false; +} + +//Returns true if special build +bool file_version_info::is_special_build() const +{ + return file_flags_ & vs_ff_specialbuild ? true : false; +} + +//Returns true if info inferred +bool file_version_info::is_info_inferred() const +{ + return file_flags_ & vs_ff_infoinferred ? true : false; +} + +//Retuens file flags (raw DWORD) +uint32_t file_version_info::get_file_flags() const +{ + return file_flags_; +} + +//Returns file version most significant DWORD +uint32_t file_version_info::get_file_version_ms() const +{ + return file_version_ms_; +} + +//Returns file version least significant DWORD +uint32_t file_version_info::get_file_version_ls() const +{ + return file_version_ls_; +} + +//Returns product version most significant DWORD +uint32_t file_version_info::get_product_version_ms() const +{ + return product_version_ms_; +} + +//Returns product version least significant DWORD +uint32_t file_version_info::get_product_version_ls() const +{ + return product_version_ls_; +} + +//Returns file OS type (raw DWORD) +uint32_t file_version_info::get_file_os_raw() const +{ + return file_os_; +} + +//Returns file OS type +file_version_info::file_os_type file_version_info::get_file_os() const +{ + //Determine file operation system type + switch(file_os_) + { + case vos_dos: + return file_os_dos; + + case vos_os216: + return file_os_os216; + + case vos_os232: + return file_os_os232; + + case vos_nt: + return file_os_nt; + + case vos_wince: + return file_os_wince; + + case vos__windows16: + return file_os_win16; + + case vos__pm16: + return file_os_pm16; + + case vos__pm32: + return file_os_pm32; + + case vos__windows32: + return file_os_win32; + + case vos_dos_windows16: + return file_os_dos_win16; + + case vos_dos_windows32: + return file_os_dos_win32; + + case vos_os216_pm16: + return file_os_os216_pm16; + + case vos_os232_pm32: + return file_os_os232_pm32; + + case vos_nt_windows32: + return file_os_nt_win32; + } + + return file_os_unknown; +} + +//Returns file type (raw DWORD) +uint32_t file_version_info::get_file_type_raw() const +{ + return file_type_; +} + +//Returns file type +file_version_info::file_type file_version_info::get_file_type() const +{ + //Determine file type + switch(file_type_) + { + case vft_app: + return file_type_application; + + case vft_dll: + return file_type_dll; + + case vft_drv: + return file_type_driver; + + case vft_font: + return file_type_font; + + case vft_vxd: + return file_type_vxd; + + case vft_static_lib: + return file_type_static_lib; + } + + return file_type_unknown; +} + +//Returns file subtype (usually non-zero for drivers and fonts) +uint32_t file_version_info::get_file_subtype() const +{ + return file_subtype_; +} + +//Returns file date most significant DWORD +uint32_t file_version_info::get_file_date_ms() const +{ + return file_date_ms_; +} + +//Returns file date least significant DWORD +uint32_t file_version_info::get_file_date_ls() const +{ + return file_date_ls_; +} + +//Helper to set file flag +void file_version_info::set_file_flag(uint32_t flag) +{ + file_flags_ |= flag; +} + +//Helper to clear file flag +void file_version_info::clear_file_flag(uint32_t flag) +{ + file_flags_ &= ~flag; +} + +//Helper to set or clear file flag +void file_version_info::set_file_flag(uint32_t flag, bool set_flag) +{ + set_flag ? set_file_flag(flag) : clear_file_flag(flag); +} + +//Sets if file is debug-built +void file_version_info::set_debug(bool debug) +{ + set_file_flag(vs_ff_debug, debug); +} + +//Sets if file is prerelease +void file_version_info::set_prerelease(bool prerelease) +{ + set_file_flag(vs_ff_prerelease, prerelease); +} + +//Sets if file is patched +void file_version_info::set_patched(bool patched) +{ + set_file_flag(vs_ff_patched, patched); +} + +//Sets if private build +void file_version_info::set_private_build(bool private_build) +{ + set_file_flag(vs_ff_privatebuild, private_build); +} + +//Sets if special build +void file_version_info::set_special_build(bool special_build) +{ + set_file_flag(vs_ff_specialbuild, special_build); +} + +//Sets if info inferred +void file_version_info::set_info_inferred(bool info_inferred) +{ + set_file_flag(vs_ff_infoinferred, info_inferred); +} + +//Sets flags (raw DWORD) +void file_version_info::set_file_flags(uint32_t file_flags) +{ + file_flags_ = file_flags; +} + +//Sets file version most significant DWORD +void file_version_info::set_file_version_ms(uint32_t file_version_ms) +{ + file_version_ms_ = file_version_ms; +} + +//Sets file version least significant DWORD +void file_version_info::set_file_version_ls(uint32_t file_version_ls) +{ + file_version_ls_ = file_version_ls; +} + +//Sets product version most significant DWORD +void file_version_info::set_product_version_ms(uint32_t product_version_ms) +{ + product_version_ms_ = product_version_ms; +} + +//Sets product version least significant DWORD +void file_version_info::set_product_version_ls(uint32_t product_version_ls) +{ + product_version_ls_ = product_version_ls; +} + +//Sets file OS type (raw DWORD) +void file_version_info::set_file_os_raw(uint32_t file_os) +{ + file_os_ = file_os; +} + +//Sets file OS type +void file_version_info::set_file_os(file_os_type file_os) +{ + //Determine file operation system type + switch(file_os) + { + case file_os_dos: + file_os_ = vos_dos; + return; + + case file_os_os216: + file_os_ = vos_os216; + return; + + case file_os_os232: + file_os_ = vos_os232; + return; + + case file_os_nt: + file_os_ = vos_nt; + return; + + case file_os_wince: + file_os_ = vos_wince; + return; + + case file_os_win16: + file_os_ = vos__windows16; + return; + + case file_os_pm16: + file_os_ = vos__pm16; + return; + + case file_os_pm32: + file_os_ = vos__pm32; + return; + + case file_os_win32: + file_os_ = vos__windows32; + return; + + case file_os_dos_win16: + file_os_ = vos_dos_windows16; + return; + + case file_os_dos_win32: + file_os_ = vos_dos_windows32; + return; + + case file_os_os216_pm16: + file_os_ = vos_os216_pm16; + return; + + case file_os_os232_pm32: + file_os_ = vos_os232_pm32; + return; + + case file_os_nt_win32: + file_os_ = vos_nt_windows32; + return; + + default: + return; + } +} + +//Sets file type (raw DWORD) +void file_version_info::set_file_type_raw(uint32_t file_type) +{ + file_type_ = file_type; +} + +//Sets file type +void file_version_info::set_file_type(file_type file_type) +{ + //Determine file type + switch(file_type) + { + case file_type_application: + file_type_ = vft_app; + return; + + case file_type_dll: + file_type_ = vft_dll; + return; + + case file_type_driver: + file_type_ = vft_drv; + return; + + case file_type_font: + file_type_ = vft_font; + return; + + case file_type_vxd: + file_type_ = vft_vxd; + return; + + case file_type_static_lib: + file_type_ = vft_static_lib; + return; + + default: + return; + } +} + +//Sets file subtype (usually non-zero for drivers and fonts) +void file_version_info::set_file_subtype(uint32_t file_subtype) +{ + file_subtype_ = file_subtype; +} + +//Sets file date most significant DWORD +void file_version_info::set_file_date_ms(uint32_t file_date_ms) +{ + file_date_ms_ = file_date_ms; +} + +//Sets file date least significant DWORD +void file_version_info::set_file_date_ls(uint32_t file_date_ls) +{ + file_date_ls_ = file_date_ls; +} +} diff --git a/tools/pe_bliss/file_version_info.h b/tools/pe_bliss/file_version_info.h new file mode 100644 index 0000000000..d898351ba1 --- /dev/null +++ b/tools/pe_bliss/file_version_info.h @@ -0,0 +1,199 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <map> +#include "stdint_defs.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +//Structure representing fixed file version info +class file_version_info +{ +public: + //Enumeration of file operating system types + enum file_os_type + { + file_os_unknown, + file_os_dos, + file_os_os216, + file_os_os232, + file_os_nt, + file_os_wince, + file_os_win16, + file_os_pm16, + file_os_pm32, + file_os_win32, + file_os_dos_win16, + file_os_dos_win32, + file_os_os216_pm16, + file_os_os232_pm32, + file_os_nt_win32 + }; + + //Enumeration of file types + enum file_type + { + file_type_unknown, + file_type_application, + file_type_dll, + file_type_driver, + file_type_font, + file_type_vxd, + file_type_static_lib + }; + +public: + //Default constructor + file_version_info(); + //Constructor from Windows fixed version info structure + explicit file_version_info(const pe_win::vs_fixedfileinfo& info); + +public: //Getters + //Returns true if file is debug-built + bool is_debug() const; + //Returns true if file is prerelease + bool is_prerelease() const; + //Returns true if file is patched + bool is_patched() const; + //Returns true if private build + bool is_private_build() const; + //Returns true if special build + bool is_special_build() const; + //Returns true if info inferred + bool is_info_inferred() const; + //Retuens file flags (raw DWORD) + uint32_t get_file_flags() const; + + //Returns file version most significant DWORD + uint32_t get_file_version_ms() const; + //Returns file version least significant DWORD + uint32_t get_file_version_ls() const; + //Returns product version most significant DWORD + uint32_t get_product_version_ms() const; + //Returns product version least significant DWORD + uint32_t get_product_version_ls() const; + + //Returns file OS type (raw DWORD) + uint32_t get_file_os_raw() const; + //Returns file OS type + file_os_type get_file_os() const; + + //Returns file type (raw DWORD) + uint32_t get_file_type_raw() const; + //Returns file type + file_type get_file_type() const; + + //Returns file subtype (usually non-zero for drivers and fonts) + uint32_t get_file_subtype() const; + + //Returns file date most significant DWORD + uint32_t get_file_date_ms() const; + //Returns file date least significant DWORD + uint32_t get_file_date_ls() const; + + //Returns file version string + template<typename T> + const std::basic_string<T> get_file_version_string() const + { + return get_version_string<T>(file_version_ms_, file_version_ls_); + } + + //Returns product version string + template<typename T> + const std::basic_string<T> get_product_version_string() const + { + return get_version_string<T>(product_version_ms_, product_version_ls_); + } + +public: //Setters + //Sets if file is debug-built + void set_debug(bool debug); + //Sets if file is prerelease + void set_prerelease(bool prerelease); + //Sets if file is patched + void set_patched(bool patched); + //Sets if private build + void set_private_build(bool private_build); + //Sets if special build + void set_special_build(bool special_build); + //Sets if info inferred + void set_info_inferred(bool info_inferred); + //Sets flags (raw DWORD) + void set_file_flags(uint32_t file_flags); + + //Sets file version most significant DWORD + void set_file_version_ms(uint32_t file_version_ms); + //Sets file version least significant DWORD + void set_file_version_ls(uint32_t file_version_ls); + //Sets product version most significant DWORD + void set_product_version_ms(uint32_t product_version_ms); + //Sets product version least significant DWORD + void set_product_version_ls(uint32_t product_version_ls); + + //Sets file OS type (raw DWORD) + void set_file_os_raw(uint32_t file_os); + //Sets file OS type + void set_file_os(file_os_type file_os); + + //Sets file type (raw DWORD) + void set_file_type_raw(uint32_t file_type); + //Sets file type + void set_file_type(file_type file_type); + + //Sets file subtype (usually non-zero for drivers and fonts) + void set_file_subtype(uint32_t file_subtype); + + //Sets file date most significant DWORD + void set_file_date_ms(uint32_t file_date_ms); + //Sets file date least significant DWORD + void set_file_date_ls(uint32_t file_date_ls); + +private: + //Helper to convert version DWORDs to string + template<typename T> + static const std::basic_string<T> get_version_string(uint32_t ms, uint32_t ls) + { + std::basic_stringstream<T> ss; + ss << (ms >> 16) << static_cast<T>(L'.') + << (ms & 0xFFFF) << static_cast<T>(L'.') + << (ls >> 16) << static_cast<T>(L'.') + << (ls & 0xFFFF); + return ss.str(); + } + + //Helper to set file flag + void set_file_flag(uint32_t flag); + //Helper to clear file flag + void clear_file_flag(uint32_t flag); + //Helper to set or clear file flag + void set_file_flag(uint32_t flag, bool set_flag); + + uint32_t file_version_ms_, file_version_ls_, + product_version_ms_, product_version_ls_; + uint32_t file_flags_; + uint32_t file_os_; + uint32_t file_type_, file_subtype_; + uint32_t file_date_ms_, file_date_ls_; +}; +} diff --git a/tools/pe_bliss/message_table.cpp b/tools/pe_bliss/message_table.cpp new file mode 100644 index 0000000000..909be5d494 --- /dev/null +++ b/tools/pe_bliss/message_table.cpp @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "message_table.h" +#include "utils.h" + +namespace pe_bliss +{ +//Default constructor +message_table_item::message_table_item() + :unicode_(false) +{} + +//Constructor from ANSI string +message_table_item::message_table_item(const std::string& str) + :unicode_(false), ansi_str_(str) +{ + pe_utils::strip_nullbytes(ansi_str_); +} + +//Constructor from UNICODE string +message_table_item::message_table_item(const std::wstring& str) + :unicode_(true), unicode_str_(str) +{ + pe_utils::strip_nullbytes(unicode_str_); +} + +//Returns true if contained string is unicode +bool message_table_item::is_unicode() const +{ + return unicode_; +} + +//Returns ANSI string +const std::string& message_table_item::get_ansi_string() const +{ + return ansi_str_; +} + +//Returns UNICODE string +const std::wstring& message_table_item::get_unicode_string() const +{ + return unicode_str_; +} + +//Sets ANSI string (clears UNICODE one) +void message_table_item::set_string(const std::string& str) +{ + ansi_str_ = str; + pe_utils::strip_nullbytes(ansi_str_); + unicode_str_.clear(); + unicode_ = false; +} + +//Sets UNICODE string (clears ANSI one) +void message_table_item::set_string(const std::wstring& str) +{ + unicode_str_ = str; + pe_utils::strip_nullbytes(unicode_str_); + ansi_str_.clear(); + unicode_ = true; +} +} diff --git a/tools/pe_bliss/message_table.h b/tools/pe_bliss/message_table.h new file mode 100644 index 0000000000..5a3feb32c1 --- /dev/null +++ b/tools/pe_bliss/message_table.h @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <map> +#include "stdint_defs.h" + +namespace pe_bliss +{ +//Structure representing message table string +class message_table_item +{ +public: + //Default constructor + message_table_item(); + //Constructors from ANSI and UNICODE strings + explicit message_table_item(const std::string& str); + explicit message_table_item(const std::wstring& str); + + //Returns true if string is UNICODE + bool is_unicode() const; + //Returns ANSI string + const std::string& get_ansi_string() const; + //Returns UNICODE string + const std::wstring& get_unicode_string() const; + +public: + //Sets ANSI or UNICODE string + void set_string(const std::string& str); + void set_string(const std::wstring& str); + +private: + bool unicode_; + std::string ansi_str_; + std::wstring unicode_str_; +}; +} diff --git a/tools/pe_bliss/pe_base.cpp b/tools/pe_bliss/pe_base.cpp new file mode 100644 index 0000000000..97baa17cb3 --- /dev/null +++ b/tools/pe_bliss/pe_base.cpp @@ -0,0 +1,1680 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string> +#include <vector> +#include <istream> +#include <ostream> +#include <algorithm> +#include <cmath> +#include <set> +#include <string.h> +#include "pe_exception.h" +#include "pe_base.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Constructor +pe_base::pe_base(std::istream& file, const pe_properties& props, bool read_debug_raw_data) +{ + props_ = props.duplicate().release(); + + //Save istream state + std::ios_base::iostate state = file.exceptions(); + std::streamoff old_offset = file.tellg(); + + try + { + file.exceptions(std::ios::goodbit); + //Read DOS header, PE headers and section data + read_dos_header(file); + read_pe(file, read_debug_raw_data); + } + catch(const std::exception&) + { + //If something went wrong, restore istream state + file.seekg(old_offset); + file.exceptions(state); + file.clear(); + //Rethrow + throw; + } + + //Restore istream state + file.seekg(old_offset); + file.exceptions(state); + file.clear(); +} + +pe_base::pe_base(const pe_properties& props, uint32_t section_alignment, bool dll, uint16_t subsystem) +{ + props_ = props.duplicate().release(); + props_->create_pe(section_alignment, subsystem); + + has_overlay_ = false; + memset(&dos_header_, 0, sizeof(dos_header_)); + + dos_header_.e_magic = 0x5A4D; //"MZ" + //Magic numbers from MSVC++ build + dos_header_.e_maxalloc = 0xFFFF; + dos_header_.e_cblp = 0x90; + dos_header_.e_cp = 3; + dos_header_.e_cparhdr = 4; + dos_header_.e_sp = 0xB8; + dos_header_.e_lfarlc = 64; + + set_characteristics(image_file_executable_image | image_file_relocs_stripped); + + if(get_pe_type() == pe_type_32) + set_characteristics_flags(image_file_32bit_machine); + + if(dll) + set_characteristics_flags(image_file_dll); + + set_subsystem_version(5, 1); //WinXP + set_os_version(5, 1); //WinXP +} + +pe_base::pe_base(const pe_base& pe) + :dos_header_(pe.dos_header_), + rich_overlay_(pe.rich_overlay_), + sections_(pe.sections_), + has_overlay_(pe.has_overlay_), + full_headers_data_(pe.full_headers_data_), + debug_data_(pe.debug_data_), + props_(0) +{ + props_ = pe.props_->duplicate().release(); +} + +pe_base& pe_base::operator=(const pe_base& pe) +{ + dos_header_ = pe.dos_header_; + rich_overlay_ = pe.rich_overlay_; + sections_ = pe.sections_; + has_overlay_ = pe.has_overlay_; + full_headers_data_ = pe.full_headers_data_; + debug_data_ = pe.debug_data_; + delete props_; + props_ = 0; + props_ = pe.props_->duplicate().release(); + + return *this; +} + +pe_base::~pe_base() +{ + delete props_; +} + +//Returns dos header +const image_dos_header& pe_base::get_dos_header() const +{ + return dos_header_; +} + +//Returns dos header +image_dos_header& pe_base::get_dos_header() +{ + return dos_header_; +} + +//Returns PE headers start position (e_lfanew) +int32_t pe_base::get_pe_header_start() const +{ + return dos_header_.e_lfanew; +} + +//Strips MSVC stub overlay +void pe_base::strip_stub_overlay() +{ + rich_overlay_.clear(); +} + +//Fills MSVC stub overlay with character c +void pe_base::fill_stub_overlay(char c) +{ + if(rich_overlay_.length()) + rich_overlay_.assign(rich_overlay_.length(), c); +} + +//Sets stub MSVS overlay +void pe_base::set_stub_overlay(const std::string& data) +{ + rich_overlay_ = data; +} + +//Returns stub overlay +const std::string& pe_base::get_stub_overlay() const +{ + return rich_overlay_; +} + +//Realigns all sections +void pe_base::realign_all_sections() +{ + for(unsigned int i = 0; i < sections_.size(); i++) + realign_section(i); +} + +//Returns number of sections from PE header +uint16_t pe_base::get_number_of_sections() const +{ + return props_->get_number_of_sections(); +} + +//Updates number of sections in PE header +uint16_t pe_base::update_number_of_sections() +{ + uint16_t new_number = static_cast<uint16_t>(sections_.size()); + props_->set_number_of_sections(new_number); + return new_number; +} + +//Returns section alignment +uint32_t pe_base::get_section_alignment() const +{ + return props_->get_section_alignment(); +} + +//Returns image sections list +section_list& pe_base::get_image_sections() +{ + return sections_; +} + +//Returns image sections list +const section_list& pe_base::get_image_sections() const +{ + return sections_; +} + +//Realigns section by index +void pe_base::realign_section(uint32_t index) +{ + //Check index + if(sections_.size() <= index) + throw pe_exception("Section not found", pe_exception::section_not_found); + + //Get section iterator + section_list::iterator it = sections_.begin() + index; + section& s = *it; + + //Calculate, how many null bytes we have in the end of raw section data + std::size_t strip = 0; + for(std::size_t i = (*it).get_raw_data().length(); i >= 1; --i) + { + if(s.get_raw_data()[i - 1] == 0) + strip++; + else + break; + } + + if(it == sections_.end() - 1) //If we're realigning the last section + { + //We can strip ending null bytes + s.set_size_of_raw_data(static_cast<uint32_t>(s.get_raw_data().length() - strip)); + s.get_raw_data().resize(s.get_raw_data().length() - strip, 0); + } + else + { + //Else just set size of raw data + uint32_t raw_size_aligned = s.get_aligned_raw_size(get_file_alignment()); + s.set_size_of_raw_data(raw_size_aligned); + s.get_raw_data().resize(raw_size_aligned, 0); + } +} + +//Returns file alignment +uint32_t pe_base::get_file_alignment() const +{ + return props_->get_file_alignment(); +} + +//Sets file alignment +void pe_base::set_file_alignment(uint32_t alignment) +{ + //Check alignment + if(alignment < minimum_file_alignment) + throw pe_exception("File alignment can't be less than 512", pe_exception::incorrect_file_alignment); + + if(!pe_utils::is_power_of_2(alignment)) + throw pe_exception("File alignment must be a power of 2", pe_exception::incorrect_file_alignment); + + if(alignment > get_section_alignment()) + throw pe_exception("File alignment must be <= section alignment", pe_exception::incorrect_file_alignment); + + //Set file alignment without any additional checks + set_file_alignment_unchecked(alignment); +} + +//Returns size of image +uint32_t pe_base::get_size_of_image() const +{ + return props_->get_size_of_image(); +} + +//Returns image entry point +uint32_t pe_base::get_ep() const +{ + return props_->get_ep(); +} + +//Sets image entry point (just a value of PE header) +void pe_base::set_ep(uint32_t new_ep) +{ + props_->set_ep(new_ep); +} + +//Returns number of RVA and sizes (number of DATA_DIRECTORY entries) +uint32_t pe_base::get_number_of_rvas_and_sizes() const +{ + return props_->get_number_of_rvas_and_sizes(); +} + +//Sets number of RVA and sizes (number of DATA_DIRECTORY entries) +void pe_base::set_number_of_rvas_and_sizes(uint32_t number) +{ + props_->set_number_of_rvas_and_sizes(number); +} + +//Returns PE characteristics +uint16_t pe_base::get_characteristics() const +{ + return props_->get_characteristics(); +} + +//Sets PE characteristics (a value inside header) +void pe_base::set_characteristics(uint16_t ch) +{ + props_->set_characteristics(ch); +} + +//Returns section from RVA +section& pe_base::section_from_rva(uint32_t rva) +{ + //Search for section + for(section_list::iterator i = sections_.begin(); i != sections_.end(); ++i) + { + section& s = *i; + //Return section if found + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) + return s; + } + + throw pe_exception("No section found by presented address", pe_exception::no_section_found); +} + +//Returns section from RVA +const section& pe_base::section_from_rva(uint32_t rva) const +{ + //Search for section + for(section_list::const_iterator i = sections_.begin(); i != sections_.end(); ++i) + { + const section& s = *i; + //Return section if found + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) + return s; + } + + throw pe_exception("No section found by presented address", pe_exception::no_section_found); +} + +//Returns section from directory ID +section& pe_base::section_from_directory(uint32_t directory_id) +{ + return section_from_rva(get_directory_rva(directory_id)); +} + +//Returns section from directory ID +const section& pe_base::section_from_directory(uint32_t directory_id) const +{ + return section_from_rva(get_directory_rva(directory_id)); +} + +//Sets section virtual size (actual for the last one of this PE or for unbound section) +void pe_base::set_section_virtual_size(section& s, uint32_t vsize) +{ + //Check if we're changing virtual size of the last section + //Of course, we can change virtual size of section that's not bound to this PE file + if(sections_.empty() || std::find_if(sections_.begin(), sections_.end() - 1, section_ptr_finder(s)) != sections_.end() - 1) + throw pe_exception("Can't change virtual size of any section, except last one", pe_exception::error_changing_section_virtual_size); + + //If we're setting virtual size to zero + if(vsize == 0) + { + //Check if section is empty + if(s.empty()) + throw pe_exception("Cannot set virtual size of empty section to zero", pe_exception::error_changing_section_virtual_size); + + //Set virtual size equal to aligned size of raw data + s.set_virtual_size(s.get_size_of_raw_data()); + } + else + { + s.set_virtual_size(vsize); + } + + //Update image size if we're changing virtual size for the last section of this PE + if(!sections_.empty() || &s == &(*(sections_.end() - 1))) + update_image_size(); +} + +//Expands section raw or virtual size to hold data from specified RVA with specified size +//Section must be free (not bound to any image) +//or the last section of this image +bool pe_base::expand_section(section& s, uint32_t needed_rva, uint32_t needed_size, section_expand_type expand) +{ + //Check if we're changing the last section + //Of course, we can change the section that's not bound to this PE file + if(sections_.empty() || std::find_if(sections_.begin(), sections_.end() - 1, section_ptr_finder(s)) != sections_.end() - 1) + throw pe_exception("Can't expand any section, except last one", pe_exception::error_expanding_section); + + //Check if we should expand our section + if(expand == expand_section_raw && section_data_length_from_rva(s, needed_rva, section_data_raw) < needed_size) + { + //Expand section raw data + s.get_raw_data().resize(needed_rva - s.get_virtual_address() + needed_size); + recalculate_section_sizes(s, false); + return true; + } + else if(expand == expand_section_virtual && section_data_length_from_rva(s, needed_rva, section_data_virtual) < needed_size) + { + //Expand section virtual data + set_section_virtual_size(s, needed_rva - s.get_virtual_address() + needed_size); + return true; + } + + return false; +} + +//Updates image virtual size +void pe_base::update_image_size() +{ + //Write virtual size of image to headers + if(!sections_.empty()) + set_size_of_image(sections_.back().get_virtual_address() + sections_.back().get_aligned_virtual_size(get_section_alignment())); + else + set_size_of_image(get_size_of_headers()); +} + +//Returns checksum of PE file from header +uint32_t pe_base::get_checksum() const +{ + return props_->get_checksum(); +} + +//Sets checksum of PE file +void pe_base::set_checksum(uint32_t checksum) +{ + props_->set_checksum(checksum); +} + +//Returns timestamp of PE file from header +uint32_t pe_base::get_time_date_stamp() const +{ + return props_->get_time_date_stamp(); +} + +//Sets timestamp of PE file +void pe_base::set_time_date_stamp(uint32_t timestamp) +{ + props_->set_time_date_stamp(timestamp); +} + +//Returns Machine field value of PE file from header +uint16_t pe_base::get_machine() const +{ + return props_->get_machine(); +} + +//Sets Machine field value of PE file +void pe_base::set_machine(uint16_t machine) +{ + props_->set_machine(machine); +} + +//Prepares section before attaching it +void pe_base::prepare_section(section& s) +{ + //Calculate its size of raw data + s.set_size_of_raw_data(static_cast<uint32_t>(pe_utils::align_up(s.get_raw_data().length(), get_file_alignment()))); + + //Check section virtual and raw size + if(!s.get_size_of_raw_data() && !s.get_virtual_size()) + throw pe_exception("Virtual and Physical sizes of section can't be 0 at the same time", pe_exception::zero_section_sizes); + + //If section virtual size is zero + if(!s.get_virtual_size()) + { + s.set_virtual_size(s.get_size_of_raw_data()); + } + else + { + //Else calculate its virtual size + s.set_virtual_size( + std::max<uint32_t>(pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment()), + pe_utils::align_up(s.get_virtual_size(), get_section_alignment()))); + } +} + +//Adds section to image +section& pe_base::add_section(section s) +{ + if(sections_.size() >= maximum_number_of_sections) + throw pe_exception("Maximum number of sections has been reached", pe_exception::no_more_sections_can_be_added); + + //Prepare section before adding it + prepare_section(s); + + //Calculate section virtual address + if(!sections_.empty()) + { + s.set_virtual_address(pe_utils::align_up(sections_.back().get_virtual_address() + sections_.back().get_aligned_virtual_size(get_section_alignment()), get_section_alignment())); + + //We should align last section raw size, if it wasn't aligned + section& last = sections_.back(); + last.set_size_of_raw_data(static_cast<uint32_t>(pe_utils::align_up(last.get_raw_data().length(), get_file_alignment()))); + } + else + { + s.set_virtual_address( + s.get_virtual_address() == 0 + ? pe_utils::align_up(get_size_of_headers(), get_section_alignment()) + : pe_utils::align_up(s.get_virtual_address(), get_section_alignment())); + } + + //Add section to the end of section list + sections_.push_back(s); + //Set number of sections in PE header + set_number_of_sections(static_cast<uint16_t>(sections_.size())); + //Recalculate virtual size of image + set_size_of_image(get_size_of_image() + s.get_aligned_virtual_size(get_section_alignment())); + //Return last section + return sections_.back(); +} + +//Returns true if sectios "s" is already attached to this PE file +bool pe_base::section_attached(const section& s) const +{ + return sections_.end() != std::find_if(sections_.begin(), sections_.end(), section_ptr_finder(s)); +} + +//Returns true if directory exists +bool pe_base::directory_exists(uint32_t id) const +{ + return props_->directory_exists(id); +} + +//Removes directory +void pe_base::remove_directory(uint32_t id) +{ + props_->remove_directory(id); +} + +//Returns directory RVA +uint32_t pe_base::get_directory_rva(uint32_t id) const +{ + return props_->get_directory_rva(id); +} + +//Returns directory size +uint32_t pe_base::get_directory_size(uint32_t id) const +{ + return props_->get_directory_size(id); +} + +//Sets directory RVA (just a value of PE header, no moving occurs) +void pe_base::set_directory_rva(uint32_t id, uint32_t rva) +{ + return props_->set_directory_rva(id, rva); +} + +//Sets directory size (just a value of PE header, no moving occurs) +void pe_base::set_directory_size(uint32_t id, uint32_t size) +{ + return props_->set_directory_size(id, size); +} + +//Strips only zero DATA_DIRECTORY entries to count = min_count +//Returns resulting number of data directories +//strip_iat_directory - if true, even not empty IAT directory will be stripped +uint32_t pe_base::strip_data_directories(uint32_t min_count, bool strip_iat_directory) +{ + return props_->strip_data_directories(min_count, strip_iat_directory); +} + +//Returns true if image has import directory +bool pe_base::has_imports() const +{ + return directory_exists(image_directory_entry_import); +} + +//Returns true if image has export directory +bool pe_base::has_exports() const +{ + return directory_exists(image_directory_entry_export); +} + +//Returns true if image has resource directory +bool pe_base::has_resources() const +{ + return directory_exists(image_directory_entry_resource); +} + +//Returns true if image has security directory +bool pe_base::has_security() const +{ + return directory_exists(image_directory_entry_security); +} + +//Returns true if image has relocations +bool pe_base::has_reloc() const +{ + return directory_exists(image_directory_entry_basereloc) && !(get_characteristics() & image_file_relocs_stripped); +} + +//Returns true if image has TLS directory +bool pe_base::has_tls() const +{ + return directory_exists(image_directory_entry_tls); +} + +//Returns true if image has config directory +bool pe_base::has_config() const +{ + return directory_exists(image_directory_entry_load_config); +} + +//Returns true if image has bound import directory +bool pe_base::has_bound_import() const +{ + return directory_exists(image_directory_entry_bound_import); +} + +//Returns true if image has delay import directory +bool pe_base::has_delay_import() const +{ + return directory_exists(image_directory_entry_delay_import); +} + +//Returns true if image has COM directory +bool pe_base::is_dotnet() const +{ + return directory_exists(image_directory_entry_com_descriptor); +} + +//Returns true if image has exception directory +bool pe_base::has_exception_directory() const +{ + return directory_exists(image_directory_entry_exception); +} + +//Returns true if image has debug directory +bool pe_base::has_debug() const +{ + return directory_exists(image_directory_entry_debug); +} + +//Returns corresponding section data pointer from RVA inside section "s" (checks bounds) +char* pe_base::section_data_from_rva(section& s, uint32_t rva) +{ + //Check if RVA is inside section "s" + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) + { + if(s.get_raw_data().empty()) + throw pe_exception("Section raw data is empty and cannot be changed", pe_exception::section_is_empty); + + return &s.get_raw_data()[rva - s.get_virtual_address()]; + } + + throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); +} + +//Returns corresponding section data pointer from RVA inside section "s" (checks bounds) +const char* pe_base::section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype) const +{ + //Check if RVA is inside section "s" + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) + return (datatype == section_data_raw ? s.get_raw_data().data() : s.get_virtual_data(get_section_alignment()).c_str()) + rva - s.get_virtual_address(); + + throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); +} + +//Returns section TOTAL RAW/VIRTUAL data length from RVA inside section +uint32_t pe_base::section_data_length_from_rva(uint32_t rva, section_data_type datatype, bool include_headers) const +{ + //if RVA is inside of headers and we're searching them too... + if(include_headers && rva < full_headers_data_.length()) + return static_cast<unsigned long>(full_headers_data_.length()); + + const section& s = section_from_rva(rva); + return static_cast<unsigned long>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment())); +} + +//Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32 +uint32_t pe_base::section_data_length_from_va(uint32_t va, section_data_type datatype, bool include_headers) const +{ + return section_data_length_from_rva(va_to_rva(va), datatype, include_headers); +} + +//Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32/PE64 +uint32_t pe_base::section_data_length_from_va(uint64_t va, section_data_type datatype, bool include_headers) const +{ + return section_data_length_from_rva(va_to_rva(va), datatype, include_headers); +} + +//Returns section remaining RAW/VIRTUAL data length from RVA "rva_inside" to the end of section containing RVA "rva" +uint32_t pe_base::section_data_length_from_rva(uint32_t rva, uint32_t rva_inside, section_data_type datatype, bool include_headers) const +{ + //if RVAs are inside of headers and we're searching them too... + if(include_headers && rva < full_headers_data_.length() && rva_inside < full_headers_data_.length()) + return static_cast<unsigned long>(full_headers_data_.length() - rva_inside); + + const section& s = section_from_rva(rva); + if(rva_inside < s.get_virtual_address()) + throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); + + //Calculate remaining length of section data from "rva" address + long length = static_cast<long>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment())) + + s.get_virtual_address() - rva_inside; + + if(length < 0) + return 0; + + return static_cast<unsigned long>(length); +} + +//Returns section remaining RAW/VIRTUAL data length from VA "va_inside" to the end of section containing VA "va" for PE32 +uint32_t pe_base::section_data_length_from_va(uint32_t va, uint32_t va_inside, section_data_type datatype, bool include_headers) const +{ + return section_data_length_from_rva(va_to_rva(va), va_to_rva(va_inside), datatype, include_headers); +} + +//Returns section remaining RAW/VIRTUAL data length from VA "va_inside" to the end of section containing VA "va" for PE32/PE64 +uint32_t pe_base::section_data_length_from_va(uint64_t va, uint64_t va_inside, section_data_type datatype, bool include_headers) const +{ + return section_data_length_from_rva(va_to_rva(va), va_to_rva(va_inside), datatype, include_headers); +} + +//Returns section remaining RAW/VIRTUAL data length from RVA to the end of section "s" (checks bounds) +uint32_t pe_base::section_data_length_from_rva(const section& s, uint32_t rva_inside, section_data_type datatype) const +{ + //Check rva_inside + if(rva_inside >= s.get_virtual_address() && rva_inside < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) + { + //Calculate remaining length of section data from "rva" address + int32_t length = static_cast<int32_t>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment())) + + s.get_virtual_address() - rva_inside; + + if(length < 0) + return 0; + + return static_cast<uint32_t>(length); + } + + throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); +} + +//Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32 (checks bounds) +uint32_t pe_base::section_data_length_from_va(const section& s, uint32_t va_inside, section_data_type datatype) const +{ + return section_data_length_from_rva(s, va_to_rva(va_inside), datatype); +} + +//Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32/PE64 (checks bounds) +uint32_t pe_base::section_data_length_from_va(const section& s, uint64_t va_inside, section_data_type datatype) const +{ + return section_data_length_from_rva(s, va_to_rva(va_inside), datatype); +} + +//Returns corresponding section data pointer from RVA inside section +char* pe_base::section_data_from_rva(uint32_t rva, bool include_headers) +{ + //if RVA is inside of headers and we're searching them too... + if(include_headers && rva < full_headers_data_.length()) + return &full_headers_data_[rva]; + + section& s = section_from_rva(rva); + + if(s.get_raw_data().empty()) + throw pe_exception("Section raw data is empty and cannot be changed", pe_exception::section_is_empty); + + return &s.get_raw_data()[rva - s.get_virtual_address()]; +} + +//Returns corresponding section data pointer from RVA inside section +const char* pe_base::section_data_from_rva(uint32_t rva, section_data_type datatype, bool include_headers) const +{ + //if RVA is inside of headers and we're searching them too... + if(include_headers && rva < full_headers_data_.length()) + return &full_headers_data_[rva]; + + const section& s = section_from_rva(rva); + return (datatype == section_data_raw ? s.get_raw_data().data() : s.get_virtual_data(get_section_alignment()).c_str()) + rva - s.get_virtual_address(); +} + +//Reads DOS headers from istream +void pe_base::read_dos_header(std::istream& file, image_dos_header& header) +{ + //Check istream flags + if(file.bad() || file.eof()) + throw pe_exception("PE file stream is bad or closed.", pe_exception::bad_pe_file); + + //Read DOS header and check istream + file.read(reinterpret_cast<char*>(&header), sizeof(image_dos_header)); + if(file.bad() || file.eof()) + throw pe_exception("Unable to read IMAGE_DOS_HEADER", pe_exception::bad_dos_header); + + //Check DOS header magic + if(header.e_magic != 0x5a4d) //"MZ" + throw pe_exception("IMAGE_DOS_HEADER signature is incorrect", pe_exception::bad_dos_header); +} + +//Reads DOS headers from istream +void pe_base::read_dos_header(std::istream& file) +{ + read_dos_header(file, dos_header_); +} + +//Reads PE image from istream +void pe_base::read_pe(std::istream& file, bool read_debug_raw_data) +{ + //Get istream size + std::streamoff filesize = pe_utils::get_file_size(file); + + //Check if PE header is DWORD-aligned + if((dos_header_.e_lfanew % sizeof(uint32_t)) != 0) + throw pe_exception("PE header is not DWORD-aligned", pe_exception::bad_dos_header); + + //Seek to NT headers + file.seekg(dos_header_.e_lfanew); + if(file.bad() || file.fail()) + throw pe_exception("Cannot reach IMAGE_NT_HEADERS", pe_exception::image_nt_headers_not_found); + + //Read NT headers + file.read(get_nt_headers_ptr(), get_sizeof_nt_header() - sizeof(image_data_directory) * image_numberof_directory_entries); + if(file.bad() || file.eof()) + throw pe_exception("Error reading IMAGE_NT_HEADERS", pe_exception::error_reading_image_nt_headers); + + //Check PE signature + if(get_pe_signature() != 0x4550) //"PE" + throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); + + //Check number of directories + if(get_number_of_rvas_and_sizes() > image_numberof_directory_entries) + set_number_of_rvas_and_sizes(image_numberof_directory_entries); + + if(get_number_of_rvas_and_sizes() > 0) + { + //Read data directory headers, if any + file.read(get_nt_headers_ptr() + (get_sizeof_nt_header() - sizeof(image_data_directory) * image_numberof_directory_entries), sizeof(image_data_directory) * get_number_of_rvas_and_sizes()); + if(file.bad() || file.eof()) + throw pe_exception("Error reading DATA_DIRECTORY headers", pe_exception::error_reading_data_directories); + } + + //Check section number + //Images with zero section number accepted + if(get_number_of_sections() > maximum_number_of_sections) + throw pe_exception("Incorrect number of sections", pe_exception::section_number_incorrect); + + //Check PE magic + if(get_magic() != get_needed_magic()) + throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); + + //Check section alignment + if(!pe_utils::is_power_of_2(get_section_alignment())) + throw pe_exception("Incorrect section alignment", pe_exception::incorrect_section_alignment); + + //Check file alignment + if(!pe_utils::is_power_of_2(get_file_alignment())) + throw pe_exception("Incorrect file alignment", pe_exception::incorrect_file_alignment); + + if(get_file_alignment() != get_section_alignment() && (get_file_alignment() < minimum_file_alignment || get_file_alignment() > get_section_alignment())) + throw pe_exception("Incorrect file alignment", pe_exception::incorrect_file_alignment); + + //Check size of image + if(pe_utils::align_up(get_size_of_image(), get_section_alignment()) == 0) + throw pe_exception("Incorrect size of image", pe_exception::incorrect_size_of_image); + + //Read rich data overlay / DOS stub (if any) + if(static_cast<uint32_t>(dos_header_.e_lfanew) > sizeof(image_dos_header)) + { + rich_overlay_.resize(dos_header_.e_lfanew - sizeof(image_dos_header)); + file.seekg(sizeof(image_dos_header)); + file.read(&rich_overlay_[0], dos_header_.e_lfanew - sizeof(image_dos_header)); + if(file.bad() || file.eof()) + throw pe_exception("Error reading 'Rich' & 'DOS stub' overlay", pe_exception::error_reading_overlay); + } + + //Calculate first section raw position + //Sum is safe here + uint32_t first_section = dos_header_.e_lfanew + get_size_of_optional_header() + sizeof(image_file_header) + sizeof(uint32_t) /* Signature */; + + if(get_number_of_sections() > 0) + { + //Go to first section + file.seekg(first_section); + if(file.bad() || file.fail()) + throw pe_exception("Cannot reach section headers", pe_exception::image_section_headers_not_found); + } + + uint32_t last_raw_size = 0; + + //Read all sections + for(int i = 0; i < get_number_of_sections(); i++) + { + section s; + //Read section header + file.read(reinterpret_cast<char*>(&s.get_raw_header()), sizeof(image_section_header)); + if(file.bad() || file.eof()) + throw pe_exception("Error reading section header", pe_exception::error_reading_section_header); + + //Save next section header position + std::streamoff next_sect = file.tellg(); + + //Check section virtual and raw sizes + if(!s.get_size_of_raw_data() && !s.get_virtual_size()) + throw pe_exception("Virtual and Physical sizes of section can't be 0 at the same time", pe_exception::zero_section_sizes); + + //Check for adequate values of section fields + if(!pe_utils::is_sum_safe(s.get_virtual_address(), s.get_virtual_size()) || s.get_virtual_size() > pe_utils::two_gb + || !pe_utils::is_sum_safe(s.get_pointer_to_raw_data(), s.get_size_of_raw_data()) || s.get_size_of_raw_data() > pe_utils::two_gb) + throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size); + + if(s.get_size_of_raw_data() != 0) + { + //If section has raw data + + //If section raw data size is greater than virtual, fix it + last_raw_size = s.get_size_of_raw_data(); + if(pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment()) > pe_utils::align_up(s.get_virtual_size(), get_section_alignment())) + s.set_size_of_raw_data(s.get_virtual_size()); + + //Check virtual and raw section sizes and addresses + if(s.get_virtual_address() + pe_utils::align_up(s.get_virtual_size(), get_section_alignment()) > pe_utils::align_up(get_size_of_image(), get_section_alignment()) + || + pe_utils::align_down(s.get_pointer_to_raw_data(), get_file_alignment()) + s.get_size_of_raw_data() > static_cast<uint32_t>(filesize)) + throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size); + + //Seek to section raw data + file.seekg(pe_utils::align_down(s.get_pointer_to_raw_data(), get_file_alignment())); + if(file.bad() || file.fail()) + throw pe_exception("Cannot reach section data", pe_exception::image_section_data_not_found); + + //Read section raw data + s.get_raw_data().resize(s.get_size_of_raw_data()); + file.read(&s.get_raw_data()[0], s.get_size_of_raw_data()); + if(file.bad() || file.fail()) + throw pe_exception("Error reading section data", pe_exception::image_section_data_not_found); + } + + //Check virtual address and size of section + if(s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()) > pe_utils::align_up(get_size_of_image(), get_section_alignment())) + throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size); + + //Save section + sections_.push_back(s); + + //Seek to the next section header + file.seekg(next_sect); + } + + //Check size of headers: SizeOfHeaders can't be larger than first section VA + if(!sections_.empty() && get_size_of_headers() > sections_.front().get_virtual_address()) + throw pe_exception("Incorrect size of headers", pe_exception::incorrect_size_of_headers); + + //If image has more than two sections + if(sections_.size() >= 2) + { + //Check sections virtual sizes + for(section_list::const_iterator i = sections_.begin() + 1; i != sections_.end(); ++i) + { + if((*i).get_virtual_address() != (*(i - 1)).get_virtual_address() + (*(i - 1)).get_aligned_virtual_size(get_section_alignment())) + throw pe_exception("Section table is incorrect", pe_exception::image_section_table_incorrect); + } + } + + //Check if image has overlay in the end of file + has_overlay_ = !sections_.empty() && filesize > static_cast<std::streamoff>(sections_.back().get_pointer_to_raw_data() + last_raw_size); + + { + //Additionally, read data from the beginning of istream to size of headers + file.seekg(0); + uint32_t size_of_headers = std::min<uint32_t>(get_size_of_headers(), static_cast<uint32_t>(filesize)); + + if(!sections_.empty()) + { + for(section_list::const_iterator i = sections_.begin(); i != sections_.end(); ++i) + { + if(!(*i).empty()) + { + size_of_headers = std::min<uint32_t>(get_size_of_headers(), (*i).get_pointer_to_raw_data()); + break; + } + } + } + + full_headers_data_.resize(size_of_headers); + file.read(&full_headers_data_[0], size_of_headers); + if(file.bad() || file.eof()) + throw pe_exception("Error reading file", pe_exception::error_reading_file); + } + + //Moreover, if there's debug directory, read its raw data for some debug info types + while(read_debug_raw_data && has_debug()) + { + try + { + //Check the length in bytes of the section containing debug directory + if(section_data_length_from_rva(get_directory_rva(image_directory_entry_debug), get_directory_rva(image_directory_entry_debug), section_data_virtual, true) < sizeof(image_debug_directory)) + break; + + unsigned long current_pos = get_directory_rva(image_directory_entry_debug); + + //First IMAGE_DEBUG_DIRECTORY table + image_debug_directory directory = section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); + + //Iterate over all IMAGE_DEBUG_DIRECTORY directories + while(directory.PointerToRawData + && current_pos < get_directory_rva(image_directory_entry_debug) + get_directory_size(image_directory_entry_debug)) + { + //If we have something to read + if((directory.Type == image_debug_type_codeview + || directory.Type == image_debug_type_misc + || directory.Type == image_debug_type_coff) + && directory.SizeOfData) + { + std::string data; + data.resize(directory.SizeOfData); + file.seekg(directory.PointerToRawData); + file.read(&data[0], directory.SizeOfData); + if(file.bad() || file.eof()) + throw pe_exception("Error reading file", pe_exception::error_reading_file); + + debug_data_.insert(std::make_pair(directory.PointerToRawData, data)); + } + + //Go to next debug entry + current_pos += sizeof(image_debug_directory); + directory = section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); + } + + break; + } + catch(const pe_exception&) + { + //Don't throw any exception here, if debug info is corrupted or incorrect + break; + } + catch(const std::bad_alloc&) + { + //Don't throw any exception here, if debug info is corrupted or incorrect + break; + } + } +} + +//Returns PE type of this image +pe_type pe_base::get_pe_type() const +{ + return props_->get_pe_type(); +} + +//Returns PE type (PE or PE+) from pe_type enumeration (minimal correctness checks) +pe_type pe_base::get_pe_type(std::istream& file) +{ + //Save state of the istream + std::ios_base::iostate state = file.exceptions(); + std::streamoff old_offset = file.tellg(); + image_nt_headers32 nt_headers; + image_dos_header header; + + try + { + //Read dos header + file.exceptions(std::ios::goodbit); + read_dos_header(file, header); + + //Seek to the NT headers start + file.seekg(header.e_lfanew); + if(file.bad() || file.fail()) + throw pe_exception("Cannot reach IMAGE_NT_HEADERS", pe_exception::image_nt_headers_not_found); + + //Read NT headers (we're using 32-bit version, because there's no significant differencies between 32 and 64 bit version structures) + file.read(reinterpret_cast<char*>(&nt_headers), sizeof(image_nt_headers32) - sizeof(image_data_directory) * image_numberof_directory_entries); + if(file.bad() || file.eof()) + throw pe_exception("Error reading IMAGE_NT_HEADERS", pe_exception::error_reading_image_nt_headers); + + //Check NT headers signature + if(nt_headers.Signature != 0x4550) //"PE" + throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); + + //Check NT headers magic + if(nt_headers.OptionalHeader.Magic != image_nt_optional_hdr32_magic && nt_headers.OptionalHeader.Magic != image_nt_optional_hdr64_magic) + throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); + } + catch(const std::exception&) + { + //If something went wrong, restore istream state + file.exceptions(state); + file.seekg(old_offset); + file.clear(); + //Retrhow exception + throw; + } + + //Restore stream state + file.exceptions(state); + file.seekg(old_offset); + file.clear(); + + //Determine PE type and return it + return nt_headers.OptionalHeader.Magic == image_nt_optional_hdr64_magic ? pe_type_64 : pe_type_32; +} + +//Returns true if image has overlay data at the end of file +bool pe_base::has_overlay() const +{ + return has_overlay_; +} + +//Clears PE characteristics flag +void pe_base::clear_characteristics_flags(uint16_t flags) +{ + set_characteristics(get_characteristics() & ~flags); +} + +//Sets PE characteristics flag +void pe_base::set_characteristics_flags(uint16_t flags) +{ + set_characteristics(get_characteristics() | flags); +} + +//Returns true if PE characteristics flag set +bool pe_base::check_characteristics_flag(uint16_t flag) const +{ + return (get_characteristics() & flag) ? true : false; +} + +//Returns subsystem value +uint16_t pe_base::get_subsystem() const +{ + return props_->get_subsystem(); +} + +//Sets subsystem value +void pe_base::set_subsystem(uint16_t subsystem) +{ + props_->set_subsystem(subsystem); +} + +//Returns true if image has console subsystem +bool pe_base::is_console() const +{ + return get_subsystem() == image_subsystem_windows_cui; +} + +//Returns true if image has Windows GUI subsystem +bool pe_base::is_gui() const +{ + return get_subsystem() == image_subsystem_windows_gui; +} + +//Sets required operation system version +void pe_base::set_os_version(uint16_t major, uint16_t minor) +{ + props_->set_os_version(major, minor); +} + +//Returns required operation system version (minor word) +uint16_t pe_base::get_minor_os_version() const +{ + return props_->get_minor_os_version(); +} + +//Returns required operation system version (major word) +uint16_t pe_base::get_major_os_version() const +{ + return props_->get_major_os_version(); +} + +//Sets required subsystem version +void pe_base::set_subsystem_version(uint16_t major, uint16_t minor) +{ + props_->set_subsystem_version(major, minor); +} + +//Returns required subsystem version (minor word) +uint16_t pe_base::get_minor_subsystem_version() const +{ + return props_->get_minor_subsystem_version(); +} + +//Returns required subsystem version (major word) +uint16_t pe_base::get_major_subsystem_version() const +{ + return props_->get_major_subsystem_version(); +} + +//Returns corresponding section data pointer from VA inside section "s" for PE32 (checks bounds) +char* pe_base::section_data_from_va(section& s, uint32_t va) //Always returns raw data +{ + return section_data_from_rva(s, va_to_rva(va)); +} + +//Returns corresponding section data pointer from VA inside section "s" for PE32 (checks bounds) +const char* pe_base::section_data_from_va(const section& s, uint32_t va, section_data_type datatype) const +{ + return section_data_from_rva(s, va_to_rva(va), datatype); +} + +//Returns corresponding section data pointer from VA inside section for PE32 +char* pe_base::section_data_from_va(uint32_t va, bool include_headers) //Always returns raw data +{ + return section_data_from_rva(va_to_rva(va), include_headers); +} + +//Returns corresponding section data pointer from VA inside section for PE32 +const char* pe_base::section_data_from_va(uint32_t va, section_data_type datatype, bool include_headers) const +{ + return section_data_from_rva(va_to_rva(va), datatype, include_headers); +} + +//Returns corresponding section data pointer from VA inside section "s" for PE32/PE64 (checks bounds) +char* pe_base::section_data_from_va(section& s, uint64_t va) //Always returns raw data +{ + return section_data_from_rva(s, va_to_rva(va)); +} + +//Returns corresponding section data pointer from VA inside section "s" for PE32/PE64 (checks bounds) +const char* pe_base::section_data_from_va(const section& s, uint64_t va, section_data_type datatype) const +{ + return section_data_from_rva(s, va_to_rva(va), datatype); +} + +//Returns corresponding section data pointer from VA inside section for PE32/PE64 +char* pe_base::section_data_from_va(uint64_t va, bool include_headers) //Always returns raw data +{ + return section_data_from_rva(va_to_rva(va), include_headers); +} + +//Returns corresponding section data pointer from VA inside section for PE32/PE64 +const char* pe_base::section_data_from_va(uint64_t va, section_data_type datatype, bool include_headers) const +{ + return section_data_from_rva(va_to_rva(va), datatype, include_headers); +} + +//Returns section from VA inside it for PE32 +section& pe_base::section_from_va(uint32_t va) +{ + return section_from_rva(va_to_rva(va)); +} + +//Returns section from VA inside it for PE32/PE64 +section& pe_base::section_from_va(uint64_t va) +{ + return section_from_rva(va_to_rva(va)); +} + +//Returns section from RVA inside it for PE32 +const section& pe_base::section_from_va(uint32_t va) const +{ + return section_from_rva(va_to_rva(va)); +} + +//Returns section from RVA inside it for PE32/PE64 +const section& pe_base::section_from_va(uint64_t va) const +{ + return section_from_rva(va_to_rva(va)); +} + +uint32_t pe_base::va_to_rva(uint32_t va, bool bound_check) const +{ + return props_->va_to_rva(va, bound_check); +} + +uint32_t pe_base::va_to_rva(uint64_t va, bool bound_check) const +{ + return props_->va_to_rva(va, bound_check); +} + +uint32_t pe_base::rva_to_va_32(uint32_t rva) const +{ + return props_->rva_to_va_32(rva); +} + +uint64_t pe_base::rva_to_va_64(uint32_t rva) const +{ + return props_->rva_to_va_64(rva); +} + +//Relative Virtual Address (RVA) to Virtual Address (VA) convertion for PE32 +void pe_base::rva_to_va(uint32_t rva, uint32_t& va) const +{ + va = rva_to_va_32(rva); +} + +//Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32/PE64 +void pe_base::rva_to_va(uint32_t rva, uint64_t& va) const +{ + va = rva_to_va_64(rva); +} + +//Returns section from file offset (4gb max) +section& pe_base::section_from_file_offset(uint32_t offset) +{ + return *file_offset_to_section(offset); +} + +//Returns section from file offset (4gb max) +const section& pe_base::section_from_file_offset(uint32_t offset) const +{ + return *file_offset_to_section(offset); +} + +//Returns section and offset (raw data only) from its start from RVA +const std::pair<uint32_t, const section*> pe_base::section_and_offset_from_rva(uint32_t rva) const +{ + const section& s = section_from_rva(rva); + return std::make_pair(rva - s.get_virtual_address(), &s); +} + +//Returns DLL Characteristics +uint16_t pe_base::get_dll_characteristics() const +{ + return props_->get_dll_characteristics(); +} + +//Sets DLL Characteristics +void pe_base::set_dll_characteristics(uint16_t characteristics) +{ + props_->set_dll_characteristics(characteristics); +} + +//Returns size of headers +uint32_t pe_base::get_size_of_headers() const +{ + return props_->get_size_of_headers(); +} + +//Returns size of optional header +uint16_t pe_base::get_size_of_optional_header() const +{ + return props_->get_size_of_optional_header(); +} + +//Returns PE signature +uint32_t pe_base::get_pe_signature() const +{ + return props_->get_pe_signature(); +} + +//Returns magic value +uint32_t pe_base::get_magic() const +{ + return props_->get_magic(); +} + +//Returns image base for PE32 +void pe_base::get_image_base(uint32_t& base) const +{ + base = get_image_base_32(); +} + +//Returns image base for PE32 and PE64 respectively +uint32_t pe_base::get_image_base_32() const +{ + return props_->get_image_base_32(); +} + +//Sets image base for PE32 and PE64 respectively +uint64_t pe_base::get_image_base_64() const +{ + return props_->get_image_base_64(); +} + +//RVA to RAW file offset convertion (4gb max) +uint32_t pe_base::rva_to_file_offset(uint32_t rva) const +{ + //Maybe, RVA is inside PE headers + if(rva < get_size_of_headers()) + return rva; + + const section& s = section_from_rva(rva); + return s.get_pointer_to_raw_data() + rva - s.get_virtual_address(); +} + +//RAW file offset to RVA convertion (4gb max) +uint32_t pe_base::file_offset_to_rva(uint32_t offset) const +{ + //Maybe, offset is inside PE headers + if(offset < get_size_of_headers()) + return offset; + + const section_list::const_iterator it = file_offset_to_section(offset); + return offset - (*it).get_pointer_to_raw_data() + (*it).get_virtual_address(); +} + +//RAW file offset to section convertion helper (4gb max) +section_list::const_iterator pe_base::file_offset_to_section(uint32_t offset) const +{ + section_list::const_iterator it = std::find_if(sections_.begin(), sections_.end(), section_by_raw_offset(offset)); + if(it == sections_.end()) + throw pe_exception("No section found by presented file offset", pe_exception::no_section_found); + + return it; +} + +//RAW file offset to section convertion helper (4gb max) +section_list::iterator pe_base::file_offset_to_section(uint32_t offset) +{ + section_list::iterator it = std::find_if(sections_.begin(), sections_.end(), section_by_raw_offset(offset)); + if(it == sections_.end()) + throw pe_exception("No section found by presented file offset", pe_exception::no_section_found); + + return it; +} + +//RVA from section raw data offset +uint32_t pe_base::rva_from_section_offset(const section& s, uint32_t raw_offset_from_section_start) +{ + return s.get_virtual_address() + raw_offset_from_section_start; +} + +//Returns image base for PE32/PE64 +void pe_base::get_image_base(uint64_t& base) const +{ + base = get_image_base_64(); +} + +//Sets new image base +void pe_base::set_image_base(uint32_t base) +{ + props_->set_image_base(base); +} + +void pe_base::set_image_base_64(uint64_t base) +{ + props_->set_image_base_64(base); +} + +//Sets heap size commit for PE32 and PE64 respectively +void pe_base::set_heap_size_commit(uint32_t size) +{ + props_->set_heap_size_commit(size); +} + +void pe_base::set_heap_size_commit(uint64_t size) +{ + props_->set_heap_size_commit(size); +} + +//Sets heap size reserve for PE32 and PE64 respectively +void pe_base::set_heap_size_reserve(uint32_t size) +{ + props_->set_heap_size_reserve(size); +} + +void pe_base::set_heap_size_reserve(uint64_t size) +{ + props_->set_heap_size_reserve(size); +} + +//Sets stack size commit for PE32 and PE64 respectively +void pe_base::set_stack_size_commit(uint32_t size) +{ + props_->set_stack_size_commit(size); +} + +void pe_base::set_stack_size_commit(uint64_t size) +{ + props_->set_stack_size_commit(size); +} + +//Sets stack size reserve for PE32 and PE64 respectively +void pe_base::set_stack_size_reserve(uint32_t size) +{ + props_->set_stack_size_reserve(size); +} + +void pe_base::set_stack_size_reserve(uint64_t size) +{ + props_->set_stack_size_reserve(size); +} + +//Returns heap size commit for PE32 and PE64 respectively +uint32_t pe_base::get_heap_size_commit_32() const +{ + return props_->get_heap_size_commit_32(); +} + +uint64_t pe_base::get_heap_size_commit_64() const +{ + return props_->get_heap_size_commit_64(); +} + +//Returns heap size reserve for PE32 and PE64 respectively +uint32_t pe_base::get_heap_size_reserve_32() const +{ + return props_->get_heap_size_reserve_32(); +} + +uint64_t pe_base::get_heap_size_reserve_64() const +{ + return props_->get_heap_size_reserve_64(); +} + +//Returns stack size commit for PE32 and PE64 respectively +uint32_t pe_base::get_stack_size_commit_32() const +{ + return props_->get_stack_size_commit_32(); +} + +uint64_t pe_base::get_stack_size_commit_64() const +{ + return props_->get_stack_size_commit_64(); +} + +//Returns stack size reserve for PE32 and PE64 respectively +uint32_t pe_base::get_stack_size_reserve_32() const +{ + return props_->get_stack_size_reserve_32(); +} + +uint64_t pe_base::get_stack_size_reserve_64() const +{ + return props_->get_stack_size_reserve_64(); +} + +//Returns heap size commit for PE32 +void pe_base::get_heap_size_commit(uint32_t& size) const +{ + size = get_heap_size_commit_32(); +} + +//Returns heap size commit for PE32/PE64 +void pe_base::get_heap_size_commit(uint64_t& size) const +{ + size = get_heap_size_commit_64(); +} + +//Returns heap size reserve for PE32 +void pe_base::get_heap_size_reserve(uint32_t& size) const +{ + size = get_heap_size_reserve_32(); +} + +//Returns heap size reserve for PE32/PE64 +void pe_base::get_heap_size_reserve(uint64_t& size) const +{ + size = get_heap_size_reserve_64(); +} + +//Returns stack size commit for PE32 +void pe_base::get_stack_size_commit(uint32_t& size) const +{ + size = get_stack_size_commit_32(); +} + +//Returns stack size commit for PE32/PE64 +void pe_base::get_stack_size_commit(uint64_t& size) const +{ + size = get_stack_size_commit_64(); +} + +//Returns stack size reserve for PE32 +void pe_base::get_stack_size_reserve(uint32_t& size) const +{ + size = get_stack_size_reserve_32(); +} + +//Returns stack size reserve for PE32/PE64 +void pe_base::get_stack_size_reserve(uint64_t& size) const +{ + size = get_stack_size_reserve_64(); +} + +//Realigns file (changes file alignment) +void pe_base::realign_file(uint32_t new_file_alignment) +{ + //Checks alignment for correctness + set_file_alignment(new_file_alignment); + realign_all_sections(); +} + +//Helper function to recalculate RAW and virtual section sizes and strip it, if necessary +void pe_base::recalculate_section_sizes(section& s, bool auto_strip) +{ + prepare_section(s); //Recalculate section raw addresses + + //Strip RAW size of section, if it is the last one + //For all others it must be file-aligned and calculated by prepare_section() call + if(auto_strip && !(sections_.empty() || &s == &*(sections_.end() - 1))) + { + //Strip ending raw data nullbytes to optimize size + std::string& raw_data = s.get_raw_data(); + if(!raw_data.empty()) + { + std::string::size_type i = raw_data.length(); + for(; i != 1; --i) + { + if(raw_data[i - 1] != 0) + break; + } + + raw_data.resize(i); + } + + s.set_size_of_raw_data(static_cast<uint32_t>(raw_data.length())); + } + + //Can occur only for last section + if(pe_utils::align_up(s.get_virtual_size(), get_section_alignment()) < pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment())) + set_section_virtual_size(s, pe_utils::align_up(s.get_size_of_raw_data(), get_section_alignment())); //Recalculate section virtual size +} + +//Returns data from the beginning of image +//Size = SizeOfHeaders +const std::string& pe_base::get_full_headers_data() const +{ + return full_headers_data_; +} + +const pe_base::debug_data_list& pe_base::get_raw_debug_data_list() const +{ + return debug_data_; +} + +//Sets number of sections +void pe_base::set_number_of_sections(uint16_t number) +{ + props_->set_number_of_sections(number); +} + +//Sets size of image +void pe_base::set_size_of_image(uint32_t size) +{ + props_->set_size_of_image(size); +} + +//Sets size of headers +void pe_base::set_size_of_headers(uint32_t size) +{ + props_->set_size_of_headers(size); +} + +//Sets size of optional headers +void pe_base::set_size_of_optional_header(uint16_t size) +{ + props_->set_size_of_optional_header(size); +} + +//Returns nt headers data pointer +char* pe_base::get_nt_headers_ptr() +{ + return props_->get_nt_headers_ptr(); +} + +//Returns nt headers data pointer +const char* pe_base::get_nt_headers_ptr() const +{ + return props_->get_nt_headers_ptr(); +} + +//Returns sizeof() nt headers +uint32_t pe_base::get_sizeof_nt_header() const +{ + return props_->get_sizeof_nt_header(); +} + +//Returns sizeof() optional headers +uint32_t pe_base::get_sizeof_opt_headers() const +{ + return props_->get_sizeof_opt_headers(); +} + +//Sets file alignment (no checks) +void pe_base::set_file_alignment_unchecked(uint32_t alignment) +{ + props_->set_file_alignment_unchecked(alignment); +} + +//Sets base of code +void pe_base::set_base_of_code(uint32_t base) +{ + props_->set_base_of_code(base); +} + +//Returns base of code +uint32_t pe_base::get_base_of_code() const +{ + return props_->get_base_of_code(); +} + +//Returns needed magic of image +uint32_t pe_base::get_needed_magic() const +{ + return props_->get_needed_magic(); +} +} diff --git a/tools/pe_bliss/pe_base.h b/tools/pe_bliss/pe_base.h new file mode 100644 index 0000000000..b5416cf1e2 --- /dev/null +++ b/tools/pe_bliss/pe_base.h @@ -0,0 +1,544 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <vector> +#include <istream> +#include <ostream> +#include <map> +#include "pe_exception.h" +#include "pe_structures.h" +#include "utils.h" +#include "pe_section.h" +#include "pe_properties.h" + +//Please don't remove this information from header +//PEBliss 1.0.0 +//(c) DX 2011 - 2012, http://kaimi.ru +//Free to use for commertial and non-commertial purposes, modification and distribution + +// == more important == +//TODO: compact import rebuilder +//TODO: remove sections in the middle +//== less important == +//TODO: relocations that take more than one element (seems to be not possible in Windows PE, but anyway) +//TODO: delay import directory +//TODO: write message tables +//TODO: write string tables +//TODO: read security information +//TODO: read full .NET information + +namespace pe_bliss +{ +//Portable executable class +class pe_base +{ +public: //CONSTRUCTORS + //Constructor from stream + pe_base(std::istream& file, const pe_properties& props, bool read_debug_raw_data = true); + + //Constructor of empty PE-file + explicit pe_base(const pe_properties& props, uint32_t section_alignment = 0x1000, bool dll = false, uint16_t subsystem = pe_win::image_subsystem_windows_gui); + + pe_base(const pe_base& pe); + pe_base& operator=(const pe_base& pe); + +public: + ~pe_base(); + +public: //STUB + //Strips stub MSVS overlay, if any + void strip_stub_overlay(); + //Fills stub MSVS overlay with specified byte + void fill_stub_overlay(char c); + //Sets stub MSVS overlay + void set_stub_overlay(const std::string& data); + //Returns stub overlay contents + const std::string& get_stub_overlay() const; + + +public: //DIRECTORIES + //Returns true if directory exists + bool directory_exists(uint32_t id) const; + //Removes directory + void remove_directory(uint32_t id); + + //Returns directory RVA + uint32_t get_directory_rva(uint32_t id) const; + //Returns directory size + uint32_t get_directory_size(uint32_t id) const; + + //Sets directory RVA (just a value of PE header, no moving occurs) + void set_directory_rva(uint32_t id, uint32_t rva); + //Sets directory size (just a value of PE header, no moving occurs) + void set_directory_size(uint32_t id, uint32_t size); + + //Strips only zero DATA_DIRECTORY entries to count = min_count + //Returns resulting number of data directories + //strip_iat_directory - if true, even not empty IAT directory will be stripped + uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true); + + //Returns true if image has import directory + bool has_imports() const; + //Returns true if image has export directory + bool has_exports() const; + //Returns true if image has resource directory + bool has_resources() const; + //Returns true if image has security directory + bool has_security() const; + //Returns true if image has relocations + bool has_reloc() const; + //Returns true if image has TLS directory + bool has_tls() const; + //Returns true if image has config directory + bool has_config() const; + //Returns true if image has bound import directory + bool has_bound_import() const; + //Returns true if image has delay import directory + bool has_delay_import() const; + //Returns true if image has COM directory + bool is_dotnet() const; + //Returns true if image has exception directory + bool has_exception_directory() const; + //Returns true if image has debug directory + bool has_debug() const; + + //Returns subsystem value + uint16_t get_subsystem() const; + //Sets subsystem value + void set_subsystem(uint16_t subsystem); + //Returns true if image has console subsystem + bool is_console() const; + //Returns true if image has Windows GUI subsystem + bool is_gui() const; + + //Sets required operation system version + void set_os_version(uint16_t major, uint16_t minor); + //Returns required operation system version (minor word) + uint16_t get_minor_os_version() const; + //Returns required operation system version (major word) + uint16_t get_major_os_version() const; + + //Sets required subsystem version + void set_subsystem_version(uint16_t major, uint16_t minor); + //Returns required subsystem version (minor word) + uint16_t get_minor_subsystem_version() const; + //Returns required subsystem version (major word) + uint16_t get_major_subsystem_version() const; + +public: //PE HEADER + //Returns DOS header + const pe_win::image_dos_header& get_dos_header() const; + pe_win::image_dos_header& get_dos_header(); + + //Returns PE header start (e_lfanew) + int32_t get_pe_header_start() const; + + //Returns file alignment + uint32_t get_file_alignment() const; + //Sets file alignment, checking the correctness of its value + void set_file_alignment(uint32_t alignment); + + //Returns size of image + uint32_t get_size_of_image() const; + + //Returns image entry point + uint32_t get_ep() const; + //Sets image entry point (just a value of PE header) + void set_ep(uint32_t new_ep); + + //Returns number of RVA and sizes (number of DATA_DIRECTORY entries) + uint32_t get_number_of_rvas_and_sizes() const; + //Sets number of RVA and sizes (number of DATA_DIRECTORY entries) + void set_number_of_rvas_and_sizes(uint32_t number); + + //Returns PE characteristics + uint16_t get_characteristics() const; + //Sets PE characteristics (a value inside header) + void set_characteristics(uint16_t ch); + //Clears PE characteristics flag + void clear_characteristics_flags(uint16_t flags); + //Sets PE characteristics flag + void set_characteristics_flags(uint16_t flags); + //Returns true if PE characteristics flag set + bool check_characteristics_flag(uint16_t flag) const; + + //Returns DLL Characteristics + uint16_t get_dll_characteristics() const; + //Sets DLL Characteristics + void set_dll_characteristics(uint16_t characteristics); + + //Returns size of headers + uint32_t get_size_of_headers() const; + //Returns size of optional header + uint16_t get_size_of_optional_header() const; + + //Returns PE signature + uint32_t get_pe_signature() const; + + //Returns magic value + uint32_t get_magic() const; + + //Returns image base for PE32 and PE64 respectively + uint32_t get_image_base_32() const; + void get_image_base(uint32_t& base) const; + //Sets image base for PE32 and PE64 respectively + uint64_t get_image_base_64() const; + void get_image_base(uint64_t& base) const; + + //Sets new image base + void set_image_base(uint32_t base); + void set_image_base_64(uint64_t base); + + //Sets heap size commit for PE32 and PE64 respectively + void set_heap_size_commit(uint32_t size); + void set_heap_size_commit(uint64_t size); + //Sets heap size reserve for PE32 and PE64 respectively + void set_heap_size_reserve(uint32_t size); + void set_heap_size_reserve(uint64_t size); + //Sets stack size commit for PE32 and PE64 respectively + void set_stack_size_commit(uint32_t size); + void set_stack_size_commit(uint64_t size); + //Sets stack size reserve for PE32 and PE64 respectively + void set_stack_size_reserve(uint32_t size); + void set_stack_size_reserve(uint64_t size); + + //Returns heap size commit for PE32 and PE64 respectively + uint32_t get_heap_size_commit_32() const; + void get_heap_size_commit(uint32_t& size) const; + uint64_t get_heap_size_commit_64() const; + void get_heap_size_commit(uint64_t& size) const; + //Returns heap size reserve for PE32 and PE64 respectively + uint32_t get_heap_size_reserve_32() const; + void get_heap_size_reserve(uint32_t& size) const; + uint64_t get_heap_size_reserve_64() const; + void get_heap_size_reserve(uint64_t& size) const; + //Returns stack size commit for PE32 and PE64 respectively + uint32_t get_stack_size_commit_32() const; + void get_stack_size_commit(uint32_t& size) const; + uint64_t get_stack_size_commit_64() const; + void get_stack_size_commit(uint64_t& size) const; + //Returns stack size reserve for PE32 and PE64 respectively + uint32_t get_stack_size_reserve_32() const; + void get_stack_size_reserve(uint32_t& size) const; + uint64_t get_stack_size_reserve_64() const; + void get_stack_size_reserve(uint64_t& size) const; + + //Updates virtual size of image corresponding to section virtual sizes + void update_image_size(); + + //Returns checksum of PE file from header + uint32_t get_checksum() const; + //Sets checksum of PE file + void set_checksum(uint32_t checksum); + + //Returns timestamp of PE file from header + uint32_t get_time_date_stamp() const; + //Sets timestamp of PE file + void set_time_date_stamp(uint32_t timestamp); + + //Returns Machine field value of PE file from header + uint16_t get_machine() const; + //Sets Machine field value of PE file + void set_machine(uint16_t machine); + + //Returns data from the beginning of image + //Size = SizeOfHeaders + const std::string& get_full_headers_data() const; + + typedef std::multimap<uint32_t, std::string> debug_data_list; + //Returns raw list of debug data + const debug_data_list& get_raw_debug_data_list() const; + + //Reads and checks DOS header + static void read_dos_header(std::istream& file, pe_win::image_dos_header& header); + + //Returns sizeof() nt headers + uint32_t get_sizeof_nt_header() const; + //Returns sizeof() optional headers + uint32_t get_sizeof_opt_headers() const; + //Returns raw nt headers data pointer + const char* get_nt_headers_ptr() const; + + //Sets size of headers (to NT headers) + void set_size_of_headers(uint32_t size); + //Sets size of optional headers (to NT headers) + void set_size_of_optional_header(uint16_t size); + + //Sets base of code + void set_base_of_code(uint32_t base); + //Returns base of code + uint32_t get_base_of_code() const; + +public: //ADDRESS CONVERTIONS + //Virtual Address (VA) to Relative Virtual Address (RVA) convertions + //for PE32 and PE64 respectively + //bound_check checks integer overflow + uint32_t va_to_rva(uint32_t va, bool bound_check = true) const; + uint32_t va_to_rva(uint64_t va, bool bound_check = true) const; + + //Relative Virtual Address (RVA) to Virtual Address (VA) convertions + //for PE32 and PE64 respectively + uint32_t rva_to_va_32(uint32_t rva) const; + void rva_to_va(uint32_t rva, uint32_t& va) const; + uint64_t rva_to_va_64(uint32_t rva) const; + void rva_to_va(uint32_t rva, uint64_t& va) const; + + //RVA to RAW file offset convertion (4gb max) + uint32_t rva_to_file_offset(uint32_t rva) const; + //RAW file offset to RVA convertion (4gb max) + uint32_t file_offset_to_rva(uint32_t offset) const; + + //RVA from section raw data offset + static uint32_t rva_from_section_offset(const section& s, uint32_t raw_offset_from_section_start); + +public: //IMAGE SECTIONS + //Returns number of sections from PE header + uint16_t get_number_of_sections() const; + + //Updates number of sections in PE header + uint16_t update_number_of_sections(); + + //Returns section alignment + uint32_t get_section_alignment() const; + + //Returns section list + section_list& get_image_sections(); + const section_list& get_image_sections() const; + + //Realigns all sections, if you made any changes to sections or alignments + void realign_all_sections(); + //Resligns section with specified index + void realign_section(uint32_t index); + + //Returns section from RVA inside it + section& section_from_rva(uint32_t rva); + const section& section_from_rva(uint32_t rva) const; + //Returns section from directory ID + section& section_from_directory(uint32_t directory_id); + const section& section_from_directory(uint32_t directory_id) const; + //Returns section from VA inside it for PE32 and PE64 respectively + section& section_from_va(uint32_t va); + const section& section_from_va(uint32_t va) const; + section& section_from_va(uint64_t va); + const section& section_from_va(uint64_t va) const; + //Returns section from file offset (4gb max) + section& section_from_file_offset(uint32_t offset); + const section& section_from_file_offset(uint32_t offset) const; + + //Returns section TOTAL RAW/VIRTUAL data length from RVA inside section + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + uint32_t section_data_length_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const; + //Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32 and PE64 respectively + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + uint32_t section_data_length_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; + uint32_t section_data_length_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; + + //Returns section remaining RAW/VIRTUAL data length from RVA to the end of section "s" (checks bounds) + uint32_t section_data_length_from_rva(const section& s, uint32_t rva_inside, section_data_type datatype = section_data_raw) const; + //Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32 and PE64 respectively (checks bounds) + uint32_t section_data_length_from_va(const section& s, uint64_t va_inside, section_data_type datatype = section_data_raw) const; + uint32_t section_data_length_from_va(const section& s, uint32_t va_inside, section_data_type datatype = section_data_raw) const; + + //Returns section remaining RAW/VIRTUAL data length from RVA "rva_inside" to the end of section containing RVA "rva" + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + uint32_t section_data_length_from_rva(uint32_t rva, uint32_t rva_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const; + //Returns section remaining RAW/VIRTUAL data length from VA "va_inside" to the end of section containing VA "va" for PE32 and PE64 respectively + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + uint32_t section_data_length_from_va(uint32_t va, uint32_t va_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const; + uint32_t section_data_length_from_va(uint64_t va, uint64_t va_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const; + + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + //Returns corresponding section data pointer from RVA inside section + char* section_data_from_rva(uint32_t rva, bool include_headers = false); + const char* section_data_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const; + //Returns corresponding section data pointer from VA inside section for PE32 and PE64 respectively + char* section_data_from_va(uint32_t va, bool include_headers = false); + const char* section_data_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; + char* section_data_from_va(uint64_t va, bool include_headers = false); + const char* section_data_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; + + //Returns corresponding section data pointer from RVA inside section "s" (checks bounds) + char* section_data_from_rva(section& s, uint32_t rva); + const char* section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw) const; + //Returns corresponding section data pointer from VA inside section "s" for PE32 and PE64 respectively (checks bounds) + char* section_data_from_va(section& s, uint32_t va); //Always returns raw data + const char* section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) const; + char* section_data_from_va(section& s, uint64_t va); //Always returns raw data + const char* section_data_from_va(const section& s, uint64_t va, section_data_type datatype = section_data_raw) const; + + //Returns corresponding section data pointer from RVA inside section "s" (checks bounds, checks sizes, the most safe function) + template<typename T> + T section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw) const + { + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()) && pe_utils::is_sum_safe(rva, sizeof(T))) + { + const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(get_section_alignment()); + //Don't check for underflow here, comparsion is unsigned + if(data.size() < rva - s.get_virtual_address() + sizeof(T)) + throw pe_exception("RVA and requested data size does not exist inside section", pe_exception::rva_not_exists); + + return *reinterpret_cast<const T*>(data.data() + rva - s.get_virtual_address()); + } + + throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); + } + + //Returns corresponding section data pointer from RVA inside section (checks rva, checks sizes, the most safe function) + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + template<typename T> + T section_data_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const + { + //if RVA is inside of headers and we're searching them too... + if(include_headers && pe_utils::is_sum_safe(rva, sizeof(T)) && (rva + sizeof(T) < full_headers_data_.length())) + return *reinterpret_cast<const T*>(&full_headers_data_[rva]); + + const section& s = section_from_rva(rva); + const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(get_section_alignment()); + //Don't check for underflow here, comparsion is unsigned + if(data.size() < rva - s.get_virtual_address() + sizeof(T)) + throw pe_exception("RVA and requested data size does not exist inside section", pe_exception::rva_not_exists); + + return *reinterpret_cast<const T*>(data.data() + rva - s.get_virtual_address()); + } + + //Returns corresponding section data pointer from VA inside section "s" (checks bounds, checks sizes, the most safe function) + template<typename T> + T section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) const + { + return section_data_from_rva<T>(s, va_to_rva(va), datatype); + } + + template<typename T> + T section_data_from_va(const section& s, uint64_t va, section_data_type datatype = section_data_raw) const + { + return section_data_from_rva<T>(s, va_to_rva(va), datatype); + } + + //Returns corresponding section data pointer from VA inside section (checks rva, checks sizes, the most safe function) + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + template<typename T> + T section_data_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const + { + return section_data_from_rva<T>(va_to_rva(va), datatype, include_headers); + } + + template<typename T> + T section_data_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const + { + return section_data_from_rva<T>(va_to_rva(va), datatype, include_headers); + } + + //Returns section and offset (raw data only) from its start from RVA + const std::pair<uint32_t, const section*> section_and_offset_from_rva(uint32_t rva) const; + + //Sets virtual size of section "s" + //Section must be free (not bound to any image) + //or the last section of this image + //Function calls update_image_size automatically in second case + void set_section_virtual_size(section& s, uint32_t vsize); + + //Represents section expand type for expand_section function + enum section_expand_type + { + expand_section_raw, //Section raw data size will be expanded + expand_section_virtual //Section virtual data size will be expanded + }; + + //Expands section raw or virtual size to hold data from specified RVA with specified size + //Section must be free (not bound to any image) + //or the last section of this image + //Returns true if section was expanded + bool expand_section(section& s, uint32_t needed_rva, uint32_t needed_size, section_expand_type expand); + + //Adds section to image + //Returns last section + section& add_section(section s); + //Prepares section to later add it to image (checks and recalculates virtual and raw section size) + //Section must be prepared by this function before calling add_section + void prepare_section(section& s); + + //Returns true if sectios "s" is already attached to this PE file + bool section_attached(const section& s) const; + + +public: //IMAGE + //Returns PE type (PE or PE+) from pe_type enumeration (minimal correctness checks) + static pe_type get_pe_type(std::istream& file); + //Returns PE type of this image + pe_type get_pe_type() const; + + //Returns true if image has overlay data at the end of file + bool has_overlay() const; + + //Realigns file (changes file alignment) + void realign_file(uint32_t new_file_alignment); + + //Helper function to recalculate RAW and virtual section sizes and strip it, if necessary + //auto_strip = strip section, if necessary + void recalculate_section_sizes(section& s, bool auto_strip); + + // ========== END OF PUBLIC MEMBERS AND STRUCTURES ========== // +private: + //Image DOS header + pe_win::image_dos_header dos_header_; + //Rich (stub) overlay data (for MSVS) + std::string rich_overlay_; + //List of image sections + section_list sections_; + //True if image has overlay + bool has_overlay_; + //Raw SizeOfHeaders-sized data from the beginning of image + std::string full_headers_data_; + //Raw debug data for all directories + //PointerToRawData; Data + debug_data_list debug_data_; + //PE or PE+ related properties + pe_properties* props_; + + //Reads and checks DOS header + void read_dos_header(std::istream& file); + + //Reads and checks PE headers and section headers, data + void read_pe(std::istream& file, bool read_debug_raw_data); + + //Sets number of sections + void set_number_of_sections(uint16_t number); + //Sets size of image + void set_size_of_image(uint32_t size); + //Sets file alignment (no checks) + void set_file_alignment_unchecked(uint32_t alignment); + //Returns needed magic of image + uint32_t get_needed_magic() const; + //Returns nt headers data pointer + char* get_nt_headers_ptr(); + +private: + static const uint16_t maximum_number_of_sections = 0x60; + static const uint32_t minimum_file_alignment = 512; + +private: + //RAW file offset to section convertion helpers (4gb max) + section_list::const_iterator file_offset_to_section(uint32_t offset) const; + section_list::iterator file_offset_to_section(uint32_t offset); +}; +} diff --git a/tools/pe_bliss/pe_bliss.h b/tools/pe_bliss/pe_bliss.h new file mode 100644 index 0000000000..1a8b430284 --- /dev/null +++ b/tools/pe_bliss/pe_bliss.h @@ -0,0 +1,39 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "pe_base.h" +#include "pe_rebuilder.h" +#include "pe_factory.h" +#include "pe_bound_import.h" +#include "pe_debug.h" +#include "pe_dotnet.h" +#include "pe_exception_directory.h" +#include "pe_exports.h" +#include "pe_imports.h" +#include "pe_load_config.h" +#include "pe_relocations.h" +#include "pe_resources.h" +#include "pe_rich_data.h" +#include "pe_tls.h" +#include "pe_properties_generic.h" +#include "pe_checksum.h" +#include "entropy.h" diff --git a/tools/pe_bliss/pe_bliss_godot.cpp b/tools/pe_bliss/pe_bliss_godot.cpp new file mode 100644 index 0000000000..8297aa1045 --- /dev/null +++ b/tools/pe_bliss/pe_bliss_godot.cpp @@ -0,0 +1,118 @@ +#include "pe_bliss/pe_bliss.h" +#include "pe_bliss/pe_bliss_resources.h" +#include "core/ustring.h" +#include "core/dvector.h" +#include "os/file_access.h" + +using namespace pe_bliss; + +String pe_bliss_add_resrc(const char* p_path, int version_major, int version_minor, + String& company_name, String& file_description, + String& legal_copyright, String& version_text, + String& product_name, String& godot_version, + DVector<uint8_t>& icon_content) { + try + { + pe_base image(pe_factory::create_pe(p_path)); + + const section_list& pe_sections = image.get_image_sections(); + uint32_t end_of_pe = 0; + FileAccess *dst; + DVector<uint8_t> overlay_data; + if(image.has_overlay()) + { + end_of_pe = pe_sections.back().get_pointer_to_raw_data() + pe_sections.back().get_size_of_raw_data(); + dst=FileAccess::open(p_path,FileAccess::READ); + if (dst) { + overlay_data.resize(dst->get_len()-end_of_pe); + dst->seek(end_of_pe); + DVector<uint8_t>::Write overlay_data_write = overlay_data.write(); + dst->get_buffer(overlay_data_write.ptr(),overlay_data.size()); + dst->close(); + memdelete(dst); + } + } + resource_directory root; + if(image.has_resources()) + { + root = resource_directory(get_resources(image)); + } + pe_resource_manager res(root); + if(image.has_resources()) + { + if(icon_content.size()) { + if(res.resource_exists(pe_resource_viewer::resource_icon)) + { + res.remove_resource_type(pe_resource_viewer::resource_icon); + } + if(res.resource_exists(pe_resource_viewer::resource_icon_group)) + { + res.remove_resource_type(pe_resource_viewer::resource_icon_group); + } + } + if(res.resource_exists(pe_resource_viewer::resource_version)) + { + res.remove_resource_type(pe_resource_viewer::resource_version); + } + } + file_version_info file_info; + file_info.set_file_os(file_version_info::file_os_nt_win32); + file_info.set_file_type(file_version_info::file_type_application); + unsigned int ver = version_major << 16; + ver = ver + version_minor; + file_info.set_file_version_ms(ver); + file_info.set_file_version_ls(0x00000000); + file_info.set_product_version_ms(ver); + file_info.set_product_version_ls(0x00000000); + lang_string_values_map strings; + translation_values_map translations; + version_info_editor version(strings, translations); + version.add_translation(version_info_editor::default_language_translation); + version.set_company_name(company_name.c_str()); + version.set_file_description(file_description.c_str()); + if (!product_name.empty()) { + version.set_internal_name((product_name+String(".exe")).c_str()); + version.set_original_filename((product_name+String(".exe")).c_str()); + version.set_product_name(product_name.c_str()); + } + version.set_legal_copyright(legal_copyright.c_str()); + version.set_product_version(version_text.c_str()); + if(!godot_version.empty()) version.set_property(L"Godot Engine Version", godot_version.c_str() ); + resource_version_info_writer(res).set_version_info(file_info, strings, translations, 1033, 1200); + if(icon_content.size()) { + std::string icon; + icon.resize(icon_content.size()); + for(int i=0; i<icon_content.size(); i++) + { + icon[i] = icon_content[i]; + } + resource_cursor_icon_writer(res).add_icon(icon, L"MAIN_ICON", 1033); + } + if(image.has_resources()) + { + rebuild_resources(image, root, image.section_from_directory(pe_win::image_directory_entry_resource)); + } else { + section new_resources; + new_resources.get_raw_data().resize(1); + new_resources.set_name(".rsrc"); + new_resources.readable(true); + section& attached_section = image.add_section(new_resources); + rebuild_resources(image, root, attached_section); + } + rebuild_pe(image, p_path); + if(image.has_overlay() && end_of_pe) { + dst=FileAccess::open(p_path,FileAccess::READ_WRITE); + if (dst) { + dst->seek_end(); + DVector<uint8_t>::Read overlay_data_read = overlay_data.read(); + dst->store_buffer(overlay_data_read.ptr(),overlay_data.size()); + dst->close(); + memdelete(dst); + } + } + return String(); + } catch(const pe_exception& e) { + String ret("Error In Add rsrc Section : "); + return ret + String(e.what()); + } +} diff --git a/tools/pe_bliss/pe_bliss_godot.h b/tools/pe_bliss/pe_bliss_godot.h new file mode 100644 index 0000000000..0365ca9eaf --- /dev/null +++ b/tools/pe_bliss/pe_bliss_godot.h @@ -0,0 +1,7 @@ + + +String pe_bliss_add_resrc(const char* p_path, int version_major, int version_minor, + String& company_name, String& file_description, + String& legal_copyright, String& version_text, + String& product_name, String& godot_version, + DVector<uint8_t>& icon_content); diff --git a/tools/pe_bliss/pe_bliss_resources.h b/tools/pe_bliss/pe_bliss_resources.h new file mode 100644 index 0000000000..60369f8011 --- /dev/null +++ b/tools/pe_bliss/pe_bliss_resources.h @@ -0,0 +1,36 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "file_version_info.h" +#include "message_table.h" +#include "pe_resource_manager.h" +#include "pe_resource_viewer.h" +#include "version_info_editor.h" +#include "version_info_viewer.h" +#include "resource_bitmap_reader.h" +#include "resource_bitmap_writer.h" +#include "resource_cursor_icon_reader.h" +#include "resource_cursor_icon_writer.h" +#include "resource_version_info_reader.h" +#include "resource_version_info_writer.h" +#include "resource_string_table_reader.h" +#include "resource_message_list_reader.h" diff --git a/tools/pe_bliss/pe_bound_import.cpp b/tools/pe_bliss/pe_bound_import.cpp new file mode 100644 index 0000000000..4b54b36105 --- /dev/null +++ b/tools/pe_bliss/pe_bound_import.cpp @@ -0,0 +1,311 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_bound_import.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//BOUND IMPORT +//Default constructor +bound_import_ref::bound_import_ref() + :timestamp_(0) +{} + +//Constructor from data +bound_import_ref::bound_import_ref(const std::string& module_name, uint32_t timestamp) + :module_name_(module_name), timestamp_(timestamp) +{} + +//Returns imported module name +const std::string& bound_import_ref::get_module_name() const +{ + return module_name_; +} + +//Returns bound import date and time stamp +uint32_t bound_import_ref::get_timestamp() const +{ + return timestamp_; +} + +//Sets module name +void bound_import_ref::set_module_name(const std::string& module_name) +{ + module_name_ = module_name; +} + +//Sets timestamp +void bound_import_ref::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Default constructor +bound_import::bound_import() + :timestamp_(0) +{} + +//Constructor from data +bound_import::bound_import(const std::string& module_name, uint32_t timestamp) + :module_name_(module_name), timestamp_(timestamp) +{} + +//Returns imported module name +const std::string& bound_import::get_module_name() const +{ + return module_name_; +} + +//Returns bound import date and time stamp +uint32_t bound_import::get_timestamp() const +{ + return timestamp_; +} + +//Returns bound references cound +size_t bound_import::get_module_ref_count() const +{ + return refs_.size(); +} + +//Returns module references +const bound_import::ref_list& bound_import::get_module_ref_list() const +{ + return refs_; +} + +//Adds module reference +void bound_import::add_module_ref(const bound_import_ref& ref) +{ + refs_.push_back(ref); +} + +//Clears module references list +void bound_import::clear_module_refs() +{ + refs_.clear(); +} + +//Returns module references +bound_import::ref_list& bound_import::get_module_ref_list() +{ + return refs_; +} + +//Sets module name +void bound_import::set_module_name(const std::string& module_name) +{ + module_name_ = module_name; +} + +//Sets timestamp +void bound_import::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +const bound_import_module_list get_bound_import_module_list(const pe_base& pe) +{ + //Returned bound import modules list + bound_import_module_list ret; + + //If image has no bound imports + if(!pe.has_bound_import()) + return ret; + + uint32_t bound_import_data_len = + pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true); + + if(bound_import_data_len < pe.get_directory_size(image_directory_entry_bound_import)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + const char* bound_import_data = pe.section_data_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true); + + //Check read in "read_pe" function raw bound import data size + if(bound_import_data_len < sizeof(image_bound_import_descriptor)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //current bound_import_data_ in-string position + unsigned long current_pos = 0; + //first bound import descriptor + //so, we're working with raw data here, no section helpers available + const image_bound_import_descriptor* descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data[current_pos]); + + //Enumerate until zero + while(descriptor->OffsetModuleName) + { + //Check module name offset + if(descriptor->OffsetModuleName >= bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Check module name for null-termination + if(!pe_utils::is_null_terminated(&bound_import_data[descriptor->OffsetModuleName], bound_import_data_len - descriptor->OffsetModuleName)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Create bound import descriptor structure + bound_import elem(&bound_import_data[descriptor->OffsetModuleName], descriptor->TimeDateStamp); + + //Check DWORDs + if(descriptor->NumberOfModuleForwarderRefs >= pe_utils::max_dword / sizeof(image_bound_forwarder_ref) + || !pe_utils::is_sum_safe(current_pos, 2 /* this descriptor and the next one */ * sizeof(image_bound_import_descriptor) + descriptor->NumberOfModuleForwarderRefs * sizeof(image_bound_forwarder_ref))) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Move after current descriptor + current_pos += sizeof(image_bound_import_descriptor); + + //Enumerate referenced bound import descriptors + for(unsigned long i = 0; i != descriptor->NumberOfModuleForwarderRefs; ++i) + { + //They're just after parent descriptor + //Check size of structure + if(current_pos + sizeof(image_bound_forwarder_ref) > bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Get IMAGE_BOUND_FORWARDER_REF pointer + const image_bound_forwarder_ref* ref_descriptor = reinterpret_cast<const image_bound_forwarder_ref*>(&bound_import_data[current_pos]); + + //Check referenced module name + if(ref_descriptor->OffsetModuleName >= bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //And its null-termination + if(!pe_utils::is_null_terminated(&bound_import_data[ref_descriptor->OffsetModuleName], bound_import_data_len - ref_descriptor->OffsetModuleName)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Add referenced module to current bound import structure + elem.add_module_ref(bound_import_ref(&bound_import_data[ref_descriptor->OffsetModuleName], ref_descriptor->TimeDateStamp)); + + //Move after referenced bound import descriptor + current_pos += sizeof(image_bound_forwarder_ref); + } + + //Check structure size + if(current_pos + sizeof(image_bound_import_descriptor) > bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Move to next bound import descriptor + descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data[current_pos]); + + //Save created descriptor structure and references + ret.push_back(elem); + } + + //Return result + return ret; +} + +//imports - bound imported modules list +//imports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from imports_section raw data start +//save_to_pe_headers - if true, new bound import directory information will be saved to PE image headers +//auto_strip_last_section - if true and bound imports are placed in the last section, it will be automatically stripped +const image_directory rebuild_bound_imports(pe_base& pe, const bound_import_module_list& imports, section& imports_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that exports_section is attached to this PE image + if(!pe.section_attached(imports_section)) + throw pe_exception("Bound import section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t directory_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + uint32_t needed_size = sizeof(image_bound_import_descriptor) /* Ending null descriptor */; + uint32_t needed_size_for_strings = 0; + + //Calculate needed size for bound import data + for(bound_import_module_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + const bound_import& import = *it; + needed_size += sizeof(image_bound_import_descriptor); + needed_size_for_strings += static_cast<uint32_t>((*it).get_module_name().length()) + 1 /* nullbyte */; + + const bound_import::ref_list& refs = import.get_module_ref_list(); + for(bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it) + { + needed_size_for_strings += static_cast<uint32_t>((*ref_it).get_module_name().length()) + 1 /* nullbyte */; + needed_size += sizeof(image_bound_forwarder_ref); + } + } + + needed_size += needed_size_for_strings; + + //Check if imports_section is last one. If it's not, check if there's enough place for bound import data + if(&imports_section != &*(pe.get_image_sections().end() - 1) && + (imports_section.empty() || pe_utils::align_up(imports_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + directory_pos)) + throw pe_exception("Insufficient space for bound import directory", pe_exception::insufficient_space); + + std::string& raw_data = imports_section.get_raw_data(); + + //This will be done only if imports_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + directory_pos) + raw_data.resize(needed_size + directory_pos); //Expand section raw data + + uint32_t current_pos_for_structures = directory_pos; + uint32_t current_pos_for_strings = current_pos_for_structures + needed_size - needed_size_for_strings; + + for(bound_import_module_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + const bound_import& import = *it; + image_bound_import_descriptor descriptor; + descriptor.NumberOfModuleForwarderRefs = static_cast<uint16_t>(import.get_module_ref_list().size()); + descriptor.OffsetModuleName = static_cast<uint16_t>(current_pos_for_strings - directory_pos); + descriptor.TimeDateStamp = import.get_timestamp(); + + memcpy(&raw_data[current_pos_for_structures], &descriptor, sizeof(descriptor)); + current_pos_for_structures += sizeof(descriptor); + + size_t length = import.get_module_name().length() + 1 /* nullbyte */; + memcpy(&raw_data[current_pos_for_strings], import.get_module_name().c_str(), length); + current_pos_for_strings += static_cast<uint32_t>(length); + + const bound_import::ref_list& refs = import.get_module_ref_list(); + for(bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it) + { + const bound_import_ref& ref = *ref_it; + image_bound_forwarder_ref ref_descriptor = {0}; + ref_descriptor.OffsetModuleName = static_cast<uint16_t>(current_pos_for_strings - directory_pos); + ref_descriptor.TimeDateStamp = ref.get_timestamp(); + + memcpy(&raw_data[current_pos_for_structures], &ref_descriptor, sizeof(ref_descriptor)); + current_pos_for_structures += sizeof(ref_descriptor); + + length = ref.get_module_name().length() + 1 /* nullbyte */; + memcpy(&raw_data[current_pos_for_strings], ref.get_module_name().c_str(), length); + current_pos_for_strings += static_cast<uint32_t>(length); + } + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(imports_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(imports_section, directory_pos), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_bound_import, ret.get_rva()); + pe.set_directory_size(image_directory_entry_bound_import, ret.get_size()); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_bound_import.h b/tools/pe_bliss/pe_bound_import.h new file mode 100644 index 0000000000..667e28792e --- /dev/null +++ b/tools/pe_bliss/pe_bound_import.h @@ -0,0 +1,108 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include <string> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing bound import reference +class bound_import_ref +{ +public: + //Default constructor + bound_import_ref(); + //Constructor from data + bound_import_ref(const std::string& module_name, uint32_t timestamp); + + //Returns imported module name + const std::string& get_module_name() const; + //Returns bound import date and time stamp + uint32_t get_timestamp() const; + +public: //Setters + //Sets module name + void set_module_name(const std::string& module_name); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + +private: + std::string module_name_; //Imported module name + uint32_t timestamp_; //Bound import timestamp +}; + +//Class representing image bound import information +class bound_import +{ +public: + typedef std::vector<bound_import_ref> ref_list; + +public: + //Default constructor + bound_import(); + //Constructor from data + bound_import(const std::string& module_name, uint32_t timestamp); + + //Returns imported module name + const std::string& get_module_name() const; + //Returns bound import date and time stamp + uint32_t get_timestamp() const; + + //Returns bound references cound + size_t get_module_ref_count() const; + //Returns module references + const ref_list& get_module_ref_list() const; + +public: //Setters + //Sets module name + void set_module_name(const std::string& module_name); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + + //Adds module reference + void add_module_ref(const bound_import_ref& ref); + //Clears module references list + void clear_module_refs(); + //Returns module references + ref_list& get_module_ref_list(); + +private: + std::string module_name_; //Imported module name + uint32_t timestamp_; //Bound import timestamp + ref_list refs_; //Module references list +}; + +typedef std::vector<bound_import> bound_import_module_list; + +//Returns bound import information +const bound_import_module_list get_bound_import_module_list(const pe_base& pe);//Export directory rebuilder + +//imports - bound imported modules list +//imports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from imports_section raw data start +//save_to_pe_headers - if true, new bound import directory information will be saved to PE image headers +//auto_strip_last_section - if true and bound imports are placed in the last section, it will be automatically stripped +const image_directory rebuild_bound_imports(pe_base& pe, const bound_import_module_list& imports, section& imports_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/pe_checksum.cpp b/tools/pe_bliss/pe_checksum.cpp new file mode 100644 index 0000000000..5971a33c90 --- /dev/null +++ b/tools/pe_bliss/pe_checksum.cpp @@ -0,0 +1,103 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_checksum.h" +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Calculate checksum of image +uint32_t calculate_checksum(std::istream& file) +{ + //Save istream state + std::ios_base::iostate state = file.exceptions(); + std::streamoff old_offset = file.tellg(); + + //Checksum value + unsigned long long checksum = 0; + + try + { + image_dos_header header; + + file.exceptions(std::ios::goodbit); + + //Read DOS header + pe_base::read_dos_header(file, header); + + //Calculate PE checksum + file.seekg(0); + unsigned long long top = 0xFFFFFFFF; + top++; + + //"CheckSum" field position in optional PE headers - it's always 64 for PE and PE+ + static const unsigned long checksum_pos_in_optional_headers = 64; + //Calculate real PE headers "CheckSum" field position + //Sum is safe here + unsigned long pe_checksum_pos = header.e_lfanew + sizeof(image_file_header) + sizeof(uint32_t) + checksum_pos_in_optional_headers; + + //Calculate checksum for each byte of file + std::streamoff filesize = pe_utils::get_file_size(file); + for(long long i = 0; i < filesize; i += 4) + { + unsigned long dw = 0; + + //Read DWORD from file + file.read(reinterpret_cast<char*>(&dw), sizeof(unsigned long)); + //Skip "CheckSum" DWORD + if(i == pe_checksum_pos) + continue; + + //Calculate checksum + checksum = (checksum & 0xffffffff) + dw + (checksum >> 32); + if(checksum > top) + checksum = (checksum & 0xffffffff) + (checksum >> 32); + } + + //Finish checksum + checksum = (checksum & 0xffff) + (checksum >> 16); + checksum = (checksum) + (checksum >> 16); + checksum = checksum & 0xffff; + + checksum += static_cast<unsigned long>(filesize); + } + catch(const std::exception&) + { + //If something went wrong, restore istream state + file.exceptions(state); + file.seekg(old_offset); + file.clear(); + //Rethrow + throw; + } + + //Restore istream state + file.exceptions(state); + file.seekg(old_offset); + file.clear(); + + //Return checksum + return static_cast<uint32_t>(checksum); +} +} diff --git a/tools/pe_bliss/pe_checksum.h b/tools/pe_bliss/pe_checksum.h new file mode 100644 index 0000000000..a568d5d369 --- /dev/null +++ b/tools/pe_bliss/pe_checksum.h @@ -0,0 +1,30 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <istream> +#include "stdint_defs.h" + +namespace pe_bliss +{ +//Calculate checksum of image (performs no checks on PE structures) +uint32_t calculate_checksum(std::istream& file); +} diff --git a/tools/pe_bliss/pe_debug.cpp b/tools/pe_bliss/pe_debug.cpp new file mode 100644 index 0000000000..a0ed3f5af1 --- /dev/null +++ b/tools/pe_bliss/pe_debug.cpp @@ -0,0 +1,865 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_debug.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; +//DEBUG +//Default constructor +debug_info::debug_info() + :characteristics_(0), + time_stamp_(0), + major_version_(0), minor_version_(0), + type_(0), + size_of_data_(0), + address_of_raw_data_(0), + pointer_to_raw_data_(0), + advanced_info_type_(advanced_info_none) +{} + +//Constructor from data +debug_info::debug_info(const image_debug_directory& debug) + :characteristics_(debug.Characteristics), + time_stamp_(debug.TimeDateStamp), + major_version_(debug.MajorVersion), minor_version_(debug.MinorVersion), + type_(debug.Type), + size_of_data_(debug.SizeOfData), + address_of_raw_data_(debug.AddressOfRawData), + pointer_to_raw_data_(debug.PointerToRawData), + advanced_info_type_(advanced_info_none) +{} + +//Returns debug characteristics +uint32_t debug_info::get_characteristics() const +{ + return characteristics_; +} + +//Returns debug datetimestamp +uint32_t debug_info::get_time_stamp() const +{ + return time_stamp_; +} + +//Returns major version +uint32_t debug_info::get_major_version() const +{ + return major_version_; +} + +//Returns minor version +uint32_t debug_info::get_minor_version() const +{ + return minor_version_; +} + +//Returns type of debug info (unchecked) +uint32_t debug_info::get_type_raw() const +{ + return type_; +} + +//Returns type of debug info from debug_info_type enumeration +debug_info::debug_info_type debug_info::get_type() const +{ + //Determine debug type + switch(type_) + { + case image_debug_type_coff: + return debug_type_coff; + + case image_debug_type_codeview: + return debug_type_codeview; + + case image_debug_type_fpo: + return debug_type_fpo; + + case image_debug_type_misc: + return debug_type_misc; + + case image_debug_type_exception: + return debug_type_exception; + + case image_debug_type_fixup: + return debug_type_fixup; + + case image_debug_type_omap_to_src: + return debug_type_omap_to_src; + + case image_debug_type_omap_from_src: + return debug_type_omap_from_src; + + case image_debug_type_borland: + return debug_type_borland; + + case image_debug_type_clsid: + return debug_type_clsid; + + case image_debug_type_reserved10: + return debug_type_reserved10; + } + + return debug_type_unknown; +} + +//Returns size of debug data (internal, .pdb or other file doesn't count) +uint32_t debug_info::get_size_of_data() const +{ + return size_of_data_; +} + +//Returns RVA of debug info when mapped to memory or zero, if info is not mapped +uint32_t debug_info::get_rva_of_raw_data() const +{ + return address_of_raw_data_; +} + +//Returns raw file pointer to raw data +uint32_t debug_info::get_pointer_to_raw_data() const +{ + return pointer_to_raw_data_; +} + +//Copy constructor +debug_info::debug_info(const debug_info& info) + :characteristics_(info.characteristics_), + time_stamp_(info.time_stamp_), + major_version_(info.major_version_), minor_version_(info.minor_version_), + type_(info.type_), + size_of_data_(info.size_of_data_), + address_of_raw_data_(info.address_of_raw_data_), + pointer_to_raw_data_(info.pointer_to_raw_data_), + advanced_info_type_(info.advanced_info_type_) +{ + copy_advanced_info(info); +} + +//Copy assignment operator +debug_info& debug_info::operator=(const debug_info& info) +{ + copy_advanced_info(info); + + characteristics_ = info.characteristics_; + time_stamp_ = info.time_stamp_; + major_version_ = info.major_version_; + minor_version_ = info.minor_version_; + type_ = info.type_; + size_of_data_ = info.size_of_data_; + address_of_raw_data_ = info.address_of_raw_data_; + pointer_to_raw_data_ = info.pointer_to_raw_data_; + advanced_info_type_ = info.advanced_info_type_; + + return *this; +} + +//Default constructor +debug_info::advanced_info::advanced_info() + :adv_pdb_7_0_info(0) //Zero pointer to advanced data +{} + +//Returns true if advanced debug info is present +bool debug_info::advanced_info::is_present() const +{ + return adv_pdb_7_0_info != 0; +} + +//Helper for advanced debug information copying +void debug_info::copy_advanced_info(const debug_info& info) +{ + free_present_advanced_info(); + + switch(info.advanced_info_type_) + { + case advanced_info_pdb_7_0: + advanced_debug_info_.adv_pdb_7_0_info = new pdb_7_0_info(*info.advanced_debug_info_.adv_pdb_7_0_info); + break; + case advanced_info_pdb_2_0: + advanced_debug_info_.adv_pdb_2_0_info = new pdb_2_0_info(*info.advanced_debug_info_.adv_pdb_2_0_info); + break; + case advanced_info_misc: + advanced_debug_info_.adv_misc_info = new misc_debug_info(*info.advanced_debug_info_.adv_misc_info); + break; + case advanced_info_coff: + advanced_debug_info_.adv_coff_info = new coff_debug_info(*info.advanced_debug_info_.adv_coff_info); + break; + default: + break; + } + + advanced_info_type_ = info.advanced_info_type_; +} + +//Helper for clearing any present advanced debug information +void debug_info::free_present_advanced_info() +{ + switch(advanced_info_type_) + { + case advanced_info_pdb_7_0: + delete advanced_debug_info_.adv_pdb_7_0_info; + break; + case advanced_info_pdb_2_0: + delete advanced_debug_info_.adv_pdb_2_0_info; + break; + case advanced_info_misc: + delete advanced_debug_info_.adv_misc_info; + break; + case advanced_info_coff: + delete advanced_debug_info_.adv_coff_info; + break; + default: + break; + } + + advanced_debug_info_.adv_pdb_7_0_info = 0; + advanced_info_type_ = advanced_info_none; +} + +//Destructor +debug_info::~debug_info() +{ + free_present_advanced_info(); +} + +//Sets advanced debug information +void debug_info::set_advanced_debug_info(const pdb_7_0_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_pdb_7_0_info = new pdb_7_0_info(info); + advanced_info_type_ = advanced_info_pdb_7_0; +} + +void debug_info::set_advanced_debug_info(const pdb_2_0_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_pdb_2_0_info = new pdb_2_0_info(info); + advanced_info_type_ = advanced_info_pdb_2_0; +} + +void debug_info::set_advanced_debug_info(const misc_debug_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_misc_info = new misc_debug_info(info); + advanced_info_type_ = advanced_info_misc; +} + +void debug_info::set_advanced_debug_info(const coff_debug_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_coff_info = new coff_debug_info(info); + advanced_info_type_ = advanced_info_coff; +} + +//Returns advanced debug information type +debug_info::advanced_info_type debug_info::get_advanced_info_type() const +{ + return advanced_info_type_; +} + +//Returns advanced debug information or throws an exception, +//if requested information type is not contained by structure +template<> +const pdb_7_0_info debug_info::get_advanced_debug_info<pdb_7_0_info>() const +{ + if(advanced_info_type_ != advanced_info_pdb_7_0) + throw pe_exception("Debug info structure does not contain PDB 7.0 data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_pdb_7_0_info; +} + +template<> +const pdb_2_0_info debug_info::get_advanced_debug_info<pdb_2_0_info>() const +{ + if(advanced_info_type_ != advanced_info_pdb_2_0) + throw pe_exception("Debug info structure does not contain PDB 2.0 data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_pdb_2_0_info; +} + +template<> +const misc_debug_info debug_info::get_advanced_debug_info<misc_debug_info>() const +{ + if(advanced_info_type_ != advanced_info_misc) + throw pe_exception("Debug info structure does not contain MISC data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_misc_info; +} + +template<> +const coff_debug_info debug_info::get_advanced_debug_info<coff_debug_info>() const +{ + if(advanced_info_type_ != advanced_info_coff) + throw pe_exception("Debug info structure does not contain COFF data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_coff_info; +} + +//Sets advanced debug information type, if no advanced info structure available +void debug_info::set_advanced_info_type(advanced_info_type type) +{ + free_present_advanced_info(); + if(advanced_info_type_ >= advanced_info_codeview_4_0) //Don't set info type for those types, which have advanced info structures + advanced_info_type_ = type; +} + +//Default constructor +pdb_7_0_info::pdb_7_0_info() + :age_(0) +{ + memset(&guid_, 0, sizeof(guid_)); +} + +//Constructor from data +pdb_7_0_info::pdb_7_0_info(const CV_INFO_PDB70* info) + :age_(info->Age), guid_(info->Signature), + pdb_file_name_(reinterpret_cast<const char*>(info->PdbFileName)) //Must be checked before for null-termination +{} + +//Returns debug PDB 7.0 structure GUID +const guid pdb_7_0_info::get_guid() const +{ + return guid_; +} + +//Returns age of build +uint32_t pdb_7_0_info::get_age() const +{ + return age_; +} + +//Returns PDB file name / path +const std::string& pdb_7_0_info::get_pdb_file_name() const +{ + return pdb_file_name_; +} + +//Default constructor +pdb_2_0_info::pdb_2_0_info() + :age_(0), signature_(0) +{} + +//Constructor from data +pdb_2_0_info::pdb_2_0_info(const CV_INFO_PDB20* info) + :age_(info->Age), signature_(info->Signature), + pdb_file_name_(reinterpret_cast<const char*>(info->PdbFileName)) //Must be checked before for null-termination +{} + +//Returns debug PDB 2.0 structure signature +uint32_t pdb_2_0_info::get_signature() const +{ + return signature_; +} + +//Returns age of build +uint32_t pdb_2_0_info::get_age() const +{ + return age_; +} + +//Returns PDB file name / path +const std::string& pdb_2_0_info::get_pdb_file_name() const +{ + return pdb_file_name_; +} + +//Default constructor +misc_debug_info::misc_debug_info() + :data_type_(0), unicode_(false) +{} + +//Constructor from data +misc_debug_info::misc_debug_info(const image_debug_misc* info) + :data_type_(info->DataType), unicode_(info->Unicode ? true : false) +{ + //IMAGE_DEBUG_MISC::Data must be checked before! + if(info->Unicode) + { +#ifdef PE_BLISS_WINDOWS + debug_data_unicode_ = std::wstring(reinterpret_cast<const wchar_t*>(info->Data), (info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */) / 2); +#else + debug_data_unicode_ = pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(info->Data), (info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */) / 2)); +#endif + + pe_utils::strip_nullbytes(debug_data_unicode_); //Strip nullbytes in the end of string + } + else + { + debug_data_ansi_ = std::string(reinterpret_cast<const char*>(info->Data), info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */); + pe_utils::strip_nullbytes(debug_data_ansi_); //Strip nullbytes in the end of string + } +} + +//Returns debug data type +uint32_t misc_debug_info::get_data_type() const +{ + return data_type_; +} + +//Returns true if data type is exe name +bool misc_debug_info::is_exe_name() const +{ + return data_type_ == image_debug_misc_exename; +} + +//Returns true if debug data is UNICODE +bool misc_debug_info::is_unicode() const +{ + return unicode_; +} + +//Returns debug data (ANSI) +const std::string& misc_debug_info::get_data_ansi() const +{ + return debug_data_ansi_; +} + +//Returns debug data (UNICODE) +const std::wstring& misc_debug_info::get_data_unicode() const +{ + return debug_data_unicode_; +} + +//Default constructor +coff_debug_info::coff_debug_info() + :number_of_symbols_(0), + lva_to_first_symbol_(0), + number_of_line_numbers_(0), + lva_to_first_line_number_(0), + rva_to_first_byte_of_code_(0), + rva_to_last_byte_of_code_(0), + rva_to_first_byte_of_data_(0), + rva_to_last_byte_of_data_(0) +{} + +//Constructor from data +coff_debug_info::coff_debug_info(const image_coff_symbols_header* info) + :number_of_symbols_(info->NumberOfSymbols), + lva_to_first_symbol_(info->LvaToFirstSymbol), + number_of_line_numbers_(info->NumberOfLinenumbers), + lva_to_first_line_number_(info->LvaToFirstLinenumber), + rva_to_first_byte_of_code_(info->RvaToFirstByteOfCode), + rva_to_last_byte_of_code_(info->RvaToLastByteOfCode), + rva_to_first_byte_of_data_(info->RvaToFirstByteOfData), + rva_to_last_byte_of_data_(info->RvaToLastByteOfData) +{} + +//Returns number of symbols +uint32_t coff_debug_info::get_number_of_symbols() const +{ + return number_of_symbols_; +} + +//Returns virtual address of the first symbol +uint32_t coff_debug_info::get_lva_to_first_symbol() const +{ + return lva_to_first_symbol_; +} + +//Returns number of line-number entries +uint32_t coff_debug_info::get_number_of_line_numbers() const +{ + return number_of_line_numbers_; +} + +//Returns virtual address of the first line-number entry +uint32_t coff_debug_info::get_lva_to_first_line_number() const +{ + return lva_to_first_line_number_; +} + +//Returns relative virtual address of the first byte of code +uint32_t coff_debug_info::get_rva_to_first_byte_of_code() const +{ + return rva_to_first_byte_of_code_; +} + +//Returns relative virtual address of the last byte of code +uint32_t coff_debug_info::get_rva_to_last_byte_of_code() const +{ + return rva_to_last_byte_of_code_; +} + +//Returns relative virtual address of the first byte of data +uint32_t coff_debug_info::get_rva_to_first_byte_of_data() const +{ + return rva_to_first_byte_of_data_; +} + +//Returns relative virtual address of the last byte of data +uint32_t coff_debug_info::get_rva_to_last_byte_of_data() const +{ + return rva_to_last_byte_of_data_; +} + +//Returns COFF symbols list +const coff_debug_info::coff_symbols_list& coff_debug_info::get_symbols() const +{ + return symbols_; +} + +//Adds COFF symbol +void coff_debug_info::add_symbol(const coff_symbol& sym) +{ + symbols_.push_back(sym); +} + +//Default constructor +coff_debug_info::coff_symbol::coff_symbol() + :storage_class_(0), + index_(0), + section_number_(0), rva_(0), + type_(0), + is_filename_(false) +{} + +//Returns storage class +uint32_t coff_debug_info::coff_symbol::get_storage_class() const +{ + return storage_class_; +} + +//Returns symbol index +uint32_t coff_debug_info::coff_symbol::get_index() const +{ + return index_; +} + +//Returns section number +uint32_t coff_debug_info::coff_symbol::get_section_number() const +{ + return section_number_; +} + +//Returns RVA +uint32_t coff_debug_info::coff_symbol::get_rva() const +{ + return rva_; +} + +//Returns true if structure contains file name +bool coff_debug_info::coff_symbol::is_file() const +{ + return is_filename_; +} + +//Returns text data (symbol or file name) +const std::string& coff_debug_info::coff_symbol::get_symbol() const +{ + return name_; +} + +//Sets storage class +void coff_debug_info::coff_symbol::set_storage_class(uint32_t storage_class) +{ + storage_class_ = storage_class; +} + +//Sets symbol index +void coff_debug_info::coff_symbol::set_index(uint32_t index) +{ + index_ = index; +} + +//Sets section number +void coff_debug_info::coff_symbol::set_section_number(uint32_t section_number) +{ + section_number_ = section_number; +} + +//Sets RVA +void coff_debug_info::coff_symbol::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Sets file name +void coff_debug_info::coff_symbol::set_file_name(const std::string& file_name) +{ + name_ = file_name; + is_filename_ = true; +} + +//Sets symbol name +void coff_debug_info::coff_symbol::set_symbol_name(const std::string& symbol_name) +{ + name_ = symbol_name; + is_filename_ = false; +} + +//Returns type +uint16_t coff_debug_info::coff_symbol::get_type() const +{ + return type_; +} + +//Sets type +void coff_debug_info::coff_symbol::set_type(uint16_t type) +{ + type_ = type; +} + +//Returns debug information list +const debug_info_list get_debug_information(const pe_base& pe) +{ + debug_info_list ret; + + //If there's no debug directory, return empty list + if(!pe.has_debug()) + return ret; + + //Check the length in bytes of the section containing debug directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_debug), pe.get_directory_rva(image_directory_entry_debug), section_data_virtual, true) + < sizeof(image_debug_directory)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + unsigned long current_pos = pe.get_directory_rva(image_directory_entry_debug); + + //First IMAGE_DEBUG_DIRECTORY table + image_debug_directory directory = pe.section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); + + if(!pe_utils::is_sum_safe(pe.get_directory_rva(image_directory_entry_debug), pe.get_directory_size(image_directory_entry_debug))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Iterate over all IMAGE_DEBUG_DIRECTORY directories + while(directory.PointerToRawData + && current_pos < pe.get_directory_rva(image_directory_entry_debug) + pe.get_directory_size(image_directory_entry_debug)) + { + //Create debug information structure + debug_info info(directory); + + //Find raw debug data + const pe_base::debug_data_list& debug_datas = pe.get_raw_debug_data_list(); + pe_base::debug_data_list::const_iterator it = debug_datas.find(directory.PointerToRawData); + if(it != debug_datas.end()) //If it exists, we'll do some detailed debug info research + { + const std::string& debug_data = (*it).second; + switch(directory.Type) + { + case image_debug_type_coff: + { + //Check data length + if(debug_data.length() < sizeof(image_coff_symbols_header)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Get coff header structure pointer + const image_coff_symbols_header* coff = reinterpret_cast<const image_coff_symbols_header*>(debug_data.data()); + + //Check possible overflows + if(coff->NumberOfSymbols >= pe_utils::max_dword / sizeof(image_symbol) + || !pe_utils::is_sum_safe(coff->NumberOfSymbols * sizeof(image_symbol), coff->LvaToFirstSymbol)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Check data length again + if(debug_data.length() < coff->NumberOfSymbols * sizeof(image_symbol) + coff->LvaToFirstSymbol) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Create COFF debug info structure + coff_debug_info coff_info(coff); + + //Enumerate debug symbols data + for(uint32_t i = 0; i < coff->NumberOfSymbols; ++i) + { + //Safe sum (checked above) + const image_symbol* sym = reinterpret_cast<const image_symbol*>(debug_data.data() + i * sizeof(image_symbol) + coff->LvaToFirstSymbol); + + coff_debug_info::coff_symbol symbol; + symbol.set_index(i); //Save symbol index + symbol.set_storage_class(sym->StorageClass); //Save storage class + symbol.set_type(sym->Type); //Save storage class + + //Check data length again + if(!pe_utils::is_sum_safe(i, sym->NumberOfAuxSymbols) + || (i + sym->NumberOfAuxSymbols) > coff->NumberOfSymbols + || debug_data.length() < (i + 1) * sizeof(image_symbol) + coff->LvaToFirstSymbol + sym->NumberOfAuxSymbols * sizeof(image_symbol)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //If symbol is filename + if(sym->StorageClass == image_sym_class_file) + { + //Save file name, it is situated just after this IMAGE_SYMBOL structure + std::string file_name(reinterpret_cast<const char*>(debug_data.data() + (i + 1) * sizeof(image_symbol)), sym->NumberOfAuxSymbols * sizeof(image_symbol)); + pe_utils::strip_nullbytes(file_name); + symbol.set_file_name(file_name); + + //Save symbol info + coff_info.add_symbol(symbol); + + //Move to next symbol + i += sym->NumberOfAuxSymbols; + continue; + } + + //Dump some other symbols + if(((sym->StorageClass == image_sym_class_static) + && (sym->NumberOfAuxSymbols == 0) + && (sym->SectionNumber == 1)) + || + ((sym->StorageClass == image_sym_class_external) + && ISFCN(sym->Type) + && (sym->SectionNumber > 0)) + ) + { + //Save RVA and section number + symbol.set_section_number(sym->SectionNumber); + symbol.set_rva(sym->Value); + + //If symbol has short name + if(sym->N.Name.Short) + { + //Copy and save symbol name + char name_buff[9]; + memcpy(name_buff, sym->N.ShortName, 8); + name_buff[8] = '\0'; + symbol.set_symbol_name(name_buff); + } + else + { + //Symbol has long name + + //Check possible overflows + if(!pe_utils::is_sum_safe(coff->LvaToFirstSymbol + coff->NumberOfSymbols * sizeof(image_symbol), sym->N.Name.Long)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Here we have an offset to the string table + uint32_t symbol_offset = coff->LvaToFirstSymbol + coff->NumberOfSymbols * sizeof(image_symbol) + sym->N.Name.Long; + + //Check data length + if(debug_data.length() < symbol_offset) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Check symbol name for null-termination + if(!pe_utils::is_null_terminated(debug_data.data() + symbol_offset, debug_data.length() - symbol_offset)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Save symbol name + symbol.set_symbol_name(debug_data.data() + symbol_offset); + } + + //Save symbol info + coff_info.add_symbol(symbol); + + //Move to next symbol + i += sym->NumberOfAuxSymbols; + continue; + } + } + + info.set_advanced_debug_info(coff_info); + } + break; + + case image_debug_type_codeview: + { + //Check data length + if(debug_data.length() < sizeof(OMFSignature*)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Get POMFSignature structure pointer from the very beginning of debug data + const OMFSignature* sig = reinterpret_cast<const OMFSignature*>(debug_data.data()); + if(!memcmp(sig->Signature, "RSDS", 4)) + { + //Signature is "RSDS" - PDB 7.0 + + //Check data length + if(debug_data.length() < sizeof(CV_INFO_PDB70)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + const CV_INFO_PDB70* pdb_data = reinterpret_cast<const CV_INFO_PDB70*>(debug_data.data()); + + //Check PDB file name null-termination + if(!pe_utils::is_null_terminated(pdb_data->PdbFileName, debug_data.length() - (sizeof(CV_INFO_PDB70) - 1 /* BYTE of filename in structure */))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + info.set_advanced_debug_info(pdb_7_0_info(pdb_data)); + } + else if(!memcmp(sig->Signature, "NB10", 4)) + { + //Signature is "NB10" - PDB 2.0 + + //Check data length + if(debug_data.length() < sizeof(CV_INFO_PDB20)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + const CV_INFO_PDB20* pdb_data = reinterpret_cast<const CV_INFO_PDB20*>(debug_data.data()); + + //Check PDB file name null-termination + if(!pe_utils::is_null_terminated(pdb_data->PdbFileName, debug_data.length() - (sizeof(CV_INFO_PDB20) - 1 /* BYTE of filename in structure */))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + info.set_advanced_debug_info(pdb_2_0_info(pdb_data)); + } + else if(!memcmp(sig->Signature, "NB09", 4)) + { + //CodeView 4.0, no structures available + info.set_advanced_info_type(debug_info::advanced_info_codeview_4_0); + } + else if(!memcmp(sig->Signature, "NB11", 4)) + { + //CodeView 5.0, no structures available + info.set_advanced_info_type(debug_info::advanced_info_codeview_5_0); + } + else if(!memcmp(sig->Signature, "NB05", 4)) + { + //Other CodeView, no structures available + info.set_advanced_info_type(debug_info::advanced_info_codeview); + } + } + + break; + + case image_debug_type_misc: + { + //Check data length + if(debug_data.length() < sizeof(image_debug_misc)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Get misc structure pointer + const image_debug_misc* misc_data = reinterpret_cast<const image_debug_misc*>(debug_data.data()); + + //Check misc data length + if(debug_data.length() < misc_data->Length /* Total length of record */) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Save advanced information + info.set_advanced_debug_info(misc_debug_info(misc_data)); + } + break; + } + } + + //Save debug information structure + ret.push_back(info); + + //Check possible overflow + if(!pe_utils::is_sum_safe(current_pos, sizeof(image_debug_directory))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Go to next debug entry + current_pos += sizeof(image_debug_directory); + directory = pe.section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_debug.h b/tools/pe_bliss/pe_debug.h new file mode 100644 index 0000000000..73a7e6860d --- /dev/null +++ b/tools/pe_bliss/pe_debug.h @@ -0,0 +1,324 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing advanced RSDS (PDB 7.0) information +class pdb_7_0_info +{ +public: + //Default constructor + pdb_7_0_info(); + //Constructor from data + explicit pdb_7_0_info(const pe_win::CV_INFO_PDB70* info); + + //Returns debug PDB 7.0 structure GUID + const pe_win::guid get_guid() const; + //Returns age of build + uint32_t get_age() const; + //Returns PDB file name / path + const std::string& get_pdb_file_name() const; + +private: + uint32_t age_; + pe_win::guid guid_; + std::string pdb_file_name_; +}; + +//Class representing advanced NB10 (PDB 2.0) information +class pdb_2_0_info +{ +public: + //Default constructor + pdb_2_0_info(); + //Constructor from data + explicit pdb_2_0_info(const pe_win::CV_INFO_PDB20* info); + + //Returns debug PDB 2.0 structure signature + uint32_t get_signature() const; + //Returns age of build + uint32_t get_age() const; + //Returns PDB file name / path + const std::string& get_pdb_file_name() const; + +private: + uint32_t age_; + uint32_t signature_; + std::string pdb_file_name_; +}; + +//Class representing advanced misc (IMAGE_DEBUG_TYPE_MISC) info +class misc_debug_info +{ +public: + //Default constructor + misc_debug_info(); + //Constructor from data + explicit misc_debug_info(const pe_win::image_debug_misc* info); + + //Returns debug data type + uint32_t get_data_type() const; + //Returns true if data type is exe name + bool is_exe_name() const; + + //Returns true if debug data is UNICODE + bool is_unicode() const; + //Returns debug data (ANSI or UNICODE) + const std::string& get_data_ansi() const; + const std::wstring& get_data_unicode() const; + +private: + uint32_t data_type_; + bool unicode_; + std::string debug_data_ansi_; + std::wstring debug_data_unicode_; +}; + +//Class representing COFF (IMAGE_DEBUG_TYPE_COFF) debug info +class coff_debug_info +{ +public: + //Structure representing COFF symbol + struct coff_symbol + { + public: + //Default constructor + coff_symbol(); + + //Returns storage class + uint32_t get_storage_class() const; + //Returns symbol index + uint32_t get_index() const; + //Returns section number + uint32_t get_section_number() const; + //Returns RVA + uint32_t get_rva() const; + //Returns type + uint16_t get_type() const; + + //Returns true if structure contains file name + bool is_file() const; + //Returns text data (symbol or file name) + const std::string& get_symbol() const; + + public: //These functions do not change everything inside image, they are used by PE class + //Sets storage class + void set_storage_class(uint32_t storage_class); + //Sets symbol index + void set_index(uint32_t index); + //Sets section number + void set_section_number(uint32_t section_number); + //Sets RVA + void set_rva(uint32_t rva); + //Sets type + void set_type(uint16_t type); + + //Sets file name + void set_file_name(const std::string& file_name); + //Sets symbol name + void set_symbol_name(const std::string& symbol_name); + + private: + uint32_t storage_class_; + uint32_t index_; + uint32_t section_number_, rva_; + uint16_t type_; + bool is_filename_; + std::string name_; + }; + +public: + typedef std::vector<coff_symbol> coff_symbols_list; + +public: + //Default constructor + coff_debug_info(); + //Constructor from data + explicit coff_debug_info(const pe_win::image_coff_symbols_header* info); + + //Returns number of symbols + uint32_t get_number_of_symbols() const; + //Returns virtual address of the first symbol + uint32_t get_lva_to_first_symbol() const; + //Returns number of line-number entries + uint32_t get_number_of_line_numbers() const; + //Returns virtual address of the first line-number entry + uint32_t get_lva_to_first_line_number() const; + //Returns relative virtual address of the first byte of code + uint32_t get_rva_to_first_byte_of_code() const; + //Returns relative virtual address of the last byte of code + uint32_t get_rva_to_last_byte_of_code() const; + //Returns relative virtual address of the first byte of data + uint32_t get_rva_to_first_byte_of_data() const; + //Returns relative virtual address of the last byte of data + uint32_t get_rva_to_last_byte_of_data() const; + + //Returns COFF symbols list + const coff_symbols_list& get_symbols() const; + +public: //These functions do not change everything inside image, they are used by PE class + //Adds COFF symbol + void add_symbol(const coff_symbol& sym); + +private: + uint32_t number_of_symbols_; + uint32_t lva_to_first_symbol_; + uint32_t number_of_line_numbers_; + uint32_t lva_to_first_line_number_; + uint32_t rva_to_first_byte_of_code_; + uint32_t rva_to_last_byte_of_code_; + uint32_t rva_to_first_byte_of_data_; + uint32_t rva_to_last_byte_of_data_; + +private: + coff_symbols_list symbols_; +}; + +//Class representing debug information +class debug_info +{ +public: + //Enumeration of debug information types + enum debug_info_type + { + debug_type_unknown, + debug_type_coff, + debug_type_codeview, + debug_type_fpo, + debug_type_misc, + debug_type_exception, + debug_type_fixup, + debug_type_omap_to_src, + debug_type_omap_from_src, + debug_type_borland, + debug_type_reserved10, + debug_type_clsid + }; + +public: + //Enumeration of advanced debug information types + enum advanced_info_type + { + advanced_info_none, //No advanced info + advanced_info_pdb_7_0, //PDB 7.0 + advanced_info_pdb_2_0, //PDB 2.0 + advanced_info_misc, //MISC debug info + advanced_info_coff, //COFF debug info + //No advanced info structures available for types below + advanced_info_codeview_4_0, //CodeView 4.0 + advanced_info_codeview_5_0, //CodeView 5.0 + advanced_info_codeview //CodeView + }; + +public: + //Default constructor + debug_info(); + //Constructor from data + explicit debug_info(const pe_win::image_debug_directory& debug); + //Copy constructor + debug_info(const debug_info& info); + //Copy assignment operator + debug_info& operator=(const debug_info& info); + //Destructor + ~debug_info(); + + //Returns debug characteristics + uint32_t get_characteristics() const; + //Returns debug datetimestamp + uint32_t get_time_stamp() const; + //Returns major version + uint32_t get_major_version() const; + //Returns minor version + uint32_t get_minor_version() const; + //Returns type of debug info (unchecked) + uint32_t get_type_raw() const; + //Returns type of debug info from debug_info_type enumeration + debug_info_type get_type() const; + //Returns size of debug data (internal, .pdb or other file doesn't count) + uint32_t get_size_of_data() const; + //Returns RVA of debug info when mapped to memory or zero, if info is not mapped + uint32_t get_rva_of_raw_data() const; + //Returns raw file pointer to raw data + uint32_t get_pointer_to_raw_data() const; + + //Returns advanced debug information type + advanced_info_type get_advanced_info_type() const; + //Returns advanced debug information or throws an exception, + //if requested information type is not contained by structure + template<typename AdvancedInfo> + const AdvancedInfo get_advanced_debug_info() const; + +public: //These functions do not change everything inside image, they are used by PE class + //Sets advanced debug information + void set_advanced_debug_info(const pdb_7_0_info& info); + void set_advanced_debug_info(const pdb_2_0_info& info); + void set_advanced_debug_info(const misc_debug_info& info); + void set_advanced_debug_info(const coff_debug_info& info); + + //Sets advanced debug information type, if no advanced info structure available + void set_advanced_info_type(advanced_info_type type); + +private: + uint32_t characteristics_; + uint32_t time_stamp_; + uint32_t major_version_, minor_version_; + uint32_t type_; + uint32_t size_of_data_; + uint32_t address_of_raw_data_; //RVA when mapped or 0 + uint32_t pointer_to_raw_data_; //RAW file offset + + //Union containing advanced debug information pointer + union advanced_info + { + public: + //Default constructor + advanced_info(); + + //Returns true if advanced debug info is present + bool is_present() const; + + public: + pdb_7_0_info* adv_pdb_7_0_info; + pdb_2_0_info* adv_pdb_2_0_info; + misc_debug_info* adv_misc_info; + coff_debug_info* adv_coff_info; + }; + + //Helper for advanced debug information copying + void copy_advanced_info(const debug_info& info); + //Helper for clearing any present advanced debug information + void free_present_advanced_info(); + + advanced_info advanced_debug_info_; + //Advanced information type + advanced_info_type advanced_info_type_; +}; + +typedef std::vector<debug_info> debug_info_list; + +//Returns debug information list +const debug_info_list get_debug_information(const pe_base& pe); +} diff --git a/tools/pe_bliss/pe_directory.cpp b/tools/pe_bliss/pe_directory.cpp new file mode 100644 index 0000000000..13ad2afc5d --- /dev/null +++ b/tools/pe_bliss/pe_directory.cpp @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_directory.h" + +namespace pe_bliss +{ +//Default constructor +image_directory::image_directory() + :rva_(0), size_(0) +{} + +//Constructor from data +image_directory::image_directory(uint32_t rva, uint32_t size) + :rva_(rva), size_(size) +{} + +//Returns RVA +uint32_t image_directory::get_rva() const +{ + return rva_; +} + +//Returns size +uint32_t image_directory::get_size() const +{ + return size_; +} + +//Sets RVA +void image_directory::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Sets size +void image_directory::set_size(uint32_t size) +{ + size_ = size; +} +} diff --git a/tools/pe_bliss/pe_directory.h b/tools/pe_bliss/pe_directory.h new file mode 100644 index 0000000000..a7b1ea7a5f --- /dev/null +++ b/tools/pe_bliss/pe_directory.h @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "stdint_defs.h" + +namespace pe_bliss +{ +//Class representing image directory data +class image_directory +{ +public: + //Default constructor + image_directory(); + //Constructor from data + image_directory(uint32_t rva, uint32_t size); + + //Returns RVA + uint32_t get_rva() const; + //Returns size + uint32_t get_size() const; + + //Sets RVA + void set_rva(uint32_t rva); + //Sets size + void set_size(uint32_t size); + +private: + uint32_t rva_; + uint32_t size_; +}; +} diff --git a/tools/pe_bliss/pe_dotnet.cpp b/tools/pe_bliss/pe_dotnet.cpp new file mode 100644 index 0000000000..f34a76eae8 --- /dev/null +++ b/tools/pe_bliss/pe_dotnet.cpp @@ -0,0 +1,186 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_dotnet.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//.NET +basic_dotnet_info::basic_dotnet_info() +{ + memset(&header_, 0, sizeof(header_)); +} + +//Constructor from data +basic_dotnet_info::basic_dotnet_info(const image_cor20_header& header) + :header_(header) +{} + +//Returns major runtime version +uint16_t basic_dotnet_info::get_major_runtime_version() const +{ + return header_.MajorRuntimeVersion; +} + +//Returns minor runtime version +uint16_t basic_dotnet_info::get_minor_runtime_version() const +{ + return header_.MinorRuntimeVersion; +} + +//Returns RVA of metadata (symbol table and startup information) +uint32_t basic_dotnet_info::get_rva_of_metadata() const +{ + return header_.MetaData.VirtualAddress; +} + +//Returns size of metadata (symbol table and startup information) +uint32_t basic_dotnet_info::get_size_of_metadata() const +{ + return header_.MetaData.Size; +} + +//Returns flags +uint32_t basic_dotnet_info::get_flags() const +{ + return header_.Flags; +} + +//Returns true if entry point is native +bool basic_dotnet_info::is_native_entry_point() const +{ + return (header_.Flags & comimage_flags_native_entrypoint) ? true : false; +} + +//Returns true if 32 bit required +bool basic_dotnet_info::is_32bit_required() const +{ + return (header_.Flags & comimage_flags_32bitrequired) ? true : false; +} + +//Returns true if image is IL library +bool basic_dotnet_info::is_il_library() const +{ + return (header_.Flags & comimage_flags_il_library) ? true : false; +} + +//Returns true if image uses IL only +bool basic_dotnet_info::is_il_only() const +{ + return (header_.Flags & comimage_flags_ilonly) ? true : false; +} + +//Returns entry point RVA (if entry point is native) +//Returns entry point managed token (if entry point is managed) +uint32_t basic_dotnet_info::get_entry_point_rva_or_token() const +{ + return header_.EntryPointToken; +} + +//Returns RVA of managed resources +uint32_t basic_dotnet_info::get_rva_of_resources() const +{ + return header_.Resources.VirtualAddress; +} + +//Returns size of managed resources +uint32_t basic_dotnet_info::get_size_of_resources() const +{ + return header_.Resources.Size; +} + +//Returns RVA of strong name signature +uint32_t basic_dotnet_info::get_rva_of_strong_name_signature() const +{ + return header_.StrongNameSignature.VirtualAddress; +} + +//Returns size of strong name signature +uint32_t basic_dotnet_info::get_size_of_strong_name_signature() const +{ + return header_.StrongNameSignature.Size; +} + +//Returns RVA of code manager table +uint32_t basic_dotnet_info::get_rva_of_code_manager_table() const +{ + return header_.CodeManagerTable.VirtualAddress; +} + +//Returns size of code manager table +uint32_t basic_dotnet_info::get_size_of_code_manager_table() const +{ + return header_.CodeManagerTable.Size; +} + +//Returns RVA of VTable fixups +uint32_t basic_dotnet_info::get_rva_of_vtable_fixups() const +{ + return header_.VTableFixups.VirtualAddress; +} + +//Returns size of VTable fixups +uint32_t basic_dotnet_info::get_size_of_vtable_fixups() const +{ + return header_.VTableFixups.Size; +} + +//Returns RVA of export address table jumps +uint32_t basic_dotnet_info::get_rva_of_export_address_table_jumps() const +{ + return header_.ExportAddressTableJumps.VirtualAddress; +} + +//Returns size of export address table jumps +uint32_t basic_dotnet_info::get_size_of_export_address_table_jumps() const +{ + return header_.ExportAddressTableJumps.Size; +} + +//Returns RVA of managed native header +//(precompiled header info, usually set to zero, for internal use) +uint32_t basic_dotnet_info::get_rva_of_managed_native_header() const +{ + return header_.ManagedNativeHeader.VirtualAddress; +} + +//Returns size of managed native header +//(precompiled header info, usually set to zero, for internal use) +uint32_t basic_dotnet_info::get_size_of_managed_native_header() const +{ + return header_.ManagedNativeHeader.Size; +} + +//Returns basic .NET information +//If image is not native, throws an exception +const basic_dotnet_info get_basic_dotnet_info(const pe_base& pe) +{ + //If there's no debug directory, return empty list + if(!pe.is_dotnet()) + throw pe_exception("Image does not have managed code", pe_exception::image_does_not_have_managed_code); + + //Return basic .NET information + return basic_dotnet_info(pe.section_data_from_rva<image_cor20_header>(pe.get_directory_rva(image_directory_entry_com_descriptor), section_data_virtual, true)); +} +} diff --git a/tools/pe_bliss/pe_dotnet.h b/tools/pe_bliss/pe_dotnet.h new file mode 100644 index 0000000000..96b0ac7d0a --- /dev/null +++ b/tools/pe_bliss/pe_dotnet.h @@ -0,0 +1,97 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing basic .NET header information +class basic_dotnet_info +{ +public: + //Default constructor + basic_dotnet_info(); + //Constructor from data + explicit basic_dotnet_info(const pe_win::image_cor20_header& header); + + //Returns major runtime version + uint16_t get_major_runtime_version() const; + //Returns minor runtime version + uint16_t get_minor_runtime_version() const; + + //Returns RVA of metadata (symbol table and startup information) + uint32_t get_rva_of_metadata() const; + //Returns size of metadata (symbol table and startup information) + uint32_t get_size_of_metadata() const; + + //Returns flags + uint32_t get_flags() const; + + //Returns true if entry point is native + bool is_native_entry_point() const; + //Returns true if 32 bit required + bool is_32bit_required() const; + //Returns true if image is IL library + bool is_il_library() const; + //Returns true if image uses IL only + bool is_il_only() const; + + //Returns entry point RVA (if entry point is native) + //Returns entry point managed token (if entry point is managed) + uint32_t get_entry_point_rva_or_token() const; + + //Returns RVA of managed resources + uint32_t get_rva_of_resources() const; + //Returns size of managed resources + uint32_t get_size_of_resources() const; + //Returns RVA of strong name signature + uint32_t get_rva_of_strong_name_signature() const; + //Returns size of strong name signature + uint32_t get_size_of_strong_name_signature() const; + //Returns RVA of code manager table + uint32_t get_rva_of_code_manager_table() const; + //Returns size of code manager table + uint32_t get_size_of_code_manager_table() const; + //Returns RVA of VTable fixups + uint32_t get_rva_of_vtable_fixups() const; + //Returns size of VTable fixups + uint32_t get_size_of_vtable_fixups() const; + //Returns RVA of export address table jumps + uint32_t get_rva_of_export_address_table_jumps() const; + //Returns size of export address table jumps + uint32_t get_size_of_export_address_table_jumps() const; + //Returns RVA of managed native header + //(precompiled header info, usually set to zero, for internal use) + uint32_t get_rva_of_managed_native_header() const; + //Returns size of managed native header + //(precompiled header info, usually set to zero, for internal use) + uint32_t get_size_of_managed_native_header() const; + +private: + pe_win::image_cor20_header header_; +}; + +//Returns basic .NET information +//If image is not native, throws an exception +const basic_dotnet_info get_basic_dotnet_info(const pe_base& pe); +} diff --git a/tools/pe_bliss/pe_exception.cpp b/tools/pe_bliss/pe_exception.cpp new file mode 100644 index 0000000000..3161f93599 --- /dev/null +++ b/tools/pe_bliss/pe_exception.cpp @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_exception.h" + +namespace pe_bliss +{ +//PE exception class constructors +pe_exception::pe_exception(const char* text, exception_id id) + :std::runtime_error(text), id_(id) +{} + +pe_exception::pe_exception(const std::string& text, exception_id id) + :std::runtime_error(text), id_(id) +{} + +//Returns exception ID +pe_exception::exception_id pe_exception::get_id() const +{ + return id_; +} +} diff --git a/tools/pe_bliss/pe_exception.h b/tools/pe_bliss/pe_exception.h new file mode 100644 index 0000000000..2b58a95772 --- /dev/null +++ b/tools/pe_bliss/pe_exception.h @@ -0,0 +1,130 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <exception> +#include <stdexcept> + +namespace pe_bliss +{ +//PE exception class +class pe_exception : public std::runtime_error +{ +public: + //Exception IDs + enum exception_id + { + unknown_error, + bad_pe_file, + bad_dos_header, + image_nt_headers_not_found, + error_reading_image_nt_headers, + error_reading_data_directories, + error_reading_file, + pe_signature_incorrect, + incorrect_number_of_rva_and_sizes, + error_changing_section_virtual_size, + section_number_incorrect, + section_table_incorrect, + incorrect_section_alignment, + incorrect_file_alignment, + incorrect_size_of_image, + incorrect_size_of_headers, + image_section_headers_not_found, + zero_section_sizes, + section_incorrect_addr_or_size, + section_not_found, + image_section_data_not_found, + no_section_found, + image_section_table_incorrect, + directory_does_not_exist, + rva_not_exists, + error_reading_section_header, + error_reading_overlay, + incorrect_address_conversion, + + incorrect_export_directory, + incorrect_import_directory, + incorrect_relocation_directory, + incorrect_tls_directory, + incorrect_config_directory, + incorrect_bound_import_directory, + incorrect_resource_directory, + incorrect_exception_directory, + incorrect_debug_directory, + + resource_directory_entry_error, + resource_directory_entry_not_found, + resource_data_entry_not_found, + resource_incorrect_bitmap, + resource_incorrect_icon, + resource_incorrect_cursor, + resource_incorrect_string_table, + resource_string_not_found, + resource_incorrect_message_table, + resource_incorrect_version_info, + + advanced_debug_information_request_error, + image_does_not_have_managed_code, + + section_is_empty, + data_is_empty, + stream_is_bad, + + section_is_not_attached, + insufficient_space, + + cannot_rebase_relocations, + + exports_list_is_empty, + duplicate_exported_function_ordinal, + duplicate_exported_function_name, + + version_info_string_does_not_exist, + + no_more_sections_can_be_added, + + no_icon_group_found, + no_cursor_group_found, + + encoding_convertion_error, + + error_expanding_section, + + cannot_rebuild_image + }; + +public: + //Class constructors + explicit pe_exception(const char* text, exception_id id = unknown_error); + explicit pe_exception(const std::string& text, exception_id id = unknown_error); + + //Returns exception ID from exception_id enumeration + exception_id get_id() const; + + //Destructor + virtual ~pe_exception() throw() + {} + +private: + exception_id id_; +}; +} diff --git a/tools/pe_bliss/pe_exception_directory.cpp b/tools/pe_bliss/pe_exception_directory.cpp new file mode 100644 index 0000000000..1813f02021 --- /dev/null +++ b/tools/pe_bliss/pe_exception_directory.cpp @@ -0,0 +1,177 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_exception_directory.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//EXCEPTION DIRECTORY (exists on PE+ only) +//Default constructor +exception_entry::exception_entry() + :begin_address_(0), end_address_(0), unwind_info_address_(0), + unwind_info_version_(0), + flags_(0), + size_of_prolog_(0), + count_of_codes_(0), + frame_register_(0), + frame_offset_(0) +{} + +//Constructor from data +exception_entry::exception_entry(const image_runtime_function_entry& entry, const unwind_info& unwind_info) + :begin_address_(entry.BeginAddress), end_address_(entry.EndAddress), unwind_info_address_(entry.UnwindInfoAddress), + unwind_info_version_(unwind_info.Version), + flags_(unwind_info.Flags), + size_of_prolog_(unwind_info.SizeOfProlog), + count_of_codes_(unwind_info.CountOfCodes), + frame_register_(unwind_info.FrameRegister), + frame_offset_(unwind_info.FrameOffset) +{} + +//Returns starting address of function, affected by exception unwinding +uint32_t exception_entry::get_begin_address() const +{ + return begin_address_; +} + +//Returns ending address of function, affected by exception unwinding +uint32_t exception_entry::get_end_address() const +{ + return end_address_; +} + +//Returns unwind info address +uint32_t exception_entry::get_unwind_info_address() const +{ + return unwind_info_address_; +} + +//Returns UNWIND_INFO structure version +uint8_t exception_entry::get_unwind_info_version() const +{ + return unwind_info_version_; +} + +//Returns unwind info flags +uint8_t exception_entry::get_flags() const +{ + return flags_; +} + +//The function has an exception handler that should be called +//when looking for functions that need to examine exceptions +bool exception_entry::has_exception_handler() const +{ + return (flags_ & unw_flag_ehandler) ? true : false; +} + +//The function has a termination handler that should be called +//when unwinding an exception +bool exception_entry::has_termination_handler() const +{ + return (flags_ & unw_flag_uhandler) ? true : false; +} + +//The unwind info structure is not the primary one for the procedure +bool exception_entry::is_chaininfo() const +{ + return (flags_ & unw_flag_chaininfo) ? true : false; +} + +//Returns size of function prolog +uint8_t exception_entry::get_size_of_prolog() const +{ + return size_of_prolog_; +} + +//Returns number of unwind slots +uint8_t exception_entry::get_number_of_unwind_slots() const +{ + return count_of_codes_; +} + +//If the function uses frame pointer +bool exception_entry::uses_frame_pointer() const +{ + return frame_register_ != 0; +} + +//Number of the nonvolatile register used as the frame pointer, +//using the same encoding for the operation info field of UNWIND_CODE nodes +uint8_t exception_entry::get_frame_pointer_register_number() const +{ + return frame_register_; +} + +//The scaled offset from RSP that is applied to the FP reg when it is established. +//The actual FP reg is set to RSP + 16 * this number, allowing offsets from 0 to 240 +uint8_t exception_entry::get_scaled_rsp_offset() const +{ + return frame_offset_; +} + +//Returns exception directory data (exists on PE+ only) +//Unwind opcodes are not listed, because their format and list are subject to change +const exception_entry_list get_exception_directory_data(const pe_base& pe) +{ + exception_entry_list ret; + + //If image doesn't have exception directory, return empty list + if(!pe.has_exception_directory()) + return ret; + + //Check the length in bytes of the section containing exception directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_exception), pe.get_directory_rva(image_directory_entry_exception), section_data_virtual, true) + < sizeof(image_runtime_function_entry)) + throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); + + unsigned long current_pos = pe.get_directory_rva(image_directory_entry_exception); + + //Check if structures are DWORD-aligned + if(current_pos % sizeof(uint32_t)) + throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); + + //First IMAGE_RUNTIME_FUNCTION_ENTRY table + image_runtime_function_entry exception_table = pe.section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true); + + //todo: virtual addresses BeginAddress and EndAddress are not checked to be inside image + while(exception_table.BeginAddress) + { + //Check addresses + if(exception_table.BeginAddress > exception_table.EndAddress) + throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); + + //Get unwind information + unwind_info info = pe.section_data_from_rva<unwind_info>(exception_table.UnwindInfoAddress, section_data_virtual, true); + + //Create exception entry and save it + ret.push_back(exception_entry(exception_table, info)); + + //Go to next exception entry + current_pos += sizeof(image_runtime_function_entry); + exception_table = pe.section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_exception_directory.h b/tools/pe_bliss/pe_exception_directory.h new file mode 100644 index 0000000000..6f4fc2298b --- /dev/null +++ b/tools/pe_bliss/pe_exception_directory.h @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing exception directory entry +class exception_entry +{ +public: + //Default constructor + exception_entry(); + //Constructor from data + exception_entry(const pe_win::image_runtime_function_entry& entry, const pe_win::unwind_info& unwind_info); + + //Returns starting address of function, affected by exception unwinding + uint32_t get_begin_address() const; + //Returns ending address of function, affected by exception unwinding + uint32_t get_end_address() const; + //Returns unwind info address + uint32_t get_unwind_info_address() const; + + //Returns UNWIND_INFO structure version + uint8_t get_unwind_info_version() const; + + //Returns unwind info flags + uint8_t get_flags() const; + //The function has an exception handler that should be called + //when looking for functions that need to examine exceptions + bool has_exception_handler() const; + //The function has a termination handler that should be called + //when unwinding an exception + bool has_termination_handler() const; + //The unwind info structure is not the primary one for the procedure + bool is_chaininfo() const; + + //Returns size of function prolog + uint8_t get_size_of_prolog() const; + + //Returns number of unwind slots + uint8_t get_number_of_unwind_slots() const; + + //If the function uses frame pointer + bool uses_frame_pointer() const; + //Number of the nonvolatile register used as the frame pointer, + //using the same encoding for the operation info field of UNWIND_CODE nodes + uint8_t get_frame_pointer_register_number() const; + //The scaled offset from RSP that is applied to the FP reg when it is established. + //The actual FP reg is set to RSP + 16 * this number, allowing offsets from 0 to 240 + uint8_t get_scaled_rsp_offset() const; + +private: + uint32_t begin_address_, end_address_, unwind_info_address_; + uint8_t unwind_info_version_; + uint8_t flags_; + uint8_t size_of_prolog_; + uint8_t count_of_codes_; + uint8_t frame_register_, frame_offset_; +}; + +typedef std::vector<exception_entry> exception_entry_list; + +//Returns exception directory data (exists on PE+ only) +//Unwind opcodes are not listed, because their format and list are subject to change +const exception_entry_list get_exception_directory_data(const pe_base& pe); +} diff --git a/tools/pe_bliss/pe_exports.cpp b/tools/pe_bliss/pe_exports.cpp new file mode 100644 index 0000000000..c2ad895554 --- /dev/null +++ b/tools/pe_bliss/pe_exports.cpp @@ -0,0 +1,700 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <set> +#include <algorithm> +#include <string.h> +#include "pe_exports.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//EXPORTS +//Default constructor +exported_function::exported_function() + :ordinal_(0), rva_(0), has_name_(false), name_ordinal_(0), forward_(false) +{} + +//Returns ordinal of function (actually, ordinal = hint + ordinal base) +uint16_t exported_function::get_ordinal() const +{ + return ordinal_; +} + +//Returns RVA of function +uint32_t exported_function::get_rva() const +{ + return rva_; +} + +//Returns name of function +const std::string& exported_function::get_name() const +{ + return name_; +} + +//Returns true if function has name and name ordinal +bool exported_function::has_name() const +{ + return has_name_; +} + +//Returns name ordinal of function +uint16_t exported_function::get_name_ordinal() const +{ + return name_ordinal_; +} + +//Returns true if function is forwarded to other library +bool exported_function::is_forwarded() const +{ + return forward_; +} + +//Returns the name of forwarded function +const std::string& exported_function::get_forwarded_name() const +{ + return forward_name_; +} + +//Sets ordinal of function +void exported_function::set_ordinal(uint16_t ordinal) +{ + ordinal_ = ordinal; +} + +//Sets RVA of function +void exported_function::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Sets name of function (or clears it, if empty name is passed) +void exported_function::set_name(const std::string& name) +{ + name_ = name; + has_name_ = !name.empty(); +} + +//Sets name ordinal +void exported_function::set_name_ordinal(uint16_t name_ordinal) +{ + name_ordinal_ = name_ordinal; +} + +//Sets forwarded function name (or clears it, if empty name is passed) +void exported_function::set_forwarded_name(const std::string& name) +{ + forward_name_ = name; + forward_ = !name.empty(); +} + +//Default constructor +export_info::export_info() + :characteristics_(0), + timestamp_(0), + major_version_(0), + minor_version_(0), + ordinal_base_(0), + number_of_functions_(0), + number_of_names_(0), + address_of_functions_(0), + address_of_names_(0), + address_of_name_ordinals_(0) +{} + +//Returns characteristics +uint32_t export_info::get_characteristics() const +{ + return characteristics_; +} + +//Returns timestamp +uint32_t export_info::get_timestamp() const +{ + return timestamp_; +} + +//Returns major version +uint16_t export_info::get_major_version() const +{ + return major_version_; +} + +//Returns minor version +uint16_t export_info::get_minor_version() const +{ + return minor_version_; +} + +//Returns DLL name +const std::string& export_info::get_name() const +{ + return name_; +} + +//Returns ordinal base +uint32_t export_info::get_ordinal_base() const +{ + return ordinal_base_; +} + +//Returns number of functions +uint32_t export_info::get_number_of_functions() const +{ + return number_of_functions_; +} + +//Returns number of function names +uint32_t export_info::get_number_of_names() const +{ + return number_of_names_; +} + +//Returns RVA of function address table +uint32_t export_info::get_rva_of_functions() const +{ + return address_of_functions_; +} + +//Returns RVA of function name address table +uint32_t export_info::get_rva_of_names() const +{ + return address_of_names_; +} + +//Returns RVA of name ordinals table +uint32_t export_info::get_rva_of_name_ordinals() const +{ + return address_of_name_ordinals_; +} + +//Sets characteristics +void export_info::set_characteristics(uint32_t characteristics) +{ + characteristics_ = characteristics; +} + +//Sets timestamp +void export_info::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Sets major version +void export_info::set_major_version(uint16_t major_version) +{ + major_version_ = major_version; +} + +//Sets minor version +void export_info::set_minor_version(uint16_t minor_version) +{ + minor_version_ = minor_version; +} + +//Sets DLL name +void export_info::set_name(const std::string& name) +{ + name_ = name; +} + +//Sets ordinal base +void export_info::set_ordinal_base(uint32_t ordinal_base) +{ + ordinal_base_ = ordinal_base; +} + +//Sets number of functions +void export_info::set_number_of_functions(uint32_t number_of_functions) +{ + number_of_functions_ = number_of_functions; +} + +//Sets number of function names +void export_info::set_number_of_names(uint32_t number_of_names) +{ + number_of_names_ = number_of_names; +} + +//Sets RVA of function address table +void export_info::set_rva_of_functions(uint32_t rva_of_functions) +{ + address_of_functions_ = rva_of_functions; +} + +//Sets RVA of function name address table +void export_info::set_rva_of_names(uint32_t rva_of_names) +{ + address_of_names_ = rva_of_names; +} + +//Sets RVA of name ordinals table +void export_info::set_rva_of_name_ordinals(uint32_t rva_of_name_ordinals) +{ + address_of_name_ordinals_ = rva_of_name_ordinals; +} + +const exported_functions_list get_exported_functions(const pe_base& pe, export_info* info); + +//Returns array of exported functions +const exported_functions_list get_exported_functions(const pe_base& pe) +{ + return get_exported_functions(pe, 0); +} + +//Returns array of exported functions and information about export +const exported_functions_list get_exported_functions(const pe_base& pe, export_info& info) +{ + return get_exported_functions(pe, &info); +} + +//Helper: sorts exported function list by ordinals +struct ordinal_sorter +{ +public: + bool operator()(const exported_function& func1, const exported_function& func2) const; +}; + +//Returns array of exported functions and information about export (if info != 0) +const exported_functions_list get_exported_functions(const pe_base& pe, export_info* info) +{ + //Returned exported functions info array + std::vector<exported_function> ret; + + if(pe.has_exports()) + { + //Check the length in bytes of the section containing export directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_export), + pe.get_directory_rva(image_directory_entry_export), section_data_virtual, true) + < sizeof(image_export_directory)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + image_export_directory exports = pe.section_data_from_rva<image_export_directory>(pe.get_directory_rva(image_directory_entry_export), section_data_virtual, true); + + unsigned long max_name_length; + + if(info) + { + //Save some export info data + info->set_characteristics(exports.Characteristics); + info->set_major_version(exports.MajorVersion); + info->set_minor_version(exports.MinorVersion); + + //Get byte count that we have for dll name + if((max_name_length = pe.section_data_length_from_rva(exports.Name, exports.Name, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Get dll name pointer + const char* dll_name = pe.section_data_from_rva(exports.Name, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(dll_name, max_name_length)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Save the rest of export information data + info->set_name(dll_name); + info->set_number_of_functions(exports.NumberOfFunctions); + info->set_number_of_names(exports.NumberOfNames); + info->set_ordinal_base(exports.Base); + info->set_rva_of_functions(exports.AddressOfFunctions); + info->set_rva_of_names(exports.AddressOfNames); + info->set_rva_of_name_ordinals(exports.AddressOfNameOrdinals); + info->set_timestamp(exports.TimeDateStamp); + } + + if(!exports.NumberOfFunctions) + return ret; + + //Check IMAGE_EXPORT_DIRECTORY fields + if(exports.NumberOfNames > exports.NumberOfFunctions) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Check some export directory fields + if((!exports.AddressOfNameOrdinals && exports.AddressOfNames) || + (exports.AddressOfNameOrdinals && !exports.AddressOfNames) || + !exports.AddressOfFunctions + || exports.NumberOfFunctions >= pe_utils::max_dword / sizeof(uint32_t) + || exports.NumberOfNames > pe_utils::max_dword / sizeof(uint32_t) + || !pe_utils::is_sum_safe(exports.AddressOfFunctions, exports.NumberOfFunctions * sizeof(uint32_t)) + || !pe_utils::is_sum_safe(exports.AddressOfNames, exports.NumberOfNames * sizeof(uint32_t)) + || !pe_utils::is_sum_safe(exports.AddressOfNameOrdinals, exports.NumberOfFunctions * sizeof(uint32_t)) + || !pe_utils::is_sum_safe(pe.get_directory_rva(image_directory_entry_export), pe.get_directory_size(image_directory_entry_export))) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Check if it is enough bytes to hold AddressOfFunctions table + if(pe.section_data_length_from_rva(exports.AddressOfFunctions, exports.AddressOfFunctions, section_data_virtual, true) + < exports.NumberOfFunctions * sizeof(uint32_t)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + if(exports.AddressOfNames) + { + //Check if it is enough bytes to hold name and ordinal tables + if(pe.section_data_length_from_rva(exports.AddressOfNameOrdinals, exports.AddressOfNameOrdinals, section_data_virtual, true) + < exports.NumberOfNames * sizeof(uint16_t)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + if(pe.section_data_length_from_rva(exports.AddressOfNames, exports.AddressOfNames, section_data_virtual, true) + < exports.NumberOfNames * sizeof(uint32_t)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + } + + for(uint32_t ordinal = 0; ordinal < exports.NumberOfFunctions; ordinal++) + { + //Get function address + //Sum and multiplication are safe (checked above) + uint32_t rva = pe.section_data_from_rva<uint32_t>(exports.AddressOfFunctions + ordinal * sizeof(uint32_t), section_data_virtual, true); + + //If we have a skip + if(!rva) + continue; + + exported_function func; + func.set_rva(rva); + + if(!pe_utils::is_sum_safe(exports.Base, ordinal) || exports.Base + ordinal > pe_utils::max_word) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + func.set_ordinal(static_cast<uint16_t>(ordinal + exports.Base)); + + //Scan for function name ordinal + for(uint32_t i = 0; i < exports.NumberOfNames; i++) + { + uint16_t ordinal2 = pe.section_data_from_rva<uint16_t>(exports.AddressOfNameOrdinals + i * sizeof(uint16_t), section_data_virtual, true); + + //If function has name (and name ordinal) + if(ordinal == ordinal2) + { + //Get function name + //Sum and multiplication are safe (checked above) + uint32_t function_name_rva = pe.section_data_from_rva<uint32_t>(exports.AddressOfNames + i * sizeof(uint32_t), section_data_virtual, true); + + //Get byte count that we have for function name + if((max_name_length = pe.section_data_length_from_rva(function_name_rva, function_name_rva, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Get function name pointer + const char* func_name = pe.section_data_from_rva(function_name_rva, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(func_name, max_name_length)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Save function info + func.set_name(func_name); + func.set_name_ordinal(ordinal2); + + //If the function is just a redirect, save its name + if(rva >= pe.get_directory_rva(image_directory_entry_export) + sizeof(image_directory_entry_export) && + rva < pe.get_directory_rva(image_directory_entry_export) + pe.get_directory_size(image_directory_entry_export)) + { + if((max_name_length = pe.section_data_length_from_rva(rva, rva, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Get forwarded function name pointer + const char* forwarded_func_name = pe.section_data_from_rva(rva, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(forwarded_func_name, max_name_length)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Set the name of forwarded function + func.set_forwarded_name(forwarded_func_name); + } + + break; + } + } + + //Add function info to output array + ret.push_back(func); + } + } + + return ret; +} + +//Helper export functions +//Returns pair: <ordinal base for supplied functions; maximum ordinal value for supplied functions> +const std::pair<uint16_t, uint16_t> get_export_ordinal_limits(const exported_functions_list& exports) +{ + if(exports.empty()) + return std::make_pair(0, 0); + + uint16_t max_ordinal = 0; //Maximum ordinal number + uint16_t ordinal_base = pe_utils::max_word; //Minimum ordinal value + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + const exported_function& func = (*it); + + //Calculate maximum and minimum ordinal numbers + max_ordinal = std::max<uint16_t>(max_ordinal, func.get_ordinal()); + ordinal_base = std::min<uint16_t>(ordinal_base, func.get_ordinal()); + } + + return std::make_pair(ordinal_base, max_ordinal); +} + +//Checks if exported function name already exists +bool exported_name_exists(const std::string& function_name, const exported_functions_list& exports) +{ + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + if((*it).has_name() && (*it).get_name() == function_name) + return true; + } + + return false; +} + +//Checks if exported function name already exists +bool exported_ordinal_exists(uint16_t ordinal, const exported_functions_list& exports) +{ + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + if((*it).get_ordinal() == ordinal) + return true; + } + + return false; +} + +//Helper: sorts exported function list by ordinals +bool ordinal_sorter::operator()(const exported_function& func1, const exported_function& func2) const +{ + return func1.get_ordinal() < func2.get_ordinal(); +} + +//Export directory rebuilder +//info - export information +//exported_functions_list - list of exported functions +//exports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from exports_section raw data start +//save_to_pe_headers - if true, new export directory information will be saved to PE image headers +//auto_strip_last_section - if true and exports are placed in the last section, it will be automatically stripped +//number_of_functions and number_of_names parameters don't matter in "info" when rebuilding, they're calculated independently +//characteristics, major_version, minor_version, timestamp and name are the only used members of "info" structure +//Returns new export directory information +//exported_functions_list is copied intentionally to be sorted by ordinal values later +//Name ordinals in exported function don't matter, they will be recalculated +const image_directory rebuild_exports(pe_base& pe, const export_info& info, exported_functions_list exports, section& exports_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that exports_section is attached to this PE image + if(!pe.section_attached(exports_section)) + throw pe_exception("Exports section must be attached to PE file", pe_exception::section_is_not_attached); + + //Needed space for strings + uint32_t needed_size_for_strings = static_cast<uint32_t>(info.get_name().length() + 1); + uint32_t number_of_names = 0; //Number of named functions + uint32_t max_ordinal = 0; //Maximum ordinal number + uint32_t ordinal_base = static_cast<uint32_t>(-1); //Minimum ordinal value + + if(exports.empty()) + ordinal_base = info.get_ordinal_base(); + + uint32_t needed_size_for_function_names = 0; //Needed space for function name strings + uint32_t needed_size_for_function_forwards = 0; //Needed space for function forwards names + + //List all exported functions + //Calculate needed size for function list + { + //Also check that there're no duplicate names and ordinals + std::set<std::string> used_function_names; + std::set<uint16_t> used_function_ordinals; + + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + const exported_function& func = (*it); + //Calculate maximum and minimum ordinal numbers + max_ordinal = std::max<uint32_t>(max_ordinal, func.get_ordinal()); + ordinal_base = std::min<uint32_t>(ordinal_base, func.get_ordinal()); + + //Check if ordinal is unique + if(!used_function_ordinals.insert(func.get_ordinal()).second) + throw pe_exception("Duplicate exported function ordinal", pe_exception::duplicate_exported_function_ordinal); + + if(func.has_name()) + { + //If function is named + ++number_of_names; + needed_size_for_function_names += static_cast<uint32_t>(func.get_name().length() + 1); + + //Check if it's name and name ordinal are unique + if(!used_function_names.insert(func.get_name()).second) + throw pe_exception("Duplicate exported function name", pe_exception::duplicate_exported_function_name); + } + + //If function is forwarded to another DLL + if(func.is_forwarded()) + needed_size_for_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1); + } + } + + //Sort functions by ordinal value + std::sort(exports.begin(), exports.end(), ordinal_sorter()); + + //Calculate needed space for different things... + needed_size_for_strings += needed_size_for_function_names; + needed_size_for_strings += needed_size_for_function_forwards; + uint32_t needed_size_for_function_name_ordinals = number_of_names * sizeof(uint16_t); + uint32_t needed_size_for_function_name_rvas = number_of_names * sizeof(uint32_t); + uint32_t needed_size_for_function_addresses = (max_ordinal - ordinal_base + 1) * sizeof(uint32_t); + + //Export directory header will be placed first + uint32_t directory_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + + uint32_t needed_size = sizeof(image_export_directory); //Calculate needed size for export tables and strings + //sizeof(IMAGE_EXPORT_DIRECTORY) = export directory header + + //Total needed space... + needed_size += needed_size_for_function_name_ordinals; //For list of names ordinals + needed_size += needed_size_for_function_addresses; //For function RVAs + needed_size += needed_size_for_strings; //For all strings + needed_size += needed_size_for_function_name_rvas; //For function name strings RVAs + + //Check if exports_section is last one. If it's not, check if there's enough place for exports data + if(&exports_section != &*(pe.get_image_sections().end() - 1) && + (exports_section.empty() || pe_utils::align_up(exports_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + directory_pos)) + throw pe_exception("Insufficient space for export directory", pe_exception::insufficient_space); + + std::string& raw_data = exports_section.get_raw_data(); + + //This will be done only if exports_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + directory_pos) + raw_data.resize(needed_size + directory_pos); //Expand section raw data + + //Library name will be placed after it + uint32_t current_pos_of_function_names = static_cast<uint32_t>(info.get_name().length() + 1 + directory_pos + sizeof(image_export_directory)); + //Next - function names + uint32_t current_pos_of_function_name_ordinals = current_pos_of_function_names + needed_size_for_function_names; + //Next - function name ordinals + uint32_t current_pos_of_function_forwards = current_pos_of_function_name_ordinals + needed_size_for_function_name_ordinals; + //Finally - function addresses + uint32_t current_pos_of_function_addresses = current_pos_of_function_forwards + needed_size_for_function_forwards; + //Next - function names RVAs + uint32_t current_pos_of_function_names_rvas = current_pos_of_function_addresses + needed_size_for_function_addresses; + + { + //Create export directory and fill it + image_export_directory dir = {0}; + dir.Characteristics = info.get_characteristics(); + dir.MajorVersion = info.get_major_version(); + dir.MinorVersion = info.get_minor_version(); + dir.TimeDateStamp = info.get_timestamp(); + dir.NumberOfFunctions = max_ordinal - ordinal_base + 1; + dir.NumberOfNames = number_of_names; + dir.Base = ordinal_base; + dir.AddressOfFunctions = pe.rva_from_section_offset(exports_section, current_pos_of_function_addresses); + dir.AddressOfNameOrdinals = pe.rva_from_section_offset(exports_section, current_pos_of_function_name_ordinals); + dir.AddressOfNames = pe.rva_from_section_offset(exports_section, current_pos_of_function_names_rvas); + dir.Name = pe.rva_from_section_offset(exports_section, directory_pos + sizeof(image_export_directory)); + + //Save it + memcpy(&raw_data[directory_pos], &dir, sizeof(dir)); + } + + //Sve library name + memcpy(&raw_data[directory_pos + sizeof(image_export_directory)], info.get_name().c_str(), info.get_name().length() + 1); + + //A map to sort function names alphabetically + typedef std::map<std::string, uint16_t> funclist; //function name; function name ordinal + funclist funcs; + + uint32_t last_ordinal = ordinal_base; + //Enumerate all exported functions + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + const exported_function& func = (*it); + + //If we're skipping some ordinals... + if(func.get_ordinal() > last_ordinal) + { + //Fill this function RVAs data with zeros + uint32_t len = sizeof(uint32_t) * (func.get_ordinal() - last_ordinal - 1); + if(len) + { + memset(&raw_data[current_pos_of_function_addresses], 0, len); + current_pos_of_function_addresses += len; + } + + //Save last encountered ordinal + last_ordinal = func.get_ordinal(); + } + + //If function is named, save its name ordinal and name in sorted alphabetically order + if(func.has_name()) + funcs.insert(std::make_pair(func.get_name(), static_cast<uint16_t>(func.get_ordinal() - ordinal_base))); //Calculate name ordinal + + //If function is forwarded to another DLL + if(func.is_forwarded()) + { + //Write its forwarded name and its RVA + uint32_t function_rva = pe.rva_from_section_offset(exports_section, current_pos_of_function_forwards); + memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva)); + current_pos_of_function_addresses += sizeof(function_rva); + + memcpy(&raw_data[current_pos_of_function_forwards], func.get_forwarded_name().c_str(), func.get_forwarded_name().length() + 1); + current_pos_of_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1); + } + else + { + //Write actual function RVA + uint32_t function_rva = func.get_rva(); + memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva)); + current_pos_of_function_addresses += sizeof(function_rva); + } + } + + //Enumerate sorted function names + for(funclist::const_iterator it = funcs.begin(); it != funcs.end(); ++it) + { + //Save function name RVA + uint32_t function_name_rva = pe.rva_from_section_offset(exports_section, current_pos_of_function_names); + memcpy(&raw_data[current_pos_of_function_names_rvas], &function_name_rva, sizeof(function_name_rva)); + current_pos_of_function_names_rvas += sizeof(function_name_rva); + + //Save function name + memcpy(&raw_data[current_pos_of_function_names], (*it).first.c_str(), (*it).first.length() + 1); + current_pos_of_function_names += static_cast<uint32_t>((*it).first.length() + 1); + + //Save function name ordinal + uint16_t name_ordinal = (*it).second; + memcpy(&raw_data[current_pos_of_function_name_ordinals], &name_ordinal, sizeof(name_ordinal)); + current_pos_of_function_name_ordinals += sizeof(name_ordinal); + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(exports_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(exports_section, directory_pos), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_export, ret.get_rva()); + pe.set_directory_size(image_directory_entry_export, ret.get_size()); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_exports.h b/tools/pe_bliss/pe_exports.h new file mode 100644 index 0000000000..127cf86ed6 --- /dev/null +++ b/tools/pe_bliss/pe_exports.h @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include <string> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing exported function +class exported_function +{ +public: + //Default constructor + exported_function(); + + //Returns ordinal of function (actually, ordinal = hint + ordinal base) + uint16_t get_ordinal() const; + + //Returns RVA of function + uint32_t get_rva() const; + + //Returns true if function has name and name ordinal + bool has_name() const; + //Returns name of function + const std::string& get_name() const; + //Returns name ordinal of function + uint16_t get_name_ordinal() const; + + //Returns true if function is forwarded to other library + bool is_forwarded() const; + //Returns the name of forwarded function + const std::string& get_forwarded_name() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You can also use them to rebuild export directory + + //Sets ordinal of function + void set_ordinal(uint16_t ordinal); + + //Sets RVA of function + void set_rva(uint32_t rva); + + //Sets name of function (or clears it, if empty name is passed) + void set_name(const std::string& name); + //Sets name ordinal + void set_name_ordinal(uint16_t name_ordinal); + + //Sets forwarded function name (or clears it, if empty name is passed) + void set_forwarded_name(const std::string& name); + +private: + uint16_t ordinal_; //Function ordinal + uint32_t rva_; //Function RVA + std::string name_; //Function name + bool has_name_; //true == function has name + uint16_t name_ordinal_; //Function name ordinal + bool forward_; //true == function is forwarded + std::string forward_name_; //Name of forwarded function +}; + +//Class representing export information +class export_info +{ +public: + //Default constructor + export_info(); + + //Returns characteristics + uint32_t get_characteristics() const; + //Returns timestamp + uint32_t get_timestamp() const; + //Returns major version + uint16_t get_major_version() const; + //Returns minor version + uint16_t get_minor_version() const; + //Returns DLL name + const std::string& get_name() const; + //Returns ordinal base + uint32_t get_ordinal_base() const; + //Returns number of functions + uint32_t get_number_of_functions() const; + //Returns number of function names + uint32_t get_number_of_names() const; + //Returns RVA of function address table + uint32_t get_rva_of_functions() const; + //Returns RVA of function name address table + uint32_t get_rva_of_names() const; + //Returns RVA of name ordinals table + uint32_t get_rva_of_name_ordinals() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You can also use them to rebuild export directory using rebuild_exports + + //Sets characteristics + void set_characteristics(uint32_t characteristics); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + //Sets major version + void set_major_version(uint16_t major_version); + //Sets minor version + void set_minor_version(uint16_t minor_version); + //Sets DLL name + void set_name(const std::string& name); + //Sets ordinal base + void set_ordinal_base(uint32_t ordinal_base); + //Sets number of functions + void set_number_of_functions(uint32_t number_of_functions); + //Sets number of function names + void set_number_of_names(uint32_t number_of_names); + //Sets RVA of function address table + void set_rva_of_functions(uint32_t rva_of_functions); + //Sets RVA of function name address table + void set_rva_of_names(uint32_t rva_of_names); + //Sets RVA of name ordinals table + void set_rva_of_name_ordinals(uint32_t rva_of_name_ordinals); + +private: + uint32_t characteristics_; + uint32_t timestamp_; + uint16_t major_version_; + uint16_t minor_version_; + std::string name_; + uint32_t ordinal_base_; + uint32_t number_of_functions_; + uint32_t number_of_names_; + uint32_t address_of_functions_; + uint32_t address_of_names_; + uint32_t address_of_name_ordinals_; +}; + +//Exported functions list typedef +typedef std::vector<exported_function> exported_functions_list; + +//Returns array of exported functions +const exported_functions_list get_exported_functions(const pe_base& pe); +//Returns array of exported functions and information about export +const exported_functions_list get_exported_functions(const pe_base& pe, export_info& info); + +//Helper export functions +//Returns pair: <ordinal base for supplied functions; maximum ordinal value for supplied functions> +const std::pair<uint16_t, uint16_t> get_export_ordinal_limits(const exported_functions_list& exports); + +//Checks if exported function name already exists +bool exported_name_exists(const std::string& function_name, const exported_functions_list& exports); + +//Checks if exported function ordinal already exists +bool exported_ordinal_exists(uint16_t ordinal, const exported_functions_list& exports); + +//Export directory rebuilder +//info - export information +//exported_functions_list - list of exported functions +//exports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from exports_section raw data start +//save_to_pe_headers - if true, new export directory information will be saved to PE image headers +//auto_strip_last_section - if true and exports are placed in the last section, it will be automatically stripped +//number_of_functions and number_of_names parameters don't matter in "info" when rebuilding, they're calculated independently +//characteristics, major_version, minor_version, timestamp and name are the only used members of "info" structure +//Returns new export directory information +//exported_functions_list is copied intentionally to be sorted by ordinal values later +//Name ordinals in exported function don't matter, they will be recalculated +const image_directory rebuild_exports(pe_base& pe, const export_info& info, exported_functions_list exports, section& exports_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/pe_factory.cpp b/tools/pe_bliss/pe_factory.cpp new file mode 100644 index 0000000000..f6d8a3e1ed --- /dev/null +++ b/tools/pe_bliss/pe_factory.cpp @@ -0,0 +1,43 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_factory.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +pe_base pe_factory::create_pe(std::istream& file, bool read_debug_raw_data) +{ + return pe_base::get_pe_type(file) == pe_type_32 + ? pe_base(file, pe_properties_32(), read_debug_raw_data) + : pe_base(file, pe_properties_64(), read_debug_raw_data); +} + +pe_base pe_factory::create_pe(const char* file_path, bool read_debug_raw_data) +{ + std::ifstream pe_file(file_path, std::ios::in | std::ios::binary); + if(!pe_file) + { + throw pe_exception("Error in open file.", pe_exception::stream_is_bad); + } + return pe_factory::create_pe(pe_file,read_debug_raw_data); +} +} diff --git a/tools/pe_bliss/pe_factory.h b/tools/pe_bliss/pe_factory.h new file mode 100644 index 0000000000..60b42d9b71 --- /dev/null +++ b/tools/pe_bliss/pe_factory.h @@ -0,0 +1,39 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <memory> +#include <istream> +#include <fstream> +#include "pe_base.h" + +namespace pe_bliss +{ +class pe_factory +{ +public: + //Creates pe_base class instance from PE or PE+ istream + //If read_bound_import_raw_data, raw bound import data will be read (used to get bound import info) + //If read_debug_raw_data, raw debug data will be read (used to get image debug info) + static pe_base create_pe(std::istream& file, bool read_debug_raw_data = true); + static pe_base create_pe(const char* file_path, bool read_debug_raw_data = true); +}; +} diff --git a/tools/pe_bliss/pe_imports.cpp b/tools/pe_bliss/pe_imports.cpp new file mode 100644 index 0000000000..0a6c01d6c0 --- /dev/null +++ b/tools/pe_bliss/pe_imports.cpp @@ -0,0 +1,777 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_imports.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//IMPORTS +//Default constructor +//If set_to_pe_headers = true, IMAGE_DIRECTORY_ENTRY_IMPORT entry will be reset +//to new value after import rebuilding +//If auto_zero_directory_entry_iat = true, IMAGE_DIRECTORY_ENTRY_IAT will be set to zero +//IMAGE_DIRECTORY_ENTRY_IAT is used by loader to temporarily make section, where IMAGE_DIRECTORY_ENTRY_IAT RVA points, writeable +//to be able to modify IAT thunks +import_rebuilder_settings::import_rebuilder_settings(bool set_to_pe_headers, bool auto_zero_directory_entry_iat) + :offset_from_section_start_(0), + build_original_iat_(true), + save_iat_and_original_iat_rvas_(true), + fill_missing_original_iats_(false), + set_to_pe_headers_(set_to_pe_headers), + zero_directory_entry_iat_(auto_zero_directory_entry_iat), + rewrite_iat_and_original_iat_contents_(false), + auto_strip_last_section_(true) +{} + +//Returns offset from section start where import directory data will be placed +uint32_t import_rebuilder_settings::get_offset_from_section_start() const +{ + return offset_from_section_start_; +} + +//Returns true if Original import address table (IAT) will be rebuilt +bool import_rebuilder_settings::build_original_iat() const +{ + return build_original_iat_; +} + +//Returns true if Original import address and import address tables will not be rebuilt, +//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero +bool import_rebuilder_settings::save_iat_and_original_iat_rvas() const +{ + return save_iat_and_original_iat_rvas_; +} + +//Returns true if Original import address and import address tables contents will be rewritten +//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero +//and save_iat_and_original_iat_rvas is true +bool import_rebuilder_settings::rewrite_iat_and_original_iat_contents() const +{ + return rewrite_iat_and_original_iat_contents_; +} + +//Returns true if original missing IATs will be rebuilt +//(only if IATs are saved) +bool import_rebuilder_settings::fill_missing_original_iats() const +{ + return fill_missing_original_iats_; +} + +//Returns true if PE headers should be updated automatically after rebuilding of imports +bool import_rebuilder_settings::auto_set_to_pe_headers() const +{ + return set_to_pe_headers_; +} + +//Returns true if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true +bool import_rebuilder_settings::zero_directory_entry_iat() const +{ + return zero_directory_entry_iat_; +} + +//Returns true if the last section should be stripped automatically, if imports are inside it +bool import_rebuilder_settings::auto_strip_last_section_enabled() const +{ + return auto_strip_last_section_; +} + +//Sets offset from section start where import directory data will be placed +void import_rebuilder_settings::set_offset_from_section_start(uint32_t offset) +{ + offset_from_section_start_ = offset; +} + +//Sets if Original import address table (IAT) will be rebuilt +void import_rebuilder_settings::build_original_iat(bool enable) +{ + build_original_iat_ = enable; +} + +//Sets if Original import address and import address tables will not be rebuilt, +//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero +void import_rebuilder_settings::save_iat_and_original_iat_rvas(bool enable, bool enable_rewrite_iat_and_original_iat_contents) +{ + save_iat_and_original_iat_rvas_ = enable; + if(save_iat_and_original_iat_rvas_) + rewrite_iat_and_original_iat_contents_ = enable_rewrite_iat_and_original_iat_contents; + else + rewrite_iat_and_original_iat_contents_ = false; +} + +//Sets if original missing IATs will be rebuilt +//(only if IATs are saved) +void import_rebuilder_settings::fill_missing_original_iats(bool enable) +{ + fill_missing_original_iats_ = enable; +} + +//Sets if PE headers should be updated automatically after rebuilding of imports +void import_rebuilder_settings::auto_set_to_pe_headers(bool enable) +{ + set_to_pe_headers_ = enable; +} + +//Sets if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true +void import_rebuilder_settings::zero_directory_entry_iat(bool enable) +{ + zero_directory_entry_iat_ = enable; +} + +//Sets if the last section should be stripped automatically, if imports are inside it, default true +void import_rebuilder_settings::enable_auto_strip_last_section(bool enable) +{ + auto_strip_last_section_ = enable; +} + +//Default constructor +imported_function::imported_function() + :hint_(0), ordinal_(0), iat_va_(0) +{} + +//Returns name of function +const std::string& imported_function::get_name() const +{ + return name_; +} + +//Returns true if imported function has name (and hint) +bool imported_function::has_name() const +{ + return !name_.empty(); +} + +//Returns hint +uint16_t imported_function::get_hint() const +{ + return hint_; +} + +//Returns ordinal of function +uint16_t imported_function::get_ordinal() const +{ + return ordinal_; +} + +//Returns IAT entry VA (usable if image has both IAT and original IAT and is bound) +uint64_t imported_function::get_iat_va() const +{ + return iat_va_; +} + +//Sets name of function +void imported_function::set_name(const std::string& name) +{ + name_ = name; +} + +//Sets hint +void imported_function::set_hint(uint16_t hint) +{ + hint_ = hint; +} + +//Sets ordinal +void imported_function::set_ordinal(uint16_t ordinal) +{ + ordinal_ = ordinal; +} + +//Sets IAT entry VA (usable if image has both IAT and original IAT and is bound) +void imported_function::set_iat_va(uint64_t va) +{ + iat_va_ = va; +} + +//Default constructor +import_library::import_library() + :rva_to_iat_(0), rva_to_original_iat_(0), timestamp_(0) +{} + +//Returns name of library +const std::string& import_library::get_name() const +{ + return name_; +} + +//Returns RVA to Import Address Table (IAT) +uint32_t import_library::get_rva_to_iat() const +{ + return rva_to_iat_; +} + +//Returns RVA to Original Import Address Table (Original IAT) +uint32_t import_library::get_rva_to_original_iat() const +{ + return rva_to_original_iat_; +} + +//Returns timestamp +uint32_t import_library::get_timestamp() const +{ + return timestamp_; +} + +//Sets name of library +void import_library::set_name(const std::string& name) +{ + name_ = name; +} + +//Sets RVA to Import Address Table (IAT) +void import_library::set_rva_to_iat(uint32_t rva_to_iat) +{ + rva_to_iat_ = rva_to_iat; +} + +//Sets RVA to Original Import Address Table (Original IAT) +void import_library::set_rva_to_original_iat(uint32_t rva_to_original_iat) +{ + rva_to_original_iat_ = rva_to_original_iat; +} + +//Sets timestamp +void import_library::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Returns imported functions list +const import_library::imported_list& import_library::get_imported_functions() const +{ + return imports_; +} + +//Adds imported function +void import_library::add_import(const imported_function& func) +{ + imports_.push_back(func); +} + +//Clears imported functions list +void import_library::clear_imports() +{ + imports_.clear(); +} + +const imported_functions_list get_imported_functions(const pe_base& pe) +{ + return (pe.get_pe_type() == pe_type_32 ? + get_imported_functions_base<pe_types_class_32>(pe) + : get_imported_functions_base<pe_types_class_64>(pe)); +} + +const image_directory rebuild_imports(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings) +{ + return (pe.get_pe_type() == pe_type_32 ? + rebuild_imports_base<pe_types_class_32>(pe, imports, import_section, import_settings) + : rebuild_imports_base<pe_types_class_64>(pe, imports, import_section, import_settings)); +} + +//Returns imported functions list with related libraries info +template<typename PEClassType> +const imported_functions_list get_imported_functions_base(const pe_base& pe) +{ + imported_functions_list ret; + + //If image has no imports, return empty array + if(!pe.has_imports()) + return ret; + + unsigned long current_descriptor_pos = pe.get_directory_rva(image_directory_entry_import); + //Get first IMAGE_IMPORT_DESCRIPTOR + image_import_descriptor import_descriptor = pe.section_data_from_rva<image_import_descriptor>(current_descriptor_pos, section_data_virtual, true); + + //Iterate them until we reach zero-element + //We don't need to check correctness of this, because exception will be thrown + //inside of loop if we go outsize of section + while(import_descriptor.Name) + { + //Get imported library information + import_library lib; + + unsigned long max_name_length; + //Get byte count that we have for library name + if((max_name_length = pe.section_data_length_from_rva(import_descriptor.Name, import_descriptor.Name, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Get DLL name pointer + const char* dll_name = pe.section_data_from_rva(import_descriptor.Name, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(dll_name, max_name_length)) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Set library name + lib.set_name(dll_name); + //Set library timestamp + lib.set_timestamp(import_descriptor.TimeDateStamp); + //Set library RVA to IAT and original IAT + lib.set_rva_to_iat(import_descriptor.FirstThunk); + lib.set_rva_to_original_iat(import_descriptor.OriginalFirstThunk); + + //Get RVA to IAT (it must be filled by loader when loading PE) + uint32_t current_thunk_rva = import_descriptor.FirstThunk; + typename PEClassType::BaseSize import_address_table = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_thunk_rva, section_data_virtual, true); + + //Get RVA to original IAT (lookup table), which must handle imported functions names + //Some linkers leave this pointer zero-filled + //Such image is valid, but it is not possible to restore imported functions names + //afted image was loaded, because IAT becomes the only one table + //containing both function names and function RVAs after loading + uint32_t current_original_thunk_rva = import_descriptor.OriginalFirstThunk; + typename PEClassType::BaseSize import_lookup_table = current_original_thunk_rva == 0 ? import_address_table : pe.section_data_from_rva<typename PEClassType::BaseSize>(current_original_thunk_rva, section_data_virtual, true); + if(current_original_thunk_rva == 0) + current_original_thunk_rva = current_thunk_rva; + + //List all imported functions for current DLL + if(import_lookup_table != 0 && import_address_table != 0) + { + while(true) + { + //Imported function description + imported_function func; + + //Get VA from IAT + typename PEClassType::BaseSize address = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_thunk_rva, section_data_virtual, true); + //Move pointer + current_thunk_rva += sizeof(typename PEClassType::BaseSize); + + //Jump to next DLL if we finished with this one + if(!address) + break; + + func.set_iat_va(address); + + //Get VA from original IAT + typename PEClassType::BaseSize lookup = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_original_thunk_rva, section_data_virtual, true); + //Move pointer + current_original_thunk_rva += sizeof(typename PEClassType::BaseSize); + + //Check if function is imported by ordinal + if((lookup & PEClassType::ImportSnapFlag) != 0) + { + //Set function ordinal + func.set_ordinal(static_cast<uint16_t>(lookup & 0xffff)); + } + else + { + //Get byte count that we have for function name + if(lookup > static_cast<uint32_t>(-1) - sizeof(uint16_t)) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Get maximum available length of function name + if((max_name_length = pe.section_data_length_from_rva(static_cast<uint32_t>(lookup + sizeof(uint16_t)), static_cast<uint32_t>(lookup + sizeof(uint16_t)), section_data_virtual, true)) < 2) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Get imported function name + const char* func_name = pe.section_data_from_rva(static_cast<uint32_t>(lookup + sizeof(uint16_t)), section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(func_name, max_name_length)) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //HINT in import table is ORDINAL in export table + uint16_t hint = pe.section_data_from_rva<uint16_t>(static_cast<uint32_t>(lookup), section_data_virtual, true); + + //Save hint and name + func.set_name(func_name); + func.set_hint(hint); + } + + //Add function to list + lib.add_import(func); + } + } + + //Check possible overflow + if(!pe_utils::is_sum_safe(current_descriptor_pos, sizeof(image_import_descriptor))) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Go to next library + current_descriptor_pos += sizeof(image_import_descriptor); + import_descriptor = pe.section_data_from_rva<image_import_descriptor>(current_descriptor_pos, section_data_virtual, true); + + //Save import information + ret.push_back(lib); + } + + //Return resulting list + return ret; +} + + +//Simple import directory rebuilder +//You can get all image imports with get_imported_functions() function +//You can use returned value to, for example, add new imported library with some functions +//to the end of list of imported libraries +//To keep PE file working, rebuild its imports with save_iat_and_original_iat_rvas = true (default) +//Don't add new imported functions to existing imported library entries, because this can cause +//rewriting of some used memory (or other IAT/orig.IAT fields) by system loader +//The safest way is just adding import libraries with functions to the end of imported_functions_list array +template<typename PEClassType> +const image_directory rebuild_imports_base(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings) +{ + //Check that import_section is attached to this PE image + if(!pe.section_attached(import_section)) + throw pe_exception("Import section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t needed_size = 0; //Calculate needed size for import structures and strings + uint32_t needed_size_for_strings = 0; //Calculate needed size for import strings (library and function names and hints) + uint32_t size_of_iat = 0; //Size of IAT structures + + needed_size += static_cast<uint32_t>((1 /* ending null descriptor */ + imports.size()) * sizeof(image_import_descriptor)); + + //Enumerate imported functions + for(imported_functions_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + needed_size_for_strings += static_cast<uint32_t>((*it).get_name().length() + 1 /* nullbyte */); + + const import_library::imported_list& funcs = (*it).get_imported_functions(); + + //IMAGE_THUNK_DATA + size_of_iat += static_cast<uint32_t>(sizeof(typename PEClassType::BaseSize) * (1 /*ending null */ + funcs.size())); + + //Enumerate all imported functions in library + for(import_library::imported_list::const_iterator f = funcs.begin(); f != funcs.end(); ++f) + { + if((*f).has_name()) + needed_size_for_strings += static_cast<uint32_t>((*f).get_name().length() + 1 /* nullbyte */ + sizeof(uint16_t) /* hint */); + } + } + + if(import_settings.build_original_iat() || import_settings.fill_missing_original_iats()) + needed_size += size_of_iat * 2; //We'll have two similar-sized IATs if we're building original IAT + else + needed_size += size_of_iat; + + needed_size += sizeof(typename PEClassType::BaseSize); //Maximum align for IAT and original IAT + + //Total needed size for import structures and strings + needed_size += needed_size_for_strings; + + //Check if import_section is last one. If it's not, check if there's enough place for import data + if(&import_section != &*(pe.get_image_sections().end() - 1) && + (import_section.empty() || pe_utils::align_up(import_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + import_settings.get_offset_from_section_start())) + throw pe_exception("Insufficient space for import directory", pe_exception::insufficient_space); + + std::string& raw_data = import_section.get_raw_data(); + + //This will be done only if image_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + import_settings.get_offset_from_section_start()) + raw_data.resize(needed_size + import_settings.get_offset_from_section_start()); //Expand section raw data + + uint32_t current_string_pointer = import_settings.get_offset_from_section_start();/* we will paste structures after strings */ + + //Position for IAT + uint32_t current_pos_for_iat = pe_utils::align_up(static_cast<uint32_t>(needed_size_for_strings + import_settings.get_offset_from_section_start() + (1 + imports.size()) * sizeof(image_import_descriptor)), sizeof(typename PEClassType::BaseSize)); + //Position for original IAT + uint32_t current_pos_for_original_iat = current_pos_for_iat + size_of_iat; + //Position for import descriptors + uint32_t current_pos_for_descriptors = needed_size_for_strings + import_settings.get_offset_from_section_start(); + + //Build imports + for(imported_functions_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + //Create import descriptor + image_import_descriptor descr; + memset(&descr, 0, sizeof(descr)); + descr.TimeDateStamp = (*it).get_timestamp(); //Restore timestamp + descr.Name = pe.rva_from_section_offset(import_section, current_string_pointer); //Library name RVA + + //If we should save IAT for current import descriptor + bool save_iats_for_this_descriptor = import_settings.save_iat_and_original_iat_rvas() && (*it).get_rva_to_iat() != 0; + //If we should write original IAT + bool write_original_iat = (!save_iats_for_this_descriptor && import_settings.build_original_iat()) || import_settings.fill_missing_original_iats(); + + //If we should rewrite saved original IAT for current import descriptor (without changing its position) + bool rewrite_saved_original_iat = save_iats_for_this_descriptor && import_settings.rewrite_iat_and_original_iat_contents() && import_settings.build_original_iat(); + //If we should rewrite saved IAT for current import descriptor (without changing its position) + bool rewrite_saved_iat = save_iats_for_this_descriptor && import_settings.rewrite_iat_and_original_iat_contents() && (*it).get_rva_to_iat() != 0; + + //Helper values if we're rewriting existing IAT or orig.IAT + uint32_t original_first_thunk = 0; + uint32_t first_thunk = 0; + + if(save_iats_for_this_descriptor) + { + //If there's no original IAT and we're asked to rebuild missing original IATs + if(!(*it).get_rva_to_original_iat() && import_settings.fill_missing_original_iats()) + descr.OriginalFirstThunk = import_settings.build_original_iat() ? pe.rva_from_section_offset(import_section, current_pos_for_original_iat) : 0; + else + descr.OriginalFirstThunk = import_settings.build_original_iat() ? (*it).get_rva_to_original_iat() : 0; + + descr.FirstThunk = (*it).get_rva_to_iat(); + + original_first_thunk = descr.OriginalFirstThunk; + first_thunk = descr.FirstThunk; + + if(rewrite_saved_original_iat) + { + if((*it).get_rva_to_original_iat()) + write_original_iat = true; + else + rewrite_saved_original_iat = false; + } + + if(rewrite_saved_iat) + save_iats_for_this_descriptor = false; + } + else + { + //We are creating new IAT and original IAT (if needed) + descr.OriginalFirstThunk = import_settings.build_original_iat() ? pe.rva_from_section_offset(import_section, current_pos_for_original_iat) : 0; + descr.FirstThunk = pe.rva_from_section_offset(import_section, current_pos_for_iat); + } + + //Save import descriptor + memcpy(&raw_data[current_pos_for_descriptors], &descr, sizeof(descr)); + current_pos_for_descriptors += sizeof(descr); + + //Save library name + memcpy(&raw_data[current_string_pointer], (*it).get_name().c_str(), (*it).get_name().length() + 1 /* nullbyte */); + current_string_pointer += static_cast<uint32_t>((*it).get_name().length() + 1 /* nullbyte */); + + //List all imported functions + const import_library::imported_list& funcs = (*it).get_imported_functions(); + for(import_library::imported_list::const_iterator f = funcs.begin(); f != funcs.end(); ++f) + { + if((*f).has_name()) //If function is imported by name + { + //Get RVA of IMAGE_IMPORT_BY_NAME + typename PEClassType::BaseSize rva_of_named_import = pe.rva_from_section_offset(import_section, current_string_pointer); + + if(!save_iats_for_this_descriptor) + { + if(write_original_iat) + { + //We're creating original IATs - so we can write to IAT saved VA (because IMAGE_IMPORT_BY_NAME will be read + //by PE loader from original IAT) + typename PEClassType::BaseSize iat_value = static_cast<typename PEClassType::BaseSize>((*f).get_iat_va()); + + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(iat_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &iat_value, sizeof(iat_value)); + + first_thunk += sizeof(iat_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &iat_value, sizeof(iat_value)); + current_pos_for_iat += sizeof(rva_of_named_import); + } + } + else + { + //Else - write to IAT RVA of IMAGE_IMPORT_BY_NAME + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(rva_of_named_import)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &rva_of_named_import, sizeof(rva_of_named_import)); + + first_thunk += sizeof(rva_of_named_import); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &rva_of_named_import, sizeof(rva_of_named_import)); + current_pos_for_iat += sizeof(rva_of_named_import); + } + } + } + + if(write_original_iat) + { + if(rewrite_saved_original_iat) + { + if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(rva_of_named_import)) + throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(original_first_thunk, true), &rva_of_named_import, sizeof(rva_of_named_import)); + + original_first_thunk += sizeof(rva_of_named_import); + } + else + { + //We're creating original IATs + memcpy(&raw_data[current_pos_for_original_iat], &rva_of_named_import, sizeof(rva_of_named_import)); + current_pos_for_original_iat += sizeof(rva_of_named_import); + } + } + + //Write IMAGE_IMPORT_BY_NAME (WORD hint + string function name) + uint16_t hint = (*f).get_hint(); + memcpy(&raw_data[current_string_pointer], &hint, sizeof(hint)); + memcpy(&raw_data[current_string_pointer + sizeof(uint16_t)], (*f).get_name().c_str(), (*f).get_name().length() + 1 /* nullbyte */); + current_string_pointer += static_cast<uint32_t>((*f).get_name().length() + 1 /* nullbyte */ + sizeof(uint16_t) /* hint */); + } + else //Function is imported by ordinal + { + uint16_t ordinal = (*f).get_ordinal(); + typename PEClassType::BaseSize thunk_value = ordinal; + thunk_value |= PEClassType::ImportSnapFlag; //Imported by ordinal + + if(!save_iats_for_this_descriptor) + { + if(write_original_iat) + { + //We're creating original IATs - so we can wtire to IAT saved VA (because ordinal will be read + //by PE loader from original IAT) + typename PEClassType::BaseSize iat_value = static_cast<typename PEClassType::BaseSize>((*f).get_iat_va()); + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(iat_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &iat_value, sizeof(iat_value)); + + first_thunk += sizeof(iat_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &iat_value, sizeof(iat_value)); + current_pos_for_iat += sizeof(thunk_value); + } + } + else + { + //Else - write ordinal to IAT + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &thunk_value, sizeof(thunk_value)); + + first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &thunk_value, sizeof(thunk_value)); + } + } + } + + //We're writing ordinal to original IAT slot + if(write_original_iat) + { + if(rewrite_saved_original_iat) + { + if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(original_first_thunk, true), &thunk_value, sizeof(thunk_value)); + + original_first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_original_iat], &thunk_value, sizeof(thunk_value)); + current_pos_for_original_iat += sizeof(thunk_value); + } + } + } + } + + if(!save_iats_for_this_descriptor) + { + //Ending null thunks + typename PEClassType::BaseSize thunk_value = 0; + + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &thunk_value, sizeof(thunk_value)); + + first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &thunk_value, sizeof(thunk_value)); + current_pos_for_iat += sizeof(thunk_value); + } + } + + if(write_original_iat) + { + //Ending null thunks + typename PEClassType::BaseSize thunk_value = 0; + + if(rewrite_saved_original_iat) + { + if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(original_first_thunk, true), &thunk_value, sizeof(thunk_value)); + + original_first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_original_iat], &thunk_value, sizeof(thunk_value)); + current_pos_for_original_iat += sizeof(thunk_value); + } + } + } + + { + //Null ending descriptor + image_import_descriptor descr; + memset(&descr, 0, sizeof(descr)); + memcpy(&raw_data[current_pos_for_descriptors], &descr, sizeof(descr)); + } + + //Strip data a little, if we saved some place + //We're allocating more space than needed, if present original IAT and IAT are saved + raw_data.resize(current_pos_for_original_iat); + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(import_section, import_settings.auto_strip_last_section_enabled()); + + //Return information about rebuilt import directory + image_directory ret(pe.rva_from_section_offset(import_section, import_settings.get_offset_from_section_start() + needed_size_for_strings), needed_size - needed_size_for_strings); + + //If auto-rewrite of PE headers is required + if(import_settings.auto_set_to_pe_headers()) + { + pe.set_directory_rva(image_directory_entry_import, ret.get_rva()); + pe.set_directory_size(image_directory_entry_import, ret.get_size()); + + //If we are requested to zero IMAGE_DIRECTORY_ENTRY_IAT also + if(import_settings.zero_directory_entry_iat()) + { + pe.set_directory_rva(image_directory_entry_iat, 0); + pe.set_directory_size(image_directory_entry_iat, 0); + } + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_imports.h b/tools/pe_bliss/pe_imports.h new file mode 100644 index 0000000000..681b5b59bd --- /dev/null +++ b/tools/pe_bliss/pe_imports.h @@ -0,0 +1,208 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include <string> +#include "pe_structures.h" +#include "pe_directory.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing imported function +class imported_function +{ +public: + //Default constructor + imported_function(); + + //Returns true if imported function has name (and hint) + bool has_name() const; + //Returns name of function + const std::string& get_name() const; + //Returns hint + uint16_t get_hint() const; + //Returns ordinal of function + uint16_t get_ordinal() const; + + //Returns IAT entry VA (usable if image has both IAT and original IAT and is bound) + uint64_t get_iat_va() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You also can use them to rebuild image imports + //Sets name of function + void set_name(const std::string& name); + //Sets hint + void set_hint(uint16_t hint); + //Sets ordinal + void set_ordinal(uint16_t ordinal); + + //Sets IAT entry VA (usable if image has both IAT and original IAT and is bound) + void set_iat_va(uint64_t rva); + +private: + std::string name_; //Function name + uint16_t hint_; //Hint + uint16_t ordinal_; //Ordinal + uint64_t iat_va_; +}; + +//Class representing imported library information +class import_library +{ +public: + typedef std::vector<imported_function> imported_list; + +public: + //Default constructor + import_library(); + + //Returns name of library + const std::string& get_name() const; + //Returns RVA to Import Address Table (IAT) + uint32_t get_rva_to_iat() const; + //Returns RVA to Original Import Address Table (Original IAT) + uint32_t get_rva_to_original_iat() const; + //Returns timestamp + uint32_t get_timestamp() const; + + //Returns imported functions list + const imported_list& get_imported_functions() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You also can use them to rebuild image imports + //Sets name of library + void set_name(const std::string& name); + //Sets RVA to Import Address Table (IAT) + void set_rva_to_iat(uint32_t rva_to_iat); + //Sets RVA to Original Import Address Table (Original IAT) + void set_rva_to_original_iat(uint32_t rva_to_original_iat); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + + //Adds imported function + void add_import(const imported_function& func); + //Clears imported functions list + void clear_imports(); + +private: + std::string name_; //Library name + uint32_t rva_to_iat_; //RVA to IAT + uint32_t rva_to_original_iat_; //RVA to original IAT + uint32_t timestamp_; //DLL TimeStamp + + imported_list imports_; +}; + +//Simple import directory rebuilder +//Class representing import rebuilder advanced settings +class import_rebuilder_settings +{ +public: + //Default constructor + //Default constructor + //If set_to_pe_headers = true, IMAGE_DIRECTORY_ENTRY_IMPORT entry will be reset + //to new value after import rebuilding + //If auto_zero_directory_entry_iat = true, IMAGE_DIRECTORY_ENTRY_IAT will be set to zero + //IMAGE_DIRECTORY_ENTRY_IAT is used by loader to temporarily make section, where IMAGE_DIRECTORY_ENTRY_IAT RVA points, writeable + //to be able to modify IAT thunks + explicit import_rebuilder_settings(bool set_to_pe_headers = true, bool auto_zero_directory_entry_iat = false); + + //Returns offset from section start where import directory data will be placed + uint32_t get_offset_from_section_start() const; + //Returns true if Original import address table (IAT) will be rebuilt + bool build_original_iat() const; + + //Returns true if Original import address and import address tables will not be rebuilt, + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + bool save_iat_and_original_iat_rvas() const; + //Returns true if Original import address and import address tables contents will be rewritten + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + //and save_iat_and_original_iat_rvas is true + bool rewrite_iat_and_original_iat_contents() const; + + //Returns true if original missing IATs will be rebuilt + //(only if IATs are saved) + bool fill_missing_original_iats() const; + //Returns true if PE headers should be updated automatically after rebuilding of imports + bool auto_set_to_pe_headers() const; + //Returns true if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true + bool zero_directory_entry_iat() const; + + //Returns true if the last section should be stripped automatically, if imports are inside it + bool auto_strip_last_section_enabled() const; + +public: //Setters + //Sets offset from section start where import directory data will be placed + void set_offset_from_section_start(uint32_t offset); + //Sets if Original import address table (IAT) will be rebuilt + void build_original_iat(bool enable); + //Sets if Original import address and import address tables will not be rebuilt, + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + //enable_rewrite_iat_and_original_iat_contents sets if Original import address and import address tables contents will be rewritten + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + //and save_iat_and_original_iat_rvas is true + void save_iat_and_original_iat_rvas(bool enable, bool enable_rewrite_iat_and_original_iat_contents = false); + //Sets if original missing IATs will be rebuilt + //(only if IATs are saved) + void fill_missing_original_iats(bool enable); + //Sets if PE headers should be updated automatically after rebuilding of imports + void auto_set_to_pe_headers(bool enable); + //Sets if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true + void zero_directory_entry_iat(bool enable); + + //Sets if the last section should be stripped automatically, if imports are inside it, default true + void enable_auto_strip_last_section(bool enable); + +private: + uint32_t offset_from_section_start_; + bool build_original_iat_; + bool save_iat_and_original_iat_rvas_; + bool fill_missing_original_iats_; + bool set_to_pe_headers_; + bool zero_directory_entry_iat_; + bool rewrite_iat_and_original_iat_contents_; + bool auto_strip_last_section_; +}; + +typedef std::vector<import_library> imported_functions_list; + + +//Returns imported functions list with related libraries info +const imported_functions_list get_imported_functions(const pe_base& pe); + +template<typename PEClassType> +const imported_functions_list get_imported_functions_base(const pe_base& pe); + + +//You can get all image imports with get_imported_functions() function +//You can use returned value to, for example, add new imported library with some functions +//to the end of list of imported libraries +//To keep PE file working, rebuild its imports with save_iat_and_original_iat_rvas = true (default) +//Don't add new imported functions to existing imported library entries, because this can cause +//rewriting of some used memory (or other IAT/orig.IAT fields) by system loader +//The safest way is just adding import libraries with functions to the end of imported_functions_list array +const image_directory rebuild_imports(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings = import_rebuilder_settings()); + +template<typename PEClassType> +const image_directory rebuild_imports_base(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings = import_rebuilder_settings()); +} diff --git a/tools/pe_bliss/pe_load_config.cpp b/tools/pe_bliss/pe_load_config.cpp new file mode 100644 index 0000000000..c05895fecd --- /dev/null +++ b/tools/pe_bliss/pe_load_config.cpp @@ -0,0 +1,557 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include <string.h> +#include "pe_load_config.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//IMAGE CONFIG +//Default constructor +image_config_info::image_config_info() + :time_stamp_(0), + major_version_(0), minor_version_(0), + global_flags_clear_(0), global_flags_set_(0), + critical_section_default_timeout_(0), + decommit_free_block_threshold_(0), decommit_total_free_threshold_(0), + lock_prefix_table_va_(0), + max_allocation_size_(0), + virtual_memory_threshold_(0), + process_affinity_mask_(0), + process_heap_flags_(0), + service_pack_version_(0), + edit_list_va_(0), + security_cookie_va_(0), + se_handler_table_va_(0), + se_handler_count_(0) +{} + +//Constructors from PE structures +template<typename ConfigStructure> +image_config_info::image_config_info(const ConfigStructure& info) + :time_stamp_(info.TimeDateStamp), + major_version_(info.MajorVersion), minor_version_(info.MinorVersion), + global_flags_clear_(info.GlobalFlagsClear), global_flags_set_(info.GlobalFlagsSet), + critical_section_default_timeout_(info.CriticalSectionDefaultTimeout), + decommit_free_block_threshold_(info.DeCommitFreeBlockThreshold), decommit_total_free_threshold_(info.DeCommitTotalFreeThreshold), + lock_prefix_table_va_(info.LockPrefixTable), + max_allocation_size_(info.MaximumAllocationSize), + virtual_memory_threshold_(info.VirtualMemoryThreshold), + process_affinity_mask_(info.ProcessAffinityMask), + process_heap_flags_(info.ProcessHeapFlags), + service_pack_version_(info.CSDVersion), + edit_list_va_(info.EditList), + security_cookie_va_(info.SecurityCookie), + se_handler_table_va_(info.SEHandlerTable), + se_handler_count_(info.SEHandlerCount) +{} + +//Instantiate template constructor with needed structures +template image_config_info::image_config_info(const image_load_config_directory32& info); +template image_config_info::image_config_info(const image_load_config_directory64& info); + +//Returns the date and time stamp value +uint32_t image_config_info::get_time_stamp() const +{ + return time_stamp_; +} + +//Returns major version number +uint16_t image_config_info::get_major_version() const +{ + return major_version_; +} + +//Returns minor version number +uint16_t image_config_info::get_minor_version() const +{ + return minor_version_; +} + +//Returns clear global flags +uint32_t image_config_info::get_global_flags_clear() const +{ + return global_flags_clear_; +} + +//Returns set global flags +uint32_t image_config_info::get_global_flags_set() const +{ + return global_flags_set_; +} + +//Returns critical section default timeout +uint32_t image_config_info::get_critical_section_default_timeout() const +{ + return critical_section_default_timeout_; +} + +//Get the size of the minimum block that +//must be freed before it is freed (de-committed), in bytes +uint64_t image_config_info::get_decommit_free_block_threshold() const +{ + return decommit_free_block_threshold_; +} + +//Returns the size of the minimum total memory +//that must be freed in the process heap before it is freed (de-committed), in bytes +uint64_t image_config_info::get_decommit_total_free_threshold() const +{ + return decommit_total_free_threshold_; +} + +//Returns VA of a list of addresses where the LOCK prefix is used +uint64_t image_config_info::get_lock_prefix_table_va() const +{ + return lock_prefix_table_va_; +} + +//Returns the maximum allocation size, in bytes +uint64_t image_config_info::get_max_allocation_size() const +{ + return max_allocation_size_; +} + +//Returns the maximum block size that can be allocated from heap segments, in bytes +uint64_t image_config_info::get_virtual_memory_threshold() const +{ + return virtual_memory_threshold_; +} + +//Returns process affinity mask +uint64_t image_config_info::get_process_affinity_mask() const +{ + return process_affinity_mask_; +} + +//Returns process heap flags +uint32_t image_config_info::get_process_heap_flags() const +{ + return process_heap_flags_; +} + +//Returns service pack version (CSDVersion) +uint16_t image_config_info::get_service_pack_version() const +{ + return service_pack_version_; +} + +//Returns VA of edit list (reserved by system) +uint64_t image_config_info::get_edit_list_va() const +{ + return edit_list_va_; +} + +//Returns a pointer to a cookie that is used by Visual C++ or GS implementation +uint64_t image_config_info::get_security_cookie_va() const +{ + return security_cookie_va_; +} + +//Returns VA of the sorted table of RVAs of each valid, unique handler in the image +uint64_t image_config_info::get_se_handler_table_va() const +{ + return se_handler_table_va_; +} + +//Returns the count of unique handlers in the table +uint64_t image_config_info::get_se_handler_count() const +{ + return se_handler_count_; +} + +//Returns SE Handler RVA list +const image_config_info::se_handler_list& image_config_info::get_se_handler_rvas() const +{ + return se_handlers_; +} + +//Returns Lock Prefix RVA list +const image_config_info::lock_prefix_rva_list& image_config_info::get_lock_prefix_rvas() const +{ + return lock_prefixes_; +} + +//Adds SE Handler RVA to list +void image_config_info::add_se_handler_rva(uint32_t rva) +{ + se_handlers_.push_back(rva); +} + +//Clears SE Handler list +void image_config_info::clear_se_handler_list() +{ + se_handlers_.clear(); +} + +//Adds Lock Prefix RVA to list +void image_config_info::add_lock_prefix_rva(uint32_t rva) +{ + lock_prefixes_.push_back(rva); +} + +//Clears Lock Prefix list +void image_config_info::clear_lock_prefix_list() +{ + lock_prefixes_.clear(); +} + +//Sets the date and time stamp value +void image_config_info::set_time_stamp(uint32_t time_stamp) +{ + time_stamp_ = time_stamp; +} + +//Sets major version number +void image_config_info::set_major_version(uint16_t major_version) +{ + major_version_ = major_version; +} + +//Sets minor version number +void image_config_info::set_minor_version(uint16_t minor_version) +{ + minor_version_ = minor_version; +} + +//Sets clear global flags +void image_config_info::set_global_flags_clear(uint32_t global_flags_clear) +{ + global_flags_clear_ = global_flags_clear; +} + +//Sets set global flags +void image_config_info::set_global_flags_set(uint32_t global_flags_set) +{ + global_flags_set_ = global_flags_set; +} + +//Sets critical section default timeout +void image_config_info::set_critical_section_default_timeout(uint32_t critical_section_default_timeout) +{ + critical_section_default_timeout_ = critical_section_default_timeout; +} + +//Sets the size of the minimum block that +//must be freed before it is freed (de-committed), in bytes +void image_config_info::set_decommit_free_block_threshold(uint64_t decommit_free_block_threshold) +{ + decommit_free_block_threshold_ = decommit_free_block_threshold; +} + +//Sets the size of the minimum total memory +//that must be freed in the process heap before it is freed (de-committed), in bytes +void image_config_info::set_decommit_total_free_threshold(uint64_t decommit_total_free_threshold) +{ + decommit_total_free_threshold_ = decommit_total_free_threshold; +} + +//Sets VA of a list of addresses where the LOCK prefix is used +//If you rebuild this list, VA will be re-assigned automatically +void image_config_info::set_lock_prefix_table_va(uint64_t lock_prefix_table_va) +{ + lock_prefix_table_va_ = lock_prefix_table_va; +} + +//Sets the maximum allocation size, in bytes +void image_config_info::set_max_allocation_size(uint64_t max_allocation_size) +{ + max_allocation_size_ = max_allocation_size; +} + +//Sets the maximum block size that can be allocated from heap segments, in bytes +void image_config_info::set_virtual_memory_threshold(uint64_t virtual_memory_threshold) +{ + virtual_memory_threshold_ = virtual_memory_threshold; +} + +//Sets process affinity mask +void image_config_info::set_process_affinity_mask(uint64_t process_affinity_mask) +{ + process_affinity_mask_ = process_affinity_mask; +} + +//Sets process heap flags +void image_config_info::set_process_heap_flags(uint32_t process_heap_flags) +{ + process_heap_flags_ = process_heap_flags; +} + +//Sets service pack version (CSDVersion) +void image_config_info::set_service_pack_version(uint16_t service_pack_version) +{ + service_pack_version_ = service_pack_version; +} + +//Sets VA of edit list (reserved by system) +void image_config_info::set_edit_list_va(uint64_t edit_list_va) +{ + edit_list_va_ = edit_list_va; +} + +//Sets a pointer to a cookie that is used by Visual C++ or GS implementation +void image_config_info::set_security_cookie_va(uint64_t security_cookie_va) +{ + security_cookie_va_ = security_cookie_va; +} + +//Sets VA of the sorted table of RVAs of each valid, unique handler in the image +//If you rebuild this list, VA will be re-assigned automatically +void image_config_info::set_se_handler_table_va(uint64_t se_handler_table_va) +{ + se_handler_table_va_ = se_handler_table_va; +} + +//Returns SE Handler RVA list +image_config_info::se_handler_list& image_config_info::get_se_handler_rvas() +{ + return se_handlers_; +} + +//Returns Lock Prefix RVA list +image_config_info::lock_prefix_rva_list& image_config_info::get_lock_prefix_rvas() +{ + return lock_prefixes_; +} + +//Returns image config info +//If image does not have config info, throws an exception +const image_config_info get_image_config(const pe_base& pe) +{ + return pe.get_pe_type() == pe_type_32 + ? get_image_config_base<pe_types_class_32>(pe) + : get_image_config_base<pe_types_class_64>(pe); +} + +//Image config rebuilder +const image_directory rebuild_image_config(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start, bool write_se_handlers, bool write_lock_prefixes, bool save_to_pe_header, bool auto_strip_last_section) +{ + return pe.get_pe_type() == pe_type_32 + ? rebuild_image_config_base<pe_types_class_32>(pe, info, image_config_section, offset_from_section_start, write_se_handlers, write_lock_prefixes, save_to_pe_header, auto_strip_last_section) + : rebuild_image_config_base<pe_types_class_64>(pe, info, image_config_section, offset_from_section_start, write_se_handlers, write_lock_prefixes, save_to_pe_header, auto_strip_last_section); +} + + +//Returns image config info +//If image does not have config info, throws an exception +template<typename PEClassType> +const image_config_info get_image_config_base(const pe_base& pe) +{ + //Check if image has config directory + if(!pe.has_config()) + throw pe_exception("Image does not have load config directory", pe_exception::directory_does_not_exist); + + //Get load config structure + typename PEClassType::ConfigStruct config_info = pe.section_data_from_rva<typename PEClassType::ConfigStruct>(pe.get_directory_rva(image_directory_entry_load_config), section_data_virtual); + + //Check size of config directory + if(config_info.Size != sizeof(config_info)) + throw pe_exception("Incorrect (or old) load config directory", pe_exception::incorrect_config_directory); + + //Fill return structure + image_config_info ret(config_info); + + //Check possible overflow + if(config_info.SEHandlerCount >= pe_utils::max_dword / sizeof(uint32_t) + || config_info.SEHandlerTable >= static_cast<typename PEClassType::BaseSize>(-1) - config_info.SEHandlerCount * sizeof(uint32_t)) + throw pe_exception("Incorrect load config directory", pe_exception::incorrect_config_directory); + + //Read sorted SE handler RVA list (if any) + for(typename PEClassType::BaseSize i = 0; i != config_info.SEHandlerCount; ++i) + ret.add_se_handler_rva(pe.section_data_from_va<uint32_t>(static_cast<typename PEClassType::BaseSize>(config_info.SEHandlerTable + i * sizeof(uint32_t)))); + + if(config_info.LockPrefixTable) + { + //Read Lock Prefix VA list (if any) + unsigned long current = 0; + while(true) + { + typename PEClassType::BaseSize lock_prefix_va = pe.section_data_from_va<typename PEClassType::BaseSize>(static_cast<typename PEClassType::BaseSize>(config_info.LockPrefixTable + current * sizeof(typename PEClassType::BaseSize))); + if(!lock_prefix_va) + break; + + ret.add_lock_prefix_rva(pe.va_to_rva(lock_prefix_va)); + + ++current; + } + } + + return ret; +} + +//Image config directory rebuilder +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +//If write_se_handlers = true, SE Handlers list will be written just after image config directory structure +//If write_lock_prefixes = true, Lock Prefixes address list will be written just after image config directory structure +template<typename PEClassType> +const image_directory rebuild_image_config_base(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start, bool write_se_handlers, bool write_lock_prefixes, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that image_config_section is attached to this PE image + if(!pe.section_attached(image_config_section)) + throw pe_exception("Image Config section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t alignment = pe_utils::align_up(offset_from_section_start, sizeof(typename PEClassType::BaseSize)) - offset_from_section_start; + + uint32_t needed_size = sizeof(typename PEClassType::ConfigStruct); //Calculate needed size for Image Config table + + uint32_t image_config_data_pos = offset_from_section_start + alignment; + + uint32_t current_pos_of_se_handlers = 0; + uint32_t current_pos_of_lock_prefixes = 0; + + if(write_se_handlers) + { + current_pos_of_se_handlers = needed_size + image_config_data_pos; + needed_size += static_cast<uint32_t>(info.get_se_handler_rvas().size()) * sizeof(uint32_t); //RVAs of SE Handlers + } + + if(write_lock_prefixes) + { + current_pos_of_lock_prefixes = needed_size + image_config_data_pos; + needed_size += static_cast<uint32_t>((info.get_lock_prefix_rvas().size() + 1) * sizeof(typename PEClassType::BaseSize)); //VAs of Lock Prefixes (and ending null element) + } + + //Check if image_config_section is last one. If it's not, check if there's enough place for Image Config data + if(&image_config_section != &*(pe.get_image_sections().end() - 1) && + (image_config_section.empty() || pe_utils::align_up(image_config_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + image_config_data_pos)) + throw pe_exception("Insufficient space for TLS directory", pe_exception::insufficient_space); + + std::string& raw_data = image_config_section.get_raw_data(); + + //This will be done only if image_config_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + image_config_data_pos) + raw_data.resize(needed_size + image_config_data_pos); //Expand section raw data + + //Create and fill Image Config structure + typename PEClassType::ConfigStruct image_config_section_struct = {0}; + image_config_section_struct.Size = sizeof(image_config_section_struct); + image_config_section_struct.TimeDateStamp = info.get_time_stamp(); + image_config_section_struct.MajorVersion = info.get_major_version(); + image_config_section_struct.MinorVersion = info.get_minor_version(); + image_config_section_struct.GlobalFlagsClear = info.get_global_flags_clear(); + image_config_section_struct.GlobalFlagsSet = info.get_global_flags_set(); + image_config_section_struct.CriticalSectionDefaultTimeout = info.get_critical_section_default_timeout(); + image_config_section_struct.DeCommitFreeBlockThreshold = static_cast<typename PEClassType::BaseSize>(info.get_decommit_free_block_threshold()); + image_config_section_struct.DeCommitTotalFreeThreshold = static_cast<typename PEClassType::BaseSize>(info.get_decommit_total_free_threshold()); + image_config_section_struct.MaximumAllocationSize = static_cast<typename PEClassType::BaseSize>(info.get_max_allocation_size()); + image_config_section_struct.VirtualMemoryThreshold = static_cast<typename PEClassType::BaseSize>(info.get_virtual_memory_threshold()); + image_config_section_struct.ProcessHeapFlags = info.get_process_heap_flags(); + image_config_section_struct.ProcessAffinityMask = static_cast<typename PEClassType::BaseSize>(info.get_process_affinity_mask()); + image_config_section_struct.CSDVersion = info.get_service_pack_version(); + image_config_section_struct.EditList = static_cast<typename PEClassType::BaseSize>(info.get_edit_list_va()); + image_config_section_struct.SecurityCookie = static_cast<typename PEClassType::BaseSize>(info.get_security_cookie_va()); + image_config_section_struct.SEHandlerCount = static_cast<typename PEClassType::BaseSize>(info.get_se_handler_rvas().size()); + + + if(write_se_handlers) + { + if(info.get_se_handler_rvas().empty()) + { + write_se_handlers = false; + image_config_section_struct.SEHandlerTable = 0; + } + else + { + typename PEClassType::BaseSize va; + pe.rva_to_va(pe.rva_from_section_offset(image_config_section, current_pos_of_se_handlers), va); + image_config_section_struct.SEHandlerTable = va; + } + } + else + { + image_config_section_struct.SEHandlerTable = static_cast<typename PEClassType::BaseSize>(info.get_se_handler_table_va()); + } + + if(write_lock_prefixes) + { + if(info.get_lock_prefix_rvas().empty()) + { + write_lock_prefixes = false; + image_config_section_struct.LockPrefixTable = 0; + } + else + { + typename PEClassType::BaseSize va; + pe.rva_to_va(pe.rva_from_section_offset(image_config_section, current_pos_of_lock_prefixes), va); + image_config_section_struct.LockPrefixTable = va; + } + } + else + { + image_config_section_struct.LockPrefixTable = static_cast<typename PEClassType::BaseSize>(info.get_lock_prefix_table_va()); + } + + //Write image config section + memcpy(&raw_data[image_config_data_pos], &image_config_section_struct, sizeof(image_config_section_struct)); + + if(write_se_handlers) + { + //Sort SE Handlers list + image_config_info::se_handler_list sorted_list = info.get_se_handler_rvas(); + std::sort(sorted_list.begin(), sorted_list.end()); + + //Write SE Handlers table + for(image_config_info::se_handler_list::const_iterator it = sorted_list.begin(); it != sorted_list.end(); ++it) + { + uint32_t se_handler_rva = *it; + memcpy(&raw_data[current_pos_of_se_handlers], &se_handler_rva, sizeof(se_handler_rva)); + current_pos_of_se_handlers += sizeof(se_handler_rva); + } + } + + if(write_lock_prefixes) + { + //Write Lock Prefixes VA list + for(image_config_info::lock_prefix_rva_list::const_iterator it = info.get_lock_prefix_rvas().begin(); it != info.get_lock_prefix_rvas().end(); ++it) + { + typename PEClassType::BaseSize lock_prefix_va; + pe.rva_to_va(*it, lock_prefix_va); + memcpy(&raw_data[current_pos_of_lock_prefixes], &lock_prefix_va, sizeof(lock_prefix_va)); + current_pos_of_lock_prefixes += sizeof(lock_prefix_va); + } + + { + //Ending null VA + typename PEClassType::BaseSize lock_prefix_va = 0; + memcpy(&raw_data[current_pos_of_lock_prefixes], &lock_prefix_va, sizeof(lock_prefix_va)); + } + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(image_config_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(image_config_section, image_config_data_pos), sizeof(typename PEClassType::ConfigStruct)); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_load_config, ret.get_rva()); + pe.set_directory_size(image_directory_entry_load_config, ret.get_size()); + } + + return ret; +} + +} diff --git a/tools/pe_bliss/pe_load_config.h b/tools/pe_bliss/pe_load_config.h new file mode 100644 index 0000000000..cb24072de7 --- /dev/null +++ b/tools/pe_bliss/pe_load_config.h @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing image configuration information +class image_config_info +{ +public: + typedef std::vector<uint32_t> se_handler_list; + typedef std::vector<uint32_t> lock_prefix_rva_list; + +public: + //Default constructor + image_config_info(); + //Constructors from PE structures (no checks) + template<typename ConfigStructure> + explicit image_config_info(const ConfigStructure& info); + + //Returns the date and time stamp value + uint32_t get_time_stamp() const; + //Returns major version number + uint16_t get_major_version() const; + //Returns minor version number + uint16_t get_minor_version() const; + //Returns clear global flags + uint32_t get_global_flags_clear() const; + //Returns set global flags + uint32_t get_global_flags_set() const; + //Returns critical section default timeout + uint32_t get_critical_section_default_timeout() const; + //Get the size of the minimum block that + //must be freed before it is freed (de-committed), in bytes + uint64_t get_decommit_free_block_threshold() const; + //Returns the size of the minimum total memory + //that must be freed in the process heap before it is freed (de-committed), in bytes + uint64_t get_decommit_total_free_threshold() const; + //Returns VA of a list of addresses where the LOCK prefix is used + uint64_t get_lock_prefix_table_va() const; + //Returns the maximum allocation size, in bytes + uint64_t get_max_allocation_size() const; + //Returns the maximum block size that can be allocated from heap segments, in bytes + uint64_t get_virtual_memory_threshold() const; + //Returns process affinity mask + uint64_t get_process_affinity_mask() const; + //Returns process heap flags + uint32_t get_process_heap_flags() const; + //Returns service pack version (CSDVersion) + uint16_t get_service_pack_version() const; + //Returns VA of edit list (reserved by system) + uint64_t get_edit_list_va() const; + //Returns a pointer to a cookie that is used by Visual C++ or GS implementation + uint64_t get_security_cookie_va() const; + //Returns VA of the sorted table of RVAs of each valid, unique handler in the image + uint64_t get_se_handler_table_va() const; + //Returns the count of unique handlers in the table + uint64_t get_se_handler_count() const; + + //Returns SE Handler RVA list + const se_handler_list& get_se_handler_rvas() const; + + //Returns Lock Prefix RVA list + const lock_prefix_rva_list& get_lock_prefix_rvas() const; + +public: //These functions do not change everything inside image, they are used by PE class + //Also you can use these functions to rebuild image config directory + + //Adds SE Handler RVA to list + void add_se_handler_rva(uint32_t rva); + //Clears SE Handler list + void clear_se_handler_list(); + + //Adds Lock Prefix RVA to list + void add_lock_prefix_rva(uint32_t rva); + //Clears Lock Prefix list + void clear_lock_prefix_list(); + + //Sets the date and time stamp value + void set_time_stamp(uint32_t time_stamp); + //Sets major version number + void set_major_version(uint16_t major_version); + //Sets minor version number + void set_minor_version(uint16_t minor_version); + //Sets clear global flags + void set_global_flags_clear(uint32_t global_flags_clear); + //Sets set global flags + void set_global_flags_set(uint32_t global_flags_set); + //Sets critical section default timeout + void set_critical_section_default_timeout(uint32_t critical_section_default_timeout); + //Sets the size of the minimum block that + //must be freed before it is freed (de-committed), in bytes + void set_decommit_free_block_threshold(uint64_t decommit_free_block_threshold); + //Sets the size of the minimum total memory + //that must be freed in the process heap before it is freed (de-committed), in bytes + void set_decommit_total_free_threshold(uint64_t decommit_total_free_threshold); + //Sets VA of a list of addresses where the LOCK prefix is used + //If you rebuild this list, VA will be re-assigned automatically + void set_lock_prefix_table_va(uint64_t lock_prefix_table_va); + //Sets the maximum allocation size, in bytes + void set_max_allocation_size(uint64_t max_allocation_size); + //Sets the maximum block size that can be allocated from heap segments, in bytes + void set_virtual_memory_threshold(uint64_t virtual_memory_threshold); + //Sets process affinity mask + void set_process_affinity_mask(uint64_t process_affinity_mask); + //Sets process heap flags + void set_process_heap_flags(uint32_t process_heap_flags); + //Sets service pack version (CSDVersion) + void set_service_pack_version(uint16_t service_pack_version); + //Sets VA of edit list (reserved by system) + void set_edit_list_va(uint64_t edit_list_va); + //Sets a pointer to a cookie that is used by Visual C++ or GS implementation + void set_security_cookie_va(uint64_t security_cookie_va); + //Sets VA of the sorted table of RVAs of each valid, unique handler in the image + //If you rebuild this list, VA will be re-assigned automatically + void set_se_handler_table_va(uint64_t se_handler_table_va); + + //Returns SE Handler RVA list + se_handler_list& get_se_handler_rvas(); + + //Returns Lock Prefix RVA list + lock_prefix_rva_list& get_lock_prefix_rvas(); + +private: + uint32_t time_stamp_; + uint16_t major_version_, minor_version_; + uint32_t global_flags_clear_, global_flags_set_; + uint32_t critical_section_default_timeout_; + uint64_t decommit_free_block_threshold_, decommit_total_free_threshold_; + uint64_t lock_prefix_table_va_; + uint64_t max_allocation_size_; + uint64_t virtual_memory_threshold_; + uint64_t process_affinity_mask_; + uint32_t process_heap_flags_; + uint16_t service_pack_version_; + uint64_t edit_list_va_; + uint64_t security_cookie_va_; + uint64_t se_handler_table_va_; + uint64_t se_handler_count_; + + se_handler_list se_handlers_; + lock_prefix_rva_list lock_prefixes_; +}; + +//Returns image config info +//If image does not have config info, throws an exception +const image_config_info get_image_config(const pe_base& pe); + +template<typename PEClassType> +const image_config_info get_image_config_base(const pe_base& pe); + + +//Image config directory rebuilder +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +//If write_se_handlers = true, SE Handlers list will be written just after image config directory structure +//If write_lock_prefixes = true, Lock Prefixes address list will be written just after image config directory structure +const image_directory rebuild_image_config(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start = 0, bool write_se_handlers = true, bool write_lock_prefixes = true, bool save_to_pe_header = true, bool auto_strip_last_section = true); + +template<typename PEClassType> +const image_directory rebuild_image_config_base(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start = 0, bool write_se_handlers = true, bool write_lock_prefixes = true, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/pe_properties.cpp b/tools/pe_bliss/pe_properties.cpp new file mode 100644 index 0000000000..8d1c2eac43 --- /dev/null +++ b/tools/pe_bliss/pe_properties.cpp @@ -0,0 +1,41 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_properties.h" + +namespace pe_bliss +{ +//Destructor +pe_properties::~pe_properties() +{} + +//Clears PE characteristics flag +void pe_properties::clear_characteristics_flags(uint16_t flags) +{ + set_characteristics(get_characteristics() & ~flags); +} + +//Sets PE characteristics flag +void pe_properties::set_characteristics_flags(uint16_t flags) +{ + set_characteristics(get_characteristics() | flags); +} +} diff --git a/tools/pe_bliss/pe_properties.h b/tools/pe_bliss/pe_properties.h new file mode 100644 index 0000000000..1db163e8b1 --- /dev/null +++ b/tools/pe_bliss/pe_properties.h @@ -0,0 +1,236 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <memory> +#include "pe_structures.h" + +namespace pe_bliss +{ +class pe_properties +{ +public: //Constructors + virtual std::auto_ptr<pe_properties> duplicate() const = 0; + + //Fills properly PE structures + virtual void create_pe(uint32_t section_alignment, uint16_t subsystem) = 0; + +public: + //Destructor + virtual ~pe_properties(); + + +public: //DIRECTORIES + //Returns true if directory exists + virtual bool directory_exists(uint32_t id) const = 0; + + //Removes directory + virtual void remove_directory(uint32_t id) = 0; + + //Returns directory RVA + virtual uint32_t get_directory_rva(uint32_t id) const = 0; + //Returns directory size + virtual uint32_t get_directory_size(uint32_t id) const = 0; + + //Sets directory RVA (just a value of PE header, no moving occurs) + virtual void set_directory_rva(uint32_t id, uint32_t rva) = 0; + //Sets directory size (just a value of PE header, no moving occurs) + virtual void set_directory_size(uint32_t id, uint32_t size) = 0; + + //Strips only zero DATA_DIRECTORY entries to count = min_count + //Returns resulting number of data directories + //strip_iat_directory - if true, even not empty IAT directory will be stripped + virtual uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true) = 0; + + +public: //IMAGE + //Returns PE type of this image + virtual pe_type get_pe_type() const = 0; + + +public: //PE HEADER + //Returns image base for PE32 and PE64 respectively + virtual uint32_t get_image_base_32() const = 0; + virtual uint64_t get_image_base_64() const = 0; + + //Sets new image base for PE32 + virtual void set_image_base(uint32_t base) = 0; + //Sets new image base for PE32/PE+ + virtual void set_image_base_64(uint64_t base) = 0; + + //Returns image entry point + virtual uint32_t get_ep() const = 0; + //Sets image entry point + virtual void set_ep(uint32_t new_ep) = 0; + + //Returns file alignment + virtual uint32_t get_file_alignment() const = 0; + //Returns section alignment + virtual uint32_t get_section_alignment() const = 0; + + //Sets heap size commit for PE32 and PE64 respectively + virtual void set_heap_size_commit(uint32_t size) = 0; + virtual void set_heap_size_commit(uint64_t size) = 0; + //Sets heap size reserve for PE32 and PE64 respectively + virtual void set_heap_size_reserve(uint32_t size) = 0; + virtual void set_heap_size_reserve(uint64_t size) = 0; + //Sets stack size commit for PE32 and PE64 respectively + virtual void set_stack_size_commit(uint32_t size) = 0; + virtual void set_stack_size_commit(uint64_t size) = 0; + //Sets stack size reserve for PE32 and PE64 respectively + virtual void set_stack_size_reserve(uint32_t size) = 0; + virtual void set_stack_size_reserve(uint64_t size) = 0; + + //Returns heap size commit for PE32 and PE64 respectively + virtual uint32_t get_heap_size_commit_32() const = 0; + virtual uint64_t get_heap_size_commit_64() const = 0; + //Returns heap size reserve for PE32 and PE64 respectively + virtual uint32_t get_heap_size_reserve_32() const = 0; + virtual uint64_t get_heap_size_reserve_64() const = 0; + //Returns stack size commit for PE32 and PE64 respectively + virtual uint32_t get_stack_size_commit_32() const = 0; + virtual uint64_t get_stack_size_commit_64() const = 0; + //Returns stack size reserve for PE32 and PE64 respectively + virtual uint32_t get_stack_size_reserve_32() const = 0; + virtual uint64_t get_stack_size_reserve_64() const = 0; + + //Returns virtual size of image + virtual uint32_t get_size_of_image() const = 0; + + //Returns number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual uint32_t get_number_of_rvas_and_sizes() const = 0; + //Sets number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual void set_number_of_rvas_and_sizes(uint32_t number) = 0; + + //Returns PE characteristics + virtual uint16_t get_characteristics() const = 0; + //Sets PE characteristics + virtual void set_characteristics(uint16_t ch) = 0; + + //Clears PE characteristics flag + void clear_characteristics_flags(uint16_t flags); + //Sets PE characteristics flag + void set_characteristics_flags(uint16_t flags); + + //Returns size of headers + virtual uint32_t get_size_of_headers() const = 0; + + //Returns subsystem + virtual uint16_t get_subsystem() const = 0; + + //Sets subsystem + virtual void set_subsystem(uint16_t subsystem) = 0; + + //Returns size of optional header + virtual uint16_t get_size_of_optional_header() const = 0; + + //Returns PE signature + virtual uint32_t get_pe_signature() const = 0; + + //Returns PE magic value + virtual uint32_t get_magic() const = 0; + + //Returns checksum of PE file from header + virtual uint32_t get_checksum() const = 0; + + //Sets checksum of PE file + virtual void set_checksum(uint32_t checksum) = 0; + + //Returns timestamp of PE file from header + virtual uint32_t get_time_date_stamp() const = 0; + + //Sets timestamp of PE file + virtual void set_time_date_stamp(uint32_t timestamp) = 0; + + //Returns Machine field value of PE file from header + virtual uint16_t get_machine() const = 0; + + //Sets Machine field value of PE file + virtual void set_machine(uint16_t machine) = 0; + + //Returns DLL Characteristics + virtual uint16_t get_dll_characteristics() const = 0; + + //Sets DLL Characteristics + virtual void set_dll_characteristics(uint16_t characteristics) = 0; + + //Sets required operation system version + virtual void set_os_version(uint16_t major, uint16_t minor) = 0; + + //Returns required operation system version (minor word) + virtual uint16_t get_minor_os_version() const = 0; + + //Returns required operation system version (major word) + virtual uint16_t get_major_os_version() const = 0; + + //Sets required subsystem version + virtual void set_subsystem_version(uint16_t major, uint16_t minor) = 0; + + //Returns required subsystem version (minor word) + virtual uint16_t get_minor_subsystem_version() const = 0; + + //Returns required subsystem version (major word) + virtual uint16_t get_major_subsystem_version() const = 0; + +public: //ADDRESS CONVERTIONS + //Virtual Address (VA) to Relative Virtual Address (RVA) convertions + //for PE32 and PE64 respectively + //bound_check checks integer overflow + virtual uint32_t va_to_rva(uint32_t va, bool bound_check = true) const = 0; + virtual uint32_t va_to_rva(uint64_t va, bool bound_check = true) const = 0; + + //Relative Virtual Address (RVA) to Virtual Address (VA) convertions + //for PE32 and PE64 respectively + virtual uint32_t rva_to_va_32(uint32_t rva) const = 0; + virtual uint64_t rva_to_va_64(uint32_t rva) const = 0; + + +public: //SECTIONS + //Returns number of sections + virtual uint16_t get_number_of_sections() const = 0; + +public: + //Sets number of sections + virtual void set_number_of_sections(uint16_t number) = 0; + //Sets virtual size of image + virtual void set_size_of_image(uint32_t size) = 0; + //Sets size of headers + virtual void set_size_of_headers(uint32_t size) = 0; + //Sets size of optional headers + virtual void set_size_of_optional_header(uint16_t size) = 0; + //Returns nt headers data pointer + virtual char* get_nt_headers_ptr() = 0; + //Returns nt headers data pointer + virtual const char* get_nt_headers_ptr() const = 0; + //Returns size of NT header + virtual uint32_t get_sizeof_nt_header() const = 0; + //Returns size of optional headers + virtual uint32_t get_sizeof_opt_headers() const = 0; + //Sets file alignment (no checks) + virtual void set_file_alignment_unchecked(uint32_t alignment) = 0; + //Sets base of code + virtual void set_base_of_code(uint32_t base) = 0; + //Returns base of code + virtual uint32_t get_base_of_code() const = 0; + //Returns needed PE magic for PE or PE+ (from template parameters) + virtual uint32_t get_needed_magic() const = 0; +}; +} diff --git a/tools/pe_bliss/pe_properties_generic.cpp b/tools/pe_bliss/pe_properties_generic.cpp new file mode 100644 index 0000000000..bcf6f2047d --- /dev/null +++ b/tools/pe_bliss/pe_properties_generic.cpp @@ -0,0 +1,645 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_properties_generic.h" +#include "pe_exception.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Constructor +template<typename PEClassType> +std::auto_ptr<pe_properties> pe_properties_generic<PEClassType>::duplicate() const +{ + return std::auto_ptr<pe_properties>(new pe_properties_generic<PEClassType>(*this)); +} + +//Fills properly PE structures +template<typename PEClassType> +void pe_properties_generic<PEClassType>::create_pe(uint32_t section_alignment, uint16_t subsystem) +{ + memset(&nt_headers_, 0, sizeof(nt_headers_)); + nt_headers_.Signature = 0x4550; //"PE" + nt_headers_.FileHeader.Machine = 0x14C; //i386 + nt_headers_.FileHeader.SizeOfOptionalHeader = sizeof(nt_headers_.OptionalHeader); + nt_headers_.OptionalHeader.Magic = PEClassType::Id; + nt_headers_.OptionalHeader.ImageBase = 0x400000; + nt_headers_.OptionalHeader.SectionAlignment = section_alignment; + nt_headers_.OptionalHeader.FileAlignment = 0x200; + nt_headers_.OptionalHeader.SizeOfHeaders = 1024; + nt_headers_.OptionalHeader.Subsystem = subsystem; + nt_headers_.OptionalHeader.SizeOfHeapReserve = 0x100000; + nt_headers_.OptionalHeader.SizeOfHeapCommit = 0x1000; + nt_headers_.OptionalHeader.SizeOfStackReserve = 0x100000; + nt_headers_.OptionalHeader.SizeOfStackCommit = 0x1000; + nt_headers_.OptionalHeader.NumberOfRvaAndSizes = 0x10; +} + +//Duplicate +template<typename PEClassType> +pe_properties_generic<PEClassType>::~pe_properties_generic() +{} + +//Returns true if directory exists +template<typename PEClassType> +bool pe_properties_generic<PEClassType>::directory_exists(uint32_t id) const +{ + return (nt_headers_.OptionalHeader.NumberOfRvaAndSizes - 1) >= id && + nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress; +} + +//Removes directory +template<typename PEClassType> +void pe_properties_generic<PEClassType>::remove_directory(uint32_t id) +{ + if(directory_exists(id)) + { + nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress = 0; + nt_headers_.OptionalHeader.DataDirectory[id].Size = 0; + + if(id == image_directory_entry_basereloc) + { + set_characteristics_flags(image_file_relocs_stripped); + set_dll_characteristics(get_dll_characteristics() & ~image_dllcharacteristics_dynamic_base); + } + else if(id == image_directory_entry_export) + { + clear_characteristics_flags(image_file_dll); + } + } +} + +//Returns directory RVA +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_directory_rva(uint32_t id) const +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + return nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress; +} + +//Returns directory size +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_directory_rva(uint32_t id, uint32_t va) +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress = va; +} + +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_directory_size(uint32_t id, uint32_t size) +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + nt_headers_.OptionalHeader.DataDirectory[id].Size = size; +} + +//Returns directory size +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_directory_size(uint32_t id) const +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + return nt_headers_.OptionalHeader.DataDirectory[id].Size; +} + +//Strips only zero DATA_DIRECTORY entries to count = min_count +//Returns resulting number of data directories +//strip_iat_directory - if true, even not empty IAT directory will be stripped +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::strip_data_directories(uint32_t min_count, bool strip_iat_directory) +{ + int i = nt_headers_.OptionalHeader.NumberOfRvaAndSizes - 1; + + //Enumerate all data directories from the end + for(; i >= 0; i--) + { + //If directory exists, break + if(nt_headers_.OptionalHeader.DataDirectory[i].VirtualAddress && (static_cast<uint32_t>(i) != image_directory_entry_iat || !strip_iat_directory)) + break; + + if(i <= static_cast<int>(min_count) - 2) + break; + } + + if(i == image_numberof_directory_entries - 1) + return image_numberof_directory_entries; + + //Return new number of data directories + return nt_headers_.OptionalHeader.NumberOfRvaAndSizes = i + 1; +} + +//Returns image base for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_image_base_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.ImageBase); +} + +//Returns image base for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_image_base_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.ImageBase); +} + +//Sets new image base +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_image_base(uint32_t base) +{ + nt_headers_.OptionalHeader.ImageBase = base; +} + +//Sets new image base +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_image_base_64(uint64_t base) +{ + nt_headers_.OptionalHeader.ImageBase = static_cast<typename PEClassType::BaseSize>(base); +} + +//Returns image entry point +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_ep() const +{ + return nt_headers_.OptionalHeader.AddressOfEntryPoint; +} + +//Sets image entry point +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_ep(uint32_t new_ep) +{ + nt_headers_.OptionalHeader.AddressOfEntryPoint = new_ep; +} + +//Returns file alignment +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_file_alignment() const +{ + return nt_headers_.OptionalHeader.FileAlignment; +} + +//Returns section alignment +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_section_alignment() const +{ + return nt_headers_.OptionalHeader.SectionAlignment; +} + +//Sets heap size commit for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_commit(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets heap size commit for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_commit(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets heap size reserve for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_reserve(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets heap size reserve for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_reserve(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size commit for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_commit(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size commit for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_commit(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size reserve for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_reserve(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size reserve for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_reserve(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Returns heap size commit for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_heap_size_commit_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfHeapCommit); +} + +//Returns heap size commit for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_heap_size_commit_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfHeapCommit); +} + +//Returns heap size reserve for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_heap_size_reserve_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfHeapReserve); +} + +//Returns heap size reserve for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_heap_size_reserve_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfHeapReserve); +} + +//Returns stack size commit for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_stack_size_commit_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfStackCommit); +} + +//Returns stack size commit for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_stack_size_commit_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfStackCommit); +} + +//Returns stack size reserve for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_stack_size_reserve_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfStackReserve); +} + +//Returns stack size reserve for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_stack_size_reserve_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfStackReserve); +} + +//Returns virtual size of image +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_size_of_image() const +{ + return nt_headers_.OptionalHeader.SizeOfImage; +} + +//Returns number of RVA and sizes (number of DATA_DIRECTORY entries) +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_number_of_rvas_and_sizes() const +{ + return nt_headers_.OptionalHeader.NumberOfRvaAndSizes; +} + +//Sets number of RVA and sizes (number of DATA_DIRECTORY entries) +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_number_of_rvas_and_sizes(uint32_t number) +{ + nt_headers_.OptionalHeader.NumberOfRvaAndSizes = number; +} + +//Returns PE characteristics +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_characteristics() const +{ + return nt_headers_.FileHeader.Characteristics; +} + +//Returns checksum of PE file from header +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_checksum() const +{ + return nt_headers_.OptionalHeader.CheckSum; +} + +//Sets checksum of PE file +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_checksum(uint32_t checksum) +{ + nt_headers_.OptionalHeader.CheckSum = checksum; +} + +//Returns DLL Characteristics +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_dll_characteristics() const +{ + return nt_headers_.OptionalHeader.DllCharacteristics; +} + +//Returns timestamp of PE file from header +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_time_date_stamp() const +{ + return nt_headers_.FileHeader.TimeDateStamp; +} + +//Sets timestamp of PE file +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_time_date_stamp(uint32_t timestamp) +{ + nt_headers_.FileHeader.TimeDateStamp = timestamp; +} + +//Sets DLL Characteristics +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_dll_characteristics(uint16_t characteristics) +{ + nt_headers_.OptionalHeader.DllCharacteristics = characteristics; +} + +//Returns Machine field value of PE file from header +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_machine() const +{ + return nt_headers_.FileHeader.Machine; +} + +//Sets Machine field value of PE file +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_machine(uint16_t machine) +{ + nt_headers_.FileHeader.Machine = machine; +} + +//Sets PE characteristics +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_characteristics(uint16_t ch) +{ + nt_headers_.FileHeader.Characteristics = ch; +} + +//Returns size of headers +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_size_of_headers() const +{ + return nt_headers_.OptionalHeader.SizeOfHeaders; +} + +//Returns subsystem +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_subsystem() const +{ + return nt_headers_.OptionalHeader.Subsystem; +} + +//Sets subsystem +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_subsystem(uint16_t subsystem) +{ + nt_headers_.OptionalHeader.Subsystem = subsystem; +} + +//Returns size of optional header +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_size_of_optional_header() const +{ + return nt_headers_.FileHeader.SizeOfOptionalHeader; +} + +//Returns PE signature +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_pe_signature() const +{ + return nt_headers_.Signature; +} + +//Returns PE magic value +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_magic() const +{ + return nt_headers_.OptionalHeader.Magic; +} + +//Sets required operation system version +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_os_version(uint16_t major, uint16_t minor) +{ + nt_headers_.OptionalHeader.MinorOperatingSystemVersion = minor; + nt_headers_.OptionalHeader.MajorOperatingSystemVersion = major; +} + +//Returns required operation system version (minor word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_minor_os_version() const +{ + return nt_headers_.OptionalHeader.MinorOperatingSystemVersion; +} + +//Returns required operation system version (major word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_major_os_version() const +{ + return nt_headers_.OptionalHeader.MajorOperatingSystemVersion; +} + +//Sets required subsystem version +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_subsystem_version(uint16_t major, uint16_t minor) +{ + nt_headers_.OptionalHeader.MinorSubsystemVersion = minor; + nt_headers_.OptionalHeader.MajorSubsystemVersion = major; +} + +//Returns required subsystem version (minor word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_minor_subsystem_version() const +{ + return nt_headers_.OptionalHeader.MinorSubsystemVersion; +} + +//Returns required subsystem version (major word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_major_subsystem_version() const +{ + return nt_headers_.OptionalHeader.MajorSubsystemVersion; +} + +//Virtual Address (VA) to Relative Virtual Address (RVA) convertions for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::va_to_rva(uint32_t va, bool bound_check) const +{ + if(bound_check && static_cast<uint64_t>(va) - nt_headers_.OptionalHeader.ImageBase > pe_utils::max_dword) + throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion); + + return static_cast<uint32_t>(va - nt_headers_.OptionalHeader.ImageBase); +} + +//Virtual Address (VA) to Relative Virtual Address (RVA) convertions for PE32/PE64 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::va_to_rva(uint64_t va, bool bound_check) const +{ + if(bound_check && va - nt_headers_.OptionalHeader.ImageBase > pe_utils::max_dword) + throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion); + + return static_cast<uint32_t>(va - nt_headers_.OptionalHeader.ImageBase); +} + +//Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::rva_to_va_32(uint32_t rva) const +{ + if(!pe_utils::is_sum_safe(rva, static_cast<uint32_t>(nt_headers_.OptionalHeader.ImageBase))) + throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion); + + return static_cast<uint32_t>(rva + nt_headers_.OptionalHeader.ImageBase); +} + +//Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::rva_to_va_64(uint32_t rva) const +{ + return static_cast<uint64_t>(rva) + nt_headers_.OptionalHeader.ImageBase; +} + +//Returns number of sections +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_number_of_sections() const +{ + return nt_headers_.FileHeader.NumberOfSections; +} + +//Sets number of sections +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_number_of_sections(uint16_t number) +{ + nt_headers_.FileHeader.NumberOfSections = number; +} + +//Sets virtual size of image +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_size_of_image(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfImage = size; +} + +//Sets size of headers +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_size_of_headers(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeaders = size; +} + +//Sets size of optional headers +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_size_of_optional_header(uint16_t size) +{ + nt_headers_.FileHeader.SizeOfOptionalHeader = size; +} + +//Returns nt headers data pointer +template<typename PEClassType> +char* pe_properties_generic<PEClassType>::get_nt_headers_ptr() +{ + return reinterpret_cast<char*>(&nt_headers_); +} + +//Returns nt headers data pointer +template<typename PEClassType> +const char* pe_properties_generic<PEClassType>::get_nt_headers_ptr() const +{ + return reinterpret_cast<const char*>(&nt_headers_); +} + +//Returns size of NT header +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_sizeof_nt_header() const +{ + return sizeof(typename PEClassType::NtHeaders); +} + +//Returns size of optional headers +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_sizeof_opt_headers() const +{ + return sizeof(typename PEClassType::OptHeaders); +} + +//Sets file alignment (no checks) +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_file_alignment_unchecked(uint32_t alignment) +{ + nt_headers_.OptionalHeader.FileAlignment = alignment; +} + +//Sets base of code +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_base_of_code(uint32_t base) +{ + nt_headers_.OptionalHeader.BaseOfCode = base; +} + +//Returns base of code +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_base_of_code() const +{ + return nt_headers_.OptionalHeader.BaseOfCode; +} + +//Returns needed PE magic for PE or PE+ (from template parameters) +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_needed_magic() const +{ + return PEClassType::Id; +} + +//Returns PE type of this image +template<typename PEClassType> +pe_type pe_properties_generic<PEClassType>::get_pe_type() const +{ + return PEClassType::Id == image_nt_optional_hdr32_magic ? pe_type_32 : pe_type_64; +} + +//Two used instantiations for PE32 (PE) and PE64 (PE+) +template class pe_properties_generic<pe_types_class_32>; +template class pe_properties_generic<pe_types_class_64>; +} diff --git a/tools/pe_bliss/pe_properties_generic.h b/tools/pe_bliss/pe_properties_generic.h new file mode 100644 index 0000000000..4ff906803c --- /dev/null +++ b/tools/pe_bliss/pe_properties_generic.h @@ -0,0 +1,277 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_properties.h" + +namespace pe_bliss +{ +//Helper class to reduce code size and ease its editing +template< + typename NtHeadersType, + typename OptHeadersType, + uint16_t IdVal, + typename BaseSizeType, + BaseSizeType ImportSnapFlagVal, + typename TLSStructType, + typename ConfigStructType> +class pe_types +{ +public: + typedef NtHeadersType NtHeaders; //NT HEADERS type + typedef OptHeadersType OptHeaders; //NT OPTIONAL HEADER type + typedef BaseSizeType BaseSize; //Base size of different values: DWORD or ULONGLONG + typedef TLSStructType TLSStruct; //TLS structure type + typedef ConfigStructType ConfigStruct; //Configuration structure type + + static const uint16_t Id = IdVal; //Magic of PE or PE+ + static const BaseSize ImportSnapFlag = ImportSnapFlagVal; //Import snap flag value +}; + +//Portable Executable derived class for PE and PE+ +//Describes PE/PE+ dependent things +template<typename PEClassType> +class pe_properties_generic : public pe_properties +{ +public: //Constructor + virtual std::auto_ptr<pe_properties> duplicate() const; + + //Fills properly PE structures + virtual void create_pe(uint32_t section_alignment, uint16_t subsystem); + +public: + //Destructor + virtual ~pe_properties_generic(); + + +public: //DIRECTORIES + //Returns true if directory exists + virtual bool directory_exists(uint32_t id) const; + + //Removes directory + virtual void remove_directory(uint32_t id); + + //Returns directory RVA + virtual uint32_t get_directory_rva(uint32_t id) const; + //Returns directory size + virtual uint32_t get_directory_size(uint32_t id) const; + + //Sets directory RVA (just a value of PE header, no moving occurs) + virtual void set_directory_rva(uint32_t id, uint32_t rva); + //Sets directory size (just a value of PE header, no moving occurs) + virtual void set_directory_size(uint32_t id, uint32_t size); + + //Strips only zero DATA_DIRECTORY entries to count = min_count + //Returns resulting number of data directories + //strip_iat_directory - if true, even not empty IAT directory will be stripped + virtual uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true); + + +public: //IMAGE + //Returns PE type of this image + virtual pe_type get_pe_type() const; + + +public: //PE HEADER + //Returns image base for PE32 and PE64 respectively + virtual uint32_t get_image_base_32() const; + virtual uint64_t get_image_base_64() const; + + //Sets new image base for PE32 + virtual void set_image_base(uint32_t base); + //Sets new image base for PE32/PE+ + virtual void set_image_base_64(uint64_t base); + + //Returns image entry point + virtual uint32_t get_ep() const; + //Sets image entry point + virtual void set_ep(uint32_t new_ep); + + //Returns file alignment + virtual uint32_t get_file_alignment() const; + //Returns section alignment + virtual uint32_t get_section_alignment() const; + + //Sets heap size commit for PE32 and PE64 respectively + virtual void set_heap_size_commit(uint32_t size); + virtual void set_heap_size_commit(uint64_t size); + //Sets heap size reserve for PE32 and PE64 respectively + virtual void set_heap_size_reserve(uint32_t size); + virtual void set_heap_size_reserve(uint64_t size); + //Sets stack size commit for PE32 and PE64 respectively + virtual void set_stack_size_commit(uint32_t size); + virtual void set_stack_size_commit(uint64_t size); + //Sets stack size reserve for PE32 and PE64 respectively + virtual void set_stack_size_reserve(uint32_t size); + virtual void set_stack_size_reserve(uint64_t size); + + //Returns heap size commit for PE32 and PE64 respectively + virtual uint32_t get_heap_size_commit_32() const; + virtual uint64_t get_heap_size_commit_64() const; + //Returns heap size reserve for PE32 and PE64 respectively + virtual uint32_t get_heap_size_reserve_32() const; + virtual uint64_t get_heap_size_reserve_64() const; + //Returns stack size commit for PE32 and PE64 respectively + virtual uint32_t get_stack_size_commit_32() const; + virtual uint64_t get_stack_size_commit_64() const; + //Returns stack size reserve for PE32 and PE64 respectively + virtual uint32_t get_stack_size_reserve_32() const; + virtual uint64_t get_stack_size_reserve_64() const; + + //Returns virtual size of image + virtual uint32_t get_size_of_image() const; + + //Returns number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual uint32_t get_number_of_rvas_and_sizes() const; + //Sets number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual void set_number_of_rvas_and_sizes(uint32_t number); + + //Returns PE characteristics + virtual uint16_t get_characteristics() const; + //Sets PE characteristics + virtual void set_characteristics(uint16_t ch); + + //Returns size of headers + virtual uint32_t get_size_of_headers() const; + + //Returns subsystem + virtual uint16_t get_subsystem() const; + + //Sets subsystem + virtual void set_subsystem(uint16_t subsystem); + + //Returns size of optional header + virtual uint16_t get_size_of_optional_header() const; + + //Returns PE signature + virtual uint32_t get_pe_signature() const; + + //Returns PE magic value + virtual uint32_t get_magic() const; + + //Returns checksum of PE file from header + virtual uint32_t get_checksum() const; + + //Sets checksum of PE file + virtual void set_checksum(uint32_t checksum); + + //Returns timestamp of PE file from header + virtual uint32_t get_time_date_stamp() const; + + //Sets timestamp of PE file + virtual void set_time_date_stamp(uint32_t timestamp); + + //Returns Machine field value of PE file from header + virtual uint16_t get_machine() const; + + //Sets Machine field value of PE file + virtual void set_machine(uint16_t machine); + + //Returns DLL Characteristics + virtual uint16_t get_dll_characteristics() const; + + //Sets DLL Characteristics + virtual void set_dll_characteristics(uint16_t characteristics); + + //Sets required operation system version + virtual void set_os_version(uint16_t major, uint16_t minor); + + //Returns required operation system version (minor word) + virtual uint16_t get_minor_os_version() const; + + //Returns required operation system version (major word) + virtual uint16_t get_major_os_version() const; + + //Sets required subsystem version + virtual void set_subsystem_version(uint16_t major, uint16_t minor); + + //Returns required subsystem version (minor word) + virtual uint16_t get_minor_subsystem_version() const; + + //Returns required subsystem version (major word) + virtual uint16_t get_major_subsystem_version() const; + +public: //ADDRESS CONVERTIONS + //Virtual Address (VA) to Relative Virtual Address (RVA) convertions + //for PE32 and PE64 respectively + //bound_check checks integer overflow + virtual uint32_t va_to_rva(uint32_t va, bool bound_check = true) const; + virtual uint32_t va_to_rva(uint64_t va, bool bound_check = true) const; + + //Relative Virtual Address (RVA) to Virtual Address (VA) convertions + //for PE32 and PE64 respectively + virtual uint32_t rva_to_va_32(uint32_t rva) const; + virtual uint64_t rva_to_va_64(uint32_t rva) const; + + +public: //SECTIONS + //Returns number of sections + virtual uint16_t get_number_of_sections() const; + +protected: + typename PEClassType::NtHeaders nt_headers_; //NT headers (PE32 or PE64) + +public: + //Sets number of sections + virtual void set_number_of_sections(uint16_t number); + //Sets virtual size of image + virtual void set_size_of_image(uint32_t size); + //Sets size of headers + virtual void set_size_of_headers(uint32_t size); + //Sets size of optional headers + virtual void set_size_of_optional_header(uint16_t size); + //Returns nt headers data pointer + virtual char* get_nt_headers_ptr(); + //Returns nt headers data pointer + virtual const char* get_nt_headers_ptr() const; + //Returns size of NT header + virtual uint32_t get_sizeof_nt_header() const; + //Returns size of optional headers + virtual uint32_t get_sizeof_opt_headers() const; + //Sets file alignment (no checks) + virtual void set_file_alignment_unchecked(uint32_t alignment); + //Sets base of code + virtual void set_base_of_code(uint32_t base); + //Returns base of code + virtual uint32_t get_base_of_code() const; + //Returns needed PE magic for PE or PE+ (from template parameters) + virtual uint32_t get_needed_magic() const; +}; + +//Two used typedefs for PE32 (PE) and PE64 (PE+) +typedef pe_types<pe_win::image_nt_headers32, + pe_win::image_optional_header32, + pe_win::image_nt_optional_hdr32_magic, + uint32_t, + pe_win::image_ordinal_flag32, + pe_win::image_tls_directory32, + pe_win::image_load_config_directory32> pe_types_class_32; + +typedef pe_types<pe_win::image_nt_headers64, + pe_win::image_optional_header64, + pe_win::image_nt_optional_hdr64_magic, + uint64_t, + pe_win::image_ordinal_flag64, + pe_win::image_tls_directory64, + pe_win::image_load_config_directory64> pe_types_class_64; + +typedef pe_properties_generic<pe_types_class_32> pe_properties_32; +typedef pe_properties_generic<pe_types_class_64> pe_properties_64; +} diff --git a/tools/pe_bliss/pe_rebuilder.cpp b/tools/pe_bliss/pe_rebuilder.cpp new file mode 100644 index 0000000000..faf5803b8c --- /dev/null +++ b/tools/pe_bliss/pe_rebuilder.cpp @@ -0,0 +1,214 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_rebuilder.h" +#include "pe_base.h" +#include "pe_structures.h" +#include "pe_exception.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Rebuilds PE image headers +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, image_dos_header& dos_header, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) +{ + dos_header = pe.get_dos_header(); + + if(strip_dos_header) + { + //Strip stub overlay + pe.strip_stub_overlay(); + //BaseOfCode NT Headers field now overlaps + //e_lfanew field, so we're acrually setting + //e_lfanew with this call + pe.set_base_of_code(8 * sizeof(uint16_t)); + } + else + { + //Set start of PE headers + dos_header.e_lfanew = sizeof(image_dos_header) + + pe_utils::align_up(static_cast<uint32_t>(pe.get_stub_overlay().size()), sizeof(uint32_t)); + } + + section_list& sections = pe.get_image_sections(); + + //Calculate pointer to section data + size_t ptr_to_section_data = (strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)) + pe.get_sizeof_nt_header() + + pe_utils::align_up(pe.get_stub_overlay().size(), sizeof(uint32_t)) + - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes()) + + sections.size() * sizeof(image_section_header); + + if(save_bound_import && pe.has_bound_import()) + { + //It will be aligned to DWORD, because we're aligning to DWORD everything above it + pe.set_directory_rva(image_directory_entry_bound_import, static_cast<uint32_t>(ptr_to_section_data)); + ptr_to_section_data += pe.get_directory_size(image_directory_entry_bound_import); + } + + ptr_to_section_data = pe_utils::align_up(ptr_to_section_data, pe.get_file_alignment()); + + //Set size of headers and size of optional header + if(change_size_of_headers) + { + if(!pe.get_image_sections().empty()) + { + if(static_cast<uint32_t>(ptr_to_section_data) > (*sections.begin()).get_virtual_address()) + throw pe_exception("Headers of PE file are too long. Try to strip STUB or don't build bound import", pe_exception::cannot_rebuild_image); + } + + pe.set_size_of_headers(static_cast<uint32_t>(ptr_to_section_data)); + } + + //Set number of sections in PE header + pe.update_number_of_sections(); + + pe.update_image_size(); + + pe.set_size_of_optional_header(static_cast<uint16_t>(pe.get_sizeof_opt_headers() + - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes()))); + + //Recalculate pointer to raw data according to section list + for(section_list::iterator it = sections.begin(); it != sections.end(); ++it) + { + //Save section headers PointerToRawData + (*it).set_pointer_to_raw_data(static_cast<uint32_t>(ptr_to_section_data)); + ptr_to_section_data += (*it).get_aligned_raw_size(pe.get_file_alignment()); + } +} + +//Rebuild PE image and write it to "out" ostream +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, std::ostream& out, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) +{ + if(out.bad()) + throw pe_exception("Stream is bad", pe_exception::stream_is_bad); + + if(save_bound_import && pe.has_bound_import()) + { + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true) + < pe.get_directory_size(image_directory_entry_bound_import)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + } + + //Change ostream state + out.exceptions(std::ios::goodbit); + out.clear(); + + uint32_t original_bound_import_rva = pe.has_bound_import() ? pe.get_directory_rva(image_directory_entry_bound_import) : 0; + if(original_bound_import_rva && original_bound_import_rva > pe.get_size_of_headers()) + { + //No need to do anything with bound import directory + //if it is placed inside of any section, not headers + original_bound_import_rva = 0; + save_bound_import = false; + } + + { + image_dos_header dos_header; + + //Rebuild PE image headers + rebuild_pe(pe, dos_header, strip_dos_header, change_size_of_headers, save_bound_import); + + //Write DOS header + out.write(reinterpret_cast<const char*>(&dos_header), strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)); + } + + //If we have stub overlay, write it too + { + const std::string& stub = pe.get_stub_overlay(); + if(stub.size()) + { + out.write(stub.data(), stub.size()); + size_t aligned_size = pe_utils::align_up(stub.size(), sizeof(uint32_t)); + //Align PE header, which is right after rich overlay + while(aligned_size > stub.size()) + { + out.put('\0'); + --aligned_size; + } + } + } + + //Write NT headers + out.write(static_cast<const pe_base&>(pe).get_nt_headers_ptr(), pe.get_sizeof_nt_header() + - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes())); + + //Write section headers + const section_list& sections = pe.get_image_sections(); + for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) + { + if(it == sections.end() - 1) //If last section encountered + { + image_section_header header((*it).get_raw_header()); + header.SizeOfRawData = static_cast<uint32_t>((*it).get_raw_data().length()); //Set non-aligned actual data length for it + out.write(reinterpret_cast<const char*>(&header), sizeof(image_section_header)); + } + else + { + out.write(reinterpret_cast<const char*>(&(*it).get_raw_header()), sizeof(image_section_header)); + } + } + + //Write bound import data if requested + if(save_bound_import && pe.has_bound_import()) + { + out.write(pe.section_data_from_rva(original_bound_import_rva, section_data_raw, true), + pe.get_directory_size(image_directory_entry_bound_import)); + } + + //Write section data finally + for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) + { + const section& s = *it; + + std::streamoff wpos = out.tellp(); + + //Fill unused overlay data between sections with null bytes + for(unsigned int i = 0; i < s.get_pointer_to_raw_data() - wpos; i++) + out.put(0); + + //Write raw section data + out.write(s.get_raw_data().data(), s.get_raw_data().length()); + } +} + +//Rebuild PE image and write it to "out" file +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, const char* out, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) +{ + std::ofstream pe_file(out, std::ios::out | std::ios::binary | std::ios::trunc); + if(!pe_file) + { + throw pe_exception("Error in open file.", pe_exception::stream_is_bad); + } + rebuild_pe(pe, pe_file, strip_dos_header, change_size_of_headers, save_bound_import); +} + + +} diff --git a/tools/pe_bliss/pe_rebuilder.h b/tools/pe_bliss/pe_rebuilder.h new file mode 100644 index 0000000000..319807e5c9 --- /dev/null +++ b/tools/pe_bliss/pe_rebuilder.h @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <ostream> +#include <fstream> + +namespace pe_bliss +{ +class pe_base; +//Rebuilds PE image, writes resulting image to ostream "out". If strip_dos_header == true, DOS header will be stripped a little +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, std::ostream& out, bool strip_dos_header = false, bool change_size_of_headers = true, bool save_bound_import = true); + +//Rebuild PE image and write it to "out" file +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, const char* out, bool strip_dos_header = false, bool change_size_of_headers = true, bool save_bound_import = true); + +} diff --git a/tools/pe_bliss/pe_relocations.cpp b/tools/pe_bliss/pe_relocations.cpp new file mode 100644 index 0000000000..d5357dd219 --- /dev/null +++ b/tools/pe_bliss/pe_relocations.cpp @@ -0,0 +1,320 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_relocations.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//RELOCATIONS +//Default constructor +relocation_entry::relocation_entry() + :rva_(0), type_(0) +{} + +//Constructor from relocation item (WORD) +relocation_entry::relocation_entry(uint16_t relocation_value) + :rva_(relocation_value & ((1 << 12) - 1)), type_(relocation_value >> 12) +{} + +//Constructor from relative rva and relocation type +relocation_entry::relocation_entry(uint16_t rrva, uint16_t type) + :rva_(rrva), type_(type) +{} + +//Returns RVA of relocation +uint16_t relocation_entry::get_rva() const +{ + return rva_; +} + +//Returns type of relocation +uint16_t relocation_entry::get_type() const +{ + return type_; +} + +//Sets RVA of relocation +void relocation_entry::set_rva(uint16_t rva) +{ + rva_ = rva; +} + +//Sets type of relocation +void relocation_entry::set_type(uint16_t type) +{ + type_ = type; +} + +//Returns relocation item (rrva + type) +uint16_t relocation_entry::get_item() const +{ + return rva_ | (type_ << 12); +} + +//Sets relocation item (rrva + type) +void relocation_entry::set_item(uint16_t item) +{ + rva_ = item & ((1 << 12) - 1); + type_ = item >> 12; +} + +//Returns relocation list +const relocation_table::relocation_list& relocation_table::get_relocations() const +{ + return relocations_; +} + +//Adds relocation to table +void relocation_table::add_relocation(const relocation_entry& entry) +{ + relocations_.push_back(entry); +} + +//Default constructor +relocation_table::relocation_table() + :rva_(0) +{} + +//Constructor from RVA of relocation table +relocation_table::relocation_table(uint32_t rva) + :rva_(rva) +{} + +//Returns RVA of block +uint32_t relocation_table::get_rva() const +{ + return rva_; +} + +//Sets RVA of block +void relocation_table::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Returns changeable relocation list +relocation_table::relocation_list& relocation_table::get_relocations() +{ + return relocations_; +} + +//Get relocation list of pe file, supports one-word sized relocations only +//If list_absolute_entries = true, IMAGE_REL_BASED_ABSOLUTE will be listed +const relocation_table_list get_relocations(const pe_base& pe, bool list_absolute_entries) +{ + relocation_table_list ret; + + //If image does not have relocations + if(!pe.has_reloc()) + return ret; + + //Check the length in bytes of the section containing relocation directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_basereloc), + pe.get_directory_rva(image_directory_entry_basereloc), section_data_virtual, true) + < sizeof(image_base_relocation)) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + unsigned long current_pos = pe.get_directory_rva(image_directory_entry_basereloc); + //First IMAGE_BASE_RELOCATION table + image_base_relocation reloc_table = pe.section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true); + + if(reloc_table.SizeOfBlock % 2) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + unsigned long reloc_size = pe.get_directory_size(image_directory_entry_basereloc); + unsigned long read_size = 0; + + //reloc_table.VirtualAddress is not checked (not so important) + while(reloc_table.SizeOfBlock && read_size < reloc_size) + { + //Create relocation table + relocation_table table; + //Save RVA + table.set_rva(reloc_table.VirtualAddress); + + if(!pe_utils::is_sum_safe(current_pos, reloc_table.SizeOfBlock)) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + //List all relocations + for(unsigned long i = sizeof(image_base_relocation); i < reloc_table.SizeOfBlock; i += sizeof(uint16_t)) + { + relocation_entry entry(pe.section_data_from_rva<uint16_t>(current_pos + i, section_data_virtual, true)); + if(list_absolute_entries || entry.get_type() != image_rel_based_absolute) + table.add_relocation(entry); + } + + //Save table + ret.push_back(table); + + //Go to next relocation block + if(!pe_utils::is_sum_safe(current_pos, reloc_table.SizeOfBlock)) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + current_pos += reloc_table.SizeOfBlock; + read_size += reloc_table.SizeOfBlock; + reloc_table = pe.section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true); + } + + return ret; +} + +//Simple relocations rebuilder +//To keep PE file working, don't remove any of existing relocations in +//relocation_table_list returned by a call to get_relocations() function +//auto_strip_last_section - if true and relocations are placed in the last section, it will be automatically stripped +//offset_from_section_start - offset from the beginning of reloc_section, where relocations data will be situated +//If save_to_pe_header is true, PE header will be modified automatically +const image_directory rebuild_relocations(pe_base& pe, const relocation_table_list& relocs, section& reloc_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that reloc_section is attached to this PE image + if(!pe.section_attached(reloc_section)) + throw pe_exception("Relocations section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t current_reloc_data_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + + uint32_t needed_size = current_reloc_data_pos - offset_from_section_start; //Calculate needed size for relocation tables + uint32_t size_delta = needed_size; + + uint32_t start_reloc_pos = current_reloc_data_pos; + + //Enumerate relocation tables + for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it) + { + needed_size += static_cast<uint32_t>((*it).get_relocations().size() * sizeof(uint16_t) /* relocations */ + sizeof(image_base_relocation) /* table header */); + //End of each table will be DWORD-aligned + if((start_reloc_pos + needed_size - size_delta) % sizeof(uint32_t)) + needed_size += sizeof(uint16_t); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation + } + + //Check if reloc_section is last one. If it's not, check if there's enough place for relocations data + if(&reloc_section != &*(pe.get_image_sections().end() - 1) && + (reloc_section.empty() || pe_utils::align_up(reloc_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + current_reloc_data_pos)) + throw pe_exception("Insufficient space for relocations directory", pe_exception::insufficient_space); + + std::string& raw_data = reloc_section.get_raw_data(); + + //This will be done only if reloc_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + current_reloc_data_pos) + raw_data.resize(needed_size + current_reloc_data_pos); //Expand section raw data + + //Enumerate relocation tables + for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it) + { + //Create relocation table header + image_base_relocation reloc; + reloc.VirtualAddress = (*it).get_rva(); + const relocation_table::relocation_list& reloc_list = (*it).get_relocations(); + reloc.SizeOfBlock = static_cast<uint32_t>(sizeof(image_base_relocation) + sizeof(uint16_t) * reloc_list.size()); + if((reloc_list.size() * sizeof(uint16_t)) % sizeof(uint32_t)) //If we must align end of relocation table + reloc.SizeOfBlock += sizeof(uint16_t); + + memcpy(&raw_data[current_reloc_data_pos], &reloc, sizeof(reloc)); + current_reloc_data_pos += sizeof(reloc); + + //Enumerate relocations in table + for(relocation_table::relocation_list::const_iterator r = reloc_list.begin(); r != reloc_list.end(); ++r) + { + //Save relocations + uint16_t reloc_value = (*r).get_item(); + memcpy(&raw_data[current_reloc_data_pos], &reloc_value, sizeof(reloc_value)); + current_reloc_data_pos += sizeof(reloc_value); + } + + if(current_reloc_data_pos % sizeof(uint32_t)) //If end of table is not DWORD-aligned + { + memset(&raw_data[current_reloc_data_pos], 0, sizeof(uint16_t)); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation + current_reloc_data_pos += sizeof(uint16_t); + } + } + + image_directory ret(pe.rva_from_section_offset(reloc_section, start_reloc_pos), needed_size - size_delta); + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(reloc_section, auto_strip_last_section); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_basereloc, ret.get_rva()); + pe.set_directory_size(image_directory_entry_basereloc, ret.get_size()); + + pe.clear_characteristics_flags(image_file_relocs_stripped); + pe.set_dll_characteristics(pe.get_dll_characteristics() | image_dllcharacteristics_dynamic_base); + } + + return ret; +} + +//Recalculates image base with the help of relocation tables +void rebase_image(pe_base& pe, const relocation_table_list& tables, uint64_t new_base) +{ + pe.get_pe_type() == pe_type_32 + ? rebase_image_base<pe_types_class_32>(pe, tables, new_base) + : rebase_image_base<pe_types_class_64>(pe, tables, new_base); +} + +//RELOCATIONS +//Recalculates image base with the help of relocation tables +//Recalculates VAs of DWORDS/QWORDS in image according to relocations +//Notice: if you move some critical structures like TLS, image relocations will not fix new +//positions of TLS VAs. Instead, some bytes that now doesn't belong to TLS will be fixed. +//It is recommended to rebase image in the very beginning and move all structures afterwards. +template<typename PEClassType> +void rebase_image_base(pe_base& pe, const relocation_table_list& tables, uint64_t new_base) +{ + //Get current image base value + typename PEClassType::BaseSize image_base; + pe.get_image_base(image_base); + + //ImageBase difference + typename PEClassType::BaseSize base_rel = static_cast<typename PEClassType::BaseSize>(static_cast<int64_t>(new_base) - image_base); + + //We need to fix addresses from relocation tables + //Enumerate relocation tables + for(relocation_table_list::const_iterator it = tables.begin(); it != tables.end(); ++it) + { + const relocation_table::relocation_list& relocs = (*it).get_relocations(); + + uint32_t base_rva = (*it).get_rva(); + + //Enumerate relocations + for(relocation_table::relocation_list::const_iterator rel = relocs.begin(); rel != relocs.end(); ++rel) + { + //Skip ABSOLUTE entries + if((*rel).get_type() == pe_win::image_rel_based_absolute) + continue; + + //Recalculate value by RVA and rewrite it + uint32_t current_rva = base_rva + (*rel).get_rva(); + typename PEClassType::BaseSize value = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_rva, section_data_raw, true); + value += base_rel; + memcpy(pe.section_data_from_rva(current_rva, true), &value, sizeof(value)); + } + } + + //Finally, save new image base + pe.set_image_base_64(new_base); +} +} diff --git a/tools/pe_bliss/pe_relocations.h b/tools/pe_bliss/pe_relocations.h new file mode 100644 index 0000000000..1bc8b2a405 --- /dev/null +++ b/tools/pe_bliss/pe_relocations.h @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing relocation entry +//RVA of relocation is not actually RVA, but +//(real RVA) - (RVA of table) +class relocation_entry +{ +public: + //Default constructor + relocation_entry(); + //Constructor from relocation item (WORD) + explicit relocation_entry(uint16_t relocation_value); + //Constructor from relative rva and relocation type + relocation_entry(uint16_t rrva, uint16_t type); + + //Returns RVA of relocation (actually, relative RVA from relocation table RVA) + uint16_t get_rva() const; + //Returns type of relocation + uint16_t get_type() const; + + //Returns relocation item (rrva + type) + uint16_t get_item() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You can also use them to rebuild relocations using rebuild_relocations() + + //Sets RVA of relocation (actually, relative RVA from relocation table RVA) + void set_rva(uint16_t rva); + //Sets type of relocation + void set_type(uint16_t type); + + //Sets relocation item (rrva + type) + void set_item(uint16_t item); + +private: + uint16_t rva_; + uint16_t type_; +}; + +//Class representing relocation table +class relocation_table +{ +public: + typedef std::vector<relocation_entry> relocation_list; + +public: + //Default constructor + relocation_table(); + //Constructor from RVA of relocation table + explicit relocation_table(uint32_t rva); + + //Returns relocation list + const relocation_list& get_relocations() const; + //Returns RVA of block + uint32_t get_rva() const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild relocations using rebuild_relocations() + + //Adds relocation to table + void add_relocation(const relocation_entry& entry); + //Returns changeable relocation list + relocation_list& get_relocations(); + //Sets RVA of block + void set_rva(uint32_t rva); + +private: + uint32_t rva_; + relocation_list relocations_; +}; + +typedef std::vector<relocation_table> relocation_table_list; + +//Get relocation list of pe file, supports one-word sized relocations only +//If list_absolute_entries = true, IMAGE_REL_BASED_ABSOLUTE will be listed +const relocation_table_list get_relocations(const pe_base& pe, bool list_absolute_entries = false); + +//Simple relocations rebuilder +//To keep PE file working, don't remove any of existing relocations in +//relocation_table_list returned by a call to get_relocations() function +//auto_strip_last_section - if true and relocations are placed in the last section, it will be automatically stripped +//offset_from_section_start - offset from the beginning of reloc_section, where relocations data will be situated +//If save_to_pe_header is true, PE header will be modified automatically +const image_directory rebuild_relocations(pe_base& pe, const relocation_table_list& relocs, section& reloc_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); + +//Recalculates image base with the help of relocation tables +//Recalculates VAs of DWORDS/QWORDS in image according to relocations +//Notice: if you move some critical structures like TLS, image relocations will not fix new +//positions of TLS VAs. Instead, some bytes that now doesn't belong to TLS will be fixed. +//It is recommended to rebase image in the very beginning and move all structures afterwards. +void rebase_image(pe_base& pe, const relocation_table_list& tables, uint64_t new_base); + +template<typename PEClassType> +void rebase_image_base(pe_base& pe, const relocation_table_list& tables, uint64_t new_base); +} diff --git a/tools/pe_bliss/pe_resource_manager.cpp b/tools/pe_bliss/pe_resource_manager.cpp new file mode 100644 index 0000000000..0ee7840ff0 --- /dev/null +++ b/tools/pe_bliss/pe_resource_manager.cpp @@ -0,0 +1,286 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include <sstream> +#include <iomanip> +#include <math.h> +#include <string.h> +#include "pe_resource_manager.h" +#include "resource_internal.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Constructor from root resource directory +pe_resource_manager::pe_resource_manager(resource_directory& root_directory) + :pe_resource_viewer(root_directory), root_dir_edit_(root_directory) +{} + +resource_directory& pe_resource_manager::get_root_directory() +{ + return root_dir_edit_; +} + +//Removes all resources of given type or root name +//If there's more than one directory entry of a given type, only the +//first one will be deleted (that's an unusual situation) +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource_type(resource_type type) +{ + //Search for resource type + resource_directory::entry_list& entries = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it = std::find_if(entries.begin(), entries.end(), resource_directory::id_entry_finder(type)); + if(it != entries.end()) + { + //Remove it, if found + entries.erase(it); + return true; + } + + return false; +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name) +{ + //Search for resource type + resource_directory::entry_list& entries = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it = std::find_if(entries.begin(), entries.end(), resource_directory::name_entry_finder(root_name)); + if(it != entries.end()) + { + //Remove it, if found + entries.erase(it); + return true; + } + + return false; +} + +//Helper to remove resource +bool pe_resource_manager::remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder) +{ + //Search for resource type + resource_directory::entry_list& entries_type = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it_type = std::find_if(entries_type.begin(), entries_type.end(), root_finder); + if(it_type != entries_type.end()) + { + //Search for resource name/ID with "finder" + resource_directory::entry_list& entries_name = (*it_type).get_resource_directory().get_entry_list(); + resource_directory::entry_list::iterator it_name = std::find_if(entries_name.begin(), entries_name.end(), finder); + if(it_name != entries_name.end()) + { + //Erase resource, if found + entries_name.erase(it_name); + if(entries_name.empty()) + entries_type.erase(it_type); + + return true; + } + } + + return false; +} + +//Removes all resource languages by resource type/root name and name +//Deletes only one entry of given type and name +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, const std::wstring& name) +{ + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(name)); +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name, const std::wstring& name) +{ + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(name)); +} + +//Removes all resource languages by resource type/root name and ID +//Deletes only one entry of given type and ID +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, uint32_t id) +{ + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(id)); +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name, uint32_t id) +{ + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(id)); +} + +//Helper to remove resource +bool pe_resource_manager::remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder, uint32_t language) +{ + //Search for resource type + resource_directory::entry_list& entries_type = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it_type = std::find_if(entries_type.begin(), entries_type.end(), root_finder); + if(it_type != entries_type.end()) + { + //Search for resource name/ID with "finder" + resource_directory::entry_list& entries_name = (*it_type).get_resource_directory().get_entry_list(); + resource_directory::entry_list::iterator it_name = std::find_if(entries_name.begin(), entries_name.end(), finder); + if(it_name != entries_name.end()) + { + //Search for resource language + resource_directory::entry_list& entries_lang = (*it_name).get_resource_directory().get_entry_list(); + resource_directory::entry_list::iterator it_lang = std::find_if(entries_lang.begin(), entries_lang.end(), resource_directory::id_entry_finder(language)); + if(it_lang != entries_lang.end()) + { + //Erase resource, if found + entries_lang.erase(it_lang); + if(entries_lang.empty()) + { + entries_name.erase(it_name); + if(entries_name.empty()) + entries_type.erase(it_type); + } + + return true; + } + } + } + + return false; +} + +//Removes resource language by resource type/root name and name +//Deletes only one entry of given type, name and language +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, const std::wstring& name, uint32_t language) +{ + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(name), language); +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name, const std::wstring& name, uint32_t language) +{ + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(name), language); +} + +//Removes recource language by resource type/root name and ID +//Deletes only one entry of given type, ID and language +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, uint32_t id, uint32_t language) +{ + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(id), language); +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name, uint32_t id, uint32_t language) +{ + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(id), language); +} + +//Helper to add/replace resource +void pe_resource_manager::add_resource(const std::string& data, resource_type type, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_type_entry; + new_type_entry.set_id(type); + + add_resource(data, new_type_entry, resource_directory::entry_finder(type), new_entry, finder, language, codepage, timestamp); +} + +//Helper to add/replace resource +void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_type_entry; + new_type_entry.set_name(root_name); + + add_resource(data, new_type_entry, resource_directory::entry_finder(root_name), new_entry, finder, language, codepage, timestamp); +} + +//Helper to add/replace resource +void pe_resource_manager::add_resource(const std::string& data, resource_directory_entry& new_root_entry, const resource_directory::entry_finder& root_finder, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + //Search for resource type + resource_directory::entry_list* entries = &root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it = std::find_if(entries->begin(), entries->end(), root_finder); + if(it == entries->end()) + { + //Add resource type directory, if it was not found + resource_directory dir; + dir.set_timestamp(timestamp); + new_root_entry.add_resource_directory(dir); + entries->push_back(new_root_entry); + it = entries->end() - 1; + } + + //Search for resource name/ID directory with "finder" + entries = &(*it).get_resource_directory().get_entry_list(); + it = std::find_if(entries->begin(), entries->end(), finder); + if(it == entries->end()) + { + //Add resource name/ID directory, if it was not found + resource_directory dir; + dir.set_timestamp(timestamp); + new_entry.add_resource_directory(dir); + entries->push_back(new_entry); + it = entries->end() - 1; + } + + //Search for data resource entry by language + entries = &(*it).get_resource_directory().get_entry_list(); + it = std::find_if(entries->begin(), entries->end(), resource_directory::id_entry_finder(language)); + if(it != entries->end()) + entries->erase(it); //Erase it, if found + + //Add new data entry + resource_directory_entry new_dir_data_entry; + resource_data_entry data_dir(data, codepage); + new_dir_data_entry.add_data_entry(data_dir); + new_dir_data_entry.set_id(language); + entries->push_back(new_dir_data_entry); +} + +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, resource_type type, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_entry; + new_entry.set_name(name); + + add_resource(data, type, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp); +} + +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_entry; + new_entry.set_name(name); + + add_resource(data, root_name, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp); +} + +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, resource_type type, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_entry; + new_entry.set_id(id); + + add_resource(data, type, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp); +} + +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_entry; + new_entry.set_id(id); + + add_resource(data, root_name, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp); +} +} diff --git a/tools/pe_bliss/pe_resource_manager.h b/tools/pe_bliss/pe_resource_manager.h new file mode 100644 index 0000000000..85d7f44a8a --- /dev/null +++ b/tools/pe_bliss/pe_resource_manager.h @@ -0,0 +1,113 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <map> +#include <sstream> +#include <string> +#include <memory> +#include "pe_base.h" +#include "pe_structures.h" +#include "pe_resources.h" +#include "message_table.h" +#include "file_version_info.h" +#include "pe_resource_viewer.h" +#include "resource_data_info.h" + +namespace pe_bliss +{ +//Derived class to edit PE resources +class pe_resource_manager : public pe_resource_viewer +{ +public: + //Constructor from root resource directory + explicit pe_resource_manager(resource_directory& root_directory); + + resource_directory& get_root_directory(); + +public: //Resource editing + //Removes all resources of given type or root name + //If there's more than one directory entry of a given type, only the + //first one will be deleted (that's an unusual situation) + //Returns true if resource was deleted + bool remove_resource_type(resource_type type); + bool remove_resource(const std::wstring& root_name); + + //Removes all resource languages by resource type/root name and name + //Deletes only one entry of given type and name + //Returns true if resource was deleted + bool remove_resource(resource_type type, const std::wstring& name); + bool remove_resource(const std::wstring& root_name, const std::wstring& name); + //Removes all resource languages by resource type/root name and ID + //Deletes only one entry of given type and ID + //Returns true if resource was deleted + bool remove_resource(resource_type type, uint32_t id); + bool remove_resource(const std::wstring& root_name, uint32_t id); + + //Removes resource language by resource type/root name and name + //Deletes only one entry of given type, name and language + //Returns true if resource was deleted + bool remove_resource(resource_type type, const std::wstring& name, uint32_t language); + bool remove_resource(const std::wstring& root_name, const std::wstring& name, uint32_t language); + //Removes recource language by resource type/root name and ID + //Deletes only one entry of given type, ID and language + //Returns true if resource was deleted + bool remove_resource(resource_type type, uint32_t id, uint32_t language); + bool remove_resource(const std::wstring& root_name, uint32_t id, uint32_t language); + + //Adds resource. If resource already exists, replaces it + //timestamp will be used for directories that will be added + void add_resource(const std::string& data, resource_type type, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_resource(const std::string& data, const std::wstring& root_name, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + //Adds resource. If resource already exists, replaces it + //timestamp will be used for directories that will be added + void add_resource(const std::string& data, resource_type type, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_resource(const std::string& data, const std::wstring& root_name, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + +public: + //Helpers to add/replace resource + void add_resource(const std::string& data, resource_type type, + resource_directory_entry& new_entry, + const resource_directory::entry_finder& finder, + uint32_t language, uint32_t codepage, uint32_t timestamp); + + void add_resource(const std::string& data, const std::wstring& root_name, + resource_directory_entry& new_entry, + const resource_directory::entry_finder& finder, + uint32_t language, uint32_t codepage, uint32_t timestamp); + + void add_resource(const std::string& data, resource_directory_entry& new_root_entry, + const resource_directory::entry_finder& root_finder, + resource_directory_entry& new_entry, + const resource_directory::entry_finder& finder, + uint32_t language, uint32_t codepage, uint32_t timestamp); + +private: + //Root resource directory. We're not copying it, because it might be heavy + resource_directory& root_dir_edit_; + + //Helper to remove resource + bool remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder); + + //Helper to remove resource + bool remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder, uint32_t language); +}; +} diff --git a/tools/pe_bliss/pe_resource_viewer.cpp b/tools/pe_bliss/pe_resource_viewer.cpp new file mode 100644 index 0000000000..712cc28d9b --- /dev/null +++ b/tools/pe_bliss/pe_resource_viewer.cpp @@ -0,0 +1,382 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include <cmath> +#include "pe_resource_viewer.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Constructor from root resource_directory +pe_resource_viewer::pe_resource_viewer(const resource_directory& root_directory) + :root_dir_(root_directory) +{} + +const resource_directory& pe_resource_viewer::get_root_directory() const +{ + return root_dir_; +} + +//Finder helpers +bool pe_resource_viewer::has_name::operator()(const resource_directory_entry& entry) const +{ + return entry.is_named(); +} + +bool pe_resource_viewer::has_id::operator()(const resource_directory_entry& entry) const +{ + return !entry.is_named(); +} + +//Lists resource types existing in PE file (non-named only) +const pe_resource_viewer::resource_type_list pe_resource_viewer::list_resource_types() const +{ + resource_type_list ret; + + //Get root directory entries list + const resource_directory::entry_list& entries = root_dir_.get_entry_list(); + for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + //List all non-named items + if(!(*it).is_named()) + ret.push_back((*it).get_id()); + } + + return ret; +} + +//Returns true if resource type exists +bool pe_resource_viewer::resource_exists(resource_type type) const +{ + const resource_directory::entry_list& entries = root_dir_.get_entry_list(); + return std::find_if(entries.begin(), entries.end(), resource_directory::id_entry_finder(type)) != entries.end(); +} + +//Returns true if resource name exists +bool pe_resource_viewer::resource_exists(const std::wstring& root_name) const +{ + const resource_directory::entry_list& entries = root_dir_.get_entry_list(); + return std::find_if(entries.begin(), entries.end(), resource_directory::name_entry_finder(root_name)) != entries.end(); +} + +//Helper function to get name list from entry list +const pe_resource_viewer::resource_name_list pe_resource_viewer::get_name_list(const resource_directory::entry_list& entries) +{ + resource_name_list ret; + + for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + //List all named items + if((*it).is_named()) + ret.push_back((*it).get_name()); + } + + return ret; +} + +//Helper function to get ID list from entry list +const pe_resource_viewer::resource_id_list pe_resource_viewer::get_id_list(const resource_directory::entry_list& entries) +{ + resource_id_list ret; + + for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + //List all non-named items + if(!(*it).is_named()) + ret.push_back((*it).get_id()); + } + + return ret; +} + +//Lists resource names existing in PE file by resource type +const pe_resource_viewer::resource_name_list pe_resource_viewer::list_resource_names(resource_type type) const +{ + return get_name_list(root_dir_.entry_by_id(type).get_resource_directory().get_entry_list()); +} + +//Lists resource names existing in PE file by resource name +const pe_resource_viewer::resource_name_list pe_resource_viewer::list_resource_names(const std::wstring& root_name) const +{ + return get_name_list(root_dir_.entry_by_name(root_name).get_resource_directory().get_entry_list()); +} + +//Lists resource IDs existing in PE file by resource type +const pe_resource_viewer::resource_id_list pe_resource_viewer::list_resource_ids(resource_type type) const +{ + return get_id_list(root_dir_.entry_by_id(type).get_resource_directory().get_entry_list()); +} + +//Lists resource IDs existing in PE file by resource name +const pe_resource_viewer::resource_id_list pe_resource_viewer::list_resource_ids(const std::wstring& root_name) const +{ + return get_id_list(root_dir_.entry_by_name(root_name).get_resource_directory().get_entry_list()); +} + +//Returns resource count by type +unsigned long pe_resource_viewer::get_resource_count(resource_type type) const +{ + return static_cast<unsigned long>( + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .get_entry_list() + .size()); +} + +//Returns resource count by name +unsigned long pe_resource_viewer::get_resource_count(const std::wstring& root_name) const +{ + return static_cast<unsigned long>( + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .get_entry_list() + .size()); +} + +//Returns language count of resource by resource type and name +unsigned long pe_resource_viewer::get_language_count(resource_type type, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Returns language count of resource by resource names +unsigned long pe_resource_viewer::get_language_count(const std::wstring& root_name, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Returns language count of resource by resource type and ID +unsigned long pe_resource_viewer::get_language_count(resource_type type, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Returns language count of resource by resource name and ID +unsigned long pe_resource_viewer::get_language_count(const std::wstring& root_name, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Lists resource languages by resource type and name +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(resource_type type, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Lists resource languages by resource names +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(const std::wstring& root_name, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Lists resource languages by resource type and ID +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(resource_type type, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Lists resource languages by resource name and ID +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(const std::wstring& root_name, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Returns raw resource data by type, name and language +const resource_data_info pe_resource_viewer::get_resource_data_by_name(uint32_t language, resource_type type, const std::wstring& name) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, name and language +const resource_data_info pe_resource_viewer::get_resource_data_by_name(uint32_t language, const std::wstring& root_name, const std::wstring& name) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by type, ID and language +const resource_data_info pe_resource_viewer::get_resource_data_by_id(uint32_t language, resource_type type, uint32_t id) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, ID and language +const resource_data_info pe_resource_viewer::get_resource_data_by_id(uint32_t language, const std::wstring& root_name, uint32_t id) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by type, name and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_name(resource_type type, const std::wstring& name, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, name and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_name(const std::wstring& root_name, const std::wstring& name, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} + +//Returns raw resource data by type, ID and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_id(resource_type type, uint32_t id, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, ID and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_id(const std::wstring& root_name, uint32_t id, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} +} diff --git a/tools/pe_bliss/pe_resource_viewer.h b/tools/pe_bliss/pe_resource_viewer.h new file mode 100644 index 0000000000..e585da6a87 --- /dev/null +++ b/tools/pe_bliss/pe_resource_viewer.h @@ -0,0 +1,153 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <map> +#include <string> +#include "pe_structures.h" +#include "pe_resources.h" +#include "message_table.h" +#include "resource_data_info.h" + +namespace pe_bliss +{ + //PE resource manager allows to read resources from PE files +class pe_resource_viewer +{ +public: + //Resource type enumeration + enum resource_type + { + resource_cursor = 1, + resource_bitmap = 2, + resource_icon = 3, + resource_menu = 4, + resource_dialog = 5, + resource_string = 6, + resource_fontdir = 7, + resource_font = 8, + resource_accelerator = 9, + resource_rcdata = 10, + resource_message_table = 11, + resource_cursor_group = 12, + resource_icon_group = 14, + resource_version = 16, + resource_dlginclude = 17, + resource_plugplay = 19, + resource_vxd = 20, + resource_anicursor = 21, + resource_aniicon = 22, + resource_html = 23, + resource_manifest = 24 + }; + +public: + //Some useful typedefs + typedef std::vector<uint32_t> resource_type_list; + typedef std::vector<uint32_t> resource_id_list; + typedef std::vector<std::wstring> resource_name_list; + typedef std::vector<uint32_t> resource_language_list; + +public: + //Constructor from root resource_directory from PE file + explicit pe_resource_viewer(const resource_directory& root_directory); + + const resource_directory& get_root_directory() const; + + //Lists resource types existing in PE file (non-named only) + const resource_type_list list_resource_types() const; + //Returns true if resource type exists + bool resource_exists(resource_type type) const; + //Returns true if resource name exists + bool resource_exists(const std::wstring& root_name) const; + + //Lists resource names existing in PE file by resource type + const resource_name_list list_resource_names(resource_type type) const; + //Lists resource names existing in PE file by resource name + const resource_name_list list_resource_names(const std::wstring& root_name) const; + //Lists resource IDs existing in PE file by resource type + const resource_id_list list_resource_ids(resource_type type) const; + //Lists resource IDs existing in PE file by resource name + const resource_id_list list_resource_ids(const std::wstring& root_name) const; + //Returns resource count by type + unsigned long get_resource_count(resource_type type) const; + //Returns resource count by name + unsigned long get_resource_count(const std::wstring& root_name) const; + + //Returns language count of resource by resource type and name + unsigned long get_language_count(resource_type type, const std::wstring& name) const; + //Returns language count of resource by resource names + unsigned long get_language_count(const std::wstring& root_name, const std::wstring& name) const; + //Returns language count of resource by resource type and ID + unsigned long get_language_count(resource_type type, uint32_t id) const; + //Returns language count of resource by resource name and ID + unsigned long get_language_count(const std::wstring& root_name, uint32_t id) const; + //Lists resource languages by resource type and name + const resource_language_list list_resource_languages(resource_type type, const std::wstring& name) const; + //Lists resource languages by resource names + const resource_language_list list_resource_languages(const std::wstring& root_name, const std::wstring& name) const; + //Lists resource languages by resource type and ID + const resource_language_list list_resource_languages(resource_type type, uint32_t id) const; + //Lists resource languages by resource name and ID + const resource_language_list list_resource_languages(const std::wstring& root_name, uint32_t id) const; + + //Returns raw resource data by type, name and language + const resource_data_info get_resource_data_by_name(uint32_t language, resource_type type, const std::wstring& name) const; + //Returns raw resource data by root name, name and language + const resource_data_info get_resource_data_by_name(uint32_t language, const std::wstring& root_name, const std::wstring& name) const; + //Returns raw resource data by type, ID and language + const resource_data_info get_resource_data_by_id(uint32_t language, resource_type type, uint32_t id) const; + //Returns raw resource data by root name, ID and language + const resource_data_info get_resource_data_by_id(uint32_t language, const std::wstring& root_name, uint32_t id) const; + //Returns raw resource data by type, name and index in language directory (instead of language) + const resource_data_info get_resource_data_by_name(resource_type type, const std::wstring& name, uint32_t index = 0) const; + //Returns raw resource data by root name, name and index in language directory (instead of language) + const resource_data_info get_resource_data_by_name(const std::wstring& root_name, const std::wstring& name, uint32_t index = 0) const; + //Returns raw resource data by type, ID and index in language directory (instead of language) + const resource_data_info get_resource_data_by_id(resource_type type, uint32_t id, uint32_t index = 0) const; + //Returns raw resource data by root name, ID and index in language directory (instead of language) + const resource_data_info get_resource_data_by_id(const std::wstring& root_name, uint32_t id, uint32_t index = 0) const; + +protected: + //Root resource directory. We're not copying it, because it might be heavy + const resource_directory& root_dir_; + + //Helper function to get ID list from entry list + static const resource_id_list get_id_list(const resource_directory::entry_list& entries); + //Helper function to get name list from entry list + static const resource_name_list get_name_list(const resource_directory::entry_list& entries); + +protected: + //Helper structure - finder of resource_directory_entry that is named + struct has_name + { + public: + bool operator()(const resource_directory_entry& entry) const; + }; + + //Helper structure - finder of resource_directory_entry that is not named (has id) + struct has_id + { + public: + bool operator()(const resource_directory_entry& entry) const; + }; +}; +} diff --git a/tools/pe_bliss/pe_resources.cpp b/tools/pe_bliss/pe_resources.cpp new file mode 100644 index 0000000000..189aba1f76 --- /dev/null +++ b/tools/pe_bliss/pe_resources.cpp @@ -0,0 +1,726 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include <string.h> +#include "pe_resources.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//RESOURCES +//Default constructor +resource_data_entry::resource_data_entry() + :codepage_(0) +{} + +//Constructor from data +resource_data_entry::resource_data_entry(const std::string& data, uint32_t codepage) + :codepage_(codepage), data_(data) +{} + +//Returns resource data codepage +uint32_t resource_data_entry::get_codepage() const +{ + return codepage_; +} + +//Returns resource data +const std::string& resource_data_entry::get_data() const +{ + return data_; +} + +//Sets resource data codepage +void resource_data_entry::set_codepage(uint32_t codepage) +{ + codepage_ = codepage; +} + +//Sets resource data +void resource_data_entry::set_data(const std::string& data) +{ + data_ = data; +} + +//Default constructor +resource_directory_entry::includes::includes() + :data_(0) +{} + +//Default constructor +resource_directory_entry::resource_directory_entry() + :id_(0), includes_data_(false), named_(false) +{} + +//Copy constructor +resource_directory_entry::resource_directory_entry(const resource_directory_entry& other) + :id_(other.id_), name_(other.name_), includes_data_(other.includes_data_), named_(other.named_) +{ + //If union'ed pointer is not zero + if(other.ptr_.data_) + { + if(other.includes_data()) + ptr_.data_ = new resource_data_entry(*other.ptr_.data_); + else + ptr_.dir_ = new resource_directory(*other.ptr_.dir_); + } +} + +//Copy assignment operator +resource_directory_entry& resource_directory_entry::operator=(const resource_directory_entry& other) +{ + release(); + + id_ = other.id_; + name_ = other.name_; + includes_data_ = other.includes_data_; + named_ = other.named_; + + //If other union'ed pointer is not zero + if(other.ptr_.data_) + { + if(other.includes_data()) + ptr_.data_ = new resource_data_entry(*other.ptr_.data_); + else + ptr_.dir_ = new resource_directory(*other.ptr_.dir_); + } + + return *this; +} + +//Destroys included data +void resource_directory_entry::release() +{ + //If union'ed pointer is not zero + if(ptr_.data_) + { + if(includes_data()) + delete ptr_.data_; + else + delete ptr_.dir_; + + ptr_.data_ = 0; + } +} + +//Destructor +resource_directory_entry::~resource_directory_entry() +{ + release(); +} + +//Returns entry ID +uint32_t resource_directory_entry::get_id() const +{ + return id_; +} + +//Returns entry name +const std::wstring& resource_directory_entry::get_name() const +{ + return name_; +} + +//Returns true, if entry has name +//Returns false, if entry has ID +bool resource_directory_entry::is_named() const +{ + return named_; +} + +//Returns true, if entry includes resource_data_entry +//Returns false, if entry includes resource_directory +bool resource_directory_entry::includes_data() const +{ + return includes_data_; +} + +//Returns resource_directory if entry includes it, otherwise throws an exception +const resource_directory& resource_directory_entry::get_resource_directory() const +{ + if(!ptr_.dir_ || includes_data_) + throw pe_exception("Resource directory entry does not contain resource directory", pe_exception::resource_directory_entry_error); + + return *ptr_.dir_; +} + +//Returns resource_data_entry if entry includes it, otherwise throws an exception +const resource_data_entry& resource_directory_entry::get_data_entry() const +{ + if(!ptr_.data_ || !includes_data_) + throw pe_exception("Resource directory entry does not contain resource data entry", pe_exception::resource_directory_entry_error); + + return *ptr_.data_; +} + +//Returns resource_directory if entry includes it, otherwise throws an exception +resource_directory& resource_directory_entry::get_resource_directory() +{ + if(!ptr_.dir_ || includes_data_) + throw pe_exception("Resource directory entry does not contain resource directory", pe_exception::resource_directory_entry_error); + + return *ptr_.dir_; +} + +//Returns resource_data_entry if entry includes it, otherwise throws an exception +resource_data_entry& resource_directory_entry::get_data_entry() +{ + if(!ptr_.data_ || !includes_data_) + throw pe_exception("Resource directory entry does not contain resource data entry", pe_exception::resource_directory_entry_error); + + return *ptr_.data_; +} + +//Sets entry name +void resource_directory_entry::set_name(const std::wstring& name) +{ + name_ = name; + named_ = true; + id_ = 0; +} + +//Sets entry ID +void resource_directory_entry::set_id(uint32_t id) +{ + id_ = id; + named_ = false; + name_.clear(); +} + +//Adds resource_data_entry +void resource_directory_entry::add_data_entry(const resource_data_entry& entry) +{ + release(); + ptr_.data_ = new resource_data_entry(entry); + includes_data_ = true; +} + +//Adds resource_directory +void resource_directory_entry::add_resource_directory(const resource_directory& dir) +{ + release(); + ptr_.dir_ = new resource_directory(dir); + includes_data_ = false; +} + +//Default constructor +resource_directory::resource_directory() + :characteristics_(0), + timestamp_(0), + major_version_(0), minor_version_(0), + number_of_named_entries_(0), number_of_id_entries_(0) +{} + +//Constructor from data +resource_directory::resource_directory(const image_resource_directory& dir) + :characteristics_(dir.Characteristics), + timestamp_(dir.TimeDateStamp), + major_version_(dir.MajorVersion), minor_version_(dir.MinorVersion), + number_of_named_entries_(0), number_of_id_entries_(0) //Set to zero here, calculate on add +{} + +//Returns characteristics of directory +uint32_t resource_directory::get_characteristics() const +{ + return characteristics_; +} + +//Returns date and time stamp of directory +uint32_t resource_directory::get_timestamp() const +{ + return timestamp_; +} + +//Returns major version of directory +uint16_t resource_directory::get_major_version() const +{ + return major_version_; +} + +//Returns minor version of directory +uint16_t resource_directory::get_minor_version() const +{ + return minor_version_; +} + +//Returns number of named entries +uint32_t resource_directory::get_number_of_named_entries() const +{ + return number_of_named_entries_; +} + +//Returns number of ID entries +uint32_t resource_directory::get_number_of_id_entries() const +{ + return number_of_id_entries_; +} + +//Returns resource_directory_entry array +const resource_directory::entry_list& resource_directory::get_entry_list() const +{ + return entries_; +} + +//Returns resource_directory_entry array +resource_directory::entry_list& resource_directory::get_entry_list() +{ + return entries_; +} + +//Adds resource_directory_entry +void resource_directory::add_resource_directory_entry(const resource_directory_entry& entry) +{ + entries_.push_back(entry); + if(entry.is_named()) + ++number_of_named_entries_; + else + ++number_of_id_entries_; +} + +//Clears resource_directory_entry array +void resource_directory::clear_resource_directory_entry_list() +{ + entries_.clear(); + number_of_named_entries_ = 0; + number_of_id_entries_ = 0; +} + +//Sets characteristics of directory +void resource_directory::set_characteristics(uint32_t characteristics) +{ + characteristics_ = characteristics; +} + +//Sets date and time stamp of directory +void resource_directory::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Sets number of named entries +void resource_directory::set_number_of_named_entries(uint32_t number) +{ + number_of_named_entries_ = number; +} + +//Sets number of ID entries +void resource_directory::set_number_of_id_entries(uint32_t number) +{ + number_of_id_entries_ = number; +} + +//Sets major version of directory +void resource_directory::set_major_version(uint16_t major_version) +{ + major_version_ = major_version; +} + +//Sets minor version of directory +void resource_directory::get_minor_version(uint16_t minor_version) +{ + minor_version_ = minor_version; +} + +//Processes resource directory +const resource_directory process_resource_directory(const pe_base& pe, uint32_t res_rva, uint32_t offset_to_directory, std::set<uint32_t>& processed) +{ + resource_directory ret; + + //Check for resource loops + if(!processed.insert(offset_to_directory).second) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + if(!pe_utils::is_sum_safe(res_rva, offset_to_directory)) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + //Get root IMAGE_RESOURCE_DIRECTORY + image_resource_directory directory = pe.section_data_from_rva<image_resource_directory>(res_rva + offset_to_directory, section_data_virtual, true); + + ret = resource_directory(directory); + + //Check DWORDs for possible overflows + if(!pe_utils::is_sum_safe(directory.NumberOfIdEntries, directory.NumberOfNamedEntries) + || directory.NumberOfIdEntries + directory.NumberOfNamedEntries >= pe_utils::max_dword / sizeof(image_resource_directory_entry) + sizeof(image_resource_directory)) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + if(!pe_utils::is_sum_safe(offset_to_directory, sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry)) + || !pe_utils::is_sum_safe(res_rva, offset_to_directory + sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry))) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + for(unsigned long i = 0; i != static_cast<unsigned long>(directory.NumberOfIdEntries) + directory.NumberOfNamedEntries; ++i) + { + //Read directory entries one by one + image_resource_directory_entry dir_entry = pe.section_data_from_rva<image_resource_directory_entry>( + res_rva + sizeof(image_resource_directory) + i * sizeof(image_resource_directory_entry) + offset_to_directory, section_data_virtual, true); + + //Create directory entry structure + resource_directory_entry entry; + + //If directory is named + if(dir_entry.NameIsString) + { + if(!pe_utils::is_sum_safe(res_rva + sizeof(uint16_t) /* safe */, dir_entry.NameOffset)) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + //get directory name length + uint16_t directory_name_length = pe.section_data_from_rva<uint16_t>(res_rva + dir_entry.NameOffset, section_data_virtual, true); + + //Check name length + if(pe.section_data_length_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true) + < directory_name_length) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + +#ifdef PE_BLISS_WINDOWS + //Set entry UNICODE name + entry.set_name(std::wstring( + reinterpret_cast<const wchar_t*>(pe.section_data_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true)), + directory_name_length)); +#else + //Set entry UNICODE name + entry.set_name(pe_utils::from_ucs2(u16string( + reinterpret_cast<const unicode16_t*>(pe.section_data_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true)), + directory_name_length))); +#endif + } + else + { + //Else - set directory ID + entry.set_id(dir_entry.Id); + } + + //If directory entry has another resource directory + if(dir_entry.DataIsDirectory) + { + entry.add_resource_directory(process_resource_directory(pe, res_rva, dir_entry.OffsetToDirectory, processed)); + } + else + { + //If directory entry has data + image_resource_data_entry data_entry = pe.section_data_from_rva<image_resource_data_entry>( + res_rva + dir_entry.OffsetToData, section_data_virtual, true); + + //Check byte count that stated by data entry + if(pe.section_data_length_from_rva(data_entry.OffsetToData, data_entry.OffsetToData, section_data_virtual, true) < data_entry.Size) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + //Add data entry to directory entry + entry.add_data_entry(resource_data_entry( + std::string(pe.section_data_from_rva(data_entry.OffsetToData, section_data_virtual, true), data_entry.Size), + data_entry.CodePage)); + } + + //Save directory entry + ret.add_resource_directory_entry(entry); + } + + //Return resource directory + return ret; +} + +//Helper function to calculate needed space for resource data +void calculate_resource_data_space(const resource_directory& root, uint32_t aligned_offset_from_section_start, uint32_t& needed_size_for_structures, uint32_t& needed_size_for_strings) +{ + needed_size_for_structures += sizeof(image_resource_directory); + for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + needed_size_for_structures += sizeof(image_resource_directory_entry); + + if((*it).is_named()) + needed_size_for_strings += static_cast<uint32_t>(((*it).get_name().length() + 1) * 2 /* unicode */ + sizeof(uint16_t) /* for string length */); + + if(!(*it).includes_data()) + calculate_resource_data_space((*it).get_resource_directory(), aligned_offset_from_section_start, needed_size_for_structures, needed_size_for_strings); + } +} + +//Helper function to calculate needed space for resource data +void calculate_resource_data_space(const resource_directory& root, uint32_t needed_size_for_structures, uint32_t needed_size_for_strings, uint32_t& needed_size_for_data, uint32_t& current_data_pos) +{ + for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + if((*it).includes_data()) + { + uint32_t data_size = static_cast<uint32_t>((*it).get_data_entry().get_data().length() + + sizeof(image_resource_data_entry) + + (pe_utils::align_up(current_data_pos, sizeof(uint32_t)) - current_data_pos) /* alignment */); + needed_size_for_data += data_size; + current_data_pos += data_size; + } + else + { + calculate_resource_data_space((*it).get_resource_directory(), needed_size_for_structures, needed_size_for_strings, needed_size_for_data, current_data_pos); + } + } +} + +//Helper: sorts resource directory entries +struct entry_sorter +{ +public: + bool operator()(const resource_directory_entry& entry1, const resource_directory_entry& entry2) const; +}; + +//Helper function to rebuild resource directory +void rebuild_resource_directory(pe_base& pe, section& resource_section, resource_directory& root, uint32_t& current_structures_pos, uint32_t& current_data_pos, uint32_t& current_strings_pos, uint32_t offset_from_section_start) +{ + //Create resource directory + image_resource_directory dir = {0}; + dir.Characteristics = root.get_characteristics(); + dir.MajorVersion = root.get_major_version(); + dir.MinorVersion = root.get_minor_version(); + dir.TimeDateStamp = root.get_timestamp(); + + { + resource_directory::entry_list& entries = root.get_entry_list(); + std::sort(entries.begin(), entries.end(), entry_sorter()); + } + + //Calculate number of named and ID entries + for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + if((*it).is_named()) + ++dir.NumberOfNamedEntries; + else + ++dir.NumberOfIdEntries; + } + + std::string& raw_data = resource_section.get_raw_data(); + + //Save resource directory + memcpy(&raw_data[current_structures_pos], &dir, sizeof(dir)); + current_structures_pos += sizeof(dir); + + uint32_t this_current_structures_pos = current_structures_pos; + + current_structures_pos += sizeof(image_resource_directory_entry) * (dir.NumberOfNamedEntries + dir.NumberOfIdEntries); + + //Create all resource directory entries + for(resource_directory::entry_list::iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + image_resource_directory_entry entry; + if((*it).is_named()) + { + entry.Name = 0x80000000 | (current_strings_pos - offset_from_section_start); + uint16_t unicode_length = static_cast<uint16_t>((*it).get_name().length()); + memcpy(&raw_data[current_strings_pos], &unicode_length, sizeof(unicode_length)); + current_strings_pos += sizeof(unicode_length); + +#ifdef PE_BLISS_WINDOWS + memcpy(&raw_data[current_strings_pos], (*it).get_name().c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); +#else + { + u16string str(pe_utils::to_ucs2((*it).get_name())); + memcpy(&raw_data[current_strings_pos], str.c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); + } +#endif + + current_strings_pos += static_cast<unsigned long>((*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); + } + else + { + entry.Name = (*it).get_id(); + } + + if((*it).includes_data()) + { + current_data_pos = pe_utils::align_up(current_data_pos, sizeof(uint32_t)); + image_resource_data_entry data_entry = {0}; + data_entry.CodePage = (*it).get_data_entry().get_codepage(); + data_entry.Size = static_cast<uint32_t>((*it).get_data_entry().get_data().length()); + data_entry.OffsetToData = pe.rva_from_section_offset(resource_section, current_data_pos + sizeof(data_entry)); + + entry.OffsetToData = current_data_pos - offset_from_section_start; + + memcpy(&raw_data[current_data_pos], &data_entry, sizeof(data_entry)); + current_data_pos += sizeof(data_entry); + + memcpy(&raw_data[current_data_pos], (*it).get_data_entry().get_data().data(), data_entry.Size); + current_data_pos += data_entry.Size; + + memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry)); + this_current_structures_pos += sizeof(entry); + } + else + { + entry.OffsetToData = 0x80000000 | (current_structures_pos - offset_from_section_start); + + memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry)); + this_current_structures_pos += sizeof(entry); + + rebuild_resource_directory(pe, resource_section, (*it).get_resource_directory(), current_structures_pos, current_data_pos, current_strings_pos, offset_from_section_start); + } + } +} + +//Helper function to rebuild resource directory +bool entry_sorter::operator()(const resource_directory_entry& entry1, const resource_directory_entry& entry2) const +{ + if(entry1.is_named() && entry2.is_named()) + return entry1.get_name() < entry2.get_name(); + else if(!entry1.is_named() && !entry2.is_named()) + return entry1.get_id() < entry2.get_id(); + else + return entry1.is_named(); +} + +//Resources rebuilder +//resource_directory - root resource directory +//resources_section - section where resource directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from resources_section raw data start +//resource_directory is non-constant, because it will be sorted +//save_to_pe_headers - if true, new resource directory information will be saved to PE image headers +//auto_strip_last_section - if true and resources are placed in the last section, it will be automatically stripped +//number_of_id_entries and number_of_named_entries for resource directories are recalculated and not used +const image_directory rebuild_resources(pe_base& pe, resource_directory& info, section& resources_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that resources_section is attached to this PE image + if(!pe.section_attached(resources_section)) + throw pe_exception("Resource section must be attached to PE file", pe_exception::section_is_not_attached); + + //Check resource directory correctness + if(info.get_entry_list().empty()) + throw pe_exception("Empty resource directory", pe_exception::incorrect_resource_directory); + + uint32_t aligned_offset_from_section_start = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + uint32_t needed_size_for_structures = aligned_offset_from_section_start - offset_from_section_start; //Calculate needed size for resource tables and data + uint32_t needed_size_for_strings = 0; + uint32_t needed_size_for_data = 0; + + calculate_resource_data_space(info, aligned_offset_from_section_start, needed_size_for_structures, needed_size_for_strings); + + { + uint32_t current_data_pos = aligned_offset_from_section_start + needed_size_for_structures + needed_size_for_strings; + calculate_resource_data_space(info, needed_size_for_structures, needed_size_for_strings, needed_size_for_data, current_data_pos); + } + + uint32_t needed_size = needed_size_for_structures + needed_size_for_strings + needed_size_for_data; + + //Check if resources_section is last one. If it's not, check if there's enough place for resource data + if(&resources_section != &*(pe.get_image_sections().end() - 1) && + (resources_section.empty() || pe_utils::align_up(resources_section.get_size_of_raw_data(), pe.get_file_alignment()) + < needed_size + aligned_offset_from_section_start)) + throw pe_exception("Insufficient space for resource directory", pe_exception::insufficient_space); + + std::string& raw_data = resources_section.get_raw_data(); + + //This will be done only if resources_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + aligned_offset_from_section_start) + raw_data.resize(needed_size + aligned_offset_from_section_start); //Expand section raw data + + uint32_t current_structures_pos = aligned_offset_from_section_start; + uint32_t current_strings_pos = current_structures_pos + needed_size_for_structures; + uint32_t current_data_pos = current_strings_pos + needed_size_for_strings; + rebuild_resource_directory(pe, resources_section, info, current_structures_pos, current_data_pos, current_strings_pos, aligned_offset_from_section_start); + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(resources_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(resources_section, aligned_offset_from_section_start), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_resource, ret.get_rva()); + pe.set_directory_size(image_directory_entry_resource, ret.get_size()); + } + + return ret; +} + +//Returns resources from PE file +const resource_directory get_resources(const pe_base& pe) +{ + resource_directory ret; + + if(!pe.has_resources()) + return ret; + + //Get resource directory RVA + uint32_t res_rva = pe.get_directory_rva(image_directory_entry_resource); + + //Store already processed directories to avoid resource loops + std::set<uint32_t> processed; + + //Process all directories (recursion) + ret = process_resource_directory(pe, res_rva, 0, processed); + + return ret; +} + +//Finds resource_directory_entry by ID +resource_directory::id_entry_finder::id_entry_finder(uint32_t id) + :id_(id) +{} + +bool resource_directory::id_entry_finder::operator()(const resource_directory_entry& entry) const +{ + return !entry.is_named() && entry.get_id() == id_; +} + +//Finds resource_directory_entry by name +resource_directory::name_entry_finder::name_entry_finder(const std::wstring& name) + :name_(name) +{} + +bool resource_directory::name_entry_finder::operator()(const resource_directory_entry& entry) const +{ + return entry.is_named() && entry.get_name() == name_; +} + +//Finds resource_directory_entry by name or ID (universal) +resource_directory::entry_finder::entry_finder(const std::wstring& name) + :name_(name), named_(true) +{} + +resource_directory::entry_finder::entry_finder(uint32_t id) + :id_(id), named_(false) +{} + +bool resource_directory::entry_finder::operator()(const resource_directory_entry& entry) const +{ + if(named_) + return entry.is_named() && entry.get_name() == name_; + else + return !entry.is_named() && entry.get_id() == id_; +} + +//Returns resource_directory_entry by ID. If not found - throws an exception +const resource_directory_entry& resource_directory::entry_by_id(uint32_t id) const +{ + entry_list::const_iterator i = std::find_if(entries_.begin(), entries_.end(), id_entry_finder(id)); + if(i == entries_.end()) + throw pe_exception("Resource directory entry not found", pe_exception::resource_directory_entry_not_found); + + return *i; +} + +//Returns resource_directory_entry by name. If not found - throws an exception +const resource_directory_entry& resource_directory::entry_by_name(const std::wstring& name) const +{ + entry_list::const_iterator i = std::find_if(entries_.begin(), entries_.end(), name_entry_finder(name)); + if(i == entries_.end()) + throw pe_exception("Resource directory entry not found", pe_exception::resource_directory_entry_not_found); + + return *i; +} +} diff --git a/tools/pe_bliss/pe_resources.h b/tools/pe_bliss/pe_resources.h new file mode 100644 index 0000000000..1eb6437563 --- /dev/null +++ b/tools/pe_bliss/pe_resources.h @@ -0,0 +1,245 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include <string> +#include <set> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing resource data entry +class resource_data_entry +{ +public: + //Default constructor + resource_data_entry(); + //Constructor from data + resource_data_entry(const std::string& data, uint32_t codepage); + + //Returns resource data codepage + uint32_t get_codepage() const; + //Returns resource data + const std::string& get_data() const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild resource directory + + //Sets resource data codepage + void set_codepage(uint32_t codepage); + //Sets resource data + void set_data(const std::string& data); + +private: + uint32_t codepage_; //Resource data codepage + std::string data_; //Resource data +}; + +//Forward declaration +class resource_directory; + +//Class representing resource directory entry +class resource_directory_entry +{ +public: + //Default constructor + resource_directory_entry(); + //Copy constructor + resource_directory_entry(const resource_directory_entry& other); + //Copy assignment operator + resource_directory_entry& operator=(const resource_directory_entry& other); + + //Returns entry ID + uint32_t get_id() const; + //Returns entry name + const std::wstring& get_name() const; + //Returns true, if entry has name + //Returns false, if entry has ID + bool is_named() const; + + //Returns true, if entry includes resource_data_entry + //Returns false, if entry includes resource_directory + bool includes_data() const; + //Returns resource_directory if entry includes it, otherwise throws an exception + const resource_directory& get_resource_directory() const; + //Returns resource_data_entry if entry includes it, otherwise throws an exception + const resource_data_entry& get_data_entry() const; + + //Destructor + ~resource_directory_entry(); + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild resource directory + + //Sets entry name + void set_name(const std::wstring& name); + //Sets entry ID + void set_id(uint32_t id); + + //Returns resource_directory if entry includes it, otherwise throws an exception + resource_directory& get_resource_directory(); + //Returns resource_data_entry if entry includes it, otherwise throws an exception + resource_data_entry& get_data_entry(); + + //Adds resource_data_entry + void add_data_entry(const resource_data_entry& entry); + //Adds resource_directory + void add_resource_directory(const resource_directory& dir); + +private: + //Destroys included data + void release(); + +private: + uint32_t id_; + std::wstring name_; + + union includes + { + //Default constructor + includes(); + + //We use pointers, we're doing manual copying here + class resource_data_entry* data_; + class resource_directory* dir_; //We use pointer, because structs include each other + }; + + includes ptr_; + + bool includes_data_, named_; +}; + +//Class representing resource directory +class resource_directory +{ +public: + typedef std::vector<resource_directory_entry> entry_list; + +public: + //Default constructor + resource_directory(); + //Constructor from data + explicit resource_directory(const pe_win::image_resource_directory& dir); + + //Returns characteristics of directory + uint32_t get_characteristics() const; + //Returns date and time stamp of directory + uint32_t get_timestamp() const; + //Returns number of named entries + uint32_t get_number_of_named_entries() const; + //Returns number of ID entries + uint32_t get_number_of_id_entries() const; + //Returns major version of directory + uint16_t get_major_version() const; + //Returns minor version of directory + uint16_t get_minor_version() const; + //Returns resource_directory_entry array + const entry_list& get_entry_list() const; + //Returns resource_directory_entry by ID. If not found - throws an exception + const resource_directory_entry& entry_by_id(uint32_t id) const; + //Returns resource_directory_entry by name. If not found - throws an exception + const resource_directory_entry& entry_by_name(const std::wstring& name) const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild resource directory + + //Adds resource_directory_entry + void add_resource_directory_entry(const resource_directory_entry& entry); + //Clears resource_directory_entry array + void clear_resource_directory_entry_list(); + + //Sets characteristics of directory + void set_characteristics(uint32_t characteristics); + //Sets date and time stamp of directory + void set_timestamp(uint32_t timestamp); + //Sets number of named entries + void set_number_of_named_entries(uint32_t number); + //Sets number of ID entries + void set_number_of_id_entries(uint32_t number); + //Sets major version of directory + void set_major_version(uint16_t major_version); + //Sets minor version of directory + void get_minor_version(uint16_t minor_version); + + //Returns resource_directory_entry array + entry_list& get_entry_list(); + +private: + uint32_t characteristics_; + uint32_t timestamp_; + uint16_t major_version_, minor_version_; + uint32_t number_of_named_entries_, number_of_id_entries_; + entry_list entries_; + +public: //Finder helpers + //Finds resource_directory_entry by ID + struct id_entry_finder + { + public: + explicit id_entry_finder(uint32_t id); + bool operator()(const resource_directory_entry& entry) const; + + private: + uint32_t id_; + }; + + //Finds resource_directory_entry by name + struct name_entry_finder + { + public: + explicit name_entry_finder(const std::wstring& name); + bool operator()(const resource_directory_entry& entry) const; + + private: + std::wstring name_; + }; + + //Finds resource_directory_entry by name or ID (universal) + struct entry_finder + { + public: + explicit entry_finder(const std::wstring& name); + explicit entry_finder(uint32_t id); + bool operator()(const resource_directory_entry& entry) const; + + private: + std::wstring name_; + uint32_t id_; + bool named_; + }; +}; + +//Returns resources (root resource_directory) from PE file +const resource_directory get_resources(const pe_base& pe); + +//Resources rebuilder +//resource_directory - root resource directory +//resources_section - section where resource directory will be placed (must be attached to PE image) +//resource_directory is non-constant, because it will be sorted +//offset_from_section_start - offset from resources_section raw data start +//save_to_pe_headers - if true, new resource directory information will be saved to PE image headers +//auto_strip_last_section - if true and resources are placed in the last section, it will be automatically stripped +//number_of_id_entries and number_of_named_entries for resource directories are recalculated and not used +const image_directory rebuild_resources(pe_base& pe, resource_directory& info, section& resources_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/pe_rich_data.cpp b/tools/pe_bliss/pe_rich_data.cpp new file mode 100644 index 0000000000..e92f7ddc1b --- /dev/null +++ b/tools/pe_bliss/pe_rich_data.cpp @@ -0,0 +1,152 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_rich_data.h" + +namespace pe_bliss +{ +//STUB OVERLAY +//Default constructor +rich_data::rich_data() + :number_(0), version_(0), times_(0) +{} + +//Who knows, what these fields mean... +uint32_t rich_data::get_number() const +{ + return number_; +} + +uint32_t rich_data::get_version() const +{ + return version_; +} + +uint32_t rich_data::get_times() const +{ + return times_; +} + +void rich_data::set_number(uint32_t number) +{ + number_ = number; +} + +void rich_data::set_version(uint32_t version) +{ + version_ = version; +} + +void rich_data::set_times(uint32_t times) +{ + times_ = times; +} + +//Returns MSVC rich data +const rich_data_list get_rich_data(const pe_base& pe) +{ + //Returned value + rich_data_list ret; + + const std::string& rich_overlay = pe.get_stub_overlay(); + + //If there's no rich overlay, return empty vector + if(rich_overlay.size() < sizeof(uint32_t)) + return ret; + + //True if rich data was found + bool found = false; + + //Rich overlay ID ("Rich" word) + static const uint32_t rich_overlay_id = 0x68636952; + + //Search for rich data overlay ID + const char* begin = &rich_overlay[0]; + const char* end = begin + rich_overlay.length(); + for(; begin != end; ++begin) + { + if(*reinterpret_cast<const uint32_t*>(begin) == rich_overlay_id) + { + found = true; //We've found it! + break; + } + } + + //If we found it + if(found) + { + //Check remaining length + if(static_cast<size_t>(end - begin) < sizeof(uint32_t)) + return ret; + + //The XOR key is after "Rich" word, we should get it + uint32_t xorkey = *reinterpret_cast<const uint32_t*>(begin + sizeof(uint32_t)); + + //True if rich data was found + found = false; + + //Second search for signature "DanS" + begin = &rich_overlay[0]; + for(; begin != end; ++begin) + { + if((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) == 0x536e6144) //"DanS" + { + found = true; + break; + } + } + + //If second signature is found + if(found) + { + begin += sizeof(uint32_t) * 3; + //List all rich data structures + while(begin < end) + { + begin += sizeof(uint32_t); + if(begin >= end) + break; + + //Check for rich overlay data end ("Rich" word reached) + if(*reinterpret_cast<const uint32_t*>(begin) == rich_overlay_id) + break; + + //Create rich_data structure + rich_data data; + data.set_number((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) >> 16); + data.set_version((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) & 0xFFFF); + + begin += sizeof(uint32_t); + if(begin >= end) + break; + + data.set_times(*reinterpret_cast<const uint32_t*>(begin) ^ xorkey); + + //Save rich data structure + ret.push_back(data); + } + } + } + + //Return rich data structures list + return ret; +} +} diff --git a/tools/pe_bliss/pe_rich_data.h b/tools/pe_bliss/pe_rich_data.h new file mode 100644 index 0000000000..3e01d3c011 --- /dev/null +++ b/tools/pe_bliss/pe_rich_data.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Rich data overlay class of Microsoft Visual Studio +class rich_data +{ +public: + //Default constructor + rich_data(); + +public: //Getters + //Who knows, what these fields mean... + uint32_t get_number() const; + uint32_t get_version() const; + uint32_t get_times() const; + +public: //Setters, used by PE library only + void set_number(uint32_t number); + void set_version(uint32_t version); + void set_times(uint32_t times); + +private: + uint32_t number_; + uint32_t version_; + uint32_t times_; +}; + +//Rich data list typedef +typedef std::vector<rich_data> rich_data_list; + +//Returns a vector with rich data (stub overlay) +const rich_data_list get_rich_data(const pe_base& pe); +} diff --git a/tools/pe_bliss/pe_section.cpp b/tools/pe_bliss/pe_section.cpp new file mode 100644 index 0000000000..72127e22e2 --- /dev/null +++ b/tools/pe_bliss/pe_section.cpp @@ -0,0 +1,303 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include <string.h> +#include "utils.h" +#include "pe_section.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Section structure default constructor +section::section() + :old_size_(static_cast<size_t>(-1)) +{ + memset(&header_, 0, sizeof(image_section_header)); +} + +//Sets the name of section (8 characters maximum) +void section::set_name(const std::string& name) +{ + memset(header_.Name, 0, sizeof(header_.Name)); + memcpy(header_.Name, name.c_str(), std::min<size_t>(name.length(), sizeof(header_.Name))); +} + +//Returns section name +const std::string section::get_name() const +{ + char buf[9] = {0}; + memcpy(buf, header_.Name, 8); + return std::string(buf); +} + +//Set flag (attribute) of section +section& section::set_flag(uint32_t flag, bool setflag) +{ + if(setflag) + header_.Characteristics |= flag; + else + header_.Characteristics &= ~flag; + + return *this; +} + +//Sets "readable" attribute of section +section& section::readable(bool readable) +{ + return set_flag(image_scn_mem_read, readable); +} + +//Sets "writeable" attribute of section +section& section::writeable(bool writeable) +{ + return set_flag(image_scn_mem_write, writeable); +} + +//Sets "executable" attribute of section +section& section::executable(bool executable) +{ + return set_flag(image_scn_mem_execute, executable); +} + +//Sets "shared" attribute of section +section& section::shared(bool shared) +{ + return set_flag(image_scn_mem_shared, shared); +} + +//Sets "discardable" attribute of section +section& section::discardable(bool discardable) +{ + return set_flag(image_scn_mem_discardable, discardable); +} + +//Returns true if section is readable +bool section::readable() const +{ + return (header_.Characteristics & image_scn_mem_read) != 0; +} + +//Returns true if section is writeable +bool section::writeable() const +{ + return (header_.Characteristics & image_scn_mem_write) != 0; +} + +//Returns true if section is executable +bool section::executable() const +{ + return (header_.Characteristics & image_scn_mem_execute) != 0; +} + +bool section::shared() const +{ + return (header_.Characteristics & image_scn_mem_shared) != 0; +} + +bool section::discardable() const +{ + return (header_.Characteristics & image_scn_mem_discardable) != 0; +} + +//Returns true if section has no RAW data +bool section::empty() const +{ + if(old_size_ != static_cast<size_t>(-1)) //If virtual memory is mapped, check raw data length (old_size_) + return old_size_ == 0; + else + return raw_data_.empty(); +} + +//Returns raw section data from file image +std::string& section::get_raw_data() +{ + unmap_virtual(); + return raw_data_; +} + +//Sets raw section data from file image +void section::set_raw_data(const std::string& data) +{ + old_size_ = static_cast<size_t>(-1); + raw_data_ = data; +} + +//Returns raw section data from file image +const std::string& section::get_raw_data() const +{ + unmap_virtual(); + return raw_data_; +} + +//Returns mapped virtual section data +const std::string& section::get_virtual_data(uint32_t section_alignment) const +{ + map_virtual(section_alignment); + return raw_data_; +} + +//Returns mapped virtual section data +std::string& section::get_virtual_data(uint32_t section_alignment) +{ + map_virtual(section_alignment); + return raw_data_; +} + +//Maps virtual section data +void section::map_virtual(uint32_t section_alignment) const +{ + uint32_t aligned_virtual_size = get_aligned_virtual_size(section_alignment); + if(old_size_ == static_cast<size_t>(-1) && aligned_virtual_size && aligned_virtual_size > raw_data_.length()) + { + old_size_ = raw_data_.length(); + raw_data_.resize(aligned_virtual_size, 0); + } +} + +//Unmaps virtual section data +void section::unmap_virtual() const +{ + if(old_size_ != static_cast<size_t>(-1)) + { + raw_data_.resize(old_size_, 0); + old_size_ = static_cast<size_t>(-1); + } +} + +//Returns section virtual size +uint32_t section::get_virtual_size() const +{ + return header_.Misc.VirtualSize; +} + +//Returns section virtual address +uint32_t section::get_virtual_address() const +{ + return header_.VirtualAddress; +} + +//Returns size of section raw data +uint32_t section::get_size_of_raw_data() const +{ + return header_.SizeOfRawData; +} + +//Returns pointer to raw section data in PE file +uint32_t section::get_pointer_to_raw_data() const +{ + return header_.PointerToRawData; +} + +//Returns section characteristics +uint32_t section::get_characteristics() const +{ + return header_.Characteristics; +} + +//Returns raw image section header +const pe_win::image_section_header& section::get_raw_header() const +{ + return header_; +} + +//Returns raw image section header +pe_win::image_section_header& section::get_raw_header() +{ + return header_; +} + +//Calculates aligned virtual section size +uint32_t section::get_aligned_virtual_size(uint32_t section_alignment) const +{ + if(get_size_of_raw_data()) + { + if(!get_virtual_size()) + { + //If section virtual size is zero + //Set aligned virtual size of section as aligned raw size + return pe_utils::align_up(get_size_of_raw_data(), section_alignment); + } + } + + return pe_utils::align_up(get_virtual_size(), section_alignment); +} + +//Calculates aligned raw section size +uint32_t section::get_aligned_raw_size(uint32_t file_alignment) const +{ + if(get_size_of_raw_data()) + return pe_utils::align_up(get_size_of_raw_data(), file_alignment); + else + return 0; +} + +//Sets size of raw section data +void section::set_size_of_raw_data(uint32_t size_of_raw_data) +{ + header_.SizeOfRawData = size_of_raw_data; +} + +//Sets pointer to section raw data +void section::set_pointer_to_raw_data(uint32_t pointer_to_raw_data) +{ + header_.PointerToRawData = pointer_to_raw_data; +} + +//Sets section characteristics +void section::set_characteristics(uint32_t characteristics) +{ + header_.Characteristics = characteristics; +} + +//Sets section virtual size +void section::set_virtual_size(uint32_t virtual_size) +{ + header_.Misc.VirtualSize = virtual_size; +} + +//Sets section virtual address +void section::set_virtual_address(uint32_t virtual_address) +{ + header_.VirtualAddress = virtual_address; +} + +//Section by file offset finder helper (4gb max) +section_by_raw_offset::section_by_raw_offset(uint32_t offset) + :offset_(offset) +{} + +bool section_by_raw_offset::operator()(const section& s) const +{ + return (s.get_pointer_to_raw_data() <= offset_) + && (s.get_pointer_to_raw_data() + s.get_size_of_raw_data() > offset_); +} + +section_ptr_finder::section_ptr_finder(const section& s) + :s_(s) +{} + +bool section_ptr_finder::operator()(const section& s) const +{ + return &s == &s_; +} +} diff --git a/tools/pe_bliss/pe_section.h b/tools/pe_bliss/pe_section.h new file mode 100644 index 0000000000..617ecebe26 --- /dev/null +++ b/tools/pe_bliss/pe_section.h @@ -0,0 +1,158 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <vector> +#include "pe_structures.h" + +namespace pe_bliss +{ +//Enumeration of section data types, used in functions below +enum section_data_type +{ + section_data_raw, + section_data_virtual +}; + +//Class representing image section +class section +{ +public: + //Default constructor + section(); + + //Sets the name of section (stripped to 8 characters) + void set_name(const std::string& name); + + //Returns the name of section + const std::string get_name() const; + + //Changes attributes of section + section& readable(bool readable); + section& writeable(bool writeable); + section& executable(bool executable); + section& shared(bool shared); + section& discardable(bool discardable); + + //Returns attributes of section + bool readable() const; + bool writeable() const; + bool executable() const; + bool shared() const; + bool discardable() const; + + //Returns true if section has no RAW data + bool empty() const; + + //Returns raw section data from file image + std::string& get_raw_data(); + //Returns raw section data from file image + const std::string& get_raw_data() const; + //Returns mapped virtual section data + const std::string& get_virtual_data(uint32_t section_alignment) const; + //Returns mapped virtual section data + std::string& get_virtual_data(uint32_t section_alignment); + +public: //Header getters + //Returns section virtual size + uint32_t get_virtual_size() const; + //Returns section virtual address (RVA) + uint32_t get_virtual_address() const; + //Returns size of section raw data + uint32_t get_size_of_raw_data() const; + //Returns pointer to raw section data in PE file + uint32_t get_pointer_to_raw_data() const; + //Returns section characteristics + uint32_t get_characteristics() const; + + //Returns raw image section header + const pe_win::image_section_header& get_raw_header() const; + +public: //Aligned sizes calculation + //Calculates aligned virtual section size + uint32_t get_aligned_virtual_size(uint32_t section_alignment) const; + //Calculates aligned raw section size + uint32_t get_aligned_raw_size(uint32_t file_alignment) const; + +public: //Setters + //Sets size of raw section data + void set_size_of_raw_data(uint32_t size_of_raw_data); + //Sets pointer to section raw data + void set_pointer_to_raw_data(uint32_t pointer_to_raw_data); + //Sets section characteristics + void set_characteristics(uint32_t characteristics); + //Sets raw section data from file image + void set_raw_data(const std::string& data); + +public: //Setters, be careful + //Sets section virtual size (doesn't set internal aligned virtual size, changes only header value) + //Better use pe_base::set_section_virtual_size + void set_virtual_size(uint32_t virtual_size); + //Sets section virtual address + void set_virtual_address(uint32_t virtual_address); + //Returns raw image section header + pe_win::image_section_header& get_raw_header(); + +private: + //Section header + pe_win::image_section_header header_; + + //Maps virtual section data + void map_virtual(uint32_t section_alignment) const; + + //Unmaps virtual section data + void unmap_virtual() const; + + //Set flag (attribute) of section + section& set_flag(uint32_t flag, bool setflag); + + //Old size of section (stored after mapping of virtual section memory) + mutable std::size_t old_size_; + + //Section raw/virtual data + mutable std::string raw_data_; +}; + +//Section by file offset finder helper (4gb max) +struct section_by_raw_offset +{ +public: + explicit section_by_raw_offset(uint32_t offset); + bool operator()(const section& s) const; + +private: + uint32_t offset_; +}; + +//Helper: finder of section* in sections list +struct section_ptr_finder +{ +public: + explicit section_ptr_finder(const section& s); + bool operator()(const section& s) const; + +private: + const section& s_; +}; + +typedef std::vector<section> section_list; +} diff --git a/tools/pe_bliss/pe_structures.h b/tools/pe_bliss/pe_structures.h new file mode 100644 index 0000000000..efc99103b2 --- /dev/null +++ b/tools/pe_bliss/pe_structures.h @@ -0,0 +1,1028 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <sstream> +#include "stdint_defs.h" +#if defined(_MSC_VER) or defined(__MINGW32__) +#define PE_BLISS_WINDOWS +#endif + +namespace pe_bliss +{ +//Enumeration of PE types +enum pe_type +{ + pe_type_32, + pe_type_64 +}; + +namespace pe_win +{ +const uint32_t image_numberof_directory_entries = 16; +const uint32_t image_nt_optional_hdr32_magic = 0x10b; +const uint32_t image_nt_optional_hdr64_magic = 0x20b; +const uint32_t image_resource_name_is_string = 0x80000000; +const uint32_t image_resource_data_is_directory = 0x80000000; + +const uint32_t image_dllcharacteristics_dynamic_base = 0x0040; // DLL can move. +const uint32_t image_dllcharacteristics_force_integrity = 0x0080; // Code Integrity Image +const uint32_t image_dllcharacteristics_nx_compat = 0x0100; // Image is NX compatible +const uint32_t image_dllcharacteristics_no_isolation = 0x0200; // Image understands isolation and doesn't want it +const uint32_t image_dllcharacteristics_no_seh = 0x0400; // Image does not use SEH. No SE handler may reside in this image +const uint32_t image_dllcharacteristics_no_bind = 0x0800; // Do not bind this image. +const uint32_t image_dllcharacteristics_wdm_driver = 0x2000; // Driver uses WDM model +const uint32_t image_dllcharacteristics_terminal_server_aware = 0x8000; + +const uint32_t image_sizeof_file_header = 20; + +const uint32_t image_file_relocs_stripped = 0x0001; // Relocation info stripped from file. +const uint32_t image_file_executable_image = 0x0002; // File is executable (i.e. no unresolved externel references). +const uint32_t image_file_line_nums_stripped = 0x0004; // Line nunbers stripped from file. +const uint32_t image_file_local_syms_stripped = 0x0008; // Local symbols stripped from file. +const uint32_t image_file_aggresive_ws_trim = 0x0010; // Agressively trim working set +const uint32_t image_file_large_address_aware = 0x0020; // App can handle >2gb addresses +const uint32_t image_file_bytes_reversed_lo = 0x0080; // Bytes of machine word are reversed. +const uint32_t image_file_32bit_machine = 0x0100; // 32 bit word machine. +const uint32_t image_file_debug_stripped = 0x0200; // Debugging info stripped from file in .DBG file +const uint32_t image_file_removable_run_from_swap = 0x0400; // If Image is on removable media, copy and run from the swap file. +const uint32_t image_file_net_run_from_swap = 0x0800; // If Image is on Net, copy and run from the swap file. +const uint32_t image_file_system = 0x1000; // System File. +const uint32_t image_file_dll = 0x2000; // File is a DLL. +const uint32_t image_file_up_system_only = 0x4000; // File should only be run on a UP machine +const uint32_t image_file_bytes_reversed_hi = 0x8000; // Bytes of machine word are reversed. + +const uint32_t image_scn_lnk_nreloc_ovfl = 0x01000000; // Section contains extended relocations. +const uint32_t image_scn_mem_discardable = 0x02000000; // Section can be discarded. +const uint32_t image_scn_mem_not_cached = 0x04000000; // Section is not cachable. +const uint32_t image_scn_mem_not_paged = 0x08000000; // Section is not pageable. +const uint32_t image_scn_mem_shared = 0x10000000; // Section is shareable. +const uint32_t image_scn_mem_execute = 0x20000000; // Section is executable. +const uint32_t image_scn_mem_read = 0x40000000; // Section is readable. +const uint32_t image_scn_mem_write = 0x80000000; // Section is writeable. + +const uint32_t image_scn_cnt_code = 0x00000020; // Section contains code. +const uint32_t image_scn_cnt_initialized_data = 0x00000040; // Section contains initialized data. +const uint32_t image_scn_cnt_uninitialized_data = 0x00000080; // Section contains uninitialized data. + +//Directory Entries +const uint32_t image_directory_entry_export = 0; // Export Directory +const uint32_t image_directory_entry_import = 1; // Import Directory +const uint32_t image_directory_entry_resource = 2; // Resource Directory +const uint32_t image_directory_entry_exception = 3; // Exception Directory +const uint32_t image_directory_entry_security = 4; // Security Directory +const uint32_t image_directory_entry_basereloc = 5; // Base Relocation Table +const uint32_t image_directory_entry_debug = 6; // Debug Directory +const uint32_t image_directory_entry_architecture = 7; // Architecture Specific Data +const uint32_t image_directory_entry_globalptr = 8; // RVA of GP +const uint32_t image_directory_entry_tls = 9; // TLS Directory +const uint32_t image_directory_entry_load_config = 10; // Load Configuration Directory +const uint32_t image_directory_entry_bound_import = 11; // Bound Import Directory in headers +const uint32_t image_directory_entry_iat = 12; // Import Address Table +const uint32_t image_directory_entry_delay_import = 13; // Delay Load Import Descriptors +const uint32_t image_directory_entry_com_descriptor = 14; // COM Runtime descriptor + +//Subsystem Values +const uint32_t image_subsystem_unknown = 0; // Unknown subsystem. +const uint32_t image_subsystem_native = 1; // Image doesn't require a subsystem. +const uint32_t image_subsystem_windows_gui = 2; // Image runs in the Windows GUI subsystem. +const uint32_t image_subsystem_windows_cui = 3; // Image runs in the Windows character subsystem. +const uint32_t image_subsystem_os2_cui = 5; // image runs in the OS/2 character subsystem. +const uint32_t image_subsystem_posix_cui = 7; // image runs in the Posix character subsystem. +const uint32_t image_subsystem_native_windows = 8; // image is a native Win9x driver. +const uint32_t image_subsystem_windows_ce_gui = 9; // Image runs in the Windows CE subsystem. +const uint32_t image_subsystem_efi_application = 10; // +const uint32_t image_subsystem_efi_boot_service_driver = 11; // +const uint32_t image_subsystem_efi_runtime_driver = 12; // +const uint32_t image_subsystem_efi_rom = 13; +const uint32_t image_subsystem_xbox = 14; +const uint32_t image_subsystem_windows_boot_application = 16; + +//Imports +const uint64_t image_ordinal_flag64 = 0x8000000000000000ull; +const uint32_t image_ordinal_flag32 = 0x80000000; + +//Based relocation types +const uint32_t image_rel_based_absolute = 0; +const uint32_t image_rel_based_high = 1; +const uint32_t image_rel_based_low = 2; +const uint32_t image_rel_based_highlow = 3; +const uint32_t image_rel_based_highadj = 4; +const uint32_t image_rel_based_mips_jmpaddr = 5; +const uint32_t image_rel_based_mips_jmpaddr16 = 9; +const uint32_t image_rel_based_ia64_imm64 = 9; +const uint32_t image_rel_based_dir64 = 10; + +//Exception directory +//The function has an exception handler that should be called when looking for functions that need to examine exceptions +const uint32_t unw_flag_ehandler = 0x01; +//The function has a termination handler that should be called when unwinding an exception +const uint32_t unw_flag_uhandler = 0x02; +//This unwind info structure is not the primary one for the procedure. +//Instead, the chained unwind info entry is the contents of a previous RUNTIME_FUNCTION entry. +//If this flag is set, then the UNW_FLAG_EHANDLER and UNW_FLAG_UHANDLER flags must be cleared. +//Also, the frame register and fixed-stack allocation fields must have the same values as in the primary unwind info +const uint32_t unw_flag_chaininfo = 0x04; + +//Debug +const uint32_t image_debug_misc_exename = 1; +const uint32_t image_debug_type_unknown = 0; +const uint32_t image_debug_type_coff = 1; +const uint32_t image_debug_type_codeview = 2; +const uint32_t image_debug_type_fpo = 3; +const uint32_t image_debug_type_misc = 4; +const uint32_t image_debug_type_exception = 5; +const uint32_t image_debug_type_fixup = 6; +const uint32_t image_debug_type_omap_to_src = 7; +const uint32_t image_debug_type_omap_from_src = 8; +const uint32_t image_debug_type_borland = 9; +const uint32_t image_debug_type_reserved10 = 10; +const uint32_t image_debug_type_clsid = 11; + + +//Storage classes +const uint32_t image_sym_class_end_of_function = static_cast<uint8_t>(-1); +const uint32_t image_sym_class_null = 0x0000; +const uint32_t image_sym_class_automatic = 0x0001; +const uint32_t image_sym_class_external = 0x0002; +const uint32_t image_sym_class_static = 0x0003; +const uint32_t image_sym_class_register = 0x0004; +const uint32_t image_sym_class_external_def = 0x0005; +const uint32_t image_sym_class_label = 0x0006; +const uint32_t image_sym_class_undefined_label = 0x0007; +const uint32_t image_sym_class_member_of_struct = 0x0008; +const uint32_t image_sym_class_argument = 0x0009; +const uint32_t image_sym_class_struct_tag = 0x000a; +const uint32_t image_sym_class_member_of_union = 0x000b; +const uint32_t image_sym_class_union_tag = 0x000c; +const uint32_t image_sym_class_type_definition = 0x000d; +const uint32_t image_sym_class_undefined_static = 0x000e; +const uint32_t image_sym_class_enum_tag = 0x000f; +const uint32_t image_sym_class_member_of_enum = 0x0010; +const uint32_t image_sym_class_register_param = 0x0011; +const uint32_t image_sym_class_bit_field = 0x0012; + +const uint32_t image_sym_class_far_external = 0x0044; + +const uint32_t image_sym_class_block = 0x0064; +const uint32_t image_sym_class_function = 0x0065; +const uint32_t image_sym_class_end_of_struct = 0x0066; +const uint32_t image_sym_class_file = 0x0067; + +const uint32_t image_sym_class_section = 0x0068; +const uint32_t image_sym_class_weak_external = 0x0069; + +const uint32_t image_sym_class_clr_token = 0x006b; + +//type packing constants +const uint32_t n_btmask = 0x000f; +const uint32_t n_tmask = 0x0030; +const uint32_t n_tmask1 = 0x00c0; +const uint32_t n_tmask2 = 0x00f0; +const uint32_t n_btshft = 4; +const uint32_t n_tshift = 2; + +//Type (derived) values. +const uint32_t image_sym_dtype_null = 0; // no derived type. +const uint32_t image_sym_dtype_pointer = 1; // pointer. +const uint32_t image_sym_dtype_function = 2; // function. +const uint32_t image_sym_dtype_array = 3; // array. + +// Is x a function? +//TODO +#ifndef ISFCN +#define ISFCN(x) (((x) & n_tmask) == (image_sym_dtype_function << n_btshft)) +#endif + +//Version info +const uint32_t vs_ffi_fileflagsmask = 0x0000003FL; + +const uint32_t vs_ffi_signature = 0xFEEF04BDL; +const uint32_t vs_ffi_strucversion = 0x00010000L; + +/* ----- VS_VERSION.dwFileFlags ----- */ +const uint32_t vs_ff_debug = 0x00000001L; +const uint32_t vs_ff_prerelease = 0x00000002L; +const uint32_t vs_ff_patched = 0x00000004L; +const uint32_t vs_ff_privatebuild = 0x00000008L; +const uint32_t vs_ff_infoinferred = 0x00000010L; +const uint32_t vs_ff_specialbuild = 0x00000020L; + +/* ----- VS_VERSION.dwFileOS ----- */ +const uint32_t vos_unknown = 0x00000000L; +const uint32_t vos_dos = 0x00010000L; +const uint32_t vos_os216 = 0x00020000L; +const uint32_t vos_os232 = 0x00030000L; +const uint32_t vos_nt = 0x00040000L; +const uint32_t vos_wince = 0x00050000L; + +const uint32_t vos__base = 0x00000000L; +const uint32_t vos__windows16 = 0x00000001L; +const uint32_t vos__pm16 = 0x00000002L; +const uint32_t vos__pm32 = 0x00000003L; +const uint32_t vos__windows32 = 0x00000004L; + +const uint32_t vos_dos_windows16 = 0x00010001L; +const uint32_t vos_dos_windows32 = 0x00010004L; +const uint32_t vos_os216_pm16 = 0x00020002L; +const uint32_t vos_os232_pm32 = 0x00030003L; +const uint32_t vos_nt_windows32 = 0x00040004L; + +/* ----- VS_VERSION.dwFileType ----- */ +const uint32_t vft_unknown = 0x00000000L; +const uint32_t vft_app = 0x00000001L; +const uint32_t vft_dll = 0x00000002L; +const uint32_t vft_drv = 0x00000003L; +const uint32_t vft_font = 0x00000004L; +const uint32_t vft_vxd = 0x00000005L; +const uint32_t vft_static_lib = 0x00000007L; + +const uint32_t message_resource_unicode = 0x0001; + +#pragma pack(push, 1) + +//Windows GUID structure +struct guid +{ + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +}; + +//DOS .EXE header +struct image_dos_header +{ + uint16_t e_magic; // Magic number + uint16_t e_cblp; // Bytes on last page of file + uint16_t e_cp; // Pages in file + uint16_t e_crlc; // Relocations + uint16_t e_cparhdr; // Size of header in paragraphs + uint16_t e_minalloc; // Minimum extra paragraphs needed + uint16_t e_maxalloc; // Maximum extra paragraphs needed + uint16_t e_ss; // Initial (relative) SS value + uint16_t e_sp; // Initial SP value + uint16_t e_csum; // Checksum + uint16_t e_ip; // Initial IP value + uint16_t e_cs; // Initial (relative) CS value + uint16_t e_lfarlc; // File address of relocation table + uint16_t e_ovno; // Overlay number + uint16_t e_res[4]; // Reserved words + uint16_t e_oemid; // OEM identifier (for e_oeminfo) + uint16_t e_oeminfo; // OEM information; e_oemid specific + uint16_t e_res2[10]; // Reserved words + int32_t e_lfanew; // File address of new exe header +}; + +//Directory format +struct image_data_directory +{ + uint32_t VirtualAddress; + uint32_t Size; +}; + +//Optional header format +struct image_optional_header32 +{ + //Standard fields + uint16_t Magic; + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint32_t BaseOfData; + + //NT additional fields + uint32_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint32_t SizeOfStackReserve; + uint32_t SizeOfStackCommit; + uint32_t SizeOfHeapReserve; + uint32_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + image_data_directory DataDirectory[image_numberof_directory_entries]; +}; + +struct image_optional_header64 +{ + uint16_t Magic; + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint64_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint64_t SizeOfStackReserve; + uint64_t SizeOfStackCommit; + uint64_t SizeOfHeapReserve; + uint64_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + image_data_directory DataDirectory[image_numberof_directory_entries]; +}; + +struct image_file_header +{ + uint16_t Machine; + uint16_t NumberOfSections; + uint32_t TimeDateStamp; + uint32_t PointerToSymbolTable; + uint32_t NumberOfSymbols; + uint16_t SizeOfOptionalHeader; + uint16_t Characteristics; +}; + +struct image_nt_headers64 +{ + uint32_t Signature; + image_file_header FileHeader; + image_optional_header64 OptionalHeader; +}; + +struct image_nt_headers32 +{ + uint32_t Signature; + image_file_header FileHeader; + image_optional_header32 OptionalHeader; +}; + +//Section header format +struct image_section_header +{ + uint8_t Name[8]; + union + { + uint32_t PhysicalAddress; + uint32_t VirtualSize; + } Misc; + + uint32_t VirtualAddress; + uint32_t SizeOfRawData; + uint32_t PointerToRawData; + uint32_t PointerToRelocations; + uint32_t PointerToLinenumbers; + uint16_t NumberOfRelocations; + uint16_t NumberOfLinenumbers; + uint32_t Characteristics; +}; + + +/// RESOURCES /// +struct image_resource_directory +{ + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint16_t NumberOfNamedEntries; + uint16_t NumberOfIdEntries; + // IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[]; +}; + +struct vs_fixedfileinfo +{ + uint32_t dwSignature; /* e.g. 0xfeef04bd */ + uint32_t dwStrucVersion; /* e.g. 0x00000042 = "0.42" */ + uint32_t dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */ + uint32_t dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */ + uint32_t dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */ + uint32_t dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */ + uint32_t dwFileFlagsMask; /* = 0x3F for version "0.42" */ + uint32_t dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */ + uint32_t dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */ + uint32_t dwFileType; /* e.g. VFT_DRIVER */ + uint32_t dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */ + uint32_t dwFileDateMS; /* e.g. 0 */ + uint32_t dwFileDateLS; /* e.g. 0 */ +}; + +struct bitmapinfoheader +{ + uint32_t biSize; + int32_t biWidth; + int32_t biHeight; + uint16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + int32_t biXPelsPerMeter; + int32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; +}; + +struct message_resource_entry +{ + uint16_t Length; + uint16_t Flags; + uint8_t Text[1]; +}; + +struct message_resource_block +{ + uint32_t LowId; + uint32_t HighId; + uint32_t OffsetToEntries; +}; + +struct message_resource_data +{ + uint32_t NumberOfBlocks; + message_resource_block Blocks[1]; +}; + +struct image_resource_directory_entry +{ + union + { + struct + { + uint32_t NameOffset:31; + uint32_t NameIsString:1; + }; + uint32_t Name; + uint16_t Id; + }; + + union + { + uint32_t OffsetToData; + struct + { + uint32_t OffsetToDirectory:31; + uint32_t DataIsDirectory:1; + }; + }; +}; + +struct image_resource_data_entry +{ + uint32_t OffsetToData; + uint32_t Size; + uint32_t CodePage; + uint32_t Reserved; +}; + +#pragma pack(push, 2) +struct bitmapfileheader +{ + uint16_t bfType; + uint32_t bfSize; + uint16_t bfReserved1; + uint16_t bfReserved2; + uint32_t bfOffBits; +}; +#pragma pack(pop) + + + +//Structure representing ICON file header +struct ico_header +{ + uint16_t Reserved; + uint16_t Type; //1 + uint16_t Count; //Count of icons included in icon group +}; + +//Structure that is stored in icon group directory in PE resources +struct icon_group +{ + uint8_t Width; + uint8_t Height; + uint8_t ColorCount; + uint8_t Reserved; + uint16_t Planes; + uint16_t BitCount; + uint32_t SizeInBytes; + uint16_t Number; //Represents resource ID in PE icon list +}; + +//Structure representing ICON directory entry inside ICON file +struct icondirentry +{ + uint8_t Width; + uint8_t Height; + uint8_t ColorCount; + uint8_t Reserved; + uint16_t Planes; + uint16_t BitCount; + uint32_t SizeInBytes; + uint32_t ImageOffset; //Offset from start of header to the image +}; + +//Structure representing CURSOR file header +struct cursor_header +{ + uint16_t Reserved; + uint16_t Type; //2 + uint16_t Count; //Count of cursors included in cursor group +}; + +struct cursor_group +{ + uint16_t Width; + uint16_t Height; //Divide by 2 to get the actual height. + uint16_t Planes; + uint16_t BitCount; + uint32_t SizeInBytes; + uint16_t Number; //Represents resource ID in PE icon list +}; + +//Structure representing CURSOR directory entry inside CURSOR file +struct cursordirentry +{ + uint8_t Width; //Set to CURSOR_GROUP::Height/2. + uint8_t Height; + uint8_t ColorCount; + uint8_t Reserved; + uint16_t HotspotX; + uint16_t HotspotY; + uint32_t SizeInBytes; + uint32_t ImageOffset; //Offset from start of header to the image +}; + +//Structure representing BLOCK in version info resource +struct version_info_block //(always aligned on 32-bit (DWORD) boundary) +{ + uint16_t Length; //Length of this block (doesn't include padding) + uint16_t ValueLength; //Value length (if any) + uint16_t Type; //Value type (0 = binary, 1 = text) + uint16_t Key[1]; //Value name (block key) (always NULL terminated) + + ////////// + //WORD padding1[]; //Padding, if any (ALIGNMENT) + //xxxxx Value[]; //Value data, if any (*ALIGNED*) + //WORD padding2[]; //Padding, if any (ALIGNMENT) + //xxxxx Child[]; //Child block(s), if any (*ALIGNED*) + ////////// +}; + + +/// IMPORTS /// +#pragma pack(push, 8) +struct image_thunk_data64 +{ + union + { + uint64_t ForwarderString; // PBYTE + uint64_t Function; // PDWORD + uint64_t Ordinal; + uint64_t AddressOfData; // PIMAGE_IMPORT_BY_NAME + } u1; +}; +#pragma pack(pop) + +struct image_thunk_data32 +{ + union + { + uint32_t ForwarderString; // PBYTE + uint32_t Function; // PDWORD + uint32_t Ordinal; + uint32_t AddressOfData; // PIMAGE_IMPORT_BY_NAME + } u1; +}; + +struct image_import_descriptor +{ + union + { + uint32_t Characteristics; // 0 for terminating null import descriptor + uint32_t OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) + }; + + uint32_t TimeDateStamp; // 0 if not bound, + // -1 if bound, and real date\time stamp + // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) + // O.W. date/time stamp of DLL bound to (Old BIND) + + uint32_t ForwarderChain; // -1 if no forwarders + uint32_t Name; + uint32_t FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) +}; + + +/// TLS /// +struct image_tls_directory64 +{ + uint64_t StartAddressOfRawData; + uint64_t EndAddressOfRawData; + uint64_t AddressOfIndex; // PDWORD + uint64_t AddressOfCallBacks; // PIMAGE_TLS_CALLBACK *; + uint32_t SizeOfZeroFill; + uint32_t Characteristics; +}; + +struct image_tls_directory32 +{ + uint32_t StartAddressOfRawData; + uint32_t EndAddressOfRawData; + uint32_t AddressOfIndex; // PDWORD + uint32_t AddressOfCallBacks; // PIMAGE_TLS_CALLBACK * + uint32_t SizeOfZeroFill; + uint32_t Characteristics; +}; + + +/// Export Format /// +struct image_export_directory +{ + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t Name; + uint32_t Base; + uint32_t NumberOfFunctions; + uint32_t NumberOfNames; + uint32_t AddressOfFunctions; // RVA from base of image + uint32_t AddressOfNames; // RVA from base of image + uint32_t AddressOfNameOrdinals; // RVA from base of image +}; + + +/// Based relocation format /// +struct image_base_relocation +{ + uint32_t VirtualAddress; + uint32_t SizeOfBlock; + // uint16_t TypeOffset[1]; +}; + + +/// New format import descriptors pointed to by DataDirectory[ IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ] /// +struct image_bound_import_descriptor +{ + uint32_t TimeDateStamp; + uint16_t OffsetModuleName; + uint16_t NumberOfModuleForwarderRefs; + // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows +}; + +struct image_bound_forwarder_ref +{ + uint32_t TimeDateStamp; + uint16_t OffsetModuleName; + uint16_t Reserved; +}; + + +/// Exception directory /// +struct image_runtime_function_entry +{ + uint32_t BeginAddress; + uint32_t EndAddress; + uint32_t UnwindInfoAddress; +}; + +enum unwind_op_codes +{ + uwop_push_nonvol = 0, /* info == register number */ + uwop_alloc_large, /* no info, alloc size in next 2 slots */ + uwop_alloc_small, /* info == size of allocation / 8 - 1 */ + uwop_set_fpreg, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */ + uwop_save_nonvol, /* info == register number, offset in next slot */ + uwop_save_nonvol_far, /* info == register number, offset in next 2 slots */ + uwop_save_xmm128, /* info == XMM reg number, offset in next slot */ + uwop_save_xmm128_far, /* info == XMM reg number, offset in next 2 slots */ + uwop_push_machframe /* info == 0: no error-code, 1: error-code */ +}; + +union unwind_code +{ + struct s + { + uint8_t CodeOffset; + uint8_t UnwindOp : 4; + uint8_t OpInfo : 4; + }; + + uint16_t FrameOffset; +}; + +struct unwind_info +{ + uint8_t Version : 3; + uint8_t Flags : 5; + uint8_t SizeOfProlog; + uint8_t CountOfCodes; + uint8_t FrameRegister : 4; + uint8_t FrameOffset : 4; + unwind_code UnwindCode[1]; + /* unwind_code MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1]; + * union { + * OPTIONAL ULONG ExceptionHandler; + * OPTIONAL ULONG FunctionEntry; + * }; + * OPTIONAL ULONG ExceptionData[]; */ +}; + + + +/// Debug /// +struct image_debug_misc +{ + uint32_t DataType; // type of misc data, see defines + uint32_t Length; // total length of record, rounded to four + // byte multiple. + uint8_t Unicode; // TRUE if data is unicode string + uint8_t Reserved[3]; + uint8_t Data[1]; // Actual data +}; + +struct image_coff_symbols_header +{ + uint32_t NumberOfSymbols; + uint32_t LvaToFirstSymbol; + uint32_t NumberOfLinenumbers; + uint32_t LvaToFirstLinenumber; + uint32_t RvaToFirstByteOfCode; + uint32_t RvaToLastByteOfCode; + uint32_t RvaToFirstByteOfData; + uint32_t RvaToLastByteOfData; +}; + +struct image_debug_directory +{ + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t Type; + uint32_t SizeOfData; + uint32_t AddressOfRawData; + uint32_t PointerToRawData; +}; + + +#pragma pack(push, 2) +struct image_symbol +{ + union + { + uint8_t ShortName[8]; + struct + { + uint32_t Short; // if 0, use LongName + uint32_t Long; // offset into string table + } Name; + uint32_t LongName[2]; // PBYTE [2] + } N; + uint32_t Value; + int16_t SectionNumber; + uint16_t Type; + uint8_t StorageClass; + uint8_t NumberOfAuxSymbols; +}; +#pragma pack(pop) + +//CodeView Debug OMF signature. The signature at the end of the file is +//a negative offset from the end of the file to another signature. At +//the negative offset (base address) is another signature whose filepos +//field points to the first OMFDirHeader in a chain of directories. +//The NB05 signature is used by the link utility to indicated a completely +//unpacked file. The NB06 signature is used by ilink to indicate that the +//executable has had CodeView information from an incremental link appended +//to the executable. The NB08 signature is used by cvpack to indicate that +//the CodeView Debug OMF has been packed. CodeView will only process +//executables with the NB08 signature. +struct OMFSignature +{ + char Signature[4]; // "NBxx" + uint32_t filepos; // offset in file +}; + +struct CV_INFO_PDB20 +{ + OMFSignature CvHeader; + uint32_t Signature; + uint32_t Age; + uint8_t PdbFileName[1]; +}; + +struct CV_INFO_PDB70 +{ + uint32_t CvSignature; + guid Signature; + uint32_t Age; + uint8_t PdbFileName[1]; +}; + +// directory information structure +// This structure contains the information describing the directory. +// It is pointed to by the signature at the base address or the directory +// link field of a preceeding directory. The directory entries immediately +// follow this structure. +struct OMFDirHeader +{ + uint16_t cbDirHeader; // length of this structure + uint16_t cbDirEntry; // number of bytes in each directory entry + uint32_t cDir; // number of directorie entries + int32_t lfoNextDir; // offset from base of next directory + uint32_t flags; // status flags +}; + +// directory structure +// The data in this structure is used to reference the data for each +// subsection of the CodeView Debug OMF information. Tables that are +// not associated with a specific module will have a module index of +// oxffff. These tables are the global types table, the global symbol +// table, the global public table and the library table. +struct OMFDirEntry +{ + uint16_t SubSection; // subsection type (sst...) + uint16_t iMod; // module index + int32_t lfo; // large file offset of subsection + uint32_t cb; // number of bytes in subsection +}; + + +/// CLR 2.0 header structure /// +struct image_cor20_header +{ + //Header versioning + uint32_t cb; + uint16_t MajorRuntimeVersion; + uint16_t MinorRuntimeVersion; + + // Symbol table and startup information + image_data_directory MetaData; + uint32_t Flags; + + // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is not set, EntryPointToken represents a managed entrypoint. + // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is set, EntryPointRVA represents an RVA to a native entrypoint. + union + { + uint32_t EntryPointToken; + uint32_t EntryPointRVA; + }; + + // Binding information + image_data_directory Resources; + image_data_directory StrongNameSignature; + + // Regular fixup and binding information + image_data_directory CodeManagerTable; + image_data_directory VTableFixups; + image_data_directory ExportAddressTableJumps; + + // Precompiled image info (internal use only - set to zero) + image_data_directory ManagedNativeHeader; +}; + +enum replaces_cor_hdr_numeric_defines +{ + // COM+ Header entry point flags. + comimage_flags_ilonly =0x00000001, + comimage_flags_32bitrequired =0x00000002, + comimage_flags_il_library =0x00000004, + comimage_flags_strongnamesigned =0x00000008, + comimage_flags_native_entrypoint =0x00000010, + comimage_flags_trackdebugdata =0x00010000, + + // Version flags for image. + cor_version_major_v2 =2, + cor_version_major =cor_version_major_v2, + cor_version_minor =0, + cor_deleted_name_length =8, + cor_vtablegap_name_length =8, + + // Maximum size of a NativeType descriptor. + native_type_max_cb =1, + cor_ilmethod_sect_small_max_datasize=0xff, + + // #defines for the MIH FLAGS + image_cor_mih_methodrva =0x01, + image_cor_mih_ehrva =0x02, + image_cor_mih_basicblock =0x08, + + // V-table constants + cor_vtable_32bit =0x01, // V-table slots are 32-bits in size. + cor_vtable_64bit =0x02, // V-table slots are 64-bits in size. + cor_vtable_from_unmanaged =0x04, // If set, transition from unmanaged. + cor_vtable_from_unmanaged_retain_appdomain =0x08, // If set, transition from unmanaged with keeping the current appdomain. + cor_vtable_call_most_derived =0x10, // Call most derived method described by + + // EATJ constants + image_cor_eatj_thunk_size =32, // Size of a jump thunk reserved range. + + // Max name lengths + //@todo: Change to unlimited name lengths. + max_class_name =1024, + max_package_name =1024 +}; + +/// Load Configuration Directory Entry /// +struct image_load_config_directory32 +{ + uint32_t Size; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t GlobalFlagsClear; + uint32_t GlobalFlagsSet; + uint32_t CriticalSectionDefaultTimeout; + uint32_t DeCommitFreeBlockThreshold; + uint32_t DeCommitTotalFreeThreshold; + uint32_t LockPrefixTable; // VA + uint32_t MaximumAllocationSize; + uint32_t VirtualMemoryThreshold; + uint32_t ProcessHeapFlags; + uint32_t ProcessAffinityMask; + uint16_t CSDVersion; + uint16_t Reserved1; + uint32_t EditList; // VA + uint32_t SecurityCookie; // VA + uint32_t SEHandlerTable; // VA + uint32_t SEHandlerCount; +}; + +struct image_load_config_directory64 +{ + uint32_t Size; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t GlobalFlagsClear; + uint32_t GlobalFlagsSet; + uint32_t CriticalSectionDefaultTimeout; + uint64_t DeCommitFreeBlockThreshold; + uint64_t DeCommitTotalFreeThreshold; + uint64_t LockPrefixTable; // VA + uint64_t MaximumAllocationSize; + uint64_t VirtualMemoryThreshold; + uint64_t ProcessAffinityMask; + uint32_t ProcessHeapFlags; + uint16_t CSDVersion; + uint16_t Reserved1; + uint64_t EditList; // VA + uint64_t SecurityCookie; // VA + uint64_t SEHandlerTable; // VA + uint64_t SEHandlerCount; +}; + +#pragma pack(pop) +} //namespace pe_win + +#ifdef PE_BLISS_WINDOWS +typedef wchar_t unicode16_t; +typedef std::basic_string<unicode16_t> u16string; +#else +//Instead of wchar_t for windows +typedef unsigned short unicode16_t; +typedef std::basic_string<unicode16_t> u16string; +#endif + +} //namespace pe_bliss diff --git a/tools/pe_bliss/pe_tls.cpp b/tools/pe_bliss/pe_tls.cpp new file mode 100644 index 0000000000..5ec68e3f10 --- /dev/null +++ b/tools/pe_bliss/pe_tls.cpp @@ -0,0 +1,396 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_tls.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//TLS +//Default constructor +tls_info::tls_info() + :start_rva_(0), end_rva_(0), index_rva_(0), callbacks_rva_(0), + size_of_zero_fill_(0), characteristics_(0) +{} + +//Returns start RVA of TLS raw data +uint32_t tls_info::get_raw_data_start_rva() const +{ + return start_rva_; +} + +//Returns end RVA of TLS raw data +uint32_t tls_info::get_raw_data_end_rva() const +{ + return end_rva_; +} + +//Returns TLS index RVA +uint32_t tls_info::get_index_rva() const +{ + return index_rva_; +} + +//Returns TLS callbacks RVA +uint32_t tls_info::get_callbacks_rva() const +{ + return callbacks_rva_; +} + +//Returns size of zero fill +uint32_t tls_info::get_size_of_zero_fill() const +{ + return size_of_zero_fill_; +} + +//Returns characteristics +uint32_t tls_info::get_characteristics() const +{ + return characteristics_; +} + +//Returns raw TLS data +const std::string& tls_info::get_raw_data() const +{ + return raw_data_; +} + +//Returns TLS callbacks addresses +const tls_info::tls_callback_list& tls_info::get_tls_callbacks() const +{ + return callbacks_; +} + +//Returns TLS callbacks addresses +tls_info::tls_callback_list& tls_info::get_tls_callbacks() +{ + return callbacks_; +} + +//Adds TLS callback +void tls_info::add_tls_callback(uint32_t rva) +{ + callbacks_.push_back(rva); +} + +//Clears TLS callbacks list +void tls_info::clear_tls_callbacks() +{ + callbacks_.clear(); +} + +//Recalculates end address of raw TLS data +void tls_info::recalc_raw_data_end_rva() +{ + end_rva_ = static_cast<uint32_t>(start_rva_ + raw_data_.length()); +} + +//Sets start RVA of TLS raw data +void tls_info::set_raw_data_start_rva(uint32_t rva) +{ + start_rva_ = rva; +} + +//Sets end RVA of TLS raw data +void tls_info::set_raw_data_end_rva(uint32_t rva) +{ + end_rva_ = rva; +} + +//Sets TLS index RVA +void tls_info::set_index_rva(uint32_t rva) +{ + index_rva_ = rva; +} + +//Sets TLS callbacks RVA +void tls_info::set_callbacks_rva(uint32_t rva) +{ + callbacks_rva_ = rva; +} + +//Sets size of zero fill +void tls_info::set_size_of_zero_fill(uint32_t size) +{ + size_of_zero_fill_ = size; +} + +//Sets characteristics +void tls_info::set_characteristics(uint32_t characteristics) +{ + characteristics_ = characteristics; +} + +//Sets raw TLS data +void tls_info::set_raw_data(const std::string& data) +{ + raw_data_ = data; +} + +//If image does not have TLS, throws an exception +const tls_info get_tls_info(const pe_base& pe) +{ + return pe.get_pe_type() == pe_type_32 + ? get_tls_info_base<pe_types_class_32>(pe) + : get_tls_info_base<pe_types_class_64>(pe); +} + +//TLS Rebuilder +const image_directory rebuild_tls(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start, bool write_tls_callbacks, bool write_tls_data, tls_data_expand_type expand, bool save_to_pe_header, bool auto_strip_last_section) +{ + return pe.get_pe_type() == pe_type_32 + ? rebuild_tls_base<pe_types_class_32>(pe, info, tls_section, offset_from_section_start, write_tls_callbacks, write_tls_data, expand, save_to_pe_header, auto_strip_last_section) + : rebuild_tls_base<pe_types_class_64>(pe, info, tls_section, offset_from_section_start, write_tls_callbacks, write_tls_data, expand, save_to_pe_header, auto_strip_last_section); +} + +//Get TLS info +//If image does not have TLS, throws an exception +template<typename PEClassType> +const tls_info get_tls_info_base(const pe_base& pe) +{ + tls_info ret; + + //If there's no TLS directory, throw an exception + if(!pe.has_tls()) + throw pe_exception("Image does not have TLS directory", pe_exception::directory_does_not_exist); + + //Get TLS directory data + typename PEClassType::TLSStruct tls_directory_data = pe.section_data_from_rva<typename PEClassType::TLSStruct>(pe.get_directory_rva(image_directory_entry_tls), section_data_virtual, true); + + //Check data addresses + if(tls_directory_data.EndAddressOfRawData == tls_directory_data.StartAddressOfRawData) + { + try + { + pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.EndAddressOfRawData)); + } + catch(const pe_exception&) + { + //Fix addressess on incorrect conversion + tls_directory_data.EndAddressOfRawData = tls_directory_data.StartAddressOfRawData = 0; + } + } + + if(tls_directory_data.StartAddressOfRawData && + pe.section_data_length_from_va(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), + static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), section_data_virtual, true) + < (tls_directory_data.EndAddressOfRawData - tls_directory_data.StartAddressOfRawData)) + throw pe_exception("Incorrect TLS directory", pe_exception::incorrect_tls_directory); + + //Fill TLS info + //VAs are not checked + ret.set_raw_data_start_rva(tls_directory_data.StartAddressOfRawData ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData)) : 0); + ret.set_raw_data_end_rva(tls_directory_data.EndAddressOfRawData ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.EndAddressOfRawData)) : 0); + ret.set_index_rva(tls_directory_data.AddressOfIndex ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfIndex)) : 0); + ret.set_callbacks_rva(tls_directory_data.AddressOfCallBacks ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfCallBacks)) : 0); + ret.set_size_of_zero_fill(tls_directory_data.SizeOfZeroFill); + ret.set_characteristics(tls_directory_data.Characteristics); + + if(tls_directory_data.StartAddressOfRawData && tls_directory_data.StartAddressOfRawData != tls_directory_data.EndAddressOfRawData) + { + //Read and save TLS RAW data + ret.set_raw_data(std::string( + pe.section_data_from_va(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), section_data_virtual, true), + static_cast<uint32_t>(tls_directory_data.EndAddressOfRawData - tls_directory_data.StartAddressOfRawData))); + } + + //If file has TLS callbacks + if(ret.get_callbacks_rva()) + { + //Read callbacks VAs + uint32_t current_tls_callback = 0; + + while(true) + { + //Read TLS callback VA + typename PEClassType::BaseSize va = pe.section_data_from_va<typename PEClassType::BaseSize>(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfCallBacks + current_tls_callback), section_data_virtual, true); + if(va == 0) + break; + + //Save it + ret.add_tls_callback(pe.va_to_rva(va, false)); + + //Move to next callback VA + current_tls_callback += sizeof(va); + } + } + + return ret; +} + +//Rebuilder of TLS structures +//If write_tls_callbacks = true, TLS callbacks VAs will be written to their place +//If write_tls_data = true, TLS data will be written to its place +//If you have chosen to rewrite raw data, only (EndAddressOfRawData - StartAddressOfRawData) bytes will be written, not the full length of string +//representing raw data content +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +//Note/TODO: TLS Callbacks array is not DWORD-aligned (seems to work on WinXP - Win7) +template<typename PEClassType> +const image_directory rebuild_tls_base(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start, bool write_tls_callbacks, bool write_tls_data, tls_data_expand_type expand, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that tls_section is attached to this PE image + if(!pe.section_attached(tls_section)) + throw pe_exception("TLS section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t tls_data_pos = pe_utils::align_up(offset_from_section_start, sizeof(typename PEClassType::BaseSize)); + uint32_t needed_size = sizeof(typename PEClassType::TLSStruct); //Calculate needed size for TLS table + + //Check if tls_section is last one. If it's not, check if there's enough place for TLS data + if(&tls_section != &*(pe.get_image_sections().end() - 1) && + (tls_section.empty() || pe_utils::align_up(tls_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + tls_data_pos)) + throw pe_exception("Insufficient space for TLS directory", pe_exception::insufficient_space); + + //Check raw data positions + if(info.get_raw_data_end_rva() < info.get_raw_data_start_rva() || info.get_index_rva() == 0) + throw pe_exception("Incorrect TLS directory", pe_exception::incorrect_tls_directory); + + std::string& raw_data = tls_section.get_raw_data(); + + //This will be done only if tls_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + tls_data_pos) + raw_data.resize(needed_size + tls_data_pos); //Expand section raw data + + //Create and fill TLS structure + typename PEClassType::TLSStruct tls_struct = {0}; + + typename PEClassType::BaseSize va; + if(info.get_raw_data_start_rva()) + { + pe.rva_to_va(info.get_raw_data_start_rva(), va); + tls_struct.StartAddressOfRawData = va; + tls_struct.SizeOfZeroFill = info.get_size_of_zero_fill(); + } + + if(info.get_raw_data_end_rva()) + { + pe.rva_to_va(info.get_raw_data_end_rva(), va); + tls_struct.EndAddressOfRawData = va; + } + + pe.rva_to_va(info.get_index_rva(), va); + tls_struct.AddressOfIndex = va; + + if(info.get_callbacks_rva()) + { + pe.rva_to_va(info.get_callbacks_rva(), va); + tls_struct.AddressOfCallBacks = va; + } + + tls_struct.Characteristics = info.get_characteristics(); + + //Save TLS structure + memcpy(&raw_data[tls_data_pos], &tls_struct, sizeof(tls_struct)); + + //If we are asked to rewrite TLS raw data + if(write_tls_data && info.get_raw_data_start_rva() && info.get_raw_data_start_rva() != info.get_raw_data_end_rva()) + { + try + { + //Check if we're going to write TLS raw data to an existing section (not to PE headers) + section& raw_data_section = pe.section_from_rva(info.get_raw_data_start_rva()); + pe.expand_section(raw_data_section, info.get_raw_data_start_rva(), info.get_raw_data_end_rva() - info.get_raw_data_start_rva(), expand == tls_data_expand_raw ? pe_base::expand_section_raw : pe_base::expand_section_virtual); + } + catch(const pe_exception&) + { + //If no section is presented by StartAddressOfRawData, just go to next step + } + + unsigned long write_raw_data_size = info.get_raw_data_end_rva() - info.get_raw_data_start_rva(); + unsigned long available_raw_length = 0; + + //Check if there's enough RAW space to write raw TLS data... + if((available_raw_length = pe.section_data_length_from_rva(info.get_raw_data_start_rva(), info.get_raw_data_start_rva(), section_data_raw, true)) + < info.get_raw_data_end_rva() - info.get_raw_data_start_rva()) + { + //Check if there's enough virtual space for it... + if(pe.section_data_length_from_rva(info.get_raw_data_start_rva(), info.get_raw_data_start_rva(), section_data_virtual, true) + < info.get_raw_data_end_rva() - info.get_raw_data_start_rva()) + throw pe_exception("Insufficient space for TLS raw data", pe_exception::insufficient_space); + else + write_raw_data_size = available_raw_length; //We'll write just a part of full raw data + } + + //Write raw TLS data, if any + if(write_raw_data_size != 0) + memcpy(pe.section_data_from_rva(info.get_raw_data_start_rva(), true), info.get_raw_data().data(), write_raw_data_size); + } + + //If we are asked to rewrite TLS callbacks addresses + if(write_tls_callbacks && info.get_callbacks_rva()) + { + unsigned long needed_callback_size = static_cast<unsigned long>((info.get_tls_callbacks().size() + 1 /* last null element */) * sizeof(typename PEClassType::BaseSize)); + + try + { + //Check if we're going to write TLS callbacks VAs to an existing section (not to PE headers) + section& raw_data_section = pe.section_from_rva(info.get_callbacks_rva()); + pe.expand_section(raw_data_section, info.get_callbacks_rva(), needed_callback_size, pe_base::expand_section_raw); + } + catch(const pe_exception&) + { + //If no section is presented by RVA of callbacks, just go to next step + } + + //Check if there's enough space to write callbacks TLS data... + if(pe.section_data_length_from_rva(info.get_callbacks_rva(), info.get_callbacks_rva(), section_data_raw, true) + < needed_callback_size - sizeof(typename PEClassType::BaseSize) /* last zero element can be virtual only */) + throw pe_exception("Insufficient space for TLS callbacks data", pe_exception::insufficient_space); + + if(pe.section_data_length_from_rva(info.get_callbacks_rva(), info.get_callbacks_rva(), section_data_virtual, true) + < needed_callback_size /* check here full virtual data length available */) + throw pe_exception("Insufficient space for TLS callbacks data", pe_exception::insufficient_space); + + std::vector<typename PEClassType::BaseSize> callbacks_virtual_addresses; + callbacks_virtual_addresses.reserve(info.get_tls_callbacks().size() + 1 /* last null element */); + + //Convert TLS RVAs to VAs + for(tls_info::tls_callback_list::const_iterator it = info.get_tls_callbacks().begin(); it != info.get_tls_callbacks().end(); ++it) + { + typename PEClassType::BaseSize cb_va = 0; + pe.rva_to_va(*it, cb_va); + callbacks_virtual_addresses.push_back(cb_va); + } + + //Ending null element + callbacks_virtual_addresses.push_back(0); + + //Write callbacks TLS data + memcpy(pe.section_data_from_rva(info.get_callbacks_rva(), true), &callbacks_virtual_addresses[0], needed_callback_size); + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(tls_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(tls_section, tls_data_pos), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_tls, ret.get_rva()); + pe.set_directory_size(image_directory_entry_tls, ret.get_size()); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_tls.h b/tools/pe_bliss/pe_tls.h new file mode 100644 index 0000000000..316e208147 --- /dev/null +++ b/tools/pe_bliss/pe_tls.h @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <memory> +#include <istream> +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing TLS info +//We use "DWORD" type to represent RVAs, because RVA is +//always 32bit even in PE+ +class tls_info +{ +public: + typedef std::vector<uint32_t> tls_callback_list; + +public: + //Default constructor + tls_info(); + + //Returns start RVA of TLS raw data + uint32_t get_raw_data_start_rva() const; + //Returns end RVA of TLS raw data + uint32_t get_raw_data_end_rva() const; + //Returns TLS index RVA + uint32_t get_index_rva() const; + //Returns TLS callbacks RVA + uint32_t get_callbacks_rva() const; + //Returns size of zero fill + uint32_t get_size_of_zero_fill() const; + //Returns characteristics + uint32_t get_characteristics() const; + //Returns raw TLS data + const std::string& get_raw_data() const; + //Returns TLS callbacks addresses + const tls_callback_list& get_tls_callbacks() const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild TLS directory + + //Sets start RVA of TLS raw data + void set_raw_data_start_rva(uint32_t rva); + //Sets end RVA of TLS raw data + void set_raw_data_end_rva(uint32_t rva); + //Sets TLS index RVA + void set_index_rva(uint32_t rva); + //Sets TLS callbacks RVA + void set_callbacks_rva(uint32_t rva); + //Sets size of zero fill + void set_size_of_zero_fill(uint32_t size); + //Sets characteristics + void set_characteristics(uint32_t characteristics); + //Sets raw TLS data + void set_raw_data(const std::string& data); + //Returns TLS callbacks addresses + tls_callback_list& get_tls_callbacks(); + //Adds TLS callback + void add_tls_callback(uint32_t rva); + //Clears TLS callbacks list + void clear_tls_callbacks(); + //Recalculates end address of raw TLS data + void recalc_raw_data_end_rva(); + +private: + uint32_t start_rva_, end_rva_, index_rva_, callbacks_rva_; + uint32_t size_of_zero_fill_, characteristics_; + + //Raw TLS data + std::string raw_data_; + + //TLS callback RVAs + tls_callback_list callbacks_; +}; + +//Represents type of expanding of TLS section containing raw data +//(Works only if you are writing TLS raw data to tls_section and it is the last one in the PE image on the moment of TLS rebuild) +enum tls_data_expand_type +{ + tls_data_expand_raw, //If there is not enough raw space for raw TLS data, it can be expanded + tls_data_expand_virtual //If there is not enough virtual place for raw TLS data, it can be expanded +}; + + +//Get TLS info +//If image does not have TLS, throws an exception +const tls_info get_tls_info(const pe_base& pe); + +template<typename PEClassType> +const tls_info get_tls_info_base(const pe_base& pe); + +//Rebuilder of TLS structures +//If write_tls_callbacks = true, TLS callbacks VAs will be written to their place +//If write_tls_data = true, TLS data will be written to its place +//If you have chosen to rewrite raw data, only (EndAddressOfRawData - StartAddressOfRawData) bytes will be written, not the full length of string +//representing raw data content +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +const image_directory rebuild_tls(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start = 0, bool write_tls_callbacks = true, bool write_tls_data = true, tls_data_expand_type expand = tls_data_expand_raw, bool save_to_pe_header = true, bool auto_strip_last_section = true); + +template<typename PEClassType> +const image_directory rebuild_tls_base(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start = 0, bool write_tls_callbacks = true, bool write_tls_data = true, tls_data_expand_type expand = tls_data_expand_raw, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/resource_bitmap_reader.cpp b/tools/pe_bliss/resource_bitmap_reader.cpp new file mode 100644 index 0000000000..3546461f53 --- /dev/null +++ b/tools/pe_bliss/resource_bitmap_reader.cpp @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <cmath> +#include "resource_bitmap_reader.h" +#include "pe_resource_viewer.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_bitmap_reader::resource_bitmap_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Returns bitmap data by name and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_name(const std::wstring& name, uint32_t index) const +{ + return create_bitmap(res_.get_resource_data_by_name(pe_resource_viewer::resource_bitmap, name, index).get_data()); +} + +//Returns bitmap data by name and language (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_name(uint32_t language, const std::wstring& name) const +{ + return create_bitmap(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_bitmap, name).get_data()); +} + +//Returns bitmap data by ID and language (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_id_lang(uint32_t language, uint32_t id) const +{ + return create_bitmap(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_bitmap, id).get_data()); +} + +//Returns bitmap data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_id(uint32_t id, uint32_t index) const +{ + return create_bitmap(res_.get_resource_data_by_id(pe_resource_viewer::resource_bitmap, id, index).get_data()); +} + +//Helper function of creating bitmap header +const std::string resource_bitmap_reader::create_bitmap(const std::string& resource_data) +{ + //Create bitmap file header + bitmapfileheader header = {0}; + header.bfType = 0x4d42; //Signature "BM" + header.bfOffBits = sizeof(bitmapfileheader) + sizeof(bitmapinfoheader); //Offset to bitmap bits + header.bfSize = static_cast<uint32_t>(sizeof(bitmapfileheader) + resource_data.length()); //Size of bitmap + + //Check size of resource data + if(resource_data.length() < sizeof(bitmapinfoheader)) + throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); + + { + //Get bitmap info header + const bitmapinfoheader* info = reinterpret_cast<const bitmapinfoheader*>(resource_data.data()); + + //If color table is present, skip it + if(info->biClrUsed != 0) + header.bfOffBits += 4 * info->biClrUsed; //Add this size to offset to bitmap bits + else if(info->biBitCount <= 8) + header.bfOffBits += 4 * static_cast<uint32_t>(std::pow(2.f, info->biBitCount)); //Add this size to offset to bitmap bits + } + + //Return final bitmap data + return std::string(reinterpret_cast<const char*>(&header), sizeof(bitmapfileheader)) + resource_data; +} +} diff --git a/tools/pe_bliss/resource_bitmap_reader.h b/tools/pe_bliss/resource_bitmap_reader.h new file mode 100644 index 0000000000..f2b92bbde7 --- /dev/null +++ b/tools/pe_bliss/resource_bitmap_reader.h @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +class resource_bitmap_reader +{ +public: + resource_bitmap_reader(const pe_resource_viewer& res); + + //Returns bitmap data by name and language (minimum checks of format correctness) + const std::string get_bitmap_by_name(uint32_t language, const std::wstring& name) const; + //Returns bitmap data by name and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_bitmap_by_name(const std::wstring& name, uint32_t index = 0) const; + //Returns bitmap data by ID and language (minimum checks of format correctness) + const std::string get_bitmap_by_id_lang(uint32_t language, uint32_t id) const; + //Returns bitmap data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_bitmap_by_id(uint32_t id, uint32_t index = 0) const; + +private: + //Helper function of creating bitmap header + static const std::string create_bitmap(const std::string& resource_data); + + const pe_resource_viewer& res_; +}; +} diff --git a/tools/pe_bliss/resource_bitmap_writer.cpp b/tools/pe_bliss/resource_bitmap_writer.cpp new file mode 100644 index 0000000000..3445a08445 --- /dev/null +++ b/tools/pe_bliss/resource_bitmap_writer.cpp @@ -0,0 +1,75 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "resource_bitmap_writer.h" +#include "pe_resource_manager.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_bitmap_writer::resource_bitmap_writer(pe_resource_manager& res) + :res_(res) +{} + +//Adds bitmap from bitmap file data. If bitmap already exists, replaces it +//timestamp will be used for directories that will be added +void resource_bitmap_writer::add_bitmap(const std::string& bitmap_file, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + //Check bitmap data a little + if(bitmap_file.length() < sizeof(bitmapfileheader)) + throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); + + resource_directory_entry new_entry; + new_entry.set_id(id); + + //Add bitmap + res_.add_resource(bitmap_file.substr(sizeof(bitmapfileheader)), pe_resource_viewer::resource_bitmap, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp); +} + +//Adds bitmap from bitmap file data. If bitmap already exists, replaces it +//timestamp will be used for directories that will be added +void resource_bitmap_writer::add_bitmap(const std::string& bitmap_file, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + //Check bitmap data a little + if(bitmap_file.length() < sizeof(bitmapfileheader)) + throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); + + resource_directory_entry new_entry; + new_entry.set_name(name); + + //Add bitmap + res_.add_resource(bitmap_file.substr(sizeof(bitmapfileheader)), pe_resource_viewer::resource_bitmap, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp); +} + +//Removes bitmap by name/ID and language +bool resource_bitmap_writer::remove_bitmap(const std::wstring& name, uint32_t language) +{ + return res_.remove_resource(pe_resource_viewer::resource_bitmap, name, language); +} + +//Removes bitmap by name/ID and language +bool resource_bitmap_writer::remove_bitmap(uint32_t id, uint32_t language) +{ + return res_.remove_resource(pe_resource_viewer::resource_bitmap, id, language); +} +} diff --git a/tools/pe_bliss/resource_bitmap_writer.h b/tools/pe_bliss/resource_bitmap_writer.h new file mode 100644 index 0000000000..4b8ea72705 --- /dev/null +++ b/tools/pe_bliss/resource_bitmap_writer.h @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_manager; + +class resource_bitmap_writer +{ +public: + resource_bitmap_writer(pe_resource_manager& res); + + //Adds bitmap from bitmap file data. If bitmap already exists, replaces it + //timestamp will be used for directories that will be added + void add_bitmap(const std::string& bitmap_file, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_bitmap(const std::string& bitmap_file, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + + //Removes bitmap by name/ID and language + bool remove_bitmap(const std::wstring& name, uint32_t language); + bool remove_bitmap(uint32_t id, uint32_t language); + +private: + pe_resource_manager& res_; +}; +} diff --git a/tools/pe_bliss/resource_cursor_icon_reader.cpp b/tools/pe_bliss/resource_cursor_icon_reader.cpp new file mode 100644 index 0000000000..28a259163e --- /dev/null +++ b/tools/pe_bliss/resource_cursor_icon_reader.cpp @@ -0,0 +1,521 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include "resource_cursor_icon_reader.h" +#include "pe_structures.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_cursor_icon_reader::resource_cursor_icon_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Helper function of creating icon headers from ICON_GROUP resource data +//Returns icon count +uint16_t resource_cursor_icon_reader::format_icon_headers(std::string& ico_data, const std::string& resource_data) +{ + //Check resource data size + if(resource_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + const ico_header* info = reinterpret_cast<const ico_header*>(resource_data.data()); + + //Check resource data size + if(resource_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Reserve memory to speed up a little + ico_data.reserve(sizeof(ico_header) + info->Count * sizeof(icondirentry)); + ico_data.append(reinterpret_cast<const char*>(info), sizeof(ico_header)); + + //Iterate over all listed icons + uint32_t offset = sizeof(ico_header) + sizeof(icondirentry) * info->Count; + for(uint16_t i = 0; i != info->Count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(resource_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + + //Fill icon data + icondirentry direntry; + direntry.BitCount = group->BitCount; + direntry.ColorCount = group->ColorCount; + direntry.Height = group->Height; + direntry.Planes = group->Planes; + direntry.Reserved = group->Reserved; + direntry.SizeInBytes = group->SizeInBytes; + direntry.Width = group->Width; + direntry.ImageOffset = offset; + + //Add icon header to returned value + ico_data.append(reinterpret_cast<const char*>(&direntry), sizeof(icondirentry)); + + offset += group->SizeInBytes; + } + + //Return icon count + return info->Count; +} + +//Returns single icon data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_icon_by_id_lang(uint32_t language, uint32_t id) const +{ + //Get icon headers + std::string icon_data(lookup_icon_group_data_by_icon(id, language)); + //Append icon data + icon_data.append(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, id).get_data()); + return icon_data; +} + +//Returns single icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_icon_by_id(uint32_t id, uint32_t index) const +{ + pe_resource_viewer::resource_language_list languages(res_.list_resource_languages(pe_resource_viewer::resource_icon, id)); + if(languages.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + //Get icon headers + std::string icon_data(lookup_icon_group_data_by_icon(id, languages.at(index))); + //Append icon data + icon_data.append(res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, id, index).get_data()); + return icon_data; +} + +//Returns icon data by name and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_name(const std::wstring& name, uint32_t index) const +{ + std::string ret; + + //Get resource by name and index + const std::string data = res_.get_resource_data_by_name(pe_resource_viewer::resource_icon_group, name, index).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, group->Number, index).get_data(); + } + + return ret; +} + +//Returns icon data by name and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_name(uint32_t language, const std::wstring& name) const +{ + std::string ret; + + //Get resource by name and language + const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, name).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, group->Number).get_data(); + } + + return ret; +} + +//Returns icon data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_id_lang(uint32_t language, uint32_t id) const +{ + std::string ret; + + //Get resource by language and id + const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, id).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, group->Number).get_data(); + } + + return ret; +} + +//Returns icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_id(uint32_t id, uint32_t index) const +{ + std::string ret; + + //Get resource by id and index + const std::string data = res_.get_resource_data_by_id(pe_resource_viewer::resource_icon_group, id, index).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, group->Number, index).get_data(); + } + + return ret; +} + +//Checks for icon presence inside icon group, fills icon headers if found +bool resource_cursor_icon_reader::check_icon_presence(const std::string& icon_group_resource_data, uint32_t icon_id, std::string& ico_data) +{ + //Check resource data size + if(icon_group_resource_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + const ico_header* info = reinterpret_cast<const ico_header*>(icon_group_resource_data.data()); + + //Check resource data size + if(icon_group_resource_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + for(uint16_t i = 0; i != info->Count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(icon_group_resource_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + if(group->Number == icon_id) + { + //Reserve memory to speed up a little + ico_data.reserve(sizeof(ico_header) + sizeof(icondirentry)); + //Write single-icon icon header + ico_header new_header = *info; + new_header.Count = 1; + ico_data.append(reinterpret_cast<const char*>(&new_header), sizeof(ico_header)); + + //Fill icon data + icondirentry direntry; + direntry.BitCount = group->BitCount; + direntry.ColorCount = group->ColorCount; + direntry.Height = group->Height; + direntry.Planes = group->Planes; + direntry.Reserved = group->Reserved; + direntry.SizeInBytes = group->SizeInBytes; + direntry.Width = group->Width; + direntry.ImageOffset = sizeof(ico_header) + sizeof(icondirentry); + ico_data.append(reinterpret_cast<const char*>(&direntry), sizeof(direntry)); + + return true; + } + } + + return false; +} + +//Looks up icon group by icon id and returns full icon headers if found +const std::string resource_cursor_icon_reader::lookup_icon_group_data_by_icon(uint32_t icon_id, uint32_t language) const +{ + std::string icon_header_data; + + { + //List all ID-resources + pe_resource_viewer::resource_id_list ids(res_.list_resource_ids(pe_resource_viewer::resource_icon_group)); + + for(pe_resource_viewer::resource_id_list::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_icon_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_icon_presence(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, *it).get_data(), icon_id, icon_header_data)) + return icon_header_data; + } + } + + { + //List all named resources + pe_resource_viewer::resource_name_list names(res_.list_resource_names(pe_resource_viewer::resource_icon_group)); + for(pe_resource_viewer::resource_name_list::const_iterator it = names.begin(); it != names.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_icon_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_icon_presence(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, *it).get_data(), icon_id, icon_header_data)) + return icon_header_data; + } + } + + throw pe_exception("No icon group find for requested icon", pe_exception::no_icon_group_found); +} + +//Returns single cursor data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_cursor_by_id_lang(uint32_t language, uint32_t id) const +{ + std::string raw_cursor_data(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, id).get_data()); + //Get cursor headers + std::string cursor_data(lookup_cursor_group_data_by_cursor(id, language, raw_cursor_data)); + //Append cursor data + cursor_data.append(raw_cursor_data.substr(sizeof(uint16_t) * 2 /* hotspot position */)); + return cursor_data; +} + +//Returns single cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_cursor_by_id(uint32_t id, uint32_t index) const +{ + pe_resource_viewer::resource_language_list languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor, id)); + if(languages.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + std::string raw_cursor_data(res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, id, index).get_data()); + //Get cursor headers + std::string cursor_data(lookup_cursor_group_data_by_cursor(id, languages.at(index), raw_cursor_data)); + //Append cursor data + cursor_data.append(raw_cursor_data.substr(sizeof(uint16_t) * 2 /* hotspot position */)); + return cursor_data; +} + +//Helper function of creating cursor headers +//Returns cursor count +uint16_t resource_cursor_icon_reader::format_cursor_headers(std::string& cur_data, const std::string& resource_data, uint32_t language, uint32_t index) const +{ + //Check resource data length + if(resource_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + const cursor_header* info = reinterpret_cast<const cursor_header*>(resource_data.data()); + + //Check resource data length + if(resource_data.length() < sizeof(cursor_header) + sizeof(cursor_group) * info->Count) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Reserve needed space to speed up a little + cur_data.reserve(sizeof(cursor_header) + info->Count * sizeof(cursordirentry)); + //Add cursor header + cur_data.append(reinterpret_cast<const char*>(info), sizeof(cursor_header)); + + //Iterate over all cursors listed in cursor group + uint32_t offset = sizeof(cursor_header) + sizeof(cursordirentry) * info->Count; + for(uint16_t i = 0; i != info->Count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + + //Fill cursor info + cursordirentry direntry; + direntry.ColorCount = 0; //OK + direntry.Width = static_cast<uint8_t>(group->Width); + direntry.Height = static_cast<uint8_t>(group->Height) / 2; + direntry.Reserved = 0; + + //Now read hotspot data from cursor data directory + const std::string cursor = index == 0xFFFFFFFF + ? res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data() + : res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data(); + if(cursor.length() < 2 * sizeof(uint16_t)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Here it is - two words in the very beginning of cursor data + direntry.HotspotX = *reinterpret_cast<const uint16_t*>(cursor.data()); + direntry.HotspotY = *reinterpret_cast<const uint16_t*>(cursor.data() + sizeof(uint16_t)); + + //Fill the rest data + direntry.SizeInBytes = group->SizeInBytes - 2 * sizeof(uint16_t); + direntry.ImageOffset = offset; + + //Add cursor header + cur_data.append(reinterpret_cast<const char*>(&direntry), sizeof(cursordirentry)); + + offset += direntry.SizeInBytes; + } + + //Return cursor count + return info->Count; +} + +//Returns cursor data by name and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_name(uint32_t language, const std::wstring& name) const +{ + std::string ret; + + //Get resource by name and language + const std::string resource_data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, name).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, language); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Returns cursor data by name and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_name(const std::wstring& name, uint32_t index) const +{ + std::string ret; + + //Get resource by name and index + const std::string resource_data = res_.get_resource_data_by_name(pe_resource_viewer::resource_cursor_group, name, index).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, 0, index); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Returns cursor data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_id_lang(uint32_t language, uint32_t id) const +{ + std::string ret; + + //Get resource by ID and language + const std::string resource_data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, id).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, language); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Returns cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_id(uint32_t id, uint32_t index) const +{ + std::string ret; + + //Get resource by ID and index + const std::string resource_data = res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor_group, id, index).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, 0, index); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Checks for cursor presence inside cursor group, fills cursor headers if found +bool resource_cursor_icon_reader::check_cursor_presence(const std::string& cursor_group_resource_data, uint32_t cursor_id, std::string& cur_header_data, const std::string& raw_cursor_data) +{ + //Check resource data length + if(cursor_group_resource_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + const cursor_header* info = reinterpret_cast<const cursor_header*>(cursor_group_resource_data.data()); + + //Check resource data length + if(cursor_group_resource_data.length() < sizeof(cursor_header) + sizeof(cursor_group)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Iterate over all cursors listed in cursor group + for(uint16_t i = 0; i != info->Count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(cursor_group_resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + + if(group->Number == cursor_id) + { + //Reserve needed space to speed up a little + cur_header_data.reserve(sizeof(cursor_header) + sizeof(cursordirentry)); + //Write single-cursor cursor header + cursor_header new_header = *info; + new_header.Count = 1; + cur_header_data.append(reinterpret_cast<const char*>(&new_header), sizeof(cursor_header)); + + //Fill cursor info + cursordirentry direntry; + direntry.ColorCount = 0; //OK + direntry.Width = static_cast<uint8_t>(group->Width); + direntry.Height = static_cast<uint8_t>(group->Height) / 2; + direntry.Reserved = 0; + + if(raw_cursor_data.length() < 2 * sizeof(uint16_t)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Here it is - two words in the very beginning of cursor data + direntry.HotspotX = *reinterpret_cast<const uint16_t*>(raw_cursor_data.data()); + direntry.HotspotY = *reinterpret_cast<const uint16_t*>(raw_cursor_data.data() + sizeof(uint16_t)); + + //Fill the rest data + direntry.SizeInBytes = group->SizeInBytes - 2 * sizeof(uint16_t); + direntry.ImageOffset = sizeof(cursor_header) + sizeof(cursordirentry); + + //Add cursor header + cur_header_data.append(reinterpret_cast<const char*>(&direntry), sizeof(cursordirentry)); + + return true; + } + } + + return false; +} + +//Looks up cursor group by cursor id and returns full cursor headers if found +const std::string resource_cursor_icon_reader::lookup_cursor_group_data_by_cursor(uint32_t cursor_id, uint32_t language, const std::string& raw_cursor_data) const +{ + std::string cursor_header_data; + + { + //List all ID-resources + pe_resource_viewer::resource_id_list ids(res_.list_resource_ids(pe_resource_viewer::resource_cursor_group)); + + for(pe_resource_viewer::resource_id_list::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_cursor_presence(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, *it).get_data(), cursor_id, cursor_header_data, raw_cursor_data)) + return cursor_header_data; + } + } + + { + //List all named resources + pe_resource_viewer::resource_name_list names(res_.list_resource_names(pe_resource_viewer::resource_cursor_group)); + for(pe_resource_viewer::resource_name_list::const_iterator it = names.begin(); it != names.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_cursor_presence(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, *it).get_data(), cursor_id, cursor_header_data, raw_cursor_data)) + return cursor_header_data; + } + } + + throw pe_exception("No cursor group find for requested icon", pe_exception::no_cursor_group_found); +} +} diff --git a/tools/pe_bliss/resource_cursor_icon_reader.h b/tools/pe_bliss/resource_cursor_icon_reader.h new file mode 100644 index 0000000000..e34fff419b --- /dev/null +++ b/tools/pe_bliss/resource_cursor_icon_reader.h @@ -0,0 +1,84 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +class resource_cursor_icon_reader +{ +public: + resource_cursor_icon_reader(const pe_resource_viewer& res); + + //Returns single icon data by ID and language (minimum checks of format correctness) + const std::string get_single_icon_by_id_lang(uint32_t language, uint32_t id) const; + //Returns single icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_single_icon_by_id(uint32_t id, uint32_t index = 0) const; + + //Returns icon data of group of icons by name and language (minimum checks of format correctness) + const std::string get_icon_by_name(uint32_t language, const std::wstring& icon_group_name) const; + //Returns icon data of group of icons by name and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_icon_by_name(const std::wstring& icon_group_name, uint32_t index = 0) const; + //Returns icon data of group of icons by ID and language (minimum checks of format correctness) + const std::string get_icon_by_id_lang(uint32_t language, uint32_t icon_group_id) const; + //Returns icon data of group of icons by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_icon_by_id(uint32_t icon_group_id, uint32_t index = 0) const; + + //Returns single cursor data by ID and language (minimum checks of format correctness) + const std::string get_single_cursor_by_id_lang(uint32_t language, uint32_t id) const; + //Returns single cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_single_cursor_by_id(uint32_t id, uint32_t index = 0) const; + + //Returns cursor data by name and language (minimum checks of format correctness) + const std::string get_cursor_by_name(uint32_t language, const std::wstring& cursor_group_name) const; + //Returns cursor data by name and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_cursor_by_name(const std::wstring& cursor_group_name, uint32_t index = 0) const; + //Returns cursor data by ID and language (minimum checks of format correctness) + const std::string get_cursor_by_id_lang(uint32_t language, uint32_t cursor_group_id) const; + //Returns cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_cursor_by_id(uint32_t cursor_group_id, uint32_t index = 0) const; + +private: + const pe_resource_viewer& res_; + + //Helper function of creating icon headers from ICON_GROUP resource data + //Returns icon count + static uint16_t format_icon_headers(std::string& ico_data, const std::string& resource_data); + + //Helper function of creating cursor headers from CURSOR_GROUP resource data + //Returns cursor count + uint16_t format_cursor_headers(std::string& cur_data, const std::string& resource_data, uint32_t language, uint32_t index = 0xFFFFFFFF) const; + + //Looks up icon group by icon id and returns full icon headers if found + const std::string lookup_icon_group_data_by_icon(uint32_t icon_id, uint32_t language) const; + //Checks for icon presence inside icon group, fills icon headers if found + static bool check_icon_presence(const std::string& icon_group_resource_data, uint32_t icon_id, std::string& ico_data); + + //Looks up cursor group by cursor id and returns full cursor headers if found + const std::string lookup_cursor_group_data_by_cursor(uint32_t cursor_id, uint32_t language, const std::string& raw_cursor_data) const; + //Checks for cursor presence inside cursor group, fills cursor headers if found + static bool check_cursor_presence(const std::string& icon_group_resource_data, uint32_t cursor_id, std::string& cur_header_data, const std::string& raw_cursor_data); +}; +} diff --git a/tools/pe_bliss/resource_cursor_icon_writer.cpp b/tools/pe_bliss/resource_cursor_icon_writer.cpp new file mode 100644 index 0000000000..2f1c4363c4 --- /dev/null +++ b/tools/pe_bliss/resource_cursor_icon_writer.cpp @@ -0,0 +1,447 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include <string.h> +#include "resource_cursor_icon_writer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_cursor_icon_writer::resource_cursor_icon_writer(pe_resource_manager& res) + :res_(res) +{} + +//Add icon helper +void resource_cursor_icon_writer::add_icon(const std::string& icon_file, const resource_data_info* group_icon_info /* or zero */, resource_directory_entry& new_icon_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + //Check icon for correctness + if(icon_file.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + const ico_header* icon_header = reinterpret_cast<const ico_header*>(&icon_file[0]); + + unsigned long size_of_headers = sizeof(ico_header) + icon_header->Count * sizeof(icondirentry); + if(icon_file.length() < size_of_headers || icon_header->Count == 0) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Enumerate all icons in file + for(uint16_t i = 0; i != icon_header->Count; ++i) + { + //Check icon entries + const icondirentry* icon_entry = reinterpret_cast<const icondirentry*>(&icon_file[sizeof(ico_header) + i * sizeof(icondirentry)]); + if(icon_entry->SizeInBytes == 0 + || icon_entry->ImageOffset < size_of_headers + || !pe_utils::is_sum_safe(icon_entry->ImageOffset, icon_entry->SizeInBytes) + || icon_entry->ImageOffset + icon_entry->SizeInBytes > icon_file.length()) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + } + + std::string icon_group_data; + ico_header* info = 0; + + if(group_icon_info) + { + //If icon group already exists + { + icon_group_data = group_icon_info->get_data(); + codepage = group_icon_info->get_codepage(); //Don't change codepage of icon group entry + } + + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + info = reinterpret_cast<ico_header*>(&icon_group_data[0]); + + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + icon_group_data.resize(sizeof(ico_header) + (info->Count + icon_header->Count) * sizeof(icon_group)); + info = reinterpret_cast<ico_header*>(&icon_group_data[0]); //In case if memory was reallocated + } + else //Entry not found - icon group doesn't exist + { + icon_group_data.resize(sizeof(ico_header) + icon_header->Count * sizeof(icon_group)); + memcpy(&icon_group_data[0], icon_header, sizeof(ico_header)); + } + + //Search for available icon IDs + std::vector<uint16_t> icon_id_list(get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_icon, mode, icon_header->Count)); + + //Enumerate all icons in file + for(uint16_t i = 0; i != icon_header->Count; ++i) + { + const icondirentry* icon_entry = reinterpret_cast<const icondirentry*>(&icon_file[sizeof(ico_header) + i * sizeof(icondirentry)]); + icon_group group = {0}; + + //Fill icon resource header + group.BitCount = icon_entry->BitCount; + group.ColorCount = icon_entry->ColorCount; + group.Height = icon_entry->Height; + group.Planes = icon_entry->Planes; + group.Reserved = icon_entry->Reserved; + group.SizeInBytes = icon_entry->SizeInBytes; + group.Width = icon_entry->Width; + group.Number = icon_id_list.at(i); + + memcpy(&icon_group_data[sizeof(ico_header) + ((info ? info->Count : 0) + i) * sizeof(icon_group)], &group, sizeof(group)); + + //Add icon to resources + resource_directory_entry new_entry; + new_entry.set_id(group.Number); + res_.add_resource(icon_file.substr(icon_entry->ImageOffset, icon_entry->SizeInBytes), pe_resource_viewer::resource_icon, new_entry, resource_directory::entry_finder(group.Number), language, codepage, timestamp); + } + + if(info) + info->Count += icon_header->Count; //Increase icon count, if we're adding icon to existing group + + { + //Add or replace icon group data entry + res_.add_resource(icon_group_data, pe_resource_viewer::resource_icon_group, new_icon_group_entry, finder, language, codepage, timestamp); + } +} + +//Returns free icon or cursor ID list depending on icon_place_mode +const std::vector<uint16_t> resource_cursor_icon_writer::get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_type type, icon_place_mode mode, uint32_t count) +{ + //Search for available icon/cursor IDs + std::vector<uint16_t> icon_cursor_id_list; + + try + { + //If any icon exists + //List icon IDs + std::vector<uint32_t> id_list(res_.list_resource_ids(type)); + std::sort(id_list.begin(), id_list.end()); + + //If we are placing icon on free spaces + //I.e., icon IDs 1, 3, 4, 7, 8 already exist + //We'll place five icons on IDs 2, 5, 6, 9, 10 + if(mode != icon_place_after_max_icon_id) + { + if(!id_list.empty()) + { + //Determine and list free icon IDs + for(std::vector<uint32_t>::const_iterator it = id_list.begin(); it != id_list.end(); ++it) + { + if(it == id_list.begin()) + { + if(*it > 1) + { + for(uint16_t i = 1; i != *it; ++i) + { + icon_cursor_id_list.push_back(i); + if(icon_cursor_id_list.size() == count) + break; + } + } + } + else if(*(it - 1) - *it > 1) + { + for(uint16_t i = static_cast<uint16_t>(*(it - 1) + 1); i != static_cast<uint16_t>(*it); ++i) + { + icon_cursor_id_list.push_back(i); + if(icon_cursor_id_list.size() == count) + break; + } + } + + if(icon_cursor_id_list.size() == count) + break; + } + } + } + + uint32_t max_id = id_list.empty() ? 0 : *std::max_element(id_list.begin(), id_list.end()); + for(uint32_t i = static_cast<uint32_t>(icon_cursor_id_list.size()); i != count; ++i) + icon_cursor_id_list.push_back(static_cast<uint16_t>(++max_id)); + } + catch(const pe_exception&) //Entry not found + { + for(uint16_t i = 1; i != count + 1; ++i) + icon_cursor_id_list.push_back(i); + } + + return icon_cursor_id_list; +} + +//Add cursor helper +void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, const resource_data_info* group_cursor_info /* or zero */, resource_directory_entry& new_cursor_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + //Check cursor for correctness + if(cursor_file.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + const cursor_header* cur_header = reinterpret_cast<const cursor_header*>(&cursor_file[0]); + + unsigned long size_of_headers = sizeof(cursor_header) + cur_header->Count * sizeof(cursordirentry); + if(cursor_file.length() < size_of_headers || cur_header->Count == 0) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Enumerate all cursors in file + for(uint16_t i = 0; i != cur_header->Count; ++i) + { + //Check cursor entries + const cursordirentry* cursor_entry = reinterpret_cast<const cursordirentry*>(&cursor_file[sizeof(cursor_header) + i * sizeof(cursordirentry)]); + if(cursor_entry->SizeInBytes == 0 + || cursor_entry->ImageOffset < size_of_headers + || !pe_utils::is_sum_safe(cursor_entry->ImageOffset, cursor_entry->SizeInBytes) + || cursor_entry->ImageOffset + cursor_entry->SizeInBytes > cursor_file.length()) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + } + + std::string cursor_group_data; + cursor_header* info = 0; + + if(group_cursor_info) + { + //If cursor group already exists + { + cursor_group_data = group_cursor_info->get_data(); + codepage = group_cursor_info->get_codepage(); //Don't change codepage of cursor group entry + } + + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Get cursor header + info = reinterpret_cast<cursor_header*>(&cursor_group_data[0]); + + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header) + info->Count * sizeof(cursor_group)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + cursor_group_data.resize(sizeof(cursor_header) + (info->Count + cur_header->Count) * sizeof(cursor_group)); + info = reinterpret_cast<cursor_header*>(&cursor_group_data[0]); //In case if memory was reallocated + } + else //Entry not found - cursor group doesn't exist + { + cursor_group_data.resize(sizeof(cursor_header) + cur_header->Count * sizeof(cursor_group)); + memcpy(&cursor_group_data[0], cur_header, sizeof(cursor_header)); + } + + //Search for available cursor IDs + std::vector<uint16_t> cursor_id_list(get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_cursor, mode, cur_header->Count)); + + //Enumerate all cursors in file + for(uint16_t i = 0; i != cur_header->Count; ++i) + { + const cursordirentry* cursor_entry = reinterpret_cast<const cursordirentry*>(&cursor_file[sizeof(cursor_header) + i * sizeof(cursordirentry)]); + cursor_group group = {0}; + + //Fill cursor resource header + group.Height = cursor_entry->Height * 2; + group.SizeInBytes = cursor_entry->SizeInBytes + 2 * sizeof(uint16_t) /* hotspot coordinates */; + group.Width = cursor_entry->Width; + group.Number = cursor_id_list.at(i); + + memcpy(&cursor_group_data[sizeof(cursor_header) + ((info ? info->Count : 0) + i) * sizeof(cursor_group)], &group, sizeof(group)); + + //Add cursor to resources + resource_directory_entry new_entry; + new_entry.set_id(group.Number); + + //Fill resource data (two WORDs for hotspot of cursor, and cursor bitmap data) + std::string cur_data; + cur_data.resize(sizeof(uint16_t) * 2); + memcpy(&cur_data[0], &cursor_entry->HotspotX, sizeof(uint16_t)); + memcpy(&cur_data[sizeof(uint16_t)], &cursor_entry->HotspotY, sizeof(uint16_t)); + cur_data.append(cursor_file.substr(cursor_entry->ImageOffset, cursor_entry->SizeInBytes)); + + res_.add_resource(cur_data, pe_resource_viewer::resource_cursor, new_entry, resource_directory::entry_finder(group.Number), language, codepage, timestamp); + } + + if(info) + info->Count += cur_header->Count; //Increase cursor count, if we're adding cursor to existing group + + { + //Add or replace cursor group data entry + res_.add_resource(cursor_group_data, pe_resource_viewer::resource_cursor_group, new_cursor_group_entry, finder, language, codepage, timestamp); + } +} + +//Adds icon(s) from icon file data +//timestamp will be used for directories that will be added +//If icon group with name "icon_group_name" or ID "icon_group_id" already exists, it will be appended with new icon(s) +//(Codepage of icon group and icons will not be changed in this case) +//icon_place_mode determines, how new icon(s) will be placed +void resource_cursor_icon_writer::add_icon(const std::string& icon_file, const std::wstring& icon_group_name, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_icon_group_entry; + new_icon_group_entry.set_name(icon_group_name); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, icon_group_name))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_icon(icon_file, data_info.get(), new_icon_group_entry, resource_directory::entry_finder(icon_group_name), language, mode, codepage, timestamp); +} + +void resource_cursor_icon_writer::add_icon(const std::string& icon_file, uint32_t icon_group_id, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_icon_group_entry; + new_icon_group_entry.set_id(icon_group_id); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, icon_group_id))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_icon(icon_file, data_info.get(), new_icon_group_entry, resource_directory::entry_finder(icon_group_id), language, mode, codepage, timestamp); +} + +//Adds cursor(s) from cursor file data +//timestamp will be used for directories that will be added +//If cursor group with name "cursor_group_name" or ID "cursor_group_id" already exists, it will be appended with new cursor(s) +//(Codepage of cursor group and cursors will not be changed in this case) +//icon_place_mode determines, how new cursor(s) will be placed +void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, const std::wstring& cursor_group_name, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_cursor_group_entry; + new_cursor_group_entry.set_name(cursor_group_name); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, cursor_group_name))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_cursor(cursor_file, data_info.get(), new_cursor_group_entry, resource_directory::entry_finder(cursor_group_name), language, mode, codepage, timestamp); +} + +void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, uint32_t cursor_group_id, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_cursor_group_entry; + new_cursor_group_entry.set_id(cursor_group_id); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, cursor_group_id))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_cursor(cursor_file, data_info.get(), new_cursor_group_entry, resource_directory::entry_finder(cursor_group_id), language, mode, codepage, timestamp); +} + +//Remove icon group helper +void resource_cursor_icon_writer::remove_icons_from_icon_group(const std::string& icon_group_data, uint32_t language) +{ + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + const ico_header* info = reinterpret_cast<const ico_header*>(icon_group_data.data()); + + uint16_t icon_count = info->Count; + + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header) + icon_count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Remove icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(icon_group_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + res_.remove_resource(pe_resource_viewer::resource_icon, group->Number, language); + } +} + +//Remove cursor group helper +void resource_cursor_icon_writer::remove_cursors_from_cursor_group(const std::string& cursor_group_data, uint32_t language) +{ + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Get icon header + const cursor_header* info = reinterpret_cast<const cursor_header*>(cursor_group_data.data()); + + uint16_t cursor_count = info->Count; + + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header) + cursor_count * sizeof(cursor_group)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Remove icon data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(cursor_group_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + res_.remove_resource(pe_resource_viewer::resource_cursor, group->Number, language); + } +} + +//Removes cursor group and all its cursors by name/ID and language +bool resource_cursor_icon_writer::remove_cursor_group(const std::wstring& cursor_group_name, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, cursor_group_name).get_data(); + remove_cursors_from_cursor_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_cursor_group, cursor_group_name, language); +} + +//Removes cursor group and all its cursors by name/ID and language +bool resource_cursor_icon_writer::remove_cursor_group(uint32_t cursor_group_id, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, cursor_group_id).get_data(); + remove_cursors_from_cursor_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_cursor_group, cursor_group_id, language); +} + +//Removes icon group and all its icons by name/ID and language +bool resource_cursor_icon_writer::remove_icon_group(const std::wstring& icon_group_name, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, icon_group_name).get_data(); + remove_icons_from_icon_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_icon_group, icon_group_name, language); +} + +//Removes icon group and all its icons by name/ID and language +bool resource_cursor_icon_writer::remove_icon_group(uint32_t icon_group_id, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, icon_group_id).get_data(); + remove_icons_from_icon_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_icon_group, icon_group_id, language); +} +} diff --git a/tools/pe_bliss/resource_cursor_icon_writer.h b/tools/pe_bliss/resource_cursor_icon_writer.h new file mode 100644 index 0000000000..e73ac6a093 --- /dev/null +++ b/tools/pe_bliss/resource_cursor_icon_writer.h @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <vector> +#include "stdint_defs.h" +#include "pe_resource_manager.h" + +namespace pe_bliss +{ +class pe_resource_manager; + +class resource_cursor_icon_writer +{ +public: + //Determines, how new icon(s) or cursor(s) will be placed + enum icon_place_mode + { + icon_place_after_max_icon_id, //Icon(s) will be placed after all existing + icon_place_free_ids //New icon(s) will take all free IDs between existing icons + }; + +public: + resource_cursor_icon_writer(pe_resource_manager& res); + + //Removes icon group and all its icons by name/ID and language + bool remove_icon_group(const std::wstring& icon_group_name, uint32_t language); + bool remove_icon_group(uint32_t icon_group_id, uint32_t language); + + //Adds icon(s) from icon file data + //timestamp will be used for directories that will be added + //If icon group with name "icon_group_name" or ID "icon_group_id" already exists, it will be appended with new icon(s) + //(Codepage of icon group and icons will not be changed in this case) + //icon_place_mode determines, how new icon(s) will be placed + void add_icon(const std::string& icon_file, + const std::wstring& icon_group_name, + uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, + uint32_t codepage = 0, uint32_t timestamp = 0); + + void add_icon(const std::string& icon_file, + uint32_t icon_group_id, + uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, + uint32_t codepage = 0, uint32_t timestamp = 0); + + //Removes cursor group and all its cursors by name/ID and language + bool remove_cursor_group(const std::wstring& cursor_group_name, uint32_t language); + bool remove_cursor_group(uint32_t cursor_group_id, uint32_t language); + + //Adds cursor(s) from cursor file data + //timestamp will be used for directories that will be added + //If cursor group with name "cursor_group_name" or ID "cursor_group_id" already exists, it will be appended with new cursor(s) + //(Codepage of cursor group and cursors will not be changed in this case) + //icon_place_mode determines, how new cursor(s) will be placed + void add_cursor(const std::string& cursor_file, const std::wstring& cursor_group_name, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_cursor(const std::string& cursor_file, uint32_t cursor_group_id, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0); + +private: + pe_resource_manager& res_; + + //Add icon helper + void add_icon(const std::string& icon_file, const resource_data_info* group_icon_info /* or zero */, resource_directory_entry& new_icon_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp); + + //Remove icon group helper + void remove_icons_from_icon_group(const std::string& icon_group_data, uint32_t language); + + //Add cursor helper + void add_cursor(const std::string& cursor_file, const resource_data_info* group_cursor_info /* or zero */, resource_directory_entry& new_cursor_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp); + + //Remove cursor group helper + void remove_cursors_from_cursor_group(const std::string& cursor_group_data, uint32_t language); + + //Returns free icon or cursor ID list depending on icon_place_mode + const std::vector<uint16_t> get_icon_or_cursor_free_id_list(pe_resource_manager::resource_type type, icon_place_mode mode, uint32_t count); +}; +} diff --git a/tools/pe_bliss/resource_data_info.cpp b/tools/pe_bliss/resource_data_info.cpp new file mode 100644 index 0000000000..75bb060eae --- /dev/null +++ b/tools/pe_bliss/resource_data_info.cpp @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "resource_data_info.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +//Default constructor +resource_data_info::resource_data_info(const std::string& data, uint32_t codepage) + :data_(data), codepage_(codepage) +{} + +//Constructor from data +resource_data_info::resource_data_info(const resource_data_entry& data) + :data_(data.get_data()), codepage_(data.get_codepage()) +{} + +//Returns resource data +const std::string& resource_data_info::get_data() const +{ + return data_; +} + +//Returns resource codepage +uint32_t resource_data_info::get_codepage() const +{ + return codepage_; +} +} diff --git a/tools/pe_bliss/resource_data_info.h b/tools/pe_bliss/resource_data_info.h new file mode 100644 index 0000000000..e2275ebbf5 --- /dev/null +++ b/tools/pe_bliss/resource_data_info.h @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class resource_data_entry; + +//Class representing resource data +class resource_data_info +{ +public: + //Constructor from data + resource_data_info(const std::string& data, uint32_t codepage); + //Constructor from data + explicit resource_data_info(const resource_data_entry& data); + + //Returns resource data + const std::string& get_data() const; + //Returns resource codepage + uint32_t get_codepage() const; + +private: + std::string data_; + uint32_t codepage_; +}; +} diff --git a/tools/pe_bliss/resource_internal.h b/tools/pe_bliss/resource_internal.h new file mode 100644 index 0000000000..64a5bf3903 --- /dev/null +++ b/tools/pe_bliss/resource_internal.h @@ -0,0 +1,34 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once + +#define U16TEXT(t) reinterpret_cast<const unicode16_t*>( t ) + +#define StringFileInfo U16TEXT("S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0") +#define SizeofStringFileInfo sizeof("S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0") +#define VarFileInfo U16TEXT("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0") +#define Translation U16TEXT("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0") + +#define VarFileInfoAligned U16TEXT("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0\0") +#define TranslationAligned U16TEXT("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0\0\0") +#define SizeofVarFileInfoAligned sizeof("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0\0") +#define SizeofTranslationAligned sizeof("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0\0\0") diff --git a/tools/pe_bliss/resource_message_list_reader.cpp b/tools/pe_bliss/resource_message_list_reader.cpp new file mode 100644 index 0000000000..f2ea142bee --- /dev/null +++ b/tools/pe_bliss/resource_message_list_reader.cpp @@ -0,0 +1,131 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "resource_message_list_reader.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_message_list_reader::resource_message_list_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Helper function of parsing message list table +const resource_message_list resource_message_list_reader::parse_message_list(const std::string& resource_data) +{ + resource_message_list ret; + + //Check resource data length + if(resource_data.length() < sizeof(message_resource_data)) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + const message_resource_data* message_data = reinterpret_cast<const message_resource_data*>(resource_data.data()); + + //Check resource data length more carefully and some possible overflows + if(message_data->NumberOfBlocks >= pe_utils::max_dword / sizeof(message_resource_block) + || !pe_utils::is_sum_safe(message_data->NumberOfBlocks * sizeof(message_resource_block), sizeof(message_resource_data)) + || resource_data.length() < message_data->NumberOfBlocks * sizeof(message_resource_block) + sizeof(message_resource_data)) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + //Iterate over all message resource blocks + for(unsigned long i = 0; i != message_data->NumberOfBlocks; ++i) + { + //Get block + const message_resource_block* block = + reinterpret_cast<const message_resource_block*>(resource_data.data() + sizeof(message_resource_data) - sizeof(message_resource_block) + sizeof(message_resource_block) * i); + + //Check resource data length and IDs + if(resource_data.length() < block->OffsetToEntries || block->LowId > block->HighId) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + unsigned long current_pos = 0; + static const unsigned long size_of_entry_headers = 4; + //List all message resource entries in block + for(uint32_t curr_id = block->LowId; curr_id <= block->HighId; curr_id++) + { + //Check resource data length and some possible overflows + if(!pe_utils::is_sum_safe(block->OffsetToEntries, current_pos) + || !pe_utils::is_sum_safe(block->OffsetToEntries + current_pos, size_of_entry_headers) + || resource_data.length() < block->OffsetToEntries + current_pos + size_of_entry_headers) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + //Get entry + const message_resource_entry* entry = reinterpret_cast<const message_resource_entry*>(resource_data.data() + block->OffsetToEntries + current_pos); + + //Check resource data length and entry length and some possible overflows + if(entry->Length < size_of_entry_headers + || !pe_utils::is_sum_safe(block->OffsetToEntries + current_pos, entry->Length) + || resource_data.length() < block->OffsetToEntries + current_pos + entry->Length + || entry->Length < size_of_entry_headers) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + if(entry->Flags & message_resource_unicode) + { + //If string is UNICODE + //Check its length + if(entry->Length % 2) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + //Add ID and string to message table +#ifdef PE_BLISS_WINDOWS + ret.insert(std::make_pair(curr_id, message_table_item( + std::wstring(reinterpret_cast<const wchar_t*>(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers), + (entry->Length - size_of_entry_headers) / 2) + ))); +#else + ret.insert(std::make_pair(curr_id, message_table_item( + pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers), + (entry->Length - size_of_entry_headers) / 2)) + ))); +#endif + } + else + { + //If string is ANSI + //Add ID and string to message table + ret.insert(std::make_pair(curr_id, message_table_item( + std::string(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers, + entry->Length - size_of_entry_headers) + ))); + } + + //Go to next entry + current_pos += entry->Length; + } + } + + return ret; +} + +//Returns message table data by ID and index in language directory (instead of language) +const resource_message_list resource_message_list_reader::get_message_table_by_id(uint32_t id, uint32_t index) const +{ + return parse_message_list(res_.get_resource_data_by_id(pe_resource_viewer::resource_message_table, id, index).get_data()); +} + +//Returns message table data by ID and language +const resource_message_list resource_message_list_reader::get_message_table_by_id_lang(uint32_t language, uint32_t id) const +{ + return parse_message_list(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_message_table, id).get_data()); +} +} diff --git a/tools/pe_bliss/resource_message_list_reader.h b/tools/pe_bliss/resource_message_list_reader.h new file mode 100644 index 0000000000..a0ac96eb8c --- /dev/null +++ b/tools/pe_bliss/resource_message_list_reader.h @@ -0,0 +1,49 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "message_table.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +//ID; message_table_item +typedef std::map<uint32_t, message_table_item> resource_message_list; + +class resource_message_list_reader +{ +public: + resource_message_list_reader(const pe_resource_viewer& res); + + //Returns message table data by ID and language + const resource_message_list get_message_table_by_id_lang(uint32_t language, uint32_t id) const; + //Returns message table data by ID and index in language directory (instead of language) + const resource_message_list get_message_table_by_id(uint32_t id, uint32_t index = 0) const; + + //Helper function of parsing message list table + //resource_data - raw message table resource data + static const resource_message_list parse_message_list(const std::string& resource_data); + +private: + const pe_resource_viewer& res_; +}; +} diff --git a/tools/pe_bliss/resource_string_table_reader.cpp b/tools/pe_bliss/resource_string_table_reader.cpp new file mode 100644 index 0000000000..8a51720e6a --- /dev/null +++ b/tools/pe_bliss/resource_string_table_reader.cpp @@ -0,0 +1,109 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "resource_string_table_reader.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +resource_string_table_reader::resource_string_table_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Returns string table data by ID and index in language directory (instead of language) +const resource_string_list resource_string_table_reader::get_string_table_by_id(uint32_t id, uint32_t index) const +{ + return parse_string_list(id, res_.get_resource_data_by_id(pe_resource_viewer::resource_string, id, index).get_data()); +} + +//Returns string table data by ID and language +const resource_string_list resource_string_table_reader::get_string_table_by_id_lang(uint32_t language, uint32_t id) const +{ + return parse_string_list(id, res_.get_resource_data_by_id(language, pe_resource_viewer::resource_string, id).get_data()); +} + +//Helper function of parsing string list table +const resource_string_list resource_string_table_reader::parse_string_list(uint32_t id, const std::string& resource_data) +{ + resource_string_list ret; + + //16 is maximum count of strings in a string table + static const unsigned long max_string_list_entries = 16; + unsigned long passed_bytes = 0; + for(unsigned long i = 0; i != max_string_list_entries; ++i) + { + //Check resource data length + if(resource_data.length() < sizeof(uint16_t) + passed_bytes) + throw pe_exception("Incorrect resource string table", pe_exception::resource_incorrect_string_table); + + //Get string length - the first WORD + uint16_t string_length = *reinterpret_cast<const uint16_t*>(resource_data.data() + passed_bytes); + passed_bytes += sizeof(uint16_t); //WORD containing string length + + //Check resource data length again + if(resource_data.length() < string_length + passed_bytes) + throw pe_exception("Incorrect resource string table", pe_exception::resource_incorrect_string_table); + + if(string_length) + { + //Create and save string (UNICODE) +#ifdef PE_BLISS_WINDOWS + ret.insert( + std::make_pair(static_cast<uint16_t>(((id - 1) << 4) + i), //ID of string is calculated such way + std::wstring(reinterpret_cast<const wchar_t*>(resource_data.data() + passed_bytes), string_length))); +#else + ret.insert( + std::make_pair(static_cast<uint16_t>(((id - 1) << 4) + i), //ID of string is calculated such way + pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + passed_bytes), string_length)))); +#endif + } + + //Go to next string + passed_bytes += string_length * 2; + } + + return ret; +} + +//Returns string from string table by ID and language +const std::wstring resource_string_table_reader::get_string_by_id_lang(uint32_t language, uint16_t id) const +{ + //List strings by string table id and language + const resource_string_list strings(get_string_table_by_id_lang(language, (id >> 4) + 1)); + resource_string_list::const_iterator it = strings.find(id); //Find string by id + if(it == strings.end()) + throw pe_exception("Resource string not found", pe_exception::resource_string_not_found); + + return (*it).second; +} + +//Returns string from string table by ID and index in language directory (instead of language) +const std::wstring resource_string_table_reader::get_string_by_id(uint16_t id, uint32_t index) const +{ + //List strings by string table id and index + const resource_string_list strings(get_string_table_by_id((id >> 4) + 1, index)); + resource_string_list::const_iterator it = strings.find(id); //Find string by id + if(it == strings.end()) + throw pe_exception("Resource string not found", pe_exception::resource_string_not_found); + + return (*it).second; +} +} diff --git a/tools/pe_bliss/resource_string_table_reader.h b/tools/pe_bliss/resource_string_table_reader.h new file mode 100644 index 0000000000..e3ded1da85 --- /dev/null +++ b/tools/pe_bliss/resource_string_table_reader.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <map> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +//ID; string +typedef std::map<uint16_t, std::wstring> resource_string_list; + +class resource_string_table_reader +{ +public: + resource_string_table_reader(const pe_resource_viewer& res); + +public: + //Returns string table data by ID and language + const resource_string_list get_string_table_by_id_lang(uint32_t language, uint32_t id) const; + //Returns string table data by ID and index in language directory (instead of language) + const resource_string_list get_string_table_by_id(uint32_t id, uint32_t index = 0) const; + //Returns string from string table by ID and language + const std::wstring get_string_by_id_lang(uint32_t language, uint16_t id) const; + //Returns string from string table by ID and index in language directory (instead of language) + const std::wstring get_string_by_id(uint16_t id, uint32_t index = 0) const; + +private: + const pe_resource_viewer& res_; + + //Helper function of parsing string list table + //Id of resource is needed to calculate string IDs correctly + //resource_data is raw string table resource data + static const resource_string_list parse_string_list(uint32_t id, const std::string& resource_data); +}; +} diff --git a/tools/pe_bliss/resource_version_info_reader.cpp b/tools/pe_bliss/resource_version_info_reader.cpp new file mode 100644 index 0000000000..8ad44c6856 --- /dev/null +++ b/tools/pe_bliss/resource_version_info_reader.cpp @@ -0,0 +1,311 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "resource_version_info_reader.h" +#include "utils.h" +#include "pe_exception.h" +#include "resource_internal.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Root version info block key value +const u16string resource_version_info_reader::version_info_key(U16TEXT("V\0S\0_\0V\0E\0R\0S\0I\0O\0N\0_\0I\0N\0F\0O\0\0")); + +resource_version_info_reader::resource_version_info_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Returns aligned version block value position +uint32_t resource_version_info_reader::get_version_block_value_pos(uint32_t base_pos, const unicode16_t* key) +{ + uint32_t string_length = static_cast<uint32_t>(u16string(key).length()); + uint32_t ret = pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 /* headers before Key data */ + + base_pos + + (string_length + 1 /* nullbyte */) * 2), + sizeof(uint32_t)); + + //Check possible overflows + if(ret < base_pos || ret < sizeof(uint16_t) * 3 || ret < (string_length + 1) * 2) + throw_incorrect_version_info(); + + return ret; +} + +//Returns aligned version block first child position +uint32_t resource_version_info_reader::get_version_block_first_child_pos(uint32_t base_pos, uint32_t value_length, const unicode16_t* key) +{ + uint32_t string_length = static_cast<uint32_t>(u16string(key).length()); + uint32_t ret = pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 /* headers before Key data */ + + base_pos + + (string_length + 1 /* nullbyte */) * 2), + sizeof(uint32_t)) + + pe_utils::align_up(value_length, sizeof(uint32_t)); + + //Check possible overflows + if(ret < base_pos || ret < value_length || ret < sizeof(uint16_t) * 3 || ret < (string_length + 1) * 2) + throw_incorrect_version_info(); + + return ret; +} + +//Throws an exception (id = resource_incorrect_version_info) +void resource_version_info_reader::throw_incorrect_version_info() +{ + throw pe_exception("Incorrect resource version info", pe_exception::resource_incorrect_version_info); +} + +//Returns full version information: +//file_version_info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +const file_version_info resource_version_info_reader::get_version_info(lang_string_values_map& string_values, translation_values_map& translations, const std::string& resource_data) const +{ + //Fixed file version info + file_version_info ret; + + //Check resource data length + if(resource_data.length() < sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Root version info block + const version_info_block* root_block = reinterpret_cast<const version_info_block*>(resource_data.data()); + + //Check root block key for null-termination and its name + if(!pe_utils::is_null_terminated(root_block->Key, resource_data.length() - sizeof(uint16_t) * 3 /* headers before Key data */) + || version_info_key != reinterpret_cast<const unicode16_t*>(root_block->Key)) + throw_incorrect_version_info(); + + //If file has fixed version info + if(root_block->ValueLength) + { + //Get root block value position + uint32_t value_pos = get_version_block_value_pos(0, reinterpret_cast<const unicode16_t*>(root_block->Key)); + //Check value length + if(resource_data.length() < value_pos + sizeof(vs_fixedfileinfo)) + throw_incorrect_version_info(); + + //Get VS_FIXEDFILEINFO structure pointer + const vs_fixedfileinfo* file_info = reinterpret_cast<const vs_fixedfileinfo*>(resource_data.data() + value_pos); + //Check its signature and some other fields + if(file_info->dwSignature != vs_ffi_signature || file_info->dwStrucVersion != vs_ffi_strucversion) //Don't check if file_info->dwFileFlagsMask == VS_FFI_FILEFLAGSMASK + throw_incorrect_version_info(); + + //Save fixed version info + ret = file_version_info(*file_info); + } + + //Iterate over child elements of VS_VERSIONINFO (StringFileInfo or VarFileInfo) + for(uint32_t child_pos = get_version_block_first_child_pos(0, root_block->ValueLength, reinterpret_cast<const unicode16_t*>(root_block->Key)); + child_pos < root_block->Length;) + { + //Check block position + if(!pe_utils::is_sum_safe(child_pos, sizeof(version_info_block)) + || resource_data.length() < child_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer + const version_info_block* block = reinterpret_cast<const version_info_block*>(resource_data.data() + child_pos); + + //Check its length + if(block->Length == 0) + throw_incorrect_version_info(); + + //Check block key for null-termination + if(!pe_utils::is_null_terminated(block->Key, resource_data.length() - child_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + u16string info_type(reinterpret_cast<const unicode16_t*>(block->Key)); + //If we encountered StringFileInfo... + if(info_type == StringFileInfo) + { + //Enumerate all string tables + for(uint32_t string_table_pos = get_version_block_first_child_pos(child_pos, block->ValueLength, reinterpret_cast<const unicode16_t*>(block->Key)); + string_table_pos - child_pos < block->Length;) + { + //Check string table block position + if(resource_data.length() < string_table_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer for string table + const version_info_block* string_table = reinterpret_cast<const version_info_block*>(resource_data.data() + string_table_pos); + + //Check its length + if(string_table->Length == 0) + throw_incorrect_version_info(); + + //Check string table key for null-termination + if(!pe_utils::is_null_terminated(string_table->Key, resource_data.length() - string_table_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + string_values_map new_values; + + //Enumerate all strings in the string table + for(uint32_t string_pos = get_version_block_first_child_pos(string_table_pos, string_table->ValueLength, reinterpret_cast<const unicode16_t*>(string_table->Key)); + string_pos - string_table_pos < string_table->Length;) + { + //Check string block position + if(resource_data.length() < string_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer for string block + const version_info_block* string_block = reinterpret_cast<const version_info_block*>(resource_data.data() + string_pos); + + //Check its length + if(string_block->Length == 0) + throw_incorrect_version_info(); + + //Check string block key for null-termination + if(!pe_utils::is_null_terminated(string_block->Key, resource_data.length() - string_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + u16string data; + //If string block has value + if(string_block->ValueLength != 0) + { + //Get value position + uint32_t value_pos = get_version_block_value_pos(string_pos, reinterpret_cast<const unicode16_t*>(string_block->Key)); + //Check it + if(resource_data.length() < value_pos + string_block->ValueLength) + throw pe_exception("Incorrect resource version info", pe_exception::resource_incorrect_version_info); + + //Get UNICODE string value + data = u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + value_pos), string_block->ValueLength); + pe_utils::strip_nullbytes(data); + } + + //Save name-value pair +#ifdef PE_BLISS_WINDOWS + new_values.insert(std::make_pair(reinterpret_cast<const unicode16_t*>(string_block->Key), data)); +#else + new_values.insert(std::make_pair(pe_utils::from_ucs2(reinterpret_cast<const unicode16_t*>(string_block->Key)), + pe_utils::from_ucs2(data))); +#endif + + //Navigate to next string block + string_pos += pe_utils::align_up(string_block->Length, sizeof(uint32_t)); + } + +#ifdef PE_BLISS_WINDOWS + string_values.insert(std::make_pair(reinterpret_cast<const unicode16_t*>(string_table->Key), new_values)); +#else + string_values.insert(std::make_pair(pe_utils::from_ucs2(reinterpret_cast<const unicode16_t*>(string_table->Key)), new_values)); +#endif + + //Navigate to next string table block + string_table_pos += pe_utils::align_up(string_table->Length, sizeof(uint32_t)); + } + } + else if(info_type == VarFileInfo) //If we encountered VarFileInfo + { + for(uint32_t var_table_pos = get_version_block_first_child_pos(child_pos, block->ValueLength, reinterpret_cast<const unicode16_t*>(block->Key)); + var_table_pos - child_pos < block->Length;) + { + //Check var block position + if(resource_data.length() < var_table_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer for var block + const version_info_block* var_table = reinterpret_cast<const version_info_block*>(resource_data.data() + var_table_pos); + + //Check its length + if(var_table->Length == 0) + throw_incorrect_version_info(); + + //Check its key for null-termination + if(!pe_utils::is_null_terminated(var_table->Key, resource_data.length() - var_table_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + //If block is "Translation" (actually, there's no other types possible in VarFileInfo) and it has value + if(u16string(reinterpret_cast<const unicode16_t*>(var_table->Key)) == Translation && var_table->ValueLength) + { + //Get its value position + uint32_t value_pos = get_version_block_value_pos(var_table_pos, reinterpret_cast<const unicode16_t*>(var_table->Key)); + //Cherck value length + if(resource_data.length() < value_pos + var_table->ValueLength) + throw_incorrect_version_info(); + + //Get list of translations: pairs of LANGUAGE_ID - CODEPAGE_ID + for(unsigned long i = 0; i < var_table->ValueLength; i += sizeof(uint16_t) * 2) + { + //Pair of WORDs + uint16_t lang_id = *reinterpret_cast<const uint16_t*>(resource_data.data() + value_pos + i); + uint16_t codepage_id = *reinterpret_cast<const uint16_t*>(resource_data.data() + value_pos + sizeof(uint16_t) + i); + //Save translation + translations.insert(std::make_pair(lang_id, codepage_id)); + } + } + + //Navigate to next var block + var_table_pos += pe_utils::align_up(var_table->Length, sizeof(uint32_t)); + } + } + else + { + throw_incorrect_version_info(); + } + + //Navigate to next element in root block + child_pos += pe_utils::align_up(block->Length, sizeof(uint32_t)); + } + + return ret; +} + +//Returns full version information: +//file_version info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +const file_version_info resource_version_info_reader::get_version_info_by_lang(lang_string_values_map& string_values, translation_values_map& translations, uint32_t language) const +{ + const std::string& resource_data = res_.get_root_directory() //Type directory + .entry_by_id(pe_resource_viewer::resource_version) + .get_resource_directory() //Name/ID directory + .entry_by_id(1) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry() //Data directory + .get_data(); + + return get_version_info(string_values, translations, resource_data); +} + +//Returns full version information: +//file_version_info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +const file_version_info resource_version_info_reader::get_version_info(lang_string_values_map& string_values, translation_values_map& translations, uint32_t index) const +{ + const resource_directory::entry_list& entries = res_.get_root_directory() //Type directory + .entry_by_id(pe_resource_viewer::resource_version) + .get_resource_directory() //Name/ID directory + .entry_by_id(1) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return get_version_info(string_values, translations, entries.at(index).get_data_entry().get_data()); //Data directory +} +} diff --git a/tools/pe_bliss/resource_version_info_reader.h b/tools/pe_bliss/resource_version_info_reader.h new file mode 100644 index 0000000000..c1dfbffdc2 --- /dev/null +++ b/tools/pe_bliss/resource_version_info_reader.h @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <map> +#include "file_version_info.h" +#include "pe_structures.h" +#include "version_info_types.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +class resource_version_info_reader +{ +public: //VERSION INFO + resource_version_info_reader(const pe_resource_viewer& res); + + //Returns full version information: + //file_version_info: versions and file info + //lang_lang_string_values_map: map of version info strings with encodings with encodings + //translation_values_map: map of translations + const file_version_info get_version_info(lang_string_values_map& string_values, translation_values_map& translations, uint32_t index = 0) const; + const file_version_info get_version_info_by_lang(lang_string_values_map& string_values, translation_values_map& translations, uint32_t language) const; + +public: + //L"VS_VERSION_INFO" key of root version info block + static const u16string version_info_key; + +private: + const pe_resource_viewer& res_; + + //VERSION INFO helpers + //Returns aligned version block value position + static uint32_t get_version_block_value_pos(uint32_t base_pos, const unicode16_t* key); + + //Returns aligned version block first child position + static uint32_t get_version_block_first_child_pos(uint32_t base_pos, uint32_t value_length, const unicode16_t* key); + + //Returns full version information: + //file_version_info: versions and file info + //lang_string_values_map: map of version info strings with encodings + //translation_values_map: map of translations + const file_version_info get_version_info(lang_string_values_map& string_values, translation_values_map& translations, const std::string& resource_data) const; + + //Throws an exception (id = resource_incorrect_version_info) + static void throw_incorrect_version_info(); +}; +} diff --git a/tools/pe_bliss/resource_version_info_writer.cpp b/tools/pe_bliss/resource_version_info_writer.cpp new file mode 100644 index 0000000000..ed95a0f7ea --- /dev/null +++ b/tools/pe_bliss/resource_version_info_writer.cpp @@ -0,0 +1,283 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "resource_version_info_writer.h" +#include "pe_structures.h" +#include "resource_internal.h" +#include "utils.h" +#include "pe_resource_manager.h" +#include "resource_version_info_reader.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_version_info_writer::resource_version_info_writer(pe_resource_manager& res) + :res_(res) +{} + +//Sets/replaces full version information: +//file_version_info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +void resource_version_info_writer::set_version_info(const file_version_info& file_info, + const lang_string_values_map& string_values, + const translation_values_map& translations, + uint32_t language, + uint32_t codepage, + uint32_t timestamp) +{ + std::string version_data; + + //Calculate total size of version resource data + uint32_t total_version_info_length = + static_cast<uint32_t>(sizeof(version_info_block) - sizeof(uint16_t) + sizeof(uint16_t) /* pading */ + + (resource_version_info_reader::version_info_key.length() + 1) * 2 + + sizeof(vs_fixedfileinfo)); + + //If we have any strings values + if(!string_values.empty()) + { + total_version_info_length += sizeof(version_info_block) - sizeof(uint16_t); //StringFileInfo block + total_version_info_length += SizeofStringFileInfo; //Name of block (key) + + //Add required size for version strings + for(lang_string_values_map::const_iterator table_it = string_values.begin(); table_it != string_values.end(); ++table_it) + { + total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 + ((*table_it).first.length() + 1) * 2), sizeof(uint32_t)); //Name of child block and block size (key of string table block) + + const string_values_map& values = (*table_it).second; + for(string_values_map::const_iterator it = values.begin(); it != values.end(); ++it) + { + total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 + ((*it).first.length() + 1) * 2), sizeof(uint32_t)); + total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(((*it).second.length() + 1) * 2), sizeof(uint32_t)); + } + } + } + + //If we have translations + if(!translations.empty()) + { + total_version_info_length += (sizeof(version_info_block) - sizeof(uint16_t)) * 2; //VarFileInfo and Translation blocks + total_version_info_length += SizeofVarFileInfoAligned; //DWORD-aligned VarFileInfo block name + total_version_info_length += SizeofTranslationAligned; //DWORD-aligned Translation block name + total_version_info_length += static_cast<uint32_t>(translations.size() * sizeof(uint16_t) * 2); + } + + //Resize version data buffer + version_data.resize(total_version_info_length); + + //Create root version block + version_info_block root_block = {0}; + root_block.ValueLength = sizeof(vs_fixedfileinfo); + root_block.Length = static_cast<uint16_t>(total_version_info_length); + + //Fill fixed file info + vs_fixedfileinfo fixed_info = {0}; + fixed_info.dwFileDateLS = file_info.get_file_date_ls(); + fixed_info.dwFileDateMS = file_info.get_file_date_ms(); + fixed_info.dwFileFlags = file_info.get_file_flags(); + fixed_info.dwFileFlagsMask = vs_ffi_fileflagsmask; + fixed_info.dwFileOS = file_info.get_file_os_raw(); + fixed_info.dwFileSubtype = file_info.get_file_subtype(); + fixed_info.dwFileType = file_info.get_file_type_raw(); + fixed_info.dwFileVersionLS = file_info.get_file_version_ls(); + fixed_info.dwFileVersionMS = file_info.get_file_version_ms(); + fixed_info.dwSignature = vs_ffi_signature; + fixed_info.dwStrucVersion = vs_ffi_strucversion; + fixed_info.dwProductVersionLS = file_info.get_product_version_ls(); + fixed_info.dwProductVersionMS = file_info.get_product_version_ms(); + + //Write root block and fixed file info to buffer + uint32_t data_ptr = 0; + memcpy(&version_data[data_ptr], &root_block, sizeof(version_info_block) - sizeof(uint16_t)); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + memcpy(&version_data[data_ptr], resource_version_info_reader::version_info_key.c_str(), (resource_version_info_reader::version_info_key.length() + 1) * sizeof(uint16_t)); + data_ptr += static_cast<uint32_t>((resource_version_info_reader::version_info_key.length() + 1) * sizeof(uint16_t)); + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + memcpy(&version_data[data_ptr], &fixed_info, sizeof(fixed_info)); + data_ptr += sizeof(fixed_info); + + //Write string values, if any + if(!string_values.empty()) + { + //Create string file info root block + version_info_block string_file_info_block = {0}; + string_file_info_block.Type = 1; //Block type is string + memcpy(&version_data[data_ptr], &string_file_info_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* string_file_info_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr1 = data_ptr; //Used to calculate string file info block length later + memcpy(&version_data[data_ptr], StringFileInfo, SizeofStringFileInfo); //Write block name + data_ptr += SizeofStringFileInfo; + + //Create string table root block (child of string file info) + version_info_block string_table_block = {0}; + string_table_block.Type = 1; //Block type is string + + for(lang_string_values_map::const_iterator table_it = string_values.begin(); table_it != string_values.end(); ++table_it) + { + const string_values_map& values = (*table_it).second; + + memcpy(&version_data[data_ptr], &string_table_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* string_table_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr2 = data_ptr; //Used to calculate string table block length later + uint32_t lang_key_length = static_cast<uint32_t>(((*table_it).first.length() + 1) * sizeof(uint16_t)); + +#ifdef PE_BLISS_WINDOWS + memcpy(&version_data[data_ptr], (*table_it).first.c_str(), lang_key_length); //Write block key +#else + { + u16string str(pe_utils::to_ucs2((*table_it).first)); + memcpy(&version_data[data_ptr], str.c_str(), lang_key_length); //Write block key + } +#endif + + data_ptr += lang_key_length; + //Align key if necessary + if((sizeof(uint16_t) * 3 + lang_key_length) % sizeof(uint32_t)) + { + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + } + + //Create string block (child of string table block) + version_info_block string_block = {0}; + string_block.Type = 1; //Block type is string + for(string_values_map::const_iterator it = values.begin(); it != values.end(); ++it) + { + //Calculate value length and key length of string block + string_block.ValueLength = static_cast<uint16_t>((*it).second.length() + 1); + uint32_t key_length = static_cast<uint32_t>(((*it).first.length() + 1) * sizeof(uint16_t)); + //Calculate length of block + string_block.Length = static_cast<uint16_t>(pe_utils::align_up(sizeof(uint16_t) * 3 + key_length, sizeof(uint32_t)) + string_block.ValueLength * sizeof(uint16_t)); + + //Write string block + memcpy(&version_data[data_ptr], &string_block, sizeof(version_info_block) - sizeof(uint16_t)); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + +#ifdef PE_BLISS_WINDOWS + memcpy(&version_data[data_ptr], (*it).first.c_str(), key_length); //Write block key +#else + { + u16string str(pe_utils::to_ucs2((*it).first)); + memcpy(&version_data[data_ptr], str.c_str(), key_length); //Write block key + } +#endif + + data_ptr += key_length; + //Align key if necessary + if((sizeof(uint16_t) * 3 + key_length) % sizeof(uint32_t)) + { + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + } + + //Write block data (value) +#ifdef PE_BLISS_WINDOWS + memcpy(&version_data[data_ptr], (*it).second.c_str(), string_block.ValueLength * sizeof(uint16_t)); +#else + { + u16string str(pe_utils::to_ucs2((*it).second)); + memcpy(&version_data[data_ptr], str.c_str(), string_block.ValueLength * sizeof(uint16_t)); + } +#endif + + data_ptr += string_block.ValueLength * 2; + //Align data if necessary + if((string_block.ValueLength * 2) % sizeof(uint32_t)) + { + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + } + } + + //Calculate string table and string file info blocks lengths + string_table_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr2 + sizeof(uint16_t) * 3); + } + + string_file_info_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr1 + sizeof(uint16_t) * 3); + } + + //If we have transactions + if(!translations.empty()) + { + //Create root var file info block + version_info_block var_file_info_block = {0}; + var_file_info_block.Type = 1; //Type of block is string + //Write block header + memcpy(&version_data[data_ptr], &var_file_info_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* var_file_info_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr1 = data_ptr; //Used to calculate var file info block length later + memcpy(&version_data[data_ptr], VarFileInfoAligned, SizeofVarFileInfoAligned); //Write block key (aligned) + data_ptr += SizeofVarFileInfoAligned; + + //Create root translation block (child of var file info block) + version_info_block translation_block = {0}; + //Write block header + memcpy(&version_data[data_ptr], &translation_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* translation_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr2 = data_ptr; //Used to calculate var file info block length later + memcpy(&version_data[data_ptr], TranslationAligned, SizeofTranslationAligned); //Write block key (aligned) + data_ptr += SizeofTranslationAligned; + + //Calculate translation block value length + translation_block_ptr->ValueLength = static_cast<uint16_t>(sizeof(uint16_t) * 2 * translations.size()); + + //Write translation values to block + for(translation_values_map::const_iterator it = translations.begin(); it != translations.end(); ++it) + { + uint16_t lang_id = (*it).first; //Language ID + uint16_t codepage_id = (*it).second; //Codepage ID + memcpy(&version_data[data_ptr], &lang_id, sizeof(lang_id)); + data_ptr += sizeof(lang_id); + memcpy(&version_data[data_ptr], &codepage_id, sizeof(codepage_id)); + data_ptr += sizeof(codepage_id); + } + + //Calculate Translation and VarFileInfo blocks lengths + translation_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr2 + sizeof(uint16_t) * 3); + var_file_info_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr1 + sizeof(uint16_t) * 3); + } + + //Add/replace version info resource + res_.add_resource(version_data, pe_resource_viewer::resource_version, 1, language, codepage, timestamp); +} + +//Removes version info by language (ID = 1) +bool resource_version_info_writer::remove_version_info(uint32_t language) +{ + return res_.remove_resource(pe_resource_viewer::resource_version, 1, language); +} +} diff --git a/tools/pe_bliss/resource_version_info_writer.h b/tools/pe_bliss/resource_version_info_writer.h new file mode 100644 index 0000000000..da279ddedb --- /dev/null +++ b/tools/pe_bliss/resource_version_info_writer.h @@ -0,0 +1,52 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "version_info_types.h" +#include "file_version_info.h" + +namespace pe_bliss +{ +class pe_resource_manager; + +class resource_version_info_writer +{ +public: + resource_version_info_writer(pe_resource_manager& res); + + //Sets/replaces full version information: + //file_version_info: versions and file info + //lang_string_values_map: map of version info strings with encodings + //translation_values_map: map of translations + void set_version_info(const file_version_info& file_info, + const lang_string_values_map& string_values, + const translation_values_map& translations, + uint32_t language, + uint32_t codepage = 0, + uint32_t timestamp = 0); + + //Removes version info by language (ID = 1) + bool remove_version_info(uint32_t language); + +private: + pe_resource_manager& res_; +}; +} diff --git a/tools/pe_bliss/stdint_defs.h b/tools/pe_bliss/stdint_defs.h new file mode 100644 index 0000000000..bbc003690a --- /dev/null +++ b/tools/pe_bliss/stdint_defs.h @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#ifdef _MSC_VER +#if _MSC_VER < 1600 +namespace pe_bliss +{ + //stdint.h definitions for MSVC 2008 and earlier, as + //it doesn't have them + typedef signed char int8_t; + typedef short int16_t; + typedef int int32_t; + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + + typedef long long int64_t; + typedef unsigned long long uint64_t; +} +#else +#include <stdint.h> +#endif +#else +#include <stdint.h> +#endif diff --git a/tools/pe_bliss/utils.cpp b/tools/pe_bliss/utils.cpp new file mode 100644 index 0000000000..e6a75d5497 --- /dev/null +++ b/tools/pe_bliss/utils.cpp @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "utils.h" +#include "pe_exception.h" + + +namespace pe_bliss +{ +const double pe_utils::log_2 = 1.44269504088896340736; //instead of using M_LOG2E + +//Returns stream size +std::streamoff pe_utils::get_file_size(std::istream& file) +{ + //Get old istream offset + std::streamoff old_offset = file.tellg(); + file.seekg(0, std::ios::end); + std::streamoff filesize = file.tellg(); + //Set old istream offset + file.seekg(old_offset); + return filesize; +} + +#ifndef PE_BLISS_WINDOWS +const u16string pe_utils::to_ucs2(const std::wstring& str) +{ + u16string ret; + if(str.empty()) + return ret; + + int len = str.length(); + + ret.resize(len); + + for(int i=0;i<len;i++) { + ret[i]=str[i]&0xFFFF; + } + + return ret; +} + +const std::wstring pe_utils::from_ucs2(const u16string& str) +{ + std::wstring ret; + if(str.empty()) + return ret; + + int len = str.length(); + ret.resize(str.length()); + + for(int i=0;i<len;i++) { + ret[i]=str[i]; + } + + return ret; +} +#endif + +bool operator==(const pe_win::guid& guid1, const pe_win::guid& guid2) +{ + return guid1.Data1 == guid2.Data1 + && guid1.Data2 == guid2.Data2 + && guid1.Data3 == guid2.Data3 + && !memcmp(guid1.Data4, guid2.Data4, sizeof(guid1.Data4)); +} +} diff --git a/tools/pe_bliss/utils.h b/tools/pe_bliss/utils.h new file mode 100644 index 0000000000..29125f8dc1 --- /dev/null +++ b/tools/pe_bliss/utils.h @@ -0,0 +1,105 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <istream> +#include <string> +#include "stdint_defs.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +class pe_utils +{ +public: + //Returns true if string "data" with maximum length "raw_length" is null-terminated + template<typename T> + static bool is_null_terminated(const T* data, size_t raw_length) + { + raw_length /= sizeof(T); + for(size_t l = 0; l < raw_length; l++) + { + if(data[l] == static_cast<T>(L'\0')) + return true; + } + + return false; + } + + //Helper template function to strip nullbytes in the end of string + template<typename T> + static void strip_nullbytes(std::basic_string<T>& str) + { + while(!*(str.end() - 1) && !str.empty()) + str.erase(str.length() - 1); + } + + //Helper function to determine if number is power of 2 + template<typename T> + static inline bool is_power_of_2(T x) + { + return !(x & (x - 1)); + } + + //Helper function to align number down + template<typename T> + static inline T align_down(T x, uint32_t align) + { + return x & ~(static_cast<T>(align) - 1); + } + + //Helper function to align number up + template<typename T> + static inline T align_up(T x, uint32_t align) + { + return (x & static_cast<T>(align - 1)) ? align_down(x, align) + static_cast<T>(align) : x; + } + + //Returns true if sum of two unsigned integers is safe (no overflow occurs) + static inline bool is_sum_safe(uint32_t a, uint32_t b) + { + return a <= static_cast<uint32_t>(-1) - b; + } + + //Two gigabytes value in bytes + static const uint32_t two_gb = 0x80000000; + static const uint32_t max_dword = 0xFFFFFFFF; + static const uint32_t max_word = 0x0000FFFF; + static const double log_2; //instead of using M_LOG2E + + //Returns stream size + static std::streamoff get_file_size(std::istream& file); + +#ifndef PE_BLISS_WINDOWS +public: + static const u16string to_ucs2(const std::wstring& str); + static const std::wstring from_ucs2(const u16string& str); +#endif + +private: + pe_utils(); + pe_utils(pe_utils&); + pe_utils& operator=(const pe_utils&); +}; + +//Windows GUID comparison +bool operator==(const pe_win::guid& guid1, const pe_win::guid& guid2); +} diff --git a/tools/pe_bliss/version_info_editor.cpp b/tools/pe_bliss/version_info_editor.cpp new file mode 100644 index 0000000000..199eebfd54 --- /dev/null +++ b/tools/pe_bliss/version_info_editor.cpp @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <sstream> +#include <iomanip> +#include "version_info_types.h" +#include "version_info_editor.h" +#include "version_info_viewer.h" + +namespace pe_bliss +{ +//Default constructor +//strings - version info strings with charsets +//translations - version info translations map +version_info_editor::version_info_editor(lang_string_values_map& strings, translation_values_map& translations) + :version_info_viewer(strings, translations), + strings_edit_(strings), + translations_edit_(translations) +{} + +//Below functions have parameter translation +//If it's empty, the default language translation will be taken +//If there's no default language translation, the first one will be taken + +//Sets company name +void version_info_editor::set_company_name(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"CompanyName", value, translation); +} + +//Sets file description +void version_info_editor::set_file_description(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"FileDescription", value, translation); +} + +//Sets file version +void version_info_editor::set_file_version(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"FileVersion", value, translation); +} + +//Sets internal file name +void version_info_editor::set_internal_name(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"InternalName", value, translation); +} + +//Sets legal copyright +void version_info_editor::set_legal_copyright(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"LegalCopyright", value, translation); +} + +//Sets original file name +void version_info_editor::set_original_filename(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"OriginalFilename", value, translation); +} + +//Sets product name +void version_info_editor::set_product_name(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"ProductName", value, translation); +} + +//Sets product version +void version_info_editor::set_product_version(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"ProductVersion", value, translation); +} + +//Sets version info property value +//property_name - property name +//value - property value +//If translation does not exist, it will be added +//If property does not exist, it will be added +void version_info_editor::set_property(const std::wstring& property_name, const std::wstring& value, const std::wstring& translation) +{ + lang_string_values_map::iterator it = strings_edit_.begin(); + + if(translation.empty()) + { + //If no translation was specified + it = strings_edit_.find(default_language_translation); //Find default translation table + if(it == strings_edit_.end()) //If there's no default translation table, take the first one + { + it = strings_edit_.begin(); + if(it == strings_edit_.end()) //If there's no any translation table, add default one + { + it = strings_edit_.insert(std::make_pair(default_language_translation, string_values_map())).first; + //Also add it to translations list + add_translation(default_language_translation); + } + } + } + else + { + it = strings_edit_.find(translation); //Find specified translation table + if(it == strings_edit_.end()) //If there's no translation, add it + { + it = strings_edit_.insert(std::make_pair(translation, string_values_map())).first; + //Also add it to translations list + add_translation(translation); + } + } + + //Change value of the required property + ((*it).second)[property_name] = value; +} + +//Adds translation to translation list +void version_info_editor::add_translation(const std::wstring& translation) +{ + std::pair<uint16_t, uint16_t> translation_ids(translation_from_string(translation)); + add_translation(translation_ids.first, translation_ids.second); +} + +void version_info_editor::add_translation(uint16_t language_id, uint16_t codepage_id) +{ + std::pair<translation_values_map::const_iterator, translation_values_map::const_iterator> + range(translations_edit_.equal_range(language_id)); + + //If translation already exists + for(translation_values_map::const_iterator it = range.first; it != range.second; ++it) + { + if((*it).second == codepage_id) + return; + } + + translations_edit_.insert(std::make_pair(language_id, codepage_id)); +} + +//Removes translation from translations and strings lists +void version_info_editor::remove_translation(const std::wstring& translation) +{ + std::pair<uint16_t, uint16_t> translation_ids(translation_from_string(translation)); + remove_translation(translation_ids.first, translation_ids.second); +} + +void version_info_editor::remove_translation(uint16_t language_id, uint16_t codepage_id) +{ + { + //Erase string table (if exists) + std::wstringstream ss; + ss << std::hex + << std::setw(4) << std::setfill(L'0') << language_id + << std::setw(4) << std::setfill(L'0') << codepage_id; + + strings_edit_.erase(ss.str()); + } + + //Find and erase translation from translations table + std::pair<translation_values_map::iterator, translation_values_map::iterator> + it_pair = translations_edit_.equal_range(language_id); + + for(translation_values_map::iterator it = it_pair.first; it != it_pair.second; ++it) + { + if((*it).second == codepage_id) + { + translations_edit_.erase(it); + break; + } + } +} +} diff --git a/tools/pe_bliss/version_info_editor.h b/tools/pe_bliss/version_info_editor.h new file mode 100644 index 0000000000..53d3dc62c1 --- /dev/null +++ b/tools/pe_bliss/version_info_editor.h @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "version_info_types.h" +#include "version_info_viewer.h" + +namespace pe_bliss +{ + //Helper class to read and edit version information + //lang_string_values_map: map of version info strings with encodings + //translation_values_map: map of translations + class version_info_editor : public version_info_viewer + { + public: + //Default constructor + //strings - version info strings with charsets + //translations - version info translations map + version_info_editor(lang_string_values_map& strings, translation_values_map& translations); + + //Below functions have parameter translation + //If it's empty, the default language translation will be taken + //If there's no default language translation, the first one will be taken + + //Sets company name + void set_company_name(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets file description + void set_file_description(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets file version + void set_file_version(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets internal file name + void set_internal_name(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets legal copyright + void set_legal_copyright(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets original file name + void set_original_filename(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets product name + void set_product_name(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets product version + void set_product_version(const std::wstring& value, const std::wstring& translation = std::wstring()); + + //Sets version info property value + //property_name - property name + //value - property value + //If translation does not exist, it will be added to strings and translations lists + //If property does not exist, it will be added + void set_property(const std::wstring& property_name, const std::wstring& value, const std::wstring& translation = std::wstring()); + + //Adds translation to translation list + void add_translation(const std::wstring& translation); + void add_translation(uint16_t language_id, uint16_t codepage_id); + + //Removes translation from translations and strings lists + void remove_translation(const std::wstring& translation); + void remove_translation(uint16_t language_id, uint16_t codepage_id); + + private: + lang_string_values_map& strings_edit_; + translation_values_map& translations_edit_; + }; +} diff --git a/tools/pe_bliss/version_info_types.h b/tools/pe_bliss/version_info_types.h new file mode 100644 index 0000000000..6010c9691e --- /dev/null +++ b/tools/pe_bliss/version_info_types.h @@ -0,0 +1,38 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <map> +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ + //Typedef for version info functions: Name - Value + typedef std::map<std::wstring, std::wstring> string_values_map; + //Typedef for version info functions: Language string - String Values Map + //Language String consists of LangID and CharsetID + //E.g. 041904b0 for Russian UNICODE, 040004b0 for Process Default Language UNICODE + typedef std::map<std::wstring, string_values_map> lang_string_values_map; + + //Typedef for version info functions: Language - Character Set + typedef std::multimap<uint16_t, uint16_t> translation_values_map; +} diff --git a/tools/pe_bliss/version_info_viewer.cpp b/tools/pe_bliss/version_info_viewer.cpp new file mode 100644 index 0000000000..6e2d0d5c5b --- /dev/null +++ b/tools/pe_bliss/version_info_viewer.cpp @@ -0,0 +1,180 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <iomanip> +#include <sstream> +#include "pe_exception.h" +#include "version_info_viewer.h" + +namespace pe_bliss +{ +//Default process language, UNICODE +const std::wstring version_info_viewer::default_language_translation(L"040904b0"); + +//Default constructor +//strings - version info strings with charsets +//translations - version info translations map +version_info_viewer::version_info_viewer(const lang_string_values_map& strings, const translation_values_map& translations) + :strings_(strings), translations_(translations) +{} + +//Below functions have parameter translation +//If it's empty, the default language translation will be taken +//If there's no default language translation, the first one will be taken + +//Returns company name +const std::wstring version_info_viewer::get_company_name(const std::wstring& translation) const +{ + return get_property(L"CompanyName", translation); +} + +//Returns file description +const std::wstring version_info_viewer::get_file_description(const std::wstring& translation) const +{ + return get_property(L"FileDescription", translation); +} + +//Returns file version +const std::wstring version_info_viewer::get_file_version(const std::wstring& translation) const +{ + return get_property(L"FileVersion", translation); +} + +//Returns internal file name +const std::wstring version_info_viewer::get_internal_name(const std::wstring& translation) const +{ + return get_property(L"InternalName", translation); +} + +//Returns legal copyright +const std::wstring version_info_viewer::get_legal_copyright(const std::wstring& translation) const +{ + return get_property(L"LegalCopyright", translation); +} + +//Returns original file name +const std::wstring version_info_viewer::get_original_filename(const std::wstring& translation) const +{ + return get_property(L"OriginalFilename", translation); +} + +//Returns product name +const std::wstring version_info_viewer::get_product_name(const std::wstring& translation) const +{ + return get_property(L"ProductName", translation); +} + +//Returns product version +const std::wstring version_info_viewer::get_product_version(const std::wstring& translation) const +{ + return get_property(L"ProductVersion", translation); +} + +//Returns list of translations in string representation +const version_info_viewer::translation_list version_info_viewer::get_translation_list() const +{ + translation_list ret; + + //Enumerate all translations + for(translation_values_map::const_iterator it = translations_.begin(); it != translations_.end(); ++it) + { + //Create string representation of translation value + std::wstringstream ss; + ss << std::hex + << std::setw(4) << std::setfill(L'0') << (*it).first + << std::setw(4) << std::setfill(L'0') << (*it).second; + + //Save it + ret.push_back(ss.str()); + } + + return ret; +} + +//Returns version info property value +//property_name - required property name +//If throw_if_absent = true, will throw exception if property does not exist +//If throw_if_absent = false, will return empty string if property does not exist +const std::wstring version_info_viewer::get_property(const std::wstring& property_name, const std::wstring& translation, bool throw_if_absent) const +{ + std::wstring ret; + + //If there're no strings + if(strings_.empty()) + { + if(throw_if_absent) + throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); + + return ret; + } + + lang_string_values_map::const_iterator it = strings_.begin(); + + if(translation.empty()) + { + //If no translation was specified + it = strings_.find(default_language_translation); //Find default translation table + if(it == strings_.end()) //If there's no default translation table, take the first one + it = strings_.begin(); + } + else + { + it = strings_.find(translation); //Find specified translation table + if(it == strings_.end()) + { + if(throw_if_absent) + throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); + + return ret; + } + } + + //Find value of the required property + string_values_map::const_iterator str_it = (*it).second.find(property_name); + + if(str_it == (*it).second.end()) + { + if(throw_if_absent) + throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); + + return ret; + } + + ret = (*str_it).second; + + return ret; +} + +//Converts translation HEX-string to pair of language ID and codepage ID +const version_info_viewer::translation_pair version_info_viewer::translation_from_string(const std::wstring& translation) +{ + uint32_t translation_id = 0; + + { + //Convert string to DWORD + std::wstringstream ss; + ss << std::hex << translation; + ss >> translation_id; + } + + return std::make_pair(static_cast<uint16_t>(translation_id >> 16), static_cast<uint16_t>(translation_id & 0xFFFF)); +} +} diff --git a/tools/pe_bliss/version_info_viewer.h b/tools/pe_bliss/version_info_viewer.h new file mode 100644 index 0000000000..bc2f6f2ba7 --- /dev/null +++ b/tools/pe_bliss/version_info_viewer.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* 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, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* 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.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <map> +#include <vector> +#include <string> +#include "pe_resource_viewer.h" +#include "pe_structures.h" +#include "version_info_types.h" + +namespace pe_bliss +{ +//Helper class to read version information +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +class version_info_viewer +{ +public: + //Useful typedefs + typedef std::pair<uint16_t, uint16_t> translation_pair; + typedef std::vector<std::wstring> translation_list; + +public: + //Default constructor + //strings - version info strings with charsets + //translations - version info translations map + version_info_viewer(const lang_string_values_map& strings, const translation_values_map& translations); + + //Below functions have parameter translation + //If it's empty, the default language translation will be taken + //If there's no default language translation, the first one will be taken + + //Returns company name + const std::wstring get_company_name(const std::wstring& translation = std::wstring()) const; + //Returns file description + const std::wstring get_file_description(const std::wstring& translation = std::wstring()) const; + //Returns file version + const std::wstring get_file_version(const std::wstring& translation = std::wstring()) const; + //Returns internal file name + const std::wstring get_internal_name(const std::wstring& translation = std::wstring()) const; + //Returns legal copyright + const std::wstring get_legal_copyright(const std::wstring& translation = std::wstring()) const; + //Returns original file name + const std::wstring get_original_filename(const std::wstring& translation = std::wstring()) const; + //Returns product name + const std::wstring get_product_name(const std::wstring& translation = std::wstring()) const; + //Returns product version + const std::wstring get_product_version(const std::wstring& translation = std::wstring()) const; + + //Returns list of translations in string representation + const translation_list get_translation_list() const; + + //Returns version info property value + //property_name - required property name + //If throw_if_absent = true, will throw exception if property does not exist + //If throw_if_absent = false, will return empty string if property does not exist + const std::wstring get_property(const std::wstring& property_name, const std::wstring& translation = std::wstring(), bool throw_if_absent = false) const; + + //Converts translation HEX-string to pair of language ID and codepage ID + static const translation_pair translation_from_string(const std::wstring& translation); + +public: + //Default process language, UNICODE + static const std::wstring default_language_translation; + +private: + const lang_string_values_map& strings_; + const translation_values_map& translations_; +}; +} diff --git a/tools/script_plugins/time/time.gd b/tools/script_plugins/time/time.gd index 66b3e9ed04..2e56d89d4f 100644 --- a/tools/script_plugins/time/time.gd +++ b/tools/script_plugins/time/time.gd @@ -21,12 +21,12 @@ func _init(): timer.set_one_shot(false) timer.connect("timeout",self,"_timeout") -func _enter_scene(): +func _enter_tree(): label = Label.new() add_custom_control(CONTAINER_TOOLBAR,label) timer.start() -func _exit_scene(): +func _exit_tree(): timer.stop() label.free() label=null diff --git a/tools/scripts/file-hex-array.py b/tools/scripts/file-hex-array.py new file mode 100755 index 0000000000..05352396f1 --- /dev/null +++ b/tools/scripts/file-hex-array.py @@ -0,0 +1,52 @@ +import binascii +import os.path +import sys + +def tof(filepath): + with open(filepath, 'r') as f: + content = f.read() + content = content.replace("0x","") + content = content.split(',') + for i in range(len(content)): + if len(content[i]) == 1: content[i] = "0" + content[i] + content = "".join(content) + with open(filepath+".file", 'wb') as f: + content = f.write(content.decode("hex")) + print(os.path.basename(filepath)+".file created.") + exit(0) + +def toa(filepath): + with open(filepath, 'rb') as f: + content = f.read() + content = binascii.hexlify(content) + content = [content[i:i+2] for i in range(0, len(content), 2)] + content = ",0x".join(content) + content = "0x" + content + content = content.replace("0x00","0x0") + with open(filepath+".array", 'w') as f: + content = f.write(content) + print(os.path.basename(filepath)+".array created.") + exit(0) + +def usage(): + print("========================================================\n\ +#\n\ +# Usage: python file-hex-array.py [action] [option]\n\ +#\n\ +# Arguments:\n\ +# action ==> toa # convert file to array [option is file path]\n\ +# tof # convert array to file [option is array file path]\n\ +#\n\ +# Example : python file-hex-array.py toa 1.png\n\ +#\n\ +========================================================") + exit(1) + +if len(sys.argv) != 3: + usage() +if sys.argv[1] == "toa" and os.path.isfile(sys.argv[2]): + toa(sys.argv[2]) +elif sys.argv[1] == "tof" and os.path.isfile(sys.argv[2]): + tof(sys.argv[2]) +else: + usage()
\ No newline at end of file diff --git a/tools/scrits/makeargs.py b/tools/scripts/makeargs.py index 6f7afa6328..6f7afa6328 100644 --- a/tools/scrits/makeargs.py +++ b/tools/scripts/makeargs.py |