summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SConstruct24
-rw-r--r--bin/SCsub2
-rw-r--r--bin/tests/SCsub2
-rw-r--r--bin/tests/test_string.cpp73
-rw-r--r--core/SCsub2
-rw-r--r--core/bind/SCsub2
-rw-r--r--core/io/SCsub2
-rw-r--r--core/io/http_client.cpp7
-rw-r--r--core/io/http_client.h2
-rw-r--r--core/io/ip.cpp37
-rw-r--r--core/io/ip.h16
-rw-r--r--core/io/ip_address.cpp186
-rw-r--r--core/io/ip_address.h38
-rw-r--r--core/io/packet_peer_udp.cpp9
-rw-r--r--core/io/packet_peer_udp.h3
-rw-r--r--core/io/tcp_server.cpp8
-rw-r--r--core/io/tcp_server.h4
-rw-r--r--core/math/SCsub2
-rw-r--r--core/math/math_funcs.cpp37
-rw-r--r--core/os/SCsub2
-rw-r--r--core/os/input_event.cpp3
-rw-r--r--core/translation.cpp39
-rw-r--r--core/ustring.cpp90
-rw-r--r--core/ustring.h3
-rw-r--r--core/variant_call.cpp5
-rw-r--r--doc/base/classes.xml139
-rw-r--r--doc/tools/doc_merge.py2
-rwxr-xr-xdoc/tools/doc_status.py2
-rw-r--r--doc/tools/makedocs.py2
-rw-r--r--doc/tools/makedoku.py2
-rw-r--r--doc/tools/makehtml.py2
-rw-r--r--doc/tools/makemd.py2
-rw-r--r--doc/tools/makerst.py2
-rw-r--r--drivers/SCsub52
-rw-r--r--drivers/alsa/SCsub2
-rw-r--r--drivers/convex_decomp/SCsub2
-rw-r--r--drivers/gl_context/SCsub2
-rw-r--r--drivers/gles2/SCsub2
-rw-r--r--drivers/gles2/shaders/SCsub2
-rw-r--r--drivers/nrex/README.md75
-rw-r--r--drivers/nrex/SCsub5
-rw-r--r--drivers/nrex/nrex.cpp1496
-rw-r--r--drivers/nrex/nrex.hpp176
-rw-r--r--drivers/nrex/nrex_config.h12
-rw-r--r--drivers/nrex/regex.cpp142
-rw-r--r--drivers/png/SCsub2
-rw-r--r--drivers/pulseaudio/SCsub2
-rw-r--r--drivers/register_driver_types.cpp4
-rw-r--r--drivers/rtaudio/SCsub2
-rw-r--r--drivers/unix/SCsub2
-rw-r--r--drivers/unix/ip_unix.cpp89
-rw-r--r--drivers/unix/ip_unix.h2
-rw-r--r--drivers/unix/packet_peer_udp_posix.cpp80
-rw-r--r--drivers/unix/packet_peer_udp_posix.h8
-rw-r--r--drivers/unix/socket_helpers.h70
-rw-r--r--drivers/unix/stream_peer_tcp_posix.cpp26
-rw-r--r--drivers/unix/tcp_server_posix.cpp32
-rw-r--r--drivers/unix/tcp_server_posix.h2
-rw-r--r--drivers/windows/SCsub2
-rw-r--r--drivers/xaudio2/SCsub9
-rw-r--r--drivers/xaudio2/audio_driver_xaudio2.cpp (renamed from platform/winrt/audio_driver_winrt.cpp)51
-rw-r--r--drivers/xaudio2/audio_driver_xaudio2.h (renamed from platform/winrt/audio_driver_winrt.h)26
-rw-r--r--drivers/zlib/SCsub2
-rw-r--r--main/SCsub2
-rwxr-xr-xmethods.py61
-rw-r--r--modules/SCsub2
-rw-r--r--modules/chibi/SCsub2
-rw-r--r--modules/cscript/SCsub2
-rw-r--r--modules/dds/SCsub2
-rw-r--r--modules/enet/SCsub2
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp9
-rw-r--r--modules/etc1/SCsub2
-rw-r--r--modules/freetype/SCsub2
-rw-r--r--modules/gdscript/SCsub2
-rw-r--r--modules/gdscript/gd_compiler.cpp6
-rw-r--r--modules/gridmap/SCsub2
-rw-r--r--modules/ik/SCsub2
-rw-r--r--modules/jpg/SCsub2
-rw-r--r--modules/mpc/SCsub2
-rw-r--r--modules/ogg/SCsub2
-rw-r--r--modules/openssl/SCsub4
-rw-r--r--modules/opus/SCsub2
-rw-r--r--modules/pbm/SCsub2
-rw-r--r--modules/pvr/SCsub2
-rw-r--r--modules/regex/SCsub7
-rw-r--r--modules/regex/config.py8
-rw-r--r--modules/regex/regex.cpp1502
-rw-r--r--modules/regex/regex.h114
-rw-r--r--modules/regex/register_types.cpp (renamed from drivers/nrex/regex.h)42
-rw-r--r--modules/regex/register_types.h31
-rw-r--r--modules/squish/SCsub2
-rw-r--r--modules/theora/SCsub2
-rw-r--r--modules/visual_script/SCsub2
-rw-r--r--modules/vorbis/SCsub2
-rw-r--r--modules/webp/SCsub2
-rw-r--r--platform/android/SCsub2
-rw-r--r--platform/android/detect.py2
-rw-r--r--platform/bb10/SCsub2
-rw-r--r--platform/haiku/SCsub2
-rw-r--r--platform/iphone/SCsub2
-rw-r--r--platform/javascript/SCsub2
-rw-r--r--platform/javascript/export/export.cpp4
-rw-r--r--platform/osx/SCsub2
-rw-r--r--platform/osx/detect.py3
-rw-r--r--platform/server/SCsub2
-rw-r--r--platform/server/detect.py3
-rw-r--r--platform/windows/SCsub2
-rw-r--r--platform/windows/detect.py26
-rw-r--r--platform/windows/os_windows.cpp10
-rw-r--r--platform/windows/os_windows.h6
-rw-r--r--platform/windows/packet_peer_udp_winsock.cpp78
-rw-r--r--platform/windows/packet_peer_udp_winsock.h8
-rw-r--r--platform/windows/stream_peer_winsock.cpp28
-rw-r--r--platform/windows/tcp_server_winsock.cpp21
-rw-r--r--platform/windows/tcp_server_winsock.h2
-rw-r--r--platform/winrt/SCsub3
-rw-r--r--platform/winrt/detect.py2
-rw-r--r--platform/winrt/os_winrt.h4
-rw-r--r--platform/x11/SCsub2
-rw-r--r--platform/x11/detect.py4
-rw-r--r--scene/2d/SCsub2
-rw-r--r--scene/2d/ray_cast_2d.cpp48
-rw-r--r--scene/2d/ray_cast_2d.h3
-rw-r--r--scene/3d/SCsub2
-rw-r--r--scene/3d/ray_cast.cpp48
-rw-r--r--scene/3d/ray_cast.h2
-rw-r--r--scene/SCsub2
-rw-r--r--scene/animation/SCsub2
-rw-r--r--scene/animation/tween.cpp18
-rw-r--r--scene/animation/tween.h1
-rw-r--r--scene/audio/SCsub2
-rw-r--r--scene/gui/SCsub2
-rw-r--r--scene/gui/file_dialog.cpp34
-rw-r--r--scene/gui/link_button.cpp7
-rw-r--r--scene/gui/link_button.h3
-rw-r--r--scene/io/SCsub2
-rw-r--r--scene/main/SCsub2
-rw-r--r--scene/resources/SCsub2
-rw-r--r--scene/resources/default_theme/SCsub2
-rw-r--r--servers/SCsub2
-rw-r--r--servers/audio/SCsub2
-rw-r--r--servers/physics/SCsub2
-rw-r--r--servers/physics/joints/SCsub2
-rw-r--r--servers/physics_2d/SCsub2
-rw-r--r--servers/spatial_sound/SCsub2
-rw-r--r--servers/spatial_sound_2d/SCsub2
-rw-r--r--servers/visual/SCsub2
-rw-r--r--servers/visual/visual_server_raster.cpp5
-rw-r--r--servers/visual/visual_server_raster.h1
-rw-r--r--servers/visual/visual_server_wrap_mt.h1
-rw-r--r--servers/visual_server.cpp1
-rw-r--r--servers/visual_server.h1
-rw-r--r--tools/SCsub2
-rw-r--r--tools/collada/SCsub2
-rw-r--r--tools/doc/SCsub2
-rw-r--r--tools/editor/SCsub2
-rw-r--r--tools/editor/editor_file_dialog.cpp47
-rw-r--r--tools/editor/editor_node.cpp8
-rw-r--r--tools/editor/editor_settings.cpp23
-rw-r--r--tools/editor/editor_settings.h3
-rw-r--r--tools/editor/fileserver/SCsub2
-rw-r--r--tools/editor/filesystem_dock.cpp7
-rw-r--r--tools/editor/icons/SCsub2
-rw-r--r--tools/editor/io_plugins/SCsub2
-rw-r--r--tools/editor/plugins/SCsub2
-rw-r--r--tools/editor/plugins/canvas_item_editor_plugin.cpp405
-rw-r--r--tools/editor/plugins/canvas_item_editor_plugin.h52
-rw-r--r--tools/editor/plugins/script_editor_plugin.cpp18
-rw-r--r--tools/editor/scene_tree_dock.cpp75
-rw-r--r--tools/editor/scene_tree_dock.h9
-rw-r--r--tools/editor/scene_tree_editor.cpp25
-rw-r--r--tools/editor/scene_tree_editor.h3
-rw-r--r--tools/editor/script_create_dialog.cpp14
-rw-r--r--tools/editor/script_create_dialog.h2
-rw-r--r--tools/scripts/make_glwrapper.py2
175 files changed, 3606 insertions, 2492 deletions
diff --git a/SConstruct b/SConstruct
index 32ef884c49..70816d82b8 100644
--- a/SConstruct
+++ b/SConstruct
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
EnsureSConsVersion(0,14);
@@ -59,10 +61,12 @@ platform_arg = ARGUMENTS.get("platform", ARGUMENTS.get("p", False))
if (os.name=="posix"):
pass
elif (os.name=="nt"):
- if (not methods.msvc_is_detected() or platform_arg=="android"):
+ if ( os.getenv("VCINSTALLDIR")==None or platform_arg=="android"):
custom_tools=['mingw']
env_base=Environment(tools=custom_tools);
+if 'TERM' in os.environ:
+ env_base['ENV']['TERM'] = os.environ['TERM']
env_base.AppendENVPath('PATH', os.getenv('PATH'))
env_base.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
env_base.global_defaults=global_defaults
@@ -96,6 +100,7 @@ env_base.__class__.disable_module = methods.disable_module
env_base.__class__.add_source_files = methods.add_source_files
env_base.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix
+env_base.__class__.split_lib = methods.split_lib
env_base["x86_libtheora_opt_gcc"]=False
env_base["x86_libtheora_opt_vc"]=False
@@ -133,15 +138,16 @@ opts.Add('openssl','OpenSSL library for openssl module (system/builtin)','builti
opts.Add('libmpcdec','libmpcdec library for mpc module (system/builtin)','builtin')
opts.Add('enet','ENet library (system/builtin)','builtin')
opts.Add('glew','GLEW library for the gl_context (system/builtin)','builtin')
+opts.Add('xaudio2','XAudio2 audio driver (yes/no)','no')
opts.Add("CXX", "C++ Compiler")
opts.Add("CC", "C Compiler")
-opts.Add("CCFLAGS", "Custom flags for the C++ compiler");
+opts.Add("CCFLAGS", "Custom flags for the C/C++ compiler");
opts.Add("CFLAGS", "Custom flags for the C compiler");
opts.Add("LINKFLAGS", "Custom flags for the linker");
opts.Add('unix_global_settings_path', 'unix-specific path to system-wide settings. Currently only used by templates.','')
opts.Add('disable_3d', 'Disable 3D nodes for smaller executable (yes/no)', "no")
opts.Add('disable_advanced_gui', 'Disable advance 3D gui nodes and behaviors (yes/no)', "no")
-opts.Add('colored', 'Enable colored output for the compilation (yes/no)', 'no')
+opts.Add('verbose', 'Enable verbose output for the compilation (yes/no)', 'yes')
opts.Add('deprecated','Enable deprecated features (yes/no)','yes')
opts.Add('extra_suffix', 'Custom extra suffix added to the base filename of all generated binary files.', '')
opts.Add('vsproj', 'Generate Visual Studio Project. (yes/no)', 'no')
@@ -260,6 +266,8 @@ if selected_platform in platform_list:
sys.exit(255)
suffix+=".opt"
+ env.Append(CCFLAGS=['-DNDEBUG']);
+
elif (env["target"]=="release_debug"):
if (env["tools"]=="yes"):
suffix+=".opt.tools"
@@ -328,8 +336,8 @@ if selected_platform in platform_list:
if (env['xml']=='yes'):
env.Append(CPPFLAGS=['-DXML_ENABLED'])
- if (env['colored']=='yes'):
- methods.colored(sys,env)
+ if (env['verbose']=='no'):
+ methods.no_verbose(sys,env)
Export('env')
@@ -377,9 +385,9 @@ if selected_platform in platform_list:
release_variants = ['release|Win32']+['release|x64']
release_debug_variants = ['release_debug|Win32']+['release_debug|x64']
variants = debug_variants + release_variants + release_debug_variants
- debug_targets = ['Debug']+['Debug']
- release_targets = ['Release']+['Release']
- release_debug_targets = ['ReleaseDebug']+['ReleaseDebug']
+ debug_targets = ['bin\\godot.windows.tools.32.exe']+['bin\\godot.windows.tools.64.exe']
+ release_targets = ['bin\\godot.windows.opt.32.exe']+['bin\\godot.windows.opt.64.exe']
+ release_debug_targets = ['bin\\godot.windows.opt.tools.32.exe']+['bin\\godot.windows.opt.tools.64.exe']
targets = debug_targets + release_targets + release_debug_targets
msvproj = env.MSVSProject(target = ['#godot' + env['MSVSPROJECTSUFFIX'] ],
incs = env.vs_incs,
diff --git a/bin/SCsub b/bin/SCsub
index db057ed103..6b29a44fd9 100644
--- a/bin/SCsub
+++ b/bin/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/python
+
Import('env')
Export('env')
diff --git a/bin/tests/SCsub b/bin/tests/SCsub
index 57c9bc63b2..26687599e1 100644
--- a/bin/tests/SCsub
+++ b/bin/tests/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/python
+
Import('env')
env.tests_sources=[]
diff --git a/bin/tests/test_string.cpp b/bin/tests/test_string.cpp
index be37ce118f..4990c58896 100644
--- a/bin/tests/test_string.cpp
+++ b/bin/tests/test_string.cpp
@@ -31,7 +31,7 @@
//#include "math_funcs.h"
#include <stdio.h>
#include "os/os.h"
-#include "drivers/nrex/regex.h"
+#include "core/io/ip_address.h"
#include "test_string.h"
@@ -461,18 +461,8 @@ bool test_25() {
bool test_26() {
- OS::get_singleton()->print("\n\nTest 26: RegEx\n");
- RegEx regexp("(.*):(.*)");
-
- int res = regexp.find("name:password");
- printf("\tmatch: %s\n", (res>=0)?"true":"false");
-
- printf("\t%i captures:\n", regexp.get_capture_count());
- for (int i = 0; i<regexp.get_capture_count(); i++)
- {
- printf("%ls\n", regexp.get_capture(i).c_str());
- }
- return (res>=0);
+ //TODO: Do replacement RegEx test
+ return true;
};
struct test_27_data {
@@ -843,6 +833,62 @@ bool test_28() {
return state;
}
+bool test_29() {
+
+ bool error = false;
+ bool state = true;
+ bool success = false;
+
+ IP_Address ip0("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ OS::get_singleton()->print("ip0 is %ls\n", String(ip0).c_str());
+
+ IP_Address ip(0x0123, 0x4567, 0x89ab, 0xcdef, IP_Address::TYPE_IPV6);
+ OS::get_singleton()->print("ip6 is %ls\n", String(ip).c_str());
+
+ IP_Address ip2("fe80::52e5:49ff:fe93:1baf");
+ OS::get_singleton()->print("ip6 is %ls\n", String(ip2).c_str());
+
+ IP_Address ip3("::ffff:192.168.0.1");
+ OS::get_singleton()->print("ip6 is %ls\n", String(ip3).c_str());
+
+ String ip4 = "192.168.0.1";
+ success = ip4.is_valid_ip_address();
+ OS::get_singleton()->print("Is valid ipv4: %ls, %s\n", ip4.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ ip4 = "192.368.0.1";
+ success = (!ip4.is_valid_ip_address());
+ OS::get_singleton()->print("Is invalid ipv4: %ls, %s\n", ip4.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ String ip6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+ success = ip6.is_valid_ip_address();
+ OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ ip6 = "2001:0db8:85j3:0000:0000:8a2e:0370:7334";
+ success = (!ip6.is_valid_ip_address());
+ OS::get_singleton()->print("Is invalid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ ip6 = "2001:0db8:85f345:0000:0000:8a2e:0370:7334";
+ success = (!ip6.is_valid_ip_address());
+ OS::get_singleton()->print("Is invalid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ ip6 = "2001:0db8::0:8a2e:370:7334";
+ success = (ip6.is_valid_ip_address());
+ OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ ip6 = "::ffff:192.168.0.1";
+ success = (ip6.is_valid_ip_address());
+ OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
+ if (!success) state = false;
+
+ return state;
+};
+
typedef bool (*TestFunc)(void);
TestFunc test_funcs[] = {
@@ -875,6 +921,7 @@ TestFunc test_funcs[] = {
test_26,
test_27,
test_28,
+ test_29,
0
};
diff --git a/core/SCsub b/core/SCsub
index 4ce91c794f..cbed2e4f35 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.core_sources=[]
diff --git a/core/bind/SCsub b/core/bind/SCsub
index 7b4a6acbc0..c2731d60e6 100644
--- a/core/bind/SCsub
+++ b/core/bind/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.core_sources,"*.cpp")
diff --git a/core/io/SCsub b/core/io/SCsub
index 3ff9b355a4..48cc9a5275 100644
--- a/core/io/SCsub
+++ b/core/io/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.core_sources,"*.cpp")
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 2a831dd992..e3289b452c 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -29,8 +29,9 @@
#include "http_client.h"
#include "io/stream_peer_ssl.h"
+VARIANT_ENUM_CAST(IP_Address::AddrType);
-Error HTTPClient::connect(const String &p_host, int p_port, bool p_ssl,bool p_verify_host){
+Error HTTPClient::connect(const String &p_host, int p_port, bool p_ssl,bool p_verify_host, IP_Address::AddrType p_addr_type){
close();
conn_port=p_port;
@@ -62,7 +63,7 @@ Error HTTPClient::connect(const String &p_host, int p_port, bool p_ssl,bool p_ve
status=STATUS_CONNECTING;
} else {
//is hostname
- resolving=IP::get_singleton()->resolve_hostname_queue_item(conn_host);
+ resolving=IP::get_singleton()->resolve_hostname_queue_item(conn_host, p_addr_type);
status=STATUS_RESOLVING;
}
@@ -635,7 +636,7 @@ Error HTTPClient::_get_http_data(uint8_t* p_buffer, int p_bytes,int &r_received)
void HTTPClient::_bind_methods() {
- ObjectTypeDB::bind_method(_MD("connect:Error","host","port","use_ssl","verify_host"),&HTTPClient::connect,DEFVAL(false),DEFVAL(true));
+ ObjectTypeDB::bind_method(_MD("connect:Error","host","port","use_ssl","verify_host"),&HTTPClient::connect,DEFVAL(false),DEFVAL(true),DEFVAL(IP_Address::TYPE_ANY));
ObjectTypeDB::bind_method(_MD("set_connection","connection:StreamPeer"),&HTTPClient::set_connection);
ObjectTypeDB::bind_method(_MD("get_connection:StreamPeer"),&HTTPClient::get_connection);
ObjectTypeDB::bind_method(_MD("request_raw","method","url","headers","body"),&HTTPClient::request_raw);
diff --git a/core/io/http_client.h b/core/io/http_client.h
index 32d2e72101..ba464c34c7 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -165,7 +165,7 @@ public:
//Error connect_and_get(const String& p_url,bool p_verify_host=true); //connects to a full url and perform request
- Error connect(const String &p_host,int p_port,bool p_ssl=false,bool p_verify_host=true);
+ Error connect(const String &p_host,int p_port,bool p_ssl=false,bool p_verify_host=true, IP_Address::AddrType p_addr_type = IP_Address::TYPE_ANY);
void set_connection(const Ref<StreamPeer>& p_connection);
Ref<StreamPeer> get_connection() const;
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index a77aace07f..4ee1b281c4 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -32,6 +32,7 @@
#include "hash_map.h"
VARIANT_ENUM_CAST(IP::ResolverStatus);
+VARIANT_ENUM_CAST(IP_Address::AddrType);
/************* RESOLVER ******************/
@@ -43,10 +44,12 @@ struct _IP_ResolverPrivate {
volatile IP::ResolverStatus status;
IP_Address response;
String hostname;
+ IP_Address::AddrType type;
void clear() {
status = IP::RESOLVER_STATUS_NONE;
response = IP_Address();
+ type = IP_Address::TYPE_NONE;
hostname="";
};
@@ -78,9 +81,9 @@ struct _IP_ResolverPrivate {
if (queue[i].status!=IP::RESOLVER_STATUS_WAITING)
continue;
- queue[i].response=IP::get_singleton()->resolve_hostname(queue[i].hostname);
+ queue[i].response=IP::get_singleton()->resolve_hostname(queue[i].hostname, queue[i].type);
- if (queue[i].response.host==0)
+ if (queue[i].response.type==IP_Address::TYPE_NONE)
queue[i].status=IP::RESOLVER_STATUS_ERROR;
else
queue[i].status=IP::RESOLVER_STATUS_DONE;
@@ -109,21 +112,23 @@ struct _IP_ResolverPrivate {
-IP_Address IP::resolve_hostname(const String& p_hostname) {
+IP_Address IP::resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type) {
- GLOBAL_LOCK_FUNCTION
+ GLOBAL_LOCK_FUNCTION;
if (resolver->cache.has(p_hostname))
- return resolver->cache[p_hostname];
+ if (resolver->cache[p_hostname].type & p_type != 0)
+ return resolver->cache[p_hostname];
+ // requested type is different from type in cache. continue resolution, if successful it'll overwrite cache
- IP_Address res = _resolve_hostname(p_hostname);
+ IP_Address res = _resolve_hostname(p_hostname, p_type);
resolver->cache[p_hostname]=res;
return res;
}
-IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname) {
+IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname, IP_Address::AddrType p_type) {
- GLOBAL_LOCK_FUNCTION
+ GLOBAL_LOCK_FUNCTION;
ResolverID id = resolver->find_empty_id();
@@ -133,7 +138,8 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname) {
}
resolver->queue[id].hostname=p_hostname;
- if (resolver->cache.has(p_hostname)) {
+ resolver->queue[id].type = p_type;
+ if (resolver->cache.has(p_hostname) && (resolver->cache[p_hostname].type & p_type) != 0) {
resolver->queue[id].response=resolver->cache[p_hostname];
resolver->queue[id].status=IP::RESOLVER_STATUS_DONE;
} else {
@@ -145,10 +151,6 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname) {
resolver->resolve_queues();
}
-
-
-
-
return id;
}
@@ -187,6 +189,14 @@ void IP::erase_resolve_item(ResolverID p_id) {
}
+void IP::clear_cache(const String &p_hostname) {
+
+ if (p_hostname.empty()) {
+ resolver->cache.clear();
+ } else {
+ resolver->cache.erase(p_hostname);
+ }
+};
Array IP::_get_local_addresses() const {
@@ -208,6 +218,7 @@ void IP::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_resolve_item_address","id"),&IP::get_resolve_item_address);
ObjectTypeDB::bind_method(_MD("erase_resolve_item","id"),&IP::erase_resolve_item);
ObjectTypeDB::bind_method(_MD("get_local_addresses"),&IP::_get_local_addresses);
+ ObjectTypeDB::bind_method(_MD("clear_cache"),&IP::clear_cache, DEFVAL(""));
BIND_CONSTANT( RESOLVER_STATUS_NONE );
BIND_CONSTANT( RESOLVER_STATUS_WAITING );
diff --git a/core/io/ip.h b/core/io/ip.h
index 38c86e7ba3..742dd0e740 100644
--- a/core/io/ip.h
+++ b/core/io/ip.h
@@ -48,6 +48,14 @@ public:
RESOLVER_STATUS_ERROR,
};
+ enum AddressType {
+
+ ADDRESS_IPV4 = 1,
+ ADDRESS_IPV6 = 2,
+
+ ADDRESS_ANY = 3,
+ };
+
enum {
RESOLVER_MAX_QUERIES = 32,
RESOLVER_INVALID_ID=-1
@@ -65,7 +73,7 @@ protected:
static IP*singleton;
static void _bind_methods();
- virtual IP_Address _resolve_hostname(const String& p_hostname)=0;
+ virtual IP_Address _resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type = IP_Address::TYPE_ANY)=0;
Array _get_local_addresses() const;
static IP* (*_create)();
@@ -73,14 +81,16 @@ public:
- IP_Address resolve_hostname(const String& p_hostname);
+ IP_Address resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type = IP_Address::TYPE_ANY);
// async resolver hostname
- ResolverID resolve_hostname_queue_item(const String& p_hostname);
+ ResolverID resolve_hostname_queue_item(const String& p_hostname, IP_Address::AddrType p_type = IP_Address::TYPE_ANY);
ResolverStatus get_resolve_item_status(ResolverID p_id) const;
IP_Address get_resolve_item_address(ResolverID p_id) const;
virtual void get_local_addresses(List<IP_Address> *r_addresses) const=0;
void erase_resolve_item(ResolverID p_id);
+ void clear_cache(const String& p_hostname = "");
+
static IP* get_singleton();
static IP* create();
diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp
index 7a51bce7c6..9887cd132b 100644
--- a/core/io/ip_address.cpp
+++ b/core/io/ip_address.cpp
@@ -32,29 +32,191 @@ IP_Address::operator Variant() const {
return operator String();
}*/
+
+#include <string.h>
+#include <stdio.h>
+
IP_Address::operator String() const {
- return itos(field[0])+"."+itos(field[1])+"."+itos(field[2])+"."+itos(field[3]);
+ if (type == TYPE_NONE)
+ return "0.0.0.0";
+ if (type == TYPE_IPV4)
+ return itos(field8[0])+"."+itos(field8[1])+"."+itos(field8[2])+"."+itos(field8[3]);
+ else {
+ String ret;
+ for (int i=0; i<8; i++) {
+ if (i > 0)
+ ret = ret + ":";
+ uint16_t num = (field8[i*2] << 8) + field8[i*2+1];
+ ret = ret + String::num_int64(num, 16);
+ };
+
+ return ret;
+ };
}
-IP_Address::IP_Address(const String& p_string) {
+static void _parse_hex(const String& p_string, int p_start, uint8_t* p_dst) {
+
+ uint16_t ret = 0;
+ for (int i=p_start; i<p_start + 4; i++) {
+
+ if (i >= p_string.length()) {
+ break;
+ };
+
+ int n = 0;
+ CharType c = p_string[i];
+ if (c >= '0' && c <= '9') {
+
+ n = c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ n = 10 + (c - 'a');
+ } else if (c >= 'A' && c <= 'F') {
+ n = 10 + (c - 'A');
+ } else if (c == ':') {
+ break;
+ } else {
+ ERR_EXPLAIN("Invalid character in ipv6 address: " + p_string);
+ ERR_FAIL();
+ };
+ ret = ret << 4;
+ ret += n;
+ };
+
+ p_dst[0] = ret >> 8;
+ p_dst[1] = ret & 0xff;
+};
+
+void IP_Address::_parse_ipv6(const String& p_string) {
+
+ static const int parts_total = 8;
+ int parts[parts_total] = {0};
+ int parts_count = 0;
+ bool part_found = false;
+ bool part_skip = false;
+ bool part_ipv4 = false;
+ int parts_idx = 0;
+
+ for (int i=0; i<p_string.length(); i++) {
+
+ CharType c = p_string[i];
+ if (c == ':') {
+
+ if (i == 0) {
+ continue; // next must be a ":"
+ };
+ if (!part_found) {
+ part_skip = true;
+ parts[parts_idx++] = -1;
+ };
+ part_found = false;
+ } else if (c == '.') {
+
+ part_ipv4 = true;
+
+ } else if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
+ if (!part_found) {
+ parts[parts_idx++] = i;
+ part_found = true;
+ ++parts_count;
+ };
+ } else {
+
+ ERR_EXPLAIN("Invalid character in IPv6 address: " + p_string);
+ ERR_FAIL();
+ };
+ };
+
+ int parts_extra = 0;
+ if (part_skip) {
+ parts_extra = parts_total - parts_count;
+ };
+
+ int idx = 0;
+ for (int i=0; i<parts_idx; i++) {
- host=0;
- int slices = p_string.get_slice_count(".");
+ if (parts[i] == -1) {
+
+ for (int j=0; j<parts_extra; j++) {
+ field16[idx++] = 0;
+ };
+ continue;
+ };
+
+ if (part_ipv4 && i == parts_idx - 1) {
+ _parse_ipv4(p_string, parts[i], (uint8_t*)&field16[idx]); // should be the last one
+ } else {
+ _parse_hex(p_string, parts[i], (uint8_t*)&(field16[idx++]));
+ };
+ };
+
+};
+
+void IP_Address::_parse_ipv4(const String& p_string, int p_start, uint8_t* p_ret) {
+
+ String ip;
+ if (p_start != 0) {
+ ip = p_string.substr(p_start, p_string.length() - p_start);
+ } else {
+ ip = p_string;
+ };
+
+ int slices = ip.get_slice_count(".");
if (slices!=4) {
- ERR_EXPLAIN("Invalid IP Address String: "+p_string);
+ ERR_EXPLAIN("Invalid IP Address String: "+ip);
ERR_FAIL();
}
for(int i=0;i<4;i++) {
-
- field[i]=p_string.get_slicec('.',i).to_int();
+ p_ret[i]=ip.get_slicec('.',i).to_int();
}
+};
+
+void IP_Address::clear() {
+
+ memset(&field8[0], 0, sizeof(field8));
+};
+
+IP_Address::IP_Address(const String& p_string) {
+
+ clear();
+ if (p_string.find(":") >= 0) {
+
+ _parse_ipv6(p_string);
+ type = TYPE_IPV6;
+ } else {
+
+ _parse_ipv4(p_string, 0, &field8[0]);
+ type = TYPE_IPV4;
+ };
}
-IP_Address::IP_Address(uint8_t p_a,uint8_t p_b,uint8_t p_c,uint8_t p_d) {
+_FORCE_INLINE_ static void _32_to_buf(uint8_t* p_dst, uint32_t p_n) {
+
+ p_dst[0] = (p_n >> 24) & 0xff;
+ p_dst[1] = (p_n >> 16) & 0xff;
+ p_dst[2] = (p_n >> 8) & 0xff;
+ p_dst[3] = (p_n >> 0) & 0xff;
+};
+
+IP_Address::IP_Address(uint32_t p_a,uint32_t p_b,uint32_t p_c,uint32_t p_d, IP_Address::AddrType p_type) {
+
+ type = p_type;
+ memset(&field8[0], 0, sizeof(field8));
+ if (p_type == TYPE_IPV4) {
+ field8[0]=p_a;
+ field8[1]=p_b;
+ field8[2]=p_c;
+ field8[3]=p_d;
+ } else if (type == TYPE_IPV6) {
+
+ _32_to_buf(&field8[0], p_a);
+ _32_to_buf(&field8[4], p_b);
+ _32_to_buf(&field8[8], p_c);
+ _32_to_buf(&field8[12], p_d);
+ } else {
+ type = TYPE_NONE;
+ ERR_EXPLAIN("Invalid type specified for IP_Address (use TYPE_IPV4 or TYPE_IPV6");
+ ERR_FAIL();
+ };
- field[0]=p_a;
- field[1]=p_b;
- field[2]=p_c;
- field[3]=p_d;
}
diff --git a/core/io/ip_address.h b/core/io/ip_address.h
index 1292311729..fe13d70611 100644
--- a/core/io/ip_address.h
+++ b/core/io/ip_address.h
@@ -33,22 +33,48 @@
struct IP_Address {
+public:
+ enum AddrType {
+ TYPE_NONE = 0,
+ TYPE_IPV4 = 1,
+ TYPE_IPV6 = 2,
+
+ TYPE_ANY = 3,
+ };
+
+ AddrType type;
+
union {
- uint8_t field[4];
- uint32_t host;
+ uint8_t field8[16];
+ uint16_t field16[8];
+ uint32_t field32[4];
};
+protected:
+ void _parse_ipv6(const String& p_string);
+ void _parse_ipv4(const String& p_string, int p_start, uint8_t* p_ret);
+
+public:
//operator Variant() const;
bool operator==(const IP_Address& p_ip) const {
- return host==p_ip.host;
+ for (int i=0; i<4; i++)
+ if (field32[i] != p_ip.field32[i])
+ return false;
+ return true;
}
bool operator!=(const IP_Address& p_ip) const {
- return host!=p_ip.host;
+ for (int i=0; i<4; i++)
+ if (field32[i] != p_ip.field32[i])
+ return true;
+ return false;
}
+
+ void clear();
+
operator String() const;
IP_Address(const String& p_string);
- IP_Address(uint8_t p_a,uint8_t p_b,uint8_t p_c,uint8_t p_d);
- IP_Address() { host=0; }
+ IP_Address(uint32_t p_a,uint32_t p_b,uint32_t p_c,uint32_t p_d, AddrType p_type=TYPE_IPV4);
+ IP_Address() { clear(); type=TYPE_NONE; }
};
diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp
index efc619e414..018dc77d91 100644
--- a/core/io/packet_peer_udp.cpp
+++ b/core/io/packet_peer_udp.cpp
@@ -29,14 +29,9 @@
#include "packet_peer_udp.h"
#include "io/ip.h"
-
PacketPeerUDP* (*PacketPeerUDP::_create)()=NULL;
-int PacketPeerUDP::_get_packet_address() const {
-
- IP_Address ip = get_packet_address();
- return ip.host;
-}
+VARIANT_ENUM_CAST(IP_Address::AddrType);
String PacketPeerUDP::_get_packet_ip() const {
@@ -65,7 +60,7 @@ void PacketPeerUDP::_bind_methods() {
ObjectTypeDB::bind_method(_MD("wait:Error"),&PacketPeerUDP::wait);
ObjectTypeDB::bind_method(_MD("is_listening"),&PacketPeerUDP::is_listening);
ObjectTypeDB::bind_method(_MD("get_packet_ip"),&PacketPeerUDP::_get_packet_ip);
- ObjectTypeDB::bind_method(_MD("get_packet_address"),&PacketPeerUDP::_get_packet_address);
+ //ObjectTypeDB::bind_method(_MD("get_packet_address"),&PacketPeerUDP::_get_packet_address);
ObjectTypeDB::bind_method(_MD("get_packet_port"),&PacketPeerUDP::get_packet_port);
ObjectTypeDB::bind_method(_MD("set_send_address","host","port"),&PacketPeerUDP::_set_send_address);
diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h
index 70d92834fc..c0806a9e6a 100644
--- a/core/io/packet_peer_udp.h
+++ b/core/io/packet_peer_udp.h
@@ -40,14 +40,13 @@ protected:
static PacketPeerUDP* (*_create)();
static void _bind_methods();
- int _get_packet_address() const;
String _get_packet_ip() const;
virtual Error _set_send_address(const String& p_address,int p_port);
public:
- virtual Error listen(int p_port,int p_recv_buffer_size=65536)=0;
+ virtual Error listen(int p_port, IP_Address::AddrType p_address_type = IP_Address::TYPE_IPV4, int p_recv_buffer_size=65536)=0;
virtual void close()=0;
virtual Error wait()=0;
virtual bool is_listening() const=0;
diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp
index 274d20a48a..53d6e900f3 100644
--- a/core/io/tcp_server.cpp
+++ b/core/io/tcp_server.cpp
@@ -30,6 +30,8 @@
TCP_Server* (*TCP_Server::_create)()=NULL;
+VARIANT_ENUM_CAST(IP_Address::AddrType);
+
Ref<TCP_Server> TCP_Server::create_ref() {
if (!_create)
@@ -44,19 +46,19 @@ TCP_Server* TCP_Server::create() {
return _create();
}
-Error TCP_Server::_listen(uint16_t p_port,DVector<String> p_accepted_hosts) {
+Error TCP_Server::_listen(uint16_t p_port, IP_Address::AddrType p_type, DVector<String> p_accepted_hosts) {
List<String> hosts;
for(int i=0;i<p_accepted_hosts.size();i++)
hosts.push_back(p_accepted_hosts.get(i));
- return listen(p_port,hosts.size()?&hosts:NULL);
+ return listen(p_port,p_type, hosts.size()?&hosts:NULL);
}
void TCP_Server::_bind_methods() {
- ObjectTypeDB::bind_method(_MD("listen","port","accepted_hosts"),&TCP_Server::_listen,DEFVAL(DVector<String>()));
+ ObjectTypeDB::bind_method(_MD("listen","port","accepted_hosts"),&TCP_Server::_listen,DEFVAL(IP_Address::TYPE_IPV4), DEFVAL(DVector<String>()));
ObjectTypeDB::bind_method(_MD("is_connection_available"),&TCP_Server::is_connection_available);
ObjectTypeDB::bind_method(_MD("take_connection"),&TCP_Server::take_connection);
ObjectTypeDB::bind_method(_MD("stop"),&TCP_Server::stop);
diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h
index 512a7e640a..883a3ef2f6 100644
--- a/core/io/tcp_server.h
+++ b/core/io/tcp_server.h
@@ -41,11 +41,11 @@ protected:
static TCP_Server* (*_create)();
//bind helper
- Error _listen(uint16_t p_port,DVector<String> p_accepted_hosts=DVector<String>());
+ Error _listen(uint16_t p_port, IP_Address::AddrType p_type = IP_Address::TYPE_IPV4 ,DVector<String> p_accepted_hosts=DVector<String>());
static void _bind_methods();
public:
- virtual Error listen(uint16_t p_port,const List<String> *p_accepted_hosts=NULL)=0;
+ virtual Error listen(uint16_t p_port, IP_Address::AddrType p_type = IP_Address::TYPE_IPV4, const List<String> *p_accepted_hosts=NULL)=0;
virtual bool is_connection_available() const=0;
virtual Ref<StreamPeerTCP> take_connection()=0;
diff --git a/core/math/SCsub b/core/math/SCsub
index 7b4a6acbc0..c2731d60e6 100644
--- a/core/math/SCsub
+++ b/core/math/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.core_sources,"*.cpp")
diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp
index 64615fe6b4..46c0218707 100644
--- a/core/math/math_funcs.cpp
+++ b/core/math/math_funcs.cpp
@@ -41,37 +41,16 @@ static uint32_t Q[4096];
#endif
uint32_t Math::rand_from_seed(uint32_t *seed) {
-
-#if 1
- uint32_t k;
- uint32_t s = (*seed);
- if (s == 0)
- s = 0x12345987;
- k = s / 127773;
- s = 16807 * (s - k * 127773) - 2836 * k;
-// if (s < 0)
-// s += 2147483647;
- (*seed) = s;
- return (s & Math::RANDOM_MAX);
-#else
- *seed = *seed * 1103515245 + 12345;
- return (*seed % ((unsigned int)RANDOM_MAX + 1));
-#endif
+ // Xorshift31 PRNG
+ if ( *seed == 0 ) *seed = Math::RANDOM_MAX;
+ (*seed) ^= (*seed) << 13;
+ (*seed) ^= (*seed) >> 17;
+ (*seed) ^= (*seed) << 5;
+ return (*seed) & Math::RANDOM_MAX;
}
void Math::seed(uint32_t x) {
-#if 0
- int i;
-
- Q[0] = x;
- Q[1] = x + PHI;
- Q[2] = x + PHI + PHI;
-
- for (i = 3; i < 4096; i++)
- Q[i] = Q[i - 3] ^ Q[i - 2] ^ PHI ^ i;
-#else
default_seed=x;
-#endif
}
void Math::randomize() {
@@ -82,12 +61,12 @@ void Math::randomize() {
uint32_t Math::rand() {
- return rand_from_seed(&default_seed)&0x7FFFFFFF;
+ return rand_from_seed(&default_seed);
}
double Math::randf() {
- return (double)rand() / (double)RANDOM_MAX;
+ return (double)rand() / (double)Math::RANDOM_MAX;
}
double Math::sin(double p_x) {
diff --git a/core/os/SCsub b/core/os/SCsub
index 7b4a6acbc0..c2731d60e6 100644
--- a/core/os/SCsub
+++ b/core/os/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.core_sources,"*.cpp")
diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp
index 9982767be1..7350be824a 100644
--- a/core/os/input_event.cpp
+++ b/core/os/input_event.cpp
@@ -50,7 +50,8 @@ bool InputEvent::operator==(const InputEvent &p_event) const {
case MOUSE_MOTION:
return mouse_motion.x == p_event.mouse_motion.x
&& mouse_motion.y == p_event.mouse_motion.y
- && mouse_motion.relative_x == p_event.mouse_motion.relative_y
+ && mouse_motion.relative_x == p_event.mouse_motion.relative_x
+ && mouse_motion.relative_y == p_event.mouse_motion.relative_y
&& mouse_motion.button_mask == p_event.mouse_motion.button_mask
&& key.mod == p_event.key.mod;
case MOUSE_BUTTON:
diff --git a/core/translation.cpp b/core/translation.cpp
index 4592d00598..4d81073fe6 100644
--- a/core/translation.cpp
+++ b/core/translation.cpp
@@ -779,6 +779,11 @@ Vector<String> TranslationServer::get_all_locale_names(){
}
+static String get_trimmed_locale(const String& p_locale) {
+
+ return p_locale.substr(0,2);
+}
+
static bool is_valid_locale(const String& p_locale) {
const char **ptr=locale_list;
@@ -839,9 +844,20 @@ void Translation::_set_messages(const DVector<String>& p_messages){
void Translation::set_locale(const String& p_locale) {
- ERR_EXPLAIN("Invalid Locale: "+p_locale);
- ERR_FAIL_COND(!is_valid_locale(p_locale));
- locale=p_locale;
+ // replaces '-' with '_' for macOS Sierra-style locales
+ String univ_locale = p_locale.replace("-", "_");
+
+ if(!is_valid_locale(univ_locale)) {
+ String trimmed_locale = get_trimmed_locale(univ_locale);
+
+ ERR_EXPLAIN("Invalid Locale: "+trimmed_locale);
+ ERR_FAIL_COND(!is_valid_locale(trimmed_locale));
+
+ locale=trimmed_locale;
+ }
+ else {
+ locale=univ_locale;
+ }
}
void Translation::add_message( const StringName& p_src_text, const StringName& p_xlated_text ) {
@@ -906,9 +922,20 @@ Translation::Translation() {
void TranslationServer::set_locale(const String& p_locale) {
- ERR_EXPLAIN("Invalid Locale: "+p_locale);
- ERR_FAIL_COND(!is_valid_locale(p_locale));
- locale=p_locale;
+ // replaces '-' with '_' for macOS Sierra-style locales
+ String univ_locale = p_locale.replace("-", "_");
+
+ if(!is_valid_locale(univ_locale)) {
+ String trimmed_locale = get_trimmed_locale(univ_locale);
+
+ ERR_EXPLAIN("Invalid Locale: "+trimmed_locale);
+ ERR_FAIL_COND(!is_valid_locale(trimmed_locale));
+
+ locale=trimmed_locale;
+ }
+ else {
+ locale=univ_locale;
+ }
}
String TranslationServer::get_locale() const {
diff --git a/core/ustring.cpp b/core/ustring.cpp
index 2e907381f7..f9c10615b3 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -1543,11 +1543,11 @@ String::String(const StrRange& p_range) {
copy_from(p_range.c_str,p_range.len);
}
-int String::hex_to_int() const {
+int String::hex_to_int(bool p_with_prefix) const {
int l = length();
- if (l<3)
- return 0;
+ if (p_with_prefix && l<3)
+ return 0;
const CharType *s=ptr();
@@ -1556,15 +1556,16 @@ int String::hex_to_int() const {
if (sign<0) {
s++;
l--;
- if (l<2)
+ if (p_with_prefix && l<2)
return 0;
}
- if (s[0]!='0' || s[1]!='x')
- return 0;
-
- s+=2;
- l-=2;
+ if (p_with_prefix) {
+ if (s[0]!='0' || s[1]!='x')
+ return 0;
+ s+=2;
+ l-=2;
+ };
int hex=0;
@@ -3510,6 +3511,36 @@ bool String::is_valid_integer() const {
}
+bool String::is_valid_hex_number(bool p_with_prefix) const {
+
+ int from = 0;
+ int len = length();
+
+ if (len!=1 && (operator[](0)=='+' || operator[](0)=='-'))
+ from++;
+
+ if (p_with_prefix) {
+
+ if (len < 2)
+ return false;
+ if (operator[](from) != '0' || operator[](from+1) != 'x') {
+ return false;
+ };
+ from += 2;
+ };
+
+ for (int i=from; i<len; i++) {
+
+ CharType c = operator[](i);
+ if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
+ continue;
+ return false;
+ };
+
+ return true;
+};
+
+
bool String::is_valid_float() const {
int len = length();
@@ -3646,20 +3677,41 @@ bool String::is_valid_html_color() const {
}
+
bool String::is_valid_ip_address() const {
- Vector<String> ip = split(".");
- if (ip.size()!=4)
- return false;
- for(int i=0;i<ip.size();i++) {
+ if (find(":") >= 0) {
- String n = ip[i];
- if (!n.is_valid_integer())
- return false;
- int val = n.to_int();
- if (val<0 || val>255)
+ Vector<String> ip = split(":");
+ for (int i=0; i<ip.size(); i++) {
+
+ String n = ip[i];
+ if (n.empty())
+ continue;
+ if (n.is_valid_hex_number(false)) {
+ int nint = n.hex_to_int(false);
+ if (nint < 0 || nint > 0xffff)
+ return false;
+ continue;
+ };
+ if (!n.is_valid_ip_address())
+ return false;
+ };
+
+ } else {
+ Vector<String> ip = split(".");
+ if (ip.size()!=4)
return false;
- }
+ for(int i=0;i<ip.size();i++) {
+
+ String n = ip[i];
+ if (!n.is_valid_integer())
+ return false;
+ int val = n.to_int();
+ if (val<0 || val>255)
+ return false;
+ }
+ };
return true;
}
diff --git a/core/ustring.h b/core/ustring.h
index 09d13a9e64..452f252857 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -144,7 +144,7 @@ public:
bool is_numeric() const;
double to_double() const;
float to_float() const;
- int hex_to_int() const;
+ int hex_to_int(bool p_with_prefix = true) const;
int to_int() const;
int64_t to_int64() const;
@@ -226,6 +226,7 @@ public:
bool is_valid_identifier() const;
bool is_valid_integer() const;
bool is_valid_float() const;
+ bool is_valid_hex_number(bool p_with_prefix) const;
bool is_valid_html_color() const;
bool is_valid_ip_address() const;
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 51cd4c2399..9b6fa27cf4 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -1820,6 +1820,11 @@ _VariantCall::addfunc(Variant::m_vtype,Variant::m_ret,_SCS(#m_method),VCALL(m_cl
_VariantCall::add_constant(Variant::IMAGE,"INTERPOLATE_NEAREST",Image::INTERPOLATE_NEAREST);
_VariantCall::add_constant(Variant::IMAGE,"INTERPOLATE_BILINEAR",Image::INTERPOLATE_BILINEAR);
_VariantCall::add_constant(Variant::IMAGE,"INTERPOLATE_CUBIC",Image::INTERPOLATE_CUBIC);
+
+ _VariantCall::add_constant(Variant::INT, "IP_TYPE_NONE", IP_Address::TYPE_NONE);
+ _VariantCall::add_constant(Variant::INT, "IP_TYPE_IPV4", IP_Address::TYPE_IPV4);
+ _VariantCall::add_constant(Variant::INT, "IP_TYPE_IPV6", IP_Address::TYPE_IPV6);
+ _VariantCall::add_constant(Variant::INT, "IP_TYPE_ANY", IP_Address::TYPE_ANY);
}
void unregister_variant_methods() {
diff --git a/doc/base/classes.xml b/doc/base/classes.xml
index 4e0ba53f48..977b44763e 100644
--- a/doc/base/classes.xml
+++ b/doc/base/classes.xml
@@ -20298,6 +20298,9 @@
<constant name="UNDERLINE_MODE_ON_HOVER" value="1">
The LinkButton will show an underline at the bottom of its text when the mouse cursor is over it.
</constant>
+ <constant name="UNDERLINE_MODE_NEVER" value="2">
+ The LinkButton will never show an underline at the bottom of its text.
+ </constant>
</constants>
<theme_items>
<theme_item name="font" type="Font">
@@ -31871,6 +31874,8 @@
RayCast can ignore some objects by adding them to the exception list via [code]add_exception[/code], setting proper filtering with layers, or by filtering object types with type masks.
Only enabled raycasts will be able to query the space and report collisions!
+
+ RayCast calculates intersection every fixed frame (see [Node]), and the result is cached so it can be used later until the next frame. If multiple queries are required between fixed frames (or during the same frame) use [method force_raycast_update] after adjusting the raycast.
</description>
<methods>
<method name="add_exception">
@@ -31891,6 +31896,11 @@
Removes all collision exception for this ray.
</description>
</method>
+ <method name="force_raycast_update">
+ <description>
+ Updates the collision information in case if this object's properties changed during the current frame (for example position, rotation or the cast_point). Note, [code]set_enabled[/code] is not required for this to work.
+ </description>
+ </method>
<method name="get_cast_to" qualifiers="const">
<return type="Vector3">
</return>
@@ -32009,6 +32019,8 @@
RayCast2D can ignore some objects by adding them to the exception list via [code]add_exception[/code], setting proper filtering with layers, or by filtering object types with type masks.
Only enabled raycasts will be able to query the space and report collisions!
+
+ RayCast2D calculates intersection every fixed frame (see [Node]), and the result is cached so it can be used later until the next frame. If multiple queries are required between fixed frames (or during the same frame) use [method force_raycast_update] after adjusting the raycast.
</description>
<methods>
<method name="add_exception">
@@ -32029,6 +32041,11 @@
Removes all collision exception for this ray.
</description>
</method>
+ <method name="force_raycast_update">
+ <description>
+ Updates the collision information in case if this object's properties changed during the current frame (for example position, rotation or the cast_point). Note, [code]set_enabled[/code] is not required for this to work.
+ </description>
+ </method>
<method name="get_cast_to" qualifiers="const">
<return type="Vector2">
</return>
@@ -32500,6 +32517,7 @@
would be read as [code]"(?:\\.|[^"])*"[/code]
Currently supported features:
* Capturing [code]()[/code] and non-capturing [code](?:)[/code] groups
+ * Named capturing groups [code](?P&lt;name&gt;)[/code]
* Any character [code].[/code]
* Shorthand character classes [code]\w \W \s \S \d \D[/code]
* User-defined character classes such as [code][A-Za-z][/code]
@@ -32508,7 +32526,7 @@
* Lazy (non-greedy) quantifiers [code]*?[/code]
* Beginning [code]^[/code] and end [code]$[/code] anchors
* Alternation [code]|[/code]
- * Backreferences [code]\1[/code] and [code]\g{1}[/code]
+ * Backreferences [code]\1[/code], [code]\g{1}[/code], and [code]\g&lt;name&gt;[/code]
* POSIX character classes [code][[:alnum:]][/code]
* Lookahead [code](?=)[/code], [code](?!)[/code] and lookbehind [code](?&lt;=)[/code], [code](?&lt;!)[/code]
* ASCII [code]\xFF[/code] and Unicode [code]\uFFFF[/code] code points (in a style similar to Python)
@@ -32517,7 +32535,7 @@
<methods>
<method name="clear">
<description>
- This method resets the state of the object, as it was freshly created. Namely, it unassigns the regular expression of this object, and forgets all captures made by the last [method find].
+ This method resets the state of the object, as it was freshly created. Namely, it unassigns the regular expression of this object.
</description>
</method>
<method name="compile">
@@ -32525,15 +32543,41 @@
</return>
<argument index="0" name="pattern" type="String">
</argument>
- <argument index="1" name="capture" type="int" default="9">
- </argument>
<description>
- Compiles and assign the regular expression pattern to use. The limit on the number of capturing groups can be specified or made unlimited if negative.
+ Compiles and assign the regular expression pattern to use.
</description>
</method>
- <method name="find" qualifiers="const">
+ <method name="get_group_count" qualifiers="const">
<return type="int">
</return>
+ <description>
+ Returns the number of numeric capturing groups.
+ </description>
+ </method>
+ <method name="get_names" qualifiers="const">
+ <return type="Array">
+ </return>
+ <description>
+ Returns an array of names of named capturing groups.
+ </description>
+ </method>
+ <method name="get_pattern" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ Returns the expression used to compile the code.
+ </description>
+ </method>
+ <method name="is_valid" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ Returns whether this object has a valid regular expression assigned.
+ </description>
+ </method>
+ <method name="search" qualifiers="const">
+ <return type="Object">
+ </return>
<argument index="0" name="text" type="String">
</argument>
<argument index="1" name="start" type="int" default="0">
@@ -32541,45 +32585,98 @@
<argument index="2" name="end" type="int" default="-1">
</argument>
<description>
- This method tries to find the pattern within the string, and returns the position where it was found. It also stores any capturing group (see [method get_capture]) for further retrieval.
+ Searches the text for the compiled pattern. Returns a [RegExMatch] container of the first matching reult if found, otherwise null. The region to search within can be specified without modifying where the start and end anchor would be.
</description>
</method>
- <method name="get_capture" qualifiers="const">
+ <method name="sub" qualifiers="const">
<return type="String">
</return>
- <argument index="0" name="capture" type="int">
+ <argument index="0" name="text" type="String">
+ </argument>
+ <argument index="1" name="replacement" type="String">
+ </argument>
+ <argument index="2" name="all" type="bool" default="false">
+ </argument>
+ <argument index="3" name="start" type="int" default="0">
+ </argument>
+ <argument index="4" name="end" type="int" default="-1">
</argument>
<description>
- Returns a captured group. A captured group is the part of a string that matches a part of the pattern delimited by parentheses (unless they are non-capturing parentheses [i](?:)[/i]).
+ Searches the text for the compiled pattern and replaces it with the specified string. Escapes and backreferences such as [code]\1[/code] and [code]\g&lt;name&gt;[/code] expanded and resolved. By default only the first instance is replaced but it can be changed for all instances (global replacement). The region to search within can be specified without modifying where the start and end anchor would be.
</description>
</method>
- <method name="get_capture_count" qualifiers="const">
- <return type="int">
+ </methods>
+ <constants>
+ </constants>
+</class>
+<class name="RegExMatch" inherits="Reference" category="Core">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <methods>
+ <method name="expand" qualifiers="const">
+ <return type="String">
</return>
+ <argument index="0" name="template" type="String">
+ </argument>
<description>
- Returns the number of capturing groups. A captured group is the part of a string that matches a part of the pattern delimited by parentheses (unless they are non-capturing parentheses [i](?:)[/i]).
+ Using results from the search, returns the specified string with escapes and backreferences such as [code]\1[/code] and [code]\g&lt;name&gt;[/code] expanded and resolved.
</description>
</method>
- <method name="get_capture_start" qualifiers="const">
+ <method name="get_end" qualifiers="const">
<return type="int">
</return>
- <argument index="0" name="capture" type="int">
+ <argument index="0" name="name" type="Variant" default="0">
</argument>
<description>
+ Returns the end position of the match in the string. An interger can be specified for numeric groups or a string for named groups. Returns -1 if that group wasn't found or doesn't exist. Defaults to 0 (whole pattern).
</description>
</method>
- <method name="get_captures" qualifiers="const">
- <return type="StringArray">
+ <method name="get_group_array" qualifiers="const">
+ <return type="Array">
</return>
<description>
- Return a list of all the captures made by the regular expression.
+ Returns an array of the results of the numeric groups.
</description>
</method>
- <method name="is_valid" qualifiers="const">
- <return type="bool">
+ <method name="get_group_count" qualifiers="const">
+ <return type="int">
</return>
<description>
- Returns whether this object has a valid regular expression assigned.
+ Returns the number of numeric capturing groups.
+ </description>
+ </method>
+ <method name="get_name_dict" qualifiers="const">
+ <return type="Dictionary">
+ </return>
+ <description>
+ Returns a dictionary containing the named capturing groups and their results.
+ </description>
+ </method>
+ <method name="get_names" qualifiers="const">
+ <return type="Array">
+ </return>
+ <description>
+ Returns an array of names of named capturing groups.
+ </description>
+ </method>
+ <method name="get_start" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="name" type="Variant" default="0">
+ </argument>
+ <description>
+ Returns the starting position of the match in the string. An interger can be specified for numeric groups or a string for named groups. Returns -1 if that group wasn't found or doesn't exist. Defaults to 0 (whole pattern).
+ </description>
+ </method>
+ <method name="get_string" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="name" type="Variant" default="0">
+ </argument>
+ <description>
+ Returns the result of the match in the string. An interger can be specified for numeric groups or a string for named groups. Returns -1 if that group wasn't found or doesn't exist. Defaults to 0 (whole pattern).
</description>
</method>
</methods>
diff --git a/doc/tools/doc_merge.py b/doc/tools/doc_merge.py
index b55902ce4f..536770bee4 100644
--- a/doc/tools/doc_merge.py
+++ b/doc/tools/doc_merge.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py
index 35a6bc8bc4..f0ede405ce 100755
--- a/doc/tools/doc_status.py
+++ b/doc/tools/doc_status.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
import sys
import re
diff --git a/doc/tools/makedocs.py b/doc/tools/makedocs.py
index 063ee29002..731e04f6fc 100644
--- a/doc/tools/makedocs.py
+++ b/doc/tools/makedocs.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
diff --git a/doc/tools/makedoku.py b/doc/tools/makedoku.py
index 97819d7da3..7c3ca29af8 100644
--- a/doc/tools/makedoku.py
+++ b/doc/tools/makedoku.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
diff --git a/doc/tools/makehtml.py b/doc/tools/makehtml.py
index 26edda0935..b0a8cbda88 100644
--- a/doc/tools/makehtml.py
+++ b/doc/tools/makehtml.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
diff --git a/doc/tools/makemd.py b/doc/tools/makemd.py
index 38b493b0bf..e0fbe9af03 100644
--- a/doc/tools/makemd.py
+++ b/doc/tools/makemd.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py
index 45e690cb65..718cf4a275 100644
--- a/doc/tools/makerst.py
+++ b/doc/tools/makerst.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
# -*- coding: utf-8 -*-
import codecs
diff --git a/drivers/SCsub b/drivers/SCsub
index c496f46a1f..8571b0c4ec 100644
--- a/drivers/SCsub
+++ b/drivers/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.drivers_sources=[]
@@ -14,6 +16,8 @@ SConscript('alsa/SCsub');
SConscript('pulseaudio/SCsub');
if (env["platform"] == "windows"):
SConscript("rtaudio/SCsub");
+if (env["xaudio2"] == "yes"):
+ SConscript("xaudio2/SCsub");
# Graphics drivers
SConscript('gles2/SCsub');
@@ -21,7 +25,6 @@ SConscript('gl_context/SCsub');
# Core dependencies
SConscript("png/SCsub");
-SConscript("nrex/SCsub");
# Tools override
# FIXME: Should likely be integrated in the tools/ codebase
@@ -31,51 +34,8 @@ if (env["tools"]=="yes"):
if env['vsproj']=="yes":
env.AddToVSProject(env.drivers_sources)
-
-# Split drivers, this used to be needed for windows until separate builders for windows were created
-# FIXME: Check if still needed now that the drivers were made more lightweight
-if (env.split_drivers):
- import string
-
- num = 0
- cur_base = ""
- max_src = 64
- list = []
- lib_list = []
-
- for f in env.drivers_sources:
- fname = ""
- if type(f) == type(""):
- fname = env.File(f).path
- else:
- fname = env.File(f)[0].path
- fname = fname.replace("\\", "/")
- base = string.join(fname.split("/")[:2], "/")
- if base != cur_base and len(list) > max_src:
- if num > 0:
- lib = env.Library("drivers"+str(num), list)
- lib_list.append(lib)
- list = []
- num = num+1
- cur_base = base
- list.append(f)
-
- lib = env.Library("drivers"+str(num), list)
- lib_list.append(lib)
-
- if len(lib_list) > 0:
- import os, sys
- if os.name=='posix' and sys.platform=='msys':
- env.Replace(ARFLAGS=['rcsT'])
-
- lib = env.Library("drivers_collated", lib_list)
- lib_list = [lib]
-
- drivers_base=[]
- env.add_source_files(drivers_base,"*.cpp")
- lib_list.insert(0, env.Library("drivers", drivers_base))
-
- env.Prepend(LIBS=lib_list)
+if env.split_drivers:
+ env.split_lib("drivers")
else:
env.add_source_files(env.drivers_sources,"*.cpp")
lib = env.Library("drivers",env.drivers_sources)
diff --git a/drivers/alsa/SCsub b/drivers/alsa/SCsub
index 0582e01978..ee39fd2631 100644
--- a/drivers/alsa/SCsub
+++ b/drivers/alsa/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.drivers_sources, "*.cpp")
diff --git a/drivers/convex_decomp/SCsub b/drivers/convex_decomp/SCsub
index 0582e01978..ee39fd2631 100644
--- a/drivers/convex_decomp/SCsub
+++ b/drivers/convex_decomp/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.drivers_sources, "*.cpp")
diff --git a/drivers/gl_context/SCsub b/drivers/gl_context/SCsub
index b05a96cb99..cf37e9fe36 100644
--- a/drivers/gl_context/SCsub
+++ b/drivers/gl_context/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
if (env["platform"] in ["haiku","osx","windows","x11"]):
diff --git a/drivers/gles2/SCsub b/drivers/gles2/SCsub
index 89d7d86360..dedd794dba 100644
--- a/drivers/gles2/SCsub
+++ b/drivers/gles2/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.drivers_sources, "*.cpp")
diff --git a/drivers/gles2/shaders/SCsub b/drivers/gles2/shaders/SCsub
index 88445f34c1..1de5604321 100644
--- a/drivers/gles2/shaders/SCsub
+++ b/drivers/gles2/shaders/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
if env['BUILDERS'].has_key('GLSL120GLES'):
diff --git a/drivers/nrex/README.md b/drivers/nrex/README.md
deleted file mode 100644
index 7a942b2452..0000000000
--- a/drivers/nrex/README.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# NREX: Node RegEx
-
-[![Build Status](https://travis-ci.org/leezh/nrex.svg?branch=master)](https://travis-ci.org/leezh/nrex)
-
-** Version 0.2 **
-
-Small node-based regular expression library. It only does text pattern
-matchhing, not replacement. To use add the files `nrex.hpp`, `nrex.cpp`
-and `nrex_config.h` to your project and follow the example:
-
- nrex regex;
- regex.compile("^(fo+)bar$");
-
- nrex_result captures[regex.capture_size()];
- if (regex.match("foobar", captures))
- {
- std::cout << captures[0].start << std::endl;
- std::cout << captures[0].length << std::endl;
- }
-
-More details about its use is documented in `nrex.hpp`
-
-Currently supported features:
- * Capturing `()` and non-capturing `(?:)` groups
- * Any character `.` (includes newlines)
- * Shorthand caracter classes `\w\W\s\S\d\D`
- * POSIX character classes such as `[[:alnum:]]`
- * Bracket expressions such as `[A-Za-z]`
- * Simple quantifiers `?`, `*` and `+`
- * Range quantifiers `{0,1}`
- * Lazy (non-greedy) quantifiers `*?`
- * Begining `^` and end `$` anchors
- * Word boundaries `\b`
- * Alternation `|`
- * ASCII `\xFF` code points
- * Unicode `\uFFFF` code points
- * Positive `(?=)` and negative `(?!)` lookahead
- * Positive `(?<=)` and negative `(?<!)` lookbehind (fixed length and no alternations)
- * Backreferences `\1` and `\g{1}` (limited by default to 9 - can be unlimited)
-
-## License
-
-Copyright (c) 2015-2016, Zher Huei Lee
-All rights reserved.
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would
- be appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not
- be misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source
- distribution.
-
-
-# Changes
-
-## Version 0.2 (2016-08-04)
- * Fixed capturing groups matching to invalid results
- * Fixed parents of recursive quantifiers not expanding properly
- * Fixed LookAhead sometimes adding to result
- * More verbose unit testing
-
-## Version 0.1 (2015-12-04)
- * Initial release
diff --git a/drivers/nrex/SCsub b/drivers/nrex/SCsub
deleted file mode 100644
index 0582e01978..0000000000
--- a/drivers/nrex/SCsub
+++ /dev/null
@@ -1,5 +0,0 @@
-Import('env')
-
-env.add_source_files(env.drivers_sources, "*.cpp")
-
-Export('env')
diff --git a/drivers/nrex/nrex.cpp b/drivers/nrex/nrex.cpp
deleted file mode 100644
index 69e04285e3..0000000000
--- a/drivers/nrex/nrex.cpp
+++ /dev/null
@@ -1,1496 +0,0 @@
-// NREX: Node RegEx
-// Version 0.2
-//
-// Copyright (c) 2015-2016, Zher Huei Lee
-// All rights reserved.
-//
-// This software is provided 'as-is', without any express or implied
-// warranty. In no event will the authors be held liable for any damages
-// arising from the use of this software.
-//
-// Permission is granted to anyone to use this software for any purpose,
-// including commercial applications, and to alter it and redistribute it
-// freely, subject to the following restrictions:
-//
-// 1. The origin of this software must not be misrepresented; you must not
-// claim that you wrote the original software. If you use this software
-// in a product, an acknowledgment in the product documentation would
-// be appreciated but is not required.
-//
-// 2. Altered source versions must be plainly marked as such, and must not
-// be misrepresented as being the original software.
-//
-// 3. This notice may not be removed or altered from any source
-// distribution.
-//
-
-#include "nrex.hpp"
-
-#ifdef NREX_UNICODE
-#include <wctype.h>
-#include <wchar.h>
-#define NREX_ISALPHANUM iswalnum
-#define NREX_ISSPACE iswspace
-#define NREX_STRLEN wcslen
-#else
-#include <ctype.h>
-#include <string.h>
-#define NREX_ISALPHANUM isalnum
-#define NREX_ISSPACE isspace
-#define NREX_STRLEN strlen
-#endif
-
-#ifdef NREX_THROW_ERROR
-#define NREX_COMPILE_ERROR(M) throw nrex_compile_error(M)
-#else
-#define NREX_COMPILE_ERROR(M) reset(); return false
-#endif
-
-#ifndef NREX_NEW
-#define NREX_NEW(X) new X
-#define NREX_NEW_ARRAY(X, N) new X[N]
-#define NREX_DELETE(X) delete X
-#define NREX_DELETE_ARRAY(X) delete[] X
-#endif
-
-template<typename T>
-class nrex_array
-{
- private:
- T* _data;
- unsigned int _reserved;
- unsigned int _size;
- public:
- nrex_array()
- : _data(NREX_NEW_ARRAY(T, 2))
- , _reserved(2)
- , _size(0)
- {
- }
-
- nrex_array(unsigned int reserved)
- : _data(NREX_NEW_ARRAY(T, reserved ? reserved : 1))
- , _reserved(reserved ? reserved : 1)
- , _size(0)
- {
- }
-
- ~nrex_array()
- {
- NREX_DELETE_ARRAY(_data);
- }
-
- unsigned int size() const
- {
- return _size;
- }
-
- void reserve(unsigned int size)
- {
- if (size < _size) {
- size = _size;
- }
- if (size == 0) {
- size = 1;
- }
- T* old = _data;
- _data = NREX_NEW_ARRAY(T, size);
- _reserved = size;
- for (unsigned int i = 0; i < _size; ++i)
- {
- _data[i] = old[i];
- }
- NREX_DELETE_ARRAY(old);
- }
-
- void push(T item)
- {
- if (_size == _reserved)
- {
- reserve(_reserved * 2);
- }
- _data[_size] = item;
- _size++;
- }
-
- const T& top() const
- {
- return _data[_size - 1];
- }
-
- const T& operator[] (unsigned int i) const
- {
- return _data[i];
- }
-
- void pop()
- {
- if (_size > 0)
- {
- --_size;
- }
- }
-};
-
-static int nrex_parse_hex(nrex_char c)
-{
- if ('0' <= c && c <= '9')
- {
- return int(c - '0');
- }
- else if ('a' <= c && c <= 'f')
- {
- return int(c - 'a') + 10;
- }
- else if ('A' <= c && c <= 'F')
- {
- return int(c - 'A') + 10;
- }
- return -1;
-}
-
-static nrex_char nrex_unescape(const nrex_char*& c)
-{
- switch (c[1])
- {
- case '0': ++c; return '\0';
- case 'a': ++c; return '\a';
- case 'e': ++c; return '\e';
- case 'f': ++c; return '\f';
- case 'n': ++c; return '\n';
- case 'r': ++c; return '\r';
- case 't': ++c; return '\t';
- case 'v': ++c; return '\v';
- case 'b': ++c; return '\b';
- case 'x':
- {
- int point = 0;
- for (int i = 2; i <= 3; ++i)
- {
- int res = nrex_parse_hex(c[i]);
- if (res == -1)
- {
- return '\0';
- }
- point = (point << 4) + res;
- }
- c = &c[3];
- return nrex_char(point);
- }
- case 'u':
- {
- int point = 0;
- for (int i = 2; i <= 5; ++i)
- {
- int res = nrex_parse_hex(c[i]);
- if (res == -1)
- {
- return '\0';
- }
- point = (point << 4) + res;
- }
- c = &c[5];
- return nrex_char(point);
- }
- }
- return (++c)[0];
-}
-
-struct nrex_search
-{
- const nrex_char* str;
- nrex_result* captures;
- int end;
- bool complete;
- nrex_array<int> lookahead_pos;
-
- nrex_char at(int pos)
- {
- return str[pos];
- }
-
- nrex_search(const nrex_char* str, nrex_result* captures, int lookahead)
- : str(str)
- , captures(captures)
- , end(0)
- , lookahead_pos(lookahead)
- {
- }
-};
-
-struct nrex_node
-{
- nrex_node* next;
- nrex_node* previous;
- nrex_node* parent;
- bool quantifiable;
- int length;
-
- nrex_node(bool quantify = false)
- : next(NULL)
- , previous(NULL)
- , parent(NULL)
- , quantifiable(quantify)
- , length(-1)
- {
- }
-
- virtual ~nrex_node()
- {
- if (next)
- {
- NREX_DELETE(next);
- }
- }
-
- virtual int test(nrex_search* s, int pos) const
- {
- return next ? next->test(s, pos) : -1;
- }
-
- virtual int test_parent(nrex_search* s, int pos) const
- {
- if (next)
- {
- pos = next->test(s, pos);
- }
- if (pos >= 0)
- {
- s->complete = true;
- }
- if (parent && pos >= 0)
- {
- pos = parent->test_parent(s, pos);
- }
- if (pos < 0)
- {
- s->complete = false;
- }
- return pos;
- }
-
- void increment_length(int amount, bool subtract = false)
- {
- if (amount >= 0 && length >= 0)
- {
- if (!subtract)
- {
- length += amount;
- }
- else
- {
- length -= amount;
- }
- }
- else
- {
- length = -1;
- }
- if (parent)
- {
- parent->increment_length(amount, subtract);
- }
- }
-};
-
-enum nrex_group_type
-{
- nrex_group_capture,
- nrex_group_non_capture,
- nrex_group_bracket,
- nrex_group_look_ahead,
- nrex_group_look_behind,
-};
-
-struct nrex_node_group : public nrex_node
-{
- nrex_group_type type;
- int id;
- bool negate;
- nrex_array<nrex_node*> childset;
- nrex_node* back;
-
- nrex_node_group(nrex_group_type type, int id = 0)
- : nrex_node(true)
- , type(type)
- , id(id)
- , negate(false)
- , back(NULL)
- {
- if (type != nrex_group_bracket)
- {
- length = 0;
- }
- else
- {
- length = 1;
- }
- if (type == nrex_group_look_ahead || type == nrex_group_look_behind)
- {
- quantifiable = false;
- }
- }
-
- virtual ~nrex_node_group()
- {
- for (unsigned int i = 0; i < childset.size(); ++i)
- {
- NREX_DELETE(childset[i]);
- }
-
- }
-
- int test(nrex_search* s, int pos) const
- {
- int old_start;
- if (type == nrex_group_capture)
- {
- old_start = s->captures[id].start;
- s->captures[id].start = pos;
- }
- for (unsigned int i = 0; i < childset.size(); ++i)
- {
- s->complete = false;
- int offset = 0;
- if (type == nrex_group_look_behind)
- {
- if (pos < length)
- {
- return -1;
- }
- offset = length;
- }
- if (type == nrex_group_look_ahead)
- {
- s->lookahead_pos.push(pos);
- }
- int res = childset[i]->test(s, pos - offset);
- if (type == nrex_group_look_ahead)
- {
- s->lookahead_pos.pop();
- }
- if (s->complete)
- {
- return res;
- }
- if (negate)
- {
- if (res < 0)
- {
- res = pos + 1;
- }
- else
- {
- return -1;
- }
- if (i + 1 < childset.size())
- {
- continue;
- }
- }
- if (res >= 0)
- {
- if (type == nrex_group_capture)
- {
- s->captures[id].length = res - pos;
- }
- else if (type == nrex_group_look_ahead || type == nrex_group_look_behind)
- {
- res = pos;
- }
- return next ? next->test(s, res) : res;
- }
- }
- if (type == nrex_group_capture)
- {
- s->captures[id].start = old_start;
- }
- return -1;
- }
-
- virtual int test_parent(nrex_search* s, int pos) const
- {
- if (type == nrex_group_capture)
- {
- s->captures[id].length = pos - s->captures[id].start;
- }
- if (type == nrex_group_look_ahead)
- {
- pos = s->lookahead_pos[id];
- }
- return nrex_node::test_parent(s, pos);
- }
-
- void add_childset()
- {
- if (childset.size() > 0 && type != nrex_group_bracket)
- {
- length = -1;
- }
- back = NULL;
- }
-
- void add_child(nrex_node* node)
- {
- node->parent = this;
- node->previous = back;
- if (back && type != nrex_group_bracket)
- {
- back->next = node;
- }
- else
- {
- childset.push(node);
- }
- if (type != nrex_group_bracket)
- {
- increment_length(node->length);
- }
- back = node;
- }
-
- nrex_node* swap_back(nrex_node* node)
- {
- if (!back)
- {
- add_child(node);
- return NULL;
- }
- nrex_node* old = back;
- if (!old->previous)
- {
- childset.pop();
- }
- if (type != nrex_group_bracket)
- {
- increment_length(old->length, true);
- }
- back = old->previous;
- add_child(node);
- return old;
- }
-
- void pop_back()
- {
- if (back)
- {
- nrex_node* old = back;
- if (!old->previous)
- {
- childset.pop();
- }
- if (type != nrex_group_bracket)
- {
- increment_length(old->length, true);
- }
- back = old->previous;
- NREX_DELETE(old);
- }
- }
-};
-
-struct nrex_node_char : public nrex_node
-{
- nrex_char ch;
-
- nrex_node_char(nrex_char c)
- : nrex_node(true)
- , ch(c)
- {
- length = 1;
- }
-
- int test(nrex_search* s, int pos) const
- {
- if (s->end <= pos || 0 > pos || s->at(pos) != ch)
- {
- return -1;
- }
- return next ? next->test(s, pos + 1) : pos + 1;
- }
-};
-
-struct nrex_node_range : public nrex_node
-{
- nrex_char start;
- nrex_char end;
-
- nrex_node_range(nrex_char s, nrex_char e)
- : nrex_node(true)
- , start(s)
- , end(e)
- {
- length = 1;
- }
-
- int test(nrex_search* s, int pos) const
- {
- if (s->end <= pos || 0 > pos)
- {
- return -1;
- }
- nrex_char c = s->at(pos);
- if (c < start || end < c)
- {
- return -1;
- }
- return next ? next->test(s, pos + 1) : pos + 1;
- }
-};
-
-enum nrex_class_type
-{
- nrex_class_none,
- nrex_class_alnum,
- nrex_class_alpha,
- nrex_class_blank,
- nrex_class_cntrl,
- nrex_class_digit,
- nrex_class_graph,
- nrex_class_lower,
- nrex_class_print,
- nrex_class_punct,
- nrex_class_space,
- nrex_class_upper,
- nrex_class_xdigit,
- nrex_class_word
-};
-
-static bool nrex_compare_class(const nrex_char** pos, const char* text)
-{
- unsigned int i = 0;
- for (i = 0; text[i] != '\0'; ++i)
- {
- if ((*pos)[i] != text[i])
- {
- return false;
- }
- }
- if ((*pos)[i++] != ':' || (*pos)[i] != ']')
- {
- return false;
- }
- *pos = &(*pos)[i];
- return true;
-}
-
-#define NREX_COMPARE_CLASS(POS, NAME) if (nrex_compare_class(POS, #NAME)) return nrex_class_ ## NAME
-
-static nrex_class_type nrex_parse_class(const nrex_char** pos)
-{
- NREX_COMPARE_CLASS(pos, alnum);
- NREX_COMPARE_CLASS(pos, alpha);
- NREX_COMPARE_CLASS(pos, blank);
- NREX_COMPARE_CLASS(pos, cntrl);
- NREX_COMPARE_CLASS(pos, digit);
- NREX_COMPARE_CLASS(pos, graph);
- NREX_COMPARE_CLASS(pos, lower);
- NREX_COMPARE_CLASS(pos, print);
- NREX_COMPARE_CLASS(pos, punct);
- NREX_COMPARE_CLASS(pos, space);
- NREX_COMPARE_CLASS(pos, upper);
- NREX_COMPARE_CLASS(pos, xdigit);
- NREX_COMPARE_CLASS(pos, word);
- return nrex_class_none;
-}
-
-struct nrex_node_class : public nrex_node
-{
- nrex_class_type type;
-
- nrex_node_class(nrex_class_type t)
- : nrex_node(true)
- , type(t)
- {
- length = 1;
- }
-
- int test(nrex_search* s, int pos) const
- {
- if (s->end <= pos || 0 > pos)
- {
- return -1;
- }
- if (!test_class(s->at(pos)))
- {
- return -1;
- }
- return next ? next->test(s, pos + 1) : pos + 1;
- }
-
- bool test_class(nrex_char c) const
- {
- if ((0 <= c && c <= 0x1F) || c == 0x7F)
- {
- if (type == nrex_class_cntrl)
- {
- return true;
- }
- }
- else if (c < 0x7F)
- {
- if (type == nrex_class_print)
- {
- return true;
- }
- else if (type == nrex_class_graph && c != ' ')
- {
- return true;
- }
- else if ('0' <= c && c <= '9')
- {
- switch (type)
- {
- case nrex_class_alnum:
- case nrex_class_digit:
- case nrex_class_xdigit:
- case nrex_class_word:
- return true;
- default:
- break;
- }
- }
- else if ('A' <= c && c <= 'Z')
- {
- switch (type)
- {
- case nrex_class_alnum:
- case nrex_class_alpha:
- case nrex_class_upper:
- case nrex_class_word:
- return true;
- case nrex_class_xdigit:
- if (c <= 'F')
- {
- return true;
- }
- default:
- break;
- }
- }
- else if ('a' <= c && c <= 'z')
- {
- switch (type)
- {
- case nrex_class_alnum:
- case nrex_class_alpha:
- case nrex_class_lower:
- case nrex_class_word:
- return true;
- case nrex_class_xdigit:
- if (c <= 'f')
- {
- return true;
- }
- default:
- break;
- }
- }
- }
- switch (c)
- {
- case ' ':
- case '\t':
- if (type == nrex_class_blank)
- {
- return true;
- }
- case '\r':
- case '\n':
- case '\f':
- if (type == nrex_class_space)
- {
- return true;
- }
- break;
- case '_':
- if (type == nrex_class_word)
- {
- return true;
- }
- case ']':
- case '[':
- case '!':
- case '"':
- case '#':
- case '$':
- case '%':
- case '&':
- case '\'':
- case '(':
- case ')':
- case '*':
- case '+':
- case ',':
- case '.':
- case '/':
- case ':':
- case ';':
- case '<':
- case '=':
- case '>':
- case '?':
- case '@':
- case '\\':
- case '^':
- case '`':
- case '{':
- case '|':
- case '}':
- case '~':
- case '-':
- if (type == nrex_class_punct)
- {
- return true;
- }
- break;
- default:
- break;
- }
- return false;
- }
-};
-
-static bool nrex_is_shorthand(nrex_char repr)
-{
- switch (repr)
- {
- case 'W':
- case 'w':
- case 'D':
- case 'd':
- case 'S':
- case 's':
- return true;
- }
- return false;
-}
-
-struct nrex_node_shorthand : public nrex_node
-{
- nrex_char repr;
-
- nrex_node_shorthand(nrex_char c)
- : nrex_node(true)
- , repr(c)
- {
- length = 1;
- }
-
- int test(nrex_search* s, int pos) const
- {
- if (s->end <= pos || 0 > pos)
- {
- return -1;
- }
- bool found = false;
- bool invert = false;
- nrex_char c = s->at(pos);
- switch (repr)
- {
- case '.':
- found = true;
- break;
- case 'W':
- invert = true;
- case 'w':
- if (c == '_' || NREX_ISALPHANUM(c))
- {
- found = true;
- }
- break;
- case 'D':
- invert = true;
- case 'd':
- if ('0' <= c && c <= '9')
- {
- found = true;
- }
- break;
- case 'S':
- invert = true;
- case 's':
- if (NREX_ISSPACE(c))
- {
- found = true;
- }
- break;
- }
- if (found == invert)
- {
- return -1;
- }
- return next ? next->test(s, pos + 1) : pos + 1;
- }
-};
-
-static bool nrex_is_quantifier(nrex_char repr)
-{
- switch (repr)
- {
- case '?':
- case '*':
- case '+':
- case '{':
- return true;
- }
- return false;
-}
-
-struct nrex_node_quantifier : public nrex_node
-{
- int min;
- int max;
- bool greedy;
- nrex_node* child;
-
- nrex_node_quantifier(int min, int max)
- : nrex_node()
- , min(min)
- , max(max)
- , greedy(true)
- , child(NULL)
- {
- }
-
- virtual ~nrex_node_quantifier()
- {
- if (child)
- {
- NREX_DELETE(child);
- }
- }
-
- int test(nrex_search* s, int pos) const
- {
- return test_step(s, pos, 0, pos);
- }
-
- int test_step(nrex_search* s, int pos, int level, int start) const
- {
- if (pos > s->end)
- {
- return -1;
- }
- if (!greedy && level > min)
- {
- int res = pos;
- if (next)
- {
- res = next->test(s, res);
- }
- if (s->complete)
- {
- return res;
- }
- if (res >= 0 && parent->test_parent(s, res) >= 0)
- {
- return res;
- }
- }
- if (max >= 0 && level > max)
- {
- return -1;
- }
- if (level > 1 && level > min + 1 && pos == start)
- {
- return -1;
- }
- int res = pos;
- if (level >= 1)
- {
- res = child->test(s, pos);
- if (s->complete)
- {
- return res;
- }
- }
- if (res >= 0)
- {
- int res_step = test_step(s, res, level + 1, start);
- if (res_step >= 0)
- {
- return res_step;
- }
- else if (greedy && level >= min)
- {
- if (next)
- {
- res = next->test(s, res);
- }
- if (s->complete)
- {
- return res;
- }
- if (res >= 0 && parent->test_parent(s, res) >= 0)
- {
- return res;
- }
- }
- }
- return -1;
- }
-
- virtual int test_parent(nrex_search* s, int pos) const
- {
- s->complete = false;
- return pos;
- }
-};
-
-struct nrex_node_anchor : public nrex_node
-{
- bool end;
-
- nrex_node_anchor(bool end)
- : nrex_node()
- , end(end)
- {
- length = 0;
- }
-
- int test(nrex_search* s, int pos) const
- {
- if (!end && pos != 0)
- {
- return -1;
- }
- else if (end && pos != s->end)
- {
- return -1;
- }
- return next ? next->test(s, pos) : pos;
- }
-};
-
-struct nrex_node_word_boundary : public nrex_node
-{
- bool inverse;
-
- nrex_node_word_boundary(bool inverse)
- : nrex_node()
- , inverse(inverse)
- {
- length = 0;
- }
-
- int test(nrex_search* s, int pos) const
- {
- bool left = false;
- bool right = false;
- if (pos != 0)
- {
- nrex_char c = s->at(pos - 1);
- if (c == '_' || NREX_ISALPHANUM(c))
- {
- left = true;
- }
- }
- if (pos != s->end)
- {
- nrex_char c = s->at(pos);
- if (c == '_' || NREX_ISALPHANUM(c))
- {
- right = true;
- }
- }
- if ((left != right) == inverse)
- {
- return -1;
- }
- return next ? next->test(s, pos) : pos;
- }
-};
-
-struct nrex_node_backreference : public nrex_node
-{
- int ref;
-
- nrex_node_backreference(int ref)
- : nrex_node(true)
- , ref(ref)
- {
- length = -1;
- }
-
- int test(nrex_search* s, int pos) const
- {
- nrex_result& r = s->captures[ref];
- for (int i = 0; i < r.length; ++i)
- {
- if (pos + i >= s->end)
- {
- return -1;
- }
- if (s->at(r.start + i) != s->at(pos + i))
- {
- return -1;
- }
- }
- return next ? next->test(s, pos + r.length) : pos + r.length;
- }
-};
-
-bool nrex_has_lookbehind(nrex_array<nrex_node_group*>& stack)
-{
- for (unsigned int i = 0; i < stack.size(); i++)
- {
- if (stack[i]->type == nrex_group_look_behind)
- {
- return true;
- }
- }
- return false;
-}
-
-nrex::nrex()
- : _capturing(0)
- , _lookahead_depth(0)
- , _root(NULL)
-{
-}
-
-nrex::nrex(const nrex_char* pattern, int captures)
- : _capturing(0)
- , _lookahead_depth(0)
- , _root(NULL)
-{
- compile(pattern, captures);
-}
-
-nrex::~nrex()
-{
- if (_root)
- {
- NREX_DELETE(_root);
- }
-}
-
-bool nrex::valid() const
-{
- return (_root != NULL);
-}
-
-void nrex::reset()
-{
- _capturing = 0;
- _lookahead_depth = 0;
- if (_root)
- {
- NREX_DELETE(_root);
- }
- _root = NULL;
-}
-
-int nrex::capture_size() const
-{
- if (_root)
- {
- return _capturing + 1;
- }
- return 0;
-}
-
-bool nrex::compile(const nrex_char* pattern, int captures)
-{
- reset();
- nrex_node_group* root = NREX_NEW(nrex_node_group(nrex_group_capture, _capturing));
- nrex_array<nrex_node_group*> stack;
- stack.push(root);
- unsigned int lookahead_level = 0;
- _root = root;
-
- for (const nrex_char* c = pattern; c[0] != '\0'; ++c)
- {
- if (c[0] == '(')
- {
- if (c[1] == '?')
- {
- if (c[2] == ':')
- {
- c = &c[2];
- nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_group_non_capture));
- stack.top()->add_child(group);
- stack.push(group);
- }
- else if (c[2] == '!' || c[2] == '=')
- {
- c = &c[2];
- nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_group_look_ahead, lookahead_level++));
- group->negate = (c[0] == '!');
- stack.top()->add_child(group);
- stack.push(group);
- if (lookahead_level > _lookahead_depth)
- {
- _lookahead_depth = lookahead_level;
- }
- }
- else if (c[2] == '<' && (c[3] == '!' || c[3] == '='))
- {
- c = &c[3];
- nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_group_look_behind));
- group->negate = (c[0] == '!');
- stack.top()->add_child(group);
- stack.push(group);
- }
- else
- {
- NREX_COMPILE_ERROR("unrecognised qualifier for group");
- }
- }
- else if (captures >= 0 && _capturing < captures)
- {
- nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_group_capture, ++_capturing));
- stack.top()->add_child(group);
- stack.push(group);
- }
- else
- {
- nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_group_non_capture));
- stack.top()->add_child(group);
- stack.push(group);
- }
- }
- else if (c[0] == ')')
- {
- if (stack.size() > 1)
- {
- if (stack.top()->type == nrex_group_look_ahead)
- {
- --lookahead_level;
- }
- stack.pop();
- }
- else
- {
- NREX_COMPILE_ERROR("unexpected ')'");
- }
- }
- else if (c[0] == '[')
- {
- nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_group_bracket));
- stack.top()->add_child(group);
- if (c[1] == '^')
- {
- group->negate = true;
- ++c;
- }
- bool first_child = true;
- nrex_char previous_child;
- bool previous_child_single = false;
- while (true)
- {
- group->add_childset();
- ++c;
- if (c[0] == '\0')
- {
- NREX_COMPILE_ERROR("unclosed bracket expression '['");
- }
- if (c[0] == '[' && c[1] == ':')
- {
- const nrex_char* d = &c[2];
- nrex_class_type cls = nrex_parse_class(&d);
- if (cls != nrex_class_none)
- {
- c = d;
- group->add_child(NREX_NEW(nrex_node_class(cls)));
- previous_child_single = false;
- }
- else
- {
- group->add_child(NREX_NEW(nrex_node_char('[')));
- previous_child = '[';
- previous_child_single = true;
- }
- }
- else if (c[0] == ']' && !first_child)
- {
- break;
- }
- else if (c[0] == '\\')
- {
- if (nrex_is_shorthand(c[1]))
- {
- group->add_child(NREX_NEW(nrex_node_shorthand(c[1])));
- ++c;
- previous_child_single = false;
- }
- else
- {
- const nrex_char* d = c;
- nrex_char unescaped = nrex_unescape(d);
- if (c == d)
- {
- NREX_COMPILE_ERROR("invalid escape token");
- }
- group->add_child(NREX_NEW(nrex_node_char(unescaped)));
- c = d;
- previous_child = unescaped;
- previous_child_single = true;
- }
- }
- else if (previous_child_single && c[0] == '-')
- {
- bool is_range = false;
- nrex_char next;
- if (c[1] != '\0' && c[1] != ']')
- {
- if (c[1] == '\\')
- {
- const nrex_char* d = ++c;
- next = nrex_unescape(d);
- if (c == d)
- {
- NREX_COMPILE_ERROR("invalid escape token in range");
- }
- }
- else
- {
- next = c[1];
- ++c;
- }
- is_range = true;
- }
- if (is_range)
- {
- if (next < previous_child)
- {
- NREX_COMPILE_ERROR("text range out of order");
- }
- group->pop_back();
- group->add_child(NREX_NEW(nrex_node_range(previous_child, next)));
- previous_child_single = false;
- }
- else
- {
- group->add_child(NREX_NEW(nrex_node_char(c[0])));
- previous_child = c[0];
- previous_child_single = true;
- }
- }
- else
- {
- group->add_child(NREX_NEW(nrex_node_char(c[0])));
- previous_child = c[0];
- previous_child_single = true;
- }
- first_child = false;
- }
- }
- else if (nrex_is_quantifier(c[0]))
- {
- int min = 0;
- int max = -1;
- bool valid_quantifier = true;
- if (c[0] == '?')
- {
- min = 0;
- max = 1;
- }
- else if (c[0] == '+')
- {
- min = 1;
- max = -1;
- }
- else if (c[0] == '*')
- {
- min = 0;
- max = -1;
- }
- else if (c[0] == '{')
- {
- bool max_set = false;
- const nrex_char* d = c;
- while (true)
- {
- ++d;
- if (d[0] == '\0')
- {
- valid_quantifier = false;
- break;
- }
- else if (d[0] == '}')
- {
- break;
- }
- else if (d[0] == ',')
- {
- max_set = true;
- continue;
- }
- else if (d[0] < '0' || '9' < d[0])
- {
- valid_quantifier = false;
- break;
- }
- if (max_set)
- {
- if (max < 0)
- {
- max = int(d[0] - '0');
- }
- else
- {
- max = max * 10 + int(d[0] - '0');
- }
- }
- else
- {
- min = min * 10 + int(d[0] - '0');
- }
- }
- if (!max_set)
- {
- max = min;
- }
- if (valid_quantifier)
- {
- c = d;
- }
- }
- if (valid_quantifier)
- {
- if (stack.top()->back == NULL || !stack.top()->back->quantifiable)
- {
- NREX_COMPILE_ERROR("element not quantifiable");
- }
- nrex_node_quantifier* quant = NREX_NEW(nrex_node_quantifier(min, max));
- if (min == max)
- {
- if (stack.top()->back->length >= 0)
- {
- quant->length = max * stack.top()->back->length;
- }
- }
- else
- {
- if (nrex_has_lookbehind(stack))
- {
- NREX_COMPILE_ERROR("variable length quantifiers inside lookbehind not supported");
- }
- }
- quant->child = stack.top()->swap_back(quant);
- quant->child->previous = NULL;
- quant->child->next = NULL;
- quant->child->parent = quant;
- if (c[1] == '?')
- {
- quant->greedy = false;
- ++c;
- }
- }
- else
- {
- stack.top()->add_child(NREX_NEW(nrex_node_char(c[0])));
- }
- }
- else if (c[0] == '|')
- {
- if (nrex_has_lookbehind(stack))
- {
- NREX_COMPILE_ERROR("alternations inside lookbehind not supported");
- }
- stack.top()->add_childset();
- }
- else if (c[0] == '^' || c[0] == '$')
- {
- stack.top()->add_child(NREX_NEW(nrex_node_anchor((c[0] == '$'))));
- }
- else if (c[0] == '.')
- {
- stack.top()->add_child(NREX_NEW(nrex_node_shorthand('.')));
- }
- else if (c[0] == '\\')
- {
- if (nrex_is_shorthand(c[1]))
- {
- stack.top()->add_child(NREX_NEW(nrex_node_shorthand(c[1])));
- ++c;
- }
- else if (('1' <= c[1] && c[1] <= '9') || (c[1] == 'g' && c[2] == '{'))
- {
- int ref = 0;
- bool unclosed = false;
- if (c[1] == 'g')
- {
- unclosed = true;
- c = &c[2];
- }
- while ('0' <= c[1] && c[1] <= '9')
- {
- ref = ref * 10 + int(c[1] - '0');
- ++c;
- }
- if (c[1] == '}')
- {
- unclosed = false;
- ++c;
- }
- if (ref > _capturing || ref <= 0 || unclosed)
- {
- NREX_COMPILE_ERROR("backreference to non-existent capture");
- }
- if (nrex_has_lookbehind(stack))
- {
- NREX_COMPILE_ERROR("backreferences inside lookbehind not supported");
- }
- stack.top()->add_child(NREX_NEW(nrex_node_backreference(ref)));
- }
- else if (c[1] == 'b' || c[1] == 'B')
- {
- stack.top()->add_child(NREX_NEW(nrex_node_word_boundary(c[1] == 'B')));
- ++c;
- }
- else
- {
- const nrex_char* d = c;
- nrex_char unescaped = nrex_unescape(d);
- if (c == d)
- {
- NREX_COMPILE_ERROR("invalid escape token");
- }
- stack.top()->add_child(NREX_NEW(nrex_node_char(unescaped)));
- c = d;
- }
- }
- else
- {
- stack.top()->add_child(NREX_NEW(nrex_node_char(c[0])));
- }
- }
- if (stack.size() > 1)
- {
- NREX_COMPILE_ERROR("unclosed group '('");
- }
- return true;
-}
-
-bool nrex::match(const nrex_char* str, nrex_result* captures, int offset, int end) const
-{
- if (!_root)
- {
- return false;
- }
- nrex_search s(str, captures, _lookahead_depth);
- if (end >= offset)
- {
- s.end = end;
- }
- else
- {
- s.end = NREX_STRLEN(str);
- }
- for (int i = offset; i <= s.end; ++i)
- {
- for (int c = 0; c <= _capturing; ++c)
- {
- captures[c].start = 0;
- captures[c].length = 0;
- }
- if (_root->test(&s, i) >= 0)
- {
- return true;
- }
- }
- return false;
-}
diff --git a/drivers/nrex/nrex.hpp b/drivers/nrex/nrex.hpp
deleted file mode 100644
index d30b7d0102..0000000000
--- a/drivers/nrex/nrex.hpp
+++ /dev/null
@@ -1,176 +0,0 @@
-// NREX: Node RegEx
-// Version 0.2
-//
-// Copyright (c) 2015-2016, Zher Huei Lee
-// All rights reserved.
-//
-// This software is provided 'as-is', without any express or implied
-// warranty. In no event will the authors be held liable for any damages
-// arising from the use of this software.
-//
-// Permission is granted to anyone to use this software for any purpose,
-// including commercial applications, and to alter it and redistribute it
-// freely, subject to the following restrictions:
-//
-// 1. The origin of this software must not be misrepresented; you must not
-// claim that you wrote the original software. If you use this software
-// in a product, an acknowledgment in the product documentation would
-// be appreciated but is not required.
-//
-// 2. Altered source versions must be plainly marked as such, and must not
-// be misrepresented as being the original software.
-//
-// 3. This notice may not be removed or altered from any source
-// distribution.
-//
-
-#ifndef NREX_HPP
-#define NREX_HPP
-
-#include "nrex_config.h"
-
-#ifdef NREX_UNICODE
-typedef wchar_t nrex_char;
-#else
-typedef char nrex_char;
-#endif
-
-/*!
- * \brief Struct to contain the range of a capture result
- *
- * The range provided is relative to the begining of the searched string.
- *
- * \see nrex_node::match()
- */
-struct nrex_result
-{
- public:
- int start; /*!< Start of text range */
- int length; /*!< Length of text range */
-};
-
-class nrex_node;
-
-/*!
- * \brief Holds the compiled regex pattern
- */
-class nrex
-{
- private:
- unsigned int _capturing;
- unsigned int _lookahead_depth;
- nrex_node* _root;
- public:
-
- /*!
- * \brief Initialises an empty regex container
- */
- nrex();
-
- /*!
- * \brief Initialises and compiles the regex pattern
- *
- * This calls nrex::compile() with the same arguments. To check whether
- * the compilation was successfull, use nrex::valid().
- *
- * If the NREX_THROW_ERROR was defined it would automatically throw a
- * runtime error nrex_compile_error if it encounters a problem when
- * parsing the pattern.
- *
- * \param pattern The regex pattern
- * \param captures The maximum number of capture groups to allow. Any
- * extra would be converted to non-capturing groups.
- * If negative, no limit would be imposed. Defaults
- * to 9.
- *
- * \see nrex::compile()
- */
- nrex(const nrex_char* pattern, int captures = 9);
-
- ~nrex();
-
- /*!
- * \brief Removes the compiled regex and frees up the memory
- */
- void reset();
-
- /*!
- * \brief Checks if there is a compiled regex being stored
- * \return True if present, False if not present
- */
- bool valid() const;
-
- /*!
- * \brief Provides number of captures the compiled regex uses
- *
- * This is used to provide the array size of the captures needed for
- * nrex::match() to work. The size is actually the number of capture
- * groups + one for the matching of the entire pattern. This can be
- * capped using the extra argument given in nrex::compile()
- * (default 10).
- *
- * \return The number of captures
- */
- int capture_size() const;
-
- /*!
- * \brief Compiles the provided regex pattern
- *
- * This automatically removes the existing compiled regex if already
- * present.
- *
- * If the NREX_THROW_ERROR was defined it would automatically throw a
- * runtime error nrex_compile_error if it encounters a problem when
- * parsing the pattern.
- *
- * \param pattern The regex pattern
- * \param captures The maximum number of capture groups to allow. Any
- * extra would be converted to non-capturing groups.
- * If negative, no limit would be imposed. Defaults
- * to 9.
- * \return True if the pattern was succesfully compiled
- */
- bool compile(const nrex_char* pattern, int captures = 9);
-
- /*!
- * \brief Uses the pattern to search through the provided string
- * \param str The text to search through. It only needs to be
- * null terminated if the end point is not provided.
- * This also determines the starting anchor.
- * \param captures The array of results to store the capture results.
- * The size of that array needs to be the same as the
- * size given in nrex::capture_size(). As it matches
- * the function fills the array with the results. 0 is
- * the result for the entire pattern, 1 and above
- * corresponds to the regex capture group if present.
- * \param offset The starting point of the search. This does not move
- * the starting anchor. Defaults to 0.
- * \param end The end point of the search. This also determines
- * the ending anchor. If a number less than the offset
- * is provided, the search would be done until null
- * termination. Defaults to -1.
- * \return True if a match was found. False otherwise.
- */
- bool match(const nrex_char* str, nrex_result* captures, int offset = 0, int end = -1) const;
-};
-
-#ifdef NREX_THROW_ERROR
-
-#include <stdexcept>
-
-class nrex_compile_error : std::runtime_error
-{
- public:
- nrex_compile_error(const char* message)
- : std::runtime_error(message)
- {
- }
-
- ~nrex_compile_error() throw()
- {
- }
-};
-
-#endif
-
-#endif // NREX_HPP
diff --git a/drivers/nrex/nrex_config.h b/drivers/nrex/nrex_config.h
deleted file mode 100644
index 540f34f8b4..0000000000
--- a/drivers/nrex/nrex_config.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Godot-specific configuration
-// To use this, replace nrex_config.h
-
-#include "core/os/memory.h"
-
-#define NREX_UNICODE
-//#define NREX_THROW_ERROR
-
-#define NREX_NEW(X) memnew(X)
-#define NREX_NEW_ARRAY(X, N) memnew_arr(X, N)
-#define NREX_DELETE(X) memdelete(X)
-#define NREX_DELETE_ARRAY(X) memdelete_arr(X)
diff --git a/drivers/nrex/regex.cpp b/drivers/nrex/regex.cpp
deleted file mode 100644
index 7bf14d14ad..0000000000
--- a/drivers/nrex/regex.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/*************************************************************************/
-/* regex.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* http://www.godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2016 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 "regex.h"
-#include "nrex.hpp"
-#include "core/os/memory.h"
-
-void RegEx::_bind_methods() {
-
- ObjectTypeDB::bind_method(_MD("compile","pattern", "capture"),&RegEx::compile, DEFVAL(9));
- ObjectTypeDB::bind_method(_MD("find","text","start","end"),&RegEx::find, DEFVAL(0), DEFVAL(-1));
- ObjectTypeDB::bind_method(_MD("clear"),&RegEx::clear);
- ObjectTypeDB::bind_method(_MD("is_valid"),&RegEx::is_valid);
- ObjectTypeDB::bind_method(_MD("get_capture_count"),&RegEx::get_capture_count);
- ObjectTypeDB::bind_method(_MD("get_capture","capture"),&RegEx::get_capture);
- ObjectTypeDB::bind_method(_MD("get_capture_start","capture"),&RegEx::get_capture_start);
- ObjectTypeDB::bind_method(_MD("get_captures"),&RegEx::_bind_get_captures);
-
-};
-
-StringArray RegEx::_bind_get_captures() const {
-
- StringArray ret;
- int count = get_capture_count();
- for (int i=0; i<count; i++) {
-
- String c = get_capture(i);
- ret.push_back(c);
- };
-
- return ret;
-
-};
-
-void RegEx::clear() {
-
- text.clear();
- captures.clear();
- exp.reset();
-
-};
-
-bool RegEx::is_valid() const {
-
- return exp.valid();
-
-};
-
-int RegEx::get_capture_count() const {
-
- ERR_FAIL_COND_V( !exp.valid(), 0 );
-
- return exp.capture_size();
-}
-
-String RegEx::get_capture(int capture) const {
-
- ERR_FAIL_COND_V( get_capture_count() <= capture, String() );
-
- return text.substr(captures[capture].start, captures[capture].length);
-
-}
-
-int RegEx::get_capture_start(int capture) const {
-
- ERR_FAIL_COND_V( get_capture_count() <= capture, -1 );
-
- return captures[capture].start;
-
-}
-
-Error RegEx::compile(const String& p_pattern, int capture) {
-
- clear();
-
- exp.compile(p_pattern.c_str(), capture);
-
- ERR_FAIL_COND_V( !exp.valid(), FAILED );
-
- captures.resize(exp.capture_size());
-
- return OK;
-
-};
-
-int RegEx::find(const String& p_text, int p_start, int p_end) const {
-
- ERR_FAIL_COND_V( !exp.valid(), -1 );
- ERR_FAIL_COND_V( p_text.length() < p_start, -1 );
- ERR_FAIL_COND_V( p_text.length() < p_end, -1 );
-
- bool res = exp.match(p_text.c_str(), &captures[0], p_start, p_end);
-
- if (res) {
- text = p_text;
- return captures[0].start;
- }
- text.clear();
- return -1;
-
-};
-
-RegEx::RegEx(const String& p_pattern) {
-
- compile(p_pattern);
-
-};
-
-RegEx::RegEx() {
-
-};
-
-RegEx::~RegEx() {
-
- clear();
-
-};
diff --git a/drivers/png/SCsub b/drivers/png/SCsub
index 1e3f54a9ca..04cb70e1c1 100644
--- a/drivers/png/SCsub
+++ b/drivers/png/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env_png = env.Clone()
diff --git a/drivers/pulseaudio/SCsub b/drivers/pulseaudio/SCsub
index 0582e01978..ee39fd2631 100644
--- a/drivers/pulseaudio/SCsub
+++ b/drivers/pulseaudio/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.drivers_sources, "*.cpp")
diff --git a/drivers/register_driver_types.cpp b/drivers/register_driver_types.cpp
index 7486d4f217..e0f35e81c4 100644
--- a/drivers/register_driver_types.cpp
+++ b/drivers/register_driver_types.cpp
@@ -40,8 +40,6 @@
#include "platform/windows/export/export.h"
#endif
-#include "drivers/nrex/regex.h"
-
static ImageLoaderPNG *image_loader_png=NULL;
static ResourceSaverPNG *resource_saver_png=NULL;
@@ -53,8 +51,6 @@ void register_core_driver_types() {
resource_saver_png = memnew( ResourceSaverPNG );
ResourceSaver::add_resource_format_saver(resource_saver_png);
-
- ObjectTypeDB::register_type<RegEx>();
}
void unregister_core_driver_types() {
diff --git a/drivers/rtaudio/SCsub b/drivers/rtaudio/SCsub
index 836c84c43c..f0273dd421 100644
--- a/drivers/rtaudio/SCsub
+++ b/drivers/rtaudio/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
# Not cloning the env, the includes need to be accessible for platform/
diff --git a/drivers/unix/SCsub b/drivers/unix/SCsub
index 36a172025d..fe427e747f 100644
--- a/drivers/unix/SCsub
+++ b/drivers/unix/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
g_set_p='#ifdef UNIX_ENABLED\n'
diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp
index 08a8c19583..d6d1be3395 100644
--- a/drivers/unix/ip_unix.cpp
+++ b/drivers/unix/ip_unix.cpp
@@ -30,6 +30,7 @@
#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
+#include <string.h>
#ifdef WINDOWS_ENABLED
#ifdef WINRT_ENABLED
@@ -62,16 +63,51 @@
#endif
#endif
-IP_Address IP_Unix::_resolve_hostname(const String& p_hostname) {
+static IP_Address _sockaddr2ip(struct sockaddr* p_addr) {
- struct hostent *he;
- if ((he=gethostbyname(p_hostname.utf8().get_data())) == NULL) { // get the host info
- ERR_PRINT("gethostbyname failed!");
- return IP_Address();
- }
IP_Address ip;
+ if (p_addr->sa_family == AF_INET) {
+ struct sockaddr_in* addr = (struct sockaddr_in*)p_addr;
+ ip.field32[0] = *((unsigned long*)&addr->sin_addr);
+ ip.type = IP_Address::TYPE_IPV4;
+ } else {
+ struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
+ for (int i=0; i<16; i++)
+ ip.field8[i] = addr6->sin6_addr.s6_addr[i];
+ ip.type = IP_Address::TYPE_IPV6;
+ };
+
+ return ip;
+};
+
+IP_Address IP_Unix::_resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type) {
- ip.host= *((unsigned long*)he->h_addr);
+ struct addrinfo hints;
+ struct addrinfo* result;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ if (p_type == IP_Address::TYPE_IPV4) {
+ hints.ai_family = AF_INET;
+ } else if (p_type == IP_Address::TYPE_IPV6) {
+ hints.ai_family = AF_INET6;
+ } else {
+ hints.ai_family = AF_UNSPEC;
+ };
+
+ int s = getaddrinfo(p_hostname.utf8().get_data(), NULL, &hints, &result);
+ if (s != 0) {
+ ERR_PRINT("getaddrinfo failed!");
+ return IP_Address();
+ };
+
+ if (result == NULL || result->ai_addr == NULL) {
+ ERR_PRINT("Invalid response from getaddrinfo");
+ return IP_Address();
+ };
+
+ IP_Address ip = _sockaddr2ip(result->ai_addr);
+
+ freeaddrinfo(result);
return ip;
@@ -90,10 +126,9 @@ void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
for (int i = 0; i < hostnames->Size; i++) {
- if (hostnames->GetAt(i)->Type == HostNameType::Ipv4 && hostnames->GetAt(i)->IPInformation != nullptr) {
+ if (hostnames->GetAt(i)->Type == HostNameType::Ipv4 || hostnames->GetAt(i)->Type == HostNameType::Ipv6 && hostnames->GetAt(i)->IPInformation != nullptr) {
r_addresses->push_back(IP_Address(String(hostnames->GetAt(i)->CanonicalName->Data())));
-
}
}
@@ -108,7 +143,7 @@ void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
while (true) {
addrs = (IP_ADAPTER_ADDRESSES*)memalloc(buf_size);
- int err = GetAdaptersAddresses(AF_INET, GAA_FLAG_SKIP_ANYCAST |
+ int err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST |
GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER |
GAA_FLAG_SKIP_FRIENDLY_NAME,
@@ -134,14 +169,23 @@ void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress;
while (address != NULL) {
- char addr_chr[INET_ADDRSTRLEN];
- SOCKADDR_IN* ipv4 = reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);
-
IP_Address ip;
- ip.host= *((unsigned long*)&ipv4->sin_addr);
+ if (address->Address.lpSockaddr->sa_family == AF_INET) {
+
+ SOCKADDR_IN* ipv4 = reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);
+
+ ip.field32[0] = *((unsigned long*)&ipv4->sin_addr);
+ ip.type = IP_Address::TYPE_IPV4;
+ } else { // ipv6
+
+ SOCKADDR_IN6* ipv6 = reinterpret_cast<SOCKADDR_IN6*>(address->Address.lpSockaddr);
+ for (int i=0; i<16; i++) {
+ ip.field8[i] = ipv6->sin6_addr.s6_addr[i];
+ };
+ ip.type = IP_Address::TYPE_IPV6;
+ };
- //inet_ntop(AF_INET, &ipv4->sin_addr, addr_chr, INET_ADDRSTRLEN);
r_addresses->push_back(ip);
@@ -167,20 +211,9 @@ void IP_Unix::get_local_addresses(List<IP_Address> *r_addresses) const {
for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr)
continue;
- if (ifa ->ifa_addr->sa_family==AF_INET) { // check it is IP4
- // is a valid IP4 Address
- IP_Address ip;
- ip.host= *((unsigned long*)&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr);
-
- r_addresses->push_back(ip);
- }/* else if (ifa->ifa_addr->sa_family==AF_INET6) { // check it is IP6
- // is a valid IP6 Address
- tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
- char addressBuffer[INET6_ADDRSTRLEN];
- inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
- printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
- } */
+ IP_Address ip = _sockaddr2ip(ifa->ifa_addr);
+ r_addresses->push_back(ip);
}
if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);
diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h
index c0034c9a3a..d198a330e7 100644
--- a/drivers/unix/ip_unix.h
+++ b/drivers/unix/ip_unix.h
@@ -36,7 +36,7 @@
class IP_Unix : public IP {
OBJ_TYPE(IP_Unix, IP);
- virtual IP_Address _resolve_hostname(const String& p_hostname);
+ virtual IP_Address _resolve_hostname(const String& p_hostname, IP_Address::AddrType p_type);
static IP* _create_unix();
public:
diff --git a/drivers/unix/packet_peer_udp_posix.cpp b/drivers/unix/packet_peer_udp_posix.cpp
index 0201a85651..0b172b6a51 100644
--- a/drivers/unix/packet_peer_udp_posix.cpp
+++ b/drivers/unix/packet_peer_udp_posix.cpp
@@ -54,6 +54,7 @@
#include <arpa/inet.h>
#endif
+#include "drivers/unix/socket_helpers.h"
int PacketPeerUDPPosix::get_available_packet_count() const {
@@ -73,7 +74,15 @@ Error PacketPeerUDPPosix::get_packet(const uint8_t **r_buffer,int &r_buffer_size
return ERR_UNAVAILABLE;
uint32_t size;
- rb.read((uint8_t*)&packet_ip.host,4,true);
+ uint8_t type;
+ rb.read(&type, 1, true);
+ if (type == IP_Address::TYPE_IPV4) {
+ rb.read((uint8_t*)&packet_ip.field8,4,true);
+ packet_ip.type = IP_Address::TYPE_IPV4;
+ } else {
+ rb.read((uint8_t*)&packet_ip.field8,16,true);
+ packet_ip.type = IP_Address::TYPE_IPV6;
+ };
rb.read((uint8_t*)&packet_port,4,true);
rb.read((uint8_t*)&size,4,true);
rb.read(packet_buffer,size,true);
@@ -85,17 +94,17 @@ Error PacketPeerUDPPosix::get_packet(const uint8_t **r_buffer,int &r_buffer_size
}
Error PacketPeerUDPPosix::put_packet(const uint8_t *p_buffer,int p_buffer_size){
- int sock = _get_socket();
+ ERR_FAIL_COND_V(peer_addr.type == IP_Address::TYPE_NONE, ERR_UNCONFIGURED);
+
+ int sock = _get_socket(peer_addr.type);
ERR_FAIL_COND_V( sock == -1, FAILED );
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(peer_port);
- addr.sin_addr = *((struct in_addr*)&peer_addr.host);
+ struct sockaddr_storage addr;
+ size_t addr_size = _set_sockaddr(&addr, peer_addr, peer_port);
errno = 0;
int err;
- while ( (err = sendto(sock, p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, sizeof(addr))) != p_buffer_size) {
+ while ( (err = sendto(sock, p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, addr_size)) != p_buffer_size) {
if (errno != EAGAIN) {
return FAILED;
@@ -110,17 +119,19 @@ int PacketPeerUDPPosix::get_max_packet_size() const{
return 512; // uhm maybe not
}
-Error PacketPeerUDPPosix::listen(int p_port, int p_recv_buffer_size){
+Error PacketPeerUDPPosix::listen(int p_port, IP_Address::AddrType p_address_type, int p_recv_buffer_size) {
+
+ ERR_FAIL_COND_V(p_address_type != IP_Address::TYPE_IPV4 && p_address_type != IP_Address::TYPE_IPV6, ERR_INVALID_PARAMETER);
close();
- int sock = _get_socket();
+ int sock = _get_socket(p_address_type);
if (sock == -1 )
return ERR_CANT_CREATE;
- sockaddr_in addr = {0};
- addr.sin_family = AF_INET;
- addr.sin_port = htons(p_port);
- addr.sin_addr.s_addr = INADDR_ANY;
- if (bind(sock, (struct sockaddr*)&addr, sizeof(sockaddr_in)) == -1 ) {
+
+ sockaddr_storage addr = {0};
+ size_t addr_size = _set_listen_sockaddr(&addr, p_port, p_address_type, NULL);
+
+ if (bind(sock, (struct sockaddr*)&addr, addr_size) == -1 ) {
close();
return ERR_UNAVAILABLE;
}
@@ -145,16 +156,41 @@ Error PacketPeerUDPPosix::wait() {
Error PacketPeerUDPPosix::_poll(bool p_wait) {
- struct sockaddr_in from = {0};
- socklen_t len = sizeof(struct sockaddr_in);
+ struct sockaddr_storage from = {0};
+ socklen_t len = sizeof(struct sockaddr_storage);
int ret;
while ( (ret = recvfrom(sockfd, recv_buffer, MIN((int)sizeof(recv_buffer),MAX(rb.space_left()-12, 0)), p_wait?0:MSG_DONTWAIT, (struct sockaddr*)&from, &len)) > 0) {
- rb.write((uint8_t*)&from.sin_addr, 4);
- uint32_t port = ntohs(from.sin_port);
+
+ uint32_t port = 0;
+
+ if (from.ss_family == AF_INET) {
+ uint8_t type = (uint8_t)IP_Address::TYPE_IPV4;
+ rb.write(&type, 1);
+ struct sockaddr_in* sin_from = (struct sockaddr_in*)&from;
+ rb.write((uint8_t*)&sin_from->sin_addr, 4);
+ port = ntohs(sin_from->sin_port);
+
+ } else if (from.ss_family == AF_INET6) {
+
+ uint8_t type = (uint8_t)IP_Address::TYPE_IPV6;
+ rb.write(&type, 1);
+
+ struct sockaddr_in6* s6_from = (struct sockaddr_in6*)&from;
+ rb.write((uint8_t*)&s6_from->sin6_addr, 16);
+
+ port = ntohs(s6_from->sin6_port);
+
+ } else {
+ // WARN_PRINT("Ignoring packet with unknown address family");
+ uint8_t type = (uint8_t)IP_Address::TYPE_NONE;
+ rb.write(&type, 1);
+ };
+
rb.write((uint8_t*)&port, 4);
rb.write((uint8_t*)&ret, 4);
rb.write(recv_buffer, ret);
- len = sizeof(struct sockaddr_in);
+
+ len = sizeof(struct sockaddr_storage);
++queue_count;
};
@@ -182,12 +218,14 @@ int PacketPeerUDPPosix::get_packet_port() const{
return packet_port;
}
-int PacketPeerUDPPosix::_get_socket() {
+int PacketPeerUDPPosix::_get_socket(IP_Address::AddrType p_type) {
if (sockfd != -1)
return sockfd;
- sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ int family = p_type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+
+ sockfd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
ERR_FAIL_COND_V( sockfd == -1, -1 );
//fcntl(sockfd, F_SETFL, O_NONBLOCK);
diff --git a/drivers/unix/packet_peer_udp_posix.h b/drivers/unix/packet_peer_udp_posix.h
index a11282e5d6..13b6969e53 100644
--- a/drivers/unix/packet_peer_udp_posix.h
+++ b/drivers/unix/packet_peer_udp_posix.h
@@ -44,15 +44,15 @@ class PacketPeerUDPPosix : public PacketPeerUDP {
mutable RingBuffer<uint8_t> rb;
uint8_t recv_buffer[PACKET_BUFFER_SIZE];
mutable uint8_t packet_buffer[PACKET_BUFFER_SIZE];
- IP_Address packet_ip;
- int packet_port;
+ mutable IP_Address packet_ip;
+ mutable int packet_port;
mutable int queue_count;
int sockfd;
IP_Address peer_addr;
int peer_port;
- _FORCE_INLINE_ int _get_socket();
+ _FORCE_INLINE_ int _get_socket(IP_Address::AddrType p_type);
static PacketPeerUDP* _create();
virtual Error _poll(bool p_block);
@@ -65,7 +65,7 @@ public:
virtual int get_max_packet_size() const;
- virtual Error listen(int p_port,int p_recv_buffer_size=65536);
+ virtual Error listen(int p_port, IP_Address::AddrType p_address_type, int p_recv_buffer_size=65536);
virtual void close();
virtual Error wait();
virtual bool is_listening() const;
diff --git a/drivers/unix/socket_helpers.h b/drivers/unix/socket_helpers.h
new file mode 100644
index 0000000000..4ee40e3d3d
--- /dev/null
+++ b/drivers/unix/socket_helpers.h
@@ -0,0 +1,70 @@
+#ifndef SOCKET_HELPERS_H
+#define SOCKET_HELPERS_H
+
+#include <string.h>
+
+// helpers for sockaddr -> IP_Address and back, should work for posix and winsock. All implementations should use this
+
+static size_t _set_sockaddr(struct sockaddr_storage* p_addr, const IP_Address& p_ip, int p_port) {
+
+ memset(p_addr, 0, sizeof(struct sockaddr_storage));
+ if (p_ip.type == IP_Address::TYPE_IPV6) {
+
+ struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_port = htons(p_port);
+ copymem(&addr6->sin6_addr.s6_addr, p_ip.field8, 16);
+ return sizeof(sockaddr_in6);
+
+ } else {
+
+ struct sockaddr_in* addr4 = (struct sockaddr_in*)p_addr;
+ addr4->sin_family = AF_INET; // host byte order
+ addr4->sin_port = htons(p_port); // short, network byte order
+ addr4->sin_addr = *((struct in_addr*)&p_ip.field32[0]);
+ return sizeof(sockaddr_in);
+ };
+};
+
+static size_t _set_listen_sockaddr(struct sockaddr_storage* p_addr, int p_port, IP_Address::AddrType p_address_type, const List<String> *p_accepted_hosts) {
+
+ memset(p_addr, 0, sizeof(struct sockaddr_storage));
+ if (p_address_type == IP_Address::TYPE_IPV4) {
+ struct sockaddr_in* addr4 = (struct sockaddr_in*)p_addr;
+ addr4->sin_family = AF_INET;
+ addr4->sin_port = htons(p_port);
+ addr4->sin_addr.s_addr = INADDR_ANY; // TODO: use accepted hosts list
+ return sizeof(sockaddr_in);
+ } else {
+ struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
+
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_port = htons(p_port);
+ addr6->sin6_addr = in6addr_any; // TODO: use accepted hosts list
+ return sizeof(sockaddr_in6);
+ };
+};
+
+static void _set_ip_addr_port(IP_Address& r_ip, int& r_port, struct sockaddr_storage* p_addr) {
+
+ if (p_addr->ss_family == AF_INET) {
+ r_ip.type = IP_Address::TYPE_IPV4;
+
+ struct sockaddr_in* addr4 = (struct sockaddr_in*)p_addr;
+ r_ip.field32[0] = (uint32_t)addr4->sin_addr.s_addr;
+
+ r_port = ntohs(addr4->sin_port);
+
+ } else if (p_addr->ss_family == AF_INET6) {
+
+ r_ip.type = IP_Address::TYPE_IPV6;
+
+ struct sockaddr_in6* addr6 = (struct sockaddr_in6*)p_addr;
+ copymem(&addr6->sin6_addr.s6_addr, r_ip.field8, 16);
+
+ r_port = ntohs(addr6->sin6_port);
+ };
+};
+
+
+#endif
diff --git a/drivers/unix/stream_peer_tcp_posix.cpp b/drivers/unix/stream_peer_tcp_posix.cpp
index 45a4b934c1..f2915b0a16 100644
--- a/drivers/unix/stream_peer_tcp_posix.cpp
+++ b/drivers/unix/stream_peer_tcp_posix.cpp
@@ -61,13 +61,7 @@
#define MSG_NOSIGNAL SO_NOSIGPIPE
#endif
-static void set_addr_in(struct sockaddr_in& their_addr, const IP_Address& p_host, uint16_t p_port) {
-
- their_addr.sin_family = AF_INET; // host byte order
- their_addr.sin_port = htons(p_port); // short, network byte order
- their_addr.sin_addr = *((struct in_addr*)&p_host.host);
- memset(&(their_addr.sin_zero), '\0', 8);
-};
+#include "drivers/unix/socket_helpers.h"
StreamPeerTCP* StreamPeerTCPPosix::_create() {
@@ -103,9 +97,10 @@ Error StreamPeerTCPPosix::_poll_connection(bool p_block) const {
_block(sockfd, false, true);
};
- struct sockaddr_in their_addr;
- set_addr_in(their_addr, peer_host, peer_port);
- if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == -1) {
+ struct sockaddr_storage their_addr;
+ size_t addr_size = _set_sockaddr(&their_addr, peer_host, peer_port);
+
+ if (::connect(sockfd, (struct sockaddr *)&their_addr,addr_size) == -1) {
if (errno == EISCONN) {
status = STATUS_CONNECTED;
@@ -140,9 +135,10 @@ void StreamPeerTCPPosix::set_socket(int p_sockfd, IP_Address p_host, int p_port)
Error StreamPeerTCPPosix::connect(const IP_Address& p_host, uint16_t p_port) {
- ERR_FAIL_COND_V( p_host.host == 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V( p_host.type == IP_Address::TYPE_NONE, ERR_INVALID_PARAMETER);
- if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ int family = p_host.type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+ if ((sockfd = socket(family, SOCK_STREAM, 0)) == -1) {
ERR_PRINT("Socket creation failed!");
disconnect();
//perror("socket");
@@ -156,11 +152,11 @@ Error StreamPeerTCPPosix::connect(const IP_Address& p_host, uint16_t p_port) {
ioctl(sockfd, FIONBIO, &bval);
#endif
- struct sockaddr_in their_addr;
- set_addr_in(their_addr, p_host, p_port);
+ struct sockaddr_storage their_addr;
+ size_t addr_size = _set_sockaddr(&their_addr, p_host, p_port);
errno = 0;
- if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == -1 && errno != EINPROGRESS) {
+ if (::connect(sockfd, (struct sockaddr *)&their_addr,addr_size) == -1 && errno != EINPROGRESS) {
ERR_PRINT("Connection to remote host failed!");
disconnect();
diff --git a/drivers/unix/tcp_server_posix.cpp b/drivers/unix/tcp_server_posix.cpp
index c67bb51334..05b739804c 100644
--- a/drivers/unix/tcp_server_posix.cpp
+++ b/drivers/unix/tcp_server_posix.cpp
@@ -55,6 +55,9 @@
#include <netinet/in.h>
#include <sys/socket.h>
#include <assert.h>
+
+#include "drivers/unix/socket_helpers.h"
+
TCP_Server* TCPServerPosix::_create() {
return memnew(TCPServerPosix);
@@ -65,10 +68,11 @@ void TCPServerPosix::make_default() {
TCP_Server::_create = TCPServerPosix::_create;
};
-Error TCPServerPosix::listen(uint16_t p_port,const List<String> *p_accepted_hosts) {
+Error TCPServerPosix::listen(uint16_t p_port, IP_Address::AddrType p_type, const List<String> *p_accepted_hosts) {
int sockfd;
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ int family = p_type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+ sockfd = socket(family, SOCK_STREAM, 0);
ERR_FAIL_COND_V(sockfd == -1, FAILED);
#ifndef NO_FCNTL
fcntl(sockfd, F_SETFL, O_NONBLOCK);
@@ -82,13 +86,12 @@ Error TCPServerPosix::listen(uint16_t p_port,const List<String> *p_accepted_host
WARN_PRINT("REUSEADDR failed!")
}
- struct sockaddr_in my_addr;
- my_addr.sin_family = AF_INET; // host byte order
- my_addr.sin_port = htons(p_port); // short, network byte order
- my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP TODO: use p_accepted_hosts
- memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
+ struct sockaddr_storage addr;
+ size_t addr_size = _set_listen_sockaddr(&addr, p_port, p_type, p_accepted_hosts);
+
+ // automatically fill with my IP TODO: use p_accepted_hosts
- if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) != -1) {
+ if (bind(sockfd, (struct sockaddr *)&addr, addr_size) != -1) {
if (::listen(sockfd, 1) == -1) {
@@ -136,9 +139,9 @@ Ref<StreamPeerTCP> TCPServerPosix::take_connection() {
return Ref<StreamPeerTCP>();
};
- struct sockaddr_in their_addr;
- socklen_t sin_size = sizeof(their_addr);
- int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &sin_size);
+ struct sockaddr_storage their_addr;
+ socklen_t size = sizeof(their_addr);
+ int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &size);
ERR_FAIL_COND_V(fd == -1, Ref<StreamPeerTCP>());
#ifndef NO_FCNTL
fcntl(fd, F_SETFL, O_NONBLOCK);
@@ -149,8 +152,11 @@ Ref<StreamPeerTCP> TCPServerPosix::take_connection() {
Ref<StreamPeerTCPPosix> conn = memnew(StreamPeerTCPPosix);
IP_Address ip;
- ip.host = (uint32_t)their_addr.sin_addr.s_addr;
- conn->set_socket(fd, ip, ntohs(their_addr.sin_port));
+
+ int port;
+ _set_ip_addr_port(ip, port, &their_addr);
+
+ conn->set_socket(fd, ip, port);
return conn;
};
diff --git a/drivers/unix/tcp_server_posix.h b/drivers/unix/tcp_server_posix.h
index 570bcaab12..134ec494cb 100644
--- a/drivers/unix/tcp_server_posix.h
+++ b/drivers/unix/tcp_server_posix.h
@@ -40,7 +40,7 @@ class TCPServerPosix : public TCP_Server {
public:
- virtual Error listen(uint16_t p_port,const List<String> *p_accepted_hosts=NULL);
+ virtual Error listen(uint16_t p_port, IP_Address::AddrType p_type = IP_Address::TYPE_IPV4, const List<String> *p_accepted_hosts=NULL);
virtual bool is_connection_available() const;
virtual Ref<StreamPeerTCP> take_connection();
diff --git a/drivers/windows/SCsub b/drivers/windows/SCsub
index 0582e01978..ee39fd2631 100644
--- a/drivers/windows/SCsub
+++ b/drivers/windows/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.drivers_sources, "*.cpp")
diff --git a/drivers/xaudio2/SCsub b/drivers/xaudio2/SCsub
new file mode 100644
index 0000000000..cb780a893b
--- /dev/null
+++ b/drivers/xaudio2/SCsub
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+Import('env')
+
+env.add_source_files(env.drivers_sources, "*.cpp")
+env.Append(CXXFLAGS=['-DXAUDIO2_ENABLED'])
+env.Append(LINKFLAGS=['xaudio2_8.lib'])
+
+Export('env')
diff --git a/platform/winrt/audio_driver_winrt.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp
index ff46244ac3..c7a8962102 100644
--- a/platform/winrt/audio_driver_winrt.cpp
+++ b/drivers/xaudio2/audio_driver_xaudio2.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* audio_driver_winrt.cpp */
+/* audio_driver_xaudio2.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -26,23 +26,17 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "audio_driver_winrt.h"
+#include "audio_driver_xaudio2.h"
#include "globals.h"
#include "os/os.h"
-using namespace Windows::Media;
-using namespace Windows::Media::Core;
-using namespace Windows::Media::MediaProperties;
-using namespace Windows::Media::Editing;
-using namespace Windows::Foundation;
-
-const char * AudioDriverWinRT::get_name() const
+const char * AudioDriverXAudio2::get_name() const
{
- return "WinRT";
+ return "XAudio2";
}
-Error AudioDriverWinRT::init() {
+Error AudioDriverXAudio2::init() {
active = false;
thread_exited = false;
@@ -86,23 +80,21 @@ Error AudioDriverWinRT::init() {
wave_format.nBlockAlign = channels * wave_format.wBitsPerSample >> 3;
wave_format.nAvgBytesPerSec = mix_rate * wave_format.nBlockAlign;
- voice_callback = memnew(XAudio2DriverVoiceCallback);
-
- hr = xaudio->CreateSourceVoice(&source_voice, &wave_format, 0, XAUDIO2_MAX_FREQ_RATIO, voice_callback);
+ hr = xaudio->CreateSourceVoice(&source_voice, &wave_format, 0, XAUDIO2_MAX_FREQ_RATIO, &voice_callback);
if (hr != S_OK) {
ERR_EXPLAIN("Error creating XAudio2 source voice. " + itos(hr));
ERR_FAIL_V(ERR_UNAVAILABLE);
}
mutex = Mutex::create();
- thread = Thread::create(AudioDriverWinRT::thread_func, this);
+ thread = Thread::create(AudioDriverXAudio2::thread_func, this);
return OK;
};
-void AudioDriverWinRT::thread_func(void* p_udata) {
+void AudioDriverXAudio2::thread_func(void* p_udata) {
- AudioDriverWinRT* ad = (AudioDriverWinRT*)p_udata;
+ AudioDriverXAudio2* ad = (AudioDriverXAudio2*)p_udata;
uint64_t usdelay = (ad->buffer_size / float(ad->mix_rate)) * 1000000;
@@ -139,7 +131,7 @@ void AudioDriverWinRT::thread_func(void* p_udata) {
XAUDIO2_VOICE_STATE state;
while (ad->source_voice->GetState(&state), state.BuffersQueued > AUDIO_BUFFERS - 1)
{
- WaitForSingleObject(ad->voice_callback->buffer_end_event, INFINITE);
+ WaitForSingleObject(ad->voice_callback.buffer_end_event, INFINITE);
}
}
@@ -149,7 +141,7 @@ void AudioDriverWinRT::thread_func(void* p_udata) {
};
-void AudioDriverWinRT::start() {
+void AudioDriverXAudio2::start() {
active = true;
HRESULT hr = source_voice->Start(0);
@@ -159,17 +151,17 @@ void AudioDriverWinRT::start() {
}
};
-int AudioDriverWinRT::get_mix_rate() const {
+int AudioDriverXAudio2::get_mix_rate() const {
return mix_rate;
};
-AudioDriverSW::OutputFormat AudioDriverWinRT::get_output_format() const {
+AudioDriverSW::OutputFormat AudioDriverXAudio2::get_output_format() const {
return output_format;
};
-float AudioDriverWinRT::get_latency() {
+float AudioDriverXAudio2::get_latency() {
XAUDIO2_PERFORMANCE_DATA perf_data;
xaudio->GetPerformanceData(&perf_data);
@@ -180,20 +172,20 @@ float AudioDriverWinRT::get_latency() {
}
}
-void AudioDriverWinRT::lock() {
+void AudioDriverXAudio2::lock() {
if (!thread || !mutex)
return;
mutex->lock();
};
-void AudioDriverWinRT::unlock() {
+void AudioDriverXAudio2::unlock() {
if (!thread || !mutex)
return;
mutex->unlock();
};
-void AudioDriverWinRT::finish() {
+void AudioDriverXAudio2::finish() {
if (!thread)
return;
@@ -203,7 +195,7 @@ void AudioDriverWinRT::finish() {
if (source_voice) {
source_voice->Stop(0);
- memdelete(source_voice);
+ source_voice->DestroyVoice();
}
if (samples_in) {
@@ -215,8 +207,7 @@ void AudioDriverWinRT::finish() {
}
};
- memdelete(voice_callback);
- memdelete(mastering_voice);
+ mastering_voice->DestroyVoice();
memdelete(thread);
if (mutex)
@@ -224,7 +215,7 @@ void AudioDriverWinRT::finish() {
thread = NULL;
};
-AudioDriverWinRT::AudioDriverWinRT() {
+AudioDriverXAudio2::AudioDriverXAudio2() {
mutex = NULL;
thread = NULL;
@@ -236,7 +227,7 @@ AudioDriverWinRT::AudioDriverWinRT() {
current_buffer = 0;
};
-AudioDriverWinRT::~AudioDriverWinRT() {
+AudioDriverXAudio2::~AudioDriverXAudio2() {
};
diff --git a/platform/winrt/audio_driver_winrt.h b/drivers/xaudio2/audio_driver_xaudio2.h
index d7a69994f8..1c6a90500d 100644
--- a/platform/winrt/audio_driver_winrt.h
+++ b/drivers/xaudio2/audio_driver_xaudio2.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* audio_driver_winrt.h */
+/* audio_driver_xaudio2.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -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. */
/*************************************************************************/
-#ifndef AUDIO_DRIVER_WINRT_H
-#define AUDIO_DRIVER_WINRT_H
+#ifndef AUDIO_DRIVER_XAUDIO2_H
+#define AUDIO_DRIVER_XAUDIO2_H
#include "servers/audio/audio_server_sw.h"
@@ -40,7 +40,7 @@
#include <xaudio2.h>
#include <wrl/client.h>
-class AudioDriverWinRT : public AudioDriverSW {
+class AudioDriverXAudio2 : public AudioDriverSW {
enum {
AUDIO_BUFFERS = 2
@@ -53,12 +53,12 @@ class AudioDriverWinRT : public AudioDriverSW {
void STDMETHODCALLTYPE OnBufferEnd(void* pBufferContext) { /*print_line("buffer ended");*/ SetEvent(buffer_end_event); }
//Unused methods are stubs
- void STDMETHODCALLTYPE OnStreamEnd() { }
- void STDMETHODCALLTYPE OnVoiceProcessingPassEnd() { }
- void STDMETHODCALLTYPE OnVoiceProcessingPassStart(UINT32 SamplesRequired) { }
- void STDMETHODCALLTYPE OnBufferStart(void * pBufferContext) { }
- void STDMETHODCALLTYPE OnLoopEnd(void * pBufferContext) { }
- void STDMETHODCALLTYPE OnVoiceError(void * pBufferContext, HRESULT Error) { }
+ void STDMETHODCALLTYPE OnStreamEnd() {}
+ void STDMETHODCALLTYPE OnVoiceProcessingPassEnd() {}
+ void STDMETHODCALLTYPE OnVoiceProcessingPassStart(UINT32 SamplesRequired) {}
+ void STDMETHODCALLTYPE OnBufferStart(void * pBufferContext) {}
+ void STDMETHODCALLTYPE OnLoopEnd(void * pBufferContext) {}
+ void STDMETHODCALLTYPE OnVoiceError(void * pBufferContext, HRESULT Error) {}
};
@@ -87,7 +87,7 @@ class AudioDriverWinRT : public AudioDriverSW {
IXAudio2MasteringVoice* mastering_voice;
XAUDIO2_BUFFER xaudio_buffer[AUDIO_BUFFERS];
IXAudio2SourceVoice* source_voice;
- XAudio2DriverVoiceCallback* voice_callback;
+ XAudio2DriverVoiceCallback voice_callback;
public:
@@ -102,8 +102,8 @@ public:
virtual void unlock();
virtual void finish();
- AudioDriverWinRT();
- ~AudioDriverWinRT();
+ AudioDriverXAudio2();
+ ~AudioDriverXAudio2();
};
#endif
diff --git a/drivers/zlib/SCsub b/drivers/zlib/SCsub
index d0bbcd452b..6a099aff52 100644
--- a/drivers/zlib/SCsub
+++ b/drivers/zlib/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
# Not cloning the env, the includes need to be accessible for core/
diff --git a/main/SCsub b/main/SCsub
index fa60ffc3e8..cd9002de0a 100644
--- a/main/SCsub
+++ b/main/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.main_sources=[]
diff --git a/methods.py b/methods.py
index c4951c69bd..efe2400c1c 100755
--- a/methods.py
+++ b/methods.py
@@ -1352,7 +1352,15 @@ def use_windows_spawn_fix(self, platform=None):
if (os.name!="nt"):
return #not needed, only for windows
- self.split_drivers=True
+ # On Windows, due to the limited command line length, when creating a static library
+ # from a very high number of objects SCons will invoke "ar" once per object file;
+ # that makes object files with same names to be overwritten so the last wins and
+ # the library looses symbols defined by overwritten objects.
+ # By enabling quick append instead of the default mode (replacing), libraries will
+ # got built correctly regardless the invokation strategy.
+ # Furthermore, since SCons will rebuild the library from scratch when an object file
+ # changes, no multiple versions of the same object file will be present.
+ self.Replace(ARFLAGS='q')
import subprocess
@@ -1394,6 +1402,50 @@ def use_windows_spawn_fix(self, platform=None):
self['SPAWN'] = mySpawn
+def split_lib(self, libname):
+ import string
+ env = self
+
+ num = 0
+ cur_base = ""
+ max_src = 64
+ list = []
+ lib_list = []
+
+ for f in getattr(env, libname + "_sources"):
+ fname = ""
+ if type(f) == type(""):
+ fname = env.File(f).path
+ else:
+ fname = env.File(f)[0].path
+ fname = fname.replace("\\", "/")
+ base = string.join(fname.split("/")[:2], "/")
+ if base != cur_base and len(list) > max_src:
+ if num > 0:
+ lib = env.Library(libname + str(num), list)
+ lib_list.append(lib)
+ list = []
+ num = num + 1
+ cur_base = base
+ list.append(f)
+
+ lib = env.Library(libname + str(num), list)
+ lib_list.append(lib)
+
+ if len(lib_list) > 0:
+ import os, sys
+ if os.name == 'posix' and sys.platform == 'msys':
+ env.Replace(ARFLAGS = ['rcsT'])
+ lib = env.Library(libname + "_collated", lib_list)
+ lib_list = [lib]
+
+ lib_base = []
+ env.add_source_files(lib_base, "*.cpp")
+ lib_list.insert(0, env.Library(libname, lib_base))
+
+ env.Prepend(LIBS = lib_list)
+
+
def save_active_platforms(apnames,ap):
for x in ap:
@@ -1416,7 +1468,7 @@ def save_active_platforms(apnames,ap):
logow.write(str)
-def colored(sys,env):
+def no_verbose(sys,env):
#If the output is not a terminal, do nothing
if not sys.stdout.isatty():
@@ -1516,11 +1568,6 @@ def detect_visual_c_compiler_version(tools_env):
return vc_chosen_compiler_str
-def msvc_is_detected() :
- # looks for VisualStudio env variable
- # or for Visual C++ Build Tools (which is a standalone MSVC)
- return os.getenv("VSINSTALLDIR") or os.getenv("VS100COMNTOOLS") or os.getenv("VS110COMNTOOLS") or os.getenv("VS120COMNTOOLS") or os.getenv("VS140COMNTOOLS");
-
def precious_program(env, program, sources, **args):
program = env.ProgramOriginal(program, sources, **args)
diff --git a/modules/SCsub b/modules/SCsub
index f37c3a55c7..4084248086 100644
--- a/modules/SCsub
+++ b/modules/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env_modules = env.Clone()
diff --git a/modules/chibi/SCsub b/modules/chibi/SCsub
index e39554977a..dffd966753 100644
--- a/modules/chibi/SCsub
+++ b/modules/chibi/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/modules/cscript/SCsub b/modules/cscript/SCsub
index 9ff13fc43f..0882406761 100644
--- a/modules/cscript/SCsub
+++ b/modules/cscript/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/dds/SCsub b/modules/dds/SCsub
index c54a58e079..3d92ff02d6 100644
--- a/modules/dds/SCsub
+++ b/modules/dds/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/modules/enet/SCsub b/modules/enet/SCsub
index 66c60baabe..5175803f44 100644
--- a/modules/enet/SCsub
+++ b/modules/enet/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index b4dfa9c62e..a82283591d 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -77,6 +77,7 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port, int p_in_bandwidth, int p_out_bandwidth){
ERR_FAIL_COND_V(active,ERR_ALREADY_IN_USE);
+ ERR_FAIL_COND_V(p_ip.type != IP_Address::TYPE_IPV4, ERR_INVALID_PARAMETER);
host = enet_host_create (NULL /* create a client host */,
1 /* only allow 1 outgoing connection */,
@@ -90,7 +91,7 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port
_setup_compressor();
ENetAddress address;
- address.host=p_ip.host;
+ address.host=p_ip.field32[0];
address.port=p_port;
//enet_address_set_host (& address, "localhost");
@@ -149,7 +150,8 @@ void NetworkedMultiplayerENet::poll(){
}
IP_Address ip;
- ip.host=event.peer -> address.host;
+ ip.type = IP_Address::TYPE_IPV4;
+ ip.field32[0]=event.peer -> address.host;
int *new_id = memnew( int );
*new_id = event.data;
@@ -683,5 +685,6 @@ NetworkedMultiplayerENet::~NetworkedMultiplayerENet(){
// sets IP for ENet to bind when using create_server
// if no IP is set, then ENet bind to ENET_HOST_ANY
void NetworkedMultiplayerENet::set_bind_ip(const IP_Address& p_ip){
- bind_ip=p_ip.host;
+ ERR_FAIL_COND(p_ip.type != IP_Address::TYPE_IPV4);
+ bind_ip=p_ip.field32[0];
}
diff --git a/modules/etc1/SCsub b/modules/etc1/SCsub
index ad343ab579..ea035fcde3 100644
--- a/modules/etc1/SCsub
+++ b/modules/etc1/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub
index 10c58558a5..1f759dee9a 100644
--- a/modules/freetype/SCsub
+++ b/modules/freetype/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
# Not building in a separate env as core needs it
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub
index 9ff13fc43f..0882406761 100644
--- a/modules/gdscript/SCsub
+++ b/modules/gdscript/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp
index 2e2cbe7b29..b75b13551e 100644
--- a/modules/gdscript/gd_compiler.cpp
+++ b/modules/gdscript/gd_compiler.cpp
@@ -1005,12 +1005,12 @@ Error GDCompiler::_parse_block(CodeGen& codegen,const GDParser::BlockNode *p_blo
switch(s->type) {
case GDParser::Node::TYPE_NEWLINE: {
-
+#ifdef DEBUG_ENABLED
const GDParser::NewLineNode *nl = static_cast<const GDParser::NewLineNode*>(s);
codegen.opcodes.push_back(GDFunction::OPCODE_LINE);
codegen.opcodes.push_back(nl->line);
codegen.current_line=nl->line;
-
+#endif
} break;
case GDParser::Node::TYPE_CONTROL_FLOW: {
// try subblocks
@@ -1201,8 +1201,10 @@ Error GDCompiler::_parse_block(CodeGen& codegen,const GDParser::BlockNode *p_blo
codegen.opcodes.push_back(ret);
} break;
case GDParser::Node::TYPE_BREAKPOINT: {
+#ifdef DEBUG_ENABLED
// try subblocks
codegen.opcodes.push_back(GDFunction::OPCODE_BREAKPOINT);
+#endif
} break;
case GDParser::Node::TYPE_LOCAL_VAR: {
diff --git a/modules/gridmap/SCsub b/modules/gridmap/SCsub
index 9ff13fc43f..0882406761 100644
--- a/modules/gridmap/SCsub
+++ b/modules/gridmap/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/ik/SCsub b/modules/ik/SCsub
index 9ff13fc43f..0882406761 100644
--- a/modules/ik/SCsub
+++ b/modules/ik/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub
index 258fd2f4ad..28fb81895d 100644
--- a/modules/jpg/SCsub
+++ b/modules/jpg/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/modules/mpc/SCsub b/modules/mpc/SCsub
index d2662c34ab..09f0c05daa 100644
--- a/modules/mpc/SCsub
+++ b/modules/mpc/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub
index fd5ddf55c8..2e1fe2e0c0 100644
--- a/modules/ogg/SCsub
+++ b/modules/ogg/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/modules/openssl/SCsub b/modules/openssl/SCsub
index 79facba99a..2327cf483c 100644
--- a/modules/openssl/SCsub
+++ b/modules/openssl/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
@@ -669,7 +671,7 @@ if (env["openssl"] != "system"): # builtin
# Workaround for compilation error with GCC/Clang when -Werror is too greedy (GH-4517)
import os
import methods
- if not (os.name=="nt" and methods.msvc_is_detected()): # not Windows and not MSVC
+ if not (os.name=="nt" and os.getenv("VCINSTALLDIR")): # not Windows and not MSVC
env_openssl.Append(CFLAGS = ["-Wno-error=implicit-function-declaration"])
diff --git a/modules/opus/SCsub b/modules/opus/SCsub
index 5c587a0783..603f5a48c4 100644
--- a/modules/opus/SCsub
+++ b/modules/opus/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/modules/pbm/SCsub b/modules/pbm/SCsub
index e0b19fb2a3..fa328be025 100644
--- a/modules/pbm/SCsub
+++ b/modules/pbm/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/modules/pvr/SCsub b/modules/pvr/SCsub
index cd0a3129da..4ead52f82f 100644
--- a/modules/pvr/SCsub
+++ b/modules/pvr/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/modules/regex/SCsub b/modules/regex/SCsub
new file mode 100644
index 0000000000..0882406761
--- /dev/null
+++ b/modules/regex/SCsub
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+Import('env')
+
+env.add_source_files(env.modules_sources, "*.cpp")
+
+Export('env')
diff --git a/modules/regex/config.py b/modules/regex/config.py
new file mode 100644
index 0000000000..667b5d8ba6
--- /dev/null
+++ b/modules/regex/config.py
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
+
diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp
new file mode 100644
index 0000000000..a0f4b4934c
--- /dev/null
+++ b/modules/regex/regex.cpp
@@ -0,0 +1,1502 @@
+/*************************************************************************/
+/* regex.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 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 "regex.h"
+#include <wctype.h>
+#include <wchar.h>
+
+static int RegEx_hex2int(const CharType c)
+{
+ if ('0' <= c && c <= '9')
+ return int(c - '0');
+ else if ('a' <= c && c <= 'f')
+ return int(c - 'a') + 10;
+ else if ('A' <= c && c <= 'F')
+ return int(c - 'A') + 10;
+ return -1;
+}
+
+struct RegExSearch {
+
+ Ref<RegExMatch> match;
+ const CharType* str;
+ int end;
+ int eof;
+
+ // For standard quantifier behaviour, test_parent is used to check the
+ // rest of the pattern. If the pattern matches, to prevent the parent
+ // from testing again, the complete flag is used as a shortcut out.
+ bool complete;
+
+ // With lookahead, the position needs to rewind to its starting position
+ // when test_parent is used. Due to functional programming, this state
+ // has to be kept as a parameter.
+ Vector<int> lookahead_pos;
+
+ CharType at(int p_pos) {
+ return str[p_pos];
+ }
+
+ RegExSearch(Ref<RegExMatch>& p_match, int p_end, int p_lookahead) : match(p_match) {
+
+ str = p_match->string.c_str();
+ end = p_end;
+ eof = p_match->string.length();
+ complete = false;
+ lookahead_pos.resize(p_lookahead);
+ }
+
+};
+
+struct RegExNode {
+
+ RegExNode* next;
+ RegExNode* previous;
+ RegExNode* parent;
+ bool quantifiable;
+ int length;
+
+ RegExNode() {
+
+ next = NULL;
+ previous = NULL;
+ parent = NULL;
+ quantifiable = false;
+ length = -1;
+ }
+
+ virtual ~RegExNode() {
+
+ if (next)
+ memdelete(next);
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ return next ? next->test(s, pos) : -1;
+ }
+
+ virtual int test_parent(RegExSearch& s, int pos) const {
+
+ if (next)
+ pos = next->test(s, pos);
+
+ if (pos >= 0) {
+ s.complete = true;
+ if (parent)
+ pos = parent->test_parent(s, pos);
+ }
+
+ if (pos < 0)
+ s.complete = false;
+
+ return pos;
+ }
+
+ void increment_length(int amount, bool subtract = false) {
+
+ if (amount >= 0 && length >= 0) {
+ if (!subtract)
+ length += amount;
+ else
+ length -= amount;
+ } else {
+ length = -1;
+ }
+
+ if (parent)
+ parent->increment_length(amount, subtract);
+
+ }
+
+};
+
+struct RegExNodeChar : public RegExNode {
+
+ CharType ch;
+
+ RegExNodeChar(CharType p_char) {
+
+ length = 1;
+ quantifiable = true;
+ ch = p_char;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (s.end <= pos || 0 > pos || s.at(pos) != ch)
+ return -1;
+
+ return next ? next->test(s, pos + 1) : pos + 1;
+ }
+
+ static CharType parse_escape(const CharType*& c) {
+
+ int point = 0;
+ switch (c[1]) {
+ case 'x':
+ for (int i = 2; i <= 3; ++i) {
+ int res = RegEx_hex2int(c[i]);
+ if (res == -1)
+ return '\0';
+ point = (point << 4) + res;
+ }
+ c = &c[3];
+ return CharType(point);
+ case 'u':
+ for (int i = 2; i <= 5; ++i) {
+ int res = RegEx_hex2int(c[i]);
+ if (res == -1)
+ return '\0';
+ point = (point << 4) + res;
+ }
+ c = &c[5];
+ return CharType(point);
+ case '0': ++c; return '\0';
+ case 'a': ++c; return '\a';
+ case 'e': ++c; return '\e';
+ case 'f': ++c; return '\f';
+ case 'n': ++c; return '\n';
+ case 'r': ++c; return '\r';
+ case 't': ++c; return '\t';
+ case 'v': ++c; return '\v';
+ case 'b': ++c; return '\b';
+ default: break;
+ }
+ return (++c)[0];
+ }
+};
+
+struct RegExNodeRange : public RegExNode {
+
+ CharType start;
+ CharType end;
+
+ RegExNodeRange(CharType p_start, CharType p_end) {
+
+ length = 1;
+ quantifiable = true;
+ start = p_start;
+ end = p_end;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (s.end <= pos || 0 > pos)
+ return -1;
+
+ CharType c = s.at(pos);
+ if (c < start || end < c)
+ return -1;
+
+ return next ? next->test(s, pos + 1) : pos + 1;
+ }
+};
+
+struct RegExNodeShorthand : public RegExNode {
+
+ CharType repr;
+
+ RegExNodeShorthand(CharType p_repr) {
+
+ length = 1;
+ quantifiable = true;
+ repr = p_repr;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (s.end <= pos || 0 > pos)
+ return -1;
+
+ bool found = false;
+ bool invert = false;
+ CharType c = s.at(pos);
+ switch (repr) {
+ case '.':
+ found = true;
+ break;
+ case 'W':
+ invert = true;
+ case 'w':
+ found = (c == '_' || iswalnum(c) != 0);
+ break;
+ case 'D':
+ invert = true;
+ case 'd':
+ found = ('0' <= c && c <= '9');
+ break;
+ case 'S':
+ invert = true;
+ case 's':
+ found = (iswspace(c) != 0);
+ break;
+ default:
+ break;
+ }
+
+ if (found == invert)
+ return -1;
+
+ return next ? next->test(s, pos + 1) : pos + 1;
+ }
+};
+
+struct RegExNodeClass : public RegExNode {
+
+ enum Type {
+ Type_none,
+ Type_alnum,
+ Type_alpha,
+ Type_ascii,
+ Type_blank,
+ Type_cntrl,
+ Type_digit,
+ Type_graph,
+ Type_lower,
+ Type_print,
+ Type_punct,
+ Type_space,
+ Type_upper,
+ Type_xdigit,
+ Type_word
+ };
+
+ Type type;
+
+ bool test_class(CharType c) const {
+
+ static Vector<CharType> REGEX_NODE_SPACE = String(" \t\r\n\f");
+ static Vector<CharType> REGEX_NODE_PUNCT = String("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~");
+
+ switch (type) {
+ case Type_alnum:
+ if ('0' <= c && c <= '9') return true;
+ if ('a' <= c && c <= 'z') return true;
+ if ('A' <= c && c <= 'Z') return true;
+ return false;
+ case Type_alpha:
+ if ('a' <= c && c <= 'z') return true;
+ if ('A' <= c && c <= 'Z') return true;
+ return false;
+ case Type_ascii:
+ return (0x00 <= c && c <= 0x7F);
+ case Type_blank:
+ return (c == ' ' || c == '\t');
+ case Type_cntrl:
+ return ((0x00 <= c && c <= 0x1F) || c == 0x7F);
+ case Type_digit:
+ return ('0' <= c && c <= '9');
+ case Type_graph:
+ return (0x20 < c && c < 0x7F);
+ case Type_lower:
+ return ('a' <= c && c <= 'z');
+ case Type_print:
+ return (0x1F < c && c < 0x1F);
+ case Type_punct:
+ return (REGEX_NODE_PUNCT.find(c) >= 0);
+ case Type_space:
+ return (REGEX_NODE_SPACE.find(c) >= 0);
+ case Type_upper:
+ return ('A' <= c && c <= 'Z');
+ case Type_xdigit:
+ if ('0' <= c && c <= '9') return true;
+ if ('a' <= c && c <= 'f') return true;
+ if ('A' <= c && c <= 'F') return true;
+ return false;
+ case Type_word:
+ if ('0' <= c && c <= '9') return true;
+ if ('a' <= c && c <= 'z') return true;
+ if ('A' <= c && c <= 'Z') return true;
+ return (c == '_');
+ default:
+ return false;
+ }
+ return false;
+ }
+
+ RegExNodeClass(Type p_type) {
+
+ length = 1;
+ quantifiable = true;
+ type = p_type;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (s.end <= pos || 0 > pos)
+ return -1;
+
+ if (!test_class(s.at(pos)))
+ return -1;
+
+ return next ? next->test(s, pos + 1) : pos + 1;
+ }
+
+#define REGEX_CMP_CLASS(POS, NAME) if (cmp_class(POS, #NAME)) return Type_ ## NAME
+
+ static Type parse_type(const CharType*& p_pos) {
+
+ REGEX_CMP_CLASS(p_pos, alnum);
+ REGEX_CMP_CLASS(p_pos, alpha);
+ REGEX_CMP_CLASS(p_pos, ascii);
+ REGEX_CMP_CLASS(p_pos, blank);
+ REGEX_CMP_CLASS(p_pos, cntrl);
+ REGEX_CMP_CLASS(p_pos, digit);
+ REGEX_CMP_CLASS(p_pos, graph);
+ REGEX_CMP_CLASS(p_pos, lower);
+ REGEX_CMP_CLASS(p_pos, print);
+ REGEX_CMP_CLASS(p_pos, punct);
+ REGEX_CMP_CLASS(p_pos, space);
+ REGEX_CMP_CLASS(p_pos, upper);
+ REGEX_CMP_CLASS(p_pos, xdigit);
+ REGEX_CMP_CLASS(p_pos, word);
+ return Type_none;
+ }
+
+ static bool cmp_class(const CharType*& p_pos, const char* p_text) {
+
+ unsigned int i = 0;
+ for (i = 0; p_text[i] != '\0'; ++i)
+ if (p_pos[i] != p_text[i])
+ return false;
+
+ if (p_pos[i++] != ':' || p_pos[i] != ']')
+ return false;
+
+ p_pos = &p_pos[i];
+ return true;
+ }
+};
+
+struct RegExNodeAnchorStart : public RegExNode {
+
+ RegExNodeAnchorStart() {
+
+ length = 0;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (pos != 0)
+ return -1;
+
+ return next ? next->test(s, pos) : pos;
+ }
+};
+
+struct RegExNodeAnchorEnd : public RegExNode {
+
+ RegExNodeAnchorEnd() {
+
+ length = 0;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (pos != s.eof)
+ return -1;
+
+ return next ? next->test(s, pos) : pos;
+ }
+};
+
+struct RegExNodeWordBoundary : public RegExNode {
+
+ bool inverse;
+
+ RegExNodeWordBoundary(bool p_inverse) {
+
+ length = 0;
+ inverse = p_inverse;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ bool left = false;
+ bool right = false;
+
+ if (pos != 0) {
+ CharType c = s.at(pos - 1);
+ if (c == '_' || iswalnum(c))
+ left = true;
+ }
+
+ if (pos != s.eof) {
+ CharType c = s.at(pos);
+ if (c == '_' || iswalnum(c))
+ right = true;
+ }
+
+ if ((left == right) != inverse)
+ return -1;
+
+ return next ? next->test(s, pos) : pos;
+ }
+};
+
+struct RegExNodeQuantifier : public RegExNode {
+
+ int min;
+ int max;
+ bool greedy;
+ RegExNode* child;
+
+ RegExNodeQuantifier(int p_min, int p_max) {
+
+ min = p_min;
+ max = p_max;
+ greedy = true;
+ child = NULL;
+ }
+
+ ~RegExNodeQuantifier() {
+
+ if (child)
+ memdelete(child);
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ return test_step(s, pos, 0, pos);
+ }
+
+ virtual int test_parent(RegExSearch& s, int pos) const {
+
+ s.complete = false;
+ return pos;
+ }
+
+ int test_step(RegExSearch& s, int pos, int level, int start) const {
+
+ if (pos > s.end)
+ return -1;
+
+ if (!greedy && level > min) {
+ int res = next ? next->test(s, pos) : pos;
+ if (s.complete)
+ return res;
+
+ if (res >= 0 && parent->test_parent(s, res) >= 0)
+ return res;
+ }
+
+ if (max >= 0 && level > max)
+ return -1;
+
+ int res = pos;
+ if (level >= 1) {
+ if (level > min + 1 && pos == start)
+ return -1;
+
+ res = child->test(s, pos);
+ if (s.complete)
+ return res;
+ }
+
+ if (res >= 0) {
+
+ int res_step = test_step(s, res, level + 1, start);
+ if (res_step >= 0)
+ return res_step;
+
+ if (greedy && level >= min) {
+ if (next)
+ res = next->test(s, res);
+ if (s.complete)
+ return res;
+
+ if (res >= 0 && parent->test_parent(s, res) >= 0)
+ return res;
+ }
+ }
+ return -1;
+ }
+};
+
+struct RegExNodeBackReference : public RegExNode {
+
+ int id;
+
+ RegExNodeBackReference(int p_id) {
+
+ length = -1;
+ quantifiable = true;
+ id = p_id;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ RegExMatch::Group& ref = s.match->captures[id];
+ for (int i = 0; i < ref.length; ++i) {
+
+ if (pos + i >= s.end)
+ return -1;
+
+ if (s.at(ref.start + i) != s.at(pos + i))
+ return -1;
+ }
+ return next ? next->test(s, pos + ref.length) : pos + ref.length;
+ }
+};
+
+
+struct RegExNodeGroup : public RegExNode {
+
+ bool inverse;
+ bool reset_pos;
+ Vector<RegExNode*> childset;
+ RegExNode* back;
+
+ RegExNodeGroup() {
+
+ length = 0;
+ quantifiable = true;
+ inverse = false;
+ reset_pos = false;
+ back = NULL;
+ }
+
+ virtual ~RegExNodeGroup() {
+
+ for (int i = 0; i < childset.size(); ++i)
+ memdelete(childset[i]);
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ for (int i = 0; i < childset.size(); ++i) {
+
+ s.complete = false;
+
+ int res = childset[i]->test(s, pos);
+
+ if (s.complete)
+ return res;
+
+ if (inverse) {
+ if (res < 0)
+ res = pos + 1;
+ else
+ return -1;
+
+ if (i + 1 < childset.size())
+ continue;
+ }
+
+ if (res >= 0) {
+ if (reset_pos)
+ res = pos;
+ return next ? next->test(s, res) : res;
+ }
+ }
+ return -1;
+ }
+
+ void add_child(RegExNode* node) {
+
+ node->parent = this;
+ node->previous = back;
+
+ if (back)
+ back->next = node;
+ else
+ childset.push_back(node);
+
+ increment_length(node->length);
+
+ back = node;
+ }
+
+ void add_childset() {
+
+ if (childset.size() > 0)
+ length = -1;
+ back = NULL;
+ }
+
+ RegExNode* swap_back(RegExNode* node) {
+
+ RegExNode* old = back;
+
+ if (old) {
+ if (!old->previous)
+ childset.remove(childset.size() - 1);
+ back = old->previous;
+ increment_length(old->length, true);
+ }
+
+ add_child(node);
+
+ return old;
+ }
+};
+
+struct RegExNodeCapturing : public RegExNodeGroup {
+
+ int id;
+
+ RegExNodeCapturing(int p_id = 0) {
+
+ id = p_id;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ RegExMatch::Group& ref = s.match->captures[id];
+ int old_start = ref.start;
+ ref.start = pos;
+
+ int res = RegExNodeGroup::test(s, pos);
+
+ if (res >= 0) {
+ if (!s.complete)
+ ref.length = res - pos;
+ } else {
+ ref.start = old_start;
+ }
+
+ return res;
+ }
+
+ virtual int test_parent(RegExSearch& s, int pos) const {
+
+ RegExMatch::Group& ref = s.match->captures[id];
+ ref.length = pos - ref.start;
+ return RegExNode::test_parent(s, pos);
+ }
+
+ static Variant parse_name(const CharType*& c, bool p_allow_numeric) {
+
+ if (c[1] == '0') {
+ return -1;
+ } else if ('1' <= c[1] && c[1] <= '9') {
+ if (!p_allow_numeric)
+ return -1;
+ int res = (++c)[0] - '0';
+ while ('0' <= c[1] && c[1] <= '9')
+ res = res * 10 + int((++c)[0] - '0');
+ if ((++c)[0] != '>')
+ return -1;
+ return res;
+ } else if (iswalnum(c[1])) {
+ String res(++c, 1);
+ while (iswalnum(c[1]))
+ res += String(++c, 1);
+ if ((++c)[0] != '>')
+ return -1;
+ return res;
+ }
+ return -1;
+ }
+};
+
+struct RegExNodeLookAhead : public RegExNodeGroup {
+
+ int id;
+
+ RegExNodeLookAhead(bool p_inverse, int p_id = 0) {
+
+ quantifiable = false;
+ inverse = p_inverse;
+ reset_pos = true;
+ id = p_id;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ s.lookahead_pos[id] = pos;
+ return RegExNodeGroup::test(s, pos);
+ }
+
+ virtual int test_parent(RegExSearch& s, int pos) const {
+
+ return RegExNode::test_parent(s, s.lookahead_pos[id]);
+ }
+};
+
+struct RegExNodeLookBehind : public RegExNodeGroup {
+
+ RegExNodeLookBehind(bool p_inverse, int p_id = 0) {
+
+ quantifiable = false;
+ inverse = p_inverse;
+ reset_pos = true;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (pos < length)
+ return -1;
+ return RegExNodeGroup::test(s, pos - length);
+ }
+};
+
+struct RegExNodeBracket : public RegExNode {
+
+ bool inverse;
+ Vector<RegExNode*> children;
+
+ RegExNodeBracket() {
+
+ length = 1;
+ quantifiable = true;
+ inverse = false;
+ }
+
+ virtual ~RegExNodeBracket() {
+
+ for (int i = 0; i < children.size(); ++i)
+ memdelete(children[i]);
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ for (int i = 0; i < children.size(); ++i) {
+
+ int res = children[i]->test(s, pos);
+
+ if (inverse) {
+ if (res < 0)
+ res = pos + 1;
+ else
+ return -1;
+
+ if (i + 1 < children.size())
+ continue;
+ }
+
+ if (res >= 0)
+ return next ? next->test(s, res) : res;
+ }
+ return -1;
+ }
+
+ void add_child(RegExNode* node) {
+
+ node->parent = this;
+ children.push_back(node);
+ }
+
+ void pop_back() {
+
+ memdelete(children[children.size() - 1]);
+ children.remove(children.size() - 1);
+ }
+};
+
+#define REGEX_EXPAND_FAIL(MSG)\
+{\
+ ERR_PRINT(MSG);\
+ return String();\
+}
+
+String RegExMatch::expand(const String& p_template) const {
+
+ String res;
+ for (const CharType* c = p_template.c_str(); *c != '\0'; ++c) {
+ if (c[0] == '\\') {
+ if (('1' <= c[1] && c[1] <= '9') || (c[1] == 'g' && c[2] == '{')) {
+
+ int ref = 0;
+ bool unclosed = false;
+
+ if (c[1] == 'g') {
+ unclosed = true;
+ c = &c[2];
+ }
+
+ while ('0' <= c[1] && c[1] <= '9') {
+ ref = ref * 10 + int(c[1] - '0');
+ ++c;
+ }
+
+ if (unclosed) {
+ if (c[1] != '}')
+ REGEX_EXPAND_FAIL("unclosed backreference '{'");
+ ++c;
+ }
+
+ res += get_string(ref);
+
+ } else if (c[1] =='g' && c[2] == '<') {
+
+ const CharType* d = &c[2];
+
+ Variant name = RegExNodeCapturing::parse_name(d, true);
+ if (name == Variant(-1))
+ REGEX_EXPAND_FAIL("unrecognised character for group name");
+
+ c = d;
+
+ res += get_string(name);
+
+ } else {
+
+ const CharType* d = c;
+ CharType ch = RegExNodeChar::parse_escape(d);
+ if (c == d)
+ REGEX_EXPAND_FAIL("invalid escape token");
+ res += String(&ch, 1);
+ c = d;
+ }
+ } else {
+ res += String(c, 1);
+ }
+ }
+ return res;
+}
+
+int RegExMatch::get_group_count() const {
+
+ int count = 0;
+ for (int i = 1; i < captures.size(); ++i)
+ if (captures[i].name.get_type() == Variant::INT)
+ ++count;
+ return count;
+}
+
+Array RegExMatch::get_group_array() const {
+
+ Array res;
+ for (int i = 1; i < captures.size(); ++i) {
+ const RegExMatch::Group& capture = captures[i];
+ if (capture.name.get_type() != Variant::INT)
+ continue;
+
+ if (capture.start >= 0)
+ res.push_back(string.substr(capture.start, capture.length));
+ else
+ res.push_back(String());
+ }
+ return res;
+}
+
+Array RegExMatch::get_names() const {
+
+ Array res;
+ for (int i = 1; i < captures.size(); ++i)
+ if (captures[i].name.get_type() == Variant::STRING)
+ res.push_back(captures[i].name);
+ return res;
+}
+
+Dictionary RegExMatch::get_name_dict() const {
+
+ Dictionary res;
+ for (int i = 1; i < captures.size(); ++i) {
+ const RegExMatch::Group& capture = captures[i];
+ if (capture.name.get_type() != Variant::STRING)
+ continue;
+
+ if (capture.start >= 0)
+ res[capture.name] = string.substr(capture.start, capture.length);
+ else
+ res[capture.name] = String();
+ }
+ return res;
+}
+
+String RegExMatch::get_string(const Variant& p_name) const {
+
+ for (int i = 0; i < captures.size(); ++i) {
+
+ const RegExMatch::Group& capture = captures[i];
+
+ if (capture.name != p_name)
+ continue;
+
+ if (capture.start == -1)
+ return String();
+
+ return string.substr(capture.start, capture.length);
+ }
+ return String();
+}
+
+int RegExMatch::get_start(const Variant& p_name) const {
+
+ for (int i = 0; i < captures.size(); ++i)
+ if (captures[i].name == p_name)
+ return captures[i].start;
+ return -1;
+}
+
+int RegExMatch::get_end(const Variant& p_name) const {
+
+ for (int i = 0; i < captures.size(); ++i)
+ if (captures[i].name == p_name)
+ return captures[i].start + captures[i].length;
+ return -1;
+}
+
+RegExMatch::RegExMatch() {
+
+}
+
+static bool RegEx_is_shorthand(CharType ch) {
+
+ switch (ch) {
+ case 'w':
+ case 'W':
+ case 'd':
+ case 'D':
+ case 's':
+ case 'S':
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+#define REGEX_COMPILE_FAIL(MSG)\
+{\
+ ERR_PRINT(MSG);\
+ clear();\
+ return FAILED;\
+}
+
+Error RegEx::compile(const String& p_pattern) {
+
+ ERR_FAIL_COND_V(p_pattern.length() == 0, FAILED);
+
+ if (pattern == p_pattern && root)
+ return OK;
+
+ clear();
+ pattern = p_pattern;
+ group_names.push_back(0);
+ RegExNodeGroup* root_group = memnew(RegExNodeCapturing(0));
+ root = root_group;
+ Vector<RegExNodeGroup*> stack;
+ stack.push_back(root_group);
+ int lookahead_level = 0;
+ int numeric_groups = 0;
+ const int numeric_max = 9;
+
+ for (const CharType* c = p_pattern.c_str(); *c != '\0'; ++c) {
+
+ switch (c[0]) {
+ case '(':
+ if (c[1] == '?') {
+
+ RegExNodeGroup* group = NULL;
+ switch (c[2]) {
+ case ':':
+ c = &c[2];
+ group = memnew(RegExNodeGroup());
+ break;
+ case '!':
+ case '=':
+ group = memnew(RegExNodeLookAhead((c[2] == '!'), lookahead_level++));
+ if (lookahead_depth < lookahead_level)
+ lookahead_depth = lookahead_level;
+ c = &c[2];
+ break;
+ case '<':
+ if (c[3] == '!' || c[3] == '=') {
+ group = memnew(RegExNodeLookBehind((c[3] == '!'), lookahead_level++));
+ c = &c[3];
+ }
+ break;
+ case 'P':
+ if (c[3] == '<') {
+ const CharType* d = &c[3];
+ Variant name = RegExNodeCapturing::parse_name(d, false);
+ if (name == Variant(-1))
+ REGEX_COMPILE_FAIL("unrecognised character for group name");
+ group = memnew(RegExNodeCapturing(group_names.size()));
+ group_names.push_back(name);
+ c = d;
+ }
+ default:
+ break;
+ }
+ if (!group)
+ REGEX_COMPILE_FAIL("unrecognised qualifier for group");
+ stack[0]->add_child(group);
+ stack.insert(0, group);
+
+ } else if (numeric_groups < numeric_max) {
+
+ RegExNodeCapturing* group = memnew(RegExNodeCapturing(group_names.size()));
+ group_names.push_back(++numeric_groups);
+ stack[0]->add_child(group);
+ stack.insert(0, group);
+
+ } else {
+
+ RegExNodeGroup* group = memnew(RegExNodeGroup());
+ stack[0]->add_child(group);
+ stack.insert(0, group);
+ }
+ break;
+ case ')':
+ if (stack.size() == 1)
+ REGEX_COMPILE_FAIL("unexpected ')'");
+ stack.remove(0);
+ break;
+ case '\\':
+ if (('1' <= c[1] && c[1] <= '9') || (c[1] == 'g' && c[2] == '{')) {
+
+ int ref = 0;
+ bool unclosed = false;
+
+ if (c[1] == 'g') {
+ unclosed = true;
+ c = &c[2];
+ }
+
+ while ('0' <= c[1] && c[1] <= '9') {
+ ref = ref * 10 + int(c[1] - '0');
+ ++c;
+ }
+
+ if (unclosed) {
+ if (c[1] != '}')
+ REGEX_COMPILE_FAIL("unclosed backreference '{'");
+ ++c;
+ }
+
+ if (ref > numeric_groups || ref <= 0)
+ REGEX_COMPILE_FAIL("backreference not found");
+
+ for (int i = 0; i < stack.size(); ++i)
+ if (dynamic_cast<RegExNodeLookBehind*>(stack[i]))
+ REGEX_COMPILE_FAIL("backreferences inside lookbehind not supported");
+
+ for (int i = 0; i < group_names.size(); ++i) {
+ if (group_names[i].get_type() == Variant::INT && int(group_names[i]) == ref) {
+ ref = group_names[i];
+ break;
+ }
+ }
+
+ stack[0]->add_child(memnew(RegExNodeBackReference(ref)));
+
+ } if (c[1] =='g' && c[2] == '<') {
+
+ const CharType* d = &c[2];
+
+ Variant name = RegExNodeCapturing::parse_name(d, true);
+ if (name == Variant(-1))
+ REGEX_COMPILE_FAIL("unrecognised character for group name");
+
+ c = d;
+
+ for (int i = 0; i < stack.size(); ++i)
+ if (dynamic_cast<RegExNodeLookBehind*>(stack[i]))
+ REGEX_COMPILE_FAIL("backreferences inside lookbehind not supported");
+
+ int ref = -1;
+
+ for (int i = 0; i < group_names.size(); ++i) {
+ if (group_names[i].get_type() == Variant::INT && int(group_names[i]) == ref) {
+ ref = group_names[i];
+ break;
+ }
+ }
+
+ if (ref == -1)
+ REGEX_COMPILE_FAIL("backreference not found");
+
+ stack[0]->add_child(memnew(RegExNodeBackReference(ref)));
+
+ } else if (c[1] == 'b' || c[1] == 'B') {
+
+ stack[0]->add_child(memnew(RegExNodeWordBoundary(*(++c) == 'B')));
+
+ } else if (RegEx_is_shorthand(c[1])) {
+
+ stack[0]->add_child(memnew(RegExNodeShorthand(*(++c))));
+
+ } else {
+
+ const CharType* d = c;
+ CharType ch = RegExNodeChar::parse_escape(d);
+ if (c == d)
+ REGEX_COMPILE_FAIL("invalid escape token");
+ stack[0]->add_child(memnew(RegExNodeChar(ch)));
+ c = d;
+
+ }
+ break;
+ case '[':
+ {
+ RegExNodeBracket* bracket = memnew(RegExNodeBracket());
+ stack[0]->add_child(bracket);
+ if (c[1] == '^') {
+ bracket->inverse = true;
+ ++c;
+ }
+ bool first_child = true;
+ CharType previous_child;
+ bool previous_child_single = false;
+ while (true) {
+ ++c;
+ if (!first_child && c[0] == ']') {
+
+ break;
+
+ } else if (c[0] == '\0') {
+
+ REGEX_COMPILE_FAIL("unclosed bracket expression '['");
+
+ } else if (c[0] == '\\') {
+
+ if (RegEx_is_shorthand(c[1])) {
+ bracket->add_child(memnew(RegExNodeShorthand(*(++c))));
+ } else {
+ const CharType* d = c;
+ CharType ch = RegExNodeChar::parse_escape(d);
+ if (c == d)
+ REGEX_COMPILE_FAIL("invalid escape token");
+ bracket->add_child(memnew(RegExNodeChar(ch)));
+ c = d;
+ previous_child = ch;
+ previous_child_single = true;
+ }
+
+ } else if (c[0] == ']' && c[1] == ':') {
+
+ const CharType* d = &c[2];
+ RegExNodeClass::Type type = RegExNodeClass::parse_type(d);
+ if (type != RegExNodeClass::Type_none) {
+
+ c = d;
+ previous_child_single = false;
+
+ } else {
+
+ bracket->add_child(memnew(RegExNodeChar('[')));
+ previous_child = '[';
+ previous_child_single = true;
+ }
+ } else if (previous_child_single && c[0] == '-') {
+
+ if (c[1] != '\0' && c[1] != ']') {
+
+ CharType next;
+
+ if (c[1] == '\\') {
+ const CharType* d = ++c;
+ next = RegExNodeChar::parse_escape(d);
+ if (c == d)
+ REGEX_COMPILE_FAIL("invalid escape token");
+ } else {
+ next = *(++c);
+ }
+
+ if (next < previous_child)
+ REGEX_COMPILE_FAIL("text range out of order");
+
+ bracket->pop_back();
+ bracket->add_child(memnew(RegExNodeRange(previous_child, next)));
+ previous_child_single = false;
+ } else {
+
+ bracket->add_child(memnew(RegExNodeChar('-')));
+ previous_child = '-';
+ previous_child_single = true;
+ }
+ } else {
+
+ bracket->add_child(memnew(RegExNodeChar(c[0])));
+ previous_child = c[0];
+ previous_child_single = true;
+ }
+ first_child = false;
+ }
+ }
+ break;
+ case '|':
+ for (int i = 0; i < stack.size(); ++i)
+ if (dynamic_cast<RegExNodeLookBehind*>(stack[i]))
+ REGEX_COMPILE_FAIL("alternations inside lookbehind not supported");
+ stack[0]->add_childset();
+ break;
+ case '^':
+ stack[0]->add_child(memnew(RegExNodeAnchorStart()));
+ break;
+ case '$':
+ stack[0]->add_child(memnew(RegExNodeAnchorEnd()));
+ break;
+ case '.':
+ stack[0]->add_child(memnew(RegExNodeShorthand('.')));
+ break;
+ case '?':
+ case '*':
+ case '+':
+ case '{':
+ {
+ int min_val = 0;
+ int max_val = -1;
+ bool valid = true;
+ const CharType* d = c;
+ bool max_set = true;
+ switch (c[0]) {
+ case '?':
+ min_val = 0;
+ max_val = 1;
+ break;
+ case '*':
+ min_val = 0;
+ max_val = -1;
+ break;
+ case '+':
+ min_val = 1;
+ max_val = -1;
+ break;
+ case '{':
+ max_set = false;
+ while (valid) {
+ ++d;
+ if (d[0] == '}') {
+ break;
+ } else if (d[0] == ',') {
+ max_set = true;
+ } else if ('0' <= d[0] && d[0] <= '9') {
+ if (max_set) {
+ if (max_val < 0)
+ max_val = int(d[0] - '0');
+ else
+ max_val = max_val * 10 + int(d[0] - '0');
+ } else {
+ min_val = min_val * 10 + int(d[0] - '0');
+ }
+ } else {
+ valid = false;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!max_set)
+ max_val = min_val;
+
+ if (valid) {
+
+ c = d;
+
+ if (stack[0]->back == NULL || !stack[0]->back->quantifiable)
+ REGEX_COMPILE_FAIL("element not quantifiable");
+
+ if (min_val != max_val)
+ for (int i = 0; i < stack.size(); ++i)
+ if (dynamic_cast<RegExNodeLookBehind*>(stack[i]))
+ REGEX_COMPILE_FAIL("variable length quantifiers inside lookbehind not supported");
+
+ RegExNodeQuantifier* quant = memnew(RegExNodeQuantifier(min_val, max_val));
+ quant->child = stack[0]->swap_back(quant);
+ quant->child->previous = NULL;
+ quant->child->parent = quant;
+
+ if (min_val == max_val && quant->child->length >= 0)
+ quant->length = max_val * quant->child->length;
+
+ if (c[1] == '?') {
+ quant->greedy = false;
+ ++c;
+ }
+ break;
+ }
+ }
+ default:
+ stack[0]->add_child(memnew(RegExNodeChar(c[0])));
+ break;
+ }
+ }
+ if (stack.size() > 1)
+ REGEX_COMPILE_FAIL("unclosed group '('");
+ return OK;
+}
+
+Ref<RegExMatch> RegEx::search(const String& p_text, int p_start, int p_end) const {
+
+ ERR_FAIL_COND_V(!is_valid(), NULL);
+ ERR_FAIL_COND_V(p_start < 0, NULL);
+ ERR_FAIL_COND_V(p_start >= p_text.length(), NULL);
+ ERR_FAIL_COND_V(p_end > p_text.length(), NULL);
+ ERR_FAIL_COND_V(p_end != -1 && p_end < p_start, NULL);
+
+ Ref<RegExMatch> res = memnew(RegExMatch());
+
+ for (int i = 0; i < group_names.size(); ++i) {
+ RegExMatch::Group group;
+ group.name = group_names[i];
+ res->captures.push_back(group);
+ }
+
+ res->string = p_text;
+
+ if (p_end == -1)
+ p_end = p_text.length();
+
+ RegExSearch s(res, p_end, lookahead_depth);
+
+ for (int i = p_start; i <= s.end; ++i) {
+ for (int c = 0; c < group_names.size(); ++c) {
+ res->captures[c].start = -1;
+ res->captures[c].length = 0;
+ }
+ if (root->test(s, i) >= 0)
+ break;
+ }
+
+ if (res->captures[0].start >= 0)
+ return res;
+ return NULL;
+}
+
+String RegEx::sub(const String& p_text, const String& p_replacement, bool p_all, int p_start, int p_end) const {
+
+ ERR_FAIL_COND_V(!is_valid(), p_text);
+ ERR_FAIL_COND_V(p_start < 0, p_text);
+ ERR_FAIL_COND_V(p_start >= p_text.length(), p_text);
+ ERR_FAIL_COND_V(p_end > p_text.length(), p_text);
+ ERR_FAIL_COND_V(p_end != -1 && p_end < p_start, p_text);
+
+ String text = p_text;
+ int start = p_start;
+
+ if (p_end == -1)
+ p_end = p_text.length();
+
+ while (start < text.length() && (p_all || start == p_start)) {
+
+ Ref<RegExMatch> m = search(text, start, p_end);
+
+ RegExMatch::Group& s = m->captures[0];
+
+ if (s.start < 0)
+ break;
+
+ String res = text.substr(0, s.start) + m->expand(p_replacement);
+
+ start = res.length();
+
+ if (s.length == 0)
+ ++start;
+
+ int sub_end = s.start + s.length;
+ if (sub_end < text.length())
+ res += text.substr(sub_end, text.length() - sub_end);
+
+ p_end += res.length() - text.length();
+
+ text = res;
+ }
+ return text;
+}
+
+void RegEx::clear() {
+
+ if (root)
+ memdelete(root);
+
+ root = NULL;
+ group_names.clear();
+ lookahead_depth = 0;
+}
+
+bool RegEx::is_valid() const {
+
+ return (root != NULL);
+}
+
+String RegEx::get_pattern() const {
+
+ return pattern;
+}
+
+int RegEx::get_group_count() const {
+
+ int count = 0;
+ for (int i = 1; i < group_names.size(); ++i)
+ if (group_names[i].get_type() == Variant::INT)
+ ++count;
+ return count;
+}
+
+Array RegEx::get_names() const {
+
+ Array res;
+ for (int i = 1; i < group_names.size(); ++i)
+ if (group_names[i].get_type() == Variant::STRING)
+ res.push_back(group_names[i]);
+ return res;
+}
+
+RegEx::RegEx() {
+
+ root = NULL;
+ lookahead_depth = 0;
+}
+
+RegEx::RegEx(const String& p_pattern) {
+
+ root = NULL;
+ compile(p_pattern);
+}
+
+RegEx::~RegEx() {
+
+ if (root)
+ memdelete(root);
+}
+
+void RegExMatch::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("expand","template"),&RegExMatch::expand);
+ ObjectTypeDB::bind_method(_MD("get_group_count"),&RegExMatch::get_group_count);
+ ObjectTypeDB::bind_method(_MD("get_group_array"),&RegExMatch::get_group_array);
+ ObjectTypeDB::bind_method(_MD("get_names"),&RegExMatch::get_names);
+ ObjectTypeDB::bind_method(_MD("get_name_dict"),&RegExMatch::get_name_dict);
+ ObjectTypeDB::bind_method(_MD("get_string","name"),&RegExMatch::get_string, DEFVAL(0));
+ ObjectTypeDB::bind_method(_MD("get_start","name"),&RegExMatch::get_start, DEFVAL(0));
+ ObjectTypeDB::bind_method(_MD("get_end","name"),&RegExMatch::get_end, DEFVAL(0));
+}
+
+void RegEx::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("clear"),&RegEx::clear);
+ ObjectTypeDB::bind_method(_MD("compile","pattern"),&RegEx::compile);
+ ObjectTypeDB::bind_method(_MD("search","text","start","end"),&RegEx::search, DEFVAL(0), DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("sub","text","replacement","all","start","end"),&RegEx::sub, DEFVAL(false), DEFVAL(0), DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("is_valid"),&RegEx::is_valid);
+ ObjectTypeDB::bind_method(_MD("get_pattern"),&RegEx::get_pattern);
+ ObjectTypeDB::bind_method(_MD("get_group_count"),&RegEx::get_group_count);
+ ObjectTypeDB::bind_method(_MD("get_names"),&RegEx::get_names);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "pattern"), _SCS("compile"), _SCS("get_pattern"));
+}
+
diff --git a/modules/regex/regex.h b/modules/regex/regex.h
new file mode 100644
index 0000000000..803aa72b3f
--- /dev/null
+++ b/modules/regex/regex.h
@@ -0,0 +1,114 @@
+/*************************************************************************/
+/* regex.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 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 REGEX_H
+#define REGEX_H
+
+#include "core/vector.h"
+#include "core/ustring.h"
+#include "core/dictionary.h"
+#include "core/reference.h"
+#include "core/resource.h"
+
+class RegExNode;
+
+class RegExMatch : public Reference {
+
+ OBJ_TYPE(RegExMatch, Reference);
+
+ struct Group {
+ Variant name;
+ int start;
+ int length;
+ };
+
+ Vector<Group> captures;
+ String string;
+
+ friend class RegEx;
+ friend class RegExSearch;
+ friend class RegExNodeCapturing;
+ friend class RegExNodeBackReference;
+
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ String expand(const String& p_template) const;
+
+ int get_group_count() const;
+ Array get_group_array() const;
+
+ Array get_names() const;
+ Dictionary get_name_dict() const;
+
+ String get_string(const Variant& p_name) const;
+ int get_start(const Variant& p_name) const;
+ int get_end(const Variant& p_name) const;
+
+ RegExMatch();
+
+};
+
+class RegEx : public Resource {
+
+ OBJ_TYPE(RegEx, Resource);
+
+ RegExNode* root;
+ Vector<Variant> group_names;
+ String pattern;
+ int lookahead_depth;
+
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ void clear();
+ Error compile(const String& p_pattern);
+
+ Ref<RegExMatch> search(const String& p_text, int p_start = 0, int p_end = -1) const;
+ String sub(const String& p_text, const String& p_replacement, bool p_all = false, int p_start = 0, int p_end = -1) const;
+
+ bool is_valid() const;
+ String get_pattern() const;
+ int get_group_count() const;
+ Array get_names() const;
+
+ RegEx();
+ RegEx(const String& p_pattern);
+ ~RegEx();
+
+};
+
+#endif // REGEX_H
+
diff --git a/drivers/nrex/regex.h b/modules/regex/register_types.cpp
index 74495442c7..050cf3efff 100644
--- a/drivers/nrex/regex.h
+++ b/modules/regex/register_types.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* regex.h */
+/* register_types.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -26,40 +26,18 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef REGEX_H
-#define REGEX_H
-#include "ustring.h"
-#include "vector.h"
-#include "core/reference.h"
-#include "nrex.hpp"
+#include "register_types.h"
+#include "object_type_db.h"
+#include "regex.h"
-class RegEx : public Reference {
+void register_regex_types() {
- OBJ_TYPE(RegEx, Reference);
+ ObjectTypeDB::register_type<RegExMatch>();
+ ObjectTypeDB::register_type<RegEx>();
+}
- mutable String text;
- mutable Vector<nrex_result> captures;
- nrex exp;
+void unregister_regex_types() {
-protected:
+}
- static void _bind_methods();
- StringArray _bind_get_captures() const;
-
-public:
-
- void clear();
- bool is_valid() const;
- int get_capture_count() const;
- int get_capture_start(int capture) const;
- String get_capture(int capture) const;
- Error compile(const String& p_pattern, int capture = 9);
- int find(const String& p_text, int p_start = 0, int p_end = -1) const;
-
- RegEx();
- RegEx(const String& p_pattern);
- ~RegEx();
-};
-
-#endif // REGEX_H
diff --git a/modules/regex/register_types.h b/modules/regex/register_types.h
new file mode 100644
index 0000000000..df3b508e14
--- /dev/null
+++ b/modules/regex/register_types.h
@@ -0,0 +1,31 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 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. */
+/*************************************************************************/
+
+void register_regex_types();
+void unregister_regex_types();
diff --git a/modules/squish/SCsub b/modules/squish/SCsub
index 072921ef64..3fdc587652 100644
--- a/modules/squish/SCsub
+++ b/modules/squish/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/modules/theora/SCsub b/modules/theora/SCsub
index cf1a8e6707..22c618fe8b 100644
--- a/modules/theora/SCsub
+++ b/modules/theora/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/modules/visual_script/SCsub b/modules/visual_script/SCsub
index 9ff13fc43f..0882406761 100644
--- a/modules/visual_script/SCsub
+++ b/modules/visual_script/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub
index f3dbc893fc..3220cb454c 100644
--- a/modules/vorbis/SCsub
+++ b/modules/vorbis/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/modules/webp/SCsub b/modules/webp/SCsub
index 4997aa3359..38585a1ff2 100644
--- a/modules/webp/SCsub
+++ b/modules/webp/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Import('env_modules')
diff --git a/platform/android/SCsub b/platform/android/SCsub
index 60bb4bd613..4f9a9709cb 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
import shutil
Import('env')
diff --git a/platform/android/detect.py b/platform/android/detect.py
index acf4ce412a..842036f986 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -131,7 +131,7 @@ def configure(env):
gcc_path=gcc_path+"/darwin-x86_64/bin"
env['SHLINKFLAGS'][1] = '-shared'
env['SHLIBSUFFIX'] = '.so'
- elif (os.platform.startswith('win')):
+ elif (sys.platform.startswith('win')):
if (platform.machine().endswith('64')):
gcc_path=gcc_path+"/windows-x86_64/bin"
else:
diff --git a/platform/bb10/SCsub b/platform/bb10/SCsub
index 81f6e726e4..84fff0828b 100644
--- a/platform/bb10/SCsub
+++ b/platform/bb10/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
bb10_lib = [
diff --git a/platform/haiku/SCsub b/platform/haiku/SCsub
index 1952e6b59b..b5a584baa4 100644
--- a/platform/haiku/SCsub
+++ b/platform/haiku/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
common_haiku = [
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index 5b2e1533da..236630bef4 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
iphone_lib = [
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub
index fc70d45a04..22af436470 100644
--- a/platform/javascript/SCsub
+++ b/platform/javascript/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
javascript_files = [
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index de57d770c4..f934916aa2 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -195,8 +195,8 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t>& p_html, const St
}
CharString cs = strnew.utf8();
- p_html.resize(cs.size());
- for(int i=9;i<cs.size();i++) {
+ p_html.resize(cs.length());
+ for(int i=9;i<cs.length();i++) {
p_html[i]=cs[i];
}
}
diff --git a/platform/osx/SCsub b/platform/osx/SCsub
index 4169795519..217dee81eb 100644
--- a/platform/osx/SCsub
+++ b/platform/osx/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
files = [
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index f8b2153aee..4e772e37eb 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -91,9 +91,6 @@ def configure(env):
env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND'])
env["CC"]="clang"
env["LD"]="clang++"
- if (env["colored"]=="yes"):
- if sys.stdout.isatty():
- env.Append(CPPFLAGS=["-fcolor-diagnostics"])
import methods
diff --git a/platform/server/SCsub b/platform/server/SCsub
index 3dda6b4395..12758c5db0 100644
--- a/platform/server/SCsub
+++ b/platform/server/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
diff --git a/platform/server/detect.py b/platform/server/detect.py
index 2f6fb00e0d..ce14100fd0 100644
--- a/platform/server/detect.py
+++ b/platform/server/detect.py
@@ -38,9 +38,6 @@ def configure(env):
env["CC"]="clang"
env["CXX"]="clang++"
env["LD"]="clang++"
- if (env["colored"]=="yes"):
- if sys.stdout.isatty():
- env.Append(CXXFLAGS=["-fcolor-diagnostics"])
is64=sys.maxsize > 2**32
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 914cee0fa1..e53eb7af34 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index a5b26930be..7ae0100762 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -1,16 +1,11 @@
#
-# tested on | Windows native | Linux cross-compilation
-# ----------------------------+-------------------+---------------------------
-# MSVS C++ 2010 Express | WORKS | n/a
+# tested on | Windows native | Linux cross-compilation
+# ----------------------------+-------------------+---------------------------
# Visual C++ Build Tools 2015 | WORKS | n/a
-# Mingw-w64 | WORKS | WORKS
-# Mingw-w32 | WORKS | WORKS
-# MinGW | WORKS | untested
-#
-#####
-# Notes about MSVS C++ :
-#
-# - MSVC2010-Express compiles to 32bits only.
+# MSVS C++ 2010 Express | WORKS | n/a
+# Mingw-w64 | WORKS | WORKS
+# Mingw-w32 | WORKS | WORKS
+# MinGW | WORKS | untested
#
#####
# Note about Visual C++ Build Tools :
@@ -19,6 +14,11 @@
# http://landinghub.visualstudio.com/visual-cpp-build-tools
#
#####
+# Notes about MSVS C++ :
+#
+# - MSVC2010-Express compiles to 32bits only.
+#
+#####
# Notes about Mingw-w64 and Mingw-w32 under Windows :
#
# - both can be installed using the official installer :
@@ -109,7 +109,7 @@ def can_build():
if (os.name=="nt"):
#building natively on windows!
- if ( methods.msvc_is_detected() ):
+ if ( os.getenv("VCINSTALLDIR") ):
return True
else:
print("\nMSVC not detected, attempting Mingw.")
@@ -204,7 +204,7 @@ def configure(env):
env.Append(CPPPATH=['#platform/windows'])
env['is_mingw']=False
- if (os.name=="nt" and methods.msvc_is_detected() ):
+ if (os.name=="nt" and os.getenv("VCINSTALLDIR") ):
#build using visual studio
env['ENV']['TMP'] = os.environ['TMP']
env.Append(CPPPATH=['#platform/windows/include'])
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 0ca5d3bd0f..286f5fb0b7 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -1366,13 +1366,16 @@ void OS_Windows::set_mouse_mode(MouseMode p_mode) {
POINT pos = { (int) center.x, (int) center.y };
ClientToScreen(hWnd, &pos);
SetCursorPos(pos.x, pos.y);
- ShowCursor(false);
} else {
- ShowCursor(true);
ReleaseCapture();
ClipCursor(NULL);
}
+ if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) {
+ hCursor = SetCursor(NULL);
+ } else {
+ SetCursor(hCursor);
+ }
}
OS_Windows::MouseMode OS_Windows::get_mouse_mode() const{
@@ -2418,6 +2421,9 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) {
#ifdef RTAUDIO_ENABLED
AudioDriverManagerSW::add_driver(&driver_rtaudio);
#endif
+#ifdef XAUDIO2_ENABLED
+ AudioDriverManagerSW::add_driver(&driver_xaudio2);
+#endif
}
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 70ef694957..903dd10c70 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -41,6 +41,9 @@
#include "servers/audio/audio_server_sw.h"
#include "servers/audio/sample_manager_sw.h"
#include "drivers/rtaudio/audio_driver_rtaudio.h"
+#ifdef XAUDIO2_ENABLED
+#include "drivers/xaudio2/audio_driver_xaudio2.h"
+#endif
#include "servers/spatial_sound/spatial_sound_server_sw.h"
#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h"
#include "drivers/unix/ip_unix.h"
@@ -137,6 +140,9 @@ class OS_Windows : public OS {
#ifdef RTAUDIO_ENABLED
AudioDriverRtAudio driver_rtaudio;
#endif
+#ifdef XAUDIO2_ENABLED
+ AudioDriverXAudio2 driver_xaudio2;
+#endif
void _drag_event(int p_x, int p_y, int idx);
void _touch_event(bool p_pressed, int p_x, int p_y, int idx);
diff --git a/platform/windows/packet_peer_udp_winsock.cpp b/platform/windows/packet_peer_udp_winsock.cpp
index 2c79365c08..b4cd60979c 100644
--- a/platform/windows/packet_peer_udp_winsock.cpp
+++ b/platform/windows/packet_peer_udp_winsock.cpp
@@ -29,6 +29,9 @@
#include "packet_peer_udp_winsock.h"
#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include "drivers/unix/socket_helpers.h"
int PacketPeerUDPWinsock::get_available_packet_count() const {
@@ -48,7 +51,15 @@ Error PacketPeerUDPWinsock::get_packet(const uint8_t **r_buffer,int &r_buffer_si
return ERR_UNAVAILABLE;
uint32_t size;
- rb.read((uint8_t*)&packet_ip.host,4,true);
+ uint8_t type;
+ rb.read(&type, 1, true);
+ if (type == IP_Address::TYPE_IPV4) {
+ rb.read((uint8_t*)&packet_ip.field8,4,true);
+ packet_ip.type = IP_Address::TYPE_IPV4;
+ } else {
+ rb.read((uint8_t*)&packet_ip.field8,16,true);
+ packet_ip.type = IP_Address::TYPE_IPV6;
+ };
rb.read((uint8_t*)&packet_port,4,true);
rb.read((uint8_t*)&size,4,true);
rb.read(packet_buffer,size,true);
@@ -60,19 +71,16 @@ Error PacketPeerUDPWinsock::get_packet(const uint8_t **r_buffer,int &r_buffer_si
}
Error PacketPeerUDPWinsock::put_packet(const uint8_t *p_buffer,int p_buffer_size){
- int sock = _get_socket();
+ int sock = _get_socket(peer_addr.type);
ERR_FAIL_COND_V( sock == -1, FAILED );
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(peer_port);
- addr.sin_addr = *((struct in_addr*)&peer_addr.host);
-
+ struct sockaddr_storage addr;
+ size_t addr_size = _set_sockaddr(&addr, peer_addr, peer_port);
_set_blocking(true);
errno = 0;
int err;
- while ( (err = sendto(sock, (const char*)p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, sizeof(addr))) != p_buffer_size) {
+ while ( (err = sendto(sock, (const char*)p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, addr_size)) != p_buffer_size) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
return FAILED;
@@ -104,17 +112,17 @@ void PacketPeerUDPWinsock::_set_blocking(bool p_blocking) {
};
}
-Error PacketPeerUDPWinsock::listen(int p_port, int p_recv_buffer_size){
+Error PacketPeerUDPWinsock::listen(int p_port, IP_Address::AddrType p_address_type, int p_recv_buffer_size) {
close();
- int sock = _get_socket();
+ int sock = _get_socket(p_address_type);
if (sock == -1 )
return ERR_CANT_CREATE;
- sockaddr_in addr = {0};
- addr.sin_family = AF_INET;
- addr.sin_port = htons(p_port);
- addr.sin_addr.s_addr = INADDR_ANY;
- if (bind(sock, (struct sockaddr*)&addr, sizeof(sockaddr_in)) == -1 ) {
+
+ struct sockaddr_storage addr = {0};
+ size_t addr_size = _set_listen_sockaddr(&addr, p_port, p_address_type, NULL);
+
+ if (bind(sock, (struct sockaddr*)&addr, addr_size) == -1 ) {
close();
return ERR_UNAVAILABLE;
}
@@ -146,17 +154,41 @@ Error PacketPeerUDPWinsock::_poll(bool p_wait) {
_set_blocking(p_wait);
- struct sockaddr_in from = {0};
- int len = sizeof(struct sockaddr_in);
+ struct sockaddr_storage from = {0};
+ int len = sizeof(struct sockaddr_storage);
int ret;
while ( (ret = recvfrom(sockfd, (char*)recv_buffer, MIN((int)sizeof(recv_buffer),MAX(rb.space_left()-12, 0)), 0, (struct sockaddr*)&from, &len)) > 0) {
- rb.write((uint8_t*)&from.sin_addr, 4);
- uint32_t port = ntohs(from.sin_port);
+
+ uint32_t port = 0;
+
+ if (from.ss_family == AF_INET) {
+ uint8_t type = (uint8_t)IP_Address::TYPE_IPV4;
+ rb.write(&type, 1);
+ struct sockaddr_in* sin_from = (struct sockaddr_in*)&from;
+ rb.write((uint8_t*)&sin_from->sin_addr, 4);
+ port = ntohs(sin_from->sin_port);
+
+ } else if (from.ss_family == AF_INET6) {
+
+ uint8_t type = (uint8_t)IP_Address::TYPE_IPV6;
+ rb.write(&type, 1);
+
+ struct sockaddr_in6* s6_from = (struct sockaddr_in6*)&from;
+ rb.write((uint8_t*)&s6_from->sin6_addr, 16);
+
+ port = ntohs(s6_from->sin6_port);
+
+ } else {
+ // WARN_PRINT("Ignoring packet with unknown address family");
+ uint8_t type = (uint8_t)IP_Address::TYPE_NONE;
+ rb.write(&type, 1);
+ };
+
rb.write((uint8_t*)&port, 4);
rb.write((uint8_t*)&ret, 4);
rb.write(recv_buffer, ret);
- len = sizeof(struct sockaddr_in);
+ len = sizeof(struct sockaddr_storage);
++queue_count;
};
@@ -202,12 +234,14 @@ int PacketPeerUDPWinsock::get_packet_port() const{
return packet_port;
}
-int PacketPeerUDPWinsock::_get_socket() {
+int PacketPeerUDPWinsock::_get_socket(IP_Address::AddrType p_type) {
if (sockfd != -1)
return sockfd;
- sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ int family = p_type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+
+ sockfd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
ERR_FAIL_COND_V( sockfd == -1, -1 );
//fcntl(sockfd, F_SETFL, O_NONBLOCK);
diff --git a/platform/windows/packet_peer_udp_winsock.h b/platform/windows/packet_peer_udp_winsock.h
index b24dbac592..9837ef6621 100644
--- a/platform/windows/packet_peer_udp_winsock.h
+++ b/platform/windows/packet_peer_udp_winsock.h
@@ -42,15 +42,15 @@ class PacketPeerUDPWinsock : public PacketPeerUDP {
mutable RingBuffer<uint8_t> rb;
uint8_t recv_buffer[PACKET_BUFFER_SIZE];
mutable uint8_t packet_buffer[PACKET_BUFFER_SIZE];
- IP_Address packet_ip;
- int packet_port;
+ mutable IP_Address packet_ip;
+ mutable int packet_port;
mutable int queue_count;
int sockfd;
IP_Address peer_addr;
int peer_port;
- _FORCE_INLINE_ int _get_socket();
+ _FORCE_INLINE_ int _get_socket(IP_Address::AddrType p_type);
static PacketPeerUDP* _create();
@@ -67,7 +67,7 @@ public:
virtual int get_max_packet_size() const;
- virtual Error listen(int p_port,int p_recv_buffer_size=65536);
+ virtual Error listen(int p_port, IP_Address::AddrType p_address_type, int p_recv_buffer_size=65536);
virtual void close();
virtual Error wait();
virtual bool is_listening() const;
diff --git a/platform/windows/stream_peer_winsock.cpp b/platform/windows/stream_peer_winsock.cpp
index fcf0cb1640..b511e38ecb 100644
--- a/platform/windows/stream_peer_winsock.cpp
+++ b/platform/windows/stream_peer_winsock.cpp
@@ -31,16 +31,11 @@
#include "stream_peer_winsock.h"
#include <winsock2.h>
+#include <ws2tcpip.h>
-int winsock_refcount = 0;
-
-static void set_addr_in(struct sockaddr_in& their_addr, const IP_Address& p_host, uint16_t p_port) {
+#include "drivers/unix/socket_helpers.h"
- their_addr.sin_family = AF_INET; // host byte order
- their_addr.sin_port = htons(p_port); // short, network byte order
- their_addr.sin_addr = *((struct in_addr*)&p_host.host);
- memset(&(their_addr.sin_zero), '\0', 8);
-};
+int winsock_refcount = 0;
StreamPeerTCP* StreamPeerWinsock::_create() {
@@ -92,10 +87,10 @@ Error StreamPeerWinsock::_poll_connection(bool p_block) const {
_block(sockfd, false, true);
};
- struct sockaddr_in their_addr;
- set_addr_in(their_addr, peer_host, peer_port);
+ struct sockaddr_storage their_addr;
+ size_t addr_size = _set_sockaddr(&their_addr, peer_host, peer_port);
- if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == SOCKET_ERROR) {
+ if (::connect(sockfd, (struct sockaddr *)&their_addr,addr_size) == SOCKET_ERROR) {
int err = WSAGetLastError();
if (err == WSAEISCONN) {
@@ -299,9 +294,10 @@ void StreamPeerWinsock::set_socket(int p_sockfd, IP_Address p_host, int p_port)
Error StreamPeerWinsock::connect(const IP_Address& p_host, uint16_t p_port) {
- ERR_FAIL_COND_V( p_host.host == 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V( p_host.type == IP_Address::TYPE_NONE, ERR_INVALID_PARAMETER);
- if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
+ int family = p_host.type == IP_Address::TYPE_IPV6 ? AF_INET6 : AF_INET;
+ if ((sockfd = socket(family, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
ERR_PRINT("Socket creation failed!");
disconnect();
//perror("socket");
@@ -315,10 +311,10 @@ Error StreamPeerWinsock::connect(const IP_Address& p_host, uint16_t p_port) {
return FAILED;
};
- struct sockaddr_in their_addr;
- set_addr_in(their_addr, p_host, p_port);
+ struct sockaddr_storage their_addr;
+ size_t addr_size = _set_sockaddr(&their_addr, p_host, p_port);
- if (::connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == SOCKET_ERROR) {
+ if (::connect(sockfd, (struct sockaddr *)&their_addr,addr_size) == SOCKET_ERROR) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
ERR_PRINT("Connection to remote host failed!");
diff --git a/platform/windows/tcp_server_winsock.cpp b/platform/windows/tcp_server_winsock.cpp
index dd1cf43f3b..1aedc52bb9 100644
--- a/platform/windows/tcp_server_winsock.cpp
+++ b/platform/windows/tcp_server_winsock.cpp
@@ -31,6 +31,9 @@
#include "stream_peer_winsock.h"
#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include "drivers/unix/socket_helpers.h"
extern int winsock_refcount;
@@ -60,7 +63,7 @@ void TCPServerWinsock::cleanup() {
};
-Error TCPServerWinsock::listen(uint16_t p_port,const List<String> *p_accepted_hosts) {
+Error TCPServerWinsock::listen(uint16_t p_port, IP_Address::AddrType p_type,const List<String> *p_accepted_hosts) {
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
@@ -73,11 +76,8 @@ Error TCPServerWinsock::listen(uint16_t p_port,const List<String> *p_accepted_ho
return FAILED;
};
- struct sockaddr_in my_addr;
- my_addr.sin_family = AF_INET; // host byte order
- my_addr.sin_port = htons(p_port); // short, network byte order
- my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP TODO: use p_accepted_hosts
- memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
+ struct sockaddr_storage my_addr;
+ size_t addr_size = _set_listen_sockaddr(&my_addr, p_port, p_type, p_accepted_hosts);
int reuse=1;
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) {
@@ -86,7 +86,7 @@ Error TCPServerWinsock::listen(uint16_t p_port,const List<String> *p_accepted_ho
}
- if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) != SOCKET_ERROR) {
+ if (bind(sockfd, (struct sockaddr *)&my_addr, addr_size) != SOCKET_ERROR) {
if (::listen(sockfd, SOMAXCONN) == SOCKET_ERROR) {
@@ -140,16 +140,17 @@ Ref<StreamPeerTCP> TCPServerWinsock::take_connection() {
return NULL;
};
- struct sockaddr_in their_addr;
+ struct sockaddr_storage their_addr;
int sin_size = sizeof(their_addr);
int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &sin_size);
ERR_FAIL_COND_V(fd == INVALID_SOCKET, NULL);
Ref<StreamPeerWinsock> conn = memnew(StreamPeerWinsock);
IP_Address ip;
- ip.host = (uint32_t)their_addr.sin_addr.s_addr;
+ int port;
+ _set_ip_addr_port(ip, port, &their_addr);
- conn->set_socket(fd, ip, ntohs(their_addr.sin_port));
+ conn->set_socket(fd, ip, port);
return conn;
};
diff --git a/platform/windows/tcp_server_winsock.h b/platform/windows/tcp_server_winsock.h
index bd6a05c74d..94601a2e12 100644
--- a/platform/windows/tcp_server_winsock.h
+++ b/platform/windows/tcp_server_winsock.h
@@ -39,7 +39,7 @@ class TCPServerWinsock : public TCP_Server {
public:
- virtual Error listen(uint16_t p_port,const List<String> *p_accepted_hosts=NULL);
+ virtual Error listen(uint16_t p_port, IP_Address::AddrType p_type = IP_Address::TYPE_IPV4,const List<String> *p_accepted_hosts=NULL);
virtual bool is_connection_available() const;
virtual Ref<StreamPeerTCP> take_connection();
diff --git a/platform/winrt/SCsub b/platform/winrt/SCsub
index fde0c11f3b..91a179084d 100644
--- a/platform/winrt/SCsub
+++ b/platform/winrt/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
files = [
@@ -8,7 +10,6 @@ files = [
'#platform/windows/key_mapping_win.cpp',
'joystick_winrt.cpp',
'gl_context_egl.cpp',
- 'audio_driver_winrt.cpp',
'app.cpp',
'os_winrt.cpp',
]
diff --git a/platform/winrt/detect.py b/platform/winrt/detect.py
index 79fc3651e9..a7bc62f685 100644
--- a/platform/winrt/detect.py
+++ b/platform/winrt/detect.py
@@ -31,6 +31,7 @@ def get_flags():
('tools', 'no'),
('builtin_zlib', 'yes'),
('openssl', 'builtin'),
+ ('xaudio2', 'yes'),
]
@@ -145,7 +146,6 @@ def configure(env):
env.Append(CCFLAGS=['/DGLES2_ENABLED','/DGL_GLEXT_PROTOTYPES','/DEGL_EGLEXT_PROTOTYPES','/DANGLE_ENABLED'])
LIBS = [
- 'xaudio2',
'WindowsApp',
'mincore',
'libANGLE',
diff --git a/platform/winrt/os_winrt.h b/platform/winrt/os_winrt.h
index 1816e0cec7..a4667f213d 100644
--- a/platform/winrt/os_winrt.h
+++ b/platform/winrt/os_winrt.h
@@ -40,7 +40,7 @@
#include "servers/spatial_sound/spatial_sound_server_sw.h"
#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h"
#include "servers/physics_2d/physics_2d_server_sw.h"
-#include "audio_driver_winrt.h"
+#include "drivers/xaudio2/audio_driver_xaudio2.h"
#include "gl_context_egl.h"
@@ -118,7 +118,7 @@ private:
MainLoop *main_loop;
- AudioDriverWinRT audio_driver;
+ AudioDriverXAudio2 audio_driver;
AudioServerSW *audio_server;
SampleManagerMallocSW *sample_manager;
SpatialSoundServerSW *spatial_sound_server;
diff --git a/platform/x11/SCsub b/platform/x11/SCsub
index 80fd347ded..e20bd44f8d 100644
--- a/platform/x11/SCsub
+++ b/platform/x11/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
diff --git a/platform/x11/detect.py b/platform/x11/detect.py
index 554a461d1b..e90af2c697 100644
--- a/platform/x11/detect.py
+++ b/platform/x11/detect.py
@@ -94,10 +94,6 @@ def configure(env):
env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND'])
env.extra_suffix=".llvm"
- if (env["colored"]=="yes"):
- if sys.stdout.isatty():
- env.Append(CXXFLAGS=["-fcolor-diagnostics"])
-
if (env["use_sanitizer"]=="yes"):
env.Append(CXXFLAGS=['-fsanitize=address','-fno-omit-frame-pointer'])
env.Append(LINKFLAGS=['-fsanitize=address'])
diff --git a/scene/2d/SCsub b/scene/2d/SCsub
index bbe59b3054..9fa89edbf7 100644
--- a/scene/2d/SCsub
+++ b/scene/2d/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.scene_sources,"*.cpp")
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index b5d62adfb4..bd7f4faae5 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -187,39 +187,44 @@ void RayCast2D::_notification(int p_what) {
if (!enabled)
break;
+ _update_raycast_state();
- Ref<World2D> w2d = get_world_2d();
- ERR_BREAK( w2d.is_null() );
-
- Physics2DDirectSpaceState *dss = Physics2DServer::get_singleton()->space_get_direct_state(w2d->get_space());
- ERR_BREAK( !dss );
-
- Matrix32 gt = get_global_transform();
+ } break;
+ }
+}
- Vector2 to = cast_to;
- if (to==Vector2())
- to=Vector2(0,0.01);
+void RayCast2D::_update_raycast_state() {
+ Ref<World2D> w2d = get_world_2d();
+ ERR_FAIL_COND( w2d.is_null() );
- Physics2DDirectSpaceState::RayResult rr;
+ Physics2DDirectSpaceState *dss = Physics2DServer::get_singleton()->space_get_direct_state(w2d->get_space());
+ ERR_FAIL_COND( !dss );
- if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr,exclude,layer_mask,type_mask)) {
+ Matrix32 gt = get_global_transform();
- collided=true;
- against=rr.collider_id;
- collision_point=rr.position;
- collision_normal=rr.normal;
- against_shape=rr.shape;
- } else {
- collided=false;
- }
+ Vector2 to = cast_to;
+ if (to==Vector2())
+ to=Vector2(0,0.01);
+ Physics2DDirectSpaceState::RayResult rr;
+ if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr,exclude,layer_mask,type_mask)) {
- } break;
+ collided=true;
+ against=rr.collider_id;
+ collision_point=rr.position;
+ collision_normal=rr.normal;
+ against_shape=rr.shape;
+ } else {
+ collided=false;
}
}
+void RayCast2D::force_raycast_update() {
+ _update_raycast_state();
+}
+
void RayCast2D::add_exception_rid(const RID& p_rid) {
exclude.insert(p_rid);
@@ -265,6 +270,7 @@ void RayCast2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_cast_to"),&RayCast2D::get_cast_to);
ObjectTypeDB::bind_method(_MD("is_colliding"),&RayCast2D::is_colliding);
+ ObjectTypeDB::bind_method(_MD("force_raycast_update"),&RayCast2D::force_raycast_update);
ObjectTypeDB::bind_method(_MD("get_collider"),&RayCast2D::get_collider);
ObjectTypeDB::bind_method(_MD("get_collider_shape"),&RayCast2D::get_collider_shape);
diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h
index e1caa8b63e..9bdcc2e199 100644
--- a/scene/2d/ray_cast_2d.h
+++ b/scene/2d/ray_cast_2d.h
@@ -52,6 +52,7 @@ class RayCast2D : public Node2D {
protected:
void _notification(int p_what);
+ void _update_raycast_state();
static void _bind_methods();
public:
@@ -70,6 +71,8 @@ public:
void set_exclude_parent_body(bool p_exclude_parent_body);
bool get_exclude_parent_body() const;
+ void force_raycast_update();
+
bool is_colliding() const;
Object *get_collider() const;
int get_collider_shape() const;
diff --git a/scene/3d/SCsub b/scene/3d/SCsub
index 116e641593..1205deb94a 100644
--- a/scene/3d/SCsub
+++ b/scene/3d/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp
index 1acda8d1f8..2b8df8265e 100644
--- a/scene/3d/ray_cast.cpp
+++ b/scene/3d/ray_cast.cpp
@@ -134,39 +134,44 @@ void RayCast::_notification(int p_what) {
if (!enabled)
break;
+ _update_raycast_state();
- Ref<World> w3d = get_world();
- ERR_BREAK( w3d.is_null() );
-
- PhysicsDirectSpaceState *dss = PhysicsServer::get_singleton()->space_get_direct_state(w3d->get_space());
- ERR_BREAK( !dss );
-
- Transform gt = get_global_transform();
+ } break;
+ }
+}
- Vector3 to = cast_to;
- if (to==Vector3())
- to=Vector3(0,0.01,0);
+void RayCast::_update_raycast_state(){
+ Ref<World> w3d = get_world();
+ ERR_FAIL_COND( w3d.is_null() );
- PhysicsDirectSpaceState::RayResult rr;
+ PhysicsDirectSpaceState *dss = PhysicsServer::get_singleton()->space_get_direct_state(w3d->get_space());
+ ERR_FAIL_COND( !dss );
- if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr,exclude, layer_mask, type_mask)) {
+ Transform gt = get_global_transform();
- collided=true;
- against=rr.collider_id;
- collision_point=rr.position;
- collision_normal=rr.normal;
- against_shape=rr.shape;
- } else {
- collided=false;
- }
+ Vector3 to = cast_to;
+ if (to==Vector3())
+ to=Vector3(0,0.01,0);
+ PhysicsDirectSpaceState::RayResult rr;
+ if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr,exclude, layer_mask, type_mask)) {
- } break;
+ collided=true;
+ against=rr.collider_id;
+ collision_point=rr.position;
+ collision_normal=rr.normal;
+ against_shape=rr.shape;
+ } else {
+ collided=false;
}
}
+void RayCast::force_raycast_update() {
+ _update_raycast_state();
+}
+
void RayCast::add_exception_rid(const RID& p_rid) {
exclude.insert(p_rid);
@@ -212,6 +217,7 @@ void RayCast::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_cast_to"),&RayCast::get_cast_to);
ObjectTypeDB::bind_method(_MD("is_colliding"),&RayCast::is_colliding);
+ ObjectTypeDB::bind_method(_MD("force_raycast_update"),&RayCast::force_raycast_update);
ObjectTypeDB::bind_method(_MD("get_collider"),&RayCast::get_collider);
ObjectTypeDB::bind_method(_MD("get_collider_shape"),&RayCast::get_collider_shape);
diff --git a/scene/3d/ray_cast.h b/scene/3d/ray_cast.h
index 4f6514e61b..47553f08ed 100644
--- a/scene/3d/ray_cast.h
+++ b/scene/3d/ray_cast.h
@@ -53,6 +53,7 @@ class RayCast : public Spatial {
protected:
void _notification(int p_what);
+ void _update_raycast_state();
static void _bind_methods();
public:
@@ -68,6 +69,7 @@ public:
void set_type_mask(uint32_t p_mask);
uint32_t get_type_mask() const;
+ void force_raycast_update();
bool is_colliding() const;
Object *get_collider() const;
int get_collider_shape() const;
diff --git a/scene/SCsub b/scene/SCsub
index 6d1dd0044f..79da365192 100644
--- a/scene/SCsub
+++ b/scene/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.scene_sources=[]
diff --git a/scene/animation/SCsub b/scene/animation/SCsub
index bbe59b3054..9fa89edbf7 100644
--- a/scene/animation/SCsub
+++ b/scene/animation/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.scene_sources,"*.cpp")
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 156f4956bb..cbaaeb03e5 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -206,6 +206,7 @@ void Tween::_bind_methods() {
ObjectTypeDB::bind_method(_MD("resume","object","key"),&Tween::resume, DEFVAL("") );
ObjectTypeDB::bind_method(_MD("resume_all"),&Tween::resume_all );
ObjectTypeDB::bind_method(_MD("remove","object","key"),&Tween::remove, DEFVAL("") );
+ ObjectTypeDB::bind_method(_MD("_remove","object","key","first_only"),&Tween::_remove );
ObjectTypeDB::bind_method(_MD("remove_all"),&Tween::remove_all );
ObjectTypeDB::bind_method(_MD("seek","time"),&Tween::seek );
ObjectTypeDB::bind_method(_MD("tell"),&Tween::tell );
@@ -620,7 +621,7 @@ void Tween::_tween_process(float p_delta) {
object->call(data.key, (const Variant **) arg, data.args, error);
}
if (!repeat)
- call_deferred("remove", object, data.key);
+ call_deferred("_remove", object, data.key, true);
}
continue;
}
@@ -634,7 +635,7 @@ void Tween::_tween_process(float p_delta) {
emit_signal("tween_complete",object,data.key);
// not repeat mode, remove completed action
if (!repeat)
- call_deferred("remove", object, data.key);
+ call_deferred("_remove", object, data.key, true);
}
}
pending_update --;
@@ -816,10 +817,15 @@ bool Tween::resume_all() {
}
bool Tween::remove(Object *p_object, String p_key) {
+ _remove(p_object, p_key, false);
+ return true;
+}
+
+void Tween::_remove(Object *p_object, String p_key, bool first_only) {
if(pending_update != 0) {
- call_deferred("remove", p_object, p_key);
- return true;
+ call_deferred("_remove", p_object, p_key, first_only);
+ return;
}
List<List<InterpolateData>::Element *> for_removal;
for(List<InterpolateData>::Element *E=interpolates.front();E;E=E->next()) {
@@ -830,12 +836,14 @@ bool Tween::remove(Object *p_object, String p_key) {
continue;
if(object == p_object && (data.key == p_key || p_key == "")) {
for_removal.push_back(E);
+ if (first_only) {
+ break;
+ }
}
}
for(List<List<InterpolateData>::Element *>::Element *E=for_removal.front();E;E=E->next()) {
interpolates.erase(E->get());
}
- return true;
}
bool Tween::remove_all() {
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index d0455cdc71..844a012b9f 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -143,6 +143,7 @@ private:
void _tween_process(float p_delta);
void _set_process(bool p_process,bool p_force=false);
+ void _remove(Object *p_node, String p_key, bool first_only);
protected:
diff --git a/scene/audio/SCsub b/scene/audio/SCsub
index bbe59b3054..9fa89edbf7 100644
--- a/scene/audio/SCsub
+++ b/scene/audio/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.scene_sources,"*.cpp")
diff --git a/scene/gui/SCsub b/scene/gui/SCsub
index bbe59b3054..9fa89edbf7 100644
--- a/scene/gui/SCsub
+++ b/scene/gui/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.scene_sources,"*.cpp")
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 6b43425edc..f942f15ed0 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -186,15 +186,17 @@ void FileDialog::_action_pressed() {
hide();
}else if (mode==MODE_OPEN_ANY || mode==MODE_OPEN_DIR) {
-
String path=dir_access->get_current_dir();
- /*if (tree->get_selected()) {
- Dictionary d = tree->get_selected()->get_metadata(0);
+
+ path=path.replace("\\","/");
+
+ if (TreeItem* item = tree->get_selected()) {
+ Dictionary d = item->get_metadata(0);
if (d["dir"]) {
- path=path+"/"+String(d["name"]);
+ path=path.plus_file(d["name"]);
}
- }*/
- path=path.replace("\\","/");
+ }
+
emit_signal("dir_selected",path);
hide();
}
@@ -348,18 +350,18 @@ void FileDialog::update_file_list() {
files.sort_custom<NoCaseComparator>();
while(!dirs.empty()) {
+ String& dir_name = dirs.front()->get();
+ TreeItem *ti=tree->create_item(root);
+ ti->set_text(0,dir_name+"/");
+ ti->set_icon(0,folder);
- if (dirs.front()->get()!=".") {
- TreeItem *ti=tree->create_item(root);
- ti->set_text(0,dirs.front()->get()+"/");
- ti->set_icon(0,folder);
- Dictionary d;
- d["name"]=dirs.front()->get();
- d["dir"]=true;
- ti->set_metadata(0,d);
- }
- dirs.pop_front();
+ Dictionary d;
+ d["name"]=dir_name;
+ d["dir"]=true;
+
+ ti->set_metadata(0,d);
+ dirs.pop_front();
}
dirs.clear();
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index 62829fd5a4..e6f85c5935 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -87,13 +87,13 @@ void LinkButton::_notification(int p_what) {
else
color=get_color("font_color");
- do_underline=true;
+ do_underline=underline_mode!=UNDERLINE_MODE_NEVER;
} break;
case DRAW_HOVER: {
color=get_color("font_color_hover");
- do_underline=true;
+ do_underline=underline_mode!=UNDERLINE_MODE_NEVER;
} break;
case DRAW_DISABLED: {
@@ -139,9 +139,10 @@ void LinkButton::_bind_methods() {
BIND_CONSTANT( UNDERLINE_MODE_ALWAYS );
BIND_CONSTANT( UNDERLINE_MODE_ON_HOVER );
+ BIND_CONSTANT( UNDERLINE_MODE_NEVER );
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING,"text"), _SCS("set_text"), _SCS("get_text"));
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT,"underline",PROPERTY_HINT_ENUM,"Always,On Hover"), _SCS("set_underline_mode"), _SCS("get_underline_mode"));
+ ADD_PROPERTYNZ(PropertyInfo(Variant::INT,"underline",PROPERTY_HINT_ENUM,"Always,On Hover,Never"), _SCS("set_underline_mode"), _SCS("get_underline_mode"));
}
diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h
index 9978f66cc0..a44fc2ec1b 100644
--- a/scene/gui/link_button.h
+++ b/scene/gui/link_button.h
@@ -40,7 +40,8 @@ public:
enum UnderlineMode {
UNDERLINE_MODE_ALWAYS,
- UNDERLINE_MODE_ON_HOVER
+ UNDERLINE_MODE_ON_HOVER,
+ UNDERLINE_MODE_NEVER
};
private:
String text;
diff --git a/scene/io/SCsub b/scene/io/SCsub
index bbe59b3054..9fa89edbf7 100644
--- a/scene/io/SCsub
+++ b/scene/io/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.scene_sources,"*.cpp")
diff --git a/scene/main/SCsub b/scene/main/SCsub
index bbe59b3054..9fa89edbf7 100644
--- a/scene/main/SCsub
+++ b/scene/main/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.scene_sources,"*.cpp")
diff --git a/scene/resources/SCsub b/scene/resources/SCsub
index bb9766e1ca..702ed1a9bf 100644
--- a/scene/resources/SCsub
+++ b/scene/resources/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.scene_sources,"*.cpp")
diff --git a/scene/resources/default_theme/SCsub b/scene/resources/default_theme/SCsub
index bbe59b3054..9fa89edbf7 100644
--- a/scene/resources/default_theme/SCsub
+++ b/scene/resources/default_theme/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.scene_sources,"*.cpp")
diff --git a/servers/SCsub b/servers/SCsub
index d861847101..57b5b36758 100644
--- a/servers/SCsub
+++ b/servers/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.servers_sources=[]
diff --git a/servers/audio/SCsub b/servers/audio/SCsub
index d31af2c1c4..4684bd6cf3 100644
--- a/servers/audio/SCsub
+++ b/servers/audio/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.servers_sources,"*.cpp")
diff --git a/servers/physics/SCsub b/servers/physics/SCsub
index 95296eadbe..b527f38ceb 100644
--- a/servers/physics/SCsub
+++ b/servers/physics/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.servers_sources,"*.cpp")
diff --git a/servers/physics/joints/SCsub b/servers/physics/joints/SCsub
index d31af2c1c4..4684bd6cf3 100644
--- a/servers/physics/joints/SCsub
+++ b/servers/physics/joints/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.servers_sources,"*.cpp")
diff --git a/servers/physics_2d/SCsub b/servers/physics_2d/SCsub
index ebb7f8be00..2114782faa 100644
--- a/servers/physics_2d/SCsub
+++ b/servers/physics_2d/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.servers_sources,"*.cpp")
diff --git a/servers/spatial_sound/SCsub b/servers/spatial_sound/SCsub
index d31af2c1c4..4684bd6cf3 100644
--- a/servers/spatial_sound/SCsub
+++ b/servers/spatial_sound/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.servers_sources,"*.cpp")
diff --git a/servers/spatial_sound_2d/SCsub b/servers/spatial_sound_2d/SCsub
index d31af2c1c4..4684bd6cf3 100644
--- a/servers/spatial_sound_2d/SCsub
+++ b/servers/spatial_sound_2d/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.servers_sources,"*.cpp")
diff --git a/servers/visual/SCsub b/servers/visual/SCsub
index d31af2c1c4..4684bd6cf3 100644
--- a/servers/visual/SCsub
+++ b/servers/visual/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.servers_sources,"*.cpp")
diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp
index 8d228ad859..1df0aafb23 100644
--- a/servers/visual/visual_server_raster.cpp
+++ b/servers/visual/visual_server_raster.cpp
@@ -7612,6 +7612,11 @@ void VisualServerRaster::set_default_clear_color(const Color& p_color) {
clear_color=p_color;
}
+Color VisualServerRaster::get_default_clear_color() const {
+
+ return clear_color;
+}
+
void VisualServerRaster::set_boot_image(const Image& p_image, const Color& p_color,bool p_scale) {
if (p_image.empty())
diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h
index 496820f4a8..1f22e31ab0 100644
--- a/servers/visual/visual_server_raster.h
+++ b/servers/visual/visual_server_raster.h
@@ -1283,6 +1283,7 @@ public:
virtual void set_boot_image(const Image& p_image, const Color& p_color, bool p_scale);
virtual void set_default_clear_color(const Color& p_color);
+ virtual Color get_default_clear_color() const;
VisualServerRaster(Rasterizer *p_rasterizer);
~VisualServerRaster();
diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h
index b4e374dd6f..f0fe80d100 100644
--- a/servers/visual/visual_server_wrap_mt.h
+++ b/servers/visual/visual_server_wrap_mt.h
@@ -718,6 +718,7 @@ public:
FUNC3(set_boot_image,const Image& , const Color&,bool );
FUNC1(set_default_clear_color,const Color& );
+ FUNC0RC(Color,get_default_clear_color );
FUNC0R(RID,get_test_cube );
diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp
index dfa0c91c7f..6099a86b96 100644
--- a/servers/visual_server.cpp
+++ b/servers/visual_server.cpp
@@ -564,6 +564,7 @@ void VisualServer::_bind_methods() {
ObjectTypeDB::bind_method(_MD("free_rid"),&VisualServer::free);
ObjectTypeDB::bind_method(_MD("set_default_clear_color"),&VisualServer::set_default_clear_color);
+ ObjectTypeDB::bind_method(_MD("get_default_clear_color"),&VisualServer::get_default_clear_color);
ObjectTypeDB::bind_method(_MD("get_render_info"),&VisualServer::get_render_info);
diff --git a/servers/visual_server.h b/servers/visual_server.h
index 2f3d8371f6..844da2d245 100644
--- a/servers/visual_server.h
+++ b/servers/visual_server.h
@@ -1181,6 +1181,7 @@ public:
virtual void set_boot_image(const Image& p_image, const Color& p_color,bool p_scale)=0;
virtual void set_default_clear_color(const Color& p_color)=0;
+ virtual Color get_default_clear_color() const=0;
enum Features {
FEATURE_SHADERS,
diff --git a/tools/SCsub b/tools/SCsub
index 67b1f20e72..b0b33d4f01 100644
--- a/tools/SCsub
+++ b/tools/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.tool_sources=[]
diff --git a/tools/collada/SCsub b/tools/collada/SCsub
index 34524f10ef..473474201a 100644
--- a/tools/collada/SCsub
+++ b/tools/collada/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.tool_sources,"*.cpp")
diff --git a/tools/doc/SCsub b/tools/doc/SCsub
index 34524f10ef..473474201a 100644
--- a/tools/doc/SCsub
+++ b/tools/doc/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env.add_source_files(env.tool_sources,"*.cpp")
diff --git a/tools/editor/SCsub b/tools/editor/SCsub
index 34651b36f2..f6cb16dc24 100644
--- a/tools/editor/SCsub
+++ b/tools/editor/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
diff --git a/tools/editor/editor_file_dialog.cpp b/tools/editor/editor_file_dialog.cpp
index b8abd1d32c..90289a275e 100644
--- a/tools/editor/editor_file_dialog.cpp
+++ b/tools/editor/editor_file_dialog.cpp
@@ -336,15 +336,21 @@ void EditorFileDialog::_action_pressed() {
hide();
}else if (mode==MODE_OPEN_ANY || 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("\\","/");
+
+ for(int i=0;i<item_list->get_item_count();i++) {
+ if (item_list->is_selected(i)) {
+ Dictionary d=item_list->get_item_metadata(i);
+ if (d["dir"]) {
+ path=path.plus_file(d["name"]);
+
+ break;
+ }
+ }
+ }
+
_save_to_recent();
emit_signal("dir_selected",path);
hide();
@@ -571,25 +577,26 @@ void EditorFileDialog::update_file_list() {
files.sort_custom<NoCaseComparator>();
while(!dirs.empty()) {
+ const String& dir_name=dirs.front()->get();
- if (dirs.front()->get()!=".") {
- item_list->add_item(dirs.front()->get()+"/");
- if (display_mode==DISPLAY_THUMBNAILS) {
+ item_list->add_item(dir_name+"/");
- item_list->set_item_icon(item_list->get_item_count()-1,folder_thumbnail);
- } else {
+ if (display_mode==DISPLAY_THUMBNAILS) {
- item_list->set_item_icon(item_list->get_item_count()-1,folder);
- }
+ item_list->set_item_icon(item_list->get_item_count()-1,folder_thumbnail);
+ } else {
- 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);
+ item_list->set_item_icon(item_list->get_item_count()-1,folder);
}
- dirs.pop_front();
+ Dictionary d;
+ d["name"]=dir_name;
+ d["path"]=String();
+ d["dir"]=true;
+
+ item_list->set_item_metadata( item_list->get_item_count() -1, d);
+
+ dirs.pop_front();
}
dirs.clear();
diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp
index c17cf05ea7..5a3deb7b9d 100644
--- a/tools/editor/editor_node.cpp
+++ b/tools/editor/editor_node.cpp
@@ -101,6 +101,7 @@
#include "plugins/collision_shape_2d_editor_plugin.h"
#include "main/input_default.h"
// end
+#include "tools/editor/editor_settings.h"
#include "tools/editor/io_plugins/editor_texture_import_plugin.h"
#include "tools/editor/io_plugins/editor_scene_import_plugin.h"
#include "tools/editor/io_plugins/editor_font_import_plugin.h"
@@ -1666,12 +1667,15 @@ void EditorNode::_edit_current() {
if (main_plugin) {
- if (main_plugin!=editor_plugin_screen && (!ScriptEditor::get_singleton() || !ScriptEditor::get_singleton()->is_visible() || ScriptEditor::get_singleton()->can_take_away_focus())) {
+ // special case if use of external editor is true
+ if (main_plugin->get_name() == "Script" && bool(EditorSettings::get_singleton()->get("external_editor/use_external_editor"))){
+ main_plugin->edit(current_obj);
+ }
+ else if (main_plugin!=editor_plugin_screen && (!ScriptEditor::get_singleton() || !ScriptEditor::get_singleton()->is_visible() || ScriptEditor::get_singleton()->can_take_away_focus())) {
// update screen main_plugin
if (!changing_scene) {
-
if (editor_plugin_screen)
editor_plugin_screen->make_visible(false);
editor_plugin_screen=main_plugin;
diff --git a/tools/editor/editor_settings.cpp b/tools/editor/editor_settings.cpp
index bdbf20e348..78bfd7cf25 100644
--- a/tools/editor/editor_settings.cpp
+++ b/tools/editor/editor_settings.cpp
@@ -1026,6 +1026,29 @@ void EditorSettings::set_optimize_save(bool p_optimize) {
optimize_save=p_optimize;
}
+String EditorSettings::get_last_selected_language()
+{
+ Ref<ConfigFile> cf = memnew( ConfigFile );
+ String path = get_project_settings_path().plus_file("project_metadata.cfg");
+ Error err = cf->load(path);
+ if (err != OK) {
+ return "";
+ }
+ Variant last_selected_language = cf->get_value("script_setup", "last_selected_language");
+ if (last_selected_language.get_type() != Variant::STRING)
+ return "";
+ return static_cast<String>(last_selected_language);
+}
+
+void EditorSettings::set_last_selected_language(String p_language)
+{
+ Ref<ConfigFile> cf = memnew( ConfigFile );
+ String path = get_project_settings_path().plus_file("project_metadata.cfg");
+ cf->load(path);
+ cf->set_value("script_setup", "last_selected_language", p_language);
+ cf->save(path);
+}
+
void EditorSettings::_bind_methods() {
ObjectTypeDB::bind_method(_MD("erase","property"),&EditorSettings::erase);
diff --git a/tools/editor/editor_settings.h b/tools/editor/editor_settings.h
index 2a7d3bb4f0..a976602304 100644
--- a/tools/editor/editor_settings.h
+++ b/tools/editor/editor_settings.h
@@ -160,6 +160,9 @@ public:
void set_optimize_save(bool p_optimize);
+ String get_last_selected_language();
+ void set_last_selected_language(String p_language);
+
EditorSettings();
~EditorSettings();
diff --git a/tools/editor/fileserver/SCsub b/tools/editor/fileserver/SCsub
index 363a2ce4c0..6299fd416c 100644
--- a/tools/editor/fileserver/SCsub
+++ b/tools/editor/fileserver/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Export('env')
env.add_source_files(env.tool_sources,"*.cpp")
diff --git a/tools/editor/filesystem_dock.cpp b/tools/editor/filesystem_dock.cpp
index 8a94c6e340..5b1e80fc3b 100644
--- a/tools/editor/filesystem_dock.cpp
+++ b/tools/editor/filesystem_dock.cpp
@@ -234,13 +234,6 @@ void FileSystemDock::_dir_selected() {
button_favorite->set_pressed(found);
- if (ti->get_parent() && ti->get_parent()->get_parent()==tree->get_root() && !ti->get_parent()->get_prev()) {
-
- //a favorite!!!
- } else {
-
-
- }
if (!split_mode) {
_open_pressed(); //go directly to dir
diff --git a/tools/editor/icons/SCsub b/tools/editor/icons/SCsub
index 44e28f49e6..9e05d8f391 100644
--- a/tools/editor/icons/SCsub
+++ b/tools/editor/icons/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
def make_editor_icons_action(target, source, env):
diff --git a/tools/editor/io_plugins/SCsub b/tools/editor/io_plugins/SCsub
index 363a2ce4c0..6299fd416c 100644
--- a/tools/editor/io_plugins/SCsub
+++ b/tools/editor/io_plugins/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Export('env')
env.add_source_files(env.tool_sources,"*.cpp")
diff --git a/tools/editor/plugins/SCsub b/tools/editor/plugins/SCsub
index 363a2ce4c0..6299fd416c 100644
--- a/tools/editor/plugins/SCsub
+++ b/tools/editor/plugins/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
Export('env')
env.add_source_files(env.tool_sources,"*.cpp")
diff --git a/tools/editor/plugins/canvas_item_editor_plugin.cpp b/tools/editor/plugins/canvas_item_editor_plugin.cpp
index b0e002ba44..ac39e0687c 100644
--- a/tools/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/tools/editor/plugins/canvas_item_editor_plugin.cpp
@@ -32,13 +32,20 @@
#include "os/keyboard.h"
#include "scene/main/viewport.h"
#include "scene/main/canvas_layer.h"
-#include "scene/2d/node_2d.h"
+#include "scene/2d/sprite.h"
+#include "scene/2d/light_2d.h"
+#include "scene/2d/particles_2d.h"
+#include "scene/2d/polygon_2d.h"
+#include "scene/2d/screen_button.h"
#include "globals.h"
#include "os/input.h"
#include "tools/editor/editor_settings.h"
#include "scene/gui/grid_container.h"
+#include "scene/gui/patch_9_frame.h"
#include "tools/editor/animation_editor.h"
#include "tools/editor/plugins/animation_player_editor_plugin.h"
+#include "tools/editor/script_editor_debugger.h"
+#include "tools/editor/plugins/script_editor_plugin.h"
#include "scene/resources/packed_scene.h"
@@ -3326,7 +3333,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
vp->add_child(p_editor->get_scene_root());
- viewport = memnew( Control );
+ viewport = memnew( CanvasItemEditorViewport(p_editor, this) );
vp_base->add_child(viewport);
viewport->set_area_as_parent_rect();
@@ -3642,3 +3649,397 @@ CanvasItemEditorPlugin::~CanvasItemEditorPlugin()
}
+void CanvasItemEditorViewport::_on_mouse_exit() {
+ if (selector->is_hidden()){
+ _remove_preview();
+ }
+}
+
+void CanvasItemEditorViewport::_on_select_type(Object* selected) {
+ CheckBox* check = selected->cast_to<CheckBox>();
+ String type = check->get_text();
+ selector_label->set_text(vformat(TTR("Add %s"),type));
+ label->set_text(vformat(TTR("Adding %s..."),type));
+}
+
+void CanvasItemEditorViewport::_on_change_type() {
+ CheckBox* check=btn_group->get_pressed_button()->cast_to<CheckBox>();
+ default_type=check->get_text();
+ _perform_drop_data();
+ selector->hide();
+}
+
+void CanvasItemEditorViewport::_create_preview(const Vector<String>& files) const {
+ label->set_pos(get_global_pos()+Point2(14,14));
+ label_desc->set_pos(label->get_pos()+Point2(0,label->get_size().height));
+ for (int i=0;i<files.size();i++) {
+ String path=files[i];
+ RES res=ResourceLoader::load(path);
+ String type=res->get_type();
+ if (type=="ImageTexture" || type=="PackedScene") {
+ if (type=="ImageTexture") {
+ Ref<ImageTexture> texture=Ref<ImageTexture> ( ResourceCache::get(path)->cast_to<ImageTexture>() );
+ Sprite* sprite=memnew(Sprite);
+ sprite->set_texture(texture);
+ sprite->set_opacity(0.7f);
+ preview->add_child(sprite);
+ label->show();
+ label_desc->show();
+ } else if (type=="PackedScene") {
+ Ref<PackedScene> scn=ResourceLoader::load(path);
+ if (scn.is_valid()){
+ Node* instance=scn->instance();
+ if (instance){
+ preview->add_child(instance);
+ }
+ }
+ }
+ editor->get_scene_root()->add_child(preview);
+ }
+ }
+}
+
+void CanvasItemEditorViewport::_remove_preview() {
+ if (preview->get_parent()){
+ editor->get_scene_root()->remove_child(preview);
+ for (int i=preview->get_child_count()-1;i>=0;i--){
+ Node* node=preview->get_child(i);
+ memdelete(node);
+ }
+ label->hide();
+ label_desc->hide();
+ }
+}
+
+bool CanvasItemEditorViewport::_cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node) {
+ if (p_desired_node->get_filename()==p_target_scene_path) {
+ return true;
+ }
+
+ int childCount=p_desired_node->get_child_count();
+ 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;
+}
+
+void CanvasItemEditorViewport::_create_nodes(Node* parent, Node* child, String& path, const Point2& p_point) {
+ child->set_name(path.get_file().basename());
+ Ref<ImageTexture> texture=Ref<ImageTexture> ( ResourceCache::get(path)->cast_to<ImageTexture>() );
+ Size2 texture_size = texture->get_size();
+
+ editor_data->get_undo_redo().add_do_method(parent,"add_child",child);
+ editor_data->get_undo_redo().add_do_method(child,"set_owner",editor->get_edited_scene());
+ 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);
+ ScriptEditorDebugger *sed=ScriptEditor::get_singleton()->get_debugger();
+ editor_data->get_undo_redo().add_do_method(sed,"live_debug_create_node",editor->get_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(editor->get_edited_scene()->get_path_to(parent))+"/"+new_name));
+
+ // handle with different property for texture
+ String property = "texture";
+ List<PropertyInfo> props;
+ child->get_property_list(&props);
+ for(const List<PropertyInfo>::Element *E=props.front();E;E=E->next() ) {
+ if (E->get().name=="config/texture") { // Particles2D
+ property="config/texture";
+ break;
+ } else if (E->get().name=="texture/texture") { // Polygon2D
+ property="texture/texture";
+ break;
+ } else if (E->get().name=="normal") { // TouchScreenButton
+ property="normal";
+ break;
+ }
+ }
+ editor_data->get_undo_redo().add_do_property(child,property,texture);
+
+ // make visible for certain node type
+ if (default_type=="Patch9Frame") {
+ editor_data->get_undo_redo().add_do_property(child,"rect/size",texture_size);
+ } else if (default_type=="Polygon2D") {
+ DVector<Vector2> list;
+ list.push_back(Vector2(0,0));
+ list.push_back(Vector2(texture_size.width,0));
+ list.push_back(Vector2(texture_size.width,texture_size.height));
+ list.push_back(Vector2(0,texture_size.height));
+ editor_data->get_undo_redo().add_do_property(child,"polygon",list);
+ }
+
+ // locate at preview position
+ Point2 pos;
+ if (parent->has_method("get_global_pos")) {
+ pos=parent->call("get_global_pos");
+ }
+ Matrix32 trans=canvas->get_canvas_transform();
+ Point2 target_pos = (p_point-trans.get_origin())/trans.get_scale().x-pos;
+ if (default_type=="Polygon2D" || default_type=="TouchScreenButton" || default_type=="TextureFrame" || default_type=="Patch9Frame") {
+ target_pos -= texture_size/2;
+ }
+ editor_data->get_undo_redo().add_do_method(child,"set_pos",target_pos);
+}
+
+bool CanvasItemEditorViewport::_create_instance(Node* parent, String& path, const Point2& p_point) {
+ Ref<PackedScene> sdata=ResourceLoader::load(path);
+ if (!sdata.is_valid()) { // invalid scene
+ return false;
+ }
+
+ Node* instanced_scene=sdata->instance(true);
+ if (!instanced_scene) { // error on instancing
+ return false;
+ }
+
+ if (editor->get_edited_scene()->get_filename()!="") { // cyclical instancing
+ if (_cyclical_dependency_exists(editor->get_edited_scene()->get_filename(), instanced_scene)) {
+ return false;
+ }
+ }
+
+ instanced_scene->set_filename( Globals::get_singleton()->localize_path(path) );
+
+ editor_data->get_undo_redo().add_do_method(parent,"add_child",instanced_scene);
+ editor_data->get_undo_redo().add_do_method(instanced_scene,"set_owner",editor->get_edited_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);
+ ScriptEditorDebugger *sed=ScriptEditor::get_singleton()->get_debugger();
+ editor_data->get_undo_redo().add_do_method(sed,"live_debug_instance_node",editor->get_edited_scene()->get_path_to(parent),path,new_name);
+ editor_data->get_undo_redo().add_undo_method(sed,"live_debug_remove_node",NodePath(String(editor->get_edited_scene()->get_path_to(parent))+"/"+new_name));
+
+ Point2 pos;
+ Node2D* parent_node2d=parent->cast_to<Node2D>();
+ if (parent_node2d) {
+ pos=parent_node2d->get_global_pos();
+ } else {
+ Control* parent_control=parent->cast_to<Control>();
+ if (parent_control) {
+ pos=parent_control->get_global_pos();
+ }
+ }
+ Matrix32 trans=canvas->get_canvas_transform();
+ editor_data->get_undo_redo().add_do_method(instanced_scene,"set_pos",(p_point-trans.get_origin())/trans.get_scale().x-pos);
+
+ return true;
+}
+
+void CanvasItemEditorViewport::_perform_drop_data(){
+ _remove_preview();
+
+ Vector<String> error_files;
+
+ editor_data->get_undo_redo().create_action(TTR("Create Node"));
+
+ for (int i=0;i<selected_files.size();i++) {
+ String path=selected_files[i];
+ RES res=ResourceLoader::load(path);
+ if (res.is_null()) {
+ continue;
+ }
+ String type=res->get_type();
+ if (type=="ImageTexture") {
+ Node* child;
+ if (default_type=="Light2D") child=memnew(Light2D);
+ else if (default_type=="Particles2D") child=memnew(Particles2D);
+ else if (default_type=="Polygon2D") child=memnew(Polygon2D);
+ else if (default_type=="TouchScreenButton") child=memnew(TouchScreenButton);
+ else if (default_type=="TextureFrame") child=memnew(TextureFrame);
+ else if (default_type=="Patch9Frame") child=memnew(Patch9Frame);
+ else child=memnew(Sprite); // default
+
+ _create_nodes(target_node, child, path, drop_pos);
+ } else if (type=="PackedScene") {
+ bool success=_create_instance(target_node, path, drop_pos);
+ if (!success) {
+ error_files.push_back(path);
+ }
+ }
+ }
+
+ editor_data->get_undo_redo().commit_action();
+
+ if (error_files.size()>0) {
+ String files_str;
+ for (int i=0;i<error_files.size();i++) {
+ files_str += error_files[i].get_file().basename() + ",";
+ }
+ files_str=files_str.substr(0,files_str.length()-1);
+ accept->get_ok()->set_text(TTR("Ugh"));
+ accept->set_text(vformat(TTR("Error instancing scene from %s"),files_str.c_str()));
+ accept->popup_centered_minsize();
+ }
+}
+
+bool CanvasItemEditorViewport::can_drop_data(const Point2& p_point,const Variant& p_data) const {
+ Dictionary d=p_data;
+ if (d.has("type")) {
+ if (String(d["type"])=="files") {
+ Vector<String> files=d["files"];
+ bool can_instance=false;
+ for (int i=0;i<files.size();i++) { // check if dragged files contain resource or scene can be created at least one
+ RES res=ResourceLoader::load(files[i]);
+ if (res.is_null()) {
+ continue;
+ }
+ String type=res->get_type();
+ if (type=="PackedScene") {
+ Ref<PackedScene> sdata=ResourceLoader::load(files[i]);
+ Node* instanced_scene=sdata->instance(true);
+ if (!instanced_scene) {
+ continue;
+ }
+ memdelete(instanced_scene);
+ }
+ can_instance=true;
+ break;
+ }
+ if (can_instance) {
+ if (!preview->get_parent()){ // create preview only once
+ _create_preview(files);
+ }
+ Matrix32 trans=canvas->get_canvas_transform();
+ preview->set_pos((p_point-trans.get_origin())/trans.get_scale().x);
+ label->set_text(vformat(TTR("Adding %s..."),default_type));
+ }
+ return can_instance;
+ }
+ }
+ label->hide();
+ return false;
+}
+
+void CanvasItemEditorViewport::drop_data(const Point2& p_point,const Variant& p_data) {
+ bool is_shift=Input::get_singleton()->is_key_pressed(KEY_SHIFT);
+ bool is_alt=Input::get_singleton()->is_key_pressed(KEY_ALT);
+
+ selected_files.clear();
+ Dictionary d=p_data;
+ if (d.has("type") && String(d["type"])=="files"){
+ selected_files=d["files"];
+ }
+
+ List<Node*> list=editor->get_editor_selection()->get_selected_node_list();
+ if (list.size()==0) {
+ accept->get_ok()->set_text(TTR("OK :("));
+ accept->set_text(TTR("No parent to instance a child at."));
+ accept->popup_centered_minsize();
+ _remove_preview();
+ return;
+ }
+ if (list.size()!=1) {
+ accept->get_ok()->set_text(TTR("I see.."));
+ accept->set_text(TTR("This operation requires a single selected node."));
+ accept->popup_centered_minsize();
+ _remove_preview();
+ return;
+ }
+
+ target_node=list[0];
+ if (is_shift && target_node!=editor->get_edited_scene()) {
+ target_node=target_node->get_parent();
+ }
+ drop_pos=p_point;
+
+ if (is_alt) {
+ List<BaseButton*> btn_list;
+ btn_group->get_button_list(&btn_list);
+ for (int i=0;i<btn_list.size();i++) {
+ CheckBox* check=btn_list[i]->cast_to<CheckBox>();
+ check->set_pressed(check->get_text()==default_type);
+ }
+ selector_label->set_text(vformat(TTR("Add %s"),default_type));
+ selector->popup_centered_minsize();
+ } else {
+ _perform_drop_data();
+ }
+}
+
+void CanvasItemEditorViewport::_notification(int p_what) {
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+ connect("mouse_exit",this,"_on_mouse_exit");
+ } else if (p_what==NOTIFICATION_EXIT_TREE) {
+ disconnect("mouse_exit",this,"_on_mouse_exit");
+ }
+}
+
+void CanvasItemEditorViewport::_bind_methods() {
+ ObjectTypeDB::bind_method(_MD("_on_select_type"),&CanvasItemEditorViewport::_on_select_type);
+ ObjectTypeDB::bind_method(_MD("_on_change_type"),&CanvasItemEditorViewport::_on_change_type);
+ ObjectTypeDB::bind_method(_MD("_on_mouse_exit"),&CanvasItemEditorViewport::_on_mouse_exit);
+}
+
+CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor* p_canvas) {
+ default_type="Sprite";
+ // Node2D
+ types.push_back("Sprite");
+ types.push_back("Light2D");
+ types.push_back("Particles2D");
+ types.push_back("Polygon2D");
+ types.push_back("TouchScreenButton");
+ // Control
+ types.push_back("TextureFrame");
+ types.push_back("Patch9Frame");
+
+ target_node=NULL;
+ editor=p_node;
+ editor_data=editor->get_scene_tree_dock()->get_editor_data();
+ canvas=p_canvas;
+ preview=memnew( Node2D );
+ accept=memnew( AcceptDialog );
+ editor->get_gui_base()->add_child(accept);
+
+ selector=memnew( WindowDialog );
+ selector->set_title(TTR("Change default type"));
+
+ VBoxContainer* vbc=memnew(VBoxContainer);
+ vbc->add_constant_override("separation",10*EDSCALE);
+ vbc->set_custom_minimum_size(Size2(200,260)*EDSCALE);
+
+ selector_label=memnew(Label);
+ selector_label->set_align(Label::ALIGN_CENTER);
+ selector_label->set_valign(Label::VALIGN_BOTTOM);
+ selector_label->set_custom_minimum_size(Size2(0,30)*EDSCALE);
+ vbc->add_child(selector_label);
+
+ btn_group=memnew( ButtonGroup );
+ btn_group->set_h_size_flags(0);
+ btn_group->connect("button_selected", this, "_on_select_type");
+
+ for (int i=0;i<types.size();i++) {
+ CheckBox* check=memnew(CheckBox);
+ check->set_text(types[i]);
+ btn_group->add_child(check);
+ }
+ vbc->add_child(btn_group);
+
+ Button* ok=memnew(Button);
+ ok->set_text(TTR("OK"));
+ ok->set_h_size_flags(0);
+ vbc->add_child(ok);
+ ok->connect("pressed", this, "_on_change_type");
+
+ selector->add_child(vbc);
+ editor->get_gui_base()->add_child(selector);
+
+ label=memnew(Label);
+ label->add_color_override("font_color", Color(1,1,0,1));
+ label->add_color_override("font_color_shadow", Color(0,0,0,1));
+ label->add_constant_override("shadow_as_outline", 1*EDSCALE);
+ label->hide();
+ editor->get_gui_base()->add_child(label);
+
+ label_desc=memnew(Label);
+ label_desc->set_text(TTR("Drag & drop + Shift : Add node as sibling\nDrag & drop + Alt : Change node type"));
+ label_desc->add_color_override("font_color", Color(0.6,0.6,0.6,1));
+ label_desc->add_color_override("font_color_shadow", Color(0.2,0.2,0.2,1));
+ label_desc->add_constant_override("shadow_as_outline", 1*EDSCALE);
+ label_desc->add_constant_override("line_spacing", 0);
+ label_desc->hide();
+ editor->get_gui_base()->add_child(label_desc);
+}
diff --git a/tools/editor/plugins/canvas_item_editor_plugin.h b/tools/editor/plugins/canvas_item_editor_plugin.h
index 9f4bc46eb4..bbec078e02 100644
--- a/tools/editor/plugins/canvas_item_editor_plugin.h
+++ b/tools/editor/plugins/canvas_item_editor_plugin.h
@@ -31,17 +31,18 @@
#include "tools/editor/editor_plugin.h"
#include "tools/editor/editor_node.h"
+#include "scene/gui/button_group.h"
+#include "scene/gui/check_box.h"
+#include "scene/gui/label.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/box_container.h"
#include "scene/2d/canvas_item.h"
-
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
-
-
+class CanvasItemEditorViewport;
class CanvasItemEditorSelectedItem : public Object {
@@ -453,4 +454,49 @@ public:
};
+class CanvasItemEditorViewport : public VBoxContainer {
+ OBJ_TYPE( CanvasItemEditorViewport, VBoxContainer );
+
+ String default_type;
+ Vector<String> types;
+
+ Vector<String> selected_files;
+ Node* target_node;
+ Point2 drop_pos;
+
+ EditorNode* editor;
+ EditorData* editor_data;
+ CanvasItemEditor* canvas;
+ Node2D* preview;
+ AcceptDialog* accept;
+ WindowDialog* selector;
+ Label* selector_label;
+ Label* label;
+ Label* label_desc;
+ ButtonGroup* btn_group;
+
+ void _on_mouse_exit();
+ void _on_select_type(Object* selected);
+ void _on_change_type();
+
+ void _create_preview(const Vector<String>& files) const;
+ void _remove_preview();
+
+ bool _cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node);
+ void _create_nodes(Node* parent, Node* child, String& path, const Point2& p_point);
+ bool _create_instance(Node* parent, String& path, const Point2& p_point);
+ void _perform_drop_data();
+
+ static void _bind_methods();
+
+protected:
+ void _notification(int p_what);
+
+public:
+ virtual bool can_drop_data(const Point2& p_point,const Variant& p_data) const;
+ virtual void drop_data(const Point2& p_point,const Variant& p_data);
+
+ CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor* p_canvas);
+};
+
#endif
diff --git a/tools/editor/plugins/script_editor_plugin.cpp b/tools/editor/plugins/script_editor_plugin.cpp
index 3cd6d8336a..b6ce3e89eb 100644
--- a/tools/editor/plugins/script_editor_plugin.cpp
+++ b/tools/editor/plugins/script_editor_plugin.cpp
@@ -1620,6 +1620,7 @@ void ScriptEditor::apply_scripts() const {
void ScriptEditor::_editor_play() {
debugger->start();
+ debug_menu->get_popup()->grab_focus();
debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true );
debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_STEP), true );
debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_BREAK), false );
@@ -1908,19 +1909,14 @@ void ScriptEditor::_update_selected_editor_menu() {
se->get_edit_menu()->hide();
}
- EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>();
-
- if (eh) {
-
- if (current)
- script_search_menu->show();
- else
- script_search_menu->hide();
- }
-
-
}
+ EditorHelp *eh=tab_container->get_current_tab_control()->cast_to<EditorHelp>();
+ if (eh) {
+ script_search_menu->show();
+ } else {
+ script_search_menu->hide();
+ }
}
void ScriptEditor::_update_history_pos(int p_new_pos) {
diff --git a/tools/editor/scene_tree_dock.cpp b/tools/editor/scene_tree_dock.cpp
index 56f10ff7f8..16f06c7ac9 100644
--- a/tools/editor/scene_tree_dock.cpp
+++ b/tools/editor/scene_tree_dock.cpp
@@ -81,7 +81,10 @@ void SceneTreeDock::_unhandled_key_input(InputEvent p_event) {
_tool_selected(TOOL_DUPLICATE);
}
else if (ED_IS_SHORTCUT("scene_tree/add_script", p_event)) {
- _tool_selected(TOOL_SCRIPT);
+ _tool_selected(TOOL_CREATE_SCRIPT);
+ }
+ else if (ED_IS_SHORTCUT("scene_tree/load_script", p_event)) {
+ _tool_selected(TOOL_LOAD_SCRIPT);
}
else if (ED_IS_SHORTCUT("scene_tree/move_up", p_event)) {
_tool_selected(TOOL_MOVE_UP);
@@ -266,6 +269,24 @@ void SceneTreeDock::_replace_with_branch_scene(const String& p_file,Node* base)
scene_tree->set_selected(instanced_scene);
}
+
+void SceneTreeDock::_file_selected(String p_file) {
+ RES p_script = ResourceLoader::load(p_file, "Script");
+ if (p_script.is_null()) {
+ accept->get_ok()->set_text(TTR("Ugh"));
+ accept->set_text(vformat(TTR("Error loading script from %s"), p_file));
+ accept->popup_centered_minsize();
+ return;
+ }
+
+ Node *selected = scene_tree->get_selected();
+ if (!selected)
+ return;
+ selected->set_script(p_script.get_ref_ptr());
+ editor->push_item(p_script.operator->());
+ file_dialog->hide();
+}
+
bool SceneTreeDock::_cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node) {
int childCount = p_desired_node->get_child_count();
@@ -358,7 +379,22 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
//groups_editor->set_current(current);
//groups_editor->popup_centered_ratio();
} break;
- case TOOL_SCRIPT: {
+ case TOOL_LOAD_SCRIPT: {
+ Node *selected = scene_tree->get_selected();
+ if (!selected)
+ break;
+
+ file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+
+ List<String> extensions;
+ ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
+ file_dialog->clear_filters();
+ for (List<String>::Element *E = extensions.front(); E; E = E->next())
+ file_dialog->add_filter("*." + E->get() + " ; " + E->get().to_upper());
+
+ file_dialog->popup_centered_ratio();
+ } break;
+ case TOOL_CREATE_SCRIPT: {
Node *selected = scene_tree->get_selected();
if (!selected)
@@ -672,6 +708,7 @@ void SceneTreeDock::_notification(int p_what) {
button_add->set_icon(get_icon("Add","EditorIcons"));
button_instance->set_icon(get_icon("Instance","EditorIcons"));
button_create_script->set_icon(get_icon("Script","EditorIcons"));
+ button_load_script->set_icon(get_icon("Script", "EditorIcons"));
filter_icon->set_texture(get_icon("Zoom","EditorIcons"));
@@ -1303,8 +1340,10 @@ void SceneTreeDock::_selection_changed() {
if (selection_size==1 && EditorNode::get_singleton()->get_editor_selection()->get_selection().front()->key()->get_script().is_null()) {
button_create_script->show();
+ button_load_script->show();
} else {
button_create_script->hide();
+ button_load_script->hide();
}
//tool_buttons[TOOL_MULTI_EDIT]->set_disabled(EditorNode::get_singleton()->get_editor_selection()->get_selection().size()<2);
@@ -1721,6 +1760,15 @@ void SceneTreeDock::_files_dropped(Vector<String> p_files,NodePath p_to,int p_ty
_perform_instance_scenes(p_files,node,to_pos);
}
+void SceneTreeDock::_script_dropped(String p_file, NodePath p_to) {
+ Ref<Script> scr = ResourceLoader::load(p_file);
+ ERR_FAIL_COND(!scr.is_valid());
+ Node *n = get_node(p_to);
+ if (n) {
+ n->set_script(scr.get_ref_ptr());
+ }
+}
+
void SceneTreeDock::_nodes_dragged(Array p_nodes,NodePath p_to,int p_type) {
Vector<Node*> nodes;
@@ -1775,7 +1823,8 @@ void SceneTreeDock::_tree_rmb(const Vector2& p_menu_pos) {
//menu->add_icon_item(get_icon("Groups","EditorIcons"),TTR("Edit Groups"),TOOL_GROUP);
//menu->add_icon_item(get_icon("Connect","EditorIcons"),TTR("Edit Connections"),TOOL_CONNECT);
menu->add_separator();
- menu->add_icon_shortcut(get_icon("Script","EditorIcons"),ED_GET_SHORTCUT("scene_tree/add_script"), TOOL_SCRIPT);
+ menu->add_icon_shortcut(get_icon("Script", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/add_script"), TOOL_CREATE_SCRIPT);
+ menu->add_icon_shortcut(get_icon("Script", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/load_script"), TOOL_LOAD_SCRIPT);
menu->add_separator();
}
@@ -1834,7 +1883,7 @@ void SceneTreeDock::_focus_node() {
void SceneTreeDock::open_script_dialog(Node* p_for_node) {
scene_tree->set_selected(p_for_node,false);
- _tool_selected(TOOL_SCRIPT);
+ _tool_selected(TOOL_CREATE_SCRIPT);
}
void SceneTreeDock::_bind_methods() {
@@ -1859,9 +1908,11 @@ void SceneTreeDock::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_new_scene_from"),&SceneTreeDock::_new_scene_from);
ObjectTypeDB::bind_method(_MD("_nodes_dragged"),&SceneTreeDock::_nodes_dragged);
ObjectTypeDB::bind_method(_MD("_files_dropped"),&SceneTreeDock::_files_dropped);
+ ObjectTypeDB::bind_method(_MD("_script_dropped"),&SceneTreeDock::_script_dropped);
ObjectTypeDB::bind_method(_MD("_tree_rmb"),&SceneTreeDock::_tree_rmb);
ObjectTypeDB::bind_method(_MD("_filter_changed"),&SceneTreeDock::_filter_changed);
ObjectTypeDB::bind_method(_MD("_focus_node"),&SceneTreeDock::_focus_node);
+ ObjectTypeDB::bind_method(_MD("_file_selected"), &SceneTreeDock::_file_selected);
ObjectTypeDB::bind_method(_MD("instance"),&SceneTreeDock::instance);
@@ -1886,6 +1937,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec
ED_SHORTCUT("scene_tree/instance_scene",TTR("Instance Child Scene"));
ED_SHORTCUT("scene_tree/change_node_type", TTR("Change Type"));
ED_SHORTCUT("scene_tree/add_script", TTR("Add Script"));
+ ED_SHORTCUT("scene_tree/load_script", TTR("Load Script"));
ED_SHORTCUT("scene_tree/move_up", TTR("Move Up"), KEY_MASK_CMD | KEY_UP);
ED_SHORTCUT("scene_tree/move_down", TTR("Move Down"), KEY_MASK_CMD | KEY_DOWN);
ED_SHORTCUT("scene_tree/duplicate", TTR("Duplicate"),KEY_MASK_CMD | KEY_D);
@@ -1922,12 +1974,19 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec
tb = memnew( ToolButton );
- tb->connect("pressed",this,"_tool_selected",make_binds(TOOL_SCRIPT, false));
+ tb->connect("pressed",this,"_tool_selected",make_binds(TOOL_CREATE_SCRIPT, false));
tb->set_tooltip(TTR("Create a new script for the selected node."));
tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/add_script"));
filter_hbc->add_child(tb);
button_create_script=tb;
+ tb = memnew(ToolButton);
+ tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_LOAD_SCRIPT, false));
+ tb->set_tooltip(TTR("Load a script for the selected node."));
+ tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/load_script"));
+ filter_hbc->add_child(tb);
+ button_load_script = tb;
+
scene_tree = memnew( SceneTreeEditor(false,true,true ));
vbc->add_child(scene_tree);
@@ -1941,6 +2000,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec
scene_tree->connect("open_script",this,"_script_open_request");
scene_tree->connect("nodes_rearranged",this,"_nodes_dragged");
scene_tree->connect("files_dropped",this,"_files_dropped");
+ scene_tree->connect("script_dropped",this,"_script_dropped");
scene_tree->connect("nodes_dragged",this,"_nodes_drag_begin");
scene_tree->get_scene_tree()->connect("item_double_clicked", this, "_focus_node");
@@ -1955,6 +2015,11 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec
add_child(create_dialog);
create_dialog->connect("create",this,"_create");
+ file_dialog = memnew(EditorFileDialog);
+ add_child(file_dialog);
+ file_dialog->hide();
+ file_dialog->connect("file_selected", this, "_file_selected");
+
//groups_editor = memnew( GroupsEditor );
//add_child(groups_editor);
//groups_editor->set_undo_redo(&editor_data->get_undo_redo());
diff --git a/tools/editor/scene_tree_dock.h b/tools/editor/scene_tree_dock.h
index 8933a03883..36d4a6d208 100644
--- a/tools/editor/scene_tree_dock.h
+++ b/tools/editor/scene_tree_dock.h
@@ -58,7 +58,8 @@ class SceneTreeDock : public VBoxContainer {
TOOL_REPLACE,
TOOL_CONNECT,
TOOL_GROUP,
- TOOL_SCRIPT,
+ TOOL_CREATE_SCRIPT,
+ TOOL_LOAD_SCRIPT,
TOOL_MOVE_UP,
TOOL_MOVE_DOWN,
TOOL_DUPLICATE,
@@ -75,10 +76,12 @@ class SceneTreeDock : public VBoxContainer {
int current_option;
CreateDialog *create_dialog;
+ EditorFileDialog *file_dialog;
ToolButton *button_add;
ToolButton *button_instance;
ToolButton *button_create_script;
+ ToolButton *button_load_script;
SceneTreeEditor *scene_tree;
@@ -147,6 +150,7 @@ class SceneTreeDock : public VBoxContainer {
void _nodes_dragged(Array p_nodes,NodePath p_to,int p_type);
void _files_dropped(Vector<String> p_files,NodePath p_to,int p_type);
+ void _script_dropped(String p_file, NodePath p_to);
void _tree_rmb(const Vector2& p_menu_pos);
@@ -155,6 +159,7 @@ class SceneTreeDock : public VBoxContainer {
void _perform_instance_scenes(const Vector<String>& p_files,Node* parent,int p_pos);
void _replace_with_branch_scene(const String& p_file,Node* base);
+ void _file_selected(String p_file);
protected:
void _notification(int p_what);
@@ -174,7 +179,7 @@ public:
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; }
-
+ EditorData *get_editor_data() { return editor_data; }
void open_script_dialog(Node* p_for_node);
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 53bfe8cc57..f5628d0c8f 100644
--- a/tools/editor/scene_tree_editor.cpp
+++ b/tools/editor/scene_tree_editor.cpp
@@ -971,6 +971,10 @@ Variant SceneTreeEditor::get_drag_data_fw(const Point2& p_point,Control* p_from)
return drag_data;
}
+bool SceneTreeEditor::_is_script_type(const StringName &p_type) const {
+ return (script_types->find(p_type));
+}
+
bool SceneTreeEditor::can_drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) const {
if (!can_rename)
@@ -998,9 +1002,13 @@ bool SceneTreeEditor::can_drop_data_fw(const Point2& p_point,const Variant& p_da
if (files.size()==0)
return false; //weird
+ if (_is_script_type(EditorFileSystem::get_singleton()->get_file_type(files[0]))) {
+ tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM);
+ return true;
+ }
for(int i=0;i<files.size();i++) {
- String file = files[0];
+ String file = files[i];
String ftype = EditorFileSystem::get_singleton()->get_file_type(file);
if (ftype!="PackedScene")
return false;
@@ -1044,7 +1052,15 @@ void SceneTreeEditor::drop_data_fw(const Point2& p_point,const Variant& p_data,C
if (String(d["type"])=="files") {
- emit_signal("files_dropped",d["files"],np,section);
+ Vector<String> files = d["files"];
+
+
+ String ftype = EditorFileSystem::get_singleton()->get_file_type(files[0]);
+ if (_is_script_type(ftype)) {
+ emit_signal("script_dropped", files[0],np);
+ } else {
+ emit_signal("files_dropped",files,np,section);
+ }
}
}
@@ -1113,6 +1129,7 @@ void SceneTreeEditor::_bind_methods() {
ADD_SIGNAL( MethodInfo("nodes_dragged") );
ADD_SIGNAL( MethodInfo("nodes_rearranged",PropertyInfo(Variant::ARRAY,"paths"),PropertyInfo(Variant::NODE_PATH,"to_path"),PropertyInfo(Variant::INT,"type") ) );
ADD_SIGNAL( MethodInfo("files_dropped",PropertyInfo(Variant::STRING_ARRAY,"files"),PropertyInfo(Variant::NODE_PATH,"to_path"),PropertyInfo(Variant::INT,"type") ) );
+ ADD_SIGNAL( MethodInfo("script_dropped",PropertyInfo(Variant::STRING,"file"),PropertyInfo(Variant::NODE_PATH,"to_path")));
ADD_SIGNAL( MethodInfo("rmb_pressed",PropertyInfo(Variant::VECTOR2,"pos")) ) ;
ADD_SIGNAL( MethodInfo("open") );
@@ -1209,12 +1226,16 @@ SceneTreeEditor::SceneTreeEditor(bool p_label,bool p_can_rename, bool p_can_open
update_timer->set_wait_time(0.5);
add_child(update_timer);
+ script_types = memnew(List<StringName>);
+ ObjectTypeDB::get_inheriters_from("Script", script_types);
+
}
SceneTreeEditor::~SceneTreeEditor() {
+ memdelete(script_types);
}
diff --git a/tools/editor/scene_tree_editor.h b/tools/editor/scene_tree_editor.h
index 79b7a64468..12d85ecdeb 100644
--- a/tools/editor/scene_tree_editor.h
+++ b/tools/editor/scene_tree_editor.h
@@ -137,6 +137,9 @@ class SceneTreeEditor : public Control {
Timer* update_timer;
+ List<StringName> *script_types;
+ bool _is_script_type(const StringName &p_type) const;
+
public:
void set_filter(const String& p_filter);
diff --git a/tools/editor/script_create_dialog.cpp b/tools/editor/script_create_dialog.cpp
index 749198314a..62d5c7cd84 100644
--- a/tools/editor/script_create_dialog.cpp
+++ b/tools/editor/script_create_dialog.cpp
@@ -121,6 +121,8 @@ void ScriptCreateDialog::ok_pressed() {
Ref<Script> scr = ScriptServer::get_language( language_menu->get_selected() )->get_template(cname,parent_name->get_text());
//scr->set_source_code(text);
+ String selected_language = language_menu->get_item_text(language_menu->get_selected());
+ editor_settings->set_last_selected_language(selected_language);
if (cname!="")
scr->set_name(cname);
@@ -330,7 +332,17 @@ ScriptCreateDialog::ScriptCreateDialog() {
language_menu->add_item(ScriptServer::get_language(i)->get_name());
}
- language_menu->select(0);
+ editor_settings = EditorSettings::get_singleton();
+ String last_selected_language = editor_settings->get_last_selected_language();
+ if (last_selected_language != "")
+ for (int i = 0; i < language_menu->get_item_count(); i++)
+ if (language_menu->get_item_text(i) == last_selected_language)
+ {
+ language_menu->select(i);
+ break;
+ }
+ else language_menu->select(0);
+
language_menu->connect("item_selected",this,"_lang_changed");
//parent_name->set_text();
diff --git a/tools/editor/script_create_dialog.h b/tools/editor/script_create_dialog.h
index 181989402e..c71ea16d39 100644
--- a/tools/editor/script_create_dialog.h
+++ b/tools/editor/script_create_dialog.h
@@ -33,6 +33,7 @@
#include "scene/gui/line_edit.h"
#include "scene/gui/option_button.h"
#include "tools/editor/editor_file_dialog.h"
+#include "tools/editor/editor_settings.h"
#include "scene/gui/check_button.h"
class ScriptCreateDialog : public ConfirmationDialog {
@@ -50,6 +51,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
AcceptDialog *alert;
bool path_valid;
String initial_bp;
+ EditorSettings *editor_settings;
void _path_changed(const String& p_path=String());
diff --git a/tools/scripts/make_glwrapper.py b/tools/scripts/make_glwrapper.py
index f3f8d39837..b4c582f1eb 100644
--- a/tools/scripts/make_glwrapper.py
+++ b/tools/scripts/make_glwrapper.py
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#! /usr/bin/env python
import sys
if (len(sys.argv)<2):