diff options
373 files changed, 23879 insertions, 2201 deletions
@@ -11,7 +11,7 @@ Godot has been developed by Juan Linietsky and Ariel Manzur for several years, a ### Documentation -Documentation has been moved to the [GitHub Wiki](https://github.com/okamstudio/godot/wiki). +Documentation has been moved to the [OpenProject Wiki](http://godotengine.org/projects/godot-engine/wiki/Documentation). ### Binary Downloads, Community, etc. @@ -22,4 +22,4 @@ http://www.godotengine.org ### Compiling from Source Compilation instructions for every platform can be found in the Wiki: -https://github.com/okamstudio/godot/wiki/advanced +http://godotengine.org/projects/godot-engine/wiki/Advanced_topics diff --git a/SConstruct b/SConstruct index 0087d97af3..f818c597fe 100644 --- a/SConstruct +++ b/SConstruct @@ -59,7 +59,7 @@ platform_arg = ARGUMENTS.get("platform", False) if (os.name=="posix"): pass elif (os.name=="nt"): - if (os.getenv("VSINSTALLDIR")==None or platform_arg=="android"): + if (os.getenv("VSINSTALLDIR")==None or platform_arg=="android"): custom_tools=['mingw'] env_base=Environment(tools=custom_tools,ENV = {'PATH' : os.environ['PATH']}); @@ -119,6 +119,7 @@ opts.Add('etc1','etc1 Texture compression support (yes/no)','yes') opts.Add('builtin_zlib','Use built-in zlib (yes/no)','yes') opts.Add('openssl','Use OpenSSL (yes/no/builtin)','no') opts.Add('musepack','Musepack Audio (yes/no)','yes') +opts.Add('ao', 'Enable libao audio driver (yes/no)', 'no') opts.Add("CXX", "Compiler"); opts.Add("CCFLAGS", "Custom flags for the C++ compiler"); opts.Add("CFLAGS", "Custom flags for the C compiler"); @@ -185,7 +186,7 @@ if selected_platform in platform_list: if env['vsproj']=="yes": env.vs_incs = [] env.vs_srcs = [] - + def AddToVSProject( sources ): for x in sources: if type(x) == type(""): @@ -197,12 +198,12 @@ if selected_platform in platform_list: basename = pieces[0] basename = basename.replace('\\\\','/') env.vs_srcs = env.vs_srcs + [basename + ".cpp"] - env.vs_incs = env.vs_incs + [basename + ".h"] - #print basename - env.AddToVSProject = AddToVSProject - + env.vs_incs = env.vs_incs + [basename + ".h"] + #print basename + env.AddToVSProject = AddToVSProject + env.extra_suffix="" - + if env["extra_suffix"] != '' : env.extra_suffix += '.'+env["extra_suffix"] @@ -229,7 +230,7 @@ if selected_platform in platform_list: #must happen after the flags, so when flags are used by configure, stuff happens (ie, ssl on x11) detect.configure(env) - #env['platform_libsuffix'] = env['LIBSUFFIX'] + #env['platform_libsuffix'] = env['LIBSUFFIX'] suffix="."+selected_platform @@ -284,10 +285,11 @@ if selected_platform in platform_list: if (env['musepack']=='yes'): env.Append(CPPFLAGS=['-DMUSEPACK_ENABLED']); - if (env['openssl']!='no'): - env.Append(CPPFLAGS=['-DOPENSSL_ENABLED']); - if (env['openssl']=="builtin"): - env.Append(CPPPATH=['#drivers/builtin_openssl2']) + + if (env['openssl']!='no'): + env.Append(CPPFLAGS=['-DOPENSSL_ENABLED']); + if (env['openssl']=="builtin"): + env.Append(CPPPATH=['#drivers/builtin_openssl2']) if (env["builtin_zlib"]=='yes'): env.Append(CPPPATH=['#drivers/builtin_zlib/zlib']) @@ -337,7 +339,7 @@ if selected_platform in platform_list: if (env['colored']=='yes'): methods.colored(sys,env) - + if (env['etc1']=='yes'): env.Append(CPPFLAGS=['-DETC1_ENABLED']) @@ -356,22 +358,22 @@ if selected_platform in platform_list: SConscript("main/SCsub") SConscript("platform/"+selected_platform+"/SCsub"); # build selected platform - - # Microsoft Visual Studio Project Generation - if (env['vsproj'])=="yes": - + + # Microsoft Visual Studio Project Generation + if (env['vsproj'])=="yes": + AddToVSProject(env.core_sources) AddToVSProject(env.main_sources) - AddToVSProject(env.modules_sources) + AddToVSProject(env.modules_sources) AddToVSProject(env.scene_sources) AddToVSProject(env.servers_sources) AddToVSProject(env.tool_sources) - + #env['MSVS_VERSION']='9.0' env['MSVSBUILDCOM'] = "scons platform=" + selected_platform + " target=" + env["target"] + " bits=" + env["bits"] + " tools=yes" env['MSVSREBUILDCOM'] = "scons platform=" + selected_platform + " target=" + env["target"] + " bits=" + env["bits"] + " tools=yes vsproj=true" env['MSVSCLEANCOM'] = "scons --clean platform=" + selected_platform + " target=" + env["target"] + " bits=" + env["bits"] + " tools=yes" - + debug_variants = ['Debug|Win32']+['Debug|x64'] release_variants = ['Release|Win32']+['Release|x64'] release_debug_variants = ['Release_Debug|Win32']+['Release_Debug|x64'] @@ -382,11 +384,11 @@ if selected_platform in platform_list: targets = debug_targets + release_targets + release_debug_targets msvproj = env.MSVSProject(target = ['#godot' + env['MSVSPROJECTSUFFIX'] ], incs = env.vs_incs, - srcs = env.vs_srcs, - runfile = targets, - buildtarget = targets, - auto_build_solution=1, - variant = variants) + srcs = env.vs_srcs, + runfile = targets, + buildtarget = targets, + auto_build_solution=1, + variant = variants) else: diff --git a/bin/tests/SCsub b/bin/tests/SCsub index 6613df9c05..57c9bc63b2 100644 --- a/bin/tests/SCsub +++ b/bin/tests/SCsub @@ -10,5 +10,3 @@ Export('env') lib = env.Library("tests",env.tests_sources) env.Prepend(LIBS=[lib]) - - diff --git a/core/SCsub b/core/SCsub index d04041141c..4ce91c794f 100644 --- a/core/SCsub +++ b/core/SCsub @@ -63,5 +63,3 @@ SConscript('bind/SCsub'); lib = env.Library("core",env.core_sources) env.Prepend(LIBS=[lib]) - - diff --git a/core/bind/SCsub b/core/bind/SCsub index c6ba1fa537..7b4a6acbc0 100644 --- a/core/bind/SCsub +++ b/core/bind/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.core_sources,"*.cpp") Export('env') - - diff --git a/core/image.cpp b/core/image.cpp index 06b7a78488..eadb7ecc8b 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -322,7 +322,7 @@ void Image::set_pallete(const DVector<uint8_t>& p_data) { DVector<uint8_t>::Write wp = data.write(); unsigned char *dst=wp.ptr() + pal_ofs; - DVector<uint8_t>::Read r = data.read(); + DVector<uint8_t>::Read r = p_data.read(); const unsigned char *src=r.ptr(); copymem(dst, src, len); diff --git a/core/io/SCsub b/core/io/SCsub index 5aecb4b915..3ff9b355a4 100644 --- a/core/io/SCsub +++ b/core/io/SCsub @@ -5,5 +5,3 @@ env.add_source_files(env.core_sources,"*.c") #env.core_sources.append("io/fastlz.c") Export('env') - - diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 24012660d2..58092efd4b 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -579,7 +579,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"),&HTTPClient::connect,DEFVAL(false),DEFVAL(true)); + ObjectTypeDB::bind_method(_MD("connect:Error","host","port","use_ssl","verify_host"),&HTTPClient::connect,DEFVAL(false),DEFVAL(true)); ObjectTypeDB::bind_method(_MD("set_connection","connection:StreamPeer"),&HTTPClient::set_connection); ObjectTypeDB::bind_method(_MD("request","method","url","headers","body"),&HTTPClient::request,DEFVAL(String())); ObjectTypeDB::bind_method(_MD("send_body_text","body"),&HTTPClient::send_body_text); @@ -601,6 +601,8 @@ void HTTPClient::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_status"),&HTTPClient::get_status); ObjectTypeDB::bind_method(_MD("poll:Error"),&HTTPClient::poll); + ObjectTypeDB::bind_method(_MD("query_string_from_dict:String","fields"),&HTTPClient::query_string_from_dict); + BIND_CONSTANT( METHOD_GET ); BIND_CONSTANT( METHOD_HEAD ); @@ -689,6 +691,16 @@ void HTTPClient::set_read_chunk_size(int p_size) { read_chunk_size=p_size; } +String HTTPClient::query_string_from_dict(const Dictionary& p_dict) { + String query = ""; + Array keys = p_dict.keys(); + for (int i = 0; i < keys.size(); ++i) { + query += "&" + String(keys[i]).http_escape() + "=" + String(p_dict[keys[i]]).http_escape(); + } + query.erase(0, 1); + return query; +} + HTTPClient::HTTPClient(){ tcp_connection = StreamPeerTCP::create_ref(); @@ -710,4 +722,3 @@ HTTPClient::~HTTPClient(){ } - diff --git a/core/io/http_client.h b/core/io/http_client.h index 21281f38c5..b103dc43fc 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -192,6 +192,8 @@ public: Error poll(); + String query_string_from_dict(const Dictionary& p_dict); + HTTPClient(); ~HTTPClient(); }; diff --git a/core/io/json.cpp b/core/io/json.cpp index 14890abd26..22c99d0465 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -177,9 +177,6 @@ Error JSON::_get_token(const CharType *p_str, int &idx, int p_len, Token& r_toke case 'n': res=10; break; case 'f': res=12; break; case 'r': res=13; break; - case '\"': res='\"'; break; - case '\\': res='\\'; break; - case '/': res='/'; break; //wtf case 'u': { //hexnumbarh - oct is deprecated @@ -218,10 +215,13 @@ Error JSON::_get_token(const CharType *p_str, int &idx, int p_len, Token& r_toke } break; + //case '\"': res='\"'; break; + //case '\\': res='\\'; break; + //case '/': res='/'; break; default: { - - r_err_str="Invalid escape sequence"; - return ERR_PARSE_ERROR; + res = next; + //r_err_str="Invalid escape sequence"; + //return ERR_PARSE_ERROR; } break; } diff --git a/core/math/SCsub b/core/math/SCsub index c6ba1fa537..7b4a6acbc0 100644 --- a/core/math/SCsub +++ b/core/math/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.core_sources,"*.cpp") Export('env') - - diff --git a/core/math/math_2d.cpp b/core/math/math_2d.cpp index 88717723ce..ce03f089e5 100644 --- a/core/math/math_2d.cpp +++ b/core/math/math_2d.cpp @@ -29,7 +29,7 @@ #include "math_2d.h" -real_t Vector2::atan2() const { +real_t Vector2::angle() const { return Math::atan2(x,y); } @@ -165,7 +165,7 @@ Vector2 Vector2::floor() const { Vector2 Vector2::rotated(float p_by) const { Vector2 v; - v.set_rotation(atan2()+p_by); + v.set_rotation(angle()+p_by); v*=length(); return v; } diff --git a/core/math/math_2d.h b/core/math/math_2d.h index 5e6cefd114..3d40e24091 100644 --- a/core/math/math_2d.h +++ b/core/math/math_2d.h @@ -133,7 +133,7 @@ struct Vector2 { bool operator<(const Vector2& p_vec2) const { return (x==p_vec2.x)?(y<p_vec2.y):(x<p_vec2.x); } bool operator<=(const Vector2& p_vec2) const { return (x==p_vec2.x)?(y<=p_vec2.y):(x<=p_vec2.x); } - real_t atan2() const; + real_t angle() const; void set_rotation(float p_radians) { diff --git a/core/math/quat.h b/core/math/quat.h index de4aedaeec..f161e35074 100644 --- a/core/math/quat.h +++ b/core/math/quat.h @@ -73,7 +73,7 @@ public: -x * v.x - y * v.y - z * v.z); } - _FORCE_INLINE_ Vector3 xform(const Vector3& v) { + _FORCE_INLINE_ Vector3 xform(const Vector3& v) const { Quat q = *this * v; q *= this->inverse(); diff --git a/core/os/SCsub b/core/os/SCsub index c6ba1fa537..7b4a6acbc0 100644 --- a/core/os/SCsub +++ b/core/os/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.core_sources,"*.cpp") Export('env') - - diff --git a/core/os/os.cpp b/core/os/os.cpp index ee9f12b79d..8caf95e4d1 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -61,9 +61,16 @@ void OS::debug_break() { void OS::print_error(const char* p_function,const char* p_file,int p_line,const char *p_code,const char*p_rationale,ErrorType p_type) { + const char* err_type; + switch(p_type) { + case ERR_ERROR: err_type="**ERROR**"; break; + case ERR_WARNING: err_type="**WARNING**"; break; + case ERR_SCRIPT: err_type="**SCRIPT ERROR**"; break; + } + if (p_rationale && *p_rationale) - print("**ERROR**: %s\n ",p_rationale); - print("**ERROR**: At: %s:%i:%s() - %s\n",p_file,p_line,p_function,p_code); + print("%s: %s\n ",err_type,p_rationale); + print("%s: At: %s:%i:%s() - %s\n",err_type,p_file,p_line,p_function,p_code); } void OS::print(const char* p_format, ...) { diff --git a/core/ustring.cpp b/core/ustring.cpp index 7582376fe0..72ad2a15e7 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -44,6 +44,11 @@ #include <stdlib.h> #include <stdio.h> #endif + +#if defined(MINGW_ENABLED) || defined(_MSC_VER) +#define snprintf _snprintf +#endif + /** STRING **/ const char *CharString::get_data() const { @@ -3079,6 +3084,48 @@ String String::world_wrap(int p_chars_per_line) const { return ret; } +String String::http_escape() const { + const CharString temp = utf8(); + String res; + for (int i = 0; i < length(); ++i) { + CharType ord = temp[i]; + if (ord == '.' || ord == '-' || ord == '_' || ord == '~' || + (ord >= 'a' && ord <= 'z') || + (ord >= 'A' && ord <= 'Z') || + (ord >= '0' && ord <= '9')) { + res += ord; + } else { + char h_Val[3]; + _snprintf(h_Val, 3, "%.2X", ord); + res += "%"; + res += h_Val; + } + } + return res; +} + +String String::http_unescape() const { + String res; + for (int i = 0; i < length(); ++i) { + if (ord_at(i) == '%' && i+2 < length()) { + CharType ord1 = ord_at(i+1); + if ((ord1 >= '0' && ord1 <= '9') || (ord1 >= 'A' && ord1 <= 'Z')) { + CharType ord2 = ord_at(i+2); + if ((ord2 >= '0' && ord2 <= '9') || (ord2 >= 'A' && ord2 <= 'Z')) { + char bytes[2] = {ord1, ord2}; + res += (char)strtol(bytes, NULL, 16); + i+=2; + } + } else { + res += ord_at(i); + } + } else { + res += ord_at(i); + } + } + return String::utf8(res.ascii()); +} + String String::c_unescape() const { String escaped=*this; diff --git a/core/ustring.h b/core/ustring.h index fa25a07eb0..2f3c4bff4d 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -207,6 +207,8 @@ public: String xml_escape(bool p_escape_quotes=false) const; String xml_unescape() const; + String http_escape() const; + String http_unescape() const; String c_escape() const; String c_unescape() const; String world_wrap(int p_chars_per_line) const; diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 222618ffa0..51d683f1fe 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -333,7 +333,7 @@ static void _call_##m_type##_##m_method(Variant& r_ret,Variant& p_self,const Var VCALL_LOCALMEM1R(Vector2,dot); VCALL_LOCALMEM1R(Vector2,slide); VCALL_LOCALMEM1R(Vector2,reflect); - VCALL_LOCALMEM0R(Vector2,atan2); + VCALL_LOCALMEM0R(Vector2,angle); // VCALL_LOCALMEM1R(Vector2,cross); VCALL_LOCALMEM0R(Rect2,get_area); @@ -409,6 +409,7 @@ static void _call_##m_type##_##m_method(Variant& r_ret,Variant& p_self,const Var VCALL_LOCALMEM0R(Quat,normalized); VCALL_LOCALMEM0R(Quat,inverse); VCALL_LOCALMEM1R(Quat,dot); + VCALL_LOCALMEM1R(Quat,xform); VCALL_LOCALMEM2R(Quat,slerp); VCALL_LOCALMEM2R(Quat,slerpni); VCALL_LOCALMEM4R(Quat,cubic_slerp); @@ -1296,7 +1297,7 @@ _VariantCall::addfunc(Variant::m_vtype,Variant::m_ret,_SCS(#m_method),VCALL(m_cl ADDFUNC0(VECTOR2,VECTOR2,Vector2,normalized,varray()); ADDFUNC0(VECTOR2,REAL,Vector2,length,varray()); - ADDFUNC0(VECTOR2,REAL,Vector2,atan2,varray()); + ADDFUNC0(VECTOR2,REAL,Vector2,angle,varray()); ADDFUNC0(VECTOR2,REAL,Vector2,length_squared,varray()); ADDFUNC1(VECTOR2,REAL,Vector2,distance_to,VECTOR2,"to",varray()); ADDFUNC1(VECTOR2,REAL,Vector2,distance_squared_to,VECTOR2,"to",varray()); @@ -1361,6 +1362,7 @@ _VariantCall::addfunc(Variant::m_vtype,Variant::m_ret,_SCS(#m_method),VCALL(m_cl ADDFUNC0(QUAT,QUAT,Quat,normalized,varray()); ADDFUNC0(QUAT,QUAT,Quat,inverse,varray()); ADDFUNC1(QUAT,REAL,Quat,dot,QUAT,"b",varray()); + ADDFUNC1(QUAT,VECTOR3,Quat,xform,VECTOR3,"v",varray()); ADDFUNC2(QUAT,QUAT,Quat,slerp,QUAT,"b",REAL,"t",varray()); ADDFUNC2(QUAT,QUAT,Quat,slerpni,QUAT,"b",REAL,"t",varray()); ADDFUNC4(QUAT,QUAT,Quat,cubic_slerp,QUAT,"b",QUAT,"pre_a",QUAT,"post_b",REAL,"t",varray()); diff --git a/core/variant_op.cpp b/core/variant_op.cpp index 1cdf6d7319..eabd647837 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -586,7 +586,21 @@ void Variant::evaluate(const Operator& p_op, const Variant& p_a, const Variant& } break; DEFAULT_OP_LOCALMEM_NUM(*,VECTOR3,Vector3); DEFAULT_OP_FAIL(PLANE); - DEFAULT_OP_FAIL(QUAT); + case QUAT: { + + switch(p_b.type) { + case VECTOR3: { + + _RETURN( reinterpret_cast<const Quat*>(p_a._data._mem)->xform( *(const Vector3*)p_b._data._mem) ); + } break; + case QUAT: { + + _RETURN( *reinterpret_cast<const Quat*>(p_a._data._mem) * *reinterpret_cast<const Quat*>(p_b._data._mem) ); + } break; + }; + r_valid=false; + return; + } break; DEFAULT_OP_FAIL(_AABB); case MATRIX3: { @@ -2573,7 +2587,7 @@ bool Variant::in(const Variant& p_index, bool *r_valid) const { String idx=p_index; const String *str=reinterpret_cast<const String*>(_data._mem); - return str->find("idx")!=-1; + return str->find(idx)!=-1; } } break; diff --git a/demos/2d/area_input/engine.cfg b/demos/2d/area_input/engine.cfg index 3227e9278f..8fa2e15beb 100644 --- a/demos/2d/area_input/engine.cfg +++ b/demos/2d/area_input/engine.cfg @@ -2,3 +2,4 @@ name="Area 2D Input Events" main_scene="res://input.scn" +icon="res://icon.png" diff --git a/demos/2d/area_input/icon.png b/demos/2d/area_input/icon.png Binary files differnew file mode 100644 index 0000000000..d9bb881693 --- /dev/null +++ b/demos/2d/area_input/icon.png diff --git a/demos/2d/dynamic_collision_shapes/engine.cfg b/demos/2d/dynamic_collision_shapes/engine.cfg index 536b75f2f2..76a074f346 100644 --- a/demos/2d/dynamic_collision_shapes/engine.cfg +++ b/demos/2d/dynamic_collision_shapes/engine.cfg @@ -2,3 +2,4 @@ name="Run-Time CollisionShape" main_scene="res://dynamic_colobjs.scn" +icon="res://icon.png" diff --git a/demos/2d/dynamic_collision_shapes/icon.png b/demos/2d/dynamic_collision_shapes/icon.png Binary files differnew file mode 100644 index 0000000000..ac01d401ba --- /dev/null +++ b/demos/2d/dynamic_collision_shapes/icon.png diff --git a/demos/2d/fog_of_war/engine.cfg b/demos/2d/fog_of_war/engine.cfg index 5c4307b5bc..1f56851c58 100644 --- a/demos/2d/fog_of_war/engine.cfg +++ b/demos/2d/fog_of_war/engine.cfg @@ -2,7 +2,7 @@ name="Fog of War" main_scene="res://fog.scn" -icon="icon.png" +icon="res://icon.png" [input] diff --git a/demos/2d/hdr/engine.cfg b/demos/2d/hdr/engine.cfg index 3d8b4222d5..ab53a022f0 100644 --- a/demos/2d/hdr/engine.cfg +++ b/demos/2d/hdr/engine.cfg @@ -2,6 +2,7 @@ name="HDR for 2D" main_scene="res://beach_cave.scn" +icon="res://icon.png" [display] diff --git a/demos/2d/hdr/icon.png b/demos/2d/hdr/icon.png Binary files differnew file mode 100644 index 0000000000..2df0ec38e9 --- /dev/null +++ b/demos/2d/hdr/icon.png diff --git a/demos/2d/isometric_light/engine.cfg b/demos/2d/isometric_light/engine.cfg index 08393f1724..a5b053aa95 100644 --- a/demos/2d/isometric_light/engine.cfg +++ b/demos/2d/isometric_light/engine.cfg @@ -2,6 +2,7 @@ name="Isometric 2D + Lighting" main_scene="res://map.scn" +icon="res://icon.png" [input] diff --git a/demos/2d/isometric_light/icon.png b/demos/2d/isometric_light/icon.png Binary files differnew file mode 100644 index 0000000000..3de9749729 --- /dev/null +++ b/demos/2d/isometric_light/icon.png diff --git a/demos/2d/light_mask/engine.cfg b/demos/2d/light_mask/engine.cfg index 8b0ae6f61d..39608669ab 100644 --- a/demos/2d/light_mask/engine.cfg +++ b/demos/2d/light_mask/engine.cfg @@ -2,6 +2,7 @@ name="Using Lights As Mask" main_scene="res://lightmask.scn" +icon="res://icon.png" [rasterizer] diff --git a/demos/2d/light_mask/icon.png b/demos/2d/light_mask/icon.png Binary files differnew file mode 100644 index 0000000000..c12b045e62 --- /dev/null +++ b/demos/2d/light_mask/icon.png diff --git a/demos/2d/lights_shadows/engine.cfg b/demos/2d/lights_shadows/engine.cfg index 771288c209..80142633d3 100644 --- a/demos/2d/lights_shadows/engine.cfg +++ b/demos/2d/lights_shadows/engine.cfg @@ -2,6 +2,7 @@ name="2D Lighting" main_scene="res://light_shadows.scn" +icon="res://icon.png" [display] diff --git a/demos/2d/lights_shadows/icon.png b/demos/2d/lights_shadows/icon.png Binary files differnew file mode 100644 index 0000000000..c7f9e13bae --- /dev/null +++ b/demos/2d/lights_shadows/icon.png diff --git a/demos/2d/navpoly/engine.cfg b/demos/2d/navpoly/engine.cfg index 40515dd3d2..b750419915 100644 --- a/demos/2d/navpoly/engine.cfg +++ b/demos/2d/navpoly/engine.cfg @@ -2,6 +2,7 @@ name="Navigation Polygon (2D)" main_scene="res://navigation.scn" +icon="res://icon.png" [display] diff --git a/demos/2d/navpoly/icon.png b/demos/2d/navpoly/icon.png Binary files differnew file mode 100644 index 0000000000..df7fb43633 --- /dev/null +++ b/demos/2d/navpoly/icon.png diff --git a/demos/2d/normalmaps/engine.cfg b/demos/2d/normalmaps/engine.cfg index f0002dc2b8..4f9f4f67f0 100644 --- a/demos/2d/normalmaps/engine.cfg +++ b/demos/2d/normalmaps/engine.cfg @@ -2,6 +2,7 @@ name="2D Normal Mapping" main_scene="res://normalmap.scn" +icon="res://icon.png" [display] diff --git a/demos/2d/normalmaps/icon.png b/demos/2d/normalmaps/icon.png Binary files differnew file mode 100644 index 0000000000..4e5d835005 --- /dev/null +++ b/demos/2d/normalmaps/icon.png diff --git a/demos/2d/polygon_path_finder_demo/engine.cfg b/demos/2d/polygon_path_finder_demo/engine.cfg index 41c4adf701..de5593c417 100644 --- a/demos/2d/polygon_path_finder_demo/engine.cfg +++ b/demos/2d/polygon_path_finder_demo/engine.cfg @@ -2,4 +2,4 @@ name="polygon_path_finder_demo" main_scene="res://new_scene_poly_with_holes.scn" -icon="icon.png" +icon="res://icon.png" diff --git a/demos/2d/screen_space_shaders/engine.cfg b/demos/2d/screen_space_shaders/engine.cfg index 527e2f8f0a..383ca7bf11 100644 --- a/demos/2d/screen_space_shaders/engine.cfg +++ b/demos/2d/screen_space_shaders/engine.cfg @@ -2,6 +2,7 @@ name="Screen-Space Shaders" main_scene="res://screen_shaders.scn" +icon="res://icon.png" [display] diff --git a/demos/2d/screen_space_shaders/icon.png b/demos/2d/screen_space_shaders/icon.png Binary files differnew file mode 100644 index 0000000000..65247f9ae7 --- /dev/null +++ b/demos/2d/screen_space_shaders/icon.png diff --git a/demos/2d/sdf_font/engine.cfg b/demos/2d/sdf_font/engine.cfg index bdf26ce741..bf983041fa 100644 --- a/demos/2d/sdf_font/engine.cfg +++ b/demos/2d/sdf_font/engine.cfg @@ -2,3 +2,4 @@ name="Signed Distance Field Font" main_scene="res://sdf.scn" +icon="res://icon.png" diff --git a/demos/2d/sdf_font/icon.png b/demos/2d/sdf_font/icon.png Binary files differnew file mode 100644 index 0000000000..be9fefa8b0 --- /dev/null +++ b/demos/2d/sdf_font/icon.png diff --git a/demos/2d/splash/engine.cfg b/demos/2d/splash/engine.cfg index cb50c7b1be..e461426305 100644 --- a/demos/2d/splash/engine.cfg +++ b/demos/2d/splash/engine.cfg @@ -2,6 +2,7 @@ name="Splash Screen" main_scene="res://splash.xml" +icon="res://icon.png" [display] diff --git a/demos/2d/splash/icon.png b/demos/2d/splash/icon.png Binary files differnew file mode 100644 index 0000000000..88620eb35b --- /dev/null +++ b/demos/2d/splash/icon.png diff --git a/demos/2d/sprite_shaders/engine.cfg b/demos/2d/sprite_shaders/engine.cfg index 09f9a59566..17bdada188 100644 --- a/demos/2d/sprite_shaders/engine.cfg +++ b/demos/2d/sprite_shaders/engine.cfg @@ -2,3 +2,4 @@ name="2D Shaders for Sprites" main_scene="res://sprite_shaders.scn" +icon="res://icon.png" diff --git a/demos/2d/sprite_shaders/icon.png b/demos/2d/sprite_shaders/icon.png Binary files differnew file mode 100644 index 0000000000..b044b31f93 --- /dev/null +++ b/demos/2d/sprite_shaders/icon.png diff --git a/demos/3d/navmesh/icon.png b/demos/3d/navmesh/icon.png Binary files differnew file mode 100644 index 0000000000..5b354f931c --- /dev/null +++ b/demos/3d/navmesh/icon.png diff --git a/demos/3d/sat_test/engine.cfg b/demos/3d/sat_test/engine.cfg index cc215c83e8..82c688635d 100644 --- a/demos/3d/sat_test/engine.cfg +++ b/demos/3d/sat_test/engine.cfg @@ -2,3 +2,4 @@ name="SAT Collision Test" main_scene="res://sat_test.xml" +icon="res://icon.png" diff --git a/demos/3d/sat_test/icon.png b/demos/3d/sat_test/icon.png Binary files differnew file mode 100644 index 0000000000..b89c5a7467 --- /dev/null +++ b/demos/3d/sat_test/icon.png diff --git a/demos/gui/input_mapping/engine.cfg b/demos/gui/input_mapping/engine.cfg index 959c0ac7d5..6470ec6cd8 100644 --- a/demos/gui/input_mapping/engine.cfg +++ b/demos/gui/input_mapping/engine.cfg @@ -2,7 +2,6 @@ name="Input Mapping GUI" main_scene="res://controls.scn" -icon="icon.png" [display] diff --git a/demos/gui/rich_text_bbcode/engine.cfg b/demos/gui/rich_text_bbcode/engine.cfg index e0ea296f6d..5f68b6a0e6 100644 --- a/demos/gui/rich_text_bbcode/engine.cfg +++ b/demos/gui/rich_text_bbcode/engine.cfg @@ -2,3 +2,4 @@ name="Rich Text Label (BBCode)" main_scene="res://rich_text_bbcode.scn" +icon="res://icon.png" diff --git a/demos/gui/rich_text_bbcode/icon.png b/demos/gui/rich_text_bbcode/icon.png Binary files differnew file mode 100644 index 0000000000..78358ba71b --- /dev/null +++ b/demos/gui/rich_text_bbcode/icon.png diff --git a/demos/misc/instancing/engine.cfg b/demos/misc/instancing/engine.cfg index 52a28a3fce..76b0c97721 100644 --- a/demos/misc/instancing/engine.cfg +++ b/demos/misc/instancing/engine.cfg @@ -2,6 +2,7 @@ name="Scene Instancing Demo" main_scene="res://container.scn" +icon="res://icon.png" [physics_2d] diff --git a/demos/misc/instancing/icon.png b/demos/misc/instancing/icon.png Binary files differnew file mode 100644 index 0000000000..7a6de677c5 --- /dev/null +++ b/demos/misc/instancing/icon.png diff --git a/demos/misc/regex/regex.gd b/demos/misc/regex/regex.gd index e648c18093..409b4cab05 100644 --- a/demos/misc/regex/regex.gd +++ b/demos/misc/regex/regex.gd @@ -2,21 +2,23 @@ extends VBoxContainer var regex = RegEx.new() -func update_expression(): - regex.compile(get_node("Expression").get_text()) +func update_expression(text): + regex.compile(text) update_text() func update_text(): var text = get_node("Text").get_text() - regex.find(text) var list = get_node("List") for child in list.get_children(): child.queue_free() - for res in regex.get_captures(): - var label = Label.new() - label.set_text(res) - list.add_child(label) + if regex.is_valid(): + regex.find(text) + for res in regex.get_captures(): + var label = Label.new() + label.set_text(res) + list.add_child(label) func _ready(): get_node("Text").set_text("They asked me \"What's going on \\\"in the manor\\\"?\"") - update_expression() + update_expression(get_node("Expression").get_text()) + diff --git a/demos/misc/regex/regex.scn b/demos/misc/regex/regex.scn Binary files differindex 2b62d6b82a..1f46521d0d 100644 --- a/demos/misc/regex/regex.scn +++ b/demos/misc/regex/regex.scn diff --git a/demos/misc/tween/engine.cfg b/demos/misc/tween/engine.cfg index f97e540dbd..3d3d639964 100644 --- a/demos/misc/tween/engine.cfg +++ b/demos/misc/tween/engine.cfg @@ -2,7 +2,7 @@ name="Tween Demo" main_scene="res://main.xml" -icon="icon.png" +icon="res://icon.png" target_fps=60 [display] diff --git a/demos/misc/window_management/engine.cfg b/demos/misc/window_management/engine.cfg index 0a34231673..911d3fd4a1 100644 --- a/demos/misc/window_management/engine.cfg +++ b/demos/misc/window_management/engine.cfg @@ -2,7 +2,7 @@ name="Window Management" main_scene="res://window_management.scn" -icon="icon.png" +icon="res://icon.png" [display] diff --git a/doc/base/classes.xml b/doc/base/classes.xml index 24c5799350..da53cb90a5 100644 --- a/doc/base/classes.xml +++ b/doc/base/classes.xml @@ -12518,9 +12518,13 @@ This approximation makes straight segments between each point, then subdivides t </argument> <argument index="2" name="use_ssl" type="bool" default="false"> </argument> - <argument index="3" name="arg3" type="bool" default="true"> + <argument index="3" name="verify_host" type="bool" default="true"> </argument> <description> + Connect to a host. This needs to be done before any requests are sent. +The host should not have http:// prepended but will strip the protocol identifier if provided. + +verify_host will check the SSL identity of the host if set to true. </description> </method> <method name="set_connection"> @@ -12541,6 +12545,20 @@ This approximation makes straight segments between each point, then subdivides t <argument index="3" name="body" type="String" default=""""> </argument> <description> + Sends a request to the connected host. The url is the what is normally behind the hostname, +i.e; +http://somehost.com/index.php +url would be "index.php" + +Headers are HTTP request headers + +To create a POST request with query strings to push to the server, do: +var fields = {"username" : "user", + "password" : "pass"} +var queryString = httpClient.query_string_from_dict(fields) +var headers = ["Content-Type: application/x-www-form-urlencoded", + "Content-Length: " + str(queryString.length())] +var result = httpClient.request(httpClient.METHOD_POST, "index.php", headers, queryString) </description> </method> <method name="send_body_text"> @@ -12548,7 +12566,8 @@ This approximation makes straight segments between each point, then subdivides t </return> <argument index="0" name="body" type="String"> </argument> - <description> + <description> + Stub function </description> </method> <method name="send_body_data"> @@ -12557,6 +12576,7 @@ This approximation makes straight segments between each point, then subdivides t <argument index="0" name="body" type="RawArray"> </argument> <description> + Stub function </description> </method> <method name="close"> @@ -12609,12 +12629,14 @@ This approximation makes straight segments between each point, then subdivides t <argument index="0" name="bytes" type="int"> </argument> <description> + Sets the size of the buffer used and maximum bytes to read per iteration </description> </method> <method name="set_blocking_mode"> <argument index="0" name="enabled" type="bool"> </argument> <description> + If set to true, execute will wait until all data is read from the response. </description> </method> <method name="is_blocking_mode_enabled" qualifiers="const"> @@ -12627,14 +12649,30 @@ This approximation makes straight segments between each point, then subdivides t <return type="int"> </return> <description> + Returns a status string like STATUS_REQUESTING. Need to call [method poll] in order to get status updates. </description> </method> <method name="poll"> <return type="Error"> </return> - <description> + <description> + This needs to be called in order to have any request processed. Check results with [method get_status] </description> </method> + <method name="query_string_from_dict"> + <return type="String"> + </return> + <argument index="0" name="fields" type="Dictionary"> + </argument> + <description> + Generates a GET/POST application/x-www-form-urlencoded style query string from a provided dictionary. + +var fields = {"username": "user", "password": "pass"} +String queryString = httpClient.query_string_from_dict(fields) + +returns:= "username=user&password=pass" + </description> + </method> </methods> <constants> <constant name="METHOD_GET" value="0"> @@ -26819,7 +26857,11 @@ This method controls whether the position between two cached points is interpola Lazy (non-greedy) quantifiers [code]*?[/code] Begining [code]^[/code] and end [code]$[/code] anchors Alternation [code]|[/code] - Backreferences [code]\1[/code] to [code]\99[/code] + Backreferences [code]\1[/code] to [code]\9[/code] + POSIX character classes [code][[:alnum:]][/code] + Lookahead [code](?=)[/code], [code](?!)[/code] and lookbehind [code](?<=)[/code], [code](?<!)[/code] + ASCII [code]\xFF[/code] and Unicode [code]\uFFFF[/code] code points (in a style similar to Python) + Word boundaries [code]\b[/code], [code]\B[/code] </description> <methods> <method name="compile"> diff --git a/drivers/SCsub b/drivers/SCsub index bc46bf2cec..3cc7b3f402 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -7,6 +7,7 @@ Export('env') SConscript('unix/SCsub'); SConscript('alsa/SCsub'); +SConscript('ao/SCsub'); SConscript('pulseaudio/SCsub'); SConscript('windows/SCsub'); SConscript('gles2/SCsub'); @@ -69,21 +70,27 @@ for f in env.drivers_sources: fname = env.File(f).path else: fname = env.File(f)[0].path - #base = string.join(fname.split("/")[:-1], "/") fname = fname.replace("\\", "/") base = string.join(fname.split("/")[:2], "/") if base != cur_base and len(list) > max_src: - lib = env.Library("drivers"+str(num), list) - lib_list.append(lib) - list = [] + if num > 0: + lib = env.Library("drivers"+str(num), list) + lib_list.append(lib) + list = [] num = num+1 cur_base = base list.append(f) -if len(list) > 0: - lib = env.Library("drivers"+str(num), list) - lib_list.append(lib) +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") @@ -93,4 +100,3 @@ env.Prepend(LIBS=lib_list) #lib = env.Library("drivers",env.drivers_sources) #env.Prepend(LIBS=[lib]) - diff --git a/drivers/alsa/SCsub b/drivers/alsa/SCsub index bcd231579c..9fbb467baa 100644 --- a/drivers/alsa/SCsub +++ b/drivers/alsa/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.drivers_sources,"*.cpp") Export('env') - - diff --git a/drivers/ao/SCsub b/drivers/ao/SCsub new file mode 100644 index 0000000000..9fbb467baa --- /dev/null +++ b/drivers/ao/SCsub @@ -0,0 +1,5 @@ +Import('env') + +env.add_source_files(env.drivers_sources,"*.cpp") + +Export('env') diff --git a/drivers/ao/audio_driver_ao.cpp b/drivers/ao/audio_driver_ao.cpp new file mode 100644 index 0000000000..a484a5882e --- /dev/null +++ b/drivers/ao/audio_driver_ao.cpp @@ -0,0 +1,161 @@ +/*************************************************************************/ +/* audio_driver_ao.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014 Anton Yabchinskiy. */ +/* */ +/* 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 "audio_driver_ao.h" + +#ifdef AO_ENABLED + +#include "globals.h" +#include "os/os.h" + +#include <cstring> + +Error AudioDriverAO::init() { + + active = false; + thread_exited = false; + exit_thread = false; + pcm_open = false; + samples_in = NULL; + + mix_rate = 44100; + output_format = OUTPUT_STEREO; + channels = 2; + + ao_sample_format format; + + format.bits = 16; + format.rate = mix_rate; + format.channels = channels; + format.byte_format = AO_FMT_LITTLE; + format.matrix = "L,R"; + + device = ao_open_live(ao_default_driver_id(), &format, NULL); + ERR_FAIL_COND_V(device == NULL, ERR_CANT_OPEN); + + int latency = GLOBAL_DEF("audio/output_latency", 25); + buffer_size = nearest_power_of_2( latency * mix_rate / 1000 ); + + samples_in = memnew_arr(int32_t, buffer_size * channels); + + mutex = Mutex::create(); + thread = Thread::create(AudioDriverAO::thread_func, this); + + return OK; +}; + +void AudioDriverAO::thread_func(void* p_udata) { + AudioDriverAO* ad = (AudioDriverAO*)p_udata; + + // Overwrite samples on conversion + int16_t* samples_out = reinterpret_cast<int16_t*>(ad->samples_in); + unsigned int n_samples = ad->buffer_size * ad->channels; + unsigned int n_bytes = n_samples * sizeof(int16_t); + + while (!ad->exit_thread) { + if (ad->active) { + ad->lock(); + ad->audio_server_process(ad->buffer_size, ad->samples_in); + ad->unlock(); + + for (unsigned int i = 0; i < n_samples; i++) { + samples_out[i] = ad->samples_in[i] >> 16; + } + } else { + memset(samples_out, 0, n_bytes); + } + + if (ad->exit_thread) + break; + + if (!ao_play(ad->device, reinterpret_cast<char*>(samples_out), n_bytes)) { + ERR_PRINT("ao_play() failed"); + } + }; + + ad->thread_exited = true; +}; + +void AudioDriverAO::start() { + active = true; +}; + +int AudioDriverAO::get_mix_rate() const { + return mix_rate; +}; + +AudioDriverSW::OutputFormat AudioDriverAO::get_output_format() const { + return output_format; +}; + +void AudioDriverAO::lock() { + if (!thread || !mutex) + return; + mutex->lock(); +}; + +void AudioDriverAO::unlock() { + if (!thread || !mutex) + return; + mutex->unlock(); +}; + +void AudioDriverAO::finish() { + if (!thread) + return; + + exit_thread = true; + Thread::wait_to_finish(thread); + + if (samples_in) { + memdelete_arr(samples_in); + }; + + memdelete(thread); + if (mutex) + memdelete(mutex); + + if (device) + ao_close(device); + + thread = NULL; +}; + +AudioDriverAO::AudioDriverAO() { + mutex = NULL; + thread = NULL; + + ao_initialize(); +}; + +AudioDriverAO::~AudioDriverAO() { + ao_shutdown(); +}; + +#endif diff --git a/drivers/ao/audio_driver_ao.h b/drivers/ao/audio_driver_ao.h new file mode 100644 index 0000000000..023e051c8b --- /dev/null +++ b/drivers/ao/audio_driver_ao.h @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* audio_driver_ao.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014 Anton Yabchinskiy. */ +/* */ +/* 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 "servers/audio/audio_server_sw.h" + +#ifdef AO_ENABLED + +#include "core/os/thread.h" +#include "core/os/mutex.h" + +#include <ao/ao.h> + +class AudioDriverAO : public AudioDriverSW { + + Thread* thread; + Mutex* mutex; + + ao_device* device; + + int32_t* samples_in; + + static void thread_func(void* p_udata); + int buffer_size; + + unsigned int mix_rate; + OutputFormat output_format; + + int channels; + + bool active; + bool thread_exited; + mutable bool exit_thread; + bool pcm_open; + +public: + + const char* get_name() const { + return "libao"; + }; + + virtual Error init(); + virtual void start(); + virtual int get_mix_rate() const; + virtual OutputFormat get_output_format() const; + virtual void lock(); + virtual void unlock(); + virtual void finish(); + + AudioDriverAO(); + ~AudioDriverAO(); +}; + +#endif diff --git a/drivers/builtin_openssl2/e_os.h b/drivers/builtin_openssl2/e_os.h index e801b4106a..7323a7b4bf 100644 --- a/drivers/builtin_openssl2/e_os.h +++ b/drivers/builtin_openssl2/e_os.h @@ -318,8 +318,8 @@ static unsigned int _strlen31(const char *str) # undef isupper # undef isxdigit # endif -# if defined(_MSC_VER) && !defined(_DLL) && defined(stdin) -# if _MSC_VER>=1300 +# if defined(_MSC_VER) && !defined(_WIN32_WCE) && !defined(_DLL) && defined(stdin) +# if _MSC_VER>=1300 && _MSC_VER<1600 # undef stdin # undef stdout # undef stderr @@ -327,7 +327,7 @@ static unsigned int _strlen31(const char *str) # define stdin (&__iob_func()[0]) # define stdout (&__iob_func()[1]) # define stderr (&__iob_func()[2]) -# elif defined(I_CAN_LIVE_WITH_LNK4049) +# elif _MSC_VER<1300 && defined(I_CAN_LIVE_WITH_LNK4049) # undef stdin # undef stdout # undef stderr diff --git a/drivers/builtin_zlib/SCsub b/drivers/builtin_zlib/SCsub index c322b236ab..e5c81c0b3b 100644 --- a/drivers/builtin_zlib/SCsub +++ b/drivers/builtin_zlib/SCsub @@ -1,7 +1,7 @@ Import('env') zlib_sources = [ - + "builtin_zlib/zlib/adler32.c", "builtin_zlib/zlib/compress.c", "builtin_zlib/zlib/crc32.c", diff --git a/drivers/chibi/SCsub b/drivers/chibi/SCsub index bcd231579c..9fbb467baa 100644 --- a/drivers/chibi/SCsub +++ b/drivers/chibi/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.drivers_sources,"*.cpp") Export('env') - - diff --git a/drivers/dds/SCsub b/drivers/dds/SCsub index e475de1dba..159829384f 100644 --- a/drivers/dds/SCsub +++ b/drivers/dds/SCsub @@ -8,4 +8,3 @@ dds_sources = [ env.drivers_sources+=dds_sources #env.add_source_files(env.drivers_sources, dds_sources) - diff --git a/drivers/etc1/SCsub b/drivers/etc1/SCsub index 251c3ffb86..4ce921ad9f 100644 --- a/drivers/etc1/SCsub +++ b/drivers/etc1/SCsub @@ -12,4 +12,3 @@ if (env["etc1"] != "no"): #env.add_source_files(env.drivers_sources, etc_sources) Export('env') - diff --git a/drivers/gl_context/SCsub b/drivers/gl_context/SCsub index 0177eec6a2..7cf8629fe1 100644 --- a/drivers/gl_context/SCsub +++ b/drivers/gl_context/SCsub @@ -3,4 +3,3 @@ Export('env'); env.add_source_files(env.drivers_sources,"*.cpp") env.add_source_files(env.drivers_sources,"*.c") - diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index d84ee5a758..b473e8493f 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -4075,6 +4075,8 @@ void RasterizerGLES2::render_target_set_size(RID p_render_target,int p_width,int glDeleteTextures(1,&rt->color); rt->fbo=0; + rt->depth=0; + rt->color=0; rt->width=0; rt->height=0; rt->texture_ptr->tex_id=0; @@ -4094,12 +4096,14 @@ void RasterizerGLES2::render_target_set_size(RID p_render_target,int p_width,int glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); //depth - glGenRenderbuffers(1, &rt->depth); - glBindRenderbuffer(GL_RENDERBUFFER, rt->depth ); + if (!low_memory_2d) { + glGenRenderbuffers(1, &rt->depth); + glBindRenderbuffer(GL_RENDERBUFFER, rt->depth ); - glRenderbufferStorage(GL_RENDERBUFFER, use_depth24?_DEPTH_COMPONENT24_OES:GL_DEPTH_COMPONENT16, rt->width,rt->height); + glRenderbufferStorage(GL_RENDERBUFFER, use_depth24?_DEPTH_COMPONENT24_OES:GL_DEPTH_COMPONENT16, rt->width,rt->height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); + } //color glGenTextures(1, &rt->color); @@ -4641,6 +4645,9 @@ void RasterizerGLES2::_update_shader( Shader* p_shader) const { enablers.push_back("#define USE_TIME\n"); uses_time=true; } + if (vertex_flags.vertex_code_writes_position) { + enablers.push_back("#define VERTEX_SHADER_WRITE_POSITION\n"); + } material_shader.set_custom_shader_code(p_shader->custom_code_id,vertex_code, vertex_globals,fragment_code, light_code, fragment_globals,uniform_names,enablers); } else if (p_shader->mode==VS::SHADER_CANVAS_ITEM) { @@ -6526,80 +6533,84 @@ void RasterizerGLES2::_render_list_forward(RenderList *p_render_list,const Trans material_shader.set_conditional(MaterialShaderGLES2::ENABLE_AMBIENT_LIGHTMAP,false); material_shader.set_conditional(MaterialShaderGLES2::ENABLE_AMBIENT_DP_SAMPLER,false); - if (e->instance->sampled_light.is_valid()) { + if (material->flags[VS::MATERIAL_FLAG_UNSHADED] == false && current_debug != VS::SCENARIO_DEBUG_SHADELESS) { - SampledLight *sl = sampled_light_owner.get(e->instance->sampled_light); - if (sl) { + if (e->instance->sampled_light.is_valid()) { - baked_light=NULL; //can't mix - material_shader.set_conditional(MaterialShaderGLES2::ENABLE_AMBIENT_DP_SAMPLER,true); - glActiveTexture(GL_TEXTURE0+max_texture_units-3); - glBindTexture(GL_TEXTURE_2D,sl->texture); //bind the texture - sampled_light_dp_multiplier=sl->multiplier; - bind_dp_sampler=true; + SampledLight *sl = sampled_light_owner.get(e->instance->sampled_light); + if (sl) { + + baked_light = NULL; //can't mix + material_shader.set_conditional(MaterialShaderGLES2::ENABLE_AMBIENT_DP_SAMPLER, true); + glActiveTexture(GL_TEXTURE0 + max_texture_units - 3); + glBindTexture(GL_TEXTURE_2D, sl->texture); //bind the texture + sampled_light_dp_multiplier = sl->multiplier; + bind_dp_sampler = true; + } } - } - if (!additive && baked_light) { + if (!additive && baked_light) { - if (baked_light->mode==VS::BAKED_LIGHT_OCTREE && baked_light->octree_texture.is_valid() && e->instance->baked_light_octree_xform) { - material_shader.set_conditional(MaterialShaderGLES2::ENABLE_AMBIENT_OCTREE,true); - bind_baked_light_octree=true; - if (prev_baked_light!=baked_light) { - Texture *tex=texture_owner.get(baked_light->octree_texture); - if (tex) { + if (baked_light->mode == VS::BAKED_LIGHT_OCTREE && baked_light->octree_texture.is_valid() && e->instance->baked_light_octree_xform) { + material_shader.set_conditional(MaterialShaderGLES2::ENABLE_AMBIENT_OCTREE, true); + bind_baked_light_octree = true; + if (prev_baked_light != baked_light) { + Texture *tex = texture_owner.get(baked_light->octree_texture); + if (tex) { - glActiveTexture(GL_TEXTURE0+max_texture_units-3); - glBindTexture(tex->target,tex->tex_id); //bind the texture - } - if (baked_light->light_texture.is_valid()) { - Texture *texl=texture_owner.get(baked_light->light_texture); - if (texl) { - glActiveTexture(GL_TEXTURE0+max_texture_units-4); - glBindTexture(texl->target,texl->tex_id); //bind the light texture + glActiveTexture(GL_TEXTURE0 + max_texture_units - 3); + glBindTexture(tex->target, tex->tex_id); //bind the texture + } + if (baked_light->light_texture.is_valid()) { + Texture *texl = texture_owner.get(baked_light->light_texture); + if (texl) { + glActiveTexture(GL_TEXTURE0 + max_texture_units - 4); + glBindTexture(texl->target, texl->tex_id); //bind the light texture + } } - } + } } - } else if (baked_light->mode==VS::BAKED_LIGHT_LIGHTMAPS) { + else if (baked_light->mode == VS::BAKED_LIGHT_LIGHTMAPS) { - int lightmap_idx = e->instance->baked_lightmap_id; + int lightmap_idx = e->instance->baked_lightmap_id; - material_shader.set_conditional(MaterialShaderGLES2::ENABLE_AMBIENT_LIGHTMAP,false); - bind_baked_lightmap=false; + material_shader.set_conditional(MaterialShaderGLES2::ENABLE_AMBIENT_LIGHTMAP, false); + bind_baked_lightmap = false; - if (baked_light->lightmaps.has(lightmap_idx)) { + if (baked_light->lightmaps.has(lightmap_idx)) { - RID texid = baked_light->lightmaps[lightmap_idx]; + RID texid = baked_light->lightmaps[lightmap_idx]; - if (prev_baked_light!=baked_light || texid!=prev_baked_light_texture) { + if (prev_baked_light != baked_light || texid != prev_baked_light_texture) { - Texture *tex = texture_owner.get(texid); - if (tex) { + Texture *tex = texture_owner.get(texid); + if (tex) { + + glActiveTexture(GL_TEXTURE0 + max_texture_units - 3); + glBindTexture(tex->target, tex->tex_id); //bind the texture + } - glActiveTexture(GL_TEXTURE0+max_texture_units-3); - glBindTexture(tex->target,tex->tex_id); //bind the texture + prev_baked_light_texture = texid; } - prev_baked_light_texture=texid; - } + if (texid.is_valid()) { + material_shader.set_conditional(MaterialShaderGLES2::ENABLE_AMBIENT_LIGHTMAP, true); + bind_baked_lightmap = true; + } - if (texid.is_valid()) { - material_shader.set_conditional(MaterialShaderGLES2::ENABLE_AMBIENT_LIGHTMAP,true); - bind_baked_lightmap=true; } - } } - } - if (int(prev_baked_light!=NULL) ^ int(baked_light!=NULL)) { - rebind=true; + if (int(prev_baked_light != NULL) ^ int(baked_light != NULL)) { + rebind = true; + } } } @@ -10293,7 +10304,11 @@ void RasterizerGLES2::_update_framebuffer() { framebuffer.fbo=0; } +#ifdef TOOLS_ENABLED framebuffer.active=use_fbo; +#else + framebuffer.active=use_fbo && !low_memory_2d; +#endif framebuffer.width=dwidth; framebuffer.height=dheight; framebuffer.scale=scale; @@ -11203,6 +11218,7 @@ RasterizerGLES2::RasterizerGLES2(bool p_compress_arrays,bool p_keep_ram_copy,boo use_fp16_fb=bool(GLOBAL_DEF("rasterizer/fp16_framebuffer",true)); use_shadow_mapping=true; use_fast_texture_filter=!bool(GLOBAL_DEF("rasterizer/trilinear_mipmap_filter",true)); + low_memory_2d=bool(GLOBAL_DEF("rasterizer/low_memory_2d_mode",false)); skel_default.resize(1024*4); for(int i=0;i<1024/3;i++) { diff --git a/drivers/gles2/rasterizer_gles2.h b/drivers/gles2/rasterizer_gles2.h index 507e46ae75..d6d9593da8 100644 --- a/drivers/gles2/rasterizer_gles2.h +++ b/drivers/gles2/rasterizer_gles2.h @@ -105,13 +105,13 @@ class RasterizerGLES2 : public Rasterizer { float anisotropic_level; bool use_half_float; - + bool low_memory_2d; Vector<float> skel_default; Image _get_gl_image_and_format(const Image& p_image, Image::Format p_format, uint32_t p_flags,GLenum& r_gl_format,GLenum& r_gl_internal_format,int &r_gl_components,bool &r_has_alpha_cache,bool &r_compressed); - class RenderTarget; + struct RenderTarget; struct Texture { @@ -305,7 +305,7 @@ class RasterizerGLES2 : public Rasterizer { virtual ~GeometryOwner() {} }; - class Mesh; + struct Mesh; struct Surface : public Geometry { diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp index d57512c936..c8f59ce22b 100644 --- a/drivers/gles2/shader_compiler_gles2.cpp +++ b/drivers/gles2/shader_compiler_gles2.cpp @@ -154,6 +154,9 @@ String ShaderCompilerGLES2::dump_node_code(SL::Node *p_node,int p_level,bool p_a if (vnode->name==vname_vertex && p_assign_left) { vertex_code_writes_vertex=true; } + if (vnode->name == vname_position && p_assign_left) { + vertex_code_writes_position = true; + } if (vnode->name==vname_color_interp) { flags->use_color_interp=true; } @@ -659,6 +662,7 @@ Error ShaderCompilerGLES2::compile(const String& p_code, ShaderLanguage::ShaderT uses_texpixel_size=false; uses_worldvec=false; vertex_code_writes_vertex=false; + vertex_code_writes_position = false; uses_shadow_color=false; uniforms=r_uniforms; flags=&r_flags; @@ -690,6 +694,7 @@ Error ShaderCompilerGLES2::compile(const String& p_code, ShaderLanguage::ShaderT r_flags.uses_texscreen=uses_texscreen; r_flags.uses_texpos=uses_texpos; r_flags.vertex_code_writes_vertex=vertex_code_writes_vertex; + r_flags.vertex_code_writes_position=vertex_code_writes_position; r_flags.uses_discard=uses_discard; r_flags.uses_screen_uv=uses_screen_uv; r_flags.uses_light=uses_light; @@ -778,125 +783,127 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { replace_table["texscreen"]= "texscreen"; replace_table["texpos"]= "texpos"; - mode_replace_table[0]["SRC_VERTEX"]="vertex_in.xyz"; - mode_replace_table[0]["SRC_NORMAL"]="normal_in"; - mode_replace_table[0]["SRC_TANGENT"]="tangent_in"; - mode_replace_table[0]["SRC_BINORMALF"]="binormalf"; - - mode_replace_table[0]["VERTEX"]="vertex_interp"; - mode_replace_table[0]["NORMAL"]="normal_interp"; - mode_replace_table[0]["TANGENT"]="tangent_interp"; - mode_replace_table[0]["BINORMAL"]="binormal_interp"; - mode_replace_table[0]["UV"]="uv_interp.xy"; - mode_replace_table[0]["UV2"]="uv_interp.zw"; - mode_replace_table[0]["COLOR"]="color_interp"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["POSITION"] = "gl_Position"; + + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["SRC_VERTEX"] = "vertex_in.xyz"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["SRC_NORMAL"] = "normal_in"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["SRC_TANGENT"]="tangent_in"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["SRC_BINORMALF"]="binormalf"; + + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["VERTEX"]="vertex_interp"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["NORMAL"]="normal_interp"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["TANGENT"]="tangent_interp"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["BINORMAL"]="binormal_interp"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["UV"]="uv_interp.xy"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["UV2"]="uv_interp.zw"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["COLOR"]="color_interp"; //@TODO convert to glsl stuff - mode_replace_table[0]["SPEC_EXP"]="vertex_specular_exp"; - mode_replace_table[0]["WORLD_MATRIX"]="world_transform"; - mode_replace_table[0]["INV_CAMERA_MATRIX"]="camera_inverse_transform"; - mode_replace_table[0]["PROJECTION_MATRIX"]="projection_transform"; - mode_replace_table[0]["MODELVIEW_MATRIX"]="modelview"; - mode_replace_table[0]["POINT_SIZE"]="gl_PointSize"; - mode_replace_table[0]["VAR1"]="var1_interp"; - mode_replace_table[0]["VAR2"]="var2_interp"; - -// mode_replace_table[0]["SCREEN_POS"]="SCREEN_POS"; -// mode_replace_table[0]["SCREEN_SIZE"]="SCREEN_SIZE"; - mode_replace_table[0]["INSTANCE_ID"]="instance_id"; - mode_replace_table[0]["TIME"]="time"; - - mode_replace_table[1]["VERTEX"]="vertex"; - //mode_replace_table[1]["POSITION"]="IN_POSITION"; - mode_replace_table[1]["NORMAL"]="normal"; - mode_replace_table[1]["TANGENT"]="tangent"; - mode_replace_table[1]["POSITION"]="gl_Position"; - mode_replace_table[1]["BINORMAL"]="binormal"; - mode_replace_table[1]["NORMALMAP"]="normalmap"; - mode_replace_table[1]["NORMALMAP_DEPTH"]="normaldepth"; - mode_replace_table[1]["VAR1"]="var1_interp"; - mode_replace_table[1]["VAR2"]="var2_interp"; - mode_replace_table[1]["UV"]="uv"; - mode_replace_table[1]["UV2"]="uv2"; - mode_replace_table[1]["SCREEN_UV"]="screen_uv"; - mode_replace_table[1]["VAR1"]="var1_interp"; - mode_replace_table[1]["VAR2"]="var2_interp"; - mode_replace_table[1]["COLOR"]="color"; - mode_replace_table[1]["DIFFUSE"]="diffuse.rgb"; - mode_replace_table[1]["DIFFUSE_ALPHA"]="diffuse"; - mode_replace_table[1]["SPECULAR"]="specular"; - mode_replace_table[1]["EMISSION"]="emission"; - mode_replace_table[1]["SHADE_PARAM"]="shade_param"; - mode_replace_table[1]["SPEC_EXP"]="specular_exp"; - mode_replace_table[1]["GLOW"]="glow"; - mode_replace_table[1]["DISCARD"]="discard_"; - mode_replace_table[1]["POINT_COORD"]="gl_PointCoord"; - mode_replace_table[1]["INV_CAMERA_MATRIX"]="camera_inverse_transform"; - - //mode_replace_table[1]["SCREEN_POS"]="SCREEN_POS"; - //mode_replace_table[1]["SCREEN_TEXEL_SIZE"]="SCREEN_TEXEL_SIZE"; - mode_replace_table[1]["TIME"]="time"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["SPEC_EXP"]="vertex_specular_exp"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["WORLD_MATRIX"]="world_transform"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["INV_CAMERA_MATRIX"]="camera_inverse_transform"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["PROJECTION_MATRIX"]="projection_transform"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["MODELVIEW_MATRIX"]="modelview"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["POINT_SIZE"]="gl_PointSize"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["VAR1"]="var1_interp"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["VAR2"]="var2_interp"; + +// mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["SCREEN_POS"]="SCREEN_POS"; +// mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["SCREEN_SIZE"]="SCREEN_SIZE"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["INSTANCE_ID"]="instance_id"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_VERTEX]["TIME"]="time"; + + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["VERTEX"]="vertex"; + //mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["POSITION"]="IN_POSITION"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["NORMAL"]="normal"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["TANGENT"]="tangent"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["POSITION"]="gl_Position"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["BINORMAL"]="binormal"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["NORMALMAP"]="normalmap"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["NORMALMAP_DEPTH"]="normaldepth"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["VAR1"]="var1_interp"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["VAR2"]="var2_interp"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["UV"]="uv"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["UV2"]="uv2"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["SCREEN_UV"]="screen_uv"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["VAR1"]="var1_interp"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["VAR2"]="var2_interp"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["COLOR"]="color"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["DIFFUSE"]="diffuse.rgb"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["DIFFUSE_ALPHA"]="diffuse"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["SPECULAR"]="specular"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["EMISSION"]="emission"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["SHADE_PARAM"]="shade_param"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["SPEC_EXP"]="specular_exp"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["GLOW"]="glow"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["DISCARD"]="discard_"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["POINT_COORD"]="gl_PointCoord"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["INV_CAMERA_MATRIX"]="camera_inverse_transform"; + + //mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["SCREEN_POS"]="SCREEN_POS"; + //mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["SCREEN_TEXEL_SIZE"]="SCREEN_TEXEL_SIZE"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_FRAGMENT]["TIME"]="time"; ////////////// - mode_replace_table[2]["NORMAL"]="normal"; - //mode_replace_table[2]["POSITION"]="IN_POSITION"; - mode_replace_table[2]["LIGHT_DIR"]="light_dir"; - mode_replace_table[2]["LIGHT_DIFFUSE"]="light_diffuse"; - mode_replace_table[2]["LIGHT_SPECULAR"]="light_specular"; - mode_replace_table[2]["EYE_VEC"]="eye_vec"; - mode_replace_table[2]["DIFFUSE"]="mdiffuse"; - mode_replace_table[2]["SPECULAR"]="specular"; - mode_replace_table[2]["SPECULAR_EXP"]="specular_exp"; - mode_replace_table[2]["SHADE_PARAM"]="shade_param"; - mode_replace_table[2]["LIGHT"]="light"; - mode_replace_table[2]["POINT_COORD"]="gl_PointCoord"; - mode_replace_table[2]["TIME"]="time"; - - mode_replace_table[3]["SRC_VERTEX"]="src_vtx"; - mode_replace_table[3]["VERTEX"]="outvec.xy"; - mode_replace_table[3]["WORLD_VERTEX"]="outvec.xy"; - mode_replace_table[3]["UV"]="uv_interp"; - mode_replace_table[3]["COLOR"]="color_interp"; - mode_replace_table[3]["VAR1"]="var1_interp"; - mode_replace_table[3]["VAR2"]="var2_interp"; - mode_replace_table[3]["POINT_SIZE"]="gl_PointSize"; - mode_replace_table[3]["WORLD_MATRIX"]="modelview_matrix"; - mode_replace_table[3]["PROJECTION_MATRIX"]="projection_matrix"; - mode_replace_table[3]["EXTRA_MATRIX"]="extra_matrix"; - mode_replace_table[3]["TIME"]="time"; - - mode_replace_table[4]["POSITION"]="gl_Position"; - mode_replace_table[4]["NORMAL"]="normal"; - mode_replace_table[4]["NORMALMAP"]="normal_map"; - mode_replace_table[4]["NORMALMAP_DEPTH"]="normal_depth"; - mode_replace_table[4]["UV"]="uv_interp"; - mode_replace_table[4]["SRC_COLOR"]="color_interp"; - mode_replace_table[4]["COLOR"]="color"; - mode_replace_table[4]["TEXTURE"]="texture"; - mode_replace_table[4]["TEXTURE_PIXEL_SIZE"]="texpixel_size"; - mode_replace_table[4]["VAR1"]="var1_interp"; - mode_replace_table[4]["VAR2"]="var2_interp"; - mode_replace_table[4]["SCREEN_UV"]="screen_uv"; - mode_replace_table[4]["POINT_COORD"]="gl_PointCoord"; - mode_replace_table[4]["TIME"]="time"; - - mode_replace_table[5]["POSITION"]="gl_Position"; - mode_replace_table[5]["NORMAL"]="normal"; - mode_replace_table[5]["UV"]="uv_interp"; - mode_replace_table[5]["COLOR"]="color"; - mode_replace_table[5]["TEXTURE"]="texture"; - mode_replace_table[5]["TEXTURE_PIXEL_SIZE"]="texpixel_size"; - mode_replace_table[5]["VAR1"]="var1_interp"; - mode_replace_table[5]["VAR2"]="var2_interp"; - mode_replace_table[5]["LIGHT_VEC"]="light_vec"; - mode_replace_table[5]["LIGHT_HEIGHT"]="light_height"; - mode_replace_table[5]["LIGHT_COLOR"]="light"; - mode_replace_table[5]["LIGHT_UV"]="light_uv"; - mode_replace_table[5]["LIGHT"]="light_out"; - mode_replace_table[5]["SHADOW"]="shadow_color"; - mode_replace_table[5]["SCREEN_UV"]="screen_uv"; - mode_replace_table[5]["POINT_COORD"]="gl_PointCoord"; - mode_replace_table[5]["TIME"]="time"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_LIGHT]["NORMAL"]="normal"; + //mode_replace_table[ShaderLanguage::SHADER_MATERIAL_LIGHT]["POSITION"]="IN_POSITION"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_LIGHT]["LIGHT_DIR"]="light_dir"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_LIGHT]["LIGHT_DIFFUSE"]="light_diffuse"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_LIGHT]["LIGHT_SPECULAR"]="light_specular"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_LIGHT]["EYE_VEC"]="eye_vec"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_LIGHT]["DIFFUSE"]="mdiffuse"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_LIGHT]["SPECULAR"]="specular"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_LIGHT]["SPECULAR_EXP"]="specular_exp"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_LIGHT]["SHADE_PARAM"]="shade_param"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_LIGHT]["LIGHT"]="light"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_LIGHT]["POINT_COORD"]="gl_PointCoord"; + mode_replace_table[ShaderLanguage::SHADER_MATERIAL_LIGHT]["TIME"]="time"; + + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["SRC_VERTEX"]="src_vtx"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["VERTEX"]="outvec.xy"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["WORLD_VERTEX"]="outvec.xy"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["UV"]="uv_interp"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["COLOR"]="color_interp"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["VAR1"]="var1_interp"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["VAR2"]="var2_interp"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["POINT_SIZE"]="gl_PointSize"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["WORLD_MATRIX"]="modelview_matrix"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["PROJECTION_MATRIX"]="projection_matrix"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["EXTRA_MATRIX"]="extra_matrix"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX]["TIME"]="time"; + + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["POSITION"]="gl_Position"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["NORMAL"]="normal"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["NORMALMAP"]="normal_map"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["NORMALMAP_DEPTH"]="normal_depth"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["UV"]="uv_interp"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["SRC_COLOR"]="color_interp"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["COLOR"]="color"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["TEXTURE"]="texture"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["TEXTURE_PIXEL_SIZE"]="texpixel_size"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["VAR1"]="var1_interp"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["VAR2"]="var2_interp"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["SCREEN_UV"]="screen_uv"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["POINT_COORD"]="gl_PointCoord"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT]["TIME"]="time"; + + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["POSITION"]="gl_Position"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["NORMAL"]="normal"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["UV"]="uv_interp"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["COLOR"]="color"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["TEXTURE"]="texture"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["TEXTURE_PIXEL_SIZE"]="texpixel_size"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["VAR1"]="var1_interp"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["VAR2"]="var2_interp"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["LIGHT_VEC"]="light_vec"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["LIGHT_HEIGHT"]="light_height"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["LIGHT_COLOR"]="light"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["LIGHT_UV"]="light_uv"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["LIGHT"]="light_out"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["SHADOW"]="shadow_color"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["SCREEN_UV"]="screen_uv"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["POINT_COORD"]="gl_PointCoord"; + mode_replace_table[ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT]["TIME"]="time"; @@ -917,6 +924,7 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { vname_var1_interp="VAR1"; vname_var2_interp="VAR2"; vname_vertex="VERTEX"; + vname_position = "POSITION"; vname_light="LIGHT"; vname_time="TIME"; vname_normalmap="NORMALMAP"; diff --git a/drivers/gles2/shader_compiler_gles2.h b/drivers/gles2/shader_compiler_gles2.h index 43902a7536..46ef2e035f 100644 --- a/drivers/gles2/shader_compiler_gles2.h +++ b/drivers/gles2/shader_compiler_gles2.h @@ -34,7 +34,7 @@ class ShaderCompilerGLES2 { class Uniform; public: - class Flags; + struct Flags; private: ShaderLanguage::ProgramNode *program_node; @@ -55,6 +55,7 @@ private: bool uses_texpixel_size; bool uses_worldvec; bool vertex_code_writes_vertex; + bool vertex_code_writes_position; bool uses_shadow_color; bool sinh_used; @@ -76,6 +77,7 @@ private: StringName vname_var1_interp; StringName vname_var2_interp; StringName vname_vertex; + StringName vname_position; StringName vname_light; StringName vname_time; StringName vname_normalmap; @@ -107,6 +109,7 @@ public: bool uses_texpos; bool uses_normalmap; bool vertex_code_writes_vertex; + bool vertex_code_writes_position; bool uses_discard; bool uses_screen_uv; bool use_color_interp; diff --git a/drivers/gles2/shaders/SCsub b/drivers/gles2/shaders/SCsub index 9679223b16..38177d725f 100644 --- a/drivers/gles2/shaders/SCsub +++ b/drivers/gles2/shaders/SCsub @@ -6,5 +6,3 @@ if env['BUILDERS'].has_key('GLSL120GLES'): env.GLSL120GLES('canvas_shadow.glsl'); env.GLSL120GLES('blur.glsl'); env.GLSL120GLES('copy.glsl'); - - diff --git a/drivers/jpg/SCsub b/drivers/jpg/SCsub index e1fcc5ea89..df91b10a02 100644 --- a/drivers/jpg/SCsub +++ b/drivers/jpg/SCsub @@ -10,4 +10,3 @@ jpg_sources = [ env.drivers_sources+=jpg_sources #env.add_source_files(env.drivers_sources, jpg_sources) - diff --git a/drivers/mpc/SCsub b/drivers/mpc/SCsub index af61d95e4c..32ffdb863f 100644 --- a/drivers/mpc/SCsub +++ b/drivers/mpc/SCsub @@ -19,4 +19,3 @@ env.add_source_files(env.drivers_sources,"*.cpp") #env.add_source_files(env.drivers_sources, mpc_sources) Export('env') - diff --git a/drivers/nedmalloc/SCsub b/drivers/nedmalloc/SCsub index 8c0028b41d..8e6edd1f96 100644 --- a/drivers/nedmalloc/SCsub +++ b/drivers/nedmalloc/SCsub @@ -3,4 +3,3 @@ Export('env'); env.add_source_files(env.drivers_sources,"*.cpp") #env.add_source_files(env.drivers_sources,"*.c") - diff --git a/drivers/nrex/README.md b/drivers/nrex/README.md index f150a5d76f..951b301c1e 100644 --- a/drivers/nrex/README.md +++ b/drivers/nrex/README.md @@ -18,47 +18,42 @@ More details about its use is documented in `nrex.hpp` Currently supported features: * Capturing `()` and non-capturing `(?:)` groups - * Any character `.` + * Any character `.` (includes newlines) * Shorthand caracter classes `\w\W\s\S\d\D` - * User-defined character classes such as `[A-Za-z]` + * 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 `|` - * Backreferences `\1` to `\99` - -To do list: + * ASCII `\xFF` code points * Unicode `\uFFFF` code points + * Positive `(?=)` and negative `(?!)` lookahead + * Positive `(?<=)` and negative `(?<!)` lookbehind (fixed length and no alternations) + * Backreferences `\1` to `\9` (with option to expand to `\99`) ## License Copyright (c) 2015, Zher Huei Lee All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +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. diff --git a/drivers/nrex/SCsub b/drivers/nrex/SCsub index 2441d3061b..a00c7b86f4 100644 --- a/drivers/nrex/SCsub +++ b/drivers/nrex/SCsub @@ -1,4 +1,3 @@ - Import('env') sources = [ @@ -6,4 +5,3 @@ sources = [ 'regex.cpp', ] env.add_source_files(env.drivers_sources, sources) - diff --git a/drivers/nrex/nrex.cpp b/drivers/nrex/nrex.cpp index 696d46240e..104e07f887 100644 --- a/drivers/nrex/nrex.cpp +++ b/drivers/nrex/nrex.cpp @@ -29,11 +29,13 @@ #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 @@ -116,34 +118,72 @@ class nrex_array } }; -static nrex_char nrex_unescape(nrex_char repr) +static int nrex_parse_hex(nrex_char c) { - switch (repr) + 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') { - case '^': return '^'; - case '$': return '$'; - case '(': return '('; - case ')': return ')'; - case '\\': return '\\'; - case '.': return '.'; - case '+': return '+'; - case '*': return '*'; - case '?': return '?'; - case '-': return '-'; - case 'a': return '\a'; - case 'e': return '\e'; - case 'f': return '\f'; - case 'n': return '\n'; - case 'r': return '\r'; - case 't': return '\t'; - case 'v': return '\v'; + return int(c - 'A') + 10; } - return 0; + 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 { - public: const nrex_char* str; nrex_result* captures; int end; @@ -168,12 +208,14 @@ struct nrex_node 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) { } @@ -206,21 +248,57 @@ struct nrex_node } 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 nrex_node_group : public nrex_node { - int capturing; + static const int NonCapture = -1; + static const int Bracket = -2; + static const int LookAhead = -3; + static const int LookBehind = -4; + + int mode; bool negate; nrex_array<nrex_node*> childset; nrex_node* back; - nrex_node_group(int capturing) + nrex_node_group(int mode) : nrex_node(true) - , capturing(capturing) + , mode(mode) , negate(false) , back(NULL) { + if (mode != Bracket) + { + length = 0; + } + else + { + length = 1; + } } virtual ~nrex_node_group() @@ -234,14 +312,19 @@ struct nrex_node_group : public nrex_node int test(nrex_search* s, int pos) const { - if (capturing >= 0) + if (mode >= 0) { - s->captures[capturing].start = pos; + s->captures[mode].start = pos; } for (unsigned int i = 0; i < childset.size(); ++i) { s->complete = false; - int res = childset[i]->test(s, pos); + int offset = 0; + if (mode == LookBehind) + { + offset = length; + } + int res = childset[i]->test(s, pos - offset); if (s->complete) { return res; @@ -256,12 +339,20 @@ struct nrex_node_group : public nrex_node { return -1; } + if (i + 1 < childset.size()) + { + continue; + } } if (res >= 0) { - if (capturing >= 0) + if (mode >= 0) { - s->captures[capturing].length = res - pos; + s->captures[mode].length = res - pos; + } + else if (mode == LookAhead || mode == LookBehind) + { + res = pos; } return next ? next->test(s, res) : res; } @@ -271,15 +362,19 @@ struct nrex_node_group : public nrex_node virtual int test_parent(nrex_search* s, int pos) const { - if (capturing >= 0) + if (mode >= 0) { - s->captures[capturing].length = pos - s->captures[capturing].start; + s->captures[mode].length = pos - s->captures[mode].start; } return nrex_node::test_parent(s, pos); } void add_childset() { + if (childset.size() > 0 && mode != Bracket) + { + length = -1; + } back = NULL; } @@ -287,7 +382,7 @@ struct nrex_node_group : public nrex_node { node->parent = this; node->previous = back; - if (back) + if (back && mode != Bracket) { back->next = node; } @@ -295,6 +390,10 @@ struct nrex_node_group : public nrex_node { childset.push(node); } + if (mode != Bracket) + { + increment_length(node->length); + } back = node; } @@ -310,10 +409,32 @@ struct nrex_node_group : public nrex_node { childset.pop(); } + if (mode != 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 (mode != Bracket) + { + increment_length(old->length, true); + } + back = old->previous; + NREX_DELETE(old); + } + } }; struct nrex_node_char : public nrex_node @@ -324,6 +445,7 @@ struct nrex_node_char : public nrex_node : nrex_node(true) , ch(c) { + length = 1; } int test(nrex_search* s, int pos) const @@ -346,6 +468,7 @@ struct nrex_node_range : public nrex_node , start(s) , end(e) { + length = 1; } int test(nrex_search* s, int pos) const @@ -363,20 +486,219 @@ struct nrex_node_range : public nrex_node } }; -static bool nrex_is_whitespace(nrex_char repr) +enum nrex_class_type { - switch (repr) + 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) { - case ' ': - case '\t': - case '\r': - case '\n': - case '\f': - return true; + if ((*pos)[i] != text[i]) + { + return false; + } } - 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) + { + 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) @@ -400,6 +722,7 @@ struct nrex_node_shorthand : public nrex_node : nrex_node(true) , repr(c) { + length = 1; } int test(nrex_search* s, int pos) const @@ -435,7 +758,7 @@ struct nrex_node_shorthand : public nrex_node case 'S': invert = true; case 's': - if (nrex_is_whitespace(c)) + if (NREX_ISSPACE(c)) { found = true; } @@ -469,10 +792,10 @@ struct nrex_node_quantifier : public nrex_node bool greedy; nrex_node* child; - nrex_node_quantifier() + nrex_node_quantifier(int min, int max) : nrex_node() - , min(0) - , max(0) + , min(min) + , max(max) , greedy(true) , child(NULL) { @@ -488,17 +811,49 @@ struct nrex_node_quantifier : public nrex_node int test(nrex_search* s, int pos) const { - nrex_array<int> backtrack; - backtrack.push(pos); - while (backtrack.top() <= s->end) + return test_step(s, pos, 1); + } + + int test_step(nrex_search* s, int pos, int level) const + { + if (max == 0) { - if (max >= 1 && backtrack.size() > (unsigned int)max) + return pos; + } + if ((max >= 1 && level > max) || pos > s->end) + { + return -1; + } + if (!greedy && level > min) + { + int res = pos; + if (next) { - break; + res = next->test(s, res); + } + if (s->complete) + { + return res; } - if (!greedy && (unsigned int)min < backtrack.size()) + if (res >= 0 && parent->test_parent(s, res) >= 0) + { + return res; + } + } + int res = child->test(s, pos); + if (s->complete) + { + return res; + } + if (res >= 0) + { + int res_step = test_step(s, res, level + 1); + if (res_step >= 0) + { + return res_step; + } + else if (greedy && level >= min) { - int res = backtrack.top(); if (next) { res = next->test(s, res); @@ -512,33 +867,6 @@ struct nrex_node_quantifier : public nrex_node return res; } } - int res = child->test(s, backtrack.top()); - if (s->complete) - { - return res; - } - if (res < 0 || res == backtrack.top()) - { - break; - } - backtrack.push(res); - } - while (greedy && (unsigned int) min < backtrack.size()) - { - int res = backtrack.top(); - if (next) - { - res = next->test(s, res); - } - if (res >= 0 && parent->test_parent(s, res) >= 0) - { - return res; - } - if (s->complete) - { - return res; - } - backtrack.pop(); } return -1; } @@ -552,6 +880,7 @@ struct nrex_node_anchor : public nrex_node : nrex_node() , end(end) { + length = 0; } int test(nrex_search* s, int pos) const @@ -568,6 +897,45 @@ struct nrex_node_anchor : public nrex_node } }; +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; @@ -576,6 +944,7 @@ struct nrex_node_backreference : public nrex_node : nrex_node(true) , ref(ref) { + length = -1; } int test(nrex_search* s, int pos) const @@ -596,6 +965,18 @@ struct nrex_node_backreference : public nrex_node } }; +bool nrex_has_lookbehind(nrex_array<nrex_node_group*>& stack) +{ + for (unsigned int i = 0; i < stack.size(); i++) + { + if (stack[i]->mode == nrex_node_group::LookBehind) + { + return true; + } + } + return false; +} + nrex::nrex() : _capturing(0) , _root(NULL) @@ -630,7 +1011,7 @@ int nrex::capture_size() const return _capturing + 1; } -bool nrex::compile(const nrex_char* pattern) +bool nrex::compile(const nrex_char* pattern, bool extended) { reset(); nrex_node_group* root = NREX_NEW(nrex_node_group(_capturing)); @@ -647,16 +1028,32 @@ bool nrex::compile(const nrex_char* pattern) if (c[2] == ':') { c = &c[2]; - nrex_node_group* group = NREX_NEW(nrex_node_group(-1)); + nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_node_group::NonCapture)); + 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_node_group::LookAhead)); + group->negate = (c[0] == '!'); + stack.top()->add_child(group); + stack.push(group); + } + else if (c[2] == '<' && (c[3] == '!' || c[3] == '=')) + { + c = &c[3]; + nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_node_group::LookBehind)); + group->negate = (c[0] == '!'); stack.top()->add_child(group); stack.push(group); } else { - NREX_COMPILE_ERROR("unrecognised qualifier for parenthesis"); + NREX_COMPILE_ERROR("unrecognised qualifier for group"); } } - else if (_capturing < 99) + else if ((!extended && _capturing < 9) || (extended && _capturing < 99)) { nrex_node_group* group = NREX_NEW(nrex_node_group(++_capturing)); stack.top()->add_child(group); @@ -664,7 +1061,7 @@ bool nrex::compile(const nrex_char* pattern) } else { - nrex_node_group* group = NREX_NEW(nrex_node_group(-1)); + nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_node_group::NonCapture)); stack.top()->add_child(group); stack.push(group); } @@ -682,152 +1079,233 @@ bool nrex::compile(const nrex_char* pattern) } else if (c[0] == '[') { - nrex_node_group* group = NREX_NEW(nrex_node_group(-1)); + nrex_node_group* group = NREX_NEW(nrex_node_group(nrex_node_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 character class '[]'"); + NREX_COMPILE_ERROR("unclosed bracket expression '['"); } - if (c[0] == ']') + 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] == '\\') { - nrex_char unescaped = nrex_unescape(c[1]); - if (unescaped) - { - group->add_child(NREX_NEW(nrex_node_char(unescaped))); - ++c; - } - else if (nrex_is_shorthand(c[1])) + if (nrex_is_shorthand(c[1])) { group->add_child(NREX_NEW(nrex_node_shorthand(c[1]))); ++c; + previous_child_single = false; } else { - NREX_COMPILE_ERROR("escape token not recognised"); + 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 + else if (previous_child_single && c[0] == '-') { - if (c[1] == '-' && c[2] != '\0') + bool is_range = false; + nrex_char next; + if (c[1] != '\0' && c[1] != ']') { - bool range = false; - if ('A' <= c[0] && c[0] <= 'Z' && 'A' <= c[2] && c[2] <= 'Z') + if (c[1] == '\\') { - range = true; + const nrex_char* d = ++c; + next = nrex_unescape(d); + if (c == d) + { + NREX_COMPILE_ERROR("invalid escape token in range"); + } } - if ('a' <= c[0] && c[0] <= 'z' && 'a' <= c[2] && c[2] <= 'z') - { - range = true; - } - if ('0' <= c[0] && c[0] <= '9' && '0' <= c[2] && c[2] <= '9') + else { - range = true; + next = c[1]; + ++c; } - if (range) + is_range = true; + } + if (is_range) + { + if (next < previous_child) { - group->add_child(NREX_NEW(nrex_node_range(c[0], c[2]))); - c = &c[2]; - continue; + 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])) { - nrex_node_quantifier* quant = NREX_NEW(nrex_node_quantifier); - quant->child = stack.top()->swap_back(quant); - if (quant->child == NULL || !quant->child->quantifiable) + if (stack.top()->back == NULL || !stack.top()->back->quantifiable) { + if (c[0] == '{') + { + stack.top()->add_child(NREX_NEW(nrex_node_char('{'))); + continue; + } NREX_COMPILE_ERROR("element not quantifiable"); } - quant->child->previous = NULL; - quant->child->next = NULL; - quant->child->parent = quant; + int min = 0; + int max = -1; + bool valid_quantifier = true; if (c[0] == '?') { - quant->min = 0; - quant->max = 1; + min = 0; + max = 1; } else if (c[0] == '+') { - quant->min = 1; - quant->max = -1; + min = 1; + max = -1; } else if (c[0] == '*') { - quant->min = 0; - quant->max = -1; + min = 0; + max = -1; } else if (c[0] == '{') { bool max_set = false; - quant->min = 0; - quant->max = -1; + const nrex_char* d = c; while (true) { - ++c; - if (c[0] == '\0') + ++d; + if (d[0] == '\0') { - NREX_COMPILE_ERROR("unclosed range quantifier '{}'"); + valid_quantifier = false; + break; } - else if (c[0] == '}') + else if (d[0] == '}') { break; } - else if (c[0] == ',') + else if (d[0] == ',') { max_set = true; continue; } - else if (c[0] < '0' || '9' < c[0]) + else if (d[0] < '0' || '9' < d[0]) { - NREX_COMPILE_ERROR("expected numeric digits, ',' or '}'"); + valid_quantifier = false; + break; } if (max_set) { - if (quant->max < 0) + if (max < 0) { - quant->max = int(c[0] - '0'); + max = int(d[0] - '0'); } else { - quant->max = quant->max * 10 + int(c[0] - '0'); + max = max * 10 + int(d[0] - '0'); } } else { - quant->min = quant->min * 10 + int(c[0] - '0'); + min = min * 10 + int(d[0] - '0'); } } if (!max_set) { - quant->max = quant->min; + max = min; + } + if (valid_quantifier) + { + c = d; } } - if (c[1] == '?') + if (valid_quantifier) { - quant->greedy = false; - ++c; + 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] == '$') @@ -840,13 +1318,7 @@ bool nrex::compile(const nrex_char* pattern) } else if (c[0] == '\\') { - nrex_char unescaped = nrex_unescape(c[1]); - if (unescaped) - { - stack.top()->add_child(NREX_NEW(nrex_node_char(unescaped))); - ++c; - } - else if (nrex_is_shorthand(c[1])) + if (nrex_is_shorthand(c[1])) { stack.top()->add_child(NREX_NEW(nrex_node_shorthand(c[1]))); ++c; @@ -854,7 +1326,7 @@ bool nrex::compile(const nrex_char* pattern) else if ('1' <= c[1] && c[1] <= '9') { int ref = 0; - if ('0' <= c[2] && c[2] <= '9') + if (extended && '0' <= c[2] && c[2] <= '9') { ref = int(c[1] - '0') * 10 + int(c[2] - '0'); c = &c[2]; @@ -868,11 +1340,27 @@ bool nrex::compile(const nrex_char* pattern) { 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 { - NREX_COMPILE_ERROR("escape token not recognised"); + 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 @@ -880,6 +1368,10 @@ bool nrex::compile(const nrex_char* pattern) stack.top()->add_child(NREX_NEW(nrex_node_char(c[0]))); } } + if (stack.size() > 1) + { + NREX_COMPILE_ERROR("unclosed group '('"); + } return true; } diff --git a/drivers/nrex/nrex.hpp b/drivers/nrex/nrex.hpp index 2a6aa08e1d..e26a61c39a 100644 --- a/drivers/nrex/nrex.hpp +++ b/drivers/nrex/nrex.hpp @@ -79,7 +79,8 @@ class nrex * 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. The result is - * always capped at 100. + * always capped at 10 or 100, depending on the extend option given in + * nrex::compile() (default 10). * * \return The number of captures */ @@ -95,10 +96,13 @@ class nrex * runtime error nrex_compile_error if it encounters a problem when * parsing the pattern. * - * \param The regex pattern + * \param pattern The regex pattern + * \param extended If true, raises the limit on number of capture + * groups and back-references to 99. Otherwise limited + * to 9. Defaults to false. * \return True if the pattern was succesfully compiled */ - bool compile(const nrex_char* pattern); + bool compile(const nrex_char* pattern, bool extended = false); /*! * \brief Uses the pattern to search through the provided string diff --git a/drivers/nrex/regex.cpp b/drivers/nrex/regex.cpp index 0a813c3490..246384b10a 100644 --- a/drivers/nrex/regex.cpp +++ b/drivers/nrex/regex.cpp @@ -15,7 +15,7 @@ void RegEx::_bind_methods() { - ObjectTypeDB::bind_method(_MD("compile","pattern"),&RegEx::compile); + ObjectTypeDB::bind_method(_MD("compile","pattern", "expanded"),&RegEx::compile, DEFVAL(true)); 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); @@ -54,7 +54,9 @@ bool RegEx::is_valid() const { }; int RegEx::get_capture_count() const { - + + ERR_FAIL_COND_V( !exp.valid(), 0 ); + return exp.capture_size(); } @@ -66,11 +68,11 @@ String RegEx::get_capture(int capture) const { } -Error RegEx::compile(const String& p_pattern) { +Error RegEx::compile(const String& p_pattern, bool expanded) { clear(); - exp.compile(p_pattern.c_str()); + exp.compile(p_pattern.c_str(), expanded); ERR_FAIL_COND_V( !exp.valid(), FAILED ); diff --git a/drivers/nrex/regex.h b/drivers/nrex/regex.h index 0626029705..be52da8149 100644 --- a/drivers/nrex/regex.h +++ b/drivers/nrex/regex.h @@ -36,7 +36,7 @@ public: bool is_valid() const; int get_capture_count() const; String get_capture(int capture) const; - Error compile(const String& p_pattern); + Error compile(const String& p_pattern, bool expanded = false); int find(const String& p_text, int p_start = 0, int p_end = -1) const; RegEx(); diff --git a/drivers/ogg/SCsub b/drivers/ogg/SCsub index dd59890064..c85d4404f3 100644 --- a/drivers/ogg/SCsub +++ b/drivers/ogg/SCsub @@ -8,4 +8,3 @@ ogg_sources = [ if env['theora'] != "yes" or env['use_theoraplayer_binary'] != "yes": env.drivers_sources+=ogg_sources - diff --git a/drivers/openssl/SCsub b/drivers/openssl/SCsub index 7197364e01..6d3e7e6732 100644 --- a/drivers/openssl/SCsub +++ b/drivers/openssl/SCsub @@ -4,5 +4,3 @@ env.add_source_files(env.drivers_sources,"*.cpp") env.add_source_files(env.drivers_sources,"*.c") Export('env') - - diff --git a/drivers/opus/opus_config.h b/drivers/opus/opus_config.h index c6470e92c3..98c3e87cc6 100644 --- a/drivers/opus/opus_config.h +++ b/drivers/opus/opus_config.h @@ -7,12 +7,16 @@ /* Define to 1 if you have the <inttypes.h> header file. */ #define HAVE_INTTYPES_H 1 +#if (!defined( _MSC_VER ) || ( _MSC_VER >= 1800 )) + /* Define to 1 if you have the `lrint' function. */ #define HAVE_LRINT 1 /* Define to 1 if you have the `lrintf' function. */ #define HAVE_LRINTF 1 +#endif + /* Define to 1 if you have the <memory.h> header file. */ #define HAVE_MEMORY_H 1 @@ -109,7 +113,11 @@ /* Define to the equivalent of the C99 'restrict' keyword, or to nothing if this is not supported. Do not define if restrict is supported directly. */ +#if (!defined( _MSC_VER ) || ( _MSC_VER >= 1800 )) #define restrict __restrict +#else +#undef restrict +#endif /* Work around a bug in Sun C++: it does not support _Restrict or __restrict__, even though the corresponding Sun C compiler ends up with "#define restrict _Restrict" or "#define restrict __restrict__" in the diff --git a/drivers/png/SCsub b/drivers/png/SCsub index 7b937d4dfb..9ee066cbb2 100644 --- a/drivers/png/SCsub +++ b/drivers/png/SCsub @@ -38,4 +38,3 @@ env.drivers_sources+=png_sources #env.add_source_files(env.drivers_sources, png_sources) Export('env') - diff --git a/drivers/register_driver_types.cpp b/drivers/register_driver_types.cpp index 2647d23011..c4ab54d102 100644 --- a/drivers/register_driver_types.cpp +++ b/drivers/register_driver_types.cpp @@ -29,6 +29,11 @@ #include "convex_decomp/b2d_decompose.h" #endif +#ifdef TOOLS_ENABLED +#include "pe_bliss/pe_bliss_godot.h" +#include "platform/windows/export/export.h" +#endif + #ifdef TREMOR_ENABLED #include "teora/audio_stream_ogg.h" #endif @@ -246,7 +251,7 @@ void register_driver_types() { #ifdef ETC1_ENABLED _register_etc1_compress_func(); #endif - + initialize_chibi(); } diff --git a/drivers/squish/SCsub b/drivers/squish/SCsub index d55a32ad5e..da39dc1ebc 100644 --- a/drivers/squish/SCsub +++ b/drivers/squish/SCsub @@ -21,4 +21,3 @@ if (env["tools"]=="yes"): #env.add_source_files(env.drivers_sources, squish_sources) Export('env') - diff --git a/drivers/theora/SCsub b/drivers/theora/SCsub index ecabce6c9d..faa1ede6a7 100644 --- a/drivers/theora/SCsub +++ b/drivers/theora/SCsub @@ -1,4 +1,3 @@ - Import('env') sources = [ @@ -34,5 +33,3 @@ sources = [ if env['use_theoraplayer_binary'] != "yes": env.drivers_sources += sources - - diff --git a/drivers/theora/decode.c b/drivers/theora/decode.c index 7be66463d8..882606ae77 100644 --- a/drivers/theora/decode.c +++ b/drivers/theora/decode.c @@ -1611,28 +1611,35 @@ static void oc_filter_hedge(unsigned char *_dst,int _dst_ystride, int sum1; int bx; int by; + int _rlimit1; + int _rlimit2; rdst=_dst; rsrc=_src; - for(bx=0;bx<8;bx++){ + for(bx=0;bx<8;++bx){ cdst=rdst; csrc=rsrc; - for(by=0;by<10;by++){ + _rlimit1 = _rlimit2 = _flimit; + for(by=0;by<10;++by){ r[by]=*csrc; csrc+=_src_ystride; } sum0=sum1=0; - for(by=0;by<4;by++){ - sum0+=abs(r[by+1]-r[by]); - sum1+=abs(r[by+5]-r[by+6]); + for(by=0;by<4;++by){ + int sumed = abs(r[by+1]-r[by]); + sum0+=sumed; + _rlimit1-=sumed; + sumed = abs(r[by+5]-r[by+6]); + sum1+=sumed; + _rlimit2-=sumed; } *_variance0+=OC_MINI(255,sum0); *_variance1+=OC_MINI(255,sum1); - if(sum0<_flimit&&sum1<_flimit&&r[5]-r[4]<_qstep&&r[4]-r[5]<_qstep){ + if(_rlimit1&&_rlimit2&&!(r[5]-r[4]-_qstep)&&!(r[4]-r[5]-_qstep)){ *cdst=(unsigned char)(r[0]*3+r[1]*2+r[2]+r[3]+r[4]+4>>3); cdst+=_dst_ystride; *cdst=(unsigned char)(r[0]*2+r[1]+r[2]*2+r[3]+r[4]+r[5]+4>>3); cdst+=_dst_ystride; - for(by=0;by<4;by++){ + for(by=0;by<4;++by){ *cdst=(unsigned char)(r[by]+r[by+1]+r[by+2]+r[by+3]*2+ r[by+4]+r[by+5]+r[by+6]+4>>3); cdst+=_dst_ystride; @@ -1642,13 +1649,13 @@ static void oc_filter_hedge(unsigned char *_dst,int _dst_ystride, *cdst=(unsigned char)(r[5]+r[6]+r[7]+r[8]*2+r[9]*3+4>>3); } else{ - for(by=1;by<=8;by++){ + for(by=1;by<=8;++by){ *cdst=(unsigned char)r[by]; cdst+=_dst_ystride; } } - rdst++; - rsrc++; + ++rdst; + ++rsrc; } } @@ -1663,19 +1670,26 @@ static void oc_filter_vedge(unsigned char *_dst,int _dst_ystride, int sum1; int bx; int by; + int _rlimit1; + int _rlimit2; cdst=_dst; - for(by=0;by<8;by++){ + for(by=0;by<8;++by){ rsrc=cdst-1; rdst=cdst; - for(bx=0;bx<10;bx++)r[bx]=*rsrc++; + for(bx=0;bx<10;++bx)r[bx]=*rsrc++; sum0=sum1=0; - for(bx=0;bx<4;bx++){ - sum0+=abs(r[bx+1]-r[bx]); - sum1+=abs(r[bx+5]-r[bx+6]); + _rlimit1 = _rlimit2 = _flimit; + for(bx=0;bx<4;++bx){ + int sumed = abs(r[bx+1]-r[bx]); + sum0+=sumed; + _rlimit1-=sumed; + sumed = abs(r[bx+5]-r[bx+6]); + sum1+=sumed; + _rlimit2-=sumed; } _variances[0]+=OC_MINI(255,sum0); _variances[1]+=OC_MINI(255,sum1); - if(sum0<_flimit&&sum1<_flimit&&r[5]-r[4]<_qstep&&r[4]-r[5]<_qstep){ + if(_rlimit1&&_rlimit2&&!(r[5]-r[4]-_qstep)&&!(r[4]-r[5]-_qstep)){ *rdst++=(unsigned char)(r[0]*3+r[1]*2+r[2]+r[3]+r[4]+4>>3); *rdst++=(unsigned char)(r[0]*2+r[1]+r[2]*2+r[3]+r[4]+r[5]+4>>3); for(bx=0;bx<4;bx++){ diff --git a/drivers/theora/video_stream_theora.cpp b/drivers/theora/video_stream_theora.cpp index ed87227876..ed2565177a 100644 --- a/drivers/theora/video_stream_theora.cpp +++ b/drivers/theora/video_stream_theora.cpp @@ -243,7 +243,9 @@ void VideoStreamPlaybackTheora::set_file(const String& p_file) { /* Only interested in Vorbis/Theora streams */ int stateflag = 0; - int audio_track_skip=audio_track; + int audio_track_skip=audio_track; + + while(!stateflag){ int ret=buffer_data(); if(ret==0)break; @@ -269,15 +271,21 @@ void VideoStreamPlaybackTheora::set_file(const String& p_file) { copymem(&to,&test,sizeof(test)); theora_p=1; }else if(!vorbis_p && vorbis_synthesis_headerin(&vi,&vc,&op)>=0){ + + /* it is vorbis */ - if (audio_track_skip) { - vorbis_info_clear(&vi); - vorbis_comment_clear(&vc); - audio_track_skip--; - } else { - copymem(&vo,&test,sizeof(test)); - vorbis_p=1; - } + if (audio_track_skip) { + vorbis_info_clear(&vi); + vorbis_comment_clear(&vc); + ogg_stream_clear(&test); + vorbis_info_init(&vi); + vorbis_comment_init(&vc); + + audio_track_skip--; + } else { + copymem(&vo,&test,sizeof(test)); + vorbis_p=1; + } }else{ /* whatever it is, we don't care about it */ ogg_stream_clear(&test); diff --git a/drivers/unix/SCsub b/drivers/unix/SCsub index bcd231579c..9fbb467baa 100644 --- a/drivers/unix/SCsub +++ b/drivers/unix/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.drivers_sources,"*.cpp") Export('env') - - diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 8617061ad4..fd8c26f6d9 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -65,15 +65,25 @@ void OS_Unix::print_error(const char* p_function,const char* p_file,int p_line,c if (!_print_error_enabled) return; - if (p_rationale && p_rationale[0]) { - - print("\E[1;31;40mERROR: %s: \E[1;37;40m%s\n",p_function,p_rationale); - print("\E[0;31;40m At: %s:%i.\E[0;0;37m\n",p_file,p_line); - - } else { - print("\E[1;31;40mERROR: %s: \E[1;37;40m%s\n",p_function,p_code); - print("\E[0;31;40m At: %s:%i.\E[0;0;37m\n",p_file,p_line); - + const char* err_details; + if (p_rationale && p_rationale[0]) + err_details=p_rationale; + else + err_details=p_code; + + switch(p_type) { + case ERR_ERROR: + print("\E[1;31mERROR: %s: \E[0m\E[1m%s\n",p_function,err_details); + print("\E[0;31m At: %s:%i.\E[0m\n",p_file,p_line); + break; + case ERR_WARNING: + print("\E[1;33mWARNING: %s: \E[0m\E[1m%s\n",p_function,err_details); + print("\E[0;33m At: %s:%i.\E[0m\n",p_file,p_line); + break; + case ERR_SCRIPT: + print("\E[1;35mSCRIPT ERROR: %s: \E[0m\E[1m%s\n",p_function,err_details); + print("\E[0;35m At: %s:%i.\E[0m\n",p_file,p_line); + break; } } diff --git a/drivers/vorbis/SCsub b/drivers/vorbis/SCsub index 2c137629ac..4a16e80dfc 100644 --- a/drivers/vorbis/SCsub +++ b/drivers/vorbis/SCsub @@ -1,4 +1,3 @@ - Import('env') sources = [ @@ -37,4 +36,3 @@ env.drivers_sources += sources if env['theora'] != "yes" or env['use_theoraplayer_binary'] != "yes": env.drivers_sources += sources_lib - diff --git a/drivers/vorbis/audio_stream_ogg_vorbis.h b/drivers/vorbis/audio_stream_ogg_vorbis.h index 827d8b0be3..5dbada962a 100644 --- a/drivers/vorbis/audio_stream_ogg_vorbis.h +++ b/drivers/vorbis/audio_stream_ogg_vorbis.h @@ -85,7 +85,7 @@ public: virtual void stop(); virtual bool is_playing() const; - virtual void set_loop_restart_time(float p_time) { loop_restart_time=0; } + virtual void set_loop_restart_time(float p_time) { loop_restart_time=p_time; } virtual void set_paused(bool p_paused); virtual bool is_paused(bool p_paused) const; diff --git a/drivers/webp/SCsub b/drivers/webp/SCsub index 3ae046ff79..5596edbe09 100644 --- a/drivers/webp/SCsub +++ b/drivers/webp/SCsub @@ -61,4 +61,3 @@ env.drivers_sources+=webp_sources #env.add_source_files(env.drivers_sources, webp_sources) Export('env') - diff --git a/drivers/windows/SCsub b/drivers/windows/SCsub index bcd231579c..9fbb467baa 100644 --- a/drivers/windows/SCsub +++ b/drivers/windows/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.drivers_sources,"*.cpp") Export('env') - - diff --git a/godot_icon.png b/godot_icon.png Binary files differnew file mode 100644 index 0000000000..013632ddf1 --- /dev/null +++ b/godot_icon.png diff --git a/godot_icon.svg b/godot_icon.svg new file mode 100644 index 0000000000..6e32074d89 --- /dev/null +++ b/godot_icon.svg @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="1024" + height="1024" + id="svg3030" + version="1.1" + inkscape:version="0.48.4 r9939" + sodipodi:docname="godot_icon.svg"> + <defs + id="defs3032" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.24748737" + inkscape:cx="340.91041" + inkscape:cy="224.06536" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="1366" + inkscape:window-height="748" + inkscape:window-x="-2" + inkscape:window-y="-3" + inkscape:window-maximized="1" /> + <metadata + id="metadata3035"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-28.362183)"> + <rect + style="fill:#a39f9f;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="rect4009" + width="990" + height="990" + x="20" + y="47.362183" + ry="187.81349" /> + <path + style="fill:#ffffff;fill-opacity:1;stroke:none" + d="m 116.99388,715.36604 43.13957,-74.51381 75.99672,-171.42666 271.088,-13.63746 282.06373,14.1696 138.45065,255.56931 -25.0756,66.96734 -376.12685,53.39482 -367.70391,-40.32222 z" + id="path3239" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccccccc" /> + <g + id="g3412" + transform="matrix(12.995388,0,0,-12.995388,898.37246,704.73082)"> + <path + inkscape:connector-curvature="0" + d="m 0,0 0,-3.942 c 0,-0.39 -0.25,-0.734 -0.621,-0.852 L -6.835,-6.8 c -0.273,-0.091 -0.57,-0.042 -0.8,0.128 -0.232,0.168 -0.37,0.437 -0.37,0.721 l 0,4.305 -5.818,-1.108 0,-4.381 c 0,-0.447 -0.332,-0.824 -0.775,-0.885 l -8.41,-1.152 c -0.039,-0.003 -0.081,-0.008 -0.121,-0.008 -0.214,0 -0.424,0.078 -0.588,0.22 -0.195,0.172 -0.306,0.416 -0.306,0.676 l 0,4.638 -4.341,-0.018 0,-10e-4 -0.318,10e-4 -0.319,-10e-4 0,10e-4 -4.34,0.018 0,-4.638 c 0,-0.26 -0.112,-0.504 -0.307,-0.676 -0.164,-0.142 -0.374,-0.22 -0.587,-0.22 -0.041,0 -0.082,0.005 -0.123,0.008 l -8.41,1.152 c -0.442,0.061 -0.774,0.438 -0.774,0.885 l 0,4.381 -5.819,1.108 0,-4.305 c 0,-0.284 -0.137,-0.553 -0.368,-0.721 -0.232,-0.17 -0.529,-0.219 -0.802,-0.128 l -6.215,2.006 c -0.369,0.118 -0.619,0.462 -0.619,0.852 l 0,3.942 -3.837,1.29 c -0.19,-0.811 -0.295,-1.642 -0.295,-2.481 0,-10.301 14.512,-18.252 32.448,-18.309 l 0.022,0 0.023,0 c 17.936,0.057 32.448,8.008 32.448,18.309 0,0.766 -0.088,1.521 -0.247,2.266 L 0,0 z" + style="fill:#478cbf;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path3414" /> + </g> + <g + id="g3416" + transform="matrix(12.995388,0,0,-12.995388,140.10982,467.34929)"> + <path + inkscape:connector-curvature="0" + d="m 0,0 0,-16.047 2.163,-0.729 c 0.364,-0.122 0.61,-0.462 0.61,-0.847 l 0,-3.936 4.426,-1.428 0,4.154 c 0,0.27 0.118,0.52 0.323,0.689 0.206,0.172 0.474,0.241 0.739,0.192 l 7.608,-1.452 c 0.422,-0.079 0.728,-0.448 0.728,-0.877 l 0,-4.338 6.62,-0.904 0,4.509 c 0,0.241 0.096,0.467 0.264,0.635 0.167,0.166 0.394,0.259 0.633,0.259 l 0.002,0 5.551,-0.022 5.549,0.022 c 0.245,-10e-4 0.468,-0.093 0.635,-0.259 0.169,-0.168 0.264,-0.394 0.264,-0.635 l 0,-4.509 6.621,0.904 0,4.338 c 0,0.429 0.304,0.798 0.726,0.877 l 7.609,1.452 c 0.262,0.049 0.533,-0.02 0.738,-0.192 0.205,-0.169 0.325,-0.419 0.325,-0.689 l 0,-4.154 4.425,1.428 0,3.936 c 0,0.385 0.245,0.725 0.609,0.847 l 1.475,0.497 0,16.279 0.04,0 c 1.437,1.834 2.767,3.767 4.042,5.828 -1.694,2.883 -3.768,5.459 -5.986,7.846 -2.057,-1.035 -4.055,-2.208 -5.942,-3.456 -0.944,0.938 -2.008,1.706 -3.052,2.509 -1.027,0.824 -2.183,1.428 -3.281,2.132 0.327,2.433 0.489,4.828 0.554,7.327 -2.831,1.424 -5.85,2.369 -8.903,3.047 -1.219,-2.048 -2.334,-4.267 -3.304,-6.436 -1.152,0.192 -2.309,0.264 -3.467,0.277 l 0,0.002 c -0.008,0 -0.015,-0.002 -0.022,-0.002 -0.008,0 -0.015,0.002 -0.022,0.002 l 0,-0.002 c -1.16,-0.013 -2.316,-0.085 -3.468,-0.277 -0.97,2.169 -2.084,4.388 -3.305,6.436 C 19.475,24.555 16.456,23.61 13.626,22.186 13.69,19.687 13.852,17.292 14.18,14.859 13.081,14.155 11.925,13.551 10.898,12.727 9.855,11.924 8.79,11.156 7.846,10.218 5.958,11.466 3.961,12.639 1.904,13.674 -0.314,11.287 -2.388,8.711 -4.082,5.828 -2.807,3.767 -1.477,1.834 -0.04,0 L 0,0 z" + style="fill:#478cbf;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path3418" /> + </g> + <g + id="g3420" + transform="matrix(12.995388,0,0,-12.995388,411.4457,567.42812)"> + <path + inkscape:connector-curvature="0" + d="m 0,0 c 0,-3.611 -2.926,-6.537 -6.537,-6.537 -3.608,0 -6.535,2.926 -6.535,6.537 0,3.609 2.927,6.533 6.535,6.533 C -2.926,6.533 0,3.609 0,0" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path3422" /> + </g> + <g + id="g3424" + transform="matrix(12.995388,0,0,-12.995388,391.00655,572.46636)"> + <path + inkscape:connector-curvature="0" + d="m 0,0 c 0,-2.396 -1.941,-4.337 -4.339,-4.337 -2.396,0 -4.339,1.941 -4.339,4.337 0,2.396 1.943,4.339 4.339,4.339 C -1.941,4.339 0,2.396 0,0" + style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path3426" /> + </g> + <g + id="g3428" + transform="matrix(12.995388,0,0,-12.995388,526.30933,660.10985)"> + <path + inkscape:connector-curvature="0" + d="m 0,0 c -1.162,0 -2.104,0.856 -2.104,1.912 l 0,6.018 c 0,1.054 0.942,1.912 2.104,1.912 1.162,0 2.106,-0.858 2.106,-1.912 l 0,-6.018 C 2.106,0.856 1.162,0 0,0" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path3430" /> + </g> + <g + id="g3432" + transform="matrix(12.995388,0,0,-12.995388,641.18731,567.42812)"> + <path + inkscape:connector-curvature="0" + d="m 0,0 c 0,-3.611 2.926,-6.537 6.537,-6.537 3.609,0 6.535,2.926 6.535,6.537 0,3.609 -2.926,6.533 -6.535,6.533 C 2.926,6.533 0,3.609 0,0" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path3434" /> + </g> + <g + id="g3436" + transform="matrix(12.995388,0,0,-12.995388,661.63165,572.46636)"> + <path + inkscape:connector-curvature="0" + d="m 0,0 c 0,-2.396 1.941,-4.337 4.336,-4.337 2.398,0 4.339,1.941 4.339,4.337 0,2.396 -1.941,4.339 -4.339,4.339 C 1.941,4.339 0,2.396 0,0" + style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path3438" /> + </g> + </g> +</svg> diff --git a/main/SCsub b/main/SCsub index 795c427c8d..fa60ffc3e8 100644 --- a/main/SCsub +++ b/main/SCsub @@ -8,5 +8,3 @@ Export('env') lib = env.Library("main",env.main_sources) env.Prepend(LIBS=[lib]) - - diff --git a/main/main.cpp b/main/main.cpp index 9cd190a0e8..b6bc10cee7 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -796,7 +796,6 @@ Error Main::setup(const char *execpath,int argc, char *argv[],bool p_second_phas main_args.clear(); print_help(execpath); - if (performance) memdelete(performance); @@ -812,6 +811,8 @@ Error Main::setup(const char *execpath,int argc, char *argv[],bool p_second_phas memdelete(packed_data); if (file_access_network_client) memdelete(file_access_network_client); + if(path_remap) + memdelete(path_remap); // Note 1: *zip_packed_data live into *packed_data // Note 2: PackedData::~PackedData destroy this. @@ -820,7 +821,7 @@ Error Main::setup(const char *execpath,int argc, char *argv[],bool p_second_phas // memdelete( zip_packed_data ); //#endif - + unregister_core_driver_types(); unregister_core_types(); OS::get_singleton()->_cmdline.clear(); @@ -1007,8 +1008,21 @@ bool Main::start() { bool export_debug=false; List<String> args = OS::get_singleton()->get_cmdline_args(); for (int i=0;i<args.size();i++) { + //parameters that do not have an argument to the right + if (args[i]=="-nodocbase") { + doc_base=false; + } else if (args[i]=="-noquit") { + noquit=true; + } else if (args[i]=="-convert_old") { + convert_old=true; + } else if (args[i]=="-editor" || args[i]=="-e") { + editor=true; + } else if (args[i].length() && args[i][0] != '-' && game_path == "") { + game_path=args[i]; + } //parameters that have an argument to the right - if (i < (args.size()-1)) { + else if (i < (args.size()-1)) { + bool parsed_pair=true; if (args[i]=="-doctool") { doc_tool=args[i+1]; } else if (args[i]=="-script" || args[i]=="-s") { @@ -1037,20 +1051,13 @@ bool Main::start() { } else if (args[i]=="-dumpstrings") { editor=true; //needs editor dumpstrings=args[i+1]; + } else { + // The parameter does not match anything known, don't skip the next argument + parsed_pair=false; + } + if (parsed_pair) { + i++; } - i++; - } - //parameters that do not have an argument to the right - if (args[i]=="-nodocbase") { - doc_base=false; - } else if (args[i]=="-noquit") { - noquit=true; - } else if (args[i]=="-convert_old") { - convert_old=true; - } else if (args[i]=="-editor" || args[i]=="-e") { - editor=true; - } else if (args[i].length() && args[i][0] != '-' && game_path == "") { - game_path=args[i]; } } diff --git a/main/splash.h b/main/splash.h index 6ad0062e24..f69383cf00 100644 --- a/main/splash.h +++ b/main/splash.h @@ -34,7 +34,7 @@ static const unsigned char boot_splash_png[]={ }; static const unsigned char app_icon_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0x80,0x8,0x6,0x0,0x0,0x0,0xc3,0x3e,0x61,0xcb,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0x32,0xb8,0x0,0x0,0x32,0xb8,0x1,0x28,0xf3,0x26,0x89,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x1,0x19,0x15,0x3b,0x3a,0x14,0xc2,0xb1,0x4b,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x20,0x0,0x49,0x44,0x41,0x54,0x78,0xda,0xed,0xbd,0x77,0x9c,0x64,0x55,0x99,0x3e,0xfe,0xbc,0xe7,0xdc,0x50,0xb1,0xf3,0x4c,0x4f,0x64,0x98,0x61,0x40,0xc2,0x24,0x92,0xc0,0x22,0x12,0x5c,0x1,0x15,0x50,0x82,0xee,0x2a,0x6,0x14,0x14,0x17,0xdd,0x9f,0x12,0x6,0x56,0x24,0x8c,0xc3,0xa,0xae,0x20,0xa8,0xdf,0x75,0xd7,0x35,0xb0,0xe6,0x48,0x46,0x10,0x90,0xc,0x4a,0x1e,0xc2,0xc,0x99,0xc9,0xb9,0xa7,0x63,0xc5,0x1b,0xce,0x39,0xef,0xef,0x8f,0x5b,0x55,0x5d,0x55,0x5d,0xdd,0x53,0x3d,0x33,0xc0,0xe0,0xce,0xfd,0x30,0x1f,0xaa,0xaa,0x6f,0xdd,0xba,0xf7,0xbc,0xcf,0x79,0xc3,0xf3,0xbe,0xe7,0x3d,0xc0,0xae,0x63,0xd7,0xb1,0xeb,0xd8,0x75,0xec,0x3a,0x76,0x1d,0xef,0xcc,0xe3,0xc0,0xcb,0x1f,0xa1,0x3,0x16,0x3d,0x22,0xde,0xf2,0xdf,0xfd,0xc6,0xa3,0xe2,0xc0,0xcb,0x1f,0xa5,0x77,0xfa,0xf8,0x59,0xef,0xc4,0x9b,0x3e,0xe0,0xf2,0x7,0x25,0x20,0xa4,0xed,0xc4,0xf5,0x13,0x5f,0x3f,0x58,0x3,0xe0,0x93,0x2e,0xfc,0x9d,0x58,0xeb,0x4e,0xec,0x78,0xf6,0x8a,0x63,0x7a,0xdf,0xcc,0xdf,0xde,0xff,0xd2,0x7,0x5b,0x1c,0xe9,0xa9,0x27,0x2e,0x7f,0x4f,0x1,0x0,0xe,0x59,0xfc,0x92,0x8,0x4d,0x9f,0xcd,0x86,0xd5,0xb3,0x8b,0xdf,0xab,0x77,0x1,0xe0,0x2d,0x38,0x58,0x17,0xc,0x5b,0x49,0x14,0xbd,0xdc,0x1e,0x7,0x5c,0xfe,0xc8,0x47,0x41,0xf8,0xe7,0xf5,0x24,0xe6,0x8,0x60,0x25,0x80,0x59,0x6f,0xa,0xe8,0x16,0x3d,0x2a,0x96,0x2c,0x7a,0x8f,0x11,0x96,0x7d,0x95,0x82,0x7d,0xce,0x81,0xdf,0x78,0x74,0x2d,0x1b,0xfc,0x2e,0xd4,0x3d,0xbf,0xd3,0xda,0x7d,0x5e,0x88,0x22,0xbf,0x13,0xc7,0x72,0xa7,0x56,0x61,0xfb,0x5f,0x7a,0xbf,0x90,0xd2,0xa2,0xa7,0x17,0x45,0x33,0xeb,0xe0,0x45,0xf,0xed,0x19,0x6a,0x3a,0x88,0x61,0x8e,0x13,0x24,0x4e,0xb0,0x6d,0xd9,0xe9,0x38,0x36,0x2c,0x5b,0x20,0x11,0x77,0xbc,0xcd,0x3d,0x59,0x9b,0xc0,0x9f,0x7b,0xfa,0xf2,0xf7,0xfc,0x62,0x47,0xdf,0xcb,0x41,0x8b,0x9e,0xb0,0x35,0x79,0xdd,0x82,0xad,0xb5,0xed,0xed,0x71,0x5f,0x8,0x69,0x17,0x8b,0xbe,0x8,0x2,0x85,0x30,0xd4,0x5a,0x69,0x73,0x33,0x11,0xee,0x76,0x2c,0x7a,0xea,0xa9,0xcb,0xdf,0xfb,0x3c,0x0,0x1c,0x76,0xe5,0x33,0xe4,0x7b,0x19,0xb1,0x64,0xf1,0xd1,0x7a,0x17,0x0,0xc6,0x33,0xdb,0x2e,0xbd,0x5f,0x40,0xa,0xb9,0x64,0xd1,0x51,0x21,0x0,0x1c,0x74,0xf9,0x43,0x5f,0x30,0x10,0xff,0x62,0xb4,0x99,0x11,0x8f,0x5b,0xed,0xb1,0x98,0x8d,0x58,0xcc,0x81,0x94,0x92,0xa1,0x3d,0xb0,0x56,0x44,0x96,0x8d,0x82,0x4f,0x18,0x18,0x2c,0xbe,0x24,0x89,0xe,0x66,0xe6,0xe0,0x99,0x6f,0xbc,0x47,0xed,0x10,0x7b,0x7f,0xc5,0x5f,0xc5,0x33,0x97,0x1e,0x6e,0xe,0xfc,0xc6,0x5f,0x97,0xa4,0x92,0xee,0xfe,0x2d,0x29,0x1,0x55,0xcc,0xc3,0x8a,0x25,0x98,0x85,0xd,0xa3,0xd,0x5,0x81,0x86,0xe7,0x85,0xc8,0xe4,0xfc,0x9c,0x14,0xb4,0x5e,0x4a,0xfa,0xb9,0x9,0x6,0xae,0x59,0xf2,0xcd,0x93,0xc2,0x3,0x17,0x3f,0x62,0x99,0x20,0xe4,0x67,0xff,0xfd,0x18,0xbd,0xb,0x0,0xcd,0xcc,0xb6,0xcb,0xef,0xb3,0x95,0x11,0x33,0xc1,0xbc,0x50,0x5a,0xf2,0x2c,0x22,0x20,0x91,0x70,0xc3,0x8e,0xd6,0x98,0xd0,0xc6,0x8,0xa3,0x42,0xd2,0x61,0x0,0x1d,0x78,0x10,0xc2,0x2a,0x3d,0x5,0xc1,0x4e,0xb7,0xa3,0xa7,0x27,0xb,0x15,0xea,0x4f,0x2f,0x59,0x7c,0xe4,0x2f,0xf,0xbc,0xec,0x61,0x7a,0x66,0xf1,0x7b,0x77,0x88,0x6a,0x3e,0xe8,0x8a,0x47,0x4f,0x86,0xc1,0x4d,0x53,0xa6,0xb4,0xea,0x20,0x3b,0x28,0x99,0x35,0xc0,0xc,0x66,0x3,0x21,0x6d,0x8,0xcb,0x81,0xb4,0x1d,0x80,0x84,0xf6,0xfc,0xd0,0xc,0xc,0x16,0x6d,0x36,0xc,0x63,0xcc,0xad,0x6c,0xf8,0x4a,0x5b,0xe2,0xb9,0xa7,0x16,0x1f,0x13,0xec,0x2,0xc0,0xd6,0x66,0xdb,0x65,0xf7,0x7f,0x42,0x69,0x3e,0x83,0x4,0xbd,0x3f,0x9d,0x8c,0x21,0x95,0x8a,0x29,0x4b,0x30,0x19,0x1d,0xca,0xd0,0xf7,0x0,0x36,0x60,0x36,0x20,0x8a,0x1c,0x7f,0xa2,0xf2,0x23,0x30,0xa4,0x1d,0x87,0x22,0x57,0xf5,0xf5,0x17,0xe4,0x33,0x8b,0xde,0x23,0xde,0xfd,0xef,0x7f,0x13,0x4f,0x5e,0xf2,0xf,0x66,0xfb,0x6c,0xff,0xc3,0x94,0x10,0x6c,0xe5,0xb5,0x78,0xa2,0xad,0x25,0xbe,0x20,0x19,0x7,0x85,0x85,0x1c,0x80,0x5a,0x5c,0xb1,0x31,0x60,0x66,0x48,0xdb,0x1,0x9,0x9,0x27,0x16,0x33,0x45,0x9f,0x74,0x18,0x86,0x76,0x2e,0xe7,0xc1,0xf,0xd4,0x93,0x8e,0x25,0x7f,0x96,0x63,0xff,0xc7,0xaf,0x5c,0x71,0x9c,0xda,0x5,0x80,0xba,0x63,0xc1,0x45,0x77,0x4d,0x11,0x8e,0xfb,0x32,0x11,0x25,0xdb,0x5a,0x62,0x32,0x16,0xb7,0x1,0xd6,0x50,0xc5,0x2c,0x18,0x4,0x30,0x57,0xdd,0x31,0x55,0x5e,0x12,0x8,0x24,0xa2,0x3f,0x33,0x80,0x58,0xaa,0x8d,0x37,0xf4,0x14,0x88,0x8d,0xb9,0xfe,0xe9,0x45,0x47,0x9c,0xb9,0x3d,0xf7,0x34,0xff,0x8a,0x47,0xc8,0xd1,0x24,0x14,0xf1,0x89,0x92,0xc4,0xcd,0x53,0xba,0xd3,0xf0,0x73,0x83,0x20,0x36,0x15,0x0,0x94,0x61,0xc0,0x5c,0xd,0x88,0xe8,0xb5,0x20,0x9,0x61,0x3b,0x10,0x4e,0x1c,0xc5,0x62,0x80,0xfe,0x81,0x2,0x8,0xf0,0xc0,0xe6,0x48,0x8,0xf1,0xf4,0x92,0xc5,0x47,0x9b,0xb7,0x7b,0xdc,0xc5,0xce,0x2,0x0,0x19,0xb3,0x93,0x52,0x8a,0x96,0x69,0x93,0x5b,0xb4,0x63,0x19,0xa8,0x42,0x6,0x41,0x7e,0x30,0x92,0x7b,0x69,0x70,0x2b,0x2,0x7,0x20,0x8,0x20,0x2a,0x6b,0x0,0x82,0x10,0x11,0x28,0x42,0xdf,0xa3,0xce,0x8e,0x84,0x56,0x6,0x1f,0x39,0x78,0xf1,0x23,0xd3,0xb7,0x2b,0x44,0x32,0xc2,0x7a,0x6a,0xd1,0x7b,0x34,0x18,0xd7,0xb5,0xa4,0x63,0xd0,0x61,0x0,0x18,0x5d,0xf9,0x5d,0xa2,0xd2,0x7d,0x60,0xf8,0x5f,0xf5,0x61,0x8c,0x86,0xf2,0x8b,0xf0,0x33,0xbd,0x70,0xa4,0xc1,0x6e,0xd3,0xda,0x8c,0x25,0x45,0xcc,0x80,0x12,0x11,0xaa,0xdf,0xfe,0x63,0xe7,0x1,0x80,0x41,0x3f,0xc0,0x60,0xa3,0x84,0xf2,0xf2,0x60,0xa3,0x21,0x48,0x80,0x4a,0x23,0x55,0x2d,0xf0,0xb2,0xd0,0x89,0xa2,0xd9,0x5f,0x16,0x82,0x14,0x4,0x15,0x78,0x88,0x39,0x42,0xba,0x8e,0xe8,0xd0,0x9a,0x3f,0xb9,0xe0,0x92,0xfb,0x68,0xff,0xcb,0x1f,0xd8,0xa6,0xc1,0x7e,0xe6,0xf2,0xc3,0xc3,0x3,0x16,0x3d,0x7c,0xb2,0x25,0xc5,0xee,0x89,0xb8,0xa5,0x95,0x9f,0x87,0x65,0x9,0x88,0x92,0xf0,0x6b,0xee,0xa3,0x1,0x20,0x2a,0x6a,0x96,0x8,0x61,0x61,0x8,0x5a,0x69,0x61,0x59,0x4,0xa3,0x4d,0x8,0xde,0x39,0xa2,0xc6,0x9d,0x2,0x0,0x87,0x5d,0xf9,0x98,0x98,0xd0,0x6a,0x86,0x2a,0xfa,0x93,0xa8,0x4a,0xe0,0xf5,0x3,0x1d,0xd,0xb2,0x24,0x82,0xa8,0x39,0xaf,0xf4,0x5e,0x0,0x41,0xb1,0x80,0x8e,0xf6,0x4,0xc,0xe3,0x6b,0x52,0xda,0x52,0x90,0x65,0x6f,0xb3,0x8d,0x24,0xf1,0x83,0x64,0xc2,0xd,0xa0,0x3,0x29,0x45,0xe9,0xb7,0x4,0x22,0x10,0xd4,0x0,0xb3,0xf1,0x7d,0x56,0x0,0x41,0xd1,0x97,0x99,0x19,0x60,0xe,0xeb,0x7d,0x88,0xff,0xd3,0x44,0x90,0xe7,0x7,0xe2,0x8e,0x8b,0xdf,0xaf,0xe,0xbc,0xec,0x7e,0x4,0x5a,0x44,0x3,0x59,0x1a,0xd4,0x61,0x41,0xa0,0x34,0xb8,0xb5,0xef,0x6b,0xfe,0x6,0x40,0xa,0x81,0x30,0xf4,0x10,0x73,0x13,0x1c,0x8f,0xd9,0x69,0x3f,0x8,0xbf,0xbc,0x64,0xd1,0x11,0xdf,0x3d,0x60,0xd1,0xc3,0x82,0x19,0xc,0x88,0x8a,0xdf,0x48,0x4c,0xc4,0x15,0xd0,0x45,0x57,0x63,0xd6,0x6c,0x10,0xe2,0xb9,0x6f,0xbc,0x8f,0xf,0xfa,0xc6,0xa3,0xff,0xcc,0xc0,0xc4,0x96,0xb4,0x43,0x41,0x6e,0x8,0x8e,0x14,0x15,0xcb,0xcf,0xe0,0xd2,0x2f,0x96,0x74,0x39,0x51,0xc9,0xf,0xe1,0x1a,0xed,0xce,0x5c,0xf3,0x1c,0x1c,0x9d,0xc6,0x1,0x68,0xe7,0x40,0xc0,0xce,0xc1,0x4,0x6a,0x4d,0xd1,0x60,0x51,0xc5,0xc7,0xa3,0xd2,0xc,0xab,0xc,0x1e,0x51,0x8d,0xc0,0xcb,0x52,0xac,0xd7,0xed,0x96,0x24,0x28,0x23,0xa0,0xfc,0x3c,0xa5,0x92,0xf1,0x20,0x8,0xf5,0x35,0x0,0xbe,0x2b,0x44,0x4c,0x3c,0x7d,0xd9,0xbb,0x15,0x9a,0x98,0x7a,0x7,0x7e,0xe3,0x9,0xfb,0x80,0x45,0x8f,0xda,0xca,0xf0,0xe7,0x3b,0x5a,0xe3,0xd2,0xa8,0x0,0x16,0x19,0x0,0x2,0x54,0xa5,0xd6,0x6b,0xdc,0xbe,0x92,0xa0,0xa9,0xc,0xf,0x2e,0x81,0xb8,0xa,0x10,0xd1,0xe4,0x47,0xc0,0x8c,0x80,0xd,0x76,0x69,0x80,0x3a,0xa7,0x19,0x60,0x86,0x89,0xb4,0x66,0x5d,0x88,0x37,0xfc,0x5a,0x94,0xa7,0x6a,0x55,0x20,0x53,0x85,0xd,0x10,0x4,0x6c,0x69,0x10,0x6a,0x85,0x64,0x4a,0x58,0x3,0x3,0x44,0x7,0x2e,0x7a,0xe4,0x8a,0x40,0x15,0x7f,0x33,0xff,0xd2,0x87,0xe,0x20,0x12,0x7b,0x13,0x51,0x37,0x11,0xd2,0x82,0xa8,0x8d,0x99,0x8,0xe0,0x21,0x63,0x38,0x3,0x42,0x3f,0x1b,0xf3,0x9a,0xd6,0xde,0x72,0x66,0xd2,0xb6,0x65,0x1d,0x93,0x48,0x38,0xd0,0x85,0x41,0x38,0x56,0xad,0xb5,0x64,0xae,0xb7,0xf3,0xe5,0xc7,0xa0,0xaa,0x59,0x5f,0x3a,0xc9,0xc,0x23,0x99,0x19,0x1,0x18,0x3b,0xd,0x1f,0xb0,0x93,0xe4,0x2,0x2a,0x1e,0x51,0x3f,0x80,0x8e,0x8a,0x83,0x42,0x75,0x61,0x1f,0x35,0x0,0x6,0xaa,0xcd,0x45,0xf4,0xb9,0x2d,0x5,0x42,0xa5,0x10,0xf8,0x45,0x31,0x61,0x42,0x12,0xbd,0x7d,0xf9,0x4b,0x92,0xae,0x7d,0x89,0x63,0xcb,0xca,0x39,0x8e,0x2d,0x61,0xd9,0x2,0x0,0xc1,0x68,0x86,0xef,0x87,0xe0,0x92,0xc0,0x7c,0x5f,0x83,0xd9,0x20,0x9d,0x4e,0x68,0x56,0xbe,0xb4,0x25,0x41,0xa,0xaa,0xdc,0x25,0x33,0x81,0x88,0xc1,0xc4,0xd1,0xf5,0x38,0xa,0x3,0xcb,0xb7,0x20,0x41,0x30,0xe0,0x61,0x30,0x80,0x21,0x2c,0x7,0x44,0x40,0xae,0x10,0x7a,0xb1,0xb8,0xed,0x6b,0xbd,0xcb,0x7,0x18,0xa1,0x2,0x88,0x78,0x39,0x8,0x1d,0xcc,0x25,0x69,0xb,0x54,0x5,0x7e,0xc3,0x66,0xa1,0x6,0x4,0x54,0x4b,0x69,0x44,0x5f,0x23,0xc4,0x5d,0x9,0x2f,0xf4,0x61,0xb9,0x12,0xdd,0xdd,0x2d,0x0,0x6b,0x86,0x51,0x95,0x2f,0x19,0xe5,0x83,0x3,0x5,0x10,0x41,0x92,0x40,0x3a,0xe9,0x44,0x6a,0x9b,0x19,0xe9,0x64,0x1c,0x44,0x4,0xa3,0x7c,0x69,0xfc,0x22,0xe2,0xae,0x15,0x81,0x3,0x25,0xb2,0xa1,0xca,0xe7,0xa8,0xd0,0x13,0x54,0xa5,0xc8,0xc0,0x10,0x15,0xe5,0x4f,0x30,0x64,0x4a,0x7f,0x64,0x8,0x20,0x4b,0x8c,0x5c,0x1d,0x71,0xf0,0x7f,0x1d,0x0,0x91,0x2f,0xa6,0x14,0xaf,0x28,0x16,0x82,0x83,0x53,0xf1,0x4,0xd8,0x2f,0x40,0x40,0x56,0x9,0x96,0xaa,0x9c,0xa9,0x61,0xe1,0x47,0x76,0x37,0x52,0xcf,0x86,0x19,0x6c,0x0,0x9f,0x35,0x42,0x1d,0x9d,0xa0,0xfc,0x2,0xb8,0x98,0x7,0x88,0x22,0xcc,0x70,0xc5,0x2d,0x2f,0xcd,0x5e,0x3,0x86,0x81,0xd1,0x61,0x5,0x8b,0x91,0x68,0x86,0xd9,0xc6,0x9c,0xaf,0x60,0x4b,0x82,0x25,0x4,0x88,0x86,0x85,0x5b,0xb6,0xf8,0x28,0x5d,0x97,0xc0,0x25,0xf5,0x3f,0xac,0x2d,0x22,0x5,0x21,0x0,0x62,0x10,0x11,0x33,0x90,0x85,0xad,0xf2,0x46,0xd3,0x2e,0xd,0xd0,0xc0,0x4,0xac,0x28,0x49,0x29,0x9a,0x3b,0x44,0x23,0x54,0x3e,0x4a,0xcc,0x5f,0xf9,0x33,0x63,0x80,0xd0,0x18,0x68,0xc3,0x30,0xcc,0xd,0xdc,0x42,0x2,0x89,0xba,0x70,0x82,0xea,0x9d,0x8f,0x3a,0x50,0x45,0xcc,0x44,0xcd,0xdf,0x43,0xcd,0x8,0xb5,0x8e,0x42,0x3f,0x1,0x58,0x44,0x90,0x92,0x20,0x84,0x28,0xf9,0x2e,0x1c,0x81,0x80,0x2a,0x91,0x2c,0xc0,0x54,0x72,0x8,0x19,0xc2,0x76,0xe0,0x79,0xa,0x24,0x28,0xf3,0xd4,0xa5,0xff,0xb8,0xcb,0x7,0xa8,0x61,0xcc,0xa2,0xe9,0x4,0xc3,0xbc,0xbc,0xc2,0xe9,0x46,0x33,0xa6,0x62,0xe3,0xab,0xed,0xbd,0x61,0x46,0xa8,0xd,0x42,0xc5,0x15,0x11,0x55,0xfb,0x0,0x6f,0x26,0xeb,0xcd,0x0,0xb4,0x1,0x34,0xc,0x58,0x45,0x7e,0x8a,0x2d,0x5,0x6c,0x19,0xdd,0x1b,0x57,0x81,0x90,0xc1,0x20,0x8e,0x62,0x7f,0x49,0x2,0x61,0xa8,0x41,0x8c,0x21,0xec,0x44,0xc7,0x4e,0x1,0x0,0x22,0x83,0x3,0x2f,0x7b,0x44,0x86,0xa1,0xb7,0x32,0x8,0x14,0xd2,0xc9,0x38,0x74,0x50,0x80,0xa0,0xf2,0xa0,0x46,0x33,0x5d,0x19,0xd,0x55,0x46,0xb,0x46,0x46,0x8,0x4d,0x9,0xbf,0x19,0x1c,0x50,0x79,0xf2,0xd3,0x18,0x51,0x63,0xe4,0x8,0x1a,0x6,0x7c,0x65,0xe0,0x2b,0x40,0xa,0xc0,0x12,0x54,0x22,0xa9,0x50,0xd1,0x3c,0x86,0x1,0x9,0x2a,0xa9,0x8,0x6c,0xd8,0x99,0x0,0xb0,0x53,0x30,0x81,0xcf,0x7d,0xf3,0x58,0x36,0x26,0x4c,0x68,0xc6,0x4b,0xc6,0x70,0x44,0xb5,0x31,0x21,0xd4,0x6,0x5,0x5f,0xa3,0xe0,0x6b,0x14,0x95,0x46,0x58,0x27,0x7c,0xda,0xaa,0xf0,0x6b,0xe2,0xc3,0xf1,0x29,0x81,0xba,0x8,0x64,0x74,0x10,0xc,0x9f,0xa1,0x4d,0x4,0x86,0x42,0xa8,0x51,0x8,0x34,0xfc,0xd0,0x54,0x7c,0x82,0xa,0x53,0x60,0xf0,0xa,0x0,0xcc,0xbb,0xf8,0x2f,0xbb,0x72,0x1,0x35,0x37,0xc2,0xa1,0xb1,0x85,0xf8,0x7f,0x65,0x16,0x90,0x29,0x1a,0x4c,0xc3,0x5c,0x37,0x7,0xab,0x8,0xa0,0x51,0x85,0x3f,0x5e,0x69,0xa3,0x79,0xd,0xd2,0xe8,0x6f,0x34,0xf2,0x5c,0x53,0xf2,0x1b,0xf2,0xbe,0xaa,0x70,0xca,0x44,0x60,0x8,0x7c,0x6a,0xff,0x8b,0xef,0x9d,0xf6,0xc2,0x95,0xef,0xe7,0x77,0x1c,0x0,0xf6,0x3b,0xff,0x8e,0xca,0xf9,0xf3,0x2e,0xfc,0x93,0xbd,0x3d,0x3f,0x3c,0xff,0xe2,0x7b,0x2a,0xd7,0xda,0xff,0x92,0xfb,0xbe,0x1c,0x18,0xd1,0x9f,0x4c,0x3a,0xa7,0x75,0x74,0x24,0x58,0xfb,0x85,0x8a,0x7,0x5e,0x33,0xc0,0x15,0x76,0x90,0x1a,0x8,0x9f,0x1a,0xcf,0xf8,0x1d,0x86,0x85,0xb1,0x2e,0x46,0x75,0xb7,0x43,0x75,0x0,0x21,0x28,0xbf,0x88,0x74,0xca,0xa6,0x96,0x74,0xec,0x0,0x26,0x5a,0xbb,0xff,0x25,0xf7,0x2e,0x3e,0xe8,0xe2,0x7b,0xe3,0x0,0xb0,0xe0,0x6b,0x77,0x6d,0xd7,0x44,0x9c,0x77,0xe1,0x9d,0xb2,0xf2,0xfa,0x82,0xdb,0xc5,0x8e,0x82,0x76,0x63,0xc1,0x5d,0xf0,0xa7,0xf,0x42,0x40,0x3e,0xff,0xed,0x13,0x6e,0x9f,0x77,0xe1,0x9d,0x36,0xb4,0x51,0x2f,0x7c,0xe7,0x84,0x71,0xa1,0x79,0xc1,0xd7,0xee,0xa2,0xe7,0xae,0x3a,0x9e,0x17,0x7c,0xfd,0x9e,0x29,0x80,0xbc,0x4f,0x4a,0xda,0xbb,0xab,0x23,0x9,0xdb,0x96,0x50,0x7e,0x1e,0x6c,0x4c,0x43,0xfa,0x77,0xf4,0xdb,0xa5,0xed,0x78,0xa2,0xed,0x60,0x2e,0x47,0xf3,0xf,0x2a,0x5a,0x8b,0x6b,0xcf,0x25,0x82,0xed,0xc6,0x11,0x28,0xa0,0x7f,0xa0,0x0,0x15,0xea,0x8d,0x82,0xcd,0x7,0x97,0x5c,0x75,0xec,0x73,0xb,0x2e,0xbe,0xc7,0x7a,0xee,0xca,0x63,0xc7,0x55,0x28,0x32,0xef,0x82,0x5b,0x8,0xc2,0xb1,0x5f,0xf8,0xf6,0x7,0x83,0x5,0x17,0xdd,0xf1,0x1e,0x66,0x6e,0x7f,0xfe,0xdb,0x27,0xdc,0x3e,0x7f,0xe1,0xed,0xe2,0xf9,0xab,0x4f,0x34,0x3b,0x4c,0x3,0xcc,0x5b,0x78,0x2b,0x1,0xc0,0xdc,0x85,0xb7,0xed,0xcd,0x82,0x7e,0xb,0x12,0xb7,0xcd,0xb9,0xe0,0xf6,0x27,0x40,0x66,0xaf,0xb2,0xf0,0xe7,0x2d,0xbc,0x73,0xab,0x43,0xbf,0xe0,0xdf,0xee,0x26,0x0,0x90,0xaa,0x40,0xf3,0xbf,0x76,0xcf,0xa7,0x1,0xb1,0x3e,0x95,0x72,0x67,0x75,0x77,0xa7,0x99,0x38,0x44,0x90,0x1f,0x2,0x1b,0x1e,0xe,0xf0,0x41,0x4d,0xc8,0xf4,0x2d,0x16,0x7e,0x33,0xf3,0x87,0xaa,0xee,0xbc,0x5a,0x35,0x30,0x23,0x28,0x16,0x20,0xa0,0x30,0xa9,0x3b,0x85,0xd6,0xb6,0x44,0x87,0x11,0xe2,0xd9,0xf9,0x5f,0xbb,0xe7,0xf2,0x89,0x7b,0x17,0x75,0x34,0x39,0xb6,0x3e,0x8e,0xf3,0x17,0xde,0x41,0x0,0xf0,0xc2,0x35,0x1f,0x61,0x66,0x33,0x69,0xee,0xc2,0x3b,0x7e,0xf,0xa2,0x47,0x40,0xe2,0xb6,0x79,0xb,0x6f,0x3f,0xa6,0x59,0xe1,0x37,0x3d,0x64,0xef,0xfe,0xda,0xc3,0x94,0xd7,0xbd,0x6d,0xc2,0xd8,0xaf,0xa5,0xd3,0xf1,0xae,0x64,0xd2,0xd5,0xf9,0xbc,0x27,0x33,0x59,0x1f,0x44,0x74,0xa3,0x6d,0xe3,0xd2,0x25,0x57,0x7e,0xe8,0xe5,0xf9,0x17,0xfe,0xd9,0x79,0xfe,0xdb,0x1f,0x68,0x18,0xe3,0xce,0xb9,0xe0,0xe,0x5a,0x76,0xcd,0x87,0xf8,0xa0,0xb,0xef,0x68,0x51,0x96,0xfd,0x6b,0x40,0x9c,0xd0,0xd5,0x99,0x32,0xf1,0x98,0x14,0x7e,0x31,0x7,0x30,0xf,0xab,0x7d,0xaa,0x21,0x7a,0x47,0x51,0xf7,0x78,0x1b,0x5,0xdf,0x9c,0x26,0x18,0x51,0x25,0x54,0x62,0x3,0xd9,0x18,0x80,0x8,0x4e,0x3c,0x89,0x20,0x44,0xd8,0x3f,0x90,0xb7,0xc3,0x40,0x3f,0x2f,0x85,0x39,0x71,0xc9,0x95,0xc7,0xad,0x5d,0xf0,0xb5,0xbb,0xac,0xe7,0xae,0x3a,0xbe,0xa1,0x36,0x98,0x7f,0xe1,0x9d,0xf6,0xf3,0xdf,0xfe,0x60,0x78,0xd0,0xbf,0xfd,0x79,0x52,0x60,0x78,0x31,0x80,0xcf,0xa7,0x92,0x2e,0xd2,0x29,0x57,0xf9,0x7e,0x68,0xb6,0xf4,0xe6,0x1d,0xd7,0x11,0xb,0x42,0xdf,0x5a,0xba,0xf4,0xba,0x63,0xcd,0xe,0x1,0xc0,0x81,0xe7,0xdf,0xe6,0x84,0x42,0x2c,0x4f,0x24,0x9c,0xc9,0x6d,0x6d,0x71,0x19,0xe6,0xb3,0xb0,0xe2,0x9,0x10,0x59,0x18,0x1c,0x2a,0x22,0x57,0x8,0x20,0x80,0x9f,0xbf,0x70,0xf5,0x87,0xce,0x58,0xf0,0xb5,0xbf,0x88,0xe7,0xae,0x7a,0x7f,0xcd,0xf,0xcf,0xbd,0xe0,0x2e,0xb2,0x62,0x52,0xb0,0xe2,0x59,0x20,0x3c,0x66,0xdb,0x56,0x67,0x77,0x77,0xb,0x54,0xe8,0xc3,0x84,0x7e,0xad,0x27,0x3f,0x9c,0xfe,0x1b,0xc3,0xd1,0xa3,0xb7,0x57,0xf0,0xcd,0x82,0xa0,0xc6,0x81,0xad,0x35,0x7,0xa5,0xba,0x0,0x8,0xcb,0x81,0xb0,0x5d,0xf4,0xf5,0x17,0xd8,0xf7,0x43,0x3,0x56,0x1f,0x79,0xee,0xaa,0xf,0xfc,0x69,0x14,0xe1,0xcb,0xe7,0xbf,0xfd,0x41,0xbd,0xe0,0xc2,0x3b,0x16,0x19,0x88,0x8b,0x85,0x84,0x3d,0xa1,0x2b,0x5,0x61,0xc,0x54,0x58,0x84,0x1d,0x4b,0x20,0x97,0xb,0x30,0x38,0xe4,0x5,0x42,0x6,0x93,0xc1,0xf1,0xec,0xb,0x57,0x7f,0x20,0xdc,0x66,0x13,0x30,0xe7,0xbc,0xdb,0x6d,0x0,0xf0,0x81,0xdb,0xa5,0xa4,0x69,0x1d,0xed,0x71,0x19,0xe4,0x33,0x0,0x31,0xc2,0x62,0x16,0xa1,0x97,0x45,0x7b,0xab,0x83,0xc9,0xdd,0xe9,0xc0,0x71,0xe5,0x67,0xe6,0x2d,0xbc,0x33,0x6f,0x54,0x70,0xd2,0xb0,0xaa,0xba,0x93,0x16,0x5c,0xf8,0x67,0x5a,0x7a,0xcd,0xf1,0x6c,0xb4,0x3e,0x8d,0xc1,0xaf,0xa5,0xd3,0x6e,0xcb,0xc4,0x89,0x49,0x84,0xc5,0x1c,0x4c,0xe0,0x8d,0x82,0xc7,0x77,0x88,0xf0,0xeb,0xb4,0x55,0x3,0x82,0x63,0xd4,0x68,0x21,0xaa,0x66,0x22,0x18,0x15,0x40,0x79,0x79,0x4c,0x9c,0x90,0xa0,0xd6,0x96,0x98,0x26,0x92,0xb7,0xcf,0xbf,0xe8,0xae,0xcb,0x0,0xe0,0x80,0xcb,0x1e,0x10,0xf3,0x16,0xfe,0xa9,0xf2,0x2d,0x41,0x38,0x74,0xee,0xc2,0x3b,0x5e,0x96,0xb6,0xbc,0xbc,0xb3,0x33,0xc1,0x53,0x27,0xa5,0x0,0xe5,0x21,0xf4,0x73,0x0,0x1b,0x84,0xc5,0x2c,0x52,0x29,0x1b,0xad,0xad,0xae,0x80,0xb1,0x5f,0x4c,0x4e,0x8b,0xeb,0x6d,0xd6,0x0,0xfb,0x9d,0x77,0x9b,0xf5,0xe2,0xb5,0x27,0xa9,0x39,0xe7,0xdf,0xf6,0x1d,0xd7,0xb5,0xce,0xeb,0xea,0x4c,0x41,0xfb,0x51,0xa9,0xd6,0xf0,0xc3,0x94,0x58,0x2e,0xdb,0x86,0xb4,0x5d,0xf6,0x3,0xa6,0xfe,0x81,0x2,0x0,0x7a,0x48,0x12,0xce,0x58,0xf2,0xad,0xf,0xac,0x2,0x80,0xf9,0x17,0xdd,0xb5,0x58,0x48,0x71,0x69,0x5b,0x6b,0x42,0x27,0xe2,0x32,0xaa,0xee,0x8d,0x72,0xa4,0xc3,0x82,0xae,0x9a,0xf9,0xa3,0x3b,0x7d,0xb4,0x73,0x2f,0x65,0xe1,0xd1,0xcc,0x1,0x97,0xc8,0x6e,0x1e,0x71,0x4e,0x59,0x13,0x80,0x19,0x56,0x2c,0xe,0x65,0x4,0x6f,0xd9,0x92,0x25,0x86,0xb9,0xf1,0xb9,0x2b,0x8f,0x3f,0xd,0x0,0x8e,0xb8,0xe8,0x6,0x3b,0x83,0xe4,0x6f,0x94,0x32,0xa7,0x75,0x75,0x26,0x10,0x4f,0x38,0x86,0x43,0x5f,0xa8,0xc0,0x3,0x95,0xe8,0x85,0xb2,0xd3,0x49,0x24,0xe0,0x24,0x52,0xbc,0x71,0x53,0x86,0xb4,0x36,0x7f,0x7e,0xe1,0xea,0x13,0x3f,0x38,0xf7,0xdc,0x9b,0xc5,0xd2,0xeb,0x4e,0x36,0xe3,0x36,0x1,0xf3,0xce,0xbf,0xf5,0xb3,0x20,0x71,0x7d,0x47,0x67,0x52,0x5b,0x8,0x24,0x6b,0x55,0x11,0x42,0x39,0x1c,0x33,0x55,0x6a,0xcd,0x72,0x13,0x80,0xb4,0x30,0x38,0xe0,0x1b,0xcf,0xf,0x5,0x1b,0xfe,0x22,0x8,0x87,0x4a,0x4b,0x9e,0x31,0xa9,0xbb,0x85,0xd9,0x68,0x32,0xa1,0x5f,0xf5,0xcb,0xc3,0xf5,0x7d,0xa0,0x77,0xb8,0xf0,0xb7,0x2,0x82,0x1a,0x7f,0x80,0xb9,0x72,0xce,0xf0,0xe7,0x11,0x8,0x48,0x5a,0x20,0xcb,0xc5,0xc6,0x4d,0x19,0x80,0x71,0xaf,0xd1,0xea,0x67,0x42,0xca,0x5f,0x39,0x8e,0xd4,0x5d,0x1d,0x71,0x61,0xb4,0x26,0xed,0x17,0xa2,0x6c,0x85,0x88,0x18,0x52,0x80,0x4b,0x72,0x28,0x5f,0xc3,0x81,0xe5,0xba,0xd8,0xb4,0x39,0x7,0xa3,0xf4,0xd7,0x5f,0xf8,0xce,0x49,0x57,0xce,0x3f,0xef,0x36,0xf1,0xfc,0xb5,0x27,0x99,0xa6,0x0,0x30,0xef,0x5f,0xfe,0x48,0x94,0x70,0xf6,0x63,0xc6,0xd2,0x8e,0xce,0x54,0x10,0x73,0xc9,0x9,0xbd,0x42,0x2d,0xfb,0x45,0x91,0x5,0x11,0xa5,0x34,0xa8,0x66,0x6,0x31,0x40,0xd2,0x82,0xe5,0xc6,0x10,0x86,0x8,0xf3,0x85,0xd0,0x26,0x22,0xa4,0xd3,0xae,0x22,0x68,0xcb,0x84,0x7e,0xc4,0xf2,0x95,0x63,0xfa,0xea,0x59,0x4f,0x4d,0x38,0x7d,0xef,0x18,0x0,0x34,0x2,0x1,0x57,0xd5,0x81,0x56,0x52,0x8e,0xc3,0x0,0xa8,0xbc,0x37,0x51,0xed,0x80,0xb0,0x38,0x97,0xf,0x48,0x6b,0x83,0x98,0x6b,0x85,0xf1,0x98,0xb4,0x2,0xaf,0x48,0xd0,0x1,0xa4,0x94,0x11,0x4d,0xe,0x86,0x36,0x5c,0x52,0x20,0x8c,0x8a,0xc7,0x61,0x18,0x64,0xd9,0x20,0xcb,0x55,0x9b,0x36,0x65,0x2c,0x0,0x27,0x2f,0xfd,0xce,0x49,0xb7,0xcc,0x39,0xef,0x16,0x5a,0x76,0xed,0x47,0x78,0x4c,0x0,0xcc,0x3d,0xf7,0x16,0x77,0xe9,0x75,0x1f,0xf1,0xf7,0xfb,0xea,0x2d,0xcb,0x5b,0x5b,0x63,0xb3,0xd2,0x29,0x1b,0xa1,0x57,0xa8,0x2a,0xc1,0xa2,0x92,0xc,0xa9,0x54,0x1d,0x5b,0xfa,0xbc,0x84,0x46,0x6d,0x22,0x95,0x24,0xa4,0x5,0x69,0xd9,0xc,0x12,0x64,0x54,0x8,0x63,0x74,0x55,0xe,0x5f,0x54,0x9,0xba,0xc9,0xd9,0xff,0x4e,0x1,0xc0,0xa8,0x20,0xe0,0x2a,0x3f,0xb0,0x4a,0xe8,0xa5,0x3a,0x42,0x54,0x69,0x82,0xe1,0x22,0x12,0x1b,0x24,0x88,0xb5,0x52,0x64,0x54,0x8,0x1,0x86,0x63,0xc9,0x92,0x8c,0xb9,0x94,0xfe,0x2e,0x55,0x2a,0x70,0x4,0x1e,0xae,0xd2,0x42,0x96,0x13,0x43,0xc1,0x63,0x35,0x34,0x54,0xc,0x60,0xf4,0xbc,0x17,0xae,0x3b,0x79,0xf9,0x98,0x1a,0x60,0xee,0x79,0xb7,0xd0,0xd2,0x6b,0x3f,0xc2,0x73,0xcf,0xbd,0xf9,0x7f,0x1d,0xc7,0x3e,0xa3,0x6b,0x42,0x92,0x43,0xaf,0x50,0xb1,0x30,0xd5,0x2c,0x5c,0x54,0xd,0x2b,0x50,0x6d,0xc2,0x5,0x9,0x30,0x3,0x81,0x2e,0xd7,0xc3,0x50,0x94,0x3b,0xad,0x66,0xcb,0xca,0xdf,0xa9,0x16,0x3c,0x8d,0x45,0xef,0xbe,0xc3,0x84,0xdf,0x94,0x16,0xe0,0x11,0xe6,0xa2,0x42,0x7e,0x55,0x9f,0x63,0x4c,0xe5,0xa,0x8e,0x45,0xb0,0x5,0x45,0x42,0xaf,0x9a,0xf1,0xc6,0x94,0x5f,0x97,0x0,0x50,0xc1,0x52,0x24,0x1,0x3b,0x9e,0x44,0x7f,0x5f,0x11,0x85,0x82,0xff,0xb0,0xe3,0xf6,0x1c,0x15,0x86,0x13,0xc4,0xd2,0x6b,0x4f,0xd6,0xd,0xa3,0x80,0xa5,0xd7,0x7e,0x84,0xe7,0x5d,0x70,0xcb,0x67,0xc,0xe3,0x8c,0xce,0xce,0x64,0xa8,0x43,0x8f,0x50,0xaa,0x5e,0xa4,0xaa,0xd9,0x29,0xaa,0x8b,0x32,0xa9,0x36,0x29,0x63,0x49,0x1,0x5b,0x8a,0x6,0x74,0x2d,0x37,0x20,0x6d,0x68,0x64,0x61,0xdd,0x9b,0x4d,0xeb,0xbf,0x8d,0xfc,0x50,0x75,0x94,0x33,0x56,0xc4,0x50,0x99,0xe,0x42,0x54,0xde,0x97,0x6b,0x12,0x9,0xb5,0x45,0xb1,0x95,0x88,0xa2,0x21,0x25,0xcd,0x8,0x8b,0x79,0x74,0x76,0x25,0x61,0xd9,0xe2,0xbd,0x41,0x30,0xe1,0xf2,0xa5,0xd7,0x9e,0xac,0xe7,0x9e,0x77,0x93,0x1c,0x71,0xab,0x73,0xce,0xbb,0x49,0x90,0xa0,0xa9,0x46,0x99,0x25,0x5d,0x5d,0xe9,0x4e,0xc7,0x26,0xd2,0x41,0xb1,0x62,0xe7,0x87,0x85,0x3f,0x5c,0x60,0x41,0x55,0xdc,0x3c,0x89,0xa8,0x14,0xcb,0x30,0xc1,0x53,0x7a,0x84,0x73,0x57,0x4d,0xf2,0x10,0x44,0x1d,0x0,0xe8,0xef,0x43,0xf5,0x37,0xe5,0x10,0xd6,0x6b,0x81,0x6a,0x5f,0x80,0x4b,0xb,0x4e,0x47,0x6a,0x6,0x4b,0x0,0x6e,0x9,0x4,0xcc,0xc3,0x4e,0x9f,0x31,0x5c,0x71,0xc4,0x4d,0x49,0x3,0x80,0xcb,0xc5,0x31,0xa5,0xe1,0x93,0x36,0xc,0x49,0xdd,0xbb,0x25,0x2b,0x99,0xcd,0x81,0x4b,0xaf,0x3d,0x75,0x49,0x8d,0x6,0x98,0x73,0xee,0x4d,0xd6,0xb2,0x6b,0x4f,0x31,0x3a,0x34,0x57,0xba,0xae,0xd5,0xe5,0xba,0x16,0x69,0xdf,0xab,0x72,0xca,0xc6,0x9e,0xf9,0xd1,0xeb,0xe8,0x73,0x3f,0x54,0xa0,0x6a,0x89,0x31,0x46,0x26,0x4a,0x68,0x1c,0x34,0xd4,0x3b,0xb9,0x9,0xb,0x8d,0xf7,0x81,0x46,0xaa,0xbb,0xf2,0x78,0xab,0x92,0x7f,0x55,0xbd,0x2c,0x8d,0x51,0x5b,0x2a,0x57,0xad,0xa5,0xab,0xaf,0x62,0x74,0x8,0x4b,0xb0,0x4c,0xa5,0x62,0x3e,0x1b,0xba,0xa1,0x86,0x8,0xda,0xef,0xab,0x37,0xd0,0xb2,0xeb,0x4e,0x51,0x73,0xbf,0x7a,0xd3,0x7,0x2c,0x4b,0x7c,0xb2,0xb3,0xb3,0x45,0x6b,0xbf,0x58,0x15,0xea,0x45,0x97,0xdb,0x9a,0xf0,0x89,0x0,0x2f,0x34,0xc3,0x2b,0x2c,0xaa,0x9e,0x89,0x6b,0x66,0x32,0x35,0xa9,0x16,0xe9,0x9d,0x2d,0xfc,0xca,0x24,0xa6,0xf1,0x9b,0x83,0xfa,0x4,0x38,0x45,0x55,0x31,0x5e,0xa8,0x2b,0xbe,0xa2,0x68,0x20,0xf8,0xea,0xb5,0x14,0x54,0x37,0x7c,0xca,0xf7,0x91,0x4e,0xbb,0x2e,0x1,0x33,0xe7,0x9d,0x7b,0xd3,0x5,0x0,0x30,0xe7,0x2b,0x37,0x46,0x97,0x58,0x70,0xfe,0x8d,0xb6,0x32,0xdc,0xdf,0x92,0x4e,0x38,0xc9,0x84,0x74,0x74,0x18,0x94,0x99,0x27,0x0,0x2,0x52,0xa0,0xa6,0x3e,0x2f,0xf2,0xe5,0xa8,0x52,0xb2,0x45,0x14,0x39,0x7e,0x4a,0x73,0xc5,0xb3,0xa7,0x1a,0x7,0x8f,0xaa,0x56,0xfb,0x88,0x11,0xa1,0x5f,0x35,0xd2,0xff,0xae,0x0,0x30,0x8a,0x19,0xa8,0x8d,0xfd,0xab,0xa8,0xe1,0x4a,0x64,0x50,0x15,0xd2,0x95,0x4c,0x43,0x94,0x24,0x63,0x8,0x2,0x12,0xae,0x55,0x71,0x4,0xeb,0x9d,0x42,0x66,0x94,0x22,0x83,0xe8,0x9a,0xcc,0x11,0xe5,0x6,0x63,0x20,0x2c,0x17,0x6,0x12,0x9b,0x36,0xf,0x42,0xa,0x74,0x32,0x28,0x27,0x22,0x3b,0x43,0x9d,0x0,0x14,0x9,0xaa,0xc3,0x25,0x41,0x94,0x6a,0xf3,0x6a,0x66,0xbe,0xa8,0x9d,0xf9,0xda,0x44,0x35,0x7a,0xcd,0xd,0x8,0x37,0xaf,0x3b,0xf9,0xef,0x41,0xf8,0xe3,0xb1,0x75,0x3c,0x86,0x8f,0x38,0x5c,0x5e,0xe6,0x87,0xa6,0x2a,0x82,0xa6,0x9a,0xd9,0x3f,0x96,0x53,0xc8,0x6c,0x0,0x18,0x8,0x41,0x60,0x70,0xa7,0x61,0xa9,0xc5,0x9c,0xaf,0xde,0x40,0xcf,0x5f,0x77,0xca,0x26,0x30,0x4e,0xcd,0x64,0x8a,0x8e,0x66,0xa9,0xcb,0xd4,0x62,0xf9,0xc2,0x3c,0x86,0xda,0x7,0x0,0x2f,0xd4,0x8d,0xe5,0xdc,0x50,0xcb,0x11,0xc6,0x25,0x5d,0x7e,0x7,0x2,0x61,0x9b,0xee,0xb9,0x7e,0xc0,0xa8,0x66,0xb2,0x54,0x5f,0x2e,0xd4,0x51,0x15,0x74,0x65,0x61,0x6a,0x95,0x16,0x15,0x55,0x8b,0x67,0x6a,0x1c,0x6b,0x21,0xe1,0xc4,0x63,0xd8,0xb2,0x25,0xb,0x62,0x5c,0xb0,0xec,0xba,0x8f,0xbe,0x2e,0x28,0x34,0x14,0xd9,0x82,0x1b,0xe4,0xb2,0xef,0x9d,0xa6,0xf7,0xfd,0xca,0x1f,0xaf,0x4f,0x27,0x63,0x9f,0x6d,0x6b,0x4f,0x22,0x28,0xe6,0x40,0x44,0xb0,0xa5,0x84,0x10,0xd5,0xc2,0xa7,0xa,0x19,0x44,0x44,0x28,0xf8,0x3a,0xf2,0x38,0xcb,0x37,0x52,0x9d,0xc7,0x17,0xc3,0x55,0x34,0xe5,0xb0,0xb0,0x11,0xfb,0x37,0xaa,0x9,0x78,0xa7,0x3a,0x82,0x3c,0xf6,0x7,0x8d,0x68,0xe1,0x46,0x8c,0x60,0x3d,0x39,0x84,0xba,0x5,0x85,0x49,0x57,0x56,0x4e,0x89,0x4c,0x41,0xf4,0xc6,0x98,0xe1,0xd7,0xda,0x18,0x68,0x6,0x6c,0xd7,0x45,0x5f,0x5f,0x51,0x5,0x41,0xf8,0xfa,0xb,0xd7,0x9e,0xba,0x6f,0x4d,0x14,0xb0,0xec,0x7b,0xa7,0xe9,0x39,0x5f,0xf9,0xa3,0x74,0x6c,0x79,0x41,0x26,0x5b,0xec,0x2b,0x16,0x7c,0x63,0x39,0x6e,0xe4,0x3c,0x68,0x53,0x61,0xfc,0x2a,0x33,0xbf,0xb4,0x4c,0xda,0xf,0x4d,0x4d,0xb8,0x31,0x52,0x50,0xcd,0x55,0xeb,0x52,0x73,0xa3,0xf8,0xe,0xf5,0xfe,0x79,0xac,0x90,0x7f,0x94,0x91,0x6a,0xb0,0xbe,0xa1,0xc1,0x77,0x8a,0x81,0xae,0x72,0x4,0x47,0x3a,0x85,0xe5,0x70,0x51,0x48,0xb,0x61,0x88,0xd0,0xf7,0x42,0x8b,0x98,0x3f,0x5c,0x13,0x5,0x94,0x5f,0x2c,0xfb,0xde,0x47,0xf5,0x73,0xd7,0x9c,0xd2,0xef,0xd8,0xf2,0xa2,0xc1,0xc1,0xbc,0x20,0x61,0xe9,0x48,0xfd,0xf3,0x70,0xf8,0x51,0xfe,0x11,0x1e,0xa7,0xdd,0xaf,0x8b,0x79,0x77,0x1d,0x5b,0xf1,0x1,0x9a,0x3c,0xc,0x33,0x42,0x65,0xaa,0xbc,0xfe,0x5a,0x7f,0x40,0x95,0x8,0x2,0x12,0x36,0xfa,0x7a,0xb3,0x36,0xb3,0x39,0xff,0xf9,0xeb,0x4e,0x7b,0x7d,0xce,0x57,0xff,0x28,0x1a,0x32,0x81,0x73,0xcf,0xbd,0x41,0x3e,0x7f,0xed,0xa9,0x3f,0x35,0x8c,0xfb,0x7b,0xfb,0xb2,0x52,0xba,0x9,0x6,0xa2,0xf2,0xec,0x9a,0x75,0xee,0x14,0xa1,0x6f,0x5b,0xf4,0x62,0x8d,0x7,0x5c,0xc3,0x8c,0xff,0x3d,0x82,0xa3,0x41,0x56,0xb0,0x3a,0x29,0x54,0x9d,0x15,0xac,0x29,0x18,0x69,0x4e,0x21,0x32,0xa2,0xca,0xe9,0xca,0xd2,0xf4,0x2a,0x10,0x84,0x3a,0x8a,0x26,0xa4,0x1b,0x47,0x2e,0xef,0x21,0xd4,0xfa,0x59,0x41,0xe2,0x7,0x73,0xbe,0xfa,0x47,0x6b,0xd9,0x77,0x3f,0x6a,0x46,0x55,0x2c,0x73,0xce,0xbd,0xd1,0xb1,0x94,0x92,0x21,0x89,0x37,0xda,0xda,0x93,0x53,0x12,0x71,0x1b,0x5a,0xf9,0x20,0x0,0x49,0xd7,0x82,0x20,0x81,0x7c,0x10,0x96,0xd8,0x27,0xaa,0x8b,0xda,0xea,0x7c,0x0,0xa2,0x51,0xc2,0x42,0x34,0x99,0x6,0x7e,0x87,0x32,0x81,0x4d,0x67,0x4,0xeb,0x26,0x46,0x7d,0x9a,0x78,0x2b,0x3e,0x40,0x19,0x34,0x4,0x20,0x19,0xb3,0x2a,0x93,0x2b,0x54,0x6,0x81,0xd2,0x10,0x96,0x8d,0x20,0x84,0xb7,0x65,0x4b,0x26,0x66,0xbb,0xd6,0x3e,0xcf,0x5f,0x7d,0xf2,0x2b,0x4d,0x1b,0xe9,0xb9,0xe7,0xdd,0x78,0xb4,0x56,0xe6,0x2f,0xdd,0xdd,0xad,0x24,0x85,0x11,0xac,0x15,0x2c,0x11,0x51,0xba,0x61,0x25,0x71,0xb1,0x15,0x0,0x88,0x3a,0x30,0x34,0x2,0x40,0x15,0x41,0xf4,0x77,0x9f,0xc,0xaa,0xae,0x16,0xae,0xe2,0x8,0x86,0x69,0xe0,0x3a,0xaa,0xb8,0x49,0x0,0x44,0x54,0x31,0x21,0xe6,0x8,0x18,0x3,0x14,0x7c,0x5,0x12,0x4,0x16,0x8e,0xd9,0xd2,0x33,0x24,0x58,0xe3,0xcc,0xa5,0xdf,0x3b,0xf5,0xfa,0x46,0xb7,0x3a,0x6a,0x49,0xd8,0xd2,0x6b,0x4f,0x7d,0xc0,0xb2,0xc4,0x35,0x5b,0xb6,0x64,0x84,0x90,0xb6,0x26,0x12,0x50,0x65,0xbb,0xdf,0xac,0xb6,0x66,0xde,0x8a,0x2e,0xe3,0xbf,0x17,0xb7,0xaf,0x9,0x9d,0x5d,0xb7,0xc0,0x85,0xc7,0x78,0x62,0x6a,0x30,0x76,0xd,0xc6,0xb2,0x86,0xe9,0x33,0xc,0xa5,0x19,0x5e,0xa8,0xc0,0xc,0x58,0x4e,0x1c,0xfd,0x7d,0x39,0xa1,0x35,0xff,0x6e,0xe9,0xf7,0x4e,0xbd,0x7e,0xce,0x57,0x6e,0xb4,0xc7,0x45,0x4c,0xef,0xf7,0x95,0x1b,0xe5,0x8b,0xdf,0x3b,0x55,0xef,0xf7,0x95,0x1b,0x1e,0x8e,0xc7,0xed,0x23,0xda,0xdb,0x93,0xd0,0x41,0xb1,0xae,0x72,0x77,0x6b,0x26,0xa0,0x81,0xda,0xaf,0x37,0x11,0x95,0x13,0xff,0x4e,0x52,0xc2,0x5b,0x2d,0xb,0x43,0xdd,0xcc,0xe6,0x91,0xfe,0x80,0xe1,0x11,0x13,0x65,0xe4,0x67,0xf5,0x26,0x65,0xf8,0x77,0x6d,0x27,0x86,0xc1,0xa1,0x22,0x67,0x32,0xde,0xaa,0x98,0x23,0xe6,0x4,0x1,0xfb,0xcb,0xbe,0x7f,0xaa,0x1e,0x6f,0x66,0x2,0x73,0xfe,0xbf,0x1b,0x1c,0x69,0xe9,0x64,0x10,0x60,0xe5,0x94,0xa9,0x1d,0xad,0x30,0x61,0x49,0xd,0xd1,0xe8,0x0,0x88,0x74,0x7f,0x6d,0xb8,0x43,0xd5,0x35,0x1,0xf5,0x66,0xa0,0x19,0x4e,0xe0,0x1d,0x2,0x82,0x31,0x2a,0x84,0x1b,0xd1,0xbf,0x35,0x9f,0xd7,0xd8,0xff,0x5a,0x2d,0xcb,0x65,0xd5,0xcf,0x5b,0x7,0x0,0x9,0x1,0xa5,0x84,0xea,0xeb,0xcf,0x59,0x0,0x1f,0xbe,0xf4,0xba,0xd3,0xfe,0x36,0xd6,0x2d,0x8f,0x59,0x15,0xcc,0x42,0x85,0xcf,0x5f,0xfb,0x4f,0x3,0x6c,0x70,0xe3,0xf8,0x38,0xfa,0xb1,0xd4,0xd7,0xc8,0x72,0x28,0x29,0xa2,0xe6,0xb,0xb6,0x45,0xb0,0x2d,0x1,0x4b,0x10,0x2c,0x49,0xa3,0x84,0x92,0xef,0x40,0xd5,0xcf,0x6,0x44,0xd1,0x33,0x59,0x32,0x7a,0x4e,0x4b,0x46,0xcf,0x29,0x88,0x86,0x5,0x5c,0x4f,0x12,0x6d,0x8b,0x7d,0x8c,0x16,0xa3,0x32,0x18,0x43,0x4b,0xaf,0x3b,0xed,0x6f,0x73,0xbe,0x7a,0xa3,0x1c,0xeb,0xf4,0x31,0x97,0x87,0x13,0x8b,0xf2,0x35,0x9d,0x5a,0x1,0x6e,0x5,0x4,0xcc,0x63,0x2c,0xdc,0x1c,0x3e,0x47,0x48,0x1,0x5b,0x48,0xf4,0x67,0xb,0x8,0x2,0xd,0x5d,0x6e,0xa3,0x22,0x24,0x6c,0x49,0xe8,0x68,0x4b,0x82,0x88,0xa0,0xb4,0xa9,0x25,0x48,0x78,0x27,0xd3,0x4,0x3c,0x3a,0xe3,0x27,0x4,0xc1,0xb6,0x24,0x72,0x79,0xf,0xf9,0x42,0x0,0x63,0x18,0x9a,0x4d,0xa5,0xcf,0x60,0x2a,0x6e,0x23,0x95,0x74,0xa1,0x94,0x8e,0xca,0xe9,0xea,0x2e,0xc3,0xdb,0xd9,0x4e,0x8c,0xb6,0x2,0x1d,0xab,0xf9,0x27,0xe4,0x6,0x56,0x63,0xc,0x49,0xd4,0x83,0xa0,0x42,0x17,0xb,0x18,0x30,0x72,0x85,0x0,0xad,0x31,0x9,0x57,0x12,0xce,0x3c,0x7c,0x6,0xde,0x35,0xa5,0x15,0x93,0xda,0x13,0x88,0x39,0x16,0x36,0xf6,0x17,0xb1,0xae,0x3f,0x8f,0x3b,0x96,0xac,0xc5,0xda,0xfe,0x22,0xf2,0x6,0x70,0x5d,0x1b,0xb6,0x25,0xab,0x54,0x26,0xed,0xd4,0xb,0x43,0x4,0x11,0x72,0x5e,0x0,0x28,0x8d,0x16,0x57,0xe0,0xa8,0xd9,0x9d,0xf8,0x87,0x3d,0xbb,0xd0,0xdd,0x1e,0xc7,0xc4,0x74,0xc,0x5b,0x32,0x1e,0x36,0xf,0x16,0xf0,0xd8,0xeb,0x5b,0xf0,0xd8,0x2b,0x9b,0x51,0xd0,0x80,0x26,0x42,0x2a,0xe1,0xc0,0x68,0x1e,0x5b,0x1b,0x6c,0xe5,0x86,0x18,0x6,0xd5,0x1d,0x8b,0x76,0x0,0x0,0x46,0x13,0x35,0x8d,0x7d,0x23,0x6,0x35,0xed,0x59,0x4,0x0,0x3f,0x8,0x90,0xc9,0x14,0x71,0xd6,0x51,0xb3,0x71,0xfc,0x82,0xa9,0x98,0x39,0xb9,0xad,0xe2,0xe4,0x98,0x92,0x7b,0x31,0xa5,0x3d,0x8e,0x3,0xf7,0xe8,0xc2,0x87,0xf,0x9e,0x81,0x4c,0x31,0xc0,0x3,0xcb,0x36,0xe1,0xbf,0xee,0x7d,0xd,0x83,0x79,0x1f,0x13,0x3a,0x52,0x8,0x94,0x1e,0xd6,0x44,0x6f,0x97,0x5f,0x30,0x86,0xbd,0xb7,0xa5,0xc0,0xaa,0x8d,0x3,0x78,0xcf,0xec,0x2e,0x9c,0x79,0xd4,0x1e,0x38,0x60,0x56,0x67,0xc9,0x79,0x36,0x30,0xa5,0x44,0x4e,0x57,0xca,0xc1,0x7e,0xd3,0x5a,0xf1,0xbe,0xf9,0x53,0x61,0x94,0xc2,0xd3,0x6f,0xf4,0xe2,0x37,0x7f,0x5b,0x89,0xfb,0x5e,0xd9,0x82,0x19,0x93,0x5a,0xe1,0x7,0xaa,0xf9,0x5b,0x68,0xf4,0x79,0x6d,0x47,0xcd,0x6d,0x3,0x0,0x57,0xdb,0x5d,0x6e,0xd6,0x75,0x1c,0xe9,0xfc,0x50,0xa9,0x45,0xaa,0x1f,0x6a,0xa4,0x49,0xe1,0xf,0x17,0x1d,0x83,0xb8,0x6b,0xc1,0x92,0x12,0xbe,0x1f,0x56,0xf5,0xff,0x8d,0x1c,0x49,0xa5,0xa2,0xb4,0x25,0x11,0x21,0xe9,0x48,0x9c,0x70,0xe0,0x34,0x7c,0xf8,0xe0,0xe9,0xf8,0xb7,0xdf,0x2c,0xc1,0x43,0xaf,0xf7,0xa1,0xbd,0x35,0x31,0x1c,0x3b,0x97,0xb5,0xc1,0x5b,0x9,0x4,0x1e,0x9b,0xe7,0x5f,0xbe,0x7a,0xb,0x7e,0xf0,0xb9,0x43,0x70,0xd4,0xbe,0xdd,0x30,0xcc,0xd0,0x3a,0x7a,0x1e,0xae,0xf2,0x7d,0x34,0x0,0xad,0x19,0x26,0x8c,0x56,0x6e,0xcd,0x9d,0xd1,0x81,0x2b,0xa6,0xb5,0xe2,0xc3,0xcb,0xfb,0x70,0xde,0xcf,0x1e,0xc7,0xc4,0x49,0x1d,0xd0,0xc6,0x6c,0xd5,0x92,0x36,0x52,0xfa,0xc4,0x80,0xa0,0x72,0xc7,0xf2,0xb1,0x35,0x80,0x18,0xbf,0x9,0x18,0xbf,0x23,0x26,0x4,0xe0,0xf9,0xa,0x7b,0x75,0x38,0xf8,0xdd,0xb9,0x47,0xc3,0xb1,0x4,0xa4,0x10,0x35,0xde,0xbe,0xd6,0x1a,0x5a,0xeb,0xa,0x60,0x8c,0x31,0x95,0xf7,0x60,0x46,0xc1,0x57,0xf8,0xd6,0x27,0xe,0xc4,0xa7,0xe,0x99,0x8e,0x2d,0x7d,0xd9,0x92,0x39,0x68,0xe0,0x20,0xf2,0xdb,0x23,0xfc,0x72,0x9f,0xc0,0xcc,0x60,0xe,0x37,0x9e,0x77,0x14,0x8e,0xdc,0xa7,0x3b,0x4a,0x96,0x95,0x32,0x73,0x5a,0xeb,0xd2,0x58,0x88,0xca,0x7b,0x63,0x4c,0x15,0x70,0xa2,0x2c,0xeb,0x3f,0xec,0xd5,0x85,0xff,0xfd,0xd2,0x11,0xc8,0xf,0x66,0xb6,0x9a,0x3b,0x19,0x35,0x89,0x46,0xdc,0x74,0xd9,0xdd,0xd8,0x0,0x60,0xb3,0x43,0x28,0x9a,0x6c,0xae,0x88,0xc9,0x9,0xc2,0x77,0x3f,0x73,0x8,0x98,0x1,0xc7,0xb6,0x2a,0x83,0x66,0x8c,0x41,0x10,0x4,0x88,0xc5,0x62,0x88,0xc5,0x62,0xf0,0x7c,0xf,0x9b,0x36,0x6d,0x82,0xe3,0x38,0x70,0x1c,0x7,0x42,0x8,0x84,0xa1,0x82,0x6b,0x9,0x68,0xad,0x71,0xce,0xf1,0xfb,0xe0,0xd4,0x83,0xa6,0xa1,0xa7,0x2f,0xb,0xab,0xa6,0x7b,0x27,0xf,0xff,0x7b,0xb3,0x80,0xc0,0x3c,0x2a,0xca,0x12,0x31,0x1b,0xab,0xd7,0xf7,0xe1,0x3f,0xcf,0x78,0x37,0xf6,0x9e,0xda,0x6,0xcd,0xc,0x41,0x8c,0x20,0x8,0x60,0xdb,0x36,0x5c,0xd7,0x45,0x18,0x2a,0x6c,0xd8,0xb0,0x1e,0x96,0x6d,0x23,0x1e,0x8f,0x97,0x9e,0x2d,0xac,0x71,0x18,0x43,0xd,0xec,0x35,0x39,0x8d,0xff,0x3e,0xeb,0x30,0xc,0xe,0xe6,0x9a,0xe8,0x7f,0xd4,0x50,0x7,0x80,0x4a,0x5a,0xb1,0xfc,0xff,0x6d,0xf4,0x1,0x78,0x14,0x4d,0x40,0xe3,0x1a,0xb3,0x18,0x1,0xd7,0x7e,0xfa,0x60,0x14,0xfc,0x10,0xf1,0x98,0x33,0xc2,0x3c,0xf4,0xf6,0xf6,0xe2,0xda,0xef,0x5c,0x87,0xdb,0x6e,0xbb,0xd,0x5a,0x6b,0x24,0x12,0x9,0x64,0x32,0x59,0xcc,0x9e,0x3d,0x1b,0x9f,0xfa,0xd4,0xe9,0x38,0xe1,0xa4,0x13,0x87,0xcd,0x48,0x10,0xe2,0xdf,0x3e,0x3c,0x17,0xaf,0x6c,0x18,0xc2,0xca,0x41,0xf,0x89,0xaa,0xeb,0x8d,0xc,0x19,0xb7,0xee,0x28,0x96,0xb3,0x9b,0xbc,0xd5,0xc7,0x1f,0xdd,0xe2,0xa,0x22,0xbc,0xb1,0xa6,0xf,0xdf,0xf8,0xe8,0x2,0x2c,0xd8,0xbd,0x3,0x4a,0x97,0xb7,0x93,0x61,0x14,0xa,0x5,0x7c,0xff,0x7b,0xff,0xf,0x37,0xdf,0x7c,0x33,0x7c,0xdf,0x47,0x32,0x99,0xc2,0xc0,0x40,0x3f,0x76,0xdb,0x6d,0x6,0xce,0xf8,0xec,0xa7,0x71,0xec,0xb1,0xc7,0x42,0x4a,0x51,0xe9,0x25,0x24,0x4,0xc1,0x18,0xc2,0x6e,0x5d,0x49,0x5c,0x78,0xc2,0x3e,0xb8,0xe6,0xcf,0xaf,0xa2,0xad,0x2d,0x39,0xee,0x44,0x2a,0x37,0x39,0x75,0x45,0xd3,0x52,0x2c,0x13,0x40,0xe3,0xac,0xd5,0xb,0x82,0x10,0x27,0xcd,0x9f,0x8c,0xb6,0x84,0x83,0x98,0x6b,0x97,0xea,0xd4,0x22,0x15,0x68,0x59,0x16,0x7e,0xf8,0xc3,0xff,0xc1,0xc7,0x3e,0xfa,0x4f,0xb8,0xfb,0xae,0xbb,0xb1,0xd7,0x5e,0x7b,0x61,0xc1,0x82,0xfd,0xb1,0xcf,0x3e,0xfb,0xe0,0xdd,0xef,0x3e,0x18,0x96,0x25,0x71,0xd5,0x55,0xdf,0xc2,0x69,0xa7,0x9e,0x86,0xa5,0x4b,0x97,0x95,0x96,0x44,0x1,0x45,0x3f,0xc4,0x45,0x27,0xee,0x8b,0xc0,0xb,0xb7,0xe2,0x29,0x73,0x8d,0x62,0xa8,0xfe,0x67,0x9,0x81,0x2d,0x3,0x5,0xac,0xda,0x30,0x84,0x35,0x3d,0x19,0x64,0xf3,0x1,0x6c,0x29,0x23,0xc6,0x8d,0x9b,0x14,0x7e,0xa9,0x31,0x65,0x31,0x50,0xd8,0xab,0x3b,0x89,0xd3,0xe,0xd9,0xd,0x5,0x2f,0x44,0x18,0x4,0xb0,0x2c,0xb,0xbf,0xfd,0xcd,0x6f,0x71,0xec,0xfb,0x8f,0xc3,0xdd,0x77,0xdf,0x83,0x59,0xb3,0x66,0xe1,0x80,0x3,0xe,0xc0,0x3e,0xfb,0xec,0x8d,0x43,0xe,0x39,0x4,0xa9,0x54,0x12,0x57,0x7d,0xf3,0x2a,0x9c,0x7e,0xfa,0x27,0xf1,0xf8,0xe3,0x4f,0xc0,0xb6,0xed,0x8a,0x49,0x10,0x42,0x40,0x8,0x81,0xf,0xec,0x3f,0x1d,0x93,0x92,0x56,0x69,0x19,0xd8,0x38,0x84,0x6f,0xaa,0xf2,0x8,0xbc,0x43,0x7c,0x80,0xba,0x72,0xe5,0x26,0xd1,0x18,0x73,0x2d,0x78,0x43,0x39,0x7c,0xee,0x1f,0xf7,0xae,0x61,0xff,0x8c,0x31,0x90,0x52,0xe2,0xc2,0xb,0x2f,0xc2,0x1f,0x7e,0xff,0x7b,0x74,0x76,0x76,0xa2,0x6b,0x42,0x17,0x98,0x19,0xb6,0x6d,0x21,0x16,0x8b,0x41,0x4a,0x9,0xcb,0xb2,0x30,0x6d,0xea,0x34,0x4,0x7e,0x80,0x2f,0x9c,0xf5,0x5,0x2c,0x59,0xb2,0x4,0x96,0x65,0xc1,0x96,0x84,0x7d,0xa6,0xb5,0x63,0xc1,0xb4,0x34,0x0,0x44,0x3,0xc7,0x3c,0x2a,0x5,0x3b,0x32,0x44,0x3,0xd6,0x6f,0xe8,0xc3,0x79,0xc7,0xee,0x89,0x5b,0xcf,0x3d,0x2,0xbf,0x3a,0xfb,0x50,0xbc,0xef,0x5d,0x9d,0x58,0xb5,0xbe,0x1f,0x31,0xd7,0x1e,0xf3,0xbb,0x15,0xc1,0x97,0x12,0x3b,0x44,0x80,0x57,0xf4,0xf1,0xf9,0xa3,0xf7,0x40,0x10,0x28,0x48,0x62,0xc4,0xe3,0x71,0x5c,0x71,0xc5,0xbf,0xe3,0x7,0x3f,0xf8,0x2f,0x4c,0x9a,0x34,0x9,0x6d,0xad,0xad,0x10,0x42,0xc0,0x71,0x1c,0xb8,0x6e,0xac,0x22,0xe0,0xdd,0x66,0xcc,0x40,0x18,0x84,0xb8,0xf4,0x92,0x4b,0x71,0xef,0xbd,0xf7,0x56,0x26,0x47,0x19,0x4,0x8e,0x94,0x38,0xe7,0xf8,0x7d,0x30,0x30,0x58,0x80,0x94,0x62,0x1c,0xdc,0xd0,0xf0,0xba,0x0,0xb3,0x23,0x0,0xc0,0x64,0x6a,0x7,0xa3,0x9,0x40,0x12,0x11,0xb2,0x59,0xf,0x87,0xec,0xdd,0x8d,0x74,0xcc,0xaa,0xfc,0x94,0x31,0x6,0xc6,0x18,0xdc,0x74,0xd3,0xcd,0x78,0xf4,0x91,0x47,0x90,0x4a,0xa5,0xa1,0xb5,0x86,0xef,0xfb,0x28,0x14,0xa,0x18,0x18,0x18,0x40,0x6f,0x6f,0x1f,0x6,0x6,0x7,0x51,0x2c,0x16,0xe1,0x7,0x1,0x8,0xc0,0x6e,0x33,0x76,0xc3,0x39,0xff,0xf2,0x25,0xf4,0xf6,0xf6,0x55,0xba,0x71,0x1e,0xb9,0x4f,0x37,0x3c,0x2f,0x88,0x54,0x68,0xc5,0x44,0xf3,0x98,0x8e,0xac,0xd2,0x1a,0x41,0xae,0x80,0x25,0xdf,0x3a,0x11,0x9f,0x38,0x7c,0x77,0xcc,0xe8,0x4a,0x62,0xce,0xb4,0x36,0x7c,0xe3,0xb4,0xf9,0xf8,0xc5,0xbf,0xfc,0x3,0xd6,0x6e,0x1c,0x40,0xb4,0x8b,0x0,0x6f,0x2d,0x36,0x8a,0xd4,0xbf,0x4,0x5a,0x5d,0xb,0x73,0xa6,0xb7,0x43,0x10,0xc1,0x75,0x5d,0xfc,0xf1,0x8f,0x37,0xe0,0xf6,0xdb,0x6e,0xc7,0xd4,0xa9,0x53,0xa3,0xa5,0x72,0x61,0x88,0x5c,0x2e,0x8f,0xfe,0xbe,0x7e,0x6c,0xe9,0xdd,0x82,0xc1,0xd2,0xb3,0x85,0x61,0x8,0x21,0x8,0xa9,0x54,0x1a,0x57,0x2c,0xbe,0x2,0x99,0x4c,0x6,0xbe,0xef,0x57,0xae,0xee,0x29,0x8d,0xe3,0xe6,0x4f,0x43,0x3e,0x9b,0xaf,0x6c,0x89,0xd3,0x4c,0x10,0x6e,0x18,0x30,0xac,0x4a,0xaf,0xd5,0xf6,0x38,0x81,0x55,0xdc,0xf4,0xd6,0xa,0x16,0xea,0x2f,0x4c,0x84,0xa1,0xbc,0x8f,0xd3,0xe,0xdd,0x1d,0x79,0x7f,0xb8,0x74,0x49,0x29,0x5,0x66,0xc6,0x77,0xaf,0xbb,0xe,0xed,0xed,0x9d,0xc3,0x37,0x6d,0x18,0x9e,0xe7,0x21,0x93,0xc9,0x62,0x70,0x70,0x0,0xb9,0x4c,0x16,0xbe,0xef,0x47,0x7e,0x82,0x10,0xc8,0xe5,0x72,0x48,0xa5,0x52,0xf8,0xd1,0x8f,0x7e,0x4,0x63,0x18,0x61,0xa8,0x70,0xec,0xfc,0x29,0xc8,0xe,0x15,0x60,0x5b,0x54,0x97,0x8b,0x19,0xfd,0x46,0x7,0x32,0x1e,0xfe,0xfb,0xac,0x43,0x11,0x94,0x98,0x37,0x5b,0xa,0x58,0x52,0x20,0x54,0x1a,0xb,0x66,0xb4,0xe3,0xe3,0x87,0x4c,0x47,0xb6,0x10,0x34,0x6d,0x73,0x9,0x84,0x9,0x49,0x1b,0x53,0x3b,0x53,0x0,0x11,0x6,0xfa,0x7,0xf0,0xd3,0x9f,0x5c,0x8f,0xa9,0x53,0xa6,0xa2,0x58,0x28,0x2,0xcc,0xd0,0x5a,0xc1,0xf7,0x3d,0x64,0xb3,0x59,0xc,0xd,0xe,0x22,0x9f,0xcf,0x23,0x8,0x82,0x92,0xca,0x2f,0x6b,0x46,0x81,0xab,0xae,0xbc,0xa,0x1d,0x1d,0x1d,0x95,0x88,0x81,0x88,0xe0,0x87,0x1a,0x27,0x1e,0x3c,0x3,0x83,0x99,0xe2,0x38,0x9c,0x40,0x6e,0xba,0x8,0xab,0x29,0xd,0x40,0x4c,0x63,0x65,0x71,0x47,0x9,0xfd,0x8,0x7d,0x3,0x39,0x2c,0xd8,0xbd,0xa3,0x44,0xdc,0x44,0x4e,0x5f,0x2c,0x1e,0xc3,0x5d,0x7f,0xbe,0xb,0x4a,0xe9,0x1a,0x54,0x53,0xd,0x77,0xce,0x95,0x22,0x47,0x66,0x3,0x63,0xa2,0x81,0xb6,0x2c,0xb,0x4f,0x3c,0xf6,0x4,0xf2,0xf9,0x3c,0xc2,0x50,0xa3,0x23,0x9d,0x80,0x36,0x66,0x14,0x1b,0xc9,0xd,0x66,0x6,0x63,0x6a,0x8b,0x83,0x89,0x2d,0xb1,0xd2,0x76,0x33,0xc3,0xdf,0xb3,0x4a,0x2a,0x76,0xee,0x6e,0xed,0xb0,0xa4,0xa8,0x8a,0xbf,0x87,0x8b,0x36,0x99,0x79,0xc4,0x80,0x1a,0x66,0xcc,0x9c,0x90,0x4,0x40,0xd0,0x5a,0x63,0xc9,0xb3,0xcf,0x22,0x97,0xcd,0xc2,0xf3,0xbd,0x28,0x27,0x5f,0xdf,0xc,0xa2,0xea,0xff,0x54,0xd9,0x43,0x80,0xd1,0xd9,0xd9,0x89,0x7,0x1f,0x7c,0x8,0xbd,0xbd,0x7d,0x90,0xa5,0x35,0x81,0x42,0x8,0x28,0xcd,0xd8,0x67,0x5a,0x1b,0xfc,0xb0,0xf9,0xa,0x2c,0xae,0x4d,0x38,0xbe,0x9,0x3c,0x40,0x13,0x26,0xc0,0x30,0x23,0x1d,0x77,0x10,0xb3,0x64,0xd,0x1b,0x15,0x73,0x63,0xb8,0xf3,0xce,0x3b,0xd1,0xd9,0xd9,0x5,0x63,0xb8,0xc9,0x18,0xd7,0x44,0x2d,0xd8,0x85,0xc0,0x6b,0xaf,0xbf,0x86,0x4c,0x26,0x3,0xa5,0x23,0xd5,0x36,0xb9,0x3d,0x1e,0x2d,0x48,0x81,0x19,0x31,0x0,0xc3,0x42,0x2b,0x35,0x66,0xe2,0x68,0x51,0x85,0x14,0xa3,0x3f,0x40,0xdc,0x96,0x51,0xe1,0xb,0x50,0x11,0x78,0x2d,0xdf,0x30,0xdc,0xd1,0x43,0x12,0x90,0xcb,0x79,0x98,0xd6,0x95,0x0,0x4a,0xaa,0xf6,0xf9,0xe7,0x9f,0xdf,0x26,0x42,0x4a,0x29,0x85,0x96,0xd6,0x16,0x3c,0xf4,0xe0,0x83,0xb0,0x6c,0xbb,0x32,0x6,0xca,0x18,0x4c,0x6d,0x4f,0x20,0x8,0xf5,0x8,0x52,0x88,0xb7,0x1a,0xae,0x6e,0x1d,0x1,0x62,0x6b,0x2c,0xde,0xf0,0x25,0xc6,0xe9,0x89,0x32,0x90,0x88,0xc9,0xa8,0x61,0x72,0xdd,0x9d,0xaf,0x5c,0xb9,0xa,0xf1,0x78,0xac,0xe6,0xe6,0x9a,0xb5,0x2e,0xf1,0x78,0x1c,0xab,0x56,0xad,0x86,0x6d,0x45,0x11,0x6c,0xc2,0xb5,0x2a,0xc2,0x45,0xb9,0x94,0xba,0x61,0xbc,0x3e,0xfe,0xc5,0x26,0x34,0x66,0x36,0x32,0x7a,0xae,0x20,0x50,0x68,0x89,0xd9,0x51,0xc7,0x6b,0x66,0x6c,0xda,0xb4,0x9,0x96,0x35,0xfe,0x16,0xcc,0xc6,0x18,0x24,0xe2,0x9,0xac,0x59,0xbb,0xa6,0xe6,0xfb,0xc6,0x30,0xd2,0x31,0xb,0x41,0xa8,0x46,0xdc,0x11,0x35,0x77,0xe3,0x3b,0x48,0x3,0x54,0x6d,0x96,0xd8,0xb4,0x9,0x18,0xf2,0x20,0x88,0x60,0xea,0xaa,0x87,0xcb,0x6c,0xd8,0xb6,0xdc,0x73,0x39,0x56,0x2e,0x7f,0x7b,0xf3,0x40,0xb1,0xa1,0x87,0xfc,0xa6,0x71,0xc3,0x55,0x97,0xd4,0xda,0xa0,0xbd,0x2d,0x89,0xb5,0xfd,0x5,0x40,0x8e,0x55,0xcb,0xd0,0xfc,0xb3,0xd5,0x7f,0xdf,0x92,0x2,0x1b,0x87,0x3c,0x24,0xe2,0x4e,0xf3,0xc9,0xa1,0x1d,0x17,0x6,0x6e,0x7,0x9d,0xc6,0xc,0x37,0xe6,0x60,0xe3,0x60,0x11,0xb2,0x2e,0xb7,0x3f,0x63,0xf7,0xdd,0x10,0x4,0xe1,0x36,0x5d,0x36,0x9f,0xcf,0x63,0xda,0xb4,0xe9,0xd0,0x2a,0xda,0xff,0x29,0xeb,0x5,0x10,0x6f,0x53,0x56,0x90,0x4b,0xce,0xee,0xba,0xde,0x3c,0xa2,0xfd,0x5,0x80,0x49,0xdd,0x93,0x10,0x86,0xe3,0xdf,0x19,0x56,0x4a,0x89,0x5c,0x2e,0x83,0xe9,0xd3,0xa7,0x57,0x9c,0x40,0xe6,0xa8,0x56,0x62,0x7d,0x5f,0xbe,0xa1,0xd9,0xda,0x11,0x64,0xa7,0x68,0xfe,0x61,0x69,0x5c,0x4b,0xdd,0xc,0x3,0xc9,0x98,0x83,0xfb,0x96,0x6e,0x40,0xcc,0x1e,0xae,0x49,0xf0,0x7d,0x1f,0xc7,0x1f,0x7f,0x3c,0x6,0x7,0xfa,0xab,0x16,0x8a,0x36,0x37,0xda,0x4a,0x29,0xcc,0x9e,0x3d,0x1b,0xad,0xad,0x2d,0x70,0x2c,0x89,0x25,0xcb,0x7b,0x90,0x4c,0xb8,0x55,0xf5,0x2,0x6f,0x65,0x22,0x28,0x7a,0x2d,0x8,0x58,0xd5,0x9b,0xaf,0x0,0x7a,0xee,0xdc,0x39,0x30,0x46,0x37,0xe5,0x81,0xd7,0xcc,0x74,0xcb,0x42,0x7f,0xff,0x20,0x8e,0x79,0xdf,0x31,0x95,0x50,0xd0,0x18,0x83,0x98,0x2d,0xf1,0xc0,0x8b,0x9b,0x90,0x8a,0x3b,0x4d,0xeb,0xb7,0xd2,0xd2,0xd0,0x11,0x41,0xeb,0x36,0x84,0x81,0xd5,0x2a,0xc5,0x8c,0xb,0x72,0xc6,0x18,0x4c,0x9a,0xd8,0x82,0x9b,0x9e,0x58,0x5,0xd7,0x95,0x15,0xda,0xb7,0x50,0x28,0xe0,0x84,0x13,0x4e,0x80,0x32,0x7a,0x7c,0xf9,0x6e,0x2,0xc2,0x30,0xc4,0xe1,0x87,0x1f,0x8e,0x64,0x32,0x9,0xd7,0xb5,0xf1,0x97,0x17,0x36,0x20,0x16,0x7b,0x8b,0x1,0x50,0x57,0xb5,0xae,0xd,0x23,0x13,0x18,0xbc,0xbe,0x21,0xda,0x7,0x62,0xfe,0x82,0xf9,0x68,0x6d,0x6d,0x85,0x94,0xb2,0xe9,0x39,0x4a,0x44,0xe8,0xef,0xeb,0xc3,0x71,0xc7,0x1f,0x87,0xd6,0x96,0xd6,0x1a,0x1,0xc,0x79,0x21,0x9e,0x5d,0xb1,0x5,0xa9,0x84,0x3b,0x3e,0x7f,0x7d,0xc7,0x44,0x1,0x65,0xe2,0x87,0x9b,0xca,0x88,0xd5,0x1f,0x5a,0x1b,0xc,0x78,0x6,0x4f,0xbe,0xda,0x13,0x6d,0x1,0x4b,0x54,0xa1,0x3c,0x2f,0x38,0xff,0x7c,0xc,0xd,0xd,0x80,0x48,0x6c,0x15,0x8,0xcc,0x88,0x12,0x45,0x9e,0x87,0xcf,0x9d,0xf9,0x59,0x18,0x66,0xc,0xe4,0x2,0x3c,0xfa,0x6a,0xf,0x5c,0xc7,0x1a,0x23,0x9a,0x78,0xf3,0xeb,0xc7,0xb4,0x61,0x4,0x4c,0xb8,0xef,0xc5,0x8d,0x30,0xcc,0x98,0x30,0x61,0x2,0xce,0x3a,0xeb,0x4c,0xac,0x5f,0xbf,0x1e,0xf1,0x78,0x62,0xab,0x5a,0xa0,0x3c,0x31,0x3c,0xdf,0xc3,0x85,0x17,0x2e,0xc4,0xd0,0xd0,0x50,0xc5,0xf,0x48,0xc7,0x6c,0x7c,0xef,0xf6,0x65,0x98,0x31,0x7d,0x42,0xc9,0x9,0xdc,0x96,0xa7,0xe3,0x1d,0x63,0x2,0x1a,0x5f,0x68,0x6c,0x5b,0x10,0x2a,0x3,0x27,0x11,0xc3,0xaf,0x1e,0x59,0x8e,0x50,0x69,0x28,0xa5,0x20,0x84,0x80,0x94,0x12,0xa7,0x9e,0x76,0xa,0xe,0x3a,0xe8,0x60,0x28,0x15,0x34,0xf4,0x9a,0xab,0x7f,0xcd,0xb6,0x6d,0xbc,0xf4,0xd2,0x4b,0xf8,0x9f,0x1f,0xfd,0x10,0x1d,0x9d,0x9d,0xd0,0xda,0xe0,0xfe,0x17,0x37,0x61,0x43,0x4e,0x45,0xf9,0x9e,0x11,0xbd,0x88,0xde,0x42,0x85,0x40,0x40,0x3a,0xe9,0xe2,0x27,0xf,0xad,0x40,0xa8,0xc,0x86,0xb2,0x79,0x9c,0x72,0xea,0x29,0x38,0xfd,0xf4,0x4f,0x60,0xd5,0xaa,0x95,0xa5,0x44,0xcf,0xe8,0xbd,0x84,0x5d,0xd7,0xc5,0xe6,0xcd,0x9b,0xb0,0xf8,0x8a,0xc5,0x68,0x6b,0x6b,0x83,0xe3,0x38,0x20,0x8a,0x38,0x85,0x57,0x36,0xc,0xe2,0x91,0xd7,0x7a,0xe1,0x38,0x56,0x43,0x20,0x51,0x53,0x2a,0x60,0x7,0xf9,0x0,0xdb,0x52,0x10,0x42,0x14,0x79,0xb1,0x8f,0x2e,0xef,0xc7,0x23,0xaf,0xf4,0x44,0xbb,0x7a,0x6a,0x3,0x21,0x4,0x8a,0x45,0xf,0x3f,0xf8,0xaf,0xff,0xc4,0x91,0x47,0x1e,0x89,0xc1,0xc1,0x1,0x64,0x72,0xd9,0x12,0x35,0x2a,0x2a,0x7d,0xf0,0x7c,0xdf,0xc7,0xe0,0xc0,0x20,0x98,0xd,0x6e,0xb9,0xf5,0x66,0xcc,0x9e,0x3d,0x1b,0x64,0x34,0xfa,0xf3,0x1,0xbe,0x75,0xdb,0x32,0x4c,0x68,0x4f,0x34,0x78,0xce,0xb7,0xc1,0x23,0x64,0xc6,0x9b,0xa6,0xb3,0x95,0x0,0x0,0x1e,0xa3,0x49,0x44,0x41,0x54,0x84,0x8e,0x14,0xbe,0xfc,0xb3,0x27,0xd1,0x9a,0x74,0x50,0x28,0x14,0x71,0xde,0xf9,0xe7,0xe1,0x8b,0x5f,0x3c,0x3b,0x62,0xff,0x86,0x86,0x22,0xf0,0x53,0xf4,0x6c,0x44,0x54,0x62,0x3d,0x33,0x8,0x2,0x1f,0xd7,0x7c,0xe7,0x1a,0x1c,0x7b,0xec,0xb1,0x95,0x28,0x20,0x8,0x15,0xa4,0x20,0xfc,0xf0,0xee,0x57,0x10,0x48,0xb,0xa1,0x32,0xd,0x1f,0x8b,0xb7,0xc2,0xe0,0x36,0xe5,0x7b,0x34,0x27,0xf5,0xba,0x1d,0x71,0xc7,0x59,0x94,0xd9,0xdd,0xd5,0x82,0x4b,0x7e,0xff,0x2c,0xba,0x5a,0x62,0x38,0x68,0x56,0x27,0xbc,0xd0,0xc0,0x75,0x5d,0xc,0xe,0xe,0xe2,0x92,0x4b,0x2f,0xc1,0x49,0x1f,0x3e,0x9,0x7f,0xb9,0xe7,0x5e,0x3c,0xfc,0xc8,0xc3,0x58,0xbb,0x66,0x2d,0x8a,0xc5,0x2,0x3a,0xbb,0xba,0x30,0x6f,0xde,0x7c,0x1c,0x75,0xd4,0x91,0x38,0xee,0xb8,0x63,0x91,0x4e,0xa7,0x10,0x86,0xa,0xf9,0xc0,0xe0,0x8c,0xff,0x7a,0x14,0x9d,0x5d,0x2d,0x4d,0xd4,0x9e,0x8e,0x1f,0xc,0xbc,0xd,0x27,0x30,0x0,0xd7,0xb1,0xb0,0x72,0xd0,0xc7,0xa2,0x1b,0x9e,0xc7,0xa2,0x8f,0x2e,0x40,0x36,0x5f,0xc4,0xd9,0x5f,0x3c,0x1b,0x47,0x1e,0x75,0x24,0xee,0xbe,0xeb,0x6e,0x3c,0xf8,0xd0,0x43,0x58,0xb3,0x7a,0x2d,0xb2,0xb9,0xc,0x3a,0xda,0x3b,0xb0,0xef,0xbe,0xfb,0xe0,0xa8,0xa3,0x8f,0xc2,0x71,0xc7,0x1e,0x87,0xf6,0xce,0x76,0x14,0x8b,0x45,0x58,0x96,0x5,0x2f,0x8,0xd1,0x9e,0x76,0x71,0xc5,0xef,0x97,0xe0,0x6f,0xab,0x87,0x86,0xab,0x9f,0x78,0xfc,0xa0,0x6c,0x6,0xc,0x56,0x53,0xd7,0xd8,0xce,0x71,0x55,0xca,0x60,0xf2,0x94,0x4e,0xfc,0xeb,0xf5,0x8f,0xe3,0x5b,0xa7,0x1f,0x84,0x43,0x66,0x4f,0x80,0xd6,0x1a,0x8e,0xe3,0xc0,0xf7,0x7d,0xcc,0x9e,0x3d,0x1b,0xb3,0x67,0xcf,0xc6,0x97,0xbe,0x7c,0x4e,0xb4,0xd,0x5b,0x3,0x32,0xca,0xf7,0x43,0x64,0x3c,0x85,0xcf,0xfc,0xd7,0xa3,0xa0,0x78,0xc,0xc6,0x94,0x8,0xa6,0x1d,0x5c,0x21,0x4c,0xcd,0x9c,0xc0,0x8d,0x7d,0x81,0x64,0xdc,0xc1,0xad,0x4b,0x36,0x40,0x6b,0xc6,0xa5,0xa7,0xcc,0x43,0xa1,0x58,0xc4,0xee,0xbb,0xef,0x8e,0x2f,0x9c,0xfd,0x5,0x9c,0xfd,0xc5,0xb3,0x47,0x38,0xc9,0x5a,0x6b,0x8,0x21,0xa0,0x95,0x86,0x94,0x12,0x61,0x18,0xc2,0x91,0x2,0x17,0xff,0xf2,0x29,0xfc,0x6d,0x75,0xa6,0xaa,0xf4,0x6d,0x7c,0x0,0x1e,0x87,0xf,0xd8,0xa4,0x9,0xa8,0xe4,0xc8,0xb7,0xb1,0x36,0x9f,0x0,0xa5,0x34,0xba,0xba,0x3b,0x70,0xc1,0xaf,0x9f,0xc5,0xcf,0x1e,0x7c,0x1d,0x7d,0x59,0xf,0x71,0x3b,0xda,0x6c,0x11,0x14,0x65,0xf3,0x42,0xa5,0xe0,0xfb,0x1,0x8a,0x9e,0x8f,0xa2,0xe7,0x43,0x29,0x85,0xb8,0x1b,0x61,0xf4,0x9e,0x17,0xd6,0xe3,0xd4,0x6b,0x1f,0x82,0xb6,0x9d,0xda,0x72,0x32,0x6a,0xfe,0x1e,0x88,0x80,0x21,0x3f,0x84,0x32,0x66,0x94,0x81,0x65,0xe4,0xbc,0x10,0x81,0x32,0xdb,0xec,0x49,0x4c,0x9b,0xdc,0x86,0xbb,0x5f,0xee,0xc1,0xe7,0x7e,0xf8,0x57,0xbc,0xb2,0x7e,0x8,0x31,0xd7,0x82,0x2d,0x5,0x82,0x20,0x84,0xe7,0xf9,0xf0,0x3c,0x1f,0x7e,0x10,0x20,0x54,0xaa,0xe4,0x0,0x47,0x2d,0xe0,0x62,0x36,0xe1,0x95,0xd,0x43,0x38,0xff,0x97,0x4f,0xe2,0xde,0xd7,0xfa,0xe0,0x94,0x6a,0x27,0xb6,0xd5,0x24,0xed,0x58,0x13,0x50,0xbd,0x11,0xf2,0x36,0x9a,0xd9,0xc8,0xb1,0x31,0xe8,0x9e,0xd8,0x82,0xdf,0x3c,0xb5,0x1e,0xb7,0x3d,0xb3,0x16,0xef,0xdb,0x6f,0x32,0x4e,0x3e,0x74,0x26,0xde,0x35,0xbd,0x1d,0xac,0x34,0xb4,0x36,0x20,0x21,0x22,0xd2,0x43,0x4a,0xf4,0x67,0xa,0xf8,0xf9,0x83,0xaf,0xe3,0x86,0xc7,0xd7,0xa0,0x37,0x30,0x48,0xb7,0x25,0x23,0x16,0xb0,0x3e,0x31,0x35,0xa2,0x21,0x43,0xe3,0xd4,0x29,0x9,0xc2,0xba,0x2d,0x45,0xbc,0xbe,0x29,0x8b,0xb9,0xd3,0xdb,0x61,0x98,0x2b,0x9,0xa1,0x40,0x19,0x38,0x96,0xc0,0x63,0xcb,0xfb,0xb6,0x8b,0xcd,0xb,0x42,0x8d,0xce,0xb6,0x4,0xd6,0x64,0x3,0x9c,0xf3,0xbf,0x4f,0x61,0xee,0xd4,0x34,0x4e,0x7f,0xcf,0x2c,0x1c,0x35,0x67,0x72,0x54,0xa4,0xaf,0x39,0x2a,0x17,0x43,0x54,0x2,0x6,0x22,0x3c,0xb0,0x74,0x3,0x7e,0xfb,0xc8,0x1b,0x58,0xb6,0x21,0xb,0x2b,0xee,0xa2,0xbd,0x25,0x51,0xb5,0x9,0xe6,0x36,0x80,0x80,0x78,0xc7,0x98,0x0,0xf0,0x28,0x88,0xda,0xe,0xb5,0xcb,0xcc,0x48,0x27,0x5c,0xd8,0xb6,0xc4,0x9f,0x5f,0xee,0xc5,0xaf,0xfe,0xb6,0xa,0x9d,0x49,0x1b,0xfb,0xce,0xe8,0xc4,0xb4,0xb6,0x38,0x5c,0x4b,0x62,0x4d,0x7f,0x1,0x2b,0x7b,0xf2,0x58,0xde,0x93,0x41,0x6b,0x3a,0x81,0xae,0x8e,0x14,0x5a,0xec,0x66,0xc9,0x15,0x1a,0x73,0x62,0x4c,0xef,0x6e,0xc5,0xd9,0x3f,0x79,0x1c,0x7f,0xba,0xf0,0x7d,0xe8,0x6e,0x89,0xd,0xef,0x4a,0x4e,0x84,0x5b,0x9f,0x59,0x8b,0x9b,0x9f,0x5e,0x8b,0x19,0x53,0xda,0xc7,0x4c,0x54,0x6d,0x9d,0x3,0x61,0xc4,0x5d,0x1b,0xa9,0xb8,0x83,0x55,0x19,0x85,0xb,0x7e,0xf7,0x1c,0x54,0xf8,0x34,0xe6,0x4e,0x6f,0xc7,0xb4,0xf6,0x38,0x26,0xb6,0xba,0xd8,0x92,0xf5,0xb1,0xbe,0xbf,0x80,0xa7,0x5f,0xef,0x41,0x2a,0x9d,0x40,0x7b,0x6b,0x1c,0x2d,0xed,0xe9,0xa8,0x5e,0xa2,0xc9,0x19,0x3c,0x2a,0x3c,0x18,0xd8,0x41,0xeb,0x2,0xaa,0x92,0x35,0xd1,0xee,0x87,0x3b,0xc6,0xd1,0x26,0x20,0x8,0xd,0xe2,0x31,0x7,0xbb,0x4f,0xef,0x82,0x61,0xe0,0x95,0x5e,0xf,0xcf,0xae,0xcb,0x46,0x31,0xbf,0x6b,0x21,0x11,0x73,0xb0,0xc7,0x8c,0x9,0xd0,0x9a,0xe1,0x7,0x7a,0xec,0x1d,0xe2,0xc6,0x23,0x1c,0x66,0x4c,0xee,0x6e,0xc3,0x87,0xae,0x7e,0x0,0xc7,0xcf,0x9b,0x82,0x19,0x1d,0x71,0x78,0x4a,0xe3,0xb9,0x35,0x43,0x78,0x7e,0xed,0x20,0x66,0x4e,0x6d,0x2f,0x65,0x17,0xb7,0x93,0x5a,0xa0,0xa8,0xec,0x5b,0x5a,0x2,0xdd,0x5d,0x69,0x10,0x11,0x7a,0x7c,0x83,0xd7,0xdf,0xe8,0xaf,0x14,0x80,0x26,0x62,0x36,0x66,0xce,0xec,0x86,0xd1,0x26,0x22,0xb3,0xb6,0xc1,0xde,0x8f,0x8a,0xf4,0x26,0x9d,0x0,0xab,0xb9,0x18,0x0,0x86,0x44,0xa9,0x6d,0xa9,0xa0,0xed,0x52,0x91,0xd5,0x21,0xa2,0x61,0x46,0x50,0xca,0x73,0xdb,0x52,0xc0,0x49,0xb9,0xa5,0xfb,0x8f,0x12,0x48,0x81,0xc1,0x76,0x27,0x58,0x46,0x23,0x6f,0xba,0xbb,0xd2,0x78,0x62,0xcd,0x10,0x1e,0x5f,0x3d,0x4,0x6,0xc3,0x12,0x2,0x93,0xba,0x52,0x63,0xb,0x1f,0xe3,0xd4,0xca,0xa5,0x46,0x5f,0x15,0xa6,0x92,0x81,0x96,0x94,0x1b,0x6d,0xf2,0x50,0xba,0x4c,0x10,0xa8,0x1d,0xf4,0x54,0x5c,0x43,0x5,0xef,0x18,0xd,0x50,0xba,0x51,0x5b,0x60,0xa8,0x67,0xf3,0x20,0x12,0x31,0x7,0xf1,0x54,0x1c,0x42,0x10,0x5b,0x8e,0xbd,0xc3,0xd7,0x66,0x31,0xef,0x10,0x2b,0xd3,0xb4,0xad,0xb2,0x2d,0x59,0x91,0x27,0xa1,0xc9,0xa5,0x58,0xbc,0xfd,0xb7,0xb4,0xa3,0xa8,0xaa,0x7a,0x2c,0xb2,0xd6,0xac,0x95,0x26,0x15,0x2a,0x1b,0x86,0x13,0x25,0x7b,0x34,0x3e,0x0,0xec,0xf3,0xb9,0xef,0x4b,0x96,0x31,0x1,0x66,0xfd,0xca,0xf,0x3f,0x57,0x6a,0x15,0xce,0x97,0xf4,0x2d,0xbb,0xff,0xf5,0x4c,0xdb,0xd4,0xa3,0xe2,0x5d,0xd3,0x8f,0x71,0xe2,0xc9,0xb6,0x64,0xd2,0xd5,0xa9,0x74,0x52,0x92,0xb4,0xc7,0x37,0x2a,0x4d,0x4a,0x96,0x76,0xf0,0xf5,0x1a,0x9d,0x5c,0x13,0x48,0xec,0xec,0xab,0x8e,0xc7,0xd2,0x3c,0xcc,0x8,0x7c,0x8f,0x33,0x43,0x79,0x52,0x81,0x46,0xbe,0x6f,0xe3,0x93,0x30,0xc1,0xcf,0xeb,0xa7,0xd5,0xde,0x9f,0xff,0x91,0x5,0xc0,0xbc,0xf2,0xe3,0x2f,0x8c,0xec,0x11,0xb4,0xf7,0x59,0x3f,0x22,0x80,0x93,0x20,0xab,0xe3,0x95,0x1f,0x9f,0xb9,0xe6,0x5d,0x5f,0xb8,0x9e,0x88,0x15,0x5e,0xf9,0xf1,0x17,0xca,0x17,0x38,0xc,0xc0,0x24,0x0,0x98,0x70,0xf0,0x29,0xf3,0x3a,0xe6,0x7f,0xe0,0xf2,0x9,0xdd,0xed,0xe4,0xc4,0x5c,0x90,0x90,0xb5,0xfd,0x1,0x4a,0xbf,0xcb,0xcc,0xb5,0xeb,0xfe,0x85,0x1c,0x7d,0x87,0x4d,0x1a,0xd9,0x1b,0x88,0x68,0x6b,0xe7,0x8d,0xdc,0x5a,0xbe,0xf6,0xf4,0xba,0x92,0xb3,0x7a,0x53,0x42,0x34,0x8a,0xe3,0x54,0xf5,0xc,0x8d,0xa6,0xfe,0x98,0xed,0x5f,0xab,0xfe,0xd6,0x60,0xe5,0xd2,0x88,0x86,0x58,0xa3,0xad,0xf9,0x2f,0x95,0xc3,0xd,0x7f,0x5f,0x34,0xa8,0x8,0x8a,0x2e,0xa8,0xc3,0x0,0xfd,0xbd,0x43,0xc8,0xf5,0xae,0xff,0xdb,0x8a,0xdf,0x5d,0x7c,0xd,0x9,0x49,0x6c,0xf4,0x93,0x0,0xd6,0x95,0xcf,0x7d,0xd7,0x59,0x3f,0xa2,0x57,0x7f,0xf2,0x85,0x11,0x10,0xaa,0xe4,0x69,0xbb,0xe,0x38,0x41,0x30,0x63,0xf,0x21,0xf8,0xf5,0x8e,0xf9,0x1f,0x3a,0x13,0x46,0x77,0xa,0x49,0xc1,0xe4,0x43,0x4e,0xed,0xef,0x79,0xea,0x96,0x0,0xc0,0x54,0x8,0xd9,0x22,0x63,0x69,0x3b,0xb7,0xfa,0xb9,0x8d,0xf1,0x49,0x7b,0xcb,0xd6,0x49,0x53,0xf7,0xb3,0x6c,0x2b,0x4a,0xeb,0x56,0x1,0x80,0x8d,0x41,0x18,0x4,0xec,0x17,0x7d,0xa,0x83,0x0,0x61,0xa0,0x10,0x6,0x1,0x88,0xc,0x54,0x10,0x1a,0x29,0x65,0x49,0x20,0xd1,0xb2,0x95,0x61,0x46,0x8f,0x9a,0x6,0x0,0x81,0x9a,0x16,0xfe,0xf0,0xd7,0xc6,0x0,0xc0,0x56,0xd5,0xd0,0xd8,0x8d,0x2b,0xb7,0xc5,0x5c,0x31,0x73,0x69,0xd3,0xa7,0xd2,0x1b,0xd6,0x8,0x83,0x90,0x75,0x18,0x40,0x5,0x21,0xf9,0xbe,0x8f,0xd0,0xf,0xa3,0x54,0x33,0x6b,0x48,0x29,0x6a,0x52,0xe8,0x95,0x9e,0xab,0x5a,0xa1,0x90,0x2f,0x78,0xaf,0xfe,0xfc,0xbc,0x85,0x4e,0xcb,0xc4,0xb8,0x9,0x3d,0xd,0x36,0xeb,0x0,0x64,0x1,0x60,0xdf,0xb3,0x7f,0xba,0x17,0x1b,0x73,0x5c,0xd7,0x81,0x27,0x7d,0x7f,0xc2,0x81,0x27,0x66,0x7a,0x97,0xdc,0xfe,0x4a,0x3,0x13,0xc0,0x64,0x8c,0x99,0xe8,0xc6,0x5c,0xb4,0x75,0xa4,0xa7,0xfb,0xc5,0xf0,0xeb,0xf9,0x5c,0xf1,0xeb,0xbe,0xf6,0x56,0xec,0xfd,0xf9,0x1f,0xfd,0x6d,0xe0,0xe5,0x7,0x1f,0xde,0xfc,0xe8,0x6f,0x7a,0xb5,0x97,0x55,0x0,0xa0,0xbd,0xdc,0x50,0xa3,0xd6,0x66,0xc,0x86,0xd1,0x1a,0x3,0xfd,0x43,0x94,0xeb,0xd9,0xf4,0x54,0x7e,0xe3,0xcb,0xf,0x3b,0xad,0x13,0xa7,0xd9,0x2d,0x13,0xf7,0x4e,0xb6,0x76,0xbd,0x8b,0x89,0x62,0x96,0x95,0x3,0x91,0x40,0x2c,0xe1,0x68,0x41,0x64,0x52,0x2d,0x9,0x61,0x34,0xaa,0x5a,0x89,0x96,0x5f,0x5a,0x55,0x5c,0x4,0x6d,0xb3,0xe6,0xc4,0x36,0x3b,0x91,0x54,0x29,0x41,0xe7,0x9a,0xae,0x68,0x8c,0x11,0x2d,0x6f,0xcb,0xfd,0x79,0xb9,0x4c,0x9a,0xf3,0xc8,0xd9,0x6a,0xca,0x79,0x7a,0x3,0x66,0x66,0x21,0x60,0xc2,0x40,0xb1,0xef,0x7,0x1c,0x6,0xca,0xe,0x43,0x5,0xa3,0x34,0x31,0x3,0x61,0x31,0xb7,0xc6,0xcb,0xf4,0xbe,0xea,0xf7,0xaf,0x7f,0x4d,0xb8,0xc9,0x54,0x7a,0xfa,0x7e,0x9f,0xec,0xea,0x6e,0x93,0xb1,0x58,0x1c,0x24,0xc4,0x48,0xa2,0x5e,0x5,0xfd,0x56,0xb2,0xdd,0xd,0xf3,0x3,0x3e,0xeb,0xd0,0xb4,0xed,0x7b,0x54,0xcb,0xe4,0xf7,0x7c,0xe2,0x93,0x86,0x71,0x5a,0x18,0x86,0xb3,0xe3,0x71,0xa7,0x8d,0xd,0x23,0xc,0xc3,0x67,0x0,0xdc,0x5c,0x3,0x80,0x77,0x9d,0xf9,0x43,0x22,0x6,0xb3,0xe1,0xc9,0xb6,0x6d,0xc1,0x75,0x5d,0xb8,0xae,0x8b,0xd6,0xf6,0x34,0x7,0x7e,0x30,0x23,0x97,0x2d,0x4c,0x6b,0xdf,0xfb,0xa8,0x4f,0x6,0x3,0x9b,0xce,0x1f,0x78,0xf1,0xfe,0x95,0x95,0xa1,0x6d,0xd4,0xb7,0x26,0xaa,0x1d,0xe0,0xe2,0xe0,0xd0,0xa6,0xf5,0xf7,0x7c,0xf7,0x7b,0x61,0x6e,0xc0,0x67,0xe5,0x57,0x4a,0x5a,0xad,0x64,0xbb,0xd3,0xb5,0xff,0x7,0xe6,0xc6,0x27,0xce,0xde,0xaf,0xd8,0xd2,0x3d,0x5f,0x58,0x6e,0x77,0x5f,0xcf,0xa0,0x65,0xbb,0x8e,0x1d,0x8b,0xbb,0x24,0xa5,0x44,0x22,0x11,0x8b,0x2e,0x4f,0x1,0xa4,0x6d,0x83,0x84,0xb5,0x9d,0x6,0xf4,0xad,0xeb,0x28,0xc1,0x35,0xd4,0x23,0xc0,0x46,0xc3,0xa8,0x10,0x5c,0x62,0x1f,0xb5,0xd2,0xc8,0xe7,0x7d,0x10,0x31,0x65,0x6,0xf3,0x2c,0x88,0x43,0x36,0xc6,0xd7,0x85,0x81,0x17,0xfd,0xfe,0x75,0x4b,0x33,0x2b,0x9e,0x5a,0x3a,0xf4,0xfa,0xe3,0x1b,0x2b,0x57,0x89,0xd4,0x39,0x4f,0xff,0xe0,0x5,0x8e,0x97,0xde,0xff,0xe3,0x31,0xd7,0xe5,0x72,0x1f,0x77,0xaa,0xb6,0x46,0x1c,0xed,0xe1,0x47,0x52,0x12,0x2b,0xa0,0xfb,0xb0,0x7f,0xba,0x53,0x90,0x98,0x91,0x48,0x38,0xba,0xb5,0x2d,0x2d,0x18,0xcc,0x85,0x6c,0x81,0x7,0x7,0xc2,0x3d,0x47,0x3a,0x81,0x44,0x10,0xca,0x63,0x40,0x76,0x5a,0x56,0x94,0xad,0x22,0xcb,0x6,0x1b,0x43,0x6e,0xcc,0x95,0xc9,0x54,0xcc,0xac,0x5b,0xbd,0x19,0xc4,0xd5,0x95,0x17,0xa3,0xe7,0xe0,0x9,0x80,0x31,0x6a,0x28,0xcc,0xf5,0x7b,0x6c,0x34,0x93,0x90,0x24,0xdc,0xa4,0x45,0x24,0xc8,0x84,0x9e,0xde,0xf4,0xe8,0x6f,0x9e,0x1,0xf0,0x4c,0xf9,0x1b,0x6d,0x7b,0x1d,0x36,0x39,0xd6,0xbd,0xe7,0x6e,0x76,0xeb,0xa4,0xe9,0x56,0xb2,0x6d,0xaa,0x70,0x92,0x53,0x9d,0x44,0x7a,0x8a,0x1b,0x8b,0x25,0x52,0xe9,0x38,0x27,0xd2,0x49,0x2,0x59,0xa3,0x84,0x84,0x75,0xaa,0xbf,0x54,0x1c,0xca,0xe3,0xea,0xd1,0x4a,0x5b,0xed,0x68,0xc6,0x5c,0xf5,0x4b,0x35,0xf9,0xa,0xaa,0x17,0x7b,0x15,0x7f,0x66,0x60,0x54,0x88,0x81,0xbe,0xc,0x54,0xa8,0x51,0xc8,0xe,0x6d,0x34,0x41,0x61,0xa3,0xf6,0x32,0xeb,0xc2,0x4c,0xef,0xba,0xb0,0x7f,0xcd,0x9a,0xcc,0xca,0xa7,0xd7,0xfa,0x3,0x9b,0x6a,0x8a,0xfe,0x65,0xbc,0xc5,0x8e,0x54,0x7b,0x68,0x4c,0xe8,0x19,0x0,0x8,0xb,0x83,0x5b,0xa2,0xee,0xaf,0x3c,0xb2,0x34,0x94,0x1a,0xae,0x5e,0xca,0xb5,0x75,0xa4,0x75,0x2c,0x1e,0xb3,0xc,0x8,0x14,0x55,0x4d,0x6b,0x30,0x77,0x8c,0x0,0x0,0x81,0xe9,0xc5,0x9f,0x7d,0xc5,0xcc,0xfe,0xcc,0x7f,0xda,0x95,0x25,0x4f,0xa5,0x42,0x5,0x90,0x5,0xad,0x55,0x14,0xba,0x52,0x5d,0x61,0x5a,0xf9,0xdc,0x11,0xc9,0x8,0x6,0x1b,0x26,0x18,0xc5,0x25,0x67,0x90,0x75,0x31,0x53,0x29,0x2,0x14,0x6e,0xd2,0x2,0x1b,0x66,0x63,0xc0,0x3a,0x30,0x83,0xaf,0x3d,0xb6,0x11,0xaf,0x3d,0xb6,0x11,0xc0,0x13,0x91,0x96,0x68,0x73,0x84,0xe5,0xca,0xae,0x3,0x3e,0x74,0x40,0xb0,0xf7,0x51,0xe7,0x93,0x10,0x3a,0x9e,0x4a,0x49,0x8c,0x48,0xfe,0xd4,0xdd,0x8e,0x61,0xe8,0xc0,0x43,0xb1,0xe8,0x6b,0x22,0x92,0xcc,0xd4,0x58,0x3,0xd4,0xcc,0x9f,0x6,0x3d,0x79,0xb9,0xba,0x91,0x53,0xad,0xaa,0xb7,0x2c,0x61,0x62,0x71,0x57,0x90,0xed,0x56,0x9d,0x47,0xb5,0xa6,0xaa,0x42,0xc3,0x6a,0xde,0xb2,0xa9,0x1f,0x5e,0xa6,0xef,0xa5,0xd5,0x7f,0xba,0xe6,0x3f,0x58,0x5,0x3a,0xcc,0xf7,0xfb,0xac,0xc2,0x9a,0xd8,0x8c,0xa4,0x2d,0x40,0x82,0x48,0x5a,0xc4,0x2a,0x30,0xd5,0x63,0x55,0x46,0x7c,0xa9,0xbc,0x9d,0xb8,0xe2,0x59,0xf3,0x48,0x87,0xbb,0x16,0xf3,0xb6,0x56,0x5a,0x80,0x4a,0x79,0x13,0x6d,0xca,0xbb,0x9,0xc8,0x11,0x0,0x60,0x6,0xbf,0xeb,0x73,0xff,0x29,0xb4,0xe6,0xd,0x41,0x10,0x20,0x6e,0x14,0x50,0x9,0xef,0xca,0xff,0xcc,0x88,0xf6,0xe5,0x5c,0xd3,0x36,0x66,0xf8,0x35,0x57,0xff,0x95,0x99,0x21,0x2c,0xda,0xf3,0x93,0x57,0x7f,0x2b,0xcc,0x67,0xd6,0x5,0x43,0x9b,0x5f,0xcb,0xaf,0x7f,0xe9,0xd5,0x30,0xdb,0x9b,0x53,0xd9,0x2d,0xf9,0x60,0x68,0xf3,0x88,0x25,0x2f,0xda,0xcb,0x2b,0x76,0x98,0x37,0x3c,0x70,0xfd,0x5f,0x53,0xbb,0x1f,0x78,0x32,0x23,0x3d,0xb,0x34,0xa,0x21,0x54,0x5,0x6,0x1d,0x16,0xd0,0xdf,0x97,0x45,0x6e,0xa0,0x77,0x55,0x30,0xb8,0xf9,0xd9,0xf2,0xa6,0x2a,0xa0,0x52,0xab,0x6b,0xaa,0xec,0x58,0x51,0x69,0x57,0x46,0x84,0xd2,0xa0,0x56,0x56,0x80,0xa0,0x62,0xa4,0xcb,0xbd,0xd9,0x4a,0x5d,0x1a,0x2d,0x37,0xd5,0xe9,0x74,0x4c,0x3a,0x62,0xc2,0xc4,0x76,0xe1,0x82,0x20,0x2c,0x7b,0xd4,0xbc,0x47,0x69,0xa6,0x1a,0x66,0x96,0x5b,0x9e,0xf8,0xc3,0xf,0xfd,0x81,0xd,0x79,0x21,0x2d,0xd1,0x88,0x63,0x16,0x76,0x4c,0x3a,0xad,0x13,0x13,0x32,0xd9,0x99,0x48,0x74,0xcf,0x9c,0x1c,0x9b,0x30,0x73,0x3f,0xa7,0x65,0xe2,0xbe,0x85,0x75,0x2f,0xfe,0x79,0xc3,0x83,0x3f,0x7d,0xb8,0x9a,0xde,0xc1,0x68,0xd,0x1f,0xa8,0x81,0xcf,0x51,0x69,0x15,0x53,0x71,0x64,0x24,0x3,0xeb,0x47,0x0,0xe0,0xd5,0x9f,0x7e,0x91,0xdf,0xf5,0xd9,0xff,0x16,0x80,0xde,0xac,0x43,0x3,0xd6,0x26,0x6a,0xb,0x55,0xd7,0xd6,0xb7,0x7a,0xa9,0x39,0x1b,0xcd,0x82,0x8,0x30,0x1a,0x4c,0x2,0x24,0xe5,0xb0,0x2a,0xad,0xf,0x93,0x8c,0x62,0x92,0xee,0xc4,0xc9,0xb3,0xf7,0xde,0xd3,0xf3,0x67,0x1d,0xad,0xf7,0x3e,0x14,0x46,0xa9,0xa0,0x90,0xc9,0x6e,0x66,0xe3,0xf5,0x18,0x2f,0xb7,0x51,0xe5,0x7a,0x56,0x14,0x37,0x2f,0x5f,0xdd,0xbf,0xf4,0xde,0x95,0xac,0x43,0xa3,0xbd,0x4c,0x74,0x15,0xe5,0xf,0x46,0x79,0xa8,0xb1,0x3d,0x7e,0xd6,0x21,0x8c,0x6,0xb2,0x7d,0x7d,0x6b,0xde,0xf8,0xf9,0x97,0x16,0xbe,0x59,0x36,0x7e,0xea,0xfb,0xff,0xf5,0x95,0x44,0xe2,0x88,0x73,0xdc,0x98,0x3b,0xac,0x7e,0x79,0x94,0x40,0xbd,0x54,0x47,0x19,0x64,0xfb,0xf2,0x30,0x9a,0x8d,0x89,0xca,0x7d,0x93,0x53,0xf6,0x6e,0x4b,0xcd,0xdc,0x7f,0x96,0xd3,0x36,0x6d,0xa6,0x95,0x6c,0xdb,0xd,0x32,0x3e,0xc1,0x8e,0xc7,0x27,0xc5,0x13,0xc9,0x36,0x10,0x23,0x16,0x73,0xe0,0x7b,0x21,0xf2,0xeb,0xe8,0x9e,0x2a,0x1f,0x7d,0x78,0x6c,0x29,0x7a,0xcf,0x6c,0xc0,0x5a,0x95,0x9a,0x4b,0x94,0x62,0xc6,0x1a,0xcf,0xb0,0x36,0x4b,0x46,0x44,0x82,0x99,0xd7,0x8c,0x42,0x4,0x19,0xb0,0x31,0x5,0x1a,0x61,0xdf,0xa9,0x21,0x85,0x25,0x6d,0x4b,0x64,0x6,0x73,0x88,0xc5,0x43,0x4e,0xb7,0x26,0xa1,0x43,0x43,0xc2,0x76,0x4a,0x28,0x69,0xd0,0x27,0xcf,0x68,0x93,0x48,0xba,0x2a,0x16,0x77,0xad,0x12,0x3e,0x1d,0x9a,0xd4,0x31,0x3d,0x8,0xc2,0xe9,0x61,0xa0,0x91,0xcb,0xcd,0xe2,0xd6,0x3d,0xe,0xc1,0xc4,0x77,0x7f,0x74,0xe8,0x8d,0xdf,0x5e,0xf4,0x45,0x36,0x8a,0xb5,0x97,0xb,0x2b,0xb9,0xe2,0x31,0x66,0x7e,0x65,0x80,0xc0,0xd0,0x7e,0x6e,0x6d,0xe4,0x3c,0x59,0xc4,0x46,0x31,0x9,0x49,0x8d,0xbd,0xf7,0x66,0x1d,0xc7,0xf2,0x4e,0xdf,0xd1,0xcc,0xf5,0x6,0xd6,0x6f,0x1a,0x9e,0x6e,0xe5,0x98,0x74,0x24,0x8,0x22,0x2d,0x50,0xf2,0x89,0xaa,0x42,0xb7,0xdd,0x3e,0x74,0xc1,0xc9,0xc9,0x69,0xfb,0x7e,0x8a,0x99,0x39,0x91,0x88,0x91,0x10,0x84,0x74,0x6b,0x12,0x5c,0x69,0x7,0x43,0x20,0x82,0x31,0xda,0xd0,0x88,0x26,0x90,0xe5,0x56,0xb2,0x5a,0xc3,0xa8,0x0,0x30,0x6,0xa1,0x52,0x5c,0xcc,0x7b,0xe4,0x7b,0x81,0x35,0xb2,0x34,0x6e,0x18,0x30,0xc4,0x1a,0xd9,0x4c,0x1e,0x52,0xd2,0xfa,0x86,0x0,0x60,0x66,0x76,0x1c,0x6b,0x63,0x2e,0x5b,0xe8,0x69,0xeb,0x6a,0x99,0x60,0x94,0x22,0x21,0xe5,0xb0,0x36,0xac,0x1a,0x4,0x0,0x18,0x7c,0xe9,0x81,0xc7,0xac,0x58,0xaa,0x23,0x9f,0xec,0x9a,0x33,0xd8,0x9f,0x98,0x1e,0x4b,0xc4,0x63,0xc9,0x74,0xc,0x44,0x82,0xdd,0xb8,0xc3,0xcc,0x5c,0x3,0x85,0xf2,0x1e,0x36,0xd2,0x8d,0x83,0x28,0x22,0x8e,0x8c,0x56,0x88,0xd9,0x36,0x62,0x9,0x46,0xba,0x3d,0xc5,0xc6,0x18,0xda,0xb2,0x61,0x4b,0xd,0xa9,0x32,0xbc,0x25,0x6a,0x59,0x20,0xd4,0xd8,0x7,0x20,0xd4,0x2c,0xaf,0x2e,0x1f,0xed,0x73,0xde,0x37,0x53,0x2b,0x66,0x22,0x22,0x46,0xa9,0xef,0xbd,0x31,0xa3,0x70,0xbf,0x14,0x39,0x78,0x95,0xfd,0x2f,0xa2,0xff,0xfc,0xbe,0x35,0x7d,0x85,0xd,0xaf,0xc,0x96,0x5,0x4b,0xe5,0xce,0x9d,0x95,0x7,0x1c,0x4d,0x13,0x70,0x1d,0x2f,0xf,0x28,0xa5,0xb8,0x73,0x62,0x2b,0x5c,0xc7,0x31,0x24,0x84,0x60,0x66,0x82,0xb0,0xa2,0xa2,0x59,0x61,0x45,0x49,0x24,0xbf,0x8,0xc3,0x6,0x54,0x4f,0x2e,0x11,0xb3,0x56,0xca,0x78,0x5,0x4f,0xfa,0x7e,0x0,0xaf,0x18,0xc0,0xcb,0x66,0x36,0x71,0x90,0x5f,0x5d,0x58,0xbb,0xec,0xde,0x30,0xd7,0xe7,0x57,0x9a,0x72,0x72,0x69,0x4f,0xd1,0xd2,0x10,0x69,0x6d,0xc,0xb3,0x11,0x4,0x79,0x77,0x43,0x0,0x90,0x20,0x7e,0xf9,0xa7,0xe7,0xac,0x9d,0xfd,0xa9,0xef,0xbf,0xe1,0x15,0xbc,0xae,0x64,0xda,0xa2,0x88,0x27,0x22,0x80,0x99,0x98,0x1,0x99,0x68,0x8b,0x16,0xe4,0xb,0x49,0x85,0xf5,0x2f,0xf7,0xaf,0x59,0xff,0xf2,0x2f,0x1,0x20,0xbd,0xc7,0xc1,0x53,0x92,0x53,0xe7,0xec,0x99,0xeb,0xde,0xf3,0x68,0xb7,0xa5,0x63,0x9e,0xed,0xd8,0x24,0x88,0x46,0xf6,0x58,0x28,0x2f,0xdb,0x12,0x91,0x30,0x85,0xb4,0xaa,0xda,0xcb,0x42,0xa8,0x7c,0x6,0xcc,0x64,0x8d,0x9c,0xa5,0xf5,0xc2,0x1f,0xc9,0xf0,0x8d,0x20,0xdd,0x58,0x43,0x48,0x8b,0xba,0xf,0x3f,0xfd,0xaa,0x44,0xdc,0x35,0x6,0x64,0x45,0xb3,0xb1,0x26,0x54,0xe7,0x3a,0xf,0x36,0xe2,0x97,0xb8,0xf4,0x92,0x98,0xc3,0xd0,0xc8,0xcc,0xaa,0x17,0x7e,0xb3,0xfa,0xb6,0x6f,0xdd,0x58,0x26,0xb9,0x2a,0x33,0xbb,0x5e,0xd7,0x8d,0x20,0xe7,0x47,0x2a,0x43,0x22,0x21,0xb5,0xd2,0x80,0x2b,0xa5,0xb0,0xdd,0xba,0x66,0x57,0x5c,0xd1,0x64,0x84,0xda,0x9a,0x7e,0x2,0xd8,0x2b,0x78,0xe4,0x17,0x7c,0xe9,0x7b,0x85,0x41,0xaf,0x77,0xed,0x7d,0xf9,0x75,0x4b,0x9f,0x2a,0x6e,0x78,0x79,0x83,0xd7,0xbb,0x26,0x57,0x56,0x3b,0xc2,0x72,0xa5,0x9,0xa,0x8a,0x41,0x5d,0x60,0xd6,0x0,0x4,0x6b,0x8d,0x62,0x3e,0x2f,0xb4,0x32,0xea,0xb5,0x9f,0x9d,0xf3,0xe2,0x5e,0x67,0xfc,0xa7,0xf5,0xda,0xcf,0xbe,0xac,0x6a,0x0,0xf0,0xea,0xf5,0x5f,0x62,0x0,0x10,0x82,0x9e,0x2a,0x64,0x8b,0x87,0x25,0xd3,0xc9,0x61,0xef,0x81,0x48,0xba,0x8e,0x6d,0x5a,0x66,0x1d,0x72,0xda,0xc0,0xd2,0x7b,0x5f,0xd1,0x61,0x51,0x57,0x7b,0x3c,0xd9,0x95,0x4b,0x36,0x66,0x57,0x2e,0xd9,0x8,0xa3,0x1f,0x22,0xcb,0x11,0x93,0x8f,0x3c,0xe3,0x18,0xa7,0xa5,0x7b,0x4f,0x92,0xb6,0x60,0xe3,0xeb,0x91,0x29,0x32,0xd4,0x9,0x32,0xa2,0x3a,0xcb,0x2e,0xe4,0xf0,0x6c,0xaa,0xa3,0x4b,0x1b,0xa,0x1f,0xd,0xe2,0x90,0x2a,0x7,0x9a,0x21,0x3a,0x27,0xb4,0x41,0x73,0xed,0x2,0x22,0x6a,0x2a,0xd7,0xc3,0x9c,0xcf,0x16,0x68,0x90,0x9b,0xc9,0x8,0x95,0xa4,0x4f,0x18,0x69,0x6,0x1b,0x74,0xda,0x21,0x1a,0x85,0x12,0x1e,0x65,0x4b,0x25,0x36,0x61,0x90,0xdf,0xb2,0xe6,0xae,0xfe,0x17,0xee,0xb9,0x3d,0xbb,0xfc,0xc9,0x8d,0xe5,0xe8,0x81,0x8d,0xaa,0x51,0x97,0x26,0x28,0xa8,0x49,0x87,0x7f,0xe2,0x30,0x1,0x9a,0xe0,0x38,0xb6,0x1,0x8,0x6c,0x42,0x84,0xa1,0x16,0x6c,0xcc,0xef,0xeb,0xf9,0xe7,0x11,0xc,0x8b,0x65,0xdb,0xd7,0x7,0xbe,0xff,0x15,0x1d,0x86,0xa1,0x25,0x1d,0x1b,0x44,0x20,0x21,0xa9,0xa5,0x2d,0x41,0x5a,0x77,0xce,0x9b,0xf5,0xcf,0x57,0xfe,0xc2,0x1f,0xea,0x79,0xdc,0xef,0x5b,0xfb,0x6c,0x7e,0xf5,0xb3,0x2f,0xe7,0xd6,0x2e,0xeb,0x5,0x6b,0x90,0xb4,0x85,0x70,0xe3,0x16,0xb3,0xe1,0xd,0xf7,0xfd,0xe8,0x5e,0x0,0xf7,0x8e,0x70,0x88,0x1a,0xc5,0xe3,0x55,0x3b,0x88,0x52,0xc3,0x92,0xb3,0xf2,0xa,0x5d,0x1d,0x99,0x8e,0x32,0x4d,0x5c,0x53,0x11,0xca,0x95,0xdf,0xa8,0xd5,0xea,0xc3,0x6d,0x58,0xed,0x58,0x92,0xc6,0xb7,0x86,0x8c,0xa0,0xfd,0x62,0xd9,0xfe,0x37,0x4e,0x5b,0x46,0x21,0x44,0xf5,0xce,0xca,0xc3,0xee,0x81,0x89,0x1a,0x60,0x31,0xd7,0x55,0xf4,0x96,0x16,0xb0,0x72,0x23,0x8e,0x82,0x47,0xa4,0x74,0x2b,0xb,0xe,0x37,0x3f,0xfa,0xab,0xc7,0x0,0x3c,0x16,0xf1,0x4,0x69,0x1b,0x86,0xa1,0xfd,0x5c,0x58,0x26,0x8b,0x3a,0xe7,0x1d,0xb7,0x8f,0xdb,0x3d,0x7b,0xae,0xd3,0x36,0xe9,0xdd,0xb1,0x54,0xeb,0xcc,0x64,0x2a,0xe,0xcb,0xb6,0x25,0x48,0x40,0x7b,0x1e,0x8a,0x79,0xf,0x0,0x7e,0xbc,0xe7,0xa7,0xaf,0x93,0x4,0x32,0xd,0x1,0xb0,0xe7,0xa7,0xaf,0x15,0x2f,0xfd,0xe4,0x8b,0x2f,0xec,0xf9,0xe9,0xeb,0x9e,0x1c,0x1a,0x18,0x7a,0x77,0xc7,0x44,0x7,0x64,0x45,0xc9,0x1e,0x27,0x96,0x44,0xc7,0x4,0x82,0xe,0x93,0x31,0xbf,0xbd,0xe5,0x28,0x6f,0xc2,0xb4,0xa3,0x52,0xd3,0xe7,0x65,0x27,0xb2,0xda,0xe2,0x6f,0x59,0xf1,0x97,0xf5,0xf7,0xfd,0xf8,0x6e,0xad,0x43,0x3,0x21,0x49,0x38,0x9,0x8b,0x75,0x68,0x58,0xf,0xc7,0xbb,0xa5,0x18,0x76,0xcc,0xfc,0x4f,0xa4,0xc,0xeb,0x6,0xdb,0x68,0x23,0x25,0x31,0x87,0x3e,0xeb,0x20,0x88,0x2a,0xbb,0x84,0x4,0x64,0x44,0x13,0x4b,0x27,0x56,0x65,0x61,0x18,0x80,0xae,0xbb,0x66,0x79,0xc0,0x4d,0xa5,0xf5,0xed,0x78,0x38,0xbd,0xda,0x8d,0x1b,0x50,0x9,0xad,0x98,0x23,0x7a,0x85,0x85,0x0,0x54,0x8,0xa3,0x75,0xe4,0xd7,0x84,0x1,0x88,0xc0,0xc6,0x18,0x58,0x92,0xc,0x8,0xb2,0xca,0x75,0xaa,0x7d,0xc6,0x1a,0x0,0x95,0x41,0x80,0x4a,0x24,0x5a,0x65,0x1a,0x18,0x44,0x44,0xd2,0x11,0x44,0x4,0x5d,0xcc,0x86,0x0,0xd0,0x31,0xf7,0x1f,0xf7,0x68,0xd9,0xeb,0xd0,0x13,0x65,0xac,0x75,0x8e,0xb0,0xec,0x96,0x78,0x3c,0x6e,0xa5,0x5a,0x92,0x0,0xc,0x9c,0x78,0x2,0xc2,0x8e,0x41,0x7,0x1e,0x82,0x30,0x44,0x10,0xf8,0xcb,0xa4,0x2b,0x1e,0x67,0x4d,0xf4,0xea,0xcf,0xbe,0xdc,0x58,0x3,0xbc,0xfe,0x8b,0xf3,0xcc,0x3e,0x9f,0xba,0x5a,0x86,0x24,0xbe,0xe8,0x79,0xfe,0x92,0xa0,0x90,0x63,0x27,0xed,0x44,0xc1,0xb2,0x65,0xc1,0xb1,0xdb,0x60,0x94,0x82,0xa3,0x2,0xa4,0xd2,0x49,0x56,0x61,0x6b,0xc2,0x2b,0x14,0x66,0xe4,0x13,0xa9,0xb3,0xdf,0xf5,0xd9,0xef,0x9f,0x1d,0xe4,0x32,0x2f,0xe6,0xd6,0xbc,0xf0,0xa7,0xdc,0x9a,0x67,0x5f,0xf3,0xb6,0xac,0xc9,0x94,0xaf,0x1b,0x6b,0x9f,0x9a,0x20,0xc0,0x2,0x78,0xf4,0x40,0x9e,0x69,0x78,0x79,0x77,0x4d,0x8e,0x5b,0xe5,0x7b,0xd6,0xf7,0x14,0xd,0xc3,0x6a,0x6d,0x6f,0x75,0x8c,0x31,0xb0,0x1d,0x1b,0x6e,0xa9,0x3b,0x98,0x28,0xe6,0x2b,0x3b,0x24,0x9,0x21,0x1b,0xf8,0x4d,0x63,0x6f,0x4d,0x35,0x66,0x1e,0xa0,0xc1,0x22,0x8b,0xca,0x36,0xed,0x5a,0xa3,0x38,0xd4,0xcb,0x20,0x22,0xd6,0x51,0x19,0x17,0x1,0xc8,0x65,0xb,0x10,0x82,0xa8,0x58,0xf4,0x74,0x50,0xf4,0x7c,0x61,0x59,0xc6,0xa8,0xd0,0x54,0xc8,0x81,0xb2,0xdb,0x51,0xad,0xf2,0xeb,0x83,0x2e,0x36,0xc4,0x86,0x49,0xb8,0xc9,0xd6,0x6a,0xf5,0x6e,0xa7,0x3b,0x63,0xf1,0xae,0xdd,0x3b,0x5b,0xf6,0x3c,0xec,0x98,0xd8,0x84,0xdd,0x3e,0x20,0x85,0xb0,0x1d,0xc7,0x66,0x37,0xee,0x98,0x54,0x3a,0x25,0xc,0x1b,0x8,0x2b,0x56,0xe2,0x27,0x4a,0x48,0x32,0x21,0xf,0xd,0xc,0x91,0x10,0xe2,0xd7,0xaf,0xfd,0xf4,0xab,0xc5,0xad,0xd6,0x3,0x28,0x92,0xf4,0xc6,0xcf,0xbf,0xf2,0xec,0x1e,0xa7,0x5f,0xf3,0x48,0x2e,0x57,0x3c,0xa2,0xd5,0xca,0x42,0xa4,0x5a,0x2b,0x9d,0xc2,0x84,0x94,0x80,0x95,0x0,0xc0,0x24,0x1c,0x25,0xdd,0x44,0x12,0xa9,0x16,0x8f,0x3,0xcf,0x33,0x5e,0x32,0xbe,0x5f,0xaa,0xf3,0x98,0xfd,0xf2,0x7b,0x1d,0x1a,0x9a,0x62,0xee,0xe5,0x60,0x68,0xf3,0xb,0x41,0xa6,0x67,0x5d,0x72,0xea,0x3e,0x47,0x39,0xf1,0x58,0x8a,0x41,0x4c,0x42,0x56,0x86,0xb7,0xe6,0xa1,0xa9,0x8a,0x59,0x14,0x52,0x18,0x15,0xad,0x85,0x5a,0x71,0xe3,0xa2,0xef,0x25,0x27,0xef,0xd5,0x1e,0xeb,0x9c,0xd6,0x31,0x18,0xef,0x68,0xb7,0x13,0xad,0x1d,0x32,0xd1,0x36,0x41,0xc4,0x92,0x13,0xa5,0x1d,0xef,0x4e,0xb4,0xb6,0x4e,0x24,0x69,0xb5,0xb0,0x61,0x72,0x5c,0x7,0x96,0x65,0x39,0xb5,0x1a,0xa0,0xbc,0xa6,0x61,0x1b,0xf2,0x1,0xd,0x7a,0xe,0x9,0x29,0x44,0x18,0x84,0xd4,0xd7,0x3b,0x18,0x15,0xb1,0x82,0x95,0x9f,0xcf,0xf5,0x4,0xc5,0x7c,0x8f,0xf1,0xf3,0x3d,0xc6,0xcf,0x6d,0x56,0x85,0xc1,0x3e,0x95,0x1f,0xec,0x57,0xd9,0x9e,0x81,0xfc,0xa6,0x37,0xfa,0xc3,0xa1,0xcd,0x45,0x8,0x49,0x60,0x5d,0xcb,0x2b,0x73,0x35,0xd4,0xb8,0x2a,0x1,0x25,0xc9,0x8d,0xd9,0x70,0x27,0xec,0x7e,0x62,0xf7,0x91,0x9f,0xcd,0xb2,0x56,0x61,0xac,0x73,0xda,0x5c,0x11,0x6f,0xdd,0x37,0x96,0x4c,0x4d,0x8a,0xb9,0xe,0x1c,0xd7,0x66,0xdb,0xb1,0xb5,0x1b,0x4f,0x8,0x90,0x90,0x10,0x12,0x96,0x94,0x35,0xe5,0x60,0x46,0x5,0xc8,0x67,0x73,0xbe,0xa,0x95,0x69,0x4b,0xb6,0x7e,0xbf,0xa9,0x82,0x90,0xd7,0x7f,0x71,0x9e,0x2,0x0,0x29,0xad,0xb,0xb3,0x43,0xb9,0xfb,0x13,0x9,0xd7,0x15,0xbe,0x27,0xa4,0xe3,0xa2,0x9e,0x18,0x17,0xd2,0x2,0x40,0xb0,0xa4,0x4d,0x56,0xb2,0x45,0xc6,0xfc,0x2,0x54,0x10,0xa2,0xb5,0xb5,0xc5,0xf6,0x3c,0x6f,0x9e,0x9e,0x34,0x65,0x1e,0x97,0xd4,0x57,0x2a,0x95,0x80,0xb0,0x9c,0xe1,0x5d,0x8f,0x1b,0x51,0xb9,0xd1,0xe3,0x5b,0xcc,0x86,0x49,0x58,0x4,0x22,0x62,0x15,0x98,0xdc,0xda,0x65,0x7d,0xb9,0xb5,0xcb,0xfa,0x6a,0xe6,0xa7,0x90,0x44,0x96,0x23,0x48,0xd8,0x82,0x2c,0x4b,0xc6,0x27,0xce,0x6e,0x4f,0x4e,0xdb,0x67,0x16,0x98,0xd5,0xf0,0x6c,0x8b,0x5c,0x85,0x7a,0x67,0xbf,0x69,0xf9,0xd3,0xc8,0xb0,0xd2,0xef,0x5b,0xb3,0xb9,0xff,0xf5,0xa7,0xfe,0xa7,0xd8,0xb3,0x72,0x55,0x7e,0xcd,0xb,0xeb,0x95,0x97,0xd,0x60,0x34,0x9b,0xd0,0xd7,0xd5,0x26,0xaf,0x9e,0x1a,0x24,0x12,0x4,0x29,0x8,0x42,0x3a,0x5c,0x36,0xef,0x65,0xbb,0x4f,0xb5,0xb5,0x3,0x32,0x16,0x87,0xed,0x17,0xd0,0x39,0xa1,0xab,0xad,0xad,0xe3,0xf0,0x73,0x4a,0x34,0xa,0xa5,0xd2,0x49,0x80,0x18,0x96,0xe3,0x42,0x3a,0x2e,0x91,0xb4,0x64,0x43,0x67,0x19,0x80,0x31,0x1a,0x7e,0x3e,0x6b,0x32,0x83,0xb9,0x98,0x90,0xf2,0x94,0xa7,0xff,0xe7,0xac,0xc2,0xb8,0x4a,0xc2,0x5e,0xfb,0xc5,0x57,0x1f,0xdf,0xeb,0xd3,0xd7,0xfd,0xef,0x96,0xcd,0x7d,0xe7,0x4c,0x9e,0x2a,0xb5,0xb4,0x1d,0x9,0x1a,0x25,0xe7,0x5e,0x7a,0x10,0x19,0x4b,0xc1,0x8a,0xb,0xe8,0xc0,0x83,0x1d,0x77,0x23,0x22,0x34,0x62,0xa0,0x88,0x2c,0x27,0xb2,0xd7,0xe5,0x72,0xe7,0x6,0x12,0x11,0x42,0x30,0x49,0x8a,0x4d,0x3a,0xe2,0xd3,0x27,0xc,0x2d,0xbb,0xf7,0x1,0x3f,0xb3,0xb9,0x10,0x66,0xfb,0xbc,0x86,0x37,0x28,0x2c,0x11,0xe5,0x26,0xc,0x60,0x8c,0xc9,0xaf,0x5b,0xda,0x93,0x5d,0xf1,0xe4,0xc6,0x7a,0xe3,0x42,0x6c,0x40,0xd8,0x9e,0xfa,0xfa,0x5a,0x27,0x30,0xbb,0xe2,0x99,0xcd,0xd9,0x15,0xcf,0xdc,0xd,0x21,0x49,0xba,0xc9,0xe1,0x30,0xda,0xb2,0x45,0x94,0xdf,0x18,0x59,0x54,0x28,0x2c,0x57,0xb8,0x1d,0x53,0x53,0x4e,0xc7,0xb4,0x8e,0xc4,0xc4,0xdd,0x3f,0x28,0x28,0x6a,0x7c,0x5,0xe6,0xc6,0x31,0x24,0x33,0x9c,0x74,0x2b,0xa4,0xed,0x80,0xcb,0xe,0x4,0x33,0xc8,0x72,0x20,0x2c,0x7b,0xf8,0xbb,0x6c,0x1a,0x42,0x9b,0x99,0x61,0xbc,0x3c,0x32,0x99,0x9c,0xd0,0x2a,0xbc,0xe5,0x8d,0x5f,0x2f,0xbc,0x79,0xaf,0x4f,0x5d,0x6b,0xbd,0xf6,0xcb,0xf3,0x54,0xd3,0x0,0x98,0x7d,0xfa,0xd5,0xf2,0xb5,0x5f,0x9c,0xfb,0xa5,0x3d,0x3e,0x71,0xf5,0x87,0x32,0x43,0xd9,0x19,0x2d,0x42,0xc0,0x49,0xb4,0xd5,0xe5,0x50,0x78,0x4,0x8,0x98,0x35,0x84,0xe5,0x2,0xc2,0x2d,0xbb,0x79,0x11,0x27,0xc0,0x63,0xd5,0x59,0x44,0xdf,0x75,0x92,0x2d,0x94,0x2c,0x14,0x58,0xcc,0x5e,0x70,0xba,0x33,0x61,0xe6,0x87,0x60,0x54,0x16,0xac,0xb3,0x8,0xc3,0xa1,0xb0,0x98,0xd9,0x60,0xbc,0xa1,0x8d,0x7e,0xff,0xba,0x75,0xc5,0x75,0x2f,0x6e,0x28,0x6c,0x59,0x9d,0x63,0xe5,0x8f,0xe0,0xd4,0xc9,0x72,0x4,0x1b,0xc5,0xac,0x35,0xb,0x37,0x21,0xca,0x89,0xa9,0x6d,0xe8,0xb9,0x3c,0x9c,0x68,0x29,0xe5,0xa3,0x85,0x1d,0x97,0x24,0xa5,0x0,0x9,0xe8,0x62,0x56,0xd5,0x24,0x6d,0x2a,0x99,0xcd,0x43,0xa7,0xc4,0x26,0xcc,0x9c,0x6a,0xa5,0x27,0x4c,0x16,0xb1,0xd6,0x69,0xd2,0x8d,0x4f,0x60,0xc8,0x14,0x49,0x99,0x74,0x93,0xa9,0x49,0x89,0x84,0xb,0x37,0x1e,0x83,0x70,0xdc,0x31,0xeb,0xf5,0x88,0x4,0xac,0x78,0x6a,0x98,0xca,0xa9,0x6e,0x7d,0x53,0x2e,0x2a,0x25,0x6a,0x50,0xc4,0xc4,0x50,0xc5,0xc,0xb2,0x3,0x43,0xca,0xcb,0x17,0xf3,0x42,0x5a,0x5f,0xdc,0xf3,0x93,0xdf,0x91,0x8d,0x84,0x3f,0x26,0x0,0xde,0xf8,0xf5,0x42,0x3d,0xfb,0xf4,0x6f,0xdb,0x82,0x70,0x48,0x76,0x28,0xbb,0xdc,0xb2,0xad,0x24,0x1b,0x86,0x9b,0x6a,0xc7,0x70,0x38,0x45,0xa3,0xc7,0xc3,0x3c,0x4a,0x8e,0x7c,0xac,0xf1,0x96,0x16,0x52,0x6d,0x1d,0x14,0x4b,0x16,0xd1,0xd9,0xd5,0xda,0xa,0xa0,0xd5,0x2b,0xfa,0x8,0x82,0x10,0x52,0xa,0x1a,0x1a,0xca,0xa3,0x65,0xd6,0x81,0xc0,0x41,0x27,0x45,0xfe,0x8d,0xa,0x7a,0x74,0x31,0xb7,0x32,0xc8,0xf5,0xaf,0xa,0x6,0x37,0xac,0x2a,0xac,0x7f,0x79,0x55,0x7e,0xdd,0x4b,0xbd,0xa5,0x1c,0x82,0x9e,0x78,0xf8,0xe7,0x8f,0x23,0x40,0x6e,0x47,0x1d,0x26,0x31,0x1b,0xe3,0xb6,0x4d,0x39,0x54,0xba,0xc9,0x1b,0x8d,0xa,0xc,0xb3,0x31,0x76,0xb2,0xcd,0x6d,0xdb,0xfb,0x88,0x3d,0xdc,0xae,0x19,0xbb,0x3b,0xa9,0xce,0x99,0x32,0xd5,0xb6,0xbb,0x74,0x12,0x33,0x89,0xc8,0x2a,0x13,0x8c,0x6e,0xcc,0x81,0x1b,0x73,0xd8,0x28,0x8d,0xd6,0xf6,0x34,0xb4,0xe6,0x88,0x61,0x22,0x1,0x3b,0xd9,0x5a,0xa5,0xfa,0x9,0xa3,0xaf,0xe6,0xe5,0xba,0xb5,0x19,0x3c,0x46,0x99,0x60,0xa9,0x31,0x64,0xe8,0x23,0x33,0x30,0xa0,0x73,0x99,0x82,0x45,0x82,0x3e,0xf6,0xda,0x2f,0xce,0xdd,0xbc,0xdd,0xb5,0x97,0x7b,0x9d,0x71,0xdd,0x11,0xca,0xf3,0x1f,0x6e,0xeb,0x68,0xf5,0x93,0xad,0x2d,0xae,0x93,0x6a,0x6b,0x48,0xc5,0xd6,0xec,0x3d,0xde,0x68,0xf,0xa0,0x31,0xf6,0xa,0xa2,0xea,0xd7,0x24,0xa0,0xc2,0x68,0x83,0x2a,0x36,0x3a,0x4a,0x38,0x1,0xc,0xa3,0x18,0x20,0x16,0x44,0x3c,0x34,0x38,0xc8,0x42,0x4a,0x4b,0x85,0x9a,0xc2,0x20,0xda,0x26,0x2d,0xf0,0xc3,0x88,0x15,0xd4,0x3a,0xc3,0xac,0xa,0x24,0x9c,0x49,0x6d,0x1d,0x29,0x24,0x5b,0xdb,0x61,0xc5,0x53,0xe3,0xaf,0xbb,0x37,0xc,0x95,0xef,0xc7,0xc6,0xf5,0xbd,0x0,0x9b,0xa2,0xd1,0xc1,0xa0,0xb0,0x63,0x93,0x98,0x99,0x2c,0x29,0x2a,0xbd,0x89,0xe2,0x89,0x18,0x8c,0xd1,0xca,0x71,0x1d,0xb8,0xae,0x5b,0x6e,0xd4,0x2e,0x28,0xea,0x85,0x44,0xc2,0x72,0xc1,0x6c,0x22,0xe6,0x53,0x5a,0xd1,0x33,0xd5,0xf3,0xfb,0xc,0x8c,0x6c,0xee,0xd7,0x78,0x93,0xc9,0x11,0xf5,0x83,0xa5,0xa1,0x54,0xc5,0x3c,0xfc,0xdc,0x10,0xf7,0xf5,0xe,0x11,0x1b,0xf3,0xf9,0x37,0x7e,0xbd,0xf0,0x27,0xb3,0x3f,0x79,0xb5,0x7c,0xe3,0x57,0xb,0xf5,0x76,0x1,0x0,0x0,0xf6,0xf8,0xc4,0xb7,0x4f,0x63,0x63,0xfe,0xd8,0xd6,0xd1,0x12,0x26,0x52,0x29,0xdb,0x6d,0xed,0x40,0xcd,0xd2,0xc2,0x6d,0x6,0xc0,0x70,0x8d,0x5f,0x8d,0x6f,0x21,0x86,0x1d,0xa5,0xf2,0x5e,0xb8,0xa6,0x7a,0xbf,0x42,0xd6,0xc3,0x59,0x40,0x15,0x32,0x9,0x9,0x2f,0x3b,0x8,0x22,0xaa,0x9,0x5,0x9d,0x64,0xa,0x76,0xa2,0x65,0x1b,0xe7,0x3f,0x41,0xfb,0x1e,0xc2,0xfc,0x10,0xb4,0xa,0x87,0x93,0x3c,0x60,0xb6,0x9d,0x58,0xc9,0x46,0x33,0xa4,0x9b,0x8c,0x7c,0x91,0x52,0xe1,0x66,0xc4,0x77,0x88,0xe1,0x4e,0xd4,0x86,0x47,0x72,0xb,0x3b,0xa,0x0,0x86,0x1,0x21,0xa0,0xa,0x59,0x4,0x85,0xac,0xd9,0xb2,0xa9,0x4f,0x18,0xe6,0xc5,0x2b,0x7e,0x7b,0xd1,0xe5,0x7b,0x9c,0x7e,0xb5,0x5c,0xfe,0xeb,0x85,0x7a,0xbb,0x35,0xc0,0xac,0x8f,0x5f,0x2d,0x6c,0x77,0x3,0xab,0x60,0xf2,0x49,0x2a,0xc,0x6f,0xe9,0x9a,0xd0,0x8e,0x78,0x3a,0x9,0x37,0xdd,0x9,0x90,0x44,0x55,0xf6,0x4,0xf5,0x9b,0x41,0x8e,0x5f,0x3,0xa0,0x31,0xe7,0xdf,0x90,0xf2,0x2d,0xbf,0x17,0xa5,0x7b,0x10,0xa3,0x74,0x33,0xd9,0xce,0x9a,0xef,0xca,0xb6,0x76,0xf5,0x14,0x75,0xb9,0xd3,0xa7,0xa9,0xe2,0x33,0x78,0xe4,0x6e,0x88,0x5c,0x5f,0x5d,0xc4,0x3b,0x54,0x3,0x84,0xf9,0x21,0x78,0xf9,0x2c,0x7a,0x7b,0xfa,0x20,0x84,0xf8,0xf6,0x1b,0xbf,0xbe,0xe8,0xa2,0x66,0x1f,0xad,0x29,0x6a,0x6c,0xc5,0x6f,0x17,0x1a,0x15,0x4c,0xa1,0xe5,0xbf,0xb9,0xf0,0x56,0x37,0xe6,0x1c,0xd7,0xd7,0xdb,0x8f,0xcc,0xc0,0xa0,0xa,0x32,0xbd,0xac,0x82,0x22,0xca,0xb1,0xfd,0x36,0x14,0xce,0xd5,0x51,0x9f,0x63,0x8,0x6e,0xcc,0x35,0xea,0xe5,0x95,0x37,0x3a,0x72,0xac,0xb8,0xea,0xdf,0x8e,0x28,0xf8,0x67,0x1e,0xe5,0xda,0x8d,0xaa,0x8d,0x68,0xdc,0xcf,0x3e,0xfe,0x13,0x4b,0xf6,0x5e,0x6b,0x84,0xb9,0x7e,0x64,0xfa,0x7a,0x75,0x6f,0x4f,0x2f,0x0,0xfa,0xd7,0x37,0x7e,0x7d,0xd1,0x45,0x7b,0xfc,0xf3,0x55,0x4d,0x17,0x51,0x6e,0x53,0xa5,0xe4,0xac,0x8f,0x5f,0xb5,0xf,0x1b,0xbd,0xc4,0xb2,0xad,0xd8,0xc4,0x49,0x13,0x21,0xec,0x18,0x9c,0x74,0x7b,0x15,0xd9,0x32,0xca,0x9e,0xc0,0x34,0xcc,0xfa,0x8c,0xfc,0xac,0x4e,0x33,0xd4,0x6b,0x91,0xad,0xe,0x2e,0x6d,0xe7,0x53,0x8d,0x57,0x1e,0x63,0xb4,0x90,0xaf,0x3f,0x87,0x6b,0x5f,0x33,0x1a,0xed,0x15,0x58,0xaf,0x1,0xb8,0x4e,0x19,0x70,0x4d,0xd9,0x99,0xe,0x7d,0x14,0x7,0x36,0x23,0x97,0xcd,0xfb,0xf9,0x6c,0xc1,0x15,0x24,0x4e,0x5e,0xfe,0xbb,0xaf,0xdd,0x32,0xeb,0x9f,0xae,0xa4,0x15,0xbf,0xbf,0x98,0x77,0xa8,0x6,0x18,0x71,0xe8,0xe0,0x35,0x99,0x6a,0xed,0xd4,0x61,0xf8,0xf0,0xfa,0xd5,0xeb,0x50,0xcc,0xf4,0x73,0x30,0xb4,0xa5,0x94,0x23,0xdf,0x6,0xb6,0x6d,0xb4,0x85,0x14,0xdb,0x3e,0x7d,0xde,0xe4,0x83,0xb7,0xf9,0x54,0x1e,0x75,0xcd,0x5e,0x13,0xd7,0x2c,0x85,0xda,0x61,0xb6,0x1f,0xb9,0x2d,0xeb,0xcd,0x60,0xff,0x10,0x72,0x83,0xd9,0x75,0x64,0xc5,0x66,0x2e,0xff,0xdd,0xd7,0x6e,0x99,0xf5,0xf1,0xab,0xc6,0x25,0xfc,0x1d,0x32,0x57,0x66,0x7e,0xec,0xdf,0x17,0x82,0xcd,0xb7,0x93,0x2d,0x49,0x9d,0x6a,0x49,0xb,0x37,0xd9,0x4a,0x4e,0xaa,0x3,0xcc,0x6a,0xe4,0x6c,0xaf,0xf6,0x3,0xaa,0x1c,0xc0,0x51,0xb5,0x40,0x1d,0xe1,0xd4,0x38,0xfd,0xfb,0x16,0x69,0x2,0xde,0x11,0xb3,0xbf,0xee,0xef,0x35,0x3b,0x88,0x8e,0xa5,0x1,0x22,0xcd,0x1a,0x16,0x32,0x30,0x41,0x9e,0x33,0x7d,0x3,0x7e,0x36,0x9b,0x8b,0x69,0xc3,0x3f,0x9c,0x3d,0x67,0xfe,0x97,0xee,0xbb,0xfc,0xc3,0x66,0xd6,0xc7,0xff,0x43,0xac,0xf8,0xed,0x45,0xe3,0xee,0x97,0x27,0xb7,0x77,0x5c,0x6,0x5f,0xbc,0xff,0x6f,0xed,0x73,0x8f,0xf9,0x7d,0xe8,0xf9,0xa7,0x16,0x73,0x85,0x94,0x2d,0x41,0xaa,0x30,0x4,0x3b,0x91,0xae,0xaa,0xbf,0x1c,0x9,0x80,0x51,0x81,0x51,0x9f,0x22,0xac,0x7,0xc1,0x78,0x0,0xb0,0xa3,0x80,0x30,0x6e,0xe1,0x6f,0xed,0x32,0xdc,0xa0,0xfb,0x3a,0x8f,0xe9,0x83,0x78,0xfd,0x1b,0x51,0xcc,0xc,0xa0,0x7f,0x73,0xf,0x79,0x5,0xcf,0x83,0x94,0xc7,0xae,0xfa,0xc3,0xa5,0x3f,0xa0,0x49,0x73,0x31,0xb0,0xec,0x7e,0xc,0x2c,0xbb,0x77,0x9b,0xd4,0xa3,0xdc,0x7e,0xd,0xf0,0x4d,0xc1,0x83,0x43,0x7d,0xab,0x6e,0xff,0xe6,0x77,0xda,0xf7,0x3b,0x9a,0x87,0x6,0x6,0xdf,0xd,0x22,0x47,0x17,0x32,0x5a,0x8,0x10,0x9,0x49,0xc2,0xb2,0x87,0x43,0x22,0xa2,0xda,0x82,0xd,0x8c,0xdc,0xdf,0x6e,0x2c,0x2d,0x30,0x12,0x24,0x4d,0x80,0xe0,0x4d,0x5f,0x13,0xc2,0x8d,0xf7,0x41,0x6f,0x68,0xde,0xb6,0xf2,0x6d,0x2e,0x85,0x93,0x42,0x40,0xfb,0x1e,0x54,0x31,0x83,0xcc,0xa6,0xd5,0x3a,0x9f,0xc9,0x89,0xc1,0xfe,0xa1,0x90,0x99,0x7f,0xb8,0xe2,0x86,0xcb,0x8f,0x18,0x58,0x76,0xff,0xea,0x99,0xff,0x7c,0xa5,0x5c,0xf9,0xbb,0xaf,0x6f,0x57,0x97,0xcc,0x1d,0x36,0x34,0x33,0x3f,0xfc,0x75,0x5a,0x79,0xeb,0x37,0x79,0xcf,0x8f,0x7f,0x73,0x4a,0xe0,0xfb,0x8b,0xa5,0x14,0x67,0x3a,0xae,0x63,0x5a,0xda,0xdb,0xd8,0x89,0x27,0xa5,0x93,0xee,0x80,0xe5,0x26,0x86,0x49,0x10,0xaa,0x25,0x7f,0x6a,0x1d,0xbe,0x46,0x21,0x61,0x1d,0x8,0xde,0xa,0xa7,0xb0,0x29,0xc7,0x8f,0x1b,0x2c,0xf9,0xe6,0x6,0xb,0x43,0xab,0xc4,0x3c,0x22,0xcc,0x1b,0x2e,0xe0,0x24,0x21,0xa1,0x82,0x2,0xc2,0x4c,0x1f,0x42,0xdf,0xd3,0x3,0xbd,0xbd,0x26,0xc,0xb4,0xad,0xb5,0xb9,0x47,0x5a,0xf2,0xab,0x2b,0xfe,0x70,0xd9,0xcb,0x3b,0x12,0xba,0x62,0x47,0x5d,0x68,0xe5,0xad,0xdf,0x64,0x0,0x50,0xa1,0xea,0x59,0x7d,0xd3,0xe2,0xb3,0xa4,0x1d,0x9b,0xe4,0x7b,0xc1,0x5f,0x37,0xaf,0xdb,0xa4,0xfa,0x36,0x6d,0x42,0x6e,0xd3,0x6a,0xc,0xad,0x7d,0x15,0x26,0xf0,0xaa,0xfa,0xd8,0x8e,0x62,0xf,0xeb,0x7,0x90,0x1b,0x2a,0xd1,0xa6,0x42,0xa5,0x1d,0xe7,0x3b,0x8e,0xbe,0xa9,0xf3,0x56,0x85,0x8f,0xad,0x34,0xc6,0x2f,0x71,0xfa,0xda,0xcf,0xa3,0xd0,0xbb,0x1e,0xb9,0x8d,0xab,0xd0,0xb7,0xb9,0x7,0x1b,0x56,0xaf,0x17,0xbe,0xa7,0x5e,0x86,0x14,0x7,0xae,0xbe,0xe9,0x1b,0xc7,0x31,0xf3,0xeb,0x3b,0x5a,0x77,0xbd,0x29,0xca,0x71,0xe6,0x69,0x8b,0x64,0x31,0xbb,0xd9,0x6c,0xba,0xfb,0xbf,0x79,0xd6,0xc7,0x16,0x1f,0xa6,0x42,0xff,0x4b,0x42,0x58,0xa7,0xdb,0x8e,0x85,0x64,0x22,0xae,0xdd,0x64,0x12,0xb1,0x74,0xbb,0x24,0x27,0x8e,0x58,0xb2,0x15,0x1c,0x95,0xa4,0x97,0xee,0x48,0x8c,0xa2,0xea,0x47,0x33,0x7,0xcd,0xf8,0x4,0xa3,0x67,0x20,0xb7,0x2e,0xef,0xb1,0x6d,0x73,0x53,0xc2,0x67,0x1e,0x41,0x1e,0x95,0x9f,0x2d,0xc,0x3c,0x68,0x2f,0xb,0x55,0xcc,0x9b,0xdc,0x60,0xbf,0x9,0x83,0xd0,0xca,0xe7,0xa,0x60,0xf0,0x1d,0xb6,0x65,0xff,0xf7,0xf2,0x3f,0x5c,0x7e,0x47,0x64,0x6a,0x17,0x5b,0x2b,0xff,0x70,0x99,0x7a,0x47,0x0,0xa0,0xfe,0x38,0xfc,0xb2,0x3f,0xd1,0x9a,0xe7,0x1f,0xef,0x20,0x41,0x67,0xa,0xe2,0x7f,0x27,0x41,0x76,0x2c,0x1e,0xb,0x5b,0xda,0xda,0x84,0xb0,0x6c,0x69,0xc7,0x53,0x88,0xb5,0x4d,0x2c,0x9,0xbf,0xaa,0x96,0x1d,0x62,0x64,0xea,0x70,0x9b,0xcc,0x41,0x3d,0xb7,0xb0,0xbd,0xc2,0x1f,0x45,0xed,0x37,0x70,0xe8,0xea,0x59,0x3f,0x22,0x1,0xa3,0x2,0x4,0x85,0xc,0x82,0x6c,0x1f,0x88,0x84,0xc9,0xc,0xf4,0x6b,0xaf,0xe8,0xd9,0x2a,0x54,0x60,0xd0,0x77,0x48,0x88,0xef,0xb2,0xed,0x6e,0x5c,0xfd,0xdb,0x8b,0xf5,0x9b,0x2d,0x1b,0xf9,0x56,0x0,0xc0,0x9a,0xb0,0x27,0x41,0x90,0x5a,0x73,0xd3,0x15,0xf,0xf,0xbe,0xfc,0xf0,0x15,0xed,0xfb,0x1d,0xbd,0x22,0xf4,0xfd,0x99,0x99,0xc1,0xa1,0xa4,0xd6,0x2a,0xa6,0xbc,0x2,0xfc,0x4c,0x2f,0x4c,0x50,0x64,0x68,0x4d,0x24,0x44,0x85,0x4b,0x17,0xa5,0x35,0x4,0x8d,0x67,0xf2,0xd6,0x2a,0x84,0xb7,0x3,0x4,0xe3,0x12,0x7e,0x3,0x6f,0x9e,0xa8,0xd2,0x8,0x9b,0xb5,0x6,0xeb,0x10,0xaa,0x98,0x43,0x71,0x60,0x33,0x67,0x7a,0x56,0xc3,0xcf,0x65,0x68,0xb0,0x7f,0x0,0x3,0x5b,0xfa,0xf2,0x81,0x1f,0xae,0x4,0xd3,0x77,0x56,0xdd,0x74,0xc5,0xfb,0x87,0x5e,0x7e,0xf8,0x9e,0xd6,0xbd,0xdf,0xeb,0x81,0x8d,0x19,0x7a,0xe9,0xa1,0x37,0x9d,0xf8,0x78,0x4b,0x1b,0xeb,0xce,0x38,0xf5,0x72,0xc1,0xca,0xa3,0x35,0xb7,0xfe,0x87,0x6,0x80,0x3d,0x3e,0x76,0xf9,0x5e,0xa1,0xe7,0x1f,0xc6,0xe0,0xd3,0x2c,0xc7,0x39,0xc1,0x92,0x2,0xb6,0x63,0xc3,0x76,0x6c,0xbf,0xa5,0xab,0x5b,0x82,0x2c,0x9,0x21,0xc8,0x89,0xb7,0x80,0x2c,0x7,0x76,0x3c,0x51,0xd9,0x1e,0xc6,0x18,0x55,0x95,0x63,0x10,0x15,0x70,0xd0,0x8e,0x0,0x1,0x8f,0xd,0x80,0x8a,0xe3,0x56,0x75,0x1e,0x95,0x72,0x22,0x24,0x25,0x74,0x18,0x40,0x79,0x79,0x68,0xbf,0x0,0x1d,0xfa,0xcc,0x3a,0xd4,0xc5,0xec,0x90,0xe,0x3c,0xdf,0xf5,0x7d,0x1f,0x2a,0x54,0x50,0xa1,0x7a,0x54,0x8,0xfa,0x83,0xb4,0xac,0x87,0x56,0xdc,0x70,0xc5,0xb,0x0,0xb0,0xdb,0x49,0x17,0xa,0xe1,0x24,0xb0,0xea,0x86,0x45,0x6f,0x59,0xff,0xfb,0xb7,0x69,0xaf,0x8d,0xda,0x63,0xf7,0x8f,0x5e,0x2a,0x74,0x28,0x2d,0x21,0xd4,0xc7,0xc0,0xe6,0x33,0x60,0xfe,0x47,0x30,0x23,0x9e,0x4c,0x42,0xda,0x16,0xb7,0xb4,0xb6,0xc2,0x30,0x13,0x98,0x61,0xc7,0x53,0x90,0xb1,0x4,0xe2,0xed,0x93,0x4b,0xeb,0x1e,0x46,0x3e,0x46,0x53,0x11,0x2,0x6d,0x85,0xfb,0x6f,0x6a,0xf6,0xf,0x97,0x80,0xfb,0xd9,0x7e,0xb0,0x31,0x28,0xe,0x6c,0x8c,0x56,0xf8,0xb0,0xe1,0x62,0xa1,0x0,0xbf,0xe0,0xc1,0xf7,0x3c,0x8a,0x56,0xe9,0x88,0x27,0x89,0xf9,0x97,0xc6,0x8e,0xfd,0x52,0x72,0x98,0x5d,0x79,0xc3,0x15,0xe6,0xed,0x1e,0xfb,0x9d,0x2,0x0,0x33,0x4e,0xbd,0x54,0xc2,0x28,0x6b,0xf5,0xcd,0x57,0x55,0x4a,0x7c,0x66,0x7d,0x6c,0xd1,0x11,0xca,0xf7,0x4f,0x4,0xf8,0x60,0xa3,0xcd,0x74,0xcb,0xb1,0xa7,0x3b,0xae,0xeb,0x38,0x4e,0x54,0x12,0x65,0xdb,0x12,0xcc,0xac,0x9d,0x44,0x1a,0x6e,0x22,0x45,0x6c,0xc,0x39,0xe9,0xce,0xa8,0xc7,0x1c,0x31,0xa4,0x13,0xaf,0x3c,0x22,0x33,0xc3,0x72,0x62,0x18,0x5e,0xd7,0x4f,0x35,0x8b,0x8d,0x6a,0x34,0x7e,0x89,0xaa,0x50,0x7e,0x1e,0xc3,0xc5,0x38,0xc,0x56,0x7e,0xd4,0xe1,0x43,0x8,0x56,0x85,0x21,0xb0,0xd1,0xac,0x55,0xc8,0xc5,0x4c,0x3f,0x98,0x59,0x6a,0x1d,0x39,0xb2,0x5e,0xd1,0x87,0xd1,0x86,0x7d,0xdf,0x5b,0x4b,0x44,0xeb,0x88,0x68,0x89,0xb0,0xed,0x3f,0xaf,0xfa,0xe3,0xe2,0x3b,0x2b,0xcf,0xfb,0xe1,0x8b,0x1c,0x92,0x96,0x11,0x96,0xad,0x57,0xfc,0x61,0x11,0xff,0x9f,0x7,0x40,0x45,0x13,0x9c,0x72,0x9,0x31,0x83,0x8a,0x83,0x2b,0xb9,0xe7,0x81,0x5f,0x57,0x6,0x66,0xf6,0x47,0x2f,0xeb,0xc,0xc2,0xb0,0xdb,0x68,0x3d,0x85,0x48,0x1c,0xce,0xc6,0xbc,0xd7,0x71,0x9d,0x83,0x8d,0xe1,0xb4,0x10,0x14,0xed,0xcf,0x67,0x80,0x54,0x4b,0x82,0x1,0x84,0xcc,0xe0,0xd6,0xae,0x89,0x54,0x8a,0x2c,0x88,0xc1,0x44,0xd2,0xa6,0xd2,0x22,0x4d,0xe2,0x91,0xca,0xa2,0x7a,0xc9,0x42,0xa4,0xd8,0x75,0xc0,0xc3,0xe9,0x7b,0xe6,0x42,0x66,0x88,0x4b,0x5b,0xc1,0xd8,0xc5,0x82,0x27,0x8c,0xd1,0x51,0x55,0x92,0x31,0x20,0xa2,0x50,0x85,0xea,0x19,0x30,0x3f,0xc,0xa2,0xbf,0x2,0x58,0x61,0x5b,0x72,0xcb,0x8a,0x9b,0xbe,0x59,0x53,0x8d,0x33,0xe3,0xe4,0xaf,0xb,0x62,0xf0,0xaa,0x5b,0xbe,0xb9,0xd3,0xf4,0x24,0xdb,0xa9,0x0,0xd0,0xd8,0x3c,0x2c,0x22,0x84,0x1,0x31,0x81,0xd8,0x18,0xac,0xb9,0xf5,0x5b,0x35,0x9e,0xf1,0xac,0x8f,0x5f,0xb1,0xb7,0xf1,0xfc,0x5,0x0,0x1f,0x68,0x8c,0x59,0xc0,0xcc,0x7b,0x11,0xd0,0xc6,0xc4,0x82,0x98,0x44,0x69,0x61,0x86,0x48,0x24,0x13,0x42,0x5a,0x56,0x84,0x16,0xf0,0x58,0x2b,0xc3,0x98,0x48,0x70,0x66,0x70,0x90,0x9,0xa4,0x41,0x6c,0xc0,0xd0,0x44,0x30,0x4c,0xc8,0x3,0xb4,0x5c,0x10,0x3d,0xb,0xa6,0xe7,0xa4,0xe3,0x3e,0xbb,0xfc,0xf,0x97,0x3d,0x5b,0x7d,0x81,0xdd,0x4e,0x58,0x28,0xc9,0x76,0x0,0x36,0x4c,0xd2,0xe6,0x55,0x37,0x2e,0xde,0xa9,0x1b,0xd0,0xed,0xf4,0x0,0x18,0x1,0x88,0x53,0x2f,0x11,0x46,0x29,0x41,0x44,0x82,0x8d,0xc1,0x9a,0xdb,0xfe,0x23,0x68,0x6c,0xc6,0x99,0xf6,0xfa,0xf4,0xd5,0x5d,0xc6,0xcb,0x77,0xb1,0x52,0x5d,0x4a,0xeb,0x34,0x33,0xbb,0x20,0x72,0x19,0x70,0x5,0x10,0x63,0x90,0x5b,0xea,0x55,0xee,0x33,0xe0,0x3,0xf0,0xc0,0xec,0x1b,0x63,0x7c,0x37,0x16,0x2b,0x80,0xd1,0x6b,0xa7,0x5a,0xb6,0xcc,0x3e,0xe2,0xa0,0xbe,0x3b,0x3e,0x7f,0x74,0xc3,0x6d,0xce,0xa6,0x9d,0x78,0xa1,0x2d,0xa5,0x24,0x66,0x18,0x21,0x85,0x11,0xb6,0xcb,0x2b,0x7e,0x7f,0x19,0xbf,0x53,0xc6,0xf3,0x1d,0x7,0x80,0x51,0xfd,0x88,0x8f,0x5c,0x5c,0x6e,0x83,0x81,0x35,0xb7,0x5c,0xf5,0xa6,0x8,0x60,0xb7,0x8f,0xfc,0x5b,0xb4,0x5e,0x9c,0x81,0xd5,0x6f,0xd2,0x6f,0xec,0x3a,0x76,0x1d,0xbb,0x8e,0x5d,0xc7,0xae,0x63,0xd7,0xb1,0xeb,0xd8,0x75,0xbc,0x15,0xc7,0xff,0xf,0xd8,0x38,0x69,0x89,0x29,0xb2,0xb8,0x39,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x0,0x0,0x0,0x0d,0x49,0x48,0x44,0x52,0x0,0x0,0x01,0x0,0x0,0x0,0x01,0x0,0x08,0x06,0x0,0x0,0x0,0x5c,0x72,0xa8,0x66,0x0,0x0,0x0,0x06,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x09,0x70,0x48,0x59,0x73,0x0,0x0,0x0d,0xd7,0x0,0x0,0x0d,0xd7,0x01,0x42,0x28,0x9b,0x78,0x0,0x0,0x20,0x0,0x49,0x44,0x41,0x54,0x78,0xda,0xed,0x9d,0x79,0x9c,0x54,0xd5,0x99,0xf7,0x7f,0xe7,0xde,0x5b,0x7b,0x55,0x57,0xf5,0xbe,0x77,0x43,0x37,0x6b,0xb3,0x8b,0x20,0x8b,0x22,0x2e,0xd1,0x98,0x31,0xd1,0xc4,0x84,0x44,0x07,0xd0,0x06,0x9d,0x64,0x26,0xaf,0x8e,0x89,0x79,0x67,0x46,0xf3,0x26,0xa2,0x93,0x65,0x7c,0xe7,0xcd,0x32,0x8e,0x66,0x24,0x09,0x69,0x40,0x8d,0x19,0x26,0x89,0x21,0x2e,0xb8,0x6f,0x80,0x88,0x82,0xa0,0xec,0x74,0x43,0xd3,0xd0,0x6b,0xf5,0x56,0xfb,0x7a,0xef,0x3d,0xef,0x1f,0x8d,0xad,0xd0,0x5b,0x55,0x77,0xed,0xf5,0x7c,0x3f,0x1f,0x3e,0x68,0x51,0x55,0xe7,0xd6,0x59,0x7e,0xe7,0x39,0xcf,0x79,0xce,0x73,0x0,0x82,0x20,0x08,0x82,0x20,0x08,0x82,0x20,0x08,0x82,0x20,0xb2,0x01,0x96,0xce,0x0f,0xbf,0x6d,0xe3,0x46,0xab,0x47,0xa3,0xd1,0xea,0x0,0x0b,0x67,0xcc,0xa4,0xaa,0xaa,0x8e,0x33,0x66,0xa3,0x66,0x25,0xe2,0x3a,0x68,0x38,0x0f,0x88,0xa2,0xe8,0x57,0x65,0xd9,0xc9,0xb5,0xda,0x90,0x28,0x8a,0x6e,0x8d,0x46,0xe3,0x5d,0xb5,0x6a,0x55,0x88,0x04,0x20,0x0e,0x34,0x34,0x34,0xd8,0x24,0xc6,0x96,0x73,0x60,0x16,0x38,0x9f,0x2e,0x70,0x3e,0x93,0x33,0x36,0x03,0x40,0x2e,0x75,0x47,0x22,0x35,0x46,0x12,0x93,0xc1,0x79,0x33,0x07,0x8e,0x0a,0x9c,0x9f,0xe0,0xc0,0x71,0xae,0xaa,0x1f,0xac,0x59,0xbf,0xfe,0x08,0x63,0x8c,0x93,0x0,0x44,0xc1,0xc6,0x8d,0x1b,0x8d,0x26,0x8d,0xe6,0x6a,0x15,0x58,0xc9,0x18,0x5b,0x09,0x60,0x3e,0x0,0x91,0x7a,0x19,0x91,0x86,0x74,0x83,0xb1,0xb7,0x39,0xf0,0x16,0x53,0x94,0xd7,0xd6,0xac,0x5f,0x7f,0x82,0x04,0x60,0x18,0xde,0xdc,0xb0,0x41,0x3a,0x57,0x5d,0x7d,0x83,0x0,0xac,0xe1,0xc0,0x17,0x01,0xe8,0xa9,0xef,0x10,0x19,0x07,0xe7,0xc7,0xc0,0xd8,0x36,0x41,0x51,0x1a,0xfe,0xf6,0xce,0x3b,0x5b,0xb2,0x5e,0x0,0xb6,0x6d,0xdc,0x68,0x0d,0x6a,0xb5,0xdf,0x06,0x70,0x27,0x80,0xc9,0xd4,0x43,0x88,0x2c,0x21,0x04,0xce,0x5f,0xe0,0x9c,0xff,0x7c,0xed,0xfa,0xf5,0xbb,0xb2,0x4e,0x0,0x9e,0xd9,0xb4,0xa9,0x4c,0x61,0xec,0x9f,0x39,0x63,0xeb,0x01,0x98,0xa8,0x3f,0x10,0x59,0x6c,0x15,0x1c,0xe0,0x8c,0xfd,0x74,0xcd,0x1d,0x77,0xfc,0x31,0xd1,0xfe,0x82,0x84,0x0b,0xc0,0xd6,0xad,0x5b,0x4d,0x90,0xe5,0xfb,0x19,0x63,0xf7,0x0,0xb0,0x50,0xeb,0x13,0xc4,0x20,0xbb,0x19,0xf0,0xbd,0xd5,0xf5,0xf5,0xef,0x65,0x9c,0x0,0x70,0xce,0xd9,0x53,0x5b,0xb6,0xac,0x01,0xe7,0x8f,0x0,0x28,0xa1,0xb6,0x26,0x88,0x11,0xc6,0x0a,0x63,0xcf,0x6b,0x54,0xf5,0x1f,0x6e,0x5d,0xb7,0xee,0x5c,0x46,0x08,0xc0,0xe6,0xcd,0x9b,0x6b,0x45,0xce,0x1f,0x03,0xf0,0x79,0x6a,0x5e,0x82,0x88,0x40,0x04,0x80,0x3e,0xc6,0xf9,0x03,0xab,0xeb,0xeb,0x7f,0x1d,0xcf,0x65,0x41,0xdc,0x05,0xe0,0xc9,0xdf,0xfd,0xee,0x4e,0x30,0xf6,0x28,0x0,0x03,0x35,0x2b,0x41,0x44,0x6d,0x39,0xbf,0xa9,0x30,0x76,0x5b,0x7d,0x7d,0x7d,0x67,0x5a,0x09,0xc0,0xc6,0x8d,0x1b,0x8d,0x26,0xad,0xf6,0x09,0x0e,0xac,0xa1,0x66,0x24,0x88,0x09,0xd1,0xc6,0x55,0xf5,0x1b,0xf1,0xd8,0x2d,0x88,0x8b,0x0,0x3c,0xb3,0x69,0x53,0x99,0x2c,0x8a,0xdb,0xc1,0xf9,0xa5,0xd4,0x76,0x04,0x11,0x13,0x42,0xe0,0xfc,0x9e,0x35,0xeb,0xd6,0x6d,0x4c,0x69,0x01,0x78,0xba,0xa1,0x61,0xbe,0x0a,0xbc,0x08,0xa0,0x94,0xda,0x8c,0x20,0x62,0x3c,0x63,0x73,0xfe,0x68,0xd3,0xd9,0xb3,0xdf,0xd9,0xb0,0x61,0x83,0x9a,0x72,0x02,0xf0,0xf4,0xe6,0xcd,0x2b,0x54,0xce,0xb7,0x03,0xa0,0x03,0x39,0x04,0x11,0x3f,0xfe,0x24,0x03,0xab,0xeb,0xeb,0xeb,0x03,0x13,0xfd,0x22,0x21,0x56,0x4f,0xf4,0x64,0x43,0xc3,0xe7,0x54,0xce,0x77,0xd0,0xe0,0x27,0x88,0xb8,0x73,0x8b,0x04,0x6c,0xdb,0xb6,0x6d,0x9b,0x36,0x25,0x2c,0x80,0xad,0x9b,0x36,0x5d,0xce,0x04,0xe1,0x15,0x90,0xa7,0x9f,0x20,0x12,0x07,0xe7,0x7f,0xd5,0x99,0xcd,0x5f,0x59,0xb5,0x6a,0x95,0x92,0x34,0x0b,0xe0,0xc9,0x4d,0x9b,0xe6,0x31,0x41,0x78,0x9e,0x06,0x3f,0x41,0x24,0xda,0x21,0xc0,0xbe,0x14,0xf4,0x7a,0x1f,0x4f,0xda,0x12,0xe0,0x99,0x4d,0x9b,0xca,0x98,0x20,0xfc,0x05,0x80,0x95,0x5a,0x83,0x20,0x92,0xc2,0x37,0x9f,0x6c,0x68,0xf8,0xe7,0x84,0x2f,0x01,0xb6,0x6d,0xdb,0xa6,0x0d,0x7a,0xbd,0xbb,0x0,0x2c,0xa2,0x36,0x20,0x88,0xe4,0x2e,0x06,0x0,0x7c,0x65,0x4d,0x7d,0xfd,0x5f,0x12,0x66,0x01,0x04,0x7d,0xbe,0x7f,0xa3,0xc1,0x4f,0x10,0xa9,0xb1,0x18,0x0,0xf0,0xbb,0x86,0x86,0x86,0x49,0x09,0x11,0x80,0xad,0x0d,0x0d,0x5f,0x01,0xe7,0xf7,0x52,0xbd,0x13,0x44,0xca,0x90,0x2b,0x02,0xff,0x1d,0xed,0xce,0x40,0xd4,0x02,0xf0,0xfb,0xdf,0xfc,0xa6,0x98,0x31,0xb6,0x11,0x69,0x9e,0x50,0x94,0x20,0x32,0xd0,0x0c,0x58,0x1c,0xf4,0x7a,0x1f,0x88,0xab,0x0,0x28,0x92,0xf4,0x4b,0x70,0x5e,0x40,0xd5,0x4d,0x10,0xa9,0xa8,0x02,0xec,0xfb,0x5b,0xb6,0x6c,0x99,0x1b,0x17,0x01,0x78,0xb2,0xa1,0xe1,0xf3,0x0,0xbe,0x41,0xb5,0x9c,0x1c,0x42,0xaa,0x80,0x10,0x17,0xd2,0xea,0x99,0xbd,0xaa,0x04,0x4e,0x4d,0x97,0x38,0x38,0x97,0x04,0x55,0x7d,0x8c,0x73,0x1e,0x91,0x85,0x1e,0xb1,0x19,0xff,0xe8,0xa3,0x8f,0xea,0x72,0x2d,0x96,0x43,0x0,0xa6,0x52,0x2d,0x27,0x06,0xa7,0xa2,0x81,0x3d,0xac,0x83,0x5d,0xd6,0xa3,0x27,0xac,0x87,0x5b,0x95,0x30,0x49,0xeb,0xc1,0xe5,0x96,0x9e,0xb4,0x19,0xfc,0x7f,0x75,0x94,0x83,0x73,0x20,0x57,0x0a,0xa3,0x44,0xe3,0x47,0xa1,0x14,0x44,0x91,0x26,0x0,0x2d,0x53,0xa9,0x81,0xe3,0x69,0x08,0x70,0xbe,0x66,0xf5,0xba,0x75,0x4f,0xc5,0x4c,0x0,0x9e,0x6a,0x68,0xb8,0x9b,0x03,0x8f,0x52,0xd5,0xc6,0x8f,0x30,0x67,0x68,0x0b,0x1b,0xd1,0x15,0xd6,0xc3,0x1e,0xd6,0xc3,0xa9,0x68,0x86,0x6d,0xb0,0xeb,0xad,0xed,0x28,0x90,0x52,0xff,0x0e,0x8a,0xdd,0xee,0x02,0x34,0x87,0xcc,0x43,0x5e,0x17,0x19,0x1f,0x14,0x82,0x72,0xad,0x1f,0x79,0x62,0x90,0x1c,0x4a,0xb1,0x5f,0x0a,0xb4,0xf6,0xbb,0x5c,0x53,0xee,0xb9,0xe7,0x9e,0xe0,0x68,0x6f,0x93,0x22,0xf9,0xae,0x6d,0xdb,0xb6,0x19,0xa2,0x75,0x2e,0x10,0x91,0xcf,0xf2,0xad,0x21,0x23,0xda,0xc2,0x46,0xd8,0xc3,0xda,0x31,0x35,0x99,0x03,0x78,0xdf,0x5b,0x80,0x2f,0x58,0xdb,0x53,0xfa,0x77,0x75,0x85,0x75,0xc3,0x0e,0x7e,0x0,0x50,0x38,0x43,0x67,0x58,0x8f,0xce,0xb0,0x1e,0x1f,0xfb,0x6c,0xd0,0x09,0x2a,0xca,0x34,0x3e,0x54,0x6a,0xfd,0x28,0xd3,0xf8,0x21,0x91,0x75,0x10,0x8b,0xa5,0x40,0x45,0x9e,0xc5,0xb2,0x1e,0xc0,0xaf,0x26,0x6c,0x01,0x6c,0x6d,0x68,0xf8,0x16,0x03,0xfe,0x8b,0x6a,0x35,0x76,0x83,0xfe,0x6c,0xc8,0x88,0xd6,0x90,0x11,0x7d,0xb2,0x6e,0x5c,0x6b,0xe4,0xe5,0xe6,0x6e,0x4c,0xd6,0x79,0x53,0xb3,0xef,0x01,0x78,0xd9,0x59,0x8a,0x1e,0x59,0x17,0xf5,0x67,0xb5,0x4c,0x45,0x99,0xc6,0x8f,0x0a,0xad,0x0f,0xe5,0x5a,0x1f,0x34,0x8c,0x3c,0x08,0x13,0x68,0x87,0x73,0x7a,0x93,0x69,0xca,0x68,0x57,0x96,0x8d,0x69,0x01,0xbc,0xb9,0x61,0x83,0xd4,0x0a,0xfc,0x13,0x55,0xe7,0xc4,0x08,0x72,0x11,0x67,0x02,0x46,0x9c,0x0e,0x59,0xd0,0x2b,0x4f,0xf8,0x10,0x17,0x0e,0xfa,0x72,0x51,0xa5,0xf5,0x41,0x4c,0xc1,0x01,0x72,0x26,0x64,0x1e,0xd7,0xe0,0x07,0x80,0x10,0x17,0x70,0x26,0x64,0xc2,0x99,0x90,0x09,0x22,0xe3,0xa8,0xd2,0x78,0x51,0xa3,0xf7,0xa2,0x54,0xe3,0xa7,0x4e,0x14,0xed,0x2a,0x0,0xa8,0x0c,0x79,0x3c,0xb7,0x02,0xd8,0x32,0x6e,0x01,0x68,0xab,0xaa,0xba,0x19,0x74,0x61,0xc7,0xb8,0x90,0x39,0x43,0x73,0xd0,0x84,0xe6,0x90,0x25,0x22,0xf3,0x3e,0x1a,0xbc,0xaa,0x84,0xa3,0x81,0x1c,0xcc,0x31,0x38,0x53,0xea,0x37,0x2b,0x9c,0xe1,0x80,0x37,0x37,0x66,0xdf,0xd5,0x1c,0x32,0xa3,0x39,0x64,0x86,0x56,0x50,0x51,0xad,0xf5,0xa1,0x46,0xe7,0x41,0xa1,0x14,0xa0,0xce,0x15,0xa9,0x15,0xc0,0xd8,0x77,0x46,0x13,0x80,0x31,0xf7,0x94,0x38,0xb0,0x8e,0xaa,0x31,0x3a,0x5c,0x8a,0x06,0xfb,0xbc,0x79,0x78,0xd6,0x51,0x89,0xbd,0xde,0x02,0xd8,0xc3,0x3a,0xc4,0x23,0x6e,0xea,0xb8,0xdf,0x9a,0x72,0xdb,0x82,0x8d,0x41,0x0b,0x7c,0x6a,0xec,0xaf,0x71,0x0c,0xa9,0x02,0x1a,0x03,0x66,0xbc,0xec,0x2c,0xc1,0x4b,0xce,0x52,0x34,0x06,0x2c,0x50,0x38,0xb9,0x0e,0x23,0x60,0xde,0xd6,0x4d,0x9b,0x2e,0x1d,0x97,0x05,0xd0,0xd0,0xd0,0x50,0x02,0xe0,0x3a,0xaa,0xc3,0xc8,0x68,0x0d,0x19,0x71,0x34,0x60,0x3d,0x3f,0xe0,0x13,0xb1,0xac,0x10,0x70,0xc4,0x6f,0xc5,0x02,0x63,0x7f,0x4a,0xfc,0xfe,0x30,0x67,0x38,0xe4,0x8b,0xff,0xc1,0xd0,0x1e,0x59,0x87,0x1e,0x59,0x87,0x0f,0x7d,0x79,0xa8,0xd1,0x7b,0x30,0x5b,0xef,0x80,0x41,0x50,0xa8,0x03,0x8e,0x34,0xcb,0x33,0xb6,0x06,0xc0,0xbe,0xa8,0x2d,0x0,0x91,0xf3,0x5b,0x41,0xb7,0xf2,0x8e,0x8a,0x0a,0x86,0xd3,0x41,0x13,0x5e,0x74,0x96,0xe1,0x2d,0x77,0x51,0xc2,0x06,0xff,0xa0,0x15,0x10,0xc8,0x81,0x5f,0x4d,0x8d,0x26,0x3a,0x16,0xb0,0x22,0xc8,0x13,0xf7,0x2c,0x61,0xce,0x70,0xc2,0x6f,0xc1,0xb3,0x8e,0x4a,0xec,0xf6,0x14,0x0c,0xbb,0x6d,0x4a,0x0,0x9c,0xb1,0x5b,0x37,0x6e,0xdc,0xa8,0x89,0xda,0x02,0x60,0x8c,0xdd,0x4a,0xd5,0x37,0x32,0xe7,0x42,0x46,0x1c,0xf4,0xe5,0x26,0xb5,0xe3,0x29,0x9c,0xe1,0x88,0xdf,0x8a,0x4b,0x4d,0x7d,0x49,0xad,0x8b,0x90,0x2a,0xe0,0xb8,0x3f,0x27,0x39,0x22,0xcc,0x81,0xe6,0xa0,0x19,0x2d,0x41,0x13,0xaa,0xb4,0x3e,0x5c,0x62,0xea,0x83,0x91,0x2c,0x82,0xcf,0x52,0x68,0xd4,0x6a,0xaf,0x01,0xf0,0x52,0xc4,0x16,0xc0,0x79,0xf3,0x9f,0xd2,0x7a,0x8f,0x42,0x73,0xd0,0x94,0x12,0xb3,0xce,0xc9,0x80,0x05,0x3e,0x55,0x4a,0xea,0x33,0x1c,0x0b,0x24,0xdf,0x1f,0xa1,0x82,0xe1,0x4c,0xc8,0x04,0xb7,0x22,0x51,0xe7,0x1c,0x3a,0x9b,0xff,0x4d,0x54,0x4b,0x0,0x89,0xf3,0xab,0x40,0x27,0xfe,0x46,0x45,0x9b,0x22,0x5b,0x70,0x2a,0x18,0x0e,0xf9,0x93,0x97,0x94,0x29,0xc8,0x45,0x1c,0x0b,0xe4,0x50,0xbb,0xa4,0xf4,0x5a,0x55,0xbd,0x26,0x3a,0x1f,0x80,0x20,0x5c,0x4d,0xb5,0x36,0x3a,0xa2,0x90,0x3a,0x1d,0xed,0x54,0xc0,0x0c,0x6f,0x92,0xac,0x80,0xa3,0x7e,0x0b,0xe4,0x14,0xf2,0xc8,0x8b,0x24,0x0,0xc3,0x59,0x0,0x33,0xb7,0x6c,0xd9,0x52,0x1e,0xb9,0x0,0x70,0x4e,0x02,0x30,0xd6,0x4c,0x83,0xd4,0x09,0x59,0x55,0x31,0xe0,0x0b,0x48,0xc6,0xec,0x7f,0xd2,0x9f,0x93,0x5a,0xed,0x42,0xa1,0xc4,0xc3,0xcf,0xe9,0x9c,0xaf,0x8c,0x48,0x0,0xb6,0x6e,0xdd,0x5a,0x04,0xa0,0x86,0xaa,0x6c,0x74,0xa4,0x14,0x9b,0x69,0x9a,0x02,0xe6,0x84,0xfb,0x02,0x8e,0xfb,0x73,0x10,0x86,0x90,0x62,0xed,0x42,0x02,0x30,0x02,0x4b,0x22,0xb3,0x0,0x64,0x79,0x26,0xd5,0x55,0x24,0xa6,0x66,0x6a,0x75,0x34,0x15,0x0c,0xc7,0x12,0x38,0x1b,0x87,0x39,0xc3,0x89,0x80,0x25,0xc5,0x5a,0x85,0xd3,0x12,0x60,0x64,0xab,0x7e,0x66,0x44,0x02,0xc0,0x04,0x61,0x3a,0xd5,0x56,0x7a,0x9a,0x9a,0x27,0x83,0x96,0x84,0xed,0xc5,0x9f,0x0c,0xe4,0xa4,0x5c,0x24,0xa2,0xc4,0x38,0x79,0xae,0x47,0xf6,0x03,0x4c,0x8f,0xcc,0x02,0xe0,0x9c,0x04,0x20,0x22,0x0b,0x20,0xf5,0x66,0x1a,0x85,0x33,0x9c,0xf0,0x9b,0xe3,0x6f,0x6d,0x70,0xe0,0x44,0x20,0x27,0xe5,0x7e,0x3f,0xed,0x0,0x8c,0x6a,0x01,0x94,0x6f,0x7b,0xfc,0x71,0xf3,0xd8,0x02,0x40,0x59,0x7f,0x22,0x42,0x93,0xa2,0x6b,0xcd,0x13,0x81,0x9c,0xb8,0xc7,0xc9,0x9f,0x0e,0x99,0xe3,0x12,0xf3,0x9f,0x89,0xa2,0x9c,0x4a,0x36,0x40,0xd8,0x68,0x9c,0x72,0x81,0xc5,0x34,0xc2,0x1b,0x4b,0xa8,0xae,0x22,0x11,0x80,0xd4,0xec,0x6c,0x41,0x2e,0xe2,0x74,0xd0,0x84,0xa9,0x7a,0xcf,0x88,0xef,0x09,0xa9,0x02,0x14,0x30,0x84,0xb9,0x80,0x30,0x67,0x0,0x18,0x04,0xc6,0x21,0x31,0x15,0x5a,0xc6,0x21,0x41,0x1d,0x71,0x30,0x71,0x20,0x69,0x51,0x7f,0xe9,0xb8,0x2c,0x4b,0x25,0xd4,0x8b,0xc6,0xb6,0x34,0x42,0x03,0x5b,0x68,0x1d,0x15,0xc9,0x7a,0x33,0x75,0x3b,0xdb,0xb1,0x80,0x15,0x12,0x1b,0x38,0x36,0xec,0x55,0x45,0x78,0x14,0x09,0x3e,0x55,0x82,0x47,0x95,0xa2,0xb2,0x0e,0xf4,0x82,0x02,0xb3,0xa0,0xc0,0x24,0x84,0x61,0x14,0x14,0x98,0x44,0x19,0x32,0x67,0x70,0x28,0xda,0x94,0xfc,0xdd,0x22,0x48,0x0,0x46,0x5d,0x05,0x0,0x96,0x31,0x05,0x80,0x5d,0xf4,0x26,0x22,0xfd,0x04,0xc0,0xa5,0x68,0xb0,0xdb,0x33,0xf1,0xec,0xed,0x01,0x55,0x44,0x40,0x15,0xd1,0x03,0x6d,0x5a,0xb4,0x09,0x59,0x0,0xa3,0x23,0x5c,0x34,0xb6,0x47,0xf2,0x01,0x90,0x0,0x44,0x80,0x81,0x3a,0x5b,0xca,0xa1,0xa3,0x43,0x40,0x63,0x91,0x13,0x89,0x0,0x98,0xa8,0x9e,0x22,0xb3,0x0,0xc8,0xe9,0x94,0x62,0xa2,0x2c,0x90,0x28,0x8f,0xea,0x03,0xe0,0x3c,0xa2,0x5d,0x0,0xca,0x01,0x10,0xe9,0x1a,0x99,0xd1,0x8c,0x93,0x5a,0x16,0x0,0x09,0xc0,0x68,0x30,0xc6,0xa4,0x48,0x04,0x80,0xa0,0x19,0x27,0x4d,0x05,0x59,0xa6,0x4a,0x88,0xce,0x27,0x40,0x50,0x87,0x23,0x41,0x26,0x01,0x20,0xc6,0x67,0x72,0x8a,0xd4,0xe1,0x48,0x90,0x49,0x0,0xb2,0x16,0x93,0x40,0x1d,0x2e,0x95,0xa0,0x54,0x60,0x24,0x0,0x09,0xc5,0x4c,0x02,0x90,0x32,0x48,0x4c,0x85,0x96,0x96,0x0,0x24,0x0,0x89,0x9d,0x71,0x48,0x0,0x52,0xc7,0x1a,0xa3,0xd9,0x9f,0x04,0x80,0x2c,0x80,0xec,0x6d,0x0b,0x91,0xda,0x82,0x04,0x20,0xd1,0x16,0x80,0xa8,0x0,0xa0,0x60,0xa0,0x94,0x68,0x0b,0x72,0x0,0x92,0x0,0x24,0x1a,0xb7,0x22,0x41,0xa4,0x93,0x53,0x29,0x41,0x90,0x8b,0x50,0x29,0x1d,0x48,0x54,0x50,0x02,0xf5,0xf1,0x76,0x36,0x55,0xc0,0x3e,0x5f,0x1e,0x9a,0x83,0x26,0x50,0xf6,0xf4,0xd4,0xe0,0x6c,0xc8,0x88,0x67,0xfb,0x2b,0xb0,0xc0,0xd8,0x87,0x9a,0x14,0xbd,0x3a,0x9d,0x04,0x20,0xcd,0xe1,0x18,0x48,0xbe,0x79,0xd0,0x9f,0x87,0xa0,0x4a,0x06,0x54,0xaa,0xe1,0x57,0x45,0xbc,0xeb,0x29,0xc4,0xe9,0xa0,0x05,0x8b,0x4c,0xbd,0xb0,0x8a,0x61,0xaa,0x14,0x12,0x80,0xd8,0x99,0xfb,0xef,0x7b,0xf3,0xd1,0x11,0x36,0x50,0x65,0xa4,0x38,0x9d,0x61,0x3d,0x76,0x38,0xcb,0x30,0xc7,0xe0,0xc4,0x4c,0x83,0x13,0x02,0xf9,0x69,0xd2,0xd3,0x07,0x10,0x50,0x45,0x7c,0xe4,0xb3,0x25,0xf4,0xd2,0xc9,0x8b,0x19,0xc8,0xb9,0x6f,0xc3,0xf3,0xce,0x72,0x1a,0xfc,0x69,0x84,0xcc,0x19,0x0e,0xf8,0x6c,0x78,0xd1,0x59,0x86,0x5e,0x59,0x97,0xd4,0x67,0xe9,0x0a,0xeb,0x71,0x3c,0x05,0x73,0x28,0xa6,0xb4,0x05,0x10,0xe6,0x0c,0xaf,0xbb,0x8b,0xd1,0x2f,0x6b,0x71,0x2c,0x90,0x83,0x3a,0xbd,0x0b,0x33,0x0d,0xce,0x84,0xa6,0xe2,0x72,0x28,0x5a,0xec,0xf6,0x14,0xa0,0x5f,0xd6,0xd2,0x88,0x4a,0x53,0x1c,0xb2,0x06,0x2f,0x39,0x4b,0x31,0xd3,0xe0,0xc2,0x7c,0x43,0x1f,0x84,0x04,0xba,0x6c,0x7a,0x65,0x1d,0x3e,0xf2,0xe7,0xa2,0x3d,0xa4,0x07,0x0,0x30,0xce,0x31,0xdd,0xe0,0x26,0x01,0x18,0x73,0xd6,0xe5,0xc0,0xdb,0xee,0xa2,0xc1,0x81,0x27,0x73,0x01,0x1f,0xfb,0x6d,0x38,0x11,0xcc,0xc1,0x2c,0x83,0x13,0xd3,0x75,0xae,0xb8,0x9f,0xc5,0x3f,0x19,0xcc,0xc1,0x7e,0x6f,0x6e,0xdc,0x13,0x6c,0x12,0xf1,0x87,0x03,0x38,0xea,0xcf,0x41,0x57,0x58,0x8f,0xcb,0xcd,0x76,0x58,0xe2,0x1c,0x33,0xe0,0x54,0x34,0x38,0xe8,0xcf,0x45,0x6b,0xd0,0x78,0xc1,0xe2,0xe3,0x03,0x5f,0x1e,0x0c,0xa2,0x8a,0x2a,0x6d,0x6a,0x38,0x29,0x85,0x54,0x6d,0xac,0x3d,0xde,0x42,0x74,0x0e,0x63,0x6e,0x07,0x55,0x01,0x1f,0x7a,0x73,0xf1,0x57,0x67,0x05,0xce,0x84,0x4c,0x71,0x59,0xd9,0x85,0xb8,0x80,0xb7,0xdd,0x45,0x78,0xdf,0x93,0x47,0x83,0x3f,0xc3,0xe8,0x95,0xb5,0x78,0xd1,0x51,0x76,0x7e,0xf7,0x26,0xf6,0x04,0xb9,0x88,0xbd,0xde,0x7c,0x3c,0xef,0x28,0xc7,0xb9,0x8b,0x06,0xff,0x0,0x0c,0xbb,0xdc,0x05,0xb0,0xcb,0x7a,0x12,0x80,0x91,0x78,0xdf,0x9b,0x3f,0x66,0x03,0x79,0x15,0x11,0xbb,0xdc,0x85,0xd8,0xe1,0x2c,0x83,0x3d,0x1c,0xbb,0xca,0xec,0x91,0x75,0x78,0xc1,0x51,0x86,0x73,0x21,0x23,0x8d,0x96,0x0c,0x25,0x0c,0x01,0xbb,0x3d,0x85,0xd8,0xed,0x29,0x88,0x99,0xc0,0x2b,0x9c,0xe1,0x63,0x9f,0x0d,0x7f,0xee,0xaf,0x40,0x63,0xc0,0x32,0xea,0xc4,0xa4,0x82,0xe1,0x0d,0x57,0x11,0xfa,0x52,0x60,0x59,0x99,0x72,0x02,0x70,0x2c,0x60,0x45,0x63,0x14,0xd7,0x4d,0xf5,0xc9,0x5a,0xbc,0xe2,0x2a,0xc6,0xbb,0x9e,0x02,0x04,0x26,0x98,0xa7,0xbe,0x39,0x68,0xc6,0xab,0xae,0x92,0xa4,0xdd,0xb2,0x4b,0x24,0x96,0x58,0xb5,0x77,0x6b,0xd8,0x88,0xe7,0x9d,0xe5,0xf8,0xd8,0x6f,0x8b,0x58,0x50,0x64,0x2e,0xe0,0x4d,0x77,0x71,0xd2,0xfb,0x5a,0x4a,0x09,0xc0,0xb9,0x90,0x11,0xfb,0xbd,0xb6,0x71,0x7c,0x92,0xe1,0x74,0xd0,0x8c,0xed,0x8e,0x0a,0x1c,0xf3,0xe7,0x44,0x1d,0x0d,0xa6,0x82,0x61,0xaf,0x37,0x3f,0xa6,0x33,0x02,0x91,0x1e,0xf4,0xc8,0x3a,0xbc,0xe8,0x28,0x1d,0x76,0xb9,0x39,0x16,0x6e,0x55,0xc2,0x5b,0xae,0x22,0xbc,0xe5,0x2a,0x82,0x5b,0x89,0x7e,0x20,0xfb,0x55,0x11,0x6f,0xba,0x8a,0x20,0x27,0xf1,0x7a,0xb5,0x94,0x11,0x0,0x87,0xac,0xc1,0xbb,0xee,0x02,0x4c,0x24,0xaa,0x2e,0xcc,0x19,0xf6,0xfb,0xf2,0xf0,0xa2,0xa3,0x14,0x5d,0x61,0x5d,0xc4,0x8d,0xf0,0x8a,0xb3,0x24,0x2a,0xab,0x83,0xc8,0x2c,0x82,0x5c,0xc4,0xeb,0xae,0x62,0x1c,0x8d,0xf0,0xb2,0x93,0x4f,0xcc,0xfd,0xe7,0x1d,0xe5,0x68,0x0d,0x4f,0x6c,0xa9,0xe8,0x50,0xb4,0xd8,0x15,0x83,0xf4,0xed,0x69,0x2d,0x0,0x41,0x2e,0xe2,0x2d,0x4f,0x71,0xcc,0xae,0x99,0x76,0x28,0x5a,0xbc,0xe6,0x2a,0xc5,0x6e,0x77,0x01,0x02,0xa3,0x44,0xeb,0xf5,0xc9,0x5a,0xec,0x70,0x96,0xa2,0x27,0xc9,0x7b,0xc4,0x44,0xf2,0xe1,0x0,0x3e,0xf4,0xe5,0xe1,0x5d,0x4f,0xc1,0xa8,0x16,0x64,0x5b,0xc8,0x80,0xe7,0xa2,0x34,0xf7,0xc7,0x5c,0x42,0x84,0x8c,0xf8,0xc8,0x67,0x4b,0xca,0xef,0x4e,0xfa,0x62,0x57,0x05,0xc3,0x4e,0x77,0x21,0x3c,0x8a,0x14,0xf3,0x06,0x6d,0x0e,0x99,0xd1,0x2e,0x1b,0xb1,0xd0,0xd8,0x3b,0x24,0x36,0xbc,0x35,0x6c,0xc4,0x2e,0x77,0x21,0x64,0x32,0xf9,0x89,0xcf,0x70,0x3a,0x68,0x86,0x47,0x95,0xb0,0xd2,0x62,0xbf,0xe0,0x92,0x91,0x20,0x17,0xf1,0x81,0x27,0x17,0x67,0x42,0xf1,0xb9,0x78,0xf5,0xb0,0xdf,0x06,0x9b,0x14,0x46,0x75,0x82,0xb7,0x07,0x93,0x6e,0x01,0xec,0xf5,0xe4,0xa3,0x33,0x1c,0xbf,0x2d,0x91,0xa0,0x2a,0xe0,0x5d,0x4f,0x21,0x5e,0x71,0x95,0xc2,0xa9,0x68,0x0,0x0,0x8d,0x01,0x0b,0xde,0x76,0x15,0xd1,0xe0,0x27,0x86,0xc5,0x1e,0xd6,0xe3,0x65,0x67,0xe9,0xa0,0x83,0xee,0x78,0x20,0x07,0xdb,0xfb,0xcb,0xe3,0x36,0xf8,0x3f,0x99,0xb0,0xde,0xf5,0x14,0xa0,0x27,0xc1,0x3b,0x03,0x49,0xb5,0x0,0x9a,0x82,0x16,0x9c,0x0a,0x9a,0x13,0xd4,0xa8,0x3a,0xec,0x70,0x96,0xa1,0x5a,0xeb,0xc5,0xa9,0x20,0xdd,0x7b,0x42,0x8c,0x8e,0x53,0xd1,0xe0,0x35,0x67,0x31,0xcc,0xa2,0x9c,0xb0,0xf0,0x6f,0x85,0x33,0xec,0x74,0x17,0xe1,0x0b,0xb6,0x0e,0xe8,0x12,0x74,0xdf,0x44,0xd2,0x2c,0x80,0x1e,0x59,0x87,0xf7,0xbd,0xf9,0x09,0x2d,0x53,0xe6,0xec,0xbc,0xe0,0xd0,0xcc,0x4f,0x8c,0x8d,0x5b,0xd5,0x24,0xfc,0xec,0x87,0x57,0x95,0xf0,0x8e,0xbb,0x30,0x61,0x47,0x97,0x92,0x22,0x0,0x41,0x55,0xc0,0x3b,0xee,0x42,0xa8,0x74,0x40,0x8b,0x20,0x86,0xd0,0x15,0xd6,0xe3,0x63,0x5f,0x6e,0xe6,0x0a,0xc0,0x7b,0xbe,0x02,0xf8,0x28,0xd8,0x86,0x20,0x46,0xe4,0x90,0x3f,0x27,0xae,0xbe,0xb1,0xa4,0x09,0x40,0x63,0xd0,0x82,0x73,0x41,0x0a,0xb3,0x25,0x88,0xd1,0x61,0xd8,0xed,0x29,0x8c,0xfb,0x31,0xf8,0x84,0x0a,0x80,0x43,0xd6,0x60,0x9f,0x37,0x8f,0xda,0x96,0x20,0x22,0xc0,0xaf,0x8a,0xd8,0xed,0x2e,0x88,0xab,0x3f,0x20,0x61,0x02,0xa0,0x70,0x86,0x9d,0x9e,0x22,0x0a,0xb5,0x25,0x88,0x28,0x68,0x0f,0x1b,0xe2,0x1a,0xa5,0x9a,0x30,0x01,0x38,0xe8,0xb3,0x0d,0xee,0xc3,0x13,0x04,0x11,0x39,0xfb,0x7d,0x79,0x71,0x1b,0x3b,0x09,0x11,0x80,0xce,0xb0,0x01,0xc7,0x03,0x56,0x6a,0x49,0x82,0x18,0xa7,0xf5,0xbc,0xcb,0x5d,0x18,0x97,0x94,0xe7,0x42,0x22,0x1e,0xfe,0x7d,0x6f,0x1e,0xa5,0x64,0x24,0x88,0x09,0xd0,0xaf,0x68,0xe3,0x92,0x53,0x30,0xee,0x02,0x70,0xd8,0x6f,0x83,0x8b,0x4c,0x7f,0x82,0x98,0x30,0x1f,0xf9,0x6c,0x70,0xc7,0x78,0xfb,0x3c,0xae,0x02,0xd0,0xaf,0x68,0x71,0xc4,0x9f,0x43,0x2d,0x47,0x10,0xb1,0xb2,0xa6,0x3d,0xf9,0xe9,0x23,0x0,0x7b,0x3d,0xf9,0x74,0x55,0x13,0x41,0xc4,0x90,0x8e,0xb0,0x21,0xa6,0xf9,0x0c,0xe3,0x26,0x0,0x27,0x03,0x16,0x3a,0x67,0x4f,0x10,0x71,0x60,0x9f,0x37,0x2f,0x66,0x01,0x42,0x71,0x11,0x80,0xa0,0x2a,0xe0,0xa0,0x3f,0x97,0x5a,0x8a,0x20,0xe2,0x31,0xbe,0xb8,0x18,0xb3,0x04,0x22,0x71,0x11,0x0,0x9d,0xa0,0xc2,0x2a,0xd0,0x9d,0x6c,0x04,0x11,0x2f,0x0a,0xa5,0x60,0x6a,0x2f,0x01,0x2e,0x33,0xf7,0x26,0xf4,0x06,0x16,0x82,0xc8,0x16,0x4a,0x35,0x7e,0x4c,0xd6,0x79,0x52,0x5b,0x0,0x6c,0x62,0x08,0xb3,0xf4,0x4e,0x6a,0x2d,0x82,0x88,0x21,0x1a,0xa8,0x58,0x62,0xee,0x8d,0xd9,0xf7,0xc5,0x75,0x17,0x60,0x8e,0xd1,0x41,0xd7,0x33,0x13,0x44,0x8c,0xc7,0x94,0x49,0x90,0xd3,0x43,0x0,0x04,0x70,0x5c,0x6a,0xea,0xa3,0x8d,0x40,0x82,0x88,0x01,0xb9,0x62,0x08,0x33,0x62,0x7c,0xb1,0x68,0xdc,0x23,0x01,0x4b,0x35,0x7e,0x4c,0xd5,0xbb,0xa9,0xf5,0x08,0x62,0x02,0x88,0x8c,0xe3,0x72,0x4b,0x37,0x84,0x18,0x07,0xd5,0x27,0xe4,0x30,0xd0,0x25,0xc6,0x3e,0x58,0x68,0x57,0x80,0x20,0xc6,0xcd,0x5c,0x83,0x33,0x2e,0xcb,0xe9,0x84,0x08,0x80,0xc4,0x38,0x96,0x9a,0x7b,0x0,0x3a,0x12,0x44,0x10,0x51,0x53,0x20,0x05,0x51,0x67,0x70,0xc4,0x69,0x99,0x9e,0x20,0x8a,0x34,0x41,0xcc,0xa0,0xa5,0x0,0x41,0x44,0x6d,0xfa,0x2f,0x35,0xf7,0xc4,0xcd,0x8f,0x96,0xd0,0x94,0x60,0xf3,0x4c,0x0e,0x58,0x62,0xe8,0xc1,0x24,0x88,0x4c,0x67,0x8e,0x21,0xbe,0x3b,0x69,0x09,0x15,0x0,0x0d,0x54,0x5c,0x61,0xb1,0x53,0x80,0x10,0x41,0x44,0x40,0x89,0x26,0x80,0xd9,0x86,0xf8,0xc6,0xd2,0x24,0x3c,0x2b,0x70,0x9e,0x14,0xc2,0xbc,0x38,0xad,0x67,0x08,0x22,0x53,0xd0,0x09,0x2a,0x96,0x9b,0xbb,0xe3,0x5e,0x4e,0x52,0xee,0x05,0x98,0x65,0x70,0xa0,0x44,0x13,0xa0,0x56,0x26,0x88,0x11,0x58,0x62,0xec,0x81,0x41,0x50,0x32,0x53,0x0,0x0,0x60,0xb9,0xb9,0x1b,0xba,0xcf,0xdc,0xbe,0x4a,0x10,0xc4,0x0,0x53,0x74,0x6e,0x54,0xea,0x7c,0x09,0x29,0x2b,0x69,0x02,0x60,0x10,0x14,0x2c,0x32,0xf5,0x52,0x6b,0x13,0xc4,0x67,0xc8,0x11,0xc3,0x58,0x68,0xea,0x4b,0x58,0x79,0x49,0xbd,0x1e,0x7c,0x92,0xce,0x8b,0x99,0x06,0x17,0xb5,0x3a,0x41,0x0,0x90,0x98,0x8a,0x95,0x16,0x3b,0x34,0x8c,0x67,0x87,0x0,0x0,0xc0,0x02,0x63,0x3f,0x8a,0x34,0x41,0x6a,0x7d,0x82,0xd6,0xfd,0xe6,0x3e,0xe4,0x24,0xf8,0xf0,0x5c,0xd2,0x05,0x40,0x0,0x3f,0xef,0x0f,0x50,0xa8,0x07,0x10,0x59,0x4b,0xad,0xde,0x83,0x49,0x5a,0x4f,0x12,0xc6,0x5f,0x0a,0x60,0x12,0x64,0x2c,0xb5,0xf4,0x82,0x42,0x85,0x89,0x6c,0xc4,0x26,0x86,0xb0,0xc8,0x98,0x1c,0x7f,0x98,0x90,0x2a,0x95,0x50,0xa1,0xf1,0xa5,0xce,0xc3,0x10,0x44,0x02,0x29,0xd3,0x06,0x20,0x31,0x9e,0xdd,0x02,0x40,0x10,0x04,0x09,0x0,0x41,0x10,0x09,0x44,0xa2,0x2a,0x88,0x1e,0x93,0x4e,0x42,0x65,0x9e,0x11,0x15,0xb9,0x46,0x68,0xa5,0x4f,0x35,0x54,0x51,0x39,0x3a,0x9d,0x7e,0x9c,0xed,0xf3,0xa1,0xdf,0x1b,0xa2,0x8a,0x4a,0x10,0x16,0xbd,0x06,0x55,0xf9,0x46,0x94,0xd9,0x0c,0xd0,0x88,0x9f,0xb6,0x87,0xac,0xaa,0xe8,0x70,0x04,0xd0,0xd2,0xeb,0x85,0xcb,0x4f,0xf9,0x28,0x48,0x0,0x26,0xc0,0xd4,0x62,0x0b,0x56,0xce,0x28,0xc2,0x82,0xea,0x5c,0x4c,0x2e,0x34,0x8f,0x79,0x3c,0xb3,0xcb,0x15,0xc0,0xc1,0x96,0x7e,0xec,0x6a,0xec,0xc6,0xfe,0x33,0xfd,0x50,0x39,0x39,0x38,0x63,0x05,0x03,0x50,0x57,0x6e,0xc5,0x8a,0xe9,0x45,0x58,0x50,0x65,0x43,0x55,0xfe,0xd8,0x37,0xe5,0xb4,0xf5,0xfb,0x70,0xe0,0xac,0x03,0xef,0x9c,0xb0,0xe3,0xd0,0x39,0x07,0xb9,0x9b,0x49,0x0,0x22,0xa8,0x1c,0x81,0x61,0xe5,0x8c,0x62,0x7c,0xe5,0xd2,0x0a,0xd4,0x14,0x9a,0xa3,0xfa,0x6c,0x71,0x8e,0x1e,0xd7,0xcf,0x29,0xc5,0xf5,0x73,0x4a,0xd1,0xeb,0x09,0xe2,0xb9,0x83,0xed,0x78,0xfe,0x60,0x1b,0x3c,0x41,0x3a,0x0e,0x3d,0x5e,0xb4,0x92,0x80,0xeb,0x66,0x95,0xe0,0xcb,0x0b,0x2b,0x50,0x9e,0x6b,0x8c,0xea,0xb3,0xe5,0xb9,0x46,0x94,0xe7,0x1a,0x71,0xe3,0xbc,0x32,0x74,0x3a,0x03,0xf8,0xcb,0x87,0xad,0x78,0xe9,0x50,0x07,0x02,0x61,0x85,0x04,0x80,0x18,0xca,0xe2,0x9a,0x7c,0xfc,0xfd,0xd5,0x53,0x50,0x6a,0x35,0x4c,0xf8,0xbb,0xf2,0xcd,0x3a,0xdc,0x71,0xf9,0x64,0x7c,0x6d,0x51,0x25,0x9e,0x7c,0xf7,0x0c,0x9e,0x3b,0xd8,0x06,0x45,0xa5,0x39,0x28,0x1a,0x56,0xce,0x28,0xc2,0xdf,0xad,0x9c,0x82,0x3c,0x93,0x76,0xc2,0xdf,0x55,0x62,0xd5,0xe3,0x5b,0x57,0x4d,0xc1,0x37,0x2e,0xab,0xc2,0xa6,0x77,0x4e,0xe3,0xb5,0x23,0x9d,0x59,0x6b,0x11,0x90,0x0,0x5c,0x84,0x51,0x2b,0xe1,0xee,0x6b,0xa7,0xe2,0xaa,0x99,0xc5,0x71,0xf1,0x1d,0x7c,0xeb,0xaa,0x29,0xb8,0xba,0xae,0x18,0x8f,0xbc,0x70,0x14,0x6d,0xfd,0x7e,0xaa,0xf0,0x31,0xb0,0x19,0xb5,0xb8,0xef,0xf3,0xd3,0xb1,0x68,0x72,0x7e,0x9c,0xbe,0x7b,0x06,0xae,0xa9,0x2b,0xc6,0xbf,0xef,0x38,0x8e,0x5e,0x4f,0xf6,0x45,0xa4,0xd2,0x2e,0xc0,0x45,0x66,0xe2,0xe3,0x6b,0x16,0xc6,0x65,0xf0,0x7f,0x96,0x69,0xc5,0x16,0x3c,0xb6,0xfa,0x52,0x2c,0xa9,0xcd,0xa7,0x4a,0x1f,0xa3,0x9e,0x7e,0xb5,0xf6,0xd2,0xb8,0x0c,0xfe,0xcf,0x32,0xbf,0x2a,0x17,0xbf,0x5a,0x7b,0x29,0x66,0x95,0x5b,0x49,0x0,0xb2,0x95,0xda,0x22,0x33,0x7e,0xf6,0x8d,0xf9,0x28,0xb5,0x19,0x12,0x52,0x9e,0x41,0x2b,0xe2,0x07,0x5f,0x9a,0x8d,0x6b,0xea,0x8a,0xa9,0xf2,0x87,0x61,0x4e,0x85,0x0d,0x8f,0xac,0x9a,0x1f,0x13,0x93,0x3f,0x12,0xac,0x06,0x0d,0x7e,0x72,0xcb,0x5c,0x2c,0x9a,0x9c,0x47,0x02,0x90,0x8d,0x83,0xff,0xff,0xae,0x9a,0x0f,0x9b,0x51,0x9b,0xd0,0x72,0x45,0x81,0xe1,0x7b,0x37,0xcc,0xc4,0xf5,0xb3,0x4b,0xa9,0x11,0x2e,0x1a,0xfc,0x3f,0xbe,0x65,0x2e,0x0c,0x5a,0x31,0xa1,0xe5,0xea,0x34,0x22,0x1e,0xbc,0x79,0x4e,0xdc,0x2d,0x0e,0x12,0x80,0x14,0x22,0xc7,0xa0,0xc1,0x83,0x37,0xcd,0x86,0x49,0x37,0x71,0x77,0x88,0xc7,0xe3,0x81,0xa2,0x44,0xe7,0x55,0x66,0x0,0xee,0xbe,0x76,0x2a,0x66,0x94,0xe6,0xd0,0xc8,0x07,0x50,0x94,0xa3,0xc7,0x0f,0x6f,0x9a,0x75,0x41,0x7c,0x45,0x24,0x70,0xce,0xe1,0x76,0xbb,0xc1,0x27,0xb8,0xdd,0x2a,0x09,0x0c,0x0f,0xdc,0x58,0x87,0x8a,0x3c,0x63,0x56,0xd4,0x77,0x56,0x3b,0x01,0x19,0x80,0xef,0x5e,0x3f,0x1d,0x45,0x39,0xfa,0xa8,0x3f,0xeb,0x74,0x3a,0xf1,0xde,0x7b,0x7b,0xb1,0xe7,0xdd,0x3d,0x68,0x6e,0x6e,0x86,0xdd,0x6e,0x47,0x30,0x18,0x84,0x28,0x8a,0xc8,0xcf,0xcf,0x43,0x79,0x79,0x05,0x16,0x2d,0x5e,0x84,0xa5,0x4b,0x97,0xa2,0xba,0xba,0x6a,0xf4,0x46,0x10,0x05,0xdc,0x7f,0x63,0x1d,0xbe,0xbd,0x75,0x5f,0x56,0x6f,0x13,0x8a,0x02,0xc3,0xbf,0xfc,0xcd,0x4c,0x58,0xf4,0x9a,0x31,0xdf,0x6b,0xb7,0xdb,0xb1,0x67,0xcf,0x1e,0xec,0xd9,0xf3,0x1e,0xce,0x9d,0x3d,0x87,0xee,0xee,0x6e,0xc8,0xb2,0x0c,0x49,0x92,0x50,0x54,0x54,0x84,0xca,0xca,0x4a,0x2c,0x5d,0xba,0x04,0x4b,0x96,0x2e,0x45,0x51,0x51,0x61,0xd4,0xcb,0xb3,0xfb,0xff,0xa6,0x0e,0xf7,0xfe,0xfe,0x43,0x84,0x15,0x95,0x04,0x20,0x53,0xf9,0xdc,0xec,0x12,0x2c,0xa9,0x2d,0x88,0xea,0x33,0x76,0xbb,0x1d,0x5b,0x36,0x6f,0xc5,0xcb,0x2f,0xbf,0x0c,0x55,0x1d,0xda,0x39,0x14,0x45,0x81,0xdd,0xde,0x0d,0xbb,0xbd,0x1b,0x07,0x0e,0x1c,0xc0,0xaf,0x37,0xfe,0x1a,0x75,0xb3,0xea,0x70,0xd7,0x5d,0x77,0x62,0xde,0xbc,0x79,0x23,0x7e,0x6f,0x71,0x8e,0x1e,0x77,0xad,0xac,0xc5,0x2f,0x5e,0x3e,0x91,0xb5,0xed,0xf1,0xd5,0x45,0x95,0xa8,0x2b,0x1b,0xdd,0x11,0x77,0xfa,0x74,0x33,0x36,0x6d,0xda,0x84,0x3d,0xef,0xee,0x19,0xf6,0xdf,0x65,0x59,0x46,0x7b,0x7b,0x3b,0xda,0xdb,0xdb,0xb1,0x77,0xef,0x5e,0xe0,0x97,0xff,0x81,0xe5,0xcb,0x97,0x63,0xdd,0xfa,0x7a,0x4c,0x9e,0x3c,0x39,0xaa,0x65,0xe1,0x6d,0x4b,0xab,0xb1,0x65,0x57,0x33,0x2d,0x01,0x32,0x11,0xa3,0x56,0x42,0xfd,0x15,0x35,0x51,0x99,0x98,0xcf,0x3c,0xf3,0x07,0xac,0x59,0xbd,0x16,0x3b,0x76,0xec,0x18,0x76,0xf0,0x8f,0xc4,0xd1,0x23,0x47,0xf1,0x9d,0x7b,0xbf,0x8b,0x1f,0xfe,0xe0,0x87,0xf0,0x7a,0xbd,0x23,0xbe,0xef,0xba,0x59,0x25,0x98,0x56,0x62,0xc9,0xca,0xf6,0xc8,0x37,0xeb,0xf0,0x8d,0xcb,0xaa,0x47,0xfc,0x77,0x59,0x96,0xf1,0x1f,0xbf,0x7c,0x14,0x77,0xdd,0x79,0xd7,0x88,0x83,0x7f,0x24,0x76,0xef,0xde,0x8d,0x3b,0xd7,0xdf,0x85,0xff,0xfc,0xcf,0xc7,0x20,0xcb,0x91,0x5b,0x58,0xb7,0x2c,0xac,0x8c,0x49,0x1c,0x08,0x09,0x40,0x0a,0x72,0xd3,0x25,0xe5,0xc8,0x8d,0xd0,0xe9,0x17,0x08,0x04,0xf0,0xd0,0x86,0x87,0xf1,0x9b,0x5f,0xff,0x06,0xe1,0xf0,0xf8,0x63,0xca,0x77,0xed,0xda,0x8d,0x6f,0xfe,0xdd,0xb7,0xd0,0xd2,0xd2,0x32,0xfc,0x92,0x84,0x31,0xdc,0xbe,0x7c,0x72,0x56,0xb6,0xc7,0xd7,0x17,0x57,0xc1,0xa0,0x19,0xde,0xe9,0xd7,0xd7,0xd7,0x87,0x7b,0xff,0xf1,0x3b,0xd8,0xbe,0x7d,0xfb,0xb8,0xd7,0xf8,0x9c,0x73,0x3c,0xfb,0xe7,0x67,0x71,0xef,0xbd,0xdf,0x41,0x5f,0x5f,0x64,0x39,0xf7,0xb4,0x92,0x80,0xdb,0x96,0x56,0x93,0x0,0x64,0xdc,0xba,0x47,0x14,0xf0,0xa5,0xf9,0xe5,0x11,0x77,0x9c,0x9f,0xfc,0xf8,0x27,0x78,0xe7,0x9d,0x77,0x62,0x52,0x76,0x7b,0x7b,0x3b,0xbe,0x77,0xdf,0xff,0x46,0x6f,0xef,0xf0,0x09,0x20,0x2e,0x99,0x94,0x87,0x49,0x05,0xa6,0x84,0xd4,0x03,0x63,0xc0,0xd5,0x33,0x8b,0xf1,0xe3,0x5b,0xe6,0xe2,0x7f,0xbe,0xbd,0x1c,0x2f,0xdd,0xb7,0x12,0xcf,0xde,0x7d,0x05,0xfe,0xfd,0xeb,0xf3,0xf1,0xc5,0xf9,0xe5,0x17,0x1c,0xac,0x89,0x27,0x66,0xbd,0x84,0xeb,0x66,0x97,0x0c,0xfb,0x6f,0xc1,0x60,0x10,0xf7,0xff,0xcb,0xfd,0x38,0x7a,0xf4,0x68,0x4c,0xca,0x3a,0x7a,0xe4,0x28,0x1e,0xb8,0xff,0xfb,0x08,0x06,0x23,0x0b,0xfa,0xb9,0x6a,0x46,0x11,0xf2,0xcd,0x3a,0x12,0x80,0x4c,0x62,0x49,0x4d,0x3e,0x72,0x23,0xdc,0x5f,0xfe,0xed,0x6f,0x37,0x61,0xd7,0xae,0xdd,0x31,0x2d,0xbf,0xb7,0xb7,0x17,0x0f,0xdc,0xff,0xc0,0xb0,0x9d,0x90,0x01,0xb8,0x61,0x4e,0xfc,0xb7,0x05,0x0b,0x2c,0x3a,0xfc,0xec,0x1b,0x0b,0xf0,0x4f,0x5f,0x98,0x89,0x85,0x93,0xf2,0x06,0x1d,0x6f,0x06,0xad,0x88,0x39,0x15,0x36,0x7c,0xfb,0x9a,0xa9,0xf8,0xd5,0xda,0x4b,0x31,0x39,0xca,0x33,0x10,0xe3,0xe1,0xaa,0x19,0xc5,0xd0,0x0f,0x33,0xfb,0x73,0x95,0xe3,0x47,0xff,0xfa,0x63,0x34,0x36,0x36,0xc5,0xb4,0xbc,0x93,0x27,0x4f,0xe2,0x27,0x3f,0xfe,0x69,0x44,0xd6,0x84,0x24,0x0a,0xb8,0x76,0x56,0x31,0x09,0x40,0x26,0x71,0xe5,0x8c,0xa2,0x88,0x67,0x8b,0x3f,0x3c,0xf3,0x87,0xb8,0x3c,0x43,0x63,0x63,0x13,0x9e,0x7e,0xfa,0xf7,0xc3,0xfe,0xdb,0x15,0xd3,0x8b,0x10,0xcf,0xdb,0xd3,0xcc,0x3a,0x09,0x3f,0xfd,0xea,0xbc,0x31,0x1d,0x6e,0x95,0x79,0x46,0xfc,0xdb,0xd7,0xe6,0xa1,0xc4,0xaa,0x8f,0x6f,0x7b,0x4c,0x1f,0xbe,0x3d,0x5e,0x79,0xf5,0x55,0xec,0xde,0xbd,0x3b,0x2e,0x65,0xee,0xdc,0xb9,0x13,0x6f,0xbc,0xf1,0x46,0x44,0xef,0x5d,0x31,0xad,0x88,0x04,0x20,0x53,0x60,0x0c,0x98,0x5f,0x65,0x8b,0xc8,0xf4,0x7f,0xec,0xb1,0xc7,0x27,0xbc,0xaf,0x3c,0x1a,0xdb,0xfe,0x7b,0x1b,0xec,0x76,0xfb,0x90,0xd7,0xf3,0x4c,0x5a,0x54,0xc7,0x71,0x19,0xb0,0x7a,0xd9,0x24,0x54,0x46,0xb8,0xcf,0x6d,0x35,0x68,0xf0,0xed,0x6b,0xa6,0xc6,0xed,0x59,0x0c,0x5a,0x11,0x33,0xcb,0x72,0x86,0xf5,0xbb,0xfc,0xf6,0x37,0xbf,0x8d,0x6b,0x5f,0xd8,0xf8,0xc4,0xaf,0x23,0x5a,0x0a,0xd4,0x14,0x99,0x23,0xf6,0x17,0x91,0x0,0xa4,0x38,0x55,0x79,0xa6,0x88,0xf6,0x99,0x3f,0xf8,0x60,0x1f,0x8e,0x1f,0x3f,0x1e,0xd7,0x67,0x09,0x85,0x42,0x23,0x5a,0x18,0xb3,0xe3,0x14,0x97,0xae,0x95,0x84,0xa8,0x23,0x0f,0x2f,0x9d,0x9c,0x1f,0x37,0x6f,0xf8,0xf4,0x92,0x1c,0x88,0xc3,0xdc,0x16,0xfb,0xc2,0x0b,0x2f,0x8e,0xe8,0x27,0x89,0x15,0x3d,0x3d,0x3d,0xd8,0xb1,0xe3,0xa5,0xb1,0x27,0x0d,0x60,0x58,0x91,0x22,0x01,0x48,0x43,0x26,0x15,0x46,0x36,0xb3,0xbe,0xf6,0xea,0xab,0x09,0x79,0x9e,0xd7,0x5f,0x7f,0x63,0xd8,0xe8,0xc1,0x78,0x39,0x02,0x2b,0xf3,0x8c,0x51,0x87,0xd8,0x32,0x0,0xd3,0x4a,0xe3,0xb3,0x3d,0x39,0x79,0x84,0xf6,0x78,0xf5,0x95,0xc4,0xd4,0x7f,0xa4,0xe5,0x24,0xca,0x31,0x4b,0x02,0x10,0x67,0xca,0x22,0x38,0xec,0x23,0xcb,0x32,0xf6,0xec,0x79,0x2f,0x21,0xcf,0xe3,0x76,0xbb,0xf1,0xf1,0xc7,0x1f,0x0f,0x7d,0xce,0xdc,0xf8,0x84,0xa2,0x9a,0xc7,0x19,0xf2,0x1c,0x89,0xd5,0x14,0xab,0xf6,0xb0,0xdb,0xed,0x68,0x6c,0x6c,0x4c,0x48,0xfd,0x1f,0x3f,0x7e,0x1c,0xdd,0xdd,0x63,0xdf,0xc2,0x5b,0x96,0x6b,0x20,0x01,0xc8,0x04,0x22,0x19,0x0,0x67,0xce,0x9c,0x19,0x35,0x60,0x27,0xd6,0x1c,0x3e,0x74,0x78,0x98,0x01,0x17,0x9f,0x20,0x4d,0xc6,0xc6,0xe7,0x5e,0x8c,0x97,0x53,0x72,0xb8,0x33,0x18,0x87,0x0f,0x1f,0x89,0xab,0xef,0xe5,0xb3,0x70,0xce,0x71,0xf4,0xe8,0xb1,0xa4,0x09,0x20,0x09,0x40,0x82,0x89,0xc4,0xfc,0xed,0xe9,0xe9,0x49,0xe8,0x33,0x0d,0x57,0xde,0x48,0x41,0x31,0x19,0xd7,0x1e,0xc3,0xfc,0xce,0x78,0xaf,0xfd,0x87,0xd4,0x7f,0x04,0x16,0x40,0xa6,0xb6,0x47,0xd6,0x09,0x40,0x58,0x19,0x7b,0x66,0xe9,0xed,0xed,0x4b,0x6c,0x07,0x1c,0xa6,0xc3,0xcb,0x59,0x92,0x32,0x6c,0xb8,0xdf,0x99,0x68,0x01,0x88,0xa4,0xbc,0x4c,0x3d,0x14,0x94,0x75,0x02,0x10,0x51,0x12,0xc8,0x44,0x67,0xf0,0x1d,0xa6,0x3c,0x5f,0x48,0xce,0xde,0xf6,0x48,0x70,0xfd,0x47,0x52,0x9a,0x3f,0x43,0x93,0x87,0x66,0x9d,0x0,0xf4,0xb8,0xc7,0xde,0xf7,0x2d,0x28,0x2c,0x48,0xe8,0x33,0x15,0x14,0x0e,0x3d,0xae,0xda,0xed,0xce,0x8e,0xfc,0x74,0xc3,0xfd,0xce,0xfc,0x82,0x04,0xd7,0x7f,0x04,0xe5,0x75,0xbb,0x82,0x24,0x0,0x99,0xc0,0xb9,0x3e,0xdf,0x98,0xef,0xc9,0xcf,0x4f,0x6c,0x46,0x98,0x82,0x61,0xca,0x8b,0xe4,0x39,0x33,0xb5,0x3d,0x12,0x5e,0xff,0x11,0x08,0x40,0x6b,0xbf,0x8f,0x04,0x20,0x13,0x38,0x65,0xf7,0x8c,0xe9,0x61,0xae,0xa9,0xa9,0x81,0xd5,0x9a,0xb8,0x04,0x91,0xf3,0xe6,0x0f,0xcd,0x13,0xd0,0xd4,0xe5,0xc9,0x8a,0xf6,0x68,0xea,0x72,0x0f,0xad,0x8f,0x79,0x73,0xc7,0xbd,0x5b,0x11,0x2d,0x8c,0x31,0xcc,0x9b,0x37,0x77,0xcc,0xf7,0x35,0x0e,0xf3,0x9c,0x24,0x0,0x69,0x88,0x3b,0x10,0xc6,0x29,0xfb,0xe8,0x83,0x4b,0x10,0x04,0x2c,0xbe,0x6c,0x71,0x42,0x9e,0xc7,0x6a,0xb5,0x62,0xf6,0xec,0xd9,0x17,0xbc,0xa6,0x72,0x8e,0x43,0xad,0x8e,0xac,0xb1,0x0,0x2e,0x4e,0xc7,0x9d,0x9f,0x9f,0x8f,0x69,0xd3,0xa7,0x25,0xa4,0xfc,0x99,0x75,0x33,0x61,0xb3,0x8d,0x1e,0x1a,0xee,0x09,0xc8,0x19,0x2b,0xc8,0x59,0x79,0x18,0xe8,0x83,0xe6,0xb1,0xbd,0xfc,0x2b,0x56,0x5c,0x91,0x90,0x67,0xb9,0xfc,0xf2,0xe5,0x10,0x84,0x0b,0x9b,0xe1,0x48,0x9b,0x13,0xde,0x2c,0x4a,0x0d,0x36,0x5c,0x7b,0xac,0x58,0xb1,0x22,0x21,0x65,0x47,0x52,0xce,0xbe,0x33,0x7d,0x19,0x7b,0xb5,0x5b,0x56,0x0a,0xc0,0x4b,0x87,0x3a,0xc6,0xf4,0xfc,0x2e,0x5f,0xbe,0x7c,0xc8,0xcc,0x1c,0x6b,0x74,0x3a,0x1d,0x6e,0xbf,0xe3,0xf6,0x21,0xaf,0xbf,0x72,0xb8,0x33,0xab,0xda,0xe3,0xe5,0xc3,0x1d,0x43,0x5e,0xfb,0xca,0x57,0xbe,0x1c,0x75,0x2e,0xbf,0x68,0x29,0x2a,0x2a,0xc2,0xcd,0x37,0xdf,0x34,0xe6,0xfb,0x5e,0x3d,0x92,0xb9,0xed,0x91,0x95,0x02,0xd0,0xe5,0x0a,0xe0,0xc3,0x33,0x63,0x5b,0x01,0xeb,0xd6,0xd5,0xc7,0xf5,0x39,0x6e,0xba,0xe9,0x4b,0x43,0x1c,0x50,0x2e,0x7f,0x18,0x3b,0x4f,0x76,0x67,0x55,0x7b,0x1c,0x6f,0x77,0x0d,0x59,0x96,0xe9,0x74,0x3a,0xdc,0x76,0xdb,0x6d,0x71,0x2d,0x77,0xed,0xed,0x6b,0xa1,0xd5,0x8e,0x7e,0xca,0xaf,0xad,0xdf,0x8f,0x03,0x2d,0xfd,0x24,0x0,0x99,0xc6,0x1f,0xf6,0x9e,0x1d,0xf3,0x3d,0xf3,0x17,0xcc,0xc7,0x9a,0xb5,0x6b,0xe2,0x52,0x7e,0xdd,0xac,0x3a,0xac,0x5b,0xbf,0x6e,0xc8,0xeb,0xcf,0xee,0x6f,0xcd,0xba,0x0b,0x2b,0x39,0x80,0x6d,0xef,0x0f,0x6d,0x8f,0x2f,0x7e,0xe9,0x8b,0xb8,0xfa,0x9a,0xab,0xe3,0x52,0xe6,0xb5,0xd7,0x5e,0x83,0x1b,0x6e,0xf8,0xfc,0x98,0xef,0xdb,0xf6,0xfe,0xd9,0x8c,0xbe,0xd9,0x39,0x6b,0x05,0xe0,0x50,0xab,0x23,0xa2,0x99,0xf6,0x8e,0xdb,0x6f,0xc7,0xb2,0x65,0xcb,0x62,0x5a,0x76,0x7e,0x7e,0x3e,0x36,0x6c,0x78,0x70,0xc8,0xec,0xd3,0xe1,0xf4,0xe3,0x4f,0xfb,0xcf,0x65,0x65,0x7b,0xbc,0x73,0xc2,0x8e,0x8f,0xce,0x5d,0xe8,0xf8,0x64,0x8c,0xe1,0xbe,0xfb,0xbe,0x8b,0xda,0xda,0xda,0x98,0x96,0x35,0x75,0xea,0x54,0xdc,0xf7,0xbd,0xfb,0xc6,0xdc,0x69,0x38,0xde,0xe1,0xc2,0x2b,0x47,0x3a,0x32,0xba,0xde,0xb3,0xfa,0x62,0x90,0xdf,0xbc,0x7d,0x6a,0xcc,0x88,0x3b,0x26,0x30,0x3c,0xf4,0xf0,0x86,0x88,0xd6,0x8a,0x91,0x30,0x6d,0xda,0x34,0xfc,0xd7,0x13,0xbf,0x1a,0x76,0xef,0x79,0xe3,0x9b,0x4d,0x08,0xc9,0x6a,0x56,0xb6,0x05,0x07,0xf0,0xc4,0x9b,0x4d,0x90,0x2f,0x0a,0xb9,0x35,0x18,0x0c,0xf8,0x8f,0x47,0x7f,0x89,0xa5,0x4b,0x97,0xc4,0xa4,0x9c,0xa5,0xcb,0x96,0xe2,0x17,0xbf,0xfc,0x39,0x74,0xba,0xd1,0xf3,0xfc,0xa9,0x9c,0xe3,0x89,0x37,0x9b,0xc0,0x33,0x3c,0x22,0x3b,0xab,0x05,0xc0,0xee,0x0a,0xe0,0xe7,0x2f,0x8d,0x9d,0x87,0x5f,0x14,0x45,0xdc,0x7d,0xcf,0xdd,0xb8,0xeb,0xae,0x3b,0x21,0x49,0xe3,0x3f,0xa5,0xb7,0x7c,0xf9,0x72,0xfc,0xec,0xe7,0xff,0x6f,0xd8,0xc1,0xbf,0xfd,0xc3,0x56,0xbc,0x77,0xaa,0x37,0x9b,0x9b,0x03,0xcd,0xdd,0x1e,0xfc,0xfa,0xed,0x53,0x43,0x5e,0x37,0x1a,0x8d,0x78,0xe8,0xe1,0x87,0x70,0xd3,0x4d,0x37,0x8d,0xff,0x34,0x23,0x63,0xb8,0xf9,0xcb,0x37,0xe3,0xa1,0x87,0x36,0xc0,0x68,0x1c,0xfb,0xa8,0xf5,0x96,0x5d,0xcd,0x38,0xde,0xe1,0xca,0xf8,0x3a,0xcf,0xfa,0xab,0xc1,0x76,0x35,0x76,0x63,0xfb,0x87,0xad,0x11,0x75,0xa0,0x5b,0x6f,0xbb,0x15,0xbf,0x7f,0xe6,0x69,0xdc,0x78,0xe3,0x8d,0x43,0xb6,0xee,0x46,0x63,0xd6,0xac,0x59,0x78,0xec,0xf1,0xff,0xc4,0xbf,0xfe,0xe8,0x61,0x98,0x4c,0x43,0x13,0x4b,0x1c,0x6b,0x77,0x61,0xd3,0xce,0xd3,0x20,0x80,0xe7,0x0e,0xb4,0xe1,0xed,0x13,0x43,0xd3,0xa4,0x49,0x92,0x84,0x7f,0xbc,0xf7,0x1e,0x34,0x6c,0xfe,0x1d,0xae,0x5c,0x79,0x65,0x54,0xdf,0x79,0xe5,0xca,0x2b,0xb1,0x79,0x4b,0x03,0xee,0xb9,0xe7,0xee,0x88,0x04,0x7c,0xef,0xe9,0x5e,0xfc,0xcf,0x07,0xd9,0xb1,0x14,0x93,0xa8,0xcb,0x01,0x4f,0xbc,0xd5,0x04,0x93,0x5e,0xc2,0xb5,0x75,0x25,0x63,0xbe,0xb7,0xa0,0xa0,0x0,0xdf,0xbd,0xef,0x3b,0xb8,0xf5,0xb6,0x6f,0xe0,0xdd,0xdd,0xef,0xe2,0xdd,0x77,0xf7,0xe0,0xec,0xd9,0xb3,0x70,0x38,0x1c,0x83,0x99,0x7d,0x72,0x72,0x72,0x50,0x5c,0x5c,0x84,0x4b,0x17,0x2d,0xc2,0xb2,0x65,0x4b,0x51,0x37,0xb3,0x0e,0x4c,0x18,0x7e,0xe6,0x3a,0xdd,0xed,0xc1,0x0f,0x9e,0xfd,0x38,0x6b,0x4d,0xff,0xe1,0x96,0x02,0xff,0xbe,0xe3,0x38,0x8c,0x5a,0x71,0xd8,0x4b,0x3a,0xab,0xaa,0xaa,0xf0,0xe0,0x83,0x3f,0xc4,0xa9,0xd5,0xa7,0xb0,0x67,0xcf,0x7b,0x78,0x6f,0xcf,0x7b,0xe8,0xe8,0xe8,0x80,0xc3,0xe1,0x0,0xe7,0x1c,0x8c,0x31,0xd8,0x6c,0x36,0x94,0x96,0x95,0x62,0xe9,0xd2,0xa5,0x58,0xba,0x74,0x09,0x6a,0x6a,0x22,0xbf,0x0,0xe6,0xe3,0x73,0x0e,0xfc,0xf8,0xb9,0x23,0x19,0xed,0xf8,0x23,0x01,0xb8,0xb8,0xd3,0x71,0xe0,0x67,0x3b,0x8e,0xc3,0xe9,0x0b,0xe3,0x96,0x4b,0x2b,0x23,0xfa,0x4c,0x69,0x69,0x29,0x6e,0xf9,0xea,0x2d,0xb8,0xe5,0xab,0xb7,0x9c,0xff,0x0e,0x0e,0x8f,0xc7,0x03,0x83,0xc1,0x10,0xf1,0x32,0xe1,0x40,0x4b,0x3f,0x1e,0xde,0x7e,0x38,0x63,0x4f,0x9a,0x8d,0x17,0x59,0x51,0xf1,0xe0,0xb3,0x87,0x71,0xef,0x75,0xd3,0x47,0xbc,0x2f,0xa0,0xb6,0xb6,0x16,0xb5,0xb5,0xb5,0x58,0xbd,0xfa,0x6f,0x01,0x0c,0x5c,0xc9,0xe6,0xf3,0xf9,0x60,0x32,0x99,0xa2,0xb2,0xce,0x3e,0xcb,0xce,0x93,0xdd,0x78,0xe4,0xc5,0x63,0x43,0xfc,0x10,0x24,0x0,0x59,0x32,0xf3,0xfc,0xf6,0xed,0x53,0xe8,0xf3,0x86,0x50,0x7f,0xf9,0x64,0x48,0x51,0x5e,0x8a,0xc1,0x18,0x83,0xc5,0x12,0x79,0xde,0xbc,0x57,0x8f,0x74,0xe2,0xf1,0xd7,0x1b,0x13,0xbe,0xe5,0x37,0x91,0x9b,0x75,0x12,0x89,0xca,0x39,0x7e,0xf9,0xca,0x09,0xf4,0x79,0x83,0xf8,0xfa,0xe2,0xaa,0x31,0xd7,0xfe,0xa2,0x28,0x46,0x55,0xff,0x17,0xb7,0xfd,0xb3,0xfb,0x5b,0xf1,0xbb,0x77,0x4e,0x65,0x4d,0x1e,0x06,0x12,0x80,0x11,0x3a,0xc2,0x9f,0xf6,0x9d,0xc3,0xa1,0x73,0x0e,0xfc,0xe3,0x75,0xd3,0x51,0x5b,0x14,0xfb,0x4b,0x31,0xfa,0xbd,0x21,0x3c,0xf1,0x56,0x13,0xde,0x3e,0x6e,0x4f,0xca,0x6f,0x74,0xfa,0xc7,0x77,0xb5,0x99,0xc3,0x17,0x4e,0xf8,0xb3,0xaa,0x9c,0x63,0xf3,0xae,0x66,0x1c,0x68,0xe9,0xc7,0x3d,0x9f,0x9b,0x8e,0xf2,0x38,0xe4,0xe5,0xeb,0x72,0x05,0xf0,0xd8,0x6b,0x27,0x23,0x0a,0x0f,0x27,0x01,0xc8,0x12,0x4e,0x76,0xb9,0x71,0xcf,0xd3,0xfb,0xf1,0x85,0xb9,0x65,0xf8,0xda,0xa2,0xca,0x71,0x5d,0x1f,0x7e,0x31,0xfe,0x90,0x82,0x17,0x3e,0x6a,0xc7,0x1f,0xf6,0xb6,0x24,0xf5,0x0a,0xf0,0xb3,0xbd,0x3e,0x38,0xfd,0x61,0x58,0x0d,0x9a,0xa8,0x06,0xe2,0x91,0x36,0x67,0xd2,0x9e,0xf9,0xa3,0x73,0x0e,0x7c,0x6b,0xcb,0x07,0xf8,0xf2,0xc2,0x0a,0x7c,0x65,0x61,0x05,0x6c,0x31,0xc8,0xd1,0xef,0xf2,0x87,0xb1,0xfd,0x40,0x1b,0xfe,0xb8,0xef,0x1c,0x82,0x59,0xbc,0x04,0x23,0x01,0x18,0x01,0x45,0xe5,0x78,0xee,0x60,0x1b,0x76,0x7c,0xdc,0x8e,0xcb,0xa7,0x15,0xe2,0xfa,0xd9,0xa5,0x98,0x5f,0x65,0x8b,0x7a,0x1b,0xaa,0xb9,0xdb,0x83,0x57,0x8f,0x74,0xe2,0x95,0xc3,0x9d,0x49,0x1d,0xf8,0x9f,0x1d,0xcc,0x2f,0x7e,0xd4,0x8e,0x5b,0x97,0x44,0x7e,0xe9,0xe5,0xce,0x93,0xdd,0xe8,0xf7,0x85,0x92,0xfa,0xdc,0x61,0x45,0xc5,0xb6,0xf7,0xcf,0xe2,0x2f,0x1f,0xb6,0xe2,0xaa,0x99,0xc5,0xb8,0x6e,0x56,0x09,0xea,0xca,0xad,0x51,0x27,0x2b,0x3d,0xde,0xe1,0xc2,0xab,0x47,0x3a,0xf1,0xfa,0xd1,0xae,0xac,0x8b,0xb8,0x24,0x01,0x18,0x07,0xb2,0xca,0xf1,0xd6,0x71,0x3b,0xde,0x3a,0x6e,0x47,0xae,0x51,0x8b,0x79,0x55,0x36,0xcc,0xae,0xb0,0xa1,0x2a,0xcf,0x88,0x8a,0x3c,0x23,0xcc,0x3a,0x09,0x5a,0x49,0x80,0xac,0x72,0x78,0x83,0x32,0x3a,0x9d,0x7e,0x9c,0xed,0xf5,0xe1,0x78,0x87,0x0b,0x07,0xcf,0xf6,0xa3,0xad,0xdf,0x9f,0x72,0xbf,0xe9,0x99,0xbd,0x2d,0x98,0x5b,0x69,0xc3,0xac,0x08,0x2e,0x1f,0x69,0x77,0xf8,0xf1,0xab,0xd7,0x1b,0x53,0xe6,0xd9,0x43,0xb2,0x8a,0x97,0x0f,0x75,0xe0,0xe5,0x43,0x1d,0x28,0xb0,0xe8,0x30,0xbf,0x32,0x17,0xb3,0xca,0xad,0xa8,0xca,0x37,0xa2,0x2c,0xd7,0x0,0x93,0x76,0xa0,0x3d,0xc2,0x8a,0x0a,0x6f,0x50,0x46,0x87,0x23,0x80,0xb3,0xbd,0x5e,0x1c,0x6d,0x77,0xe1,0xc0,0xd9,0x7e,0xd8,0x5d,0x01,0xea,0xd4,0x24,0x0,0xe3,0x5c,0xbf,0xfb,0x42,0x83,0x62,0x90,0xce,0x84,0x64,0x15,0x3f,0x7c,0xf6,0x10,0xfe,0xf7,0x0d,0x33,0xb0,0xa4,0xb6,0x60,0xd4,0xd9,0xf2,0x47,0xcf,0x1d,0x19,0xb7,0xdf,0x20,0xde,0xf4,0xb8,0x83,0x78,0xed,0x68,0x27,0x5e,0x3b,0xda,0x49,0x9d,0x93,0x04,0x80,0x88,0x06,0x6f,0x50,0xc6,0x86,0xbf,0x1c,0xc6,0x9c,0x0a,0x1b,0xae,0x9d,0x55,0x8c,0xb9,0x15,0x36,0x58,0xf4,0x1a,0xf8,0x42,0x32,0x8e,0x77,0xb8,0xf0,0xc6,0x31,0x3b,0xf6,0x9e,0xee,0x01,0xe7,0x54,0x57,0x24,0x0,0x44,0xc6,0x72,0xa8,0xd5,0x91,0x35,0xd9,0x87,0x88,0xa1,0x08,0x54,0x05,0x04,0x41,0x02,0x90,0x12,0x24,0x2a,0x11,0x24,0x41,0x10,0x29,0x26,0x0,0x4c,0x10,0x20,0x8a,0x64,0x90,0x10,0xd9,0x87,0x28,0x26,0xef,0xda,0xb1,0x94,0xf1,0x01,0x4c,0x9b,0x36,0x0d,0x4f,0xcd,0x5b,0x88,0x3d,0x4d,0x3d,0x78,0xe7,0x64,0x37,0x3e,0x3a,0xdb,0x9f,0x75,0x61,0x99,0x44,0xf6,0x61,0xd0,0x8a,0x98,0x39,0xa9,0x04,0xe8,0xe8,0xce,0x5e,0x01,0xd0,0xe9,0x74,0x98,0x77,0xc9,0x25,0xd0,0x6a,0x35,0xb8,0x7e,0x4e,0x29,0xae,0x9f,0x53,0x8a,0x60,0x58,0xc1,0x81,0xb3,0x0e,0xec,0x3c,0x69,0xc7,0xee,0xc6,0x1e,0x0a,0xda,0x20,0x32,0x06,0x9b,0x51,0x8b,0x2b,0x67,0x14,0x61,0xc5,0xb4,0x42,0xcc,0x2c,0xcb,0x01,0x38,0xc7,0x73,0xcf,0x9e,0x81,0xdb,0xed,0xce,0x4c,0x01,0x50,0xc1,0x20,0x8c,0x92,0x87,0x77,0xee,0xfc,0xf9,0x43,0xd2,0x63,0xe9,0x34,0x22,0x96,0xd4,0xe6,0x63,0x49,0x6d,0x3e,0xbe,0x75,0x55,0x18,0x2f,0x1f,0xea,0xc4,0xe6,0xdd,0xcd,0x59,0x75,0x52,0x8b,0xc8,0xbc,0x81,0x7f,0xd7,0x95,0xb5,0xb8,0x62,0x5a,0x21,0xb4,0xd2,0x67,0x96,0xbb,0x8c,0x61,0xee,0x82,0x05,0xd8,0xfd,0xce,0x3b,0x99,0xe9,0x03,0x68,0x09,0x1a,0xf1,0x57,0x47,0x39,0x0e,0xfa,0x6c,0x70,0x28,0x17,0x0e,0x74,0x5b,0x6e,0x2e,0xa6,0xcd,0x98,0x31,0xea,0xe7,0x2d,0x7a,0x0d,0xbe,0xba,0xa8,0x12,0x77,0xad,0xa8,0xa1,0x5e,0x44,0xa4,0x2d,0xff,0xe7,0x8b,0xb3,0x70,0x4d,0x5d,0xf1,0x85,0x83,0xff,0x3c,0x93,0x6b,0x6a,0x90,0x37,0xca,0x95,0x68,0xbd,0x8a,0x0e,0x3c,0x5d,0x05,0xc0,0xa1,0x68,0xe1,0x52,0x34,0x38,0xec,0xb7,0xe1,0x79,0x47,0x19,0x9e,0x75,0x54,0xe2,0x03,0x5f,0x3e,0xba,0x65,0x3d,0x16,0x2e,0x5e,0x1c,0xb1,0xf7,0xff,0x73,0xb3,0x4b,0x40,0xfb,0x04,0x44,0x3a,0x52,0x66,0x33,0x60,0x76,0xc5,0xe8,0xa1,0xd7,0x0b,0x16,0x2e,0xbc,0xc0,0x6a,0x6e,0x0d,0x19,0xb1,0xdb,0x53,0x80,0xff,0xe9,0xab,0xc4,0x0e,0x47,0x29,0x7c,0x6a,0xec,0x0d,0x76,0x29,0x51,0x02,0xf0,0x59,0xbc,0x8a,0x88,0x13,0x7e,0x0b,0x4e,0xf8,0x2d,0x38,0xf0,0x7c,0x0b,0x96,0x4e,0xf1,0x62,0xc5,0xb4,0xc2,0x31,0x63,0xd3,0x8d,0x5a,0x09,0x1a,0x49,0xa0,0xec,0x39,0x44,0xda,0x61,0x35,0x8e,0x7d,0xfa,0xb2,0xa8,0xa4,0x14,0x5e,0x73,0x05,0x0e,0x76,0x06,0xd0,0x1e,0x32,0x20,0xc8,0x2f,0xdc,0x1d,0x70,0x2a,0x1a,0x98,0x04,0x39,0x0d,0x05,0x40,0x1e,0xf9,0xc7,0xdb,0x5d,0x01,0x6c,0xff,0xb0,0x15,0xdb,0x3f,0x6c,0x45,0x4d,0xa1,0x19,0x2b,0xa6,0x17,0xe2,0x8a,0x69,0x45,0x71,0x39,0xfb,0xbd,0x70,0x52,0x1e,0xee,0x5c,0x51,0x83,0x7c,0xb3,0x0e,0xed,0x0e,0x3f,0x4e,0xd9,0x3d,0x83,0x7f,0x9a,0x7b,0x3c,0x24,0x2c,0x04,0xcc,0x3a,0x09,0x35,0x45,0x66,0xd4,0x16,0x99,0x51,0x53,0x38,0xf0,0x77,0x91,0x45,0x8f,0x26,0xbb,0x1b,0xff,0xf5,0x46,0x13,0x5a,0x7a,0xbd,0x31,0x2f,0xf3,0x64,0x97,0x1b,0x3b,0x4f,0x74,0x63,0xe7,0x49,0x3b,0x3a,0x9d,0x12,0x80,0xe1,0xf3,0x50,0x38,0x15,0x0d,0xca,0x34,0xfe,0xf4,0x12,0x0,0x0e,0xc0,0xab,0x46,0xb6,0xcf,0x79,0xba,0xdb,0x83,0xd3,0xdd,0x1e,0x6c,0xde,0xd5,0x8c,0x47,0xff,0x76,0x21,0xa6,0x95,0x58,0x62,0xf6,0x1c,0xd5,0xf9,0x26,0x3c,0x78,0xd3,0xec,0xc1,0xf5,0x57,0x8e,0x41,0x83,0x19,0xa5,0x39,0x9f,0x9a,0x5c,0x9c,0xa3,0xb5,0xcf,0x37,0x20,0x08,0xdd,0x1e,0x9c,0xb6,0x7b,0xd0,0x64,0xf7,0xc0,0x95,0xa2,0x07,0x61,0x88,0x89,0x53,0x94,0xa3,0x1f,0x1c,0xe4,0xb5,0x85,0x66,0xd4,0x14,0x99,0x51,0x62,0x1d,0x3e,0xf7,0xc3,0xfc,0xaa,0x5c,0xfc,0xe8,0x96,0xb9,0xf8,0xe6,0xe6,0x0f,0xc6,0x4c,0x25,0x1f,0x0d,0x2f,0x1d,0xea,0xc0,0x2f,0x5f,0x39,0x11,0xd1,0x7b,0xdd,0x8a,0x26,0xe6,0x75,0x10,0x77,0x01,0x08,0x73,0x01,0x18,0xc7,0xca,0x5d,0x89,0x71,0x0c,0xc0,0xb2,0xa9,0x05,0xc3,0x3a,0x5f,0x06,0x9d,0x21,0x8c,0xa1,0x2a,0xdf,0x84,0xaa,0x7c,0x13,0xae,0x9a,0x59,0x3c,0xf8,0x7a,0x8f,0x3b,0x38,0x28,0x08,0x9f,0x88,0x43,0xa7,0xc3,0x0f,0x8a,0x50,0x48,0x1f,0x44,0x81,0xa1,0x32,0xcf,0xf8,0xe9,0x60,0x3f,0xff,0xc7,0xa2,0x8f,0x6e,0x40,0x15,0x5a,0x74,0x98,0x53,0x61,0xc5,0xde,0xd3,0xb1,0x4b,0xdf,0x1e,0x4d,0x3f,0x1f,0x18,0x4b,0xe9,0x26,0x0,0x6a,0x6a,0xb8,0xed,0xf2,0x4d,0xe3,0xcb,0x22,0x53,0x60,0xd1,0xa1,0xc0,0xa2,0xc3,0x65,0x35,0x9f,0x7a,0x68,0x65,0x95,0xa3,0xad,0xdf,0x87,0xc6,0x2e,0x37,0x1a,0xbb,0x3c,0x68,0xea,0x72,0xe3,0xb4,0xdd,0x43,0xc9,0x3d,0x53,0x80,0x5c,0xa3,0x16,0xd3,0x4a,0x2c,0x98,0x5a,0x6c,0xc1,0x94,0x62,0x0b,0xaa,0x0b,0x8c,0x28,0xce,0xd1,0x43,0x88,0x51,0x98,0x79,0xbe,0x59,0x97,0xb4,0xdf,0x16,0xe6,0x2c,0x0d,0x05,0x0,0x62,0xc6,0x75,0x32,0x49,0x60,0xa8,0xce,0x37,0xa1,0x3a,0xdf,0x84,0x6b,0xeb,0x3e,0x15,0x85,0xb3,0xbd,0x5e,0x9c,0xb2,0x0f,0x2c,0x63,0x4e,0x76,0xba,0x71,0xb4,0xcd,0x49,0x96,0x42,0x3c,0xdb,0x41,0x14,0x30,0xaf,0xd2,0x86,0x29,0x9f,0x99,0xd5,0xcb,0x6c,0x33,0x6b,0x97,0xff,0x0,0x0,0x10,0xb2,0x49,0x44,0x41,0x54,0x86,0x8c,0x3d,0x53,0x12,0x52,0xd3,0xd0,0x02,0xc8,0x96,0x03,0xe5,0x92,0xc0,0x50,0x53,0x38,0xe0,0x38,0xfa,0xac,0x73,0xe7,0xe1,0xed,0x87,0xd1,0xe3,0x0e,0xd2,0x68,0x8d,0x31,0xd3,0x4b,0x72,0xf0,0x83,0x2f,0xcd,0x42,0x81,0x45,0x47,0x95,0x31,0x01,0xe2,0x1e,0x07,0x90,0xea,0xe7,0x7b,0x5e,0x39,0xdc,0x89,0x5d,0x8d,0xdd,0xe8,0x70,0xc6,0x3e,0x75,0xd7,0xb4,0x62,0x0b,0xbe,0xb9,0x72,0x0a,0xf5,0xb2,0x38,0xf0,0x4f,0x5f,0x98,0x19,0xf3,0xc1,0xaf,0xa8,0x1c,0x2d,0xbd,0x5e,0xbc,0x71,0xac,0x2b,0xa6,0xeb,0xfc,0x98,0x4d,0x32,0x2c,0xf6,0x93,0x69,0xdc,0x2d,0x0,0x0d,0x52,0x7b,0x6b,0xed,0xa5,0x43,0x1d,0x38,0xda,0xee,0x1c,0x9c,0xc5,0xcb,0x72,0x8d,0x98,0x5a,0x6c,0xc6,0xd4,0xe2,0x81,0x75,0x64,0x6d,0x91,0x19,0x7a,0xcd,0xf8,0x97,0x31,0x63,0x05,0x7f,0x4c,0x84,0xda,0x22,0x33,0xd6,0xaf,0xa8,0x45,0xa9,0x4d,0x9f,0x52,0x75,0xaa,0xaa,0xc0,0x29,0xbb,0x1b,0x1b,0xdf,0x3a,0x85,0x5e,0x4f,0xec,0xad,0x9f,0x3c,0x93,0x76,0xc2,0xdb,0xc4,0x4e,0x7f,0x18,0xc7,0xda,0x5d,0x68,0xea,0x72,0xa3,0xa5,0xd7,0x8b,0x96,0x5e,0x1f,0xda,0xfa,0x7d,0x83,0x4e,0xb9,0x1b,0xe7,0x97,0x5f,0xe0,0xf7,0x49,0x05,0x34,0x42,0x1a,0x0a,0x80,0x56,0x50,0x31,0xb0,0x19,0x98,0xfa,0xeb,0xb2,0x4f,0xd6,0xf1,0x67,0x7b,0xbd,0x78,0xfd,0x68,0xd7,0x40,0xa5,0x8b,0x02,0x26,0x15,0x98,0x2e,0xf0,0x1e,0x4f,0x2e,0x34,0xc3,0x10,0xa1,0x28,0xe8,0xa5,0xf8,0xf8,0x40,0x0a,0x2c,0x3a,0x3c,0xb2,0x6a,0x3e,0xcc,0xba,0xd4,0x4c,0xea,0x54,0x9e,0x6b,0x40,0x75,0x81,0x09,0xff,0xeb,0xc9,0xfd,0x08,0xc7,0xf8,0xfc,0xc6,0x68,0xbb,0x39,0xc3,0xe1,0xf2,0x87,0x07,0x77,0x70,0x3e,0x89,0xfb,0x68,0xed,0xf3,0xa5,0xdd,0xf5,0x5f,0x3a,0xa6,0xa4,0x9f,0x0,0x08,0xe0,0x30,0x89,0x2a,0xbc,0x4a,0x7a,0x3a,0x03,0xc3,0x8a,0x7a,0xde,0xdb,0xef,0x1e,0x32,0x0b,0x4d,0x2d,0xb6,0xa0,0xba,0xc0,0x84,0xaa,0x7c,0x23,0xa6,0x16,0x5b,0x50,0x95,0x6f,0x4a,0x98,0xcc,0xad,0x98,0x56,0x94,0xb2,0x83,0xff,0x13,0xaa,0xf3,0x4d,0x98,0x55,0x6e,0xc5,0xc1,0xb3,0xfd,0x89,0xb1,0x3c,0x38,0xc7,0xb9,0xbe,0x0b,0x77,0x67,0x5a,0x7a,0xbc,0x29,0x91,0x8e,0x3d,0x16,0x98,0x85,0x70,0xfa,0x09,0x0,0x0,0x98,0x58,0x18,0xde,0x0c,0xdb,0x0d,0xe8,0xf3,0x86,0xb0,0xf7,0x74,0xef,0x05,0x6b,0xc5,0x86,0x3b,0x2f,0x43,0xa9,0xd5,0x90,0x90,0xf2,0xf3,0x4c,0xda,0xb4,0xa8,0xa7,0x44,0x3e,0xe7,0x7b,0x4d,0xbd,0x78,0xf8,0xaf,0x87,0x33,0xd6,0xef,0x61,0x11,0x63,0x2f,0x64,0x09,0x71,0xd1,0x59,0xa5,0xd8,0x29,0x57,0x2c,0x6e,0x85,0x21,0x88,0x44,0x63,0x35,0x4c,0xbc,0xdf,0xe6,0x88,0x69,0x6a,0x01,0x14,0x4a,0x01,0x34,0x22,0x36,0x61,0xbd,0x5b,0xef,0x5a,0x82,0x96,0x5e,0x2f,0x76,0x9e,0xe8,0xc6,0xdb,0x27,0xec,0x38,0xd7,0xe7,0xa3,0xde,0xf5,0x19,0xfc,0x61,0x05,0x8a,0x12,0xfd,0xda,0x56,0xaf,0x11,0x20,0x89,0x03,0x07,0xad,0xc6,0x73,0x26,0x42,0x2b,0x09,0x51,0xaf,0xcd,0x33,0x19,0x06,0xa0,0xae,0xdc,0x8a,0x2b,0xa6,0x15,0x62,0xe9,0x94,0x02,0x14,0x4f,0xf0,0x7a,0x39,0x1d,0x53,0xd3,0x57,0x0,0x0a,0xa4,0xd8,0x5e,0x2b,0x55,0x9d,0x6f,0x42,0xf5,0x32,0x13,0x56,0x2f,0x9b,0x34,0x28,0x06,0x7b,0x4f,0xf7,0x0e,0x59,0xa7,0x67,0x23,0x3f,0xfa,0xeb,0x11,0xec,0x3f,0x93,0xf8,0x8b,0x2e,0xbf,0x7a,0x69,0x25,0xee,0xbc,0xb2,0x36,0xab,0xeb,0x5e,0x14,0x18,0xe6,0x56,0xda,0x70,0x59,0x4d,0x3e,0x96,0x4d,0x29,0x88,0xc9,0x9d,0x92,0x9f,0x90,0x2f,0x05,0xe3,0xe2,0x5f,0x4a,0x88,0x0,0x58,0xc4,0x30,0x8c,0x82,0x1c,0xd5,0x79,0xe6,0x7e,0x6f,0x28,0x6a,0x31,0xe8,0x74,0x06,0xf0,0xde,0xa9,0x1e,0xec,0x3c,0xd9,0x4d,0x51,0x78,0x44,0x42,0x90,0x04,0x86,0x85,0x93,0xf2,0x70,0xc5,0xf4,0x42,0x2c,0x9e,0x9c,0x8f,0x1c,0x43,0x74,0xe7,0x0b,0x22,0xdd,0x26,0x2d,0xd1,0xc6,0xe7,0x4a,0xb3,0x84,0x08,0x0,0x03,0x50,0xa9,0xf5,0xe1,0x44,0x20,0x27,0xe2,0xcf,0x3c,0xf7,0x51,0x1b,0x96,0x4c,0xc9,0x8f,0x2a,0x86,0xbb,0xc4,0xaa,0xc7,0xcd,0x97,0x54,0xe0,0xe6,0x4b,0x2a,0x60,0x77,0x05,0xf0,0x6e,0xd3,0x79,0x31,0x68,0x77,0x52,0x4f,0x25,0x62,0x37,0x68,0x44,0x01,0x0b,0xab,0x73,0x71,0xc5,0xf4,0x42,0x5c,0x56,0x93,0x1f,0xf5,0xa1,0xa2,0x4f,0xf0,0x06,0xe5,0xc1,0xed,0xe6,0xb1,0xa8,0xd4,0xfa,0xd2,0x57,0x0,0x30,0x0e,0x01,0x38,0xd0,0xd2,0x8f,0xef,0xff,0xe9,0x63,0xdc,0xbe,0x7c,0x32,0xa6,0x97,0xe6,0x44,0x6d,0xfe,0x14,0xe5,0x7c,0x2a,0x06,0x1d,0x0e,0x7f,0xcc,0x4f,0x17,0x12,0xd9,0xc9,0x0d,0x73,0x4b,0xb1,0x76,0xf9,0xa4,0x09,0x39,0xa3,0x65,0x95,0x63,0xff,0x99,0x3e,0x6c,0x7a,0xe7,0x34,0xba,0x22,0xb8,0xac,0xd4,0x26,0x86,0x60,0x11,0xc2,0xe9,0x2d,0x0,0x45,0x9a,0x20,0x0c,0x82,0x02,0xbf,0x2a,0x46,0x25,0x02,0x07,0x5a,0xfa,0x51,0x60,0xd1,0x61,0xf9,0x94,0x02,0x2c,0x9b,0x5a,0x88,0x39,0x15,0xd6,0xa8,0x4f,0x76,0x95,0xda,0x0c,0xd4,0x73,0x89,0x98,0x30,0xb5,0x78,0x7c,0xce,0xec,0x90,0xac,0x62,0x5f,0x73,0x1f,0x76,0x37,0x0d,0xf8,0xab,0x3c,0x81,0xc8,0xb7,0xf4,0x26,0xeb,0xe2,0xe7,0xe8,0x4e,0x98,0x0,0x08,0xe0,0x98,0xaa,0x77,0xe3,0x63,0x9f,0x2d,0xea,0xcf,0xf6,0xb8,0x83,0xd8,0x7e,0xa0,0x0d,0xdb,0x0f,0xb4,0x41,0xa7,0x11,0x31,0xbf,0xd2,0x86,0x2b,0xa6,0x17,0x62,0xf9,0x94,0x42,0x18,0xb4,0x22,0xf5,0x4a,0x22,0x25,0xe9,0xf7,0x86,0xf0,0xf6,0x09,0x3b,0x76,0x9e,0xec,0xc6,0xb1,0x76,0xd7,0xb8,0x22,0x0f,0x05,0x70,0x4c,0xd1,0xbb,0xd3,0x5f,0x0,0x0,0x60,0x8a,0xce,0x83,0xc3,0x3e,0x2b,0xd4,0x09,0xf8,0x33,0x83,0x61,0x65,0x30,0x0,0xe7,0x51,0xe9,0x24,0x16,0x54,0xe5,0xe2,0xb2,0xda,0x01,0xaf,0x2b,0xc5,0x08,0x10,0xc9,0xa6,0xc7,0x1d,0xc4,0xae,0xc6,0xee,0x41,0xdf,0xd3,0x44,0xa3,0x8d,0x2b,0x75,0xbe,0xb8,0x84,0x0,0x27,0x45,0x0,0x8c,0x82,0x8c,0x2a,0xad,0x0f,0x67,0x42,0xa6,0x98,0x7c,0x5f,0x48,0x56,0x07,0xc5,0xe0,0x89,0x37,0x9b,0xb0,0x70,0x52,0x1e,0x96,0x4d,0x29,0xc0,0x92,0xda,0xc8,0x1d,0x33,0x74,0xcf,0x0,0x31,0x1c,0x8a,0x1a,0x79,0xbf,0xb0,0xbb,0x02,0xd8,0xd3,0xd4,0x83,0x5d,0x8d,0x3d,0x38,0xd2,0xe6,0x8c,0xe9,0x19,0x83,0x99,0x7a,0x57,0x5c,0x7f,0x67,0xc2,0x83,0xc9,0xe7,0x18,0x1d,0x68,0x09,0x99,0x62,0xbe,0x45,0x17,0x92,0x55,0xec,0x69,0xea,0xc1,0x9e,0xa6,0x1e,0x48,0xe7,0xf7,0x63,0x97,0x4f,0x1d,0x08,0xc2,0x18,0x29,0x1c,0xd5,0x17,0x92,0x71,0x36,0xce,0x81,0x44,0xa2,0xc0,0x30,0xb3,0x2c,0x07,0xed,0xfd,0x7e,0x38,0x29,0xbf,0xe0,0x84,0x60,0x0,0xf2,0x2d,0xba,0x71,0xaf,0xc3,0xa3,0xe1,0x64,0xa7,0x7b,0xd4,0x23,0x6c,0x6d,0xfd,0x3e,0xec,0x6a,0xec,0xc1,0xee,0xc6,0x6e,0x34,0x9e,0x7f,0x6f,0xac,0x29,0xd7,0xfa,0x51,0x20,0x05,0x33,0x4b,0x0,0xac,0x62,0x18,0x93,0x74,0x5e,0x34,0x07,0x4d,0x71,0x2b,0x43,0x56,0x39,0x3e,0x6c,0xe9,0xc7,0x87,0x2d,0xfd,0x78,0xec,0xf5,0x93,0xa8,0x2b,0xb3,0x62,0xd9,0x94,0x02,0x2c,0x9f,0x5a,0x38,0x98,0xf4,0x31,0x10,0x56,0xf0,0xb3,0x97,0x4e,0xc4,0xfd,0xca,0x31,0xad,0x24,0xe0,0x17,0xb7,0x5e,0x32,0x68,0x6d,0x74,0x7b,0x82,0xe8,0x74,0x04,0xd0,0xe1,0xf4,0xa3,0xd3,0x19,0x40,0x87,0x63,0xe0,0xef,0x96,0x5e,0x2f,0x65,0x25,0x06,0x60,0xd0,0x88,0xa8,0xc8,0x33,0xa2,0xc4,0xaa,0x47,0xa9,0xcd,0x30,0xf0,0xb7,0xd5,0x80,0x12,0x9b,0x1e,0x45,0x16,0x3d,0x44,0x21,0x31,0xc7,0xad,0x4e,0xd9,0x3d,0xd8,0xb2,0xab,0x19,0x6b,0x97,0x4f,0x1a,0x74,0x3a,0x9f,0xb2,0x7b,0xb0,0xbb,0xb1,0x1b,0xbb,0x1b,0x7b,0xe2,0x92,0x1d,0xf8,0x62,0xb1,0x9b,0x6b,0x70,0xc4,0xfd,0x77,0x26,0xe5,0x38,0xd9,0x7c,0x63,0x3f,0xce,0x86,0x8c,0x50,0x78,0xfc,0x1b,0x93,0x73,0xe0,0x48,0x9b,0x13,0x47,0xda,0x9c,0xf8,0xcd,0xdb,0xa7,0x30,0xa9,0xc0,0x84,0x5c,0x93,0x16,0xa7,0x92,0x90,0xf1,0x57,0x12,0x05,0x94,0x5a,0x0d,0x28,0xb5,0x1a,0xb0,0x0,0xb9,0x17,0x99,0x9c,0x1c,0x76,0x77,0x60,0x58,0x71,0x38,0xd7,0xe7,0xcb,0xa8,0xbb,0x11,0x4d,0x3a,0x09,0x65,0xe7,0x07,0x77,0x75,0xbe,0x09,0xd5,0x05,0xa6,0xc1,0x81,0x6e,0xd6,0xa7,0xce,0x09,0xc7,0x3f,0xec,0x6d,0xc1,0x5b,0xc7,0xbb,0x50,0x6a,0x33,0xa0,0xad,0xdf,0x0f,0xbb,0x2b,0x90,0xb0,0xb2,0x27,0xe9,0xbc,0xc8,0x97,0x82,0x99,0x29,0x0,0x26,0x41,0xc6,0x6c,0x83,0x13,0x1f,0x8d,0x63,0x47,0x60,0xa2,0x9c,0xe9,0xf1,0xe2,0x4c,0x4f,0x7c,0xd4,0x3b,0x18,0x1e,0xff,0x0c,0x2e,0x0a,0x6c,0x44,0x71,0x90,0x15,0x15,0x5d,0xae,0x0,0xda,0x1d,0x01,0xb4,0x3b,0xfc,0x68,0x77,0xf8,0x51,0x91,0x97,0x1e,0x5b,0x9b,0x75,0x65,0x39,0xc8,0x33,0x6b,0x51,0x66,0x33,0x0c,0xfe,0x29,0xb4,0xe8,0xe2,0x92,0xb7,0x2f,0x1e,0x49,0x59,0x3b,0x9d,0x01,0x74,0x3a,0x03,0x09,0xad,0x33,0x89,0xa9,0x58,0x60,0x4c,0x4c,0x38,0x77,0xd2,0xe4,0xb6,0x4e,0xef,0xc4,0xa9,0xa0,0x19,0x1e,0x45,0x42,0xa6,0xb0,0xaf,0xb9,0x0f,0x93,0x0a,0x62,0xbf,0xb4,0x91,0x44,0x01,0xe5,0xb9,0x46,0x94,0xe7,0x1a,0xd3,0xae,0x4e,0x6e,0x9c,0x5f,0x9e,0xb0,0xb2,0x3e,0x68,0xee,0xcd,0x88,0x7e,0x34,0xd7,0xe8,0x84,0x51,0x48,0x8c,0xc5,0x97,0xb4,0xe3,0x5b,0x22,0xe3,0x58,0x6e,0xee,0xc9,0xa8,0xbb,0xfe,0x9e,0xda,0x73,0x06,0xef,0x36,0xf5,0xd0,0x19,0x84,0x04,0x23,0xab,0x1c,0x7f,0xdc,0x77,0x0e,0x6f,0x1f,0xb7,0xa7,0xfd,0x6f,0x29,0x94,0x82,0x98,0xa9,0x4f,0x5c,0xe8,0xba,0x94,0xdc,0x1f,0x1b,0xc0,0x2c,0x83,0x13,0x87,0xfd,0xd6,0x8c,0xe8,0x88,0x81,0xb0,0x82,0x87,0xb7,0x1f,0x46,0x81,0x45,0x87,0x49,0xf9,0x26,0x94,0xda,0x0c,0x28,0xb5,0x19,0x50,0x66,0x1b,0x70,0x68,0x95,0x5a,0x0d,0x74,0x64,0x76,0x02,0x78,0x83,0x32,0x3a,0x1c,0x7e,0x74,0x38,0x07,0x96,0x42,0x9d,0x4e,0x3f,0x3a,0x1c,0x01,0x9c,0xee,0xce,0x8c,0x1b,0x9c,0x24,0xc6,0xb1,0xcc,0x92,0xd8,0x49,0x31,0xe9,0xf6,0xf7,0x5c,0x43,0x3f,0x3a,0xc2,0x7a,0xf4,0xca,0x99,0x93,0xde,0xb9,0xc7,0x1d,0x1c,0x31,0x15,0x78,0xbe,0x59,0x87,0x52,0xdb,0x80,0xc3,0xab,0xec,0xbc,0x40,0x7c,0xf2,0xff,0xd1,0x9e,0x24,0x1b,0xa9,0xec,0x64,0xd0,0xeb,0x8d,0xcd,0x91,0xef,0x5e,0x4f,0x10,0x1d,0xe7,0x1d,0xa1,0xed,0x0e,0xff,0xa0,0x23,0xb4,0xdd,0xe1,0xcf,0xf8,0x6b,0xda,0x16,0x99,0xfa,0xe2,0x16,0xf3,0x9f,0xb2,0x02,0x20,0x30,0xe0,0x4a,0x4b,0x37,0x5e,0x74,0x96,0x22,0xa0,0x66,0x7e,0x58,0x6f,0xaf,0x27,0x88,0x5e,0x4f,0x10,0x87,0x5b,0x87,0x9a,0x79,0x26,0x9d,0x74,0xde,0x52,0xd0,0x0f,0x8a,0x43,0xc9,0xf9,0xff,0x8e,0xc4,0x71,0x76,0xa2,0xd3,0x85,0xb3,0x7d,0xde,0xa4,0xfc,0xae,0xfd,0x67,0xfa,0xd0,0xef,0x0b,0x21,0x77,0x8c,0x68,0xcc,0x01,0x87,0x66,0x10,0x1d,0xce,0x81,0xc1,0xdd,0xe1,0xf0,0xa3,0xdd,0x11,0x40,0xe7,0xf9,0x01,0x9f,0xad,0x5b,0xa1,0xd3,0xf5,0x2e,0xd4,0xea,0x12,0x9f,0xcf,0x22,0x25,0x3c,0x70,0x46,0x41,0xc6,0x95,0x16,0x3b,0x5e,0x75,0x95,0x22,0x9b,0x0f,0xed,0x79,0x83,0x32,0x9a,0xba,0xdc,0x68,0x1a,0x26,0xb1,0x89,0x24,0x0a,0x83,0x5b,0x65,0x65,0x36,0x3d,0x4a,0xac,0x9f,0x2e,0x2f,0x04,0xc6,0x70,0xa4,0xdd,0x89,0xad,0xbb,0xcf,0x24,0xed,0x1e,0x16,0x97,0x3f,0x8c,0xef,0xff,0xf1,0x63,0xac,0x5e,0x36,0x09,0x93,0x0a,0x4c,0xf0,0x87,0x94,0xcf,0x0c,0xf2,0xc0,0xe0,0x6c,0xde,0xed,0x0e,0xa6,0x5d,0x36,0xde,0x78,0x53,0xa2,0x09,0x60,0xa1,0xa9,0x3f,0x29,0x65,0xa7,0x8c,0x0b,0xbe,0x50,0x0a,0x62,0x85,0xb9,0x0b,0x6f,0xbb,0x8b,0xc9,0x89,0x36,0xc2,0xcc,0xd9,0xda,0xe7,0x43,0x6b,0x0a,0xa7,0x40,0x3b,0xdd,0xed,0xc1,0xc3,0xdb,0x0f,0x53,0x63,0x45,0x41,0x9e,0x14,0xc2,0x95,0x96,0x2e,0x08,0x49,0xea,0xf5,0x29,0xe5,0x91,0xaa,0xd0,0xfa,0xb1,0xd8,0xd4,0x4b,0xbd,0x82,0xc8,0x0a,0x4c,0x82,0x8c,0x95,0x96,0x2e,0x68,0x58,0xf2,0xa6,0xbc,0x94,0x73,0x49,0x4f,0xd5,0xbb,0x51,0xa7,0xa7,0x0c,0x3e,0x44,0x66,0xa3,0x63,0x2a,0xae,0xb2,0x74,0x25,0x6c,0xbf,0x3f,0x6d,0x04,0x0,0x0,0x2e,0x31,0xf5,0x63,0x81,0xb1,0x9f,0x7a,0x09,0x91,0x91,0x18,0x04,0x05,0xd7,0x59,0x3b,0x60,0x93,0x92,0xbf,0xab,0x91,0xb2,0x61,0x78,0xb3,0x0c,0x4e,0x30,0x70,0x7c,0xe8,0xcb,0xa3,0x1e,0x43,0x64,0x0c,0x46,0x41,0xc6,0xb5,0x39,0x5d,0x71,0x49,0xf1,0x9d,0x51,0x02,0x0,0x0,0x75,0x06,0x17,0x18,0x80,0xfd,0xbe,0x5c,0x0,0x8c,0x7a,0x0f,0x91,0xd6,0x58,0x44,0x19,0xd7,0x58,0x3a,0x61,0x16,0x53,0xe7,0xaa,0xb2,0x91,0x04,0x40,0x01,0x52,0xe3,0x2e,0xaf,0x99,0x06,0x17,0xac,0x62,0x08,0x3b,0x3d,0xc5,0x08,0x73,0x12,0x01,0x22,0x3d,0x29,0xd5,0xf8,0x71,0xa5,0xa5,0x1b,0x12,0x4b,0x6e,0x9c,0x03,0xe7,0x5c,0x8e,0xc4,0x07,0xe0,0x49,0xa5,0xca,0x2b,0xd3,0x06,0x70,0x8d,0xa5,0x13,0x06,0x41,0xa1,0x9e,0x44,0xa4,0x1d,0xd5,0x3a,0x2f,0x56,0x5a,0xec,0x49,0x1f,0xfc,0x0,0xc0,0x18,0x73,0x8f,0x2d,0x0,0x17,0xbd,0x29,0x15,0x28,0xd0,0x04,0x71,0xa3,0xad,0x1d,0x25,0x9a,0x0,0xf5,0x28,0x22,0x2d,0x10,0x18,0x70,0x99,0xa9,0x07,0x57,0x98,0xbb,0x21,0xb2,0x14,0x89,0x6e,0xe1,0xdc,0x35,0xb6,0x0,0xa8,0x6a,0x4a,0xde,0xb1,0xa5,0x63,0x0a,0xae,0xb6,0x74,0x62,0x9a,0xce,0x45,0xbd,0x8b,0x48,0x69,0xf4,0x82,0x8a,0xab,0x2d,0x9d,0x98,0xaa,0x4f,0x29,0x63,0x1a,0x0c,0x70,0x8f,0xe9,0x03,0xe0,0x8c,0xb9,0x53,0x75,0xb5,0x2d,0x30,0x60,0xb1,0xb9,0x0f,0x95,0x3a,0x3f,0xf6,0x78,0x0a,0xe0,0x53,0x29,0x2d,0x38,0x91,0x5a,0x4c,0xd2,0x7a,0xb0,0xd8,0xdc,0x07,0x2d,0x4b,0xbd,0x73,0x0d,0xea,0x45,0x02,0x20,0x8c,0xa0,0x12,0x5d,0xa9,0x5e,0xc9,0xa5,0x1a,0x3f,0x6e,0xb0,0xb6,0xa3,0x54,0xe3,0xa7,0x1e,0x47,0xa4,0x04,0x22,0xe3,0x58,0x6c,0xea,0xc5,0x72,0x4b,0x4f,0x4a,0x0e,0xfe,0x81,0x67,0x64,0x5d,0x63,0x2f,0x01,0x80,0xe3,0xe9,0x50,0xe1,0x06,0x41,0xc1,0x35,0x39,0x5d,0x58,0x61,0xe9,0x8e,0x6b,0xee,0x74,0x82,0x18,0x8b,0x4a,0xad,0x0f,0x37,0xd9,0x5a,0x31,0x4d,0xef,0x4e,0xe5,0x0d,0x6b,0x55,0x63,0x32,0x9d,0x88,0x64,0x09,0x70,0x82,0xa5,0xd1,0x89,0xad,0x2a,0xad,0x17,0x25,0x36,0x3f,0x0e,0xf8,0xf3,0xd0,0x18,0x30,0x53,0x6f,0x24,0x12,0x86,0x8e,0x29,0xb8,0xcc,0xdc,0x87,0x2a,0xad,0x37,0x1d,0x1e,0xf7,0xdc,0xaa,0x55,0xab,0xfc,0x63,0x0a,0x0,0x14,0xe5,0x04,0x84,0xf4,0xca,0x5c,0xa3,0x15,0x54,0x5c,0x66,0xea,0x41,0xb9,0xc6,0x87,0xfd,0xde,0x3c,0xb8,0x55,0x89,0x7a,0x27,0x11,0x37,0x18,0x80,0x49,0x3a,0x0f,0x2e,0x31,0xf6,0xa7,0xd3,0xf6,0xf4,0x89,0x8b,0x5f,0x18,0x7e,0x94,0x68,0x34,0x27,0xa1,0xa4,0xa7,0x49,0x5d,0xa1,0xf5,0xa1,0x4c,0xeb,0xc7,0xc9,0x80,0x05,0x1f,0xf9,0x72,0x29,0x78,0x88,0x88,0x39,0x85,0x52,0x10,0x8b,0x4c,0xbd,0xc8,0x93,0x42,0xe9,0xf5,0xe0,0x8c,0x9d,0xbc,0xf8,0xa5,0x61,0xa7,0xf9,0xb5,0x6b,0xd7,0xda,0x01,0x9c,0x4c,0xd7,0x06,0x12,0xc0,0x31,0x43,0xef,0xc2,0x8d,0xb6,0x36,0x4c,0xd1,0x79,0x28,0x88,0x98,0x88,0x09,0x26,0x41,0xc6,0x52,0x53,0x0f,0xae,0xb3,0x76,0xa4,0xdf,0xe0,0x07,0xc0,0x54,0x75,0x57,0x64,0x16,0xc0,0x80,0x89,0xf3,0x06,0x07,0xa6,0xa5,0x7b,0x83,0x2d,0x31,0xf7,0x60,0x8e,0xa1,0x1f,0x87,0x03,0xb9,0x68,0x0a,0x98,0x29,0xd9,0x08,0x11,0x35,0x46,0x41,0xc1,0x1c,0x43,0x3f,0x6a,0xf5,0xde,0xa4,0x25,0xee,0x88,0x01,0x5c,0x95,0xa4,0x37,0x23,0x16,0x0,0x15,0x78,0x83,0x01,0xdf,0xca,0x08,0xe5,0x16,0x15,0x5c,0x66,0xea,0xc1,0x54,0x9d,0x0b,0x87,0x02,0x36,0xb4,0x06,0x8d,0x24,0x04,0xc4,0x98,0xe8,0x05,0x15,0x33,0xf5,0x4e,0x4c,0xd7,0xbb,0x53,0x22,0x8c,0x77,0x82,0x1c,0x3a,0x6f,0xd9,0x47,0x26,0x0,0x5c,0x10,0xde,0x60,0xaa,0xaa,0x22,0x45,0x73,0x06,0x8c,0x87,0x3c,0x29,0x84,0x2b,0xcd,0x76,0x38,0x0c,0x5a,0x1c,0xf6,0x59,0x71,0x26,0x64,0x04,0x9d,0x32,0x24,0x2e,0xc6,0x20,0x28,0xa8,0x33,0xb8,0x30,0x55,0xe7,0x82,0xc4,0x32,0x63,0xaa,0xe0,0xc0,0x1b,0xc3,0x2f,0x97,0x47,0xe0,0xf6,0xdb,0x6f,0xef,0x05,0xb0,0x3f,0x13,0x1b,0xd8,0x26,0x86,0x70,0xb9,0xa5,0x1b,0x5f,0xcd,0x6d,0xc5,0x5c,0x83,0x83,0x62,0x08,0x08,0x0,0x40,0x89,0xc6,0x8f,0x95,0x16,0x3b,0x6e,0xc9,0x3d,0x87,0x99,0x7a,0x67,0xc6,0x0c,0xfe,0xf3,0x4b,0xfa,0x97,0x87,0x7b,0x7d,0xd4,0xbd,0x32,0xce,0xd8,0x16,0xc6,0xf9,0xa2,0xcc,0x35,0xf1,0x14,0xcc,0x35,0x3a,0x30,0xd3,0xe0,0xc4,0xa9,0xa0,0x05,0x27,0x03,0x16,0xb8,0x14,0x0d,0x8d,0x84,0x2c,0x42,0x0,0x47,0x85,0xd6,0x8f,0xe9,0x7a,0x27,0x8a,0x35,0xc1,0xcc,0xfc,0x91,0x8c,0xb5,0x9e,0x3a,0x73,0xe6,0x95,0xa8,0x05,0x40,0x2f,0xcb,0xcf,0x04,0x45,0xf1,0x67,0x0,0x74,0x99,0xdc,0x09,0x34,0x6c,0x60,0xd7,0x60,0x86,0xde,0x85,0x5e,0x59,0x8b,0xa6,0x60,0x0e,0x9a,0x83,0x26,0xc8,0xb4,0x85,0x98,0xb1,0xd8,0xc4,0x10,0xa6,0xeb,0x5d,0xa8,0xd6,0xf9,0x52,0x36,0x6c,0x37,0x76,0xf6,0x3f,0x7f,0x66,0xc3,0x86,0x0d,0xea,0x08,0x96,0xc1,0xe8,0x6c,0x6d,0x68,0xf8,0x0b,0x03,0x6e,0xca,0xb6,0x0e,0xe2,0xe7,0x22,0x9a,0x83,0x66,0x9c,0x0e,0x9a,0xe1,0x90,0xc9,0x2a,0xc8,0x04,0x44,0xc6,0x51,0xa9,0xf1,0xa1,0x46,0xef,0x41,0x59,0x16,0x9d,0x21,0x51,0x18,0x9b,0x7d,0xc7,0x1d,0x77,0x1c,0x89,0xda,0x02,0x0,0x0,0x81,0xf3,0xa7,0x38,0x63,0x59,0x27,0x0,0x06,0xa6,0xa0,0x4e,0xef,0x44,0x9d,0xde,0x09,0x87,0xa2,0x45,0x73,0xd0,0x84,0x33,0x41,0x13,0xbc,0x14,0x61,0x98,0x6e,0x6b,0x5f,0x14,0x6b,0x02,0xa8,0xd1,0x79,0x50,0xa9,0xf5,0x26,0x35,0x05,0x77,0x92,0xf8,0x68,0xa4,0xc1,0x1f,0x91,0x0,0x34,0x9d,0x3d,0xfb,0xe7,0x29,0xd5,0xd5,0x47,0x38,0x30,0x2b,0x9b,0xcd,0xc5,0x05,0xc6,0x10,0x16,0x18,0xfb,0xe1,0x54,0x34,0x68,0x0d,0x19,0xd1,0x16,0x36,0xa2,0x3b,0xac,0xa3,0xed,0xc4,0x14,0x5d,0xd2,0x55,0x68,0xbd,0xa8,0xd6,0xfa,0x50,0xa2,0xf1,0x67,0x94,0x33,0x2f,0x6a,0x01,0xe4,0xfc,0xa7,0x63,0x09,0xe4,0x98,0x6c,0x6d,0x68,0xb8,0x83,0x01,0x0d,0xd4,0xb5,0x2e,0xc4,0xa9,0x68,0xd0,0x16,0x32,0xa2,0x3d,0x6c,0x80,0x3d,0xac,0x83,0x4a,0x5b,0x8a,0x49,0xc3,0x28,0xc8,0x28,0xd5,0x06,0x50,0xae,0xf1,0xa3,0x4c,0xe3,0xcf,0x84,0x7d,0xfb,0x58,0x70,0x52,0x67,0x32,0xd5,0xad,0x5a,0xb5,0x4a,0x19,0xb7,0x05,0x0,0x0,0xfe,0x50,0xe8,0x69,0x93,0x56,0xfb,0x20,0x07,0x26,0x51,0x9d,0x7e,0x8a,0x55,0x0c,0xc3,0x6a,0x70,0xa2,0xce,0xe0,0x84,0xcc,0x19,0xba,0xc2,0x7a,0xb4,0xcb,0x46,0x74,0x86,0xf4,0x70,0x29,0x1a,0xb2,0x0e,0xe2,0x39,0xcb,0x43,0x45,0x91,0x36,0x88,0x62,0xc9,0x8f,0x32,0x6d,0x0,0x36,0x31,0x44,0x95,0x32,0x74,0x76,0x7f,0x64,0xb4,0xc1,0x1f,0xb1,0x05,0x0,0x0,0x4f,0x35,0x34,0xdc,0xcd,0x81,0x47,0xa9,0x5a,0x23,0xc3,0xcf,0x45,0x74,0x85,0xf5,0xe8,0x3a,0x6f,0x1d,0x90,0x20,0x4c,0x0c,0x89,0xa9,0x28,0x90,0x42,0x28,0xd1,0x04,0x50,0x24,0xf9,0x51,0xa0,0x09,0xa5,0x73,0x58,0x6e,0x02,0x46,0x3f,0x6b,0xed,0x77,0xb9,0xa6,0xdc,0x73,0xcf,0x3d,0xc1,0x98,0x08,0xc0,0xb6,0x6d,0xdb,0xc4,0xa0,0xd7,0xbb,0x0f,0xc0,0x7c,0xaa,0xdd,0xe8,0x09,0x72,0x01,0xbd,0xb2,0x0e,0xdd,0x61,0x1d,0xba,0x65,0x3d,0xfa,0x64,0x2d,0x42,0x5c,0xa0,0x8a,0x19,0x06,0x91,0x71,0xe4,0x8a,0x21,0x14,0x48,0x41,0xe4,0x49,0x41,0xe4,0x4b,0x21,0x58,0x53,0xe4,0x22,0x8d,0x74,0x81,0x03,0xb7,0xac,0xad,0xaf,0xff,0x73,0x04,0x56,0x42,0xe4,0x3c,0xbd,0x65,0xcb,0x62,0x55,0x55,0xf7,0x20,0x83,0xc2,0x83,0x93,0x89,0x5b,0x95,0xe0,0x90,0xb5,0xe8,0x95,0xb5,0x70,0xc8,0x5a,0x38,0x54,0x2d,0x7c,0x8a,0x98,0x55,0xbe,0x04,0x2d,0x53,0x61,0x11,0x65,0xe4,0x8a,0x41,0xe4,0x4b,0x41,0x14,0x48,0x21,0x58,0xa5,0x30,0xcd,0xee,0x13,0x19,0xfc,0x8c,0x3d,0xbf,0xf6,0x8e,0x3b,0xbe,0x18,0xe1,0x32,0x21,0x3a,0xb6,0x36,0x34,0x6c,0x62,0xc0,0x3a,0xaa,0xe6,0xf8,0x11,0x52,0x05,0x38,0x55,0x2d,0x1c,0xb2,0x04,0x8f,0xaa,0x81,0x5b,0x91,0xe0,0x55,0x35,0x70,0x28,0x1a,0x28,0x69,0x18,0x9c,0x64,0x11,0x64,0x98,0xc4,0x30,0x2c,0xa2,0x02,0xb3,0x10,0x86,0x45,0x94,0x61,0x15,0x43,0x30,0x0b,0x72,0xea,0xa4,0xcb,0xce,0x1c,0x02,0xe7,0xf7,0xfd,0x4f,0x45,0xb4,0xb4,0x8a,0x7a,0x2d,0x26,0xcb,0x0f,0x28,0x1a,0xcd,0x97,0xc0,0x79,0x01,0xd5,0x75,0x9c,0x66,0x45,0x41,0x45,0xa1,0x10,0x40,0xe1,0x45,0xad,0xa3,0x82,0x9d,0x17,0x03,0x09,0x41,0x2e,0x22,0xa0,0x8a,0x08,0xaa,0x02,0x02,0xaa,0x88,0x0,0x1f,0xf8,0xef,0x20,0x17,0xe0,0x57,0xa5,0xb8,0x27,0x42,0x11,0xc0,0xa1,0x13,0x54,0xe8,0x98,0x02,0xbd,0xa0,0x42,0xcf,0x64,0xe8,0x05,0x15,0x5a,0xa6,0x40,0x2f,0x72,0x18,0x98,0x0c,0x1d,0x53,0x61,0x11,0xc3,0x74,0xa1,0x4b,0x62,0x79,0x24,0xd2,0xc1,0x3f,0x2e,0x0b,0xe0,0xfc,0x52,0xe0,0x2a,0x55,0x55,0x5f,0x45,0x8a,0x5c,0x1f,0x46,0x0c,0x45,0x05,0x43,0x50,0x15,0x20,0x73,0x86,0x10,0x17,0x10,0xe6,0x02,0x64,0x2e,0x40,0x86,0x80,0x70,0x84,0x3b,0x64,0x8c,0x01,0x5a,0xc6,0x21,0x41,0x81,0xc4,0x38,0x24,0xc6,0xa1,0x11,0x54,0x68,0xa1,0x42,0x27,0xd0,0x36,0x5b,0x0a,0xf2,0xea,0xa9,0x96,0x96,0xcf,0x8f,0x14,0xf6,0x1b,0x33,0x01,0x0,0x80,0x27,0x1b,0x1a,0x1e,0x06,0xf0,0x03,0xaa,0x73,0x82,0x48,0x09,0xda,0xb9,0x28,0x2e,0x18,0xee,0xcc,0xff,0xe8,0x96,0xdc,0x38,0x39,0xd5,0xd2,0xb2,0x81,0x31,0xf6,0x1a,0xd5,0x3b,0x41,0x24,0x1d,0x85,0x73,0x7e,0x5b,0xb4,0x83,0x7f,0x42,0x16,0x0,0x0,0x3c,0xb3,0x69,0x53,0x99,0x22,0x08,0xbb,0x29,0x40,0x88,0x20,0x92,0xca,0xbf,0xac,0xa9,0xaf,0x7f,0x64,0x3c,0x1f,0x9c,0xd0,0x76,0xde,0xad,0xeb,0xd7,0xb7,0x33,0xce,0xaf,0x01,0xd0,0x41,0x6d,0x40,0x10,0x89,0x87,0x03,0xff,0x36,0xde,0xc1,0x3f,0x61,0x0b,0xe0,0x13,0x9e,0x6a,0x68,0x98,0xc3,0x81,0xb7,0x01,0xe4,0x52,0x93,0x10,0x44,0xc2,0xd8,0xb4,0xfa,0x8e,0x3b,0xee,0x62,0x6c,0xfc,0x7b,0xa9,0x31,0x09,0xe8,0x59,0x5d,0x5f,0x7f,0x08,0xc0,0xd7,0x01,0xf8,0xa8,0x4d,0x08,0x22,0x21,0x3c,0xa7,0x33,0x99,0xfe,0x61,0x22,0x83,0x3f,0x66,0x02,0x0,0x0,0x6b,0xea,0xeb,0x5f,0x65,0xc0,0x12,0x0,0x6d,0xd4,0x36,0x04,0x11,0x57,0x7e,0xa5,0x33,0x99,0xbe,0xbc,0x6a,0xd5,0xaa,0x09,0x9f,0x80,0x8a,0x79,0xb4,0xc8,0xd6,0xad,0x5b,0x27,0x33,0x45,0x79,0x09,0x69,0x7e,0xa7,0x0,0x41,0xa4,0xe6,0x92,0x1f,0xf7,0x4f,0x64,0xcd,0x1f,0x37,0x0b,0xe0,0x13,0xd6,0xae,0x5d,0xdb,0x2c,0xa9,0xea,0x55,0x60,0x6c,0x1f,0xb5,0x17,0x41,0xc4,0x8c,0x10,0x38,0xff,0xfb,0x58,0x0e,0xfe,0xb8,0x08,0x0,0x30,0xb0,0x3b,0x50,0x71,0xe6,0xcc,0x52,0x0,0x0f,0x01,0xa0,0x90,0x31,0x82,0x98,0x18,0x47,0x19,0xe7,0x0b,0xd6,0xac,0x5b,0xb7,0x31,0xd6,0x5f,0x1c,0xf7,0x93,0x25,0x4f,0xfd,0xee,0x77,0x37,0xaa,0x8c,0x6d,0x61,0x40,0x1e,0xb5,0x23,0x41,0x44,0xcd,0x1f,0x42,0xaa,0xfa,0x77,0xeb,0xd7,0xaf,0x77,0xc7,0xe3,0xcb,0x13,0x72,0xb4,0x6c,0xf3,0xe6,0xcd,0xb5,0x22,0xe7,0x8f,0x01,0xf8,0x3c,0xb5,0x27,0x41,0x44,0xb4,0xd8,0xef,0x63,0x9c,0x3f,0xb0,0xba,0xbe,0xfe,0xd7,0x13,0xf5,0xf4,0x27,0x5d,0x0,0x06,0xad,0x81,0xcd,0x9b,0xaf,0x51,0x39,0x7f,0x8c,0x01,0x33,0xa8,0x89,0x09,0x62,0x58,0x14,0xc6,0xf9,0xe3,0x61,0xc6,0x1e,0xac,0xaf,0xaf,0x77,0xc4,0xbb,0xb0,0x84,0x1f,0x2e,0xdf,0xba,0x75,0xab,0x09,0xb2,0x7c,0x3f,0x63,0xec,0x1e,0x0,0x16,0x6a,0x6f,0x82,0x18,0x64,0x37,0x03,0xbe,0xb7,0xba,0xbe,0xfe,0xbd,0x44,0x15,0x98,0xb4,0xec,0x12,0xdb,0x1e,0x7f,0xdc,0x1c,0x34,0x1a,0xd7,0x03,0xf8,0x67,0x0,0xa5,0xd4,0xf6,0x44,0xb6,0x5a,0xfb,0x9c,0xb1,0x17,0x38,0x63,0x3f,0xb9,0xfd,0xf6,0xdb,0xf7,0x24,0xba,0xf0,0xa4,0xa7,0x97,0xd9,0xb6,0x71,0xa3,0x35,0xa0,0xd1,0xfc,0x03,0x13,0x84,0x75,0xe0,0x7c,0x0a,0xf5,0x07,0x22,0x4b,0x08,0x32,0xe0,0x05,0x55,0x55,0x7f,0xb1,0x76,0xfd,0xfa,0x5d,0xc9,0x7a,0x88,0x94,0xca,0x2f,0xb5,0x79,0xf3,0xe6,0x59,0x22,0xe7,0x5f,0x03,0xb0,0x06,0x40,0x0d,0xf5,0x11,0x22,0xc3,0x08,0x73,0xc6,0x5e,0x06,0xe7,0x5b,0xf5,0x26,0xd3,0xf3,0xab,0x56,0xad,0x4a,0xfa,0xfd,0x64,0x29,0x99,0x60,0xee,0xcd,0x0d,0x1b,0xa4,0xd6,0xea,0xea,0xa5,0x9c,0xf3,0x6b,0x19,0x63,0xd7,0x82,0xb1,0xc5,0xe0,0x9c,0xee,0xe4,0x22,0xd2,0x91,0x0e,0x06,0xbc,0xc6,0x19,0x7b,0x4d,0xe6,0xfc,0x95,0xfa,0xfa,0xfa,0xce,0x54,0x7a,0xb8,0xb4,0xc8,0x30,0xf9,0xd4,0x53,0x4f,0xe5,0xa8,0xe1,0xf0,0xe5,0x60,0x6c,0x36,0x38,0x9f,0x2e,0x70,0x3e,0x93,0x33,0x36,0x03,0x74,0xfa,0x90,0x48,0x99,0x91,0xc4,0x64,0x70,0xde,0xcc,0x81,0xa3,0x02,0xe7,0x27,0x38,0x70,0x5c,0x11,0x84,0xf7,0x47,0xbb,0x97,0x8f,0x04,0x20,0x06,0xfe,0x03,0x8f,0x46,0xa3,0xd5,0x01,0x16,0xce,0x98,0x89,0x31,0xa6,0xa5,0x9e,0x48,0x24,0x0a,0x55,0x96,0x9d,0x5c,0xab,0x0d,0x89,0xa2,0xe8,0xd6,0x68,0x34,0xde,0x58,0x1c,0xce,0x21,0x08,0x82,0x20,0x08,0x82,0x20,0x08,0x82,0x88,0x23,0xff,0x1f,0x03,0x13,0x3a,0x12,0x64,0x41,0x40,0x31,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; static const Color boot_splash_bg_color = Color(224/255.0,224/255.0,224/255.0); diff --git a/methods.py b/methods.py index 9608b1b61d..21c2293bf0 100755 --- a/methods.py +++ b/methods.py @@ -1098,6 +1098,8 @@ def update_version(): f.write("#define VERSION_MINOR "+str(version.minor)+"\n") f.write("#define VERSION_REVISION "+str(rev)+"\n") f.write("#define VERSION_STATUS "+str(version.status)+"\n") + import datetime + f.write("#define VERSION_YEAR "+str(datetime.datetime.now().year)+"\n") def parse_cg_file(fname, uniforms, sizes, conditionals): diff --git a/modules/SCsub b/modules/SCsub index d215f72c08..9215bfd48f 100644 --- a/modules/SCsub +++ b/modules/SCsub @@ -19,5 +19,3 @@ for x in env.module_list: lib = env_modules.Library("modules",env.modules_sources)
env.Prepend(LIBS=[lib])
-
-
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub index d20da72b72..403fe68f66 100644 --- a/modules/gdscript/SCsub +++ b/modules/gdscript/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.modules_sources,"*.cpp") Export('env') - - diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index 313fb57d0e..202ab76da0 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -1940,9 +1940,15 @@ void GDParser::_parse_extends(ClassNode *p_class) { p_class->extends_used=true; - //see if inheritance happens from a file tokenizer->advance(); + if (tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_TYPE && tokenizer->get_token_type()==Variant::OBJECT) { + p_class->extends_class.push_back(Variant::get_type_name(Variant::OBJECT)); + tokenizer->advance(); + return; + } + + // see if inheritance happens from a file if (tokenizer->get_token()==GDTokenizer::TK_CONSTANT) { Variant constant = tokenizer->get_token_constant(); diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h index 134279b6d8..04f3dff3de 100644 --- a/modules/gdscript/gd_parser.h +++ b/modules/gdscript/gd_parser.h @@ -276,7 +276,6 @@ public: }; struct NewLineNode : public Node { - int line; NewLineNode() { type=TYPE_NEWLINE; } }; diff --git a/modules/gridmap/SCsub b/modules/gridmap/SCsub index 4cb47e7e67..211a043468 100644 --- a/modules/gridmap/SCsub +++ b/modules/gridmap/SCsub @@ -1,6 +1,3 @@ Import('env') env.add_source_files(env.modules_sources,"*.cpp") - - - diff --git a/platform/android/SCsub b/platform/android/SCsub index 6feeb8b365..834ee58adc 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -62,10 +62,10 @@ pp_baseout.write( manifest ) for x in env.android_source_files: - shutil.copy(x,abspath+"/java/src/com/android/godot") + shutil.copy(x,abspath+"/java/src/com/android/godot") for x in env.android_module_libraries: - shutil.copy(x,abspath+"/java/libs") + shutil.copy(x,abspath+"/java/libs") env_android.SharedLibrary("#bin/libgodot",[android_objects],SHLIBSUFFIX=env["SHLIBSUFFIX"]) diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index f8fc03ec61..1deeb3457a 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -1123,6 +1123,10 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d if (file=="lib/armeabi/libgodot_android.so" && !export_arm) { skip=true; } + + if (file.begins_with("META-INF") && _signed) { + skip=true; + } print_line("ADDING: "+file); diff --git a/platform/android/java/res/drawable/icon.png b/platform/android/java/res/drawable/icon.png Binary files differindex 78757e9035..013632ddf1 100644 --- a/platform/android/java/res/drawable/icon.png +++ b/platform/android/java/res/drawable/icon.png diff --git a/platform/android/java/res/values-fa/strings.xml b/platform/android/java/res/values-fa/strings.xml new file mode 100644 index 0000000000..450f9fe212 --- /dev/null +++ b/platform/android/java/res/values-fa/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-fa</string> + <string name="testuf8">سلام</string> + <string name="text_paused_cellular">آیا می خواهید بر روی اتصال داده همراه دانلود را شروع کنید؟ بر اساس نوع سطح داده شما این ممکن است برای شما هزینه مالی داشته باشد.</string> + <string name="text_paused_cellular_2">اگر نمی خواهید بر روی اتصال داده همراه دانلود را شروع کنید ، دانلود به صورت خودکار در زمان دسترسی به وای-فای شروع می شود.</string> + <string name="text_button_resume_cellular">ادامه دانلود</string> + <string name="text_button_wifi_settings">تنظیمات وای-فای</string> + <string name="text_verifying_download">درحال تایید دانلود</string> + <string name="text_validation_complete">تایید فایل XAPK تکمیل شد. برای خروج تایید کنید.</string> + <string name="text_validation_failed">اعتبارسنجی فایل XAPK ناموق.</string> + <string name="text_button_pause">توقف دانلود</string> + <string name="text_button_resume">ادامه دانلود</string> + <string name="text_button_cancel">انصراف</string> + <string name="text_button_cancel_verify">انصراف از تایید شدن</string> +</resources> diff --git a/platform/android/java/src/com/android/godot/GodotPaymentV3.java b/platform/android/java/src/com/android/godot/GodotPaymentV3.java index 0fd102ac55..0799e1e83d 100644 --- a/platform/android/java/src/com/android/godot/GodotPaymentV3.java +++ b/platform/android/java/src/com/android/godot/GodotPaymentV3.java @@ -27,7 +27,7 @@ public class GodotPaymentV3 extends Godot.SingletonBase { activity.getPaymentsManager().requestPurchase(sku, transactionId); } }); - }; + } /* public string requestPurchasedTicket(){ activity.getPaymentsManager() @@ -42,7 +42,7 @@ public class GodotPaymentV3 extends Godot.SingletonBase { public GodotPaymentV3(Activity p_activity) { - registerClass("GodotPayments", new String[] {"purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases"}); + registerClass("GodotPayments", new String[] {"purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume"}); activity=(Godot) p_activity; } @@ -54,7 +54,6 @@ public class GodotPaymentV3 extends Godot.SingletonBase { activity.getPaymentsManager().consumeUnconsumedPurchases(); } }); - } private String signature; @@ -63,25 +62,26 @@ public class GodotPaymentV3 extends Godot.SingletonBase { } - public void callbackSuccess(String ticket, String signature){ -// Log.d(this.getClass().getName(), "PRE-Send callback to purchase success"); - GodotLib.callobject(purchaseCallbackId, "purchase_success", new Object[]{ticket, signature}); -// Log.d(this.getClass().getName(), "POST-Send callback to purchase success"); + public void callbackSuccess(String ticket, String signature, String sku){ +// Log.d(this.getClass().getName(), "PRE-Send callback to purchase success"); + GodotLib.callobject(purchaseCallbackId, "purchase_success", new Object[]{ticket, signature, sku}); +// Log.d(this.getClass().getName(), "POST-Send callback to purchase success"); } public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku){ -// Log.d(this.getClass().getName(), "PRE-Send callback to consume success"); - GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[]{ticket, signature, sku}); -// Log.d(this.getClass().getName(), "POST-Send callback to consume success"); +// Log.d(this.getClass().getName(), "PRE-Send callback to consume success"); + Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > "+ticket+","+signature+","+sku); + GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[]{ticket, signature, sku}); +// Log.d(this.getClass().getName(), "POST-Send callback to consume success"); } public void callbackSuccessNoUnconsumedPurchases(){ - GodotLib.calldeferred(purchaseCallbackId, "no_validation_required", new Object[]{}); + GodotLib.calldeferred(purchaseCallbackId, "no_validation_required", new Object[]{}); } public void callbackFail(){ - GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[]{}); -// GodotLib.callobject(purchaseCallbackId, "purchase_fail", new Object[]{}); + GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[]{}); +// GodotLib.callobject(purchaseCallbackId, "purchase_fail", new Object[]{}); } public void callbackCancel(){ @@ -89,6 +89,10 @@ public class GodotPaymentV3 extends Godot.SingletonBase { // GodotLib.callobject(purchaseCallbackId, "purchase_cancel", new Object[]{}); } + public void callbackAlreadyOwned(String sku){ + GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[]{sku}); + } + public int getPurchaseCallbackId() { return purchaseCallbackId; } @@ -97,8 +101,6 @@ public class GodotPaymentV3 extends Godot.SingletonBase { this.purchaseCallbackId = purchaseCallbackId; } - - public String getPurchaseValidationUrlPrefix(){ return this.purchaseValidationUrlPrefix ; } @@ -107,12 +109,10 @@ public class GodotPaymentV3 extends Godot.SingletonBase { this.purchaseValidationUrlPrefix = url; } - public String getAccessToken() { return accessToken; } - public void setAccessToken(String accessToken) { this.accessToken = accessToken; } @@ -125,4 +125,30 @@ public class GodotPaymentV3 extends Godot.SingletonBase { return this.transactionId; } + // request purchased items are not consumed + public void requestPurchased(){ + activity.getPaymentsManager().setBaseSingleton(this); + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + activity.getPaymentsManager().requestPurchased(); + } + }); + } + + // callback for requestPurchased() + public void callbackPurchased(String receipt, String signature, String sku){ + GodotLib.calldeferred(purchaseCallbackId, "has_purchased", new Object[]{receipt, signature, sku}); + } + + // consume item automatically after purchase. default is true. + public void setAutoConsume(boolean autoConsume){ + activity.getPaymentsManager().setAutoConsume(autoConsume); + } + + // consume a specific item + public void consume(String sku){ + activity.getPaymentsManager().consume(sku); + } } + diff --git a/platform/android/java/src/com/android/godot/payments/PaymentsManager.java b/platform/android/java/src/com/android/godot/payments/PaymentsManager.java index 5bf86d0b69..189f7108c1 100644 --- a/platform/android/java/src/com/android/godot/payments/PaymentsManager.java +++ b/platform/android/java/src/com/android/godot/payments/PaymentsManager.java @@ -25,10 +25,8 @@ import com.android.vending.billing.IInAppBillingService; public class PaymentsManager { public static final int BILLING_RESPONSE_RESULT_OK = 0; - - public static final int REQUEST_CODE_FOR_PURCHASE = 0x1001; - + private static boolean auto_consume = true; private Activity activity; IInAppBillingService mService; @@ -69,13 +67,12 @@ public class PaymentsManager { } @Override - public void onServiceConnected(ComponentName name, - IBinder service) { - mService = IInAppBillingService.Stub.asInterface(service); + public void onServiceConnected(ComponentName name, IBinder service) { + mService = IInAppBillingService.Stub.asInterface(service); } }; - public void requestPurchase(String sku, String transactionId){ + public void requestPurchase(final String sku, String transactionId){ new PurchaseTask(mService, Godot.getInstance()) { @Override @@ -88,6 +85,12 @@ public class PaymentsManager { protected void canceled() { godotPaymentV3.callbackCancel(); } + + @Override + protected void alreadyOwned() { + godotPaymentV3.callbackAlreadyOwned(sku); + } + }.purchase(sku, transactionId); } @@ -114,26 +117,82 @@ public class PaymentsManager { }.consumeItAll(); } + public void requestPurchased(){ + try{ + PaymentsCache pc = new PaymentsCache(Godot.getInstance()); + +// Log.d("godot", "requestPurchased for " + activity.getPackageName()); + Bundle bundle = mService.getPurchases(3, activity.getPackageName(), "inapp",null); + +/* + for (String key : bundle.keySet()) { + Object value = bundle.get(key); + Log.d("godot", String.format("%s %s (%s)", key, value.toString(), value.getClass().getName())); + } +*/ + + if (bundle.getInt("RESPONSE_CODE") == 0){ + + final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); + final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); + + + if (myPurchases == null || myPurchases.size() == 0){ +// Log.d("godot", "No purchases!"); + godotPaymentV3.callbackPurchased("", "", ""); + return; + } + +// Log.d("godot", "# products are purchased:" + myPurchases.size()); + for (int i=0;i<myPurchases.size();i++) + { + + try{ + String receipt = myPurchases.get(i); + JSONObject inappPurchaseData = new JSONObject(receipt); + String sku = inappPurchaseData.getString("productId"); + String token = inappPurchaseData.getString("purchaseToken"); + String signature = mySignatures.get(i); +// Log.d("godot", "purchased item:" + token + "\n" + receipt); + + pc.setConsumableValue("ticket_signautre", sku, signature); + pc.setConsumableValue("ticket", sku, receipt); + pc.setConsumableFlag("block", sku, true); + pc.setConsumableValue("token", sku, token); + + godotPaymentV3.callbackPurchased(receipt, signature, sku); + } catch (JSONException e) { + } + } + + } + }catch(Exception e){ + Log.d("godot", "Error requesting purchased products:" + e.getClass().getName() + ":" + e.getMessage()); + } + } + public void processPurchaseResponse(int resultCode, Intent data) { new HandlePurchaseTask(activity){ @Override protected void success(final String sku, final String signature, final String ticket) { - godotPaymentV3.callbackSuccess(ticket, signature); - new ConsumeTask(mService, activity) { + godotPaymentV3.callbackSuccess(ticket, signature, sku); + + if (auto_consume){ + new ConsumeTask(mService, activity) { - @Override - protected void success(String ticket) { -// godotPaymentV3.callbackSuccess(""); - } + @Override + protected void success(String ticket) { +// godotPaymentV3.callbackSuccess(""); + } - @Override - protected void error(String message) { - godotPaymentV3.callbackFail(); + @Override + protected void error(String message) { + godotPaymentV3.callbackFail(); - } - }.consume(sku); - + } + }.consume(sku); + } // godotPaymentV3.callbackSuccess(new PaymentsCache(activity).getConsumableValue("ticket", sku),signature); // godotPaymentV3.callbackSuccess(ticket); @@ -151,7 +210,7 @@ public class PaymentsManager { godotPaymentV3.callbackCancel(); } - }.handlePurchaseRequest(resultCode, data); + }.handlePurchaseRequest(resultCode, data); } public void validatePurchase(String purchaseToken, final String sku){ @@ -165,7 +224,7 @@ public class PaymentsManager { @Override protected void success(String ticket) { - godotPaymentV3.callbackSuccess(ticket, null); + godotPaymentV3.callbackSuccess(ticket, null, sku); } @@ -192,11 +251,31 @@ public class PaymentsManager { }.validatePurchase(sku); } + public void setAutoConsume(boolean autoConsume){ + auto_consume = autoConsume; + } + + public void consume(final String sku){ + new ConsumeTask(mService, activity) { + + @Override + protected void success(String ticket) { + godotPaymentV3.callbackSuccessProductMassConsumed(ticket, "", sku); + + } + + @Override + protected void error(String message) { + godotPaymentV3.callbackFail(); + + } + }.consume(sku); + } + private GodotPaymentV3 godotPaymentV3; public void setBaseSingleton(GodotPaymentV3 godotPaymentV3) { this.godotPaymentV3 = godotPaymentV3; - } } diff --git a/platform/android/java/src/com/android/godot/payments/PurchaseTask.java b/platform/android/java/src/com/android/godot/payments/PurchaseTask.java index 75662a442e..c1f9d164a1 100644 --- a/platform/android/java/src/com/android/godot/payments/PurchaseTask.java +++ b/platform/android/java/src/com/android/godot/payments/PurchaseTask.java @@ -62,7 +62,11 @@ abstract public class PurchaseTask { // Log.d("XXX", "Buy intent response code: " + responseCode); if(responseCode == 1 || responseCode == 3 || responseCode == 4){ canceled(); - return ; + return; + } + if(responseCode == 7){ + alreadyOwned(); + return; } @@ -92,6 +96,6 @@ abstract public class PurchaseTask { abstract protected void error(String message); abstract protected void canceled(); - + abstract protected void alreadyOwned(); } diff --git a/platform/bb10/SCsub b/platform/bb10/SCsub index 24f2b5d242..81f6e726e4 100644 --- a/platform/bb10/SCsub +++ b/platform/bb10/SCsub @@ -19,4 +19,3 @@ if env['bb10_lgles_override'] == "yes": prog = None prog = env_bps.Program('#bin/godot', bb10_lib) - diff --git a/platform/bb10/os_bb10.cpp b/platform/bb10/os_bb10.cpp index d89033b1df..c8e7ae561e 100644 --- a/platform/bb10/os_bb10.cpp +++ b/platform/bb10/os_bb10.cpp @@ -195,9 +195,10 @@ void OSBB10::finalize() { // memdelete(debugger_connection_console); //} + memdelete(sample_manager); + audio_server->finish(); memdelete(audio_server); - memdelete(sample_manager); visual_server->finish(); memdelete(visual_server); diff --git a/platform/flash/SCsub b/platform/flash/SCsub index b7aef3b65d..4e5f26d52a 100644 --- a/platform/flash/SCsub +++ b/platform/flash/SCsub @@ -36,5 +36,3 @@ java -jar $ALCHEMY/usr/lib/asc2.jar -md -strict -optimize -AS3 \ -import ../platform/flash/lib/libGL.abc \ ../platform/flash/Console.as """ - - diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp index 1edb23d504..ef483657ca 100644 --- a/platform/haiku/os_haiku.cpp +++ b/platform/haiku/os_haiku.cpp @@ -137,9 +137,10 @@ void OS_Haiku::finalize() { spatial_sound_2d_server->finish(); memdelete(spatial_sound_2d_server); + memdelete(sample_manager); + audio_server->finish(); memdelete(audio_server); - memdelete(sample_manager); visual_server->finish(); memdelete(visual_server); diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm index 647bf1a2d1..e5bd7a96b4 100644 --- a/platform/iphone/app_delegate.mm +++ b/platform/iphone/app_delegate.mm @@ -56,6 +56,8 @@ #import "Appirater.h" #endif +Error _shell_open(String); + Error _shell_open(String p_uri) { NSString* url = [[NSString alloc] initWithUTF8String:p_uri.utf8().get_data()]; diff --git a/platform/iphone/game_center.mm b/platform/iphone/game_center.mm index 79c056776d..4cb7a20a7f 100644 --- a/platform/iphone/game_center.mm +++ b/platform/iphone/game_center.mm @@ -30,8 +30,18 @@ #include "game_center.h" +#ifdef __IPHONE_9_0 + +#import <GameKit/GameKit.h> +extern "C" { + +#else + extern "C" { #import <GameKit/GameKit.h> + +#endif + #import "app_delegate.h" }; diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm index 279fbdafa8..f19e16f3f6 100755 --- a/platform/iphone/gl_view.mm +++ b/platform/iphone/gl_view.mm @@ -54,6 +54,14 @@ static bool video_playing = false; static float video_previous_volume = 0.0f; static CMTime video_current_time; +void _show_keyboard(String); +void _hide_keyboard(); +bool _play_video(String, float, String, String); +bool _is_video_playing(); +void _focus_out_video(); +void _unpause_video(); +void _stop_video(); + void _show_keyboard(String p_existing) { keyboard_text = p_existing; printf("instance on show is %p\n", _instance); @@ -618,7 +626,7 @@ static void clear_touches() { - (void)audioRouteChangeListenerCallback:(NSNotification*)notification { - printf("*********** route changed!%i\n"); + printf("*********** route changed!\n"); NSDictionary *interuptionDict = notification.userInfo; NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; diff --git a/platform/iphone/godot_iphone.cpp b/platform/iphone/godot_iphone.cpp index b7b9b747b4..f0cb929429 100644 --- a/platform/iphone/godot_iphone.cpp +++ b/platform/iphone/godot_iphone.cpp @@ -40,6 +40,8 @@ int add_path(int p_argc, char** p_args); int add_cmdline(int p_argc, char** p_args); }; +int iphone_main(int, int, int, char**); + int iphone_main(int width, int height, int argc, char** argv) { int len = strlen(argv[0]); diff --git a/platform/iphone/icloud.mm b/platform/iphone/icloud.mm index 2dc2f7d9c1..518385992d 100644 --- a/platform/iphone/icloud.mm +++ b/platform/iphone/icloud.mm @@ -30,10 +30,16 @@ #include "icloud.h" +#ifndef __IPHONE_9_0 extern "C" { +#endif + #import <Foundation/Foundation.h> #import "app_delegate.h" + +#ifndef __IPHONE_9_0 }; +#endif ICloud* ICloud::instance = NULL; diff --git a/platform/iphone/sem_iphone.cpp b/platform/iphone/sem_iphone.cpp index 5afaa7b308..36baa40427 100644 --- a/platform/iphone/sem_iphone.cpp +++ b/platform/iphone/sem_iphone.cpp @@ -31,6 +31,11 @@ #include <unistd.h> #include <fcntl.h> +void cgsem_init(cgsem_t*); +void cgsem_post(cgsem_t*); +void cgsem_wait(cgsem_t*); +void cgsem_destroy(cgsem_t*); + void cgsem_init(cgsem_t *cgsem) { int flags, fd, i; diff --git a/platform/iphone/view_controller.mm b/platform/iphone/view_controller.mm index 6a9c3ac9ec..f98fb7c355 100644 --- a/platform/iphone/view_controller.mm +++ b/platform/iphone/view_controller.mm @@ -32,6 +32,9 @@ extern "C" { +int add_path(int, char**); +int add_cmdline(int, char**); + int add_path(int p_argc, char** p_args) { NSString* str = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_path"]; diff --git a/platform/nacl/html/icon_128.png b/platform/nacl/html/icon_128.png Binary files differindex 1793aa7e7a..653669c38d 100644 --- a/platform/nacl/html/icon_128.png +++ b/platform/nacl/html/icon_128.png diff --git a/platform/nacl/html/icon_16.png b/platform/nacl/html/icon_16.png Binary files differindex 09de19e418..9f6678c289 100644 --- a/platform/nacl/html/icon_16.png +++ b/platform/nacl/html/icon_16.png diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp index 75e0878bac..8b831140d6 100644 --- a/platform/server/os_server.cpp +++ b/platform/server/os_server.cpp @@ -107,9 +107,10 @@ void OS_Server::finalize() { // memdelete(debugger_connection_console); //} + memdelete(sample_manager); + audio_server->finish(); memdelete(audio_server); - memdelete(sample_manager); visual_server->finish(); memdelete(visual_server); diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 1ad32e7989..1fa793e2de 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -11,9 +11,15 @@ common_win=[ "stream_peer_winsock.cpp", ] +env.RES('godot_res.rc') +if env["is_mingw"]: + common_win.append("godot_res.o") +else: + common_win.append("godot_res.res") + env.Program('#bin/godot',['godot_win.cpp']+common_win,PROGSUFFIX=env["PROGSUFFIX"]) -# Microsoft Visual Studio Project Generation +# Microsoft Visual Studio Project Generation if (env['vsproj'])=="yes": env.vs_srcs = env.vs_srcs + ["platform/windows/godot_win.cpp"] for x in common_win: diff --git a/platform/windows/detect.py b/platform/windows/detect.py index f0d2a7cc40..fcde14030f 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -88,7 +88,7 @@ import os -import sys +import sys def is_active(): @@ -173,13 +173,30 @@ def get_flags(): ('theora','no'), ] +def build_res_file( target, source, env ): + cmdbase = "" + if (env["bits"] == "32"): + cmdbase = env['mingw_prefix'] + else: + cmdbase = env['mingw_prefix_64'] + CPPPATH = env['CPPPATH'] + cmdbase = cmdbase + 'windres --include-dir . ' + import subprocess + for x in range(len(source)): + cmd = cmdbase + '-i ' + str(source[x]) + ' -o ' + str(target[x]) + try: + out = subprocess.Popen(cmd,shell = True,stderr = subprocess.PIPE).communicate() + if len(out[1]): + return 1 + except: + return 1 + return 0 def configure(env): env.Append(CPPPATH=['#platform/windows']) - - + env['is_mingw']=False if (os.name=="nt" and os.getenv("VSINSTALLDIR")!=None): #build using visual studio env['ENV']['TMP'] = os.environ['TMP'] @@ -203,14 +220,14 @@ def configure(env): env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) elif (env["target"]=="debug_release"): - env.Append(CCFLAGS=['/Zi','/Od']) + env.Append(CCFLAGS=['/Z7','/Od']) env.Append(LINKFLAGS=['/DEBUG']) env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) elif (env["target"]=="debug"): - env.Append(CCFLAGS=['/Zi','/DDEBUG_ENABLED','/DDEBUG_MEMORY_ENABLED','/DD3D_DEBUG_INFO','/Od']) + env.Append(CCFLAGS=['/Z7','/DDEBUG_ENABLED','/DDEBUG_MEMORY_ENABLED','/DD3D_DEBUG_INFO','/Od']) env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) env.Append(LINKFLAGS=['/DEBUG']) @@ -354,7 +371,7 @@ def configure(env): env.Append(LIBS=['mingw32','opengl32', 'dsound', 'ole32', 'd3d9','winmm','gdi32','iphlpapi','shlwapi','wsock32','kernel32']) # if (env["bits"]=="32"): -# # env.Append(LIBS=['gcc_s']) + # env.Append(LIBS=['gcc_s']) # #--with-arch=i686 # env.Append(CPPFLAGS=['-march=i686']) # env.Append(LINKFLAGS=['-march=i686']) @@ -366,6 +383,10 @@ def configure(env): env.Append(CPPFLAGS=['-DMINGW_ENABLED']) env.Append(LINKFLAGS=['-g']) + # resrc + env['is_mingw']=True + env.Append( BUILDERS = { 'RES' : env.Builder(action = build_res_file, suffix = '.o',src_suffix = '.rc') } ) + import methods env.Append( BUILDERS = { 'GLSL120' : env.Builder(action = methods.build_legacygl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) env.Append( BUILDERS = { 'GLSL' : env.Builder(action = methods.build_glsl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) @@ -373,4 +394,3 @@ def configure(env): env.Append( BUILDERS = { 'GLSL120GLES' : env.Builder(action = methods.build_gles2_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) - diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 952f51fdd4..29f21bf227 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -1,6 +1,345 @@ +/*************************************************************************/ +/* export.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + #include "export.h" #include "platform/windows/logo.h" -#include "tools/editor/editor_import_export.h" +#include "os/os.h" +#include "globals.h" +#include "tools/editor/editor_node.h" +#include "tools/pe_bliss/pe_bliss_godot.h" + +/** + @author Masoud BaniHashemian <masoudbh3@gmail.com> +*/ + + +void EditorExportPlatformWindows::store_16(DVector<uint8_t>& vector, uint16_t value) { + const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&value); + int size = vector.size(); + vector.resize( size + 2 ); + DVector<uint8_t>::Write w = vector.write(); + w[size]=bytes[0]; + w[size+1]=bytes[1]; +} +void EditorExportPlatformWindows::store_32(DVector<uint8_t>& vector, uint32_t value) { + const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&value); + int size = vector.size(); + vector.resize( size + 4 ); + DVector<uint8_t>::Write w = vector.write(); + w[size]=bytes[0]; + w[size+1]=bytes[1]; + w[size+2]=bytes[2]; + w[size+3]=bytes[3]; +} + +bool EditorExportPlatformWindows::_set(const StringName& p_name, const Variant& p_value) { + + String n = p_name; + + if (n=="icon/icon_ico") { + + icon_ico=p_value; + } else if (n=="icon/icon_png") { + + icon_png=p_value; + } else if (n=="icon/icon_png16x16") { + + icon16=p_value; + } else if (n=="icon/icon_png32x32") { + + icon32=p_value; + } else if (n=="icon/icon_png48x48") { + + icon48=p_value; + } else if (n=="icon/icon_png64x64") { + + icon64=p_value; + } else if (n=="icon/icon_png128x128") { + + icon128=p_value; + } else if (n=="icon/icon_png256x256") { + + icon256=p_value; + } else if (n=="version_info/version_major") { + + version_major=p_value; + } else if (n=="version_info/version_minor") { + + version_minor=p_value; + } else if (n=="version_info/version_text") { + + version_text=p_value; + } else if (n=="version_info/company_name") { + + company_name=p_value; + } else if (n=="version_info/file_description") { + + file_description=p_value; + } else if (n=="version_info/product_name") { + + product_name=p_value; + } else if (n=="version_info/legal_copyright") { + + legal_copyright=p_value; + } else if (n=="version_info/add_godot_version") { + + set_godot_version=p_value; + } else + return false; + + return true; + +} + +bool EditorExportPlatformWindows::_get(const StringName& p_name,Variant &r_ret) const { + + String n = p_name; + + if (n=="icon/icon_ico") { + + r_ret=icon_ico; + } else if (n=="icon/icon_png") { + + r_ret=icon_png; + } else if (n=="icon/icon_png16x16") { + + r_ret=icon16; + } else if (n=="icon/icon_png32x32") { + + r_ret=icon32; + } else if (n=="icon/icon_png48x48") { + + r_ret=icon48; + } else if (n=="icon/icon_png64x64") { + + r_ret=icon64; + } else if (n=="icon/icon_png128x128") { + + r_ret=icon128; + } else if (n=="icon/icon_png256x256") { + + r_ret=icon256; + } else if (n=="version_info/version_major") { + + r_ret=version_major; + } else if (n=="version_info/version_minor") { + + r_ret=version_minor; + } else if (n=="version_info/version_text") { + + r_ret=version_text; + } else if (n=="version_info/company_name") { + + r_ret=company_name; + } else if (n=="version_info/file_description") { + + r_ret=file_description; + } else if (n=="version_info/product_name") { + + r_ret=product_name; + } else if (n=="version_info/legal_copyright") { + + r_ret=legal_copyright; + } else if (n=="version_info/add_godot_version") { + + r_ret=set_godot_version; + } else + return false; + + return true; + +} + +void EditorExportPlatformWindows::_get_property_list( List<PropertyInfo> *p_list) const { + + p_list->push_back( PropertyInfo( Variant::STRING, "icon/icon_ico",PROPERTY_HINT_FILE,"ico") ); + p_list->push_back( PropertyInfo( Variant::STRING, "icon/icon_png",PROPERTY_HINT_FILE,"png") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "icon/icon_png16x16") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "icon/icon_png32x32") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "icon/icon_png48x48") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "icon/icon_png64x64") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "icon/icon_png128x128") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "icon/icon_png256x256") ); + p_list->push_back( PropertyInfo( Variant::INT, "version_info/version_major", PROPERTY_HINT_RANGE,"0,65535,1")); + p_list->push_back( PropertyInfo( Variant::INT, "version_info/version_minor", PROPERTY_HINT_RANGE,"0,65535,0")); + p_list->push_back( PropertyInfo( Variant::STRING, "version_info/version_text") ); + p_list->push_back( PropertyInfo( Variant::STRING, "version_info/company_name") ); + p_list->push_back( PropertyInfo( Variant::STRING, "version_info/file_description") ); + p_list->push_back( PropertyInfo( Variant::STRING, "version_info/product_name") ); + p_list->push_back( PropertyInfo( Variant::STRING, "version_info/legal_copyright") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "version_info/add_godot_version") ); + +} + +Error EditorExportPlatformWindows::export_project(const String& p_path, bool p_debug, int p_flags) { + + Error err = EditorExportPlatformPC::export_project(p_path, p_debug, p_flags); + if(err != OK) + { + return err; + } + EditorProgress ep("editexe","Edit EXE File",102); + ep.step("Create ico file..",0); + + DVector<uint8_t> icon_content; + if (this->icon_ico!="" && this->icon_ico.ends_with(".ico")) { + FileAccess *f = FileAccess::open(this->icon_ico,FileAccess::READ); + if (f) { + icon_content.resize(f->get_len()); + DVector<uint8_t>::Write write = icon_content.write(); + f->get_buffer(write.ptr(),icon_content.size()); + f->close(); + memdelete(f); + } + } else if (this->icon_png!="" && this->icon_png.ends_with(".png") && (icon16 || icon32 || icon48 || icon64 || icon128 || icon256)) { + #ifdef PNG_ENABLED + Vector<Image> pngs; + Image png; + Error err_png = png.load(this->icon_png); + if (err_png==OK && !png.empty()) { + if(icon256) { + Image icon_256(png); + if(!(png.get_height()==256 && png.get_width()==256)) icon_256.resize(256,256); + pngs.push_back(icon_256); + } + if(icon128) { + Image icon_128(png); + if(!(png.get_height()==128 && png.get_width()==128)) icon_128.resize(128,128); + pngs.push_back(icon_128); + } + if(icon64) { + Image icon_64(png); + if(!(png.get_height()==64 && png.get_width()==64)) icon_64.resize(64,64); + pngs.push_back(icon_64); + } + if(icon48) { + Image icon_48(png); + if(!(png.get_height()==48 && png.get_width()==48)) icon_48.resize(48,48); + pngs.push_back(icon_48); + } + if(icon32) { + Image icon_32(png); + if(!(png.get_height()==32 && png.get_width()==32)) icon_32.resize(32,32); + pngs.push_back(icon_32); + } + if(icon16) { + Image icon_16(png); + if(!(png.get_height()==16 && png.get_width()==16)) icon_16.resize(16,16); + pngs.push_back(icon_16); + } + // create icon according to https://www.daubnet.com/en/file-format-ico + store_16(icon_content,0); //Reserved + store_16(icon_content,1); //Type + store_16(icon_content,pngs.size()); //Count + int offset = 6+pngs.size()*16; + //List of bitmaps + for(int i=0;i<pngs.size();i++) { + int w = pngs[i].get_width(); + int h = pngs[i].get_height(); + icon_content.push_back(w<256?w:0); //width + icon_content.push_back(h<256?h:0); //height + icon_content.push_back(0); //ColorCount = 0 + icon_content.push_back(0); //Reserved + store_16(icon_content,1); //Planes + store_16(icon_content,32); //BitCount (bit per pixel) + int size = 40 + (w * h * 4) + (w * h / 8); + store_32(icon_content,size); //Size of (InfoHeader + ANDbitmap + XORbitmap) + store_32(icon_content,offset); //FileOffset + offset += size; + } + //Write bmp files. + for(int i=0;i<pngs.size();i++) { + int w = pngs[i].get_width(); + int h = pngs[i].get_height(); + store_32(icon_content,40); //Size of InfoHeader structure = 40 + store_32(icon_content,w); //Width + store_32(icon_content,h*2); //Height + store_16(icon_content,1); //Planes + store_16(icon_content,32); //BitCount + store_32(icon_content,0); //Compression + store_32(icon_content,w*h*4); //ImageSize = Size of Image in Bytes + store_32(icon_content,0); //unused = 0 + store_32(icon_content,0); //unused = 0 + store_32(icon_content,0); //unused = 0 + store_32(icon_content,0); //unused = 0 + //XORBitmap + for(int y=h-1;y>=0;y--) { + for(int x=0;x<w;x++) { + store_32(icon_content,pngs[i].get_pixel(x,y).to_32()); + } + } + //ANDBitmap + for(int m=0;m<(w * h / 8);m+=4) store_32(icon_content,0x00000000); // Add empty ANDBitmap , TODO create full ANDBitmap Structure if need. + } + } + #endif + } + + ep.step("Add rsrc..",50); + + String basename = Globals::get_singleton()->get("application/name"); + product_name=product_name.replace("$genname",basename); + String godot_version; + if(set_godot_version) godot_version = String( VERSION_MKSTRING ); + String ret = pe_bliss_add_resrc(p_path.utf8(), version_major, version_minor, + company_name, file_description, legal_copyright, version_text, + product_name, godot_version, icon_content); + if (ret.empty()) { + return OK; + } else { + EditorNode::add_io_error(ret); + return ERR_FILE_CANT_WRITE; + } +} + +EditorExportPlatformWindows::EditorExportPlatformWindows() { + + icon16=true; + icon32=true; + icon48=true; + icon64=true; + icon128=true; + icon256=true; + product_name="$genname"; + company_name="Okam Studio"; + file_description="Created With Godot Engine"; + version_text="1.0"; + OS::Date date = OS::get_singleton()->get_date(); + legal_copyright="Copyright (c) 2007-"; + legal_copyright+=String::num(date.year); + legal_copyright+=" Juan Linietsky, Ariel Manzur"; + version_major=1; + version_minor=0; + set_godot_version=true; +} + + void register_windows_exporter() { @@ -9,7 +348,7 @@ void register_windows_exporter() { logo->create_from_image(img); { - Ref<EditorExportPlatformPC> exporter = Ref<EditorExportPlatformPC>( memnew(EditorExportPlatformPC) ); + Ref<EditorExportPlatformWindows> exporter = Ref<EditorExportPlatformWindows>( memnew(EditorExportPlatformWindows) ); exporter->set_binary_extension("exe"); exporter->set_release_binary32("windows_32_release.exe"); exporter->set_debug_binary32("windows_32_debug.exe"); diff --git a/platform/windows/export/export.h b/platform/windows/export/export.h index de3dc3fa50..2424efc861 100644 --- a/platform/windows/export/export.h +++ b/platform/windows/export/export.h @@ -1,3 +1,37 @@ +#include "tools/editor/editor_import_export.h" + +class EditorExportPlatformWindows : public EditorExportPlatformPC { + OBJ_TYPE( EditorExportPlatformWindows,EditorExportPlatformPC ); + +private: + String icon_ico; + String icon_png; + bool icon16; + bool icon32; + bool icon48; + bool icon64; + bool icon128; + bool icon256; + String company_name; + String file_description; + String product_name; + String legal_copyright; + String version_text; + int version_major; + int version_minor; + bool set_godot_version; + void store_16(DVector<uint8_t>& vector, uint16_t value); ///< store 16 bits uint + void store_32(DVector<uint8_t>& vector, uint32_t value); ///< store 32 bits uint + +protected: + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + +public: + Error export_project(const String& p_path, bool p_debug,int p_flags=0); + EditorExportPlatformWindows(); +}; void register_windows_exporter(); diff --git a/platform/windows/godot.ico b/platform/windows/godot.ico Binary files differnew file mode 100644 index 0000000000..e57ce36529 --- /dev/null +++ b/platform/windows/godot.ico diff --git a/platform/windows/godot_res.rc b/platform/windows/godot_res.rc new file mode 100644 index 0000000000..73f36e5e59 --- /dev/null +++ b/platform/windows/godot_res.rc @@ -0,0 +1,33 @@ +#include "core/version.h" +#ifndef _STR +#define _STR(m_x) #m_x +#define _MKSTR(m_x) _STR(m_x) +#endif + +GODOT_ICON ICON platform/windows/godot.ico + +1 VERSIONINFO +FILEVERSION VERSION_MAJOR,VERSION_MINOR,0 +PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,0 +FILEOS 4 +FILETYPE 1 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Okam Studio" + VALUE "FileDescription", _MKSTR(VERSION_NAME) " Editor (" _MKSTR(VERSION_STATUS) ")" + VALUE "FileVersion", _MKSTR(VERSION_MAJOR) "." _MKSTR(VERSION_MINOR) "."_MKSTR(VERSION_REVISION) + VALUE "ProductName", _MKSTR(VERSION_NAME) + VALUE "Licence", "MIT" + VALUE "LegalCopyright", "Copyright (c) 2007-" _MKSTR(VERSION_YEAR) " Juan Linietsky, Ariel Manzur" + VALUE "Info", "http://www.godotengine.org" + VALUE "ProductVersion", _MKSTR(VERSION_MAJOR) "." _MKSTR(VERSION_MINOR) "."_MKSTR(VERSION_REVISION) + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END
\ No newline at end of file diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 438a5a6903..1fb8e6dbd0 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1350,7 +1350,9 @@ void OS_Windows::finalize() { memdelete(main_loop); main_loop=NULL; - + + memdelete(input); + visual_server->finish(); memdelete(visual_server); #ifdef OPENGL_ENABLED @@ -1373,11 +1375,10 @@ void OS_Windows::finalize() { // memdelete(debugger_connection_console); //} - audio_server->finish(); - memdelete(audio_server); memdelete(sample_manager); - memdelete(input); + audio_server->finish(); + memdelete(audio_server); physics_server->finish(); memdelete(physics_server); @@ -1764,73 +1765,96 @@ bool OS_Windows::is_window_maximized() const{ } -void OS_Windows::print_error(const char* p_function,const char* p_file,int p_line,const char *p_code,const char*p_rationale,ErrorType p_type) { - - HANDLE hCon=GetStdHandle(STD_OUTPUT_HANDLE); - if (!hCon || hCon==INVALID_HANDLE_VALUE) { - if (p_rationale && p_rationale[0]) { +void OS_Windows::print_error(const char* p_function, const char* p_file, int p_line, const char* p_code, const char* p_rationale, ErrorType p_type) { - print("\E[1;31;40mERROR: %s: \E[1;37;40m%s\n",p_function,p_rationale); - print("\E[0;31;40m At: %s:%i.\E[0;0;37m\n",p_file,p_line); + HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE); + if (!hCon || hCon == INVALID_HANDLE_VALUE) { - } else { - print("\E[1;31;40mERROR: %s: \E[1;37;40m%s\n",p_function,p_code); - print("\E[0;31;40m At: %s:%i.\E[0;0;37m\n",p_file,p_line); + const char* err_details; + if (p_rationale && p_rationale[0]) + err_details = p_rationale; + else + err_details = p_code; + switch(p_type) { + case ERR_ERROR: + print("ERROR: %s: %s\n", p_function, err_details); + print(" At: %s:%i\n", p_file, p_line); + break; + case ERR_WARNING: + print("WARNING: %s: %s\n", p_function, err_details); + print(" At: %s:%i\n", p_file, p_line); + break; + case ERR_SCRIPT: + print("SCRIPT ERROR: %s: %s\n", p_function, err_details); + print(" At: %s:%i\n", p_file, p_line); + break; } + } else { CONSOLE_SCREEN_BUFFER_INFO sbi; //original - GetConsoleScreenBufferInfo(hCon,&sbi); - - SetConsoleTextAttribute(hCon,sbi.wAttributes); + GetConsoleScreenBufferInfo(hCon, &sbi); + WORD current_fg = sbi.wAttributes & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); + WORD current_bg = sbi.wAttributes & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY); - - uint32_t basecol=0; + uint32_t basecol = 0; switch(p_type) { case ERR_ERROR: basecol = FOREGROUND_RED; break; - case ERR_WARNING: basecol = FOREGROUND_RED|FOREGROUND_GREEN; break; - case ERR_SCRIPT: basecol = FOREGROUND_GREEN; break; + case ERR_WARNING: basecol = FOREGROUND_RED | FOREGROUND_GREEN; break; + case ERR_SCRIPT: basecol = FOREGROUND_RED | FOREGROUND_BLUE; break; } - if (p_rationale && p_rationale[0]) { - - SetConsoleTextAttribute(hCon,basecol|FOREGROUND_INTENSITY); + basecol |= current_bg; + if (p_rationale && p_rationale[0]) { + SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY); switch(p_type) { case ERR_ERROR: print("ERROR: "); break; case ERR_WARNING: print("WARNING: "); break; case ERR_SCRIPT: print("SCRIPT ERROR: "); break; } - SetConsoleTextAttribute(hCon,FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY); - print(" %s\n",p_rationale); - SetConsoleTextAttribute(hCon,basecol); - print("At: "); - SetConsoleTextAttribute(hCon,FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN); - print(" %s:%i\n",p_file,p_line); + SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY); + print("%s\n", p_rationale); + + SetConsoleTextAttribute(hCon, basecol); + switch (p_type) { + case ERR_ERROR: print(" At: "); break; + case ERR_WARNING: print(" At: "); break; + case ERR_SCRIPT: print(" At: "); break; + } + SetConsoleTextAttribute(hCon, current_fg | current_bg); + print("%s:%i\n", p_file, p_line); } else { - SetConsoleTextAttribute(hCon,basecol|FOREGROUND_INTENSITY); + + SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY); switch(p_type) { - case ERR_ERROR: print("ERROR: %s: ",p_function); break; - case ERR_WARNING: print("WARNING: %s: ",p_function); break; - case ERR_SCRIPT: print("SCRIPT ERROR: %s: ",p_function); break; + case ERR_ERROR: print("ERROR: %s: ", p_function); break; + case ERR_WARNING: print("WARNING: %s: ", p_function); break; + case ERR_SCRIPT: print("SCRIPT ERROR: %s: ", p_function); break; } - SetConsoleTextAttribute(hCon,FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY); - print(" %s\n",p_code); - SetConsoleTextAttribute(hCon,basecol); - print("At: "); - SetConsoleTextAttribute(hCon,FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN); - print(" %s:%i\n",p_file,p_line); + + SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY); + print("%s\n", p_code); + + SetConsoleTextAttribute(hCon, basecol); + switch (p_type) { + case ERR_ERROR: print(" At: "); break; + case ERR_WARNING: print(" At: "); break; + case ERR_SCRIPT: print(" At: "); break; + } + + SetConsoleTextAttribute(hCon, current_fg | current_bg); + print("%s:%i\n", p_file, p_line); } - SetConsoleTextAttribute(hCon,sbi.wAttributes); + SetConsoleTextAttribute(hCon, sbi.wAttributes); } - } diff --git a/platform/winrt/os_winrt.cpp b/platform/winrt/os_winrt.cpp index 3e06d9d59b..24be2f47e7 100644 --- a/platform/winrt/os_winrt.cpp +++ b/platform/winrt/os_winrt.cpp @@ -326,10 +326,11 @@ void OSWinrt::finalize() { //if (debugger_connection_console) { // memdelete(debugger_connection_console); //} + + memdelete(sample_manager); audio_server->finish(); memdelete(audio_server); - memdelete(sample_manager); memdelete(input); @@ -422,17 +423,27 @@ void OSWinrt::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) con } -void OSWinrt::print_error(const char* p_function,const char* p_file,int p_line,const char *p_code,const char*p_rationale,ErrorType p_type) { - - if (p_rationale && p_rationale[0]) { - - print("\E[1;31;40mERROR: %s: \E[1;37;40m%s\n",p_function,p_rationale); - print("\E[0;31;40m At: %s:%i.\E[0;0;37m\n",p_file,p_line); +void OSWinrt::print_error(const char* p_function, const char* p_file, int p_line, const char* p_code, const char* p_rationale, ErrorType p_type) { - } else { - print("\E[1;31;40mERROR: %s: \E[1;37;40m%s\n",p_function,p_code); - print("\E[0;31;40m At: %s:%i.\E[0;0;37m\n",p_file,p_line); + const char* err_details; + if (p_rationale && p_rationale[0]) + err_details = p_rationale; + else + err_details = p_code; + switch(p_type) { + case ERR_ERROR: + print("ERROR: %s: %s\n", p_function, err_details); + print(" At: %s:%i\n", p_file, p_line); + break; + case ERR_WARNING: + print("WARNING: %s: %s\n", p_function, err_details); + print(" At: %s:%i\n", p_file, p_line); + break; + case ERR_SCRIPT: + print("SCRIPT ERROR: %s: %s\n", p_function, err_details); + print(" At: %s:%i\n", p_file, p_line); + break; } } diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 9a52a7c92b..6b1347a3cc 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -1,6 +1,7 @@ import os import sys +import platform def is_active(): @@ -143,10 +144,14 @@ def configure(env): - - env.Append(CPPFLAGS=['-DOPENGL_ENABLED','-DGLEW_ENABLED']) - env.Append(CPPFLAGS=["-DALSA_ENABLED"]) + env.Append(CPPFLAGS=['-DOPENGL_ENABLED','-DGLEW_ENABLED']) + if platform.platform() == 'Linux': + env.Append(CPPFLAGS=["-DALSA_ENABLED"]) + env.Append(LIBS=['asound']) + if env['ao'] == 'yes': + env.ParseConfig('pkg-config ao --cflags --libs') + env.Append(CPPFLAGS=['-DAO_ENABLED']) if (env["pulseaudio"]=="yes"): if not os.system("pkg-config --exists libpulse-simple"): print("Enabling PulseAudio") @@ -156,7 +161,7 @@ def configure(env): print("PulseAudio development libraries not found, disabling driver") env.Append(CPPFLAGS=['-DX11_ENABLED','-DUNIX_ENABLED','-DGLES2_ENABLED','-DGLES_OVER_GL']) - env.Append(LIBS=['GL', 'GLU', 'pthread','asound','z']) #TODO detect linux/BSD! + env.Append(LIBS=['GL', 'GLU', 'pthread', 'z']) #env.Append(CPPFLAGS=['-DMPC_FIXED_POINT']) #host compiler is default.. diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 34bd1184a1..bd60cf480e 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -32,6 +32,7 @@ #include "key_mapping_x11.h" #include <stdio.h> #include <stdlib.h> +#include <string.h> #include "print_string.h" #include "servers/physics/physics_server_sw.h" @@ -452,9 +453,10 @@ void OS_X11::finalize() { // memdelete(debugger_connection_console); //} + memdelete(sample_manager); + audio_server->finish(); memdelete(audio_server); - memdelete(sample_manager); visual_server->finish(); memdelete(visual_server); @@ -1671,7 +1673,7 @@ void OS_X11::close_joystick(int p_id) { }; void OS_X11::probe_joystick(int p_id) { - #ifndef __FreeBSD__ + #if !defined(__FreeBSD__) && !defined(__OpenBSD__) if (p_id == -1) { @@ -1726,7 +1728,7 @@ void OS_X11::move_window_to_foreground() { } void OS_X11::process_joysticks() { - #ifndef __FreeBSD__ + #if !defined(__FreeBSD__) && !defined(__OpenBSD__) int bytes; js_event events[32]; InputEvent ievent; @@ -1959,6 +1961,10 @@ OS_X11::OS_X11() { AudioDriverManagerSW::add_driver(&driver_alsa); #endif +#ifdef AO_ENABLED + AudioDriverManagerSW::add_driver(&driver_ao); +#endif + minimized = false; xim_style=0L; mouse_mode=MOUSE_MODE_VISIBLE; diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index ed61df8f0e..a4b3d580d1 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -43,6 +43,7 @@ #include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" #include "drivers/rtaudio/audio_driver_rtaudio.h" #include "drivers/alsa/audio_driver_alsa.h" +#include "drivers/ao/audio_driver_ao.h" #include "drivers/pulseaudio/audio_driver_pulseaudio.h" #include "servers/physics_2d/physics_2d_server_sw.h" #include "servers/physics_2d/physics_2d_server_wrap_mt.h" @@ -134,6 +135,10 @@ class OS_X11 : public OS_Unix { AudioDriverALSA driver_alsa; #endif +#ifdef AO_ENABLED + AudioDriverAO driver_ao; +#endif + #ifdef PULSEAUDIO_ENABLED AudioDriverPulseAudio driver_pulseaudio; #endif diff --git a/platform/x11/platform_config.h b/platform/x11/platform_config.h index c01d0aa380..1556b56058 100644 --- a/platform/x11/platform_config.h +++ b/platform/x11/platform_config.h @@ -29,7 +29,7 @@ #ifdef __linux__ #include <alloca.h> #endif -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__OpenBSD__) #include <stdlib.h> #endif diff --git a/scene/2d/SCsub b/scene/2d/SCsub index 055d2f2474..bbe59b3054 100644 --- a/scene/2d/SCsub +++ b/scene/2d/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources,"*.cpp") Export('env') - - diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index 342b86b4c1..458246671c 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -165,7 +165,8 @@ void AnimatedSprite::_notification(int p_what) { if (vflip) dst_rect.size.y=-dst_rect.size.y; - texture->draw_rect(ci,dst_rect,false,modulate); + //texture->draw_rect(ci,dst_rect,false,modulate); + texture->draw_rect_region(ci,dst_rect,Rect2(Vector2(),texture->get_size()),modulate); // VisualServer::get_singleton()->canvas_item_add_texture_rect_region(ci,dst_rect,texture->get_rid(),src_rect,modulate); } break; diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 49683da226..52ae5d2954 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -57,7 +57,9 @@ void Camera2D::_update_scroll() { void Camera2D::set_zoom(const Vector2 &p_zoom) { zoom = p_zoom; + Point2 old_smoothed_camera_pos = smoothed_camera_pos; _update_scroll(); + smoothed_camera_pos = old_smoothed_camera_pos; }; Vector2 Camera2D::get_zoom() const { diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 6141b6a09e..52b112f090 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -354,7 +354,7 @@ void Node2D::look_at(const Vector2& p_pos) { float Node2D::get_angle_to(const Vector2& p_pos) const { - return (get_global_transform().affine_inverse().xform(p_pos)).atan2(); + return (get_global_transform().affine_inverse().xform(p_pos)).angle(); } void Node2D::_bind_methods() { diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 7ba1bb28b6..8f110b3931 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -118,7 +118,7 @@ void PathFollow2D::_update_transform() { pos+=n*h_offset; pos+=t*v_offset; - set_rot(t.atan2()); + set_rot(t.angle()); } else { diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp index acc4c620e6..4a199e3418 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -131,7 +131,7 @@ void RayCast2D::_notification(int p_what) { if (!get_tree()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) break; Matrix32 xf; - xf.rotate(cast_to.atan2()); + xf.rotate(cast_to.angle()); xf.translate(Vector2(0,cast_to.length())); //Vector2 tip = Vector2(0,s->get_length()); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 418ee192b2..167b637bdc 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1031,13 +1031,12 @@ Vector2 TileMap::world_to_map(const Vector2& p_pos) const{ switch(half_offset) { case HALF_OFFSET_X: { - if (int(ret.y)&1) { - + if ( ret.y > 0 ? int(ret.y)&1 : (int(ret.y)-1)&1 ) { ret.x-=0.5; } } break; case HALF_OFFSET_Y: { - if (int(ret.x)&1) { + if ( ret.x > 0 ? int(ret.x)&1 : (int(ret.x)-1)&1) { ret.y-=0.5; } } break; diff --git a/scene/3d/SCsub b/scene/3d/SCsub index 3c2144bedc..116e641593 100644 --- a/scene/3d/SCsub +++ b/scene/3d/SCsub @@ -4,10 +4,8 @@ Import('env') if (env["disable_3d"]=="yes"): env.scene_sources.append("3d/spatial.cpp") - env.scene_sources.append("3d/skeleton.cpp") + env.scene_sources.append("3d/skeleton.cpp") else: env.add_source_files(env.scene_sources,"*.cpp") Export('env') - - diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index 4712ba308a..cb81228dff 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -250,7 +250,7 @@ void Skeleton::_notification(int p_what) { ERR_CONTINUE(!obj); Spatial *sp = obj->cast_to<Spatial>(); ERR_CONTINUE(!sp); - sp->set_transform(b.pose_global * b.rest_global_inverse); + sp->set_transform(b.pose_global); } } diff --git a/scene/SCsub b/scene/SCsub index 8c4f0499c4..6d1dd0044f 100644 --- a/scene/SCsub +++ b/scene/SCsub @@ -18,5 +18,3 @@ SConscript('io/SCsub'); lib = env.Library("scene",env.scene_sources) env.Prepend(LIBS=[lib]) - - diff --git a/scene/animation/SCsub b/scene/animation/SCsub index 055d2f2474..bbe59b3054 100644 --- a/scene/animation/SCsub +++ b/scene/animation/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources,"*.cpp") Export('env') - - diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 14f2110915..c7e259c3c6 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -29,6 +29,42 @@ #include "animation_tree_player.h" #include "animation_player.h" +#include "scene/scene_string_names.h" + + +void AnimationTreePlayer::set_animation_process_mode(AnimationProcessMode p_mode) { + + if (animation_process_mode == p_mode) + return; + + bool pr = processing; + if (pr) + _set_process(false); + animation_process_mode = p_mode; + if (pr) + _set_process(true); + +} + +AnimationTreePlayer::AnimationProcessMode AnimationTreePlayer::get_animation_process_mode() const{ + + return animation_process_mode; +} + +void AnimationTreePlayer::_set_process(bool p_process, bool p_force) +{ + if (processing == p_process && !p_force) + return; + + switch (animation_process_mode) { + + case ANIMATION_PROCESS_FIXED: set_fixed_process(p_process && active); break; + case ANIMATION_PROCESS_IDLE: set_process(p_process && active); break; + } + + processing = p_process; +} + bool AnimationTreePlayer::_set(const StringName& p_name, const Variant& p_value) { @@ -42,6 +78,11 @@ bool AnimationTreePlayer::_set(const StringName& p_name, const Variant& p_value) return true; } + if(String(p_name) == SceneStringNames::get_singleton()->playback_active) { + set_active(p_value); + return true; + } + if (String(p_name)!="data") return false; @@ -190,6 +231,11 @@ bool AnimationTreePlayer::_get(const StringName& p_name,Variant &r_ret) const { return true; } + if (String(p_name) == "playback/active") { + r_ret=is_active(); + return true; + } + if (String(p_name)!="data") return false; @@ -342,11 +388,24 @@ void AnimationTreePlayer::_get_property_list( List<PropertyInfo> *p_list) const p_list->push_back( PropertyInfo(Variant::DICTIONARY,"data",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_STORAGE|PROPERTY_USAGE_NETWORK) ); } +void AnimationTreePlayer::advance(float p_time) { + + _process_animation(p_time); +} void AnimationTreePlayer::_notification(int p_what) { switch(p_what) { + case NOTIFICATION_ENTER_TREE: { + + if (!processing) { + //make sure that a previous process state was not saved + //only process if "processing" is set + set_fixed_process(false); + set_process(false); + } + } break; case NOTIFICATION_READY: { dirty_caches=true; if (master!=NodePath()) { @@ -354,7 +413,19 @@ void AnimationTreePlayer::_notification(int p_what) { } } break; case NOTIFICATION_PROCESS: { - _process_animation(); + if (animation_process_mode==ANIMATION_PROCESS_FIXED) + break; + + if (processing) + _process_animation( get_process_delta_time() ); + } break; + case NOTIFICATION_FIXED_PROCESS: { + + if (animation_process_mode==ANIMATION_PROCESS_IDLE) + break; + + if (processing) + _process_animation(get_fixed_process_delta_time()); } break; } @@ -656,10 +727,7 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode } -void AnimationTreePlayer::_process_animation() { - - if (!active) - return; +void AnimationTreePlayer::_process_animation(float p_delta) { if (last_error!=CONNECT_OK) return; @@ -675,7 +743,7 @@ void AnimationTreePlayer::_process_animation() { _process_node(out_name,&prev, 1.0, 0, true ); reset_request=false; } else - _process_node(out_name,&prev, 1.0, get_process_delta_time(), false ); + _process_node(out_name,&prev, 1.0, p_delta, false ); if (dirty_caches) { //some animation changed.. ignore this pass @@ -1520,8 +1588,12 @@ void AnimationTreePlayer::recompute_caches() { void AnimationTreePlayer::set_active(bool p_active) { - active=p_active; - set_process(active); + if (active == p_active) + return; + + active = p_active; + processing = active; + _set_process(processing, true); } bool AnimationTreePlayer::is_active() const { @@ -1743,13 +1815,18 @@ void AnimationTreePlayer::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_node_list"),&AnimationTreePlayer::_get_node_list); + ObjectTypeDB::bind_method(_MD("set_animation_process_mode","mode"),&AnimationTreePlayer::set_animation_process_mode); + ObjectTypeDB::bind_method(_MD("get_animation_process_mode"),&AnimationTreePlayer::get_animation_process_mode); + ObjectTypeDB::bind_method(_MD("advance", "delta"), &AnimationTreePlayer::advance); ObjectTypeDB::bind_method(_MD("reset"),&AnimationTreePlayer::reset); ObjectTypeDB::bind_method(_MD("recompute_caches"),&AnimationTreePlayer::recompute_caches); + ADD_PROPERTY(PropertyInfo(Variant::INT, "playback/process_mode", PROPERTY_HINT_ENUM, "Fixed,Idle"), _SCS("set_animation_process_mode"), _SCS("get_animation_process_mode")); + BIND_CONSTANT( NODE_OUTPUT ); BIND_CONSTANT( NODE_ANIMATION ); BIND_CONSTANT( NODE_ONESHOT ); @@ -1770,6 +1847,9 @@ AnimationTreePlayer::AnimationTreePlayer() { out_name="out"; out->pos=Point2(40,40); node_map.insert( out_name , out); + AnimationProcessMode animation_process_mode; + animation_process_mode = ANIMATION_PROCESS_IDLE; + processing = false; active=false; dirty_caches=true; reset_request=false; diff --git a/scene/animation/animation_tree_player.h b/scene/animation/animation_tree_player.h index 9e936304c6..9ea5ccf330 100644 --- a/scene/animation/animation_tree_player.h +++ b/scene/animation/animation_tree_player.h @@ -34,6 +34,7 @@ #include "scene/3d/spatial.h" #include "scene/3d/skeleton.h" #include "scene/main/misc.h" +#include "animation_player.h" class AnimationTreePlayer : public Node { @@ -42,7 +43,10 @@ class AnimationTreePlayer : public Node { OBJ_CATEGORY("Animation Nodes"); public: - + enum AnimationProcessMode { + ANIMATION_PROCESS_FIXED, + ANIMATION_PROCESS_IDLE, + }; enum NodeType { @@ -256,13 +260,15 @@ private: ConnectError last_error; AnimationNode *active_list; + AnimationProcessMode animation_process_mode; + bool processing; bool active; bool dirty_caches; Map<StringName,NodeBase*> node_map; // return time left to finish animation float _process_node(const StringName& p_node,AnimationNode **r_prev_anim, float p_weight,float p_step, bool p_seek=false,const HashMap<NodePath,bool> *p_filter=NULL, float p_reverse_weight=0); - void _process_animation(); + void _process_animation(float p_delta); bool reset_request; ConnectError _cycle_test(const StringName &p_at_node); @@ -409,12 +415,21 @@ public: ConnectError get_last_error() const; + void set_animation_process_mode(AnimationProcessMode p_mode); + AnimationProcessMode get_animation_process_mode() const; + + void _set_process(bool p_process, bool p_force = false); + + void advance(float p_time); + AnimationTreePlayer(); ~AnimationTreePlayer(); }; VARIANT_ENUM_CAST( AnimationTreePlayer::NodeType ); +VARIANT_ENUM_CAST( AnimationTreePlayer::AnimationProcessMode ); + #endif // ANIMATION_TREE_PLAYER_H diff --git a/scene/audio/SCsub b/scene/audio/SCsub index 055d2f2474..bbe59b3054 100644 --- a/scene/audio/SCsub +++ b/scene/audio/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources,"*.cpp") Export('env') - - diff --git a/scene/gui/SCsub b/scene/gui/SCsub index 055d2f2474..bbe59b3054 100644 --- a/scene/gui/SCsub +++ b/scene/gui/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources,"*.cpp") Export('env') - - diff --git a/scene/gui/button_group.cpp b/scene/gui/button_group.cpp index 8d1fa80b84..c92d7f2696 100644 --- a/scene/gui/button_group.cpp +++ b/scene/gui/button_group.cpp @@ -155,6 +155,6 @@ void ButtonGroup::_bind_methods() { } -ButtonGroup::ButtonGroup() +ButtonGroup::ButtonGroup() : BoxContainer(true) { } diff --git a/scene/gui/button_group.h b/scene/gui/button_group.h index 24edf94994..74e847e937 100644 --- a/scene/gui/button_group.h +++ b/scene/gui/button_group.h @@ -29,14 +29,14 @@ #ifndef BUTTON_GROUP_H #define BUTTON_GROUP_H -#include "scene/gui/control.h" +#include "scene/gui/box_container.h" class BaseButton; -class ButtonGroup : public Control { +class ButtonGroup : public BoxContainer { - OBJ_TYPE(ButtonGroup,Control); + OBJ_TYPE(ButtonGroup,BoxContainer); Set<BaseButton*> buttons; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 40fade840c..f035cb7722 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -235,6 +235,37 @@ int ItemList::get_current() const { return current; } +void ItemList::move_item(int p_item,int p_to_pos) { + + ERR_FAIL_INDEX(p_item,items.size()); + ERR_FAIL_INDEX(p_to_pos,items.size()+1); + + Item it=items[p_item]; + items.remove(p_item);; + + if (p_to_pos>p_item) { + p_to_pos--; + } + + if (p_to_pos>=items.size()) { + items.push_back(it); + } else { + items.insert(p_to_pos,it); + } + + if (current<0) { + //do none + } if (p_item==current) { + current=p_to_pos; + } else if (p_to_pos>p_item && current>p_item && current<p_to_pos) { + current--; + } else if (p_to_pos<p_item && current<p_item && current>p_to_pos) { + current++; + } + + + update(); +} int ItemList::get_item_count() const{ diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 7cf58a6426..bd3cf6484e 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -101,6 +101,7 @@ public: void set_current(int p_current); int get_current() const; + void move_item(int p_item,int p_to_pos); int get_item_count() const; void remove_item(int p_idx); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 2b4d7db01e..18de8ed568 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -145,6 +145,13 @@ void LineEdit::_input_event(InputEvent p_event) { int old_cursor_pos = cursor_pos; text = undo_text; + + Ref<Font> font = get_font("font"); + + cached_width = 0; + for (int i = 0; i<text.length(); i++) + cached_width += font->get_char_size(text[i]).width; + if(old_cursor_pos > text.length()) { set_cursor_pos(text.length()); } else { @@ -164,6 +171,15 @@ void LineEdit::_input_event(InputEvent p_event) { selection_clear(); undo_text = text; text = text.substr(cursor_pos,text.length()-cursor_pos); + + Ref<Font> font = get_font("font"); + + cached_width = 0; + if (font != NULL) { + for (int i = 0; i < text.length(); i++) + cached_width += font->get_char_size(text[i]).width; + } + set_cursor_pos(0); emit_signal("text_changed",text); _change_notify("text"); @@ -192,6 +208,9 @@ void LineEdit::_input_event(InputEvent p_event) { } } break; + case (KEY_A): { //Select All + select(); + } break; default: { handled=false;} } @@ -303,6 +322,18 @@ void LineEdit::_input_event(InputEvent p_event) { } } +void LineEdit::set_align(Align p_align) { + + ERR_FAIL_INDEX(p_align, 4); + align = p_align; + update(); +} + +LineEdit::Align LineEdit::get_align() const{ + + return align; +} + Variant LineEdit::get_drag_data(const Point2& p_point) { if (selection.drag_attempt && selection.enabled) { @@ -325,7 +356,15 @@ void LineEdit::drop_data(const Point2& p_point,const Variant& p_data){ if (p_data.get_type()==Variant::STRING) { set_cursor_at_pixel_pos(p_point.x); int selected = selection.end - selection.begin; + + Ref<Font> font = get_font("font"); + if (font != NULL) { + for (int i = selection.begin; i < selection.end; i++) + cached_width -= font->get_char_size(text[i]).width; + } + text.erase(selection.begin, selected); + append_at_cursor(p_data); selection.begin = cursor_pos-selected; selection.end = cursor_pos; @@ -365,8 +404,25 @@ void LineEdit::_notification(int p_what) { get_stylebox("focus")->draw( ci, Rect2( Point2(), size ) ); } - - int ofs=style->get_offset().x; + int x_ofs=0; + + switch (align) { + + case ALIGN_FILL: + case ALIGN_LEFT: { + + x_ofs=style->get_offset().x; + } break; + case ALIGN_CENTER: { + + x_ofs=x_ofs=int(size.width-(cached_width))/2; + } break; + case ALIGN_RIGHT: { + + x_ofs=x_ofs=int(size.width-style->get_offset().x-(cached_width)); + } break; + } + int ofs_max=width-style->get_minimum_size().width; int char_ofs=window_pos; @@ -391,29 +447,29 @@ void LineEdit::_notification(int p_what) { int char_width=font->get_char_size( cchar,next ).width; // end of widget, break! - if ( (ofs+char_width) > ofs_max ) + if ((x_ofs + char_width) > ofs_max) break; bool selected=selection.enabled && char_ofs>=selection.begin && char_ofs<selection.end; if (selected) - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2( Point2( ofs , y_ofs ),Size2( char_width, y_area )),selection_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(char_width, y_area)), selection_color); - font->draw_char(ci,Point2( ofs , y_ofs+font_ascent ), cchar, next,selected?font_color_selected:font_color ); + font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, selected ? font_color_selected : font_color); if (char_ofs==cursor_pos && has_focus()) VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2( - Point2( ofs , y_ofs ), Size2( 1, y_area ) ), cursor_color ); + Point2( x_ofs , y_ofs ), Size2( 1, y_area ) ), cursor_color ); - ofs+=char_width; + x_ofs+=char_width; char_ofs++; } if (char_ofs==cursor_pos && has_focus()) //may be at the end VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2( - Point2( ofs , y_ofs ), Size2( 1, y_area ) ), cursor_color ); + Point2( x_ofs , y_ofs ), Size2( 1, y_area ) ), cursor_color ); } break; case NOTIFICATION_FOCUS_ENTER: { @@ -484,13 +540,36 @@ void LineEdit::shift_selection_check_post(bool p_shift) { void LineEdit::set_cursor_at_pixel_pos(int p_x) { - int ofs=window_pos; - int pixel_ofs=get_stylebox("normal")->get_offset().x; - Ref<Font> font=get_font("font"); + Ref<Font> font = get_font("font"); + int ofs = window_pos; + Ref<StyleBox> style = get_stylebox("normal"); + int pixel_ofs = 0; + Size2 size = get_size(); + + switch (align) { + + case ALIGN_FILL: + case ALIGN_LEFT: { + + pixel_ofs = int(style->get_offset().x); + } break; + case ALIGN_CENTER: { + + pixel_ofs=int(size.width-(cached_width))/2; + } break; + case ALIGN_RIGHT: { + + pixel_ofs=int(size.width-style->get_offset().x-(cached_width)); + } break; + } + while (ofs<text.length()) { - int char_w=font->get_char_size( text[ofs] ).width; + int char_w = 0; + if (font != NULL) { + int char_w = font->get_char_size(text[ofs]).width; + } pixel_ofs+=char_w; if (pixel_ofs > p_x) { //found what we look for @@ -523,6 +602,10 @@ void LineEdit::delete_char() { if ((text.length()<=0) || (cursor_pos==0)) return; + Ref<Font> font = get_font("font"); + if (font != NULL) { + cached_width -= font->get_char_size(text[cursor_pos - 1]).width; + } text.erase( cursor_pos-1, 1 ); @@ -593,13 +676,15 @@ void LineEdit::set_cursor_pos(int p_pos) { int width_to_cursor=0; int wp=window_pos; - for (int i=window_pos;i<cursor_pos;i++) - width_to_cursor+=font->get_char_size( text[i] ).width; + if (font != NULL) { + for (int i=window_pos;i<cursor_pos;i++) + width_to_cursor+=font->get_char_size( text[i] ).width; - while(width_to_cursor>=window_width && wp<text.length()) { - - width_to_cursor-=font->get_char_size( text[ wp ] ).width; - wp++; + while (width_to_cursor >= window_width && wp < text.length()) { + + width_to_cursor -= font->get_char_size(text[wp]).width; + wp++; + } } if (wp!=window_pos) @@ -626,17 +711,26 @@ void LineEdit::append_at_cursor(String p_text) { if ( ( max_length <= 0 ) || (text.length()+p_text.length() <= max_length)) { undo_text = text; + + Ref<Font> font = get_font("font"); + if (font != NULL) { + for (int i = 0; i < p_text.length(); i++) + cached_width += font->get_char_size(p_text[i]).width; + } + else { + cached_width = 0; + } + String pre = text.substr( 0, cursor_pos ); String post = text.substr( cursor_pos, text.length()-cursor_pos ); text=pre+p_text+post; set_cursor_pos(cursor_pos+p_text.length()); - } - } void LineEdit::clear_internal() { + cached_width = 0; cursor_pos=0; window_pos=0; undo_text=""; @@ -676,6 +770,20 @@ void LineEdit::selection_delete() { if (selection.enabled) { undo_text = text; + + if (text.size() > 0) + { + Ref<Font> font = get_font("font"); + if (font != NULL) { + for (int i = selection.begin; i < selection.end; i++) + cached_width -= font->get_char_size(text[i]).width; + } + } + else + { + cached_width = 0; + } + text.erase(selection.begin,selection.end-selection.begin); cursor_pos-=CLAMP( cursor_pos-selection.begin, 0, selection.end-selection.begin); @@ -789,6 +897,8 @@ bool LineEdit::is_text_field() const { void LineEdit::_bind_methods() { + ObjectTypeDB::bind_method(_MD("set_align", "align"), &LineEdit::set_align); + ObjectTypeDB::bind_method(_MD("get_align"), &LineEdit::get_align); ObjectTypeDB::bind_method(_MD("_input_event"),&LineEdit::_input_event); ObjectTypeDB::bind_method(_MD("clear"),&LineEdit::clear); @@ -809,15 +919,22 @@ void LineEdit::_bind_methods() { ADD_SIGNAL( MethodInfo("text_changed", PropertyInfo( Variant::STRING, "text" )) ); ADD_SIGNAL( MethodInfo("text_entered", PropertyInfo( Variant::STRING, "text" )) ); + BIND_CONSTANT(ALIGN_LEFT); + BIND_CONSTANT(ALIGN_CENTER); + BIND_CONSTANT(ALIGN_RIGHT); + BIND_CONSTANT(ALIGN_FILL); + ADD_PROPERTY( PropertyInfo( Variant::STRING, "text" ), _SCS("set_text"),_SCS("get_text") ); + ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), _SCS("set_align"), _SCS("get_align")); ADD_PROPERTY( PropertyInfo( Variant::INT, "max_length" ), _SCS("set_max_length"),_SCS("get_max_length") ); ADD_PROPERTY( PropertyInfo( Variant::BOOL, "editable" ), _SCS("set_editable"),_SCS("is_editable") ); ADD_PROPERTY( PropertyInfo( Variant::BOOL, "secret" ), _SCS("set_secret"),_SCS("is_secret") ); - } LineEdit::LineEdit() { + align = ALIGN_LEFT; + cached_width = 0; cursor_pos=0; window_pos=0; max_length = 0; diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index b1c4c8f616..f28136d66e 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -36,7 +36,18 @@ class LineEdit : public Control { OBJ_TYPE( LineEdit, Control ); - + +public: + enum Align { + + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT, + ALIGN_FILL + }; +private: + Align align; + bool editable; bool pass; @@ -46,6 +57,8 @@ class LineEdit : public Control { int cursor_pos; int window_pos; int max_length; // 0 for no maximum + + int cached_width; struct Selection { @@ -83,7 +96,8 @@ class LineEdit : public Control { protected: static void _bind_methods(); public: - + void set_align(Align p_align); + Align get_align() const; virtual Variant get_drag_data(const Point2& p_point); virtual bool can_drop_data(const Point2& p_point,const Variant& p_data) const; @@ -119,4 +133,7 @@ public: }; + +VARIANT_ENUM_CAST(LineEdit::Align); + #endif diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 13ff7074ea..be7a6b468a 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -54,6 +54,8 @@ void MenuButton::_unhandled_key_input(InputEvent p_event) { int item = popup->find_item_by_accelerator(code); + + if (item>=0 && ! popup->is_item_disabled(item)) popup->activate_item(item); /* diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 6c21ea639f..99663fb2e2 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -323,11 +323,14 @@ void PopupMenu::_input_event(const InputEvent &p_event) { invalidated_click=false; break; } - if (over<0 || items[over].separator || items[over].disabled) { + if (over<0) { hide(); break; //non-activable } + if (items[over].separator || items[over].disabled) + break; + if (items[over].submenu!="") { _activate_submenu(over); @@ -362,8 +365,11 @@ void PopupMenu::_input_event(const InputEvent &p_event) { int over=_get_mouse_over(Point2(m.x,m.y)); int id = (over<0 || items[over].separator || items[over].disabled)?-1:items[over].ID; - if (id<0) + if (id<0) { + mouse_over=-1; + update(); break; + } if (items[over].submenu!="" && submenu_over!=over) { submenu_over=over; @@ -774,6 +780,7 @@ void PopupMenu::add_separator() { void PopupMenu::clear() { items.clear(); + mouse_over=-1; update(); idcount=0; diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index ad708d16f0..7103ee651f 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -243,7 +243,7 @@ void Range::_bind_methods() { ADD_PROPERTY( PropertyInfo( Variant::REAL, "range/step" ), _SCS("set_step"), _SCS("get_step") ); ADD_PROPERTY( PropertyInfo( Variant::REAL, "range/page" ), _SCS("set_page"), _SCS("get_page") ); ADD_PROPERTY( PropertyInfo( Variant::REAL, "range/value" ), _SCS("set_val"), _SCS("get_val") ); - ADD_PROPERTY( PropertyInfo( Variant::REAL, "range/exp_edit" ), _SCS("set_exp_unit_value"), _SCS("is_unit_value_exp") ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "range/exp_edit" ), _SCS("set_exp_unit_value"), _SCS("is_unit_value_exp") ); ADD_PROPERTY( PropertyInfo( Variant::BOOL, "rounded_values" ), _SCS("set_rounded_values"), _SCS("get_rounded_values") ); } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index ef6a2ba6aa..b98fec1bde 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -719,7 +719,7 @@ void RichTextLabel::_input_event(InputEvent p_event) { case InputEvent::KEY: { const InputEventKey &k=p_event.key; - if (k.pressed) { + if (k.pressed && !k.mod.alt && !k.mod.shift && !k.mod.command && !k.mod.meta) { bool handled=true; switch(k.scancode) { case KEY_PAGEUP: { @@ -765,6 +765,7 @@ void RichTextLabel::_input_event(InputEvent p_event) { default: handled=false; } + if (handled) accept_event(); } diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index d7ee7a6b86..49067bb3a0 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -435,8 +435,8 @@ void SplitContainer::_bind_methods() { ADD_SIGNAL( MethodInfo("dragged",PropertyInfo(Variant::INT,"offset"))); ADD_PROPERTY( PropertyInfo(Variant::INT,"split/offset"),_SCS("set_split_offset"),_SCS("get_split_offset")); - ADD_PROPERTY( PropertyInfo(Variant::INT,"split/collapsed"),_SCS("set_collapsed"),_SCS("is_collapsed")); - ADD_PROPERTY( PropertyInfo(Variant::INT,"split/dragger_visible"),_SCS("set_dragger_visible"),_SCS("is_dragger_visible")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"split/collapsed"),_SCS("set_collapsed"),_SCS("is_collapsed")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"split/dragger_visible"),_SCS("set_dragger_visible"),_SCS("is_dragger_visible")); } diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 6d84f028b3..47a55e0716 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -58,7 +58,7 @@ Size2 Tabs::get_minimum_size() const { if (tabs[i].right_button.is_valid()) { Ref<Texture> rb=tabs[i].right_button; - Size2 bms = rb->get_size()+get_stylebox("button")->get_minimum_size(); + Size2 bms = rb->get_size();//+get_stylebox("button")->get_minimum_size(); bms.width+=get_constant("hseparation"); ms.width+=bms.width; @@ -67,9 +67,8 @@ Size2 Tabs::get_minimum_size() const { if (tabs[i].close_button.is_valid()) { Ref<Texture> cb=tabs[i].close_button; - Size2 bms = cb->get_size()+get_stylebox("button")->get_minimum_size(); + Size2 bms = cb->get_size();//+get_stylebox("button")->get_minimum_size(); bms.width+=get_constant("hseparation"); - ms.width+=bms.width; ms.height=MAX(bms.height+tab_bg->get_minimum_size().height,ms.height); } @@ -103,11 +102,13 @@ void Tabs::_input_event(const InputEvent& p_event) { // test hovering right button and close button if (tabs[i].rb_rect.has_point(pos)) { rb_hover=i; + cb_hover=-1; hover_buttons = i; break; } else if (tabs[i].cb_rect.has_point(pos)) { cb_hover=i; + rb_hover=-1; hover_buttons = i; break; } @@ -262,9 +263,9 @@ void Tabs::_notification(int p_what) { Ref<Texture> rb=tabs[i].right_button; lsize+=get_constant("hseparation"); - lsize+=style->get_margin(MARGIN_LEFT); + //lsize+=style->get_margin(MARGIN_LEFT); lsize+=rb->get_width(); - lsize+=style->get_margin(MARGIN_RIGHT); + //lsize+=style->get_margin(MARGIN_RIGHT); } @@ -276,9 +277,9 @@ void Tabs::_notification(int p_what) { Ref<Texture> rb=tabs[i].close_button; lsize+=get_constant("hseparation"); - lsize+=style->get_margin(MARGIN_LEFT); + //lsize+=style->get_margin(MARGIN_LEFT); lsize+=rb->get_width(); - lsize+=style->get_margin(MARGIN_RIGHT); + //lsize+=style->get_margin(MARGIN_RIGHT); } } break; @@ -289,9 +290,9 @@ void Tabs::_notification(int p_what) { Ref<Texture> rb=tabs[i].close_button; lsize+=get_constant("hseparation"); - lsize+=style->get_margin(MARGIN_LEFT); + //lsize+=style->get_margin(MARGIN_LEFT); lsize+=rb->get_width(); - lsize+=style->get_margin(MARGIN_RIGHT); + //lsize+=style->get_margin(MARGIN_RIGHT); } } @@ -303,9 +304,9 @@ void Tabs::_notification(int p_what) { Ref<Texture> rb=tabs[i].close_button; lsize+=get_constant("hseparation"); - lsize+=style->get_margin(MARGIN_LEFT); + //lsize+=style->get_margin(MARGIN_LEFT); lsize+=rb->get_width(); - lsize+=style->get_margin(MARGIN_RIGHT); + //lsize+=style->get_margin(MARGIN_RIGHT); } } @@ -404,11 +405,11 @@ void Tabs::_notification(int p_what) { style->draw(ci,cb_rect); } - w+=style->get_margin(MARGIN_LEFT); + //w+=style->get_margin(MARGIN_LEFT); cb->draw(ci,Point2i( w,cb_rect.pos.y+style->get_margin(MARGIN_TOP) )); w+=cb->get_width(); - w+=style->get_margin(MARGIN_RIGHT); + //w+=style->get_margin(MARGIN_RIGHT); tabs[i].cb_rect=cb_rect; } } break; @@ -432,11 +433,11 @@ void Tabs::_notification(int p_what) { style->draw(ci,cb_rect); } - w+=style->get_margin(MARGIN_LEFT); + //w+=style->get_margin(MARGIN_LEFT); cb->draw(ci,Point2i( w,cb_rect.pos.y+style->get_margin(MARGIN_TOP) )); w+=cb->get_width(); - w+=style->get_margin(MARGIN_RIGHT); + //w+=style->get_margin(MARGIN_RIGHT); tabs[i].cb_rect=cb_rect; } } @@ -461,11 +462,11 @@ void Tabs::_notification(int p_what) { style->draw(ci,cb_rect); } - w+=style->get_margin(MARGIN_LEFT); + //w+=style->get_margin(MARGIN_LEFT); cb->draw(ci,Point2i( w,cb_rect.pos.y+style->get_margin(MARGIN_TOP) )); w+=cb->get_width(); - w+=style->get_margin(MARGIN_RIGHT); + //w+=style->get_margin(MARGIN_RIGHT); tabs[i].cb_rect=cb_rect; } } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index be6c0d0a8b..5415484009 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1094,16 +1094,16 @@ void TextEdit::backspace_at_cursor() { } -bool TextEdit::_get_mouse_pos(const Point2i& p_mouse, int &r_row, int &r_col) const { +void TextEdit::_get_mouse_pos(const Point2i& p_mouse, int &r_row, int &r_col) const { - int row=p_mouse.y; - row-=cache.style_normal->get_margin(MARGIN_TOP); - row/=get_row_height(); - - if (row<0 || row>=get_visible_rows()) - return false; - - row+=cursor.line_ofs; + float rows=p_mouse.y; + rows-=cache.style_normal->get_margin(MARGIN_TOP); + rows/=get_row_height(); + int row=cursor.line_ofs+rows; + + if (row<0) + row=0; + int col=0; if (row>=text.size()) { @@ -1119,7 +1119,6 @@ bool TextEdit::_get_mouse_pos(const Point2i& p_mouse, int &r_row, int &r_col) co r_row=row; r_col=col; - return true; } void TextEdit::_input_event(const InputEvent& p_input_event) { @@ -1177,8 +1176,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { if (mb.button_index==BUTTON_LEFT) { int row,col; - if (!_get_mouse_pos(Point2i(mb.x,mb.y), row,col)) - return; + _get_mouse_pos(Point2i(mb.x,mb.y), row,col); int prev_col=cursor.column; int prev_line=cursor.line; @@ -1210,27 +1208,30 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { update(); } else { - if (cursor.line<selection.from_line || (cursor.line==selection.from_line && cursor.column<=selection.from_column)) { - selection.from_column=cursor.column; - selection.from_line=cursor.line; - } else if (cursor.line>selection.to_line || (cursor.line==selection.to_line && cursor.column>=selection.to_column)) { - selection.to_column=cursor.column; - selection.to_line=cursor.line; - - } else if (!selection.shiftclick_left) { + if (cursor.line<selection.selecting_line || (cursor.line==selection.selecting_line && cursor.column<selection.selecting_column)) { + if (selection.shiftclick_left) { + SWAP(selection.from_column,selection.to_column); + SWAP(selection.from_line,selection.to_line); + selection.shiftclick_left = !selection.shiftclick_left; + } selection.from_column=cursor.column; selection.from_line=cursor.line; - } else { + } else if (cursor.line>selection.selecting_line || (cursor.line==selection.selecting_line && cursor.column>selection.selecting_column)) { + + if (!selection.shiftclick_left) { + SWAP(selection.from_column,selection.to_column); + SWAP(selection.from_line,selection.to_line); + selection.shiftclick_left = !selection.shiftclick_left; + } selection.to_column=cursor.column; selection.to_line=cursor.line; - } - if (selection.from_line>selection.to_line || (selection.from_line==selection.to_line && selection.from_column>selection.to_column)) { - SWAP(selection.from_column,selection.to_column); - SWAP(selection.from_line,selection.to_line); + } else { + selection.active=false; } + update(); } @@ -1255,6 +1256,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { if (!mb.doubleclick && (OS::get_singleton()->get_ticks_msec()-last_dblclk)<600 && cursor.line==prev_line) { //tripleclick select line select(cursor.line,0,cursor.line,text[cursor.line].length()); + selection.selecting_column=0; last_dblclk=0; } else if (mb.doubleclick && text[cursor.line].length()) { @@ -1279,6 +1281,8 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { end+=1; select(cursor.line,beg,cursor.line,end); + + selection.selecting_column=beg; } last_dblclk = OS::get_singleton()->get_ticks_msec(); @@ -1289,7 +1293,6 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { } } else { - selection.selecting_mode=Selection::MODE_NONE; // notify to show soft keyboard notification(NOTIFICATION_FOCUS_ENTER); } @@ -1302,10 +1305,9 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { if (mm.button_mask&BUTTON_MASK_LEFT) { int row,col; - if (!_get_mouse_pos(Point2i(mm.x,mm.y), row,col)) - return; + _get_mouse_pos(Point2i(mm.x,mm.y), row,col); - if (selection.selecting_mode==Selection::MODE_POINTER) { + if (selection.selecting_mode!=Selection::MODE_NONE) { select(selection.selecting_line,selection.selecting_column,row,col); @@ -1585,7 +1587,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { break; } - selection.selecting_test=false; + selection.selecting_text=false; bool scancode_handled=true; @@ -1647,8 +1649,60 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { case KEY_BACKSPACE: { if (readonly) break; - backspace_at_cursor(); - + +#ifdef APPLE_STYLE_KEYS + if (k.mod.alt) { +#else + if (k.mod.alt) { + scancode_handled=false; + break; + } else if (k.mod.command) { +#endif + int line=cursor.line; + int column=cursor.column; + + bool prev_char=false; + bool only_whitespace=true; + + while (only_whitespace && line > 0) { + + while (column>0) { + CharType c=text[line][column-1]; + + if (c != '\t' && c != ' ') { + only_whitespace=false; + break; + } + + column--; + } + + if (only_whitespace) { + line--; + column=text[line].length(); + } + } + + while (column>0) { + bool ischar=_is_text_char(text[line][column-1]); + + if (prev_char && !ischar) + break; + + prev_char=ischar; + column--; + + } + + _remove_text(line, column, cursor.line, cursor.column); + + cursor_set_line(line); + cursor_set_column(column); + + } else { + backspace_at_cursor(); + } + } break; case KEY_LEFT: { @@ -1789,10 +1843,63 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { if (cursor.line==text.size()-1 && cursor.column==curline_len) break; //nothing to do - int next_line = cursor.column<curline_len?cursor.line:cursor.line+1; - int next_column = cursor.column<curline_len?(cursor.column+1):0; + int next_line=cursor.column<curline_len?cursor.line:cursor.line+1; + int next_column; + +#ifdef APPLE_STYLE_KEYS + if (k.mod.alt) { +#else + if (k.mod.alt) { + scancode_handled=false; + break; + } else if (k.mod.command) { +#endif + int last_line=text.size()-1; + + int line=cursor.line; + int column=cursor.column; + + bool prev_char=false; + bool only_whitespace=true; + + while (only_whitespace && line < last_line) { + + while (column<text[line].length()) { + CharType c=text[line][column]; + + if (c != '\t' && c != ' ') { + only_whitespace=false; + break; + } + + column++; + } + + if (only_whitespace) { + line++; + column=0; + } + } + + while (column<text[line].length()) { + + bool ischar=_is_text_char(text[line][column]); + + if (prev_char && !ischar) + break; + prev_char=ischar; + column++; + } + + next_line=line; + next_column=column; + } else { + next_column=cursor.column<curline_len?(cursor.column+1):0; + } + _remove_text(cursor.line,cursor.column,next_line,next_column); update(); + } break; #ifdef APPLE_STYLE_KEYS case KEY_HOME: { @@ -1903,15 +2010,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { break; } - if (text.size()==1 && text[0].length()==0) - break; - selection.active=true; - selection.from_line=0; - selection.from_column=0; - selection.to_line=text.size()-1; - selection.to_column=text[selection.to_line].length(); - selection.selecting_mode=Selection::MODE_NONE; - update(); + select_all(); } break; case KEY_X: { @@ -2096,12 +2195,6 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { } } - - if (!selection.selecting_test) { - - selection.selecting_mode=Selection::MODE_NONE; - } - return; } break; @@ -2113,13 +2206,14 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { void TextEdit::_pre_shift_selection() { - if (!selection.active || selection.selecting_mode!=Selection::MODE_SHIFT) { + if (!selection.active || selection.selecting_mode==Selection::MODE_NONE) { selection.selecting_line=cursor.line; selection.selecting_column=cursor.column; selection.active=true; - selection.selecting_mode=Selection::MODE_SHIFT; } + + selection.selecting_mode=Selection::MODE_SHIFT; } void TextEdit::_post_shift_selection() { @@ -2132,7 +2226,7 @@ void TextEdit::_post_shift_selection() { } - selection.selecting_test=true; + selection.selecting_text=true; } /**** TEXT EDIT CORE API ****/ @@ -2428,7 +2522,7 @@ void TextEdit::adjust_viewport_to_cursor() { } -void TextEdit::cursor_set_column(int p_col) { +void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) { if (p_col<0) p_col=0; @@ -2439,7 +2533,8 @@ void TextEdit::cursor_set_column(int p_col) { cursor.last_fit_x=get_column_x_offset(cursor.column,get_line(cursor.line)); - adjust_viewport_to_cursor(); + if (p_adjust_viewport) + adjust_viewport_to_cursor(); if (!cursor_changed_dirty) { if (is_inside_tree()) @@ -2450,7 +2545,7 @@ void TextEdit::cursor_set_column(int p_col) { } -void TextEdit::cursor_set_line(int p_row) { +void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport) { if (setting_row) return; @@ -2466,8 +2561,8 @@ void TextEdit::cursor_set_line(int p_row) { cursor.line=p_row; cursor.column=get_char_pos_for( cursor.last_fit_x, get_line( cursor.line) ); - - adjust_viewport_to_cursor(); + if (p_adjust_viewport) + adjust_viewport_to_cursor(); setting_row=false; @@ -2832,9 +2927,14 @@ void TextEdit::select_all() { selection.active=true; selection.from_line=0; selection.from_column=0; + selection.selecting_line=0; + selection.selecting_column=0; selection.to_line=text.size()-1; selection.to_column=text[selection.to_line].length(); - selection.selecting_mode=Selection::MODE_NONE; + selection.selecting_mode=Selection::MODE_SHIFT; + selection.shiftclick_left=true; + cursor_set_line( selection.to_line, false ); + cursor_set_column( selection.to_column, false ); update(); } @@ -2873,12 +2973,20 @@ void TextEdit::select(int p_from_line,int p_from_column,int p_to_line,int p_to_c } else if (selection.from_column>selection.to_column) { + selection.shiftclick_left = false; SWAP( selection.from_column, selection.to_column ); + } else { + + selection.shiftclick_left = true; } } else if (selection.from_line>selection.to_line) { + selection.shiftclick_left = false; SWAP( selection.from_line, selection.to_line ); SWAP( selection.from_column, selection.to_column ); + } else { + + selection.shiftclick_left = true; } @@ -3502,10 +3610,8 @@ String TextEdit::get_tooltip(const Point2& p_pos) const { if (!tooltip_obj) return Control::get_tooltip(p_pos); int row,col; - if (!_get_mouse_pos(p_pos, row,col)) { - return Control::get_tooltip(p_pos); - } - + _get_mouse_pos(p_pos, row, col); + String s = text[row]; if (s.length()==0) return Control::get_tooltip(p_pos); @@ -3684,7 +3790,7 @@ TextEdit::TextEdit() { selection.selecting_mode=Selection::MODE_NONE; selection.selecting_line=0; selection.selecting_column=0; - selection.selecting_test=false; + selection.selecting_text=false; selection.active=false; syntax_coloring=false; diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 9ffe8a5bae..059e15dcff 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -55,7 +55,7 @@ class TextEdit : public Control { Mode selecting_mode; int selecting_line,selecting_column; - bool selecting_test; + bool selecting_text; bool active; @@ -270,7 +270,7 @@ class TextEdit : public Control { void _confirm_completion(); void _update_completion_candidates(); - bool _get_mouse_pos(const Point2i& p_mouse, int &r_row, int &r_col) const; + void _get_mouse_pos(const Point2i& p_mouse, int &r_row, int &r_col) const; protected: @@ -324,8 +324,8 @@ public: update(); } - void cursor_set_column(int p_col); - void cursor_set_line(int p_row); + void cursor_set_column(int p_col, bool p_adjust_viewport=true); + void cursor_set_line(int p_row, bool p_adjust_viewport=true); int cursor_get_column() const; int cursor_get_line() const; diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp index 0d549108fa..c8930add6e 100644 --- a/scene/gui/texture_progress.cpp +++ b/scene/gui/texture_progress.cpp @@ -233,11 +233,7 @@ float TextureProgress::get_radial_initial_angle() void TextureProgress::set_fill_degrees(float p_angle) { - while(p_angle>360) - p_angle-=360; - while (p_angle<0) - p_angle+=360; - rad_max_degrees=p_angle; + rad_max_degrees=CLAMP(p_angle,0,360); update(); } @@ -302,4 +298,5 @@ TextureProgress::TextureProgress() { mode=FILL_LEFT_TO_RIGHT; rad_center_off=Point2(); + rad_max_degrees=360; } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 5df6f2ced9..16a12fe407 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -962,7 +962,9 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2& Point2i guide_space=Point2i( cache.guide_width , height ); - if (p_item->childs) { //has childs, draw the guide box + + + if (!hide_folding && p_item->childs) { //has childs, draw the guide box Ref<Texture> arrow; @@ -986,7 +988,7 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2& int font_ascent=font->get_ascent(); - int ofs = p_pos.x + cache.item_margin; + int ofs = p_pos.x + (hide_folding?cache.hseparation:cache.item_margin); for (int i=0;i<columns.size();i++) { int w = get_column_width(i); @@ -1062,7 +1064,10 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2& if (p_item->cells[i].custom_bg_color) { - VisualServer::get_singleton()->canvas_item_add_rect(ci,cell_rect,p_item->cells[i].bg_color); + Rect2 r=cell_rect; + r.pos.x-=cache.hseparation; + r.size.x+=cache.hseparation; + VisualServer::get_singleton()->canvas_item_add_rect(ci,r,p_item->cells[i].bg_color); } Color col=p_item->cells[i].custom_color?p_item->cells[i].color:get_color( p_item->cells[i].selected?"font_color_selected":"font_color"); @@ -1376,7 +1381,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ if (!skip && p_pos.y<item_h) { // check event! - if (p_pos.x >=x_ofs && p_pos.x < (x_ofs+cache.item_margin) ) { + if (!hide_folding && (p_pos.x >=x_ofs && p_pos.x < (x_ofs+cache.item_margin) )) { if (p_item->childs) @@ -2266,9 +2271,12 @@ bool Tree::edit_selected() { TreeItem::Cell &c = s->cells[col]; + if (c.mode==TreeItem::CELL_MODE_CHECK) { - - if (c.mode==TreeItem::CELL_MODE_CUSTOM) { + s->set_checked(col, !c.checked); + item_edited(col,s); + return true; + } else if (c.mode==TreeItem::CELL_MODE_CUSTOM) { edited_item=s; edited_col=col; @@ -3114,6 +3122,16 @@ bool Tree::can_cursor_exit_tree() const { return cursor_can_exit_tree; } +void Tree::set_hide_folding(bool p_hide) { + hide_folding=p_hide; + update(); +} + +bool Tree::is_folding_hidden() const { + + return hide_folding; +} + void Tree::_bind_methods() { @@ -3155,6 +3173,9 @@ void Tree::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_column_title","column"),&Tree::get_column_title); ObjectTypeDB::bind_method(_MD("get_scroll"),&Tree::get_scroll); + ObjectTypeDB::bind_method(_MD("set_hide_folding","hide"),&Tree::set_hide_folding); + ObjectTypeDB::bind_method(_MD("is_folding_hidden"),&Tree::is_folding_hidden); + ADD_SIGNAL( MethodInfo("item_selected")); ADD_SIGNAL( MethodInfo("cell_selected")); @@ -3242,6 +3263,8 @@ Tree::Tree() { pressing_for_editor=false; range_drag_enabled=false; + hide_folding=false; + } diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 3fbd7c95d9..8fb9b802a1 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -228,6 +228,7 @@ public: void set_tooltip(int p_column, const String& p_tooltip); String get_tooltip(int p_column) const; + void clear_children(); void move_to_top(); @@ -410,6 +411,8 @@ friend class TreeItem; bool drag_touching_deaccel; bool click_handled; + bool hide_folding; + protected: static void _bind_methods(); @@ -467,6 +470,11 @@ public: VScrollBar *get_vscroll_bar() { return v_scroll; } + void set_hide_folding(bool p_hide); + bool is_folding_hidden() const; + + + Tree(); ~Tree(); diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp index b4cd437d35..9b9c797ed9 100644 --- a/scene/gui/video_player.cpp +++ b/scene/gui/video_player.cpp @@ -384,12 +384,12 @@ void VideoPlayer::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_buffering_msec","msec"),&VideoPlayer::set_buffering_msec); ObjectTypeDB::bind_method(_MD("get_buffering_msec"),&VideoPlayer::get_buffering_msec); + ADD_PROPERTY( PropertyInfo(Variant::INT, "stream/audio_track",PROPERTY_HINT_RANGE,"0,128,1"), _SCS("set_audio_track"), _SCS("get_audio_track") ); ADD_PROPERTY( PropertyInfo(Variant::OBJECT, "stream/stream", PROPERTY_HINT_RESOURCE_TYPE,"VideoStream"), _SCS("set_stream"), _SCS("get_stream") ); // ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), _SCS("set_loop"), _SCS("has_loop") ); ADD_PROPERTY( PropertyInfo(Variant::REAL, "stream/volume_db", PROPERTY_HINT_RANGE,"-80,24,0.01"), _SCS("set_volume_db"), _SCS("get_volume_db") ); ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/autoplay"), _SCS("set_autoplay"), _SCS("has_autoplay") ); ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/paused"), _SCS("set_paused"), _SCS("is_paused") ); - ADD_PROPERTY( PropertyInfo(Variant::INT, "stream/audio_track",PROPERTY_HINT_RANGE,"0,128,1"), _SCS("set_audio_track"), _SCS("get_audio_track") ); ADD_PROPERTY( PropertyInfo( Variant::BOOL, "expand" ), _SCS("set_expand"),_SCS("has_expand") ); } diff --git a/scene/io/SCsub b/scene/io/SCsub index 055d2f2474..bbe59b3054 100644 --- a/scene/io/SCsub +++ b/scene/io/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources,"*.cpp") Export('env') - - diff --git a/scene/main/SCsub b/scene/main/SCsub index 055d2f2474..bbe59b3054 100644 --- a/scene/main/SCsub +++ b/scene/main/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources,"*.cpp") Export('env') - - diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp index 370eb1e74a..12f6086bf0 100644 --- a/scene/main/instance_placeholder.cpp +++ b/scene/main/instance_placeholder.cpp @@ -22,12 +22,12 @@ void InstancePlaceholder::_get_property_list( List<PropertyInfo> *p_list) const{ } -void InstancePlaceholder::set_path(const String& p_name) { +void InstancePlaceholder::set_instance_path(const String& p_name) { path=p_name; } -String InstancePlaceholder::get_path() const { +String InstancePlaceholder::get_instance_path() const { return path; } @@ -66,6 +66,7 @@ void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_s void InstancePlaceholder::_bind_methods() { ObjectTypeDB::bind_method(_MD("replace_by_instance","custom_scene:PackedScene"),&InstancePlaceholder::replace_by_instance,DEFVAL(Variant())); + ObjectTypeDB::bind_method(_MD("get_instance_path"),&InstancePlaceholder::get_instance_path); } InstancePlaceholder::InstancePlaceholder() { diff --git a/scene/main/instance_placeholder.h b/scene/main/instance_placeholder.h index e9e76e7a2d..9c47655ce7 100644 --- a/scene/main/instance_placeholder.h +++ b/scene/main/instance_placeholder.h @@ -26,8 +26,8 @@ protected: public: - void set_path(const String& p_name); - String get_path() const; + void set_instance_path(const String& p_name); + String get_instance_path() const; void replace_by_instance(const Ref<PackedScene>& p_custom_scene=Ref<PackedScene>()); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 631dc8dcc7..97c36ff71b 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2039,6 +2039,10 @@ void Node::_bind_methods() { ObjectTypeDB::bind_method(_MD("duplicate:Node","use_instancing"),&Node::duplicate,DEFVAL(false)); ObjectTypeDB::bind_method(_MD("replace_by","node:Node","keep_data"),&Node::replace_by,DEFVAL(false)); + ObjectTypeDB::bind_method(_MD("set_scene_instance_load_placeholder","load_placeholder"),&Node::set_scene_instance_load_placeholder); + ObjectTypeDB::bind_method(_MD("get_scene_instance_load_placeholder"),&Node::get_scene_instance_load_placeholder); + + ObjectTypeDB::bind_method(_MD("get_viewport"),&Node::get_viewport); ObjectTypeDB::bind_method(_MD("queue_free"),&Node::queue_delete); diff --git a/scene/resources/SCsub b/scene/resources/SCsub index eaa282ae1a..bb9766e1ca 100644 --- a/scene/resources/SCsub +++ b/scene/resources/SCsub @@ -6,4 +6,3 @@ env.add_source_files(env.scene_sources,"*.c") Export('env') SConscript("default_theme/SCsub"); - diff --git a/scene/resources/concave_polygon_shape_2d.cpp b/scene/resources/concave_polygon_shape_2d.cpp index 923e2817ef..01b7531f14 100644 --- a/scene/resources/concave_polygon_shape_2d.cpp +++ b/scene/resources/concave_polygon_shape_2d.cpp @@ -34,6 +34,7 @@ void ConcavePolygonShape2D::set_segments(const DVector<Vector2>& p_segments) { Physics2DServer::get_singleton()->shape_set_data(get_rid(),p_segments); + emit_changed(); } DVector<Vector2> ConcavePolygonShape2D::get_segments() const { diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp index dac39fc846..a1137ba614 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -33,6 +33,7 @@ void ConvexPolygonShape2D::_update_shape() { Physics2DServer::get_singleton()->shape_set_data(get_rid(),points); + emit_changed(); } @@ -62,7 +63,7 @@ void ConvexPolygonShape2D::_bind_methods() { - ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"points"),_SCS("set_points"),_SCS("get_points") ); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR2_ARRAY,"points"),_SCS("set_points"),_SCS("get_points") ); } diff --git a/scene/resources/default_theme/SCsub b/scene/resources/default_theme/SCsub index 055d2f2474..bbe59b3054 100644 --- a/scene/resources/default_theme/SCsub +++ b/scene/resources/default_theme/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources,"*.cpp") Export('env') - - diff --git a/scene/resources/default_theme/checked.png b/scene/resources/default_theme/checked.png Binary files differindex a41b33cccf..a2240c227f 100644 --- a/scene/resources/default_theme/checked.png +++ b/scene/resources/default_theme/checked.png diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 7e36366e74..25407a5b84 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -188,6 +188,7 @@ void make_default_theme() { // Font Colors Color control_font_color = Color::html("e0e0e0"); + Color control_font_color_lower = Color::html("a0a0a0"); Color control_font_color_low = Color::html("b0b0b0"); Color control_font_color_hover = Color::html("f0f0f0"); Color control_font_color_disabled = Color(0.9,0.9,0.9,0.2); @@ -212,8 +213,8 @@ void make_default_theme() { // Button - Ref<StyleBox> sb_button_normal = sb_expand( make_stylebox( button_normal_png,4,4,4,4,6,2,6,2),2,2,2,2); - Ref<StyleBox> sb_button_pressed = sb_expand( make_stylebox( button_pressed_png,4,4,4,4,6,2,6,2),2,2,2,2); + Ref<StyleBox> sb_button_normal = sb_expand( make_stylebox( button_normal_png,4,4,4,4,6,3,6,3),2,2,2,2); + Ref<StyleBox> sb_button_pressed = sb_expand( make_stylebox( button_pressed_png,4,4,4,4,6,3,6,3),2,2,2,2); Ref<StyleBox> sb_button_hover = sb_expand( make_stylebox( button_hover_png,4,4,4,4,6,2,6,2),2,2,2,2); Ref<StyleBox> sb_button_disabled = sb_expand( make_stylebox( button_disabled_png,4,4,4,4,6,2,6,2),2,2,2,2); Ref<StyleBox> sb_button_focus = sb_expand( make_stylebox( button_focus_png,4,4,4,4,6,2,6,2),2,2,2,2); @@ -273,14 +274,14 @@ void make_default_theme() { t->set_color("font_color_hover","ToolButton", control_font_color_hover ); t->set_color("font_color_disabled","ToolButton", Color(0.9,0.95,1,0.3) ); - t->set_constant("hseparation","ToolButton", 0 ); + t->set_constant("hseparation","ToolButton", 3 ); // OptionButton - Ref<StyleBox> sb_optbutton_normal = sb_expand( make_stylebox( option_button_normal_png,4,4,21,4,6,2,21,2),2,2,2,2); - Ref<StyleBox> sb_optbutton_pressed = sb_expand( make_stylebox( option_button_pressed_png,4,4,21,4,6,2,21,2),2,2,2,2); + Ref<StyleBox> sb_optbutton_normal = sb_expand( make_stylebox( option_button_normal_png,4,4,21,4,6,3,21,3),2,2,2,2); + Ref<StyleBox> sb_optbutton_pressed = sb_expand( make_stylebox( option_button_pressed_png,4,4,21,4,6,3,21,3),2,2,2,2); Ref<StyleBox> sb_optbutton_hover = sb_expand( make_stylebox( option_button_hover_png,4,4,21,4,6,2,21,2),2,2,2,2); Ref<StyleBox> sb_optbutton_disabled = sb_expand( make_stylebox( option_button_disabled_png,4,4,21,4,6,2,21,2),2,2,2,2); Ref<StyleBox> sb_optbutton_focus = sb_expand( make_stylebox( button_focus_png,4,4,4,4,6,2,6,2),2,2,2,2); @@ -311,6 +312,7 @@ void make_default_theme() { t->set_stylebox("pressed","MenuButton", sb_button_pressed ); t->set_stylebox("hover","MenuButton", sb_button_pressed ); t->set_stylebox("disabled","MenuButton", make_empty_stylebox(0,0,0,0) ); + t->set_stylebox("focus","MenuButton", sb_button_focus ); t->set_font("font","MenuButton", default_font ); @@ -321,6 +323,10 @@ void make_default_theme() { t->set_constant("hseparation","MenuButton", 3 ); + // ButtonGroup + + t->set_stylebox("panel","ButtonGroup", memnew( StyleBoxEmpty )); + // CheckBox Ref<StyleBox> cbx_empty = memnew( StyleBoxEmpty ); @@ -460,10 +466,10 @@ void make_default_theme() { // HScrollBar - t->set_stylebox("scroll","HScrollBar", make_stylebox( scroll_bg_png,3,3,3,3,0,0,0,0) ); - t->set_stylebox("scroll_focus","HScrollBar", make_stylebox( scroll_bg_png,3,3,3,3,0,0,0,0) ); - t->set_stylebox("grabber","HScrollBar", make_stylebox( scroll_grabber_png,3,3,3,3,2,2,2,2) ); - t->set_stylebox("grabber_hilite","HScrollBar", make_stylebox( scroll_grabber_hl_png,3,3,3,3,2,2,2,2) ); + t->set_stylebox("scroll","HScrollBar", make_stylebox( scroll_bg_png,5,5,5,5,0,0,0,0) ); + t->set_stylebox("scroll_focus","HScrollBar", make_stylebox( scroll_bg_png,5,5,5,5,0,0,0,0) ); + t->set_stylebox("grabber","HScrollBar", make_stylebox( scroll_grabber_png,5,5,5,5,2,2,2,2) ); + t->set_stylebox("grabber_hilite","HScrollBar", make_stylebox( scroll_grabber_hl_png,5,5,5,5,2,2,2,2) ); t->set_icon("increment","HScrollBar",empty_icon); t->set_icon("increment_hilite","HScrollBar",empty_icon); @@ -474,10 +480,10 @@ void make_default_theme() { // VScrollBar - t->set_stylebox("scroll","VScrollBar", make_stylebox( scroll_bg_png,3,3,3,3,0,0,0,0) ); - t->set_stylebox("scroll_focus","VScrollBar", make_stylebox( scroll_bg_png,3,3,3,3,0,0,0,0) ); - t->set_stylebox("grabber","VScrollBar", make_stylebox( scroll_grabber_png,3,3,3,3,2,2,2,2) ); - t->set_stylebox("grabber_hilite","VScrollBar", make_stylebox( scroll_grabber_hl_png,3,3,3,3,2,2,2,2) ); + t->set_stylebox("scroll","VScrollBar", make_stylebox( scroll_bg_png,5,5,5,5,0,0,0,0) ); + t->set_stylebox("scroll_focus","VScrollBar", make_stylebox( scroll_bg_png,5,5,5,5,0,0,0,0) ); + t->set_stylebox("grabber","VScrollBar", make_stylebox( scroll_grabber_png,5,5,5,5,2,2,2,2) ); + t->set_stylebox("grabber_hilite","VScrollBar", make_stylebox( scroll_grabber_hl_png,5,5,5,5,2,2,2,2) ); t->set_icon("increment","VScrollBar",empty_icon); t->set_icon("increment_hilite","VScrollBar",empty_icon); @@ -649,7 +655,7 @@ void make_default_theme() { t->set_constant("icon_margin","ItemList",4); t->set_constant("line_separation","ItemList",2); t->set_font("font","ItemList", default_font ); - t->set_color("font_color","ItemList", control_font_color_low ); + t->set_color("font_color","ItemList", control_font_color_lower ); t->set_color("font_color_selected","ItemList", control_font_color_pressed ); t->set_color("guide_color","ItemList", Color(0,0,0,0.1) ); t->set_stylebox("selected","ItemList", item_selected_oof ); @@ -697,8 +703,8 @@ void make_default_theme() { // Tabs - t->set_stylebox("tab_fg","Tabs", sb_expand( make_stylebox( tab_current_png,4,4,4,1,16,4,16,4),2,2,2,2) ); - t->set_stylebox("tab_bg","Tabs", sb_expand( make_stylebox( tab_behind_png,5,5,5,1,16,6,16,4),3,3,3,3) ); + t->set_stylebox("tab_fg","Tabs", sb_expand( make_stylebox( tab_current_png,4,3,4,1,16,3,16,2),2,2,2,2) ); + t->set_stylebox("tab_bg","Tabs", sb_expand( make_stylebox( tab_behind_png,5,4,5,1,16,5,16,2),3,3,3,3) ); t->set_stylebox("panel","Tabs",tc_sb ); t->set_stylebox("button_pressed","Tabs", make_stylebox( button_pressed_png,4,4,4,4) ); t->set_stylebox("button","Tabs", make_stylebox( button_normal_png,4,4,4,4) ); diff --git a/scene/resources/default_theme/hslider_bg.png b/scene/resources/default_theme/hslider_bg.png Binary files differindex 963e4c8456..701c1d43b5 100644 --- a/scene/resources/default_theme/hslider_bg.png +++ b/scene/resources/default_theme/hslider_bg.png diff --git a/scene/resources/default_theme/hslider_grabber.png b/scene/resources/default_theme/hslider_grabber.png Binary files differindex b72ec4d8f4..343d247bc5 100644 --- a/scene/resources/default_theme/hslider_grabber.png +++ b/scene/resources/default_theme/hslider_grabber.png diff --git a/scene/resources/default_theme/hslider_grabber_hl.png b/scene/resources/default_theme/hslider_grabber_hl.png Binary files differindex 0dc5f2b615..f0ba7dd212 100644 --- a/scene/resources/default_theme/hslider_grabber_hl.png +++ b/scene/resources/default_theme/hslider_grabber_hl.png diff --git a/scene/resources/default_theme/scroll_bg.png b/scene/resources/default_theme/scroll_bg.png Binary files differindex 53797886cd..e1e419d663 100644 --- a/scene/resources/default_theme/scroll_bg.png +++ b/scene/resources/default_theme/scroll_bg.png diff --git a/scene/resources/default_theme/scroll_grabber.png b/scene/resources/default_theme/scroll_grabber.png Binary files differindex 16beda1514..3a193e5448 100644 --- a/scene/resources/default_theme/scroll_grabber.png +++ b/scene/resources/default_theme/scroll_grabber.png diff --git a/scene/resources/default_theme/scroll_grabber_hl.png b/scene/resources/default_theme/scroll_grabber_hl.png Binary files differindex acfb7c835b..82c94c03c8 100644 --- a/scene/resources/default_theme/scroll_grabber_hl.png +++ b/scene/resources/default_theme/scroll_grabber_hl.png diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h index dcfaa6e3c0..c338126d3a 100644 --- a/scene/resources/default_theme/theme_data.h +++ b/scene/resources/default_theme/theme_data.h @@ -50,7 +50,7 @@ static const unsigned char button_pressed_png[]={ static const unsigned char checked_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x43,0xbb,0x7f,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x9,0x12,0x0,0x36,0x36,0x55,0x46,0x2e,0x76,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0xbd,0x49,0x44,0x41,0x54,0x38,0x8d,0x8d,0x93,0x4f,0x6b,0x13,0x51,0x14,0xc5,0x7f,0x93,0x99,0x84,0xa1,0xd2,0x8c,0x90,0x49,0xba,0xea,0xb6,0x1f,0xa0,0x8,0x76,0xa3,0xad,0x6d,0x53,0x4a,0x96,0x22,0x4a,0xb,0x2e,0x55,0xfc,0x6,0x36,0x74,0x2b,0x45,0xba,0xaf,0x71,0x61,0x71,0x95,0x42,0x37,0x52,0xdc,0x34,0x85,0x92,0xa2,0xb8,0xb7,0x4,0x4d,0xd5,0xb8,0xd2,0x85,0x98,0xcc,0x22,0x23,0x99,0xc9,0xcc,0xa4,0x6f,0xba,0xc8,0x4c,0x9b,0x92,0x97,0xd2,0xb,0x8f,0xb,0xef,0xbd,0x73,0xce,0xfd,0xab,0xd0,0x37,0x5,0x48,0x0,0x5a,0xe4,0xaf,0x32,0x1,0xf4,0x22,0x1f,0x2a,0x11,0x38,0x9,0x64,0x80,0x49,0x20,0x7d,0x5,0x89,0x0,0x6c,0xe0,0x37,0x60,0x1,0x41,0xac,0x98,0xd1,0x54,0x6d,0x6e,0x61,0x3e,0x5f,0x4e,0x26,0x93,0x52,0x64,0x36,0x97,0x65,0xad,0xf8,0x2,0xd7,0x75,0x99,0x99,0xb9,0xfd,0xd8,0x75,0xdd,0x43,0xe0,0x9f,0xa,0xa4,0x80,0xa9,0xa5,0xfc,0xf2,0xbe,0xe7,0x79,0xd8,0xb6,0x8d,0xe3,0x38,0x43,0xe7,0xd9,0xd3,0x27,0x8c,0xa7,0xc7,0x29,0x6d,0xbd,0x21,0x97,0x9d,0xb8,0xff,0xfd,0xc7,0xc9,0x2e,0xd0,0x4c,0x44,0x11,0x18,0x9a,0xa6,0xd1,0xed,0x76,0xa5,0xea,0x85,0x42,0x81,0xe9,0x5b,0xd3,0xec,0xbd,0xdf,0xa3,0x56,0xab,0x11,0x86,0x21,0x80,0x1,0x24,0xe2,0x5c,0x95,0x41,0x80,0xaa,0xaa,0x97,0x8,0x1e,0x3e,0x7a,0x40,0xe3,0xe7,0x2f,0xaa,0xd5,0x23,0x82,0x20,0x40,0x8,0x41,0x24,0xac,0x48,0x8b,0xb5,0xb2,0xba,0x42,0xa9,0xf4,0x1a,0xc3,0x30,0xb8,0x3b,0x77,0x87,0xb1,0x1b,0x63,0x54,0x2a,0x7,0x74,0x3a,0x9d,0xa1,0xbf,0x9a,0x8c,0x60,0xa7,0xbc,0xc3,0xe2,0xe2,0x3c,0x1b,0x1b,0x2f,0xe9,0x7a,0x1e,0xdf,0xbe,0xd6,0x39,0xfe,0x72,0x2c,0x4d,0x4f,0x1a,0x81,0x10,0x82,0x62,0x71,0x9d,0xf4,0x4d,0x3,0xd3,0xcc,0x50,0xd9,0x3f,0xc0,0xb2,0xac,0xeb,0x13,0x84,0x61,0x88,0xd5,0x6a,0xb1,0xf9,0x6a,0x13,0xdb,0xfe,0x4f,0xbd,0x5e,0x97,0x82,0x47,0x12,0x0,0x78,0x9e,0x8f,0xe3,0xb8,0x6c,0xbf,0xdd,0xa6,0xdd,0x6e,0x8f,0x24,0x88,0x6b,0x10,0xca,0x1e,0x1b,0x8d,0x6,0xa6,0x99,0x89,0xdb,0x76,0xa1,0xda,0x6f,0xde,0x79,0x2b,0x4,0xd0,0xee,0xf5,0x7a,0xe8,0xba,0x3e,0x94,0x4a,0xb3,0xd9,0xba,0x74,0xa7,0xeb,0x3a,0xbe,0xef,0x43,0x7f,0xa4,0x4f,0x15,0x40,0x5,0x26,0x52,0xa9,0x54,0xfe,0xde,0xec,0xc2,0xbb,0x51,0xa3,0x1c,0x5b,0x10,0x4,0x7c,0xfa,0xfc,0xf1,0xb9,0xe3,0x74,0x3e,0x0,0x7f,0x7,0x97,0xc9,0xe4,0xfa,0xcb,0xf4,0x7,0x68,0x1,0x7e,0x3c,0x81,0x83,0xeb,0xac,0xca,0xb1,0xe7,0x76,0xca,0xc0,0x3a,0x9f,0x1,0x62,0x9,0xad,0x4a,0x1e,0xbc,0xe7,0x4d,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdf,0xb,0xd,0x16,0x30,0x0,0x6d,0xeb,0x4,0xa7,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0xc1,0x49,0x44,0x41,0x54,0x38,0xcb,0xad,0x93,0x41,0x6b,0x13,0x51,0x14,0x85,0xbf,0x77,0x33,0xe3,0x80,0x5d,0x58,0x82,0x13,0x5b,0x28,0xa6,0xdb,0xda,0x3f,0xd0,0x76,0x67,0x17,0x5d,0xa4,0x2d,0x23,0xa,0x62,0xc0,0x5f,0xa0,0x69,0x37,0x82,0xd0,0x5d,0xb0,0x8b,0x52,0xa1,0x20,0x14,0x9,0x48,0x5,0x3,0x52,0x29,0x36,0x48,0x28,0x2e,0x14,0x8a,0xab,0x26,0xf5,0x7,0x84,0xb1,0xbb,0x11,0xa,0xb6,0x49,0x9,0xd9,0xa6,0x49,0xdf,0x73,0xe1,0x4c,0x48,0x71,0xd2,0x8d,0x5e,0x78,0xf0,0xe0,0x9e,0x7b,0xee,0xb9,0xf7,0x9d,0x7,0xff,0x18,0xa,0x60,0x71,0x3e,0xeb,0x0,0xe3,0xc0,0x4,0x90,0x2,0x24,0xe,0x3c,0x34,0x74,0x5d,0x1e,0x65,0xef,0xdf,0x7a,0xbb,0xf5,0x7e,0x1b,0x8,0xf6,0x3e,0x7f,0x68,0x5b,0x61,0x6e,0xbc,0x71,0xf6,0x6b,0xe3,0xe4,0xf4,0x78,0x4e,0x6b,0x6d,0x2b,0xa5,0x62,0xbb,0xe5,0x72,0x4f,0xf0,0xee,0x65,0xb8,0xe6,0x58,0x8f,0xb,0xaf,0xdf,0x65,0x80,0xa3,0x88,0x60,0xe2,0xe4,0xf4,0x78,0xe,0xb0,0x2d,0xcb,0x26,0x8e,0x20,0x9d,0x4e,0xb3,0xb4,0xf4,0x14,0x63,0xc,0xc5,0x62,0xf1,0x76,0xa8,0xf6,0x28,0x92,0x9a,0xd2,0x5a,0xdb,0x22,0x9,0x12,0x89,0x4,0x22,0xc2,0xe4,0xe4,0x9d,0xde,0x5d,0x44,0x58,0x5d,0xcd,0xe3,0x38,0xe,0xbb,0xbb,0x25,0xaa,0xd5,0x43,0x2b,0x1c,0xb5,0x37,0xab,0x28,0xa5,0x7a,0x9d,0x37,0x37,0x5f,0x51,0x2e,0x97,0x98,0x9e,0x9e,0x2,0xc0,0xf3,0x16,0x99,0x99,0x99,0xa2,0xd9,0x6c,0xb2,0xbe,0xbe,0x11,0xe1,0x84,0x41,0xcb,0xaa,0xd5,0x6a,0x0,0x64,0xb3,0xf,0x19,0x1e,0xbe,0xc1,0xca,0xca,0x73,0x0,0xd6,0xd6,0x5e,0xd2,0x6a,0xb5,0x2e,0x61,0xad,0x38,0x82,0x52,0xe9,0x13,0xcb,0xcb,0x39,0x66,0x67,0xef,0x72,0x7e,0xde,0x21,0x99,0x4c,0x52,0xa9,0x1c,0x52,0x2e,0xef,0xfd,0x85,0x8d,0x55,0xd0,0x68,0x9c,0xb1,0xbf,0xff,0xd,0xdb,0xb6,0x59,0x58,0xc8,0xd0,0x6e,0xb7,0xc9,0xe7,0x5f,0xc4,0xbe,0x8c,0xc,0x32,0xc8,0xce,0xce,0xc7,0x3f,0x46,0x51,0x8a,0x42,0xe1,0xd,0x41,0xf0,0x33,0x16,0x67,0xd,0x22,0x38,0x38,0xa8,0xe0,0x79,0xf,0x70,0xdd,0x9b,0x54,0xab,0xdf,0x7,0x3a,0x31,0x22,0xd0,0xc6,0x18,0x8c,0x31,0xbd,0x84,0x31,0x6,0xdf,0xff,0x81,0xef,0x5f,0x2e,0xe8,0xc3,0xe9,0x7e,0x82,0xba,0x88,0x74,0xb4,0xbe,0xb0,0x23,0xd9,0x71,0x61,0x8c,0x41,0xeb,0xb,0x44,0xa4,0xb,0xd4,0xfb,0x77,0xe0,0x8f,0xa4,0xc6,0xbe,0x2,0xdd,0x6e,0xb7,0xc3,0x55,0x7,0x54,0x67,0x24,0x35,0xf6,0x5,0xf0,0xfb,0x15,0x4,0xae,0x3b,0xfa,0xcc,0x75,0x47,0xb7,0xae,0xfa,0x4c,0xa1,0xec,0x7a,0x58,0x1c,0xf0,0x3f,0xe2,0x37,0x4f,0x82,0xa8,0x62,0xbb,0xcc,0x8d,0x11,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; @@ -150,17 +150,17 @@ static const unsigned char hseparator_png[]={ static const unsigned char hslider_bg_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xdd,0x0,0xdd,0x0,0xdd,0xf5,0x15,0x8,0x9d,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x9,0x14,0xf,0xc,0x8,0x9f,0xb9,0xf5,0x45,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x0,0x4d,0x49,0x44,0x41,0x54,0x38,0x8d,0x63,0x60,0x18,0x5,0x14,0x3,0x46,0x18,0x43,0x42,0x42,0x62,0x1a,0x3,0x3,0x43,0x26,0x91,0xfa,0xa6,0xbf,0x78,0xf1,0x22,0xb,0xdd,0x80,0xff,0xec,0xec,0x9c,0x44,0xe9,0xfe,0xf9,0xf3,0x3b,0xc3,0x8b,0x17,0x2f,0x18,0x19,0x18,0x18,0x18,0x98,0x88,0x76,0x2b,0xe,0x30,0xf0,0x6,0xb0,0x20,0xb1,0xa7,0xff,0xfc,0xf9,0x9d,0xe8,0x40,0xa4,0xd4,0xe2,0x51,0x80,0x4,0x0,0x2b,0x51,0x10,0x8d,0x9f,0x1f,0x30,0xd7,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdf,0xb,0xd,0x16,0x2c,0x16,0x7f,0x48,0xec,0xab,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0x47,0x49,0x44,0x41,0x54,0x38,0xcb,0xc5,0x92,0x31,0x8e,0x52,0x61,0x14,0x85,0xbf,0xff,0x5e,0x22,0xfc,0xbc,0x79,0x3a,0xcd,0xd3,0x4,0x42,0x62,0x61,0x78,0x15,0x95,0xcb,0x98,0xb8,0x0,0x13,0x66,0x1,0x66,0xda,0xd9,0x89,0xb1,0xb6,0x30,0xc1,0x5,0x18,0x97,0xe1,0x2,0x4,0xb,0xd,0x19,0xa,0x5f,0xcc,0x20,0x4f,0x78,0x23,0x70,0xff,0xdf,0x2,0xd0,0x40,0x43,0x22,0xc5,0x9c,0xe4,0x54,0xe7,0x7c,0xb7,0xb9,0x7,0xee,0x5b,0xe,0xe0,0xc5,0xc5,0xcb,0xff,0x82,0x3f,0x7c,0x7c,0x8f,0xdb,0xc2,0x35,0x20,0x3,0x72,0xa0,0x3,0x78,0x40,0xe,0xfa,0x1,0xa8,0x80,0x31,0xf0,0x19,0x28,0x80,0x75,0x6d,0xb,0x3f,0x2b,0xcb,0x9f,0x57,0xdf,0x8b,0x9b,0xcb,0x45,0x35,0x3f,0xb,0x21,0xaa,0x73,0xfb,0x74,0x8c,0x20,0xe2,0xac,0xe9,0x93,0x5f,0x8f,0xb3,0xf6,0xbb,0x34,0x7d,0xf4,0x6,0xf8,0xa2,0x79,0xb7,0xf7,0x64,0x56,0x4e,0xaf,0xbf,0x7e,0x1b,0xbd,0x5a,0xaf,0xd7,0x5e,0x44,0x45,0x55,0x10,0xd1,0x3d,0xab,0xa,0xce,0x89,0x2c,0x57,0xcb,0xc6,0xed,0xf4,0xc7,0x73,0xef,0x13,0xa9,0xd7,0x1b,0x9f,0x4,0xc8,0x8b,0x62,0xd2,0x77,0xce,0xa9,0xaa,0xa2,0xba,0x83,0xe4,0xc0,0x9b,0x4c,0x55,0x71,0xce,0x69,0x51,0x4c,0xfa,0x40,0x5e,0x3,0x3a,0x8b,0x6a,0x9e,0xee,0xa0,0x63,0x12,0x11,0x62,0x8c,0x2c,0xaa,0x79,0xa,0x74,0xe4,0xd4,0x37,0xa,0x30,0x6e,0xfa,0xa4,0xc,0x21,0x10,0x42,0x38,0xa,0xec,0x7a,0x4d,0x9f,0x94,0xc0,0x58,0x80,0x61,0x96,0xb5,0x6,0x31,0x46,0x33,0x33,0xcc,0x8c,0x10,0xec,0x6f,0xf1,0x9f,0x37,0x99,0x99,0x11,0x63,0xb4,0x2c,0x6b,0xd,0x80,0xa1,0xe6,0xdd,0x5e,0x55,0xaf,0x37,0x46,0xde,0x27,0xba,0x5a,0xde,0x75,0x97,0xab,0xdf,0xf,0xcc,0x82,0x8b,0x71,0xff,0x80,0x59,0x0,0x62,0x68,0xfa,0x64,0xd6,0x6e,0x3d,0x7d,0xfb,0x30,0x3d,0x7f,0xd,0x8c,0x4f,0x1e,0xd2,0xc9,0x53,0xbe,0x7f,0xfd,0x1,0xde,0x4b,0xa1,0x14,0xaf,0xc,0xa2,0x3a,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; static const unsigned char hslider_grabber_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0x92,0x0,0x92,0x0,0x99,0x25,0xc1,0x88,0x71,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x9,0x12,0x0,0x2,0x21,0x6d,0xbf,0x58,0x46,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0x4b,0x49,0x44,0x41,0x54,0x38,0x8d,0xa5,0x93,0x31,0x6b,0xc2,0x40,0x14,0xc7,0xff,0x77,0xbd,0x34,0x26,0x97,0x5a,0x2c,0x41,0xa,0x9d,0x6b,0x8,0x86,0x2e,0xe,0xfd,0x4,0x1d,0xb2,0xf8,0x29,0x3a,0x15,0xec,0x87,0xa9,0x63,0xfb,0x3d,0x9c,0x3b,0x76,0xd0,0x82,0x8,0xe1,0x14,0xdc,0x82,0x74,0xc8,0x50,0xc4,0xdc,0x5d,0x32,0xd8,0xe5,0x22,0xa1,0x60,0x95,0xfa,0x1f,0x1f,0xef,0xf7,0xe3,0xdd,0xe3,0x1d,0x70,0x62,0xc8,0xef,0x42,0x10,0x4,0x37,0x0,0x6,0x0,0x62,0x0,0x1d,0x53,0x9e,0x3,0x18,0x1,0x18,0xa,0x21,0xd2,0xbd,0x82,0x20,0x8,0x1e,0x1,0xbc,0x84,0x61,0xe4,0xb8,0x2e,0x87,0x65,0x9d,0x3,0x0,0xca,0xb2,0x40,0x9e,0x6f,0x90,0x24,0x33,0x9,0xe0,0x59,0x8,0xf1,0x56,0x31,0x67,0x75,0xb8,0xd5,0xba,0x7a,0xed,0x76,0xef,0x2c,0xcf,0x6b,0x82,0x31,0x6,0x42,0x8,0x8,0x21,0x60,0x8c,0xc1,0x71,0x5c,0xb4,0xdb,0xd7,0x96,0x52,0xb2,0xcf,0x39,0x4f,0xb3,0x2c,0x9b,0xec,0x26,0x30,0x63,0x2f,0x7a,0xbd,0x7b,0xc7,0xb6,0x1b,0x7f,0xbe,0x59,0x6b,0x85,0xf1,0xf8,0x43,0x2,0xb8,0x15,0x42,0xa4,0xd4,0xd4,0x7,0x61,0x18,0x1d,0x84,0x1,0xc0,0xb6,0x1b,0x8,0xc3,0xc8,0x31,0x7b,0x42,0x25,0x88,0x5d,0x97,0x1f,0x84,0xab,0x98,0xde,0xb8,0x2e,0xe8,0x54,0xb,0x3b,0x26,0xa6,0xb7,0x53,0x17,0xfc,0x3b,0x95,0x60,0x5e,0x96,0xc5,0xd1,0x90,0xe9,0x9d,0xd7,0x5,0xa3,0x3c,0xdf,0x1c,0x2d,0x30,0xbd,0xa3,0xba,0x60,0x98,0x24,0x33,0xa9,0xb5,0x3a,0x8,0x6b,0xad,0xaa,0x83,0x1a,0x2,0xe6,0x90,0xb2,0x2c,0x5b,0xfb,0xbe,0xff,0xa5,0x94,0xec,0x37,0x9b,0x97,0x60,0x8c,0xed,0x85,0x97,0xcb,0x5,0x94,0x92,0x4f,0x42,0x88,0xf7,0x9d,0xc0,0x48,0x26,0x9c,0xf3,0x74,0xb5,0x4a,0x1f,0x3c,0xef,0xc2,0xa2,0x94,0x82,0x52,0x8a,0xed,0x76,0x8b,0xa2,0xd0,0x58,0xaf,0xbf,0x31,0x9d,0x7e,0x4a,0x3,0xef,0x4e,0xf9,0xe4,0xcf,0x74,0x72,0x7e,0x0,0xd9,0x87,0x82,0x9b,0x21,0x12,0xa2,0x6e,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdf,0xb,0xd,0x15,0x1a,0x26,0xd2,0xcb,0xf3,0x2b,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0xae,0x49,0x44,0x41,0x54,0x38,0xcb,0xa5,0x93,0x3f,0x8f,0x12,0x41,0x18,0xc6,0x7f,0x3b,0xbb,0xcb,0xc2,0x62,0xb2,0x85,0xc5,0xd1,0x10,0x8b,0xa5,0x93,0x5c,0x61,0xc9,0x77,0xb0,0xa2,0xb1,0xb1,0xdd,0xca,0x4f,0xe0,0xa7,0xb0,0x20,0x39,0x42,0x63,0x41,0x63,0x48,0xae,0x54,0x1a,0xb,0xa,0xb,0x8b,0x23,0x4,0xa3,0xd,0x39,0x42,0x22,0x24,0x86,0x9c,0xde,0xf1,0x3f,0xcc,0x32,0xbb,0x63,0xb3,0x87,0x66,0xef,0xc0,0x4b,0x7c,0xab,0xc9,0x33,0x33,0xbf,0xbc,0xef,0xf3,0xcc,0xc0,0x7f,0x96,0x71,0x8f,0x66,0x2,0x2e,0xe0,0x1,0xf9,0x44,0x5b,0x3,0x73,0x60,0x3,0x44,0xc7,0x0,0x26,0xf0,0x38,0x8,0x82,0x17,0xbe,0xef,0x57,0x3d,0xcf,0x3b,0x5,0x98,0xcf,0xe7,0x5f,0x86,0xc3,0xe1,0x79,0xa3,0xd1,0x78,0x7,0xfc,0xfa,0x1b,0x62,0xa6,0x0,0x8f,0x82,0x20,0x78,0x59,0x2e,0x97,0x5f,0x1b,0x86,0xf1,0x74,0xb5,0x5a,0x65,0xb7,0xdb,0x6d,0x36,0x93,0xc9,0x3c,0x29,0x14,0xa,0xcf,0x8a,0xc5,0xe2,0x4d,0xb7,0xdb,0xfd,0x6,0x84,0xb7,0x17,0x44,0xa,0xe0,0xf9,0xbe,0x5f,0x55,0x4a,0x9d,0x6c,0x36,0x1b,0x94,0x52,0x28,0xa5,0x48,0xd6,0x27,0xbe,0xef,0x57,0x93,0xd1,0x38,0x4,0xc8,0x7b,0x9e,0x77,0x2a,0xa5,0x44,0x6b,0xbd,0x17,0xb5,0xd6,0x48,0x29,0x49,0x46,0xca,0x1f,0x3,0x18,0x71,0x1c,0x1b,0x87,0x1c,0x8f,0xa2,0xc8,0x48,0xfb,0x96,0x6,0x84,0x52,0xca,0x2b,0xd3,0x34,0xef,0xc6,0x65,0x18,0x48,0x29,0xaf,0x1,0x75,0xc,0xb0,0x5e,0x2c,0x16,0x5f,0x6d,0xdb,0xbe,0x3,0xb0,0x6d,0x1b,0xad,0xf5,0x8f,0x24,0xca,0x83,0x80,0x65,0xbf,0xdf,0xff,0xe0,0x38,0x4e,0x24,0xc4,0x9f,0x2d,0x21,0x4,0xb9,0x5c,0x4e,0x77,0x3a,0x9d,0xb7,0xc0,0xe2,0x18,0x40,0xf6,0x7a,0xbd,0xb,0x29,0xe5,0xc4,0xb2,0xac,0xbd,0x68,0x59,0x16,0xbb,0xdd,0xee,0x7b,0xbb,0xdd,0xfe,0xf4,0xaf,0xe,0xe2,0xd1,0x68,0x34,0x9e,0x4e,0xa7,0x1f,0x5d,0xd7,0x45,0x8,0x81,0x10,0x2,0xd7,0x75,0x19,0x8f,0xc7,0xef,0x95,0x52,0x57,0x40,0x7c,0xc,0xa0,0x81,0x79,0xad,0x56,0x7b,0x13,0x45,0xd1,0xa5,0xe3,0x38,0x38,0x8e,0x43,0x1c,0xc7,0xc3,0x7a,0xbd,0x7e,0x96,0x6e,0xff,0xbe,0x97,0x8,0x10,0x87,0x61,0xb8,0x9e,0xcd,0x66,0x97,0x95,0x4a,0xe5,0x79,0x36,0x9b,0x8d,0x5b,0xad,0xd6,0xab,0xc1,0x60,0x70,0x1,0x6c,0x1f,0x2,0x0,0x50,0x93,0xc9,0xe4,0x67,0xa9,0x54,0xba,0x59,0x2e,0x97,0x9f,0x9b,0xcd,0xe6,0x39,0x30,0x7b,0xe8,0x6f,0xdc,0x7b,0x7,0x38,0xc9,0x99,0x6d,0x3a,0xff,0xdb,0xfa,0xd,0x29,0xd4,0xb4,0x4b,0x76,0xdc,0xe7,0x79,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; static const unsigned char hslider_grabber_hl_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0x92,0x0,0x92,0x0,0x99,0x25,0xc1,0x88,0x71,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x9,0x12,0x0,0x2,0x1d,0x42,0xd0,0x24,0xc1,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0x30,0x49,0x44,0x41,0x54,0x38,0x8d,0xa5,0x93,0xb1,0x6a,0xc2,0x50,0x14,0x86,0xbf,0x63,0xe2,0x90,0xd0,0x2e,0xc1,0xad,0x8b,0xad,0x60,0x9e,0xc0,0xbe,0x41,0x7,0x33,0x38,0x7,0x1d,0xba,0x74,0x2a,0xe8,0xc3,0xe8,0x68,0xc1,0x49,0xc8,0xec,0x10,0x9f,0xa1,0x8,0xee,0x71,0xcf,0x52,0x4d,0x70,0x73,0x8a,0xb7,0x83,0x37,0x72,0x11,0xb4,0xa1,0xfe,0xe3,0xe5,0x7c,0xdf,0xbd,0x1c,0xfe,0xb,0x77,0x46,0x2e,0xf,0x7c,0xdf,0x7f,0x2,0x86,0x40,0x17,0x68,0xeb,0xe3,0xd,0xb0,0x4,0x26,0x49,0x92,0xa4,0x57,0x5,0xbe,0xef,0x7f,0x0,0xe3,0x30,0x1c,0x38,0x9e,0xd7,0x40,0xa4,0x6,0x80,0x52,0x47,0xf2,0x7c,0x47,0x14,0xcd,0xf,0xc0,0x28,0x49,0x92,0xaf,0x92,0xb1,0x4c,0xb8,0xd9,0x7c,0x9e,0xf6,0xfb,0xef,0x75,0xd7,0x7d,0xd4,0xb0,0x0,0x82,0x48,0xd,0xd7,0x7d,0xa0,0xd3,0x79,0xad,0x6f,0xb7,0x3f,0x3d,0xdb,0xb6,0xd2,0x2c,0xcb,0xd6,0x0,0x35,0xe3,0xd9,0xe3,0x20,0xe8,0x21,0x62,0x71,0x2d,0x22,0x16,0x41,0xd0,0x3,0x18,0x6b,0xe6,0x24,0x0,0x86,0x61,0x38,0x70,0x6e,0xc1,0xa6,0x24,0xc,0x7,0x8e,0xde,0xd3,0x59,0xd0,0xf5,0xbc,0xc6,0x9f,0x70,0x19,0x3d,0xdb,0x35,0x5,0xed,0x72,0x61,0x55,0xa2,0x67,0xdb,0xa6,0xe0,0xdf,0x29,0x5,0x1b,0xa5,0x8e,0x95,0x21,0x3d,0xbb,0x31,0x5,0xcb,0x3c,0xdf,0x55,0x16,0xe8,0xd9,0xa5,0x29,0x98,0x44,0xd1,0xfc,0xa0,0x54,0x51,0xe1,0xf6,0xa2,0x2c,0xd4,0xe4,0x2c,0xd0,0xf5,0x1c,0xc5,0xf1,0x82,0x5b,0x12,0xa5,0xa,0xe2,0x78,0x1,0xa7,0x36,0xa6,0x60,0x34,0x31,0xcb,0xb2,0xb5,0x6d,0x5b,0xe9,0x6a,0xf5,0xfd,0xd6,0x6a,0xbd,0xd4,0x1d,0xc7,0x41,0x44,0x34,0x78,0x24,0xcf,0xb7,0xcc,0x66,0xd3,0xc3,0x7e,0xbf,0xff,0x34,0xab,0x7c,0xf7,0x67,0xba,0x3b,0xbf,0x4d,0x78,0x75,0x34,0x1f,0x21,0x5d,0xa6,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdf,0xb,0xd,0x15,0x17,0x2d,0xf0,0xb7,0x54,0xee,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0xcb,0x49,0x44,0x41,0x54,0x38,0xcb,0xa5,0x93,0xcd,0x4a,0x1b,0x51,0x1c,0xc5,0xcf,0x9d,0x3b,0x33,0x4e,0x3a,0x43,0x7,0x9b,0x16,0x93,0x82,0x14,0x8b,0x50,0xa8,0x61,0x28,0xf5,0x1,0xba,0xb5,0xb,0x41,0xc8,0x46,0x4,0xdf,0xa0,0x4f,0x50,0x70,0x95,0x95,0xb8,0x77,0x5b,0x10,0x84,0x6e,0xa2,0xab,0x4a,0x17,0x42,0x5a,0x74,0xe1,0xc2,0x82,0xa8,0xb5,0x14,0x12,0xa,0x2a,0xda,0xd4,0xc4,0x4c,0x32,0x33,0x72,0xe7,0xeb,0x5e,0x37,0x93,0x5a,0x26,0x31,0x16,0x3c,0xcb,0xc3,0xbd,0x3f,0xce,0xff,0xb,0xb8,0xa7,0x48,0x1f,0x8f,0x2,0x78,0x0,0xc0,0x4,0xa0,0x27,0x9e,0x7,0xa0,0xd,0xe0,0xa,0x40,0x3c,0x8,0x40,0x1,0x64,0xa7,0x4a,0xa5,0xd9,0x5c,0xc1,0x2a,0x2a,0x46,0xde,0x2,0x80,0xd0,0x3d,0xdf,0xff,0x7d,0xb8,0x5f,0xfe,0xbc,0xb0,0xf0,0x11,0x40,0xf3,0x5f,0x8,0x4d,0x1,0x8c,0xa9,0x52,0x69,0x3e,0xff,0xea,0xcd,0x7b,0xb7,0x6d,0x4e,0xd8,0x67,0xbe,0xe6,0x36,0x23,0x8d,0x13,0xf3,0x59,0x76,0x6c,0xf4,0x75,0xee,0xe5,0xf3,0x56,0xb5,0x52,0xf9,0xe,0x20,0xe8,0x7e,0x90,0x52,0x0,0x33,0x57,0xb0,0x8a,0x4e,0x4b,0x1d,0x71,0x1b,0xc,0x21,0x8b,0x11,0xb2,0x18,0x6e,0x83,0xc1,0x69,0xa9,0x23,0xb9,0x82,0x55,0x4c,0x4a,0xc3,0x6d,0x0,0x5d,0x31,0xf2,0x16,0xeb,0x4,0x10,0x5c,0xfc,0x35,0x5,0x17,0x60,0x9d,0x0,0x49,0x49,0xfa,0x20,0x0,0xe1,0x11,0x27,0x42,0xf4,0x76,0x56,0x70,0x80,0x47,0x31,0x49,0xf7,0x2d,0xd,0x8,0x78,0xe4,0x5e,0xc8,0x8a,0xd4,0x3,0x90,0x64,0x82,0x38,0xf0,0x2e,0x1,0x44,0x83,0x0,0x5e,0xe0,0xd4,0xf,0x55,0x5d,0xee,0x1,0xa8,0x19,0x19,0x94,0x6,0xe7,0xc9,0x28,0x6f,0x5,0x38,0xbf,0xb6,0xb6,0x37,0x32,0xf,0x95,0x58,0xa2,0x37,0x49,0x25,0x4a,0xa0,0xf,0xab,0xe2,0x60,0xbd,0xfc,0x1,0x40,0x67,0x10,0xc0,0xaf,0x7d,0xfd,0xb2,0x2b,0x11,0xfb,0x54,0xd1,0x6e,0x52,0x28,0x9a,0xc,0x99,0x76,0x8e,0xbf,0xad,0xac,0x6c,0xdf,0x95,0x80,0xd7,0x8f,0x7e,0x9c,0x78,0xf5,0xda,0xa6,0xf1,0x78,0x8,0x12,0x25,0x90,0x28,0x81,0xf1,0x44,0x83,0x7d,0x72,0xf4,0x29,0xe,0xc3,0xb,0x0,0xfc,0xae,0x55,0x56,0x34,0xd3,0x7c,0x31,0xb7,0xba,0xb6,0x6e,0xff,0xc9,0x8c,0xb,0x1,0x3c,0x7a,0xea,0xd7,0x56,0x67,0x67,0x66,0x58,0xbb,0xfd,0x13,0x40,0x98,0x5e,0xdd,0xb4,0x78,0xe4,0xfb,0xde,0xd5,0x65,0xbd,0x5a,0x98,0x7e,0x3b,0x6d,0x64,0x15,0xbe,0xb3,0xbc,0xf4,0xee,0x6c,0x6f,0x6f,0x17,0x0,0xeb,0x77,0x38,0xfd,0x14,0x35,0xab,0xb5,0xc6,0xe8,0xe4,0x44,0x2b,0x70,0x1a,0x3b,0x95,0xc5,0xc5,0x32,0x0,0xfb,0x7f,0xaf,0xb1,0x2b,0x19,0xc0,0x50,0xf2,0x86,0xa5,0xe7,0xdf,0xd5,0x35,0xea,0x59,0xb2,0xa3,0x9f,0xba,0x1f,0x74,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; @@ -310,7 +310,7 @@ static const unsigned char reference_border_png[]={ static const unsigned char scroll_bg_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x8,0x8,0x6,0x0,0x0,0x0,0xc4,0xf,0xbe,0x8b,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0x26,0x0,0x26,0x0,0x26,0x59,0xf,0xde,0x74,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x9,0x14,0x17,0x37,0x2c,0x8d,0x3d,0xc,0x64,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x0,0x3f,0x49,0x44,0x41,0x54,0x18,0x95,0xad,0x8e,0x31,0xa,0x0,0x20,0xc,0x3,0xaf,0xda,0x47,0xf4,0xe5,0xf6,0xb3,0x4a,0x5d,0x1c,0x54,0x50,0x17,0xf,0x42,0x96,0x24,0x44,0xcc,0x8c,0x1b,0x69,0x78,0x1,0x62,0x53,0x1,0x90,0xb1,0x10,0xb5,0xb6,0xa5,0xa9,0x9a,0x1,0x24,0xf1,0xe0,0x5f,0xc0,0x55,0x33,0xb3,0x0,0x9f,0x4f,0x1e,0xe9,0xf,0x1d,0xb,0x68,0x95,0x6b,0x4f,0xeb,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0xc,0x8,0x6,0x0,0x0,0x0,0x56,0x75,0x5c,0xe7,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdf,0xb,0xd,0x15,0x25,0x28,0x6d,0xad,0xf4,0x10,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0x3c,0x49,0x44,0x41,0x54,0x28,0xcf,0x95,0x92,0x31,0x6e,0x13,0x61,0x10,0x46,0xdf,0xcc,0x44,0xd8,0xff,0x2e,0xb,0x34,0xb,0x92,0x23,0x4b,0x14,0xc8,0x5b,0xa5,0xe2,0x18,0x88,0x3,0x20,0x25,0x7,0x40,0xb4,0xb9,0x49,0x44,0x4d,0x81,0x14,0xe,0x80,0x38,0x6,0x7,0x20,0xa1,0x0,0x59,0xa4,0x60,0x85,0x8,0x59,0xbc,0xb,0xb6,0xff,0x19,0x8a,0xc5,0xd,0x28,0x45,0x9e,0xf4,0x15,0xdf,0xa7,0x27,0x4d,0x33,0x2,0xf0,0xf4,0xc9,0xb3,0x3d,0xa0,0x6,0x1a,0x60,0xe,0x24,0x46,0x6,0x60,0x9,0x7c,0x0,0xda,0xb7,0xef,0xde,0x6c,0xe5,0xaf,0xfc,0xa8,0xeb,0x7e,0xbc,0xf8,0xda,0x7e,0x39,0xea,0x87,0xd5,0x6d,0xf7,0x30,0x0,0x55,0xc9,0x45,0x2a,0x7f,0xde,0xaf,0xf7,0x5f,0x57,0xd5,0xdd,0x97,0xc0,0x47,0x6b,0x16,0x7,0xf,0xae,0xba,0xcb,0xe3,0x4f,0x9f,0xcf,0x9f,0x6f,0xb7,0xdb,0xa4,0x6a,0x6a,0xa6,0x98,0x29,0x22,0xaa,0xeb,0xcd,0x7a,0xfa,0xfd,0xf2,0xdb,0xe3,0x94,0x4a,0x9d,0x4c,0xa6,0xef,0x15,0x68,0xda,0xf6,0xe2,0x50,0x44,0xcc,0xcc,0x30,0x33,0x54,0xc7,0xec,0xba,0x88,0x58,0xdb,0x5e,0x1c,0x2,0xcd,0x1e,0x30,0xef,0x87,0x55,0x35,0x4a,0xca,0xbf,0xa8,0x2a,0x11,0x41,0x3f,0xac,0x2a,0x60,0xae,0x40,0xe1,0x1e,0x2a,0xc2,0xb5,0x88,0x80,0x7b,0x28,0x50,0x28,0x37,0x44,0x81,0x5e,0x55,0x3c,0xe2,0x7a,0x29,0x2,0x54,0xc5,0x81,0x5e,0x81,0x65,0x91,0xca,0xce,0xdd,0x71,0xf7,0xff,0xe4,0xdd,0x5e,0xa4,0xb2,0x3,0x96,0xa,0x9c,0xd5,0xf5,0xec,0x34,0x22,0x72,0xce,0x99,0x9c,0x33,0xee,0x63,0x76,0x3d,0x22,0x72,0x5d,0xcf,0x4e,0x81,0x33,0x6b,0x16,0x7,0xc3,0x64,0x32,0x3d,0x4f,0xa9,0xb4,0xcd,0xfa,0xd7,0x62,0xbd,0xf9,0x7d,0x2b,0x67,0x97,0xf1,0x5a,0x78,0x91,0xca,0xab,0xfd,0xd9,0xc3,0x57,0x77,0xaa,0x7b,0x27,0xc0,0x52,0x6e,0xfa,0x1a,0x7f,0x0,0x2,0xd3,0x92,0x1e,0xd2,0x75,0x7c,0x7f,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; @@ -355,12 +355,12 @@ static const unsigned char scroll_button_up_hl_png[]={ static const unsigned char scroll_grabber_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x8,0x8,0x6,0x0,0x0,0x0,0xc4,0xf,0xbe,0x8b,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0x26,0x0,0x26,0x0,0x26,0x59,0xf,0xde,0x74,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x9,0x14,0x17,0x25,0x29,0x85,0xa3,0x88,0x38,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x0,0x72,0x49,0x44,0x41,0x54,0x18,0x95,0x7d,0xcd,0xb1,0x9,0xc3,0x30,0x0,0x44,0xd1,0x17,0x21,0x91,0x42,0x10,0xdc,0x4,0x34,0x80,0x77,0xc8,0x4,0x6e,0x32,0xb0,0x1b,0xf,0xe2,0x26,0x4d,0x6a,0x37,0xc2,0x5d,0x40,0x45,0x1a,0x1b,0x4c,0x20,0xfe,0xe5,0xbf,0x3b,0xee,0x52,0x4a,0x71,0x46,0x8c,0x31,0x1a,0x86,0x67,0x87,0x19,0x7b,0x7b,0x41,0x3f,0x4d,0xe3,0x1a,0x36,0x31,0xa3,0x3b,0xc,0x6f,0x78,0x41,0x6c,0xad,0x39,0x2c,0x77,0xae,0xb8,0xb7,0xd6,0x84,0x94,0xd2,0xdf,0xff,0x94,0x92,0x90,0x73,0x86,0xf7,0x4f,0xf6,0xc1,0x92,0x73,0x16,0x6a,0xad,0xf0,0xd8,0xe4,0xce,0x8a,0x7e,0xcb,0xce,0xf9,0x2,0x99,0xd9,0x19,0x5e,0xac,0x65,0x2e,0x22,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0xc,0x8,0x6,0x0,0x0,0x0,0x56,0x75,0x5c,0xe7,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdf,0xb,0xd,0x16,0x26,0x17,0xf2,0xa0,0x34,0xb7,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x0,0xd4,0x49,0x44,0x41,0x54,0x28,0xcf,0xbd,0x8f,0x31,0x4a,0x3,0x61,0x10,0x85,0xdf,0xfb,0x67,0xb1,0x91,0x3d,0x80,0x6d,0xca,0x20,0x39,0xc1,0x7a,0xe,0x59,0x2c,0xc4,0x3b,0x8,0xb9,0x43,0xf0,0xe,0x62,0x11,0x82,0xa0,0x65,0x8a,0xd4,0xe6,0x4,0x31,0x24,0x67,0x48,0xbd,0xe5,0xbf,0x33,0x2f,0x8d,0xbf,0xae,0x6c,0xad,0xf,0x6,0x66,0x98,0xf7,0x31,0xf3,0x80,0xbf,0x16,0x87,0xc3,0x5d,0xfb,0x70,0xd,0xe0,0x49,0x52,0x3,0xe0,0x92,0xe4,0x6,0xc0,0xe3,0x72,0xf5,0xbc,0x1f,0x1,0xed,0xed,0xfd,0x8c,0xe4,0xb6,0xef,0xfb,0x3a,0x14,0x0,0x80,0xc4,0x84,0xaa,0xaa,0x3a,0x49,0xcd,0xea,0xf5,0x65,0x7,0x0,0xe9,0x9b,0x24,0x17,0x39,0xe7,0xda,0xdd,0xa1,0x10,0x14,0x82,0xbb,0x23,0xe7,0x5c,0x93,0x5c,0x14,0x5f,0x55,0x1a,0x8f,0x68,0x3c,0x2,0x90,0x7e,0xfd,0xec,0x11,0xb0,0x88,0x9b,0x11,0x20,0x9,0x92,0x46,0x40,0xd9,0x15,0xa5,0x41,0x98,0x2d,0x7,0x60,0xa9,0xaf,0x90,0x1f,0x23,0x20,0x22,0xe6,0x66,0xd6,0x91,0xfc,0xb9,0x46,0xc2,0xcc,0xba,0x88,0x98,0x17,0x9f,0x95,0xe6,0x70,0xfc,0x3c,0x4d,0xa7,0xb3,0xb5,0x99,0x4d,0x52,0x4a,0x57,0x66,0x76,0x61,0x66,0x1b,0x1,0xed,0xdb,0xfb,0x72,0x87,0x7f,0xd3,0x19,0xa9,0x1d,0x69,0xa0,0x52,0x76,0xa0,0x72,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; static const unsigned char scroll_grabber_hl_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x8,0x8,0x6,0x0,0x0,0x0,0xc4,0xf,0xbe,0x8b,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0x26,0x0,0x26,0x0,0x26,0x59,0xf,0xde,0x74,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x9,0x14,0x17,0x25,0x15,0xaa,0xcc,0xf4,0xbf,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x0,0x86,0x49,0x44,0x41,0x54,0x18,0x95,0x7d,0x8e,0xb1,0xa,0x83,0x30,0x18,0x84,0xbf,0x36,0x81,0x6e,0xe5,0xf,0xad,0x6e,0xe,0x11,0x97,0x2e,0xfa,0xae,0x6e,0x7d,0x29,0xa1,0xce,0xa5,0xae,0x5,0x7,0x83,0x53,0xb,0xf9,0xa1,0x4b,0x94,0xe,0xe2,0x8d,0x77,0xdf,0x71,0x77,0x68,0xdb,0x3b,0x7b,0xb2,0x0,0xc3,0xf0,0xba,0x86,0x30,0x75,0x40,0x91,0xfc,0x51,0xc4,0x55,0xde,0x97,0xb3,0x5,0x8,0x61,0xea,0xea,0xba,0xb9,0x18,0x63,0x1,0x50,0x8d,0xe7,0xbe,0x7f,0x3c,0x81,0xdc,0xa6,0x46,0xb1,0x84,0x0,0xc6,0xd8,0x13,0x90,0x1,0x1c,0x77,0xf,0xfc,0x1,0xa3,0x6a,0xfc,0x2e,0xa6,0x6a,0xfc,0x0,0xef,0xf5,0xa4,0x88,0xab,0xd2,0x66,0x96,0x98,0x20,0xe2,0x6e,0x2b,0xe0,0x7d,0x39,0x3,0xf9,0xd6,0xc4,0xf,0x70,0x6e,0x25,0xf5,0x5c,0xbc,0xd7,0xd3,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0xc,0x8,0x6,0x0,0x0,0x0,0x56,0x75,0x5c,0xe7,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdf,0xb,0xd,0x16,0xe,0x1f,0xa1,0x26,0x12,0x2f,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0x3,0x49,0x44,0x41,0x54,0x28,0xcf,0xbd,0x90,0x3f,0x4b,0xc3,0x50,0x14,0xc5,0x4f,0xf2,0xf2,0xca,0x33,0x12,0xf3,0x10,0x44,0x3a,0x74,0x10,0x91,0xae,0x75,0x13,0xe7,0xea,0x17,0xe8,0xa7,0x10,0x67,0x41,0xec,0x1e,0x4,0x67,0xf1,0x5b,0xf4,0x3,0x88,0xb3,0xb8,0xd9,0xb5,0x88,0xb8,0x28,0x5,0xb5,0x35,0xf4,0xc5,0x90,0xe6,0xbe,0x3f,0x4e,0x29,0xd,0x75,0xd5,0x3,0x7,0xee,0xb9,0xf0,0xe3,0x1e,0x2e,0xf0,0xd7,0xf2,0x96,0xc3,0xcd,0xf0,0x65,0x8f,0xc8,0x5c,0xce,0xd2,0xa2,0x6b,0x1d,0x36,0xa4,0x14,0x3,0xde,0x60,0xe7,0x27,0x9d,0x9d,0xa7,0x15,0xe0,0x7a,0xf8,0xdc,0x9e,0x7e,0xe4,0xf,0xe3,0xb1,0x92,0x45,0xa1,0xe1,0xe0,0xb0,0x26,0x38,0x9a,0xcd,0x28,0xdd,0xdc,0xa,0xf,0x4e,0x3b,0xbb,0x23,0x0,0x8,0x2a,0x20,0x2f,0xca,0xe4,0xed,0xf5,0x4b,0x7e,0xab,0xf9,0xe2,0xa2,0x2a,0x4a,0x58,0xa3,0xa5,0x88,0x82,0x4,0x40,0xaf,0x6,0x4c,0x27,0x59,0x37,0x53,0x39,0x34,0xd9,0x5a,0xe7,0x4c,0x19,0x4c,0x3e,0xd5,0x71,0x95,0x17,0x40,0x49,0x1a,0x46,0x13,0xac,0x36,0x35,0xc0,0x78,0xc,0xb4,0xb4,0xf3,0xab,0x41,0x48,0x71,0xc7,0x98,0x83,0xa1,0x79,0xcd,0x8c,0x39,0x8,0x29,0x6e,0x57,0x0,0xcb,0xfd,0x7e,0xdc,0x8a,0x53,0xbe,0xce,0xe1,0x39,0x82,0xe7,0x8,0x41,0x18,0x20,0x6e,0xc5,0xa9,0xe5,0x7e,0xff,0xd7,0xb7,0x9e,0xdd,0x3f,0xb6,0x29,0xa7,0x24,0x7b,0x9f,0x1d,0xc1,0xd9,0x28,0xda,0x8e,0x7,0x41,0xd8,0xb8,0xb8,0x3a,0xdc,0x1f,0xe1,0xdf,0xf4,0x3,0x35,0xb3,0x71,0xac,0x4,0x64,0x3e,0xbb,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; @@ -415,12 +415,12 @@ static const unsigned char tab_menu_hl_png[]={ static const unsigned char toggle_off_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0xa2,0x9d,0x7e,0x84,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0x26,0x0,0x26,0x0,0x26,0x59,0xf,0xde,0x74,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x9,0x14,0x17,0x2,0x16,0xe9,0x0,0x17,0x60,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x2,0x45,0x49,0x44,0x41,0x54,0x68,0x81,0xed,0x99,0x5f,0x4f,0xd3,0x50,0x18,0x87,0x9f,0xb3,0x96,0xb5,0xcc,0x3a,0xa6,0xac,0x6d,0xa,0x91,0x84,0x5b,0x8d,0x7e,0x21,0x60,0x2c,0x5c,0x1a,0x63,0xe2,0x27,0x31,0x31,0xc6,0x4b,0x32,0xa7,0x7e,0x20,0x84,0x6b,0x6f,0xc,0x90,0x6d,0x32,0xca,0x6c,0xb0,0x24,0x75,0xc7,0x8b,0xba,0xb9,0x35,0xdd,0x84,0xec,0xe0,0x26,0xed,0x73,0xd7,0xf7,0xed,0xce,0x7b,0x7e,0xbf,0xbe,0x67,0x7f,0xde,0x41,0x4e,0x4e,0xa6,0x11,0x89,0xeb,0x12,0x50,0x1,0x2c,0x40,0x4b,0xc9,0xff,0xaf,0x48,0xe0,0x27,0x10,0x0,0x3e,0x70,0x39,0x48,0xe8,0x89,0x1b,0x2b,0xc0,0x33,0xe0,0x11,0x60,0x72,0xb7,0xc,0x8,0x81,0xaf,0xc0,0x67,0xa6,0x18,0x60,0x1,0x1b,0x4f,0x1e,0x3f,0x7d,0x6d,0x57,0xed,0x65,0x40,0x48,0x64,0xfc,0x72,0x95,0x8,0x10,0x8,0x74,0x5d,0x67,0x69,0x49,0x47,0xd3,0x92,0xdb,0x50,0x4b,0x14,0x45,0xf2,0xf8,0xe4,0xf8,0xc7,0xe1,0xd1,0xc1,0x2b,0xe0,0xcb,0x68,0x2e,0x59,0x59,0x7,0xc,0xbb,0x6a,0x97,0xce,0xba,0x67,0x4,0x41,0x80,0xec,0xf7,0x91,0x8a,0x1d,0x10,0x8,0x44,0xa1,0x80,0x51,0x2c,0x52,0x2a,0xdd,0xa3,0x58,0x2c,0x2a,0x5d,0x3f,0x89,0x69,0x9a,0x62,0x7d,0x6d,0xbd,0x74,0x78,0x74,0x60,0x90,0xd0,0x9c,0x66,0xbd,0x0,0x8,0x82,0xef,0x54,0x2a,0xf,0x70,0x6c,0x7,0xa1,0xf8,0x24,0x48,0x24,0xed,0x4e,0x1b,0xdf,0x3f,0xc7,0x30,0x4c,0xa5,0x6b,0xa7,0x11,0x86,0x21,0x96,0x65,0x41,0xca,0x91,0x4e,0xed,0x3d,0x29,0x25,0xb2,0x2f,0x71,0x1d,0x97,0x30,0xc,0x39,0x6d,0x9d,0x2a,0xdd,0x90,0xe7,0x7a,0xb8,0x8e,0xcb,0x79,0xb7,0xab,0xbc,0xbb,0x6e,0x4a,0x61,0x52,0x62,0x70,0xf6,0x55,0x8b,0x87,0xdf,0x6b,0x4a,0xe6,0x2e,0x1e,0xa6,0x18,0x90,0x15,0x32,0x6f,0xc0,0xed,0x7e,0xfe,0xcc,0x80,0x65,0x59,0xd4,0xf7,0x6a,0x63,0xb1,0xc6,0x7e,0x93,0x20,0x8,0x78,0xf1,0xf2,0xf9,0x58,0xfc,0xed,0x9b,0x77,0xa9,0xb1,0xeb,0xb0,0xb0,0x6,0xc,0xc4,0x7f,0x78,0xff,0x9,0x80,0x9d,0xdd,0x2d,0xea,0x7b,0xb5,0xa1,0xb0,0x34,0x81,0xd7,0x15,0x3d,0xca,0xc2,0x1a,0x0,0xd0,0x6c,0x7c,0xa4,0xd7,0xeb,0x1,0xb1,0x11,0x3b,0xbb,0x5b,0xc3,0xdc,0xe8,0x13,0x1f,0x8,0x4f,0x8b,0xfd,0x8d,0x85,0x36,0x60,0x1a,0xaa,0x3a,0x60,0xa1,0xdf,0x4,0x6b,0xf5,0x6d,0xca,0xe5,0x32,0xe5,0x72,0x79,0xec,0xe9,0xab,0x24,0x69,0x80,0x9,0xac,0xdc,0x4a,0xa5,0x1b,0xd2,0xd8,0x6f,0x2,0xb1,0x9,0xb5,0xfa,0xf6,0x58,0x6c,0x6,0x56,0x88,0x35,0xe,0x49,0x1e,0x81,0x10,0xb8,0x98,0xb5,0x8a,0xa,0x82,0x20,0x98,0xd8,0xd2,0x33,0xb4,0xff,0x5,0xb1,0xc6,0x21,0xb,0x7d,0x4,0xfe,0x5,0xb9,0x1,0xf3,0xde,0xc0,0xbc,0x99,0x68,0x80,0x88,0xa7,0x16,0x78,0xae,0xa7,0xbc,0xa8,0xe7,0x7a,0xc3,0xa1,0xc8,0xbc,0x49,0xfd,0x1e,0x20,0x84,0x40,0x14,0x4,0xad,0x76,0xb,0xc7,0x76,0xd8,0xdc,0xd8,0x54,0x5a,0x54,0x22,0x69,0xb5,0x5b,0x88,0x82,0x98,0xbb,0x9,0x69,0x6,0x48,0x0,0xcb,0xba,0x8f,0xef,0xfb,0xb7,0xf2,0x9b,0xfd,0xcf,0x44,0xc8,0x40,0xd3,0x34,0xa5,0x6b,0xa7,0x61,0x9a,0x26,0x51,0x14,0x41,0xca,0x70,0x2f,0x69,0x40,0x4,0x5c,0x75,0xbe,0x75,0x2e,0xed,0xaa,0xbd,0xbc,0xfa,0x70,0xf5,0x4e,0xcd,0x4,0x81,0x2b,0x62,0x8d,0xa3,0x5b,0x19,0x63,0x8d,0x6c,0x4c,0x85,0x4f,0x6,0x89,0xcc,0xff,0x2f,0x90,0x93,0x93,0x93,0x6d,0x7e,0x1,0x6b,0xe,0xc1,0xdb,0xd6,0xe0,0xc4,0xba,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0xa2,0x9d,0x7e,0x84,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdf,0xb,0xd,0x15,0x16,0x28,0x99,0xc6,0x91,0x20,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x5,0xb5,0x49,0x44,0x41,0x54,0x68,0xde,0xed,0x99,0xdb,0x6f,0x54,0x55,0x14,0xc6,0x7f,0xfb,0xdc,0xe6,0x72,0x66,0x3a,0x9d,0x76,0x5a,0xa0,0x3,0xe5,0xd2,0xc4,0xb4,0x21,0x26,0x46,0x2,0x31,0xbe,0x11,0xc0,0x17,0x62,0x14,0xe2,0xe5,0xc1,0xc4,0x77,0xc2,0xe5,0xf,0x30,0x31,0xd1,0x18,0x8,0xbc,0x5a,0x2,0xaf,0x3e,0x90,0x80,0xa8,0x28,0x1a,0x5f,0x4,0x9,0x8f,0x62,0x42,0x88,0x1a,0x9c,0x82,0x5c,0x14,0x68,0x11,0x5a,0x3a,0xed,0xdc,0x3b,0xe7,0xb2,0x7d,0x70,0x77,0x98,0xe,0x67,0x86,0x96,0xe,0x97,0x4,0x56,0xb2,0x33,0x33,0xeb,0x5c,0xe6,0xac,0x6f,0x7f,0x7b,0x9d,0xb5,0xbe,0xd,0x2f,0xec,0xf9,0x36,0xb1,0xc0,0x73,0x45,0xdd,0x35,0xe2,0x19,0x89,0x41,0xd6,0x7d,0xca,0xba,0xdf,0x6d,0x3,0x40,0x53,0xc3,0xac,0x1b,0xba,0xf2,0x89,0x67,0x20,0x78,0x1f,0xf0,0x0,0xa7,0x6e,0xf8,0x6a,0x2c,0xa,0x0,0xa1,0x2,0xb5,0x80,0x18,0xd0,0xa9,0x46,0x7,0x10,0x51,0x40,0x3c,0x4d,0x10,0x66,0x83,0x77,0x80,0x32,0x90,0x3,0xa6,0xd4,0x28,0x0,0x55,0x5,0x8c,0x7c,0x14,0x0,0x34,0xc0,0x50,0x81,0xf7,0x0,0xcb,0x53,0xa9,0x25,0xef,0xc6,0x62,0x1d,0x6f,0x1a,0x86,0xde,0x3,0xc2,0x0,0x84,0x10,0x4f,0x97,0x0,0x52,0x4a,0x5,0x84,0x74,0x5d,0xd7,0x1b,0x2f,0x14,0x72,0x3f,0x4c,0x4c,0xdc,0xf9,0xa,0xb8,0x5,0x8c,0x2b,0x20,0xdc,0x56,0x6c,0x10,0x4d,0x82,0xb7,0x80,0x4,0xd0,0xdf,0xd9,0xd9,0xbd,0xad,0xab,0x2b,0xb5,0x5b,0xd3,0x44,0xec,0x7e,0xbc,0x62,0x1e,0x18,0xca,0x26,0x3e,0xa1,0x9e,0xa7,0xfd,0xe4,0x91,0x52,0xe2,0xfb,0x7e,0x61,0x72,0x72,0x62,0x78,0x6a,0xea,0xde,0xb7,0xc0,0xd,0x60,0x5a,0xb1,0xc1,0x9f,0xf,0x0,0x42,0x5,0x9f,0x4,0xd6,0x2c,0x5b,0xb6,0xfc,0x23,0xdb,0x8e,0x6d,0x15,0x2,0xc2,0xc2,0x60,0x95,0x69,0xd3,0x6b,0x84,0xb1,0x35,0x3,0xbd,0xc5,0xec,0x3b,0xd2,0x67,0xd4,0x29,0x73,0xa5,0x9a,0xa7,0x22,0xfd,0x0,0x50,0x7c,0xf5,0x57,0xda,0x63,0x63,0x46,0xb1,0x98,0xff,0xf1,0xf6,0xed,0x5b,0xfb,0x80,0x6b,0x40,0x56,0x81,0x20,0x5b,0x1,0x30,0xbb,0xe6,0x13,0xc0,0x40,0x5f,0xdf,0x8a,0xcf,0xa2,0x51,0xfb,0xd,0x21,0x4,0x6b,0xcc,0x18,0x2b,0x2d,0x9b,0xdb,0x4e,0x85,0x51,0xa7,0x44,0x41,0xba,0x81,0xf3,0x2b,0x80,0x30,0x1a,0x1d,0xba,0x49,0xaf,0x11,0xa6,0xd7,0x8,0x71,0xbd,0x5a,0xe4,0x6f,0xa7,0x18,0x0,0xbf,0x7c,0xac,0xe9,0x43,0x4a,0x49,0xa9,0x54,0xf8,0x69,0x6c,0xec,0xe6,0xc7,0xc0,0x55,0xc5,0x84,0x7,0x72,0x82,0xde,0x40,0xfd,0x30,0xb0,0x2c,0x99,0x4c,0x7d,0x90,0x48,0x74,0x7e,0x28,0x84,0x60,0x6d,0x28,0x41,0x4a,0xf,0xf1,0x47,0x65,0x9a,0x5b,0x6e,0x99,0xea,0x43,0x92,0xab,0x8b,0xa4,0x28,0x3d,0xee,0x7a,0x33,0xe4,0x3c,0x87,0x95,0x96,0x4d,0x87,0x6e,0x72,0xcf,0x9b,0x69,0x0,0xed,0xf1,0xe6,0xf,0x21,0x4,0xa6,0x69,0xd,0x0,0xd3,0xe5,0x72,0xe9,0x12,0x50,0x54,0xf9,0x40,0x36,0xae,0xf7,0xfa,0xef,0x31,0x60,0x79,0x57,0x57,0xf7,0x2e,0x21,0xa0,0xdf,0xb4,0x49,0xe8,0x16,0x17,0xca,0x59,0xa6,0x7c,0x67,0xc1,0xf,0x71,0xcf,0xaf,0xf2,0x5b,0x39,0x4b,0x42,0xb7,0x58,0x61,0x46,0x9f,0xf8,0xeb,0x42,0x8,0x41,0x32,0xd9,0xbd,0x13,0x58,0xa1,0x62,0xd3,0x1b,0x91,0xd7,0xea,0xa6,0xc3,0x4,0x12,0x3d,0x3d,0x4b,0xdf,0xd7,0x34,0x11,0xb1,0x84,0xc1,0x6a,0xd3,0x26,0x53,0xc9,0x51,0x9e,0xdf,0x2b,0x35,0xd0,0x4a,0xf8,0x8c,0x54,0x72,0xac,0x31,0x63,0x58,0x42,0x7b,0xe2,0x6f,0xa,0x4d,0xd3,0x23,0x3d,0x3d,0x4b,0xdf,0x53,0x4b,0xdb,0x8,0xca,0xf8,0xf5,0x0,0x74,0xda,0x76,0x7c,0x2b,0x40,0xda,0x88,0x30,0xe9,0x55,0xc9,0xfa,0xd5,0x45,0x3f,0xc4,0xa4,0x5f,0xe5,0x9e,0x37,0x43,0x9f,0x11,0x69,0x79,0x5e,0x3a,0xdd,0xc7,0xfe,0xfd,0x7b,0xb9,0x7c,0xf9,0x22,0xe7,0xcf,0xff,0xc2,0xf6,0xed,0x6f,0x3,0xb0,0x61,0xc3,0x7a,0x2e,0x5f,0xbe,0x38,0x67,0x4,0xf9,0xe2,0xf1,0x78,0xe0,0x7d,0x6d,0x3b,0xb6,0x55,0x25,0x76,0xab,0x91,0x1,0x46,0x3,0x0,0x1d,0x86,0xa1,0xa7,0x40,0xb0,0xc4,0x8,0xf3,0x8f,0x53,0x6c,0xdb,0x4c,0x8c,0x7b,0x33,0xac,0x34,0x6d,0xae,0xb7,0xb8,0xe7,0x81,0x3,0xfb,0xc8,0xe5,0xf2,0x6c,0xdc,0xb8,0x85,0xa1,0xa1,0x41,0xe,0x1d,0x1a,0xe6,0xdc,0xb9,0x5f,0x6b,0xc7,0xd7,0xad,0x7b,0x8d,0x7c,0x3e,0x5f,0x3,0xa5,0xd1,0xd7,0xcc,0xc,0xc3,0xe8,0x51,0x5,0x9c,0xd1,0xc,0x80,0xd9,0x84,0x18,0x9e,0xf5,0xc5,0x34,0x83,0x69,0xcf,0x69,0x1b,0x0,0xd3,0x9e,0x43,0x2c,0x64,0xb4,0x9c,0xfd,0xd,0x1b,0xd6,0xd7,0x2,0x1a,0x1d,0x1d,0xe3,0xf4,0xe9,0x33,0x6c,0xde,0xbc,0x89,0x4c,0x66,0x84,0x4c,0x66,0xe4,0x81,0x6b,0x82,0x7c,0x4d,0xb2,0x81,0xa1,0xaa,0xd7,0xa6,0x39,0xa0,0xfe,0x35,0x28,0x0,0x4c,0xa1,0x51,0x96,0x5e,0xdb,0x0,0x28,0x4b,0xf,0xb3,0x45,0xe,0x48,0xa7,0xd3,0x64,0x32,0x23,0x73,0x66,0x33,0x93,0xc9,0x30,0x34,0x34,0x8,0x40,0x3e,0x9f,0xe7,0xf0,0xe1,0x61,0x8e,0x1c,0xf9,0xa2,0x46,0xf5,0x20,0x5f,0xb3,0x64,0xd8,0xac,0xf2,0x32,0x2,0x6a,0xeb,0x5a,0x31,0x13,0x11,0x3a,0xa5,0x36,0x81,0x10,0x11,0x3a,0x8e,0x6c,0x9e,0x4c,0x33,0x99,0x11,0x86,0x86,0x6,0x89,0xc7,0xe3,0x35,0x10,0xd2,0xe9,0xf4,0x1c,0x40,0x76,0xec,0xd8,0xfd,0x0,0xdd,0x83,0x7c,0x4d,0x4a,0x66,0x3f,0xa8,0x10,0xaa,0x9f,0x12,0xef,0xff,0xa6,0x42,0xba,0x20,0x29,0xf8,0x2e,0x9,0xdd,0x6c,0x1b,0x3,0x12,0xba,0x49,0xc1,0x77,0x9b,0x1e,0xcf,0xe7,0xf3,0x64,0x32,0x23,0xb5,0xc4,0x17,0x8f,0xc7,0xd9,0xb2,0x65,0xd3,0x2,0x68,0xde,0x12,0x2,0x57,0x35,0x4c,0x5e,0xb3,0x3a,0x40,0xaa,0xae,0x2a,0xef,0xba,0xfe,0x38,0xc0,0x1d,0xb7,0x42,0x4a,0xf,0xb5,0xd,0x80,0x1e,0x3d,0xc4,0x1d,0xb7,0xd2,0xf2,0x9c,0xbd,0x7b,0xf7,0xb3,0x67,0xcf,0x4e,0x4e,0x9e,0xfc,0x86,0xb3,0x67,0x4f,0x71,0xea,0xd4,0xcf,0x9c,0x38,0xf1,0xdd,0xa2,0xff,0xdb,0x75,0xdd,0x71,0xd5,0x2d,0xba,0xf,0xab,0x4,0x2d,0xd3,0xb4,0x7a,0xc3,0xe1,0xf0,0xab,0x25,0xe9,0xf3,0x92,0x15,0x27,0xe7,0xb9,0x54,0x16,0xb9,0xc,0xba,0x34,0x8b,0xd5,0x56,0x8c,0x3f,0xab,0x39,0xbc,0x16,0xdd,0xe9,0xe8,0xe8,0x18,0x47,0x8f,0x7e,0x9,0xc0,0xf0,0xf0,0x21,0x8e,0x1d,0x3b,0x3e,0x87,0x21,0xd7,0xae,0x5d,0xa7,0x5a,0xad,0xb6,0xf4,0x5,0xb3,0x6b,0xfa,0x78,0xa9,0x54,0x38,0x3,0x4c,0x2a,0x10,0x2,0xeb,0x51,0x3,0xe8,0x6,0x5e,0x1e,0x18,0x18,0xfc,0x5e,0xd3,0x44,0xa4,0xdf,0xb4,0x59,0x6e,0x46,0xb9,0x50,0x9a,0x7c,0xe4,0x62,0x28,0x8a,0xc6,0x2b,0xd1,0x2e,0x46,0x9d,0x12,0x37,0x9c,0xd2,0xc2,0xe4,0x9a,0x36,0x98,0xef,0x7b,0xe5,0xab,0x57,0x2f,0xbd,0x5,0xfc,0x5e,0x7,0x80,0xc,0x62,0x40,0xad,0x43,0x11,0x2,0x37,0x12,0x89,0xbe,0x9e,0xf3,0x1d,0x3a,0x34,0x93,0x55,0x96,0x4d,0xde,0x77,0x1b,0x3a,0xbb,0x87,0x5b,0xb7,0x66,0xb1,0x36,0x9c,0x20,0xe7,0x3b,0x5c,0xad,0x16,0x9e,0x78,0xf0,0x52,0x4a,0xb2,0xd9,0x89,0xcf,0xcb,0xe5,0xd2,0x59,0x60,0x22,0xa8,0x23,0x6c,0x4,0xc0,0x7,0xbc,0x72,0xb9,0x74,0x3b,0x1c,0x8e,0xac,0x34,0x4d,0x73,0x60,0xdc,0x9b,0xc1,0x14,0x1a,0x83,0xe1,0xe,0xc2,0x42,0x67,0xc6,0xf7,0x70,0x9a,0xb0,0x41,0x0,0x11,0x34,0xba,0x75,0x8b,0x35,0x56,0x8c,0x81,0x50,0x8c,0x51,0xb7,0xc2,0x5f,0xd5,0xfc,0x22,0x8a,0xe9,0xc5,0x75,0x83,0x77,0xef,0xfe,0x7b,0x50,0x9,0x24,0xc5,0x20,0x4d,0xa0,0x95,0x1e,0x30,0xd0,0xd7,0xd7,0xff,0x49,0x34,0x1a,0xdd,0x2c,0x4,0x84,0x84,0xce,0x6a,0x33,0xb6,0x48,0x3d,0xe0,0x49,0x6,0x5f,0x3c,0x3d,0x36,0x76,0xe3,0x53,0xe0,0xca,0x7c,0xf5,0x80,0x46,0x45,0xa8,0x13,0xe8,0x4f,0x26,0xbb,0xb7,0x25,0x93,0xdd,0xbb,0x74,0x5d,0x8b,0xdd,0xef,0xe1,0x9b,0x9,0xc3,0xf,0x23,0xf9,0xec,0xf5,0x8d,0xf7,0x69,0x9f,0x79,0x9e,0x57,0xc8,0x66,0x27,0xe,0x66,0xb3,0x35,0x45,0x68,0x6a,0x21,0x8a,0x50,0x3d,0x8,0xa6,0x6a,0x21,0x53,0xc0,0x8a,0x54,0x6a,0xc9,0x3b,0x4a,0x13,0xec,0x5,0x61,0x3c,0x6d,0x3d,0x70,0x6e,0x91,0x23,0x5d,0xd7,0xf5,0xee,0x2a,0x4d,0xf0,0x6b,0xe0,0xa6,0x5a,0xf3,0x85,0x3a,0x95,0xf8,0x91,0x55,0xe1,0x10,0x60,0x2b,0x46,0x24,0x3,0x54,0xe1,0xa7,0x69,0x8d,0xaa,0x70,0x56,0xcd,0x78,0x11,0x98,0x59,0x8c,0x2a,0x5c,0x7f,0x7c,0x16,0x8,0x43,0x2d,0xd,0xb3,0xae,0x67,0x78,0x16,0xf6,0x5,0x24,0xf7,0xf7,0x5,0xaa,0xea,0x35,0xe7,0x31,0xcf,0x4d,0x92,0x47,0xdd,0x19,0x7a,0x56,0x76,0x85,0x1a,0x81,0x58,0xf0,0xce,0xd0,0xb,0x7b,0x61,0xcf,0xb9,0xfd,0x7,0xde,0xc4,0x73,0xf5,0xe8,0x6c,0xed,0xda,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; static const unsigned char toggle_on_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0xa2,0x9d,0x7e,0x84,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0x26,0x0,0x26,0x0,0x26,0x59,0xf,0xde,0x74,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x9,0x14,0x17,0x2,0x12,0xee,0x6d,0xd3,0x79,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x2,0xa7,0x49,0x44,0x41,0x54,0x68,0x81,0xed,0x99,0x4d,0x4f,0x13,0x51,0x14,0x86,0x9f,0x99,0x4e,0x6c,0x4b,0xc6,0x52,0xc5,0xb6,0x49,0x1b,0x5d,0xb1,0xf4,0x63,0x69,0x28,0x89,0xfc,0x7,0x63,0x42,0x8,0xff,0x40,0x11,0xc4,0x8d,0x51,0x2,0xb,0x5,0x56,0x86,0x8f,0xb8,0x51,0xff,0x0,0x69,0x20,0x24,0x6,0x24,0xfc,0x1,0x68,0x58,0x82,0x11,0x77,0x6e,0xc,0x24,0x2d,0x6,0x3a,0x49,0xad,0xd3,0x32,0xe5,0xba,0x18,0xdb,0x94,0x71,0x2,0xb1,0xb9,0x63,0x23,0x9d,0x67,0xd5,0xde,0x73,0xe6,0xcc,0x7b,0xde,0xe9,0x9d,0xcc,0x9c,0x82,0x8f,0x4f,0x5b,0xa3,0x38,0xbe,0x77,0x0,0x51,0x40,0x7,0x2,0x2e,0xf1,0xff,0x15,0x1,0x54,0x81,0x22,0x50,0x0,0x4a,0xb5,0x80,0xe6,0x48,0x8c,0x2,0xb7,0x81,0xeb,0x40,0x88,0x8b,0x65,0x80,0x9,0x7c,0x3,0x76,0x38,0xc3,0x0,0x1d,0xb8,0x71,0xeb,0xe6,0x9d,0xd9,0x54,0x32,0x15,0xd6,0x34,0xcd,0x53,0x3,0xaa,0x55,0x8b,0xe3,0x63,0xb,0xcb,0xb2,0x10,0x8,0x5b,0xa6,0x4c,0x14,0x50,0xec,0x6b,0x28,0xe,0xbe,0x1f,0xfc,0xfc,0xbc,0xfb,0x69,0x14,0xf8,0xda,0x98,0xe2,0x34,0x40,0x3,0x82,0xa9,0x64,0xaa,0xc3,0x34,0x4d,0x4c,0xd3,0x94,0xac,0xe8,0x34,0x95,0x4a,0x85,0x52,0xe9,0x7,0xe5,0x4a,0x5,0x71,0x72,0x62,0x9b,0x20,0x11,0x5,0x5,0x45,0x55,0xd1,0x75,0x5d,0x89,0x5d,0x8b,0x75,0x0,0x41,0x1c,0x3d,0x3b,0xd,0x0,0x50,0x34,0x4d,0xf3,0xbc,0x79,0x80,0x6a,0xb5,0x4a,0xb9,0x52,0x26,0x1a,0xbd,0x42,0x3c,0x16,0xaf,0x5d,0x2d,0x69,0x8,0x4,0xf9,0x83,0x3c,0x85,0xc2,0x11,0x5d,0x57,0xbb,0xc0,0x65,0x4b,0xbb,0x19,0xf0,0xcf,0x10,0x8,0xc4,0x89,0x20,0x11,0x4f,0x60,0x9a,0x26,0xf7,0x1f,0xf4,0x4b,0xad,0xbf,0xbc,0x94,0x21,0x11,0x4f,0x70,0x74,0x78,0x88,0x10,0xee,0xbf,0x2e,0x55,0xea,0x19,0x9b,0xa0,0xb6,0xf7,0x65,0x37,0xf,0xbf,0x6b,0xa,0xce,0xdc,0x5a,0x2d,0x37,0xa0,0xd5,0xf8,0x6,0x34,0x73,0x90,0xae,0xeb,0x8c,0x4f,0x8c,0xb1,0xb6,0xbe,0xca,0xda,0xfa,0x2a,0xe3,0x13,0x63,0xe8,0xba,0xe,0x40,0x76,0x6b,0x83,0xe7,0x2f,0x9e,0xd5,0x73,0xb3,0x5b,0x1b,0x72,0x94,0x7a,0x44,0x53,0x6,0x8c,0x3e,0x1d,0x1,0x60,0xa0,0x7f,0x90,0x81,0xfe,0x41,0x54,0x35,0x50,0x5f,0x3,0x48,0xf7,0xa6,0xe9,0xee,0xee,0x96,0xa3,0xd0,0x63,0x9a,0x32,0x20,0xdd,0x9b,0x66,0x7e,0xee,0xd,0x86,0x61,0x60,0x18,0x6,0x73,0xb3,0xf3,0xf4,0xa4,0x7b,0xea,0xf1,0x77,0x6f,0xdf,0xf3,0x64,0x74,0x58,0x9a,0x48,0x2f,0xf1,0xe4,0x1e,0xb0,0xba,0xf2,0x91,0x50,0x38,0x4c,0x5f,0xdf,0x3d,0x2f,0xca,0x4b,0xa5,0x29,0x3,0x36,0x37,0x36,0x79,0x3c,0x3c,0x44,0x24,0x12,0x21,0x12,0x89,0x30,0x3c,0x32,0x44,0x76,0x33,0x7b,0x2a,0x67,0xe6,0xf5,0xc,0x8f,0x86,0x1e,0x4a,0x11,0xe9,0x25,0x4e,0x3,0x42,0x40,0xe7,0x79,0x7,0xcd,0xce,0xcc,0xa3,0xaa,0xa,0x99,0xc5,0x5,0x32,0x8b,0xb,0xf5,0xb5,0x46,0x76,0x77,0xbf,0xb0,0xbd,0xbd,0x23,0x4b,0xa7,0x2c,0x3a,0xb1,0x7b,0xac,0xe3,0x7c,0x12,0x34,0x1,0xe3,0xbc,0x2a,0xc5,0x62,0x91,0x57,0x2f,0xa7,0x5c,0x63,0x3d,0x77,0x7b,0xeb,0x9f,0xa7,0x26,0xa7,0x99,0x9a,0x9c,0xfe,0x6b,0x95,0x1e,0x62,0x60,0xf7,0x58,0xc7,0x7f,0xe,0x68,0xb5,0x80,0x56,0xe3,0x1b,0xd0,0x6a,0x1,0x8a,0x3d,0xb5,0x60,0x79,0x29,0x23,0xbd,0xf6,0xf2,0x52,0xa6,0x71,0x28,0xe2,0x4a,0x4b,0x5f,0x87,0xed,0x81,0x85,0x42,0x2e,0x9f,0x23,0x1e,0x8b,0xb3,0xb6,0xf2,0x41,0x6a,0xfd,0x60,0x30,0x48,0x2e,0x9f,0x43,0x51,0x15,0x14,0xc5,0xdd,0x4,0x37,0x3,0x84,0x65,0x59,0x84,0x42,0x21,0xcf,0x87,0x22,0x81,0x40,0x80,0xe0,0xa5,0x20,0x85,0x42,0xc1,0x7e,0x67,0xf7,0x6c,0x22,0x74,0xb9,0xb6,0xf4,0xc7,0x9,0x9c,0x6,0x58,0x40,0x79,0x6f,0x7f,0xaf,0x94,0x4a,0xa6,0xc2,0xba,0xae,0x5f,0xa8,0x99,0x20,0x50,0xc6,0xee,0xb1,0x31,0xe5,0x14,0x49,0xda,0x63,0x2a,0xbc,0x5f,0xb,0xb4,0xfd,0xff,0x2,0x3e,0x3e,0x3e,0xed,0xcd,0x2f,0xbd,0x80,0xe4,0x2f,0x1f,0x1c,0x6a,0x6c,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x40,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0xa2,0x9d,0x7e,0x84,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdf,0xb,0xd,0x15,0xc,0x32,0xd4,0x89,0x92,0x81,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x5,0xba,0x49,0x44,0x41,0x54,0x68,0xde,0xed,0x99,0x5d,0x6c,0x93,0x55,0x18,0xc7,0x7f,0xe7,0xbc,0xdd,0xdb,0x8e,0xf6,0xed,0xba,0x76,0x8d,0x8e,0x7d,0xf0,0x31,0x15,0x88,0x9a,0x11,0x95,0x85,0x18,0x24,0x12,0x21,0xde,0x1a,0xae,0x4d,0x8c,0xd1,0xe0,0x85,0x89,0x37,0x5e,0x68,0xe4,0xca,0xb,0xcc,0x48,0xbc,0x23,0x7a,0x41,0xd4,0xa0,0x21,0x41,0x4d,0x20,0x70,0x69,0xc0,0x8f,0xe0,0x47,0xc,0x86,0x44,0x8c,0xda,0x39,0x1,0x27,0x6c,0x40,0xe8,0xe7,0xf6,0xb6,0x5b,0xfb,0xb6,0xef,0x7b,0xbc,0xe8,0x59,0xa9,0xa5,0xd4,0x32,0x96,0x6d,0x6,0x4e,0x72,0xd2,0xf4,0xeb,0xbc,0xf9,0xff,0x9f,0xff,0x79,0xce,0xf3,0xfc,0xf,0xdc,0x1b,0x77,0xf7,0x10,0xb7,0xf9,0x5b,0x51,0xf7,0x1f,0xb1,0x42,0x30,0xa8,0xba,0x57,0x55,0xf7,0x7e,0xd1,0x8,0x90,0x7a,0x76,0xd4,0x4d,0x43,0x7f,0x26,0x56,0x0,0x78,0xf,0x70,0x81,0x72,0xdd,0xf4,0xf4,0xbc,0x23,0x2,0x84,0x6,0x6a,0x2,0x21,0x20,0xa2,0x67,0x18,0xe8,0xd4,0x44,0x2c,0x27,0x9,0xf3,0xe0,0xcb,0xc0,0x1c,0x30,0x3,0xe4,0xf4,0xcc,0x3,0x8e,0x26,0x46,0x2d,0x84,0x0,0x9,0xf8,0x34,0xf0,0x38,0xd0,0x6f,0x59,0x5d,0x4f,0x59,0x56,0xd7,0x2e,0xd3,0x34,0xd7,0x4b,0x69,0x84,0xa5,0x94,0x9d,0x42,0x88,0x65,0x55,0x80,0x52,0x4a,0x79,0x9e,0x37,0xe7,0x79,0xee,0x8c,0xe3,0x38,0x17,0x6d,0x7b,0xfa,0xa4,0x6d,0x4f,0x7f,0xb,0x4c,0x2,0x49,0x4d,0x44,0xa5,0x95,0x1a,0xc4,0x2d,0xc0,0x9b,0x40,0x17,0x30,0xb8,0x6a,0x55,0x68,0x7b,0x2c,0x16,0xdf,0xe3,0xf7,0x7,0x1e,0x5a,0x66,0xbc,0xed,0x10,0x42,0xa9,0x54,0x1c,0x4f,0xa7,0x93,0x7,0x67,0x67,0xf3,0xa7,0x81,0x4b,0xc0,0xb4,0x56,0x83,0xd7,0xe,0x1,0x42,0x83,0xef,0x6,0xd6,0x77,0x77,0xf7,0x3c,0x1f,0x8d,0xf6,0xbc,0x24,0xa5,0x34,0x97,0x1c,0x8d,0x3f,0x84,0x1c,0x18,0x46,0xf6,0xac,0x45,0x74,0x46,0x40,0xfa,0x6e,0xd,0xbc,0x52,0x42,0x5d,0xfb,0x3,0x77,0xe2,0x27,0x28,0xe5,0xf1,0x3c,0xcf,0xc9,0x64,0x52,0x1f,0x66,0xb3,0xa9,0xc3,0xc0,0x45,0x20,0xab,0x49,0x50,0xad,0x8,0x98,0xdf,0xf3,0x5d,0xc0,0x50,0x34,0x1a,0x7f,0x39,0x1a,0xed,0x79,0x79,0xc9,0x65,0x2e,0xd,0x64,0xff,0x30,0xb2,0xff,0x51,0xbc,0xeb,0x17,0xf0,0xae,0x8e,0xc1,0x6c,0x16,0x54,0x93,0x0,0xa,0x1,0xfe,0x10,0x58,0x3d,0xc8,0xd8,0x1a,0x64,0x6c,0x1d,0xde,0xe4,0xcf,0x78,0x97,0xcf,0xa1,0xdc,0x8a,0xca,0x64,0x52,0x1f,0x64,0x32,0xc9,0xf,0x80,0xb,0x5a,0x9,0x37,0xe5,0x4,0xa3,0x41,0xfa,0x1,0xa0,0x37,0x18,0xb4,0x9e,0x8d,0xc7,0xef,0x7b,0x5d,0x4a,0x69,0x2c,0x39,0xf8,0x7,0xb6,0x21,0xa2,0x3,0xb8,0x63,0x5f,0xa3,0xae,0xfe,0xe,0xe5,0xd9,0x16,0x79,0x4c,0x41,0xa5,0x4,0xb3,0x59,0x54,0x6a,0x2,0xcf,0xbe,0x8e,0xec,0x1f,0x46,0x84,0x62,0x90,0x9b,0x14,0x1,0xbf,0x7f,0xd8,0x71,0x4a,0x7f,0x95,0xcb,0xce,0x65,0xa0,0xa0,0xf3,0xc1,0x2d,0x9,0x30,0x74,0x96,0x7f,0xb0,0xb7,0xb7,0xff,0xed,0x8e,0x8e,0x8e,0xd8,0xd2,0x56,0x24,0x2,0xb9,0xfa,0x61,0x44,0xcf,0x1a,0xdc,0x5f,0xbf,0x80,0x42,0xfa,0xf6,0xd7,0x28,0xda,0xa8,0xec,0x24,0x72,0x60,0x18,0x21,0x4,0xe4,0x53,0xd2,0x34,0xcd,0xa1,0xe9,0xe9,0xec,0x69,0xbd,0xd,0x4a,0x8d,0x4,0xc8,0x3a,0xf9,0x77,0x0,0x5d,0xe1,0x70,0x64,0xbb,0xdf,0x1f,0x58,0xb7,0xe4,0x7b,0xde,0x17,0x40,0xe,0x6c,0xc6,0xfd,0xf3,0x7b,0x28,0xce,0x2c,0x7c,0x9d,0xe2,0xc,0xee,0xf9,0x1f,0x90,0x83,0x8f,0x41,0x47,0x27,0x7e,0x7f,0x60,0x5d,0x38,0x1c,0x79,0x4a,0x6f,0x6d,0x5f,0xb3,0x8c,0x5f,0x4f,0x40,0x24,0x14,0xa,0xef,0x6c,0xf7,0x59,0x7d,0x7d,0xab,0x19,0x1d,0xdd,0xc7,0xf8,0xf8,0x6f,0x9c,0x3d,0xfb,0x23,0xbb,0x77,0x3f,0x7,0xc0,0xc8,0xc8,0x16,0xc6,0xc7,0x7f,0x63,0xd3,0xa6,0x8d,0xb5,0xf7,0x87,0xf,0x1f,0x6a,0xad,0xfe,0xde,0x8d,0x78,0xb9,0x29,0x98,0xbe,0x72,0xe7,0x64,0xe6,0xa6,0xf0,0xb2,0x93,0xc8,0xfb,0x37,0x0,0x10,0xa,0x59,0x3b,0x75,0x62,0x37,0x1b,0x13,0x7f,0x23,0x1,0x61,0xd3,0x34,0xdb,0x8e,0xfe,0xfe,0xfd,0xef,0x10,0xe,0x87,0xd9,0xb1,0x63,0x17,0x6f,0xbc,0xf1,0x16,0xa3,0xa3,0xfb,0xe8,0xeb,0x5b,0x5d,0xfb,0x7e,0xef,0xde,0x37,0xdb,0xdf,0x1,0xb1,0xb5,0xa8,0xcc,0xa5,0xc5,0x3b,0x12,0x53,0x7f,0x23,0x62,0x6b,0x1,0x30,0x4d,0xff,0x90,0x2e,0xe0,0x7c,0x8d,0x4,0xf8,0x1a,0x72,0x40,0x40,0x4a,0x23,0xdc,0x6e,0xf4,0x47,0x46,0xb6,0xf0,0xf8,0xe3,0x5b,0xb1,0x6d,0x9b,0xa9,0xa9,0x2b,0x9c,0x3a,0xf5,0x15,0x3b,0x77,0x3e,0x43,0x22,0x31,0x46,0x22,0x31,0xc6,0xa6,0x4d,0x1b,0x19,0x19,0xd9,0xd2,0x1e,0x1,0xc1,0x28,0xca,0x4e,0x2e,0x1e,0x1,0xf9,0x24,0x22,0x18,0xad,0x2,0x33,0x7c,0x11,0x5d,0xbd,0x1a,0xb7,0x52,0x40,0xed,0x18,0x94,0x52,0x76,0xb6,0x47,0x40,0x1f,0x89,0xc4,0x18,0xb6,0x6d,0xd7,0x3e,0x4b,0x24,0x12,0x35,0xd9,0xdb,0xb6,0xcd,0xa1,0x43,0x9f,0xb4,0xad,0x2,0xd1,0x11,0x80,0x62,0x7e,0xf1,0x72,0x4a,0x31,0x5f,0x5d,0x13,0xd0,0x75,0x4c,0xd3,0xb2,0x5d,0x36,0xd6,0xd6,0x4a,0xa9,0x72,0x3b,0xeb,0xcf,0x47,0xd8,0xb2,0xac,0x7f,0x91,0x52,0x4f,0xc8,0x81,0x3,0xef,0x13,0xe,0x87,0x6b,0xb9,0xa1,0x65,0xc4,0xca,0x45,0x8,0x84,0x16,0x8f,0x80,0x40,0xa8,0xba,0x26,0xe0,0x79,0xde,0x7c,0x25,0xa8,0x5a,0x11,0xe0,0x2,0x73,0xae,0x5b,0xc9,0xb5,0xb3,0xbe,0x6d,0xdb,0x24,0x12,0x63,0x35,0x70,0x96,0x65,0xb1,0x6b,0x57,0x55,0xfe,0xf5,0xe3,0xc0,0x81,0xf7,0xda,0x23,0xa0,0x90,0x41,0x58,0xf1,0xc5,0x3b,0x55,0x43,0x71,0x54,0x21,0x53,0x5,0x56,0xc5,0x34,0xd7,0xac,0x10,0x92,0x75,0xd1,0x2f,0x3,0xb6,0xe3,0x38,0x17,0xda,0x7d,0xc8,0xbe,0x7d,0xa3,0xbc,0xf6,0xda,0xab,0x9c,0x38,0x71,0x94,0x6f,0xbe,0x39,0xc9,0xc9,0x93,0x5f,0x72,0xec,0xd8,0xf1,0x7f,0xfd,0xe6,0xd8,0xb1,0xe3,0x37,0x91,0xd2,0x94,0x80,0xf4,0x4,0x22,0x3a,0xb8,0x78,0x4,0xf4,0xac,0x41,0xa5,0x27,0x0,0x70,0x9c,0xd2,0x5,0xdd,0x2d,0x56,0xfe,0xab,0x12,0x34,0x85,0x10,0x56,0x28,0x64,0x6d,0x6f,0xe7,0x21,0x53,0x53,0x57,0x38,0x72,0xe4,0xb3,0x9a,0xdc,0x3f,0xfd,0xf4,0xf3,0x9b,0x14,0x2,0x70,0xee,0xdc,0x2f,0xa4,0x52,0xa9,0x96,0x44,0xa8,0xd9,0x69,0x8c,0xf5,0x5b,0xf1,0xec,0x14,0x94,0xec,0x3b,0x43,0x1f,0xe9,0xc3,0x18,0xdc,0x8c,0x3b,0x7e,0x1a,0xdc,0x32,0xd9,0x6c,0xfa,0xe3,0x52,0xa9,0x78,0x6,0xc8,0x68,0x12,0x9a,0xf6,0x2,0x3e,0x20,0x6,0x3c,0x3a,0x38,0xb8,0xfe,0xe0,0x92,0x17,0x43,0x42,0x20,0x57,0x3f,0x82,0xe8,0xdd,0x58,0xad,0x4,0x17,0x5a,0xc,0x5,0xc2,0x18,0x8f,0x3c,0x8b,0xba,0x36,0x86,0x37,0xf5,0x2b,0xa5,0xe2,0xdc,0x5f,0x97,0x2e,0x5d,0x7c,0x5,0xf8,0xa5,0x8e,0x0,0xd5,0x4c,0x1,0xf3,0x5b,0x41,0x54,0x2a,0x95,0x7c,0x30,0x68,0x3d,0x2d,0x84,0x90,0x4b,0xda,0xce,0x16,0xd2,0x88,0x60,0xc,0x39,0x30,0x8c,0x2a,0x64,0xa1,0x94,0xbf,0xfd,0xc8,0x6f,0xd8,0x81,0xb2,0xaf,0xe3,0xfd,0x7d,0x16,0xcf,0xad,0x54,0x92,0xc9,0x6b,0xef,0x96,0xcb,0xce,0x19,0x20,0xd5,0xac,0x23,0x6c,0x24,0xc0,0x3,0xdc,0x72,0xd9,0x99,0x16,0x42,0xc8,0xce,0xce,0x55,0x8f,0x2d,0x69,0x37,0xa8,0x14,0x2a,0x3b,0x89,0xf0,0x99,0x18,0x43,0x4f,0x82,0xdf,0x42,0x95,0x66,0xab,0xd,0xcf,0x4d,0xb6,0xa4,0x0,0x21,0x21,0x60,0x41,0x77,0x1f,0x72,0x70,0x33,0xc6,0x9a,0x27,0xaa,0x91,0x9f,0x38,0x83,0x72,0x2b,0x2a,0x9b,0x4d,0x7f,0x34,0x33,0x93,0x3d,0xaa,0xd,0x92,0x42,0x33,0x4f,0xa0,0x95,0x1f,0x30,0x14,0x8b,0xc5,0x5f,0x8c,0x44,0x62,0x2f,0x48,0xd9,0xa2,0x19,0x5f,0x99,0x7e,0x40,0x25,0x97,0x4b,0x7f,0x9c,0x4e,0x27,0xf,0x1,0xe7,0xdb,0xf5,0x3,0x1a,0x1d,0xa1,0x8,0x30,0x18,0xc,0x86,0xb6,0x47,0xa3,0xf1,0x3d,0x81,0x40,0xe7,0x83,0xff,0x7,0x9b,0xbb,0x58,0x9c,0xfb,0x33,0x93,0x49,0x1e,0x2c,0x14,0x6a,0x8e,0x50,0xee,0x76,0x1c,0xa1,0x7a,0x12,0x3a,0xb4,0x27,0xd8,0x3,0xc,0x58,0x56,0xd7,0x36,0xed,0x9,0x3e,0x60,0x18,0xbe,0xc8,0xb2,0xb8,0x44,0x4d,0x86,0xe7,0x79,0x8e,0xeb,0x56,0x72,0x8e,0xe3,0x9c,0xd7,0x9e,0xe0,0x77,0xc0,0x65,0xbd,0xe7,0xf3,0x75,0x2e,0xf1,0x82,0x5d,0x61,0x3f,0x10,0xd4,0x8a,0xe8,0x6e,0xe2,0xa,0x2f,0x2b,0xfe,0x6,0x57,0x38,0xab,0x23,0x5e,0xd0,0xbd,0xff,0x82,0x5d,0x61,0x1a,0xb2,0x8e,0xa1,0x8f,0x49,0x93,0x1b,0xf7,0x2,0x82,0x95,0x71,0x2f,0xa0,0xb8,0x71,0x2f,0xe0,0xe8,0x63,0xce,0xa5,0xcd,0x4b,0x92,0x85,0xde,0xc,0xad,0x34,0x7b,0x58,0xb1,0xc0,0x9b,0xa1,0x7b,0xe3,0xde,0xb8,0xcb,0xc7,0x3f,0xd2,0xdb,0x54,0x98,0x63,0x18,0x87,0x7d,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; @@ -465,7 +465,7 @@ static const unsigned char tree_title_pressed_png[]={ static const unsigned char unchecked_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0x0,0x0,0x0,0x0,0x0,0xf9,0x43,0xbb,0x7f,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x9,0x11,0x14,0x5,0x3b,0xd6,0x6,0x93,0xb9,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x0,0xf2,0x49,0x44,0x41,0x54,0x38,0x8d,0xed,0x93,0xb1,0x4a,0xc4,0x50,0x10,0x45,0xcf,0x4b,0x26,0x71,0x2a,0xb7,0x58,0xc1,0xca,0x6f,0xb0,0xb4,0x74,0x75,0x59,0x11,0xbf,0xc1,0x1f,0xf0,0xaf,0xac,0xfc,0x1,0x61,0x11,0x45,0x85,0x5d,0xb6,0xb0,0xf4,0x1b,0xb4,0x10,0xd1,0x42,0xc1,0x65,0x30,0xb3,0x59,0x8b,0x24,0x10,0x10,0xf2,0x82,0xb5,0xb7,0x99,0xea,0xdc,0x79,0xf3,0xb8,0x37,0x50,0x29,0x0,0x9,0x20,0xf5,0xec,0x52,0x9,0x78,0x3d,0xd7,0xa1,0x86,0x33,0x60,0x8,0xec,0x0,0x9b,0x1d,0x26,0x25,0xf0,0x9,0x3c,0x1,0xef,0x40,0xd1,0x6c,0x1c,0x4a,0x2a,0xa3,0xf1,0xe1,0xe4,0x22,0xcb,0xb2,0xce,0xf5,0x45,0x51,0x70,0x7b,0x7f,0x73,0xea,0xee,0x77,0xc0,0x6b,0x0,0x36,0x80,0xdd,0xe3,0xa3,0x93,0x7,0x33,0xc3,0xcc,0x3a,0xd,0x54,0x15,0x55,0xe5,0xea,0x7a,0xba,0x7,0x3c,0x26,0xf5,0xb,0x6,0x22,0x12,0x85,0x1,0xcc,0xc,0x11,0x1,0x18,0x0,0x49,0x73,0x6b,0x88,0x92,0xbf,0x95,0x0,0x21,0xf6,0xe3,0xbd,0x5c,0xfe,0xd,0x2a,0xad,0xff,0xc0,0x96,0x8d,0x41,0x9,0x7c,0xb8,0x3b,0xaa,0x1a,0xa5,0x54,0x15,0x77,0x87,0x2a,0xd2,0xab,0x0,0xa4,0xc0,0x76,0x9e,0xe7,0x93,0x83,0xfd,0xf1,0x79,0x9f,0x28,0xcf,0x17,0xb3,0xb3,0xe5,0xf2,0xeb,0x12,0x78,0x69,0x97,0x69,0x8b,0xfe,0x65,0x7a,0x6,0xde,0x80,0xef,0x26,0x81,0xed,0x3a,0xa7,0x91,0x2b,0x56,0xb4,0xea,0xfc,0x3,0x6e,0x28,0x47,0x29,0x38,0xc5,0x49,0x7f,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdf,0xb,0xd,0x16,0x2f,0x23,0x2,0xd6,0x7b,0x4b,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x0,0xe9,0x49,0x44,0x41,0x54,0x38,0xcb,0xed,0x93,0x4d,0x4e,0xc3,0x30,0x10,0x85,0x3f,0x4f,0x6c,0x71,0x1,0x1c,0x12,0x29,0x8b,0x6e,0x73,0x91,0x6e,0x10,0x7,0xe0,0x1c,0x3d,0x7,0x37,0x60,0xcf,0x1,0x10,0x8b,0xf6,0x22,0x15,0xbb,0x2e,0x2a,0xf5,0x7,0x2e,0x80,0xec,0x8e,0xd9,0x38,0x69,0x91,0x48,0x5a,0x4,0x4b,0x9e,0x34,0x2b,0xbf,0x19,0xbf,0x37,0x3f,0xf0,0x4b,0x18,0x80,0xbb,0xdb,0xfb,0x2b,0x60,0x2,0xb4,0x40,0x9,0xc8,0x0,0x5f,0x81,0x3d,0xb0,0x4,0x56,0xcf,0x2f,0x4f,0x1f,0x36,0x3f,0x4c,0xde,0xde,0x37,0xf,0xdb,0xdd,0x7a,0xaa,0xaa,0xce,0x18,0xf3,0x6d,0x76,0x4a,0x9,0x11,0x9,0xd5,0x4d,0xb3,0xf0,0xd7,0xf5,0xc,0x78,0xed,0x7e,0x6a,0xb7,0xbb,0xf5,0x14,0x70,0xd6,0x3a,0xc6,0x2,0x70,0x99,0xdb,0x2,0x74,0xa,0x4a,0x55,0x75,0xd6,0x3a,0x8a,0xa2,0x38,0xeb,0x3b,0xc6,0xe0,0xb2,0xd5,0xde,0xab,0x18,0x63,0x18,0x92,0xfe,0xa5,0x69,0x47,0x9e,0x30,0xd2,0xac,0x8b,0xf1,0x5f,0xe0,0x58,0x40,0x53,0x4a,0xa4,0x94,0xce,0x26,0x9c,0xf0,0xf4,0x74,0xf,0xf6,0x22,0x12,0x54,0xf,0xae,0x1b,0xd5,0x50,0xb2,0xea,0x1,0x11,0x89,0x79,0xa5,0x7b,0x5,0xcb,0xaa,0x6c,0x16,0x40,0x8c,0x31,0x30,0x16,0x60,0x42,0x55,0x36,0xf3,0x7c,0xf,0xbd,0x82,0x95,0xf7,0xf5,0xcc,0xfb,0xfa,0xf1,0x27,0xc7,0xc4,0x5f,0xe0,0x13,0xe5,0xc4,0x63,0x4f,0x20,0x8a,0x2e,0x80,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; @@ -480,17 +480,17 @@ static const unsigned char vseparator_png[]={ static const unsigned char vslider_bg_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xdd,0x0,0xdd,0x0,0xdd,0xf5,0x15,0x8,0x9d,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x9,0x14,0xf,0xc,0x18,0x82,0xe,0xe5,0x21,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x0,0x4d,0x49,0x44,0x41,0x54,0x38,0x8d,0x63,0x60,0x18,0x68,0xc0,0x88,0x4d,0x50,0x42,0x42,0x62,0x1a,0x3,0x3,0x43,0x26,0x9a,0xf0,0xf4,0x17,0x2f,0x5e,0x64,0x11,0x6b,0xc0,0x7f,0x76,0x76,0x4e,0x14,0xb1,0x9f,0x3f,0xbf,0x33,0xbc,0x78,0xf1,0x2,0x43,0x3d,0x13,0xd1,0x6e,0xc5,0x1,0x46,0xd,0x18,0x35,0x0,0x9f,0x1,0xd3,0x7f,0xfe,0xfc,0xce,0x80,0x8c,0x19,0x18,0x18,0xa6,0x53,0x6a,0x19,0x6d,0x0,0x0,0x59,0x9c,0x18,0xe9,0x50,0xa4,0x59,0x7a,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdf,0xb,0xd,0x16,0x2c,0x30,0xad,0x45,0x69,0x56,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0x87,0x49,0x44,0x41,0x54,0x38,0xcb,0xcd,0x93,0xcf,0x4e,0x13,0x51,0x14,0xc6,0x7f,0xf7,0xdc,0xc6,0x3b,0xd3,0xb1,0xc0,0x66,0x48,0x5b,0x42,0xea,0xc2,0xb4,0x2b,0x9f,0x81,0x7,0x30,0x3c,0x80,0x12,0x57,0x24,0xc6,0xc4,0xc8,0xd3,0xe0,0x12,0x37,0x68,0x7c,0x0,0xe3,0x3,0xf0,0xe,0x6e,0xb0,0x2c,0x68,0x50,0x20,0xce,0x6,0x3b,0x74,0xa6,0x3,0xce,0x3d,0x6c,0x4a,0x9d,0xd2,0x62,0xa2,0x2b,0xbf,0xe4,0x2c,0xee,0x9f,0xef,0x97,0x9c,0x2f,0xe7,0x18,0x2a,0xda,0x7c,0xfa,0xc,0xa0,0x6,0xac,0x2,0x5d,0x60,0x7d,0xf2,0x74,0x2,0x1c,0x2,0x9,0xf0,0xeb,0xd3,0xe7,0x8f,0x53,0x8f,0x59,0x60,0x7e,0x3c,0x4c,0x2f,0x76,0x92,0xe4,0xf4,0x79,0x96,0x8f,0x1a,0x0,0xf5,0x30,0x4a,0xe3,0xb8,0xfd,0x7e,0xa9,0xb1,0xb2,0xb,0x1c,0x55,0x21,0xc2,0xac,0xe2,0x61,0x7a,0xf1,0xe6,0x78,0xd0,0x7f,0x99,0xe5,0xd9,0xb2,0x88,0x15,0x11,0x2b,0x59,0x9e,0x2d,0x1f,0xf,0xfa,0xaf,0xd2,0xf4,0xe7,0x6b,0x20,0xae,0x1a,0xee,0x2,0x7a,0x49,0x72,0xba,0x65,0x8c,0xb1,0xd6,0x5a,0xaa,0x65,0x8c,0xb1,0x3f,0x92,0xef,0x2f,0x80,0x5e,0xd5,0x50,0xbb,0x3,0x58,0xcf,0xf2,0x51,0x43,0xc4,0x22,0xf2,0x9b,0x2d,0x22,0xa8,0x2a,0x59,0x3e,0x7a,0x58,0xc9,0x65,0x16,0x70,0x76,0x3e,0x0,0xd8,0xf6,0x5e,0xc5,0x5a,0xe6,0x64,0xc,0x78,0xaf,0xf6,0xec,0x7c,0xb0,0xd,0xec,0xcf,0x1,0x8a,0x62,0xc,0xb0,0x61,0xcc,0x4c,0xb6,0x55,0x4,0xc6,0x40,0x51,0x8c,0x37,0xfe,0x94,0xc1,0x5f,0xeb,0x3f,0x2,0x38,0x17,0xe0,0x5c,0x70,0xa0,0xa,0xa0,0xb,0xbe,0x2a,0xaa,0xe0,0x5c,0x70,0xe0,0x5c,0x30,0xf,0x68,0x35,0x3b,0xb4,0x9a,0x9d,0x3d,0x11,0xe3,0x75,0x81,0x5f,0x15,0x44,0x4c,0xd9,0x6a,0x76,0xf6,0x5a,0xcd,0xce,0xbd,0x2d,0x9c,0xd4,0xc3,0x28,0xf5,0xde,0xe3,0xbd,0x9f,0x5e,0xde,0x9e,0xeb,0x61,0x74,0x39,0xd9,0x8b,0x7b,0x1,0x87,0x71,0xdc,0xfe,0xa0,0xaa,0x65,0x59,0x96,0x54,0x4b,0x55,0xcb,0xd5,0x78,0x6d,0x7f,0xb2,0x54,0x53,0x4d,0x47,0xa6,0xd7,0x7d,0x2,0x30,0x76,0x2e,0x38,0xa,0xc3,0xa8,0x76,0x7d,0x35,0xee,0x5e,0x5d,0x17,0xf,0x54,0x55,0xeb,0x61,0x34,0x5c,0x6b,0x3f,0x7a,0xb7,0xd4,0x58,0x79,0xb,0x7c,0x3,0xfc,0xd7,0xfe,0x17,0xe6,0x26,0xe6,0x5f,0xd6,0xf9,0x6,0xaa,0x73,0x9f,0xf0,0x6d,0xf0,0x57,0x1b,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; static const unsigned char vslider_grabber_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0x92,0x0,0x92,0x0,0x99,0x25,0xc1,0x88,0x71,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x9,0x12,0x0,0x2,0x1f,0xac,0xde,0x45,0xed,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0xa4,0x49,0x44,0x41,0x54,0x38,0x8d,0x9d,0x93,0xb1,0x6e,0x1a,0x41,0x14,0x45,0xcf,0xcc,0xce,0x9a,0x85,0x25,0x44,0x58,0x8a,0x15,0xc5,0x49,0x93,0x2,0xb2,0x2,0x51,0xf2,0x7,0x2e,0x68,0xf2,0x15,0xae,0x22,0xc1,0x77,0xa4,0xe,0x65,0x5c,0xe4,0x2f,0xf8,0xb,0x47,0xb6,0x2c,0x21,0xb0,0x44,0xb7,0x22,0x52,0x9a,0x0,0x82,0x9d,0xd9,0x1d,0x76,0xd3,0xec,0x22,0x40,0xb1,0x8d,0x72,0xa5,0x29,0xe6,0xcd,0xbd,0xf7,0xbd,0x79,0x33,0x4f,0x70,0x84,0x66,0xb3,0x79,0x1c,0x3a,0xc0,0x64,0x32,0x39,0xd8,0xcb,0x7f,0x88,0x2f,0x81,0xaf,0xc0,0x1d,0x10,0xe5,0xeb,0x2e,0x8f,0x5d,0x1e,0x27,0x10,0xdd,0x6e,0x17,0x80,0xc5,0x62,0x1,0x70,0xd,0x7c,0xb,0x82,0x76,0xb9,0x52,0xf1,0x71,0xdd,0x33,0x0,0x92,0x24,0x66,0xb3,0x59,0x33,0x1e,0x3f,0x44,0xc0,0xc0,0x5a,0x7b,0xb3,0x5e,0xaf,0x49,0xd3,0x14,0xe7,0xd3,0x87,0xe,0xbf,0xff,0xfc,0x2,0xb8,0xae,0xd7,0xcf,0xbf,0xb7,0x5a,0x1d,0xb7,0x5a,0xad,0xa1,0x94,0x42,0x8,0x81,0x10,0x2,0xa5,0x14,0xe5,0x72,0x85,0x8b,0x8b,0xb7,0xae,0xd6,0xd1,0xe7,0x38,0x36,0xa1,0xef,0xfb,0xb7,0x5a,0x6b,0x9c,0x33,0xdf,0x29,0xca,0x1e,0xb5,0x5a,0x1d,0xb7,0x54,0xf2,0x9e,0xbc,0xbf,0x52,0x8a,0x5a,0xed,0x35,0xf3,0x79,0x78,0x95,0x65,0xd9,0xf,0x6b,0xed,0xaa,0xe8,0x41,0x3f,0x8,0xda,0xe5,0xe7,0xc4,0x5,0x4a,0x25,0x8f,0x20,0x68,0x97,0x81,0xbe,0xeb,0xba,0xbb,0x26,0xf6,0x2a,0x15,0xff,0x45,0x71,0x81,0x9c,0xdb,0x53,0x4a,0x21,0xb3,0x2c,0x3,0x68,0x14,0xd,0x3b,0x5,0x39,0xb7,0x21,0xa5,0x3c,0x7c,0xc6,0xff,0x81,0x14,0x42,0x0,0x4c,0x93,0x24,0x3e,0x59,0x94,0x73,0xa7,0x69,0x9a,0xee,0x2a,0x18,0x6d,0x36,0xeb,0x93,0xd,0x72,0xee,0xc8,0x5a,0xbb,0x33,0x18,0x8e,0xc7,0xf,0x91,0x31,0xfa,0x45,0xb1,0x31,0xba,0xf8,0x50,0xc3,0x24,0x49,0x90,0xef,0xeb,0x1f,0x1,0x42,0x60,0x30,0x9b,0x3d,0xf2,0x9c,0x89,0x31,0x9a,0xd9,0xec,0x11,0x60,0xe0,0x38,0x4e,0xb8,0xdd,0x6e,0x71,0xde,0xbc,0x3b,0xc7,0xf3,0x3c,0x8c,0x31,0xb7,0x5a,0x47,0xe1,0x7c,0x1e,0x5e,0x55,0xab,0xaf,0x5c,0x29,0x25,0x52,0x4a,0xb2,0x2c,0x23,0x8e,0xd,0xab,0xd5,0x82,0xfb,0xfb,0x9f,0x91,0xd6,0xd1,0x17,0x6b,0xed,0xcd,0x72,0xb9,0x24,0x4d,0x53,0xc4,0x7e,0x86,0xbd,0x61,0xea,0x3,0x3d,0xa0,0x91,0x1f,0x4d,0x81,0x11,0x30,0x4,0xc2,0xfd,0x89,0x3c,0x30,0xd8,0x33,0x79,0x12,0xc7,0xe3,0xfc,0x17,0x9c,0xcc,0xa8,0xb2,0xd4,0xe8,0x7,0x23,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdf,0xb,0xd,0x15,0x1b,0x38,0x31,0xdf,0xff,0x9,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0x7d,0x49,0x44,0x41,0x54,0x38,0xcb,0xdd,0x92,0x3d,0x6b,0x53,0x61,0x14,0x80,0x9f,0xf7,0x43,0xdf,0xfb,0x11,0x6f,0x9b,0x86,0x1a,0x84,0x3a,0x84,0x98,0x25,0x29,0xd,0x11,0x87,0x40,0x3b,0x64,0x70,0x89,0x6b,0x97,0x6c,0xa5,0x7b,0x6,0xc9,0x3f,0xe8,0xe2,0xe4,0x2f,0xb8,0x8b,0x6b,0x17,0x9d,0x1d,0xb2,0x25,0x43,0x29,0x4,0x41,0x42,0x21,0x43,0x20,0x1d,0x3b,0x8,0x49,0x24,0x2f,0x5c,0xc3,0xbd,0xaf,0x4b,0xb,0xb6,0xb6,0xe,0x6e,0xfa,0x2c,0x7,0xe,0x9c,0xf,0xce,0x73,0xe0,0x9f,0x47,0x3c,0x90,0x93,0x80,0x1,0x9e,0x0,0x21,0xf0,0x18,0x70,0xc0,0xa,0x58,0x0,0x16,0x48,0xef,0x6b,0xf0,0x8,0xd8,0x28,0x95,0x4a,0xcf,0x1b,0x8d,0xc6,0xab,0x7a,0xbd,0xfe,0x26,0x8a,0xa2,0x5d,0x63,0xcc,0xb6,0x94,0xd2,0x2d,0x16,0x8b,0xaf,0xd3,0xe9,0xf4,0x63,0x1c,0xc7,0xa7,0xc0,0x37,0x20,0x15,0xbf,0x4c,0xd,0x72,0xb9,0x5c,0xa9,0xdb,0xed,0xbe,0x2d,0x16,0x8b,0xaf,0x8d,0x31,0x3b,0x49,0x92,0xa8,0xf5,0x7a,0x4d,0x9a,0xa6,0x0,0x18,0x63,0xd0,0x5a,0x5f,0x8d,0xc7,0xe3,0x77,0x71,0x1c,0x7f,0x0,0xbe,0x6b,0x40,0x3,0x9b,0xcd,0x66,0x73,0xbf,0xd3,0xe9,0xbc,0x57,0x4a,0xbd,0xb0,0xd6,0x62,0xad,0x25,0xcb,0xb2,0x5b,0xeb,0xa5,0x69,0x4a,0x10,0x4,0xc5,0x72,0xb9,0x7c,0x8,0x7c,0xba,0x69,0x60,0x7a,0xbd,0xde,0x51,0xb5,0x5a,0x3d,0x99,0xcf,0xe7,0xe1,0x6a,0xb5,0xfa,0xad,0xf0,0x6,0xe7,0x1c,0x49,0x92,0x90,0xcf,0xe7,0xf7,0xae,0x6f,0x83,0x4,0x84,0xe7,0x79,0x1,0xa0,0xfe,0xc6,0x82,0x2,0xb2,0xc1,0x60,0x70,0xb1,0x5c,0x2e,0xbf,0xd4,0x6a,0xb5,0x97,0x9e,0xe7,0x6d,0x65,0x59,0x86,0x73,0xe,0xe7,0xdc,0x6d,0x3d,0x42,0xe0,0xfb,0x3e,0xd6,0xda,0xf3,0x7e,0xbf,0x7f,0xa,0x2c,0x15,0x90,0x1,0x76,0x36,0x9b,0x5d,0xe,0x87,0xc3,0xcf,0x95,0x4a,0x85,0x42,0xa1,0xf0,0x34,0xc,0xc3,0xd,0x21,0x84,0x0,0x90,0x52,0x22,0xa5,0xc4,0xf7,0x7d,0xb4,0xd6,0x57,0x93,0xc9,0x24,0x1e,0x8d,0x46,0x67,0xc0,0x8f,0xfb,0x34,0x46,0x5a,0xeb,0xed,0x76,0xbb,0x7d,0xd0,0x6a,0xb5,0x8e,0x85,0x10,0xcf,0x8c,0x31,0x5b,0x4a,0xa9,0x3f,0x6a,0xbc,0x8b,0x4,0x2,0x20,0xba,0x8e,0xfa,0xa1,0x47,0xfa,0xf,0xf8,0x9,0xc2,0x2d,0x88,0xfe,0x7,0xd8,0xc3,0x3a,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; static const unsigned char vslider_grabber_hl_png[]={ -0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0x92,0x0,0x92,0x0,0x99,0x25,0xc1,0x88,0x71,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xde,0x9,0x12,0x0,0x2,0x21,0x6d,0xbf,0x58,0x46,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0x30,0x49,0x44,0x41,0x54,0x38,0x8d,0xa5,0x93,0xb1,0x6a,0xc2,0x50,0x14,0x86,0xbf,0x63,0xe2,0x90,0xd0,0x2e,0xc1,0xad,0x8b,0xad,0x60,0x9e,0xc0,0xbe,0x41,0x7,0x33,0x38,0x7,0x1d,0xba,0x74,0x2a,0xe8,0xc3,0xe8,0x68,0xc1,0x49,0xc8,0xec,0x10,0x9f,0xa1,0x8,0xee,0x71,0xcf,0x52,0x4d,0x70,0x73,0x8a,0xb7,0x83,0x37,0x72,0x11,0xb4,0xa1,0xfe,0xe3,0xe5,0x7c,0xdf,0xbd,0x1c,0xfe,0xb,0x77,0x46,0x2e,0xf,0x7c,0xdf,0x7f,0x2,0x86,0x40,0x17,0x68,0xeb,0xe3,0xd,0xb0,0x4,0x26,0x49,0x92,0xa4,0x57,0x5,0xbe,0xef,0x7f,0x0,0xe3,0x30,0x1c,0x38,0x9e,0xd7,0x40,0xa4,0x6,0x80,0x52,0x47,0xf2,0x7c,0x47,0x14,0xcd,0xf,0xc0,0x28,0x49,0x92,0xaf,0x92,0xb1,0x4c,0xb8,0xd9,0x7c,0x9e,0xf6,0xfb,0xef,0x75,0xd7,0x7d,0xd4,0xb0,0x0,0x82,0x48,0xd,0xd7,0x7d,0xa0,0xd3,0x79,0xad,0x6f,0xb7,0x3f,0x3d,0xdb,0xb6,0xd2,0x2c,0xcb,0xd6,0x0,0x35,0xe3,0xd9,0xe3,0x20,0xe8,0x21,0x62,0x71,0x2d,0x22,0x16,0x41,0xd0,0x3,0x18,0x6b,0xe6,0x24,0x0,0x86,0x61,0x38,0x70,0x6e,0xc1,0xa6,0x24,0xc,0x7,0x8e,0xde,0xd3,0x59,0xd0,0xf5,0xbc,0xc6,0x9f,0x70,0x19,0x3d,0xdb,0x35,0x5,0xed,0x72,0x61,0x55,0xa2,0x67,0xdb,0xa6,0xe0,0xdf,0x29,0x5,0x1b,0xa5,0x8e,0x95,0x21,0x3d,0xbb,0x31,0x5,0xcb,0x3c,0xdf,0x55,0x16,0xe8,0xd9,0xa5,0x29,0x98,0x44,0xd1,0xfc,0xa0,0x54,0x51,0xe1,0xf6,0xa2,0x2c,0xd4,0xe4,0x2c,0xd0,0xf5,0x1c,0xc5,0xf1,0x82,0x5b,0x12,0xa5,0xa,0xe2,0x78,0x1,0xa7,0x36,0xa6,0x60,0x34,0x31,0xcb,0xb2,0xb5,0x6d,0x5b,0xe9,0x6a,0xf5,0xfd,0xd6,0x6a,0xbd,0xd4,0x1d,0xc7,0x41,0x44,0x34,0x78,0x24,0xcf,0xb7,0xcc,0x66,0xd3,0xc3,0x7e,0xbf,0xff,0x34,0xab,0x7c,0xf7,0x67,0xba,0x3b,0xbf,0x4d,0x78,0x75,0x34,0x1f,0x21,0x5d,0xa6,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,0x0,0x0,0x0,0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdf,0xb,0xd,0x15,0x1c,0x11,0x3c,0x2c,0xf1,0xa2,0x0,0x0,0x0,0x19,0x74,0x45,0x58,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0x0,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x47,0x49,0x4d,0x50,0x57,0x81,0xe,0x17,0x0,0x0,0x1,0x93,0x49,0x44,0x41,0x54,0x38,0xcb,0xdd,0x92,0x3f,0x4c,0x13,0x71,0x14,0xc7,0x3f,0xef,0x77,0x7f,0x5a,0xae,0xde,0x1d,0x57,0x3,0x47,0x13,0x19,0x1a,0x48,0x14,0x2,0xc,0xe2,0x62,0x80,0xc8,0x40,0x22,0x31,0x71,0x20,0xec,0x18,0x77,0x7,0x27,0xb6,0xe,0x2e,0x2e,0x32,0xe1,0xc6,0xc2,0x5a,0x7,0x9,0x21,0xe,0xdd,0x88,0x9b,0x61,0x91,0x34,0x6a,0x17,0x63,0xe2,0x60,0x28,0x10,0x4c,0x28,0x96,0x96,0xbb,0xde,0xfd,0x5c,0x20,0x51,0xfe,0x98,0x38,0xea,0x67,0x79,0xc9,0x4b,0xde,0xf7,0xbd,0x7c,0xbf,0xf,0xfe,0x79,0xe4,0x8a,0x9e,0x2,0x32,0x80,0xb,0xe4,0x0,0x1b,0xd0,0x40,0x13,0x38,0x4,0x8e,0x81,0xe4,0x32,0x1,0xb,0xf0,0xc3,0xe1,0xa1,0xfe,0x81,0x7b,0xd3,0x77,0x8a,0x53,0x93,0xf,0x6c,0x37,0x1c,0x51,0xe6,0xb5,0x1e,0x65,0x2a,0x1d,0xff,0xd8,0xa9,0xd6,0x3f,0x54,0x5f,0x57,0x4a,0xa5,0x32,0x70,0x0,0x24,0xf2,0xcb,0x56,0x27,0xeb,0xfb,0xc5,0x87,0x4b,0x2f,0x9e,0xe6,0xc2,0x81,0x99,0x54,0x77,0xdf,0x68,0x35,0x62,0x23,0x6a,0x76,0xe8,0xc4,0x29,0x22,0x90,0xf5,0x6c,0xdc,0x20,0xda,0xdd,0xd9,0x7e,0xfb,0xbc,0x52,0x2a,0xad,0x2,0x47,0x26,0x60,0x2,0xdd,0x37,0x67,0xef,0x4f,0x4c,0x3c,0x59,0x5c,0x6a,0xb7,0xbc,0xc1,0xef,0xdf,0x4e,0x88,0xdb,0x47,0xa4,0x89,0xfe,0xed,0xbc,0x4e,0xd4,0x6,0xb2,0x61,0xdf,0xc8,0xd8,0x3c,0xb0,0x76,0x26,0x90,0x99,0x7b,0xb9,0xfc,0x28,0x1c,0x9d,0x7a,0xb6,0xf7,0xa5,0x95,0x6b,0x1d,0x36,0x2f,0xc,0x9e,0xa1,0x53,0x4d,0xbb,0x11,0x11,0x14,0xa,0x63,0xa7,0xde,0xa0,0x0,0xb1,0x1c,0xc7,0x41,0x30,0xb4,0xfe,0xfb,0x14,0xc,0x20,0xfd,0xb8,0xb1,0xf1,0xe9,0xa4,0xb1,0xff,0x7e,0x70,0x7a,0xfc,0xb6,0xd9,0xe5,0xe6,0xd3,0x58,0xa3,0x53,0xcd,0x79,0x41,0x51,0x82,0x13,0x64,0x30,0xa5,0xbe,0xb5,0xfd,0xaa,0x5c,0x6,0x1a,0x6,0x90,0x2,0xc7,0x7b,0xb5,0xda,0xd7,0xda,0x9b,0xf5,0x4a,0xf1,0xee,0x2d,0xf2,0xfd,0xd7,0x7b,0xbb,0x7c,0xcf,0x17,0x11,0x41,0x40,0x44,0x30,0x2c,0x85,0x13,0x64,0x70,0x83,0x68,0xb7,0x5e,0xdd,0x5a,0xf9,0xbc,0xb9,0xf9,0xe,0x88,0x2e,0x8b,0xd1,0x33,0x2c,0xab,0x67,0x7c,0x61,0x61,0x72,0x74,0x6e,0xfe,0x71,0x92,0xd8,0x5,0xc3,0xce,0xe5,0x95,0x69,0xfc,0x31,0xc6,0xf3,0x28,0xc0,0x1,0xbc,0xd3,0x6a,0x5e,0xf5,0x48,0xff,0x1,0x3f,0x1,0xa3,0x8a,0x90,0x14,0xe9,0x66,0x95,0x43,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; diff --git a/scene/resources/default_theme/toggle_off.png b/scene/resources/default_theme/toggle_off.png Binary files differindex 3e92aa0ece..aa1c96e5a0 100644 --- a/scene/resources/default_theme/toggle_off.png +++ b/scene/resources/default_theme/toggle_off.png diff --git a/scene/resources/default_theme/toggle_on.png b/scene/resources/default_theme/toggle_on.png Binary files differindex a49c234f51..0a69d36ae8 100644 --- a/scene/resources/default_theme/toggle_on.png +++ b/scene/resources/default_theme/toggle_on.png diff --git a/scene/resources/default_theme/unchecked.png b/scene/resources/default_theme/unchecked.png Binary files differindex 39a70e6003..f8710d03df 100644 --- a/scene/resources/default_theme/unchecked.png +++ b/scene/resources/default_theme/unchecked.png diff --git a/scene/resources/default_theme/vslider_bg.png b/scene/resources/default_theme/vslider_bg.png Binary files differindex 5472bb366f..d58d4b1659 100644 --- a/scene/resources/default_theme/vslider_bg.png +++ b/scene/resources/default_theme/vslider_bg.png diff --git a/scene/resources/default_theme/vslider_grabber.png b/scene/resources/default_theme/vslider_grabber.png Binary files differindex 988c25d9dd..50ef5680da 100644 --- a/scene/resources/default_theme/vslider_grabber.png +++ b/scene/resources/default_theme/vslider_grabber.png diff --git a/scene/resources/default_theme/vslider_grabber_hl.png b/scene/resources/default_theme/vslider_grabber_hl.png Binary files differindex f319df3319..93eba4b174 100644 --- a/scene/resources/default_theme/vslider_grabber_hl.png +++ b/scene/resources/default_theme/vslider_grabber_hl.png diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index bbb2a386f3..55bb4e9073 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -406,7 +406,6 @@ void FixedMaterial::_bind_methods() { BIND_CONSTANT( PARAM_SHADE_PARAM ); BIND_CONSTANT( PARAM_MAX ); - BIND_CONSTANT( TEXCOORD_SPHERE ); BIND_CONSTANT( TEXCOORD_UV ); BIND_CONSTANT( TEXCOORD_UV_TRANSFORM ); @@ -417,6 +416,11 @@ void FixedMaterial::_bind_methods() { BIND_CONSTANT( FLAG_USE_POINT_SIZE ); BIND_CONSTANT( FLAG_DISCARD_ALPHA ); + BIND_CONSTANT( LIGHT_SHADER_LAMBERT ); + BIND_CONSTANT( LIGHT_SHADER_WRAP ); + BIND_CONSTANT( LIGHT_SHADER_VELVET ); + BIND_CONSTANT( LIGHT_SHADER_TOON ); + } diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index fdf1692495..896b4fb2fa 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -124,7 +124,7 @@ Node *SceneState::instance(bool p_gen_edit_state) const { ERR_FAIL_COND_V(!node,NULL); } else { InstancePlaceholder *ip = memnew( InstancePlaceholder ); - ip->set_path(path); + ip->set_instance_path(path); node=ip; } node->set_scene_instance_load_placeholder(true); diff --git a/scene/resources/shader_graph.cpp b/scene/resources/shader_graph.cpp index 49a1bdccb1..d5204f45d5 100644 --- a/scene/resources/shader_graph.cpp +++ b/scene/resources/shader_graph.cpp @@ -2083,7 +2083,7 @@ void ShaderGraph::_add_node_code(ShaderType p_type,Node *p_node,const Vector<Str #define DEF_MATRIX(slot) \ if (p_inputs[slot].ends_with("def")){\ Transform xf = p_node->defaults[slot]; \ - code+=String(typestr[3])+" "+p_inputs[slot]+"=mat4(\n";\ + code+=String(typestr[2])+" "+p_inputs[slot]+"=mat4(\n";\ code+="\tvec4(vec3("+rtos(xf.basis.get_axis(0).x)+","+rtos(xf.basis.get_axis(0).y)+","+rtos(xf.basis.get_axis(0).z)+"),0),\n";\ code+="\tvec4(vec3("+rtos(xf.basis.get_axis(1).x)+","+rtos(xf.basis.get_axis(1).y)+","+rtos(xf.basis.get_axis(1).z)+"),0),\n";\ code+="\tvec4(vec3("+rtos(xf.basis.get_axis(2).x)+","+rtos(xf.basis.get_axis(2).y)+","+rtos(xf.basis.get_axis(2).z)+"),0),\n";\ @@ -2393,15 +2393,29 @@ void ShaderGraph::_add_node_code(ShaderType p_type,Node *p_node,const Vector<Str DEF_VEC(1); DEF_VEC(2); DEF_VEC(3); - code += OUTNAME(p_node->id,0)+"=xform("+p_inputs[0]+","+p_inputs[1]+","+p_inputs[2]+","+","+p_inputs[3]+");\n"; + code += OUTNAME(p_node->id, 0) + "=mat4(" + + "vec4(" + p_inputs[0] + ".x," + p_inputs[0] + ".y," + p_inputs[0] + ".z, 0.0)," + "vec4(" + p_inputs[1] + ".x," + p_inputs[1] + ".y," + p_inputs[1] + ".z, 0.0)," + "vec4(" + p_inputs[2] + ".x," + p_inputs[2] + ".y," + p_inputs[2] + ".z, 0.0)," + "vec4(" + p_inputs[3] + ".x," + p_inputs[3] + ".y," + p_inputs[3] + ".z, 1.0));\n"; }break; case NODE_XFORM_TO_VEC: { DEF_MATRIX(0); - code += OUTNAME(p_node->id,0)+"="+p_inputs[0]+".x;\n"; - code += OUTNAME(p_node->id,1)+"="+p_inputs[0]+".y;\n"; - code += OUTNAME(p_node->id,2)+"="+p_inputs[0]+".z;\n"; - code += OUTNAME(p_node->id,3)+"="+p_inputs[0]+".o;\n"; + code += OUTNAME(p_node->id, 0) + ";\n"; + code += OUTNAME(p_node->id, 1) + ";\n"; + code += OUTNAME(p_node->id, 2) + ";\n"; + code += OUTNAME(p_node->id, 3) + ";\n"; + code += "{\n"; + code += "\tvec4 xform_row_01=" + p_inputs[0] + ".x;\n"; + code += "\tvec4 xform_row_02=" + p_inputs[0] + ".y;\n"; + code += "\tvec4 xform_row_03=" + p_inputs[0] + ".z;\n"; + code += "\tvec4 xform_row_04=" + p_inputs[0] + ".w;\n"; + code += "\t" + OUTVAR(p_node->id, 0) + "=vec3(xform_row_01.x, xform_row_01.y, xform_row_01.z);\n"; + code += "\t" + OUTVAR(p_node->id, 1) + "=vec3(xform_row_02.x, xform_row_02.y, xform_row_02.z);\n"; + code += "\t" + OUTVAR(p_node->id, 2) + "=vec3(xform_row_03.x, xform_row_03.y, xform_row_03.z);\n"; + code += "\t" + OUTVAR(p_node->id, 3) + "=vec3(xform_row_04.x, xform_row_04.y, xform_row_04.z);\n"; + code += "}\n"; }break; case NODE_SCALAR_INTERP: { DEF_SCALAR(0); diff --git a/scene/resources/shape_line_2d.cpp b/scene/resources/shape_line_2d.cpp index c660b604f3..97e9985754 100644 --- a/scene/resources/shape_line_2d.cpp +++ b/scene/resources/shape_line_2d.cpp @@ -35,6 +35,7 @@ void LineShape2D::_update_shape() { arr.push_back(normal); arr.push_back(d); Physics2DServer::get_singleton()->shape_set_data(get_rid(),arr); + emit_changed(); } diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index d8d9c5b675..3b1f1d2346 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -374,7 +374,7 @@ World2D::World2D() { Physics2DServer::get_singleton()->area_set_param(space,Physics2DServer::AREA_PARAM_GRAVITY,GLOBAL_DEF("physics_2d/default_gravity",98)); Physics2DServer::get_singleton()->area_set_param(space,Physics2DServer::AREA_PARAM_GRAVITY_VECTOR,GLOBAL_DEF("physics_2d/default_gravity_vector",Vector2(0,1))); Physics2DServer::get_singleton()->area_set_param(space,Physics2DServer::AREA_PARAM_LINEAR_DAMP,GLOBAL_DEF("physics_2d/default_density",0.1)); - Physics2DServer::get_singleton()->area_set_param(space,Physics2DServer::AREA_PARAM_ANGULAR_DAMP,GLOBAL_DEF("physics_2d/default_density",1)); + Physics2DServer::get_singleton()->area_set_param(space,Physics2DServer::AREA_PARAM_ANGULAR_DAMP,GLOBAL_DEF("physics_2d/default_angular_damp",1)); Physics2DServer::get_singleton()->space_set_param(space,Physics2DServer::SPACE_PARAM_CONTACT_RECYCLE_RADIUS,1.0); Physics2DServer::get_singleton()->space_set_param(space,Physics2DServer::SPACE_PARAM_CONTACT_MAX_SEPARATION,1.5); Physics2DServer::get_singleton()->space_set_param(space,Physics2DServer::SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION,0.3); diff --git a/servers/SCsub b/servers/SCsub index 3871c30cfa..d861847101 100644 --- a/servers/SCsub +++ b/servers/SCsub @@ -15,5 +15,3 @@ SConscript('spatial_sound_2d/SCsub'); lib = env.Library("servers",env.servers_sources) env.Prepend(LIBS=[lib]) - - diff --git a/servers/audio/SCsub b/servers/audio/SCsub index 16fe3a59ac..d31af2c1c4 100644 --- a/servers/audio/SCsub +++ b/servers/audio/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.servers_sources,"*.cpp") Export('env') - - diff --git a/servers/audio/audio_mixer_sw.cpp b/servers/audio/audio_mixer_sw.cpp index 791f31719e..033cd333d5 100644 --- a/servers/audio/audio_mixer_sw.cpp +++ b/servers/audio/audio_mixer_sw.cpp @@ -45,14 +45,14 @@ void AudioMixerSW::do_resample(const Depth* p_src, int32_t *p_dst, ResamplerStat while (p_state->amount--) { int32_t pos=p_state->pos >> MIX_FRAC_BITS; - if (is_stereo) + if (is_stereo && !is_ima_adpcm) pos<<=1; if (is_ima_adpcm) { - int sample_pos = pos + p_state->ima_adpcm->window_ofs; + int sample_pos = pos + p_state->ima_adpcm[0].window_ofs; - while(sample_pos>p_state->ima_adpcm->last_nibble) { + while(sample_pos>p_state->ima_adpcm[0].last_nibble) { static const int16_t _ima_adpcm_step_table[89] = { @@ -72,52 +72,64 @@ void AudioMixerSW::do_resample(const Depth* p_src, int32_t *p_dst, ResamplerStat -1, -1, -1, -1, 2, 4, 6, 8 }; - int16_t nibble,signed_nibble,diff,step; + for(int i=0;i<(is_stereo?2:1);i++) { - p_state->ima_adpcm->last_nibble++; - const uint8_t *src_ptr=p_state->ima_adpcm->ptr; - nibble = (p_state->ima_adpcm->last_nibble&1)? - (src_ptr[p_state->ima_adpcm->last_nibble>>1]>>4):(src_ptr[p_state->ima_adpcm->last_nibble>>1]&0xF); - step=_ima_adpcm_step_table[p_state->ima_adpcm->step_index]; + int16_t nibble,signed_nibble,diff,step; - p_state->ima_adpcm->step_index += _ima_adpcm_index_table[nibble]; - if (p_state->ima_adpcm->step_index<0) - p_state->ima_adpcm->step_index=0; - if (p_state->ima_adpcm->step_index>88) - p_state->ima_adpcm->step_index=88; + p_state->ima_adpcm[i].last_nibble++; + const uint8_t *src_ptr=p_state->ima_adpcm[i].ptr; - /* - signed_nibble = (nibble&7) * ((nibble&8)?-1:1); - diff = (2 * signed_nibble + 1) * step / 4; */ - diff = step >> 3 ; - if (nibble & 1) - diff += step >> 2 ; - if (nibble & 2) - diff += step >> 1 ; - if (nibble & 4) - diff += step ; - if (nibble & 8) - diff = -diff ; + uint8_t nbb = src_ptr[ (p_state->ima_adpcm[i].last_nibble>>1) * (is_stereo?2:1) + i ]; + nibble = (p_state->ima_adpcm[i].last_nibble&1)?(nbb>>4):(nbb&0xF); + step=_ima_adpcm_step_table[p_state->ima_adpcm[i].step_index]; - p_state->ima_adpcm->predictor+=diff; - if (p_state->ima_adpcm->predictor<-0x8000) - p_state->ima_adpcm->predictor=-0x8000; - else if (p_state->ima_adpcm->predictor>0x7FFF) - p_state->ima_adpcm->predictor=0x7FFF; + p_state->ima_adpcm[i].step_index += _ima_adpcm_index_table[nibble]; + if (p_state->ima_adpcm[i].step_index<0) + p_state->ima_adpcm[i].step_index=0; + if (p_state->ima_adpcm[i].step_index>88) + p_state->ima_adpcm[i].step_index=88; - /* store loop if there */ - if (p_state->ima_adpcm->last_nibble==p_state->ima_adpcm->loop_pos) { + /* + signed_nibble = (nibble&7) * ((nibble&8)?-1:1); + diff = (2 * signed_nibble + 1) * step / 4; */ + + diff = step >> 3 ; + if (nibble & 1) + diff += step >> 2 ; + if (nibble & 2) + diff += step >> 1 ; + if (nibble & 4) + diff += step ; + if (nibble & 8) + diff = -diff ; + + p_state->ima_adpcm[i].predictor+=diff; + if (p_state->ima_adpcm[i].predictor<-0x8000) + p_state->ima_adpcm[i].predictor=-0x8000; + else if (p_state->ima_adpcm[i].predictor>0x7FFF) + p_state->ima_adpcm[i].predictor=0x7FFF; + + + /* store loop if there */ + if (p_state->ima_adpcm[i].last_nibble==p_state->ima_adpcm[i].loop_pos) { + + p_state->ima_adpcm[i].loop_step_index = p_state->ima_adpcm[i].step_index; + p_state->ima_adpcm[i].loop_predictor = p_state->ima_adpcm[i].predictor; + } + + //printf("%i - %i - pred %i\n",int(p_state->ima_adpcm[i].last_nibble),int(nibble),int(p_state->ima_adpcm[i].predictor)); - p_state->ima_adpcm->loop_step_index = p_state->ima_adpcm->step_index; - p_state->ima_adpcm->loop_predictor = p_state->ima_adpcm->predictor; } } - final=p_state->ima_adpcm->predictor; + final=p_state->ima_adpcm[0].predictor; + if (is_stereo) { + final_r=p_state->ima_adpcm[1].predictor; + } } else { final=p_src[pos]; @@ -399,9 +411,10 @@ void AudioMixerSW::mix_channel(Channel& c) { if (format==AS::SAMPLE_FORMAT_IMA_ADPCM) { - rstate.ima_adpcm=&c.mix.ima_adpcm; + rstate.ima_adpcm=c.mix.ima_adpcm; if (loop_format!=AS::SAMPLE_LOOP_NONE) { - c.mix.ima_adpcm.loop_pos=loop_begin_fp>>MIX_FRAC_BITS; + c.mix.ima_adpcm[0].loop_pos=loop_begin_fp>>MIX_FRAC_BITS; + c.mix.ima_adpcm[1].loop_pos=loop_begin_fp>>MIX_FRAC_BITS; loop_format=AS::SAMPLE_LOOP_FORWARD; } } @@ -447,9 +460,11 @@ void AudioMixerSW::mix_channel(Channel& c) { /* go to loop-begin */ if (format==AS::SAMPLE_FORMAT_IMA_ADPCM) { - c.mix.ima_adpcm.step_index=c.mix.ima_adpcm.loop_step_index; - c.mix.ima_adpcm.predictor=c.mix.ima_adpcm.loop_predictor; - c.mix.ima_adpcm.last_nibble=loop_begin_fp>>MIX_FRAC_BITS; + for(int i=0;i<2;i++) { + c.mix.ima_adpcm[i].step_index=c.mix.ima_adpcm[i].loop_step_index; + c.mix.ima_adpcm[i].predictor=c.mix.ima_adpcm[i].loop_predictor; + c.mix.ima_adpcm[i].last_nibble=loop_begin_fp>>MIX_FRAC_BITS; + } c.mix.offset=loop_begin_fp; } else { c.mix.offset=loop_begin_fp+(c.mix.offset-loop_end_fp); @@ -549,10 +564,12 @@ void AudioMixerSW::mix_channel(Channel& c) { CALL_RESAMPLE_MODE(int16_t,is_stereo,false,use_filter,use_fx,interpolation_type,mix_channels); } else if (format==AS::SAMPLE_FORMAT_IMA_ADPCM) { - c.mix.ima_adpcm.window_ofs=c.mix.offset>>MIX_FRAC_BITS; - c.mix.ima_adpcm.ptr=(const uint8_t*)data; - int8_t *src_ptr = &((int8_t*)data)[(c.mix.offset >> MIX_FRAC_BITS)<<(is_stereo?1:0) ]; - CALL_RESAMPLE_MODE(int8_t,false,true,use_filter,use_fx,interpolation_type,mix_channels); + for(int i=0;i<2;i++) { + c.mix.ima_adpcm[i].window_ofs=c.mix.offset>>MIX_FRAC_BITS; + c.mix.ima_adpcm[i].ptr=(const uint8_t*)data; + } + int8_t *src_ptr = NULL; + CALL_RESAMPLE_MODE(int8_t,is_stereo,true,use_filter,use_fx,interpolation_type,mix_channels); } @@ -781,14 +798,16 @@ AudioMixer::ChannelID AudioMixerSW::channel_alloc(RID p_sample) { if (sample_manager->sample_get_format(c.sample)==AudioServer::SAMPLE_FORMAT_IMA_ADPCM) { - c.mix.ima_adpcm.step_index=0; - c.mix.ima_adpcm.predictor=0; - c.mix.ima_adpcm.loop_step_index=0; - c.mix.ima_adpcm.loop_predictor=0; - c.mix.ima_adpcm.last_nibble=-1; - c.mix.ima_adpcm.loop_pos=0x7FFFFFFF; - c.mix.ima_adpcm.window_ofs=0; - c.mix.ima_adpcm.ptr=NULL; + for(int i=0;i<2;i++) { + c.mix.ima_adpcm[i].step_index=0; + c.mix.ima_adpcm[i].predictor=0; + c.mix.ima_adpcm[i].loop_step_index=0; + c.mix.ima_adpcm[i].loop_predictor=0; + c.mix.ima_adpcm[i].last_nibble=-1; + c.mix.ima_adpcm[i].loop_pos=0x7FFFFFFF; + c.mix.ima_adpcm[i].window_ofs=0; + c.mix.ima_adpcm[i].ptr=NULL; + } } ChannelID ret_id = index+c.check*MAX_CHANNELS; diff --git a/servers/audio/audio_mixer_sw.h b/servers/audio/audio_mixer_sw.h index cb38561c27..d8d9b7bacd 100644 --- a/servers/audio/audio_mixer_sw.h +++ b/servers/audio/audio_mixer_sw.h @@ -105,7 +105,7 @@ private: int32_t loop_pos; int32_t window_ofs; const uint8_t *ptr; - } ima_adpcm; + } ima_adpcm[2]; } mix; diff --git a/servers/audio/sample_manager_sw.cpp b/servers/audio/sample_manager_sw.cpp index 9195136a5d..375aa88cd2 100644 --- a/servers/audio/sample_manager_sw.cpp +++ b/servers/audio/sample_manager_sw.cpp @@ -38,12 +38,8 @@ SampleManagerSW::~SampleManagerSW() RID SampleManagerMallocSW::sample_create(AS::SampleFormat p_format, bool p_stereo, int p_length) { - ERR_EXPLAIN("IMA-ADPCM and STEREO are not a valid combination for sample format."); - ERR_FAIL_COND_V( p_format == AS::SAMPLE_FORMAT_IMA_ADPCM && p_stereo,RID()); Sample *s = memnew( Sample ); int datalen = p_length; - if (p_stereo) - datalen*=2; if (p_format==AS::SAMPLE_FORMAT_PCM16) datalen*=2; else if (p_format==AS::SAMPLE_FORMAT_IMA_ADPCM) { @@ -53,6 +49,10 @@ RID SampleManagerMallocSW::sample_create(AS::SampleFormat p_format, bool p_stere datalen/=2; datalen+=4; } + + if (p_stereo) + datalen*=2; + #define SAMPLE_EXTRA 16 s->data = memalloc(datalen+SAMPLE_EXTRA); //help the interpolator by allocating a little more.. diff --git a/servers/physics/SCsub b/servers/physics/SCsub index 3b84c5ef18..95296eadbe 100644 --- a/servers/physics/SCsub +++ b/servers/physics/SCsub @@ -5,5 +5,3 @@ env.add_source_files(env.servers_sources,"*.cpp") Export('env') SConscript("joints/SCsub") - - diff --git a/servers/physics/joints/SCsub b/servers/physics/joints/SCsub index 97d6edea21..d31af2c1c4 100644 --- a/servers/physics/joints/SCsub +++ b/servers/physics/joints/SCsub @@ -3,6 +3,3 @@ Import('env') env.add_source_files(env.servers_sources,"*.cpp") Export('env') - - - diff --git a/servers/physics_2d/SCsub b/servers/physics_2d/SCsub index a2c2b51a61..ebb7f8be00 100644 --- a/servers/physics_2d/SCsub +++ b/servers/physics_2d/SCsub @@ -1,4 +1,3 @@ Import('env') env.add_source_files(env.servers_sources,"*.cpp") - diff --git a/servers/physics_2d/body_2d_sw.cpp b/servers/physics_2d/body_2d_sw.cpp index 38835c9a82..3afbbe5455 100644 --- a/servers/physics_2d/body_2d_sw.cpp +++ b/servers/physics_2d/body_2d_sw.cpp @@ -442,7 +442,7 @@ void Body2DSW::integrate_forces(real_t p_step) { //compute motion, angular and etc. velocities from prev transform linear_velocity = (new_transform.elements[2] - get_transform().elements[2])/p_step; - real_t rot = new_transform.affine_inverse().basis_xform(get_transform().elements[1]).atan2(); + real_t rot = new_transform.affine_inverse().basis_xform(get_transform().elements[1]).angle(); angular_velocity = rot / p_step; motion = new_transform.elements[2] - get_transform().elements[2]; diff --git a/servers/spatial_sound/SCsub b/servers/spatial_sound/SCsub index 16fe3a59ac..d31af2c1c4 100644 --- a/servers/spatial_sound/SCsub +++ b/servers/spatial_sound/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.servers_sources,"*.cpp") Export('env') - - diff --git a/servers/spatial_sound_2d/SCsub b/servers/spatial_sound_2d/SCsub index 16fe3a59ac..d31af2c1c4 100644 --- a/servers/spatial_sound_2d/SCsub +++ b/servers/spatial_sound_2d/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.servers_sources,"*.cpp") Export('env') - - diff --git a/servers/visual/SCsub b/servers/visual/SCsub index 16fe3a59ac..d31af2c1c4 100644 --- a/servers/visual/SCsub +++ b/servers/visual/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.servers_sources,"*.cpp") Export('env') - - diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index e22b3c3a6c..aca301e0a8 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -693,7 +693,7 @@ public: Rect2 rect; RID texture; float margin[4]; - float draw_center; + bool draw_center; Color color; CommandStyle() { draw_center=true; type = TYPE_STYLE; } }; diff --git a/servers/visual/rasterizer_dummy.h b/servers/visual/rasterizer_dummy.h index f582fbd8ee..2c503249fe 100644 --- a/servers/visual/rasterizer_dummy.h +++ b/servers/visual/rasterizer_dummy.h @@ -162,10 +162,6 @@ class RasterizerDummy : public Rasterizer { uint32_t format; uint32_t morph_format; - RID material; - bool material_owned; - - Surface() { packed=false; diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index ea56306241..6532c3f0ac 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -1043,6 +1043,7 @@ const ShaderLanguage::BuiltinsDef ShaderLanguage::vertex_builtins_defs[]={ { "SRC_TANGENT", TYPE_VEC3}, { "SRC_BINORMALF", TYPE_FLOAT}, + { "POSITION", TYPE_VEC4 }, { "VERTEX", TYPE_VEC3}, { "NORMAL", TYPE_VEC3}, { "TANGENT", TYPE_VEC3}, diff --git a/tools/Godot.app/Contents/Resources/Godot.icns b/tools/Godot.app/Contents/Resources/Godot.icns Binary files differindex 18bc68d6ea..4a3dc0415a 100644 --- a/tools/Godot.app/Contents/Resources/Godot.icns +++ b/tools/Godot.app/Contents/Resources/Godot.icns diff --git a/tools/SCsub b/tools/SCsub index ce7df2c35b..f046e9ad08 100644 --- a/tools/SCsub +++ b/tools/SCsub @@ -11,11 +11,10 @@ if (env["tools"]!="no"): SConscript('collada/SCsub'); SConscript('docdump/SCsub'); SConscript('freetype/SCsub'); + SConscript('pe_bliss/SCsub'); SConscript('doc/SCsub') SConscript('pck/SCsub') lib = env.Library("tool",env.tool_sources) env.Prepend(LIBS=[lib]) - - diff --git a/tools/collada/SCsub b/tools/collada/SCsub index c8eaa596d1..34524f10ef 100644 --- a/tools/collada/SCsub +++ b/tools/collada/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.tool_sources,"*.cpp") Export('env') - - diff --git a/tools/doc/SCsub b/tools/doc/SCsub index c8eaa596d1..34524f10ef 100644 --- a/tools/doc/SCsub +++ b/tools/doc/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.tool_sources,"*.cpp") Export('env') - - diff --git a/tools/doc/doc_data.cpp b/tools/doc/doc_data.cpp index 432f358627..c1d3e5e314 100644 --- a/tools/doc/doc_data.cpp +++ b/tools/doc/doc_data.cpp @@ -189,9 +189,11 @@ void DocData::generate(bool p_basic_types) { arginfo=E->get().return_val; if (arginfo.type==Variant::NIL) continue; +#ifdef DEBUG_METHODS_ENABLED if (m && m->get_return_type()!=StringName()) method.return_type=m->get_return_type(); else +#endif method.return_type=(arginfo.hint==PROPERTY_HINT_RESOURCE_TYPE)?arginfo.hint_string:Variant::get_type_name(arginfo.type); } else { diff --git a/tools/docdump/SCsub b/tools/docdump/SCsub index c8eaa596d1..34524f10ef 100644 --- a/tools/docdump/SCsub +++ b/tools/docdump/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.tool_sources,"*.cpp") Export('env') - - diff --git a/tools/docdump/makehtml.py b/tools/docdump/makehtml.py index d533ca1b8b..9b9c62f33b 100644 --- a/tools/docdump/makehtml.py +++ b/tools/docdump/makehtml.py @@ -1,5 +1,19 @@ import sys import xml.etree.ElementTree as ET +from xml.sax.saxutils import escape, unescape + +html_escape_table = { + '"': """, + "'": "'" +} + +html_unescape_table = {v:k for k, v in html_escape_table.items()} + +def html_escape(text): + return escape(text, html_escape_table) + +def html_unescape(text): + return unescape(text, html_unescape_table) input_list = [] @@ -96,7 +110,7 @@ def make_html_class_list(class_list,columns): idx=0 for n in class_list: - col = idx/col_max + col = int(idx/col_max) if (col>=columns): col=columns-1 fit_columns[col]+=[n] @@ -299,6 +313,7 @@ def make_type(p_type,p_parent): def make_text_def(class_name,parent,text): + text = html_escape(text) pos=0 while(True): pos = text.find("[",pos) @@ -598,7 +613,6 @@ def make_html_class(node): descr=node.find("description") if (descr!=None and descr.text.strip()!=""): - h4=ET.SubElement(div,"h4") h4.text="Description:" @@ -644,7 +658,6 @@ def make_html_class(node): class_names=[] classes={} - for file in input_list: tree = ET.parse(file) doc=tree.getroot() diff --git a/tools/editor/SCsub b/tools/editor/SCsub index 73ec530177..cd46ff8353 100644 --- a/tools/editor/SCsub +++ b/tools/editor/SCsub @@ -28,7 +28,7 @@ def make_doc_header(target,source,env): - + if (env["tools"]=="yes"): @@ -43,17 +43,16 @@ if (env["tools"]=="yes"): f.write(reg_exporters_inc) f.write(reg_exporters) f.close() - + env.Depends("#tools/editor/doc_data_compressed.h","#doc/base/classes.xml") env.Command("#tools/editor/doc_data_compressed.h","#doc/base/classes.xml",make_doc_header) #make_doc_header(env.File("#tools/editor/doc_data_raw.h").srcnode().abspath,env.File("#doc/base/classes.xml").srcnode().abspath,env) - + env.add_source_files(env.tool_sources,"*.cpp") - + Export('env') - SConscript('icons/SCsub'); + SConscript('icons/SCsub'); SConscript('plugins/SCsub'); SConscript('fileserver/SCsub'); SConscript('io_plugins/SCsub'); - diff --git a/tools/editor/animation_editor.cpp b/tools/editor/animation_editor.cpp index 5df49bd327..ace6fda696 100644 --- a/tools/editor/animation_editor.cpp +++ b/tools/editor/animation_editor.cpp @@ -627,31 +627,41 @@ public: }; -void AnimationKeyEditor::_menu_track(int p_type) { +void AnimationKeyEditor::_menu_add_track(int p_type) { ERR_FAIL_COND(!animation.is_valid()); - last_menu_track_opt=p_type; switch(p_type) { - case TRACK_MENU_ADD_CALL_TRACK: { + case ADD_TRACK_MENU_ADD_CALL_TRACK: { if (root) { call_select->popup_centered_ratio(); break; } } break; - case TRACK_MENU_ADD_VALUE_TRACK: - case TRACK_MENU_ADD_TRANSFORM_TRACK: { + case ADD_TRACK_MENU_ADD_VALUE_TRACK: + case ADD_TRACK_MENU_ADD_TRANSFORM_TRACK: { undo_redo->create_action("Anim Add Track"); - undo_redo->add_do_method(animation.ptr(),"add_track",p_type); + undo_redo->add_do_method(animation.ptr(),"add_track",p_type); undo_redo->add_do_method(animation.ptr(),"track_set_path",animation->get_track_count(),"."); undo_redo->add_undo_method(animation.ptr(),"remove_track",animation->get_track_count()); undo_redo->commit_action(); } break; + } +} + +void AnimationKeyEditor::_menu_track(int p_type) { + + ERR_FAIL_COND(!animation.is_valid()); + + + last_menu_track_opt=p_type; + switch(p_type) { + case TRACK_MENU_SCALE: case TRACK_MENU_SCALE_PIVOT: { @@ -999,6 +1009,7 @@ void AnimationKeyEditor::_track_editor_draw() { if (!animation.is_valid()) { v_scroll->hide(); h_scroll->hide(); + menu_add_track->set_disabled(true); menu_track->set_disabled(true); edit_button->set_disabled(true); key_editor_tab->hide(); @@ -1008,6 +1019,7 @@ void AnimationKeyEditor::_track_editor_draw() { return; } + menu_add_track->set_disabled(false); menu_track->set_disabled(false); edit_button->set_disabled(false); move_up_button->set_disabled(false); @@ -2659,12 +2671,14 @@ void AnimationKeyEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { - zoomicon->set_texture( get_icon("Zoom","EditorIcons") ); - //menu_track->set_icon(get_icon("AddTrack","EditorIcons")); - menu_track->get_popup()->add_icon_item(get_icon("KeyValue","EditorIcons"),"Add Normal Track",TRACK_MENU_ADD_VALUE_TRACK); - menu_track->get_popup()->add_icon_item(get_icon("KeyXform","EditorIcons"),"Add Transform Track",TRACK_MENU_ADD_TRANSFORM_TRACK); - menu_track->get_popup()->add_icon_item(get_icon("KeyCall","EditorIcons"),"Add Call Func Track",TRACK_MENU_ADD_CALL_TRACK); - menu_track->get_popup()->add_separator(); + zoomicon->set_texture( get_icon("Zoom","EditorIcons") ); + + menu_add_track->set_icon(get_icon("AddTrack","EditorIcons")); + menu_add_track->get_popup()->add_icon_item(get_icon("KeyValue","EditorIcons"),"Add Normal Track",ADD_TRACK_MENU_ADD_VALUE_TRACK); + menu_add_track->get_popup()->add_icon_item(get_icon("KeyXform","EditorIcons"),"Add Transform Track",ADD_TRACK_MENU_ADD_TRANSFORM_TRACK); + menu_add_track->get_popup()->add_icon_item(get_icon("KeyCall","EditorIcons"),"Add Call Func Track",ADD_TRACK_MENU_ADD_CALL_TRACK); + + menu_track->set_icon(get_icon("Tools","EditorIcons")); menu_track->get_popup()->add_item("Scale Selection",TRACK_MENU_SCALE); menu_track->get_popup()->add_item("Scale From Cursor",TRACK_MENU_SCALE_PIVOT); menu_track->get_popup()->add_separator(); @@ -3511,6 +3525,7 @@ void AnimationKeyEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("_track_editor_input_event"),&AnimationKeyEditor::_track_editor_input_event); ObjectTypeDB::bind_method(_MD("_track_name_changed"),&AnimationKeyEditor::_track_name_changed); ObjectTypeDB::bind_method(_MD("_track_menu_selected"),&AnimationKeyEditor::_track_menu_selected); + ObjectTypeDB::bind_method(_MD("_menu_add_track"),&AnimationKeyEditor::_menu_add_track); ObjectTypeDB::bind_method(_MD("_menu_track"),&AnimationKeyEditor::_menu_track); ObjectTypeDB::bind_method(_MD("_clear_selection_for_anim"),&AnimationKeyEditor::_clear_selection_for_anim); ObjectTypeDB::bind_method(_MD("_select_at_anim"),&AnimationKeyEditor::_select_at_anim); @@ -3563,15 +3578,6 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h //menu->set_pos(Point2()); //add_child(menu); - menu_track = memnew( MenuButton ); - menu_track->set_text("Tracks"); - hb->add_child(menu_track); - menu_track->get_popup()->connect("item_pressed",this,"_menu_track"); - - - - hb->add_child( memnew( VSeparator ) ); - zoomicon = memnew( TextureFrame ); hb->add_child(zoomicon); zoomicon->set_tooltip("Animation zoom."); @@ -3629,6 +3635,10 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h hb->add_child( memnew( VSeparator ) ); + menu_add_track = memnew( MenuButton ); + hb->add_child(menu_add_track); + menu_add_track->get_popup()->connect("item_pressed",this,"_menu_add_track"); + menu_add_track->set_tooltip("Add new tracks."); move_up_button = memnew( ToolButton ); hb->add_child(move_up_button); @@ -3653,6 +3663,11 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h hb->add_child(memnew( VSeparator )); + menu_track = memnew( MenuButton ); + hb->add_child(menu_track); + menu_track->get_popup()->connect("item_pressed",this,"_menu_track"); + menu_track->set_tooltip("Track tools"); + edit_button = memnew( ToolButton ); edit_button->set_toggle_mode(true); edit_button->set_focus_mode(FOCUS_NONE); diff --git a/tools/editor/animation_editor.h b/tools/editor/animation_editor.h index 4a1cc21154..629377d78e 100644 --- a/tools/editor/animation_editor.h +++ b/tools/editor/animation_editor.h @@ -70,9 +70,9 @@ class AnimationKeyEditor : public VBoxContainer { enum { - TRACK_MENU_ADD_VALUE_TRACK, - TRACK_MENU_ADD_TRANSFORM_TRACK, - TRACK_MENU_ADD_CALL_TRACK, + ADD_TRACK_MENU_ADD_VALUE_TRACK, + ADD_TRACK_MENU_ADD_TRANSFORM_TRACK, + ADD_TRACK_MENU_ADD_CALL_TRACK, TRACK_MENU_SCALE, TRACK_MENU_SCALE_PIVOT, TRACK_MENU_MOVE_UP, @@ -192,6 +192,7 @@ class AnimationKeyEditor : public VBoxContainer { SpinBox *step; + MenuButton *menu_add_track; MenuButton *menu_track; HScrollBar *h_scroll; @@ -286,6 +287,7 @@ class AnimationKeyEditor : public VBoxContainer { void _scroll_changed(double); + void _menu_add_track(int p_type); void _menu_track(int p_type); void _clear_selection_for_anim(const Ref<Animation>& p_anim); diff --git a/tools/editor/code_editor.cpp b/tools/editor/code_editor.cpp index 2ed03a1858..685763cadb 100644 --- a/tools/editor/code_editor.cpp +++ b/tools/editor/code_editor.cpp @@ -618,7 +618,7 @@ CodeTextEditor::CodeTextEditor() { line_col = memnew( Label ); add_child(line_col); line_col->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,135); - line_col->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,20); + line_col->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,15); line_col->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,1); line_col->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,5); //line_col->set_align(Label::ALIGN_RIGHT); @@ -637,7 +637,7 @@ CodeTextEditor::CodeTextEditor() { error = memnew( Label ); add_child(error); error->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN,5); - error->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,20); + error->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,15); error->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,1); error->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,130); error->hide(); diff --git a/tools/editor/editor_help.cpp b/tools/editor/editor_help.cpp index 46ed2194a8..1bc09c5102 100644 --- a/tools/editor/editor_help.cpp +++ b/tools/editor/editor_help.cpp @@ -300,9 +300,9 @@ void EditorHelpSearch::_bind_methods() { } -EditorHelpSearch::EditorHelpSearch(EditorNode *p_editor) { +EditorHelpSearch::EditorHelpSearch() { - editor=p_editor; + editor=EditorNode::get_singleton(); VBoxContainer *vbc = memnew( VBoxContainer ); add_child(vbc); set_child_rect(vbc); @@ -318,17 +318,138 @@ EditorHelpSearch::EditorHelpSearch(EditorNode *p_editor) { search_box->connect("input_event",this,"_sbox_input"); search_options = memnew( Tree ); vbc->add_margin_child("Matches:",search_options,true); - get_ok()->set_text("View"); + get_ok()->set_text("Open"); get_ok()->set_disabled(true); register_text_enter(search_box); set_hide_on_ok(false); search_options->connect("item_activated",this,"_confirmed"); set_title("Search Classes"); + // search_options->set_hide_root(true); } +///////////////////////////////// + +//////////////////////////////////// +/// ///////////////////////////////// + + + +void EditorHelpIndex::add_type(const String& p_type,HashMap<String,TreeItem*>& p_types,TreeItem *p_root) { + + if (p_types.has(p_type)) + return; +// if (!ObjectTypeDB::is_type(p_type,base) || p_type==base) +// return; + + String inherits=EditorHelp::get_doc_data()->class_list[p_type].inherits; + + TreeItem *parent=p_root; + + + if (inherits.length()) { + + if (!p_types.has(inherits)) { + + add_type(inherits,p_types,p_root); + } + + if (p_types.has(inherits) ) + parent=p_types[inherits]; + } + + TreeItem *item = class_list->create_item(parent); + item->set_metadata(0,p_type); + item->set_tooltip(0,EditorHelp::get_doc_data()->class_list[p_type].brief_description); + item->set_text(0,p_type); + + + if (has_icon(p_type,"EditorIcons")) { + + item->set_icon(0, get_icon(p_type,"EditorIcons")); + } + + p_types[p_type]=item; +} + + +void EditorHelpIndex::_tree_item_selected() { + + + TreeItem *s=class_list->get_selected(); + if (!s) + return; + + emit_signal("open_class",s->get_text(0)); + + hide(); + + //_goto_desc(s->get_text(0)); + +} + +void EditorHelpIndex::select_class(const String& p_class) { + + if (!tree_item_map.has(p_class)) + return; + tree_item_map[p_class]->select(0); + class_list->ensure_cursor_is_visible(); +} + +void EditorHelpIndex::_notification(int p_what) { + + if (p_what==NOTIFICATION_ENTER_TREE) { + + class_list->clear(); + tree_item_map.clear(); + TreeItem *root = class_list->create_item(); + class_list->set_hide_root(true); + connect("confirmed",this,"_tree_item_selected"); + + + for(Map<String,DocData::ClassDoc>::Element *E=EditorHelp::get_doc_data()->class_list.front();E;E=E->next()) { + + + add_type(E->key(),tree_item_map,root); + } + + } +} + +void EditorHelpIndex::_bind_methods() { + + ObjectTypeDB::bind_method("_tree_item_selected",&EditorHelpIndex::_tree_item_selected); + ObjectTypeDB::bind_method("select_class",&EditorHelpIndex::select_class); + ADD_SIGNAL( MethodInfo("open_class")); +} + + +EditorHelpIndex::EditorHelpIndex() { + + + VBoxContainer *vbc = memnew( VBoxContainer ); + add_child(vbc); + set_child_rect(vbc); + + class_list = memnew( Tree ); + vbc->add_margin_child("Class List: ",class_list,true); + class_list->set_v_size_flags(SIZE_EXPAND_FILL); + + + class_list->connect("item_activated",this,"_tree_item_selected"); + + + get_ok()->set_text("Open"); +} + + + +///////////////////////////////// + +//////////////////////////////////// +/// ///////////////////////////////// DocData *EditorHelp::doc=NULL; void EditorHelp::_unhandled_key_input(const InputEvent& p_ev) { @@ -339,8 +460,6 @@ void EditorHelp::_unhandled_key_input(const InputEvent& p_ev) { search->grab_focus(); search->select_all(); - } else if (p_ev.key.mod.shift && p_ev.key.scancode==KEY_F1) { - class_search->popup(); } } @@ -351,17 +470,19 @@ void EditorHelp::_search(const String&) { String stext=search->get_text(); - bool keep = prev_search==stext && class_list->get_selected() && prev_search_page==class_list->get_selected()->get_text(0); + bool keep = prev_search==stext; - class_desc->search(stext, keep); + bool ret = class_desc->search(stext, keep); + if (!ret) { + class_desc->search(stext, false); + } prev_search=stext; - if (class_list->get_selected()) - prev_search_page=class_list->get_selected()->get_text(0); } +#if 0 void EditorHelp::_button_pressed(int p_idx) { if (p_idx==PAGE_CLASS_LIST) { @@ -399,16 +520,11 @@ void EditorHelp::_button_pressed(int p_idx) { } else if (p_idx==PAGE_SEARCH) { _search(""); - } else if (p_idx==CLASS_SEARCH) { - - class_search->popup(); } - - } - +#endif void EditorHelp::_class_list_select(const String& p_select) { @@ -417,16 +533,28 @@ void EditorHelp::_class_list_select(const String& p_select) { void EditorHelp::_class_desc_select(const String& p_select) { + + +// print_line("LINK: "+p_select); if (p_select.begins_with("#")) { - _goto_desc(p_select.substr(1,p_select.length())); + //_goto_desc(p_select.substr(1,p_select.length())); + emit_signal("go_to_help","class_name:"+p_select.substr(1,p_select.length())); return; } else if (p_select.begins_with("@")) { String m = p_select.substr(1,p_select.length()); - if (!method_line.has(m)) - return; - class_desc->scroll_to_line(method_line[m]); - return; + + if (m.find(".")!=-1) { + //must go somewhere else + + emit_signal("go_to_help","class_method:"+m.get_slice(".",0)+":"+m.get_slice(".",0)); + } else { + + if (!method_line.has(m)) + return; + class_desc->scroll_to_line(method_line[m]); + } + } @@ -449,68 +577,40 @@ void EditorHelp::_add_type(const String& p_type) { } -void EditorHelp::_update_history_buttons() { - - back->set_disabled(history_pos<2); - forward->set_disabled(history_pos>=history.size()); - -} - - void EditorHelp::_scroll_changed(double p_scroll) { if (scroll_locked) return; - int p = history_pos -1; - if (p<0 || p>=history.size()) - return; - if (class_desc->get_v_scroll()->is_hidden()) p_scroll=0; - history[p].scroll=p_scroll; + //history[p].scroll=p_scroll; } -Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_vscr) { +Error EditorHelp::_goto_desc(const String& p_class,int p_vscr) { //ERR_FAIL_COND(!doc->class_list.has(p_class)); if (!doc->class_list.has(p_class)) return ERR_DOES_NOT_EXIST; - if (tree_item_map.has(p_class)) { + //if (tree_item_map.has(p_class)) { select_locked = true; - tree_item_map[p_class]->select(0); - class_list->ensure_cursor_is_visible(); - } + //} class_desc->show(); //tabs->set_current_tab(PAGE_CLASS_DESC); - edited_class->set_pressed(true); - class_list_button->set_pressed(false); description_line=0; - if (p_class==edited_class->get_text()) + if (p_class==edited_class) return OK; //already there scroll_locked=true; - if (p_update_history) { - - history.resize(history_pos); - history_pos++; - History h; - h.c=p_class; - h.scroll=0; - history.push_back(h); - _update_history_buttons(); - class_desc->get_v_scroll()->set_val(0); - } - class_desc->clear(); method_line.clear(); - edited_class->set_text(p_class); + edited_class=p_class; //edited_class->show(); @@ -529,7 +629,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color")); class_desc->add_text("Class: "); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/base_type_color")); - class_desc->add_text(p_class); + _add_text(p_class); class_desc->pop(); class_desc->pop(); class_desc->pop(); @@ -547,7 +647,6 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->pop(); class_desc->add_newline(); class_desc->add_newline(); - class_desc->add_newline(); } @@ -561,8 +660,13 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v //class_desc->add_newline(); class_desc->add_newline(); + class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); + class_desc->push_font( get_font("normal","Fonts") ); + class_desc->push_indent(1); _add_text(cd.brief_description); - class_desc->add_newline(); + class_desc->pop(); + class_desc->pop(); + class_desc->pop(); class_desc->add_newline(); class_desc->add_newline(); } @@ -593,7 +697,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_meta("@"+cd.methods[i].name); } class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); - class_desc->add_text(cd.methods[i].name); + _add_text(cd.methods[i].name); class_desc->pop(); if (cd.methods[i].description!="") class_desc->pop(); @@ -605,13 +709,14 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (j>0) class_desc->add_text(", "); _add_type(cd.methods[i].arguments[j].type); - class_desc->add_text(" "+cd.methods[i].arguments[j].name); + class_desc->add_text(" "); + _add_text(cd.methods[i].arguments[j].name); if (cd.methods[i].arguments[j].default_value!="") { class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text("="); class_desc->pop(); - class_desc->add_text(cd.methods[i].arguments[j].default_value); + _add_text(cd.methods[i].arguments[j].default_value); } class_desc->pop(); @@ -623,7 +728,8 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (cd.methods[i].qualifiers!="") { class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color")); - class_desc->add_text(" "+cd.methods[i].qualifiers); + class_desc->add_text(" "); + _add_text(cd.methods[i].qualifiers); class_desc->pop(); } @@ -639,6 +745,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (cd.properties.size()) { + class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color")); class_desc->push_font(doc_title_font); class_desc->add_text("Members:"); @@ -656,7 +763,8 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_font(doc_code_font); _add_type(cd.properties[i].type); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); - class_desc->add_text(" "+cd.properties[i].name); + class_desc->add_text(" "); + _add_text(cd.properties[i].name); class_desc->pop(); class_desc->pop(); @@ -664,7 +772,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_font(doc_font); class_desc->add_text(" "); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/comment_color")); - class_desc->add_text(cd.properties[i].description); + _add_text(cd.properties[i].description); class_desc->pop(); class_desc->pop(); @@ -699,7 +807,8 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_font(doc_code_font); _add_type(cd.theme_properties[i].type); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); - class_desc->add_text(" "+cd.theme_properties[i].name); + class_desc->add_text(" "); + _add_text(cd.theme_properties[i].name); class_desc->pop(); class_desc->pop(); @@ -707,7 +816,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_font(doc_font); class_desc->add_text(" "); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/comment_color")); - class_desc->add_text(cd.theme_properties[i].description); + _add_text(cd.theme_properties[i].description); class_desc->pop(); class_desc->pop(); @@ -716,10 +825,9 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->add_newline(); } + class_desc->add_newline(); class_desc->pop(); - class_desc->add_newline(); - class_desc->add_newline(); } if (cd.signals.size()) { @@ -738,11 +846,11 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v for(int i=0;i<cd.signals.size();i++) { signal_line[cd.signals[i].name]=class_desc->get_line_count()-2; //gets overriden if description - class_desc->push_font(doc_code_font); + class_desc->push_font(doc_code_font); // monofont //_add_type("void"); //class_desc->add_text(" "); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); - class_desc->add_text(cd.signals[i].name); + _add_text(cd.signals[i].name); class_desc->pop(); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text(cd.signals[i].arguments.size()?"( ":"("); @@ -752,13 +860,14 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (j>0) class_desc->add_text(", "); _add_type(cd.signals[i].arguments[j].type); - class_desc->add_text(" "+cd.signals[i].arguments[j].name); + class_desc->add_text(" "); + _add_text(cd.signals[i].arguments[j].name); if (cd.signals[i].arguments[j].default_value!="") { class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text("="); class_desc->pop(); - class_desc->add_text(cd.signals[i].arguments[j].default_value); + _add_text(cd.signals[i].arguments[j].default_value); } class_desc->pop(); @@ -767,21 +876,21 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text(cd.signals[i].arguments.size()?" )":")"); class_desc->pop(); + class_desc->pop(); // end monofont if (cd.signals[i].description!="") { class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/comment_color")); - class_desc->add_text(" "+cd.signals[i].description); + class_desc->add_text(" "); + _add_text(cd.signals[i].description); class_desc->pop(); } - class_desc->pop();//monofont class_desc->add_newline(); } class_desc->pop(); class_desc->add_newline(); - class_desc->add_newline(); } @@ -803,20 +912,20 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v constant_line[cd.constants[i].name]=class_desc->get_line_count()-2; class_desc->push_font(doc_code_font); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/base_type_color")); - class_desc->add_text(cd.constants[i].name); + _add_text(cd.constants[i].name); class_desc->pop(); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text(" = "); class_desc->pop(); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color")); - class_desc->add_text(cd.constants[i].value); + _add_text(cd.constants[i].value); class_desc->pop(); class_desc->pop(); if (cd.constants[i].description!="") { class_desc->push_font(doc_font); class_desc->add_text(" "); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/comment_color")); - class_desc->add_text(cd.constants[i].description); + _add_text(cd.constants[i].description); class_desc->pop(); class_desc->pop(); } @@ -826,7 +935,6 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->pop(); class_desc->add_newline(); - class_desc->add_newline(); } @@ -834,7 +942,6 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (cd.description!="") { description_line=class_desc->get_line_count()-2; - class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color")); class_desc->push_font(doc_title_font); class_desc->add_text("Description:"); @@ -842,8 +949,14 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->pop(); class_desc->add_newline(); - _add_text(cd.description); class_desc->add_newline(); + class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); + class_desc->push_font( get_font("normal","Fonts") ); + class_desc->push_indent(1); + _add_text(cd.description); + class_desc->pop(); + class_desc->pop(); + class_desc->pop(); class_desc->add_newline(); class_desc->add_newline(); } @@ -858,22 +971,18 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->add_newline(); class_desc->add_newline(); - class_desc->push_indent(1); for(int i=0;i<cd.methods.size();i++) { method_line[cd.methods[i].name]=class_desc->get_line_count()-2; - if( cd.methods[i].description != "") { - class_desc->add_newline(); - } class_desc->push_font(doc_code_font); _add_type(cd.methods[i].return_type); class_desc->add_text(" "); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); - class_desc->add_text(cd.methods[i].name); + _add_text(cd.methods[i].name); class_desc->pop(); class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text(cd.methods[i].arguments.size()?"( ":"("); @@ -883,13 +992,14 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (j>0) class_desc->add_text(", "); _add_type(cd.methods[i].arguments[j].type); - class_desc->add_text(" "+cd.methods[i].arguments[j].name); + class_desc->add_text(" "); + _add_text(cd.methods[i].arguments[j].name); if (cd.methods[i].arguments[j].default_value!="") { class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/symbol_color")); class_desc->add_text("="); class_desc->pop(); - class_desc->add_text(cd.methods[i].arguments[j].default_value); + _add_text(cd.methods[i].arguments[j].default_value); } class_desc->pop(); @@ -901,19 +1011,23 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (cd.methods[i].qualifiers!="") { class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color")); - class_desc->add_text(" "+cd.methods[i].qualifiers); + class_desc->add_text(" "); + _add_text(cd.methods[i].qualifiers); class_desc->pop(); } class_desc->pop(); - if( cd.methods[i].description != "") { - class_desc->add_text(" "); - _add_text(cd.methods[i].description); - class_desc->add_newline(); - class_desc->add_newline(); - } + class_desc->add_newline(); + class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); + class_desc->push_font( get_font("normal","Fonts") ); + class_desc->push_indent(1); + _add_text(cd.methods[i].description); + class_desc->pop(); + class_desc->pop(); + class_desc->pop(); + class_desc->add_newline(); class_desc->add_newline(); class_desc->add_newline(); @@ -925,10 +1039,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v } - if (!p_update_history) { - class_desc->get_v_scroll()->set_val(history[history_pos-1].scroll); - } scroll_locked=false; @@ -939,8 +1050,6 @@ void EditorHelp::_request_help(const String& p_string) { Error err = _goto_desc(p_string); if (err==OK) { editor->call("_editor_select",3); - } else { - class_search->popup(p_string); } //100 palabras } @@ -987,9 +1096,9 @@ void EditorHelp::_help_callback(const String& p_topic) { void EditorHelp::_add_text(const String& p_bbcode) { - class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); + /*class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/text_color")); class_desc->push_font( get_font("normal","Fonts") ); - class_desc->push_indent(1); + class_desc->push_indent(1);*/ int pos = 0; List<String> tag_stack; @@ -1067,7 +1176,7 @@ void EditorHelp::_add_text(const String& p_bbcode) { } else if (tag=="i") { //use italics font - //class_desc->push_font(get_font("italic","Fonts")); + class_desc->push_font(get_font("italic","Fonts")); pos=brk_end+1; tag_stack.push_front(tag); } else if (tag=="code") { @@ -1203,321 +1312,161 @@ void EditorHelp::_add_text(const String& p_bbcode) { } } + /*class_desc->pop(); class_desc->pop(); - class_desc->pop(); + class_desc->pop();*/ } -void EditorHelp::add_type(const String& p_type,HashMap<String,TreeItem*>& p_types,TreeItem *p_root) { - - if (p_types.has(p_type)) - return; -// if (!ObjectTypeDB::is_type(p_type,base) || p_type==base) -// return; - - String inherits=doc->class_list[p_type].inherits; - - TreeItem *parent=p_root; - if (inherits.length()) { +void EditorHelp::_update_doc() { - if (!p_types.has(inherits)) { - add_type(inherits,p_types,p_root); - } - if (p_types.has(inherits) ) - parent=p_types[inherits]; - } +} - TreeItem *item = class_list->create_item(parent); - item->set_metadata(0,p_type); - item->set_tooltip(0,doc->class_list[p_type].brief_description); - item->set_text(0,p_type); +void EditorHelp::generate_doc() { - if (has_icon(p_type,"EditorIcons")) { + doc = memnew( DocData ); + doc->generate(true); + DocData compdoc; + compdoc.load_compressed(_doc_data_compressed,_doc_data_compressed_size,_doc_data_uncompressed_size); + doc->merge_from(compdoc); //ensure all is up to date - item->set_icon(0, get_icon(p_type,"EditorIcons")); - } - p_types[p_type]=item; } +void EditorHelp::_notification(int p_what) { -void EditorHelp::_update_doc() { - - - class_list->clear(); - - List<StringName> type_list; - - tree_item_map.clear(); + switch(p_what) { - TreeItem *root = class_list->create_item(); - class_list->set_hide_root(true); - List<StringName>::Element *I=type_list.front(); + case NOTIFICATION_READY: { - for(Map<String,DocData::ClassDoc>::Element *E=doc->class_list.front();E;E=E->next()) { +// forward->set_icon(get_icon("Forward","EditorIcons")); +// back->set_icon(get_icon("Back","EditorIcons")); + _update_doc(); + editor->connect("request_help",this,"_request_help"); - add_type(E->key(),tree_item_map,root); + } break; } +} + +void EditorHelp::go_to_help(const String& p_help) { + _help_callback(p_help); } +void EditorHelp::go_to_class(const String& p_class,int p_scroll) { -void EditorHelp::generate_doc() { + _goto_desc(p_class,p_scroll); +} - doc = memnew( DocData ); - doc->generate(true); - DocData compdoc; - compdoc.load_compressed(_doc_data_compressed,_doc_data_compressed_size,_doc_data_uncompressed_size); - doc->merge_from(compdoc); //ensure all is up to date +void EditorHelp::popup_search() { + search_dialog->popup_centered(Size2(250,80)); + search->grab_focus(); } -void EditorHelp::_notification(int p_what) { +void EditorHelp::_search_cbk() { + _search(search->get_text()); +} - switch(p_what) { +String EditorHelp::get_class_name() { - case NOTIFICATION_READY: { + return edited_class; +} +void EditorHelp::search_again() { + _search(prev_search); +} - forward->set_icon(get_icon("Forward","EditorIcons")); - back->set_icon(get_icon("Back","EditorIcons")); - _update_doc(); - editor->connect("request_help",this,"_request_help"); +int EditorHelp::get_scroll() const { - } break; - } + return class_desc->get_v_scroll()->get_val(); } +void EditorHelp::set_scroll(int p_scroll) { -void EditorHelp::_tree_item_selected() { - if (select_locked) { - select_locked = false; - return; - } - TreeItem *s=class_list->get_selected(); - if (!s) - return; - select_locked=true; - _goto_desc(s->get_text(0)); - select_locked=false; + class_desc->get_v_scroll()->set_val(p_scroll); + } void EditorHelp::_bind_methods() { ObjectTypeDB::bind_method("_class_list_select",&EditorHelp::_class_list_select); ObjectTypeDB::bind_method("_class_desc_select",&EditorHelp::_class_desc_select); - ObjectTypeDB::bind_method("_button_pressed",&EditorHelp::_button_pressed); +// ObjectTypeDB::bind_method("_button_pressed",&EditorHelp::_button_pressed); ObjectTypeDB::bind_method("_scroll_changed",&EditorHelp::_scroll_changed); ObjectTypeDB::bind_method("_request_help",&EditorHelp::_request_help); ObjectTypeDB::bind_method("_unhandled_key_input",&EditorHelp::_unhandled_key_input); ObjectTypeDB::bind_method("_search",&EditorHelp::_search); - ObjectTypeDB::bind_method("_tree_item_selected",&EditorHelp::_tree_item_selected); + ObjectTypeDB::bind_method("_search_cbk",&EditorHelp::_search_cbk); + ObjectTypeDB::bind_method("_help_callback",&EditorHelp::_help_callback); + ADD_SIGNAL(MethodInfo("go_to_help")); + } -EditorHelp::EditorHelp(EditorNode *p_editor) { +EditorHelp::EditorHelp() { - editor=p_editor; + editor=EditorNode::get_singleton(); VBoxContainer *vbc = this; - HBoxContainer *panel_hb = memnew( HBoxContainer ); - - Button *b = memnew( Button ); - b->set_text("Class List"); - panel_hb->add_child(b); - vbc->add_child(panel_hb); - b->set_toggle_mode(true); - b->set_pressed(true); - b->connect("pressed",this,"_button_pressed",make_binds(PAGE_CLASS_LIST)); - class_list_button=b; - class_list_button->hide(); - - b = memnew( Button ); - b->set_text("Class"); - panel_hb->add_child(b); - edited_class=b; - edited_class->hide(); - b->set_toggle_mode(true); - b->connect("pressed",this,"_button_pressed",make_binds(PAGE_CLASS_DESC)); - - b = memnew( Button ); - b->set_text("Search in Classes"); - panel_hb->add_child(b); - b->connect("pressed",this,"_button_pressed",make_binds(CLASS_SEARCH)); - - Control *expand = memnew( Control ); - expand->set_h_size_flags(SIZE_EXPAND_FILL); - panel_hb->add_child(expand); - - b = memnew( Button ); - panel_hb->add_child(b); - back=b; - b->connect("pressed",this,"_button_pressed",make_binds(PAGE_CLASS_PREV)); - - b = memnew( Button ); - panel_hb->add_child(b); - forward=b; - b->connect("pressed",this,"_button_pressed",make_binds(PAGE_CLASS_NEXT)); - - Separator *hs = memnew( VSeparator ); - panel_hb->add_child(hs); - Control *ec = memnew( Control ); - ec->set_custom_minimum_size(Size2(200,1)); - panel_hb->add_child(ec); - search = memnew( LineEdit ); - ec->add_child(search); - search->set_area_as_parent_rect(); - search->connect("text_entered",this,"_search"); - - b = memnew( Button ); - b->set_text("Find"); - panel_hb->add_child(b); - b->connect("pressed",this,"_button_pressed",make_binds(PAGE_SEARCH)); - - hs = memnew( VSeparator ); - panel_hb->add_child(hs); - - h_split = memnew( HSplitContainer ); - h_split->set_v_size_flags(SIZE_EXPAND_FILL); - - vbc->add_child(h_split); - - class_list = memnew( Tree ); - h_split->add_child(class_list); //class_list->connect("meta_clicked",this,"_class_list_select"); //class_list->set_selection_enabled(true); { - PanelContainer *pc = memnew( PanelContainer ); + Panel *pc = memnew( Panel ); Ref<StyleBoxFlat> style( memnew( StyleBoxFlat ) ); style->set_bg_color( EditorSettings::get_singleton()->get("text_editor/background_color") ); - style->set_default_margin(MARGIN_LEFT,20); - style->set_default_margin(MARGIN_TOP,20); pc->add_style_override("panel", style); //get_stylebox("normal","TextEdit")); - h_split->add_child(pc); + vbc->add_child(pc); class_desc = memnew( RichTextLabel ); pc->add_child(class_desc); + class_desc->set_area_as_parent_rect(8); class_desc->connect("meta_clicked",this,"_class_desc_select"); } class_desc->get_v_scroll()->connect("value_changed",this,"_scroll_changed"); class_desc->set_selection_enabled(true); - editor=p_editor; - history_pos=0; + scroll_locked=false; select_locked=false; set_process_unhandled_key_input(true); - h_split->set_split_offset(200); - class_list->connect("cell_selected",this,"_tree_item_selected"); class_desc->hide(); - class_search = memnew( EditorHelpSearch(editor) ); - editor->get_gui_base()->add_child(class_search); - class_search->connect("go_to_help",this,"_help_callback"); -// prev_search_page=-1; -} - -EditorHelp::~EditorHelp() { - if (doc) - memdelete(doc); -} - - -void EditorHelpPlugin::edit(Object *p_object) { - - if (!p_object->cast_to<Script>()) - return; - - //editor_help->edit(p_object->cast_to<Script>()); -} - -bool EditorHelpPlugin::handles(Object *p_object) const { - - return false; -} - -void EditorHelpPlugin::make_visible(bool p_visible) { - - if (p_visible) { - editor_help->show(); - } else { - - editor_help->hide(); - } - -} - -void EditorHelpPlugin::selected_notify() { - - //editor_help->ensure_select_current(); -} - -Dictionary EditorHelpPlugin::get_state() const { - - return Dictionary(); -} - -void EditorHelpPlugin::set_state(const Dictionary& p_state) { - - //editor_help->set_state(p_state); -} -void EditorHelpPlugin::clear() { - - //editor_help->clear(); -} - -void EditorHelpPlugin::save_external_data() { - - //editor_help->save_external_data(); -} - -void EditorHelpPlugin::apply_changes() { - - //editor_help->apply_helps(); -} - -void EditorHelpPlugin::restore_global_state() { - - //if (bool(EDITOR_DEF("text_editor/restore_helps_on_load",true))) { -// editor_help->_load_files_state(); - //} - -} + search_dialog = memnew( ConfirmationDialog ); + add_child(search_dialog); + VBoxContainer *search_vb = memnew( VBoxContainer ); + search_dialog->add_child(search_vb); + search_dialog->set_child_rect(search_vb); + search = memnew( LineEdit ); + search_dialog->register_text_enter(search); + search_vb->add_margin_child("Search Text",search); + search_dialog->get_ok()->set_text("Find"); + search_dialog->connect("confirmed",this,"_search_cbk"); + search_dialog->set_hide_on_ok(false); + search_dialog->set_self_opacity(0.8); -void EditorHelpPlugin::save_global_state() { - //if (bool(EDITOR_DEF("text_editor/restore_helps_on_load",true))) { -// editor_help->_save_files_state(); -// } + /*class_search = memnew( EditorHelpSearch(editor) ); + editor->get_gui_base()->add_child(class_search); + class_search->connect("go_to_help",this,"_help_callback");*/ +// prev_search_page=-1; } - -EditorHelpPlugin::EditorHelpPlugin(EditorNode *p_node) { - - editor=p_node; - editor_help = memnew( EditorHelp(p_node) ); - editor->get_viewport()->add_child(editor_help); - editor_help->set_area_as_parent_rect(); - editor_help->hide(); - +EditorHelp::~EditorHelp() { } - -EditorHelpPlugin::~EditorHelpPlugin() -{ -} diff --git a/tools/editor/editor_help.h b/tools/editor/editor_help.h index d4066d076a..b5ee6eca6c 100644 --- a/tools/editor/editor_help.h +++ b/tools/editor/editor_help.h @@ -70,9 +70,29 @@ public: void popup(const String& p_term=""); - EditorHelpSearch(EditorNode *p_editor); + EditorHelpSearch(); }; +class EditorHelpIndex : public ConfirmationDialog { + OBJ_TYPE( EditorHelpIndex, ConfirmationDialog ); + + + Tree *class_list; + HashMap<String,TreeItem*> tree_item_map; + + void _tree_item_selected(); + void add_type(const String& p_type,HashMap<String,TreeItem*>& p_types,TreeItem *p_root); +protected: + + void _notification(int p_what); + static void _bind_methods(); + +public: + + void select_class(const String& p_class); + + EditorHelpIndex(); +}; class EditorHelp : public VBoxContainer { @@ -91,17 +111,11 @@ class EditorHelp : public VBoxContainer { }; - struct History { - String c; - int scroll; - }; - - Vector<History> history; - int history_pos; bool select_locked; String prev_search; - String prev_search_page; + + String edited_class; EditorNode *editor; Map<String,int> method_line; @@ -111,21 +125,17 @@ class EditorHelp : public VBoxContainer { Map<String,int> constant_line; int description_line; - Tree *class_list; RichTextLabel *class_desc; HSplitContainer *h_split; static DocData *doc; - Button *class_list_button; - Button *edited_class; - Button *back; - Button *forward; + + ConfirmationDialog *search_dialog; LineEdit *search; - String base_path; - HashMap<String,TreeItem*> tree_item_map; + String base_path; void _help_callback(const String& p_topic); @@ -133,25 +143,24 @@ class EditorHelp : public VBoxContainer { void _add_text(const String& p_text); bool scroll_locked; - void _button_pressed(int p_idx); + //void _button_pressed(int p_idx); void _add_type(const String& p_type); void _scroll_changed(double p_scroll); void _class_list_select(const String& p_select); void _class_desc_select(const String& p_select); - Error _goto_desc(const String& p_class,bool p_update_history=true,int p_vscr=-1); - void _update_history_buttons(); + Error _goto_desc(const String& p_class, int p_vscr=-1); + //void _update_history_buttons(); void _update_doc(); void _request_help(const String& p_string); void _search(const String& p_str); + void _search_cbk(); void _unhandled_key_input(const InputEvent& p_ev); - void add_type(const String& p_type,HashMap<String,TreeItem*>& p_types,TreeItem *p_root); - void _tree_item_selected(); - EditorHelpSearch *class_search; + protected: @@ -163,41 +172,25 @@ public: static void generate_doc(); static DocData *get_doc_data() { return doc; } - EditorHelp(EditorNode *p_editor=NULL); - ~EditorHelp(); -}; - - + void go_to_help(const String& p_help); + void go_to_class(const String& p_class,int p_scroll=0); -class EditorHelpPlugin : public EditorPlugin { + void popup_search(); + void search_again(); - OBJ_TYPE( EditorHelpPlugin, EditorPlugin ); + String get_class_name(); - EditorHelp *editor_help; - EditorNode *editor; -public: + void set_focused() { class_desc->grab_focus(); } - virtual String get_name() const { return "Help"; } - bool has_main_screen() const { return true; } - virtual void edit(Object *p_node); - virtual bool handles(Object *p_node) const; - virtual void make_visible(bool p_visible); - virtual void selected_notify(); + int get_scroll() const; + void set_scroll(int p_scroll); - Dictionary get_state() const; - virtual void set_state(const Dictionary& p_state); - virtual void clear(); - - virtual void save_external_data(); - virtual void apply_changes(); + EditorHelp(); + ~EditorHelp(); +}; - virtual void restore_global_state(); - virtual void save_global_state(); - EditorHelpPlugin(EditorNode *p_node); - ~EditorHelpPlugin(); -}; #endif // EDITOR_HELP_H diff --git a/tools/editor/editor_import_export.cpp b/tools/editor/editor_import_export.cpp index b13473e61c..f52c6e67a2 100644 --- a/tools/editor/editor_import_export.cpp +++ b/tools/editor/editor_import_export.cpp @@ -1209,30 +1209,42 @@ bool EditorExportPlatformPC::can_export(String *r_error) const { String exe_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; - if (!FileAccess::exists(exe_path+debug_binary32) || !FileAccess::exists(exe_path+release_binary32)) { + if (use64 && (!FileAccess::exists(exe_path+debug_binary64) || !FileAccess::exists(exe_path+release_binary64))) { valid=false; - err="No 32 bits export templates found.\nDownload and install export templates.\n"; + err="No 64 bits export templates found.\nDownload and install export templates.\n"; } - if (!FileAccess::exists(exe_path+debug_binary64) || !FileAccess::exists(exe_path+release_binary64)) { + + if (!use64 && (!FileAccess::exists(exe_path+debug_binary32) || !FileAccess::exists(exe_path+release_binary32))) { valid=false; - err="No 64 bits export templates found.\nDownload and install export templates.\n"; + err="No 32 bits export templates found.\nDownload and install export templates.\n"; } + if(custom_debug_binary=="" && custom_release_binary=="") { + if (r_error) *r_error=err; + return valid; + } - if (custom_debug_binary!="" && !FileAccess::exists(custom_debug_binary)) { - valid=false; - err+="Custom debug binary not found.\n"; + bool dvalid = true; + bool rvalid = true; + + if(!FileAccess::exists(custom_debug_binary)) { + dvalid = false; + err = "Custom debug binary not found.\n"; } - if (custom_release_binary!="" && !FileAccess::exists(custom_release_binary)) { - valid=false; - err+="Custom release binary not found.\n"; + if(!FileAccess::exists(custom_release_binary)) { + rvalid = false; + err = "Custom release binary not found.\n"; } + if (dvalid || rvalid) + valid = true; + else + valid = false; + if (r_error) *r_error=err; return valid; - } @@ -1706,6 +1718,17 @@ void EditorImportExport::load_config() { } } + if (cf->has_section("convert_samples")) { + + if (cf->has_section_key("convert_samples","max_hz")) + sample_action_max_hz=cf->get_value("convert_samples","max_hz"); + + if (cf->has_section_key("convert_samples","trim")) + sample_action_trim=cf->get_value("convert_samples","trim"); + } + + + } @@ -1816,6 +1839,14 @@ void EditorImportExport::save_config() { cf->set_value("script","encrypt_key",script_key); + switch(sample_action) { + case SAMPLE_ACTION_NONE: cf->set_value("convert_samples","action","none"); break; + case SAMPLE_ACTION_COMPRESS_RAM: cf->set_value("convert_samples","action","compress_ram"); break; + } + + cf->set_value("convert_samples","max_hz",sample_action_max_hz); + cf->set_value("convert_samples","trim",sample_action_trim); + cf->save("res://export.cfg"); } @@ -1841,6 +1872,35 @@ String EditorImportExport::script_get_encryption_key() const{ } +void EditorImportExport::sample_set_action(SampleAction p_action) { + + sample_action=p_action; +} + +EditorImportExport::SampleAction EditorImportExport::sample_get_action() const{ + + return sample_action; +} + +void EditorImportExport::sample_set_max_hz(int p_hz){ + + sample_action_max_hz=p_hz; +} +int EditorImportExport::sample_get_max_hz() const{ + + return sample_action_max_hz; +} + +void EditorImportExport::sample_set_trim(bool p_trim){ + + sample_action_trim=p_trim; +} +bool EditorImportExport::sample_get_trim() const{ + + return sample_action_trim; +} + + void EditorImportExport::_bind_methods() { ObjectTypeDB::bind_method(_MD("image_export_group_create"),&EditorImportExport::image_export_group_create); @@ -1868,8 +1928,13 @@ EditorImportExport::EditorImportExport() { image_formats.insert("png"); image_shrink=1; + script_action=SCRIPT_ACTION_COMPILE; + sample_action=SAMPLE_ACTION_NONE; + sample_action_max_hz=44100; + sample_action_trim=false; + } diff --git a/tools/editor/editor_import_export.h b/tools/editor/editor_import_export.h index 245adffbfd..1a3171e66b 100644 --- a/tools/editor/editor_import_export.h +++ b/tools/editor/editor_import_export.h @@ -245,6 +245,12 @@ public: SCRIPT_ACTION_ENCRYPT }; + enum SampleAction { + + SAMPLE_ACTION_NONE, + SAMPLE_ACTION_COMPRESS_RAM, + }; + protected: struct ImageGroup { @@ -274,6 +280,10 @@ protected: ScriptAction script_action; String script_key; + SampleAction sample_action; + int sample_action_max_hz; + bool sample_action_trim; + static EditorImportExport* singleton; static void _bind_methods(); @@ -343,6 +353,15 @@ public: void script_set_encryption_key(const String& p_key); String script_get_encryption_key() const; + void sample_set_action(SampleAction p_action); + SampleAction sample_get_action() const; + + void sample_set_max_hz(int p_hz); + int sample_get_max_hz() const; + + void sample_set_trim(bool p_trim); + bool sample_get_trim() const; + void load_config(); void save_config(); diff --git a/tools/editor/editor_log.cpp b/tools/editor/editor_log.cpp index 2d26490a8a..264117eecd 100644 --- a/tools/editor/editor_log.cpp +++ b/tools/editor/editor_log.cpp @@ -81,6 +81,7 @@ void EditorLog::_notification(int p_what) { log->add_color_override("default_color",get_color("font_color","Tree")); tb->set_normal_texture( get_icon("Collapse","EditorIcons")); tb->set_hover_texture( get_icon("CollapseHl","EditorIcons")); + //button->set_icon(get_icon("Console","EditorIcons")); } @@ -125,6 +126,7 @@ void EditorLog::add_message(const String& p_msg,bool p_error) { log->push_color(get_color("fg_error","Editor")); } else { button->set_icon(Ref<Texture>()); + } @@ -154,17 +156,20 @@ void EditorLog::_dragged(const Point2& p_ofs) { */ -ToolButton *EditorLog::get_button() { +Button *EditorLog::get_button() { return button; } void EditorLog::_flip_request() { - if (is_visible()) + if (is_visible()) { hide(); - else + button->show(); + } else { show(); + button->hide(); + } } void EditorLog::_undo_redo_cbk(void *p_self,const String& p_name) { @@ -200,7 +205,7 @@ EditorLog::EditorLog() { hb->add_child(title); - button = memnew( ToolButton ); + button = memnew( Button ); button->set_text_align(Button::ALIGN_LEFT); button->connect("pressed",this,"_flip_request"); button->set_focus_mode(FOCUS_NONE); diff --git a/tools/editor/editor_log.h b/tools/editor/editor_log.h index 6950ffa1a0..93044f9a2d 100644 --- a/tools/editor/editor_log.h +++ b/tools/editor/editor_log.h @@ -44,7 +44,7 @@ class EditorLog : public PanelContainer { OBJ_TYPE( EditorLog, PanelContainer ); - ToolButton *button; + Button *button; Button *clearbutton; Label *title; RichTextLabel *log; @@ -73,7 +73,7 @@ public: void add_message(const String& p_msg, bool p_error=false); void deinit(); - ToolButton *get_button(); + Button *get_button(); void clear(); EditorLog(); ~EditorLog(); diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp index a3d7cbd7cf..22acfd4d07 100644 --- a/tools/editor/editor_node.cpp +++ b/tools/editor/editor_node.cpp @@ -111,6 +111,8 @@ EditorNode *EditorNode::singleton=NULL; void EditorNode::_update_scene_tabs() { + bool show_rb = EditorSettings::get_singleton()->get("global/show_script_in_scene_tabs"); + scene_tabs->clear_tabs(); Ref<Texture> script_icon = gui_base->get_icon("Script","EditorIcons"); for(int i=0;i<editor_data.get_edited_scene_count();i++) { @@ -133,7 +135,7 @@ void EditorNode::_update_scene_tabs() { bool unsaved = (i==current)?saved_version!=editor_data.get_undo_redo().get_version():editor_data.get_scene_version(i)!=0; scene_tabs->add_tab(editor_data.get_scene_title(i)+(unsaved?"(*)":""),icon); - if (editor_data.get_scene_root_script(i).is_valid()) { + if (show_rb && editor_data.get_scene_root_script(i).is_valid()) { scene_tabs->set_tab_right_button(i,script_icon); } @@ -163,13 +165,13 @@ void EditorNode::_unhandled_input(const InputEvent& p_event) { switch(p_event.key.scancode) { - case KEY_F1: + /*case KEY_F1: if (!p_event.key.mod.shift && !p_event.key.mod.command) _editor_select(3); - break; - case KEY_F2: _editor_select(0); break; - case KEY_F3: _editor_select(1); break; - case KEY_F4: _editor_select(2); break; + break;*/ + case KEY_F1: _editor_select(0); break; + case KEY_F2: _editor_select(1); break; + case KEY_F3: _editor_select(2); break; case KEY_F5: _menu_option_confirm((p_event.key.mod.control&&p_event.key.mod.shift)?RUN_PLAY_CUSTOM_SCENE:RUN_PLAY,true); break; case KEY_F6: _menu_option_confirm(RUN_PLAY_SCENE,true); break; case KEY_F7: _menu_option_confirm(RUN_PAUSE,true); break; @@ -1617,10 +1619,8 @@ void EditorNode::_edit_current() { for(int i=0;i<editor_table.size();i++) { - if (editor_table[i]==main_plugin) { - main_editor_tabs->set_current_tab(i); - break; - } + + main_editor_buttons[i]->set_pressed(editor_table[i]==main_plugin); } } @@ -1740,8 +1740,10 @@ void EditorNode::_run(bool p_current,const String& p_custom) { } play_button->set_pressed(false); + play_button->set_icon(gui_base->get_icon("Play","EditorIcons")); //pause_button->set_pressed(false); play_scene_button->set_pressed(false); + play_scene_button->set_icon(gui_base->get_icon("PlayScene","EditorIcons")); String current_filename; String run_filename; @@ -1859,8 +1861,10 @@ void EditorNode::_run(bool p_current,const String& p_custom) { emit_signal("play_pressed"); if (p_current) { play_scene_button->set_pressed(true); + play_scene_button->set_icon(gui_base->get_icon("Reload","EditorIcons")); } else { play_button->set_pressed(true); + play_button->set_icon(gui_base->get_icon("Reload","EditorIcons")); } _playing_edited=p_current; @@ -2577,12 +2581,12 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { call_dialog->popup_centered_ratio(); } break; case RUN_PLAY: { - + _menu_option_confirm(RUN_STOP,true); _run(false); } break; case RUN_PLAY_CUSTOM_SCENE: { - + _menu_option_confirm(RUN_STOP,true); quick_run->popup("PackedScene",true); quick_run->set_title("Quick Run Scene.."); @@ -2599,18 +2603,20 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { editor_run.stop(); play_button->set_pressed(false); + play_button->set_icon(gui_base->get_icon("Play","EditorIcons")); play_scene_button->set_pressed(false); + play_scene_button->set_icon(gui_base->get_icon("PlayScene","EditorIcons")); //pause_button->set_pressed(false); emit_signal("stop_pressed"); } break; case RUN_PLAY_SCENE: { - + _menu_option_confirm(RUN_STOP,true); _run(true); } break; case RUN_PLAY_NATIVE: { - + _menu_option_confirm(RUN_STOP,true); emit_signal("play_pressed"); editor_run.run_native_notify(); @@ -2846,7 +2852,7 @@ Control* EditorNode::get_viewport() { void EditorNode::_editor_select(int p_which) { static bool selecting=false; - if (selecting) + if (selecting || changing_scene) return; selecting=true; @@ -2854,7 +2860,9 @@ void EditorNode::_editor_select(int p_which) { ERR_FAIL_INDEX(p_which,editor_table.size()); - main_editor_tabs->set_current_tab(p_which); + for(int i=0;i<main_editor_buttons.size();i++) { + main_editor_buttons[i]->set_pressed(i==p_which); + } selecting=false; @@ -2872,6 +2880,8 @@ void EditorNode::_editor_select(int p_which) { editor_plugin_screen=new_editor; editor_plugin_screen->make_visible(true); editor_plugin_screen->selected_notify(); + + } void EditorNode::add_editor_plugin(EditorPlugin *p_editor) { @@ -2879,7 +2889,12 @@ void EditorNode::add_editor_plugin(EditorPlugin *p_editor) { if (p_editor->has_main_screen()) { - singleton->main_editor_tabs->add_tab(p_editor->get_name()); + ToolButton *tb = memnew( ToolButton ); + tb->set_toggle_mode(true); + tb->connect("pressed",singleton,"_editor_select",varray(singleton->main_editor_buttons.size())); + tb->set_text(p_editor->get_name()); + singleton->main_editor_buttons.push_back(tb); + singleton->main_editor_button_vb->add_child(tb); singleton->editor_table.push_back(p_editor); } singleton->editor_data.add_editor_plugin( p_editor ); @@ -2891,16 +2906,18 @@ void EditorNode::remove_editor_plugin(EditorPlugin *p_editor) { if (p_editor->has_main_screen()) { - for(int i=0;i<singleton->main_editor_tabs->get_tab_count();i++) { + for(int i=0;i<singleton->main_editor_buttons.size();i++) { - if (p_editor->get_name()==singleton->main_editor_tabs->get_tab_title(i)) { + if (p_editor->get_name()==singleton->main_editor_buttons[i]->get_name()) { + + memdelete( singleton->main_editor_buttons[i] ); + singleton->main_editor_buttons.remove(i); - singleton->main_editor_tabs->remove_tab(i); break; } } - singleton->main_editor_tabs->add_tab(p_editor->get_name()); + //singleton->main_editor_tabs->add_tab(p_editor->get_name()); singleton->editor_table.erase(p_editor); } singleton->remove_child(p_editor); @@ -3034,7 +3051,7 @@ Error EditorNode::save_translatable_strings(const String& p_to_file) { OS::Time time = OS::get_singleton()->get_time(); f->store_line("# Translation Strings Dump."); f->store_line("# Created By."); - f->store_line("# \t"VERSION_FULL_NAME" (c) 2008-2015 Juan Linietsky, Ariel Manzur."); + f->store_line("# \t" VERSION_FULL_NAME " (c) 2008-2015 Juan Linietsky, Ariel Manzur."); f->store_line("# From Scene: "); f->store_line("# \t"+get_edited_scene()->get_filename()); f->store_line(""); @@ -3209,10 +3226,20 @@ Error EditorNode::save_optimized_copy(const String& p_scene,const String& p_pres } +int EditorNode::_get_current_main_editor() { + + for(int i=0;i<editor_table.size();i++) { + if (editor_table[i]==editor_plugin_screen) + return i; + } + + return 0; +} + Dictionary EditorNode::_get_main_scene_state() { Dictionary state; - state["main_tab"]=main_editor_tabs->get_current_tab(); + state["main_tab"]=_get_current_main_editor(); state["scene_tree_offset"]=scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_val(); state["property_edit_offset"]=get_property_editor()->get_scene_tree()->get_vscroll_bar()->get_val(); state["saved_version"]=saved_version; @@ -3223,9 +3250,14 @@ Dictionary EditorNode::_get_main_scene_state() { void EditorNode::_set_main_scene_state(Dictionary p_state) { //print_line("set current 7 "); + changing_scene=false; +#if 0 if (p_state.has("main_tab")) { int idx = p_state["main_tab"]; + + + print_line("comes with tab: "+itos(idx)); int current=-1; for(int i=0;i<editor_table.size();i++) { if (editor_plugin_screen==editor_table[i]) { @@ -3234,12 +3266,41 @@ void EditorNode::_set_main_scene_state(Dictionary p_state) { } } + if (idx<2 && current<2) { //only set tab for 2D and 3D - _editor_select(p_state["main_tab"]); + _editor_select(idx); //print_line(" setting main tab: "+itos(p_state["main_tab"])); } } +#else + + if (get_edited_scene()) { + + int current=-1; + for(int i=0;i<editor_table.size();i++) { + if (editor_plugin_screen==editor_table[i]) { + current=i; + break; + } + } + + if (current<2) { + //use heuristic instead + + int n2d=0,n3d=0; + _find_node_types(get_edited_scene(),n2d,n3d); + if (n2d>n3d) { + _editor_select(0); + } else if (n3d>n2d) { + _editor_select(1); + + } + } + + } +#endif + if (p_state.has("scene_tree_offset")) scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->set_val(p_state["scene_tree_offset"]); @@ -3248,6 +3309,12 @@ void EditorNode::_set_main_scene_state(Dictionary p_state) { //print_line("set current 8 "); + //this should only happen at the very end + + //changing_scene=true; //avoid script change from opening editor + ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root(); + ScriptEditor::get_singleton()->set_scene_root_script( editor_data.get_scene_root_script(editor_data.get_edited_scene()) ); + //changing_scene=false; } @@ -3312,8 +3379,6 @@ void EditorNode::set_current_scene(int p_idx) { call_deferred("_set_main_scene_state",state); //do after everything else is done setting up //print_line("set current 6 "); - changing_scene=false; - ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root(); } @@ -3752,9 +3817,7 @@ void EditorNode::_quick_run(const String& p_resource) { void EditorNode::notify_child_process_exited() { - play_button->set_pressed(false); - play_scene_button->set_pressed(false); - //pause_button->set_pressed(false); + _menu_option_confirm(RUN_STOP,false); stop_button->set_pressed(false); editor_run.stop(); @@ -4551,6 +4614,7 @@ EditorNode::EditorNode() { gui_base->add_child(main_vbox); main_vbox->set_area_as_parent_rect(8); +#if 0 PanelContainer *top_dark_panel = memnew( PanelContainer ); Ref<StyleBoxTexture> top_dark_sb; top_dark_sb.instance();; @@ -4566,21 +4630,14 @@ EditorNode::EditorNode() { VBoxContainer *top_dark_vb = memnew( VBoxContainer ); main_vbox->add_child(top_dark_panel); top_dark_panel->add_child(top_dark_vb); - +#endif menu_hb = memnew( HBoxContainer ); - top_dark_vb->add_child(menu_hb); + main_vbox->add_child(menu_hb); - scene_tabs=memnew( Tabs ); - scene_tabs->add_tab("unsaved"); - scene_tabs->set_tab_align(Tabs::ALIGN_CENTER); - scene_tabs->set_tab_close_display_policy(Tabs::SHOW_HOVER); - scene_tabs->connect("tab_changed",this,"_scene_tab_changed"); - scene_tabs->connect("right_button_pressed",this,"_scene_tab_script_edited"); - scene_tabs->connect("tab_close", this, "_scene_tab_closed"); - top_dark_vb->add_child(scene_tabs); +// top_dark_vb->add_child(scene_tabs); //left left_l_hsplit = memnew( HSplitContainer ); main_vbox->add_child(left_l_hsplit); @@ -4613,11 +4670,15 @@ EditorNode::EditorNode() { main_hsplit = memnew( HSplitContainer ); left_r_hsplit->add_child(main_hsplit); //main_split->set_v_size_flags(Control::SIZE_EXPAND_FILL); + VBoxContainer * center_vb = memnew( VBoxContainer); + main_hsplit->add_child(center_vb); + center_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL); center_split = memnew( VSplitContainer ); - main_hsplit->add_child(center_split); - center_split->set_h_size_flags(Control::SIZE_EXPAND_FILL); + //main_hsplit->add_child(center_split); + center_split->set_v_size_flags(Control::SIZE_EXPAND_FILL); center_split->set_collapsed(false); + center_vb->add_child(center_split); right_hsplit = memnew( HSplitContainer ); main_hsplit->add_child(right_hsplit); @@ -4628,6 +4689,9 @@ EditorNode::EditorNode() { right_l_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_UL]); dock_slot[DOCK_SLOT_RIGHT_BL]=memnew( TabContainer ); right_l_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_BL]); + //right_l_vsplit->hide(); + //dock_slot[DOCK_SLOT_RIGHT_UL]->hide(); + //dock_slot[DOCK_SLOT_RIGHT_BL]->hide(); right_r_vsplit = memnew( VSplitContainer ); right_hsplit->add_child(right_r_vsplit); @@ -4636,8 +4700,8 @@ EditorNode::EditorNode() { dock_slot[DOCK_SLOT_RIGHT_BR]=memnew( TabContainer ); right_r_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_BR]); right_r_vsplit->hide(); - //dock_slot[DOCK_SLOT_RIGHT_UL]->hide(); - //dock_slot[DOCK_SLOT_RIGHT_BL]->hide(); + dock_slot[DOCK_SLOT_RIGHT_UR]->hide(); + dock_slot[DOCK_SLOT_RIGHT_BR]->hide(); left_l_vsplit->connect("dragged",this,"_dock_split_dragged"); left_r_vsplit->connect("dragged",this,"_dock_split_dragged"); @@ -4714,20 +4778,19 @@ EditorNode::EditorNode() { srt->add_constant_override("separation",0); - main_editor_tabs = memnew( Tabs ); +/* main_editor_tabs = memnew( Tabs ); main_editor_tabs->connect("tab_changed",this,"_editor_select"); main_editor_tabs->set_tab_close_display_policy(Tabs::SHOW_NEVER); - HBoxContainer *srth = memnew( HBoxContainer ); - srt->add_child( srth ); - Control *tec = memnew( Control ); - tec->set_custom_minimum_size(Size2(100,0)); - tec->set_h_size_flags(Control::SIZE_EXPAND_FILL); - srth->add_child(tec); - srth->add_child(main_editor_tabs); - tec = memnew( Control ); - tec->set_custom_minimum_size(Size2(100,0)); - srth->add_child(tec); - tec->set_h_size_flags(Control::SIZE_EXPAND_FILL); +*/ + scene_tabs=memnew( Tabs ); + scene_tabs->add_tab("unsaved"); + scene_tabs->set_tab_align(Tabs::ALIGN_CENTER); + scene_tabs->set_tab_close_display_policy(Tabs::SHOW_ACTIVE_ONLY); + scene_tabs->connect("tab_changed",this,"_scene_tab_changed"); + scene_tabs->connect("right_button_pressed",this,"_scene_tab_script_edited"); + scene_tabs->connect("tab_close", this, "_scene_tab_closed"); + + srt->add_child(scene_tabs); scene_root_parent = memnew( Panel ); @@ -4804,6 +4867,8 @@ EditorNode::EditorNode() { anim_close->set_pressed_texture( anim_close->get_icon("Close","EditorIcons")); + + PanelContainer *top_region = memnew( PanelContainer ); top_region->add_style_override("panel",gui_base->get_stylebox("hover","Button")); HBoxContainer *left_menu_hb = memnew( HBoxContainer ); @@ -4880,6 +4945,18 @@ EditorNode::EditorNode() { p->add_child(recent_scenes); recent_scenes->connect("item_pressed",this,"_open_recent_scene"); + { + Control *sp = memnew( Control ); + sp->set_custom_minimum_size(Size2(30,0)); + menu_hb->add_child(sp); + } + + PanelContainer *editor_region = memnew( PanelContainer ); + editor_region->add_style_override("panel",gui_base->get_stylebox("hover","Button")); + main_editor_button_vb = memnew( HBoxContainer ); + editor_region->add_child(main_editor_button_vb); + menu_hb->add_child(editor_region); + //menu_hb->add_spacer(); #if 0 node_menu = memnew( MenuButton ); @@ -5040,6 +5117,39 @@ EditorNode::EditorNode() { */ + progress_hb = memnew( BackgroundProgress ); + menu_hb->add_child(progress_hb); + + { + Control *sp = memnew( Control ); + sp->set_custom_minimum_size(Size2(30,0)); + menu_hb->add_child(sp); + } + + + PanelContainer *vu_cont = memnew( PanelContainer ); + vu_cont->add_style_override("panel",gui_base->get_stylebox("hover","Button")); + menu_hb->add_child(vu_cont); + + audio_vu = memnew( TextureProgress ); + CenterContainer *vu_cc = memnew( CenterContainer ); + vu_cc->add_child(audio_vu); + vu_cont->add_child(vu_cc); + audio_vu->set_under_texture(gui_base->get_icon("VuEmpty","EditorIcons")); + audio_vu->set_progress_texture(gui_base->get_icon("VuFull","EditorIcons")); + audio_vu->set_max(24); + audio_vu->set_min(-80); + audio_vu->set_step(0.01); + audio_vu->set_val(0); + + { + Control *sp = memnew( Control ); + sp->set_custom_minimum_size(Size2(30,0)); + menu_hb->add_child(sp); + } + + + top_region = memnew( PanelContainer ); top_region->add_style_override("panel",gui_base->get_stylebox("hover","Button")); HBoxContainer *right_menu_hb = memnew( HBoxContainer ); @@ -5071,6 +5181,15 @@ EditorNode::EditorNode() { sources_button->connect("pressed",this,"_menu_option",varray(SOURCES_REIMPORT)); sources_button->set_tooltip("Alerts when an external resource has changed."); + update_menu = memnew( MenuButton ); + update_menu->set_tooltip("Spins when the editor window repaints!"); + right_menu_hb->add_child(update_menu); + update_menu->set_icon(gui_base->get_icon("Progress1","EditorIcons")); + p=update_menu->get_popup(); + p->add_check_item("Update Always",SETTINGS_UPDATE_ALWAYS); + p->add_check_item("Update Changes",SETTINGS_UPDATE_CHANGES); + p->set_item_checked(1,true); + //sources_button->connect(); /* @@ -5173,6 +5292,7 @@ EditorNode::EditorNode() { editor_history_menu = memnew( MenuButton ); + editor_history_menu->set_tooltip("History of recently edited objects"); editor_history_menu->set_icon( gui_base->get_icon("History","EditorIcons")); prop_editor_hb->add_child(editor_history_menu); editor_history_menu->connect("about_to_show",this,"_prepare_history"); @@ -5224,6 +5344,8 @@ EditorNode::EditorNode() { log->connect("close_request",this,"_close_messages"); log->connect("show_request",this,"_show_messages"); //left_split->set_dragger_visible(false); + + old_split_ofs=0; @@ -5241,37 +5363,18 @@ EditorNode::EditorNode() { animation_editor->hide(); - PanelContainer *bottom_pc = memnew( PanelContainer ); - main_vbox->add_child(bottom_pc); + /*PanelContainer *bottom_pc = memnew( PanelContainer ); + srt->add_child(bottom_pc); bottom_hb = memnew( HBoxContainer ); - bottom_pc->add_child(bottom_hb); + bottom_pc->add_child(bottom_hb);*/ - bottom_hb->add_child( log->get_button() ); + center_vb->add_child( log->get_button() ); log->get_button()->set_h_size_flags(Control::SIZE_EXPAND_FILL); - progress_hb = memnew( BackgroundProgress ); - bottom_hb->add_child(progress_hb); + //progress_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); - audio_vu = memnew( TextureProgress ); - CenterContainer *vu_cc = memnew( CenterContainer ); - vu_cc->add_child(audio_vu); - bottom_hb->add_child(vu_cc); - audio_vu->set_under_texture(gui_base->get_icon("VuEmpty","EditorIcons")); - audio_vu->set_progress_texture(gui_base->get_icon("VuFull","EditorIcons")); - audio_vu->set_max(24); - audio_vu->set_min(-80); - audio_vu->set_step(0.01); - audio_vu->set_val(0); - update_menu = memnew( MenuButton ); - update_menu->set_tooltip("Spins when the editor window repaints!"); - bottom_hb->add_child(update_menu); - update_menu->set_icon(gui_base->get_icon("Progress1","EditorIcons")); - p=update_menu->get_popup(); - p->add_check_item("Update Always",SETTINGS_UPDATE_ALWAYS); - p->add_check_item("Update Changes",SETTINGS_UPDATE_CHANGES); - p->set_item_checked(1,true); /* animation_menu = memnew( ToolButton ); @@ -5488,11 +5591,11 @@ EditorNode::EditorNode() { editor_import_export->add_import_plugin( Ref<EditorTranslationImportPlugin>( memnew(EditorTranslationImportPlugin(this)))); editor_import_export->add_export_plugin( Ref<EditorTextureExportPlugin>( memnew(EditorTextureExportPlugin))); + editor_import_export->add_export_plugin( Ref<EditorSampleExportPlugin>( memnew(EditorSampleExportPlugin))); add_editor_plugin( memnew( CanvasItemEditorPlugin(this) ) ); add_editor_plugin( memnew( SpatialEditorPlugin(this) ) ); add_editor_plugin( memnew( ScriptEditorPlugin(this) ) ); - add_editor_plugin( memnew( EditorHelpPlugin(this) ) ); add_editor_plugin( memnew( AnimationPlayerEditorPlugin(this) ) ); add_editor_plugin( memnew( ShaderGraphEditorPlugin(this,true) ) ); add_editor_plugin( memnew( ShaderGraphEditorPlugin(this,false) ) ); @@ -5559,9 +5662,9 @@ EditorNode::EditorNode() { Globals::get_singleton()->set("debug/indicators_enabled",true); Globals::get_singleton()->set("render/room_cull_enabled",false); - theme->set_color("prop_category","Editor",Color::hex(0x403d41ff)); - theme->set_color("prop_section","Editor",Color::hex(0x383539ff)); - theme->set_color("prop_subsection","Editor",Color::hex(0x343135ff)); + theme->set_color("prop_category","Editor",Color::hex(0x3f3a44ff)); + theme->set_color("prop_section","Editor",Color::hex(0x35313aff)); + theme->set_color("prop_subsection","Editor",Color::hex(0x312e37ff)); theme->set_color("fg_selected","Editor",Color::html("ffbd8e8e")); theme->set_color("fg_error","Editor",Color::html("ffbd8e8e")); @@ -5668,6 +5771,7 @@ EditorNode::EditorNode() { EditorNode::~EditorNode() { + memdelete( EditorHelp::get_doc_data() ); memdelete(editor_selection); memdelete(file_server); EditorSettings::destroy(); diff --git a/tools/editor/editor_node.h b/tools/editor/editor_node.h index 56e455c9c0..bd88e1a4b9 100644 --- a/tools/editor/editor_node.h +++ b/tools/editor/editor_node.h @@ -314,7 +314,9 @@ class EditorNode : public Node { CenterContainer *tabs_center; EditorQuickOpen *quick_open; EditorQuickOpen *quick_run; - Tabs *main_editor_tabs; + + HBoxContainer *main_editor_button_vb; + Vector<ToolButton*> main_editor_buttons; Vector<EditorPlugin*> editor_table; EditorReImportDialog *reimport_dialog; @@ -508,6 +510,8 @@ class EditorNode : public Node { Dictionary _get_main_scene_state(); void _set_main_scene_state(Dictionary p_state); + int _get_current_main_editor(); + void _save_docks(); void _load_docks(); diff --git a/tools/editor/editor_settings.cpp b/tools/editor/editor_settings.cpp index 651b30c724..15de6e7266 100644 --- a/tools/editor/editor_settings.cpp +++ b/tools/editor/editor_settings.cpp @@ -447,7 +447,7 @@ void EditorSettings::_load_defaults() { hints["global/default_project_path"]=PropertyInfo(Variant::STRING,"global/default_project_path",PROPERTY_HINT_GLOBAL_DIR); set("global/default_project_export_path",""); hints["global/default_project_export_path"]=PropertyInfo(Variant::STRING,"global/default_project_export_path",PROPERTY_HINT_GLOBAL_DIR); - + set("global/show_script_in_scene_tabs",false); set("text_editor/background_color",Color::html("3b000000")); set("text_editor/text_color",Color::html("aaaaaa")); set("text_editor/text_selected_color",Color::html("000000")); diff --git a/tools/editor/fileserver/SCsub b/tools/editor/fileserver/SCsub index b525fb3f75..363a2ce4c0 100644 --- a/tools/editor/fileserver/SCsub +++ b/tools/editor/fileserver/SCsub @@ -1,7 +1,3 @@ Import('env') Export('env') env.add_source_files(env.tool_sources,"*.cpp") - - - - diff --git a/tools/editor/icons/SCsub b/tools/editor/icons/SCsub index aea053d22b..addf6879a2 100644 --- a/tools/editor/icons/SCsub +++ b/tools/editor/icons/SCsub @@ -9,30 +9,30 @@ def make_editor_icons_action(target, source, env): pixmaps = source s = cStringIO.StringIO() - + s.write("#include \"editor_icons.h\"\n\n") s.write("#include \"scene/resources/theme.h\"\n\n") for x in pixmaps: - + x=str(x) var_str=os.path.basename(x)[:-4]+"_png"; #print(var_str) - + s.write("static const unsigned char "+ var_str +"[]={\n"); - + pngf=open(x,"rb"); - + b=pngf.read(1); while(len(b)==1): s.write(hex(ord(b))) b=pngf.read(1); if (len(b)==1): s.write(",") - + s.write("\n};\n\n\n"); pngf.close(); - + s.write("static Ref<ImageTexture> make_icon(const uint8_t* p_png) {\n") s.write("\tRef<ImageTexture> texture( memnew( ImageTexture ) );\n") s.write("\ttexture->create_from_image( Image(p_png),ImageTexture::FLAG_FILTER );\n") @@ -42,14 +42,14 @@ def make_editor_icons_action(target, source, env): s.write("void editor_register_icons(Ref<Theme> p_theme) {\n\n") for x in pixmaps: - + x=os.path.basename(str(x)) type=x[5:-4].title().replace("_",""); var_str=x[:-4]+"_png"; s.write("\tp_theme->set_icon(\""+type+"\",\"EditorIcons\",make_icon("+var_str+"));\n"); s.write("\n\n}\n\n"); - + f = open(dst,"wb") f.write(s.getvalue()) f.close() @@ -63,4 +63,3 @@ env.Alias('editor_icons',[env.MakeEditorIconsBuilder('#tools/editor/editor_icons env.tool_sources.append("#tools/editor/editor_icons.cpp") Export('env') - diff --git a/tools/editor/icons/icon_back.png b/tools/editor/icons/icon_back.png Binary files differindex d60e3b2640..f7e507d92b 100644 --- a/tools/editor/icons/icon_back.png +++ b/tools/editor/icons/icon_back.png diff --git a/tools/editor/icons/icon_class_list.png b/tools/editor/icons/icon_class_list.png Binary files differnew file mode 100644 index 0000000000..fb756c0fe1 --- /dev/null +++ b/tools/editor/icons/icon_class_list.png diff --git a/tools/editor/icons/icon_forward.png b/tools/editor/icons/icon_forward.png Binary files differindex ca6838ae9e..14e8bc9a5a 100644 --- a/tools/editor/icons/icon_forward.png +++ b/tools/editor/icons/icon_forward.png diff --git a/tools/editor/icons/icon_godot.png b/tools/editor/icons/icon_godot.png Binary files differnew file mode 100644 index 0000000000..e80820fc10 --- /dev/null +++ b/tools/editor/icons/icon_godot.png diff --git a/tools/editor/icons/icon_help.png b/tools/editor/icons/icon_help.png Binary files differindex 3f4f8453a7..d2085589ae 100644 --- a/tools/editor/icons/icon_help.png +++ b/tools/editor/icons/icon_help.png diff --git a/tools/editor/icons/icon_multi_edit.png b/tools/editor/icons/icon_multi_edit.png Binary files differnew file mode 100644 index 0000000000..70faee3d6a --- /dev/null +++ b/tools/editor/icons/icon_multi_edit.png diff --git a/tools/editor/io_plugins/SCsub b/tools/editor/io_plugins/SCsub index b525fb3f75..363a2ce4c0 100644 --- a/tools/editor/io_plugins/SCsub +++ b/tools/editor/io_plugins/SCsub @@ -1,7 +1,3 @@ Import('env') Export('env') env.add_source_files(env.tool_sources,"*.cpp") - - - - diff --git a/tools/editor/io_plugins/editor_atlas.cpp b/tools/editor/io_plugins/editor_atlas.cpp index 4a260a9a6f..7e9acd193d 100644 --- a/tools/editor/io_plugins/editor_atlas.cpp +++ b/tools/editor/io_plugins/editor_atlas.cpp @@ -27,7 +27,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "editor_atlas.h" - +#include "print_string.h" struct _EditorAtlasWorkRect { @@ -83,6 +83,7 @@ void EditorAtlas::fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, S //place them int ofs=0; int limit_h=0; + for(int j=0;j<wrects.size();j++) { @@ -100,6 +101,9 @@ void EditorAtlas::fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, S wrects[j].p.x=ofs; wrects[j].p.y=from_y; + + + int end_h = from_y+wrects[j].s.height; int end_w = ofs+wrects[j].s.width; if (ofs==0) @@ -116,7 +120,7 @@ void EditorAtlas::fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, S if (end_w > max_w) max_w=end_w; - if (ofs==0 || end_h>limit_h ) //while h limit not reched, keep stacking + //if (ofs==0 || end_h>limit_h ) //while h limit not reched, keep stacking ofs+=wrects[j].s.width; } @@ -136,8 +140,8 @@ void EditorAtlas::fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, S for(int i=0;i<results.size();i++) { - float h = nearest_power_of_2(results[i].max_h); - float w = nearest_power_of_2(results[i].max_w); + float h = results[i].max_h; + float w = results[i].max_w; float aspect = h>w ? h/w : w/h; if (aspect < best_aspect) { best=i; diff --git a/tools/editor/io_plugins/editor_sample_import_plugin.cpp b/tools/editor/io_plugins/editor_sample_import_plugin.cpp index 9298b35b3b..7888246956 100644 --- a/tools/editor/io_plugins/editor_sample_import_plugin.cpp +++ b/tools/editor/io_plugins/editor_sample_import_plugin.cpp @@ -35,6 +35,7 @@ #include "io/resource_saver.h" #include "os/file_access.h" #include "io/marshalls.h" +#include "tools/editor/editor_settings.h" class _EditorSampleImportOptions : public Object { @@ -156,7 +157,7 @@ public: edit_normalize=true; edit_loop=false; - compress_mode=COMPRESS_MODE_DISABLED; + compress_mode=COMPRESS_MODE_RAM; compress_bitrate=COMPRESS_128; } @@ -580,8 +581,7 @@ Error EditorSampleImportPlugin::import(const String& p_path, const Ref<ResourceI int compression = from->get_option("compress/mode"); bool force_mono = from->get_option("force/mono"); - if (compression==_EditorSampleImportOptions::COMPRESS_MODE_RAM) - force_mono=true; + if (force_mono && chans==2) { @@ -608,9 +608,47 @@ Error EditorSampleImportPlugin::import(const String& p_path, const Ref<ResourceI if ( compression == _EditorSampleImportOptions::COMPRESS_MODE_RAM) { dst_format=Sample::FORMAT_IMA_ADPCM; + if (chans==1) { + _compress_ima_adpcm(data,dst_data); + } else { + + print_line("INTERLEAAVE!"); + + + + //byte interleave + Vector<float> left; + Vector<float> right; + + int tlen = data.size()/2; + left.resize(tlen); + right.resize(tlen); + + for(int i=0;i<tlen;i++) { + left[i]=data[i*2+0]; + right[i]=data[i*2+1]; + } + + DVector<uint8_t> bleft; + DVector<uint8_t> bright; - _compress_ima_adpcm(data,dst_data); - print_line("compressing ima-adpcm, resulting buffersize is "+itos(dst_data.size())+" from "+itos(data.size())); + _compress_ima_adpcm(left,bleft); + _compress_ima_adpcm(right,bright); + + int dl = bleft.size(); + dst_data.resize( dl *2 ); + + DVector<uint8_t>::Write w=dst_data.write(); + DVector<uint8_t>::Read rl=bleft.read(); + DVector<uint8_t>::Read rr=bright.read(); + + for(int i=0;i<dl;i++) { + w[i*2+0]=rl[i]; + w[i*2+1]=rr[i]; + } + } + +// print_line("compressing ima-adpcm, resulting buffersize is "+itos(dst_data.size())+" from "+itos(data.size())); } else { @@ -781,9 +819,54 @@ void EditorSampleImportPlugin::_compress_ima_adpcm(const Vector<float>& p_data,D } + +EditorSampleImportPlugin* EditorSampleImportPlugin::singleton=NULL; + + + EditorSampleImportPlugin::EditorSampleImportPlugin(EditorNode* p_editor) { + singleton=this; dialog = memnew( EditorSampleImportDialog(this)); p_editor->get_gui_base()->add_child(dialog); } +Vector<uint8_t> EditorSampleExportPlugin::custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform) { + + + + if (EditorImportExport::get_singleton()->sample_get_action()==EditorImportExport::SAMPLE_ACTION_NONE || p_path.extension().to_lower()!="wav") { + + return Vector<uint8_t>(); + } + + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + + imd->add_source(EditorImportPlugin::validate_source_path(p_path)); + + imd->set_option("force/8_bit",false); + imd->set_option("force/mono",false); + imd->set_option("force/max_rate",true); + imd->set_option("force/max_rate_hz",EditorImportExport::get_singleton()->sample_get_max_hz()); + imd->set_option("edit/trim",EditorImportExport::get_singleton()->sample_get_trim()); + imd->set_option("edit/normalize",false); + imd->set_option("edit/loop",false); + imd->set_option("compress/mode",1); + + String savepath = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp/smpconv.smp"); + Error err = EditorSampleImportPlugin::singleton->import(savepath,imd); + + + ERR_FAIL_COND_V(err!=OK,Vector<uint8_t>()); + + p_path=p_path.basename()+".smp"; + return FileAccess::get_file_as_array(savepath); + +} + + +EditorSampleExportPlugin::EditorSampleExportPlugin() { + +} + + diff --git a/tools/editor/io_plugins/editor_sample_import_plugin.h b/tools/editor/io_plugins/editor_sample_import_plugin.h index 03a4d38ab3..89319affa0 100644 --- a/tools/editor/io_plugins/editor_sample_import_plugin.h +++ b/tools/editor/io_plugins/editor_sample_import_plugin.h @@ -43,6 +43,8 @@ class EditorSampleImportPlugin : public EditorImportPlugin { void _compress_ima_adpcm(const Vector<float>& p_data,DVector<uint8_t>& dst_data); public: + static EditorSampleImportPlugin *singleton; + virtual String get_name() const; virtual String get_visible_name() const; virtual void import_dialog(const String& p_from=""); @@ -52,4 +54,16 @@ public: EditorSampleImportPlugin(EditorNode* p_editor); }; +class EditorSampleExportPlugin : public EditorExportPlugin { + + OBJ_TYPE( EditorSampleExportPlugin, EditorExportPlugin); + + +public: + + virtual Vector<uint8_t> custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform); + + EditorSampleExportPlugin(); +}; + #endif // EDITOR_SAMPLE_IMPORT_PLUGIN_H diff --git a/tools/editor/io_plugins/editor_scene_import_plugin.cpp b/tools/editor/io_plugins/editor_scene_import_plugin.cpp index 99dcf4ed28..ca44df269b 100644 --- a/tools/editor/io_plugins/editor_scene_import_plugin.cpp +++ b/tools/editor/io_plugins/editor_scene_import_plugin.cpp @@ -1814,8 +1814,8 @@ Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh> for(int i=0;i<portal_points.size()-1;i++) { - float a = portal_points[i].atan2(); - float b = portal_points[i+1].atan2(); + float a = portal_points[i].angle(); + float b = portal_points[i+1].angle(); if (a>b) { SWAP( portal_points[i], portal_points[i+1] ); diff --git a/tools/editor/io_plugins/editor_texture_import_plugin.h b/tools/editor/io_plugins/editor_texture_import_plugin.h index 78383d1d77..38fd671e9d 100644 --- a/tools/editor/io_plugins/editor_texture_import_plugin.h +++ b/tools/editor/io_plugins/editor_texture_import_plugin.h @@ -123,6 +123,7 @@ public: virtual Vector<uint8_t> custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform); EditorTextureExportPlugin(); }; + class EditorImportTextureOptions : public VBoxContainer { OBJ_TYPE( EditorImportTextureOptions, VBoxContainer ); diff --git a/tools/editor/plugins/SCsub b/tools/editor/plugins/SCsub index b525fb3f75..363a2ce4c0 100644 --- a/tools/editor/plugins/SCsub +++ b/tools/editor/plugins/SCsub @@ -1,7 +1,3 @@ Import('env') Export('env') env.add_source_files(env.tool_sources,"*.cpp") - - - - diff --git a/tools/editor/plugins/animation_player_editor_plugin.cpp b/tools/editor/plugins/animation_player_editor_plugin.cpp index f8c484e886..6542fc8b4a 100644 --- a/tools/editor/plugins/animation_player_editor_plugin.cpp +++ b/tools/editor/plugins/animation_player_editor_plugin.cpp @@ -27,7 +27,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "animation_player_editor_plugin.h" +#include "globals.h" #include "io/resource_loader.h" +#include "io/resource_saver.h" #include "os/keyboard.h" #include "tools/editor/editor_settings.h" @@ -98,6 +100,8 @@ void AnimationPlayerEditor::_notification(int p_what) { duplicate_anim->set_icon( get_icon("Duplicate","EditorIcons") ); autoplay->set_icon( get_icon("AutoPlay","EditorIcons") ); load_anim->set_icon( get_icon("Folder","EditorIcons") ); + save_anim->set_icon(get_icon("Save", "EditorIcons")); + save_anim->get_popup()->connect("item_pressed", this, "_animation_save_menu"); remove_anim->set_icon( get_icon("Remove","EditorIcons") ); edit_anim->set_icon( get_icon("Edit","EditorIcons") ); blend_anim->set_icon( get_icon("Blend","EditorIcons") ); @@ -367,8 +371,78 @@ void AnimationPlayerEditor::_animation_load() { } file->popup_centered_ratio(); + current_option = RESOURCE_LOAD; +} + + +void AnimationPlayerEditor::_animation_save_in_path(const Ref<Resource>& p_resource, const String& p_path) { + + int flg = 0; + if (EditorSettings::get_singleton()->get("on_save/compress_binary_resources")) + flg |= ResourceSaver::FLAG_COMPRESS; + if (EditorSettings::get_singleton()->get("on_save/save_paths_as_relative")) + flg |= ResourceSaver::FLAG_RELATIVE_PATHS; + + String path = Globals::get_singleton()->localize_path(p_path); + Error err = ResourceSaver::save(path, p_resource, flg | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS); + + if (err != OK) { + accept->set_text("Error saving resource!"); + accept->popup_centered_minsize(); + return; + } + // EditorFileSystem::get_singleton()->update_file(path,p_resource->get_type()); + + ((Resource*)p_resource.ptr())->set_path(path); + editor->emit_signal("resource_saved", p_resource); + +} + +void AnimationPlayerEditor::_animation_save(const Ref<Resource>& p_resource) { + + if (p_resource->get_path().is_resource_file()) { + _animation_save_in_path(p_resource, p_resource->get_path()); + } + else { + _animation_save_as(p_resource); + } +} + +void AnimationPlayerEditor::_animation_save_as(const Ref<Resource>& p_resource) { + + file->set_mode(EditorFileDialog::MODE_SAVE_FILE); + bool relpaths = (p_resource->has_meta("__editor_relpaths__") && p_resource->get_meta("__editor_relpaths__").operator bool()); + + List<String> extensions; + ResourceSaver::get_recognized_extensions(p_resource, &extensions); + file->clear_filters(); + for (int i = 0; i<extensions.size(); i++) { + + file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); + } + //file->set_current_path(current_path); + if (p_resource->get_path() != "") { + file->set_current_path(p_resource->get_path()); + if (extensions.size()) { + String ext = p_resource->get_path().extension().to_lower(); + if (extensions.find(ext) == NULL) { + file->set_current_path(p_resource->get_path().replacen("." + ext, "." + extensions.front()->get())); + } + } + } + else { + String existing; + if (extensions.size()) { + existing = "new_" + p_resource->get_type().to_lower() + "." + extensions.front()->get().to_lower(); + } + file->set_current_path(existing); + + } + file->popup_centered_ratio(); + file->set_title("Save Resource As.."); + current_option = RESOURCE_SAVE; } void AnimationPlayerEditor::_animation_remove() { @@ -635,38 +709,55 @@ void AnimationPlayerEditor::_animation_edit() { //get_scene()->get_root_node()->call("_resource_selected",anim,""); } -void AnimationPlayerEditor::_file_selected(String p_file) { +void AnimationPlayerEditor::_dialog_action(String p_file) { - ERR_FAIL_COND(!player); + switch (current_option) { + case RESOURCE_LOAD: { + ERR_FAIL_COND(!player); - Ref<Resource> res = ResourceLoader::load(p_file,"Animation"); - ERR_FAIL_COND(res.is_null()); - ERR_FAIL_COND( !res->is_type("Animation") ); - if (p_file.find_last("/")!=-1) { + Ref<Resource> res = ResourceLoader::load(p_file, "Animation"); + ERR_FAIL_COND(res.is_null()); + ERR_FAIL_COND(!res->is_type("Animation")); + if (p_file.find_last("/") != -1) { - p_file=p_file.substr( p_file.find_last("/")+1, p_file.length() ); + p_file = p_file.substr(p_file.find_last("/") + 1, p_file.length()); - } - if (p_file.find_last("\\")!=-1) { + } + if (p_file.find_last("\\") != -1) { - p_file=p_file.substr( p_file.find_last("\\")+1, p_file.length() ); + p_file = p_file.substr(p_file.find_last("\\") + 1, p_file.length()); - } + } - if (p_file.find(".")!=-1) - p_file=p_file.substr(0,p_file.find(".")); + if (p_file.find(".") != -1) + p_file = p_file.substr(0, p_file.find(".")); - undo_redo->create_action("Load Animation"); - undo_redo->add_do_method(player,"add_animation",p_file,res); - undo_redo->add_undo_method(player,"remove_animation",p_file); - if (player->has_animation(p_file)) { - undo_redo->add_undo_method(player,"add_animation",p_file,player->get_animation(p_file)); + undo_redo->create_action("Load Animation"); + undo_redo->add_do_method(player, "add_animation", p_file, res); + undo_redo->add_undo_method(player, "remove_animation", p_file); + if (player->has_animation(p_file)) { + undo_redo->add_undo_method(player, "add_animation", p_file, player->get_animation(p_file)); - } - undo_redo->add_do_method(this,"_animation_player_changed",player); - undo_redo->add_undo_method(this,"_animation_player_changed",player); - undo_redo->commit_action(); + } + undo_redo->add_do_method(this, "_animation_player_changed", player); + undo_redo->add_undo_method(this, "_animation_player_changed", player); + undo_redo->commit_action(); + break; + } + case RESOURCE_SAVE: { + + String current = animation->get_item_text(animation->get_selected()); + if (current != "") { + Ref<Animation> anim = player->get_animation(current); + + ERR_FAIL_COND(!anim->cast_to<Resource>()) + + RES current_res = RES(anim->cast_to<Resource>()); + _animation_save_in_path(current_res, p_file); + } + } + } } void AnimationPlayerEditor::_scale_changed(const String& p_scale) { @@ -730,6 +821,8 @@ void AnimationPlayerEditor::_update_player() { blend_anim->set_disabled(animlist.size()==0); remove_anim->set_disabled(animlist.size()==0); resource_edit_anim->set_disabled(animlist.size()==0); + save_anim->set_disabled(animlist.size() == 0); + int active_idx=-1; for (List<StringName>::Element *E=animlist.front();E;E=E->next()) { @@ -1072,6 +1165,23 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { } } +void AnimationPlayerEditor::_animation_save_menu(int p_option) { + + String current = animation->get_item_text(animation->get_selected()); + if (current != "") { + Ref<Animation> anim = player->get_animation(current); + + switch (p_option) { + case ANIM_SAVE: + _animation_save(anim); + break; + case ANIM_SAVE_AS: + _animation_save_as(anim); + break; + } + } +} + void AnimationPlayerEditor::_unhandled_key_input(const InputEvent& p_ev) { if (is_visible() && p_ev.type==InputEvent::KEY && p_ev.key.pressed && !p_ev.key.echo && !p_ev.key.mod.alt && !p_ev.key.mod.control && !p_ev.key.mod.meta) { @@ -1117,7 +1227,7 @@ void AnimationPlayerEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("_animation_blend"),&AnimationPlayerEditor::_animation_blend); ObjectTypeDB::bind_method(_MD("_animation_edit"),&AnimationPlayerEditor::_animation_edit); ObjectTypeDB::bind_method(_MD("_animation_resource_edit"),&AnimationPlayerEditor::_animation_resource_edit); - ObjectTypeDB::bind_method(_MD("_file_selected"),&AnimationPlayerEditor::_file_selected); + ObjectTypeDB::bind_method(_MD("_dialog_action"),&AnimationPlayerEditor::_dialog_action); ObjectTypeDB::bind_method(_MD("_seek_value_changed"),&AnimationPlayerEditor::_seek_value_changed); ObjectTypeDB::bind_method(_MD("_animation_player_changed"),&AnimationPlayerEditor::_animation_player_changed); ObjectTypeDB::bind_method(_MD("_blend_edited"),&AnimationPlayerEditor::_blend_edited); @@ -1133,6 +1243,7 @@ void AnimationPlayerEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("_blend_editor_next_changed"),&AnimationPlayerEditor::_blend_editor_next_changed); ObjectTypeDB::bind_method(_MD("_unhandled_key_input"),&AnimationPlayerEditor::_unhandled_key_input); ObjectTypeDB::bind_method(_MD("_animation_tool_menu"),&AnimationPlayerEditor::_animation_tool_menu); + ObjectTypeDB::bind_method(_MD("_animation_save_menu"), &AnimationPlayerEditor::_animation_save_menu); @@ -1170,6 +1281,17 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { load_anim->set_tooltip("Load an animation from disk."); hb->add_child(load_anim); + save_anim = memnew(MenuButton); + save_anim->set_tooltip("Save the current animation"); + save_anim->get_popup()->add_item("Save", ANIM_SAVE); + save_anim->get_popup()->add_item("Save As..", ANIM_SAVE_AS); + save_anim->set_focus_mode(Control::FOCUS_NONE); + hb->add_child(save_anim); + + accept = memnew(AcceptDialog); + add_child(accept); + accept->connect("confirmed", this, "_menu_confirm_current"); + duplicate_anim = memnew( ToolButton ); hb->add_child(duplicate_anim); duplicate_anim->set_tooltip("Duplicate Animation"); @@ -1279,6 +1401,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { add_child(file); name_dialog = memnew( ConfirmationDialog ); + name_dialog->set_title("Create New Animation"); name_dialog->set_hide_on_ok(false); add_child(name_dialog); name = memnew( LineEdit ); @@ -1339,7 +1462,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { remove_anim->connect("pressed", this,"_animation_remove"); animation->connect("item_selected", this,"_animation_selected",Vector<Variant>(),true); resource_edit_anim->connect("pressed", this,"_animation_resource_edit"); - file->connect("file_selected", this,"_file_selected"); + file->connect("file_selected", this,"_dialog_action"); seek->connect("value_changed", this, "_seek_value_changed",Vector<Variant>(),true); scale->connect("text_entered", this, "_scale_changed",Vector<Variant>(),true); editor->get_animation_editor()->connect("timeline_changed",this,"_animation_key_editor_seek"); diff --git a/tools/editor/plugins/animation_player_editor_plugin.h b/tools/editor/plugins/animation_player_editor_plugin.h index 5705742565..ac4d1ab6ba 100644 --- a/tools/editor/plugins/animation_player_editor_plugin.h +++ b/tools/editor/plugins/animation_player_editor_plugin.h @@ -55,6 +55,17 @@ class AnimationPlayerEditor : public VBoxContainer { TOOL_EDIT_RESOURCE }; + enum { + ANIM_SAVE, + ANIM_SAVE_AS + }; + + enum { + RESOURCE_LOAD, + RESOURCE_SAVE + }; + + OptionButton *animation; Button *stop; Button *play; @@ -70,6 +81,7 @@ class AnimationPlayerEditor : public VBoxContainer { Button *edit_anim; Button *resource_edit_anim; Button *load_anim; + MenuButton *save_anim; Button *blend_anim; Button *remove_anim; MenuButton *tool_anim; @@ -85,6 +97,8 @@ class AnimationPlayerEditor : public VBoxContainer { bool last_active; EditorFileDialog *file; + AcceptDialog *accept; + int current_option; struct BlendEditor { @@ -116,13 +130,18 @@ class AnimationPlayerEditor : public VBoxContainer { void _animation_rename(); void _animation_name_edited(); void _animation_load(); + + void _animation_save_in_path(const Ref<Resource>& p_resource, const String& p_path); + void _animation_save(const Ref<Resource>& p_resource); + void _animation_save_as(const Ref<Resource>& p_resource); + void _animation_remove(); void _animation_blend(); void _animation_edit(); void _animation_duplicate(); void _animation_resource_edit(); void _scale_changed(const String& p_scale); - void _file_selected(String p_file); + void _dialog_action(String p_file); void _seek_frame_changed(const String& p_frame); void _seek_value_changed(float p_value); void _blend_editor_next_changed(const String& p_string); @@ -141,6 +160,7 @@ class AnimationPlayerEditor : public VBoxContainer { void _animation_key_editor_anim_len_changed(float p_new); void _unhandled_key_input(const InputEvent& p_ev); void _animation_tool_menu(int p_option); + void _animation_save_menu(int p_option); AnimationPlayerEditor(); protected: diff --git a/tools/editor/plugins/canvas_item_editor_plugin.cpp b/tools/editor/plugins/canvas_item_editor_plugin.cpp index d318f6f6fa..fbc41a7269 100644 --- a/tools/editor/plugins/canvas_item_editor_plugin.cpp +++ b/tools/editor/plugins/canvas_item_editor_plugin.cpp @@ -41,9 +41,9 @@ class SnapDialog : public ConfirmationDialog { OBJ_TYPE(SnapDialog,ConfirmationDialog); - -protected: - friend class CanvasItemEditor; + +friend class CanvasItemEditor; + SpinBox *grid_offset_x; SpinBox *grid_offset_y; SpinBox *grid_step_x; @@ -58,63 +58,75 @@ public: Label *label; VBoxContainer *container; GridContainer *child_container; - + set_title("Configure Snap"); get_ok()->set_text("Close"); - container = memnew(VBoxContainer); + + container = memnew( VBoxContainer ); add_child(container); - - child_container = memnew(GridContainer); + set_child_rect(container); + + child_container = memnew( GridContainer ); child_container->set_columns(3); container->add_child(child_container); - - label = memnew(Label); + + label = memnew( Label ); label->set_text("Grid Offset:"); child_container->add_child(label); - grid_offset_x=memnew(SpinBox); + label->set_h_size_flags(SIZE_EXPAND_FILL); + + grid_offset_x = memnew( SpinBox ); grid_offset_x->set_min(-SPIN_BOX_GRID_RANGE); grid_offset_x->set_max(SPIN_BOX_GRID_RANGE); grid_offset_x->set_suffix("px"); child_container->add_child(grid_offset_x); - grid_offset_y=memnew(SpinBox); + + grid_offset_y = memnew( SpinBox ); grid_offset_y->set_min(-SPIN_BOX_GRID_RANGE); grid_offset_y->set_max(SPIN_BOX_GRID_RANGE); grid_offset_y->set_suffix("px"); child_container->add_child(grid_offset_y); - label = memnew(Label); + label = memnew( Label ); label->set_text("Grid Step:"); child_container->add_child(label); - grid_step_x=memnew(SpinBox); + label->set_h_size_flags(SIZE_EXPAND_FILL); + + grid_step_x = memnew( SpinBox ); grid_step_x->set_min(-SPIN_BOX_GRID_RANGE); grid_step_x->set_max(SPIN_BOX_GRID_RANGE); grid_step_x->set_suffix("px"); child_container->add_child(grid_step_x); - grid_step_y=memnew(SpinBox); + + grid_step_y = memnew( SpinBox ); grid_step_y->set_min(-SPIN_BOX_GRID_RANGE); grid_step_y->set_max(SPIN_BOX_GRID_RANGE); grid_step_y->set_suffix("px"); child_container->add_child(grid_step_y); - - container->add_child(memnew(HSeparator)); - child_container = memnew(GridContainer); + container->add_child( memnew( HSeparator ) ); + + child_container = memnew( GridContainer ); child_container->set_columns(2); container->add_child(child_container); - label = memnew(Label); + label = memnew( Label ); label->set_text("Rotation Offset:"); child_container->add_child(label); - rotation_offset=memnew(SpinBox); + label->set_h_size_flags(SIZE_EXPAND_FILL); + + rotation_offset = memnew( SpinBox ); rotation_offset->set_min(-SPIN_BOX_ROTATION_RANGE); rotation_offset->set_max(SPIN_BOX_ROTATION_RANGE); rotation_offset->set_suffix("deg"); child_container->add_child(rotation_offset); - - label = memnew(Label); + + label = memnew( Label ); label->set_text("Rotation Step:"); child_container->add_child(label); - rotation_step=memnew(SpinBox); + label->set_h_size_flags(SIZE_EXPAND_FILL); + + rotation_step = memnew( SpinBox ); rotation_step->set_min(-SPIN_BOX_ROTATION_RANGE); rotation_step->set_max(SPIN_BOX_ROTATION_RANGE); rotation_step->set_suffix("deg"); @@ -435,6 +447,47 @@ CanvasItem* CanvasItemEditor::_select_canvas_item_at_pos(const Point2& p_pos,Nod return NULL; } +void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform, Vector<_SelectResult> &r_items) { + if (!p_node) + return; + if (p_node->cast_to<Viewport>()) + return; + + CanvasItem *c=p_node->cast_to<CanvasItem>(); + + for (int i=p_node->get_child_count()-1;i>=0;i--) { + + if (c && !c->is_set_as_toplevel()) + _find_canvas_items_at_pos(p_pos,p_node->get_child(i),p_parent_xform * c->get_transform(),p_canvas_xform, r_items); + else { + CanvasLayer *cl = p_node->cast_to<CanvasLayer>(); + if (cl) + return; + _find_canvas_items_at_pos(p_pos,p_node->get_child(i),transform ,cl ? cl->get_transform() : p_canvas_xform, r_items); //use base transform + } + } + + + if (c && c->is_visible() && !c->has_meta("_edit_lock_")) { + + Rect2 rect = c->get_item_rect(); + Point2 local_pos = (p_parent_xform * p_canvas_xform * c->get_transform()).affine_inverse().xform(p_pos); + + + if (rect.has_point(local_pos)) { + Node2D *node=c->cast_to<Node2D>(); + + _SelectResult res; + res.item=c; + res.z=node?node->get_z():0; + res.has_z=node; + r_items.push_back(res); + } + + } + + return; +} void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2& p_rect,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform,List<CanvasItem*> *r_items) { @@ -477,6 +530,96 @@ void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2& p_rect,Node* p_no } +bool CanvasItemEditor::_select(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag) { + + if (p_append) { + //additive selection + + if (!item) { + + if (p_drag) { + drag_from=transform.affine_inverse().xform(p_click_pos); + + box_selecting=true; + box_selecting_to=drag_from; + } + + return false; //nothing to add + } + + if (editor_selection->is_selected(item)) { + //already in here, erase it + editor_selection->remove_node(item); + //_remove_canvas_item(c); + + viewport->update(); + return false; + + } + _append_canvas_item(item); + viewport->update(); + + } else { + //regular selection + + if (!item) { + //clear because nothing clicked + editor_selection->clear();; + + if (p_drag) { + drag_from=transform.affine_inverse().xform(p_click_pos); + + box_selecting=true; + box_selecting_to=drag_from; + } + + viewport->update(); + return false; + } + + if (!editor_selection->is_selected(item)) { + //select a new one and clear previous selection + editor_selection->clear(); + editor_selection->add_node(item); + //reselect + if (get_tree()->is_editor_hint()) { + editor->call("edit_node",item); + } + + } + + if (p_drag) { + //prepare to move! + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item || !canvas_item->is_visible()) + continue; + CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + if (!se) + continue; + + se->undo_state=canvas_item->edit_get_state(); + if (canvas_item->cast_to<Node2D>()) + se->undo_pivot=canvas_item->cast_to<Node2D>()->edit_get_pivot(); + + } + + drag=DRAG_ALL; + drag_from=transform.affine_inverse().xform(p_click_pos); + drag_point_from=_find_topleftmost_point(); + } + + viewport->update(); + + return true; + + } +} + void CanvasItemEditor::_key_move(const Vector2& p_dir, bool p_snap, KeyMoveMODE p_move_mode) { @@ -776,6 +919,24 @@ void CanvasItemEditor::_dialog_value_changed(double) { } } +void CanvasItemEditor::_selection_result_pressed(int p_result) { + + if (selection_results.size() <= p_result) + return; + + CanvasItem *item=selection_results[p_result].item; + + if (item) + _select(item, Point2(), additive_selection, false); +} + +void CanvasItemEditor::_selection_menu_hide() { + + selection_results.clear(); + selection_menu->clear(); + selection_menu->set_size(Vector2(0, 0)); +} + bool CanvasItemEditor::get_remove_list(List<Node*> *p_list) { @@ -838,7 +999,60 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { if (b.button_index==BUTTON_RIGHT) { + if (!b.pressed && tool==TOOL_SELECT && b.mod.alt) { + Point2 click=Point2(b.x,b.y); + + Node* scene = editor->get_edited_scene(); + if (!scene) + return; + + _find_canvas_items_at_pos(click, scene,transform,Matrix32(), selection_results); + + if (selection_results.size() == 1) { + + CanvasItem *item = selection_results[0].item; + selection_results.clear(); + + additive_selection=b.mod.shift; + if (!_select(item, click, additive_selection, false)) + return; + + } else if (!selection_results.empty()) { + + selection_results.sort(); + + NodePath root_path = get_tree()->get_edited_scene_root()->get_path(); + StringName root_name = root_path.get_name(root_path.get_name_count()-1); + + for (int i = 0; i < selection_results.size(); i++) { + + CanvasItem *item=selection_results[i].item; + + Ref<Texture> icon; + if (item->has_meta("_editor_icon")) + icon=item->get_meta("_editor_icon"); + else + icon=get_icon( has_icon(item->get_type(),"EditorIcons")?item->get_type():String("Object"),"EditorIcons"); + + String node_path="/"+root_name+"/"+root_path.rel_path_to(item->get_path()); + + selection_menu->add_item(item->get_name()); + selection_menu->set_item_icon(i, icon ); + selection_menu->set_item_metadata(i, node_path); + selection_menu->set_item_tooltip(i,String(item->get_name())+ + "\nType: "+item->get_type()+"\nPath: "+node_path); + } + + additive_selection=b.mod.shift; + + selection_menu->set_global_pos(Vector2( b.global_x, b.global_y )); + selection_menu->popup(); + selection_menu->call_deferred("grab_click_focus"); + + return; + } + } if (get_item_count() > 0 && drag!=DRAG_NONE) { //cancel drag @@ -1203,82 +1417,10 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { #if 0 if ( b.pressed ) box_selection_start( click ); #endif - if (b.mod.shift) { //additive selection - - if (!c) { - - drag_from=transform.affine_inverse().xform(click); - - box_selecting=true; - box_selecting_to=drag_from; - - return; //nothing to add - } - - if (editor_selection->is_selected(c)) { - //already in here, erase it - editor_selection->remove_node(c); - //_remove_canvas_item(c); - viewport->update(); - return; - - } - _append_canvas_item(c); - viewport->update(); - } else { - //regular selection - - - - if (!c) { - //clear because nothing clicked - editor_selection->clear();; - - drag_from=transform.affine_inverse().xform(click); - - box_selecting=true; - box_selecting_to=drag_from; - viewport->update(); - return; - } - - if (!editor_selection->is_selected(c)) { - //select a new one and clear previous selection - editor_selection->clear(); - editor_selection->add_node(c); - //reselect - if (get_tree()->is_editor_hint()) { - editor->call("edit_node",c); - } - - } - - //prepare to move! - - List<Node*> &selection = editor_selection->get_selected_node_list(); - - for(List<Node*>::Element *E=selection.front();E;E=E->next()) { - - CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); - if (!canvas_item || !canvas_item->is_visible()) - continue; - CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); - if (!se) - continue; - - se->undo_state=canvas_item->edit_get_state(); - if (canvas_item->cast_to<Node2D>()) - se->undo_pivot=canvas_item->cast_to<Node2D>()->edit_get_pivot(); - - } - - drag=DRAG_ALL; - drag_from=transform.affine_inverse().xform(click); - drag_point_from=_find_topleftmost_point(); - viewport->update(); - - } + additive_selection=b.mod.shift; + if (!_select(c, click, additive_selection)) + return; } @@ -1346,7 +1488,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { Matrix32 rot; rot.elements[1] = (dfrom - center).normalized(); rot.elements[0] = rot.elements[1].tangent(); - node->set_rot(snap_angle(rot.xform_inv(dto-center).atan2(), node->get_rot())); + node->set_rot(snap_angle(rot.xform_inv(dto-center).angle(), node->get_rot())); display_rotate_to = dto; display_rotate_from = center; viewport->update(); @@ -2278,7 +2420,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { } break; case SNAP_CONFIGURE: { ((SnapDialog *)snap_dialog)->set_fields(snap_offset, snap_step, snap_rotation_offset, snap_rotation_step); - snap_dialog->popup_centered(Size2(200,160)); + snap_dialog->popup_centered(Size2(220,160)); } break; case ZOOM_IN: { zoom=zoom*(1.0/0.5); @@ -2867,6 +3009,8 @@ void CanvasItemEditor::_bind_methods() { ObjectTypeDB::bind_method("_viewport_draw",&CanvasItemEditor::_viewport_draw); ObjectTypeDB::bind_method("_viewport_input_event",&CanvasItemEditor::_viewport_input_event); ObjectTypeDB::bind_method("_snap_changed",&CanvasItemEditor::_snap_changed); + ObjectTypeDB::bind_method(_MD("_selection_result_pressed"),&CanvasItemEditor::_selection_result_pressed); + ObjectTypeDB::bind_method(_MD("_selection_menu_hide"),&CanvasItemEditor::_selection_menu_hide); ADD_SIGNAL( MethodInfo("item_lock_status_changed") ); ADD_SIGNAL( MethodInfo("item_group_status_changed") ); @@ -3173,7 +3317,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p->add_item("Paste Pose",ANIM_PASTE_POSE); p->add_item("Clear Pose",ANIM_CLEAR_POSE,KEY_MASK_SHIFT|KEY_K); - snap_dialog = memnew(SnapDialog); + snap_dialog = memnew( SnapDialog ); snap_dialog->connect("confirmed",this,"_snap_changed"); add_child(snap_dialog); @@ -3196,6 +3340,12 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { dialog_val->connect("value_changed",this,"_dialog_value_changed"); select_sb = Ref<StyleBoxTexture>( memnew( StyleBoxTexture) ); + selection_menu = memnew( PopupMenu ); + add_child(selection_menu); + selection_menu->set_custom_minimum_size(Vector2(100, 0)); + selection_menu->connect("item_pressed", this, "_selection_result_pressed"); + selection_menu->connect("popup_hide", this, "_selection_menu_hide"); + key_pos=true; key_rot=true; key_scale=false; @@ -3218,6 +3368,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { can_move_pivot=false; drag=DRAG_NONE; bone_last_frame=0; + additive_selection=false; } CanvasItemEditor *CanvasItemEditor::singleton=NULL; diff --git a/tools/editor/plugins/canvas_item_editor_plugin.h b/tools/editor/plugins/canvas_item_editor_plugin.h index 485422028e..b96d36f7dc 100644 --- a/tools/editor/plugins/canvas_item_editor_plugin.h +++ b/tools/editor/plugins/canvas_item_editor_plugin.h @@ -150,6 +150,7 @@ class CanvasItemEditor : public VBoxContainer { }; EditorSelection *editor_selection; + bool additive_selection; Tool tool; bool first_update; @@ -183,6 +184,18 @@ class CanvasItemEditor : public VBoxContainer { MenuOption last_option; + struct _SelectResult { + + CanvasItem* item; + float z; + bool has_z; + _FORCE_INLINE_ bool operator<(const _SelectResult& p_rr) const { + return has_z && p_rr.has_z ? p_rr.z < z : p_rr.has_z; + } + }; + + Vector<_SelectResult> selection_results; + struct LockList { Point2 pos; bool lock; @@ -249,6 +262,8 @@ class CanvasItemEditor : public VBoxContainer { Button *key_scale_button; Button *key_insert_button; + PopupMenu *selection_menu; + //PopupMenu *popup; DragType drag; Point2 drag_from; @@ -276,8 +291,11 @@ class CanvasItemEditor : public VBoxContainer { int handle_len; CanvasItem* _select_canvas_item_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform); + void _find_canvas_items_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform, Vector<_SelectResult> &r_items); void _find_canvas_items_at_rect(const Rect2& p_rect,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform,List<CanvasItem*> *r_items); + bool _select(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag=true); + ConfirmationDialog *snap_dialog; AcceptDialog *value_dialog; @@ -304,6 +322,9 @@ class CanvasItemEditor : public VBoxContainer { void _append_canvas_item(CanvasItem *p_item); void _dialog_value_changed(double); void _snap_changed(); + void _selection_result_pressed(int); + void _selection_menu_hide(); + UndoRedo *undo_redo; Point2 _find_topleftmost_point(); diff --git a/tools/editor/plugins/collision_polygon_editor_plugin.cpp b/tools/editor/plugins/collision_polygon_editor_plugin.cpp index 381cfd74ab..60683f4eda 100644 --- a/tools/editor/plugins/collision_polygon_editor_plugin.cpp +++ b/tools/editor/plugins/collision_polygon_editor_plugin.cpp @@ -113,6 +113,7 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const return false; Transform gt = node->get_global_transform(); + Transform gi = gt.affine_inverse(); float depth = node->get_depth()*0.5; Vector3 n = gt.basis.get_axis(2).normalized(); Plane p(gt.origin+n*depth,n); @@ -135,6 +136,8 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const if (!p.intersects_ray(ray_from,ray_dir,&spoint)) break; + spoint = gi.xform(spoint); + Vector2 cpoint(spoint.x,spoint.y); cpoint=CanvasItemEditor::get_singleton()->snap_point(cpoint); @@ -349,6 +352,8 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const if (!p.intersects_ray(ray_from,ray_dir,&spoint)) break; + spoint = gi.xform(spoint); + Vector2 cpoint(spoint.x,spoint.y); cpoint=CanvasItemEditor::get_singleton()->snap_point(cpoint); diff --git a/tools/editor/plugins/collision_shape_2d_editor_plugin.cpp b/tools/editor/plugins/collision_shape_2d_editor_plugin.cpp index 62cf1b4acb..f56b9a2fd9 100644 --- a/tools/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/tools/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -115,6 +115,18 @@ void CollisionShape2DEditor::set_handle(int idx, Point2& p_point) { } break; case LINE_SHAPE: { + if (idx<2) { + Ref<LineShape2D> line = node->get_shape(); + + if (idx==0){ + line->set_d(p_point.length()); + }else{ + line->set_normal(p_point/30.0); + } + + canvas_item_editor->get_viewport_control()->update(); + } + } break; @@ -200,6 +212,19 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant& p_org) { } break; case LINE_SHAPE: { + Ref<LineShape2D> line = node->get_shape(); + + if (idx==0) { + undo_redo->add_do_method(line.ptr(),"set_d",line->get_d()); + undo_redo->add_do_method(c,"update"); + undo_redo->add_undo_method(line.ptr(),"set_d",p_org); + undo_redo->add_undo_method(c,"update"); + } else { + undo_redo->add_do_method(line.ptr(),"set_normal",line->get_normal()); + undo_redo->add_do_method(c,"update"); + undo_redo->add_undo_method(line.ptr(),"set_normal",p_org); + undo_redo->add_undo_method(c,"update"); + } } break; @@ -418,6 +443,14 @@ void CollisionShape2DEditor::_canvas_draw() { } break; case LINE_SHAPE: { + Ref<LineShape2D> shape = node->get_shape(); + + handles.resize(2); + handles[0] = shape->get_normal() * shape->get_d(); + handles[1] = shape->get_normal() * (shape->get_d() + 30.0); + + c->draw_texture(h,gt.xform(handles[0])-size); + c->draw_texture(h,gt.xform(handles[1])-size); } break; diff --git a/tools/editor/plugins/editor_preview_plugins.cpp b/tools/editor/plugins/editor_preview_plugins.cpp index c2b3ecfcda..5f52d4c3e7 100644 --- a/tools/editor/plugins/editor_preview_plugins.cpp +++ b/tools/editor/plugins/editor_preview_plugins.cpp @@ -491,8 +491,14 @@ Ref<Texture> EditorSamplePreviewPlugin::generate(const RES& p_from) { ima_adpcm.last_nibble++; const uint8_t *src_ptr=sdata; + int ofs = ima_adpcm.last_nibble>>1; + + if (stereo) + ofs*=2; + + nibble = (ima_adpcm.last_nibble&1)? - (src_ptr[ima_adpcm.last_nibble>>1]>>4):(src_ptr[ima_adpcm.last_nibble>>1]&0xF); + (src_ptr[ofs]>>4):(src_ptr[ofs]&0xF); step=_ima_adpcm_step_table[ima_adpcm.step_index]; ima_adpcm.step_index += _ima_adpcm_index_table[nibble]; diff --git a/tools/editor/plugins/mesh_editor_plugin.cpp b/tools/editor/plugins/mesh_editor_plugin.cpp index 13d4c8db5a..cea774f94b 100644 --- a/tools/editor/plugins/mesh_editor_plugin.cpp +++ b/tools/editor/plugins/mesh_editor_plugin.cpp @@ -160,7 +160,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { } break; case MENU_OPTION_CREATE_OUTLINE_MESH: { - outline_dialog->popup_centered_minsize(); + outline_dialog->popup_centered(Vector2(200, 90)); } break; } @@ -212,7 +212,6 @@ MeshInstanceEditor::MeshInstanceEditor() { options = memnew( MenuButton ); - //add_child(options); SpatialEditor::get_singleton()->add_control_to_menu_panel(options); options->set_text("Mesh"); @@ -231,14 +230,20 @@ MeshInstanceEditor::MeshInstanceEditor() { options->get_popup()->connect("item_pressed", this,"_menu_option"); outline_dialog = memnew( ConfirmationDialog ); - outline_dialog->set_title("Outline Size: "); + outline_dialog->set_title("Create Outline Mesh"); + outline_dialog->get_ok()->set_text("Create"); + + VBoxContainer *outline_dialog_vbc = memnew( VBoxContainer ); + outline_dialog->add_child(outline_dialog_vbc); + outline_dialog->set_child_rect(outline_dialog_vbc); + outline_size = memnew( SpinBox ); outline_size->set_min(0.001); outline_size->set_max(1024); outline_size->set_step(0.001); outline_size->set_val(0.05); - outline_dialog->add_child(outline_size); - outline_dialog->set_child_rect(outline_size); + outline_dialog_vbc->add_margin_child("Outline Size:",outline_size); + add_child(outline_dialog); outline_dialog->connect("confirmed",this,"_create_outline_mesh"); diff --git a/tools/editor/plugins/multimesh_editor_plugin.cpp b/tools/editor/plugins/multimesh_editor_plugin.cpp index 3c88b1d3a8..a5c823f8bd 100644 --- a/tools/editor/plugins/multimesh_editor_plugin.cpp +++ b/tools/editor/plugins/multimesh_editor_plugin.cpp @@ -289,7 +289,7 @@ void MultiMeshEditor::_menu_option(int p_option) { _last_pp_node=node; } - populate_dialog->popup_centered(Size2(250,395)); + populate_dialog->popup_centered(Size2(250,380)); } break; } @@ -325,10 +325,8 @@ MultiMeshEditor::MultiMeshEditor() { options = memnew( MenuButton ); - //add_child(options); SpatialEditor::get_singleton()->add_control_to_menu_panel(options); - options->set_area_as_parent_rect(); - + options->set_text("MultiMesh"); options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MultiMeshInstance","EditorIcons")); @@ -373,12 +371,12 @@ MultiMeshEditor::MultiMeshEditor() { populate_axis->select(2); vbc->add_margin_child("Mesh Up Axis:",populate_axis); - populate_rotate_random = memnew( HScrollBar ); + populate_rotate_random = memnew( HSlider ); populate_rotate_random->set_max(1); populate_rotate_random->set_step(0.01); vbc->add_margin_child("Random Rotation:",populate_rotate_random); - populate_tilt_random = memnew( HScrollBar ); + populate_tilt_random = memnew( HSlider ); populate_tilt_random->set_max(1); populate_tilt_random->set_step(0.01); vbc->add_margin_child("Random Tilt:",populate_tilt_random); @@ -416,8 +414,7 @@ MultiMeshEditor::MultiMeshEditor() { std->connect("selected",this,"_browsed"); _last_pp_node=NULL; - //options->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); - //options->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); + err_dialog = memnew( AcceptDialog ); add_child(err_dialog); } @@ -451,13 +448,6 @@ MultiMeshEditorPlugin::MultiMeshEditorPlugin(EditorNode *p_node) { multimesh_editor = memnew( MultiMeshEditor ); editor->get_viewport()->add_child(multimesh_editor); -// multimesh_editor->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); -// multimesh_editor->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); - multimesh_editor->set_margin(MARGIN_LEFT,253); - multimesh_editor->set_margin(MARGIN_RIGHT,310); - multimesh_editor->set_margin(MARGIN_TOP,0); - multimesh_editor->set_margin(MARGIN_BOTTOM,10); - multimesh_editor->options->hide(); } diff --git a/tools/editor/plugins/multimesh_editor_plugin.h b/tools/editor/plugins/multimesh_editor_plugin.h index 4f0c0d008b..edc3dfd55f 100644 --- a/tools/editor/plugins/multimesh_editor_plugin.h +++ b/tools/editor/plugins/multimesh_editor_plugin.h @@ -42,10 +42,10 @@ class MultiMeshEditor : public Control { OBJ_TYPE(MultiMeshEditor, Control ); - friend class MultiMeshEditorPlugin; +friend class MultiMeshEditorPlugin; AcceptDialog *err_dialog; - MenuButton * options; + MenuButton * options; MultiMeshInstance *_last_pp_node; bool browsing_source; @@ -59,8 +59,8 @@ class MultiMeshEditor : public Control { ConfirmationDialog *populate_dialog; OptionButton *populate_axis; - HScrollBar *populate_rotate_random; - HScrollBar *populate_tilt_random; + HSlider *populate_rotate_random; + HSlider *populate_tilt_random; SpinBox *populate_scale_random; SpinBox *populate_scale; SpinBox *populate_amount; diff --git a/tools/editor/plugins/polygon_2d_editor_plugin.cpp b/tools/editor/plugins/polygon_2d_editor_plugin.cpp index 3029dcf2ab..cd82297365 100644 --- a/tools/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/tools/editor/plugins/polygon_2d_editor_plugin.cpp @@ -54,6 +54,8 @@ void Polygon2DEditor::_notification(int p_what) { b_snap_enable->set_icon( get_icon("Snap", "EditorIcons")); uv_icon_zoom->set_texture( get_icon("Zoom", "EditorIcons")); + get_tree()->connect("node_removed", this, "_node_removed"); + } break; case NOTIFICATION_FIXED_PROCESS: { @@ -65,8 +67,10 @@ void Polygon2DEditor::_notification(int p_what) { void Polygon2DEditor::_node_removed(Node *p_node) { if(p_node==node) { - node=NULL; + edit(NULL); hide(); + + canvas_item_editor->get_viewport_control()->update(); } } @@ -757,16 +761,13 @@ void Polygon2DEditor::edit(Node *p_collision_polygon) { node=p_collision_polygon->cast_to<Polygon2D>(); if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw"); - node->connect("exit_tree",this,"_node_removed",varray(),CONNECT_ONESHOT); + wip.clear(); wip_active=false; edited_point=-1; } else { - if (node) - node->disconnect("exit_tree",this,"_node_removed"); - node=NULL; if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) diff --git a/tools/editor/plugins/sample_editor_plugin.cpp b/tools/editor/plugins/sample_editor_plugin.cpp index 31fa7246ae..d88f2adc73 100644 --- a/tools/editor/plugins/sample_editor_plugin.cpp +++ b/tools/editor/plugins/sample_editor_plugin.cpp @@ -156,8 +156,14 @@ void SampleEditor::generate_preview_texture(const Ref<Sample>& p_sample,Ref<Imag ima_adpcm.last_nibble++; const uint8_t *src_ptr=sdata; + int ofs = ima_adpcm.last_nibble>>1; + + if (stereo) + ofs*=2; + nibble = (ima_adpcm.last_nibble&1)? - (src_ptr[ima_adpcm.last_nibble>>1]>>4):(src_ptr[ima_adpcm.last_nibble>>1]&0xF); + (src_ptr[ofs]>>4):(src_ptr[ofs]&0xF); + step=_ima_adpcm_step_table[ima_adpcm.step_index]; ima_adpcm.step_index += _ima_adpcm_index_table[nibble]; diff --git a/tools/editor/plugins/sample_library_editor_plugin.cpp b/tools/editor/plugins/sample_library_editor_plugin.cpp index cf9a6c41a4..b497458a2a 100644 --- a/tools/editor/plugins/sample_library_editor_plugin.cpp +++ b/tools/editor/plugins/sample_library_editor_plugin.cpp @@ -49,9 +49,13 @@ void SampleLibraryEditor::_notification(int p_what) { if (p_what==NOTIFICATION_ENTER_TREE) { play->set_icon( get_icon("Play","EditorIcons") ); + play->set_tooltip("Play Sample"); stop->set_icon( get_icon("Stop","EditorIcons") ); + stop->set_tooltip("Stop Sample"); load->set_icon( get_icon("Folder","EditorIcons") ); + load->set_tooltip("Open Sample File(s)"); _delete->set_icon( get_icon("Del","EditorIcons") ); + _delete->set_tooltip("Remove Sample"); } if (p_what==NOTIFICATION_READY) { diff --git a/tools/editor/plugins/script_editor_plugin.cpp b/tools/editor/plugins/script_editor_plugin.cpp index bd0f580a34..65ed420a51 100644 --- a/tools/editor/plugins/script_editor_plugin.cpp +++ b/tools/editor/plugins/script_editor_plugin.cpp @@ -572,28 +572,119 @@ void ScriptEditor::_goto_script_line(REF p_script,int p_line) { } + +void ScriptEditor::_update_history_arrows() { + + script_back->set_disabled( history_pos<=0 ); + script_forward->set_disabled( history_pos>=history.size()-1 ); +} + + +void ScriptEditor::_go_to_tab(int p_idx) { + + Node *cn = tab_container->get_child(p_idx); + if (!cn) + return; + Control *c = cn->cast_to<Control>(); + if (!c) + return; + + if (history_pos>=0 && history_pos<history.size() && history[history_pos].control==tab_container->get_current_tab_control()) { + + Node *n = tab_container->get_current_tab_control(); + + if (n->cast_to<ScriptTextEditor>()) { + + history[history_pos].scroll_pos=n->cast_to<ScriptTextEditor>()->get_text_edit()->get_v_scroll(); + history[history_pos].cursor_column=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_column(); + history[history_pos].cursor_row=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_line(); + } + if (n->cast_to<EditorHelp>()) { + + history[history_pos].scroll_pos=n->cast_to<EditorHelp>()->get_scroll(); + } + } + + history.resize(history_pos+1); + ScriptHistory sh; + sh.control=c; + sh.scroll_pos=0; + + history.push_back(sh); + history_pos++; + + + tab_container->set_current_tab(p_idx); + + c = tab_container->get_current_tab_control(); + + if (c->cast_to<ScriptTextEditor>()) { + + script_name_label->set_text(c->cast_to<ScriptTextEditor>()->get_name()); + script_icon->set_texture(c->cast_to<ScriptTextEditor>()->get_icon()); + if (is_visible()) + c->cast_to<ScriptTextEditor>()->get_text_edit()->grab_focus(); + } + if (c->cast_to<EditorHelp>()) { + + script_name_label->set_text(c->cast_to<EditorHelp>()->get_class_name()); + script_icon->set_texture(get_icon("Help","EditorIcons")); + if (is_visible()) + c->cast_to<EditorHelp>()->set_focused(); + } + + + + c->set_meta("__editor_pass",++edit_pass); + _update_history_arrows(); + _update_script_colors(); +} + void ScriptEditor::_close_current_tab() { int selected = tab_container->get_current_tab(); if (selected<0 || selected>=tab_container->get_child_count()) return; + Node *tselected = tab_container->get_child(selected); ScriptTextEditor *current = tab_container->get_child(selected)->cast_to<ScriptTextEditor>(); - if (!current) - return; + if (current) { + apply_scripts(); + } - apply_scripts(); + //remove from history + history.resize(history_pos+1); + + for(int i=0;i<history.size();i++) { + if (history[i].control==tselected) { + history.remove(i); + i--; + history_pos--; + } + } + + if (history_pos>=history.size()) { + history_pos=history.size()-1; + } int idx = tab_container->get_current_tab(); - memdelete(current); + memdelete(tselected); if (idx>=tab_container->get_child_count()) idx=tab_container->get_child_count()-1; if (idx>=0) { + + if (history_pos>=0) { + idx = history[history_pos].control->get_index(); + } tab_container->set_current_tab(idx); + //script_list->select(idx); } + _update_history_arrows(); + + _update_script_names(); EditorNode::get_singleton()->save_layout(); @@ -755,33 +846,15 @@ void ScriptEditor::swap_lines(TextEdit *tx, int line1, int line2) void ScriptEditor::_menu_option(int p_option) { - if (p_option==FILE_OPEN) { - editor->open_resource("Script"); - return; - } - int selected = tab_container->get_current_tab(); - if (selected<0 || selected>=tab_container->get_child_count()) - return; - - ScriptTextEditor *current = tab_container->get_child(selected)->cast_to<ScriptTextEditor>(); - if (!current) - return; - switch(p_option) { case FILE_NEW: { script_create_dialog->config("Node", ".gd"); script_create_dialog->popup_centered(Size2(300, 300)); } break; - case FILE_SAVE: { - if (!_test_script_times_on_disk()) - return; - editor->save_resource( current->get_edited_script() ); - - } break; - case FILE_SAVE_AS: { - - editor->save_resource_as( current->get_edited_script() ); + case FILE_OPEN: { + editor->open_resource("Script"); + return; } break; case FILE_SAVE_ALL: { @@ -806,387 +879,470 @@ void ScriptEditor::_menu_option(int p_option) { } break; - case EDIT_UNDO: { - current->get_text_edit()->undo(); - current->get_text_edit()->call_deferred("grab_focus"); - } break; - case EDIT_REDO: { - current->get_text_edit()->redo(); - current->get_text_edit()->call_deferred("grab_focus"); - } break; - case EDIT_CUT: { - - current->get_text_edit()->cut(); - current->get_text_edit()->call_deferred("grab_focus"); - } break; - case EDIT_COPY: { - current->get_text_edit()->copy(); - current->get_text_edit()->call_deferred("grab_focus"); - - } break; - case EDIT_PASTE: { - current->get_text_edit()->paste(); - current->get_text_edit()->call_deferred("grab_focus"); + case SEARCH_HELP: { + help_search_dialog->popup("current"); } break; - case EDIT_SELECT_ALL: { - - current->get_text_edit()->select_all(); - current->get_text_edit()->call_deferred("grab_focus"); + case SEARCH_CLASSES: { - } break; - case EDIT_MOVE_LINE_UP: { - - TextEdit *tx = current->get_text_edit(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - - if (tx->is_selection_active()) - { - int from_line = tx->get_selection_from_line(); - int from_col = tx->get_selection_from_column(); - int to_line = tx->get_selection_to_line(); - int to_column = tx->get_selection_to_column(); - - for (int i = from_line; i <= to_line; i++) - { - int line_id = i; - int next_id = i - 1; - - if (line_id == 0 || next_id < 0) - return; - - swap_lines(tx, line_id, next_id); - } - int from_line_up = from_line > 0 ? from_line-1 : from_line; - int to_line_up = to_line > 0 ? to_line-1 : to_line; - tx->select(from_line_up, from_col, to_line_up, to_column); - } - else - { - int line_id = tx->cursor_get_line(); - int next_id = line_id - 1; - - if (line_id == 0 || next_id < 0) - return; - - swap_lines(tx, line_id, next_id); - } - tx->update(); - - } break; - case EDIT_MOVE_LINE_DOWN: { - - TextEdit *tx = current->get_text_edit(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - - if (tx->is_selection_active()) - { - int from_line = tx->get_selection_from_line(); - int from_col = tx->get_selection_from_column(); - int to_line = tx->get_selection_to_line(); - int to_column = tx->get_selection_to_column(); - - for (int i = to_line; i >= from_line; i--) - { - int line_id = i; - int next_id = i + 1; - - if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count()) - return; - - swap_lines(tx, line_id, next_id); - } - int from_line_down = from_line < tx->get_line_count() ? from_line+1 : from_line; - int to_line_down = to_line < tx->get_line_count() ? to_line+1 : to_line; - tx->select(from_line_down, from_col, to_line_down, to_column); - } - else - { - int line_id = tx->cursor_get_line(); - int next_id = line_id + 1; - - if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count()) - return; - - swap_lines(tx, line_id, next_id); - } - tx->update(); - - } break; - case EDIT_INDENT_LEFT: { - - TextEdit *tx = current->get_text_edit(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - - - if (tx->is_selection_active()) - { - int begin = tx->get_selection_from_line(); - int end = tx->get_selection_to_line(); - for (int i = begin; i <= end; i++) - { - String line_text = tx->get_line(i); - // begins with tab - if (line_text.begins_with("\t")) - { - line_text = line_text.substr(1, line_text.length()); - tx->set_line(i, line_text); - } - // begins with 4 spaces - else if (line_text.begins_with(" ")) - { - line_text = line_text.substr(4, line_text.length()); - tx->set_line(i, line_text); - } - } - } - else - { - int begin = tx->cursor_get_line(); - String line_text = tx->get_line(begin); - // begins with tab - if (line_text.begins_with("\t")) - { - line_text = line_text.substr(1, line_text.length()); - tx->set_line(begin, line_text); - } - // begins with 4 spaces - else if (line_text.begins_with(" ")) - { - line_text = line_text.substr(4, line_text.length()); - tx->set_line(begin, line_text); - } - } - tx->update(); - //tx->deselect(); - - } break; - case EDIT_INDENT_RIGHT: { - - TextEdit *tx = current->get_text_edit(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - - if (tx->is_selection_active()) - { - int begin = tx->get_selection_from_line(); - int end = tx->get_selection_to_line(); - for (int i = begin; i <= end; i++) - { - String line_text = tx->get_line(i); - line_text = '\t' + line_text; - tx->set_line(i, line_text); - } - } - else - { - int begin = tx->cursor_get_line(); - String line_text = tx->get_line(begin); - line_text = '\t' + line_text; - tx->set_line(begin, line_text); - } - tx->update(); - //tx->deselect(); - - } break; - case EDIT_CLONE_DOWN: { - - TextEdit *tx = current->get_text_edit(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - int line = tx->cursor_get_line(); - int next_line = line + 1; - - if (line == tx->get_line_count() || next_line > tx->get_line_count()) - return; - - String line_clone = tx->get_line(line); - tx->insert_at(line_clone, next_line); - tx->update(); - - } break; - case EDIT_TOGGLE_COMMENT: { - - TextEdit *tx = current->get_text_edit(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - - - - if (tx->is_selection_active()) - { - int begin = tx->get_selection_from_line(); - int end = tx->get_selection_to_line(); - for (int i = begin; i <= end; i++) - { - String line_text = tx->get_line(i); - - if (line_text.begins_with("#")) - line_text = line_text.substr(1, line_text.length()); - else - line_text = "#" + line_text; - tx->set_line(i, line_text); - } - } - else - { - int begin = tx->cursor_get_line(); - String line_text = tx->get_line(begin); - - if (line_text.begins_with("#")) - line_text = line_text.substr(1, line_text.length()); - else - line_text = "#" + line_text; - tx->set_line(begin, line_text); - } - tx->update(); - //tx->deselect(); - - } break; - case EDIT_COMPLETE: { - - current->get_text_edit()->query_code_comple(); + if (tab_container->get_tab_count()==0) + break; - } break; - case EDIT_AUTO_INDENT: { + String current; - TextEdit *te = current->get_text_edit(); - String text = te->get_text(); - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) - return; - int begin,end; - if (te->is_selection_active()) { - begin=te->get_selection_from_line(); - end=te->get_selection_to_line(); - } else { - begin=0; - end=te->get_line_count()-1; + EditorHelp *eh = tab_container->get_child( tab_container->get_current_tab() )->cast_to<EditorHelp>(); + if (eh) { + current=eh->get_class_name(); } - scr->get_language()->auto_indent_code(text,begin,end); - te->set_text(text); + help_index->popup_centered_ratio(0.6); + if (current!="") { + help_index->call_deferred("select_class",current); + } } break; - case SEARCH_FIND: { + case SEARCH_WEBSITE: { - find_replace_dialog->set_text_edit(current->get_text_edit()); - find_replace_dialog->popup_search(); + OS::get_singleton()->shell_open("http://www.godotengine.org/projects/godot-engine/wiki/Documentation#Tutorials"); } break; - case SEARCH_FIND_NEXT: { - find_replace_dialog->set_text_edit(current->get_text_edit()); - find_replace_dialog->search_next(); - } break; - case SEARCH_REPLACE: { + case WINDOW_NEXT: { - find_replace_dialog->set_text_edit(current->get_text_edit()); - find_replace_dialog->popup_replace(); + _history_forward(); } break; - case SEARCH_LOCATE_FUNCTION: { - - if (!current) - return; - quick_open->popup(current->get_functions()); + case WINDOW_PREV: { + _history_back(); } break; - case SEARCH_GOTO_LINE: { - goto_line_dialog->popup_find_line(current->get_text_edit()); - } break; - case DEBUG_TOGGLE_BREAKPOINT: { - int line=current->get_text_edit()->cursor_get_line(); - bool dobreak = !current->get_text_edit()->is_line_set_as_breakpoint(line); - current->get_text_edit()->set_line_as_breakpoint(line,dobreak); - get_debugger()->set_breakpoint(current->get_edited_script()->get_path(),line+1,dobreak); - } break; - case DEBUG_NEXT: { + } - if (debugger) - debugger->debug_next(); - } break; - case DEBUG_STEP: { - if (debugger) - debugger->debug_step(); + int selected = tab_container->get_current_tab(); + if (selected<0 || selected>=tab_container->get_child_count()) + return; - } break; - case DEBUG_BREAK: { + ScriptTextEditor *current = tab_container->get_child(selected)->cast_to<ScriptTextEditor>(); + if (current) { + + switch(p_option) { + case FILE_NEW: { + script_create_dialog->config("Node", ".gd"); + script_create_dialog->popup_centered(Size2(300, 300)); + } break; + case FILE_SAVE: { + if (!_test_script_times_on_disk()) + return; + editor->save_resource( current->get_edited_script() ); + + } break; + case FILE_SAVE_AS: { + + editor->save_resource_as( current->get_edited_script() ); + + } break; + case EDIT_UNDO: { + current->get_text_edit()->undo(); + current->get_text_edit()->call_deferred("grab_focus"); + } break; + case EDIT_REDO: { + current->get_text_edit()->redo(); + current->get_text_edit()->call_deferred("grab_focus"); + } break; + case EDIT_CUT: { + + current->get_text_edit()->cut(); + current->get_text_edit()->call_deferred("grab_focus"); + } break; + case EDIT_COPY: { + current->get_text_edit()->copy(); + current->get_text_edit()->call_deferred("grab_focus"); + + } break; + case EDIT_PASTE: { + current->get_text_edit()->paste(); + current->get_text_edit()->call_deferred("grab_focus"); + + } break; + case EDIT_SELECT_ALL: { + + current->get_text_edit()->select_all(); + current->get_text_edit()->call_deferred("grab_focus"); + + } break; + case EDIT_MOVE_LINE_UP: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + if (tx->is_selection_active()) + { + int from_line = tx->get_selection_from_line(); + int from_col = tx->get_selection_from_column(); + int to_line = tx->get_selection_to_line(); + int to_column = tx->get_selection_to_column(); + + for (int i = from_line; i <= to_line; i++) + { + int line_id = i; + int next_id = i - 1; + + if (line_id == 0 || next_id < 0) + return; + + swap_lines(tx, line_id, next_id); + } + int from_line_up = from_line > 0 ? from_line-1 : from_line; + int to_line_up = to_line > 0 ? to_line-1 : to_line; + tx->select(from_line_up, from_col, to_line_up, to_column); + } + else + { + int line_id = tx->cursor_get_line(); + int next_id = line_id - 1; - if (debugger) - debugger->debug_break(); + if (line_id == 0 || next_id < 0) + return; - } break; - case DEBUG_CONTINUE: { + swap_lines(tx, line_id, next_id); + } + tx->update(); + + } break; + case EDIT_MOVE_LINE_DOWN: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + if (tx->is_selection_active()) + { + int from_line = tx->get_selection_from_line(); + int from_col = tx->get_selection_from_column(); + int to_line = tx->get_selection_to_line(); + int to_column = tx->get_selection_to_column(); + + for (int i = to_line; i >= from_line; i--) + { + int line_id = i; + int next_id = i + 1; + + if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count()) + return; + + swap_lines(tx, line_id, next_id); + } + int from_line_down = from_line < tx->get_line_count() ? from_line+1 : from_line; + int to_line_down = to_line < tx->get_line_count() ? to_line+1 : to_line; + tx->select(from_line_down, from_col, to_line_down, to_column); + } + else + { + int line_id = tx->cursor_get_line(); + int next_id = line_id + 1; - if (debugger) - debugger->debug_continue(); + if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count()) + return; - } break; - case DEBUG_SHOW: { - if (debugger) { - bool visible = debug_menu->get_popup()->is_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW) ); - debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), !visible); - if (visible) - debugger->hide(); + swap_lines(tx, line_id, next_id); + } + tx->update(); + + } break; + case EDIT_INDENT_LEFT: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + + if (tx->is_selection_active()) + { + int begin = tx->get_selection_from_line(); + int end = tx->get_selection_to_line(); + for (int i = begin; i <= end; i++) + { + String line_text = tx->get_line(i); + // begins with tab + if (line_text.begins_with("\t")) + { + line_text = line_text.substr(1, line_text.length()); + tx->set_line(i, line_text); + } + // begins with 4 spaces + else if (line_text.begins_with(" ")) + { + line_text = line_text.substr(4, line_text.length()); + tx->set_line(i, line_text); + } + } + } else - debugger->show(); - } - } break; - case HELP_CONTEXTUAL: { - String text = current->get_text_edit()->get_selection_text(); - if (text == "") - text = current->get_text_edit()->get_word_under_cursor(); - if (text != "") - editor->emit_signal("request_help", text); - } break; - case FILE_CLOSE: { - if (current->get_text_edit()->get_version()!=current->get_text_edit()->get_saved_version()) { - erase_tab_confirm->set_text("Close and save changes?\n\""+current->get_name()+"\""); - erase_tab_confirm->popup_centered_minsize(); - } else { - _close_current_tab(); - } - } break; - case WINDOW_MOVE_LEFT: { + { + int begin = tx->cursor_get_line(); + String line_text = tx->get_line(begin); + // begins with tab + if (line_text.begins_with("\t")) + { + line_text = line_text.substr(1, line_text.length()); + tx->set_line(begin, line_text); + } + // begins with 4 spaces + else if (line_text.begins_with(" ")) + { + line_text = line_text.substr(4, line_text.length()); + tx->set_line(begin, line_text); + } + } + tx->update(); + //tx->deselect(); + + } break; + case EDIT_INDENT_RIGHT: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + if (tx->is_selection_active()) + { + int begin = tx->get_selection_from_line(); + int end = tx->get_selection_to_line(); + for (int i = begin; i <= end; i++) + { + String line_text = tx->get_line(i); + line_text = '\t' + line_text; + tx->set_line(i, line_text); + } + } + else + { + int begin = tx->cursor_get_line(); + String line_text = tx->get_line(begin); + line_text = '\t' + line_text; + tx->set_line(begin, line_text); + } + tx->update(); + //tx->deselect(); + + } break; + case EDIT_CLONE_DOWN: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + int line = tx->cursor_get_line(); + int next_line = line + 1; + + if (line == tx->get_line_count() || next_line > tx->get_line_count()) + return; + + String line_clone = tx->get_line(line); + tx->insert_at(line_clone, next_line); + tx->update(); + + } break; + case EDIT_TOGGLE_COMMENT: { + + TextEdit *tx = current->get_text_edit(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + + + + if (tx->is_selection_active()) + { + int begin = tx->get_selection_from_line(); + int end = tx->get_selection_to_line(); + for (int i = begin; i <= end; i++) + { + String line_text = tx->get_line(i); + + if (line_text.begins_with("#")) + line_text = line_text.substr(1, line_text.length()); + else + line_text = "#" + line_text; + tx->set_line(i, line_text); + } + } + else + { + int begin = tx->cursor_get_line(); + String line_text = tx->get_line(begin); + + if (line_text.begins_with("#")) + line_text = line_text.substr(1, line_text.length()); + else + line_text = "#" + line_text; + tx->set_line(begin, line_text); + } + tx->update(); + //tx->deselect(); + + } break; + case EDIT_COMPLETE: { + + current->get_text_edit()->query_code_comple(); + + } break; + case EDIT_AUTO_INDENT: { + + TextEdit *te = current->get_text_edit(); + String text = te->get_text(); + Ref<Script> scr = current->get_edited_script(); + if (scr.is_null()) + return; + int begin,end; + if (te->is_selection_active()) { + begin=te->get_selection_from_line(); + end=te->get_selection_to_line(); + } else { + begin=0; + end=te->get_line_count()-1; + } + scr->get_language()->auto_indent_code(text,begin,end); + te->set_text(text); + + + } break; + case SEARCH_FIND: { + + find_replace_dialog->set_text_edit(current->get_text_edit()); + find_replace_dialog->popup_search(); + } break; + case SEARCH_FIND_NEXT: { + + find_replace_dialog->set_text_edit(current->get_text_edit()); + find_replace_dialog->search_next(); + } break; + case SEARCH_REPLACE: { + + find_replace_dialog->set_text_edit(current->get_text_edit()); + find_replace_dialog->popup_replace(); + } break; + case SEARCH_LOCATE_FUNCTION: { + + if (!current) + return; + quick_open->popup(current->get_functions()); + } break; + case SEARCH_GOTO_LINE: { + + goto_line_dialog->popup_find_line(current->get_text_edit()); + } break; + case DEBUG_TOGGLE_BREAKPOINT: { + int line=current->get_text_edit()->cursor_get_line(); + bool dobreak = !current->get_text_edit()->is_line_set_as_breakpoint(line); + current->get_text_edit()->set_line_as_breakpoint(line,dobreak); + get_debugger()->set_breakpoint(current->get_edited_script()->get_path(),line+1,dobreak); + } break; + case DEBUG_NEXT: { + + if (debugger) + debugger->debug_next(); + } break; + case DEBUG_STEP: { + + if (debugger) + debugger->debug_step(); + + } break; + case DEBUG_BREAK: { + + if (debugger) + debugger->debug_break(); + + } break; + case DEBUG_CONTINUE: { + + if (debugger) + debugger->debug_continue(); + + } break; + case DEBUG_SHOW: { + if (debugger) { + bool visible = debug_menu->get_popup()->is_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW) ); + debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), !visible); + if (visible) + debugger->hide(); + else + debugger->show(); + } + } break; + case HELP_CONTEXTUAL: { + String text = current->get_text_edit()->get_selection_text(); + if (text == "") + text = current->get_text_edit()->get_word_under_cursor(); + if (text != "") + help_search_dialog->popup(text); + } break; + case FILE_CLOSE: { + if (current->get_text_edit()->get_version()!=current->get_text_edit()->get_saved_version()) { + erase_tab_confirm->set_text("Close and save changes?\n\""+current->get_name()+"\""); + erase_tab_confirm->popup_centered_minsize(); + } else { + _close_current_tab(); + } + } break; + case WINDOW_MOVE_LEFT: { + + if (tab_container->get_current_tab()>0) { + tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()-1); + script_list->call_deferred("select",tab_container->get_current_tab()-1); + tab_container->move_child(current,tab_container->get_current_tab()-1); + _update_script_names(); + } + } break; + case WINDOW_MOVE_RIGHT: { + + if (tab_container->get_current_tab()<tab_container->get_child_count()-1) { + tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()+1); + script_list->call_deferred("select",tab_container->get_current_tab()+1); + tab_container->move_child(current,tab_container->get_current_tab()+1); + _update_script_names(); + } - if (tab_container->get_current_tab()>0) { - tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()-1); - script_list->call_deferred("select",tab_container->get_current_tab()-1); - tab_container->move_child(current,tab_container->get_current_tab()-1); - _update_script_names(); - } - } break; - case WINDOW_MOVE_RIGHT: { - if (tab_container->get_current_tab()<tab_container->get_child_count()-1) { - tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()+1); - script_list->call_deferred("select",tab_container->get_current_tab()+1); - tab_container->move_child(current,tab_container->get_current_tab()+1); - _update_script_names(); + } break; + + default: { + + if (p_option>=WINDOW_SELECT_BASE) { + + tab_container->set_current_tab(p_option-WINDOW_SELECT_BASE); + script_list->select(p_option-WINDOW_SELECT_BASE); + + } } + } + } + EditorHelp *help = tab_container->get_child(selected)->cast_to<EditorHelp>(); + if (help) { - } break; - default: { + switch(p_option) { - if (p_option>=WINDOW_SELECT_BASE) { + case SEARCH_FIND: { + help->popup_search(); + } break; + case SEARCH_FIND_NEXT: { + help->search_again(); + } break; + case FILE_CLOSE: { + _close_current_tab(); + } break; - tab_container->set_current_tab(p_option-WINDOW_SELECT_BASE); - script_list->select(p_option-WINDOW_SELECT_BASE); - } } } + } void ScriptEditor::_tab_changed(int p_which) { @@ -1217,6 +1373,14 @@ void ScriptEditor::_notification(int p_what) { } EditorSettings::get_singleton()->connect("settings_changed",this,"_editor_settings_changed"); + help_search->set_icon(get_icon("Help","EditorIcons")); + site_search->set_icon(get_icon("Godot","EditorIcons")); + class_search->set_icon(get_icon("ClassList","EditorIcons")); + + script_forward->set_icon(get_icon("Forward","EditorIcons")); + script_back->set_icon(get_icon("Back","EditorIcons")); + + } @@ -1346,7 +1510,7 @@ void ScriptEditor::set_state(const Dictionary& p_state) { } void ScriptEditor::clear() { - +#if 0 List<ScriptTextEditor*> stes; for(int i=0;i<tab_container->get_child_count();i++) { @@ -1371,7 +1535,7 @@ void ScriptEditor::clear() { script_list->select( script_list->find_metadata(idx) ); } - +#endif } @@ -1402,31 +1566,6 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) { -void ScriptEditor::_bind_methods() { - - ObjectTypeDB::bind_method("_tab_changed",&ScriptEditor::_tab_changed); - ObjectTypeDB::bind_method("_menu_option",&ScriptEditor::_menu_option); - ObjectTypeDB::bind_method("_close_current_tab",&ScriptEditor::_close_current_tab); - ObjectTypeDB::bind_method("_editor_play",&ScriptEditor::_editor_play); - ObjectTypeDB::bind_method("_editor_pause",&ScriptEditor::_editor_pause); - ObjectTypeDB::bind_method("_editor_stop",&ScriptEditor::_editor_stop); - ObjectTypeDB::bind_method("_add_callback",&ScriptEditor::_add_callback); - ObjectTypeDB::bind_method("_reload_scripts",&ScriptEditor::_reload_scripts); - ObjectTypeDB::bind_method("_resave_scripts",&ScriptEditor::_resave_scripts); - ObjectTypeDB::bind_method("_res_saved_callback",&ScriptEditor::_res_saved_callback); - ObjectTypeDB::bind_method("_goto_script_line",&ScriptEditor::_goto_script_line); - ObjectTypeDB::bind_method("_goto_script_line2",&ScriptEditor::_goto_script_line2); - ObjectTypeDB::bind_method("_breaked",&ScriptEditor::_breaked); - ObjectTypeDB::bind_method("_show_debugger",&ScriptEditor::_show_debugger); - ObjectTypeDB::bind_method("_get_debug_tooltip",&ScriptEditor::_get_debug_tooltip); - ObjectTypeDB::bind_method("_autosave_scripts",&ScriptEditor::_autosave_scripts); - ObjectTypeDB::bind_method("_editor_settings_changed",&ScriptEditor::_editor_settings_changed); - ObjectTypeDB::bind_method("_update_script_names",&ScriptEditor::_update_script_names); - ObjectTypeDB::bind_method("_tree_changed",&ScriptEditor::_tree_changed); - ObjectTypeDB::bind_method("_script_selected",&ScriptEditor::_script_selected); - ObjectTypeDB::bind_method("_script_created",&ScriptEditor::_script_created); - ObjectTypeDB::bind_method("_script_split_dragged",&ScriptEditor::_script_split_dragged); -} void ScriptEditor::ensure_focus_current() { @@ -1448,7 +1587,8 @@ void ScriptEditor::ensure_focus_current() { void ScriptEditor::_script_selected(int p_idx) { grab_focus_block = !Input::get_singleton()->is_mouse_button_pressed(1); //amazing hack, simply amazing - tab_container->set_current_tab(script_list->get_item_metadata(p_idx)); + + _go_to_tab(script_list->get_item_metadata(p_idx)); grab_focus_block=false; } @@ -1457,16 +1597,38 @@ void ScriptEditor::ensure_select_current() { if (tab_container->get_child_count() && tab_container->get_current_tab()>=0) { - ScriptTextEditor *ste = tab_container->get_child(tab_container->get_current_tab())->cast_to<ScriptTextEditor>(); - if (!ste) - return; - Ref<Script> script = ste->get_edited_script(); + Node *current = tab_container->get_child(tab_container->get_current_tab()); + + + ScriptTextEditor *ste = current->cast_to<ScriptTextEditor>(); + if (ste) { + + Ref<Script> script = ste->get_edited_script(); + + if (!grab_focus_block && is_inside_tree()) + ste->get_text_edit()->grab_focus(); + + edit_menu->show(); + search_menu->show(); + script_search_menu->hide(); + + + } + + EditorHelp *eh = current->cast_to<EditorHelp>(); - if (!grab_focus_block && is_inside_tree()) - ste->get_text_edit()->grab_focus(); + if (eh) { + edit_menu->hide(); + search_menu->hide(); + script_search_menu->show(); + + } } + + + } void ScriptEditor::_find_scripts(Node* p_base, Node* p_current, Set<Ref<Script> > &used) { @@ -1485,6 +1647,57 @@ void ScriptEditor::_find_scripts(Node* p_base, Node* p_current, Set<Ref<Script> } +struct _ScriptEditorItemData { + + String name; + Ref<Texture> icon; + int index; + String tooltip; + bool used; + int category; + + + bool operator<(const _ScriptEditorItemData& id) const { + + return category==id.category?name.nocasecmp_to(id.name)<0:category<id.category; + } + +}; + + +void ScriptEditor::_update_script_colors() { + + bool enabled = EditorSettings::get_singleton()->get("text_editor/script_temperature_enabled"); + if (!enabled) + return; + + int hist_size = EditorSettings::get_singleton()->get("text_editor/script_temperature_history_size"); + Color hot_color=EditorSettings::get_singleton()->get("text_editor/script_temperature_hot_color"); + Color cold_color=EditorSettings::get_singleton()->get("text_editor/script_temperature_cold_color"); + + for(int i=0;i<script_list->get_item_count();i++) { + + int c = script_list->get_item_metadata(i); + Node *n = tab_container->get_child(c); + if (!n) + continue; + + script_list->set_item_custom_bg_color(i,Color(0,0,0,0)); + if (!n->has_meta("__editor_pass")) { + continue; + } + + int pass=n->get_meta("__editor_pass"); + int h = edit_pass - pass; + if (h>hist_size) { + continue; + } + float v = Math::ease((edit_pass-pass)/float_t(hist_size),0.4); + + + script_list->set_item_custom_bg_color(i,hot_color.linear_interpolate(cold_color,v)); + } +} void ScriptEditor::_update_script_names() { @@ -1496,31 +1709,75 @@ void ScriptEditor::_update_script_names() { } script_list->clear(); + bool split_script_help = EditorSettings::get_singleton()->get("text_editor/group_help_pages"); + + Vector<_ScriptEditorItemData> sedata; + for(int i=0;i<tab_container->get_child_count();i++) { + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); - if (!ste) - continue; + if (ste) { - String script = ste->get_name(); - Ref<Texture> icon = ste->get_icon(); - String path = ste->get_edited_script()->get_path(); - script_list->add_item(script,icon); + String name = ste->get_name(); + Ref<Texture> icon = ste->get_icon(); + String tooltip = ste->get_edited_script()->get_path(); - int index = script_list->get_item_count()-1; + _ScriptEditorItemData sd; + sd.icon=icon; + sd.name=name; + sd.tooltip=tooltip; + sd.index=i; + sd.used=used.has(ste->get_edited_script()); + sd.category=0; + + sedata.push_back(sd); + } - script_list->set_item_tooltip(index,path); - script_list->set_item_metadata(index,i); - if (used.has(ste->get_edited_script())) { + EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>(); + if (eh) { + + String name = eh->get_class_name(); + Ref<Texture> icon = get_icon("Help","EditorIcons"); + String tooltip = name+" Class Reference"; + + _ScriptEditorItemData sd; + sd.icon=icon; + sd.name=name; + sd.tooltip=tooltip; + sd.index=i; + sd.used=false; + sd.category=split_script_help?1:0; + sedata.push_back(sd); + + } + + } + + sedata.sort(); + + for(int i=0;i<sedata.size();i++) { + + script_list->add_item(sedata[i].name,sedata[i].icon); + int index = script_list->get_item_count()-1; + script_list->set_item_tooltip(index,sedata[i].tooltip); + script_list->set_item_metadata(index,sedata[i].index); + if (sedata[i].used) { script_list->set_item_custom_bg_color(index,Color(88/255.0,88/255.0,60/255.0)); } - if (tab_container->get_current_tab()==index) { + if (tab_container->get_current_tab()==sedata[i].index) { script_list->select(index); + script_name_label->set_text(sedata[i].name); + script_icon->set_texture(sedata[i].icon); + } } - script_list->sort_items_by_text(); + _update_script_colors(); + + + } @@ -1531,6 +1788,8 @@ void ScriptEditor::edit(const Ref<Script>& p_script) { // see if already has it + bool open_dominant = EditorSettings::get_singleton()->get("text_editor/open_dominant_script_on_scene_change"); + if (p_script->get_path().is_resource_file() && bool(EditorSettings::get_singleton()->get("external_editor/use_external_editor"))) { String path = EditorSettings::get_singleton()->get("external_editor/exec_path"); @@ -1559,12 +1818,13 @@ void ScriptEditor::edit(const Ref<Script>& p_script) { if (ste->get_edited_script()==p_script) { - if (!EditorNode::get_singleton()->is_changing_scene()) { + if (open_dominant || !EditorNode::get_singleton()->is_changing_scene()) { if (tab_container->get_current_tab()!=i) { - tab_container->set_current_tab(i); + _go_to_tab(i); script_list->select( script_list->find_metadata(i) ); } - ste->get_text_edit()->grab_focus(); + if (is_visible()) + ste->get_text_edit()->grab_focus(); } return; } @@ -1576,7 +1836,8 @@ void ScriptEditor::edit(const Ref<Script>& p_script) { ste->set_edited_script(p_script); ste->get_text_edit()->set_tooltip_request_func(this,"_get_debug_tooltip",ste); tab_container->add_child(ste); - tab_container->set_current_tab(tab_container->get_tab_count()-1); + _go_to_tab(tab_container->get_tab_count()-1); + @@ -1675,7 +1936,7 @@ void ScriptEditor::_add_callback(Object *p_obj, const String& p_function, const ste->get_text_edit()->insert_text_at_cursor("\n\n"+func); } - tab_container->set_current_tab(i); + _go_to_tab(i); ste->get_text_edit()->cursor_set_line(pos); ste->get_text_edit()->cursor_set_column(1); @@ -1726,10 +1987,13 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) { return; } - if (!p_layout->has_section_key("ScriptEditor","open_scripts")) + if (!p_layout->has_section_key("ScriptEditor","open_scripts") && !p_layout->has_section_key("ScriptEditor","open_help")) return; Array scripts = p_layout->get_value("ScriptEditor","open_scripts"); + Array helps; + if (p_layout->has_section_key("ScriptEditor","open_help")) + helps=p_layout->get_value("ScriptEditor","open_help"); restoring_layout=true; @@ -1742,6 +2006,18 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) { } } + + for(int i=0;i<helps.size();i++) { + + String path = helps[i]; + _help_class_open(path); + } + + for(int i=0;i<tab_container->get_child_count();i++) { + tab_container->get_child(i)->set_meta("__editor_pass",Variant()); + } + + if (p_layout->has_section_key("ScriptEditor","split_offset")) { script_split->set_split_offset(p_layout->get_value("ScriptEditor","split_offset")); } @@ -1754,27 +2030,184 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) { void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) { Array scripts; + Array helps; for(int i=0;i<tab_container->get_child_count();i++) { ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); - if (!ste) - continue; + if (ste) { - String path = ste->get_edited_script()->get_path(); - if (!path.is_resource_file()) - continue; + String path = ste->get_edited_script()->get_path(); + if (!path.is_resource_file()) + continue; + + scripts.push_back(path); + } + + EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>(); + + if (eh) { + + helps.push_back(eh->get_class_name()); + } - scripts.push_back(path); } p_layout->set_value("ScriptEditor","open_scripts",scripts); + p_layout->set_value("ScriptEditor","open_help",helps); p_layout->set_value("ScriptEditor","split_offset",script_split->get_split_offset()); } +void ScriptEditor::_help_class_open(const String& p_class) { + + + for(int i=0;i<tab_container->get_child_count();i++) { + + EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>(); + + if (eh && eh->get_class_name()==p_class) { + + _go_to_tab(i); + _update_script_names(); + return; + } + } + + EditorHelp * eh = memnew( EditorHelp ); + + + eh->set_name(p_class); + tab_container->add_child(eh); + _go_to_tab(tab_container->get_tab_count()-1); + eh->go_to_class(p_class,0); + eh->connect("go_to_help",this,"_help_class_goto"); + _update_script_names(); + +} + +void ScriptEditor::_help_class_goto(const String& p_desc) { + + + String cname=p_desc.get_slice(":",1); + + for(int i=0;i<tab_container->get_child_count();i++) { + + EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>(); + + if (eh && eh->get_class_name()==cname) { + + _go_to_tab(i); + eh->go_to_help(p_desc); + _update_script_names(); + return; + } + } + + EditorHelp * eh = memnew( EditorHelp ); + + eh->set_name(cname); + tab_container->add_child(eh); + _go_to_tab(tab_container->get_tab_count()-1); + eh->go_to_help(p_desc); + eh->connect("go_to_help",this,"_help_class_goto"); + _update_script_names(); + +} + +void ScriptEditor::_update_history_pos(int p_new_pos) { + + Node *n = tab_container->get_current_tab_control(); + + if (n->cast_to<ScriptTextEditor>()) { + + history[history_pos].scroll_pos=n->cast_to<ScriptTextEditor>()->get_text_edit()->get_v_scroll(); + history[history_pos].cursor_column=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_column(); + history[history_pos].cursor_row=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_line(); + } + if (n->cast_to<EditorHelp>()) { + + history[history_pos].scroll_pos=n->cast_to<EditorHelp>()->get_scroll(); + } + + history_pos=p_new_pos; + tab_container->set_current_tab(history[history_pos].control->get_index()); + + n = history[history_pos].control; + + if (n->cast_to<ScriptTextEditor>()) { + + n->cast_to<ScriptTextEditor>()->get_text_edit()->set_v_scroll(history[history_pos].scroll_pos); + n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_set_column( history[history_pos].cursor_column ); + n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_set_line( history[history_pos].cursor_row ); + n->cast_to<ScriptTextEditor>()->get_text_edit()->grab_focus(); + } + + if (n->cast_to<EditorHelp>()) { + + n->cast_to<EditorHelp>()->set_scroll(history[history_pos].scroll_pos); + n->cast_to<EditorHelp>()->set_focused(); + } + + n->set_meta("__editor_pass",++edit_pass); + _update_script_names(); + _update_history_arrows(); + +} + +void ScriptEditor::_history_forward() { + + if (history_pos<history.size()-1) { + _update_history_pos(history_pos+1); + } +} + +void ScriptEditor::_history_back(){ + + if (history_pos>0) { + _update_history_pos(history_pos-1); + } + +} +void ScriptEditor::set_scene_root_script( Ref<Script> p_script ) { + + bool open_dominant = EditorSettings::get_singleton()->get("text_editor/open_dominant_script_on_scene_change"); + if (open_dominant && p_script.is_valid()) { + edit(p_script); + } +} + +void ScriptEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_tab_changed",&ScriptEditor::_tab_changed); + ObjectTypeDB::bind_method("_menu_option",&ScriptEditor::_menu_option); + ObjectTypeDB::bind_method("_close_current_tab",&ScriptEditor::_close_current_tab); + ObjectTypeDB::bind_method("_editor_play",&ScriptEditor::_editor_play); + ObjectTypeDB::bind_method("_editor_pause",&ScriptEditor::_editor_pause); + ObjectTypeDB::bind_method("_editor_stop",&ScriptEditor::_editor_stop); + ObjectTypeDB::bind_method("_add_callback",&ScriptEditor::_add_callback); + ObjectTypeDB::bind_method("_reload_scripts",&ScriptEditor::_reload_scripts); + ObjectTypeDB::bind_method("_resave_scripts",&ScriptEditor::_resave_scripts); + ObjectTypeDB::bind_method("_res_saved_callback",&ScriptEditor::_res_saved_callback); + ObjectTypeDB::bind_method("_goto_script_line",&ScriptEditor::_goto_script_line); + ObjectTypeDB::bind_method("_goto_script_line2",&ScriptEditor::_goto_script_line2); + ObjectTypeDB::bind_method("_breaked",&ScriptEditor::_breaked); + ObjectTypeDB::bind_method("_show_debugger",&ScriptEditor::_show_debugger); + ObjectTypeDB::bind_method("_get_debug_tooltip",&ScriptEditor::_get_debug_tooltip); + ObjectTypeDB::bind_method("_autosave_scripts",&ScriptEditor::_autosave_scripts); + ObjectTypeDB::bind_method("_editor_settings_changed",&ScriptEditor::_editor_settings_changed); + ObjectTypeDB::bind_method("_update_script_names",&ScriptEditor::_update_script_names); + ObjectTypeDB::bind_method("_tree_changed",&ScriptEditor::_tree_changed); + ObjectTypeDB::bind_method("_script_selected",&ScriptEditor::_script_selected); + ObjectTypeDB::bind_method("_script_created",&ScriptEditor::_script_created); + ObjectTypeDB::bind_method("_script_split_dragged",&ScriptEditor::_script_split_dragged); + ObjectTypeDB::bind_method("_help_class_open",&ScriptEditor::_help_class_open); + ObjectTypeDB::bind_method("_help_class_goto",&ScriptEditor::_help_class_goto); + ObjectTypeDB::bind_method("_history_forward",&ScriptEditor::_history_forward); + ObjectTypeDB::bind_method("_history_back",&ScriptEditor::_history_back); +} ScriptEditor::ScriptEditor(EditorNode *p_editor) { @@ -1816,6 +2249,9 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { file_menu->get_popup()->add_item("Save As..",FILE_SAVE_AS); file_menu->get_popup()->add_item("Save All",FILE_SAVE_ALL,KEY_MASK_CMD|KEY_MASK_SHIFT|KEY_S); file_menu->get_popup()->add_separator(); + file_menu->get_popup()->add_item("History Prev",WINDOW_PREV,KEY_MASK_CTRL|KEY_MASK_ALT|KEY_LEFT); + file_menu->get_popup()->add_item("History Next",WINDOW_NEXT,KEY_MASK_CTRL|KEY_MASK_ALT|KEY_RIGHT); + file_menu->get_popup()->add_separator(); file_menu->get_popup()->add_item("Close",FILE_CLOSE,KEY_MASK_CMD|KEY_W); file_menu->get_popup()->connect("item_pressed", this,"_menu_option"); @@ -1851,13 +2287,22 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { menu_hb->add_child(search_menu); search_menu->set_text("Search"); search_menu->get_popup()->add_item("Find..",SEARCH_FIND,KEY_MASK_CMD|KEY_F); - search_menu->get_popup()->add_item("Find Next",SEARCH_FIND_NEXT,KEY_MASK_CMD|KEY_G); + search_menu->get_popup()->add_item("Find Next",SEARCH_FIND_NEXT,KEY_F3); search_menu->get_popup()->add_item("Replace..",SEARCH_REPLACE,KEY_MASK_CMD|KEY_R); search_menu->get_popup()->add_separator(); search_menu->get_popup()->add_item("Goto Function..",SEARCH_LOCATE_FUNCTION,KEY_MASK_SHIFT|KEY_MASK_CMD|KEY_F); search_menu->get_popup()->add_item("Goto Line..",SEARCH_GOTO_LINE,KEY_MASK_CMD|KEY_L); search_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + script_search_menu = memnew( MenuButton ); + menu_hb->add_child(script_search_menu); + script_search_menu->set_text("Search"); + script_search_menu->get_popup()->add_item("Find..",SEARCH_FIND,KEY_MASK_CMD|KEY_F); + script_search_menu->get_popup()->add_item("Find Next",SEARCH_FIND_NEXT,KEY_F3); + script_search_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + script_search_menu->hide(); + + debug_menu = memnew( MenuButton ); menu_hb->add_child(debug_menu); debug_menu->set_text("Debug"); @@ -1897,6 +2342,53 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { help_menu->get_popup()->add_item("Contextual", HELP_CONTEXTUAL, KEY_MASK_SHIFT|KEY_F1); help_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + menu_hb->add_spacer(); + + + script_icon = memnew( TextureFrame ); + menu_hb->add_child(script_icon); + script_name_label = memnew( Label ); + menu_hb->add_child(script_name_label); + + script_icon->hide(); + script_name_label->hide(); + + menu_hb->add_spacer(); + + site_search = memnew( ToolButton ); + site_search->set_text("Tutorials"); + site_search->connect("pressed",this,"_menu_option",varray(SEARCH_WEBSITE)); + menu_hb->add_child(site_search); + site_search->set_tooltip("Open http://www.godotengine.org at tutorials section."); + + class_search = memnew( ToolButton ); + class_search->set_text("Classes"); + class_search->connect("pressed",this,"_menu_option",varray(SEARCH_CLASSES)); + menu_hb->add_child(class_search); + class_search->set_tooltip("Search the class hierarchy."); + + help_search = memnew( ToolButton ); + help_search->set_text("Search Help"); + help_search->connect("pressed",this,"_menu_option",varray(SEARCH_HELP)); + menu_hb->add_child(help_search); + help_search->set_tooltip("Search the reference documentation."); + + menu_hb->add_child( memnew( VSeparator) ); + + script_back = memnew( ToolButton ); + script_back->connect("pressed",this,"_history_back"); + menu_hb->add_child(script_back); + script_back->set_disabled(true); + help_search->set_tooltip("Go to previous edited document."); + + script_forward = memnew( ToolButton ); + script_forward->connect("pressed",this,"_history_forward"); + menu_hb->add_child(script_forward); + script_forward->set_disabled(true); + help_search->set_tooltip("Go to next edited document."); + + + tab_container->connect("tab_changed", this,"_tab_changed"); find_replace_dialog = memnew(FindReplaceDialog); @@ -1959,8 +2451,20 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { grab_focus_block=false; + help_search_dialog = memnew( EditorHelpSearch ); + add_child(help_search_dialog); + help_search_dialog->connect("go_to_help",this,"_help_class_goto"); + + + help_index = memnew( EditorHelpIndex ); + add_child(help_index); + help_index->connect("open_class",this,"_help_class_open"); + + history_pos=-1; // debugger_gui->hide(); + edit_pass=0; + } @@ -2062,8 +2566,14 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) { script_editor->hide(); EDITOR_DEF("text_editor/auto_reload_changed_scripts",false); + EDITOR_DEF("text_editor/open_dominant_script_on_scene_change",true); EDITOR_DEF("external_editor/use_external_editor",false); EDITOR_DEF("external_editor/exec_path",""); + EDITOR_DEF("text_editor/script_temperature_enabled",true); + EDITOR_DEF("text_editor/script_temperature_history_size",15); + EDITOR_DEF("text_editor/script_temperature_hot_color",Color(1,0,0,0.3)); + EDITOR_DEF("text_editor/script_temperature_cold_color",Color(0,0,1,0.3)); + EDITOR_DEF("text_editor/group_help_pages",false); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"external_editor/exec_path",PROPERTY_HINT_GLOBAL_FILE)); EDITOR_DEF("external_editor/exec_flags",""); diff --git a/tools/editor/plugins/script_editor_plugin.h b/tools/editor/plugins/script_editor_plugin.h index e635a1974b..e755f570ef 100644 --- a/tools/editor/plugins/script_editor_plugin.h +++ b/tools/editor/plugins/script_editor_plugin.h @@ -41,6 +41,7 @@ #include "tools/editor/code_editor.h" #include "scene/gui/split_container.h" #include "scene/gui/item_list.h" +#include "tools/editor/editor_help.h" class ScriptEditorQuickOpen : public ConfirmationDialog { @@ -141,6 +142,9 @@ class ScriptEditor : public VBoxContainer { SEARCH_REPLACE, SEARCH_LOCATE_FUNCTION, SEARCH_GOTO_LINE, + SEARCH_HELP, + SEARCH_CLASSES, + SEARCH_WEBSITE, DEBUG_TOGGLE_BREAKPOINT, DEBUG_NEXT, DEBUG_STEP, @@ -150,6 +154,8 @@ class ScriptEditor : public VBoxContainer { HELP_CONTEXTUAL, WINDOW_MOVE_LEFT, WINDOW_MOVE_RIGHT, + WINDOW_NEXT, + WINDOW_PREV, WINDOW_SELECT_BASE=100 }; @@ -157,11 +163,17 @@ class ScriptEditor : public VBoxContainer { MenuButton *file_menu; MenuButton *edit_menu; MenuButton *search_menu; + MenuButton *script_search_menu; MenuButton *debug_menu; MenuButton *help_menu; Timer *autosave_timer; uint64_t idle; + Button *help_search; + Button *site_search; + Button *class_search; + EditorHelpSearch *help_search_dialog; + ItemList *script_list; HSplitContainer *script_split; TabContainer *tab_container; @@ -172,6 +184,27 @@ class ScriptEditor : public VBoxContainer { ScriptEditorDebugger* debugger; ToolButton *scripts_visible; + TextureFrame *script_icon; + Label *script_name_label; + + ToolButton *script_back; + ToolButton *script_forward; + + + struct ScriptHistory { + + Control *control; + int scroll_pos; + int cursor_column; + int cursor_row; + }; + + Vector<ScriptHistory> history; + int history_pos; + + + EditorHelpIndex *help_index; + void _tab_changed(int p_which); void _menu_option(int p_optin); @@ -201,6 +234,8 @@ class ScriptEditor : public VBoxContainer { void _editor_pause(); void _editor_stop(); + int edit_pass; + void _add_callback(Object *p_obj, const String& p_function, const StringArray& p_args); void _res_saved_callback(const Ref<Resource>& p_res); @@ -224,8 +259,20 @@ class ScriptEditor : public VBoxContainer { void _script_split_dragged(float); + + void _history_forward(); + void _history_back(); + bool waiting_update_names; + void _help_class_open(const String& p_class); + void _help_class_goto(const String& p_desc); + void _update_history_arrows(); + void _go_to_tab(int p_idx); + void _update_history_pos(int p_new_pos); + void _update_script_colors(); + + static ScriptEditor *script_editor; protected: void _notification(int p_what); @@ -253,6 +300,8 @@ public: void set_window_layout(Ref<ConfigFile> p_layout); void get_window_layout(Ref<ConfigFile> p_layout); + void set_scene_root_script( Ref<Script> p_script ); + ScriptEditorDebugger *get_debugger() { return debugger; } ScriptEditor(EditorNode *p_editor); diff --git a/tools/editor/plugins/shader_graph_editor_plugin.cpp b/tools/editor/plugins/shader_graph_editor_plugin.cpp index 684e7e32ef..3a7dc26466 100644 --- a/tools/editor/plugins/shader_graph_editor_plugin.cpp +++ b/tools/editor/plugins/shader_graph_editor_plugin.cpp @@ -2542,7 +2542,7 @@ void ShaderGraphView::_notification(int p_what) { void ShaderGraphView::add_node(int p_type, const Vector2 &location) { - if ((p_type==ShaderGraph::NODE_INPUT||p_type==ShaderGraph::NODE_INPUT) && graph->node_count(type, p_type)>0) + if (p_type==ShaderGraph::NODE_INPUT && graph->node_count(type, p_type)>0) return; List<int> existing; diff --git a/tools/editor/plugins/spatial_editor_plugin.cpp b/tools/editor/plugins/spatial_editor_plugin.cpp index 3ab9339265..e2202214dd 100644 --- a/tools/editor/plugins/spatial_editor_plugin.cpp +++ b/tools/editor/plugins/spatial_editor_plugin.cpp @@ -232,15 +232,6 @@ void SpatialEditorViewport::_select(Spatial *p_node, bool p_append,bool p_single } - -struct _RayResult { - - Spatial* item; - float depth; - int handle; - _FORCE_INLINE_ bool operator<(const _RayResult& p_rr) const { return depth<p_rr.depth; } -}; - ObjectID SpatialEditorViewport::_select_ray(const Point2& p_pos, bool p_append,bool &r_includes_current,int *r_gizmo_handle,bool p_alt_select) { if (r_gizmo_handle) @@ -379,6 +370,70 @@ ObjectID SpatialEditorViewport::_select_ray(const Point2& p_pos, bool p_append,b } +void SpatialEditorViewport::_find_items_at_pos(const Point2& p_pos,bool &r_includes_current,Vector<_RayResult> &results,bool p_alt_select) { + + Vector3 ray=_get_ray(p_pos); + Vector3 pos=_get_ray_pos(p_pos); + + Vector<RID> instances=VisualServer::get_singleton()->instances_cull_ray(pos,ray,get_tree()->get_root()->get_world()->get_scenario() ); + Set<Ref<SpatialEditorGizmo> > found_gizmos; + + r_includes_current=false; + + for (int i=0;i<instances.size();i++) { + + uint32_t id=VisualServer::get_singleton()->instance_get_object_instance_ID(instances[i]); + Object *obj=ObjectDB::get_instance(id); + if (!obj) + continue; + + Spatial *spat=obj->cast_to<Spatial>(); + + if (!spat) + continue; + + Ref<SpatialEditorGizmo> seg = spat->get_gizmo(); + + if (!seg.is_valid()) + continue; + + if (found_gizmos.has(seg)) + continue; + + found_gizmos.insert(seg); + Vector3 point; + Vector3 normal; + + int handle=-1; + bool inters = seg->intersect_ray(camera,p_pos,point,normal,NULL,p_alt_select); + + if (!inters) + continue; + + float dist = pos.distance_to(point); + + if (dist<0) + continue; + + + + if (editor_selection->is_selected(spat)) + r_includes_current=true; + + _RayResult res; + res.item=spat; + res.depth=dist; + res.handle=handle; + results.push_back(res); + } + + + if (results.empty()) + return; + + results.sort(); +} + Vector3 SpatialEditorViewport::_get_screen_to_space(const Vector3& p_pos) { @@ -724,6 +779,7 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { } break; case BUTTON_RIGHT: { + NavigationScheme nav_scheme = _get_navigation_schema("3d_editor/navigation_scheme"); if (b.pressed && _edit.gizmo.is_valid()) { //restore @@ -832,6 +888,57 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) { //VisualServer::get_singleton()->poly_clear(indicators); set_message("Transform Aborted.",3); } + + if (!b.pressed && (spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_SELECT && b.mod.alt)) { + + if (nav_scheme == NAVIGATION_MAYA) + break; + + _find_items_at_pos(Vector2( b.x, b.y ),clicked_includes_current,selection_results,b.mod.shift); + + clicked_wants_append=b.mod.shift; + + if (selection_results.size() == 1) { + + clicked=selection_results[0].item->get_instance_ID(); + selection_results.clear(); + + if (clicked) { + _select_clicked(clicked_wants_append,true); + clicked=0; + } + + } else if (!selection_results.empty()) { + + NodePath root_path = get_tree()->get_edited_scene_root()->get_path(); + StringName root_name = root_path.get_name(root_path.get_name_count()-1); + + for (int i = 0; i < selection_results.size(); i++) { + + Spatial *spat=selection_results[i].item; + + Ref<Texture> icon; + if (spat->has_meta("_editor_icon")) + icon=spat->get_meta("_editor_icon"); + else + icon=get_icon( has_icon(spat->get_type(),"EditorIcons")?spat->get_type():String("Object"),"EditorIcons"); + + String node_path="/"+root_name+"/"+root_path.rel_path_to(spat->get_path()); + + selection_menu->add_item(spat->get_name()); + selection_menu->set_item_icon(i, icon ); + selection_menu->set_item_metadata(i, node_path); + selection_menu->set_item_tooltip(i,String(spat->get_name())+ + "\nType: "+spat->get_type()+"\nPath: "+node_path); + } + + selection_menu->set_global_pos(Vector2( b.global_x, b.global_y )); + selection_menu->popup(); + selection_menu->call_deferred("grab_click_focus"); + + break; + } + } } break; case BUTTON_MIDDLE: { @@ -1957,11 +2064,11 @@ void SpatialEditorViewport::_menu_option(int p_option) { if (!se) continue; - Vector3 original_scale = sp->get_scale(); - sp->set_global_transform(camera_transform); - sp->set_scale(original_scale); - undo_redo->add_do_method(sp,"set_global_transform",sp->get_global_transform()); - undo_redo->add_undo_method(sp,"set_global_transform",se->original); + Transform xform = camera_transform; + xform.scale_basis(sp->get_scale()); + + undo_redo->add_do_method(sp,"set_global_transform",xform); + undo_redo->add_undo_method(sp,"set_global_transform",sp->get_global_transform()); } undo_redo->commit_action(); } break; @@ -2096,6 +2203,26 @@ void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) { } } +void SpatialEditorViewport::_selection_result_pressed(int p_result) { + + if (selection_results.size() <= p_result) + return; + + clicked=selection_results[p_result].item->get_instance_ID(); + + if (clicked) { + _select_clicked(clicked_wants_append,true); + clicked=0; + } +} + +void SpatialEditorViewport::_selection_menu_hide() { + + selection_results.clear(); + selection_menu->clear(); + selection_menu->set_size(Vector2(0, 0)); +} + void SpatialEditorViewport::set_can_preview(Camera* p_preview) { preview=p_preview; @@ -2210,6 +2337,8 @@ void SpatialEditorViewport::_bind_methods(){ ObjectTypeDB::bind_method(_MD("_toggle_camera_preview"),&SpatialEditorViewport::_toggle_camera_preview); ObjectTypeDB::bind_method(_MD("_preview_exited_scene"),&SpatialEditorViewport::_preview_exited_scene); ObjectTypeDB::bind_method(_MD("update_transform_gizmo_view"),&SpatialEditorViewport::update_transform_gizmo_view); + ObjectTypeDB::bind_method(_MD("_selection_result_pressed"),&SpatialEditorViewport::_selection_result_pressed); + ObjectTypeDB::bind_method(_MD("_selection_menu_hide"),&SpatialEditorViewport::_selection_menu_hide); ADD_SIGNAL( MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport")) ); } @@ -2307,6 +2436,12 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed preview=NULL; gizmo_scale=1.0; + selection_menu = memnew( PopupMenu ); + add_child(selection_menu); + selection_menu->set_custom_minimum_size(Vector2(100, 0)); + selection_menu->connect("item_pressed", this, "_selection_result_pressed"); + selection_menu->connect("popup_hide", this, "_selection_menu_hide"); + if (p_index==0) { view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER),true); viewport->set_as_audio_listener(true); @@ -2725,7 +2860,7 @@ void SpatialEditor::_menu_item_pressed(int p_option) { } break; case MENU_TRANSFORM_CONFIGURE_SNAP: { - snap_dialog->popup_centered(Size2(200,160)); + snap_dialog->popup_centered(Size2(200,180)); } break; case MENU_TRANSFORM_LOCAL_COORDS: { @@ -3793,46 +3928,24 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { snap_dialog = memnew( ConfirmationDialog ); snap_dialog->set_title("Snap Settings"); add_child(snap_dialog); - Label *l = memnew(Label); - l->set_text("Translate Snap:"); - l->set_pos(Point2(5,5)); - snap_dialog->add_child(l); + + VBoxContainer *snap_dialog_vbc = memnew( VBoxContainer ); + snap_dialog->add_child(snap_dialog_vbc); + snap_dialog->set_child_rect(snap_dialog_vbc); snap_translate = memnew( LineEdit ); - snap_translate->set_anchor( MARGIN_RIGHT, ANCHOR_END ); - snap_translate->set_begin( Point2(15,22) ); - snap_translate->set_end( Point2(15,35) ); snap_translate->set_text("1"); - snap_dialog->add_child(snap_translate); - - l = memnew(Label); - l->set_text("Rotate Snap (deg.):"); - l->set_pos(Point2(5,45)); - snap_dialog->add_child(l); + snap_dialog_vbc->add_margin_child("Translate Snap:",snap_translate); snap_rotate = memnew( LineEdit ); - snap_rotate->set_anchor( MARGIN_RIGHT, ANCHOR_END ); - snap_rotate->set_begin( Point2(15,62) ); - snap_rotate->set_end( Point2(15,75) ); snap_rotate->set_text("5"); - snap_dialog->add_child(snap_rotate); - - - l = memnew(Label); - l->set_text("Scale Snap (%):"); - l->set_pos(Point2(5,85)); - snap_dialog->add_child(l); + snap_dialog_vbc->add_margin_child("Rotate Snap (deg.):",snap_rotate); snap_scale = memnew( LineEdit ); - snap_scale->set_anchor( MARGIN_RIGHT, ANCHOR_END ); - snap_scale->set_begin( Point2(15,102) ); - snap_scale->set_end( Point2(15,115) ); snap_scale->set_text("5"); - snap_dialog->add_child(snap_scale); + snap_dialog_vbc->add_margin_child("Scale Snap (%):",snap_scale); - //snap_dialog->get_cancel()->hide(); - - /* SNAP DIALOG */ + /* SETTINGS DIALOG */ settings_dialog = memnew( ConfirmationDialog ); settings_dialog->set_title("Viewport Settings"); @@ -3906,7 +4019,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { xform_dialog = memnew( ConfirmationDialog ); xform_dialog->set_title("Transform Change"); add_child(xform_dialog); - l = memnew(Label); + Label *l = memnew(Label); l->set_text("Translate:"); l->set_pos(Point2(5,5)); xform_dialog->add_child(l); diff --git a/tools/editor/plugins/spatial_editor_plugin.h b/tools/editor/plugins/spatial_editor_plugin.h index ff8912fca8..ebd3f77fe7 100644 --- a/tools/editor/plugins/spatial_editor_plugin.h +++ b/tools/editor/plugins/spatial_editor_plugin.h @@ -111,12 +111,21 @@ private: bool orthogonal; float gizmo_scale; + struct _RayResult { + + Spatial* item; + float depth; + int handle; + _FORCE_INLINE_ bool operator<(const _RayResult& p_rr) const { return depth<p_rr.depth; } + }; + void _update_name(); void _compute_edit(const Point2& p_point); void _clear_selected(); void _select_clicked(bool p_append,bool p_single); void _select(Spatial *p_node, bool p_append,bool p_single); ObjectID _select_ray(const Point2& p_pos, bool p_append,bool &r_includes_current,int *r_gizmo_handle=NULL,bool p_alt_select=false); + void _find_items_at_pos(const Point2& p_pos,bool &r_includes_current,Vector<_RayResult> &results,bool p_alt_select=false); Vector3 _get_ray_pos(const Vector2& p_pos) const; Vector3 _get_ray(const Vector2& p_pos); Point2 _point_to_screen(const Vector3& p_point); @@ -136,9 +145,12 @@ private: float get_fov() const; ObjectID clicked; + Vector<_RayResult> selection_results; bool clicked_includes_current; bool clicked_wants_append; + PopupMenu *selection_menu; + enum NavigationScheme { NAVIGATION_GODOT, NAVIGATION_MAYA, @@ -225,6 +237,8 @@ private: void _toggle_camera_preview(bool); void _init_gizmo_instance(int p_idx); void _finish_gizmo_instances(); + void _selection_result_pressed(int); + void _selection_menu_hide(); protected: diff --git a/tools/editor/plugins/theme_editor_plugin.cpp b/tools/editor/plugins/theme_editor_plugin.cpp index 55e8f164d6..63ba57bfc0 100644 --- a/tools/editor/plugins/theme_editor_plugin.cpp +++ b/tools/editor/plugins/theme_editor_plugin.cpp @@ -568,26 +568,24 @@ ThemeEditor::ThemeEditor() { CheckButton *cb = memnew( CheckButton ); cb->set_text("CheckButton"); first_vb->add_child(cb ); - CheckBox *cbx = memnew( CheckBox ); - cbx->set_text("CheckBox"); - first_vb->add_child(cbx ); - - /* TODO: This is not working properly, controls are overlapping*/ - /* - ButtonGroup *bg = memnew( ButtonGroup ); - bg->set_v_size_flags(SIZE_EXPAND_FILL); - VBoxContainer *gbvb = memnew( VBoxContainer ); - gbvb->set_v_size_flags(SIZE_EXPAND_FILL); - CheckBox *rbx1 = memnew( CheckBox ); - rbx1->set_text("CheckBox Radio1"); - rbx1->set_pressed(true); - gbvb->add_child(rbx1); - CheckBox *rbx2 = memnew( CheckBox ); - rbx2->set_text("CheckBox Radio2"); - gbvb->add_child(rbx2); - bg->add_child(gbvb); - first_vb->add_child(bg); - */ + CheckBox *cbx = memnew( CheckBox ); + cbx->set_text("CheckBox"); + first_vb->add_child(cbx ); + + + ButtonGroup *bg = memnew( ButtonGroup ); + bg->set_v_size_flags(SIZE_EXPAND_FILL); + VBoxContainer *gbvb = memnew( VBoxContainer ); + gbvb->set_v_size_flags(SIZE_EXPAND_FILL); + CheckBox *rbx1 = memnew( CheckBox ); + rbx1->set_text("CheckBox Radio1"); + rbx1->set_pressed(true); + gbvb->add_child(rbx1); + CheckBox *rbx2 = memnew( CheckBox ); + rbx2->set_text("CheckBox Radio2"); + gbvb->add_child(rbx2); + bg->add_child(gbvb); + first_vb->add_child(bg); MenuButton* test_menu_button = memnew( MenuButton ); test_menu_button->set_text("MenuButton"); diff --git a/tools/editor/plugins/tile_set_editor_plugin.cpp b/tools/editor/plugins/tile_set_editor_plugin.cpp index 39b0ef3c75..09115472a8 100644 --- a/tools/editor/plugins/tile_set_editor_plugin.cpp +++ b/tools/editor/plugins/tile_set_editor_plugin.cpp @@ -110,11 +110,15 @@ void TileSetEditor::_import_scene(Node *scene, Ref<TileSet> p_library, bool p_me if (!child2->cast_to<StaticBody2D>()) continue; StaticBody2D *sb = child2->cast_to<StaticBody2D>(); - if (sb->get_shape_count()==0) + int shape_count = sb->get_shape_count(); + if (shape_count==0) continue; - Ref<Shape2D> collision=sb->get_shape(0); - if (collision.is_valid()) { - collisions.push_back(collision); + for (int shape_index=0; shape_index<shape_count; ++shape_index) + { + Ref<Shape2D> collision=sb->get_shape(shape_index); + if (collision.is_valid()) { + collisions.push_back(collision); + } } } diff --git a/tools/editor/project_export.cpp b/tools/editor/project_export.cpp index 6003b976aa..cd6dc06f75 100644 --- a/tools/editor/project_export.cpp +++ b/tools/editor/project_export.cpp @@ -193,6 +193,8 @@ void ProjectExportDialog::_prop_edited(String what) { _save_export_cfg(); + _validate_platform(); + } void ProjectExportDialog::_filters_edited(String what) { @@ -252,6 +254,13 @@ void ProjectExportDialog::_script_edited(Variant v) { } +void ProjectExportDialog::_sample_convert_edited(int what) { + EditorImportExport::get_singleton()->sample_set_action( EditorImportExport::SampleAction(sample_mode->get_selected())); + EditorImportExport::get_singleton()->sample_set_max_hz( sample_max_hz->get_val() ); + EditorImportExport::get_singleton()->sample_set_trim( sample_trim->is_pressed() ); + +} + void ProjectExportDialog::_notification(int p_what) { switch(p_what) { @@ -319,6 +328,15 @@ void ProjectExportDialog::_notification(int p_what) { _update_group(); _update_group_tree(); + sample_mode->select( EditorImportExport::get_singleton()->sample_get_action() ); + sample_max_hz->set_val( EditorImportExport::get_singleton()->sample_get_max_hz() ); + sample_trim->set_pressed( EditorImportExport::get_singleton()->sample_get_trim() ); + + sample_mode->connect("item_selected",this,"_sample_convert_edited"); + sample_max_hz->connect("value_changed",this,"_sample_convert_edited"); + sample_trim->connect("toggled",this,"_sample_convert_edited"); + + } break; case NOTIFICATION_EXIT_TREE: { @@ -1045,6 +1063,7 @@ void ProjectExportDialog::_bind_methods() { ObjectTypeDB::bind_method(_MD("_group_select_none"),&ProjectExportDialog::_group_select_none); ObjectTypeDB::bind_method(_MD("_script_edited"),&ProjectExportDialog::_script_edited); ObjectTypeDB::bind_method(_MD("_update_script"),&ProjectExportDialog::_update_script); + ObjectTypeDB::bind_method(_MD("_sample_convert_edited"),&ProjectExportDialog::_sample_convert_edited); ObjectTypeDB::bind_method(_MD("export_platform"),&ProjectExportDialog::export_platform); @@ -1316,6 +1335,22 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) { hbc->add_child(button_reload); */ + + sample_vbox = memnew( VBoxContainer ); + sample_vbox->set_name("Samples"); + sections->add_child(sample_vbox); + sample_mode = memnew( OptionButton ); + sample_vbox->add_margin_child("Sample Conversion Mode: (.wav files):",sample_mode); + sample_mode->add_item("Keep"); + sample_mode->add_item("Compress (RAM - IMA-ADPCM)"); + sample_max_hz = memnew( SpinBox ); + sample_max_hz->set_max(192000); + sample_max_hz->set_min(8000); + sample_vbox->add_margin_child("Sampling Rate Limit: (hz)",sample_max_hz); + sample_trim = memnew( CheckButton ); + sample_trim->set_text("Trim"); + sample_vbox->add_margin_child("Trailing Silence:",sample_trim); + script_vbox = memnew( VBoxContainer ); script_vbox->set_name("Script"); sections->add_child(script_vbox); diff --git a/tools/editor/project_export.h b/tools/editor/project_export.h index d85e688e58..2f824e5ff7 100644 --- a/tools/editor/project_export.h +++ b/tools/editor/project_export.h @@ -139,6 +139,10 @@ private: OptionButton *script_mode; LineEdit *script_key; + VBoxContainer *sample_vbox; + OptionButton *sample_mode; + SpinBox *sample_max_hz; + CheckButton *sample_trim; void _export_mode_changed(int p_idx); @@ -162,6 +166,8 @@ private: void _image_export_edited(int what); void _shrink_edited(float what); + void _sample_convert_edited(int what); + void _update_group_list(); void _select_group(const String& p_by_name); diff --git a/tools/editor/project_manager.cpp b/tools/editor/project_manager.cpp index 9f47291433..893df04709 100644 --- a/tools/editor/project_manager.cpp +++ b/tools/editor/project_manager.cpp @@ -144,7 +144,7 @@ class NewProjectDialog : public ConfirmationDialog { fdialog->set_mode(FileDialog::MODE_OPEN_FILE); fdialog->clear_filters(); - fdialog->add_filter("engine.cfg ; "_MKSTR(VERSION_NAME)" Project"); + fdialog->add_filter("engine.cfg ; " _MKSTR(VERSION_NAME) " Project"); } else { fdialog->set_mode(FileDialog::MODE_OPEN_DIR); } @@ -193,7 +193,7 @@ class NewProjectDialog : public ConfirmationDialog { f->store_line("\n"); f->store_line("[application]"); f->store_line("name=\""+project_name->get_text()+"\""); - f->store_line("icon=\"icon.png\""); + f->store_line("icon=\"res://icon.png\""); memdelete(f); @@ -480,20 +480,25 @@ void ProjectManager::_load_recent_projects() { bool favorite = (_name.begins_with("favorite_projects/"))?true:false; uint64_t last_modified = 0; - if (FileAccess::exists(conf)) + if (FileAccess::exists(conf)) { last_modified = FileAccess::get_modified_time(conf); - String fscache = path.plus_file(".fscache"); - if (FileAccess::exists(fscache)) { - uint64_t cache_modified = FileAccess::get_modified_time(fscache); - if ( cache_modified > last_modified ) - last_modified = cache_modified; - } - ProjectItem item(project, path, conf, last_modified, favorite); - if (favorite) - favorite_projects.push_back(item); - else - projects.push_back(item); + String fscache = path.plus_file(".fscache"); + if (FileAccess::exists(fscache)) { + uint64_t cache_modified = FileAccess::get_modified_time(fscache); + if ( cache_modified > last_modified ) + last_modified = cache_modified; + } + + ProjectItem item(project, path, conf, last_modified, favorite); + if (favorite) + favorite_projects.push_back(item); + else + projects.push_back(item); + } else { + //project doesn't exist on disk but it's in the XML settings file + EditorSettings::get_singleton()->erase(_name); //remove it + } } projects.sort(); @@ -601,6 +606,8 @@ void ProjectManager::_load_recent_projects() { erase_btn->set_disabled(selected_list.size()<1); open_btn->set_disabled(selected_list.size()<1); run_btn->set_disabled(selected_list.size()<1 || (selected_list.size()==1 && single_selected_main=="")); + + EditorSettings::get_singleton()->save(); } void ProjectManager::_open_project_confirm() { @@ -839,7 +846,7 @@ ProjectManager::ProjectManager() { l->set_align(Label::ALIGN_CENTER); vb->add_child(l); l = memnew( Label ); - l->set_text("v"VERSION_MKSTRING); + l->set_text("v" VERSION_MKSTRING); //l->add_font_override("font",get_font("bold","Fonts")); l->set_align(Label::ALIGN_CENTER); vb->add_child(l); diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp index 549a3f7ffb..d6eae51fbd 100644 --- a/tools/editor/property_editor.cpp +++ b/tools/editor/property_editor.cpp @@ -3514,6 +3514,7 @@ PropertyEditor::PropertyEditor() { tree->connect("button_pressed", this,"_edit_button"); custom_editor->connect("variant_changed", this,"_custom_editor_edited"); custom_editor->connect("resource_edit_request", this,"_resource_edit_request",make_binds(),CONNECT_DEFERRED); + tree->set_hide_folding(true); capitalize_paths=true; autoclear=false; diff --git a/tools/editor/scene_tree_dock.cpp b/tools/editor/scene_tree_dock.cpp index 08aa68d792..0cafe7459b 100644 --- a/tools/editor/scene_tree_dock.cpp +++ b/tools/editor/scene_tree_dock.cpp @@ -1223,24 +1223,21 @@ void SceneTreeDock::_create() { } - String newname=n->get_name(); n->replace_by(newnode,true); - if (n==edited_scene) { edited_scene=newnode; editor->set_edited_scene(newnode); } - - - editor_data->get_undo_redo().clear_history(); - memdelete(n); newnode->set_name(newname); + editor->push_item(newnode); + memdelete(n); + _update_tool_buttons(); } diff --git a/tools/editor/scene_tree_editor.cpp b/tools/editor/scene_tree_editor.cpp index 60395d5ff5..ac2f76acdc 100644 --- a/tools/editor/scene_tree_editor.cpp +++ b/tools/editor/scene_tree_editor.cpp @@ -649,20 +649,28 @@ void SceneTreeEditor::_rename_node(ObjectID p_node,const String& p_name) { void SceneTreeEditor::_renamed() { TreeItem *which=tree->get_edited(); - + ERR_FAIL_COND(!which); NodePath np = which->get_metadata(0); Node *n=get_node(np); ERR_FAIL_COND(!n); + String new_name=which->get_text(0); + if (new_name.find(".") != -1 || new_name.find("/") != -1) { + + error->set_text("Invalid node name, the following characters are not allowed:\n \".\", \"/\""); + error->popup_centered_minsize(); + new_name=n->get_name(); + } + if (!undo_redo) { - n->set_name( which->get_text(0) ); + n->set_name( new_name ); which->set_metadata(0,n->get_path()); emit_signal("node_renamed"); } else { undo_redo->create_action("Rename Node"); - emit_signal("node_prerename",n,which->get_text(0)); - undo_redo->add_do_method(this,"_rename_node",n->get_instance_ID(),which->get_text(0)); + emit_signal("node_prerename",n,new_name); + undo_redo->add_do_method(this,"_rename_node",n->get_instance_ID(),new_name); undo_redo->add_undo_method(this,"_rename_node",n->get_instance_ID(),n->get_name()); undo_redo->commit_action(); } diff --git a/tools/editor/scenes_dock.cpp b/tools/editor/scenes_dock.cpp index 7d9c5b24b2..dfd729354d 100644 --- a/tools/editor/scenes_dock.cpp +++ b/tools/editor/scenes_dock.cpp @@ -352,10 +352,30 @@ String ScenesDock::get_selected_path() const { void ScenesDock::_instance_pressed() { - TreeItem *sel = tree->get_selected(); - if (!sel) - return; - String path = sel->get_metadata(0); + if (tree_mode) + { + TreeItem *sel = tree->get_selected(); + if (!sel) + return; + String path = sel->get_metadata(0); + } + else + { + int idx = -1; + for (int i = 0; i<files->get_item_count(); i++) { + if (files->is_selected(i)) + { + idx = i; + break; + } + } + + if (idx<0) + return; + + path = files->get_item_metadata(idx); + } + emit_signal("instance",path); } diff --git a/tools/editor/script_editor_debugger.cpp b/tools/editor/script_editor_debugger.cpp index 8e0e7ddb49..60f2afa2c2 100644 --- a/tools/editor/script_editor_debugger.cpp +++ b/tools/editor/script_editor_debugger.cpp @@ -1376,7 +1376,6 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor){ vmem_refresh->connect("pressed",this,"_video_mem_request"); MarginContainer *vmmc = memnew( MarginContainer ); - vmmc = memnew( MarginContainer ); vmem_tree = memnew( Tree ); vmem_tree->set_v_size_flags(SIZE_EXPAND_FILL); vmem_tree->set_h_size_flags(SIZE_EXPAND_FILL); diff --git a/tools/editor/spatial_editor_gizmos.cpp b/tools/editor/spatial_editor_gizmos.cpp index 4dc9c4f43e..5efca44c7d 100644 --- a/tools/editor/spatial_editor_gizmos.cpp +++ b/tools/editor/spatial_editor_gizmos.cpp @@ -745,7 +745,7 @@ static float _find_closest_angle_to_half_pi_arc(const Vector3& p_from, const Vec } //min_p = p_arc_xform.affine_inverse().xform(min_p); - float a = Vector2(min_p.x,-min_p.z).atan2(); + float a = Vector2(min_p.x,-min_p.z).angle(); return a*180.0/Math_PI; } diff --git a/tools/export/blender25/godot_export_manager.py b/tools/export/blender25/godot_export_manager.py index e390ae6ce3..582d76f94f 100644 --- a/tools/export/blender25/godot_export_manager.py +++ b/tools/export/blender25/godot_export_manager.py @@ -107,7 +107,6 @@ class godot_export_manager(bpy.types.Panel): col.prop(group,"use_triangles") col.prop(group,"use_copy_images") col.prop(group,"use_active_layers") - col.prop(group,"use_exclude_ctrl_bones") col.prop(group,"use_anim") col.prop(group,"use_anim_action_all") col.prop(group,"use_anim_skip_noexp") @@ -351,7 +350,7 @@ class export_group(bpy.types.Operator): bpy.data.objects[object.name].select = True bpy.ops.object.transform_apply(location=group[self.idx].apply_loc, rotation=group[self.idx].apply_rot, scale=group[self.idx].apply_scale) - bpy.ops.export_scene.dae(check_existing=True, filepath=path, filter_glob="*.dae", object_types=group[self.idx].object_types, use_export_selected=group[self.idx].use_export_selected, use_mesh_modifiers=group[self.idx].use_mesh_modifiers, use_tangent_arrays=group[self.idx].use_tangent_arrays, use_triangles=group[self.idx].use_triangles, use_copy_images=group[self.idx].use_copy_images, use_active_layers=group[self.idx].use_active_layers, use_exclude_ctrl_bones=group[self.idx].use_exclude_ctrl_bones, use_anim=group[self.idx].use_anim, use_anim_action_all=group[self.idx].use_anim_action_all, use_anim_skip_noexp=group[self.idx].use_anim_skip_noexp, use_anim_optimize=group[self.idx].use_anim_optimize, anim_optimize_precision=group[self.idx].anim_optimize_precision, use_metadata=group[self.idx].use_metadata) + bpy.ops.export_scene.dae(check_existing=True, filepath=path, filter_glob="*.dae", object_types=group[self.idx].object_types, use_export_selected=group[self.idx].use_export_selected, use_mesh_modifiers=group[self.idx].use_mesh_modifiers, use_tangent_arrays=group[self.idx].use_tangent_arrays, use_triangles=group[self.idx].use_triangles, use_copy_images=group[self.idx].use_copy_images, use_active_layers=group[self.idx].use_active_layers, use_anim=group[self.idx].use_anim, use_anim_action_all=group[self.idx].use_anim_action_all, use_anim_skip_noexp=group[self.idx].use_anim_skip_noexp, use_anim_optimize=group[self.idx].use_anim_optimize, anim_optimize_precision=group[self.idx].anim_optimize_precision, use_metadata=group[self.idx].use_metadata) self.report({'INFO'}, '"'+group[self.idx].name+'"' + " Group exported." ) msg = "Export Group "+group[self.idx].name @@ -422,7 +421,6 @@ class godot_export_groups(bpy.types.PropertyGroup): use_copy_images = BoolProperty(name="Copy Images",description="Copy Images (create images/ subfolder)",default=False) use_active_layers = BoolProperty(name="Active Layers",description="Export only objects on the active layers.",default=True) - use_exclude_ctrl_bones = BoolProperty(name="Exclude Control Bones",description="Exclude skeleton bones with names that begin with 'ctrl'.",default=True) use_anim = BoolProperty(name="Export Animation",description="Export keyframe animation",default=False) use_anim_action_all = BoolProperty(name="All Actions",description=("Export all actions for the first armature found in separate DAE files"),default=False) use_anim_skip_noexp = BoolProperty(name="Skip (-noexp) Actions",description="Skip exporting of actions whose name end in (-noexp). Useful to skip control animations.",default=True) diff --git a/tools/freetype/SCsub b/tools/freetype/SCsub index a31b8c4602..65b4827f9c 100644 --- a/tools/freetype/SCsub +++ b/tools/freetype/SCsub @@ -65,7 +65,5 @@ if (env["freetype"]=="builtin"): # lib = env.Library("freetype_builtin",ft_sources) # env.Prepend(LIBS=[lib]) - -Export('env') - +Export('env') diff --git a/tools/osx_template.app/Contents/Resources/icon.icns b/tools/osx_template.app/Contents/Resources/icon.icns Binary files differindex 18bc68d6ea..4a3dc0415a 100644 --- a/tools/osx_template.app/Contents/Resources/icon.icns +++ b/tools/osx_template.app/Contents/Resources/icon.icns diff --git a/tools/pck/SCsub b/tools/pck/SCsub index b1fed9a472..cf98ae145d 100644 --- a/tools/pck/SCsub +++ b/tools/pck/SCsub @@ -2,4 +2,3 @@ Import('env') if env["tools"] == "yes": env.add_source_files(env.tool_sources, "*.cpp") - diff --git a/tools/pe_bliss/README b/tools/pe_bliss/README new file mode 100644 index 0000000000..d5d1355444 --- /dev/null +++ b/tools/pe_bliss/README @@ -0,0 +1,84 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + + +Открытая бесплатная библиотека для работы с PE-файлами PE Bliss. +Бесплатна к использованию, модификации и распространению. +Автор: DX +(c) DX 2011-2012, kaimi.ru + +Совместимость: Windows, Linux + +Возможности: +[+] Создание PE или PE+ файла с нуля +[+] Чтение 32-разрядных и 64-разрядных PE-файлов (PE, PE+) и единообразная работа с ними +[+] Пересборка 32-разрядных и 64-разрядных PE-файлов +[+] Работа с директориями и заголовками +[+] Конвертирование адресов +[+] Чтение и редактирование секций PE-файла +[+] Чтение и редактирование таблицы импортов +[+] Чтение и редактирование таблицы экспортов +[+] Чтение и редактирование таблиц релокаций +[+] Чтение и редактирование ресурсов +[+] Чтение и редактирование TLS +[+] Чтение и редактирование конфигурации образа (image config) +[+] Чтение базовой информации .NET +[+] Чтение и редактирование информации о привязанном импорте +[+] Чтение директории исключений (только PE+) +[+] Чтение отладочной директории с расширенной информацией +[+] Вычисление энтропии +[+] Изменение файлового выравнивания +[+] Изменение базового адреса загрузки +[+] Работа с DOS Stub'ом и Rich overlay +[+] Высокоуровневое чтение ресурсов: картинки, иконки, курсоры, информация о версии, строковые таблицы, таблицы сообщений +[+] Высокоуровневое редактирование ресурсов: картинки, иконки, курсоры, информация о версии + +[English] +Open a free library for working with PE-file PE Bliss. +Free to use, modify, and distribute. +Author: DX +(c) DX 2011-2012, kaimi.ru +Compatibility: Windows, Linux + +### Capabilities: +[+] Creation of PE or PE + file from scratch +[+] Reading the 32-bit and 64-bit PE-file (PE, PE +) and uniform working with them +[+] Rebuild 32-bit and 64-bit PE-files +[+] Working with the directors and titles +[+] Converting addresses +[+] Reading and editing sections of PE-file +[+] Reading and editing the import table +[+] Reading and editing tables exports +[+] Reading and editing tables relocations +[+] Reading and editing resources +[+] Reading and editing TLS +[+] Reading and editing the configuration of the image (image config) +[+] Reading data base .NET +[+] Reading and editing information about tethered import +[+] Read the directory exceptions (only PE +) +[+] Read debug directories with extended information +[+] The calculation of entropy +[+] Changing file alignment +[+] Change the base load address +[+] Support of DOS Stub'om and Rich overlay +[+] High-level reading resources: images, icons, cursors, version information, string tables, message table +[+] High-level editing resources: images, icons, cursors, version information
\ No newline at end of file diff --git a/tools/pe_bliss/SCsub b/tools/pe_bliss/SCsub new file mode 100644 index 0000000000..34524f10ef --- /dev/null +++ b/tools/pe_bliss/SCsub @@ -0,0 +1,5 @@ +Import('env') + +env.add_source_files(env.tool_sources,"*.cpp") + +Export('env') diff --git a/tools/pe_bliss/entropy.cpp b/tools/pe_bliss/entropy.cpp new file mode 100644 index 0000000000..acefa63e83 --- /dev/null +++ b/tools/pe_bliss/entropy.cpp @@ -0,0 +1,111 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <cmath> +#include "entropy.h" +#include "utils.h" + +namespace pe_bliss +{ +//Calculates entropy for PE image section +double entropy_calculator::calculate_entropy(const section& s) +{ + if(s.get_raw_data().empty()) //Don't count entropy for empty sections + throw pe_exception("Section is empty", pe_exception::section_is_empty); + + return calculate_entropy(s.get_raw_data().data(), s.get_raw_data().length()); +} + +//Calculates entropy for istream (from current position of stream) +double entropy_calculator::calculate_entropy(std::istream& file) +{ + uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes + + if(file.bad()) + throw pe_exception("Stream is bad", pe_exception::stream_is_bad); + + std::streamoff pos = file.tellg(); + + std::streamoff length = pe_utils::get_file_size(file); + length -= file.tellg(); + + if(!length) //Don't calculate entropy for empty buffers + throw pe_exception("Data length is zero", pe_exception::data_is_empty); + + //Count bytes + for(std::streamoff i = 0; i != length; ++i) + ++byte_count[static_cast<unsigned char>(file.get())]; + + file.seekg(pos); + + return calculate_entropy(byte_count, length); +} + +//Calculates entropy for data block +double entropy_calculator::calculate_entropy(const char* data, size_t length) +{ + uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes + + if(!length) //Don't calculate entropy for empty buffers + throw pe_exception("Data length is zero", pe_exception::data_is_empty); + + //Count bytes + for(size_t i = 0; i != length; ++i) + ++byte_count[static_cast<unsigned char>(data[i])]; + + return calculate_entropy(byte_count, length); +} + +//Calculates entropy for this PE file (only section data) +double entropy_calculator::calculate_entropy(const pe_base& pe) +{ + uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes + + size_t total_data_length = 0; + + //Count bytes for each section + for(section_list::const_iterator it = pe.get_image_sections().begin(); it != pe.get_image_sections().end(); ++it) + { + const std::string& data = (*it).get_raw_data(); + size_t length = data.length(); + total_data_length += length; + for(size_t i = 0; i != length; ++i) + ++byte_count[static_cast<unsigned char>(data[i])]; + } + + return calculate_entropy(byte_count, total_data_length); +} + +//Calculates entropy from bytes count +double entropy_calculator::calculate_entropy(const uint32_t byte_count[256], std::streamoff total_length) +{ + double entropy = 0.; //Entropy result value + //Calculate entropy + for(uint32_t i = 0; i < 256; ++i) + { + double temp = static_cast<double>(byte_count[i]) / total_length; + if(temp > 0.) + entropy += std::abs(temp * (std::log(temp) * pe_utils::log_2)); + } + + return entropy; +} +} diff --git a/tools/pe_bliss/entropy.h b/tools/pe_bliss/entropy.h new file mode 100644 index 0000000000..7d225a3e32 --- /dev/null +++ b/tools/pe_bliss/entropy.h @@ -0,0 +1,51 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <istream> +#include "pe_base.h" + +namespace pe_bliss +{ +class entropy_calculator +{ +public: + //Calculates entropy for PE image section + static double calculate_entropy(const section& s); + + //Calculates entropy for istream (from current position of stream) + static double calculate_entropy(std::istream& file); + + //Calculates entropy for data block + static double calculate_entropy(const char* data, size_t length); + + //Calculates entropy for this PE file (only section data) + static double calculate_entropy(const pe_base& pe); + +private: + entropy_calculator(); + entropy_calculator(const entropy_calculator&); + entropy_calculator& operator=(const entropy_calculator&); + + //Calculates entropy from bytes count + static double calculate_entropy(const uint32_t byte_count[256], std::streamoff total_length); +}; +} diff --git a/tools/pe_bliss/file_version_info.cpp b/tools/pe_bliss/file_version_info.cpp new file mode 100644 index 0000000000..3f2ba454b4 --- /dev/null +++ b/tools/pe_bliss/file_version_info.cpp @@ -0,0 +1,440 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "file_version_info.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Default constructor +file_version_info::file_version_info() + :file_version_ms_(0), file_version_ls_(0), + product_version_ms_(0), product_version_ls_(0), + file_flags_(0), + file_os_(0), + file_type_(0), file_subtype_(0), + file_date_ms_(0), file_date_ls_(0) +{} + +//Constructor from Windows fixed version info structure +file_version_info::file_version_info(const vs_fixedfileinfo& info) + :file_version_ms_(info.dwFileVersionMS), file_version_ls_(info.dwFileVersionLS), + product_version_ms_(info.dwProductVersionMS), product_version_ls_(info.dwProductVersionLS), + file_flags_(info.dwFileFlags), + file_os_(info.dwFileOS), + file_type_(info.dwFileType), file_subtype_(info.dwFileSubtype), + file_date_ms_(info.dwFileDateMS), file_date_ls_(info.dwFileDateLS) +{} + +//Returns true if file is debug-built +bool file_version_info::is_debug() const +{ + return file_flags_ & vs_ff_debug ? true : false; +} + +//Returns true if file is release-built +bool file_version_info::is_prerelease() const +{ + return file_flags_ & vs_ff_prerelease ? true : false; +} + +//Returns true if file is patched +bool file_version_info::is_patched() const +{ + return file_flags_ & vs_ff_patched ? true : false; +} + +//Returns true if private build +bool file_version_info::is_private_build() const +{ + return file_flags_ & vs_ff_privatebuild ? true : false; +} + +//Returns true if special build +bool file_version_info::is_special_build() const +{ + return file_flags_ & vs_ff_specialbuild ? true : false; +} + +//Returns true if info inferred +bool file_version_info::is_info_inferred() const +{ + return file_flags_ & vs_ff_infoinferred ? true : false; +} + +//Retuens file flags (raw DWORD) +uint32_t file_version_info::get_file_flags() const +{ + return file_flags_; +} + +//Returns file version most significant DWORD +uint32_t file_version_info::get_file_version_ms() const +{ + return file_version_ms_; +} + +//Returns file version least significant DWORD +uint32_t file_version_info::get_file_version_ls() const +{ + return file_version_ls_; +} + +//Returns product version most significant DWORD +uint32_t file_version_info::get_product_version_ms() const +{ + return product_version_ms_; +} + +//Returns product version least significant DWORD +uint32_t file_version_info::get_product_version_ls() const +{ + return product_version_ls_; +} + +//Returns file OS type (raw DWORD) +uint32_t file_version_info::get_file_os_raw() const +{ + return file_os_; +} + +//Returns file OS type +file_version_info::file_os_type file_version_info::get_file_os() const +{ + //Determine file operation system type + switch(file_os_) + { + case vos_dos: + return file_os_dos; + + case vos_os216: + return file_os_os216; + + case vos_os232: + return file_os_os232; + + case vos_nt: + return file_os_nt; + + case vos_wince: + return file_os_wince; + + case vos__windows16: + return file_os_win16; + + case vos__pm16: + return file_os_pm16; + + case vos__pm32: + return file_os_pm32; + + case vos__windows32: + return file_os_win32; + + case vos_dos_windows16: + return file_os_dos_win16; + + case vos_dos_windows32: + return file_os_dos_win32; + + case vos_os216_pm16: + return file_os_os216_pm16; + + case vos_os232_pm32: + return file_os_os232_pm32; + + case vos_nt_windows32: + return file_os_nt_win32; + } + + return file_os_unknown; +} + +//Returns file type (raw DWORD) +uint32_t file_version_info::get_file_type_raw() const +{ + return file_type_; +} + +//Returns file type +file_version_info::file_type file_version_info::get_file_type() const +{ + //Determine file type + switch(file_type_) + { + case vft_app: + return file_type_application; + + case vft_dll: + return file_type_dll; + + case vft_drv: + return file_type_driver; + + case vft_font: + return file_type_font; + + case vft_vxd: + return file_type_vxd; + + case vft_static_lib: + return file_type_static_lib; + } + + return file_type_unknown; +} + +//Returns file subtype (usually non-zero for drivers and fonts) +uint32_t file_version_info::get_file_subtype() const +{ + return file_subtype_; +} + +//Returns file date most significant DWORD +uint32_t file_version_info::get_file_date_ms() const +{ + return file_date_ms_; +} + +//Returns file date least significant DWORD +uint32_t file_version_info::get_file_date_ls() const +{ + return file_date_ls_; +} + +//Helper to set file flag +void file_version_info::set_file_flag(uint32_t flag) +{ + file_flags_ |= flag; +} + +//Helper to clear file flag +void file_version_info::clear_file_flag(uint32_t flag) +{ + file_flags_ &= ~flag; +} + +//Helper to set or clear file flag +void file_version_info::set_file_flag(uint32_t flag, bool set_flag) +{ + set_flag ? set_file_flag(flag) : clear_file_flag(flag); +} + +//Sets if file is debug-built +void file_version_info::set_debug(bool debug) +{ + set_file_flag(vs_ff_debug, debug); +} + +//Sets if file is prerelease +void file_version_info::set_prerelease(bool prerelease) +{ + set_file_flag(vs_ff_prerelease, prerelease); +} + +//Sets if file is patched +void file_version_info::set_patched(bool patched) +{ + set_file_flag(vs_ff_patched, patched); +} + +//Sets if private build +void file_version_info::set_private_build(bool private_build) +{ + set_file_flag(vs_ff_privatebuild, private_build); +} + +//Sets if special build +void file_version_info::set_special_build(bool special_build) +{ + set_file_flag(vs_ff_specialbuild, special_build); +} + +//Sets if info inferred +void file_version_info::set_info_inferred(bool info_inferred) +{ + set_file_flag(vs_ff_infoinferred, info_inferred); +} + +//Sets flags (raw DWORD) +void file_version_info::set_file_flags(uint32_t file_flags) +{ + file_flags_ = file_flags; +} + +//Sets file version most significant DWORD +void file_version_info::set_file_version_ms(uint32_t file_version_ms) +{ + file_version_ms_ = file_version_ms; +} + +//Sets file version least significant DWORD +void file_version_info::set_file_version_ls(uint32_t file_version_ls) +{ + file_version_ls_ = file_version_ls; +} + +//Sets product version most significant DWORD +void file_version_info::set_product_version_ms(uint32_t product_version_ms) +{ + product_version_ms_ = product_version_ms; +} + +//Sets product version least significant DWORD +void file_version_info::set_product_version_ls(uint32_t product_version_ls) +{ + product_version_ls_ = product_version_ls; +} + +//Sets file OS type (raw DWORD) +void file_version_info::set_file_os_raw(uint32_t file_os) +{ + file_os_ = file_os; +} + +//Sets file OS type +void file_version_info::set_file_os(file_os_type file_os) +{ + //Determine file operation system type + switch(file_os) + { + case file_os_dos: + file_os_ = vos_dos; + return; + + case file_os_os216: + file_os_ = vos_os216; + return; + + case file_os_os232: + file_os_ = vos_os232; + return; + + case file_os_nt: + file_os_ = vos_nt; + return; + + case file_os_wince: + file_os_ = vos_wince; + return; + + case file_os_win16: + file_os_ = vos__windows16; + return; + + case file_os_pm16: + file_os_ = vos__pm16; + return; + + case file_os_pm32: + file_os_ = vos__pm32; + return; + + case file_os_win32: + file_os_ = vos__windows32; + return; + + case file_os_dos_win16: + file_os_ = vos_dos_windows16; + return; + + case file_os_dos_win32: + file_os_ = vos_dos_windows32; + return; + + case file_os_os216_pm16: + file_os_ = vos_os216_pm16; + return; + + case file_os_os232_pm32: + file_os_ = vos_os232_pm32; + return; + + case file_os_nt_win32: + file_os_ = vos_nt_windows32; + return; + + default: + return; + } +} + +//Sets file type (raw DWORD) +void file_version_info::set_file_type_raw(uint32_t file_type) +{ + file_type_ = file_type; +} + +//Sets file type +void file_version_info::set_file_type(file_type file_type) +{ + //Determine file type + switch(file_type) + { + case file_type_application: + file_type_ = vft_app; + return; + + case file_type_dll: + file_type_ = vft_dll; + return; + + case file_type_driver: + file_type_ = vft_drv; + return; + + case file_type_font: + file_type_ = vft_font; + return; + + case file_type_vxd: + file_type_ = vft_vxd; + return; + + case file_type_static_lib: + file_type_ = vft_static_lib; + return; + + default: + return; + } +} + +//Sets file subtype (usually non-zero for drivers and fonts) +void file_version_info::set_file_subtype(uint32_t file_subtype) +{ + file_subtype_ = file_subtype; +} + +//Sets file date most significant DWORD +void file_version_info::set_file_date_ms(uint32_t file_date_ms) +{ + file_date_ms_ = file_date_ms; +} + +//Sets file date least significant DWORD +void file_version_info::set_file_date_ls(uint32_t file_date_ls) +{ + file_date_ls_ = file_date_ls; +} +} diff --git a/tools/pe_bliss/file_version_info.h b/tools/pe_bliss/file_version_info.h new file mode 100644 index 0000000000..d898351ba1 --- /dev/null +++ b/tools/pe_bliss/file_version_info.h @@ -0,0 +1,199 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <map> +#include "stdint_defs.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +//Structure representing fixed file version info +class file_version_info +{ +public: + //Enumeration of file operating system types + enum file_os_type + { + file_os_unknown, + file_os_dos, + file_os_os216, + file_os_os232, + file_os_nt, + file_os_wince, + file_os_win16, + file_os_pm16, + file_os_pm32, + file_os_win32, + file_os_dos_win16, + file_os_dos_win32, + file_os_os216_pm16, + file_os_os232_pm32, + file_os_nt_win32 + }; + + //Enumeration of file types + enum file_type + { + file_type_unknown, + file_type_application, + file_type_dll, + file_type_driver, + file_type_font, + file_type_vxd, + file_type_static_lib + }; + +public: + //Default constructor + file_version_info(); + //Constructor from Windows fixed version info structure + explicit file_version_info(const pe_win::vs_fixedfileinfo& info); + +public: //Getters + //Returns true if file is debug-built + bool is_debug() const; + //Returns true if file is prerelease + bool is_prerelease() const; + //Returns true if file is patched + bool is_patched() const; + //Returns true if private build + bool is_private_build() const; + //Returns true if special build + bool is_special_build() const; + //Returns true if info inferred + bool is_info_inferred() const; + //Retuens file flags (raw DWORD) + uint32_t get_file_flags() const; + + //Returns file version most significant DWORD + uint32_t get_file_version_ms() const; + //Returns file version least significant DWORD + uint32_t get_file_version_ls() const; + //Returns product version most significant DWORD + uint32_t get_product_version_ms() const; + //Returns product version least significant DWORD + uint32_t get_product_version_ls() const; + + //Returns file OS type (raw DWORD) + uint32_t get_file_os_raw() const; + //Returns file OS type + file_os_type get_file_os() const; + + //Returns file type (raw DWORD) + uint32_t get_file_type_raw() const; + //Returns file type + file_type get_file_type() const; + + //Returns file subtype (usually non-zero for drivers and fonts) + uint32_t get_file_subtype() const; + + //Returns file date most significant DWORD + uint32_t get_file_date_ms() const; + //Returns file date least significant DWORD + uint32_t get_file_date_ls() const; + + //Returns file version string + template<typename T> + const std::basic_string<T> get_file_version_string() const + { + return get_version_string<T>(file_version_ms_, file_version_ls_); + } + + //Returns product version string + template<typename T> + const std::basic_string<T> get_product_version_string() const + { + return get_version_string<T>(product_version_ms_, product_version_ls_); + } + +public: //Setters + //Sets if file is debug-built + void set_debug(bool debug); + //Sets if file is prerelease + void set_prerelease(bool prerelease); + //Sets if file is patched + void set_patched(bool patched); + //Sets if private build + void set_private_build(bool private_build); + //Sets if special build + void set_special_build(bool special_build); + //Sets if info inferred + void set_info_inferred(bool info_inferred); + //Sets flags (raw DWORD) + void set_file_flags(uint32_t file_flags); + + //Sets file version most significant DWORD + void set_file_version_ms(uint32_t file_version_ms); + //Sets file version least significant DWORD + void set_file_version_ls(uint32_t file_version_ls); + //Sets product version most significant DWORD + void set_product_version_ms(uint32_t product_version_ms); + //Sets product version least significant DWORD + void set_product_version_ls(uint32_t product_version_ls); + + //Sets file OS type (raw DWORD) + void set_file_os_raw(uint32_t file_os); + //Sets file OS type + void set_file_os(file_os_type file_os); + + //Sets file type (raw DWORD) + void set_file_type_raw(uint32_t file_type); + //Sets file type + void set_file_type(file_type file_type); + + //Sets file subtype (usually non-zero for drivers and fonts) + void set_file_subtype(uint32_t file_subtype); + + //Sets file date most significant DWORD + void set_file_date_ms(uint32_t file_date_ms); + //Sets file date least significant DWORD + void set_file_date_ls(uint32_t file_date_ls); + +private: + //Helper to convert version DWORDs to string + template<typename T> + static const std::basic_string<T> get_version_string(uint32_t ms, uint32_t ls) + { + std::basic_stringstream<T> ss; + ss << (ms >> 16) << static_cast<T>(L'.') + << (ms & 0xFFFF) << static_cast<T>(L'.') + << (ls >> 16) << static_cast<T>(L'.') + << (ls & 0xFFFF); + return ss.str(); + } + + //Helper to set file flag + void set_file_flag(uint32_t flag); + //Helper to clear file flag + void clear_file_flag(uint32_t flag); + //Helper to set or clear file flag + void set_file_flag(uint32_t flag, bool set_flag); + + uint32_t file_version_ms_, file_version_ls_, + product_version_ms_, product_version_ls_; + uint32_t file_flags_; + uint32_t file_os_; + uint32_t file_type_, file_subtype_; + uint32_t file_date_ms_, file_date_ls_; +}; +} diff --git a/tools/pe_bliss/message_table.cpp b/tools/pe_bliss/message_table.cpp new file mode 100644 index 0000000000..909be5d494 --- /dev/null +++ b/tools/pe_bliss/message_table.cpp @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "message_table.h" +#include "utils.h" + +namespace pe_bliss +{ +//Default constructor +message_table_item::message_table_item() + :unicode_(false) +{} + +//Constructor from ANSI string +message_table_item::message_table_item(const std::string& str) + :unicode_(false), ansi_str_(str) +{ + pe_utils::strip_nullbytes(ansi_str_); +} + +//Constructor from UNICODE string +message_table_item::message_table_item(const std::wstring& str) + :unicode_(true), unicode_str_(str) +{ + pe_utils::strip_nullbytes(unicode_str_); +} + +//Returns true if contained string is unicode +bool message_table_item::is_unicode() const +{ + return unicode_; +} + +//Returns ANSI string +const std::string& message_table_item::get_ansi_string() const +{ + return ansi_str_; +} + +//Returns UNICODE string +const std::wstring& message_table_item::get_unicode_string() const +{ + return unicode_str_; +} + +//Sets ANSI string (clears UNICODE one) +void message_table_item::set_string(const std::string& str) +{ + ansi_str_ = str; + pe_utils::strip_nullbytes(ansi_str_); + unicode_str_.clear(); + unicode_ = false; +} + +//Sets UNICODE string (clears ANSI one) +void message_table_item::set_string(const std::wstring& str) +{ + unicode_str_ = str; + pe_utils::strip_nullbytes(unicode_str_); + ansi_str_.clear(); + unicode_ = true; +} +} diff --git a/tools/pe_bliss/message_table.h b/tools/pe_bliss/message_table.h new file mode 100644 index 0000000000..5a3feb32c1 --- /dev/null +++ b/tools/pe_bliss/message_table.h @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <map> +#include "stdint_defs.h" + +namespace pe_bliss +{ +//Structure representing message table string +class message_table_item +{ +public: + //Default constructor + message_table_item(); + //Constructors from ANSI and UNICODE strings + explicit message_table_item(const std::string& str); + explicit message_table_item(const std::wstring& str); + + //Returns true if string is UNICODE + bool is_unicode() const; + //Returns ANSI string + const std::string& get_ansi_string() const; + //Returns UNICODE string + const std::wstring& get_unicode_string() const; + +public: + //Sets ANSI or UNICODE string + void set_string(const std::string& str); + void set_string(const std::wstring& str); + +private: + bool unicode_; + std::string ansi_str_; + std::wstring unicode_str_; +}; +} diff --git a/tools/pe_bliss/pe_base.cpp b/tools/pe_bliss/pe_base.cpp new file mode 100644 index 0000000000..97baa17cb3 --- /dev/null +++ b/tools/pe_bliss/pe_base.cpp @@ -0,0 +1,1680 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string> +#include <vector> +#include <istream> +#include <ostream> +#include <algorithm> +#include <cmath> +#include <set> +#include <string.h> +#include "pe_exception.h" +#include "pe_base.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Constructor +pe_base::pe_base(std::istream& file, const pe_properties& props, bool read_debug_raw_data) +{ + props_ = props.duplicate().release(); + + //Save istream state + std::ios_base::iostate state = file.exceptions(); + std::streamoff old_offset = file.tellg(); + + try + { + file.exceptions(std::ios::goodbit); + //Read DOS header, PE headers and section data + read_dos_header(file); + read_pe(file, read_debug_raw_data); + } + catch(const std::exception&) + { + //If something went wrong, restore istream state + file.seekg(old_offset); + file.exceptions(state); + file.clear(); + //Rethrow + throw; + } + + //Restore istream state + file.seekg(old_offset); + file.exceptions(state); + file.clear(); +} + +pe_base::pe_base(const pe_properties& props, uint32_t section_alignment, bool dll, uint16_t subsystem) +{ + props_ = props.duplicate().release(); + props_->create_pe(section_alignment, subsystem); + + has_overlay_ = false; + memset(&dos_header_, 0, sizeof(dos_header_)); + + dos_header_.e_magic = 0x5A4D; //"MZ" + //Magic numbers from MSVC++ build + dos_header_.e_maxalloc = 0xFFFF; + dos_header_.e_cblp = 0x90; + dos_header_.e_cp = 3; + dos_header_.e_cparhdr = 4; + dos_header_.e_sp = 0xB8; + dos_header_.e_lfarlc = 64; + + set_characteristics(image_file_executable_image | image_file_relocs_stripped); + + if(get_pe_type() == pe_type_32) + set_characteristics_flags(image_file_32bit_machine); + + if(dll) + set_characteristics_flags(image_file_dll); + + set_subsystem_version(5, 1); //WinXP + set_os_version(5, 1); //WinXP +} + +pe_base::pe_base(const pe_base& pe) + :dos_header_(pe.dos_header_), + rich_overlay_(pe.rich_overlay_), + sections_(pe.sections_), + has_overlay_(pe.has_overlay_), + full_headers_data_(pe.full_headers_data_), + debug_data_(pe.debug_data_), + props_(0) +{ + props_ = pe.props_->duplicate().release(); +} + +pe_base& pe_base::operator=(const pe_base& pe) +{ + dos_header_ = pe.dos_header_; + rich_overlay_ = pe.rich_overlay_; + sections_ = pe.sections_; + has_overlay_ = pe.has_overlay_; + full_headers_data_ = pe.full_headers_data_; + debug_data_ = pe.debug_data_; + delete props_; + props_ = 0; + props_ = pe.props_->duplicate().release(); + + return *this; +} + +pe_base::~pe_base() +{ + delete props_; +} + +//Returns dos header +const image_dos_header& pe_base::get_dos_header() const +{ + return dos_header_; +} + +//Returns dos header +image_dos_header& pe_base::get_dos_header() +{ + return dos_header_; +} + +//Returns PE headers start position (e_lfanew) +int32_t pe_base::get_pe_header_start() const +{ + return dos_header_.e_lfanew; +} + +//Strips MSVC stub overlay +void pe_base::strip_stub_overlay() +{ + rich_overlay_.clear(); +} + +//Fills MSVC stub overlay with character c +void pe_base::fill_stub_overlay(char c) +{ + if(rich_overlay_.length()) + rich_overlay_.assign(rich_overlay_.length(), c); +} + +//Sets stub MSVS overlay +void pe_base::set_stub_overlay(const std::string& data) +{ + rich_overlay_ = data; +} + +//Returns stub overlay +const std::string& pe_base::get_stub_overlay() const +{ + return rich_overlay_; +} + +//Realigns all sections +void pe_base::realign_all_sections() +{ + for(unsigned int i = 0; i < sections_.size(); i++) + realign_section(i); +} + +//Returns number of sections from PE header +uint16_t pe_base::get_number_of_sections() const +{ + return props_->get_number_of_sections(); +} + +//Updates number of sections in PE header +uint16_t pe_base::update_number_of_sections() +{ + uint16_t new_number = static_cast<uint16_t>(sections_.size()); + props_->set_number_of_sections(new_number); + return new_number; +} + +//Returns section alignment +uint32_t pe_base::get_section_alignment() const +{ + return props_->get_section_alignment(); +} + +//Returns image sections list +section_list& pe_base::get_image_sections() +{ + return sections_; +} + +//Returns image sections list +const section_list& pe_base::get_image_sections() const +{ + return sections_; +} + +//Realigns section by index +void pe_base::realign_section(uint32_t index) +{ + //Check index + if(sections_.size() <= index) + throw pe_exception("Section not found", pe_exception::section_not_found); + + //Get section iterator + section_list::iterator it = sections_.begin() + index; + section& s = *it; + + //Calculate, how many null bytes we have in the end of raw section data + std::size_t strip = 0; + for(std::size_t i = (*it).get_raw_data().length(); i >= 1; --i) + { + if(s.get_raw_data()[i - 1] == 0) + strip++; + else + break; + } + + if(it == sections_.end() - 1) //If we're realigning the last section + { + //We can strip ending null bytes + s.set_size_of_raw_data(static_cast<uint32_t>(s.get_raw_data().length() - strip)); + s.get_raw_data().resize(s.get_raw_data().length() - strip, 0); + } + else + { + //Else just set size of raw data + uint32_t raw_size_aligned = s.get_aligned_raw_size(get_file_alignment()); + s.set_size_of_raw_data(raw_size_aligned); + s.get_raw_data().resize(raw_size_aligned, 0); + } +} + +//Returns file alignment +uint32_t pe_base::get_file_alignment() const +{ + return props_->get_file_alignment(); +} + +//Sets file alignment +void pe_base::set_file_alignment(uint32_t alignment) +{ + //Check alignment + if(alignment < minimum_file_alignment) + throw pe_exception("File alignment can't be less than 512", pe_exception::incorrect_file_alignment); + + if(!pe_utils::is_power_of_2(alignment)) + throw pe_exception("File alignment must be a power of 2", pe_exception::incorrect_file_alignment); + + if(alignment > get_section_alignment()) + throw pe_exception("File alignment must be <= section alignment", pe_exception::incorrect_file_alignment); + + //Set file alignment without any additional checks + set_file_alignment_unchecked(alignment); +} + +//Returns size of image +uint32_t pe_base::get_size_of_image() const +{ + return props_->get_size_of_image(); +} + +//Returns image entry point +uint32_t pe_base::get_ep() const +{ + return props_->get_ep(); +} + +//Sets image entry point (just a value of PE header) +void pe_base::set_ep(uint32_t new_ep) +{ + props_->set_ep(new_ep); +} + +//Returns number of RVA and sizes (number of DATA_DIRECTORY entries) +uint32_t pe_base::get_number_of_rvas_and_sizes() const +{ + return props_->get_number_of_rvas_and_sizes(); +} + +//Sets number of RVA and sizes (number of DATA_DIRECTORY entries) +void pe_base::set_number_of_rvas_and_sizes(uint32_t number) +{ + props_->set_number_of_rvas_and_sizes(number); +} + +//Returns PE characteristics +uint16_t pe_base::get_characteristics() const +{ + return props_->get_characteristics(); +} + +//Sets PE characteristics (a value inside header) +void pe_base::set_characteristics(uint16_t ch) +{ + props_->set_characteristics(ch); +} + +//Returns section from RVA +section& pe_base::section_from_rva(uint32_t rva) +{ + //Search for section + for(section_list::iterator i = sections_.begin(); i != sections_.end(); ++i) + { + section& s = *i; + //Return section if found + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) + return s; + } + + throw pe_exception("No section found by presented address", pe_exception::no_section_found); +} + +//Returns section from RVA +const section& pe_base::section_from_rva(uint32_t rva) const +{ + //Search for section + for(section_list::const_iterator i = sections_.begin(); i != sections_.end(); ++i) + { + const section& s = *i; + //Return section if found + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) + return s; + } + + throw pe_exception("No section found by presented address", pe_exception::no_section_found); +} + +//Returns section from directory ID +section& pe_base::section_from_directory(uint32_t directory_id) +{ + return section_from_rva(get_directory_rva(directory_id)); +} + +//Returns section from directory ID +const section& pe_base::section_from_directory(uint32_t directory_id) const +{ + return section_from_rva(get_directory_rva(directory_id)); +} + +//Sets section virtual size (actual for the last one of this PE or for unbound section) +void pe_base::set_section_virtual_size(section& s, uint32_t vsize) +{ + //Check if we're changing virtual size of the last section + //Of course, we can change virtual size of section that's not bound to this PE file + if(sections_.empty() || std::find_if(sections_.begin(), sections_.end() - 1, section_ptr_finder(s)) != sections_.end() - 1) + throw pe_exception("Can't change virtual size of any section, except last one", pe_exception::error_changing_section_virtual_size); + + //If we're setting virtual size to zero + if(vsize == 0) + { + //Check if section is empty + if(s.empty()) + throw pe_exception("Cannot set virtual size of empty section to zero", pe_exception::error_changing_section_virtual_size); + + //Set virtual size equal to aligned size of raw data + s.set_virtual_size(s.get_size_of_raw_data()); + } + else + { + s.set_virtual_size(vsize); + } + + //Update image size if we're changing virtual size for the last section of this PE + if(!sections_.empty() || &s == &(*(sections_.end() - 1))) + update_image_size(); +} + +//Expands section raw or virtual size to hold data from specified RVA with specified size +//Section must be free (not bound to any image) +//or the last section of this image +bool pe_base::expand_section(section& s, uint32_t needed_rva, uint32_t needed_size, section_expand_type expand) +{ + //Check if we're changing the last section + //Of course, we can change the section that's not bound to this PE file + if(sections_.empty() || std::find_if(sections_.begin(), sections_.end() - 1, section_ptr_finder(s)) != sections_.end() - 1) + throw pe_exception("Can't expand any section, except last one", pe_exception::error_expanding_section); + + //Check if we should expand our section + if(expand == expand_section_raw && section_data_length_from_rva(s, needed_rva, section_data_raw) < needed_size) + { + //Expand section raw data + s.get_raw_data().resize(needed_rva - s.get_virtual_address() + needed_size); + recalculate_section_sizes(s, false); + return true; + } + else if(expand == expand_section_virtual && section_data_length_from_rva(s, needed_rva, section_data_virtual) < needed_size) + { + //Expand section virtual data + set_section_virtual_size(s, needed_rva - s.get_virtual_address() + needed_size); + return true; + } + + return false; +} + +//Updates image virtual size +void pe_base::update_image_size() +{ + //Write virtual size of image to headers + if(!sections_.empty()) + set_size_of_image(sections_.back().get_virtual_address() + sections_.back().get_aligned_virtual_size(get_section_alignment())); + else + set_size_of_image(get_size_of_headers()); +} + +//Returns checksum of PE file from header +uint32_t pe_base::get_checksum() const +{ + return props_->get_checksum(); +} + +//Sets checksum of PE file +void pe_base::set_checksum(uint32_t checksum) +{ + props_->set_checksum(checksum); +} + +//Returns timestamp of PE file from header +uint32_t pe_base::get_time_date_stamp() const +{ + return props_->get_time_date_stamp(); +} + +//Sets timestamp of PE file +void pe_base::set_time_date_stamp(uint32_t timestamp) +{ + props_->set_time_date_stamp(timestamp); +} + +//Returns Machine field value of PE file from header +uint16_t pe_base::get_machine() const +{ + return props_->get_machine(); +} + +//Sets Machine field value of PE file +void pe_base::set_machine(uint16_t machine) +{ + props_->set_machine(machine); +} + +//Prepares section before attaching it +void pe_base::prepare_section(section& s) +{ + //Calculate its size of raw data + s.set_size_of_raw_data(static_cast<uint32_t>(pe_utils::align_up(s.get_raw_data().length(), get_file_alignment()))); + + //Check section virtual and raw size + if(!s.get_size_of_raw_data() && !s.get_virtual_size()) + throw pe_exception("Virtual and Physical sizes of section can't be 0 at the same time", pe_exception::zero_section_sizes); + + //If section virtual size is zero + if(!s.get_virtual_size()) + { + s.set_virtual_size(s.get_size_of_raw_data()); + } + else + { + //Else calculate its virtual size + s.set_virtual_size( + std::max<uint32_t>(pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment()), + pe_utils::align_up(s.get_virtual_size(), get_section_alignment()))); + } +} + +//Adds section to image +section& pe_base::add_section(section s) +{ + if(sections_.size() >= maximum_number_of_sections) + throw pe_exception("Maximum number of sections has been reached", pe_exception::no_more_sections_can_be_added); + + //Prepare section before adding it + prepare_section(s); + + //Calculate section virtual address + if(!sections_.empty()) + { + s.set_virtual_address(pe_utils::align_up(sections_.back().get_virtual_address() + sections_.back().get_aligned_virtual_size(get_section_alignment()), get_section_alignment())); + + //We should align last section raw size, if it wasn't aligned + section& last = sections_.back(); + last.set_size_of_raw_data(static_cast<uint32_t>(pe_utils::align_up(last.get_raw_data().length(), get_file_alignment()))); + } + else + { + s.set_virtual_address( + s.get_virtual_address() == 0 + ? pe_utils::align_up(get_size_of_headers(), get_section_alignment()) + : pe_utils::align_up(s.get_virtual_address(), get_section_alignment())); + } + + //Add section to the end of section list + sections_.push_back(s); + //Set number of sections in PE header + set_number_of_sections(static_cast<uint16_t>(sections_.size())); + //Recalculate virtual size of image + set_size_of_image(get_size_of_image() + s.get_aligned_virtual_size(get_section_alignment())); + //Return last section + return sections_.back(); +} + +//Returns true if sectios "s" is already attached to this PE file +bool pe_base::section_attached(const section& s) const +{ + return sections_.end() != std::find_if(sections_.begin(), sections_.end(), section_ptr_finder(s)); +} + +//Returns true if directory exists +bool pe_base::directory_exists(uint32_t id) const +{ + return props_->directory_exists(id); +} + +//Removes directory +void pe_base::remove_directory(uint32_t id) +{ + props_->remove_directory(id); +} + +//Returns directory RVA +uint32_t pe_base::get_directory_rva(uint32_t id) const +{ + return props_->get_directory_rva(id); +} + +//Returns directory size +uint32_t pe_base::get_directory_size(uint32_t id) const +{ + return props_->get_directory_size(id); +} + +//Sets directory RVA (just a value of PE header, no moving occurs) +void pe_base::set_directory_rva(uint32_t id, uint32_t rva) +{ + return props_->set_directory_rva(id, rva); +} + +//Sets directory size (just a value of PE header, no moving occurs) +void pe_base::set_directory_size(uint32_t id, uint32_t size) +{ + return props_->set_directory_size(id, size); +} + +//Strips only zero DATA_DIRECTORY entries to count = min_count +//Returns resulting number of data directories +//strip_iat_directory - if true, even not empty IAT directory will be stripped +uint32_t pe_base::strip_data_directories(uint32_t min_count, bool strip_iat_directory) +{ + return props_->strip_data_directories(min_count, strip_iat_directory); +} + +//Returns true if image has import directory +bool pe_base::has_imports() const +{ + return directory_exists(image_directory_entry_import); +} + +//Returns true if image has export directory +bool pe_base::has_exports() const +{ + return directory_exists(image_directory_entry_export); +} + +//Returns true if image has resource directory +bool pe_base::has_resources() const +{ + return directory_exists(image_directory_entry_resource); +} + +//Returns true if image has security directory +bool pe_base::has_security() const +{ + return directory_exists(image_directory_entry_security); +} + +//Returns true if image has relocations +bool pe_base::has_reloc() const +{ + return directory_exists(image_directory_entry_basereloc) && !(get_characteristics() & image_file_relocs_stripped); +} + +//Returns true if image has TLS directory +bool pe_base::has_tls() const +{ + return directory_exists(image_directory_entry_tls); +} + +//Returns true if image has config directory +bool pe_base::has_config() const +{ + return directory_exists(image_directory_entry_load_config); +} + +//Returns true if image has bound import directory +bool pe_base::has_bound_import() const +{ + return directory_exists(image_directory_entry_bound_import); +} + +//Returns true if image has delay import directory +bool pe_base::has_delay_import() const +{ + return directory_exists(image_directory_entry_delay_import); +} + +//Returns true if image has COM directory +bool pe_base::is_dotnet() const +{ + return directory_exists(image_directory_entry_com_descriptor); +} + +//Returns true if image has exception directory +bool pe_base::has_exception_directory() const +{ + return directory_exists(image_directory_entry_exception); +} + +//Returns true if image has debug directory +bool pe_base::has_debug() const +{ + return directory_exists(image_directory_entry_debug); +} + +//Returns corresponding section data pointer from RVA inside section "s" (checks bounds) +char* pe_base::section_data_from_rva(section& s, uint32_t rva) +{ + //Check if RVA is inside section "s" + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) + { + if(s.get_raw_data().empty()) + throw pe_exception("Section raw data is empty and cannot be changed", pe_exception::section_is_empty); + + return &s.get_raw_data()[rva - s.get_virtual_address()]; + } + + throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); +} + +//Returns corresponding section data pointer from RVA inside section "s" (checks bounds) +const char* pe_base::section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype) const +{ + //Check if RVA is inside section "s" + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) + return (datatype == section_data_raw ? s.get_raw_data().data() : s.get_virtual_data(get_section_alignment()).c_str()) + rva - s.get_virtual_address(); + + throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); +} + +//Returns section TOTAL RAW/VIRTUAL data length from RVA inside section +uint32_t pe_base::section_data_length_from_rva(uint32_t rva, section_data_type datatype, bool include_headers) const +{ + //if RVA is inside of headers and we're searching them too... + if(include_headers && rva < full_headers_data_.length()) + return static_cast<unsigned long>(full_headers_data_.length()); + + const section& s = section_from_rva(rva); + return static_cast<unsigned long>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment())); +} + +//Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32 +uint32_t pe_base::section_data_length_from_va(uint32_t va, section_data_type datatype, bool include_headers) const +{ + return section_data_length_from_rva(va_to_rva(va), datatype, include_headers); +} + +//Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32/PE64 +uint32_t pe_base::section_data_length_from_va(uint64_t va, section_data_type datatype, bool include_headers) const +{ + return section_data_length_from_rva(va_to_rva(va), datatype, include_headers); +} + +//Returns section remaining RAW/VIRTUAL data length from RVA "rva_inside" to the end of section containing RVA "rva" +uint32_t pe_base::section_data_length_from_rva(uint32_t rva, uint32_t rva_inside, section_data_type datatype, bool include_headers) const +{ + //if RVAs are inside of headers and we're searching them too... + if(include_headers && rva < full_headers_data_.length() && rva_inside < full_headers_data_.length()) + return static_cast<unsigned long>(full_headers_data_.length() - rva_inside); + + const section& s = section_from_rva(rva); + if(rva_inside < s.get_virtual_address()) + throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); + + //Calculate remaining length of section data from "rva" address + long length = static_cast<long>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment())) + + s.get_virtual_address() - rva_inside; + + if(length < 0) + return 0; + + return static_cast<unsigned long>(length); +} + +//Returns section remaining RAW/VIRTUAL data length from VA "va_inside" to the end of section containing VA "va" for PE32 +uint32_t pe_base::section_data_length_from_va(uint32_t va, uint32_t va_inside, section_data_type datatype, bool include_headers) const +{ + return section_data_length_from_rva(va_to_rva(va), va_to_rva(va_inside), datatype, include_headers); +} + +//Returns section remaining RAW/VIRTUAL data length from VA "va_inside" to the end of section containing VA "va" for PE32/PE64 +uint32_t pe_base::section_data_length_from_va(uint64_t va, uint64_t va_inside, section_data_type datatype, bool include_headers) const +{ + return section_data_length_from_rva(va_to_rva(va), va_to_rva(va_inside), datatype, include_headers); +} + +//Returns section remaining RAW/VIRTUAL data length from RVA to the end of section "s" (checks bounds) +uint32_t pe_base::section_data_length_from_rva(const section& s, uint32_t rva_inside, section_data_type datatype) const +{ + //Check rva_inside + if(rva_inside >= s.get_virtual_address() && rva_inside < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) + { + //Calculate remaining length of section data from "rva" address + int32_t length = static_cast<int32_t>(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment())) + + s.get_virtual_address() - rva_inside; + + if(length < 0) + return 0; + + return static_cast<uint32_t>(length); + } + + throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); +} + +//Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32 (checks bounds) +uint32_t pe_base::section_data_length_from_va(const section& s, uint32_t va_inside, section_data_type datatype) const +{ + return section_data_length_from_rva(s, va_to_rva(va_inside), datatype); +} + +//Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32/PE64 (checks bounds) +uint32_t pe_base::section_data_length_from_va(const section& s, uint64_t va_inside, section_data_type datatype) const +{ + return section_data_length_from_rva(s, va_to_rva(va_inside), datatype); +} + +//Returns corresponding section data pointer from RVA inside section +char* pe_base::section_data_from_rva(uint32_t rva, bool include_headers) +{ + //if RVA is inside of headers and we're searching them too... + if(include_headers && rva < full_headers_data_.length()) + return &full_headers_data_[rva]; + + section& s = section_from_rva(rva); + + if(s.get_raw_data().empty()) + throw pe_exception("Section raw data is empty and cannot be changed", pe_exception::section_is_empty); + + return &s.get_raw_data()[rva - s.get_virtual_address()]; +} + +//Returns corresponding section data pointer from RVA inside section +const char* pe_base::section_data_from_rva(uint32_t rva, section_data_type datatype, bool include_headers) const +{ + //if RVA is inside of headers and we're searching them too... + if(include_headers && rva < full_headers_data_.length()) + return &full_headers_data_[rva]; + + const section& s = section_from_rva(rva); + return (datatype == section_data_raw ? s.get_raw_data().data() : s.get_virtual_data(get_section_alignment()).c_str()) + rva - s.get_virtual_address(); +} + +//Reads DOS headers from istream +void pe_base::read_dos_header(std::istream& file, image_dos_header& header) +{ + //Check istream flags + if(file.bad() || file.eof()) + throw pe_exception("PE file stream is bad or closed.", pe_exception::bad_pe_file); + + //Read DOS header and check istream + file.read(reinterpret_cast<char*>(&header), sizeof(image_dos_header)); + if(file.bad() || file.eof()) + throw pe_exception("Unable to read IMAGE_DOS_HEADER", pe_exception::bad_dos_header); + + //Check DOS header magic + if(header.e_magic != 0x5a4d) //"MZ" + throw pe_exception("IMAGE_DOS_HEADER signature is incorrect", pe_exception::bad_dos_header); +} + +//Reads DOS headers from istream +void pe_base::read_dos_header(std::istream& file) +{ + read_dos_header(file, dos_header_); +} + +//Reads PE image from istream +void pe_base::read_pe(std::istream& file, bool read_debug_raw_data) +{ + //Get istream size + std::streamoff filesize = pe_utils::get_file_size(file); + + //Check if PE header is DWORD-aligned + if((dos_header_.e_lfanew % sizeof(uint32_t)) != 0) + throw pe_exception("PE header is not DWORD-aligned", pe_exception::bad_dos_header); + + //Seek to NT headers + file.seekg(dos_header_.e_lfanew); + if(file.bad() || file.fail()) + throw pe_exception("Cannot reach IMAGE_NT_HEADERS", pe_exception::image_nt_headers_not_found); + + //Read NT headers + file.read(get_nt_headers_ptr(), get_sizeof_nt_header() - sizeof(image_data_directory) * image_numberof_directory_entries); + if(file.bad() || file.eof()) + throw pe_exception("Error reading IMAGE_NT_HEADERS", pe_exception::error_reading_image_nt_headers); + + //Check PE signature + if(get_pe_signature() != 0x4550) //"PE" + throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); + + //Check number of directories + if(get_number_of_rvas_and_sizes() > image_numberof_directory_entries) + set_number_of_rvas_and_sizes(image_numberof_directory_entries); + + if(get_number_of_rvas_and_sizes() > 0) + { + //Read data directory headers, if any + file.read(get_nt_headers_ptr() + (get_sizeof_nt_header() - sizeof(image_data_directory) * image_numberof_directory_entries), sizeof(image_data_directory) * get_number_of_rvas_and_sizes()); + if(file.bad() || file.eof()) + throw pe_exception("Error reading DATA_DIRECTORY headers", pe_exception::error_reading_data_directories); + } + + //Check section number + //Images with zero section number accepted + if(get_number_of_sections() > maximum_number_of_sections) + throw pe_exception("Incorrect number of sections", pe_exception::section_number_incorrect); + + //Check PE magic + if(get_magic() != get_needed_magic()) + throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); + + //Check section alignment + if(!pe_utils::is_power_of_2(get_section_alignment())) + throw pe_exception("Incorrect section alignment", pe_exception::incorrect_section_alignment); + + //Check file alignment + if(!pe_utils::is_power_of_2(get_file_alignment())) + throw pe_exception("Incorrect file alignment", pe_exception::incorrect_file_alignment); + + if(get_file_alignment() != get_section_alignment() && (get_file_alignment() < minimum_file_alignment || get_file_alignment() > get_section_alignment())) + throw pe_exception("Incorrect file alignment", pe_exception::incorrect_file_alignment); + + //Check size of image + if(pe_utils::align_up(get_size_of_image(), get_section_alignment()) == 0) + throw pe_exception("Incorrect size of image", pe_exception::incorrect_size_of_image); + + //Read rich data overlay / DOS stub (if any) + if(static_cast<uint32_t>(dos_header_.e_lfanew) > sizeof(image_dos_header)) + { + rich_overlay_.resize(dos_header_.e_lfanew - sizeof(image_dos_header)); + file.seekg(sizeof(image_dos_header)); + file.read(&rich_overlay_[0], dos_header_.e_lfanew - sizeof(image_dos_header)); + if(file.bad() || file.eof()) + throw pe_exception("Error reading 'Rich' & 'DOS stub' overlay", pe_exception::error_reading_overlay); + } + + //Calculate first section raw position + //Sum is safe here + uint32_t first_section = dos_header_.e_lfanew + get_size_of_optional_header() + sizeof(image_file_header) + sizeof(uint32_t) /* Signature */; + + if(get_number_of_sections() > 0) + { + //Go to first section + file.seekg(first_section); + if(file.bad() || file.fail()) + throw pe_exception("Cannot reach section headers", pe_exception::image_section_headers_not_found); + } + + uint32_t last_raw_size = 0; + + //Read all sections + for(int i = 0; i < get_number_of_sections(); i++) + { + section s; + //Read section header + file.read(reinterpret_cast<char*>(&s.get_raw_header()), sizeof(image_section_header)); + if(file.bad() || file.eof()) + throw pe_exception("Error reading section header", pe_exception::error_reading_section_header); + + //Save next section header position + std::streamoff next_sect = file.tellg(); + + //Check section virtual and raw sizes + if(!s.get_size_of_raw_data() && !s.get_virtual_size()) + throw pe_exception("Virtual and Physical sizes of section can't be 0 at the same time", pe_exception::zero_section_sizes); + + //Check for adequate values of section fields + if(!pe_utils::is_sum_safe(s.get_virtual_address(), s.get_virtual_size()) || s.get_virtual_size() > pe_utils::two_gb + || !pe_utils::is_sum_safe(s.get_pointer_to_raw_data(), s.get_size_of_raw_data()) || s.get_size_of_raw_data() > pe_utils::two_gb) + throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size); + + if(s.get_size_of_raw_data() != 0) + { + //If section has raw data + + //If section raw data size is greater than virtual, fix it + last_raw_size = s.get_size_of_raw_data(); + if(pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment()) > pe_utils::align_up(s.get_virtual_size(), get_section_alignment())) + s.set_size_of_raw_data(s.get_virtual_size()); + + //Check virtual and raw section sizes and addresses + if(s.get_virtual_address() + pe_utils::align_up(s.get_virtual_size(), get_section_alignment()) > pe_utils::align_up(get_size_of_image(), get_section_alignment()) + || + pe_utils::align_down(s.get_pointer_to_raw_data(), get_file_alignment()) + s.get_size_of_raw_data() > static_cast<uint32_t>(filesize)) + throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size); + + //Seek to section raw data + file.seekg(pe_utils::align_down(s.get_pointer_to_raw_data(), get_file_alignment())); + if(file.bad() || file.fail()) + throw pe_exception("Cannot reach section data", pe_exception::image_section_data_not_found); + + //Read section raw data + s.get_raw_data().resize(s.get_size_of_raw_data()); + file.read(&s.get_raw_data()[0], s.get_size_of_raw_data()); + if(file.bad() || file.fail()) + throw pe_exception("Error reading section data", pe_exception::image_section_data_not_found); + } + + //Check virtual address and size of section + if(s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()) > pe_utils::align_up(get_size_of_image(), get_section_alignment())) + throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size); + + //Save section + sections_.push_back(s); + + //Seek to the next section header + file.seekg(next_sect); + } + + //Check size of headers: SizeOfHeaders can't be larger than first section VA + if(!sections_.empty() && get_size_of_headers() > sections_.front().get_virtual_address()) + throw pe_exception("Incorrect size of headers", pe_exception::incorrect_size_of_headers); + + //If image has more than two sections + if(sections_.size() >= 2) + { + //Check sections virtual sizes + for(section_list::const_iterator i = sections_.begin() + 1; i != sections_.end(); ++i) + { + if((*i).get_virtual_address() != (*(i - 1)).get_virtual_address() + (*(i - 1)).get_aligned_virtual_size(get_section_alignment())) + throw pe_exception("Section table is incorrect", pe_exception::image_section_table_incorrect); + } + } + + //Check if image has overlay in the end of file + has_overlay_ = !sections_.empty() && filesize > static_cast<std::streamoff>(sections_.back().get_pointer_to_raw_data() + last_raw_size); + + { + //Additionally, read data from the beginning of istream to size of headers + file.seekg(0); + uint32_t size_of_headers = std::min<uint32_t>(get_size_of_headers(), static_cast<uint32_t>(filesize)); + + if(!sections_.empty()) + { + for(section_list::const_iterator i = sections_.begin(); i != sections_.end(); ++i) + { + if(!(*i).empty()) + { + size_of_headers = std::min<uint32_t>(get_size_of_headers(), (*i).get_pointer_to_raw_data()); + break; + } + } + } + + full_headers_data_.resize(size_of_headers); + file.read(&full_headers_data_[0], size_of_headers); + if(file.bad() || file.eof()) + throw pe_exception("Error reading file", pe_exception::error_reading_file); + } + + //Moreover, if there's debug directory, read its raw data for some debug info types + while(read_debug_raw_data && has_debug()) + { + try + { + //Check the length in bytes of the section containing debug directory + if(section_data_length_from_rva(get_directory_rva(image_directory_entry_debug), get_directory_rva(image_directory_entry_debug), section_data_virtual, true) < sizeof(image_debug_directory)) + break; + + unsigned long current_pos = get_directory_rva(image_directory_entry_debug); + + //First IMAGE_DEBUG_DIRECTORY table + image_debug_directory directory = section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); + + //Iterate over all IMAGE_DEBUG_DIRECTORY directories + while(directory.PointerToRawData + && current_pos < get_directory_rva(image_directory_entry_debug) + get_directory_size(image_directory_entry_debug)) + { + //If we have something to read + if((directory.Type == image_debug_type_codeview + || directory.Type == image_debug_type_misc + || directory.Type == image_debug_type_coff) + && directory.SizeOfData) + { + std::string data; + data.resize(directory.SizeOfData); + file.seekg(directory.PointerToRawData); + file.read(&data[0], directory.SizeOfData); + if(file.bad() || file.eof()) + throw pe_exception("Error reading file", pe_exception::error_reading_file); + + debug_data_.insert(std::make_pair(directory.PointerToRawData, data)); + } + + //Go to next debug entry + current_pos += sizeof(image_debug_directory); + directory = section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); + } + + break; + } + catch(const pe_exception&) + { + //Don't throw any exception here, if debug info is corrupted or incorrect + break; + } + catch(const std::bad_alloc&) + { + //Don't throw any exception here, if debug info is corrupted or incorrect + break; + } + } +} + +//Returns PE type of this image +pe_type pe_base::get_pe_type() const +{ + return props_->get_pe_type(); +} + +//Returns PE type (PE or PE+) from pe_type enumeration (minimal correctness checks) +pe_type pe_base::get_pe_type(std::istream& file) +{ + //Save state of the istream + std::ios_base::iostate state = file.exceptions(); + std::streamoff old_offset = file.tellg(); + image_nt_headers32 nt_headers; + image_dos_header header; + + try + { + //Read dos header + file.exceptions(std::ios::goodbit); + read_dos_header(file, header); + + //Seek to the NT headers start + file.seekg(header.e_lfanew); + if(file.bad() || file.fail()) + throw pe_exception("Cannot reach IMAGE_NT_HEADERS", pe_exception::image_nt_headers_not_found); + + //Read NT headers (we're using 32-bit version, because there's no significant differencies between 32 and 64 bit version structures) + file.read(reinterpret_cast<char*>(&nt_headers), sizeof(image_nt_headers32) - sizeof(image_data_directory) * image_numberof_directory_entries); + if(file.bad() || file.eof()) + throw pe_exception("Error reading IMAGE_NT_HEADERS", pe_exception::error_reading_image_nt_headers); + + //Check NT headers signature + if(nt_headers.Signature != 0x4550) //"PE" + throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); + + //Check NT headers magic + if(nt_headers.OptionalHeader.Magic != image_nt_optional_hdr32_magic && nt_headers.OptionalHeader.Magic != image_nt_optional_hdr64_magic) + throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); + } + catch(const std::exception&) + { + //If something went wrong, restore istream state + file.exceptions(state); + file.seekg(old_offset); + file.clear(); + //Retrhow exception + throw; + } + + //Restore stream state + file.exceptions(state); + file.seekg(old_offset); + file.clear(); + + //Determine PE type and return it + return nt_headers.OptionalHeader.Magic == image_nt_optional_hdr64_magic ? pe_type_64 : pe_type_32; +} + +//Returns true if image has overlay data at the end of file +bool pe_base::has_overlay() const +{ + return has_overlay_; +} + +//Clears PE characteristics flag +void pe_base::clear_characteristics_flags(uint16_t flags) +{ + set_characteristics(get_characteristics() & ~flags); +} + +//Sets PE characteristics flag +void pe_base::set_characteristics_flags(uint16_t flags) +{ + set_characteristics(get_characteristics() | flags); +} + +//Returns true if PE characteristics flag set +bool pe_base::check_characteristics_flag(uint16_t flag) const +{ + return (get_characteristics() & flag) ? true : false; +} + +//Returns subsystem value +uint16_t pe_base::get_subsystem() const +{ + return props_->get_subsystem(); +} + +//Sets subsystem value +void pe_base::set_subsystem(uint16_t subsystem) +{ + props_->set_subsystem(subsystem); +} + +//Returns true if image has console subsystem +bool pe_base::is_console() const +{ + return get_subsystem() == image_subsystem_windows_cui; +} + +//Returns true if image has Windows GUI subsystem +bool pe_base::is_gui() const +{ + return get_subsystem() == image_subsystem_windows_gui; +} + +//Sets required operation system version +void pe_base::set_os_version(uint16_t major, uint16_t minor) +{ + props_->set_os_version(major, minor); +} + +//Returns required operation system version (minor word) +uint16_t pe_base::get_minor_os_version() const +{ + return props_->get_minor_os_version(); +} + +//Returns required operation system version (major word) +uint16_t pe_base::get_major_os_version() const +{ + return props_->get_major_os_version(); +} + +//Sets required subsystem version +void pe_base::set_subsystem_version(uint16_t major, uint16_t minor) +{ + props_->set_subsystem_version(major, minor); +} + +//Returns required subsystem version (minor word) +uint16_t pe_base::get_minor_subsystem_version() const +{ + return props_->get_minor_subsystem_version(); +} + +//Returns required subsystem version (major word) +uint16_t pe_base::get_major_subsystem_version() const +{ + return props_->get_major_subsystem_version(); +} + +//Returns corresponding section data pointer from VA inside section "s" for PE32 (checks bounds) +char* pe_base::section_data_from_va(section& s, uint32_t va) //Always returns raw data +{ + return section_data_from_rva(s, va_to_rva(va)); +} + +//Returns corresponding section data pointer from VA inside section "s" for PE32 (checks bounds) +const char* pe_base::section_data_from_va(const section& s, uint32_t va, section_data_type datatype) const +{ + return section_data_from_rva(s, va_to_rva(va), datatype); +} + +//Returns corresponding section data pointer from VA inside section for PE32 +char* pe_base::section_data_from_va(uint32_t va, bool include_headers) //Always returns raw data +{ + return section_data_from_rva(va_to_rva(va), include_headers); +} + +//Returns corresponding section data pointer from VA inside section for PE32 +const char* pe_base::section_data_from_va(uint32_t va, section_data_type datatype, bool include_headers) const +{ + return section_data_from_rva(va_to_rva(va), datatype, include_headers); +} + +//Returns corresponding section data pointer from VA inside section "s" for PE32/PE64 (checks bounds) +char* pe_base::section_data_from_va(section& s, uint64_t va) //Always returns raw data +{ + return section_data_from_rva(s, va_to_rva(va)); +} + +//Returns corresponding section data pointer from VA inside section "s" for PE32/PE64 (checks bounds) +const char* pe_base::section_data_from_va(const section& s, uint64_t va, section_data_type datatype) const +{ + return section_data_from_rva(s, va_to_rva(va), datatype); +} + +//Returns corresponding section data pointer from VA inside section for PE32/PE64 +char* pe_base::section_data_from_va(uint64_t va, bool include_headers) //Always returns raw data +{ + return section_data_from_rva(va_to_rva(va), include_headers); +} + +//Returns corresponding section data pointer from VA inside section for PE32/PE64 +const char* pe_base::section_data_from_va(uint64_t va, section_data_type datatype, bool include_headers) const +{ + return section_data_from_rva(va_to_rva(va), datatype, include_headers); +} + +//Returns section from VA inside it for PE32 +section& pe_base::section_from_va(uint32_t va) +{ + return section_from_rva(va_to_rva(va)); +} + +//Returns section from VA inside it for PE32/PE64 +section& pe_base::section_from_va(uint64_t va) +{ + return section_from_rva(va_to_rva(va)); +} + +//Returns section from RVA inside it for PE32 +const section& pe_base::section_from_va(uint32_t va) const +{ + return section_from_rva(va_to_rva(va)); +} + +//Returns section from RVA inside it for PE32/PE64 +const section& pe_base::section_from_va(uint64_t va) const +{ + return section_from_rva(va_to_rva(va)); +} + +uint32_t pe_base::va_to_rva(uint32_t va, bool bound_check) const +{ + return props_->va_to_rva(va, bound_check); +} + +uint32_t pe_base::va_to_rva(uint64_t va, bool bound_check) const +{ + return props_->va_to_rva(va, bound_check); +} + +uint32_t pe_base::rva_to_va_32(uint32_t rva) const +{ + return props_->rva_to_va_32(rva); +} + +uint64_t pe_base::rva_to_va_64(uint32_t rva) const +{ + return props_->rva_to_va_64(rva); +} + +//Relative Virtual Address (RVA) to Virtual Address (VA) convertion for PE32 +void pe_base::rva_to_va(uint32_t rva, uint32_t& va) const +{ + va = rva_to_va_32(rva); +} + +//Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32/PE64 +void pe_base::rva_to_va(uint32_t rva, uint64_t& va) const +{ + va = rva_to_va_64(rva); +} + +//Returns section from file offset (4gb max) +section& pe_base::section_from_file_offset(uint32_t offset) +{ + return *file_offset_to_section(offset); +} + +//Returns section from file offset (4gb max) +const section& pe_base::section_from_file_offset(uint32_t offset) const +{ + return *file_offset_to_section(offset); +} + +//Returns section and offset (raw data only) from its start from RVA +const std::pair<uint32_t, const section*> pe_base::section_and_offset_from_rva(uint32_t rva) const +{ + const section& s = section_from_rva(rva); + return std::make_pair(rva - s.get_virtual_address(), &s); +} + +//Returns DLL Characteristics +uint16_t pe_base::get_dll_characteristics() const +{ + return props_->get_dll_characteristics(); +} + +//Sets DLL Characteristics +void pe_base::set_dll_characteristics(uint16_t characteristics) +{ + props_->set_dll_characteristics(characteristics); +} + +//Returns size of headers +uint32_t pe_base::get_size_of_headers() const +{ + return props_->get_size_of_headers(); +} + +//Returns size of optional header +uint16_t pe_base::get_size_of_optional_header() const +{ + return props_->get_size_of_optional_header(); +} + +//Returns PE signature +uint32_t pe_base::get_pe_signature() const +{ + return props_->get_pe_signature(); +} + +//Returns magic value +uint32_t pe_base::get_magic() const +{ + return props_->get_magic(); +} + +//Returns image base for PE32 +void pe_base::get_image_base(uint32_t& base) const +{ + base = get_image_base_32(); +} + +//Returns image base for PE32 and PE64 respectively +uint32_t pe_base::get_image_base_32() const +{ + return props_->get_image_base_32(); +} + +//Sets image base for PE32 and PE64 respectively +uint64_t pe_base::get_image_base_64() const +{ + return props_->get_image_base_64(); +} + +//RVA to RAW file offset convertion (4gb max) +uint32_t pe_base::rva_to_file_offset(uint32_t rva) const +{ + //Maybe, RVA is inside PE headers + if(rva < get_size_of_headers()) + return rva; + + const section& s = section_from_rva(rva); + return s.get_pointer_to_raw_data() + rva - s.get_virtual_address(); +} + +//RAW file offset to RVA convertion (4gb max) +uint32_t pe_base::file_offset_to_rva(uint32_t offset) const +{ + //Maybe, offset is inside PE headers + if(offset < get_size_of_headers()) + return offset; + + const section_list::const_iterator it = file_offset_to_section(offset); + return offset - (*it).get_pointer_to_raw_data() + (*it).get_virtual_address(); +} + +//RAW file offset to section convertion helper (4gb max) +section_list::const_iterator pe_base::file_offset_to_section(uint32_t offset) const +{ + section_list::const_iterator it = std::find_if(sections_.begin(), sections_.end(), section_by_raw_offset(offset)); + if(it == sections_.end()) + throw pe_exception("No section found by presented file offset", pe_exception::no_section_found); + + return it; +} + +//RAW file offset to section convertion helper (4gb max) +section_list::iterator pe_base::file_offset_to_section(uint32_t offset) +{ + section_list::iterator it = std::find_if(sections_.begin(), sections_.end(), section_by_raw_offset(offset)); + if(it == sections_.end()) + throw pe_exception("No section found by presented file offset", pe_exception::no_section_found); + + return it; +} + +//RVA from section raw data offset +uint32_t pe_base::rva_from_section_offset(const section& s, uint32_t raw_offset_from_section_start) +{ + return s.get_virtual_address() + raw_offset_from_section_start; +} + +//Returns image base for PE32/PE64 +void pe_base::get_image_base(uint64_t& base) const +{ + base = get_image_base_64(); +} + +//Sets new image base +void pe_base::set_image_base(uint32_t base) +{ + props_->set_image_base(base); +} + +void pe_base::set_image_base_64(uint64_t base) +{ + props_->set_image_base_64(base); +} + +//Sets heap size commit for PE32 and PE64 respectively +void pe_base::set_heap_size_commit(uint32_t size) +{ + props_->set_heap_size_commit(size); +} + +void pe_base::set_heap_size_commit(uint64_t size) +{ + props_->set_heap_size_commit(size); +} + +//Sets heap size reserve for PE32 and PE64 respectively +void pe_base::set_heap_size_reserve(uint32_t size) +{ + props_->set_heap_size_reserve(size); +} + +void pe_base::set_heap_size_reserve(uint64_t size) +{ + props_->set_heap_size_reserve(size); +} + +//Sets stack size commit for PE32 and PE64 respectively +void pe_base::set_stack_size_commit(uint32_t size) +{ + props_->set_stack_size_commit(size); +} + +void pe_base::set_stack_size_commit(uint64_t size) +{ + props_->set_stack_size_commit(size); +} + +//Sets stack size reserve for PE32 and PE64 respectively +void pe_base::set_stack_size_reserve(uint32_t size) +{ + props_->set_stack_size_reserve(size); +} + +void pe_base::set_stack_size_reserve(uint64_t size) +{ + props_->set_stack_size_reserve(size); +} + +//Returns heap size commit for PE32 and PE64 respectively +uint32_t pe_base::get_heap_size_commit_32() const +{ + return props_->get_heap_size_commit_32(); +} + +uint64_t pe_base::get_heap_size_commit_64() const +{ + return props_->get_heap_size_commit_64(); +} + +//Returns heap size reserve for PE32 and PE64 respectively +uint32_t pe_base::get_heap_size_reserve_32() const +{ + return props_->get_heap_size_reserve_32(); +} + +uint64_t pe_base::get_heap_size_reserve_64() const +{ + return props_->get_heap_size_reserve_64(); +} + +//Returns stack size commit for PE32 and PE64 respectively +uint32_t pe_base::get_stack_size_commit_32() const +{ + return props_->get_stack_size_commit_32(); +} + +uint64_t pe_base::get_stack_size_commit_64() const +{ + return props_->get_stack_size_commit_64(); +} + +//Returns stack size reserve for PE32 and PE64 respectively +uint32_t pe_base::get_stack_size_reserve_32() const +{ + return props_->get_stack_size_reserve_32(); +} + +uint64_t pe_base::get_stack_size_reserve_64() const +{ + return props_->get_stack_size_reserve_64(); +} + +//Returns heap size commit for PE32 +void pe_base::get_heap_size_commit(uint32_t& size) const +{ + size = get_heap_size_commit_32(); +} + +//Returns heap size commit for PE32/PE64 +void pe_base::get_heap_size_commit(uint64_t& size) const +{ + size = get_heap_size_commit_64(); +} + +//Returns heap size reserve for PE32 +void pe_base::get_heap_size_reserve(uint32_t& size) const +{ + size = get_heap_size_reserve_32(); +} + +//Returns heap size reserve for PE32/PE64 +void pe_base::get_heap_size_reserve(uint64_t& size) const +{ + size = get_heap_size_reserve_64(); +} + +//Returns stack size commit for PE32 +void pe_base::get_stack_size_commit(uint32_t& size) const +{ + size = get_stack_size_commit_32(); +} + +//Returns stack size commit for PE32/PE64 +void pe_base::get_stack_size_commit(uint64_t& size) const +{ + size = get_stack_size_commit_64(); +} + +//Returns stack size reserve for PE32 +void pe_base::get_stack_size_reserve(uint32_t& size) const +{ + size = get_stack_size_reserve_32(); +} + +//Returns stack size reserve for PE32/PE64 +void pe_base::get_stack_size_reserve(uint64_t& size) const +{ + size = get_stack_size_reserve_64(); +} + +//Realigns file (changes file alignment) +void pe_base::realign_file(uint32_t new_file_alignment) +{ + //Checks alignment for correctness + set_file_alignment(new_file_alignment); + realign_all_sections(); +} + +//Helper function to recalculate RAW and virtual section sizes and strip it, if necessary +void pe_base::recalculate_section_sizes(section& s, bool auto_strip) +{ + prepare_section(s); //Recalculate section raw addresses + + //Strip RAW size of section, if it is the last one + //For all others it must be file-aligned and calculated by prepare_section() call + if(auto_strip && !(sections_.empty() || &s == &*(sections_.end() - 1))) + { + //Strip ending raw data nullbytes to optimize size + std::string& raw_data = s.get_raw_data(); + if(!raw_data.empty()) + { + std::string::size_type i = raw_data.length(); + for(; i != 1; --i) + { + if(raw_data[i - 1] != 0) + break; + } + + raw_data.resize(i); + } + + s.set_size_of_raw_data(static_cast<uint32_t>(raw_data.length())); + } + + //Can occur only for last section + if(pe_utils::align_up(s.get_virtual_size(), get_section_alignment()) < pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment())) + set_section_virtual_size(s, pe_utils::align_up(s.get_size_of_raw_data(), get_section_alignment())); //Recalculate section virtual size +} + +//Returns data from the beginning of image +//Size = SizeOfHeaders +const std::string& pe_base::get_full_headers_data() const +{ + return full_headers_data_; +} + +const pe_base::debug_data_list& pe_base::get_raw_debug_data_list() const +{ + return debug_data_; +} + +//Sets number of sections +void pe_base::set_number_of_sections(uint16_t number) +{ + props_->set_number_of_sections(number); +} + +//Sets size of image +void pe_base::set_size_of_image(uint32_t size) +{ + props_->set_size_of_image(size); +} + +//Sets size of headers +void pe_base::set_size_of_headers(uint32_t size) +{ + props_->set_size_of_headers(size); +} + +//Sets size of optional headers +void pe_base::set_size_of_optional_header(uint16_t size) +{ + props_->set_size_of_optional_header(size); +} + +//Returns nt headers data pointer +char* pe_base::get_nt_headers_ptr() +{ + return props_->get_nt_headers_ptr(); +} + +//Returns nt headers data pointer +const char* pe_base::get_nt_headers_ptr() const +{ + return props_->get_nt_headers_ptr(); +} + +//Returns sizeof() nt headers +uint32_t pe_base::get_sizeof_nt_header() const +{ + return props_->get_sizeof_nt_header(); +} + +//Returns sizeof() optional headers +uint32_t pe_base::get_sizeof_opt_headers() const +{ + return props_->get_sizeof_opt_headers(); +} + +//Sets file alignment (no checks) +void pe_base::set_file_alignment_unchecked(uint32_t alignment) +{ + props_->set_file_alignment_unchecked(alignment); +} + +//Sets base of code +void pe_base::set_base_of_code(uint32_t base) +{ + props_->set_base_of_code(base); +} + +//Returns base of code +uint32_t pe_base::get_base_of_code() const +{ + return props_->get_base_of_code(); +} + +//Returns needed magic of image +uint32_t pe_base::get_needed_magic() const +{ + return props_->get_needed_magic(); +} +} diff --git a/tools/pe_bliss/pe_base.h b/tools/pe_bliss/pe_base.h new file mode 100644 index 0000000000..b5416cf1e2 --- /dev/null +++ b/tools/pe_bliss/pe_base.h @@ -0,0 +1,544 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <vector> +#include <istream> +#include <ostream> +#include <map> +#include "pe_exception.h" +#include "pe_structures.h" +#include "utils.h" +#include "pe_section.h" +#include "pe_properties.h" + +//Please don't remove this information from header +//PEBliss 1.0.0 +//(c) DX 2011 - 2012, http://kaimi.ru +//Free to use for commertial and non-commertial purposes, modification and distribution + +// == more important == +//TODO: compact import rebuilder +//TODO: remove sections in the middle +//== less important == +//TODO: relocations that take more than one element (seems to be not possible in Windows PE, but anyway) +//TODO: delay import directory +//TODO: write message tables +//TODO: write string tables +//TODO: read security information +//TODO: read full .NET information + +namespace pe_bliss +{ +//Portable executable class +class pe_base +{ +public: //CONSTRUCTORS + //Constructor from stream + pe_base(std::istream& file, const pe_properties& props, bool read_debug_raw_data = true); + + //Constructor of empty PE-file + explicit pe_base(const pe_properties& props, uint32_t section_alignment = 0x1000, bool dll = false, uint16_t subsystem = pe_win::image_subsystem_windows_gui); + + pe_base(const pe_base& pe); + pe_base& operator=(const pe_base& pe); + +public: + ~pe_base(); + +public: //STUB + //Strips stub MSVS overlay, if any + void strip_stub_overlay(); + //Fills stub MSVS overlay with specified byte + void fill_stub_overlay(char c); + //Sets stub MSVS overlay + void set_stub_overlay(const std::string& data); + //Returns stub overlay contents + const std::string& get_stub_overlay() const; + + +public: //DIRECTORIES + //Returns true if directory exists + bool directory_exists(uint32_t id) const; + //Removes directory + void remove_directory(uint32_t id); + + //Returns directory RVA + uint32_t get_directory_rva(uint32_t id) const; + //Returns directory size + uint32_t get_directory_size(uint32_t id) const; + + //Sets directory RVA (just a value of PE header, no moving occurs) + void set_directory_rva(uint32_t id, uint32_t rva); + //Sets directory size (just a value of PE header, no moving occurs) + void set_directory_size(uint32_t id, uint32_t size); + + //Strips only zero DATA_DIRECTORY entries to count = min_count + //Returns resulting number of data directories + //strip_iat_directory - if true, even not empty IAT directory will be stripped + uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true); + + //Returns true if image has import directory + bool has_imports() const; + //Returns true if image has export directory + bool has_exports() const; + //Returns true if image has resource directory + bool has_resources() const; + //Returns true if image has security directory + bool has_security() const; + //Returns true if image has relocations + bool has_reloc() const; + //Returns true if image has TLS directory + bool has_tls() const; + //Returns true if image has config directory + bool has_config() const; + //Returns true if image has bound import directory + bool has_bound_import() const; + //Returns true if image has delay import directory + bool has_delay_import() const; + //Returns true if image has COM directory + bool is_dotnet() const; + //Returns true if image has exception directory + bool has_exception_directory() const; + //Returns true if image has debug directory + bool has_debug() const; + + //Returns subsystem value + uint16_t get_subsystem() const; + //Sets subsystem value + void set_subsystem(uint16_t subsystem); + //Returns true if image has console subsystem + bool is_console() const; + //Returns true if image has Windows GUI subsystem + bool is_gui() const; + + //Sets required operation system version + void set_os_version(uint16_t major, uint16_t minor); + //Returns required operation system version (minor word) + uint16_t get_minor_os_version() const; + //Returns required operation system version (major word) + uint16_t get_major_os_version() const; + + //Sets required subsystem version + void set_subsystem_version(uint16_t major, uint16_t minor); + //Returns required subsystem version (minor word) + uint16_t get_minor_subsystem_version() const; + //Returns required subsystem version (major word) + uint16_t get_major_subsystem_version() const; + +public: //PE HEADER + //Returns DOS header + const pe_win::image_dos_header& get_dos_header() const; + pe_win::image_dos_header& get_dos_header(); + + //Returns PE header start (e_lfanew) + int32_t get_pe_header_start() const; + + //Returns file alignment + uint32_t get_file_alignment() const; + //Sets file alignment, checking the correctness of its value + void set_file_alignment(uint32_t alignment); + + //Returns size of image + uint32_t get_size_of_image() const; + + //Returns image entry point + uint32_t get_ep() const; + //Sets image entry point (just a value of PE header) + void set_ep(uint32_t new_ep); + + //Returns number of RVA and sizes (number of DATA_DIRECTORY entries) + uint32_t get_number_of_rvas_and_sizes() const; + //Sets number of RVA and sizes (number of DATA_DIRECTORY entries) + void set_number_of_rvas_and_sizes(uint32_t number); + + //Returns PE characteristics + uint16_t get_characteristics() const; + //Sets PE characteristics (a value inside header) + void set_characteristics(uint16_t ch); + //Clears PE characteristics flag + void clear_characteristics_flags(uint16_t flags); + //Sets PE characteristics flag + void set_characteristics_flags(uint16_t flags); + //Returns true if PE characteristics flag set + bool check_characteristics_flag(uint16_t flag) const; + + //Returns DLL Characteristics + uint16_t get_dll_characteristics() const; + //Sets DLL Characteristics + void set_dll_characteristics(uint16_t characteristics); + + //Returns size of headers + uint32_t get_size_of_headers() const; + //Returns size of optional header + uint16_t get_size_of_optional_header() const; + + //Returns PE signature + uint32_t get_pe_signature() const; + + //Returns magic value + uint32_t get_magic() const; + + //Returns image base for PE32 and PE64 respectively + uint32_t get_image_base_32() const; + void get_image_base(uint32_t& base) const; + //Sets image base for PE32 and PE64 respectively + uint64_t get_image_base_64() const; + void get_image_base(uint64_t& base) const; + + //Sets new image base + void set_image_base(uint32_t base); + void set_image_base_64(uint64_t base); + + //Sets heap size commit for PE32 and PE64 respectively + void set_heap_size_commit(uint32_t size); + void set_heap_size_commit(uint64_t size); + //Sets heap size reserve for PE32 and PE64 respectively + void set_heap_size_reserve(uint32_t size); + void set_heap_size_reserve(uint64_t size); + //Sets stack size commit for PE32 and PE64 respectively + void set_stack_size_commit(uint32_t size); + void set_stack_size_commit(uint64_t size); + //Sets stack size reserve for PE32 and PE64 respectively + void set_stack_size_reserve(uint32_t size); + void set_stack_size_reserve(uint64_t size); + + //Returns heap size commit for PE32 and PE64 respectively + uint32_t get_heap_size_commit_32() const; + void get_heap_size_commit(uint32_t& size) const; + uint64_t get_heap_size_commit_64() const; + void get_heap_size_commit(uint64_t& size) const; + //Returns heap size reserve for PE32 and PE64 respectively + uint32_t get_heap_size_reserve_32() const; + void get_heap_size_reserve(uint32_t& size) const; + uint64_t get_heap_size_reserve_64() const; + void get_heap_size_reserve(uint64_t& size) const; + //Returns stack size commit for PE32 and PE64 respectively + uint32_t get_stack_size_commit_32() const; + void get_stack_size_commit(uint32_t& size) const; + uint64_t get_stack_size_commit_64() const; + void get_stack_size_commit(uint64_t& size) const; + //Returns stack size reserve for PE32 and PE64 respectively + uint32_t get_stack_size_reserve_32() const; + void get_stack_size_reserve(uint32_t& size) const; + uint64_t get_stack_size_reserve_64() const; + void get_stack_size_reserve(uint64_t& size) const; + + //Updates virtual size of image corresponding to section virtual sizes + void update_image_size(); + + //Returns checksum of PE file from header + uint32_t get_checksum() const; + //Sets checksum of PE file + void set_checksum(uint32_t checksum); + + //Returns timestamp of PE file from header + uint32_t get_time_date_stamp() const; + //Sets timestamp of PE file + void set_time_date_stamp(uint32_t timestamp); + + //Returns Machine field value of PE file from header + uint16_t get_machine() const; + //Sets Machine field value of PE file + void set_machine(uint16_t machine); + + //Returns data from the beginning of image + //Size = SizeOfHeaders + const std::string& get_full_headers_data() const; + + typedef std::multimap<uint32_t, std::string> debug_data_list; + //Returns raw list of debug data + const debug_data_list& get_raw_debug_data_list() const; + + //Reads and checks DOS header + static void read_dos_header(std::istream& file, pe_win::image_dos_header& header); + + //Returns sizeof() nt headers + uint32_t get_sizeof_nt_header() const; + //Returns sizeof() optional headers + uint32_t get_sizeof_opt_headers() const; + //Returns raw nt headers data pointer + const char* get_nt_headers_ptr() const; + + //Sets size of headers (to NT headers) + void set_size_of_headers(uint32_t size); + //Sets size of optional headers (to NT headers) + void set_size_of_optional_header(uint16_t size); + + //Sets base of code + void set_base_of_code(uint32_t base); + //Returns base of code + uint32_t get_base_of_code() const; + +public: //ADDRESS CONVERTIONS + //Virtual Address (VA) to Relative Virtual Address (RVA) convertions + //for PE32 and PE64 respectively + //bound_check checks integer overflow + uint32_t va_to_rva(uint32_t va, bool bound_check = true) const; + uint32_t va_to_rva(uint64_t va, bool bound_check = true) const; + + //Relative Virtual Address (RVA) to Virtual Address (VA) convertions + //for PE32 and PE64 respectively + uint32_t rva_to_va_32(uint32_t rva) const; + void rva_to_va(uint32_t rva, uint32_t& va) const; + uint64_t rva_to_va_64(uint32_t rva) const; + void rva_to_va(uint32_t rva, uint64_t& va) const; + + //RVA to RAW file offset convertion (4gb max) + uint32_t rva_to_file_offset(uint32_t rva) const; + //RAW file offset to RVA convertion (4gb max) + uint32_t file_offset_to_rva(uint32_t offset) const; + + //RVA from section raw data offset + static uint32_t rva_from_section_offset(const section& s, uint32_t raw_offset_from_section_start); + +public: //IMAGE SECTIONS + //Returns number of sections from PE header + uint16_t get_number_of_sections() const; + + //Updates number of sections in PE header + uint16_t update_number_of_sections(); + + //Returns section alignment + uint32_t get_section_alignment() const; + + //Returns section list + section_list& get_image_sections(); + const section_list& get_image_sections() const; + + //Realigns all sections, if you made any changes to sections or alignments + void realign_all_sections(); + //Resligns section with specified index + void realign_section(uint32_t index); + + //Returns section from RVA inside it + section& section_from_rva(uint32_t rva); + const section& section_from_rva(uint32_t rva) const; + //Returns section from directory ID + section& section_from_directory(uint32_t directory_id); + const section& section_from_directory(uint32_t directory_id) const; + //Returns section from VA inside it for PE32 and PE64 respectively + section& section_from_va(uint32_t va); + const section& section_from_va(uint32_t va) const; + section& section_from_va(uint64_t va); + const section& section_from_va(uint64_t va) const; + //Returns section from file offset (4gb max) + section& section_from_file_offset(uint32_t offset); + const section& section_from_file_offset(uint32_t offset) const; + + //Returns section TOTAL RAW/VIRTUAL data length from RVA inside section + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + uint32_t section_data_length_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const; + //Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32 and PE64 respectively + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + uint32_t section_data_length_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; + uint32_t section_data_length_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; + + //Returns section remaining RAW/VIRTUAL data length from RVA to the end of section "s" (checks bounds) + uint32_t section_data_length_from_rva(const section& s, uint32_t rva_inside, section_data_type datatype = section_data_raw) const; + //Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32 and PE64 respectively (checks bounds) + uint32_t section_data_length_from_va(const section& s, uint64_t va_inside, section_data_type datatype = section_data_raw) const; + uint32_t section_data_length_from_va(const section& s, uint32_t va_inside, section_data_type datatype = section_data_raw) const; + + //Returns section remaining RAW/VIRTUAL data length from RVA "rva_inside" to the end of section containing RVA "rva" + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + uint32_t section_data_length_from_rva(uint32_t rva, uint32_t rva_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const; + //Returns section remaining RAW/VIRTUAL data length from VA "va_inside" to the end of section containing VA "va" for PE32 and PE64 respectively + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + uint32_t section_data_length_from_va(uint32_t va, uint32_t va_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const; + uint32_t section_data_length_from_va(uint64_t va, uint64_t va_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const; + + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + //Returns corresponding section data pointer from RVA inside section + char* section_data_from_rva(uint32_t rva, bool include_headers = false); + const char* section_data_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const; + //Returns corresponding section data pointer from VA inside section for PE32 and PE64 respectively + char* section_data_from_va(uint32_t va, bool include_headers = false); + const char* section_data_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; + char* section_data_from_va(uint64_t va, bool include_headers = false); + const char* section_data_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; + + //Returns corresponding section data pointer from RVA inside section "s" (checks bounds) + char* section_data_from_rva(section& s, uint32_t rva); + const char* section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw) const; + //Returns corresponding section data pointer from VA inside section "s" for PE32 and PE64 respectively (checks bounds) + char* section_data_from_va(section& s, uint32_t va); //Always returns raw data + const char* section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) const; + char* section_data_from_va(section& s, uint64_t va); //Always returns raw data + const char* section_data_from_va(const section& s, uint64_t va, section_data_type datatype = section_data_raw) const; + + //Returns corresponding section data pointer from RVA inside section "s" (checks bounds, checks sizes, the most safe function) + template<typename T> + T section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw) const + { + if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()) && pe_utils::is_sum_safe(rva, sizeof(T))) + { + const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(get_section_alignment()); + //Don't check for underflow here, comparsion is unsigned + if(data.size() < rva - s.get_virtual_address() + sizeof(T)) + throw pe_exception("RVA and requested data size does not exist inside section", pe_exception::rva_not_exists); + + return *reinterpret_cast<const T*>(data.data() + rva - s.get_virtual_address()); + } + + throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); + } + + //Returns corresponding section data pointer from RVA inside section (checks rva, checks sizes, the most safe function) + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + template<typename T> + T section_data_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const + { + //if RVA is inside of headers and we're searching them too... + if(include_headers && pe_utils::is_sum_safe(rva, sizeof(T)) && (rva + sizeof(T) < full_headers_data_.length())) + return *reinterpret_cast<const T*>(&full_headers_data_[rva]); + + const section& s = section_from_rva(rva); + const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(get_section_alignment()); + //Don't check for underflow here, comparsion is unsigned + if(data.size() < rva - s.get_virtual_address() + sizeof(T)) + throw pe_exception("RVA and requested data size does not exist inside section", pe_exception::rva_not_exists); + + return *reinterpret_cast<const T*>(data.data() + rva - s.get_virtual_address()); + } + + //Returns corresponding section data pointer from VA inside section "s" (checks bounds, checks sizes, the most safe function) + template<typename T> + T section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) const + { + return section_data_from_rva<T>(s, va_to_rva(va), datatype); + } + + template<typename T> + T section_data_from_va(const section& s, uint64_t va, section_data_type datatype = section_data_raw) const + { + return section_data_from_rva<T>(s, va_to_rva(va), datatype); + } + + //Returns corresponding section data pointer from VA inside section (checks rva, checks sizes, the most safe function) + //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too + template<typename T> + T section_data_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const + { + return section_data_from_rva<T>(va_to_rva(va), datatype, include_headers); + } + + template<typename T> + T section_data_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const + { + return section_data_from_rva<T>(va_to_rva(va), datatype, include_headers); + } + + //Returns section and offset (raw data only) from its start from RVA + const std::pair<uint32_t, const section*> section_and_offset_from_rva(uint32_t rva) const; + + //Sets virtual size of section "s" + //Section must be free (not bound to any image) + //or the last section of this image + //Function calls update_image_size automatically in second case + void set_section_virtual_size(section& s, uint32_t vsize); + + //Represents section expand type for expand_section function + enum section_expand_type + { + expand_section_raw, //Section raw data size will be expanded + expand_section_virtual //Section virtual data size will be expanded + }; + + //Expands section raw or virtual size to hold data from specified RVA with specified size + //Section must be free (not bound to any image) + //or the last section of this image + //Returns true if section was expanded + bool expand_section(section& s, uint32_t needed_rva, uint32_t needed_size, section_expand_type expand); + + //Adds section to image + //Returns last section + section& add_section(section s); + //Prepares section to later add it to image (checks and recalculates virtual and raw section size) + //Section must be prepared by this function before calling add_section + void prepare_section(section& s); + + //Returns true if sectios "s" is already attached to this PE file + bool section_attached(const section& s) const; + + +public: //IMAGE + //Returns PE type (PE or PE+) from pe_type enumeration (minimal correctness checks) + static pe_type get_pe_type(std::istream& file); + //Returns PE type of this image + pe_type get_pe_type() const; + + //Returns true if image has overlay data at the end of file + bool has_overlay() const; + + //Realigns file (changes file alignment) + void realign_file(uint32_t new_file_alignment); + + //Helper function to recalculate RAW and virtual section sizes and strip it, if necessary + //auto_strip = strip section, if necessary + void recalculate_section_sizes(section& s, bool auto_strip); + + // ========== END OF PUBLIC MEMBERS AND STRUCTURES ========== // +private: + //Image DOS header + pe_win::image_dos_header dos_header_; + //Rich (stub) overlay data (for MSVS) + std::string rich_overlay_; + //List of image sections + section_list sections_; + //True if image has overlay + bool has_overlay_; + //Raw SizeOfHeaders-sized data from the beginning of image + std::string full_headers_data_; + //Raw debug data for all directories + //PointerToRawData; Data + debug_data_list debug_data_; + //PE or PE+ related properties + pe_properties* props_; + + //Reads and checks DOS header + void read_dos_header(std::istream& file); + + //Reads and checks PE headers and section headers, data + void read_pe(std::istream& file, bool read_debug_raw_data); + + //Sets number of sections + void set_number_of_sections(uint16_t number); + //Sets size of image + void set_size_of_image(uint32_t size); + //Sets file alignment (no checks) + void set_file_alignment_unchecked(uint32_t alignment); + //Returns needed magic of image + uint32_t get_needed_magic() const; + //Returns nt headers data pointer + char* get_nt_headers_ptr(); + +private: + static const uint16_t maximum_number_of_sections = 0x60; + static const uint32_t minimum_file_alignment = 512; + +private: + //RAW file offset to section convertion helpers (4gb max) + section_list::const_iterator file_offset_to_section(uint32_t offset) const; + section_list::iterator file_offset_to_section(uint32_t offset); +}; +} diff --git a/tools/pe_bliss/pe_bliss.h b/tools/pe_bliss/pe_bliss.h new file mode 100644 index 0000000000..1a8b430284 --- /dev/null +++ b/tools/pe_bliss/pe_bliss.h @@ -0,0 +1,39 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "pe_base.h" +#include "pe_rebuilder.h" +#include "pe_factory.h" +#include "pe_bound_import.h" +#include "pe_debug.h" +#include "pe_dotnet.h" +#include "pe_exception_directory.h" +#include "pe_exports.h" +#include "pe_imports.h" +#include "pe_load_config.h" +#include "pe_relocations.h" +#include "pe_resources.h" +#include "pe_rich_data.h" +#include "pe_tls.h" +#include "pe_properties_generic.h" +#include "pe_checksum.h" +#include "entropy.h" diff --git a/tools/pe_bliss/pe_bliss_godot.cpp b/tools/pe_bliss/pe_bliss_godot.cpp new file mode 100644 index 0000000000..8297aa1045 --- /dev/null +++ b/tools/pe_bliss/pe_bliss_godot.cpp @@ -0,0 +1,118 @@ +#include "pe_bliss/pe_bliss.h" +#include "pe_bliss/pe_bliss_resources.h" +#include "core/ustring.h" +#include "core/dvector.h" +#include "os/file_access.h" + +using namespace pe_bliss; + +String pe_bliss_add_resrc(const char* p_path, int version_major, int version_minor, + String& company_name, String& file_description, + String& legal_copyright, String& version_text, + String& product_name, String& godot_version, + DVector<uint8_t>& icon_content) { + try + { + pe_base image(pe_factory::create_pe(p_path)); + + const section_list& pe_sections = image.get_image_sections(); + uint32_t end_of_pe = 0; + FileAccess *dst; + DVector<uint8_t> overlay_data; + if(image.has_overlay()) + { + end_of_pe = pe_sections.back().get_pointer_to_raw_data() + pe_sections.back().get_size_of_raw_data(); + dst=FileAccess::open(p_path,FileAccess::READ); + if (dst) { + overlay_data.resize(dst->get_len()-end_of_pe); + dst->seek(end_of_pe); + DVector<uint8_t>::Write overlay_data_write = overlay_data.write(); + dst->get_buffer(overlay_data_write.ptr(),overlay_data.size()); + dst->close(); + memdelete(dst); + } + } + resource_directory root; + if(image.has_resources()) + { + root = resource_directory(get_resources(image)); + } + pe_resource_manager res(root); + if(image.has_resources()) + { + if(icon_content.size()) { + if(res.resource_exists(pe_resource_viewer::resource_icon)) + { + res.remove_resource_type(pe_resource_viewer::resource_icon); + } + if(res.resource_exists(pe_resource_viewer::resource_icon_group)) + { + res.remove_resource_type(pe_resource_viewer::resource_icon_group); + } + } + if(res.resource_exists(pe_resource_viewer::resource_version)) + { + res.remove_resource_type(pe_resource_viewer::resource_version); + } + } + file_version_info file_info; + file_info.set_file_os(file_version_info::file_os_nt_win32); + file_info.set_file_type(file_version_info::file_type_application); + unsigned int ver = version_major << 16; + ver = ver + version_minor; + file_info.set_file_version_ms(ver); + file_info.set_file_version_ls(0x00000000); + file_info.set_product_version_ms(ver); + file_info.set_product_version_ls(0x00000000); + lang_string_values_map strings; + translation_values_map translations; + version_info_editor version(strings, translations); + version.add_translation(version_info_editor::default_language_translation); + version.set_company_name(company_name.c_str()); + version.set_file_description(file_description.c_str()); + if (!product_name.empty()) { + version.set_internal_name((product_name+String(".exe")).c_str()); + version.set_original_filename((product_name+String(".exe")).c_str()); + version.set_product_name(product_name.c_str()); + } + version.set_legal_copyright(legal_copyright.c_str()); + version.set_product_version(version_text.c_str()); + if(!godot_version.empty()) version.set_property(L"Godot Engine Version", godot_version.c_str() ); + resource_version_info_writer(res).set_version_info(file_info, strings, translations, 1033, 1200); + if(icon_content.size()) { + std::string icon; + icon.resize(icon_content.size()); + for(int i=0; i<icon_content.size(); i++) + { + icon[i] = icon_content[i]; + } + resource_cursor_icon_writer(res).add_icon(icon, L"MAIN_ICON", 1033); + } + if(image.has_resources()) + { + rebuild_resources(image, root, image.section_from_directory(pe_win::image_directory_entry_resource)); + } else { + section new_resources; + new_resources.get_raw_data().resize(1); + new_resources.set_name(".rsrc"); + new_resources.readable(true); + section& attached_section = image.add_section(new_resources); + rebuild_resources(image, root, attached_section); + } + rebuild_pe(image, p_path); + if(image.has_overlay() && end_of_pe) { + dst=FileAccess::open(p_path,FileAccess::READ_WRITE); + if (dst) { + dst->seek_end(); + DVector<uint8_t>::Read overlay_data_read = overlay_data.read(); + dst->store_buffer(overlay_data_read.ptr(),overlay_data.size()); + dst->close(); + memdelete(dst); + } + } + return String(); + } catch(const pe_exception& e) { + String ret("Error In Add rsrc Section : "); + return ret + String(e.what()); + } +} diff --git a/tools/pe_bliss/pe_bliss_godot.h b/tools/pe_bliss/pe_bliss_godot.h new file mode 100644 index 0000000000..0365ca9eaf --- /dev/null +++ b/tools/pe_bliss/pe_bliss_godot.h @@ -0,0 +1,7 @@ + + +String pe_bliss_add_resrc(const char* p_path, int version_major, int version_minor, + String& company_name, String& file_description, + String& legal_copyright, String& version_text, + String& product_name, String& godot_version, + DVector<uint8_t>& icon_content); diff --git a/tools/pe_bliss/pe_bliss_resources.h b/tools/pe_bliss/pe_bliss_resources.h new file mode 100644 index 0000000000..60369f8011 --- /dev/null +++ b/tools/pe_bliss/pe_bliss_resources.h @@ -0,0 +1,36 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "file_version_info.h" +#include "message_table.h" +#include "pe_resource_manager.h" +#include "pe_resource_viewer.h" +#include "version_info_editor.h" +#include "version_info_viewer.h" +#include "resource_bitmap_reader.h" +#include "resource_bitmap_writer.h" +#include "resource_cursor_icon_reader.h" +#include "resource_cursor_icon_writer.h" +#include "resource_version_info_reader.h" +#include "resource_version_info_writer.h" +#include "resource_string_table_reader.h" +#include "resource_message_list_reader.h" diff --git a/tools/pe_bliss/pe_bound_import.cpp b/tools/pe_bliss/pe_bound_import.cpp new file mode 100644 index 0000000000..4b54b36105 --- /dev/null +++ b/tools/pe_bliss/pe_bound_import.cpp @@ -0,0 +1,311 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_bound_import.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//BOUND IMPORT +//Default constructor +bound_import_ref::bound_import_ref() + :timestamp_(0) +{} + +//Constructor from data +bound_import_ref::bound_import_ref(const std::string& module_name, uint32_t timestamp) + :module_name_(module_name), timestamp_(timestamp) +{} + +//Returns imported module name +const std::string& bound_import_ref::get_module_name() const +{ + return module_name_; +} + +//Returns bound import date and time stamp +uint32_t bound_import_ref::get_timestamp() const +{ + return timestamp_; +} + +//Sets module name +void bound_import_ref::set_module_name(const std::string& module_name) +{ + module_name_ = module_name; +} + +//Sets timestamp +void bound_import_ref::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Default constructor +bound_import::bound_import() + :timestamp_(0) +{} + +//Constructor from data +bound_import::bound_import(const std::string& module_name, uint32_t timestamp) + :module_name_(module_name), timestamp_(timestamp) +{} + +//Returns imported module name +const std::string& bound_import::get_module_name() const +{ + return module_name_; +} + +//Returns bound import date and time stamp +uint32_t bound_import::get_timestamp() const +{ + return timestamp_; +} + +//Returns bound references cound +size_t bound_import::get_module_ref_count() const +{ + return refs_.size(); +} + +//Returns module references +const bound_import::ref_list& bound_import::get_module_ref_list() const +{ + return refs_; +} + +//Adds module reference +void bound_import::add_module_ref(const bound_import_ref& ref) +{ + refs_.push_back(ref); +} + +//Clears module references list +void bound_import::clear_module_refs() +{ + refs_.clear(); +} + +//Returns module references +bound_import::ref_list& bound_import::get_module_ref_list() +{ + return refs_; +} + +//Sets module name +void bound_import::set_module_name(const std::string& module_name) +{ + module_name_ = module_name; +} + +//Sets timestamp +void bound_import::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +const bound_import_module_list get_bound_import_module_list(const pe_base& pe) +{ + //Returned bound import modules list + bound_import_module_list ret; + + //If image has no bound imports + if(!pe.has_bound_import()) + return ret; + + uint32_t bound_import_data_len = + pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true); + + if(bound_import_data_len < pe.get_directory_size(image_directory_entry_bound_import)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + const char* bound_import_data = pe.section_data_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true); + + //Check read in "read_pe" function raw bound import data size + if(bound_import_data_len < sizeof(image_bound_import_descriptor)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //current bound_import_data_ in-string position + unsigned long current_pos = 0; + //first bound import descriptor + //so, we're working with raw data here, no section helpers available + const image_bound_import_descriptor* descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data[current_pos]); + + //Enumerate until zero + while(descriptor->OffsetModuleName) + { + //Check module name offset + if(descriptor->OffsetModuleName >= bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Check module name for null-termination + if(!pe_utils::is_null_terminated(&bound_import_data[descriptor->OffsetModuleName], bound_import_data_len - descriptor->OffsetModuleName)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Create bound import descriptor structure + bound_import elem(&bound_import_data[descriptor->OffsetModuleName], descriptor->TimeDateStamp); + + //Check DWORDs + if(descriptor->NumberOfModuleForwarderRefs >= pe_utils::max_dword / sizeof(image_bound_forwarder_ref) + || !pe_utils::is_sum_safe(current_pos, 2 /* this descriptor and the next one */ * sizeof(image_bound_import_descriptor) + descriptor->NumberOfModuleForwarderRefs * sizeof(image_bound_forwarder_ref))) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Move after current descriptor + current_pos += sizeof(image_bound_import_descriptor); + + //Enumerate referenced bound import descriptors + for(unsigned long i = 0; i != descriptor->NumberOfModuleForwarderRefs; ++i) + { + //They're just after parent descriptor + //Check size of structure + if(current_pos + sizeof(image_bound_forwarder_ref) > bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Get IMAGE_BOUND_FORWARDER_REF pointer + const image_bound_forwarder_ref* ref_descriptor = reinterpret_cast<const image_bound_forwarder_ref*>(&bound_import_data[current_pos]); + + //Check referenced module name + if(ref_descriptor->OffsetModuleName >= bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //And its null-termination + if(!pe_utils::is_null_terminated(&bound_import_data[ref_descriptor->OffsetModuleName], bound_import_data_len - ref_descriptor->OffsetModuleName)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Add referenced module to current bound import structure + elem.add_module_ref(bound_import_ref(&bound_import_data[ref_descriptor->OffsetModuleName], ref_descriptor->TimeDateStamp)); + + //Move after referenced bound import descriptor + current_pos += sizeof(image_bound_forwarder_ref); + } + + //Check structure size + if(current_pos + sizeof(image_bound_import_descriptor) > bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Move to next bound import descriptor + descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data[current_pos]); + + //Save created descriptor structure and references + ret.push_back(elem); + } + + //Return result + return ret; +} + +//imports - bound imported modules list +//imports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from imports_section raw data start +//save_to_pe_headers - if true, new bound import directory information will be saved to PE image headers +//auto_strip_last_section - if true and bound imports are placed in the last section, it will be automatically stripped +const image_directory rebuild_bound_imports(pe_base& pe, const bound_import_module_list& imports, section& imports_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that exports_section is attached to this PE image + if(!pe.section_attached(imports_section)) + throw pe_exception("Bound import section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t directory_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + uint32_t needed_size = sizeof(image_bound_import_descriptor) /* Ending null descriptor */; + uint32_t needed_size_for_strings = 0; + + //Calculate needed size for bound import data + for(bound_import_module_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + const bound_import& import = *it; + needed_size += sizeof(image_bound_import_descriptor); + needed_size_for_strings += static_cast<uint32_t>((*it).get_module_name().length()) + 1 /* nullbyte */; + + const bound_import::ref_list& refs = import.get_module_ref_list(); + for(bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it) + { + needed_size_for_strings += static_cast<uint32_t>((*ref_it).get_module_name().length()) + 1 /* nullbyte */; + needed_size += sizeof(image_bound_forwarder_ref); + } + } + + needed_size += needed_size_for_strings; + + //Check if imports_section is last one. If it's not, check if there's enough place for bound import data + if(&imports_section != &*(pe.get_image_sections().end() - 1) && + (imports_section.empty() || pe_utils::align_up(imports_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + directory_pos)) + throw pe_exception("Insufficient space for bound import directory", pe_exception::insufficient_space); + + std::string& raw_data = imports_section.get_raw_data(); + + //This will be done only if imports_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + directory_pos) + raw_data.resize(needed_size + directory_pos); //Expand section raw data + + uint32_t current_pos_for_structures = directory_pos; + uint32_t current_pos_for_strings = current_pos_for_structures + needed_size - needed_size_for_strings; + + for(bound_import_module_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + const bound_import& import = *it; + image_bound_import_descriptor descriptor; + descriptor.NumberOfModuleForwarderRefs = static_cast<uint16_t>(import.get_module_ref_list().size()); + descriptor.OffsetModuleName = static_cast<uint16_t>(current_pos_for_strings - directory_pos); + descriptor.TimeDateStamp = import.get_timestamp(); + + memcpy(&raw_data[current_pos_for_structures], &descriptor, sizeof(descriptor)); + current_pos_for_structures += sizeof(descriptor); + + size_t length = import.get_module_name().length() + 1 /* nullbyte */; + memcpy(&raw_data[current_pos_for_strings], import.get_module_name().c_str(), length); + current_pos_for_strings += static_cast<uint32_t>(length); + + const bound_import::ref_list& refs = import.get_module_ref_list(); + for(bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it) + { + const bound_import_ref& ref = *ref_it; + image_bound_forwarder_ref ref_descriptor = {0}; + ref_descriptor.OffsetModuleName = static_cast<uint16_t>(current_pos_for_strings - directory_pos); + ref_descriptor.TimeDateStamp = ref.get_timestamp(); + + memcpy(&raw_data[current_pos_for_structures], &ref_descriptor, sizeof(ref_descriptor)); + current_pos_for_structures += sizeof(ref_descriptor); + + length = ref.get_module_name().length() + 1 /* nullbyte */; + memcpy(&raw_data[current_pos_for_strings], ref.get_module_name().c_str(), length); + current_pos_for_strings += static_cast<uint32_t>(length); + } + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(imports_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(imports_section, directory_pos), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_bound_import, ret.get_rva()); + pe.set_directory_size(image_directory_entry_bound_import, ret.get_size()); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_bound_import.h b/tools/pe_bliss/pe_bound_import.h new file mode 100644 index 0000000000..667e28792e --- /dev/null +++ b/tools/pe_bliss/pe_bound_import.h @@ -0,0 +1,108 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include <string> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing bound import reference +class bound_import_ref +{ +public: + //Default constructor + bound_import_ref(); + //Constructor from data + bound_import_ref(const std::string& module_name, uint32_t timestamp); + + //Returns imported module name + const std::string& get_module_name() const; + //Returns bound import date and time stamp + uint32_t get_timestamp() const; + +public: //Setters + //Sets module name + void set_module_name(const std::string& module_name); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + +private: + std::string module_name_; //Imported module name + uint32_t timestamp_; //Bound import timestamp +}; + +//Class representing image bound import information +class bound_import +{ +public: + typedef std::vector<bound_import_ref> ref_list; + +public: + //Default constructor + bound_import(); + //Constructor from data + bound_import(const std::string& module_name, uint32_t timestamp); + + //Returns imported module name + const std::string& get_module_name() const; + //Returns bound import date and time stamp + uint32_t get_timestamp() const; + + //Returns bound references cound + size_t get_module_ref_count() const; + //Returns module references + const ref_list& get_module_ref_list() const; + +public: //Setters + //Sets module name + void set_module_name(const std::string& module_name); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + + //Adds module reference + void add_module_ref(const bound_import_ref& ref); + //Clears module references list + void clear_module_refs(); + //Returns module references + ref_list& get_module_ref_list(); + +private: + std::string module_name_; //Imported module name + uint32_t timestamp_; //Bound import timestamp + ref_list refs_; //Module references list +}; + +typedef std::vector<bound_import> bound_import_module_list; + +//Returns bound import information +const bound_import_module_list get_bound_import_module_list(const pe_base& pe);//Export directory rebuilder + +//imports - bound imported modules list +//imports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from imports_section raw data start +//save_to_pe_headers - if true, new bound import directory information will be saved to PE image headers +//auto_strip_last_section - if true and bound imports are placed in the last section, it will be automatically stripped +const image_directory rebuild_bound_imports(pe_base& pe, const bound_import_module_list& imports, section& imports_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/pe_checksum.cpp b/tools/pe_bliss/pe_checksum.cpp new file mode 100644 index 0000000000..5971a33c90 --- /dev/null +++ b/tools/pe_bliss/pe_checksum.cpp @@ -0,0 +1,103 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_checksum.h" +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Calculate checksum of image +uint32_t calculate_checksum(std::istream& file) +{ + //Save istream state + std::ios_base::iostate state = file.exceptions(); + std::streamoff old_offset = file.tellg(); + + //Checksum value + unsigned long long checksum = 0; + + try + { + image_dos_header header; + + file.exceptions(std::ios::goodbit); + + //Read DOS header + pe_base::read_dos_header(file, header); + + //Calculate PE checksum + file.seekg(0); + unsigned long long top = 0xFFFFFFFF; + top++; + + //"CheckSum" field position in optional PE headers - it's always 64 for PE and PE+ + static const unsigned long checksum_pos_in_optional_headers = 64; + //Calculate real PE headers "CheckSum" field position + //Sum is safe here + unsigned long pe_checksum_pos = header.e_lfanew + sizeof(image_file_header) + sizeof(uint32_t) + checksum_pos_in_optional_headers; + + //Calculate checksum for each byte of file + std::streamoff filesize = pe_utils::get_file_size(file); + for(long long i = 0; i < filesize; i += 4) + { + unsigned long dw = 0; + + //Read DWORD from file + file.read(reinterpret_cast<char*>(&dw), sizeof(unsigned long)); + //Skip "CheckSum" DWORD + if(i == pe_checksum_pos) + continue; + + //Calculate checksum + checksum = (checksum & 0xffffffff) + dw + (checksum >> 32); + if(checksum > top) + checksum = (checksum & 0xffffffff) + (checksum >> 32); + } + + //Finish checksum + checksum = (checksum & 0xffff) + (checksum >> 16); + checksum = (checksum) + (checksum >> 16); + checksum = checksum & 0xffff; + + checksum += static_cast<unsigned long>(filesize); + } + catch(const std::exception&) + { + //If something went wrong, restore istream state + file.exceptions(state); + file.seekg(old_offset); + file.clear(); + //Rethrow + throw; + } + + //Restore istream state + file.exceptions(state); + file.seekg(old_offset); + file.clear(); + + //Return checksum + return static_cast<uint32_t>(checksum); +} +} diff --git a/tools/pe_bliss/pe_checksum.h b/tools/pe_bliss/pe_checksum.h new file mode 100644 index 0000000000..a568d5d369 --- /dev/null +++ b/tools/pe_bliss/pe_checksum.h @@ -0,0 +1,30 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <istream> +#include "stdint_defs.h" + +namespace pe_bliss +{ +//Calculate checksum of image (performs no checks on PE structures) +uint32_t calculate_checksum(std::istream& file); +} diff --git a/tools/pe_bliss/pe_debug.cpp b/tools/pe_bliss/pe_debug.cpp new file mode 100644 index 0000000000..a0ed3f5af1 --- /dev/null +++ b/tools/pe_bliss/pe_debug.cpp @@ -0,0 +1,865 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_debug.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; +//DEBUG +//Default constructor +debug_info::debug_info() + :characteristics_(0), + time_stamp_(0), + major_version_(0), minor_version_(0), + type_(0), + size_of_data_(0), + address_of_raw_data_(0), + pointer_to_raw_data_(0), + advanced_info_type_(advanced_info_none) +{} + +//Constructor from data +debug_info::debug_info(const image_debug_directory& debug) + :characteristics_(debug.Characteristics), + time_stamp_(debug.TimeDateStamp), + major_version_(debug.MajorVersion), minor_version_(debug.MinorVersion), + type_(debug.Type), + size_of_data_(debug.SizeOfData), + address_of_raw_data_(debug.AddressOfRawData), + pointer_to_raw_data_(debug.PointerToRawData), + advanced_info_type_(advanced_info_none) +{} + +//Returns debug characteristics +uint32_t debug_info::get_characteristics() const +{ + return characteristics_; +} + +//Returns debug datetimestamp +uint32_t debug_info::get_time_stamp() const +{ + return time_stamp_; +} + +//Returns major version +uint32_t debug_info::get_major_version() const +{ + return major_version_; +} + +//Returns minor version +uint32_t debug_info::get_minor_version() const +{ + return minor_version_; +} + +//Returns type of debug info (unchecked) +uint32_t debug_info::get_type_raw() const +{ + return type_; +} + +//Returns type of debug info from debug_info_type enumeration +debug_info::debug_info_type debug_info::get_type() const +{ + //Determine debug type + switch(type_) + { + case image_debug_type_coff: + return debug_type_coff; + + case image_debug_type_codeview: + return debug_type_codeview; + + case image_debug_type_fpo: + return debug_type_fpo; + + case image_debug_type_misc: + return debug_type_misc; + + case image_debug_type_exception: + return debug_type_exception; + + case image_debug_type_fixup: + return debug_type_fixup; + + case image_debug_type_omap_to_src: + return debug_type_omap_to_src; + + case image_debug_type_omap_from_src: + return debug_type_omap_from_src; + + case image_debug_type_borland: + return debug_type_borland; + + case image_debug_type_clsid: + return debug_type_clsid; + + case image_debug_type_reserved10: + return debug_type_reserved10; + } + + return debug_type_unknown; +} + +//Returns size of debug data (internal, .pdb or other file doesn't count) +uint32_t debug_info::get_size_of_data() const +{ + return size_of_data_; +} + +//Returns RVA of debug info when mapped to memory or zero, if info is not mapped +uint32_t debug_info::get_rva_of_raw_data() const +{ + return address_of_raw_data_; +} + +//Returns raw file pointer to raw data +uint32_t debug_info::get_pointer_to_raw_data() const +{ + return pointer_to_raw_data_; +} + +//Copy constructor +debug_info::debug_info(const debug_info& info) + :characteristics_(info.characteristics_), + time_stamp_(info.time_stamp_), + major_version_(info.major_version_), minor_version_(info.minor_version_), + type_(info.type_), + size_of_data_(info.size_of_data_), + address_of_raw_data_(info.address_of_raw_data_), + pointer_to_raw_data_(info.pointer_to_raw_data_), + advanced_info_type_(info.advanced_info_type_) +{ + copy_advanced_info(info); +} + +//Copy assignment operator +debug_info& debug_info::operator=(const debug_info& info) +{ + copy_advanced_info(info); + + characteristics_ = info.characteristics_; + time_stamp_ = info.time_stamp_; + major_version_ = info.major_version_; + minor_version_ = info.minor_version_; + type_ = info.type_; + size_of_data_ = info.size_of_data_; + address_of_raw_data_ = info.address_of_raw_data_; + pointer_to_raw_data_ = info.pointer_to_raw_data_; + advanced_info_type_ = info.advanced_info_type_; + + return *this; +} + +//Default constructor +debug_info::advanced_info::advanced_info() + :adv_pdb_7_0_info(0) //Zero pointer to advanced data +{} + +//Returns true if advanced debug info is present +bool debug_info::advanced_info::is_present() const +{ + return adv_pdb_7_0_info != 0; +} + +//Helper for advanced debug information copying +void debug_info::copy_advanced_info(const debug_info& info) +{ + free_present_advanced_info(); + + switch(info.advanced_info_type_) + { + case advanced_info_pdb_7_0: + advanced_debug_info_.adv_pdb_7_0_info = new pdb_7_0_info(*info.advanced_debug_info_.adv_pdb_7_0_info); + break; + case advanced_info_pdb_2_0: + advanced_debug_info_.adv_pdb_2_0_info = new pdb_2_0_info(*info.advanced_debug_info_.adv_pdb_2_0_info); + break; + case advanced_info_misc: + advanced_debug_info_.adv_misc_info = new misc_debug_info(*info.advanced_debug_info_.adv_misc_info); + break; + case advanced_info_coff: + advanced_debug_info_.adv_coff_info = new coff_debug_info(*info.advanced_debug_info_.adv_coff_info); + break; + default: + break; + } + + advanced_info_type_ = info.advanced_info_type_; +} + +//Helper for clearing any present advanced debug information +void debug_info::free_present_advanced_info() +{ + switch(advanced_info_type_) + { + case advanced_info_pdb_7_0: + delete advanced_debug_info_.adv_pdb_7_0_info; + break; + case advanced_info_pdb_2_0: + delete advanced_debug_info_.adv_pdb_2_0_info; + break; + case advanced_info_misc: + delete advanced_debug_info_.adv_misc_info; + break; + case advanced_info_coff: + delete advanced_debug_info_.adv_coff_info; + break; + default: + break; + } + + advanced_debug_info_.adv_pdb_7_0_info = 0; + advanced_info_type_ = advanced_info_none; +} + +//Destructor +debug_info::~debug_info() +{ + free_present_advanced_info(); +} + +//Sets advanced debug information +void debug_info::set_advanced_debug_info(const pdb_7_0_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_pdb_7_0_info = new pdb_7_0_info(info); + advanced_info_type_ = advanced_info_pdb_7_0; +} + +void debug_info::set_advanced_debug_info(const pdb_2_0_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_pdb_2_0_info = new pdb_2_0_info(info); + advanced_info_type_ = advanced_info_pdb_2_0; +} + +void debug_info::set_advanced_debug_info(const misc_debug_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_misc_info = new misc_debug_info(info); + advanced_info_type_ = advanced_info_misc; +} + +void debug_info::set_advanced_debug_info(const coff_debug_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_coff_info = new coff_debug_info(info); + advanced_info_type_ = advanced_info_coff; +} + +//Returns advanced debug information type +debug_info::advanced_info_type debug_info::get_advanced_info_type() const +{ + return advanced_info_type_; +} + +//Returns advanced debug information or throws an exception, +//if requested information type is not contained by structure +template<> +const pdb_7_0_info debug_info::get_advanced_debug_info<pdb_7_0_info>() const +{ + if(advanced_info_type_ != advanced_info_pdb_7_0) + throw pe_exception("Debug info structure does not contain PDB 7.0 data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_pdb_7_0_info; +} + +template<> +const pdb_2_0_info debug_info::get_advanced_debug_info<pdb_2_0_info>() const +{ + if(advanced_info_type_ != advanced_info_pdb_2_0) + throw pe_exception("Debug info structure does not contain PDB 2.0 data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_pdb_2_0_info; +} + +template<> +const misc_debug_info debug_info::get_advanced_debug_info<misc_debug_info>() const +{ + if(advanced_info_type_ != advanced_info_misc) + throw pe_exception("Debug info structure does not contain MISC data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_misc_info; +} + +template<> +const coff_debug_info debug_info::get_advanced_debug_info<coff_debug_info>() const +{ + if(advanced_info_type_ != advanced_info_coff) + throw pe_exception("Debug info structure does not contain COFF data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_coff_info; +} + +//Sets advanced debug information type, if no advanced info structure available +void debug_info::set_advanced_info_type(advanced_info_type type) +{ + free_present_advanced_info(); + if(advanced_info_type_ >= advanced_info_codeview_4_0) //Don't set info type for those types, which have advanced info structures + advanced_info_type_ = type; +} + +//Default constructor +pdb_7_0_info::pdb_7_0_info() + :age_(0) +{ + memset(&guid_, 0, sizeof(guid_)); +} + +//Constructor from data +pdb_7_0_info::pdb_7_0_info(const CV_INFO_PDB70* info) + :age_(info->Age), guid_(info->Signature), + pdb_file_name_(reinterpret_cast<const char*>(info->PdbFileName)) //Must be checked before for null-termination +{} + +//Returns debug PDB 7.0 structure GUID +const guid pdb_7_0_info::get_guid() const +{ + return guid_; +} + +//Returns age of build +uint32_t pdb_7_0_info::get_age() const +{ + return age_; +} + +//Returns PDB file name / path +const std::string& pdb_7_0_info::get_pdb_file_name() const +{ + return pdb_file_name_; +} + +//Default constructor +pdb_2_0_info::pdb_2_0_info() + :age_(0), signature_(0) +{} + +//Constructor from data +pdb_2_0_info::pdb_2_0_info(const CV_INFO_PDB20* info) + :age_(info->Age), signature_(info->Signature), + pdb_file_name_(reinterpret_cast<const char*>(info->PdbFileName)) //Must be checked before for null-termination +{} + +//Returns debug PDB 2.0 structure signature +uint32_t pdb_2_0_info::get_signature() const +{ + return signature_; +} + +//Returns age of build +uint32_t pdb_2_0_info::get_age() const +{ + return age_; +} + +//Returns PDB file name / path +const std::string& pdb_2_0_info::get_pdb_file_name() const +{ + return pdb_file_name_; +} + +//Default constructor +misc_debug_info::misc_debug_info() + :data_type_(0), unicode_(false) +{} + +//Constructor from data +misc_debug_info::misc_debug_info(const image_debug_misc* info) + :data_type_(info->DataType), unicode_(info->Unicode ? true : false) +{ + //IMAGE_DEBUG_MISC::Data must be checked before! + if(info->Unicode) + { +#ifdef PE_BLISS_WINDOWS + debug_data_unicode_ = std::wstring(reinterpret_cast<const wchar_t*>(info->Data), (info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */) / 2); +#else + debug_data_unicode_ = pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(info->Data), (info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */) / 2)); +#endif + + pe_utils::strip_nullbytes(debug_data_unicode_); //Strip nullbytes in the end of string + } + else + { + debug_data_ansi_ = std::string(reinterpret_cast<const char*>(info->Data), info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */); + pe_utils::strip_nullbytes(debug_data_ansi_); //Strip nullbytes in the end of string + } +} + +//Returns debug data type +uint32_t misc_debug_info::get_data_type() const +{ + return data_type_; +} + +//Returns true if data type is exe name +bool misc_debug_info::is_exe_name() const +{ + return data_type_ == image_debug_misc_exename; +} + +//Returns true if debug data is UNICODE +bool misc_debug_info::is_unicode() const +{ + return unicode_; +} + +//Returns debug data (ANSI) +const std::string& misc_debug_info::get_data_ansi() const +{ + return debug_data_ansi_; +} + +//Returns debug data (UNICODE) +const std::wstring& misc_debug_info::get_data_unicode() const +{ + return debug_data_unicode_; +} + +//Default constructor +coff_debug_info::coff_debug_info() + :number_of_symbols_(0), + lva_to_first_symbol_(0), + number_of_line_numbers_(0), + lva_to_first_line_number_(0), + rva_to_first_byte_of_code_(0), + rva_to_last_byte_of_code_(0), + rva_to_first_byte_of_data_(0), + rva_to_last_byte_of_data_(0) +{} + +//Constructor from data +coff_debug_info::coff_debug_info(const image_coff_symbols_header* info) + :number_of_symbols_(info->NumberOfSymbols), + lva_to_first_symbol_(info->LvaToFirstSymbol), + number_of_line_numbers_(info->NumberOfLinenumbers), + lva_to_first_line_number_(info->LvaToFirstLinenumber), + rva_to_first_byte_of_code_(info->RvaToFirstByteOfCode), + rva_to_last_byte_of_code_(info->RvaToLastByteOfCode), + rva_to_first_byte_of_data_(info->RvaToFirstByteOfData), + rva_to_last_byte_of_data_(info->RvaToLastByteOfData) +{} + +//Returns number of symbols +uint32_t coff_debug_info::get_number_of_symbols() const +{ + return number_of_symbols_; +} + +//Returns virtual address of the first symbol +uint32_t coff_debug_info::get_lva_to_first_symbol() const +{ + return lva_to_first_symbol_; +} + +//Returns number of line-number entries +uint32_t coff_debug_info::get_number_of_line_numbers() const +{ + return number_of_line_numbers_; +} + +//Returns virtual address of the first line-number entry +uint32_t coff_debug_info::get_lva_to_first_line_number() const +{ + return lva_to_first_line_number_; +} + +//Returns relative virtual address of the first byte of code +uint32_t coff_debug_info::get_rva_to_first_byte_of_code() const +{ + return rva_to_first_byte_of_code_; +} + +//Returns relative virtual address of the last byte of code +uint32_t coff_debug_info::get_rva_to_last_byte_of_code() const +{ + return rva_to_last_byte_of_code_; +} + +//Returns relative virtual address of the first byte of data +uint32_t coff_debug_info::get_rva_to_first_byte_of_data() const +{ + return rva_to_first_byte_of_data_; +} + +//Returns relative virtual address of the last byte of data +uint32_t coff_debug_info::get_rva_to_last_byte_of_data() const +{ + return rva_to_last_byte_of_data_; +} + +//Returns COFF symbols list +const coff_debug_info::coff_symbols_list& coff_debug_info::get_symbols() const +{ + return symbols_; +} + +//Adds COFF symbol +void coff_debug_info::add_symbol(const coff_symbol& sym) +{ + symbols_.push_back(sym); +} + +//Default constructor +coff_debug_info::coff_symbol::coff_symbol() + :storage_class_(0), + index_(0), + section_number_(0), rva_(0), + type_(0), + is_filename_(false) +{} + +//Returns storage class +uint32_t coff_debug_info::coff_symbol::get_storage_class() const +{ + return storage_class_; +} + +//Returns symbol index +uint32_t coff_debug_info::coff_symbol::get_index() const +{ + return index_; +} + +//Returns section number +uint32_t coff_debug_info::coff_symbol::get_section_number() const +{ + return section_number_; +} + +//Returns RVA +uint32_t coff_debug_info::coff_symbol::get_rva() const +{ + return rva_; +} + +//Returns true if structure contains file name +bool coff_debug_info::coff_symbol::is_file() const +{ + return is_filename_; +} + +//Returns text data (symbol or file name) +const std::string& coff_debug_info::coff_symbol::get_symbol() const +{ + return name_; +} + +//Sets storage class +void coff_debug_info::coff_symbol::set_storage_class(uint32_t storage_class) +{ + storage_class_ = storage_class; +} + +//Sets symbol index +void coff_debug_info::coff_symbol::set_index(uint32_t index) +{ + index_ = index; +} + +//Sets section number +void coff_debug_info::coff_symbol::set_section_number(uint32_t section_number) +{ + section_number_ = section_number; +} + +//Sets RVA +void coff_debug_info::coff_symbol::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Sets file name +void coff_debug_info::coff_symbol::set_file_name(const std::string& file_name) +{ + name_ = file_name; + is_filename_ = true; +} + +//Sets symbol name +void coff_debug_info::coff_symbol::set_symbol_name(const std::string& symbol_name) +{ + name_ = symbol_name; + is_filename_ = false; +} + +//Returns type +uint16_t coff_debug_info::coff_symbol::get_type() const +{ + return type_; +} + +//Sets type +void coff_debug_info::coff_symbol::set_type(uint16_t type) +{ + type_ = type; +} + +//Returns debug information list +const debug_info_list get_debug_information(const pe_base& pe) +{ + debug_info_list ret; + + //If there's no debug directory, return empty list + if(!pe.has_debug()) + return ret; + + //Check the length in bytes of the section containing debug directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_debug), pe.get_directory_rva(image_directory_entry_debug), section_data_virtual, true) + < sizeof(image_debug_directory)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + unsigned long current_pos = pe.get_directory_rva(image_directory_entry_debug); + + //First IMAGE_DEBUG_DIRECTORY table + image_debug_directory directory = pe.section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); + + if(!pe_utils::is_sum_safe(pe.get_directory_rva(image_directory_entry_debug), pe.get_directory_size(image_directory_entry_debug))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Iterate over all IMAGE_DEBUG_DIRECTORY directories + while(directory.PointerToRawData + && current_pos < pe.get_directory_rva(image_directory_entry_debug) + pe.get_directory_size(image_directory_entry_debug)) + { + //Create debug information structure + debug_info info(directory); + + //Find raw debug data + const pe_base::debug_data_list& debug_datas = pe.get_raw_debug_data_list(); + pe_base::debug_data_list::const_iterator it = debug_datas.find(directory.PointerToRawData); + if(it != debug_datas.end()) //If it exists, we'll do some detailed debug info research + { + const std::string& debug_data = (*it).second; + switch(directory.Type) + { + case image_debug_type_coff: + { + //Check data length + if(debug_data.length() < sizeof(image_coff_symbols_header)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Get coff header structure pointer + const image_coff_symbols_header* coff = reinterpret_cast<const image_coff_symbols_header*>(debug_data.data()); + + //Check possible overflows + if(coff->NumberOfSymbols >= pe_utils::max_dword / sizeof(image_symbol) + || !pe_utils::is_sum_safe(coff->NumberOfSymbols * sizeof(image_symbol), coff->LvaToFirstSymbol)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Check data length again + if(debug_data.length() < coff->NumberOfSymbols * sizeof(image_symbol) + coff->LvaToFirstSymbol) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Create COFF debug info structure + coff_debug_info coff_info(coff); + + //Enumerate debug symbols data + for(uint32_t i = 0; i < coff->NumberOfSymbols; ++i) + { + //Safe sum (checked above) + const image_symbol* sym = reinterpret_cast<const image_symbol*>(debug_data.data() + i * sizeof(image_symbol) + coff->LvaToFirstSymbol); + + coff_debug_info::coff_symbol symbol; + symbol.set_index(i); //Save symbol index + symbol.set_storage_class(sym->StorageClass); //Save storage class + symbol.set_type(sym->Type); //Save storage class + + //Check data length again + if(!pe_utils::is_sum_safe(i, sym->NumberOfAuxSymbols) + || (i + sym->NumberOfAuxSymbols) > coff->NumberOfSymbols + || debug_data.length() < (i + 1) * sizeof(image_symbol) + coff->LvaToFirstSymbol + sym->NumberOfAuxSymbols * sizeof(image_symbol)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //If symbol is filename + if(sym->StorageClass == image_sym_class_file) + { + //Save file name, it is situated just after this IMAGE_SYMBOL structure + std::string file_name(reinterpret_cast<const char*>(debug_data.data() + (i + 1) * sizeof(image_symbol)), sym->NumberOfAuxSymbols * sizeof(image_symbol)); + pe_utils::strip_nullbytes(file_name); + symbol.set_file_name(file_name); + + //Save symbol info + coff_info.add_symbol(symbol); + + //Move to next symbol + i += sym->NumberOfAuxSymbols; + continue; + } + + //Dump some other symbols + if(((sym->StorageClass == image_sym_class_static) + && (sym->NumberOfAuxSymbols == 0) + && (sym->SectionNumber == 1)) + || + ((sym->StorageClass == image_sym_class_external) + && ISFCN(sym->Type) + && (sym->SectionNumber > 0)) + ) + { + //Save RVA and section number + symbol.set_section_number(sym->SectionNumber); + symbol.set_rva(sym->Value); + + //If symbol has short name + if(sym->N.Name.Short) + { + //Copy and save symbol name + char name_buff[9]; + memcpy(name_buff, sym->N.ShortName, 8); + name_buff[8] = '\0'; + symbol.set_symbol_name(name_buff); + } + else + { + //Symbol has long name + + //Check possible overflows + if(!pe_utils::is_sum_safe(coff->LvaToFirstSymbol + coff->NumberOfSymbols * sizeof(image_symbol), sym->N.Name.Long)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Here we have an offset to the string table + uint32_t symbol_offset = coff->LvaToFirstSymbol + coff->NumberOfSymbols * sizeof(image_symbol) + sym->N.Name.Long; + + //Check data length + if(debug_data.length() < symbol_offset) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Check symbol name for null-termination + if(!pe_utils::is_null_terminated(debug_data.data() + symbol_offset, debug_data.length() - symbol_offset)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Save symbol name + symbol.set_symbol_name(debug_data.data() + symbol_offset); + } + + //Save symbol info + coff_info.add_symbol(symbol); + + //Move to next symbol + i += sym->NumberOfAuxSymbols; + continue; + } + } + + info.set_advanced_debug_info(coff_info); + } + break; + + case image_debug_type_codeview: + { + //Check data length + if(debug_data.length() < sizeof(OMFSignature*)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Get POMFSignature structure pointer from the very beginning of debug data + const OMFSignature* sig = reinterpret_cast<const OMFSignature*>(debug_data.data()); + if(!memcmp(sig->Signature, "RSDS", 4)) + { + //Signature is "RSDS" - PDB 7.0 + + //Check data length + if(debug_data.length() < sizeof(CV_INFO_PDB70)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + const CV_INFO_PDB70* pdb_data = reinterpret_cast<const CV_INFO_PDB70*>(debug_data.data()); + + //Check PDB file name null-termination + if(!pe_utils::is_null_terminated(pdb_data->PdbFileName, debug_data.length() - (sizeof(CV_INFO_PDB70) - 1 /* BYTE of filename in structure */))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + info.set_advanced_debug_info(pdb_7_0_info(pdb_data)); + } + else if(!memcmp(sig->Signature, "NB10", 4)) + { + //Signature is "NB10" - PDB 2.0 + + //Check data length + if(debug_data.length() < sizeof(CV_INFO_PDB20)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + const CV_INFO_PDB20* pdb_data = reinterpret_cast<const CV_INFO_PDB20*>(debug_data.data()); + + //Check PDB file name null-termination + if(!pe_utils::is_null_terminated(pdb_data->PdbFileName, debug_data.length() - (sizeof(CV_INFO_PDB20) - 1 /* BYTE of filename in structure */))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + info.set_advanced_debug_info(pdb_2_0_info(pdb_data)); + } + else if(!memcmp(sig->Signature, "NB09", 4)) + { + //CodeView 4.0, no structures available + info.set_advanced_info_type(debug_info::advanced_info_codeview_4_0); + } + else if(!memcmp(sig->Signature, "NB11", 4)) + { + //CodeView 5.0, no structures available + info.set_advanced_info_type(debug_info::advanced_info_codeview_5_0); + } + else if(!memcmp(sig->Signature, "NB05", 4)) + { + //Other CodeView, no structures available + info.set_advanced_info_type(debug_info::advanced_info_codeview); + } + } + + break; + + case image_debug_type_misc: + { + //Check data length + if(debug_data.length() < sizeof(image_debug_misc)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Get misc structure pointer + const image_debug_misc* misc_data = reinterpret_cast<const image_debug_misc*>(debug_data.data()); + + //Check misc data length + if(debug_data.length() < misc_data->Length /* Total length of record */) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Save advanced information + info.set_advanced_debug_info(misc_debug_info(misc_data)); + } + break; + } + } + + //Save debug information structure + ret.push_back(info); + + //Check possible overflow + if(!pe_utils::is_sum_safe(current_pos, sizeof(image_debug_directory))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Go to next debug entry + current_pos += sizeof(image_debug_directory); + directory = pe.section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_debug.h b/tools/pe_bliss/pe_debug.h new file mode 100644 index 0000000000..73a7e6860d --- /dev/null +++ b/tools/pe_bliss/pe_debug.h @@ -0,0 +1,324 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing advanced RSDS (PDB 7.0) information +class pdb_7_0_info +{ +public: + //Default constructor + pdb_7_0_info(); + //Constructor from data + explicit pdb_7_0_info(const pe_win::CV_INFO_PDB70* info); + + //Returns debug PDB 7.0 structure GUID + const pe_win::guid get_guid() const; + //Returns age of build + uint32_t get_age() const; + //Returns PDB file name / path + const std::string& get_pdb_file_name() const; + +private: + uint32_t age_; + pe_win::guid guid_; + std::string pdb_file_name_; +}; + +//Class representing advanced NB10 (PDB 2.0) information +class pdb_2_0_info +{ +public: + //Default constructor + pdb_2_0_info(); + //Constructor from data + explicit pdb_2_0_info(const pe_win::CV_INFO_PDB20* info); + + //Returns debug PDB 2.0 structure signature + uint32_t get_signature() const; + //Returns age of build + uint32_t get_age() const; + //Returns PDB file name / path + const std::string& get_pdb_file_name() const; + +private: + uint32_t age_; + uint32_t signature_; + std::string pdb_file_name_; +}; + +//Class representing advanced misc (IMAGE_DEBUG_TYPE_MISC) info +class misc_debug_info +{ +public: + //Default constructor + misc_debug_info(); + //Constructor from data + explicit misc_debug_info(const pe_win::image_debug_misc* info); + + //Returns debug data type + uint32_t get_data_type() const; + //Returns true if data type is exe name + bool is_exe_name() const; + + //Returns true if debug data is UNICODE + bool is_unicode() const; + //Returns debug data (ANSI or UNICODE) + const std::string& get_data_ansi() const; + const std::wstring& get_data_unicode() const; + +private: + uint32_t data_type_; + bool unicode_; + std::string debug_data_ansi_; + std::wstring debug_data_unicode_; +}; + +//Class representing COFF (IMAGE_DEBUG_TYPE_COFF) debug info +class coff_debug_info +{ +public: + //Structure representing COFF symbol + struct coff_symbol + { + public: + //Default constructor + coff_symbol(); + + //Returns storage class + uint32_t get_storage_class() const; + //Returns symbol index + uint32_t get_index() const; + //Returns section number + uint32_t get_section_number() const; + //Returns RVA + uint32_t get_rva() const; + //Returns type + uint16_t get_type() const; + + //Returns true if structure contains file name + bool is_file() const; + //Returns text data (symbol or file name) + const std::string& get_symbol() const; + + public: //These functions do not change everything inside image, they are used by PE class + //Sets storage class + void set_storage_class(uint32_t storage_class); + //Sets symbol index + void set_index(uint32_t index); + //Sets section number + void set_section_number(uint32_t section_number); + //Sets RVA + void set_rva(uint32_t rva); + //Sets type + void set_type(uint16_t type); + + //Sets file name + void set_file_name(const std::string& file_name); + //Sets symbol name + void set_symbol_name(const std::string& symbol_name); + + private: + uint32_t storage_class_; + uint32_t index_; + uint32_t section_number_, rva_; + uint16_t type_; + bool is_filename_; + std::string name_; + }; + +public: + typedef std::vector<coff_symbol> coff_symbols_list; + +public: + //Default constructor + coff_debug_info(); + //Constructor from data + explicit coff_debug_info(const pe_win::image_coff_symbols_header* info); + + //Returns number of symbols + uint32_t get_number_of_symbols() const; + //Returns virtual address of the first symbol + uint32_t get_lva_to_first_symbol() const; + //Returns number of line-number entries + uint32_t get_number_of_line_numbers() const; + //Returns virtual address of the first line-number entry + uint32_t get_lva_to_first_line_number() const; + //Returns relative virtual address of the first byte of code + uint32_t get_rva_to_first_byte_of_code() const; + //Returns relative virtual address of the last byte of code + uint32_t get_rva_to_last_byte_of_code() const; + //Returns relative virtual address of the first byte of data + uint32_t get_rva_to_first_byte_of_data() const; + //Returns relative virtual address of the last byte of data + uint32_t get_rva_to_last_byte_of_data() const; + + //Returns COFF symbols list + const coff_symbols_list& get_symbols() const; + +public: //These functions do not change everything inside image, they are used by PE class + //Adds COFF symbol + void add_symbol(const coff_symbol& sym); + +private: + uint32_t number_of_symbols_; + uint32_t lva_to_first_symbol_; + uint32_t number_of_line_numbers_; + uint32_t lva_to_first_line_number_; + uint32_t rva_to_first_byte_of_code_; + uint32_t rva_to_last_byte_of_code_; + uint32_t rva_to_first_byte_of_data_; + uint32_t rva_to_last_byte_of_data_; + +private: + coff_symbols_list symbols_; +}; + +//Class representing debug information +class debug_info +{ +public: + //Enumeration of debug information types + enum debug_info_type + { + debug_type_unknown, + debug_type_coff, + debug_type_codeview, + debug_type_fpo, + debug_type_misc, + debug_type_exception, + debug_type_fixup, + debug_type_omap_to_src, + debug_type_omap_from_src, + debug_type_borland, + debug_type_reserved10, + debug_type_clsid + }; + +public: + //Enumeration of advanced debug information types + enum advanced_info_type + { + advanced_info_none, //No advanced info + advanced_info_pdb_7_0, //PDB 7.0 + advanced_info_pdb_2_0, //PDB 2.0 + advanced_info_misc, //MISC debug info + advanced_info_coff, //COFF debug info + //No advanced info structures available for types below + advanced_info_codeview_4_0, //CodeView 4.0 + advanced_info_codeview_5_0, //CodeView 5.0 + advanced_info_codeview //CodeView + }; + +public: + //Default constructor + debug_info(); + //Constructor from data + explicit debug_info(const pe_win::image_debug_directory& debug); + //Copy constructor + debug_info(const debug_info& info); + //Copy assignment operator + debug_info& operator=(const debug_info& info); + //Destructor + ~debug_info(); + + //Returns debug characteristics + uint32_t get_characteristics() const; + //Returns debug datetimestamp + uint32_t get_time_stamp() const; + //Returns major version + uint32_t get_major_version() const; + //Returns minor version + uint32_t get_minor_version() const; + //Returns type of debug info (unchecked) + uint32_t get_type_raw() const; + //Returns type of debug info from debug_info_type enumeration + debug_info_type get_type() const; + //Returns size of debug data (internal, .pdb or other file doesn't count) + uint32_t get_size_of_data() const; + //Returns RVA of debug info when mapped to memory or zero, if info is not mapped + uint32_t get_rva_of_raw_data() const; + //Returns raw file pointer to raw data + uint32_t get_pointer_to_raw_data() const; + + //Returns advanced debug information type + advanced_info_type get_advanced_info_type() const; + //Returns advanced debug information or throws an exception, + //if requested information type is not contained by structure + template<typename AdvancedInfo> + const AdvancedInfo get_advanced_debug_info() const; + +public: //These functions do not change everything inside image, they are used by PE class + //Sets advanced debug information + void set_advanced_debug_info(const pdb_7_0_info& info); + void set_advanced_debug_info(const pdb_2_0_info& info); + void set_advanced_debug_info(const misc_debug_info& info); + void set_advanced_debug_info(const coff_debug_info& info); + + //Sets advanced debug information type, if no advanced info structure available + void set_advanced_info_type(advanced_info_type type); + +private: + uint32_t characteristics_; + uint32_t time_stamp_; + uint32_t major_version_, minor_version_; + uint32_t type_; + uint32_t size_of_data_; + uint32_t address_of_raw_data_; //RVA when mapped or 0 + uint32_t pointer_to_raw_data_; //RAW file offset + + //Union containing advanced debug information pointer + union advanced_info + { + public: + //Default constructor + advanced_info(); + + //Returns true if advanced debug info is present + bool is_present() const; + + public: + pdb_7_0_info* adv_pdb_7_0_info; + pdb_2_0_info* adv_pdb_2_0_info; + misc_debug_info* adv_misc_info; + coff_debug_info* adv_coff_info; + }; + + //Helper for advanced debug information copying + void copy_advanced_info(const debug_info& info); + //Helper for clearing any present advanced debug information + void free_present_advanced_info(); + + advanced_info advanced_debug_info_; + //Advanced information type + advanced_info_type advanced_info_type_; +}; + +typedef std::vector<debug_info> debug_info_list; + +//Returns debug information list +const debug_info_list get_debug_information(const pe_base& pe); +} diff --git a/tools/pe_bliss/pe_directory.cpp b/tools/pe_bliss/pe_directory.cpp new file mode 100644 index 0000000000..13ad2afc5d --- /dev/null +++ b/tools/pe_bliss/pe_directory.cpp @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_directory.h" + +namespace pe_bliss +{ +//Default constructor +image_directory::image_directory() + :rva_(0), size_(0) +{} + +//Constructor from data +image_directory::image_directory(uint32_t rva, uint32_t size) + :rva_(rva), size_(size) +{} + +//Returns RVA +uint32_t image_directory::get_rva() const +{ + return rva_; +} + +//Returns size +uint32_t image_directory::get_size() const +{ + return size_; +} + +//Sets RVA +void image_directory::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Sets size +void image_directory::set_size(uint32_t size) +{ + size_ = size; +} +} diff --git a/tools/pe_bliss/pe_directory.h b/tools/pe_bliss/pe_directory.h new file mode 100644 index 0000000000..a7b1ea7a5f --- /dev/null +++ b/tools/pe_bliss/pe_directory.h @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "stdint_defs.h" + +namespace pe_bliss +{ +//Class representing image directory data +class image_directory +{ +public: + //Default constructor + image_directory(); + //Constructor from data + image_directory(uint32_t rva, uint32_t size); + + //Returns RVA + uint32_t get_rva() const; + //Returns size + uint32_t get_size() const; + + //Sets RVA + void set_rva(uint32_t rva); + //Sets size + void set_size(uint32_t size); + +private: + uint32_t rva_; + uint32_t size_; +}; +} diff --git a/tools/pe_bliss/pe_dotnet.cpp b/tools/pe_bliss/pe_dotnet.cpp new file mode 100644 index 0000000000..f34a76eae8 --- /dev/null +++ b/tools/pe_bliss/pe_dotnet.cpp @@ -0,0 +1,186 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_dotnet.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//.NET +basic_dotnet_info::basic_dotnet_info() +{ + memset(&header_, 0, sizeof(header_)); +} + +//Constructor from data +basic_dotnet_info::basic_dotnet_info(const image_cor20_header& header) + :header_(header) +{} + +//Returns major runtime version +uint16_t basic_dotnet_info::get_major_runtime_version() const +{ + return header_.MajorRuntimeVersion; +} + +//Returns minor runtime version +uint16_t basic_dotnet_info::get_minor_runtime_version() const +{ + return header_.MinorRuntimeVersion; +} + +//Returns RVA of metadata (symbol table and startup information) +uint32_t basic_dotnet_info::get_rva_of_metadata() const +{ + return header_.MetaData.VirtualAddress; +} + +//Returns size of metadata (symbol table and startup information) +uint32_t basic_dotnet_info::get_size_of_metadata() const +{ + return header_.MetaData.Size; +} + +//Returns flags +uint32_t basic_dotnet_info::get_flags() const +{ + return header_.Flags; +} + +//Returns true if entry point is native +bool basic_dotnet_info::is_native_entry_point() const +{ + return (header_.Flags & comimage_flags_native_entrypoint) ? true : false; +} + +//Returns true if 32 bit required +bool basic_dotnet_info::is_32bit_required() const +{ + return (header_.Flags & comimage_flags_32bitrequired) ? true : false; +} + +//Returns true if image is IL library +bool basic_dotnet_info::is_il_library() const +{ + return (header_.Flags & comimage_flags_il_library) ? true : false; +} + +//Returns true if image uses IL only +bool basic_dotnet_info::is_il_only() const +{ + return (header_.Flags & comimage_flags_ilonly) ? true : false; +} + +//Returns entry point RVA (if entry point is native) +//Returns entry point managed token (if entry point is managed) +uint32_t basic_dotnet_info::get_entry_point_rva_or_token() const +{ + return header_.EntryPointToken; +} + +//Returns RVA of managed resources +uint32_t basic_dotnet_info::get_rva_of_resources() const +{ + return header_.Resources.VirtualAddress; +} + +//Returns size of managed resources +uint32_t basic_dotnet_info::get_size_of_resources() const +{ + return header_.Resources.Size; +} + +//Returns RVA of strong name signature +uint32_t basic_dotnet_info::get_rva_of_strong_name_signature() const +{ + return header_.StrongNameSignature.VirtualAddress; +} + +//Returns size of strong name signature +uint32_t basic_dotnet_info::get_size_of_strong_name_signature() const +{ + return header_.StrongNameSignature.Size; +} + +//Returns RVA of code manager table +uint32_t basic_dotnet_info::get_rva_of_code_manager_table() const +{ + return header_.CodeManagerTable.VirtualAddress; +} + +//Returns size of code manager table +uint32_t basic_dotnet_info::get_size_of_code_manager_table() const +{ + return header_.CodeManagerTable.Size; +} + +//Returns RVA of VTable fixups +uint32_t basic_dotnet_info::get_rva_of_vtable_fixups() const +{ + return header_.VTableFixups.VirtualAddress; +} + +//Returns size of VTable fixups +uint32_t basic_dotnet_info::get_size_of_vtable_fixups() const +{ + return header_.VTableFixups.Size; +} + +//Returns RVA of export address table jumps +uint32_t basic_dotnet_info::get_rva_of_export_address_table_jumps() const +{ + return header_.ExportAddressTableJumps.VirtualAddress; +} + +//Returns size of export address table jumps +uint32_t basic_dotnet_info::get_size_of_export_address_table_jumps() const +{ + return header_.ExportAddressTableJumps.Size; +} + +//Returns RVA of managed native header +//(precompiled header info, usually set to zero, for internal use) +uint32_t basic_dotnet_info::get_rva_of_managed_native_header() const +{ + return header_.ManagedNativeHeader.VirtualAddress; +} + +//Returns size of managed native header +//(precompiled header info, usually set to zero, for internal use) +uint32_t basic_dotnet_info::get_size_of_managed_native_header() const +{ + return header_.ManagedNativeHeader.Size; +} + +//Returns basic .NET information +//If image is not native, throws an exception +const basic_dotnet_info get_basic_dotnet_info(const pe_base& pe) +{ + //If there's no debug directory, return empty list + if(!pe.is_dotnet()) + throw pe_exception("Image does not have managed code", pe_exception::image_does_not_have_managed_code); + + //Return basic .NET information + return basic_dotnet_info(pe.section_data_from_rva<image_cor20_header>(pe.get_directory_rva(image_directory_entry_com_descriptor), section_data_virtual, true)); +} +} diff --git a/tools/pe_bliss/pe_dotnet.h b/tools/pe_bliss/pe_dotnet.h new file mode 100644 index 0000000000..96b0ac7d0a --- /dev/null +++ b/tools/pe_bliss/pe_dotnet.h @@ -0,0 +1,97 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing basic .NET header information +class basic_dotnet_info +{ +public: + //Default constructor + basic_dotnet_info(); + //Constructor from data + explicit basic_dotnet_info(const pe_win::image_cor20_header& header); + + //Returns major runtime version + uint16_t get_major_runtime_version() const; + //Returns minor runtime version + uint16_t get_minor_runtime_version() const; + + //Returns RVA of metadata (symbol table and startup information) + uint32_t get_rva_of_metadata() const; + //Returns size of metadata (symbol table and startup information) + uint32_t get_size_of_metadata() const; + + //Returns flags + uint32_t get_flags() const; + + //Returns true if entry point is native + bool is_native_entry_point() const; + //Returns true if 32 bit required + bool is_32bit_required() const; + //Returns true if image is IL library + bool is_il_library() const; + //Returns true if image uses IL only + bool is_il_only() const; + + //Returns entry point RVA (if entry point is native) + //Returns entry point managed token (if entry point is managed) + uint32_t get_entry_point_rva_or_token() const; + + //Returns RVA of managed resources + uint32_t get_rva_of_resources() const; + //Returns size of managed resources + uint32_t get_size_of_resources() const; + //Returns RVA of strong name signature + uint32_t get_rva_of_strong_name_signature() const; + //Returns size of strong name signature + uint32_t get_size_of_strong_name_signature() const; + //Returns RVA of code manager table + uint32_t get_rva_of_code_manager_table() const; + //Returns size of code manager table + uint32_t get_size_of_code_manager_table() const; + //Returns RVA of VTable fixups + uint32_t get_rva_of_vtable_fixups() const; + //Returns size of VTable fixups + uint32_t get_size_of_vtable_fixups() const; + //Returns RVA of export address table jumps + uint32_t get_rva_of_export_address_table_jumps() const; + //Returns size of export address table jumps + uint32_t get_size_of_export_address_table_jumps() const; + //Returns RVA of managed native header + //(precompiled header info, usually set to zero, for internal use) + uint32_t get_rva_of_managed_native_header() const; + //Returns size of managed native header + //(precompiled header info, usually set to zero, for internal use) + uint32_t get_size_of_managed_native_header() const; + +private: + pe_win::image_cor20_header header_; +}; + +//Returns basic .NET information +//If image is not native, throws an exception +const basic_dotnet_info get_basic_dotnet_info(const pe_base& pe); +} diff --git a/tools/pe_bliss/pe_exception.cpp b/tools/pe_bliss/pe_exception.cpp new file mode 100644 index 0000000000..3161f93599 --- /dev/null +++ b/tools/pe_bliss/pe_exception.cpp @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_exception.h" + +namespace pe_bliss +{ +//PE exception class constructors +pe_exception::pe_exception(const char* text, exception_id id) + :std::runtime_error(text), id_(id) +{} + +pe_exception::pe_exception(const std::string& text, exception_id id) + :std::runtime_error(text), id_(id) +{} + +//Returns exception ID +pe_exception::exception_id pe_exception::get_id() const +{ + return id_; +} +} diff --git a/tools/pe_bliss/pe_exception.h b/tools/pe_bliss/pe_exception.h new file mode 100644 index 0000000000..2b58a95772 --- /dev/null +++ b/tools/pe_bliss/pe_exception.h @@ -0,0 +1,130 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <exception> +#include <stdexcept> + +namespace pe_bliss +{ +//PE exception class +class pe_exception : public std::runtime_error +{ +public: + //Exception IDs + enum exception_id + { + unknown_error, + bad_pe_file, + bad_dos_header, + image_nt_headers_not_found, + error_reading_image_nt_headers, + error_reading_data_directories, + error_reading_file, + pe_signature_incorrect, + incorrect_number_of_rva_and_sizes, + error_changing_section_virtual_size, + section_number_incorrect, + section_table_incorrect, + incorrect_section_alignment, + incorrect_file_alignment, + incorrect_size_of_image, + incorrect_size_of_headers, + image_section_headers_not_found, + zero_section_sizes, + section_incorrect_addr_or_size, + section_not_found, + image_section_data_not_found, + no_section_found, + image_section_table_incorrect, + directory_does_not_exist, + rva_not_exists, + error_reading_section_header, + error_reading_overlay, + incorrect_address_conversion, + + incorrect_export_directory, + incorrect_import_directory, + incorrect_relocation_directory, + incorrect_tls_directory, + incorrect_config_directory, + incorrect_bound_import_directory, + incorrect_resource_directory, + incorrect_exception_directory, + incorrect_debug_directory, + + resource_directory_entry_error, + resource_directory_entry_not_found, + resource_data_entry_not_found, + resource_incorrect_bitmap, + resource_incorrect_icon, + resource_incorrect_cursor, + resource_incorrect_string_table, + resource_string_not_found, + resource_incorrect_message_table, + resource_incorrect_version_info, + + advanced_debug_information_request_error, + image_does_not_have_managed_code, + + section_is_empty, + data_is_empty, + stream_is_bad, + + section_is_not_attached, + insufficient_space, + + cannot_rebase_relocations, + + exports_list_is_empty, + duplicate_exported_function_ordinal, + duplicate_exported_function_name, + + version_info_string_does_not_exist, + + no_more_sections_can_be_added, + + no_icon_group_found, + no_cursor_group_found, + + encoding_convertion_error, + + error_expanding_section, + + cannot_rebuild_image + }; + +public: + //Class constructors + explicit pe_exception(const char* text, exception_id id = unknown_error); + explicit pe_exception(const std::string& text, exception_id id = unknown_error); + + //Returns exception ID from exception_id enumeration + exception_id get_id() const; + + //Destructor + virtual ~pe_exception() throw() + {} + +private: + exception_id id_; +}; +} diff --git a/tools/pe_bliss/pe_exception_directory.cpp b/tools/pe_bliss/pe_exception_directory.cpp new file mode 100644 index 0000000000..1813f02021 --- /dev/null +++ b/tools/pe_bliss/pe_exception_directory.cpp @@ -0,0 +1,177 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_exception_directory.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//EXCEPTION DIRECTORY (exists on PE+ only) +//Default constructor +exception_entry::exception_entry() + :begin_address_(0), end_address_(0), unwind_info_address_(0), + unwind_info_version_(0), + flags_(0), + size_of_prolog_(0), + count_of_codes_(0), + frame_register_(0), + frame_offset_(0) +{} + +//Constructor from data +exception_entry::exception_entry(const image_runtime_function_entry& entry, const unwind_info& unwind_info) + :begin_address_(entry.BeginAddress), end_address_(entry.EndAddress), unwind_info_address_(entry.UnwindInfoAddress), + unwind_info_version_(unwind_info.Version), + flags_(unwind_info.Flags), + size_of_prolog_(unwind_info.SizeOfProlog), + count_of_codes_(unwind_info.CountOfCodes), + frame_register_(unwind_info.FrameRegister), + frame_offset_(unwind_info.FrameOffset) +{} + +//Returns starting address of function, affected by exception unwinding +uint32_t exception_entry::get_begin_address() const +{ + return begin_address_; +} + +//Returns ending address of function, affected by exception unwinding +uint32_t exception_entry::get_end_address() const +{ + return end_address_; +} + +//Returns unwind info address +uint32_t exception_entry::get_unwind_info_address() const +{ + return unwind_info_address_; +} + +//Returns UNWIND_INFO structure version +uint8_t exception_entry::get_unwind_info_version() const +{ + return unwind_info_version_; +} + +//Returns unwind info flags +uint8_t exception_entry::get_flags() const +{ + return flags_; +} + +//The function has an exception handler that should be called +//when looking for functions that need to examine exceptions +bool exception_entry::has_exception_handler() const +{ + return (flags_ & unw_flag_ehandler) ? true : false; +} + +//The function has a termination handler that should be called +//when unwinding an exception +bool exception_entry::has_termination_handler() const +{ + return (flags_ & unw_flag_uhandler) ? true : false; +} + +//The unwind info structure is not the primary one for the procedure +bool exception_entry::is_chaininfo() const +{ + return (flags_ & unw_flag_chaininfo) ? true : false; +} + +//Returns size of function prolog +uint8_t exception_entry::get_size_of_prolog() const +{ + return size_of_prolog_; +} + +//Returns number of unwind slots +uint8_t exception_entry::get_number_of_unwind_slots() const +{ + return count_of_codes_; +} + +//If the function uses frame pointer +bool exception_entry::uses_frame_pointer() const +{ + return frame_register_ != 0; +} + +//Number of the nonvolatile register used as the frame pointer, +//using the same encoding for the operation info field of UNWIND_CODE nodes +uint8_t exception_entry::get_frame_pointer_register_number() const +{ + return frame_register_; +} + +//The scaled offset from RSP that is applied to the FP reg when it is established. +//The actual FP reg is set to RSP + 16 * this number, allowing offsets from 0 to 240 +uint8_t exception_entry::get_scaled_rsp_offset() const +{ + return frame_offset_; +} + +//Returns exception directory data (exists on PE+ only) +//Unwind opcodes are not listed, because their format and list are subject to change +const exception_entry_list get_exception_directory_data(const pe_base& pe) +{ + exception_entry_list ret; + + //If image doesn't have exception directory, return empty list + if(!pe.has_exception_directory()) + return ret; + + //Check the length in bytes of the section containing exception directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_exception), pe.get_directory_rva(image_directory_entry_exception), section_data_virtual, true) + < sizeof(image_runtime_function_entry)) + throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); + + unsigned long current_pos = pe.get_directory_rva(image_directory_entry_exception); + + //Check if structures are DWORD-aligned + if(current_pos % sizeof(uint32_t)) + throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); + + //First IMAGE_RUNTIME_FUNCTION_ENTRY table + image_runtime_function_entry exception_table = pe.section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true); + + //todo: virtual addresses BeginAddress and EndAddress are not checked to be inside image + while(exception_table.BeginAddress) + { + //Check addresses + if(exception_table.BeginAddress > exception_table.EndAddress) + throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); + + //Get unwind information + unwind_info info = pe.section_data_from_rva<unwind_info>(exception_table.UnwindInfoAddress, section_data_virtual, true); + + //Create exception entry and save it + ret.push_back(exception_entry(exception_table, info)); + + //Go to next exception entry + current_pos += sizeof(image_runtime_function_entry); + exception_table = pe.section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_exception_directory.h b/tools/pe_bliss/pe_exception_directory.h new file mode 100644 index 0000000000..6f4fc2298b --- /dev/null +++ b/tools/pe_bliss/pe_exception_directory.h @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing exception directory entry +class exception_entry +{ +public: + //Default constructor + exception_entry(); + //Constructor from data + exception_entry(const pe_win::image_runtime_function_entry& entry, const pe_win::unwind_info& unwind_info); + + //Returns starting address of function, affected by exception unwinding + uint32_t get_begin_address() const; + //Returns ending address of function, affected by exception unwinding + uint32_t get_end_address() const; + //Returns unwind info address + uint32_t get_unwind_info_address() const; + + //Returns UNWIND_INFO structure version + uint8_t get_unwind_info_version() const; + + //Returns unwind info flags + uint8_t get_flags() const; + //The function has an exception handler that should be called + //when looking for functions that need to examine exceptions + bool has_exception_handler() const; + //The function has a termination handler that should be called + //when unwinding an exception + bool has_termination_handler() const; + //The unwind info structure is not the primary one for the procedure + bool is_chaininfo() const; + + //Returns size of function prolog + uint8_t get_size_of_prolog() const; + + //Returns number of unwind slots + uint8_t get_number_of_unwind_slots() const; + + //If the function uses frame pointer + bool uses_frame_pointer() const; + //Number of the nonvolatile register used as the frame pointer, + //using the same encoding for the operation info field of UNWIND_CODE nodes + uint8_t get_frame_pointer_register_number() const; + //The scaled offset from RSP that is applied to the FP reg when it is established. + //The actual FP reg is set to RSP + 16 * this number, allowing offsets from 0 to 240 + uint8_t get_scaled_rsp_offset() const; + +private: + uint32_t begin_address_, end_address_, unwind_info_address_; + uint8_t unwind_info_version_; + uint8_t flags_; + uint8_t size_of_prolog_; + uint8_t count_of_codes_; + uint8_t frame_register_, frame_offset_; +}; + +typedef std::vector<exception_entry> exception_entry_list; + +//Returns exception directory data (exists on PE+ only) +//Unwind opcodes are not listed, because their format and list are subject to change +const exception_entry_list get_exception_directory_data(const pe_base& pe); +} diff --git a/tools/pe_bliss/pe_exports.cpp b/tools/pe_bliss/pe_exports.cpp new file mode 100644 index 0000000000..c2ad895554 --- /dev/null +++ b/tools/pe_bliss/pe_exports.cpp @@ -0,0 +1,700 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <set> +#include <algorithm> +#include <string.h> +#include "pe_exports.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//EXPORTS +//Default constructor +exported_function::exported_function() + :ordinal_(0), rva_(0), has_name_(false), name_ordinal_(0), forward_(false) +{} + +//Returns ordinal of function (actually, ordinal = hint + ordinal base) +uint16_t exported_function::get_ordinal() const +{ + return ordinal_; +} + +//Returns RVA of function +uint32_t exported_function::get_rva() const +{ + return rva_; +} + +//Returns name of function +const std::string& exported_function::get_name() const +{ + return name_; +} + +//Returns true if function has name and name ordinal +bool exported_function::has_name() const +{ + return has_name_; +} + +//Returns name ordinal of function +uint16_t exported_function::get_name_ordinal() const +{ + return name_ordinal_; +} + +//Returns true if function is forwarded to other library +bool exported_function::is_forwarded() const +{ + return forward_; +} + +//Returns the name of forwarded function +const std::string& exported_function::get_forwarded_name() const +{ + return forward_name_; +} + +//Sets ordinal of function +void exported_function::set_ordinal(uint16_t ordinal) +{ + ordinal_ = ordinal; +} + +//Sets RVA of function +void exported_function::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Sets name of function (or clears it, if empty name is passed) +void exported_function::set_name(const std::string& name) +{ + name_ = name; + has_name_ = !name.empty(); +} + +//Sets name ordinal +void exported_function::set_name_ordinal(uint16_t name_ordinal) +{ + name_ordinal_ = name_ordinal; +} + +//Sets forwarded function name (or clears it, if empty name is passed) +void exported_function::set_forwarded_name(const std::string& name) +{ + forward_name_ = name; + forward_ = !name.empty(); +} + +//Default constructor +export_info::export_info() + :characteristics_(0), + timestamp_(0), + major_version_(0), + minor_version_(0), + ordinal_base_(0), + number_of_functions_(0), + number_of_names_(0), + address_of_functions_(0), + address_of_names_(0), + address_of_name_ordinals_(0) +{} + +//Returns characteristics +uint32_t export_info::get_characteristics() const +{ + return characteristics_; +} + +//Returns timestamp +uint32_t export_info::get_timestamp() const +{ + return timestamp_; +} + +//Returns major version +uint16_t export_info::get_major_version() const +{ + return major_version_; +} + +//Returns minor version +uint16_t export_info::get_minor_version() const +{ + return minor_version_; +} + +//Returns DLL name +const std::string& export_info::get_name() const +{ + return name_; +} + +//Returns ordinal base +uint32_t export_info::get_ordinal_base() const +{ + return ordinal_base_; +} + +//Returns number of functions +uint32_t export_info::get_number_of_functions() const +{ + return number_of_functions_; +} + +//Returns number of function names +uint32_t export_info::get_number_of_names() const +{ + return number_of_names_; +} + +//Returns RVA of function address table +uint32_t export_info::get_rva_of_functions() const +{ + return address_of_functions_; +} + +//Returns RVA of function name address table +uint32_t export_info::get_rva_of_names() const +{ + return address_of_names_; +} + +//Returns RVA of name ordinals table +uint32_t export_info::get_rva_of_name_ordinals() const +{ + return address_of_name_ordinals_; +} + +//Sets characteristics +void export_info::set_characteristics(uint32_t characteristics) +{ + characteristics_ = characteristics; +} + +//Sets timestamp +void export_info::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Sets major version +void export_info::set_major_version(uint16_t major_version) +{ + major_version_ = major_version; +} + +//Sets minor version +void export_info::set_minor_version(uint16_t minor_version) +{ + minor_version_ = minor_version; +} + +//Sets DLL name +void export_info::set_name(const std::string& name) +{ + name_ = name; +} + +//Sets ordinal base +void export_info::set_ordinal_base(uint32_t ordinal_base) +{ + ordinal_base_ = ordinal_base; +} + +//Sets number of functions +void export_info::set_number_of_functions(uint32_t number_of_functions) +{ + number_of_functions_ = number_of_functions; +} + +//Sets number of function names +void export_info::set_number_of_names(uint32_t number_of_names) +{ + number_of_names_ = number_of_names; +} + +//Sets RVA of function address table +void export_info::set_rva_of_functions(uint32_t rva_of_functions) +{ + address_of_functions_ = rva_of_functions; +} + +//Sets RVA of function name address table +void export_info::set_rva_of_names(uint32_t rva_of_names) +{ + address_of_names_ = rva_of_names; +} + +//Sets RVA of name ordinals table +void export_info::set_rva_of_name_ordinals(uint32_t rva_of_name_ordinals) +{ + address_of_name_ordinals_ = rva_of_name_ordinals; +} + +const exported_functions_list get_exported_functions(const pe_base& pe, export_info* info); + +//Returns array of exported functions +const exported_functions_list get_exported_functions(const pe_base& pe) +{ + return get_exported_functions(pe, 0); +} + +//Returns array of exported functions and information about export +const exported_functions_list get_exported_functions(const pe_base& pe, export_info& info) +{ + return get_exported_functions(pe, &info); +} + +//Helper: sorts exported function list by ordinals +struct ordinal_sorter +{ +public: + bool operator()(const exported_function& func1, const exported_function& func2) const; +}; + +//Returns array of exported functions and information about export (if info != 0) +const exported_functions_list get_exported_functions(const pe_base& pe, export_info* info) +{ + //Returned exported functions info array + std::vector<exported_function> ret; + + if(pe.has_exports()) + { + //Check the length in bytes of the section containing export directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_export), + pe.get_directory_rva(image_directory_entry_export), section_data_virtual, true) + < sizeof(image_export_directory)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + image_export_directory exports = pe.section_data_from_rva<image_export_directory>(pe.get_directory_rva(image_directory_entry_export), section_data_virtual, true); + + unsigned long max_name_length; + + if(info) + { + //Save some export info data + info->set_characteristics(exports.Characteristics); + info->set_major_version(exports.MajorVersion); + info->set_minor_version(exports.MinorVersion); + + //Get byte count that we have for dll name + if((max_name_length = pe.section_data_length_from_rva(exports.Name, exports.Name, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Get dll name pointer + const char* dll_name = pe.section_data_from_rva(exports.Name, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(dll_name, max_name_length)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Save the rest of export information data + info->set_name(dll_name); + info->set_number_of_functions(exports.NumberOfFunctions); + info->set_number_of_names(exports.NumberOfNames); + info->set_ordinal_base(exports.Base); + info->set_rva_of_functions(exports.AddressOfFunctions); + info->set_rva_of_names(exports.AddressOfNames); + info->set_rva_of_name_ordinals(exports.AddressOfNameOrdinals); + info->set_timestamp(exports.TimeDateStamp); + } + + if(!exports.NumberOfFunctions) + return ret; + + //Check IMAGE_EXPORT_DIRECTORY fields + if(exports.NumberOfNames > exports.NumberOfFunctions) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Check some export directory fields + if((!exports.AddressOfNameOrdinals && exports.AddressOfNames) || + (exports.AddressOfNameOrdinals && !exports.AddressOfNames) || + !exports.AddressOfFunctions + || exports.NumberOfFunctions >= pe_utils::max_dword / sizeof(uint32_t) + || exports.NumberOfNames > pe_utils::max_dword / sizeof(uint32_t) + || !pe_utils::is_sum_safe(exports.AddressOfFunctions, exports.NumberOfFunctions * sizeof(uint32_t)) + || !pe_utils::is_sum_safe(exports.AddressOfNames, exports.NumberOfNames * sizeof(uint32_t)) + || !pe_utils::is_sum_safe(exports.AddressOfNameOrdinals, exports.NumberOfFunctions * sizeof(uint32_t)) + || !pe_utils::is_sum_safe(pe.get_directory_rva(image_directory_entry_export), pe.get_directory_size(image_directory_entry_export))) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Check if it is enough bytes to hold AddressOfFunctions table + if(pe.section_data_length_from_rva(exports.AddressOfFunctions, exports.AddressOfFunctions, section_data_virtual, true) + < exports.NumberOfFunctions * sizeof(uint32_t)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + if(exports.AddressOfNames) + { + //Check if it is enough bytes to hold name and ordinal tables + if(pe.section_data_length_from_rva(exports.AddressOfNameOrdinals, exports.AddressOfNameOrdinals, section_data_virtual, true) + < exports.NumberOfNames * sizeof(uint16_t)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + if(pe.section_data_length_from_rva(exports.AddressOfNames, exports.AddressOfNames, section_data_virtual, true) + < exports.NumberOfNames * sizeof(uint32_t)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + } + + for(uint32_t ordinal = 0; ordinal < exports.NumberOfFunctions; ordinal++) + { + //Get function address + //Sum and multiplication are safe (checked above) + uint32_t rva = pe.section_data_from_rva<uint32_t>(exports.AddressOfFunctions + ordinal * sizeof(uint32_t), section_data_virtual, true); + + //If we have a skip + if(!rva) + continue; + + exported_function func; + func.set_rva(rva); + + if(!pe_utils::is_sum_safe(exports.Base, ordinal) || exports.Base + ordinal > pe_utils::max_word) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + func.set_ordinal(static_cast<uint16_t>(ordinal + exports.Base)); + + //Scan for function name ordinal + for(uint32_t i = 0; i < exports.NumberOfNames; i++) + { + uint16_t ordinal2 = pe.section_data_from_rva<uint16_t>(exports.AddressOfNameOrdinals + i * sizeof(uint16_t), section_data_virtual, true); + + //If function has name (and name ordinal) + if(ordinal == ordinal2) + { + //Get function name + //Sum and multiplication are safe (checked above) + uint32_t function_name_rva = pe.section_data_from_rva<uint32_t>(exports.AddressOfNames + i * sizeof(uint32_t), section_data_virtual, true); + + //Get byte count that we have for function name + if((max_name_length = pe.section_data_length_from_rva(function_name_rva, function_name_rva, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Get function name pointer + const char* func_name = pe.section_data_from_rva(function_name_rva, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(func_name, max_name_length)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Save function info + func.set_name(func_name); + func.set_name_ordinal(ordinal2); + + //If the function is just a redirect, save its name + if(rva >= pe.get_directory_rva(image_directory_entry_export) + sizeof(image_directory_entry_export) && + rva < pe.get_directory_rva(image_directory_entry_export) + pe.get_directory_size(image_directory_entry_export)) + { + if((max_name_length = pe.section_data_length_from_rva(rva, rva, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Get forwarded function name pointer + const char* forwarded_func_name = pe.section_data_from_rva(rva, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(forwarded_func_name, max_name_length)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Set the name of forwarded function + func.set_forwarded_name(forwarded_func_name); + } + + break; + } + } + + //Add function info to output array + ret.push_back(func); + } + } + + return ret; +} + +//Helper export functions +//Returns pair: <ordinal base for supplied functions; maximum ordinal value for supplied functions> +const std::pair<uint16_t, uint16_t> get_export_ordinal_limits(const exported_functions_list& exports) +{ + if(exports.empty()) + return std::make_pair(0, 0); + + uint16_t max_ordinal = 0; //Maximum ordinal number + uint16_t ordinal_base = pe_utils::max_word; //Minimum ordinal value + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + const exported_function& func = (*it); + + //Calculate maximum and minimum ordinal numbers + max_ordinal = std::max<uint16_t>(max_ordinal, func.get_ordinal()); + ordinal_base = std::min<uint16_t>(ordinal_base, func.get_ordinal()); + } + + return std::make_pair(ordinal_base, max_ordinal); +} + +//Checks if exported function name already exists +bool exported_name_exists(const std::string& function_name, const exported_functions_list& exports) +{ + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + if((*it).has_name() && (*it).get_name() == function_name) + return true; + } + + return false; +} + +//Checks if exported function name already exists +bool exported_ordinal_exists(uint16_t ordinal, const exported_functions_list& exports) +{ + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + if((*it).get_ordinal() == ordinal) + return true; + } + + return false; +} + +//Helper: sorts exported function list by ordinals +bool ordinal_sorter::operator()(const exported_function& func1, const exported_function& func2) const +{ + return func1.get_ordinal() < func2.get_ordinal(); +} + +//Export directory rebuilder +//info - export information +//exported_functions_list - list of exported functions +//exports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from exports_section raw data start +//save_to_pe_headers - if true, new export directory information will be saved to PE image headers +//auto_strip_last_section - if true and exports are placed in the last section, it will be automatically stripped +//number_of_functions and number_of_names parameters don't matter in "info" when rebuilding, they're calculated independently +//characteristics, major_version, minor_version, timestamp and name are the only used members of "info" structure +//Returns new export directory information +//exported_functions_list is copied intentionally to be sorted by ordinal values later +//Name ordinals in exported function don't matter, they will be recalculated +const image_directory rebuild_exports(pe_base& pe, const export_info& info, exported_functions_list exports, section& exports_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that exports_section is attached to this PE image + if(!pe.section_attached(exports_section)) + throw pe_exception("Exports section must be attached to PE file", pe_exception::section_is_not_attached); + + //Needed space for strings + uint32_t needed_size_for_strings = static_cast<uint32_t>(info.get_name().length() + 1); + uint32_t number_of_names = 0; //Number of named functions + uint32_t max_ordinal = 0; //Maximum ordinal number + uint32_t ordinal_base = static_cast<uint32_t>(-1); //Minimum ordinal value + + if(exports.empty()) + ordinal_base = info.get_ordinal_base(); + + uint32_t needed_size_for_function_names = 0; //Needed space for function name strings + uint32_t needed_size_for_function_forwards = 0; //Needed space for function forwards names + + //List all exported functions + //Calculate needed size for function list + { + //Also check that there're no duplicate names and ordinals + std::set<std::string> used_function_names; + std::set<uint16_t> used_function_ordinals; + + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + const exported_function& func = (*it); + //Calculate maximum and minimum ordinal numbers + max_ordinal = std::max<uint32_t>(max_ordinal, func.get_ordinal()); + ordinal_base = std::min<uint32_t>(ordinal_base, func.get_ordinal()); + + //Check if ordinal is unique + if(!used_function_ordinals.insert(func.get_ordinal()).second) + throw pe_exception("Duplicate exported function ordinal", pe_exception::duplicate_exported_function_ordinal); + + if(func.has_name()) + { + //If function is named + ++number_of_names; + needed_size_for_function_names += static_cast<uint32_t>(func.get_name().length() + 1); + + //Check if it's name and name ordinal are unique + if(!used_function_names.insert(func.get_name()).second) + throw pe_exception("Duplicate exported function name", pe_exception::duplicate_exported_function_name); + } + + //If function is forwarded to another DLL + if(func.is_forwarded()) + needed_size_for_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1); + } + } + + //Sort functions by ordinal value + std::sort(exports.begin(), exports.end(), ordinal_sorter()); + + //Calculate needed space for different things... + needed_size_for_strings += needed_size_for_function_names; + needed_size_for_strings += needed_size_for_function_forwards; + uint32_t needed_size_for_function_name_ordinals = number_of_names * sizeof(uint16_t); + uint32_t needed_size_for_function_name_rvas = number_of_names * sizeof(uint32_t); + uint32_t needed_size_for_function_addresses = (max_ordinal - ordinal_base + 1) * sizeof(uint32_t); + + //Export directory header will be placed first + uint32_t directory_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + + uint32_t needed_size = sizeof(image_export_directory); //Calculate needed size for export tables and strings + //sizeof(IMAGE_EXPORT_DIRECTORY) = export directory header + + //Total needed space... + needed_size += needed_size_for_function_name_ordinals; //For list of names ordinals + needed_size += needed_size_for_function_addresses; //For function RVAs + needed_size += needed_size_for_strings; //For all strings + needed_size += needed_size_for_function_name_rvas; //For function name strings RVAs + + //Check if exports_section is last one. If it's not, check if there's enough place for exports data + if(&exports_section != &*(pe.get_image_sections().end() - 1) && + (exports_section.empty() || pe_utils::align_up(exports_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + directory_pos)) + throw pe_exception("Insufficient space for export directory", pe_exception::insufficient_space); + + std::string& raw_data = exports_section.get_raw_data(); + + //This will be done only if exports_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + directory_pos) + raw_data.resize(needed_size + directory_pos); //Expand section raw data + + //Library name will be placed after it + uint32_t current_pos_of_function_names = static_cast<uint32_t>(info.get_name().length() + 1 + directory_pos + sizeof(image_export_directory)); + //Next - function names + uint32_t current_pos_of_function_name_ordinals = current_pos_of_function_names + needed_size_for_function_names; + //Next - function name ordinals + uint32_t current_pos_of_function_forwards = current_pos_of_function_name_ordinals + needed_size_for_function_name_ordinals; + //Finally - function addresses + uint32_t current_pos_of_function_addresses = current_pos_of_function_forwards + needed_size_for_function_forwards; + //Next - function names RVAs + uint32_t current_pos_of_function_names_rvas = current_pos_of_function_addresses + needed_size_for_function_addresses; + + { + //Create export directory and fill it + image_export_directory dir = {0}; + dir.Characteristics = info.get_characteristics(); + dir.MajorVersion = info.get_major_version(); + dir.MinorVersion = info.get_minor_version(); + dir.TimeDateStamp = info.get_timestamp(); + dir.NumberOfFunctions = max_ordinal - ordinal_base + 1; + dir.NumberOfNames = number_of_names; + dir.Base = ordinal_base; + dir.AddressOfFunctions = pe.rva_from_section_offset(exports_section, current_pos_of_function_addresses); + dir.AddressOfNameOrdinals = pe.rva_from_section_offset(exports_section, current_pos_of_function_name_ordinals); + dir.AddressOfNames = pe.rva_from_section_offset(exports_section, current_pos_of_function_names_rvas); + dir.Name = pe.rva_from_section_offset(exports_section, directory_pos + sizeof(image_export_directory)); + + //Save it + memcpy(&raw_data[directory_pos], &dir, sizeof(dir)); + } + + //Sve library name + memcpy(&raw_data[directory_pos + sizeof(image_export_directory)], info.get_name().c_str(), info.get_name().length() + 1); + + //A map to sort function names alphabetically + typedef std::map<std::string, uint16_t> funclist; //function name; function name ordinal + funclist funcs; + + uint32_t last_ordinal = ordinal_base; + //Enumerate all exported functions + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + const exported_function& func = (*it); + + //If we're skipping some ordinals... + if(func.get_ordinal() > last_ordinal) + { + //Fill this function RVAs data with zeros + uint32_t len = sizeof(uint32_t) * (func.get_ordinal() - last_ordinal - 1); + if(len) + { + memset(&raw_data[current_pos_of_function_addresses], 0, len); + current_pos_of_function_addresses += len; + } + + //Save last encountered ordinal + last_ordinal = func.get_ordinal(); + } + + //If function is named, save its name ordinal and name in sorted alphabetically order + if(func.has_name()) + funcs.insert(std::make_pair(func.get_name(), static_cast<uint16_t>(func.get_ordinal() - ordinal_base))); //Calculate name ordinal + + //If function is forwarded to another DLL + if(func.is_forwarded()) + { + //Write its forwarded name and its RVA + uint32_t function_rva = pe.rva_from_section_offset(exports_section, current_pos_of_function_forwards); + memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva)); + current_pos_of_function_addresses += sizeof(function_rva); + + memcpy(&raw_data[current_pos_of_function_forwards], func.get_forwarded_name().c_str(), func.get_forwarded_name().length() + 1); + current_pos_of_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1); + } + else + { + //Write actual function RVA + uint32_t function_rva = func.get_rva(); + memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva)); + current_pos_of_function_addresses += sizeof(function_rva); + } + } + + //Enumerate sorted function names + for(funclist::const_iterator it = funcs.begin(); it != funcs.end(); ++it) + { + //Save function name RVA + uint32_t function_name_rva = pe.rva_from_section_offset(exports_section, current_pos_of_function_names); + memcpy(&raw_data[current_pos_of_function_names_rvas], &function_name_rva, sizeof(function_name_rva)); + current_pos_of_function_names_rvas += sizeof(function_name_rva); + + //Save function name + memcpy(&raw_data[current_pos_of_function_names], (*it).first.c_str(), (*it).first.length() + 1); + current_pos_of_function_names += static_cast<uint32_t>((*it).first.length() + 1); + + //Save function name ordinal + uint16_t name_ordinal = (*it).second; + memcpy(&raw_data[current_pos_of_function_name_ordinals], &name_ordinal, sizeof(name_ordinal)); + current_pos_of_function_name_ordinals += sizeof(name_ordinal); + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(exports_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(exports_section, directory_pos), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_export, ret.get_rva()); + pe.set_directory_size(image_directory_entry_export, ret.get_size()); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_exports.h b/tools/pe_bliss/pe_exports.h new file mode 100644 index 0000000000..127cf86ed6 --- /dev/null +++ b/tools/pe_bliss/pe_exports.h @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include <string> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing exported function +class exported_function +{ +public: + //Default constructor + exported_function(); + + //Returns ordinal of function (actually, ordinal = hint + ordinal base) + uint16_t get_ordinal() const; + + //Returns RVA of function + uint32_t get_rva() const; + + //Returns true if function has name and name ordinal + bool has_name() const; + //Returns name of function + const std::string& get_name() const; + //Returns name ordinal of function + uint16_t get_name_ordinal() const; + + //Returns true if function is forwarded to other library + bool is_forwarded() const; + //Returns the name of forwarded function + const std::string& get_forwarded_name() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You can also use them to rebuild export directory + + //Sets ordinal of function + void set_ordinal(uint16_t ordinal); + + //Sets RVA of function + void set_rva(uint32_t rva); + + //Sets name of function (or clears it, if empty name is passed) + void set_name(const std::string& name); + //Sets name ordinal + void set_name_ordinal(uint16_t name_ordinal); + + //Sets forwarded function name (or clears it, if empty name is passed) + void set_forwarded_name(const std::string& name); + +private: + uint16_t ordinal_; //Function ordinal + uint32_t rva_; //Function RVA + std::string name_; //Function name + bool has_name_; //true == function has name + uint16_t name_ordinal_; //Function name ordinal + bool forward_; //true == function is forwarded + std::string forward_name_; //Name of forwarded function +}; + +//Class representing export information +class export_info +{ +public: + //Default constructor + export_info(); + + //Returns characteristics + uint32_t get_characteristics() const; + //Returns timestamp + uint32_t get_timestamp() const; + //Returns major version + uint16_t get_major_version() const; + //Returns minor version + uint16_t get_minor_version() const; + //Returns DLL name + const std::string& get_name() const; + //Returns ordinal base + uint32_t get_ordinal_base() const; + //Returns number of functions + uint32_t get_number_of_functions() const; + //Returns number of function names + uint32_t get_number_of_names() const; + //Returns RVA of function address table + uint32_t get_rva_of_functions() const; + //Returns RVA of function name address table + uint32_t get_rva_of_names() const; + //Returns RVA of name ordinals table + uint32_t get_rva_of_name_ordinals() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You can also use them to rebuild export directory using rebuild_exports + + //Sets characteristics + void set_characteristics(uint32_t characteristics); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + //Sets major version + void set_major_version(uint16_t major_version); + //Sets minor version + void set_minor_version(uint16_t minor_version); + //Sets DLL name + void set_name(const std::string& name); + //Sets ordinal base + void set_ordinal_base(uint32_t ordinal_base); + //Sets number of functions + void set_number_of_functions(uint32_t number_of_functions); + //Sets number of function names + void set_number_of_names(uint32_t number_of_names); + //Sets RVA of function address table + void set_rva_of_functions(uint32_t rva_of_functions); + //Sets RVA of function name address table + void set_rva_of_names(uint32_t rva_of_names); + //Sets RVA of name ordinals table + void set_rva_of_name_ordinals(uint32_t rva_of_name_ordinals); + +private: + uint32_t characteristics_; + uint32_t timestamp_; + uint16_t major_version_; + uint16_t minor_version_; + std::string name_; + uint32_t ordinal_base_; + uint32_t number_of_functions_; + uint32_t number_of_names_; + uint32_t address_of_functions_; + uint32_t address_of_names_; + uint32_t address_of_name_ordinals_; +}; + +//Exported functions list typedef +typedef std::vector<exported_function> exported_functions_list; + +//Returns array of exported functions +const exported_functions_list get_exported_functions(const pe_base& pe); +//Returns array of exported functions and information about export +const exported_functions_list get_exported_functions(const pe_base& pe, export_info& info); + +//Helper export functions +//Returns pair: <ordinal base for supplied functions; maximum ordinal value for supplied functions> +const std::pair<uint16_t, uint16_t> get_export_ordinal_limits(const exported_functions_list& exports); + +//Checks if exported function name already exists +bool exported_name_exists(const std::string& function_name, const exported_functions_list& exports); + +//Checks if exported function ordinal already exists +bool exported_ordinal_exists(uint16_t ordinal, const exported_functions_list& exports); + +//Export directory rebuilder +//info - export information +//exported_functions_list - list of exported functions +//exports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from exports_section raw data start +//save_to_pe_headers - if true, new export directory information will be saved to PE image headers +//auto_strip_last_section - if true and exports are placed in the last section, it will be automatically stripped +//number_of_functions and number_of_names parameters don't matter in "info" when rebuilding, they're calculated independently +//characteristics, major_version, minor_version, timestamp and name are the only used members of "info" structure +//Returns new export directory information +//exported_functions_list is copied intentionally to be sorted by ordinal values later +//Name ordinals in exported function don't matter, they will be recalculated +const image_directory rebuild_exports(pe_base& pe, const export_info& info, exported_functions_list exports, section& exports_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/pe_factory.cpp b/tools/pe_bliss/pe_factory.cpp new file mode 100644 index 0000000000..f6d8a3e1ed --- /dev/null +++ b/tools/pe_bliss/pe_factory.cpp @@ -0,0 +1,43 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_factory.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +pe_base pe_factory::create_pe(std::istream& file, bool read_debug_raw_data) +{ + return pe_base::get_pe_type(file) == pe_type_32 + ? pe_base(file, pe_properties_32(), read_debug_raw_data) + : pe_base(file, pe_properties_64(), read_debug_raw_data); +} + +pe_base pe_factory::create_pe(const char* file_path, bool read_debug_raw_data) +{ + std::ifstream pe_file(file_path, std::ios::in | std::ios::binary); + if(!pe_file) + { + throw pe_exception("Error in open file.", pe_exception::stream_is_bad); + } + return pe_factory::create_pe(pe_file,read_debug_raw_data); +} +} diff --git a/tools/pe_bliss/pe_factory.h b/tools/pe_bliss/pe_factory.h new file mode 100644 index 0000000000..60b42d9b71 --- /dev/null +++ b/tools/pe_bliss/pe_factory.h @@ -0,0 +1,39 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <memory> +#include <istream> +#include <fstream> +#include "pe_base.h" + +namespace pe_bliss +{ +class pe_factory +{ +public: + //Creates pe_base class instance from PE or PE+ istream + //If read_bound_import_raw_data, raw bound import data will be read (used to get bound import info) + //If read_debug_raw_data, raw debug data will be read (used to get image debug info) + static pe_base create_pe(std::istream& file, bool read_debug_raw_data = true); + static pe_base create_pe(const char* file_path, bool read_debug_raw_data = true); +}; +} diff --git a/tools/pe_bliss/pe_imports.cpp b/tools/pe_bliss/pe_imports.cpp new file mode 100644 index 0000000000..0a6c01d6c0 --- /dev/null +++ b/tools/pe_bliss/pe_imports.cpp @@ -0,0 +1,777 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_imports.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//IMPORTS +//Default constructor +//If set_to_pe_headers = true, IMAGE_DIRECTORY_ENTRY_IMPORT entry will be reset +//to new value after import rebuilding +//If auto_zero_directory_entry_iat = true, IMAGE_DIRECTORY_ENTRY_IAT will be set to zero +//IMAGE_DIRECTORY_ENTRY_IAT is used by loader to temporarily make section, where IMAGE_DIRECTORY_ENTRY_IAT RVA points, writeable +//to be able to modify IAT thunks +import_rebuilder_settings::import_rebuilder_settings(bool set_to_pe_headers, bool auto_zero_directory_entry_iat) + :offset_from_section_start_(0), + build_original_iat_(true), + save_iat_and_original_iat_rvas_(true), + fill_missing_original_iats_(false), + set_to_pe_headers_(set_to_pe_headers), + zero_directory_entry_iat_(auto_zero_directory_entry_iat), + rewrite_iat_and_original_iat_contents_(false), + auto_strip_last_section_(true) +{} + +//Returns offset from section start where import directory data will be placed +uint32_t import_rebuilder_settings::get_offset_from_section_start() const +{ + return offset_from_section_start_; +} + +//Returns true if Original import address table (IAT) will be rebuilt +bool import_rebuilder_settings::build_original_iat() const +{ + return build_original_iat_; +} + +//Returns true if Original import address and import address tables will not be rebuilt, +//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero +bool import_rebuilder_settings::save_iat_and_original_iat_rvas() const +{ + return save_iat_and_original_iat_rvas_; +} + +//Returns true if Original import address and import address tables contents will be rewritten +//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero +//and save_iat_and_original_iat_rvas is true +bool import_rebuilder_settings::rewrite_iat_and_original_iat_contents() const +{ + return rewrite_iat_and_original_iat_contents_; +} + +//Returns true if original missing IATs will be rebuilt +//(only if IATs are saved) +bool import_rebuilder_settings::fill_missing_original_iats() const +{ + return fill_missing_original_iats_; +} + +//Returns true if PE headers should be updated automatically after rebuilding of imports +bool import_rebuilder_settings::auto_set_to_pe_headers() const +{ + return set_to_pe_headers_; +} + +//Returns true if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true +bool import_rebuilder_settings::zero_directory_entry_iat() const +{ + return zero_directory_entry_iat_; +} + +//Returns true if the last section should be stripped automatically, if imports are inside it +bool import_rebuilder_settings::auto_strip_last_section_enabled() const +{ + return auto_strip_last_section_; +} + +//Sets offset from section start where import directory data will be placed +void import_rebuilder_settings::set_offset_from_section_start(uint32_t offset) +{ + offset_from_section_start_ = offset; +} + +//Sets if Original import address table (IAT) will be rebuilt +void import_rebuilder_settings::build_original_iat(bool enable) +{ + build_original_iat_ = enable; +} + +//Sets if Original import address and import address tables will not be rebuilt, +//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero +void import_rebuilder_settings::save_iat_and_original_iat_rvas(bool enable, bool enable_rewrite_iat_and_original_iat_contents) +{ + save_iat_and_original_iat_rvas_ = enable; + if(save_iat_and_original_iat_rvas_) + rewrite_iat_and_original_iat_contents_ = enable_rewrite_iat_and_original_iat_contents; + else + rewrite_iat_and_original_iat_contents_ = false; +} + +//Sets if original missing IATs will be rebuilt +//(only if IATs are saved) +void import_rebuilder_settings::fill_missing_original_iats(bool enable) +{ + fill_missing_original_iats_ = enable; +} + +//Sets if PE headers should be updated automatically after rebuilding of imports +void import_rebuilder_settings::auto_set_to_pe_headers(bool enable) +{ + set_to_pe_headers_ = enable; +} + +//Sets if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true +void import_rebuilder_settings::zero_directory_entry_iat(bool enable) +{ + zero_directory_entry_iat_ = enable; +} + +//Sets if the last section should be stripped automatically, if imports are inside it, default true +void import_rebuilder_settings::enable_auto_strip_last_section(bool enable) +{ + auto_strip_last_section_ = enable; +} + +//Default constructor +imported_function::imported_function() + :hint_(0), ordinal_(0), iat_va_(0) +{} + +//Returns name of function +const std::string& imported_function::get_name() const +{ + return name_; +} + +//Returns true if imported function has name (and hint) +bool imported_function::has_name() const +{ + return !name_.empty(); +} + +//Returns hint +uint16_t imported_function::get_hint() const +{ + return hint_; +} + +//Returns ordinal of function +uint16_t imported_function::get_ordinal() const +{ + return ordinal_; +} + +//Returns IAT entry VA (usable if image has both IAT and original IAT and is bound) +uint64_t imported_function::get_iat_va() const +{ + return iat_va_; +} + +//Sets name of function +void imported_function::set_name(const std::string& name) +{ + name_ = name; +} + +//Sets hint +void imported_function::set_hint(uint16_t hint) +{ + hint_ = hint; +} + +//Sets ordinal +void imported_function::set_ordinal(uint16_t ordinal) +{ + ordinal_ = ordinal; +} + +//Sets IAT entry VA (usable if image has both IAT and original IAT and is bound) +void imported_function::set_iat_va(uint64_t va) +{ + iat_va_ = va; +} + +//Default constructor +import_library::import_library() + :rva_to_iat_(0), rva_to_original_iat_(0), timestamp_(0) +{} + +//Returns name of library +const std::string& import_library::get_name() const +{ + return name_; +} + +//Returns RVA to Import Address Table (IAT) +uint32_t import_library::get_rva_to_iat() const +{ + return rva_to_iat_; +} + +//Returns RVA to Original Import Address Table (Original IAT) +uint32_t import_library::get_rva_to_original_iat() const +{ + return rva_to_original_iat_; +} + +//Returns timestamp +uint32_t import_library::get_timestamp() const +{ + return timestamp_; +} + +//Sets name of library +void import_library::set_name(const std::string& name) +{ + name_ = name; +} + +//Sets RVA to Import Address Table (IAT) +void import_library::set_rva_to_iat(uint32_t rva_to_iat) +{ + rva_to_iat_ = rva_to_iat; +} + +//Sets RVA to Original Import Address Table (Original IAT) +void import_library::set_rva_to_original_iat(uint32_t rva_to_original_iat) +{ + rva_to_original_iat_ = rva_to_original_iat; +} + +//Sets timestamp +void import_library::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Returns imported functions list +const import_library::imported_list& import_library::get_imported_functions() const +{ + return imports_; +} + +//Adds imported function +void import_library::add_import(const imported_function& func) +{ + imports_.push_back(func); +} + +//Clears imported functions list +void import_library::clear_imports() +{ + imports_.clear(); +} + +const imported_functions_list get_imported_functions(const pe_base& pe) +{ + return (pe.get_pe_type() == pe_type_32 ? + get_imported_functions_base<pe_types_class_32>(pe) + : get_imported_functions_base<pe_types_class_64>(pe)); +} + +const image_directory rebuild_imports(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings) +{ + return (pe.get_pe_type() == pe_type_32 ? + rebuild_imports_base<pe_types_class_32>(pe, imports, import_section, import_settings) + : rebuild_imports_base<pe_types_class_64>(pe, imports, import_section, import_settings)); +} + +//Returns imported functions list with related libraries info +template<typename PEClassType> +const imported_functions_list get_imported_functions_base(const pe_base& pe) +{ + imported_functions_list ret; + + //If image has no imports, return empty array + if(!pe.has_imports()) + return ret; + + unsigned long current_descriptor_pos = pe.get_directory_rva(image_directory_entry_import); + //Get first IMAGE_IMPORT_DESCRIPTOR + image_import_descriptor import_descriptor = pe.section_data_from_rva<image_import_descriptor>(current_descriptor_pos, section_data_virtual, true); + + //Iterate them until we reach zero-element + //We don't need to check correctness of this, because exception will be thrown + //inside of loop if we go outsize of section + while(import_descriptor.Name) + { + //Get imported library information + import_library lib; + + unsigned long max_name_length; + //Get byte count that we have for library name + if((max_name_length = pe.section_data_length_from_rva(import_descriptor.Name, import_descriptor.Name, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Get DLL name pointer + const char* dll_name = pe.section_data_from_rva(import_descriptor.Name, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(dll_name, max_name_length)) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Set library name + lib.set_name(dll_name); + //Set library timestamp + lib.set_timestamp(import_descriptor.TimeDateStamp); + //Set library RVA to IAT and original IAT + lib.set_rva_to_iat(import_descriptor.FirstThunk); + lib.set_rva_to_original_iat(import_descriptor.OriginalFirstThunk); + + //Get RVA to IAT (it must be filled by loader when loading PE) + uint32_t current_thunk_rva = import_descriptor.FirstThunk; + typename PEClassType::BaseSize import_address_table = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_thunk_rva, section_data_virtual, true); + + //Get RVA to original IAT (lookup table), which must handle imported functions names + //Some linkers leave this pointer zero-filled + //Such image is valid, but it is not possible to restore imported functions names + //afted image was loaded, because IAT becomes the only one table + //containing both function names and function RVAs after loading + uint32_t current_original_thunk_rva = import_descriptor.OriginalFirstThunk; + typename PEClassType::BaseSize import_lookup_table = current_original_thunk_rva == 0 ? import_address_table : pe.section_data_from_rva<typename PEClassType::BaseSize>(current_original_thunk_rva, section_data_virtual, true); + if(current_original_thunk_rva == 0) + current_original_thunk_rva = current_thunk_rva; + + //List all imported functions for current DLL + if(import_lookup_table != 0 && import_address_table != 0) + { + while(true) + { + //Imported function description + imported_function func; + + //Get VA from IAT + typename PEClassType::BaseSize address = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_thunk_rva, section_data_virtual, true); + //Move pointer + current_thunk_rva += sizeof(typename PEClassType::BaseSize); + + //Jump to next DLL if we finished with this one + if(!address) + break; + + func.set_iat_va(address); + + //Get VA from original IAT + typename PEClassType::BaseSize lookup = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_original_thunk_rva, section_data_virtual, true); + //Move pointer + current_original_thunk_rva += sizeof(typename PEClassType::BaseSize); + + //Check if function is imported by ordinal + if((lookup & PEClassType::ImportSnapFlag) != 0) + { + //Set function ordinal + func.set_ordinal(static_cast<uint16_t>(lookup & 0xffff)); + } + else + { + //Get byte count that we have for function name + if(lookup > static_cast<uint32_t>(-1) - sizeof(uint16_t)) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Get maximum available length of function name + if((max_name_length = pe.section_data_length_from_rva(static_cast<uint32_t>(lookup + sizeof(uint16_t)), static_cast<uint32_t>(lookup + sizeof(uint16_t)), section_data_virtual, true)) < 2) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Get imported function name + const char* func_name = pe.section_data_from_rva(static_cast<uint32_t>(lookup + sizeof(uint16_t)), section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(func_name, max_name_length)) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //HINT in import table is ORDINAL in export table + uint16_t hint = pe.section_data_from_rva<uint16_t>(static_cast<uint32_t>(lookup), section_data_virtual, true); + + //Save hint and name + func.set_name(func_name); + func.set_hint(hint); + } + + //Add function to list + lib.add_import(func); + } + } + + //Check possible overflow + if(!pe_utils::is_sum_safe(current_descriptor_pos, sizeof(image_import_descriptor))) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Go to next library + current_descriptor_pos += sizeof(image_import_descriptor); + import_descriptor = pe.section_data_from_rva<image_import_descriptor>(current_descriptor_pos, section_data_virtual, true); + + //Save import information + ret.push_back(lib); + } + + //Return resulting list + return ret; +} + + +//Simple import directory rebuilder +//You can get all image imports with get_imported_functions() function +//You can use returned value to, for example, add new imported library with some functions +//to the end of list of imported libraries +//To keep PE file working, rebuild its imports with save_iat_and_original_iat_rvas = true (default) +//Don't add new imported functions to existing imported library entries, because this can cause +//rewriting of some used memory (or other IAT/orig.IAT fields) by system loader +//The safest way is just adding import libraries with functions to the end of imported_functions_list array +template<typename PEClassType> +const image_directory rebuild_imports_base(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings) +{ + //Check that import_section is attached to this PE image + if(!pe.section_attached(import_section)) + throw pe_exception("Import section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t needed_size = 0; //Calculate needed size for import structures and strings + uint32_t needed_size_for_strings = 0; //Calculate needed size for import strings (library and function names and hints) + uint32_t size_of_iat = 0; //Size of IAT structures + + needed_size += static_cast<uint32_t>((1 /* ending null descriptor */ + imports.size()) * sizeof(image_import_descriptor)); + + //Enumerate imported functions + for(imported_functions_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + needed_size_for_strings += static_cast<uint32_t>((*it).get_name().length() + 1 /* nullbyte */); + + const import_library::imported_list& funcs = (*it).get_imported_functions(); + + //IMAGE_THUNK_DATA + size_of_iat += static_cast<uint32_t>(sizeof(typename PEClassType::BaseSize) * (1 /*ending null */ + funcs.size())); + + //Enumerate all imported functions in library + for(import_library::imported_list::const_iterator f = funcs.begin(); f != funcs.end(); ++f) + { + if((*f).has_name()) + needed_size_for_strings += static_cast<uint32_t>((*f).get_name().length() + 1 /* nullbyte */ + sizeof(uint16_t) /* hint */); + } + } + + if(import_settings.build_original_iat() || import_settings.fill_missing_original_iats()) + needed_size += size_of_iat * 2; //We'll have two similar-sized IATs if we're building original IAT + else + needed_size += size_of_iat; + + needed_size += sizeof(typename PEClassType::BaseSize); //Maximum align for IAT and original IAT + + //Total needed size for import structures and strings + needed_size += needed_size_for_strings; + + //Check if import_section is last one. If it's not, check if there's enough place for import data + if(&import_section != &*(pe.get_image_sections().end() - 1) && + (import_section.empty() || pe_utils::align_up(import_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + import_settings.get_offset_from_section_start())) + throw pe_exception("Insufficient space for import directory", pe_exception::insufficient_space); + + std::string& raw_data = import_section.get_raw_data(); + + //This will be done only if image_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + import_settings.get_offset_from_section_start()) + raw_data.resize(needed_size + import_settings.get_offset_from_section_start()); //Expand section raw data + + uint32_t current_string_pointer = import_settings.get_offset_from_section_start();/* we will paste structures after strings */ + + //Position for IAT + uint32_t current_pos_for_iat = pe_utils::align_up(static_cast<uint32_t>(needed_size_for_strings + import_settings.get_offset_from_section_start() + (1 + imports.size()) * sizeof(image_import_descriptor)), sizeof(typename PEClassType::BaseSize)); + //Position for original IAT + uint32_t current_pos_for_original_iat = current_pos_for_iat + size_of_iat; + //Position for import descriptors + uint32_t current_pos_for_descriptors = needed_size_for_strings + import_settings.get_offset_from_section_start(); + + //Build imports + for(imported_functions_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + //Create import descriptor + image_import_descriptor descr; + memset(&descr, 0, sizeof(descr)); + descr.TimeDateStamp = (*it).get_timestamp(); //Restore timestamp + descr.Name = pe.rva_from_section_offset(import_section, current_string_pointer); //Library name RVA + + //If we should save IAT for current import descriptor + bool save_iats_for_this_descriptor = import_settings.save_iat_and_original_iat_rvas() && (*it).get_rva_to_iat() != 0; + //If we should write original IAT + bool write_original_iat = (!save_iats_for_this_descriptor && import_settings.build_original_iat()) || import_settings.fill_missing_original_iats(); + + //If we should rewrite saved original IAT for current import descriptor (without changing its position) + bool rewrite_saved_original_iat = save_iats_for_this_descriptor && import_settings.rewrite_iat_and_original_iat_contents() && import_settings.build_original_iat(); + //If we should rewrite saved IAT for current import descriptor (without changing its position) + bool rewrite_saved_iat = save_iats_for_this_descriptor && import_settings.rewrite_iat_and_original_iat_contents() && (*it).get_rva_to_iat() != 0; + + //Helper values if we're rewriting existing IAT or orig.IAT + uint32_t original_first_thunk = 0; + uint32_t first_thunk = 0; + + if(save_iats_for_this_descriptor) + { + //If there's no original IAT and we're asked to rebuild missing original IATs + if(!(*it).get_rva_to_original_iat() && import_settings.fill_missing_original_iats()) + descr.OriginalFirstThunk = import_settings.build_original_iat() ? pe.rva_from_section_offset(import_section, current_pos_for_original_iat) : 0; + else + descr.OriginalFirstThunk = import_settings.build_original_iat() ? (*it).get_rva_to_original_iat() : 0; + + descr.FirstThunk = (*it).get_rva_to_iat(); + + original_first_thunk = descr.OriginalFirstThunk; + first_thunk = descr.FirstThunk; + + if(rewrite_saved_original_iat) + { + if((*it).get_rva_to_original_iat()) + write_original_iat = true; + else + rewrite_saved_original_iat = false; + } + + if(rewrite_saved_iat) + save_iats_for_this_descriptor = false; + } + else + { + //We are creating new IAT and original IAT (if needed) + descr.OriginalFirstThunk = import_settings.build_original_iat() ? pe.rva_from_section_offset(import_section, current_pos_for_original_iat) : 0; + descr.FirstThunk = pe.rva_from_section_offset(import_section, current_pos_for_iat); + } + + //Save import descriptor + memcpy(&raw_data[current_pos_for_descriptors], &descr, sizeof(descr)); + current_pos_for_descriptors += sizeof(descr); + + //Save library name + memcpy(&raw_data[current_string_pointer], (*it).get_name().c_str(), (*it).get_name().length() + 1 /* nullbyte */); + current_string_pointer += static_cast<uint32_t>((*it).get_name().length() + 1 /* nullbyte */); + + //List all imported functions + const import_library::imported_list& funcs = (*it).get_imported_functions(); + for(import_library::imported_list::const_iterator f = funcs.begin(); f != funcs.end(); ++f) + { + if((*f).has_name()) //If function is imported by name + { + //Get RVA of IMAGE_IMPORT_BY_NAME + typename PEClassType::BaseSize rva_of_named_import = pe.rva_from_section_offset(import_section, current_string_pointer); + + if(!save_iats_for_this_descriptor) + { + if(write_original_iat) + { + //We're creating original IATs - so we can write to IAT saved VA (because IMAGE_IMPORT_BY_NAME will be read + //by PE loader from original IAT) + typename PEClassType::BaseSize iat_value = static_cast<typename PEClassType::BaseSize>((*f).get_iat_va()); + + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(iat_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &iat_value, sizeof(iat_value)); + + first_thunk += sizeof(iat_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &iat_value, sizeof(iat_value)); + current_pos_for_iat += sizeof(rva_of_named_import); + } + } + else + { + //Else - write to IAT RVA of IMAGE_IMPORT_BY_NAME + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(rva_of_named_import)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &rva_of_named_import, sizeof(rva_of_named_import)); + + first_thunk += sizeof(rva_of_named_import); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &rva_of_named_import, sizeof(rva_of_named_import)); + current_pos_for_iat += sizeof(rva_of_named_import); + } + } + } + + if(write_original_iat) + { + if(rewrite_saved_original_iat) + { + if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(rva_of_named_import)) + throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(original_first_thunk, true), &rva_of_named_import, sizeof(rva_of_named_import)); + + original_first_thunk += sizeof(rva_of_named_import); + } + else + { + //We're creating original IATs + memcpy(&raw_data[current_pos_for_original_iat], &rva_of_named_import, sizeof(rva_of_named_import)); + current_pos_for_original_iat += sizeof(rva_of_named_import); + } + } + + //Write IMAGE_IMPORT_BY_NAME (WORD hint + string function name) + uint16_t hint = (*f).get_hint(); + memcpy(&raw_data[current_string_pointer], &hint, sizeof(hint)); + memcpy(&raw_data[current_string_pointer + sizeof(uint16_t)], (*f).get_name().c_str(), (*f).get_name().length() + 1 /* nullbyte */); + current_string_pointer += static_cast<uint32_t>((*f).get_name().length() + 1 /* nullbyte */ + sizeof(uint16_t) /* hint */); + } + else //Function is imported by ordinal + { + uint16_t ordinal = (*f).get_ordinal(); + typename PEClassType::BaseSize thunk_value = ordinal; + thunk_value |= PEClassType::ImportSnapFlag; //Imported by ordinal + + if(!save_iats_for_this_descriptor) + { + if(write_original_iat) + { + //We're creating original IATs - so we can wtire to IAT saved VA (because ordinal will be read + //by PE loader from original IAT) + typename PEClassType::BaseSize iat_value = static_cast<typename PEClassType::BaseSize>((*f).get_iat_va()); + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(iat_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &iat_value, sizeof(iat_value)); + + first_thunk += sizeof(iat_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &iat_value, sizeof(iat_value)); + current_pos_for_iat += sizeof(thunk_value); + } + } + else + { + //Else - write ordinal to IAT + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &thunk_value, sizeof(thunk_value)); + + first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &thunk_value, sizeof(thunk_value)); + } + } + } + + //We're writing ordinal to original IAT slot + if(write_original_iat) + { + if(rewrite_saved_original_iat) + { + if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(original_first_thunk, true), &thunk_value, sizeof(thunk_value)); + + original_first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_original_iat], &thunk_value, sizeof(thunk_value)); + current_pos_for_original_iat += sizeof(thunk_value); + } + } + } + } + + if(!save_iats_for_this_descriptor) + { + //Ending null thunks + typename PEClassType::BaseSize thunk_value = 0; + + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &thunk_value, sizeof(thunk_value)); + + first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &thunk_value, sizeof(thunk_value)); + current_pos_for_iat += sizeof(thunk_value); + } + } + + if(write_original_iat) + { + //Ending null thunks + typename PEClassType::BaseSize thunk_value = 0; + + if(rewrite_saved_original_iat) + { + if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(original_first_thunk, true), &thunk_value, sizeof(thunk_value)); + + original_first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_original_iat], &thunk_value, sizeof(thunk_value)); + current_pos_for_original_iat += sizeof(thunk_value); + } + } + } + + { + //Null ending descriptor + image_import_descriptor descr; + memset(&descr, 0, sizeof(descr)); + memcpy(&raw_data[current_pos_for_descriptors], &descr, sizeof(descr)); + } + + //Strip data a little, if we saved some place + //We're allocating more space than needed, if present original IAT and IAT are saved + raw_data.resize(current_pos_for_original_iat); + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(import_section, import_settings.auto_strip_last_section_enabled()); + + //Return information about rebuilt import directory + image_directory ret(pe.rva_from_section_offset(import_section, import_settings.get_offset_from_section_start() + needed_size_for_strings), needed_size - needed_size_for_strings); + + //If auto-rewrite of PE headers is required + if(import_settings.auto_set_to_pe_headers()) + { + pe.set_directory_rva(image_directory_entry_import, ret.get_rva()); + pe.set_directory_size(image_directory_entry_import, ret.get_size()); + + //If we are requested to zero IMAGE_DIRECTORY_ENTRY_IAT also + if(import_settings.zero_directory_entry_iat()) + { + pe.set_directory_rva(image_directory_entry_iat, 0); + pe.set_directory_size(image_directory_entry_iat, 0); + } + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_imports.h b/tools/pe_bliss/pe_imports.h new file mode 100644 index 0000000000..681b5b59bd --- /dev/null +++ b/tools/pe_bliss/pe_imports.h @@ -0,0 +1,208 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include <string> +#include "pe_structures.h" +#include "pe_directory.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing imported function +class imported_function +{ +public: + //Default constructor + imported_function(); + + //Returns true if imported function has name (and hint) + bool has_name() const; + //Returns name of function + const std::string& get_name() const; + //Returns hint + uint16_t get_hint() const; + //Returns ordinal of function + uint16_t get_ordinal() const; + + //Returns IAT entry VA (usable if image has both IAT and original IAT and is bound) + uint64_t get_iat_va() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You also can use them to rebuild image imports + //Sets name of function + void set_name(const std::string& name); + //Sets hint + void set_hint(uint16_t hint); + //Sets ordinal + void set_ordinal(uint16_t ordinal); + + //Sets IAT entry VA (usable if image has both IAT and original IAT and is bound) + void set_iat_va(uint64_t rva); + +private: + std::string name_; //Function name + uint16_t hint_; //Hint + uint16_t ordinal_; //Ordinal + uint64_t iat_va_; +}; + +//Class representing imported library information +class import_library +{ +public: + typedef std::vector<imported_function> imported_list; + +public: + //Default constructor + import_library(); + + //Returns name of library + const std::string& get_name() const; + //Returns RVA to Import Address Table (IAT) + uint32_t get_rva_to_iat() const; + //Returns RVA to Original Import Address Table (Original IAT) + uint32_t get_rva_to_original_iat() const; + //Returns timestamp + uint32_t get_timestamp() const; + + //Returns imported functions list + const imported_list& get_imported_functions() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You also can use them to rebuild image imports + //Sets name of library + void set_name(const std::string& name); + //Sets RVA to Import Address Table (IAT) + void set_rva_to_iat(uint32_t rva_to_iat); + //Sets RVA to Original Import Address Table (Original IAT) + void set_rva_to_original_iat(uint32_t rva_to_original_iat); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + + //Adds imported function + void add_import(const imported_function& func); + //Clears imported functions list + void clear_imports(); + +private: + std::string name_; //Library name + uint32_t rva_to_iat_; //RVA to IAT + uint32_t rva_to_original_iat_; //RVA to original IAT + uint32_t timestamp_; //DLL TimeStamp + + imported_list imports_; +}; + +//Simple import directory rebuilder +//Class representing import rebuilder advanced settings +class import_rebuilder_settings +{ +public: + //Default constructor + //Default constructor + //If set_to_pe_headers = true, IMAGE_DIRECTORY_ENTRY_IMPORT entry will be reset + //to new value after import rebuilding + //If auto_zero_directory_entry_iat = true, IMAGE_DIRECTORY_ENTRY_IAT will be set to zero + //IMAGE_DIRECTORY_ENTRY_IAT is used by loader to temporarily make section, where IMAGE_DIRECTORY_ENTRY_IAT RVA points, writeable + //to be able to modify IAT thunks + explicit import_rebuilder_settings(bool set_to_pe_headers = true, bool auto_zero_directory_entry_iat = false); + + //Returns offset from section start where import directory data will be placed + uint32_t get_offset_from_section_start() const; + //Returns true if Original import address table (IAT) will be rebuilt + bool build_original_iat() const; + + //Returns true if Original import address and import address tables will not be rebuilt, + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + bool save_iat_and_original_iat_rvas() const; + //Returns true if Original import address and import address tables contents will be rewritten + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + //and save_iat_and_original_iat_rvas is true + bool rewrite_iat_and_original_iat_contents() const; + + //Returns true if original missing IATs will be rebuilt + //(only if IATs are saved) + bool fill_missing_original_iats() const; + //Returns true if PE headers should be updated automatically after rebuilding of imports + bool auto_set_to_pe_headers() const; + //Returns true if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true + bool zero_directory_entry_iat() const; + + //Returns true if the last section should be stripped automatically, if imports are inside it + bool auto_strip_last_section_enabled() const; + +public: //Setters + //Sets offset from section start where import directory data will be placed + void set_offset_from_section_start(uint32_t offset); + //Sets if Original import address table (IAT) will be rebuilt + void build_original_iat(bool enable); + //Sets if Original import address and import address tables will not be rebuilt, + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + //enable_rewrite_iat_and_original_iat_contents sets if Original import address and import address tables contents will be rewritten + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + //and save_iat_and_original_iat_rvas is true + void save_iat_and_original_iat_rvas(bool enable, bool enable_rewrite_iat_and_original_iat_contents = false); + //Sets if original missing IATs will be rebuilt + //(only if IATs are saved) + void fill_missing_original_iats(bool enable); + //Sets if PE headers should be updated automatically after rebuilding of imports + void auto_set_to_pe_headers(bool enable); + //Sets if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true + void zero_directory_entry_iat(bool enable); + + //Sets if the last section should be stripped automatically, if imports are inside it, default true + void enable_auto_strip_last_section(bool enable); + +private: + uint32_t offset_from_section_start_; + bool build_original_iat_; + bool save_iat_and_original_iat_rvas_; + bool fill_missing_original_iats_; + bool set_to_pe_headers_; + bool zero_directory_entry_iat_; + bool rewrite_iat_and_original_iat_contents_; + bool auto_strip_last_section_; +}; + +typedef std::vector<import_library> imported_functions_list; + + +//Returns imported functions list with related libraries info +const imported_functions_list get_imported_functions(const pe_base& pe); + +template<typename PEClassType> +const imported_functions_list get_imported_functions_base(const pe_base& pe); + + +//You can get all image imports with get_imported_functions() function +//You can use returned value to, for example, add new imported library with some functions +//to the end of list of imported libraries +//To keep PE file working, rebuild its imports with save_iat_and_original_iat_rvas = true (default) +//Don't add new imported functions to existing imported library entries, because this can cause +//rewriting of some used memory (or other IAT/orig.IAT fields) by system loader +//The safest way is just adding import libraries with functions to the end of imported_functions_list array +const image_directory rebuild_imports(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings = import_rebuilder_settings()); + +template<typename PEClassType> +const image_directory rebuild_imports_base(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings = import_rebuilder_settings()); +} diff --git a/tools/pe_bliss/pe_load_config.cpp b/tools/pe_bliss/pe_load_config.cpp new file mode 100644 index 0000000000..c05895fecd --- /dev/null +++ b/tools/pe_bliss/pe_load_config.cpp @@ -0,0 +1,557 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include <string.h> +#include "pe_load_config.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//IMAGE CONFIG +//Default constructor +image_config_info::image_config_info() + :time_stamp_(0), + major_version_(0), minor_version_(0), + global_flags_clear_(0), global_flags_set_(0), + critical_section_default_timeout_(0), + decommit_free_block_threshold_(0), decommit_total_free_threshold_(0), + lock_prefix_table_va_(0), + max_allocation_size_(0), + virtual_memory_threshold_(0), + process_affinity_mask_(0), + process_heap_flags_(0), + service_pack_version_(0), + edit_list_va_(0), + security_cookie_va_(0), + se_handler_table_va_(0), + se_handler_count_(0) +{} + +//Constructors from PE structures +template<typename ConfigStructure> +image_config_info::image_config_info(const ConfigStructure& info) + :time_stamp_(info.TimeDateStamp), + major_version_(info.MajorVersion), minor_version_(info.MinorVersion), + global_flags_clear_(info.GlobalFlagsClear), global_flags_set_(info.GlobalFlagsSet), + critical_section_default_timeout_(info.CriticalSectionDefaultTimeout), + decommit_free_block_threshold_(info.DeCommitFreeBlockThreshold), decommit_total_free_threshold_(info.DeCommitTotalFreeThreshold), + lock_prefix_table_va_(info.LockPrefixTable), + max_allocation_size_(info.MaximumAllocationSize), + virtual_memory_threshold_(info.VirtualMemoryThreshold), + process_affinity_mask_(info.ProcessAffinityMask), + process_heap_flags_(info.ProcessHeapFlags), + service_pack_version_(info.CSDVersion), + edit_list_va_(info.EditList), + security_cookie_va_(info.SecurityCookie), + se_handler_table_va_(info.SEHandlerTable), + se_handler_count_(info.SEHandlerCount) +{} + +//Instantiate template constructor with needed structures +template image_config_info::image_config_info(const image_load_config_directory32& info); +template image_config_info::image_config_info(const image_load_config_directory64& info); + +//Returns the date and time stamp value +uint32_t image_config_info::get_time_stamp() const +{ + return time_stamp_; +} + +//Returns major version number +uint16_t image_config_info::get_major_version() const +{ + return major_version_; +} + +//Returns minor version number +uint16_t image_config_info::get_minor_version() const +{ + return minor_version_; +} + +//Returns clear global flags +uint32_t image_config_info::get_global_flags_clear() const +{ + return global_flags_clear_; +} + +//Returns set global flags +uint32_t image_config_info::get_global_flags_set() const +{ + return global_flags_set_; +} + +//Returns critical section default timeout +uint32_t image_config_info::get_critical_section_default_timeout() const +{ + return critical_section_default_timeout_; +} + +//Get the size of the minimum block that +//must be freed before it is freed (de-committed), in bytes +uint64_t image_config_info::get_decommit_free_block_threshold() const +{ + return decommit_free_block_threshold_; +} + +//Returns the size of the minimum total memory +//that must be freed in the process heap before it is freed (de-committed), in bytes +uint64_t image_config_info::get_decommit_total_free_threshold() const +{ + return decommit_total_free_threshold_; +} + +//Returns VA of a list of addresses where the LOCK prefix is used +uint64_t image_config_info::get_lock_prefix_table_va() const +{ + return lock_prefix_table_va_; +} + +//Returns the maximum allocation size, in bytes +uint64_t image_config_info::get_max_allocation_size() const +{ + return max_allocation_size_; +} + +//Returns the maximum block size that can be allocated from heap segments, in bytes +uint64_t image_config_info::get_virtual_memory_threshold() const +{ + return virtual_memory_threshold_; +} + +//Returns process affinity mask +uint64_t image_config_info::get_process_affinity_mask() const +{ + return process_affinity_mask_; +} + +//Returns process heap flags +uint32_t image_config_info::get_process_heap_flags() const +{ + return process_heap_flags_; +} + +//Returns service pack version (CSDVersion) +uint16_t image_config_info::get_service_pack_version() const +{ + return service_pack_version_; +} + +//Returns VA of edit list (reserved by system) +uint64_t image_config_info::get_edit_list_va() const +{ + return edit_list_va_; +} + +//Returns a pointer to a cookie that is used by Visual C++ or GS implementation +uint64_t image_config_info::get_security_cookie_va() const +{ + return security_cookie_va_; +} + +//Returns VA of the sorted table of RVAs of each valid, unique handler in the image +uint64_t image_config_info::get_se_handler_table_va() const +{ + return se_handler_table_va_; +} + +//Returns the count of unique handlers in the table +uint64_t image_config_info::get_se_handler_count() const +{ + return se_handler_count_; +} + +//Returns SE Handler RVA list +const image_config_info::se_handler_list& image_config_info::get_se_handler_rvas() const +{ + return se_handlers_; +} + +//Returns Lock Prefix RVA list +const image_config_info::lock_prefix_rva_list& image_config_info::get_lock_prefix_rvas() const +{ + return lock_prefixes_; +} + +//Adds SE Handler RVA to list +void image_config_info::add_se_handler_rva(uint32_t rva) +{ + se_handlers_.push_back(rva); +} + +//Clears SE Handler list +void image_config_info::clear_se_handler_list() +{ + se_handlers_.clear(); +} + +//Adds Lock Prefix RVA to list +void image_config_info::add_lock_prefix_rva(uint32_t rva) +{ + lock_prefixes_.push_back(rva); +} + +//Clears Lock Prefix list +void image_config_info::clear_lock_prefix_list() +{ + lock_prefixes_.clear(); +} + +//Sets the date and time stamp value +void image_config_info::set_time_stamp(uint32_t time_stamp) +{ + time_stamp_ = time_stamp; +} + +//Sets major version number +void image_config_info::set_major_version(uint16_t major_version) +{ + major_version_ = major_version; +} + +//Sets minor version number +void image_config_info::set_minor_version(uint16_t minor_version) +{ + minor_version_ = minor_version; +} + +//Sets clear global flags +void image_config_info::set_global_flags_clear(uint32_t global_flags_clear) +{ + global_flags_clear_ = global_flags_clear; +} + +//Sets set global flags +void image_config_info::set_global_flags_set(uint32_t global_flags_set) +{ + global_flags_set_ = global_flags_set; +} + +//Sets critical section default timeout +void image_config_info::set_critical_section_default_timeout(uint32_t critical_section_default_timeout) +{ + critical_section_default_timeout_ = critical_section_default_timeout; +} + +//Sets the size of the minimum block that +//must be freed before it is freed (de-committed), in bytes +void image_config_info::set_decommit_free_block_threshold(uint64_t decommit_free_block_threshold) +{ + decommit_free_block_threshold_ = decommit_free_block_threshold; +} + +//Sets the size of the minimum total memory +//that must be freed in the process heap before it is freed (de-committed), in bytes +void image_config_info::set_decommit_total_free_threshold(uint64_t decommit_total_free_threshold) +{ + decommit_total_free_threshold_ = decommit_total_free_threshold; +} + +//Sets VA of a list of addresses where the LOCK prefix is used +//If you rebuild this list, VA will be re-assigned automatically +void image_config_info::set_lock_prefix_table_va(uint64_t lock_prefix_table_va) +{ + lock_prefix_table_va_ = lock_prefix_table_va; +} + +//Sets the maximum allocation size, in bytes +void image_config_info::set_max_allocation_size(uint64_t max_allocation_size) +{ + max_allocation_size_ = max_allocation_size; +} + +//Sets the maximum block size that can be allocated from heap segments, in bytes +void image_config_info::set_virtual_memory_threshold(uint64_t virtual_memory_threshold) +{ + virtual_memory_threshold_ = virtual_memory_threshold; +} + +//Sets process affinity mask +void image_config_info::set_process_affinity_mask(uint64_t process_affinity_mask) +{ + process_affinity_mask_ = process_affinity_mask; +} + +//Sets process heap flags +void image_config_info::set_process_heap_flags(uint32_t process_heap_flags) +{ + process_heap_flags_ = process_heap_flags; +} + +//Sets service pack version (CSDVersion) +void image_config_info::set_service_pack_version(uint16_t service_pack_version) +{ + service_pack_version_ = service_pack_version; +} + +//Sets VA of edit list (reserved by system) +void image_config_info::set_edit_list_va(uint64_t edit_list_va) +{ + edit_list_va_ = edit_list_va; +} + +//Sets a pointer to a cookie that is used by Visual C++ or GS implementation +void image_config_info::set_security_cookie_va(uint64_t security_cookie_va) +{ + security_cookie_va_ = security_cookie_va; +} + +//Sets VA of the sorted table of RVAs of each valid, unique handler in the image +//If you rebuild this list, VA will be re-assigned automatically +void image_config_info::set_se_handler_table_va(uint64_t se_handler_table_va) +{ + se_handler_table_va_ = se_handler_table_va; +} + +//Returns SE Handler RVA list +image_config_info::se_handler_list& image_config_info::get_se_handler_rvas() +{ + return se_handlers_; +} + +//Returns Lock Prefix RVA list +image_config_info::lock_prefix_rva_list& image_config_info::get_lock_prefix_rvas() +{ + return lock_prefixes_; +} + +//Returns image config info +//If image does not have config info, throws an exception +const image_config_info get_image_config(const pe_base& pe) +{ + return pe.get_pe_type() == pe_type_32 + ? get_image_config_base<pe_types_class_32>(pe) + : get_image_config_base<pe_types_class_64>(pe); +} + +//Image config rebuilder +const image_directory rebuild_image_config(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start, bool write_se_handlers, bool write_lock_prefixes, bool save_to_pe_header, bool auto_strip_last_section) +{ + return pe.get_pe_type() == pe_type_32 + ? rebuild_image_config_base<pe_types_class_32>(pe, info, image_config_section, offset_from_section_start, write_se_handlers, write_lock_prefixes, save_to_pe_header, auto_strip_last_section) + : rebuild_image_config_base<pe_types_class_64>(pe, info, image_config_section, offset_from_section_start, write_se_handlers, write_lock_prefixes, save_to_pe_header, auto_strip_last_section); +} + + +//Returns image config info +//If image does not have config info, throws an exception +template<typename PEClassType> +const image_config_info get_image_config_base(const pe_base& pe) +{ + //Check if image has config directory + if(!pe.has_config()) + throw pe_exception("Image does not have load config directory", pe_exception::directory_does_not_exist); + + //Get load config structure + typename PEClassType::ConfigStruct config_info = pe.section_data_from_rva<typename PEClassType::ConfigStruct>(pe.get_directory_rva(image_directory_entry_load_config), section_data_virtual); + + //Check size of config directory + if(config_info.Size != sizeof(config_info)) + throw pe_exception("Incorrect (or old) load config directory", pe_exception::incorrect_config_directory); + + //Fill return structure + image_config_info ret(config_info); + + //Check possible overflow + if(config_info.SEHandlerCount >= pe_utils::max_dword / sizeof(uint32_t) + || config_info.SEHandlerTable >= static_cast<typename PEClassType::BaseSize>(-1) - config_info.SEHandlerCount * sizeof(uint32_t)) + throw pe_exception("Incorrect load config directory", pe_exception::incorrect_config_directory); + + //Read sorted SE handler RVA list (if any) + for(typename PEClassType::BaseSize i = 0; i != config_info.SEHandlerCount; ++i) + ret.add_se_handler_rva(pe.section_data_from_va<uint32_t>(static_cast<typename PEClassType::BaseSize>(config_info.SEHandlerTable + i * sizeof(uint32_t)))); + + if(config_info.LockPrefixTable) + { + //Read Lock Prefix VA list (if any) + unsigned long current = 0; + while(true) + { + typename PEClassType::BaseSize lock_prefix_va = pe.section_data_from_va<typename PEClassType::BaseSize>(static_cast<typename PEClassType::BaseSize>(config_info.LockPrefixTable + current * sizeof(typename PEClassType::BaseSize))); + if(!lock_prefix_va) + break; + + ret.add_lock_prefix_rva(pe.va_to_rva(lock_prefix_va)); + + ++current; + } + } + + return ret; +} + +//Image config directory rebuilder +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +//If write_se_handlers = true, SE Handlers list will be written just after image config directory structure +//If write_lock_prefixes = true, Lock Prefixes address list will be written just after image config directory structure +template<typename PEClassType> +const image_directory rebuild_image_config_base(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start, bool write_se_handlers, bool write_lock_prefixes, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that image_config_section is attached to this PE image + if(!pe.section_attached(image_config_section)) + throw pe_exception("Image Config section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t alignment = pe_utils::align_up(offset_from_section_start, sizeof(typename PEClassType::BaseSize)) - offset_from_section_start; + + uint32_t needed_size = sizeof(typename PEClassType::ConfigStruct); //Calculate needed size for Image Config table + + uint32_t image_config_data_pos = offset_from_section_start + alignment; + + uint32_t current_pos_of_se_handlers = 0; + uint32_t current_pos_of_lock_prefixes = 0; + + if(write_se_handlers) + { + current_pos_of_se_handlers = needed_size + image_config_data_pos; + needed_size += static_cast<uint32_t>(info.get_se_handler_rvas().size()) * sizeof(uint32_t); //RVAs of SE Handlers + } + + if(write_lock_prefixes) + { + current_pos_of_lock_prefixes = needed_size + image_config_data_pos; + needed_size += static_cast<uint32_t>((info.get_lock_prefix_rvas().size() + 1) * sizeof(typename PEClassType::BaseSize)); //VAs of Lock Prefixes (and ending null element) + } + + //Check if image_config_section is last one. If it's not, check if there's enough place for Image Config data + if(&image_config_section != &*(pe.get_image_sections().end() - 1) && + (image_config_section.empty() || pe_utils::align_up(image_config_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + image_config_data_pos)) + throw pe_exception("Insufficient space for TLS directory", pe_exception::insufficient_space); + + std::string& raw_data = image_config_section.get_raw_data(); + + //This will be done only if image_config_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + image_config_data_pos) + raw_data.resize(needed_size + image_config_data_pos); //Expand section raw data + + //Create and fill Image Config structure + typename PEClassType::ConfigStruct image_config_section_struct = {0}; + image_config_section_struct.Size = sizeof(image_config_section_struct); + image_config_section_struct.TimeDateStamp = info.get_time_stamp(); + image_config_section_struct.MajorVersion = info.get_major_version(); + image_config_section_struct.MinorVersion = info.get_minor_version(); + image_config_section_struct.GlobalFlagsClear = info.get_global_flags_clear(); + image_config_section_struct.GlobalFlagsSet = info.get_global_flags_set(); + image_config_section_struct.CriticalSectionDefaultTimeout = info.get_critical_section_default_timeout(); + image_config_section_struct.DeCommitFreeBlockThreshold = static_cast<typename PEClassType::BaseSize>(info.get_decommit_free_block_threshold()); + image_config_section_struct.DeCommitTotalFreeThreshold = static_cast<typename PEClassType::BaseSize>(info.get_decommit_total_free_threshold()); + image_config_section_struct.MaximumAllocationSize = static_cast<typename PEClassType::BaseSize>(info.get_max_allocation_size()); + image_config_section_struct.VirtualMemoryThreshold = static_cast<typename PEClassType::BaseSize>(info.get_virtual_memory_threshold()); + image_config_section_struct.ProcessHeapFlags = info.get_process_heap_flags(); + image_config_section_struct.ProcessAffinityMask = static_cast<typename PEClassType::BaseSize>(info.get_process_affinity_mask()); + image_config_section_struct.CSDVersion = info.get_service_pack_version(); + image_config_section_struct.EditList = static_cast<typename PEClassType::BaseSize>(info.get_edit_list_va()); + image_config_section_struct.SecurityCookie = static_cast<typename PEClassType::BaseSize>(info.get_security_cookie_va()); + image_config_section_struct.SEHandlerCount = static_cast<typename PEClassType::BaseSize>(info.get_se_handler_rvas().size()); + + + if(write_se_handlers) + { + if(info.get_se_handler_rvas().empty()) + { + write_se_handlers = false; + image_config_section_struct.SEHandlerTable = 0; + } + else + { + typename PEClassType::BaseSize va; + pe.rva_to_va(pe.rva_from_section_offset(image_config_section, current_pos_of_se_handlers), va); + image_config_section_struct.SEHandlerTable = va; + } + } + else + { + image_config_section_struct.SEHandlerTable = static_cast<typename PEClassType::BaseSize>(info.get_se_handler_table_va()); + } + + if(write_lock_prefixes) + { + if(info.get_lock_prefix_rvas().empty()) + { + write_lock_prefixes = false; + image_config_section_struct.LockPrefixTable = 0; + } + else + { + typename PEClassType::BaseSize va; + pe.rva_to_va(pe.rva_from_section_offset(image_config_section, current_pos_of_lock_prefixes), va); + image_config_section_struct.LockPrefixTable = va; + } + } + else + { + image_config_section_struct.LockPrefixTable = static_cast<typename PEClassType::BaseSize>(info.get_lock_prefix_table_va()); + } + + //Write image config section + memcpy(&raw_data[image_config_data_pos], &image_config_section_struct, sizeof(image_config_section_struct)); + + if(write_se_handlers) + { + //Sort SE Handlers list + image_config_info::se_handler_list sorted_list = info.get_se_handler_rvas(); + std::sort(sorted_list.begin(), sorted_list.end()); + + //Write SE Handlers table + for(image_config_info::se_handler_list::const_iterator it = sorted_list.begin(); it != sorted_list.end(); ++it) + { + uint32_t se_handler_rva = *it; + memcpy(&raw_data[current_pos_of_se_handlers], &se_handler_rva, sizeof(se_handler_rva)); + current_pos_of_se_handlers += sizeof(se_handler_rva); + } + } + + if(write_lock_prefixes) + { + //Write Lock Prefixes VA list + for(image_config_info::lock_prefix_rva_list::const_iterator it = info.get_lock_prefix_rvas().begin(); it != info.get_lock_prefix_rvas().end(); ++it) + { + typename PEClassType::BaseSize lock_prefix_va; + pe.rva_to_va(*it, lock_prefix_va); + memcpy(&raw_data[current_pos_of_lock_prefixes], &lock_prefix_va, sizeof(lock_prefix_va)); + current_pos_of_lock_prefixes += sizeof(lock_prefix_va); + } + + { + //Ending null VA + typename PEClassType::BaseSize lock_prefix_va = 0; + memcpy(&raw_data[current_pos_of_lock_prefixes], &lock_prefix_va, sizeof(lock_prefix_va)); + } + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(image_config_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(image_config_section, image_config_data_pos), sizeof(typename PEClassType::ConfigStruct)); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_load_config, ret.get_rva()); + pe.set_directory_size(image_directory_entry_load_config, ret.get_size()); + } + + return ret; +} + +} diff --git a/tools/pe_bliss/pe_load_config.h b/tools/pe_bliss/pe_load_config.h new file mode 100644 index 0000000000..cb24072de7 --- /dev/null +++ b/tools/pe_bliss/pe_load_config.h @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing image configuration information +class image_config_info +{ +public: + typedef std::vector<uint32_t> se_handler_list; + typedef std::vector<uint32_t> lock_prefix_rva_list; + +public: + //Default constructor + image_config_info(); + //Constructors from PE structures (no checks) + template<typename ConfigStructure> + explicit image_config_info(const ConfigStructure& info); + + //Returns the date and time stamp value + uint32_t get_time_stamp() const; + //Returns major version number + uint16_t get_major_version() const; + //Returns minor version number + uint16_t get_minor_version() const; + //Returns clear global flags + uint32_t get_global_flags_clear() const; + //Returns set global flags + uint32_t get_global_flags_set() const; + //Returns critical section default timeout + uint32_t get_critical_section_default_timeout() const; + //Get the size of the minimum block that + //must be freed before it is freed (de-committed), in bytes + uint64_t get_decommit_free_block_threshold() const; + //Returns the size of the minimum total memory + //that must be freed in the process heap before it is freed (de-committed), in bytes + uint64_t get_decommit_total_free_threshold() const; + //Returns VA of a list of addresses where the LOCK prefix is used + uint64_t get_lock_prefix_table_va() const; + //Returns the maximum allocation size, in bytes + uint64_t get_max_allocation_size() const; + //Returns the maximum block size that can be allocated from heap segments, in bytes + uint64_t get_virtual_memory_threshold() const; + //Returns process affinity mask + uint64_t get_process_affinity_mask() const; + //Returns process heap flags + uint32_t get_process_heap_flags() const; + //Returns service pack version (CSDVersion) + uint16_t get_service_pack_version() const; + //Returns VA of edit list (reserved by system) + uint64_t get_edit_list_va() const; + //Returns a pointer to a cookie that is used by Visual C++ or GS implementation + uint64_t get_security_cookie_va() const; + //Returns VA of the sorted table of RVAs of each valid, unique handler in the image + uint64_t get_se_handler_table_va() const; + //Returns the count of unique handlers in the table + uint64_t get_se_handler_count() const; + + //Returns SE Handler RVA list + const se_handler_list& get_se_handler_rvas() const; + + //Returns Lock Prefix RVA list + const lock_prefix_rva_list& get_lock_prefix_rvas() const; + +public: //These functions do not change everything inside image, they are used by PE class + //Also you can use these functions to rebuild image config directory + + //Adds SE Handler RVA to list + void add_se_handler_rva(uint32_t rva); + //Clears SE Handler list + void clear_se_handler_list(); + + //Adds Lock Prefix RVA to list + void add_lock_prefix_rva(uint32_t rva); + //Clears Lock Prefix list + void clear_lock_prefix_list(); + + //Sets the date and time stamp value + void set_time_stamp(uint32_t time_stamp); + //Sets major version number + void set_major_version(uint16_t major_version); + //Sets minor version number + void set_minor_version(uint16_t minor_version); + //Sets clear global flags + void set_global_flags_clear(uint32_t global_flags_clear); + //Sets set global flags + void set_global_flags_set(uint32_t global_flags_set); + //Sets critical section default timeout + void set_critical_section_default_timeout(uint32_t critical_section_default_timeout); + //Sets the size of the minimum block that + //must be freed before it is freed (de-committed), in bytes + void set_decommit_free_block_threshold(uint64_t decommit_free_block_threshold); + //Sets the size of the minimum total memory + //that must be freed in the process heap before it is freed (de-committed), in bytes + void set_decommit_total_free_threshold(uint64_t decommit_total_free_threshold); + //Sets VA of a list of addresses where the LOCK prefix is used + //If you rebuild this list, VA will be re-assigned automatically + void set_lock_prefix_table_va(uint64_t lock_prefix_table_va); + //Sets the maximum allocation size, in bytes + void set_max_allocation_size(uint64_t max_allocation_size); + //Sets the maximum block size that can be allocated from heap segments, in bytes + void set_virtual_memory_threshold(uint64_t virtual_memory_threshold); + //Sets process affinity mask + void set_process_affinity_mask(uint64_t process_affinity_mask); + //Sets process heap flags + void set_process_heap_flags(uint32_t process_heap_flags); + //Sets service pack version (CSDVersion) + void set_service_pack_version(uint16_t service_pack_version); + //Sets VA of edit list (reserved by system) + void set_edit_list_va(uint64_t edit_list_va); + //Sets a pointer to a cookie that is used by Visual C++ or GS implementation + void set_security_cookie_va(uint64_t security_cookie_va); + //Sets VA of the sorted table of RVAs of each valid, unique handler in the image + //If you rebuild this list, VA will be re-assigned automatically + void set_se_handler_table_va(uint64_t se_handler_table_va); + + //Returns SE Handler RVA list + se_handler_list& get_se_handler_rvas(); + + //Returns Lock Prefix RVA list + lock_prefix_rva_list& get_lock_prefix_rvas(); + +private: + uint32_t time_stamp_; + uint16_t major_version_, minor_version_; + uint32_t global_flags_clear_, global_flags_set_; + uint32_t critical_section_default_timeout_; + uint64_t decommit_free_block_threshold_, decommit_total_free_threshold_; + uint64_t lock_prefix_table_va_; + uint64_t max_allocation_size_; + uint64_t virtual_memory_threshold_; + uint64_t process_affinity_mask_; + uint32_t process_heap_flags_; + uint16_t service_pack_version_; + uint64_t edit_list_va_; + uint64_t security_cookie_va_; + uint64_t se_handler_table_va_; + uint64_t se_handler_count_; + + se_handler_list se_handlers_; + lock_prefix_rva_list lock_prefixes_; +}; + +//Returns image config info +//If image does not have config info, throws an exception +const image_config_info get_image_config(const pe_base& pe); + +template<typename PEClassType> +const image_config_info get_image_config_base(const pe_base& pe); + + +//Image config directory rebuilder +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +//If write_se_handlers = true, SE Handlers list will be written just after image config directory structure +//If write_lock_prefixes = true, Lock Prefixes address list will be written just after image config directory structure +const image_directory rebuild_image_config(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start = 0, bool write_se_handlers = true, bool write_lock_prefixes = true, bool save_to_pe_header = true, bool auto_strip_last_section = true); + +template<typename PEClassType> +const image_directory rebuild_image_config_base(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start = 0, bool write_se_handlers = true, bool write_lock_prefixes = true, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/pe_properties.cpp b/tools/pe_bliss/pe_properties.cpp new file mode 100644 index 0000000000..8d1c2eac43 --- /dev/null +++ b/tools/pe_bliss/pe_properties.cpp @@ -0,0 +1,41 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_properties.h" + +namespace pe_bliss +{ +//Destructor +pe_properties::~pe_properties() +{} + +//Clears PE characteristics flag +void pe_properties::clear_characteristics_flags(uint16_t flags) +{ + set_characteristics(get_characteristics() & ~flags); +} + +//Sets PE characteristics flag +void pe_properties::set_characteristics_flags(uint16_t flags) +{ + set_characteristics(get_characteristics() | flags); +} +} diff --git a/tools/pe_bliss/pe_properties.h b/tools/pe_bliss/pe_properties.h new file mode 100644 index 0000000000..1db163e8b1 --- /dev/null +++ b/tools/pe_bliss/pe_properties.h @@ -0,0 +1,236 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <memory> +#include "pe_structures.h" + +namespace pe_bliss +{ +class pe_properties +{ +public: //Constructors + virtual std::auto_ptr<pe_properties> duplicate() const = 0; + + //Fills properly PE structures + virtual void create_pe(uint32_t section_alignment, uint16_t subsystem) = 0; + +public: + //Destructor + virtual ~pe_properties(); + + +public: //DIRECTORIES + //Returns true if directory exists + virtual bool directory_exists(uint32_t id) const = 0; + + //Removes directory + virtual void remove_directory(uint32_t id) = 0; + + //Returns directory RVA + virtual uint32_t get_directory_rva(uint32_t id) const = 0; + //Returns directory size + virtual uint32_t get_directory_size(uint32_t id) const = 0; + + //Sets directory RVA (just a value of PE header, no moving occurs) + virtual void set_directory_rva(uint32_t id, uint32_t rva) = 0; + //Sets directory size (just a value of PE header, no moving occurs) + virtual void set_directory_size(uint32_t id, uint32_t size) = 0; + + //Strips only zero DATA_DIRECTORY entries to count = min_count + //Returns resulting number of data directories + //strip_iat_directory - if true, even not empty IAT directory will be stripped + virtual uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true) = 0; + + +public: //IMAGE + //Returns PE type of this image + virtual pe_type get_pe_type() const = 0; + + +public: //PE HEADER + //Returns image base for PE32 and PE64 respectively + virtual uint32_t get_image_base_32() const = 0; + virtual uint64_t get_image_base_64() const = 0; + + //Sets new image base for PE32 + virtual void set_image_base(uint32_t base) = 0; + //Sets new image base for PE32/PE+ + virtual void set_image_base_64(uint64_t base) = 0; + + //Returns image entry point + virtual uint32_t get_ep() const = 0; + //Sets image entry point + virtual void set_ep(uint32_t new_ep) = 0; + + //Returns file alignment + virtual uint32_t get_file_alignment() const = 0; + //Returns section alignment + virtual uint32_t get_section_alignment() const = 0; + + //Sets heap size commit for PE32 and PE64 respectively + virtual void set_heap_size_commit(uint32_t size) = 0; + virtual void set_heap_size_commit(uint64_t size) = 0; + //Sets heap size reserve for PE32 and PE64 respectively + virtual void set_heap_size_reserve(uint32_t size) = 0; + virtual void set_heap_size_reserve(uint64_t size) = 0; + //Sets stack size commit for PE32 and PE64 respectively + virtual void set_stack_size_commit(uint32_t size) = 0; + virtual void set_stack_size_commit(uint64_t size) = 0; + //Sets stack size reserve for PE32 and PE64 respectively + virtual void set_stack_size_reserve(uint32_t size) = 0; + virtual void set_stack_size_reserve(uint64_t size) = 0; + + //Returns heap size commit for PE32 and PE64 respectively + virtual uint32_t get_heap_size_commit_32() const = 0; + virtual uint64_t get_heap_size_commit_64() const = 0; + //Returns heap size reserve for PE32 and PE64 respectively + virtual uint32_t get_heap_size_reserve_32() const = 0; + virtual uint64_t get_heap_size_reserve_64() const = 0; + //Returns stack size commit for PE32 and PE64 respectively + virtual uint32_t get_stack_size_commit_32() const = 0; + virtual uint64_t get_stack_size_commit_64() const = 0; + //Returns stack size reserve for PE32 and PE64 respectively + virtual uint32_t get_stack_size_reserve_32() const = 0; + virtual uint64_t get_stack_size_reserve_64() const = 0; + + //Returns virtual size of image + virtual uint32_t get_size_of_image() const = 0; + + //Returns number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual uint32_t get_number_of_rvas_and_sizes() const = 0; + //Sets number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual void set_number_of_rvas_and_sizes(uint32_t number) = 0; + + //Returns PE characteristics + virtual uint16_t get_characteristics() const = 0; + //Sets PE characteristics + virtual void set_characteristics(uint16_t ch) = 0; + + //Clears PE characteristics flag + void clear_characteristics_flags(uint16_t flags); + //Sets PE characteristics flag + void set_characteristics_flags(uint16_t flags); + + //Returns size of headers + virtual uint32_t get_size_of_headers() const = 0; + + //Returns subsystem + virtual uint16_t get_subsystem() const = 0; + + //Sets subsystem + virtual void set_subsystem(uint16_t subsystem) = 0; + + //Returns size of optional header + virtual uint16_t get_size_of_optional_header() const = 0; + + //Returns PE signature + virtual uint32_t get_pe_signature() const = 0; + + //Returns PE magic value + virtual uint32_t get_magic() const = 0; + + //Returns checksum of PE file from header + virtual uint32_t get_checksum() const = 0; + + //Sets checksum of PE file + virtual void set_checksum(uint32_t checksum) = 0; + + //Returns timestamp of PE file from header + virtual uint32_t get_time_date_stamp() const = 0; + + //Sets timestamp of PE file + virtual void set_time_date_stamp(uint32_t timestamp) = 0; + + //Returns Machine field value of PE file from header + virtual uint16_t get_machine() const = 0; + + //Sets Machine field value of PE file + virtual void set_machine(uint16_t machine) = 0; + + //Returns DLL Characteristics + virtual uint16_t get_dll_characteristics() const = 0; + + //Sets DLL Characteristics + virtual void set_dll_characteristics(uint16_t characteristics) = 0; + + //Sets required operation system version + virtual void set_os_version(uint16_t major, uint16_t minor) = 0; + + //Returns required operation system version (minor word) + virtual uint16_t get_minor_os_version() const = 0; + + //Returns required operation system version (major word) + virtual uint16_t get_major_os_version() const = 0; + + //Sets required subsystem version + virtual void set_subsystem_version(uint16_t major, uint16_t minor) = 0; + + //Returns required subsystem version (minor word) + virtual uint16_t get_minor_subsystem_version() const = 0; + + //Returns required subsystem version (major word) + virtual uint16_t get_major_subsystem_version() const = 0; + +public: //ADDRESS CONVERTIONS + //Virtual Address (VA) to Relative Virtual Address (RVA) convertions + //for PE32 and PE64 respectively + //bound_check checks integer overflow + virtual uint32_t va_to_rva(uint32_t va, bool bound_check = true) const = 0; + virtual uint32_t va_to_rva(uint64_t va, bool bound_check = true) const = 0; + + //Relative Virtual Address (RVA) to Virtual Address (VA) convertions + //for PE32 and PE64 respectively + virtual uint32_t rva_to_va_32(uint32_t rva) const = 0; + virtual uint64_t rva_to_va_64(uint32_t rva) const = 0; + + +public: //SECTIONS + //Returns number of sections + virtual uint16_t get_number_of_sections() const = 0; + +public: + //Sets number of sections + virtual void set_number_of_sections(uint16_t number) = 0; + //Sets virtual size of image + virtual void set_size_of_image(uint32_t size) = 0; + //Sets size of headers + virtual void set_size_of_headers(uint32_t size) = 0; + //Sets size of optional headers + virtual void set_size_of_optional_header(uint16_t size) = 0; + //Returns nt headers data pointer + virtual char* get_nt_headers_ptr() = 0; + //Returns nt headers data pointer + virtual const char* get_nt_headers_ptr() const = 0; + //Returns size of NT header + virtual uint32_t get_sizeof_nt_header() const = 0; + //Returns size of optional headers + virtual uint32_t get_sizeof_opt_headers() const = 0; + //Sets file alignment (no checks) + virtual void set_file_alignment_unchecked(uint32_t alignment) = 0; + //Sets base of code + virtual void set_base_of_code(uint32_t base) = 0; + //Returns base of code + virtual uint32_t get_base_of_code() const = 0; + //Returns needed PE magic for PE or PE+ (from template parameters) + virtual uint32_t get_needed_magic() const = 0; +}; +} diff --git a/tools/pe_bliss/pe_properties_generic.cpp b/tools/pe_bliss/pe_properties_generic.cpp new file mode 100644 index 0000000000..bcf6f2047d --- /dev/null +++ b/tools/pe_bliss/pe_properties_generic.cpp @@ -0,0 +1,645 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_properties_generic.h" +#include "pe_exception.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Constructor +template<typename PEClassType> +std::auto_ptr<pe_properties> pe_properties_generic<PEClassType>::duplicate() const +{ + return std::auto_ptr<pe_properties>(new pe_properties_generic<PEClassType>(*this)); +} + +//Fills properly PE structures +template<typename PEClassType> +void pe_properties_generic<PEClassType>::create_pe(uint32_t section_alignment, uint16_t subsystem) +{ + memset(&nt_headers_, 0, sizeof(nt_headers_)); + nt_headers_.Signature = 0x4550; //"PE" + nt_headers_.FileHeader.Machine = 0x14C; //i386 + nt_headers_.FileHeader.SizeOfOptionalHeader = sizeof(nt_headers_.OptionalHeader); + nt_headers_.OptionalHeader.Magic = PEClassType::Id; + nt_headers_.OptionalHeader.ImageBase = 0x400000; + nt_headers_.OptionalHeader.SectionAlignment = section_alignment; + nt_headers_.OptionalHeader.FileAlignment = 0x200; + nt_headers_.OptionalHeader.SizeOfHeaders = 1024; + nt_headers_.OptionalHeader.Subsystem = subsystem; + nt_headers_.OptionalHeader.SizeOfHeapReserve = 0x100000; + nt_headers_.OptionalHeader.SizeOfHeapCommit = 0x1000; + nt_headers_.OptionalHeader.SizeOfStackReserve = 0x100000; + nt_headers_.OptionalHeader.SizeOfStackCommit = 0x1000; + nt_headers_.OptionalHeader.NumberOfRvaAndSizes = 0x10; +} + +//Duplicate +template<typename PEClassType> +pe_properties_generic<PEClassType>::~pe_properties_generic() +{} + +//Returns true if directory exists +template<typename PEClassType> +bool pe_properties_generic<PEClassType>::directory_exists(uint32_t id) const +{ + return (nt_headers_.OptionalHeader.NumberOfRvaAndSizes - 1) >= id && + nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress; +} + +//Removes directory +template<typename PEClassType> +void pe_properties_generic<PEClassType>::remove_directory(uint32_t id) +{ + if(directory_exists(id)) + { + nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress = 0; + nt_headers_.OptionalHeader.DataDirectory[id].Size = 0; + + if(id == image_directory_entry_basereloc) + { + set_characteristics_flags(image_file_relocs_stripped); + set_dll_characteristics(get_dll_characteristics() & ~image_dllcharacteristics_dynamic_base); + } + else if(id == image_directory_entry_export) + { + clear_characteristics_flags(image_file_dll); + } + } +} + +//Returns directory RVA +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_directory_rva(uint32_t id) const +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + return nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress; +} + +//Returns directory size +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_directory_rva(uint32_t id, uint32_t va) +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress = va; +} + +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_directory_size(uint32_t id, uint32_t size) +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + nt_headers_.OptionalHeader.DataDirectory[id].Size = size; +} + +//Returns directory size +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_directory_size(uint32_t id) const +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + return nt_headers_.OptionalHeader.DataDirectory[id].Size; +} + +//Strips only zero DATA_DIRECTORY entries to count = min_count +//Returns resulting number of data directories +//strip_iat_directory - if true, even not empty IAT directory will be stripped +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::strip_data_directories(uint32_t min_count, bool strip_iat_directory) +{ + int i = nt_headers_.OptionalHeader.NumberOfRvaAndSizes - 1; + + //Enumerate all data directories from the end + for(; i >= 0; i--) + { + //If directory exists, break + if(nt_headers_.OptionalHeader.DataDirectory[i].VirtualAddress && (static_cast<uint32_t>(i) != image_directory_entry_iat || !strip_iat_directory)) + break; + + if(i <= static_cast<int>(min_count) - 2) + break; + } + + if(i == image_numberof_directory_entries - 1) + return image_numberof_directory_entries; + + //Return new number of data directories + return nt_headers_.OptionalHeader.NumberOfRvaAndSizes = i + 1; +} + +//Returns image base for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_image_base_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.ImageBase); +} + +//Returns image base for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_image_base_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.ImageBase); +} + +//Sets new image base +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_image_base(uint32_t base) +{ + nt_headers_.OptionalHeader.ImageBase = base; +} + +//Sets new image base +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_image_base_64(uint64_t base) +{ + nt_headers_.OptionalHeader.ImageBase = static_cast<typename PEClassType::BaseSize>(base); +} + +//Returns image entry point +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_ep() const +{ + return nt_headers_.OptionalHeader.AddressOfEntryPoint; +} + +//Sets image entry point +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_ep(uint32_t new_ep) +{ + nt_headers_.OptionalHeader.AddressOfEntryPoint = new_ep; +} + +//Returns file alignment +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_file_alignment() const +{ + return nt_headers_.OptionalHeader.FileAlignment; +} + +//Returns section alignment +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_section_alignment() const +{ + return nt_headers_.OptionalHeader.SectionAlignment; +} + +//Sets heap size commit for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_commit(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets heap size commit for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_commit(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets heap size reserve for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_reserve(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets heap size reserve for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_reserve(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size commit for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_commit(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size commit for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_commit(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size reserve for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_reserve(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size reserve for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_reserve(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Returns heap size commit for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_heap_size_commit_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfHeapCommit); +} + +//Returns heap size commit for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_heap_size_commit_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfHeapCommit); +} + +//Returns heap size reserve for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_heap_size_reserve_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfHeapReserve); +} + +//Returns heap size reserve for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_heap_size_reserve_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfHeapReserve); +} + +//Returns stack size commit for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_stack_size_commit_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfStackCommit); +} + +//Returns stack size commit for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_stack_size_commit_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfStackCommit); +} + +//Returns stack size reserve for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_stack_size_reserve_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfStackReserve); +} + +//Returns stack size reserve for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_stack_size_reserve_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfStackReserve); +} + +//Returns virtual size of image +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_size_of_image() const +{ + return nt_headers_.OptionalHeader.SizeOfImage; +} + +//Returns number of RVA and sizes (number of DATA_DIRECTORY entries) +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_number_of_rvas_and_sizes() const +{ + return nt_headers_.OptionalHeader.NumberOfRvaAndSizes; +} + +//Sets number of RVA and sizes (number of DATA_DIRECTORY entries) +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_number_of_rvas_and_sizes(uint32_t number) +{ + nt_headers_.OptionalHeader.NumberOfRvaAndSizes = number; +} + +//Returns PE characteristics +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_characteristics() const +{ + return nt_headers_.FileHeader.Characteristics; +} + +//Returns checksum of PE file from header +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_checksum() const +{ + return nt_headers_.OptionalHeader.CheckSum; +} + +//Sets checksum of PE file +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_checksum(uint32_t checksum) +{ + nt_headers_.OptionalHeader.CheckSum = checksum; +} + +//Returns DLL Characteristics +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_dll_characteristics() const +{ + return nt_headers_.OptionalHeader.DllCharacteristics; +} + +//Returns timestamp of PE file from header +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_time_date_stamp() const +{ + return nt_headers_.FileHeader.TimeDateStamp; +} + +//Sets timestamp of PE file +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_time_date_stamp(uint32_t timestamp) +{ + nt_headers_.FileHeader.TimeDateStamp = timestamp; +} + +//Sets DLL Characteristics +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_dll_characteristics(uint16_t characteristics) +{ + nt_headers_.OptionalHeader.DllCharacteristics = characteristics; +} + +//Returns Machine field value of PE file from header +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_machine() const +{ + return nt_headers_.FileHeader.Machine; +} + +//Sets Machine field value of PE file +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_machine(uint16_t machine) +{ + nt_headers_.FileHeader.Machine = machine; +} + +//Sets PE characteristics +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_characteristics(uint16_t ch) +{ + nt_headers_.FileHeader.Characteristics = ch; +} + +//Returns size of headers +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_size_of_headers() const +{ + return nt_headers_.OptionalHeader.SizeOfHeaders; +} + +//Returns subsystem +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_subsystem() const +{ + return nt_headers_.OptionalHeader.Subsystem; +} + +//Sets subsystem +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_subsystem(uint16_t subsystem) +{ + nt_headers_.OptionalHeader.Subsystem = subsystem; +} + +//Returns size of optional header +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_size_of_optional_header() const +{ + return nt_headers_.FileHeader.SizeOfOptionalHeader; +} + +//Returns PE signature +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_pe_signature() const +{ + return nt_headers_.Signature; +} + +//Returns PE magic value +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_magic() const +{ + return nt_headers_.OptionalHeader.Magic; +} + +//Sets required operation system version +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_os_version(uint16_t major, uint16_t minor) +{ + nt_headers_.OptionalHeader.MinorOperatingSystemVersion = minor; + nt_headers_.OptionalHeader.MajorOperatingSystemVersion = major; +} + +//Returns required operation system version (minor word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_minor_os_version() const +{ + return nt_headers_.OptionalHeader.MinorOperatingSystemVersion; +} + +//Returns required operation system version (major word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_major_os_version() const +{ + return nt_headers_.OptionalHeader.MajorOperatingSystemVersion; +} + +//Sets required subsystem version +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_subsystem_version(uint16_t major, uint16_t minor) +{ + nt_headers_.OptionalHeader.MinorSubsystemVersion = minor; + nt_headers_.OptionalHeader.MajorSubsystemVersion = major; +} + +//Returns required subsystem version (minor word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_minor_subsystem_version() const +{ + return nt_headers_.OptionalHeader.MinorSubsystemVersion; +} + +//Returns required subsystem version (major word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_major_subsystem_version() const +{ + return nt_headers_.OptionalHeader.MajorSubsystemVersion; +} + +//Virtual Address (VA) to Relative Virtual Address (RVA) convertions for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::va_to_rva(uint32_t va, bool bound_check) const +{ + if(bound_check && static_cast<uint64_t>(va) - nt_headers_.OptionalHeader.ImageBase > pe_utils::max_dword) + throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion); + + return static_cast<uint32_t>(va - nt_headers_.OptionalHeader.ImageBase); +} + +//Virtual Address (VA) to Relative Virtual Address (RVA) convertions for PE32/PE64 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::va_to_rva(uint64_t va, bool bound_check) const +{ + if(bound_check && va - nt_headers_.OptionalHeader.ImageBase > pe_utils::max_dword) + throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion); + + return static_cast<uint32_t>(va - nt_headers_.OptionalHeader.ImageBase); +} + +//Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::rva_to_va_32(uint32_t rva) const +{ + if(!pe_utils::is_sum_safe(rva, static_cast<uint32_t>(nt_headers_.OptionalHeader.ImageBase))) + throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion); + + return static_cast<uint32_t>(rva + nt_headers_.OptionalHeader.ImageBase); +} + +//Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::rva_to_va_64(uint32_t rva) const +{ + return static_cast<uint64_t>(rva) + nt_headers_.OptionalHeader.ImageBase; +} + +//Returns number of sections +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_number_of_sections() const +{ + return nt_headers_.FileHeader.NumberOfSections; +} + +//Sets number of sections +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_number_of_sections(uint16_t number) +{ + nt_headers_.FileHeader.NumberOfSections = number; +} + +//Sets virtual size of image +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_size_of_image(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfImage = size; +} + +//Sets size of headers +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_size_of_headers(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeaders = size; +} + +//Sets size of optional headers +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_size_of_optional_header(uint16_t size) +{ + nt_headers_.FileHeader.SizeOfOptionalHeader = size; +} + +//Returns nt headers data pointer +template<typename PEClassType> +char* pe_properties_generic<PEClassType>::get_nt_headers_ptr() +{ + return reinterpret_cast<char*>(&nt_headers_); +} + +//Returns nt headers data pointer +template<typename PEClassType> +const char* pe_properties_generic<PEClassType>::get_nt_headers_ptr() const +{ + return reinterpret_cast<const char*>(&nt_headers_); +} + +//Returns size of NT header +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_sizeof_nt_header() const +{ + return sizeof(typename PEClassType::NtHeaders); +} + +//Returns size of optional headers +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_sizeof_opt_headers() const +{ + return sizeof(typename PEClassType::OptHeaders); +} + +//Sets file alignment (no checks) +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_file_alignment_unchecked(uint32_t alignment) +{ + nt_headers_.OptionalHeader.FileAlignment = alignment; +} + +//Sets base of code +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_base_of_code(uint32_t base) +{ + nt_headers_.OptionalHeader.BaseOfCode = base; +} + +//Returns base of code +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_base_of_code() const +{ + return nt_headers_.OptionalHeader.BaseOfCode; +} + +//Returns needed PE magic for PE or PE+ (from template parameters) +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_needed_magic() const +{ + return PEClassType::Id; +} + +//Returns PE type of this image +template<typename PEClassType> +pe_type pe_properties_generic<PEClassType>::get_pe_type() const +{ + return PEClassType::Id == image_nt_optional_hdr32_magic ? pe_type_32 : pe_type_64; +} + +//Two used instantiations for PE32 (PE) and PE64 (PE+) +template class pe_properties_generic<pe_types_class_32>; +template class pe_properties_generic<pe_types_class_64>; +} diff --git a/tools/pe_bliss/pe_properties_generic.h b/tools/pe_bliss/pe_properties_generic.h new file mode 100644 index 0000000000..4ff906803c --- /dev/null +++ b/tools/pe_bliss/pe_properties_generic.h @@ -0,0 +1,277 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_properties.h" + +namespace pe_bliss +{ +//Helper class to reduce code size and ease its editing +template< + typename NtHeadersType, + typename OptHeadersType, + uint16_t IdVal, + typename BaseSizeType, + BaseSizeType ImportSnapFlagVal, + typename TLSStructType, + typename ConfigStructType> +class pe_types +{ +public: + typedef NtHeadersType NtHeaders; //NT HEADERS type + typedef OptHeadersType OptHeaders; //NT OPTIONAL HEADER type + typedef BaseSizeType BaseSize; //Base size of different values: DWORD or ULONGLONG + typedef TLSStructType TLSStruct; //TLS structure type + typedef ConfigStructType ConfigStruct; //Configuration structure type + + static const uint16_t Id = IdVal; //Magic of PE or PE+ + static const BaseSize ImportSnapFlag = ImportSnapFlagVal; //Import snap flag value +}; + +//Portable Executable derived class for PE and PE+ +//Describes PE/PE+ dependent things +template<typename PEClassType> +class pe_properties_generic : public pe_properties +{ +public: //Constructor + virtual std::auto_ptr<pe_properties> duplicate() const; + + //Fills properly PE structures + virtual void create_pe(uint32_t section_alignment, uint16_t subsystem); + +public: + //Destructor + virtual ~pe_properties_generic(); + + +public: //DIRECTORIES + //Returns true if directory exists + virtual bool directory_exists(uint32_t id) const; + + //Removes directory + virtual void remove_directory(uint32_t id); + + //Returns directory RVA + virtual uint32_t get_directory_rva(uint32_t id) const; + //Returns directory size + virtual uint32_t get_directory_size(uint32_t id) const; + + //Sets directory RVA (just a value of PE header, no moving occurs) + virtual void set_directory_rva(uint32_t id, uint32_t rva); + //Sets directory size (just a value of PE header, no moving occurs) + virtual void set_directory_size(uint32_t id, uint32_t size); + + //Strips only zero DATA_DIRECTORY entries to count = min_count + //Returns resulting number of data directories + //strip_iat_directory - if true, even not empty IAT directory will be stripped + virtual uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true); + + +public: //IMAGE + //Returns PE type of this image + virtual pe_type get_pe_type() const; + + +public: //PE HEADER + //Returns image base for PE32 and PE64 respectively + virtual uint32_t get_image_base_32() const; + virtual uint64_t get_image_base_64() const; + + //Sets new image base for PE32 + virtual void set_image_base(uint32_t base); + //Sets new image base for PE32/PE+ + virtual void set_image_base_64(uint64_t base); + + //Returns image entry point + virtual uint32_t get_ep() const; + //Sets image entry point + virtual void set_ep(uint32_t new_ep); + + //Returns file alignment + virtual uint32_t get_file_alignment() const; + //Returns section alignment + virtual uint32_t get_section_alignment() const; + + //Sets heap size commit for PE32 and PE64 respectively + virtual void set_heap_size_commit(uint32_t size); + virtual void set_heap_size_commit(uint64_t size); + //Sets heap size reserve for PE32 and PE64 respectively + virtual void set_heap_size_reserve(uint32_t size); + virtual void set_heap_size_reserve(uint64_t size); + //Sets stack size commit for PE32 and PE64 respectively + virtual void set_stack_size_commit(uint32_t size); + virtual void set_stack_size_commit(uint64_t size); + //Sets stack size reserve for PE32 and PE64 respectively + virtual void set_stack_size_reserve(uint32_t size); + virtual void set_stack_size_reserve(uint64_t size); + + //Returns heap size commit for PE32 and PE64 respectively + virtual uint32_t get_heap_size_commit_32() const; + virtual uint64_t get_heap_size_commit_64() const; + //Returns heap size reserve for PE32 and PE64 respectively + virtual uint32_t get_heap_size_reserve_32() const; + virtual uint64_t get_heap_size_reserve_64() const; + //Returns stack size commit for PE32 and PE64 respectively + virtual uint32_t get_stack_size_commit_32() const; + virtual uint64_t get_stack_size_commit_64() const; + //Returns stack size reserve for PE32 and PE64 respectively + virtual uint32_t get_stack_size_reserve_32() const; + virtual uint64_t get_stack_size_reserve_64() const; + + //Returns virtual size of image + virtual uint32_t get_size_of_image() const; + + //Returns number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual uint32_t get_number_of_rvas_and_sizes() const; + //Sets number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual void set_number_of_rvas_and_sizes(uint32_t number); + + //Returns PE characteristics + virtual uint16_t get_characteristics() const; + //Sets PE characteristics + virtual void set_characteristics(uint16_t ch); + + //Returns size of headers + virtual uint32_t get_size_of_headers() const; + + //Returns subsystem + virtual uint16_t get_subsystem() const; + + //Sets subsystem + virtual void set_subsystem(uint16_t subsystem); + + //Returns size of optional header + virtual uint16_t get_size_of_optional_header() const; + + //Returns PE signature + virtual uint32_t get_pe_signature() const; + + //Returns PE magic value + virtual uint32_t get_magic() const; + + //Returns checksum of PE file from header + virtual uint32_t get_checksum() const; + + //Sets checksum of PE file + virtual void set_checksum(uint32_t checksum); + + //Returns timestamp of PE file from header + virtual uint32_t get_time_date_stamp() const; + + //Sets timestamp of PE file + virtual void set_time_date_stamp(uint32_t timestamp); + + //Returns Machine field value of PE file from header + virtual uint16_t get_machine() const; + + //Sets Machine field value of PE file + virtual void set_machine(uint16_t machine); + + //Returns DLL Characteristics + virtual uint16_t get_dll_characteristics() const; + + //Sets DLL Characteristics + virtual void set_dll_characteristics(uint16_t characteristics); + + //Sets required operation system version + virtual void set_os_version(uint16_t major, uint16_t minor); + + //Returns required operation system version (minor word) + virtual uint16_t get_minor_os_version() const; + + //Returns required operation system version (major word) + virtual uint16_t get_major_os_version() const; + + //Sets required subsystem version + virtual void set_subsystem_version(uint16_t major, uint16_t minor); + + //Returns required subsystem version (minor word) + virtual uint16_t get_minor_subsystem_version() const; + + //Returns required subsystem version (major word) + virtual uint16_t get_major_subsystem_version() const; + +public: //ADDRESS CONVERTIONS + //Virtual Address (VA) to Relative Virtual Address (RVA) convertions + //for PE32 and PE64 respectively + //bound_check checks integer overflow + virtual uint32_t va_to_rva(uint32_t va, bool bound_check = true) const; + virtual uint32_t va_to_rva(uint64_t va, bool bound_check = true) const; + + //Relative Virtual Address (RVA) to Virtual Address (VA) convertions + //for PE32 and PE64 respectively + virtual uint32_t rva_to_va_32(uint32_t rva) const; + virtual uint64_t rva_to_va_64(uint32_t rva) const; + + +public: //SECTIONS + //Returns number of sections + virtual uint16_t get_number_of_sections() const; + +protected: + typename PEClassType::NtHeaders nt_headers_; //NT headers (PE32 or PE64) + +public: + //Sets number of sections + virtual void set_number_of_sections(uint16_t number); + //Sets virtual size of image + virtual void set_size_of_image(uint32_t size); + //Sets size of headers + virtual void set_size_of_headers(uint32_t size); + //Sets size of optional headers + virtual void set_size_of_optional_header(uint16_t size); + //Returns nt headers data pointer + virtual char* get_nt_headers_ptr(); + //Returns nt headers data pointer + virtual const char* get_nt_headers_ptr() const; + //Returns size of NT header + virtual uint32_t get_sizeof_nt_header() const; + //Returns size of optional headers + virtual uint32_t get_sizeof_opt_headers() const; + //Sets file alignment (no checks) + virtual void set_file_alignment_unchecked(uint32_t alignment); + //Sets base of code + virtual void set_base_of_code(uint32_t base); + //Returns base of code + virtual uint32_t get_base_of_code() const; + //Returns needed PE magic for PE or PE+ (from template parameters) + virtual uint32_t get_needed_magic() const; +}; + +//Two used typedefs for PE32 (PE) and PE64 (PE+) +typedef pe_types<pe_win::image_nt_headers32, + pe_win::image_optional_header32, + pe_win::image_nt_optional_hdr32_magic, + uint32_t, + pe_win::image_ordinal_flag32, + pe_win::image_tls_directory32, + pe_win::image_load_config_directory32> pe_types_class_32; + +typedef pe_types<pe_win::image_nt_headers64, + pe_win::image_optional_header64, + pe_win::image_nt_optional_hdr64_magic, + uint64_t, + pe_win::image_ordinal_flag64, + pe_win::image_tls_directory64, + pe_win::image_load_config_directory64> pe_types_class_64; + +typedef pe_properties_generic<pe_types_class_32> pe_properties_32; +typedef pe_properties_generic<pe_types_class_64> pe_properties_64; +} diff --git a/tools/pe_bliss/pe_rebuilder.cpp b/tools/pe_bliss/pe_rebuilder.cpp new file mode 100644 index 0000000000..faf5803b8c --- /dev/null +++ b/tools/pe_bliss/pe_rebuilder.cpp @@ -0,0 +1,214 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_rebuilder.h" +#include "pe_base.h" +#include "pe_structures.h" +#include "pe_exception.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Rebuilds PE image headers +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, image_dos_header& dos_header, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) +{ + dos_header = pe.get_dos_header(); + + if(strip_dos_header) + { + //Strip stub overlay + pe.strip_stub_overlay(); + //BaseOfCode NT Headers field now overlaps + //e_lfanew field, so we're acrually setting + //e_lfanew with this call + pe.set_base_of_code(8 * sizeof(uint16_t)); + } + else + { + //Set start of PE headers + dos_header.e_lfanew = sizeof(image_dos_header) + + pe_utils::align_up(static_cast<uint32_t>(pe.get_stub_overlay().size()), sizeof(uint32_t)); + } + + section_list& sections = pe.get_image_sections(); + + //Calculate pointer to section data + size_t ptr_to_section_data = (strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)) + pe.get_sizeof_nt_header() + + pe_utils::align_up(pe.get_stub_overlay().size(), sizeof(uint32_t)) + - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes()) + + sections.size() * sizeof(image_section_header); + + if(save_bound_import && pe.has_bound_import()) + { + //It will be aligned to DWORD, because we're aligning to DWORD everything above it + pe.set_directory_rva(image_directory_entry_bound_import, static_cast<uint32_t>(ptr_to_section_data)); + ptr_to_section_data += pe.get_directory_size(image_directory_entry_bound_import); + } + + ptr_to_section_data = pe_utils::align_up(ptr_to_section_data, pe.get_file_alignment()); + + //Set size of headers and size of optional header + if(change_size_of_headers) + { + if(!pe.get_image_sections().empty()) + { + if(static_cast<uint32_t>(ptr_to_section_data) > (*sections.begin()).get_virtual_address()) + throw pe_exception("Headers of PE file are too long. Try to strip STUB or don't build bound import", pe_exception::cannot_rebuild_image); + } + + pe.set_size_of_headers(static_cast<uint32_t>(ptr_to_section_data)); + } + + //Set number of sections in PE header + pe.update_number_of_sections(); + + pe.update_image_size(); + + pe.set_size_of_optional_header(static_cast<uint16_t>(pe.get_sizeof_opt_headers() + - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes()))); + + //Recalculate pointer to raw data according to section list + for(section_list::iterator it = sections.begin(); it != sections.end(); ++it) + { + //Save section headers PointerToRawData + (*it).set_pointer_to_raw_data(static_cast<uint32_t>(ptr_to_section_data)); + ptr_to_section_data += (*it).get_aligned_raw_size(pe.get_file_alignment()); + } +} + +//Rebuild PE image and write it to "out" ostream +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, std::ostream& out, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) +{ + if(out.bad()) + throw pe_exception("Stream is bad", pe_exception::stream_is_bad); + + if(save_bound_import && pe.has_bound_import()) + { + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true) + < pe.get_directory_size(image_directory_entry_bound_import)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + } + + //Change ostream state + out.exceptions(std::ios::goodbit); + out.clear(); + + uint32_t original_bound_import_rva = pe.has_bound_import() ? pe.get_directory_rva(image_directory_entry_bound_import) : 0; + if(original_bound_import_rva && original_bound_import_rva > pe.get_size_of_headers()) + { + //No need to do anything with bound import directory + //if it is placed inside of any section, not headers + original_bound_import_rva = 0; + save_bound_import = false; + } + + { + image_dos_header dos_header; + + //Rebuild PE image headers + rebuild_pe(pe, dos_header, strip_dos_header, change_size_of_headers, save_bound_import); + + //Write DOS header + out.write(reinterpret_cast<const char*>(&dos_header), strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)); + } + + //If we have stub overlay, write it too + { + const std::string& stub = pe.get_stub_overlay(); + if(stub.size()) + { + out.write(stub.data(), stub.size()); + size_t aligned_size = pe_utils::align_up(stub.size(), sizeof(uint32_t)); + //Align PE header, which is right after rich overlay + while(aligned_size > stub.size()) + { + out.put('\0'); + --aligned_size; + } + } + } + + //Write NT headers + out.write(static_cast<const pe_base&>(pe).get_nt_headers_ptr(), pe.get_sizeof_nt_header() + - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes())); + + //Write section headers + const section_list& sections = pe.get_image_sections(); + for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) + { + if(it == sections.end() - 1) //If last section encountered + { + image_section_header header((*it).get_raw_header()); + header.SizeOfRawData = static_cast<uint32_t>((*it).get_raw_data().length()); //Set non-aligned actual data length for it + out.write(reinterpret_cast<const char*>(&header), sizeof(image_section_header)); + } + else + { + out.write(reinterpret_cast<const char*>(&(*it).get_raw_header()), sizeof(image_section_header)); + } + } + + //Write bound import data if requested + if(save_bound_import && pe.has_bound_import()) + { + out.write(pe.section_data_from_rva(original_bound_import_rva, section_data_raw, true), + pe.get_directory_size(image_directory_entry_bound_import)); + } + + //Write section data finally + for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) + { + const section& s = *it; + + std::streamoff wpos = out.tellp(); + + //Fill unused overlay data between sections with null bytes + for(unsigned int i = 0; i < s.get_pointer_to_raw_data() - wpos; i++) + out.put(0); + + //Write raw section data + out.write(s.get_raw_data().data(), s.get_raw_data().length()); + } +} + +//Rebuild PE image and write it to "out" file +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, const char* out, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) +{ + std::ofstream pe_file(out, std::ios::out | std::ios::binary | std::ios::trunc); + if(!pe_file) + { + throw pe_exception("Error in open file.", pe_exception::stream_is_bad); + } + rebuild_pe(pe, pe_file, strip_dos_header, change_size_of_headers, save_bound_import); +} + + +} diff --git a/tools/pe_bliss/pe_rebuilder.h b/tools/pe_bliss/pe_rebuilder.h new file mode 100644 index 0000000000..319807e5c9 --- /dev/null +++ b/tools/pe_bliss/pe_rebuilder.h @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <ostream> +#include <fstream> + +namespace pe_bliss +{ +class pe_base; +//Rebuilds PE image, writes resulting image to ostream "out". If strip_dos_header == true, DOS header will be stripped a little +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, std::ostream& out, bool strip_dos_header = false, bool change_size_of_headers = true, bool save_bound_import = true); + +//Rebuild PE image and write it to "out" file +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, const char* out, bool strip_dos_header = false, bool change_size_of_headers = true, bool save_bound_import = true); + +} diff --git a/tools/pe_bliss/pe_relocations.cpp b/tools/pe_bliss/pe_relocations.cpp new file mode 100644 index 0000000000..d5357dd219 --- /dev/null +++ b/tools/pe_bliss/pe_relocations.cpp @@ -0,0 +1,320 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_relocations.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//RELOCATIONS +//Default constructor +relocation_entry::relocation_entry() + :rva_(0), type_(0) +{} + +//Constructor from relocation item (WORD) +relocation_entry::relocation_entry(uint16_t relocation_value) + :rva_(relocation_value & ((1 << 12) - 1)), type_(relocation_value >> 12) +{} + +//Constructor from relative rva and relocation type +relocation_entry::relocation_entry(uint16_t rrva, uint16_t type) + :rva_(rrva), type_(type) +{} + +//Returns RVA of relocation +uint16_t relocation_entry::get_rva() const +{ + return rva_; +} + +//Returns type of relocation +uint16_t relocation_entry::get_type() const +{ + return type_; +} + +//Sets RVA of relocation +void relocation_entry::set_rva(uint16_t rva) +{ + rva_ = rva; +} + +//Sets type of relocation +void relocation_entry::set_type(uint16_t type) +{ + type_ = type; +} + +//Returns relocation item (rrva + type) +uint16_t relocation_entry::get_item() const +{ + return rva_ | (type_ << 12); +} + +//Sets relocation item (rrva + type) +void relocation_entry::set_item(uint16_t item) +{ + rva_ = item & ((1 << 12) - 1); + type_ = item >> 12; +} + +//Returns relocation list +const relocation_table::relocation_list& relocation_table::get_relocations() const +{ + return relocations_; +} + +//Adds relocation to table +void relocation_table::add_relocation(const relocation_entry& entry) +{ + relocations_.push_back(entry); +} + +//Default constructor +relocation_table::relocation_table() + :rva_(0) +{} + +//Constructor from RVA of relocation table +relocation_table::relocation_table(uint32_t rva) + :rva_(rva) +{} + +//Returns RVA of block +uint32_t relocation_table::get_rva() const +{ + return rva_; +} + +//Sets RVA of block +void relocation_table::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Returns changeable relocation list +relocation_table::relocation_list& relocation_table::get_relocations() +{ + return relocations_; +} + +//Get relocation list of pe file, supports one-word sized relocations only +//If list_absolute_entries = true, IMAGE_REL_BASED_ABSOLUTE will be listed +const relocation_table_list get_relocations(const pe_base& pe, bool list_absolute_entries) +{ + relocation_table_list ret; + + //If image does not have relocations + if(!pe.has_reloc()) + return ret; + + //Check the length in bytes of the section containing relocation directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_basereloc), + pe.get_directory_rva(image_directory_entry_basereloc), section_data_virtual, true) + < sizeof(image_base_relocation)) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + unsigned long current_pos = pe.get_directory_rva(image_directory_entry_basereloc); + //First IMAGE_BASE_RELOCATION table + image_base_relocation reloc_table = pe.section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true); + + if(reloc_table.SizeOfBlock % 2) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + unsigned long reloc_size = pe.get_directory_size(image_directory_entry_basereloc); + unsigned long read_size = 0; + + //reloc_table.VirtualAddress is not checked (not so important) + while(reloc_table.SizeOfBlock && read_size < reloc_size) + { + //Create relocation table + relocation_table table; + //Save RVA + table.set_rva(reloc_table.VirtualAddress); + + if(!pe_utils::is_sum_safe(current_pos, reloc_table.SizeOfBlock)) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + //List all relocations + for(unsigned long i = sizeof(image_base_relocation); i < reloc_table.SizeOfBlock; i += sizeof(uint16_t)) + { + relocation_entry entry(pe.section_data_from_rva<uint16_t>(current_pos + i, section_data_virtual, true)); + if(list_absolute_entries || entry.get_type() != image_rel_based_absolute) + table.add_relocation(entry); + } + + //Save table + ret.push_back(table); + + //Go to next relocation block + if(!pe_utils::is_sum_safe(current_pos, reloc_table.SizeOfBlock)) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + current_pos += reloc_table.SizeOfBlock; + read_size += reloc_table.SizeOfBlock; + reloc_table = pe.section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true); + } + + return ret; +} + +//Simple relocations rebuilder +//To keep PE file working, don't remove any of existing relocations in +//relocation_table_list returned by a call to get_relocations() function +//auto_strip_last_section - if true and relocations are placed in the last section, it will be automatically stripped +//offset_from_section_start - offset from the beginning of reloc_section, where relocations data will be situated +//If save_to_pe_header is true, PE header will be modified automatically +const image_directory rebuild_relocations(pe_base& pe, const relocation_table_list& relocs, section& reloc_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that reloc_section is attached to this PE image + if(!pe.section_attached(reloc_section)) + throw pe_exception("Relocations section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t current_reloc_data_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + + uint32_t needed_size = current_reloc_data_pos - offset_from_section_start; //Calculate needed size for relocation tables + uint32_t size_delta = needed_size; + + uint32_t start_reloc_pos = current_reloc_data_pos; + + //Enumerate relocation tables + for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it) + { + needed_size += static_cast<uint32_t>((*it).get_relocations().size() * sizeof(uint16_t) /* relocations */ + sizeof(image_base_relocation) /* table header */); + //End of each table will be DWORD-aligned + if((start_reloc_pos + needed_size - size_delta) % sizeof(uint32_t)) + needed_size += sizeof(uint16_t); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation + } + + //Check if reloc_section is last one. If it's not, check if there's enough place for relocations data + if(&reloc_section != &*(pe.get_image_sections().end() - 1) && + (reloc_section.empty() || pe_utils::align_up(reloc_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + current_reloc_data_pos)) + throw pe_exception("Insufficient space for relocations directory", pe_exception::insufficient_space); + + std::string& raw_data = reloc_section.get_raw_data(); + + //This will be done only if reloc_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + current_reloc_data_pos) + raw_data.resize(needed_size + current_reloc_data_pos); //Expand section raw data + + //Enumerate relocation tables + for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it) + { + //Create relocation table header + image_base_relocation reloc; + reloc.VirtualAddress = (*it).get_rva(); + const relocation_table::relocation_list& reloc_list = (*it).get_relocations(); + reloc.SizeOfBlock = static_cast<uint32_t>(sizeof(image_base_relocation) + sizeof(uint16_t) * reloc_list.size()); + if((reloc_list.size() * sizeof(uint16_t)) % sizeof(uint32_t)) //If we must align end of relocation table + reloc.SizeOfBlock += sizeof(uint16_t); + + memcpy(&raw_data[current_reloc_data_pos], &reloc, sizeof(reloc)); + current_reloc_data_pos += sizeof(reloc); + + //Enumerate relocations in table + for(relocation_table::relocation_list::const_iterator r = reloc_list.begin(); r != reloc_list.end(); ++r) + { + //Save relocations + uint16_t reloc_value = (*r).get_item(); + memcpy(&raw_data[current_reloc_data_pos], &reloc_value, sizeof(reloc_value)); + current_reloc_data_pos += sizeof(reloc_value); + } + + if(current_reloc_data_pos % sizeof(uint32_t)) //If end of table is not DWORD-aligned + { + memset(&raw_data[current_reloc_data_pos], 0, sizeof(uint16_t)); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation + current_reloc_data_pos += sizeof(uint16_t); + } + } + + image_directory ret(pe.rva_from_section_offset(reloc_section, start_reloc_pos), needed_size - size_delta); + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(reloc_section, auto_strip_last_section); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_basereloc, ret.get_rva()); + pe.set_directory_size(image_directory_entry_basereloc, ret.get_size()); + + pe.clear_characteristics_flags(image_file_relocs_stripped); + pe.set_dll_characteristics(pe.get_dll_characteristics() | image_dllcharacteristics_dynamic_base); + } + + return ret; +} + +//Recalculates image base with the help of relocation tables +void rebase_image(pe_base& pe, const relocation_table_list& tables, uint64_t new_base) +{ + pe.get_pe_type() == pe_type_32 + ? rebase_image_base<pe_types_class_32>(pe, tables, new_base) + : rebase_image_base<pe_types_class_64>(pe, tables, new_base); +} + +//RELOCATIONS +//Recalculates image base with the help of relocation tables +//Recalculates VAs of DWORDS/QWORDS in image according to relocations +//Notice: if you move some critical structures like TLS, image relocations will not fix new +//positions of TLS VAs. Instead, some bytes that now doesn't belong to TLS will be fixed. +//It is recommended to rebase image in the very beginning and move all structures afterwards. +template<typename PEClassType> +void rebase_image_base(pe_base& pe, const relocation_table_list& tables, uint64_t new_base) +{ + //Get current image base value + typename PEClassType::BaseSize image_base; + pe.get_image_base(image_base); + + //ImageBase difference + typename PEClassType::BaseSize base_rel = static_cast<typename PEClassType::BaseSize>(static_cast<int64_t>(new_base) - image_base); + + //We need to fix addresses from relocation tables + //Enumerate relocation tables + for(relocation_table_list::const_iterator it = tables.begin(); it != tables.end(); ++it) + { + const relocation_table::relocation_list& relocs = (*it).get_relocations(); + + uint32_t base_rva = (*it).get_rva(); + + //Enumerate relocations + for(relocation_table::relocation_list::const_iterator rel = relocs.begin(); rel != relocs.end(); ++rel) + { + //Skip ABSOLUTE entries + if((*rel).get_type() == pe_win::image_rel_based_absolute) + continue; + + //Recalculate value by RVA and rewrite it + uint32_t current_rva = base_rva + (*rel).get_rva(); + typename PEClassType::BaseSize value = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_rva, section_data_raw, true); + value += base_rel; + memcpy(pe.section_data_from_rva(current_rva, true), &value, sizeof(value)); + } + } + + //Finally, save new image base + pe.set_image_base_64(new_base); +} +} diff --git a/tools/pe_bliss/pe_relocations.h b/tools/pe_bliss/pe_relocations.h new file mode 100644 index 0000000000..1bc8b2a405 --- /dev/null +++ b/tools/pe_bliss/pe_relocations.h @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing relocation entry +//RVA of relocation is not actually RVA, but +//(real RVA) - (RVA of table) +class relocation_entry +{ +public: + //Default constructor + relocation_entry(); + //Constructor from relocation item (WORD) + explicit relocation_entry(uint16_t relocation_value); + //Constructor from relative rva and relocation type + relocation_entry(uint16_t rrva, uint16_t type); + + //Returns RVA of relocation (actually, relative RVA from relocation table RVA) + uint16_t get_rva() const; + //Returns type of relocation + uint16_t get_type() const; + + //Returns relocation item (rrva + type) + uint16_t get_item() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You can also use them to rebuild relocations using rebuild_relocations() + + //Sets RVA of relocation (actually, relative RVA from relocation table RVA) + void set_rva(uint16_t rva); + //Sets type of relocation + void set_type(uint16_t type); + + //Sets relocation item (rrva + type) + void set_item(uint16_t item); + +private: + uint16_t rva_; + uint16_t type_; +}; + +//Class representing relocation table +class relocation_table +{ +public: + typedef std::vector<relocation_entry> relocation_list; + +public: + //Default constructor + relocation_table(); + //Constructor from RVA of relocation table + explicit relocation_table(uint32_t rva); + + //Returns relocation list + const relocation_list& get_relocations() const; + //Returns RVA of block + uint32_t get_rva() const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild relocations using rebuild_relocations() + + //Adds relocation to table + void add_relocation(const relocation_entry& entry); + //Returns changeable relocation list + relocation_list& get_relocations(); + //Sets RVA of block + void set_rva(uint32_t rva); + +private: + uint32_t rva_; + relocation_list relocations_; +}; + +typedef std::vector<relocation_table> relocation_table_list; + +//Get relocation list of pe file, supports one-word sized relocations only +//If list_absolute_entries = true, IMAGE_REL_BASED_ABSOLUTE will be listed +const relocation_table_list get_relocations(const pe_base& pe, bool list_absolute_entries = false); + +//Simple relocations rebuilder +//To keep PE file working, don't remove any of existing relocations in +//relocation_table_list returned by a call to get_relocations() function +//auto_strip_last_section - if true and relocations are placed in the last section, it will be automatically stripped +//offset_from_section_start - offset from the beginning of reloc_section, where relocations data will be situated +//If save_to_pe_header is true, PE header will be modified automatically +const image_directory rebuild_relocations(pe_base& pe, const relocation_table_list& relocs, section& reloc_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); + +//Recalculates image base with the help of relocation tables +//Recalculates VAs of DWORDS/QWORDS in image according to relocations +//Notice: if you move some critical structures like TLS, image relocations will not fix new +//positions of TLS VAs. Instead, some bytes that now doesn't belong to TLS will be fixed. +//It is recommended to rebase image in the very beginning and move all structures afterwards. +void rebase_image(pe_base& pe, const relocation_table_list& tables, uint64_t new_base); + +template<typename PEClassType> +void rebase_image_base(pe_base& pe, const relocation_table_list& tables, uint64_t new_base); +} diff --git a/tools/pe_bliss/pe_resource_manager.cpp b/tools/pe_bliss/pe_resource_manager.cpp new file mode 100644 index 0000000000..0ee7840ff0 --- /dev/null +++ b/tools/pe_bliss/pe_resource_manager.cpp @@ -0,0 +1,286 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include <sstream> +#include <iomanip> +#include <math.h> +#include <string.h> +#include "pe_resource_manager.h" +#include "resource_internal.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Constructor from root resource directory +pe_resource_manager::pe_resource_manager(resource_directory& root_directory) + :pe_resource_viewer(root_directory), root_dir_edit_(root_directory) +{} + +resource_directory& pe_resource_manager::get_root_directory() +{ + return root_dir_edit_; +} + +//Removes all resources of given type or root name +//If there's more than one directory entry of a given type, only the +//first one will be deleted (that's an unusual situation) +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource_type(resource_type type) +{ + //Search for resource type + resource_directory::entry_list& entries = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it = std::find_if(entries.begin(), entries.end(), resource_directory::id_entry_finder(type)); + if(it != entries.end()) + { + //Remove it, if found + entries.erase(it); + return true; + } + + return false; +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name) +{ + //Search for resource type + resource_directory::entry_list& entries = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it = std::find_if(entries.begin(), entries.end(), resource_directory::name_entry_finder(root_name)); + if(it != entries.end()) + { + //Remove it, if found + entries.erase(it); + return true; + } + + return false; +} + +//Helper to remove resource +bool pe_resource_manager::remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder) +{ + //Search for resource type + resource_directory::entry_list& entries_type = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it_type = std::find_if(entries_type.begin(), entries_type.end(), root_finder); + if(it_type != entries_type.end()) + { + //Search for resource name/ID with "finder" + resource_directory::entry_list& entries_name = (*it_type).get_resource_directory().get_entry_list(); + resource_directory::entry_list::iterator it_name = std::find_if(entries_name.begin(), entries_name.end(), finder); + if(it_name != entries_name.end()) + { + //Erase resource, if found + entries_name.erase(it_name); + if(entries_name.empty()) + entries_type.erase(it_type); + + return true; + } + } + + return false; +} + +//Removes all resource languages by resource type/root name and name +//Deletes only one entry of given type and name +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, const std::wstring& name) +{ + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(name)); +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name, const std::wstring& name) +{ + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(name)); +} + +//Removes all resource languages by resource type/root name and ID +//Deletes only one entry of given type and ID +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, uint32_t id) +{ + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(id)); +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name, uint32_t id) +{ + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(id)); +} + +//Helper to remove resource +bool pe_resource_manager::remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder, uint32_t language) +{ + //Search for resource type + resource_directory::entry_list& entries_type = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it_type = std::find_if(entries_type.begin(), entries_type.end(), root_finder); + if(it_type != entries_type.end()) + { + //Search for resource name/ID with "finder" + resource_directory::entry_list& entries_name = (*it_type).get_resource_directory().get_entry_list(); + resource_directory::entry_list::iterator it_name = std::find_if(entries_name.begin(), entries_name.end(), finder); + if(it_name != entries_name.end()) + { + //Search for resource language + resource_directory::entry_list& entries_lang = (*it_name).get_resource_directory().get_entry_list(); + resource_directory::entry_list::iterator it_lang = std::find_if(entries_lang.begin(), entries_lang.end(), resource_directory::id_entry_finder(language)); + if(it_lang != entries_lang.end()) + { + //Erase resource, if found + entries_lang.erase(it_lang); + if(entries_lang.empty()) + { + entries_name.erase(it_name); + if(entries_name.empty()) + entries_type.erase(it_type); + } + + return true; + } + } + } + + return false; +} + +//Removes resource language by resource type/root name and name +//Deletes only one entry of given type, name and language +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, const std::wstring& name, uint32_t language) +{ + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(name), language); +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name, const std::wstring& name, uint32_t language) +{ + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(name), language); +} + +//Removes recource language by resource type/root name and ID +//Deletes only one entry of given type, ID and language +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, uint32_t id, uint32_t language) +{ + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(id), language); +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name, uint32_t id, uint32_t language) +{ + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(id), language); +} + +//Helper to add/replace resource +void pe_resource_manager::add_resource(const std::string& data, resource_type type, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_type_entry; + new_type_entry.set_id(type); + + add_resource(data, new_type_entry, resource_directory::entry_finder(type), new_entry, finder, language, codepage, timestamp); +} + +//Helper to add/replace resource +void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_type_entry; + new_type_entry.set_name(root_name); + + add_resource(data, new_type_entry, resource_directory::entry_finder(root_name), new_entry, finder, language, codepage, timestamp); +} + +//Helper to add/replace resource +void pe_resource_manager::add_resource(const std::string& data, resource_directory_entry& new_root_entry, const resource_directory::entry_finder& root_finder, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + //Search for resource type + resource_directory::entry_list* entries = &root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it = std::find_if(entries->begin(), entries->end(), root_finder); + if(it == entries->end()) + { + //Add resource type directory, if it was not found + resource_directory dir; + dir.set_timestamp(timestamp); + new_root_entry.add_resource_directory(dir); + entries->push_back(new_root_entry); + it = entries->end() - 1; + } + + //Search for resource name/ID directory with "finder" + entries = &(*it).get_resource_directory().get_entry_list(); + it = std::find_if(entries->begin(), entries->end(), finder); + if(it == entries->end()) + { + //Add resource name/ID directory, if it was not found + resource_directory dir; + dir.set_timestamp(timestamp); + new_entry.add_resource_directory(dir); + entries->push_back(new_entry); + it = entries->end() - 1; + } + + //Search for data resource entry by language + entries = &(*it).get_resource_directory().get_entry_list(); + it = std::find_if(entries->begin(), entries->end(), resource_directory::id_entry_finder(language)); + if(it != entries->end()) + entries->erase(it); //Erase it, if found + + //Add new data entry + resource_directory_entry new_dir_data_entry; + resource_data_entry data_dir(data, codepage); + new_dir_data_entry.add_data_entry(data_dir); + new_dir_data_entry.set_id(language); + entries->push_back(new_dir_data_entry); +} + +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, resource_type type, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_entry; + new_entry.set_name(name); + + add_resource(data, type, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp); +} + +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_entry; + new_entry.set_name(name); + + add_resource(data, root_name, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp); +} + +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, resource_type type, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_entry; + new_entry.set_id(id); + + add_resource(data, type, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp); +} + +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_entry; + new_entry.set_id(id); + + add_resource(data, root_name, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp); +} +} diff --git a/tools/pe_bliss/pe_resource_manager.h b/tools/pe_bliss/pe_resource_manager.h new file mode 100644 index 0000000000..85d7f44a8a --- /dev/null +++ b/tools/pe_bliss/pe_resource_manager.h @@ -0,0 +1,113 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <map> +#include <sstream> +#include <string> +#include <memory> +#include "pe_base.h" +#include "pe_structures.h" +#include "pe_resources.h" +#include "message_table.h" +#include "file_version_info.h" +#include "pe_resource_viewer.h" +#include "resource_data_info.h" + +namespace pe_bliss +{ +//Derived class to edit PE resources +class pe_resource_manager : public pe_resource_viewer +{ +public: + //Constructor from root resource directory + explicit pe_resource_manager(resource_directory& root_directory); + + resource_directory& get_root_directory(); + +public: //Resource editing + //Removes all resources of given type or root name + //If there's more than one directory entry of a given type, only the + //first one will be deleted (that's an unusual situation) + //Returns true if resource was deleted + bool remove_resource_type(resource_type type); + bool remove_resource(const std::wstring& root_name); + + //Removes all resource languages by resource type/root name and name + //Deletes only one entry of given type and name + //Returns true if resource was deleted + bool remove_resource(resource_type type, const std::wstring& name); + bool remove_resource(const std::wstring& root_name, const std::wstring& name); + //Removes all resource languages by resource type/root name and ID + //Deletes only one entry of given type and ID + //Returns true if resource was deleted + bool remove_resource(resource_type type, uint32_t id); + bool remove_resource(const std::wstring& root_name, uint32_t id); + + //Removes resource language by resource type/root name and name + //Deletes only one entry of given type, name and language + //Returns true if resource was deleted + bool remove_resource(resource_type type, const std::wstring& name, uint32_t language); + bool remove_resource(const std::wstring& root_name, const std::wstring& name, uint32_t language); + //Removes recource language by resource type/root name and ID + //Deletes only one entry of given type, ID and language + //Returns true if resource was deleted + bool remove_resource(resource_type type, uint32_t id, uint32_t language); + bool remove_resource(const std::wstring& root_name, uint32_t id, uint32_t language); + + //Adds resource. If resource already exists, replaces it + //timestamp will be used for directories that will be added + void add_resource(const std::string& data, resource_type type, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_resource(const std::string& data, const std::wstring& root_name, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + //Adds resource. If resource already exists, replaces it + //timestamp will be used for directories that will be added + void add_resource(const std::string& data, resource_type type, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_resource(const std::string& data, const std::wstring& root_name, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + +public: + //Helpers to add/replace resource + void add_resource(const std::string& data, resource_type type, + resource_directory_entry& new_entry, + const resource_directory::entry_finder& finder, + uint32_t language, uint32_t codepage, uint32_t timestamp); + + void add_resource(const std::string& data, const std::wstring& root_name, + resource_directory_entry& new_entry, + const resource_directory::entry_finder& finder, + uint32_t language, uint32_t codepage, uint32_t timestamp); + + void add_resource(const std::string& data, resource_directory_entry& new_root_entry, + const resource_directory::entry_finder& root_finder, + resource_directory_entry& new_entry, + const resource_directory::entry_finder& finder, + uint32_t language, uint32_t codepage, uint32_t timestamp); + +private: + //Root resource directory. We're not copying it, because it might be heavy + resource_directory& root_dir_edit_; + + //Helper to remove resource + bool remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder); + + //Helper to remove resource + bool remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder, uint32_t language); +}; +} diff --git a/tools/pe_bliss/pe_resource_viewer.cpp b/tools/pe_bliss/pe_resource_viewer.cpp new file mode 100644 index 0000000000..712cc28d9b --- /dev/null +++ b/tools/pe_bliss/pe_resource_viewer.cpp @@ -0,0 +1,382 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include <cmath> +#include "pe_resource_viewer.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Constructor from root resource_directory +pe_resource_viewer::pe_resource_viewer(const resource_directory& root_directory) + :root_dir_(root_directory) +{} + +const resource_directory& pe_resource_viewer::get_root_directory() const +{ + return root_dir_; +} + +//Finder helpers +bool pe_resource_viewer::has_name::operator()(const resource_directory_entry& entry) const +{ + return entry.is_named(); +} + +bool pe_resource_viewer::has_id::operator()(const resource_directory_entry& entry) const +{ + return !entry.is_named(); +} + +//Lists resource types existing in PE file (non-named only) +const pe_resource_viewer::resource_type_list pe_resource_viewer::list_resource_types() const +{ + resource_type_list ret; + + //Get root directory entries list + const resource_directory::entry_list& entries = root_dir_.get_entry_list(); + for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + //List all non-named items + if(!(*it).is_named()) + ret.push_back((*it).get_id()); + } + + return ret; +} + +//Returns true if resource type exists +bool pe_resource_viewer::resource_exists(resource_type type) const +{ + const resource_directory::entry_list& entries = root_dir_.get_entry_list(); + return std::find_if(entries.begin(), entries.end(), resource_directory::id_entry_finder(type)) != entries.end(); +} + +//Returns true if resource name exists +bool pe_resource_viewer::resource_exists(const std::wstring& root_name) const +{ + const resource_directory::entry_list& entries = root_dir_.get_entry_list(); + return std::find_if(entries.begin(), entries.end(), resource_directory::name_entry_finder(root_name)) != entries.end(); +} + +//Helper function to get name list from entry list +const pe_resource_viewer::resource_name_list pe_resource_viewer::get_name_list(const resource_directory::entry_list& entries) +{ + resource_name_list ret; + + for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + //List all named items + if((*it).is_named()) + ret.push_back((*it).get_name()); + } + + return ret; +} + +//Helper function to get ID list from entry list +const pe_resource_viewer::resource_id_list pe_resource_viewer::get_id_list(const resource_directory::entry_list& entries) +{ + resource_id_list ret; + + for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + //List all non-named items + if(!(*it).is_named()) + ret.push_back((*it).get_id()); + } + + return ret; +} + +//Lists resource names existing in PE file by resource type +const pe_resource_viewer::resource_name_list pe_resource_viewer::list_resource_names(resource_type type) const +{ + return get_name_list(root_dir_.entry_by_id(type).get_resource_directory().get_entry_list()); +} + +//Lists resource names existing in PE file by resource name +const pe_resource_viewer::resource_name_list pe_resource_viewer::list_resource_names(const std::wstring& root_name) const +{ + return get_name_list(root_dir_.entry_by_name(root_name).get_resource_directory().get_entry_list()); +} + +//Lists resource IDs existing in PE file by resource type +const pe_resource_viewer::resource_id_list pe_resource_viewer::list_resource_ids(resource_type type) const +{ + return get_id_list(root_dir_.entry_by_id(type).get_resource_directory().get_entry_list()); +} + +//Lists resource IDs existing in PE file by resource name +const pe_resource_viewer::resource_id_list pe_resource_viewer::list_resource_ids(const std::wstring& root_name) const +{ + return get_id_list(root_dir_.entry_by_name(root_name).get_resource_directory().get_entry_list()); +} + +//Returns resource count by type +unsigned long pe_resource_viewer::get_resource_count(resource_type type) const +{ + return static_cast<unsigned long>( + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .get_entry_list() + .size()); +} + +//Returns resource count by name +unsigned long pe_resource_viewer::get_resource_count(const std::wstring& root_name) const +{ + return static_cast<unsigned long>( + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .get_entry_list() + .size()); +} + +//Returns language count of resource by resource type and name +unsigned long pe_resource_viewer::get_language_count(resource_type type, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Returns language count of resource by resource names +unsigned long pe_resource_viewer::get_language_count(const std::wstring& root_name, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Returns language count of resource by resource type and ID +unsigned long pe_resource_viewer::get_language_count(resource_type type, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Returns language count of resource by resource name and ID +unsigned long pe_resource_viewer::get_language_count(const std::wstring& root_name, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Lists resource languages by resource type and name +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(resource_type type, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Lists resource languages by resource names +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(const std::wstring& root_name, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Lists resource languages by resource type and ID +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(resource_type type, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Lists resource languages by resource name and ID +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(const std::wstring& root_name, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Returns raw resource data by type, name and language +const resource_data_info pe_resource_viewer::get_resource_data_by_name(uint32_t language, resource_type type, const std::wstring& name) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, name and language +const resource_data_info pe_resource_viewer::get_resource_data_by_name(uint32_t language, const std::wstring& root_name, const std::wstring& name) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by type, ID and language +const resource_data_info pe_resource_viewer::get_resource_data_by_id(uint32_t language, resource_type type, uint32_t id) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, ID and language +const resource_data_info pe_resource_viewer::get_resource_data_by_id(uint32_t language, const std::wstring& root_name, uint32_t id) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by type, name and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_name(resource_type type, const std::wstring& name, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, name and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_name(const std::wstring& root_name, const std::wstring& name, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} + +//Returns raw resource data by type, ID and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_id(resource_type type, uint32_t id, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, ID and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_id(const std::wstring& root_name, uint32_t id, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} +} diff --git a/tools/pe_bliss/pe_resource_viewer.h b/tools/pe_bliss/pe_resource_viewer.h new file mode 100644 index 0000000000..e585da6a87 --- /dev/null +++ b/tools/pe_bliss/pe_resource_viewer.h @@ -0,0 +1,153 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <map> +#include <string> +#include "pe_structures.h" +#include "pe_resources.h" +#include "message_table.h" +#include "resource_data_info.h" + +namespace pe_bliss +{ + //PE resource manager allows to read resources from PE files +class pe_resource_viewer +{ +public: + //Resource type enumeration + enum resource_type + { + resource_cursor = 1, + resource_bitmap = 2, + resource_icon = 3, + resource_menu = 4, + resource_dialog = 5, + resource_string = 6, + resource_fontdir = 7, + resource_font = 8, + resource_accelerator = 9, + resource_rcdata = 10, + resource_message_table = 11, + resource_cursor_group = 12, + resource_icon_group = 14, + resource_version = 16, + resource_dlginclude = 17, + resource_plugplay = 19, + resource_vxd = 20, + resource_anicursor = 21, + resource_aniicon = 22, + resource_html = 23, + resource_manifest = 24 + }; + +public: + //Some useful typedefs + typedef std::vector<uint32_t> resource_type_list; + typedef std::vector<uint32_t> resource_id_list; + typedef std::vector<std::wstring> resource_name_list; + typedef std::vector<uint32_t> resource_language_list; + +public: + //Constructor from root resource_directory from PE file + explicit pe_resource_viewer(const resource_directory& root_directory); + + const resource_directory& get_root_directory() const; + + //Lists resource types existing in PE file (non-named only) + const resource_type_list list_resource_types() const; + //Returns true if resource type exists + bool resource_exists(resource_type type) const; + //Returns true if resource name exists + bool resource_exists(const std::wstring& root_name) const; + + //Lists resource names existing in PE file by resource type + const resource_name_list list_resource_names(resource_type type) const; + //Lists resource names existing in PE file by resource name + const resource_name_list list_resource_names(const std::wstring& root_name) const; + //Lists resource IDs existing in PE file by resource type + const resource_id_list list_resource_ids(resource_type type) const; + //Lists resource IDs existing in PE file by resource name + const resource_id_list list_resource_ids(const std::wstring& root_name) const; + //Returns resource count by type + unsigned long get_resource_count(resource_type type) const; + //Returns resource count by name + unsigned long get_resource_count(const std::wstring& root_name) const; + + //Returns language count of resource by resource type and name + unsigned long get_language_count(resource_type type, const std::wstring& name) const; + //Returns language count of resource by resource names + unsigned long get_language_count(const std::wstring& root_name, const std::wstring& name) const; + //Returns language count of resource by resource type and ID + unsigned long get_language_count(resource_type type, uint32_t id) const; + //Returns language count of resource by resource name and ID + unsigned long get_language_count(const std::wstring& root_name, uint32_t id) const; + //Lists resource languages by resource type and name + const resource_language_list list_resource_languages(resource_type type, const std::wstring& name) const; + //Lists resource languages by resource names + const resource_language_list list_resource_languages(const std::wstring& root_name, const std::wstring& name) const; + //Lists resource languages by resource type and ID + const resource_language_list list_resource_languages(resource_type type, uint32_t id) const; + //Lists resource languages by resource name and ID + const resource_language_list list_resource_languages(const std::wstring& root_name, uint32_t id) const; + + //Returns raw resource data by type, name and language + const resource_data_info get_resource_data_by_name(uint32_t language, resource_type type, const std::wstring& name) const; + //Returns raw resource data by root name, name and language + const resource_data_info get_resource_data_by_name(uint32_t language, const std::wstring& root_name, const std::wstring& name) const; + //Returns raw resource data by type, ID and language + const resource_data_info get_resource_data_by_id(uint32_t language, resource_type type, uint32_t id) const; + //Returns raw resource data by root name, ID and language + const resource_data_info get_resource_data_by_id(uint32_t language, const std::wstring& root_name, uint32_t id) const; + //Returns raw resource data by type, name and index in language directory (instead of language) + const resource_data_info get_resource_data_by_name(resource_type type, const std::wstring& name, uint32_t index = 0) const; + //Returns raw resource data by root name, name and index in language directory (instead of language) + const resource_data_info get_resource_data_by_name(const std::wstring& root_name, const std::wstring& name, uint32_t index = 0) const; + //Returns raw resource data by type, ID and index in language directory (instead of language) + const resource_data_info get_resource_data_by_id(resource_type type, uint32_t id, uint32_t index = 0) const; + //Returns raw resource data by root name, ID and index in language directory (instead of language) + const resource_data_info get_resource_data_by_id(const std::wstring& root_name, uint32_t id, uint32_t index = 0) const; + +protected: + //Root resource directory. We're not copying it, because it might be heavy + const resource_directory& root_dir_; + + //Helper function to get ID list from entry list + static const resource_id_list get_id_list(const resource_directory::entry_list& entries); + //Helper function to get name list from entry list + static const resource_name_list get_name_list(const resource_directory::entry_list& entries); + +protected: + //Helper structure - finder of resource_directory_entry that is named + struct has_name + { + public: + bool operator()(const resource_directory_entry& entry) const; + }; + + //Helper structure - finder of resource_directory_entry that is not named (has id) + struct has_id + { + public: + bool operator()(const resource_directory_entry& entry) const; + }; +}; +} diff --git a/tools/pe_bliss/pe_resources.cpp b/tools/pe_bliss/pe_resources.cpp new file mode 100644 index 0000000000..189aba1f76 --- /dev/null +++ b/tools/pe_bliss/pe_resources.cpp @@ -0,0 +1,726 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include <string.h> +#include "pe_resources.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//RESOURCES +//Default constructor +resource_data_entry::resource_data_entry() + :codepage_(0) +{} + +//Constructor from data +resource_data_entry::resource_data_entry(const std::string& data, uint32_t codepage) + :codepage_(codepage), data_(data) +{} + +//Returns resource data codepage +uint32_t resource_data_entry::get_codepage() const +{ + return codepage_; +} + +//Returns resource data +const std::string& resource_data_entry::get_data() const +{ + return data_; +} + +//Sets resource data codepage +void resource_data_entry::set_codepage(uint32_t codepage) +{ + codepage_ = codepage; +} + +//Sets resource data +void resource_data_entry::set_data(const std::string& data) +{ + data_ = data; +} + +//Default constructor +resource_directory_entry::includes::includes() + :data_(0) +{} + +//Default constructor +resource_directory_entry::resource_directory_entry() + :id_(0), includes_data_(false), named_(false) +{} + +//Copy constructor +resource_directory_entry::resource_directory_entry(const resource_directory_entry& other) + :id_(other.id_), name_(other.name_), includes_data_(other.includes_data_), named_(other.named_) +{ + //If union'ed pointer is not zero + if(other.ptr_.data_) + { + if(other.includes_data()) + ptr_.data_ = new resource_data_entry(*other.ptr_.data_); + else + ptr_.dir_ = new resource_directory(*other.ptr_.dir_); + } +} + +//Copy assignment operator +resource_directory_entry& resource_directory_entry::operator=(const resource_directory_entry& other) +{ + release(); + + id_ = other.id_; + name_ = other.name_; + includes_data_ = other.includes_data_; + named_ = other.named_; + + //If other union'ed pointer is not zero + if(other.ptr_.data_) + { + if(other.includes_data()) + ptr_.data_ = new resource_data_entry(*other.ptr_.data_); + else + ptr_.dir_ = new resource_directory(*other.ptr_.dir_); + } + + return *this; +} + +//Destroys included data +void resource_directory_entry::release() +{ + //If union'ed pointer is not zero + if(ptr_.data_) + { + if(includes_data()) + delete ptr_.data_; + else + delete ptr_.dir_; + + ptr_.data_ = 0; + } +} + +//Destructor +resource_directory_entry::~resource_directory_entry() +{ + release(); +} + +//Returns entry ID +uint32_t resource_directory_entry::get_id() const +{ + return id_; +} + +//Returns entry name +const std::wstring& resource_directory_entry::get_name() const +{ + return name_; +} + +//Returns true, if entry has name +//Returns false, if entry has ID +bool resource_directory_entry::is_named() const +{ + return named_; +} + +//Returns true, if entry includes resource_data_entry +//Returns false, if entry includes resource_directory +bool resource_directory_entry::includes_data() const +{ + return includes_data_; +} + +//Returns resource_directory if entry includes it, otherwise throws an exception +const resource_directory& resource_directory_entry::get_resource_directory() const +{ + if(!ptr_.dir_ || includes_data_) + throw pe_exception("Resource directory entry does not contain resource directory", pe_exception::resource_directory_entry_error); + + return *ptr_.dir_; +} + +//Returns resource_data_entry if entry includes it, otherwise throws an exception +const resource_data_entry& resource_directory_entry::get_data_entry() const +{ + if(!ptr_.data_ || !includes_data_) + throw pe_exception("Resource directory entry does not contain resource data entry", pe_exception::resource_directory_entry_error); + + return *ptr_.data_; +} + +//Returns resource_directory if entry includes it, otherwise throws an exception +resource_directory& resource_directory_entry::get_resource_directory() +{ + if(!ptr_.dir_ || includes_data_) + throw pe_exception("Resource directory entry does not contain resource directory", pe_exception::resource_directory_entry_error); + + return *ptr_.dir_; +} + +//Returns resource_data_entry if entry includes it, otherwise throws an exception +resource_data_entry& resource_directory_entry::get_data_entry() +{ + if(!ptr_.data_ || !includes_data_) + throw pe_exception("Resource directory entry does not contain resource data entry", pe_exception::resource_directory_entry_error); + + return *ptr_.data_; +} + +//Sets entry name +void resource_directory_entry::set_name(const std::wstring& name) +{ + name_ = name; + named_ = true; + id_ = 0; +} + +//Sets entry ID +void resource_directory_entry::set_id(uint32_t id) +{ + id_ = id; + named_ = false; + name_.clear(); +} + +//Adds resource_data_entry +void resource_directory_entry::add_data_entry(const resource_data_entry& entry) +{ + release(); + ptr_.data_ = new resource_data_entry(entry); + includes_data_ = true; +} + +//Adds resource_directory +void resource_directory_entry::add_resource_directory(const resource_directory& dir) +{ + release(); + ptr_.dir_ = new resource_directory(dir); + includes_data_ = false; +} + +//Default constructor +resource_directory::resource_directory() + :characteristics_(0), + timestamp_(0), + major_version_(0), minor_version_(0), + number_of_named_entries_(0), number_of_id_entries_(0) +{} + +//Constructor from data +resource_directory::resource_directory(const image_resource_directory& dir) + :characteristics_(dir.Characteristics), + timestamp_(dir.TimeDateStamp), + major_version_(dir.MajorVersion), minor_version_(dir.MinorVersion), + number_of_named_entries_(0), number_of_id_entries_(0) //Set to zero here, calculate on add +{} + +//Returns characteristics of directory +uint32_t resource_directory::get_characteristics() const +{ + return characteristics_; +} + +//Returns date and time stamp of directory +uint32_t resource_directory::get_timestamp() const +{ + return timestamp_; +} + +//Returns major version of directory +uint16_t resource_directory::get_major_version() const +{ + return major_version_; +} + +//Returns minor version of directory +uint16_t resource_directory::get_minor_version() const +{ + return minor_version_; +} + +//Returns number of named entries +uint32_t resource_directory::get_number_of_named_entries() const +{ + return number_of_named_entries_; +} + +//Returns number of ID entries +uint32_t resource_directory::get_number_of_id_entries() const +{ + return number_of_id_entries_; +} + +//Returns resource_directory_entry array +const resource_directory::entry_list& resource_directory::get_entry_list() const +{ + return entries_; +} + +//Returns resource_directory_entry array +resource_directory::entry_list& resource_directory::get_entry_list() +{ + return entries_; +} + +//Adds resource_directory_entry +void resource_directory::add_resource_directory_entry(const resource_directory_entry& entry) +{ + entries_.push_back(entry); + if(entry.is_named()) + ++number_of_named_entries_; + else + ++number_of_id_entries_; +} + +//Clears resource_directory_entry array +void resource_directory::clear_resource_directory_entry_list() +{ + entries_.clear(); + number_of_named_entries_ = 0; + number_of_id_entries_ = 0; +} + +//Sets characteristics of directory +void resource_directory::set_characteristics(uint32_t characteristics) +{ + characteristics_ = characteristics; +} + +//Sets date and time stamp of directory +void resource_directory::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Sets number of named entries +void resource_directory::set_number_of_named_entries(uint32_t number) +{ + number_of_named_entries_ = number; +} + +//Sets number of ID entries +void resource_directory::set_number_of_id_entries(uint32_t number) +{ + number_of_id_entries_ = number; +} + +//Sets major version of directory +void resource_directory::set_major_version(uint16_t major_version) +{ + major_version_ = major_version; +} + +//Sets minor version of directory +void resource_directory::get_minor_version(uint16_t minor_version) +{ + minor_version_ = minor_version; +} + +//Processes resource directory +const resource_directory process_resource_directory(const pe_base& pe, uint32_t res_rva, uint32_t offset_to_directory, std::set<uint32_t>& processed) +{ + resource_directory ret; + + //Check for resource loops + if(!processed.insert(offset_to_directory).second) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + if(!pe_utils::is_sum_safe(res_rva, offset_to_directory)) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + //Get root IMAGE_RESOURCE_DIRECTORY + image_resource_directory directory = pe.section_data_from_rva<image_resource_directory>(res_rva + offset_to_directory, section_data_virtual, true); + + ret = resource_directory(directory); + + //Check DWORDs for possible overflows + if(!pe_utils::is_sum_safe(directory.NumberOfIdEntries, directory.NumberOfNamedEntries) + || directory.NumberOfIdEntries + directory.NumberOfNamedEntries >= pe_utils::max_dword / sizeof(image_resource_directory_entry) + sizeof(image_resource_directory)) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + if(!pe_utils::is_sum_safe(offset_to_directory, sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry)) + || !pe_utils::is_sum_safe(res_rva, offset_to_directory + sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry))) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + for(unsigned long i = 0; i != static_cast<unsigned long>(directory.NumberOfIdEntries) + directory.NumberOfNamedEntries; ++i) + { + //Read directory entries one by one + image_resource_directory_entry dir_entry = pe.section_data_from_rva<image_resource_directory_entry>( + res_rva + sizeof(image_resource_directory) + i * sizeof(image_resource_directory_entry) + offset_to_directory, section_data_virtual, true); + + //Create directory entry structure + resource_directory_entry entry; + + //If directory is named + if(dir_entry.NameIsString) + { + if(!pe_utils::is_sum_safe(res_rva + sizeof(uint16_t) /* safe */, dir_entry.NameOffset)) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + //get directory name length + uint16_t directory_name_length = pe.section_data_from_rva<uint16_t>(res_rva + dir_entry.NameOffset, section_data_virtual, true); + + //Check name length + if(pe.section_data_length_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true) + < directory_name_length) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + +#ifdef PE_BLISS_WINDOWS + //Set entry UNICODE name + entry.set_name(std::wstring( + reinterpret_cast<const wchar_t*>(pe.section_data_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true)), + directory_name_length)); +#else + //Set entry UNICODE name + entry.set_name(pe_utils::from_ucs2(u16string( + reinterpret_cast<const unicode16_t*>(pe.section_data_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true)), + directory_name_length))); +#endif + } + else + { + //Else - set directory ID + entry.set_id(dir_entry.Id); + } + + //If directory entry has another resource directory + if(dir_entry.DataIsDirectory) + { + entry.add_resource_directory(process_resource_directory(pe, res_rva, dir_entry.OffsetToDirectory, processed)); + } + else + { + //If directory entry has data + image_resource_data_entry data_entry = pe.section_data_from_rva<image_resource_data_entry>( + res_rva + dir_entry.OffsetToData, section_data_virtual, true); + + //Check byte count that stated by data entry + if(pe.section_data_length_from_rva(data_entry.OffsetToData, data_entry.OffsetToData, section_data_virtual, true) < data_entry.Size) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + //Add data entry to directory entry + entry.add_data_entry(resource_data_entry( + std::string(pe.section_data_from_rva(data_entry.OffsetToData, section_data_virtual, true), data_entry.Size), + data_entry.CodePage)); + } + + //Save directory entry + ret.add_resource_directory_entry(entry); + } + + //Return resource directory + return ret; +} + +//Helper function to calculate needed space for resource data +void calculate_resource_data_space(const resource_directory& root, uint32_t aligned_offset_from_section_start, uint32_t& needed_size_for_structures, uint32_t& needed_size_for_strings) +{ + needed_size_for_structures += sizeof(image_resource_directory); + for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + needed_size_for_structures += sizeof(image_resource_directory_entry); + + if((*it).is_named()) + needed_size_for_strings += static_cast<uint32_t>(((*it).get_name().length() + 1) * 2 /* unicode */ + sizeof(uint16_t) /* for string length */); + + if(!(*it).includes_data()) + calculate_resource_data_space((*it).get_resource_directory(), aligned_offset_from_section_start, needed_size_for_structures, needed_size_for_strings); + } +} + +//Helper function to calculate needed space for resource data +void calculate_resource_data_space(const resource_directory& root, uint32_t needed_size_for_structures, uint32_t needed_size_for_strings, uint32_t& needed_size_for_data, uint32_t& current_data_pos) +{ + for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + if((*it).includes_data()) + { + uint32_t data_size = static_cast<uint32_t>((*it).get_data_entry().get_data().length() + + sizeof(image_resource_data_entry) + + (pe_utils::align_up(current_data_pos, sizeof(uint32_t)) - current_data_pos) /* alignment */); + needed_size_for_data += data_size; + current_data_pos += data_size; + } + else + { + calculate_resource_data_space((*it).get_resource_directory(), needed_size_for_structures, needed_size_for_strings, needed_size_for_data, current_data_pos); + } + } +} + +//Helper: sorts resource directory entries +struct entry_sorter +{ +public: + bool operator()(const resource_directory_entry& entry1, const resource_directory_entry& entry2) const; +}; + +//Helper function to rebuild resource directory +void rebuild_resource_directory(pe_base& pe, section& resource_section, resource_directory& root, uint32_t& current_structures_pos, uint32_t& current_data_pos, uint32_t& current_strings_pos, uint32_t offset_from_section_start) +{ + //Create resource directory + image_resource_directory dir = {0}; + dir.Characteristics = root.get_characteristics(); + dir.MajorVersion = root.get_major_version(); + dir.MinorVersion = root.get_minor_version(); + dir.TimeDateStamp = root.get_timestamp(); + + { + resource_directory::entry_list& entries = root.get_entry_list(); + std::sort(entries.begin(), entries.end(), entry_sorter()); + } + + //Calculate number of named and ID entries + for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + if((*it).is_named()) + ++dir.NumberOfNamedEntries; + else + ++dir.NumberOfIdEntries; + } + + std::string& raw_data = resource_section.get_raw_data(); + + //Save resource directory + memcpy(&raw_data[current_structures_pos], &dir, sizeof(dir)); + current_structures_pos += sizeof(dir); + + uint32_t this_current_structures_pos = current_structures_pos; + + current_structures_pos += sizeof(image_resource_directory_entry) * (dir.NumberOfNamedEntries + dir.NumberOfIdEntries); + + //Create all resource directory entries + for(resource_directory::entry_list::iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + image_resource_directory_entry entry; + if((*it).is_named()) + { + entry.Name = 0x80000000 | (current_strings_pos - offset_from_section_start); + uint16_t unicode_length = static_cast<uint16_t>((*it).get_name().length()); + memcpy(&raw_data[current_strings_pos], &unicode_length, sizeof(unicode_length)); + current_strings_pos += sizeof(unicode_length); + +#ifdef PE_BLISS_WINDOWS + memcpy(&raw_data[current_strings_pos], (*it).get_name().c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); +#else + { + u16string str(pe_utils::to_ucs2((*it).get_name())); + memcpy(&raw_data[current_strings_pos], str.c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); + } +#endif + + current_strings_pos += static_cast<unsigned long>((*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); + } + else + { + entry.Name = (*it).get_id(); + } + + if((*it).includes_data()) + { + current_data_pos = pe_utils::align_up(current_data_pos, sizeof(uint32_t)); + image_resource_data_entry data_entry = {0}; + data_entry.CodePage = (*it).get_data_entry().get_codepage(); + data_entry.Size = static_cast<uint32_t>((*it).get_data_entry().get_data().length()); + data_entry.OffsetToData = pe.rva_from_section_offset(resource_section, current_data_pos + sizeof(data_entry)); + + entry.OffsetToData = current_data_pos - offset_from_section_start; + + memcpy(&raw_data[current_data_pos], &data_entry, sizeof(data_entry)); + current_data_pos += sizeof(data_entry); + + memcpy(&raw_data[current_data_pos], (*it).get_data_entry().get_data().data(), data_entry.Size); + current_data_pos += data_entry.Size; + + memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry)); + this_current_structures_pos += sizeof(entry); + } + else + { + entry.OffsetToData = 0x80000000 | (current_structures_pos - offset_from_section_start); + + memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry)); + this_current_structures_pos += sizeof(entry); + + rebuild_resource_directory(pe, resource_section, (*it).get_resource_directory(), current_structures_pos, current_data_pos, current_strings_pos, offset_from_section_start); + } + } +} + +//Helper function to rebuild resource directory +bool entry_sorter::operator()(const resource_directory_entry& entry1, const resource_directory_entry& entry2) const +{ + if(entry1.is_named() && entry2.is_named()) + return entry1.get_name() < entry2.get_name(); + else if(!entry1.is_named() && !entry2.is_named()) + return entry1.get_id() < entry2.get_id(); + else + return entry1.is_named(); +} + +//Resources rebuilder +//resource_directory - root resource directory +//resources_section - section where resource directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from resources_section raw data start +//resource_directory is non-constant, because it will be sorted +//save_to_pe_headers - if true, new resource directory information will be saved to PE image headers +//auto_strip_last_section - if true and resources are placed in the last section, it will be automatically stripped +//number_of_id_entries and number_of_named_entries for resource directories are recalculated and not used +const image_directory rebuild_resources(pe_base& pe, resource_directory& info, section& resources_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that resources_section is attached to this PE image + if(!pe.section_attached(resources_section)) + throw pe_exception("Resource section must be attached to PE file", pe_exception::section_is_not_attached); + + //Check resource directory correctness + if(info.get_entry_list().empty()) + throw pe_exception("Empty resource directory", pe_exception::incorrect_resource_directory); + + uint32_t aligned_offset_from_section_start = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + uint32_t needed_size_for_structures = aligned_offset_from_section_start - offset_from_section_start; //Calculate needed size for resource tables and data + uint32_t needed_size_for_strings = 0; + uint32_t needed_size_for_data = 0; + + calculate_resource_data_space(info, aligned_offset_from_section_start, needed_size_for_structures, needed_size_for_strings); + + { + uint32_t current_data_pos = aligned_offset_from_section_start + needed_size_for_structures + needed_size_for_strings; + calculate_resource_data_space(info, needed_size_for_structures, needed_size_for_strings, needed_size_for_data, current_data_pos); + } + + uint32_t needed_size = needed_size_for_structures + needed_size_for_strings + needed_size_for_data; + + //Check if resources_section is last one. If it's not, check if there's enough place for resource data + if(&resources_section != &*(pe.get_image_sections().end() - 1) && + (resources_section.empty() || pe_utils::align_up(resources_section.get_size_of_raw_data(), pe.get_file_alignment()) + < needed_size + aligned_offset_from_section_start)) + throw pe_exception("Insufficient space for resource directory", pe_exception::insufficient_space); + + std::string& raw_data = resources_section.get_raw_data(); + + //This will be done only if resources_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + aligned_offset_from_section_start) + raw_data.resize(needed_size + aligned_offset_from_section_start); //Expand section raw data + + uint32_t current_structures_pos = aligned_offset_from_section_start; + uint32_t current_strings_pos = current_structures_pos + needed_size_for_structures; + uint32_t current_data_pos = current_strings_pos + needed_size_for_strings; + rebuild_resource_directory(pe, resources_section, info, current_structures_pos, current_data_pos, current_strings_pos, aligned_offset_from_section_start); + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(resources_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(resources_section, aligned_offset_from_section_start), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_resource, ret.get_rva()); + pe.set_directory_size(image_directory_entry_resource, ret.get_size()); + } + + return ret; +} + +//Returns resources from PE file +const resource_directory get_resources(const pe_base& pe) +{ + resource_directory ret; + + if(!pe.has_resources()) + return ret; + + //Get resource directory RVA + uint32_t res_rva = pe.get_directory_rva(image_directory_entry_resource); + + //Store already processed directories to avoid resource loops + std::set<uint32_t> processed; + + //Process all directories (recursion) + ret = process_resource_directory(pe, res_rva, 0, processed); + + return ret; +} + +//Finds resource_directory_entry by ID +resource_directory::id_entry_finder::id_entry_finder(uint32_t id) + :id_(id) +{} + +bool resource_directory::id_entry_finder::operator()(const resource_directory_entry& entry) const +{ + return !entry.is_named() && entry.get_id() == id_; +} + +//Finds resource_directory_entry by name +resource_directory::name_entry_finder::name_entry_finder(const std::wstring& name) + :name_(name) +{} + +bool resource_directory::name_entry_finder::operator()(const resource_directory_entry& entry) const +{ + return entry.is_named() && entry.get_name() == name_; +} + +//Finds resource_directory_entry by name or ID (universal) +resource_directory::entry_finder::entry_finder(const std::wstring& name) + :name_(name), named_(true) +{} + +resource_directory::entry_finder::entry_finder(uint32_t id) + :id_(id), named_(false) +{} + +bool resource_directory::entry_finder::operator()(const resource_directory_entry& entry) const +{ + if(named_) + return entry.is_named() && entry.get_name() == name_; + else + return !entry.is_named() && entry.get_id() == id_; +} + +//Returns resource_directory_entry by ID. If not found - throws an exception +const resource_directory_entry& resource_directory::entry_by_id(uint32_t id) const +{ + entry_list::const_iterator i = std::find_if(entries_.begin(), entries_.end(), id_entry_finder(id)); + if(i == entries_.end()) + throw pe_exception("Resource directory entry not found", pe_exception::resource_directory_entry_not_found); + + return *i; +} + +//Returns resource_directory_entry by name. If not found - throws an exception +const resource_directory_entry& resource_directory::entry_by_name(const std::wstring& name) const +{ + entry_list::const_iterator i = std::find_if(entries_.begin(), entries_.end(), name_entry_finder(name)); + if(i == entries_.end()) + throw pe_exception("Resource directory entry not found", pe_exception::resource_directory_entry_not_found); + + return *i; +} +} diff --git a/tools/pe_bliss/pe_resources.h b/tools/pe_bliss/pe_resources.h new file mode 100644 index 0000000000..1eb6437563 --- /dev/null +++ b/tools/pe_bliss/pe_resources.h @@ -0,0 +1,245 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include <string> +#include <set> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing resource data entry +class resource_data_entry +{ +public: + //Default constructor + resource_data_entry(); + //Constructor from data + resource_data_entry(const std::string& data, uint32_t codepage); + + //Returns resource data codepage + uint32_t get_codepage() const; + //Returns resource data + const std::string& get_data() const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild resource directory + + //Sets resource data codepage + void set_codepage(uint32_t codepage); + //Sets resource data + void set_data(const std::string& data); + +private: + uint32_t codepage_; //Resource data codepage + std::string data_; //Resource data +}; + +//Forward declaration +class resource_directory; + +//Class representing resource directory entry +class resource_directory_entry +{ +public: + //Default constructor + resource_directory_entry(); + //Copy constructor + resource_directory_entry(const resource_directory_entry& other); + //Copy assignment operator + resource_directory_entry& operator=(const resource_directory_entry& other); + + //Returns entry ID + uint32_t get_id() const; + //Returns entry name + const std::wstring& get_name() const; + //Returns true, if entry has name + //Returns false, if entry has ID + bool is_named() const; + + //Returns true, if entry includes resource_data_entry + //Returns false, if entry includes resource_directory + bool includes_data() const; + //Returns resource_directory if entry includes it, otherwise throws an exception + const resource_directory& get_resource_directory() const; + //Returns resource_data_entry if entry includes it, otherwise throws an exception + const resource_data_entry& get_data_entry() const; + + //Destructor + ~resource_directory_entry(); + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild resource directory + + //Sets entry name + void set_name(const std::wstring& name); + //Sets entry ID + void set_id(uint32_t id); + + //Returns resource_directory if entry includes it, otherwise throws an exception + resource_directory& get_resource_directory(); + //Returns resource_data_entry if entry includes it, otherwise throws an exception + resource_data_entry& get_data_entry(); + + //Adds resource_data_entry + void add_data_entry(const resource_data_entry& entry); + //Adds resource_directory + void add_resource_directory(const resource_directory& dir); + +private: + //Destroys included data + void release(); + +private: + uint32_t id_; + std::wstring name_; + + union includes + { + //Default constructor + includes(); + + //We use pointers, we're doing manual copying here + class resource_data_entry* data_; + class resource_directory* dir_; //We use pointer, because structs include each other + }; + + includes ptr_; + + bool includes_data_, named_; +}; + +//Class representing resource directory +class resource_directory +{ +public: + typedef std::vector<resource_directory_entry> entry_list; + +public: + //Default constructor + resource_directory(); + //Constructor from data + explicit resource_directory(const pe_win::image_resource_directory& dir); + + //Returns characteristics of directory + uint32_t get_characteristics() const; + //Returns date and time stamp of directory + uint32_t get_timestamp() const; + //Returns number of named entries + uint32_t get_number_of_named_entries() const; + //Returns number of ID entries + uint32_t get_number_of_id_entries() const; + //Returns major version of directory + uint16_t get_major_version() const; + //Returns minor version of directory + uint16_t get_minor_version() const; + //Returns resource_directory_entry array + const entry_list& get_entry_list() const; + //Returns resource_directory_entry by ID. If not found - throws an exception + const resource_directory_entry& entry_by_id(uint32_t id) const; + //Returns resource_directory_entry by name. If not found - throws an exception + const resource_directory_entry& entry_by_name(const std::wstring& name) const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild resource directory + + //Adds resource_directory_entry + void add_resource_directory_entry(const resource_directory_entry& entry); + //Clears resource_directory_entry array + void clear_resource_directory_entry_list(); + + //Sets characteristics of directory + void set_characteristics(uint32_t characteristics); + //Sets date and time stamp of directory + void set_timestamp(uint32_t timestamp); + //Sets number of named entries + void set_number_of_named_entries(uint32_t number); + //Sets number of ID entries + void set_number_of_id_entries(uint32_t number); + //Sets major version of directory + void set_major_version(uint16_t major_version); + //Sets minor version of directory + void get_minor_version(uint16_t minor_version); + + //Returns resource_directory_entry array + entry_list& get_entry_list(); + +private: + uint32_t characteristics_; + uint32_t timestamp_; + uint16_t major_version_, minor_version_; + uint32_t number_of_named_entries_, number_of_id_entries_; + entry_list entries_; + +public: //Finder helpers + //Finds resource_directory_entry by ID + struct id_entry_finder + { + public: + explicit id_entry_finder(uint32_t id); + bool operator()(const resource_directory_entry& entry) const; + + private: + uint32_t id_; + }; + + //Finds resource_directory_entry by name + struct name_entry_finder + { + public: + explicit name_entry_finder(const std::wstring& name); + bool operator()(const resource_directory_entry& entry) const; + + private: + std::wstring name_; + }; + + //Finds resource_directory_entry by name or ID (universal) + struct entry_finder + { + public: + explicit entry_finder(const std::wstring& name); + explicit entry_finder(uint32_t id); + bool operator()(const resource_directory_entry& entry) const; + + private: + std::wstring name_; + uint32_t id_; + bool named_; + }; +}; + +//Returns resources (root resource_directory) from PE file +const resource_directory get_resources(const pe_base& pe); + +//Resources rebuilder +//resource_directory - root resource directory +//resources_section - section where resource directory will be placed (must be attached to PE image) +//resource_directory is non-constant, because it will be sorted +//offset_from_section_start - offset from resources_section raw data start +//save_to_pe_headers - if true, new resource directory information will be saved to PE image headers +//auto_strip_last_section - if true and resources are placed in the last section, it will be automatically stripped +//number_of_id_entries and number_of_named_entries for resource directories are recalculated and not used +const image_directory rebuild_resources(pe_base& pe, resource_directory& info, section& resources_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/pe_rich_data.cpp b/tools/pe_bliss/pe_rich_data.cpp new file mode 100644 index 0000000000..e92f7ddc1b --- /dev/null +++ b/tools/pe_bliss/pe_rich_data.cpp @@ -0,0 +1,152 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "pe_rich_data.h" + +namespace pe_bliss +{ +//STUB OVERLAY +//Default constructor +rich_data::rich_data() + :number_(0), version_(0), times_(0) +{} + +//Who knows, what these fields mean... +uint32_t rich_data::get_number() const +{ + return number_; +} + +uint32_t rich_data::get_version() const +{ + return version_; +} + +uint32_t rich_data::get_times() const +{ + return times_; +} + +void rich_data::set_number(uint32_t number) +{ + number_ = number; +} + +void rich_data::set_version(uint32_t version) +{ + version_ = version; +} + +void rich_data::set_times(uint32_t times) +{ + times_ = times; +} + +//Returns MSVC rich data +const rich_data_list get_rich_data(const pe_base& pe) +{ + //Returned value + rich_data_list ret; + + const std::string& rich_overlay = pe.get_stub_overlay(); + + //If there's no rich overlay, return empty vector + if(rich_overlay.size() < sizeof(uint32_t)) + return ret; + + //True if rich data was found + bool found = false; + + //Rich overlay ID ("Rich" word) + static const uint32_t rich_overlay_id = 0x68636952; + + //Search for rich data overlay ID + const char* begin = &rich_overlay[0]; + const char* end = begin + rich_overlay.length(); + for(; begin != end; ++begin) + { + if(*reinterpret_cast<const uint32_t*>(begin) == rich_overlay_id) + { + found = true; //We've found it! + break; + } + } + + //If we found it + if(found) + { + //Check remaining length + if(static_cast<size_t>(end - begin) < sizeof(uint32_t)) + return ret; + + //The XOR key is after "Rich" word, we should get it + uint32_t xorkey = *reinterpret_cast<const uint32_t*>(begin + sizeof(uint32_t)); + + //True if rich data was found + found = false; + + //Second search for signature "DanS" + begin = &rich_overlay[0]; + for(; begin != end; ++begin) + { + if((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) == 0x536e6144) //"DanS" + { + found = true; + break; + } + } + + //If second signature is found + if(found) + { + begin += sizeof(uint32_t) * 3; + //List all rich data structures + while(begin < end) + { + begin += sizeof(uint32_t); + if(begin >= end) + break; + + //Check for rich overlay data end ("Rich" word reached) + if(*reinterpret_cast<const uint32_t*>(begin) == rich_overlay_id) + break; + + //Create rich_data structure + rich_data data; + data.set_number((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) >> 16); + data.set_version((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) & 0xFFFF); + + begin += sizeof(uint32_t); + if(begin >= end) + break; + + data.set_times(*reinterpret_cast<const uint32_t*>(begin) ^ xorkey); + + //Save rich data structure + ret.push_back(data); + } + } + } + + //Return rich data structures list + return ret; +} +} diff --git a/tools/pe_bliss/pe_rich_data.h b/tools/pe_bliss/pe_rich_data.h new file mode 100644 index 0000000000..3e01d3c011 --- /dev/null +++ b/tools/pe_bliss/pe_rich_data.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <vector> +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Rich data overlay class of Microsoft Visual Studio +class rich_data +{ +public: + //Default constructor + rich_data(); + +public: //Getters + //Who knows, what these fields mean... + uint32_t get_number() const; + uint32_t get_version() const; + uint32_t get_times() const; + +public: //Setters, used by PE library only + void set_number(uint32_t number); + void set_version(uint32_t version); + void set_times(uint32_t times); + +private: + uint32_t number_; + uint32_t version_; + uint32_t times_; +}; + +//Rich data list typedef +typedef std::vector<rich_data> rich_data_list; + +//Returns a vector with rich data (stub overlay) +const rich_data_list get_rich_data(const pe_base& pe); +} diff --git a/tools/pe_bliss/pe_section.cpp b/tools/pe_bliss/pe_section.cpp new file mode 100644 index 0000000000..72127e22e2 --- /dev/null +++ b/tools/pe_bliss/pe_section.cpp @@ -0,0 +1,303 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include <string.h> +#include "utils.h" +#include "pe_section.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Section structure default constructor +section::section() + :old_size_(static_cast<size_t>(-1)) +{ + memset(&header_, 0, sizeof(image_section_header)); +} + +//Sets the name of section (8 characters maximum) +void section::set_name(const std::string& name) +{ + memset(header_.Name, 0, sizeof(header_.Name)); + memcpy(header_.Name, name.c_str(), std::min<size_t>(name.length(), sizeof(header_.Name))); +} + +//Returns section name +const std::string section::get_name() const +{ + char buf[9] = {0}; + memcpy(buf, header_.Name, 8); + return std::string(buf); +} + +//Set flag (attribute) of section +section& section::set_flag(uint32_t flag, bool setflag) +{ + if(setflag) + header_.Characteristics |= flag; + else + header_.Characteristics &= ~flag; + + return *this; +} + +//Sets "readable" attribute of section +section& section::readable(bool readable) +{ + return set_flag(image_scn_mem_read, readable); +} + +//Sets "writeable" attribute of section +section& section::writeable(bool writeable) +{ + return set_flag(image_scn_mem_write, writeable); +} + +//Sets "executable" attribute of section +section& section::executable(bool executable) +{ + return set_flag(image_scn_mem_execute, executable); +} + +//Sets "shared" attribute of section +section& section::shared(bool shared) +{ + return set_flag(image_scn_mem_shared, shared); +} + +//Sets "discardable" attribute of section +section& section::discardable(bool discardable) +{ + return set_flag(image_scn_mem_discardable, discardable); +} + +//Returns true if section is readable +bool section::readable() const +{ + return (header_.Characteristics & image_scn_mem_read) != 0; +} + +//Returns true if section is writeable +bool section::writeable() const +{ + return (header_.Characteristics & image_scn_mem_write) != 0; +} + +//Returns true if section is executable +bool section::executable() const +{ + return (header_.Characteristics & image_scn_mem_execute) != 0; +} + +bool section::shared() const +{ + return (header_.Characteristics & image_scn_mem_shared) != 0; +} + +bool section::discardable() const +{ + return (header_.Characteristics & image_scn_mem_discardable) != 0; +} + +//Returns true if section has no RAW data +bool section::empty() const +{ + if(old_size_ != static_cast<size_t>(-1)) //If virtual memory is mapped, check raw data length (old_size_) + return old_size_ == 0; + else + return raw_data_.empty(); +} + +//Returns raw section data from file image +std::string& section::get_raw_data() +{ + unmap_virtual(); + return raw_data_; +} + +//Sets raw section data from file image +void section::set_raw_data(const std::string& data) +{ + old_size_ = static_cast<size_t>(-1); + raw_data_ = data; +} + +//Returns raw section data from file image +const std::string& section::get_raw_data() const +{ + unmap_virtual(); + return raw_data_; +} + +//Returns mapped virtual section data +const std::string& section::get_virtual_data(uint32_t section_alignment) const +{ + map_virtual(section_alignment); + return raw_data_; +} + +//Returns mapped virtual section data +std::string& section::get_virtual_data(uint32_t section_alignment) +{ + map_virtual(section_alignment); + return raw_data_; +} + +//Maps virtual section data +void section::map_virtual(uint32_t section_alignment) const +{ + uint32_t aligned_virtual_size = get_aligned_virtual_size(section_alignment); + if(old_size_ == static_cast<size_t>(-1) && aligned_virtual_size && aligned_virtual_size > raw_data_.length()) + { + old_size_ = raw_data_.length(); + raw_data_.resize(aligned_virtual_size, 0); + } +} + +//Unmaps virtual section data +void section::unmap_virtual() const +{ + if(old_size_ != static_cast<size_t>(-1)) + { + raw_data_.resize(old_size_, 0); + old_size_ = static_cast<size_t>(-1); + } +} + +//Returns section virtual size +uint32_t section::get_virtual_size() const +{ + return header_.Misc.VirtualSize; +} + +//Returns section virtual address +uint32_t section::get_virtual_address() const +{ + return header_.VirtualAddress; +} + +//Returns size of section raw data +uint32_t section::get_size_of_raw_data() const +{ + return header_.SizeOfRawData; +} + +//Returns pointer to raw section data in PE file +uint32_t section::get_pointer_to_raw_data() const +{ + return header_.PointerToRawData; +} + +//Returns section characteristics +uint32_t section::get_characteristics() const +{ + return header_.Characteristics; +} + +//Returns raw image section header +const pe_win::image_section_header& section::get_raw_header() const +{ + return header_; +} + +//Returns raw image section header +pe_win::image_section_header& section::get_raw_header() +{ + return header_; +} + +//Calculates aligned virtual section size +uint32_t section::get_aligned_virtual_size(uint32_t section_alignment) const +{ + if(get_size_of_raw_data()) + { + if(!get_virtual_size()) + { + //If section virtual size is zero + //Set aligned virtual size of section as aligned raw size + return pe_utils::align_up(get_size_of_raw_data(), section_alignment); + } + } + + return pe_utils::align_up(get_virtual_size(), section_alignment); +} + +//Calculates aligned raw section size +uint32_t section::get_aligned_raw_size(uint32_t file_alignment) const +{ + if(get_size_of_raw_data()) + return pe_utils::align_up(get_size_of_raw_data(), file_alignment); + else + return 0; +} + +//Sets size of raw section data +void section::set_size_of_raw_data(uint32_t size_of_raw_data) +{ + header_.SizeOfRawData = size_of_raw_data; +} + +//Sets pointer to section raw data +void section::set_pointer_to_raw_data(uint32_t pointer_to_raw_data) +{ + header_.PointerToRawData = pointer_to_raw_data; +} + +//Sets section characteristics +void section::set_characteristics(uint32_t characteristics) +{ + header_.Characteristics = characteristics; +} + +//Sets section virtual size +void section::set_virtual_size(uint32_t virtual_size) +{ + header_.Misc.VirtualSize = virtual_size; +} + +//Sets section virtual address +void section::set_virtual_address(uint32_t virtual_address) +{ + header_.VirtualAddress = virtual_address; +} + +//Section by file offset finder helper (4gb max) +section_by_raw_offset::section_by_raw_offset(uint32_t offset) + :offset_(offset) +{} + +bool section_by_raw_offset::operator()(const section& s) const +{ + return (s.get_pointer_to_raw_data() <= offset_) + && (s.get_pointer_to_raw_data() + s.get_size_of_raw_data() > offset_); +} + +section_ptr_finder::section_ptr_finder(const section& s) + :s_(s) +{} + +bool section_ptr_finder::operator()(const section& s) const +{ + return &s == &s_; +} +} diff --git a/tools/pe_bliss/pe_section.h b/tools/pe_bliss/pe_section.h new file mode 100644 index 0000000000..617ecebe26 --- /dev/null +++ b/tools/pe_bliss/pe_section.h @@ -0,0 +1,158 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <vector> +#include "pe_structures.h" + +namespace pe_bliss +{ +//Enumeration of section data types, used in functions below +enum section_data_type +{ + section_data_raw, + section_data_virtual +}; + +//Class representing image section +class section +{ +public: + //Default constructor + section(); + + //Sets the name of section (stripped to 8 characters) + void set_name(const std::string& name); + + //Returns the name of section + const std::string get_name() const; + + //Changes attributes of section + section& readable(bool readable); + section& writeable(bool writeable); + section& executable(bool executable); + section& shared(bool shared); + section& discardable(bool discardable); + + //Returns attributes of section + bool readable() const; + bool writeable() const; + bool executable() const; + bool shared() const; + bool discardable() const; + + //Returns true if section has no RAW data + bool empty() const; + + //Returns raw section data from file image + std::string& get_raw_data(); + //Returns raw section data from file image + const std::string& get_raw_data() const; + //Returns mapped virtual section data + const std::string& get_virtual_data(uint32_t section_alignment) const; + //Returns mapped virtual section data + std::string& get_virtual_data(uint32_t section_alignment); + +public: //Header getters + //Returns section virtual size + uint32_t get_virtual_size() const; + //Returns section virtual address (RVA) + uint32_t get_virtual_address() const; + //Returns size of section raw data + uint32_t get_size_of_raw_data() const; + //Returns pointer to raw section data in PE file + uint32_t get_pointer_to_raw_data() const; + //Returns section characteristics + uint32_t get_characteristics() const; + + //Returns raw image section header + const pe_win::image_section_header& get_raw_header() const; + +public: //Aligned sizes calculation + //Calculates aligned virtual section size + uint32_t get_aligned_virtual_size(uint32_t section_alignment) const; + //Calculates aligned raw section size + uint32_t get_aligned_raw_size(uint32_t file_alignment) const; + +public: //Setters + //Sets size of raw section data + void set_size_of_raw_data(uint32_t size_of_raw_data); + //Sets pointer to section raw data + void set_pointer_to_raw_data(uint32_t pointer_to_raw_data); + //Sets section characteristics + void set_characteristics(uint32_t characteristics); + //Sets raw section data from file image + void set_raw_data(const std::string& data); + +public: //Setters, be careful + //Sets section virtual size (doesn't set internal aligned virtual size, changes only header value) + //Better use pe_base::set_section_virtual_size + void set_virtual_size(uint32_t virtual_size); + //Sets section virtual address + void set_virtual_address(uint32_t virtual_address); + //Returns raw image section header + pe_win::image_section_header& get_raw_header(); + +private: + //Section header + pe_win::image_section_header header_; + + //Maps virtual section data + void map_virtual(uint32_t section_alignment) const; + + //Unmaps virtual section data + void unmap_virtual() const; + + //Set flag (attribute) of section + section& set_flag(uint32_t flag, bool setflag); + + //Old size of section (stored after mapping of virtual section memory) + mutable std::size_t old_size_; + + //Section raw/virtual data + mutable std::string raw_data_; +}; + +//Section by file offset finder helper (4gb max) +struct section_by_raw_offset +{ +public: + explicit section_by_raw_offset(uint32_t offset); + bool operator()(const section& s) const; + +private: + uint32_t offset_; +}; + +//Helper: finder of section* in sections list +struct section_ptr_finder +{ +public: + explicit section_ptr_finder(const section& s); + bool operator()(const section& s) const; + +private: + const section& s_; +}; + +typedef std::vector<section> section_list; +} diff --git a/tools/pe_bliss/pe_structures.h b/tools/pe_bliss/pe_structures.h new file mode 100644 index 0000000000..efc99103b2 --- /dev/null +++ b/tools/pe_bliss/pe_structures.h @@ -0,0 +1,1028 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <sstream> +#include "stdint_defs.h" +#if defined(_MSC_VER) or defined(__MINGW32__) +#define PE_BLISS_WINDOWS +#endif + +namespace pe_bliss +{ +//Enumeration of PE types +enum pe_type +{ + pe_type_32, + pe_type_64 +}; + +namespace pe_win +{ +const uint32_t image_numberof_directory_entries = 16; +const uint32_t image_nt_optional_hdr32_magic = 0x10b; +const uint32_t image_nt_optional_hdr64_magic = 0x20b; +const uint32_t image_resource_name_is_string = 0x80000000; +const uint32_t image_resource_data_is_directory = 0x80000000; + +const uint32_t image_dllcharacteristics_dynamic_base = 0x0040; // DLL can move. +const uint32_t image_dllcharacteristics_force_integrity = 0x0080; // Code Integrity Image +const uint32_t image_dllcharacteristics_nx_compat = 0x0100; // Image is NX compatible +const uint32_t image_dllcharacteristics_no_isolation = 0x0200; // Image understands isolation and doesn't want it +const uint32_t image_dllcharacteristics_no_seh = 0x0400; // Image does not use SEH. No SE handler may reside in this image +const uint32_t image_dllcharacteristics_no_bind = 0x0800; // Do not bind this image. +const uint32_t image_dllcharacteristics_wdm_driver = 0x2000; // Driver uses WDM model +const uint32_t image_dllcharacteristics_terminal_server_aware = 0x8000; + +const uint32_t image_sizeof_file_header = 20; + +const uint32_t image_file_relocs_stripped = 0x0001; // Relocation info stripped from file. +const uint32_t image_file_executable_image = 0x0002; // File is executable (i.e. no unresolved externel references). +const uint32_t image_file_line_nums_stripped = 0x0004; // Line nunbers stripped from file. +const uint32_t image_file_local_syms_stripped = 0x0008; // Local symbols stripped from file. +const uint32_t image_file_aggresive_ws_trim = 0x0010; // Agressively trim working set +const uint32_t image_file_large_address_aware = 0x0020; // App can handle >2gb addresses +const uint32_t image_file_bytes_reversed_lo = 0x0080; // Bytes of machine word are reversed. +const uint32_t image_file_32bit_machine = 0x0100; // 32 bit word machine. +const uint32_t image_file_debug_stripped = 0x0200; // Debugging info stripped from file in .DBG file +const uint32_t image_file_removable_run_from_swap = 0x0400; // If Image is on removable media, copy and run from the swap file. +const uint32_t image_file_net_run_from_swap = 0x0800; // If Image is on Net, copy and run from the swap file. +const uint32_t image_file_system = 0x1000; // System File. +const uint32_t image_file_dll = 0x2000; // File is a DLL. +const uint32_t image_file_up_system_only = 0x4000; // File should only be run on a UP machine +const uint32_t image_file_bytes_reversed_hi = 0x8000; // Bytes of machine word are reversed. + +const uint32_t image_scn_lnk_nreloc_ovfl = 0x01000000; // Section contains extended relocations. +const uint32_t image_scn_mem_discardable = 0x02000000; // Section can be discarded. +const uint32_t image_scn_mem_not_cached = 0x04000000; // Section is not cachable. +const uint32_t image_scn_mem_not_paged = 0x08000000; // Section is not pageable. +const uint32_t image_scn_mem_shared = 0x10000000; // Section is shareable. +const uint32_t image_scn_mem_execute = 0x20000000; // Section is executable. +const uint32_t image_scn_mem_read = 0x40000000; // Section is readable. +const uint32_t image_scn_mem_write = 0x80000000; // Section is writeable. + +const uint32_t image_scn_cnt_code = 0x00000020; // Section contains code. +const uint32_t image_scn_cnt_initialized_data = 0x00000040; // Section contains initialized data. +const uint32_t image_scn_cnt_uninitialized_data = 0x00000080; // Section contains uninitialized data. + +//Directory Entries +const uint32_t image_directory_entry_export = 0; // Export Directory +const uint32_t image_directory_entry_import = 1; // Import Directory +const uint32_t image_directory_entry_resource = 2; // Resource Directory +const uint32_t image_directory_entry_exception = 3; // Exception Directory +const uint32_t image_directory_entry_security = 4; // Security Directory +const uint32_t image_directory_entry_basereloc = 5; // Base Relocation Table +const uint32_t image_directory_entry_debug = 6; // Debug Directory +const uint32_t image_directory_entry_architecture = 7; // Architecture Specific Data +const uint32_t image_directory_entry_globalptr = 8; // RVA of GP +const uint32_t image_directory_entry_tls = 9; // TLS Directory +const uint32_t image_directory_entry_load_config = 10; // Load Configuration Directory +const uint32_t image_directory_entry_bound_import = 11; // Bound Import Directory in headers +const uint32_t image_directory_entry_iat = 12; // Import Address Table +const uint32_t image_directory_entry_delay_import = 13; // Delay Load Import Descriptors +const uint32_t image_directory_entry_com_descriptor = 14; // COM Runtime descriptor + +//Subsystem Values +const uint32_t image_subsystem_unknown = 0; // Unknown subsystem. +const uint32_t image_subsystem_native = 1; // Image doesn't require a subsystem. +const uint32_t image_subsystem_windows_gui = 2; // Image runs in the Windows GUI subsystem. +const uint32_t image_subsystem_windows_cui = 3; // Image runs in the Windows character subsystem. +const uint32_t image_subsystem_os2_cui = 5; // image runs in the OS/2 character subsystem. +const uint32_t image_subsystem_posix_cui = 7; // image runs in the Posix character subsystem. +const uint32_t image_subsystem_native_windows = 8; // image is a native Win9x driver. +const uint32_t image_subsystem_windows_ce_gui = 9; // Image runs in the Windows CE subsystem. +const uint32_t image_subsystem_efi_application = 10; // +const uint32_t image_subsystem_efi_boot_service_driver = 11; // +const uint32_t image_subsystem_efi_runtime_driver = 12; // +const uint32_t image_subsystem_efi_rom = 13; +const uint32_t image_subsystem_xbox = 14; +const uint32_t image_subsystem_windows_boot_application = 16; + +//Imports +const uint64_t image_ordinal_flag64 = 0x8000000000000000ull; +const uint32_t image_ordinal_flag32 = 0x80000000; + +//Based relocation types +const uint32_t image_rel_based_absolute = 0; +const uint32_t image_rel_based_high = 1; +const uint32_t image_rel_based_low = 2; +const uint32_t image_rel_based_highlow = 3; +const uint32_t image_rel_based_highadj = 4; +const uint32_t image_rel_based_mips_jmpaddr = 5; +const uint32_t image_rel_based_mips_jmpaddr16 = 9; +const uint32_t image_rel_based_ia64_imm64 = 9; +const uint32_t image_rel_based_dir64 = 10; + +//Exception directory +//The function has an exception handler that should be called when looking for functions that need to examine exceptions +const uint32_t unw_flag_ehandler = 0x01; +//The function has a termination handler that should be called when unwinding an exception +const uint32_t unw_flag_uhandler = 0x02; +//This unwind info structure is not the primary one for the procedure. +//Instead, the chained unwind info entry is the contents of a previous RUNTIME_FUNCTION entry. +//If this flag is set, then the UNW_FLAG_EHANDLER and UNW_FLAG_UHANDLER flags must be cleared. +//Also, the frame register and fixed-stack allocation fields must have the same values as in the primary unwind info +const uint32_t unw_flag_chaininfo = 0x04; + +//Debug +const uint32_t image_debug_misc_exename = 1; +const uint32_t image_debug_type_unknown = 0; +const uint32_t image_debug_type_coff = 1; +const uint32_t image_debug_type_codeview = 2; +const uint32_t image_debug_type_fpo = 3; +const uint32_t image_debug_type_misc = 4; +const uint32_t image_debug_type_exception = 5; +const uint32_t image_debug_type_fixup = 6; +const uint32_t image_debug_type_omap_to_src = 7; +const uint32_t image_debug_type_omap_from_src = 8; +const uint32_t image_debug_type_borland = 9; +const uint32_t image_debug_type_reserved10 = 10; +const uint32_t image_debug_type_clsid = 11; + + +//Storage classes +const uint32_t image_sym_class_end_of_function = static_cast<uint8_t>(-1); +const uint32_t image_sym_class_null = 0x0000; +const uint32_t image_sym_class_automatic = 0x0001; +const uint32_t image_sym_class_external = 0x0002; +const uint32_t image_sym_class_static = 0x0003; +const uint32_t image_sym_class_register = 0x0004; +const uint32_t image_sym_class_external_def = 0x0005; +const uint32_t image_sym_class_label = 0x0006; +const uint32_t image_sym_class_undefined_label = 0x0007; +const uint32_t image_sym_class_member_of_struct = 0x0008; +const uint32_t image_sym_class_argument = 0x0009; +const uint32_t image_sym_class_struct_tag = 0x000a; +const uint32_t image_sym_class_member_of_union = 0x000b; +const uint32_t image_sym_class_union_tag = 0x000c; +const uint32_t image_sym_class_type_definition = 0x000d; +const uint32_t image_sym_class_undefined_static = 0x000e; +const uint32_t image_sym_class_enum_tag = 0x000f; +const uint32_t image_sym_class_member_of_enum = 0x0010; +const uint32_t image_sym_class_register_param = 0x0011; +const uint32_t image_sym_class_bit_field = 0x0012; + +const uint32_t image_sym_class_far_external = 0x0044; + +const uint32_t image_sym_class_block = 0x0064; +const uint32_t image_sym_class_function = 0x0065; +const uint32_t image_sym_class_end_of_struct = 0x0066; +const uint32_t image_sym_class_file = 0x0067; + +const uint32_t image_sym_class_section = 0x0068; +const uint32_t image_sym_class_weak_external = 0x0069; + +const uint32_t image_sym_class_clr_token = 0x006b; + +//type packing constants +const uint32_t n_btmask = 0x000f; +const uint32_t n_tmask = 0x0030; +const uint32_t n_tmask1 = 0x00c0; +const uint32_t n_tmask2 = 0x00f0; +const uint32_t n_btshft = 4; +const uint32_t n_tshift = 2; + +//Type (derived) values. +const uint32_t image_sym_dtype_null = 0; // no derived type. +const uint32_t image_sym_dtype_pointer = 1; // pointer. +const uint32_t image_sym_dtype_function = 2; // function. +const uint32_t image_sym_dtype_array = 3; // array. + +// Is x a function? +//TODO +#ifndef ISFCN +#define ISFCN(x) (((x) & n_tmask) == (image_sym_dtype_function << n_btshft)) +#endif + +//Version info +const uint32_t vs_ffi_fileflagsmask = 0x0000003FL; + +const uint32_t vs_ffi_signature = 0xFEEF04BDL; +const uint32_t vs_ffi_strucversion = 0x00010000L; + +/* ----- VS_VERSION.dwFileFlags ----- */ +const uint32_t vs_ff_debug = 0x00000001L; +const uint32_t vs_ff_prerelease = 0x00000002L; +const uint32_t vs_ff_patched = 0x00000004L; +const uint32_t vs_ff_privatebuild = 0x00000008L; +const uint32_t vs_ff_infoinferred = 0x00000010L; +const uint32_t vs_ff_specialbuild = 0x00000020L; + +/* ----- VS_VERSION.dwFileOS ----- */ +const uint32_t vos_unknown = 0x00000000L; +const uint32_t vos_dos = 0x00010000L; +const uint32_t vos_os216 = 0x00020000L; +const uint32_t vos_os232 = 0x00030000L; +const uint32_t vos_nt = 0x00040000L; +const uint32_t vos_wince = 0x00050000L; + +const uint32_t vos__base = 0x00000000L; +const uint32_t vos__windows16 = 0x00000001L; +const uint32_t vos__pm16 = 0x00000002L; +const uint32_t vos__pm32 = 0x00000003L; +const uint32_t vos__windows32 = 0x00000004L; + +const uint32_t vos_dos_windows16 = 0x00010001L; +const uint32_t vos_dos_windows32 = 0x00010004L; +const uint32_t vos_os216_pm16 = 0x00020002L; +const uint32_t vos_os232_pm32 = 0x00030003L; +const uint32_t vos_nt_windows32 = 0x00040004L; + +/* ----- VS_VERSION.dwFileType ----- */ +const uint32_t vft_unknown = 0x00000000L; +const uint32_t vft_app = 0x00000001L; +const uint32_t vft_dll = 0x00000002L; +const uint32_t vft_drv = 0x00000003L; +const uint32_t vft_font = 0x00000004L; +const uint32_t vft_vxd = 0x00000005L; +const uint32_t vft_static_lib = 0x00000007L; + +const uint32_t message_resource_unicode = 0x0001; + +#pragma pack(push, 1) + +//Windows GUID structure +struct guid +{ + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +}; + +//DOS .EXE header +struct image_dos_header +{ + uint16_t e_magic; // Magic number + uint16_t e_cblp; // Bytes on last page of file + uint16_t e_cp; // Pages in file + uint16_t e_crlc; // Relocations + uint16_t e_cparhdr; // Size of header in paragraphs + uint16_t e_minalloc; // Minimum extra paragraphs needed + uint16_t e_maxalloc; // Maximum extra paragraphs needed + uint16_t e_ss; // Initial (relative) SS value + uint16_t e_sp; // Initial SP value + uint16_t e_csum; // Checksum + uint16_t e_ip; // Initial IP value + uint16_t e_cs; // Initial (relative) CS value + uint16_t e_lfarlc; // File address of relocation table + uint16_t e_ovno; // Overlay number + uint16_t e_res[4]; // Reserved words + uint16_t e_oemid; // OEM identifier (for e_oeminfo) + uint16_t e_oeminfo; // OEM information; e_oemid specific + uint16_t e_res2[10]; // Reserved words + int32_t e_lfanew; // File address of new exe header +}; + +//Directory format +struct image_data_directory +{ + uint32_t VirtualAddress; + uint32_t Size; +}; + +//Optional header format +struct image_optional_header32 +{ + //Standard fields + uint16_t Magic; + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint32_t BaseOfData; + + //NT additional fields + uint32_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint32_t SizeOfStackReserve; + uint32_t SizeOfStackCommit; + uint32_t SizeOfHeapReserve; + uint32_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + image_data_directory DataDirectory[image_numberof_directory_entries]; +}; + +struct image_optional_header64 +{ + uint16_t Magic; + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint64_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint64_t SizeOfStackReserve; + uint64_t SizeOfStackCommit; + uint64_t SizeOfHeapReserve; + uint64_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + image_data_directory DataDirectory[image_numberof_directory_entries]; +}; + +struct image_file_header +{ + uint16_t Machine; + uint16_t NumberOfSections; + uint32_t TimeDateStamp; + uint32_t PointerToSymbolTable; + uint32_t NumberOfSymbols; + uint16_t SizeOfOptionalHeader; + uint16_t Characteristics; +}; + +struct image_nt_headers64 +{ + uint32_t Signature; + image_file_header FileHeader; + image_optional_header64 OptionalHeader; +}; + +struct image_nt_headers32 +{ + uint32_t Signature; + image_file_header FileHeader; + image_optional_header32 OptionalHeader; +}; + +//Section header format +struct image_section_header +{ + uint8_t Name[8]; + union + { + uint32_t PhysicalAddress; + uint32_t VirtualSize; + } Misc; + + uint32_t VirtualAddress; + uint32_t SizeOfRawData; + uint32_t PointerToRawData; + uint32_t PointerToRelocations; + uint32_t PointerToLinenumbers; + uint16_t NumberOfRelocations; + uint16_t NumberOfLinenumbers; + uint32_t Characteristics; +}; + + +/// RESOURCES /// +struct image_resource_directory +{ + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint16_t NumberOfNamedEntries; + uint16_t NumberOfIdEntries; + // IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[]; +}; + +struct vs_fixedfileinfo +{ + uint32_t dwSignature; /* e.g. 0xfeef04bd */ + uint32_t dwStrucVersion; /* e.g. 0x00000042 = "0.42" */ + uint32_t dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */ + uint32_t dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */ + uint32_t dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */ + uint32_t dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */ + uint32_t dwFileFlagsMask; /* = 0x3F for version "0.42" */ + uint32_t dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */ + uint32_t dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */ + uint32_t dwFileType; /* e.g. VFT_DRIVER */ + uint32_t dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */ + uint32_t dwFileDateMS; /* e.g. 0 */ + uint32_t dwFileDateLS; /* e.g. 0 */ +}; + +struct bitmapinfoheader +{ + uint32_t biSize; + int32_t biWidth; + int32_t biHeight; + uint16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + int32_t biXPelsPerMeter; + int32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; +}; + +struct message_resource_entry +{ + uint16_t Length; + uint16_t Flags; + uint8_t Text[1]; +}; + +struct message_resource_block +{ + uint32_t LowId; + uint32_t HighId; + uint32_t OffsetToEntries; +}; + +struct message_resource_data +{ + uint32_t NumberOfBlocks; + message_resource_block Blocks[1]; +}; + +struct image_resource_directory_entry +{ + union + { + struct + { + uint32_t NameOffset:31; + uint32_t NameIsString:1; + }; + uint32_t Name; + uint16_t Id; + }; + + union + { + uint32_t OffsetToData; + struct + { + uint32_t OffsetToDirectory:31; + uint32_t DataIsDirectory:1; + }; + }; +}; + +struct image_resource_data_entry +{ + uint32_t OffsetToData; + uint32_t Size; + uint32_t CodePage; + uint32_t Reserved; +}; + +#pragma pack(push, 2) +struct bitmapfileheader +{ + uint16_t bfType; + uint32_t bfSize; + uint16_t bfReserved1; + uint16_t bfReserved2; + uint32_t bfOffBits; +}; +#pragma pack(pop) + + + +//Structure representing ICON file header +struct ico_header +{ + uint16_t Reserved; + uint16_t Type; //1 + uint16_t Count; //Count of icons included in icon group +}; + +//Structure that is stored in icon group directory in PE resources +struct icon_group +{ + uint8_t Width; + uint8_t Height; + uint8_t ColorCount; + uint8_t Reserved; + uint16_t Planes; + uint16_t BitCount; + uint32_t SizeInBytes; + uint16_t Number; //Represents resource ID in PE icon list +}; + +//Structure representing ICON directory entry inside ICON file +struct icondirentry +{ + uint8_t Width; + uint8_t Height; + uint8_t ColorCount; + uint8_t Reserved; + uint16_t Planes; + uint16_t BitCount; + uint32_t SizeInBytes; + uint32_t ImageOffset; //Offset from start of header to the image +}; + +//Structure representing CURSOR file header +struct cursor_header +{ + uint16_t Reserved; + uint16_t Type; //2 + uint16_t Count; //Count of cursors included in cursor group +}; + +struct cursor_group +{ + uint16_t Width; + uint16_t Height; //Divide by 2 to get the actual height. + uint16_t Planes; + uint16_t BitCount; + uint32_t SizeInBytes; + uint16_t Number; //Represents resource ID in PE icon list +}; + +//Structure representing CURSOR directory entry inside CURSOR file +struct cursordirentry +{ + uint8_t Width; //Set to CURSOR_GROUP::Height/2. + uint8_t Height; + uint8_t ColorCount; + uint8_t Reserved; + uint16_t HotspotX; + uint16_t HotspotY; + uint32_t SizeInBytes; + uint32_t ImageOffset; //Offset from start of header to the image +}; + +//Structure representing BLOCK in version info resource +struct version_info_block //(always aligned on 32-bit (DWORD) boundary) +{ + uint16_t Length; //Length of this block (doesn't include padding) + uint16_t ValueLength; //Value length (if any) + uint16_t Type; //Value type (0 = binary, 1 = text) + uint16_t Key[1]; //Value name (block key) (always NULL terminated) + + ////////// + //WORD padding1[]; //Padding, if any (ALIGNMENT) + //xxxxx Value[]; //Value data, if any (*ALIGNED*) + //WORD padding2[]; //Padding, if any (ALIGNMENT) + //xxxxx Child[]; //Child block(s), if any (*ALIGNED*) + ////////// +}; + + +/// IMPORTS /// +#pragma pack(push, 8) +struct image_thunk_data64 +{ + union + { + uint64_t ForwarderString; // PBYTE + uint64_t Function; // PDWORD + uint64_t Ordinal; + uint64_t AddressOfData; // PIMAGE_IMPORT_BY_NAME + } u1; +}; +#pragma pack(pop) + +struct image_thunk_data32 +{ + union + { + uint32_t ForwarderString; // PBYTE + uint32_t Function; // PDWORD + uint32_t Ordinal; + uint32_t AddressOfData; // PIMAGE_IMPORT_BY_NAME + } u1; +}; + +struct image_import_descriptor +{ + union + { + uint32_t Characteristics; // 0 for terminating null import descriptor + uint32_t OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) + }; + + uint32_t TimeDateStamp; // 0 if not bound, + // -1 if bound, and real date\time stamp + // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) + // O.W. date/time stamp of DLL bound to (Old BIND) + + uint32_t ForwarderChain; // -1 if no forwarders + uint32_t Name; + uint32_t FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) +}; + + +/// TLS /// +struct image_tls_directory64 +{ + uint64_t StartAddressOfRawData; + uint64_t EndAddressOfRawData; + uint64_t AddressOfIndex; // PDWORD + uint64_t AddressOfCallBacks; // PIMAGE_TLS_CALLBACK *; + uint32_t SizeOfZeroFill; + uint32_t Characteristics; +}; + +struct image_tls_directory32 +{ + uint32_t StartAddressOfRawData; + uint32_t EndAddressOfRawData; + uint32_t AddressOfIndex; // PDWORD + uint32_t AddressOfCallBacks; // PIMAGE_TLS_CALLBACK * + uint32_t SizeOfZeroFill; + uint32_t Characteristics; +}; + + +/// Export Format /// +struct image_export_directory +{ + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t Name; + uint32_t Base; + uint32_t NumberOfFunctions; + uint32_t NumberOfNames; + uint32_t AddressOfFunctions; // RVA from base of image + uint32_t AddressOfNames; // RVA from base of image + uint32_t AddressOfNameOrdinals; // RVA from base of image +}; + + +/// Based relocation format /// +struct image_base_relocation +{ + uint32_t VirtualAddress; + uint32_t SizeOfBlock; + // uint16_t TypeOffset[1]; +}; + + +/// New format import descriptors pointed to by DataDirectory[ IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ] /// +struct image_bound_import_descriptor +{ + uint32_t TimeDateStamp; + uint16_t OffsetModuleName; + uint16_t NumberOfModuleForwarderRefs; + // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows +}; + +struct image_bound_forwarder_ref +{ + uint32_t TimeDateStamp; + uint16_t OffsetModuleName; + uint16_t Reserved; +}; + + +/// Exception directory /// +struct image_runtime_function_entry +{ + uint32_t BeginAddress; + uint32_t EndAddress; + uint32_t UnwindInfoAddress; +}; + +enum unwind_op_codes +{ + uwop_push_nonvol = 0, /* info == register number */ + uwop_alloc_large, /* no info, alloc size in next 2 slots */ + uwop_alloc_small, /* info == size of allocation / 8 - 1 */ + uwop_set_fpreg, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */ + uwop_save_nonvol, /* info == register number, offset in next slot */ + uwop_save_nonvol_far, /* info == register number, offset in next 2 slots */ + uwop_save_xmm128, /* info == XMM reg number, offset in next slot */ + uwop_save_xmm128_far, /* info == XMM reg number, offset in next 2 slots */ + uwop_push_machframe /* info == 0: no error-code, 1: error-code */ +}; + +union unwind_code +{ + struct s + { + uint8_t CodeOffset; + uint8_t UnwindOp : 4; + uint8_t OpInfo : 4; + }; + + uint16_t FrameOffset; +}; + +struct unwind_info +{ + uint8_t Version : 3; + uint8_t Flags : 5; + uint8_t SizeOfProlog; + uint8_t CountOfCodes; + uint8_t FrameRegister : 4; + uint8_t FrameOffset : 4; + unwind_code UnwindCode[1]; + /* unwind_code MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1]; + * union { + * OPTIONAL ULONG ExceptionHandler; + * OPTIONAL ULONG FunctionEntry; + * }; + * OPTIONAL ULONG ExceptionData[]; */ +}; + + + +/// Debug /// +struct image_debug_misc +{ + uint32_t DataType; // type of misc data, see defines + uint32_t Length; // total length of record, rounded to four + // byte multiple. + uint8_t Unicode; // TRUE if data is unicode string + uint8_t Reserved[3]; + uint8_t Data[1]; // Actual data +}; + +struct image_coff_symbols_header +{ + uint32_t NumberOfSymbols; + uint32_t LvaToFirstSymbol; + uint32_t NumberOfLinenumbers; + uint32_t LvaToFirstLinenumber; + uint32_t RvaToFirstByteOfCode; + uint32_t RvaToLastByteOfCode; + uint32_t RvaToFirstByteOfData; + uint32_t RvaToLastByteOfData; +}; + +struct image_debug_directory +{ + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t Type; + uint32_t SizeOfData; + uint32_t AddressOfRawData; + uint32_t PointerToRawData; +}; + + +#pragma pack(push, 2) +struct image_symbol +{ + union + { + uint8_t ShortName[8]; + struct + { + uint32_t Short; // if 0, use LongName + uint32_t Long; // offset into string table + } Name; + uint32_t LongName[2]; // PBYTE [2] + } N; + uint32_t Value; + int16_t SectionNumber; + uint16_t Type; + uint8_t StorageClass; + uint8_t NumberOfAuxSymbols; +}; +#pragma pack(pop) + +//CodeView Debug OMF signature. The signature at the end of the file is +//a negative offset from the end of the file to another signature. At +//the negative offset (base address) is another signature whose filepos +//field points to the first OMFDirHeader in a chain of directories. +//The NB05 signature is used by the link utility to indicated a completely +//unpacked file. The NB06 signature is used by ilink to indicate that the +//executable has had CodeView information from an incremental link appended +//to the executable. The NB08 signature is used by cvpack to indicate that +//the CodeView Debug OMF has been packed. CodeView will only process +//executables with the NB08 signature. +struct OMFSignature +{ + char Signature[4]; // "NBxx" + uint32_t filepos; // offset in file +}; + +struct CV_INFO_PDB20 +{ + OMFSignature CvHeader; + uint32_t Signature; + uint32_t Age; + uint8_t PdbFileName[1]; +}; + +struct CV_INFO_PDB70 +{ + uint32_t CvSignature; + guid Signature; + uint32_t Age; + uint8_t PdbFileName[1]; +}; + +// directory information structure +// This structure contains the information describing the directory. +// It is pointed to by the signature at the base address or the directory +// link field of a preceeding directory. The directory entries immediately +// follow this structure. +struct OMFDirHeader +{ + uint16_t cbDirHeader; // length of this structure + uint16_t cbDirEntry; // number of bytes in each directory entry + uint32_t cDir; // number of directorie entries + int32_t lfoNextDir; // offset from base of next directory + uint32_t flags; // status flags +}; + +// directory structure +// The data in this structure is used to reference the data for each +// subsection of the CodeView Debug OMF information. Tables that are +// not associated with a specific module will have a module index of +// oxffff. These tables are the global types table, the global symbol +// table, the global public table and the library table. +struct OMFDirEntry +{ + uint16_t SubSection; // subsection type (sst...) + uint16_t iMod; // module index + int32_t lfo; // large file offset of subsection + uint32_t cb; // number of bytes in subsection +}; + + +/// CLR 2.0 header structure /// +struct image_cor20_header +{ + //Header versioning + uint32_t cb; + uint16_t MajorRuntimeVersion; + uint16_t MinorRuntimeVersion; + + // Symbol table and startup information + image_data_directory MetaData; + uint32_t Flags; + + // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is not set, EntryPointToken represents a managed entrypoint. + // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is set, EntryPointRVA represents an RVA to a native entrypoint. + union + { + uint32_t EntryPointToken; + uint32_t EntryPointRVA; + }; + + // Binding information + image_data_directory Resources; + image_data_directory StrongNameSignature; + + // Regular fixup and binding information + image_data_directory CodeManagerTable; + image_data_directory VTableFixups; + image_data_directory ExportAddressTableJumps; + + // Precompiled image info (internal use only - set to zero) + image_data_directory ManagedNativeHeader; +}; + +enum replaces_cor_hdr_numeric_defines +{ + // COM+ Header entry point flags. + comimage_flags_ilonly =0x00000001, + comimage_flags_32bitrequired =0x00000002, + comimage_flags_il_library =0x00000004, + comimage_flags_strongnamesigned =0x00000008, + comimage_flags_native_entrypoint =0x00000010, + comimage_flags_trackdebugdata =0x00010000, + + // Version flags for image. + cor_version_major_v2 =2, + cor_version_major =cor_version_major_v2, + cor_version_minor =0, + cor_deleted_name_length =8, + cor_vtablegap_name_length =8, + + // Maximum size of a NativeType descriptor. + native_type_max_cb =1, + cor_ilmethod_sect_small_max_datasize=0xff, + + // #defines for the MIH FLAGS + image_cor_mih_methodrva =0x01, + image_cor_mih_ehrva =0x02, + image_cor_mih_basicblock =0x08, + + // V-table constants + cor_vtable_32bit =0x01, // V-table slots are 32-bits in size. + cor_vtable_64bit =0x02, // V-table slots are 64-bits in size. + cor_vtable_from_unmanaged =0x04, // If set, transition from unmanaged. + cor_vtable_from_unmanaged_retain_appdomain =0x08, // If set, transition from unmanaged with keeping the current appdomain. + cor_vtable_call_most_derived =0x10, // Call most derived method described by + + // EATJ constants + image_cor_eatj_thunk_size =32, // Size of a jump thunk reserved range. + + // Max name lengths + //@todo: Change to unlimited name lengths. + max_class_name =1024, + max_package_name =1024 +}; + +/// Load Configuration Directory Entry /// +struct image_load_config_directory32 +{ + uint32_t Size; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t GlobalFlagsClear; + uint32_t GlobalFlagsSet; + uint32_t CriticalSectionDefaultTimeout; + uint32_t DeCommitFreeBlockThreshold; + uint32_t DeCommitTotalFreeThreshold; + uint32_t LockPrefixTable; // VA + uint32_t MaximumAllocationSize; + uint32_t VirtualMemoryThreshold; + uint32_t ProcessHeapFlags; + uint32_t ProcessAffinityMask; + uint16_t CSDVersion; + uint16_t Reserved1; + uint32_t EditList; // VA + uint32_t SecurityCookie; // VA + uint32_t SEHandlerTable; // VA + uint32_t SEHandlerCount; +}; + +struct image_load_config_directory64 +{ + uint32_t Size; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t GlobalFlagsClear; + uint32_t GlobalFlagsSet; + uint32_t CriticalSectionDefaultTimeout; + uint64_t DeCommitFreeBlockThreshold; + uint64_t DeCommitTotalFreeThreshold; + uint64_t LockPrefixTable; // VA + uint64_t MaximumAllocationSize; + uint64_t VirtualMemoryThreshold; + uint64_t ProcessAffinityMask; + uint32_t ProcessHeapFlags; + uint16_t CSDVersion; + uint16_t Reserved1; + uint64_t EditList; // VA + uint64_t SecurityCookie; // VA + uint64_t SEHandlerTable; // VA + uint64_t SEHandlerCount; +}; + +#pragma pack(pop) +} //namespace pe_win + +#ifdef PE_BLISS_WINDOWS +typedef wchar_t unicode16_t; +typedef std::basic_string<unicode16_t> u16string; +#else +//Instead of wchar_t for windows +typedef unsigned short unicode16_t; +typedef std::basic_string<unicode16_t> u16string; +#endif + +} //namespace pe_bliss diff --git a/tools/pe_bliss/pe_tls.cpp b/tools/pe_bliss/pe_tls.cpp new file mode 100644 index 0000000000..5ec68e3f10 --- /dev/null +++ b/tools/pe_bliss/pe_tls.cpp @@ -0,0 +1,396 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "pe_tls.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//TLS +//Default constructor +tls_info::tls_info() + :start_rva_(0), end_rva_(0), index_rva_(0), callbacks_rva_(0), + size_of_zero_fill_(0), characteristics_(0) +{} + +//Returns start RVA of TLS raw data +uint32_t tls_info::get_raw_data_start_rva() const +{ + return start_rva_; +} + +//Returns end RVA of TLS raw data +uint32_t tls_info::get_raw_data_end_rva() const +{ + return end_rva_; +} + +//Returns TLS index RVA +uint32_t tls_info::get_index_rva() const +{ + return index_rva_; +} + +//Returns TLS callbacks RVA +uint32_t tls_info::get_callbacks_rva() const +{ + return callbacks_rva_; +} + +//Returns size of zero fill +uint32_t tls_info::get_size_of_zero_fill() const +{ + return size_of_zero_fill_; +} + +//Returns characteristics +uint32_t tls_info::get_characteristics() const +{ + return characteristics_; +} + +//Returns raw TLS data +const std::string& tls_info::get_raw_data() const +{ + return raw_data_; +} + +//Returns TLS callbacks addresses +const tls_info::tls_callback_list& tls_info::get_tls_callbacks() const +{ + return callbacks_; +} + +//Returns TLS callbacks addresses +tls_info::tls_callback_list& tls_info::get_tls_callbacks() +{ + return callbacks_; +} + +//Adds TLS callback +void tls_info::add_tls_callback(uint32_t rva) +{ + callbacks_.push_back(rva); +} + +//Clears TLS callbacks list +void tls_info::clear_tls_callbacks() +{ + callbacks_.clear(); +} + +//Recalculates end address of raw TLS data +void tls_info::recalc_raw_data_end_rva() +{ + end_rva_ = static_cast<uint32_t>(start_rva_ + raw_data_.length()); +} + +//Sets start RVA of TLS raw data +void tls_info::set_raw_data_start_rva(uint32_t rva) +{ + start_rva_ = rva; +} + +//Sets end RVA of TLS raw data +void tls_info::set_raw_data_end_rva(uint32_t rva) +{ + end_rva_ = rva; +} + +//Sets TLS index RVA +void tls_info::set_index_rva(uint32_t rva) +{ + index_rva_ = rva; +} + +//Sets TLS callbacks RVA +void tls_info::set_callbacks_rva(uint32_t rva) +{ + callbacks_rva_ = rva; +} + +//Sets size of zero fill +void tls_info::set_size_of_zero_fill(uint32_t size) +{ + size_of_zero_fill_ = size; +} + +//Sets characteristics +void tls_info::set_characteristics(uint32_t characteristics) +{ + characteristics_ = characteristics; +} + +//Sets raw TLS data +void tls_info::set_raw_data(const std::string& data) +{ + raw_data_ = data; +} + +//If image does not have TLS, throws an exception +const tls_info get_tls_info(const pe_base& pe) +{ + return pe.get_pe_type() == pe_type_32 + ? get_tls_info_base<pe_types_class_32>(pe) + : get_tls_info_base<pe_types_class_64>(pe); +} + +//TLS Rebuilder +const image_directory rebuild_tls(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start, bool write_tls_callbacks, bool write_tls_data, tls_data_expand_type expand, bool save_to_pe_header, bool auto_strip_last_section) +{ + return pe.get_pe_type() == pe_type_32 + ? rebuild_tls_base<pe_types_class_32>(pe, info, tls_section, offset_from_section_start, write_tls_callbacks, write_tls_data, expand, save_to_pe_header, auto_strip_last_section) + : rebuild_tls_base<pe_types_class_64>(pe, info, tls_section, offset_from_section_start, write_tls_callbacks, write_tls_data, expand, save_to_pe_header, auto_strip_last_section); +} + +//Get TLS info +//If image does not have TLS, throws an exception +template<typename PEClassType> +const tls_info get_tls_info_base(const pe_base& pe) +{ + tls_info ret; + + //If there's no TLS directory, throw an exception + if(!pe.has_tls()) + throw pe_exception("Image does not have TLS directory", pe_exception::directory_does_not_exist); + + //Get TLS directory data + typename PEClassType::TLSStruct tls_directory_data = pe.section_data_from_rva<typename PEClassType::TLSStruct>(pe.get_directory_rva(image_directory_entry_tls), section_data_virtual, true); + + //Check data addresses + if(tls_directory_data.EndAddressOfRawData == tls_directory_data.StartAddressOfRawData) + { + try + { + pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.EndAddressOfRawData)); + } + catch(const pe_exception&) + { + //Fix addressess on incorrect conversion + tls_directory_data.EndAddressOfRawData = tls_directory_data.StartAddressOfRawData = 0; + } + } + + if(tls_directory_data.StartAddressOfRawData && + pe.section_data_length_from_va(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), + static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), section_data_virtual, true) + < (tls_directory_data.EndAddressOfRawData - tls_directory_data.StartAddressOfRawData)) + throw pe_exception("Incorrect TLS directory", pe_exception::incorrect_tls_directory); + + //Fill TLS info + //VAs are not checked + ret.set_raw_data_start_rva(tls_directory_data.StartAddressOfRawData ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData)) : 0); + ret.set_raw_data_end_rva(tls_directory_data.EndAddressOfRawData ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.EndAddressOfRawData)) : 0); + ret.set_index_rva(tls_directory_data.AddressOfIndex ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfIndex)) : 0); + ret.set_callbacks_rva(tls_directory_data.AddressOfCallBacks ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfCallBacks)) : 0); + ret.set_size_of_zero_fill(tls_directory_data.SizeOfZeroFill); + ret.set_characteristics(tls_directory_data.Characteristics); + + if(tls_directory_data.StartAddressOfRawData && tls_directory_data.StartAddressOfRawData != tls_directory_data.EndAddressOfRawData) + { + //Read and save TLS RAW data + ret.set_raw_data(std::string( + pe.section_data_from_va(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), section_data_virtual, true), + static_cast<uint32_t>(tls_directory_data.EndAddressOfRawData - tls_directory_data.StartAddressOfRawData))); + } + + //If file has TLS callbacks + if(ret.get_callbacks_rva()) + { + //Read callbacks VAs + uint32_t current_tls_callback = 0; + + while(true) + { + //Read TLS callback VA + typename PEClassType::BaseSize va = pe.section_data_from_va<typename PEClassType::BaseSize>(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfCallBacks + current_tls_callback), section_data_virtual, true); + if(va == 0) + break; + + //Save it + ret.add_tls_callback(pe.va_to_rva(va, false)); + + //Move to next callback VA + current_tls_callback += sizeof(va); + } + } + + return ret; +} + +//Rebuilder of TLS structures +//If write_tls_callbacks = true, TLS callbacks VAs will be written to their place +//If write_tls_data = true, TLS data will be written to its place +//If you have chosen to rewrite raw data, only (EndAddressOfRawData - StartAddressOfRawData) bytes will be written, not the full length of string +//representing raw data content +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +//Note/TODO: TLS Callbacks array is not DWORD-aligned (seems to work on WinXP - Win7) +template<typename PEClassType> +const image_directory rebuild_tls_base(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start, bool write_tls_callbacks, bool write_tls_data, tls_data_expand_type expand, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that tls_section is attached to this PE image + if(!pe.section_attached(tls_section)) + throw pe_exception("TLS section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t tls_data_pos = pe_utils::align_up(offset_from_section_start, sizeof(typename PEClassType::BaseSize)); + uint32_t needed_size = sizeof(typename PEClassType::TLSStruct); //Calculate needed size for TLS table + + //Check if tls_section is last one. If it's not, check if there's enough place for TLS data + if(&tls_section != &*(pe.get_image_sections().end() - 1) && + (tls_section.empty() || pe_utils::align_up(tls_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + tls_data_pos)) + throw pe_exception("Insufficient space for TLS directory", pe_exception::insufficient_space); + + //Check raw data positions + if(info.get_raw_data_end_rva() < info.get_raw_data_start_rva() || info.get_index_rva() == 0) + throw pe_exception("Incorrect TLS directory", pe_exception::incorrect_tls_directory); + + std::string& raw_data = tls_section.get_raw_data(); + + //This will be done only if tls_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + tls_data_pos) + raw_data.resize(needed_size + tls_data_pos); //Expand section raw data + + //Create and fill TLS structure + typename PEClassType::TLSStruct tls_struct = {0}; + + typename PEClassType::BaseSize va; + if(info.get_raw_data_start_rva()) + { + pe.rva_to_va(info.get_raw_data_start_rva(), va); + tls_struct.StartAddressOfRawData = va; + tls_struct.SizeOfZeroFill = info.get_size_of_zero_fill(); + } + + if(info.get_raw_data_end_rva()) + { + pe.rva_to_va(info.get_raw_data_end_rva(), va); + tls_struct.EndAddressOfRawData = va; + } + + pe.rva_to_va(info.get_index_rva(), va); + tls_struct.AddressOfIndex = va; + + if(info.get_callbacks_rva()) + { + pe.rva_to_va(info.get_callbacks_rva(), va); + tls_struct.AddressOfCallBacks = va; + } + + tls_struct.Characteristics = info.get_characteristics(); + + //Save TLS structure + memcpy(&raw_data[tls_data_pos], &tls_struct, sizeof(tls_struct)); + + //If we are asked to rewrite TLS raw data + if(write_tls_data && info.get_raw_data_start_rva() && info.get_raw_data_start_rva() != info.get_raw_data_end_rva()) + { + try + { + //Check if we're going to write TLS raw data to an existing section (not to PE headers) + section& raw_data_section = pe.section_from_rva(info.get_raw_data_start_rva()); + pe.expand_section(raw_data_section, info.get_raw_data_start_rva(), info.get_raw_data_end_rva() - info.get_raw_data_start_rva(), expand == tls_data_expand_raw ? pe_base::expand_section_raw : pe_base::expand_section_virtual); + } + catch(const pe_exception&) + { + //If no section is presented by StartAddressOfRawData, just go to next step + } + + unsigned long write_raw_data_size = info.get_raw_data_end_rva() - info.get_raw_data_start_rva(); + unsigned long available_raw_length = 0; + + //Check if there's enough RAW space to write raw TLS data... + if((available_raw_length = pe.section_data_length_from_rva(info.get_raw_data_start_rva(), info.get_raw_data_start_rva(), section_data_raw, true)) + < info.get_raw_data_end_rva() - info.get_raw_data_start_rva()) + { + //Check if there's enough virtual space for it... + if(pe.section_data_length_from_rva(info.get_raw_data_start_rva(), info.get_raw_data_start_rva(), section_data_virtual, true) + < info.get_raw_data_end_rva() - info.get_raw_data_start_rva()) + throw pe_exception("Insufficient space for TLS raw data", pe_exception::insufficient_space); + else + write_raw_data_size = available_raw_length; //We'll write just a part of full raw data + } + + //Write raw TLS data, if any + if(write_raw_data_size != 0) + memcpy(pe.section_data_from_rva(info.get_raw_data_start_rva(), true), info.get_raw_data().data(), write_raw_data_size); + } + + //If we are asked to rewrite TLS callbacks addresses + if(write_tls_callbacks && info.get_callbacks_rva()) + { + unsigned long needed_callback_size = static_cast<unsigned long>((info.get_tls_callbacks().size() + 1 /* last null element */) * sizeof(typename PEClassType::BaseSize)); + + try + { + //Check if we're going to write TLS callbacks VAs to an existing section (not to PE headers) + section& raw_data_section = pe.section_from_rva(info.get_callbacks_rva()); + pe.expand_section(raw_data_section, info.get_callbacks_rva(), needed_callback_size, pe_base::expand_section_raw); + } + catch(const pe_exception&) + { + //If no section is presented by RVA of callbacks, just go to next step + } + + //Check if there's enough space to write callbacks TLS data... + if(pe.section_data_length_from_rva(info.get_callbacks_rva(), info.get_callbacks_rva(), section_data_raw, true) + < needed_callback_size - sizeof(typename PEClassType::BaseSize) /* last zero element can be virtual only */) + throw pe_exception("Insufficient space for TLS callbacks data", pe_exception::insufficient_space); + + if(pe.section_data_length_from_rva(info.get_callbacks_rva(), info.get_callbacks_rva(), section_data_virtual, true) + < needed_callback_size /* check here full virtual data length available */) + throw pe_exception("Insufficient space for TLS callbacks data", pe_exception::insufficient_space); + + std::vector<typename PEClassType::BaseSize> callbacks_virtual_addresses; + callbacks_virtual_addresses.reserve(info.get_tls_callbacks().size() + 1 /* last null element */); + + //Convert TLS RVAs to VAs + for(tls_info::tls_callback_list::const_iterator it = info.get_tls_callbacks().begin(); it != info.get_tls_callbacks().end(); ++it) + { + typename PEClassType::BaseSize cb_va = 0; + pe.rva_to_va(*it, cb_va); + callbacks_virtual_addresses.push_back(cb_va); + } + + //Ending null element + callbacks_virtual_addresses.push_back(0); + + //Write callbacks TLS data + memcpy(pe.section_data_from_rva(info.get_callbacks_rva(), true), &callbacks_virtual_addresses[0], needed_callback_size); + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(tls_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(tls_section, tls_data_pos), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_tls, ret.get_rva()); + pe.set_directory_size(image_directory_entry_tls, ret.get_size()); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_tls.h b/tools/pe_bliss/pe_tls.h new file mode 100644 index 0000000000..316e208147 --- /dev/null +++ b/tools/pe_bliss/pe_tls.h @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <memory> +#include <istream> +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing TLS info +//We use "DWORD" type to represent RVAs, because RVA is +//always 32bit even in PE+ +class tls_info +{ +public: + typedef std::vector<uint32_t> tls_callback_list; + +public: + //Default constructor + tls_info(); + + //Returns start RVA of TLS raw data + uint32_t get_raw_data_start_rva() const; + //Returns end RVA of TLS raw data + uint32_t get_raw_data_end_rva() const; + //Returns TLS index RVA + uint32_t get_index_rva() const; + //Returns TLS callbacks RVA + uint32_t get_callbacks_rva() const; + //Returns size of zero fill + uint32_t get_size_of_zero_fill() const; + //Returns characteristics + uint32_t get_characteristics() const; + //Returns raw TLS data + const std::string& get_raw_data() const; + //Returns TLS callbacks addresses + const tls_callback_list& get_tls_callbacks() const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild TLS directory + + //Sets start RVA of TLS raw data + void set_raw_data_start_rva(uint32_t rva); + //Sets end RVA of TLS raw data + void set_raw_data_end_rva(uint32_t rva); + //Sets TLS index RVA + void set_index_rva(uint32_t rva); + //Sets TLS callbacks RVA + void set_callbacks_rva(uint32_t rva); + //Sets size of zero fill + void set_size_of_zero_fill(uint32_t size); + //Sets characteristics + void set_characteristics(uint32_t characteristics); + //Sets raw TLS data + void set_raw_data(const std::string& data); + //Returns TLS callbacks addresses + tls_callback_list& get_tls_callbacks(); + //Adds TLS callback + void add_tls_callback(uint32_t rva); + //Clears TLS callbacks list + void clear_tls_callbacks(); + //Recalculates end address of raw TLS data + void recalc_raw_data_end_rva(); + +private: + uint32_t start_rva_, end_rva_, index_rva_, callbacks_rva_; + uint32_t size_of_zero_fill_, characteristics_; + + //Raw TLS data + std::string raw_data_; + + //TLS callback RVAs + tls_callback_list callbacks_; +}; + +//Represents type of expanding of TLS section containing raw data +//(Works only if you are writing TLS raw data to tls_section and it is the last one in the PE image on the moment of TLS rebuild) +enum tls_data_expand_type +{ + tls_data_expand_raw, //If there is not enough raw space for raw TLS data, it can be expanded + tls_data_expand_virtual //If there is not enough virtual place for raw TLS data, it can be expanded +}; + + +//Get TLS info +//If image does not have TLS, throws an exception +const tls_info get_tls_info(const pe_base& pe); + +template<typename PEClassType> +const tls_info get_tls_info_base(const pe_base& pe); + +//Rebuilder of TLS structures +//If write_tls_callbacks = true, TLS callbacks VAs will be written to their place +//If write_tls_data = true, TLS data will be written to its place +//If you have chosen to rewrite raw data, only (EndAddressOfRawData - StartAddressOfRawData) bytes will be written, not the full length of string +//representing raw data content +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +const image_directory rebuild_tls(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start = 0, bool write_tls_callbacks = true, bool write_tls_data = true, tls_data_expand_type expand = tls_data_expand_raw, bool save_to_pe_header = true, bool auto_strip_last_section = true); + +template<typename PEClassType> +const image_directory rebuild_tls_base(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start = 0, bool write_tls_callbacks = true, bool write_tls_data = true, tls_data_expand_type expand = tls_data_expand_raw, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/resource_bitmap_reader.cpp b/tools/pe_bliss/resource_bitmap_reader.cpp new file mode 100644 index 0000000000..3546461f53 --- /dev/null +++ b/tools/pe_bliss/resource_bitmap_reader.cpp @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <cmath> +#include "resource_bitmap_reader.h" +#include "pe_resource_viewer.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_bitmap_reader::resource_bitmap_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Returns bitmap data by name and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_name(const std::wstring& name, uint32_t index) const +{ + return create_bitmap(res_.get_resource_data_by_name(pe_resource_viewer::resource_bitmap, name, index).get_data()); +} + +//Returns bitmap data by name and language (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_name(uint32_t language, const std::wstring& name) const +{ + return create_bitmap(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_bitmap, name).get_data()); +} + +//Returns bitmap data by ID and language (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_id_lang(uint32_t language, uint32_t id) const +{ + return create_bitmap(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_bitmap, id).get_data()); +} + +//Returns bitmap data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_id(uint32_t id, uint32_t index) const +{ + return create_bitmap(res_.get_resource_data_by_id(pe_resource_viewer::resource_bitmap, id, index).get_data()); +} + +//Helper function of creating bitmap header +const std::string resource_bitmap_reader::create_bitmap(const std::string& resource_data) +{ + //Create bitmap file header + bitmapfileheader header = {0}; + header.bfType = 0x4d42; //Signature "BM" + header.bfOffBits = sizeof(bitmapfileheader) + sizeof(bitmapinfoheader); //Offset to bitmap bits + header.bfSize = static_cast<uint32_t>(sizeof(bitmapfileheader) + resource_data.length()); //Size of bitmap + + //Check size of resource data + if(resource_data.length() < sizeof(bitmapinfoheader)) + throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); + + { + //Get bitmap info header + const bitmapinfoheader* info = reinterpret_cast<const bitmapinfoheader*>(resource_data.data()); + + //If color table is present, skip it + if(info->biClrUsed != 0) + header.bfOffBits += 4 * info->biClrUsed; //Add this size to offset to bitmap bits + else if(info->biBitCount <= 8) + header.bfOffBits += 4 * static_cast<uint32_t>(std::pow(2.f, info->biBitCount)); //Add this size to offset to bitmap bits + } + + //Return final bitmap data + return std::string(reinterpret_cast<const char*>(&header), sizeof(bitmapfileheader)) + resource_data; +} +} diff --git a/tools/pe_bliss/resource_bitmap_reader.h b/tools/pe_bliss/resource_bitmap_reader.h new file mode 100644 index 0000000000..f2b92bbde7 --- /dev/null +++ b/tools/pe_bliss/resource_bitmap_reader.h @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +class resource_bitmap_reader +{ +public: + resource_bitmap_reader(const pe_resource_viewer& res); + + //Returns bitmap data by name and language (minimum checks of format correctness) + const std::string get_bitmap_by_name(uint32_t language, const std::wstring& name) const; + //Returns bitmap data by name and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_bitmap_by_name(const std::wstring& name, uint32_t index = 0) const; + //Returns bitmap data by ID and language (minimum checks of format correctness) + const std::string get_bitmap_by_id_lang(uint32_t language, uint32_t id) const; + //Returns bitmap data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_bitmap_by_id(uint32_t id, uint32_t index = 0) const; + +private: + //Helper function of creating bitmap header + static const std::string create_bitmap(const std::string& resource_data); + + const pe_resource_viewer& res_; +}; +} diff --git a/tools/pe_bliss/resource_bitmap_writer.cpp b/tools/pe_bliss/resource_bitmap_writer.cpp new file mode 100644 index 0000000000..3445a08445 --- /dev/null +++ b/tools/pe_bliss/resource_bitmap_writer.cpp @@ -0,0 +1,75 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "resource_bitmap_writer.h" +#include "pe_resource_manager.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_bitmap_writer::resource_bitmap_writer(pe_resource_manager& res) + :res_(res) +{} + +//Adds bitmap from bitmap file data. If bitmap already exists, replaces it +//timestamp will be used for directories that will be added +void resource_bitmap_writer::add_bitmap(const std::string& bitmap_file, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + //Check bitmap data a little + if(bitmap_file.length() < sizeof(bitmapfileheader)) + throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); + + resource_directory_entry new_entry; + new_entry.set_id(id); + + //Add bitmap + res_.add_resource(bitmap_file.substr(sizeof(bitmapfileheader)), pe_resource_viewer::resource_bitmap, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp); +} + +//Adds bitmap from bitmap file data. If bitmap already exists, replaces it +//timestamp will be used for directories that will be added +void resource_bitmap_writer::add_bitmap(const std::string& bitmap_file, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + //Check bitmap data a little + if(bitmap_file.length() < sizeof(bitmapfileheader)) + throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); + + resource_directory_entry new_entry; + new_entry.set_name(name); + + //Add bitmap + res_.add_resource(bitmap_file.substr(sizeof(bitmapfileheader)), pe_resource_viewer::resource_bitmap, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp); +} + +//Removes bitmap by name/ID and language +bool resource_bitmap_writer::remove_bitmap(const std::wstring& name, uint32_t language) +{ + return res_.remove_resource(pe_resource_viewer::resource_bitmap, name, language); +} + +//Removes bitmap by name/ID and language +bool resource_bitmap_writer::remove_bitmap(uint32_t id, uint32_t language) +{ + return res_.remove_resource(pe_resource_viewer::resource_bitmap, id, language); +} +} diff --git a/tools/pe_bliss/resource_bitmap_writer.h b/tools/pe_bliss/resource_bitmap_writer.h new file mode 100644 index 0000000000..4b8ea72705 --- /dev/null +++ b/tools/pe_bliss/resource_bitmap_writer.h @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_manager; + +class resource_bitmap_writer +{ +public: + resource_bitmap_writer(pe_resource_manager& res); + + //Adds bitmap from bitmap file data. If bitmap already exists, replaces it + //timestamp will be used for directories that will be added + void add_bitmap(const std::string& bitmap_file, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_bitmap(const std::string& bitmap_file, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + + //Removes bitmap by name/ID and language + bool remove_bitmap(const std::wstring& name, uint32_t language); + bool remove_bitmap(uint32_t id, uint32_t language); + +private: + pe_resource_manager& res_; +}; +} diff --git a/tools/pe_bliss/resource_cursor_icon_reader.cpp b/tools/pe_bliss/resource_cursor_icon_reader.cpp new file mode 100644 index 0000000000..28a259163e --- /dev/null +++ b/tools/pe_bliss/resource_cursor_icon_reader.cpp @@ -0,0 +1,521 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include "resource_cursor_icon_reader.h" +#include "pe_structures.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_cursor_icon_reader::resource_cursor_icon_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Helper function of creating icon headers from ICON_GROUP resource data +//Returns icon count +uint16_t resource_cursor_icon_reader::format_icon_headers(std::string& ico_data, const std::string& resource_data) +{ + //Check resource data size + if(resource_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + const ico_header* info = reinterpret_cast<const ico_header*>(resource_data.data()); + + //Check resource data size + if(resource_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Reserve memory to speed up a little + ico_data.reserve(sizeof(ico_header) + info->Count * sizeof(icondirentry)); + ico_data.append(reinterpret_cast<const char*>(info), sizeof(ico_header)); + + //Iterate over all listed icons + uint32_t offset = sizeof(ico_header) + sizeof(icondirentry) * info->Count; + for(uint16_t i = 0; i != info->Count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(resource_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + + //Fill icon data + icondirentry direntry; + direntry.BitCount = group->BitCount; + direntry.ColorCount = group->ColorCount; + direntry.Height = group->Height; + direntry.Planes = group->Planes; + direntry.Reserved = group->Reserved; + direntry.SizeInBytes = group->SizeInBytes; + direntry.Width = group->Width; + direntry.ImageOffset = offset; + + //Add icon header to returned value + ico_data.append(reinterpret_cast<const char*>(&direntry), sizeof(icondirentry)); + + offset += group->SizeInBytes; + } + + //Return icon count + return info->Count; +} + +//Returns single icon data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_icon_by_id_lang(uint32_t language, uint32_t id) const +{ + //Get icon headers + std::string icon_data(lookup_icon_group_data_by_icon(id, language)); + //Append icon data + icon_data.append(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, id).get_data()); + return icon_data; +} + +//Returns single icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_icon_by_id(uint32_t id, uint32_t index) const +{ + pe_resource_viewer::resource_language_list languages(res_.list_resource_languages(pe_resource_viewer::resource_icon, id)); + if(languages.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + //Get icon headers + std::string icon_data(lookup_icon_group_data_by_icon(id, languages.at(index))); + //Append icon data + icon_data.append(res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, id, index).get_data()); + return icon_data; +} + +//Returns icon data by name and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_name(const std::wstring& name, uint32_t index) const +{ + std::string ret; + + //Get resource by name and index + const std::string data = res_.get_resource_data_by_name(pe_resource_viewer::resource_icon_group, name, index).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, group->Number, index).get_data(); + } + + return ret; +} + +//Returns icon data by name and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_name(uint32_t language, const std::wstring& name) const +{ + std::string ret; + + //Get resource by name and language + const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, name).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, group->Number).get_data(); + } + + return ret; +} + +//Returns icon data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_id_lang(uint32_t language, uint32_t id) const +{ + std::string ret; + + //Get resource by language and id + const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, id).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, group->Number).get_data(); + } + + return ret; +} + +//Returns icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_id(uint32_t id, uint32_t index) const +{ + std::string ret; + + //Get resource by id and index + const std::string data = res_.get_resource_data_by_id(pe_resource_viewer::resource_icon_group, id, index).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, group->Number, index).get_data(); + } + + return ret; +} + +//Checks for icon presence inside icon group, fills icon headers if found +bool resource_cursor_icon_reader::check_icon_presence(const std::string& icon_group_resource_data, uint32_t icon_id, std::string& ico_data) +{ + //Check resource data size + if(icon_group_resource_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + const ico_header* info = reinterpret_cast<const ico_header*>(icon_group_resource_data.data()); + + //Check resource data size + if(icon_group_resource_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + for(uint16_t i = 0; i != info->Count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(icon_group_resource_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + if(group->Number == icon_id) + { + //Reserve memory to speed up a little + ico_data.reserve(sizeof(ico_header) + sizeof(icondirentry)); + //Write single-icon icon header + ico_header new_header = *info; + new_header.Count = 1; + ico_data.append(reinterpret_cast<const char*>(&new_header), sizeof(ico_header)); + + //Fill icon data + icondirentry direntry; + direntry.BitCount = group->BitCount; + direntry.ColorCount = group->ColorCount; + direntry.Height = group->Height; + direntry.Planes = group->Planes; + direntry.Reserved = group->Reserved; + direntry.SizeInBytes = group->SizeInBytes; + direntry.Width = group->Width; + direntry.ImageOffset = sizeof(ico_header) + sizeof(icondirentry); + ico_data.append(reinterpret_cast<const char*>(&direntry), sizeof(direntry)); + + return true; + } + } + + return false; +} + +//Looks up icon group by icon id and returns full icon headers if found +const std::string resource_cursor_icon_reader::lookup_icon_group_data_by_icon(uint32_t icon_id, uint32_t language) const +{ + std::string icon_header_data; + + { + //List all ID-resources + pe_resource_viewer::resource_id_list ids(res_.list_resource_ids(pe_resource_viewer::resource_icon_group)); + + for(pe_resource_viewer::resource_id_list::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_icon_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_icon_presence(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, *it).get_data(), icon_id, icon_header_data)) + return icon_header_data; + } + } + + { + //List all named resources + pe_resource_viewer::resource_name_list names(res_.list_resource_names(pe_resource_viewer::resource_icon_group)); + for(pe_resource_viewer::resource_name_list::const_iterator it = names.begin(); it != names.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_icon_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_icon_presence(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, *it).get_data(), icon_id, icon_header_data)) + return icon_header_data; + } + } + + throw pe_exception("No icon group find for requested icon", pe_exception::no_icon_group_found); +} + +//Returns single cursor data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_cursor_by_id_lang(uint32_t language, uint32_t id) const +{ + std::string raw_cursor_data(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, id).get_data()); + //Get cursor headers + std::string cursor_data(lookup_cursor_group_data_by_cursor(id, language, raw_cursor_data)); + //Append cursor data + cursor_data.append(raw_cursor_data.substr(sizeof(uint16_t) * 2 /* hotspot position */)); + return cursor_data; +} + +//Returns single cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_cursor_by_id(uint32_t id, uint32_t index) const +{ + pe_resource_viewer::resource_language_list languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor, id)); + if(languages.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + std::string raw_cursor_data(res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, id, index).get_data()); + //Get cursor headers + std::string cursor_data(lookup_cursor_group_data_by_cursor(id, languages.at(index), raw_cursor_data)); + //Append cursor data + cursor_data.append(raw_cursor_data.substr(sizeof(uint16_t) * 2 /* hotspot position */)); + return cursor_data; +} + +//Helper function of creating cursor headers +//Returns cursor count +uint16_t resource_cursor_icon_reader::format_cursor_headers(std::string& cur_data, const std::string& resource_data, uint32_t language, uint32_t index) const +{ + //Check resource data length + if(resource_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + const cursor_header* info = reinterpret_cast<const cursor_header*>(resource_data.data()); + + //Check resource data length + if(resource_data.length() < sizeof(cursor_header) + sizeof(cursor_group) * info->Count) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Reserve needed space to speed up a little + cur_data.reserve(sizeof(cursor_header) + info->Count * sizeof(cursordirentry)); + //Add cursor header + cur_data.append(reinterpret_cast<const char*>(info), sizeof(cursor_header)); + + //Iterate over all cursors listed in cursor group + uint32_t offset = sizeof(cursor_header) + sizeof(cursordirentry) * info->Count; + for(uint16_t i = 0; i != info->Count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + + //Fill cursor info + cursordirentry direntry; + direntry.ColorCount = 0; //OK + direntry.Width = static_cast<uint8_t>(group->Width); + direntry.Height = static_cast<uint8_t>(group->Height) / 2; + direntry.Reserved = 0; + + //Now read hotspot data from cursor data directory + const std::string cursor = index == 0xFFFFFFFF + ? res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data() + : res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data(); + if(cursor.length() < 2 * sizeof(uint16_t)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Here it is - two words in the very beginning of cursor data + direntry.HotspotX = *reinterpret_cast<const uint16_t*>(cursor.data()); + direntry.HotspotY = *reinterpret_cast<const uint16_t*>(cursor.data() + sizeof(uint16_t)); + + //Fill the rest data + direntry.SizeInBytes = group->SizeInBytes - 2 * sizeof(uint16_t); + direntry.ImageOffset = offset; + + //Add cursor header + cur_data.append(reinterpret_cast<const char*>(&direntry), sizeof(cursordirentry)); + + offset += direntry.SizeInBytes; + } + + //Return cursor count + return info->Count; +} + +//Returns cursor data by name and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_name(uint32_t language, const std::wstring& name) const +{ + std::string ret; + + //Get resource by name and language + const std::string resource_data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, name).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, language); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Returns cursor data by name and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_name(const std::wstring& name, uint32_t index) const +{ + std::string ret; + + //Get resource by name and index + const std::string resource_data = res_.get_resource_data_by_name(pe_resource_viewer::resource_cursor_group, name, index).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, 0, index); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Returns cursor data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_id_lang(uint32_t language, uint32_t id) const +{ + std::string ret; + + //Get resource by ID and language + const std::string resource_data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, id).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, language); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Returns cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_id(uint32_t id, uint32_t index) const +{ + std::string ret; + + //Get resource by ID and index + const std::string resource_data = res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor_group, id, index).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, 0, index); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Checks for cursor presence inside cursor group, fills cursor headers if found +bool resource_cursor_icon_reader::check_cursor_presence(const std::string& cursor_group_resource_data, uint32_t cursor_id, std::string& cur_header_data, const std::string& raw_cursor_data) +{ + //Check resource data length + if(cursor_group_resource_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + const cursor_header* info = reinterpret_cast<const cursor_header*>(cursor_group_resource_data.data()); + + //Check resource data length + if(cursor_group_resource_data.length() < sizeof(cursor_header) + sizeof(cursor_group)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Iterate over all cursors listed in cursor group + for(uint16_t i = 0; i != info->Count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(cursor_group_resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + + if(group->Number == cursor_id) + { + //Reserve needed space to speed up a little + cur_header_data.reserve(sizeof(cursor_header) + sizeof(cursordirentry)); + //Write single-cursor cursor header + cursor_header new_header = *info; + new_header.Count = 1; + cur_header_data.append(reinterpret_cast<const char*>(&new_header), sizeof(cursor_header)); + + //Fill cursor info + cursordirentry direntry; + direntry.ColorCount = 0; //OK + direntry.Width = static_cast<uint8_t>(group->Width); + direntry.Height = static_cast<uint8_t>(group->Height) / 2; + direntry.Reserved = 0; + + if(raw_cursor_data.length() < 2 * sizeof(uint16_t)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Here it is - two words in the very beginning of cursor data + direntry.HotspotX = *reinterpret_cast<const uint16_t*>(raw_cursor_data.data()); + direntry.HotspotY = *reinterpret_cast<const uint16_t*>(raw_cursor_data.data() + sizeof(uint16_t)); + + //Fill the rest data + direntry.SizeInBytes = group->SizeInBytes - 2 * sizeof(uint16_t); + direntry.ImageOffset = sizeof(cursor_header) + sizeof(cursordirentry); + + //Add cursor header + cur_header_data.append(reinterpret_cast<const char*>(&direntry), sizeof(cursordirentry)); + + return true; + } + } + + return false; +} + +//Looks up cursor group by cursor id and returns full cursor headers if found +const std::string resource_cursor_icon_reader::lookup_cursor_group_data_by_cursor(uint32_t cursor_id, uint32_t language, const std::string& raw_cursor_data) const +{ + std::string cursor_header_data; + + { + //List all ID-resources + pe_resource_viewer::resource_id_list ids(res_.list_resource_ids(pe_resource_viewer::resource_cursor_group)); + + for(pe_resource_viewer::resource_id_list::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_cursor_presence(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, *it).get_data(), cursor_id, cursor_header_data, raw_cursor_data)) + return cursor_header_data; + } + } + + { + //List all named resources + pe_resource_viewer::resource_name_list names(res_.list_resource_names(pe_resource_viewer::resource_cursor_group)); + for(pe_resource_viewer::resource_name_list::const_iterator it = names.begin(); it != names.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_cursor_presence(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, *it).get_data(), cursor_id, cursor_header_data, raw_cursor_data)) + return cursor_header_data; + } + } + + throw pe_exception("No cursor group find for requested icon", pe_exception::no_cursor_group_found); +} +} diff --git a/tools/pe_bliss/resource_cursor_icon_reader.h b/tools/pe_bliss/resource_cursor_icon_reader.h new file mode 100644 index 0000000000..e34fff419b --- /dev/null +++ b/tools/pe_bliss/resource_cursor_icon_reader.h @@ -0,0 +1,84 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +class resource_cursor_icon_reader +{ +public: + resource_cursor_icon_reader(const pe_resource_viewer& res); + + //Returns single icon data by ID and language (minimum checks of format correctness) + const std::string get_single_icon_by_id_lang(uint32_t language, uint32_t id) const; + //Returns single icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_single_icon_by_id(uint32_t id, uint32_t index = 0) const; + + //Returns icon data of group of icons by name and language (minimum checks of format correctness) + const std::string get_icon_by_name(uint32_t language, const std::wstring& icon_group_name) const; + //Returns icon data of group of icons by name and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_icon_by_name(const std::wstring& icon_group_name, uint32_t index = 0) const; + //Returns icon data of group of icons by ID and language (minimum checks of format correctness) + const std::string get_icon_by_id_lang(uint32_t language, uint32_t icon_group_id) const; + //Returns icon data of group of icons by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_icon_by_id(uint32_t icon_group_id, uint32_t index = 0) const; + + //Returns single cursor data by ID and language (minimum checks of format correctness) + const std::string get_single_cursor_by_id_lang(uint32_t language, uint32_t id) const; + //Returns single cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_single_cursor_by_id(uint32_t id, uint32_t index = 0) const; + + //Returns cursor data by name and language (minimum checks of format correctness) + const std::string get_cursor_by_name(uint32_t language, const std::wstring& cursor_group_name) const; + //Returns cursor data by name and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_cursor_by_name(const std::wstring& cursor_group_name, uint32_t index = 0) const; + //Returns cursor data by ID and language (minimum checks of format correctness) + const std::string get_cursor_by_id_lang(uint32_t language, uint32_t cursor_group_id) const; + //Returns cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_cursor_by_id(uint32_t cursor_group_id, uint32_t index = 0) const; + +private: + const pe_resource_viewer& res_; + + //Helper function of creating icon headers from ICON_GROUP resource data + //Returns icon count + static uint16_t format_icon_headers(std::string& ico_data, const std::string& resource_data); + + //Helper function of creating cursor headers from CURSOR_GROUP resource data + //Returns cursor count + uint16_t format_cursor_headers(std::string& cur_data, const std::string& resource_data, uint32_t language, uint32_t index = 0xFFFFFFFF) const; + + //Looks up icon group by icon id and returns full icon headers if found + const std::string lookup_icon_group_data_by_icon(uint32_t icon_id, uint32_t language) const; + //Checks for icon presence inside icon group, fills icon headers if found + static bool check_icon_presence(const std::string& icon_group_resource_data, uint32_t icon_id, std::string& ico_data); + + //Looks up cursor group by cursor id and returns full cursor headers if found + const std::string lookup_cursor_group_data_by_cursor(uint32_t cursor_id, uint32_t language, const std::string& raw_cursor_data) const; + //Checks for cursor presence inside cursor group, fills cursor headers if found + static bool check_cursor_presence(const std::string& icon_group_resource_data, uint32_t cursor_id, std::string& cur_header_data, const std::string& raw_cursor_data); +}; +} diff --git a/tools/pe_bliss/resource_cursor_icon_writer.cpp b/tools/pe_bliss/resource_cursor_icon_writer.cpp new file mode 100644 index 0000000000..2f1c4363c4 --- /dev/null +++ b/tools/pe_bliss/resource_cursor_icon_writer.cpp @@ -0,0 +1,447 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <algorithm> +#include <string.h> +#include "resource_cursor_icon_writer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_cursor_icon_writer::resource_cursor_icon_writer(pe_resource_manager& res) + :res_(res) +{} + +//Add icon helper +void resource_cursor_icon_writer::add_icon(const std::string& icon_file, const resource_data_info* group_icon_info /* or zero */, resource_directory_entry& new_icon_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + //Check icon for correctness + if(icon_file.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + const ico_header* icon_header = reinterpret_cast<const ico_header*>(&icon_file[0]); + + unsigned long size_of_headers = sizeof(ico_header) + icon_header->Count * sizeof(icondirentry); + if(icon_file.length() < size_of_headers || icon_header->Count == 0) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Enumerate all icons in file + for(uint16_t i = 0; i != icon_header->Count; ++i) + { + //Check icon entries + const icondirentry* icon_entry = reinterpret_cast<const icondirentry*>(&icon_file[sizeof(ico_header) + i * sizeof(icondirentry)]); + if(icon_entry->SizeInBytes == 0 + || icon_entry->ImageOffset < size_of_headers + || !pe_utils::is_sum_safe(icon_entry->ImageOffset, icon_entry->SizeInBytes) + || icon_entry->ImageOffset + icon_entry->SizeInBytes > icon_file.length()) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + } + + std::string icon_group_data; + ico_header* info = 0; + + if(group_icon_info) + { + //If icon group already exists + { + icon_group_data = group_icon_info->get_data(); + codepage = group_icon_info->get_codepage(); //Don't change codepage of icon group entry + } + + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + info = reinterpret_cast<ico_header*>(&icon_group_data[0]); + + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + icon_group_data.resize(sizeof(ico_header) + (info->Count + icon_header->Count) * sizeof(icon_group)); + info = reinterpret_cast<ico_header*>(&icon_group_data[0]); //In case if memory was reallocated + } + else //Entry not found - icon group doesn't exist + { + icon_group_data.resize(sizeof(ico_header) + icon_header->Count * sizeof(icon_group)); + memcpy(&icon_group_data[0], icon_header, sizeof(ico_header)); + } + + //Search for available icon IDs + std::vector<uint16_t> icon_id_list(get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_icon, mode, icon_header->Count)); + + //Enumerate all icons in file + for(uint16_t i = 0; i != icon_header->Count; ++i) + { + const icondirentry* icon_entry = reinterpret_cast<const icondirentry*>(&icon_file[sizeof(ico_header) + i * sizeof(icondirentry)]); + icon_group group = {0}; + + //Fill icon resource header + group.BitCount = icon_entry->BitCount; + group.ColorCount = icon_entry->ColorCount; + group.Height = icon_entry->Height; + group.Planes = icon_entry->Planes; + group.Reserved = icon_entry->Reserved; + group.SizeInBytes = icon_entry->SizeInBytes; + group.Width = icon_entry->Width; + group.Number = icon_id_list.at(i); + + memcpy(&icon_group_data[sizeof(ico_header) + ((info ? info->Count : 0) + i) * sizeof(icon_group)], &group, sizeof(group)); + + //Add icon to resources + resource_directory_entry new_entry; + new_entry.set_id(group.Number); + res_.add_resource(icon_file.substr(icon_entry->ImageOffset, icon_entry->SizeInBytes), pe_resource_viewer::resource_icon, new_entry, resource_directory::entry_finder(group.Number), language, codepage, timestamp); + } + + if(info) + info->Count += icon_header->Count; //Increase icon count, if we're adding icon to existing group + + { + //Add or replace icon group data entry + res_.add_resource(icon_group_data, pe_resource_viewer::resource_icon_group, new_icon_group_entry, finder, language, codepage, timestamp); + } +} + +//Returns free icon or cursor ID list depending on icon_place_mode +const std::vector<uint16_t> resource_cursor_icon_writer::get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_type type, icon_place_mode mode, uint32_t count) +{ + //Search for available icon/cursor IDs + std::vector<uint16_t> icon_cursor_id_list; + + try + { + //If any icon exists + //List icon IDs + std::vector<uint32_t> id_list(res_.list_resource_ids(type)); + std::sort(id_list.begin(), id_list.end()); + + //If we are placing icon on free spaces + //I.e., icon IDs 1, 3, 4, 7, 8 already exist + //We'll place five icons on IDs 2, 5, 6, 9, 10 + if(mode != icon_place_after_max_icon_id) + { + if(!id_list.empty()) + { + //Determine and list free icon IDs + for(std::vector<uint32_t>::const_iterator it = id_list.begin(); it != id_list.end(); ++it) + { + if(it == id_list.begin()) + { + if(*it > 1) + { + for(uint16_t i = 1; i != *it; ++i) + { + icon_cursor_id_list.push_back(i); + if(icon_cursor_id_list.size() == count) + break; + } + } + } + else if(*(it - 1) - *it > 1) + { + for(uint16_t i = static_cast<uint16_t>(*(it - 1) + 1); i != static_cast<uint16_t>(*it); ++i) + { + icon_cursor_id_list.push_back(i); + if(icon_cursor_id_list.size() == count) + break; + } + } + + if(icon_cursor_id_list.size() == count) + break; + } + } + } + + uint32_t max_id = id_list.empty() ? 0 : *std::max_element(id_list.begin(), id_list.end()); + for(uint32_t i = static_cast<uint32_t>(icon_cursor_id_list.size()); i != count; ++i) + icon_cursor_id_list.push_back(static_cast<uint16_t>(++max_id)); + } + catch(const pe_exception&) //Entry not found + { + for(uint16_t i = 1; i != count + 1; ++i) + icon_cursor_id_list.push_back(i); + } + + return icon_cursor_id_list; +} + +//Add cursor helper +void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, const resource_data_info* group_cursor_info /* or zero */, resource_directory_entry& new_cursor_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + //Check cursor for correctness + if(cursor_file.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + const cursor_header* cur_header = reinterpret_cast<const cursor_header*>(&cursor_file[0]); + + unsigned long size_of_headers = sizeof(cursor_header) + cur_header->Count * sizeof(cursordirentry); + if(cursor_file.length() < size_of_headers || cur_header->Count == 0) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Enumerate all cursors in file + for(uint16_t i = 0; i != cur_header->Count; ++i) + { + //Check cursor entries + const cursordirentry* cursor_entry = reinterpret_cast<const cursordirentry*>(&cursor_file[sizeof(cursor_header) + i * sizeof(cursordirentry)]); + if(cursor_entry->SizeInBytes == 0 + || cursor_entry->ImageOffset < size_of_headers + || !pe_utils::is_sum_safe(cursor_entry->ImageOffset, cursor_entry->SizeInBytes) + || cursor_entry->ImageOffset + cursor_entry->SizeInBytes > cursor_file.length()) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + } + + std::string cursor_group_data; + cursor_header* info = 0; + + if(group_cursor_info) + { + //If cursor group already exists + { + cursor_group_data = group_cursor_info->get_data(); + codepage = group_cursor_info->get_codepage(); //Don't change codepage of cursor group entry + } + + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Get cursor header + info = reinterpret_cast<cursor_header*>(&cursor_group_data[0]); + + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header) + info->Count * sizeof(cursor_group)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + cursor_group_data.resize(sizeof(cursor_header) + (info->Count + cur_header->Count) * sizeof(cursor_group)); + info = reinterpret_cast<cursor_header*>(&cursor_group_data[0]); //In case if memory was reallocated + } + else //Entry not found - cursor group doesn't exist + { + cursor_group_data.resize(sizeof(cursor_header) + cur_header->Count * sizeof(cursor_group)); + memcpy(&cursor_group_data[0], cur_header, sizeof(cursor_header)); + } + + //Search for available cursor IDs + std::vector<uint16_t> cursor_id_list(get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_cursor, mode, cur_header->Count)); + + //Enumerate all cursors in file + for(uint16_t i = 0; i != cur_header->Count; ++i) + { + const cursordirentry* cursor_entry = reinterpret_cast<const cursordirentry*>(&cursor_file[sizeof(cursor_header) + i * sizeof(cursordirentry)]); + cursor_group group = {0}; + + //Fill cursor resource header + group.Height = cursor_entry->Height * 2; + group.SizeInBytes = cursor_entry->SizeInBytes + 2 * sizeof(uint16_t) /* hotspot coordinates */; + group.Width = cursor_entry->Width; + group.Number = cursor_id_list.at(i); + + memcpy(&cursor_group_data[sizeof(cursor_header) + ((info ? info->Count : 0) + i) * sizeof(cursor_group)], &group, sizeof(group)); + + //Add cursor to resources + resource_directory_entry new_entry; + new_entry.set_id(group.Number); + + //Fill resource data (two WORDs for hotspot of cursor, and cursor bitmap data) + std::string cur_data; + cur_data.resize(sizeof(uint16_t) * 2); + memcpy(&cur_data[0], &cursor_entry->HotspotX, sizeof(uint16_t)); + memcpy(&cur_data[sizeof(uint16_t)], &cursor_entry->HotspotY, sizeof(uint16_t)); + cur_data.append(cursor_file.substr(cursor_entry->ImageOffset, cursor_entry->SizeInBytes)); + + res_.add_resource(cur_data, pe_resource_viewer::resource_cursor, new_entry, resource_directory::entry_finder(group.Number), language, codepage, timestamp); + } + + if(info) + info->Count += cur_header->Count; //Increase cursor count, if we're adding cursor to existing group + + { + //Add or replace cursor group data entry + res_.add_resource(cursor_group_data, pe_resource_viewer::resource_cursor_group, new_cursor_group_entry, finder, language, codepage, timestamp); + } +} + +//Adds icon(s) from icon file data +//timestamp will be used for directories that will be added +//If icon group with name "icon_group_name" or ID "icon_group_id" already exists, it will be appended with new icon(s) +//(Codepage of icon group and icons will not be changed in this case) +//icon_place_mode determines, how new icon(s) will be placed +void resource_cursor_icon_writer::add_icon(const std::string& icon_file, const std::wstring& icon_group_name, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_icon_group_entry; + new_icon_group_entry.set_name(icon_group_name); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, icon_group_name))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_icon(icon_file, data_info.get(), new_icon_group_entry, resource_directory::entry_finder(icon_group_name), language, mode, codepage, timestamp); +} + +void resource_cursor_icon_writer::add_icon(const std::string& icon_file, uint32_t icon_group_id, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_icon_group_entry; + new_icon_group_entry.set_id(icon_group_id); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, icon_group_id))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_icon(icon_file, data_info.get(), new_icon_group_entry, resource_directory::entry_finder(icon_group_id), language, mode, codepage, timestamp); +} + +//Adds cursor(s) from cursor file data +//timestamp will be used for directories that will be added +//If cursor group with name "cursor_group_name" or ID "cursor_group_id" already exists, it will be appended with new cursor(s) +//(Codepage of cursor group and cursors will not be changed in this case) +//icon_place_mode determines, how new cursor(s) will be placed +void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, const std::wstring& cursor_group_name, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_cursor_group_entry; + new_cursor_group_entry.set_name(cursor_group_name); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, cursor_group_name))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_cursor(cursor_file, data_info.get(), new_cursor_group_entry, resource_directory::entry_finder(cursor_group_name), language, mode, codepage, timestamp); +} + +void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, uint32_t cursor_group_id, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_cursor_group_entry; + new_cursor_group_entry.set_id(cursor_group_id); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, cursor_group_id))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_cursor(cursor_file, data_info.get(), new_cursor_group_entry, resource_directory::entry_finder(cursor_group_id), language, mode, codepage, timestamp); +} + +//Remove icon group helper +void resource_cursor_icon_writer::remove_icons_from_icon_group(const std::string& icon_group_data, uint32_t language) +{ + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + const ico_header* info = reinterpret_cast<const ico_header*>(icon_group_data.data()); + + uint16_t icon_count = info->Count; + + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header) + icon_count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Remove icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(icon_group_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + res_.remove_resource(pe_resource_viewer::resource_icon, group->Number, language); + } +} + +//Remove cursor group helper +void resource_cursor_icon_writer::remove_cursors_from_cursor_group(const std::string& cursor_group_data, uint32_t language) +{ + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Get icon header + const cursor_header* info = reinterpret_cast<const cursor_header*>(cursor_group_data.data()); + + uint16_t cursor_count = info->Count; + + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header) + cursor_count * sizeof(cursor_group)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Remove icon data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(cursor_group_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + res_.remove_resource(pe_resource_viewer::resource_cursor, group->Number, language); + } +} + +//Removes cursor group and all its cursors by name/ID and language +bool resource_cursor_icon_writer::remove_cursor_group(const std::wstring& cursor_group_name, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, cursor_group_name).get_data(); + remove_cursors_from_cursor_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_cursor_group, cursor_group_name, language); +} + +//Removes cursor group and all its cursors by name/ID and language +bool resource_cursor_icon_writer::remove_cursor_group(uint32_t cursor_group_id, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, cursor_group_id).get_data(); + remove_cursors_from_cursor_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_cursor_group, cursor_group_id, language); +} + +//Removes icon group and all its icons by name/ID and language +bool resource_cursor_icon_writer::remove_icon_group(const std::wstring& icon_group_name, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, icon_group_name).get_data(); + remove_icons_from_icon_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_icon_group, icon_group_name, language); +} + +//Removes icon group and all its icons by name/ID and language +bool resource_cursor_icon_writer::remove_icon_group(uint32_t icon_group_id, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, icon_group_id).get_data(); + remove_icons_from_icon_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_icon_group, icon_group_id, language); +} +} diff --git a/tools/pe_bliss/resource_cursor_icon_writer.h b/tools/pe_bliss/resource_cursor_icon_writer.h new file mode 100644 index 0000000000..e73ac6a093 --- /dev/null +++ b/tools/pe_bliss/resource_cursor_icon_writer.h @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <vector> +#include "stdint_defs.h" +#include "pe_resource_manager.h" + +namespace pe_bliss +{ +class pe_resource_manager; + +class resource_cursor_icon_writer +{ +public: + //Determines, how new icon(s) or cursor(s) will be placed + enum icon_place_mode + { + icon_place_after_max_icon_id, //Icon(s) will be placed after all existing + icon_place_free_ids //New icon(s) will take all free IDs between existing icons + }; + +public: + resource_cursor_icon_writer(pe_resource_manager& res); + + //Removes icon group and all its icons by name/ID and language + bool remove_icon_group(const std::wstring& icon_group_name, uint32_t language); + bool remove_icon_group(uint32_t icon_group_id, uint32_t language); + + //Adds icon(s) from icon file data + //timestamp will be used for directories that will be added + //If icon group with name "icon_group_name" or ID "icon_group_id" already exists, it will be appended with new icon(s) + //(Codepage of icon group and icons will not be changed in this case) + //icon_place_mode determines, how new icon(s) will be placed + void add_icon(const std::string& icon_file, + const std::wstring& icon_group_name, + uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, + uint32_t codepage = 0, uint32_t timestamp = 0); + + void add_icon(const std::string& icon_file, + uint32_t icon_group_id, + uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, + uint32_t codepage = 0, uint32_t timestamp = 0); + + //Removes cursor group and all its cursors by name/ID and language + bool remove_cursor_group(const std::wstring& cursor_group_name, uint32_t language); + bool remove_cursor_group(uint32_t cursor_group_id, uint32_t language); + + //Adds cursor(s) from cursor file data + //timestamp will be used for directories that will be added + //If cursor group with name "cursor_group_name" or ID "cursor_group_id" already exists, it will be appended with new cursor(s) + //(Codepage of cursor group and cursors will not be changed in this case) + //icon_place_mode determines, how new cursor(s) will be placed + void add_cursor(const std::string& cursor_file, const std::wstring& cursor_group_name, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_cursor(const std::string& cursor_file, uint32_t cursor_group_id, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0); + +private: + pe_resource_manager& res_; + + //Add icon helper + void add_icon(const std::string& icon_file, const resource_data_info* group_icon_info /* or zero */, resource_directory_entry& new_icon_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp); + + //Remove icon group helper + void remove_icons_from_icon_group(const std::string& icon_group_data, uint32_t language); + + //Add cursor helper + void add_cursor(const std::string& cursor_file, const resource_data_info* group_cursor_info /* or zero */, resource_directory_entry& new_cursor_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp); + + //Remove cursor group helper + void remove_cursors_from_cursor_group(const std::string& cursor_group_data, uint32_t language); + + //Returns free icon or cursor ID list depending on icon_place_mode + const std::vector<uint16_t> get_icon_or_cursor_free_id_list(pe_resource_manager::resource_type type, icon_place_mode mode, uint32_t count); +}; +} diff --git a/tools/pe_bliss/resource_data_info.cpp b/tools/pe_bliss/resource_data_info.cpp new file mode 100644 index 0000000000..75bb060eae --- /dev/null +++ b/tools/pe_bliss/resource_data_info.cpp @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "resource_data_info.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +//Default constructor +resource_data_info::resource_data_info(const std::string& data, uint32_t codepage) + :data_(data), codepage_(codepage) +{} + +//Constructor from data +resource_data_info::resource_data_info(const resource_data_entry& data) + :data_(data.get_data()), codepage_(data.get_codepage()) +{} + +//Returns resource data +const std::string& resource_data_info::get_data() const +{ + return data_; +} + +//Returns resource codepage +uint32_t resource_data_info::get_codepage() const +{ + return codepage_; +} +} diff --git a/tools/pe_bliss/resource_data_info.h b/tools/pe_bliss/resource_data_info.h new file mode 100644 index 0000000000..e2275ebbf5 --- /dev/null +++ b/tools/pe_bliss/resource_data_info.h @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class resource_data_entry; + +//Class representing resource data +class resource_data_info +{ +public: + //Constructor from data + resource_data_info(const std::string& data, uint32_t codepage); + //Constructor from data + explicit resource_data_info(const resource_data_entry& data); + + //Returns resource data + const std::string& get_data() const; + //Returns resource codepage + uint32_t get_codepage() const; + +private: + std::string data_; + uint32_t codepage_; +}; +} diff --git a/tools/pe_bliss/resource_internal.h b/tools/pe_bliss/resource_internal.h new file mode 100644 index 0000000000..64a5bf3903 --- /dev/null +++ b/tools/pe_bliss/resource_internal.h @@ -0,0 +1,34 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once + +#define U16TEXT(t) reinterpret_cast<const unicode16_t*>( t ) + +#define StringFileInfo U16TEXT("S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0") +#define SizeofStringFileInfo sizeof("S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0") +#define VarFileInfo U16TEXT("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0") +#define Translation U16TEXT("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0") + +#define VarFileInfoAligned U16TEXT("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0\0") +#define TranslationAligned U16TEXT("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0\0\0") +#define SizeofVarFileInfoAligned sizeof("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0\0") +#define SizeofTranslationAligned sizeof("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0\0\0") diff --git a/tools/pe_bliss/resource_message_list_reader.cpp b/tools/pe_bliss/resource_message_list_reader.cpp new file mode 100644 index 0000000000..f2ea142bee --- /dev/null +++ b/tools/pe_bliss/resource_message_list_reader.cpp @@ -0,0 +1,131 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "resource_message_list_reader.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_message_list_reader::resource_message_list_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Helper function of parsing message list table +const resource_message_list resource_message_list_reader::parse_message_list(const std::string& resource_data) +{ + resource_message_list ret; + + //Check resource data length + if(resource_data.length() < sizeof(message_resource_data)) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + const message_resource_data* message_data = reinterpret_cast<const message_resource_data*>(resource_data.data()); + + //Check resource data length more carefully and some possible overflows + if(message_data->NumberOfBlocks >= pe_utils::max_dword / sizeof(message_resource_block) + || !pe_utils::is_sum_safe(message_data->NumberOfBlocks * sizeof(message_resource_block), sizeof(message_resource_data)) + || resource_data.length() < message_data->NumberOfBlocks * sizeof(message_resource_block) + sizeof(message_resource_data)) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + //Iterate over all message resource blocks + for(unsigned long i = 0; i != message_data->NumberOfBlocks; ++i) + { + //Get block + const message_resource_block* block = + reinterpret_cast<const message_resource_block*>(resource_data.data() + sizeof(message_resource_data) - sizeof(message_resource_block) + sizeof(message_resource_block) * i); + + //Check resource data length and IDs + if(resource_data.length() < block->OffsetToEntries || block->LowId > block->HighId) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + unsigned long current_pos = 0; + static const unsigned long size_of_entry_headers = 4; + //List all message resource entries in block + for(uint32_t curr_id = block->LowId; curr_id <= block->HighId; curr_id++) + { + //Check resource data length and some possible overflows + if(!pe_utils::is_sum_safe(block->OffsetToEntries, current_pos) + || !pe_utils::is_sum_safe(block->OffsetToEntries + current_pos, size_of_entry_headers) + || resource_data.length() < block->OffsetToEntries + current_pos + size_of_entry_headers) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + //Get entry + const message_resource_entry* entry = reinterpret_cast<const message_resource_entry*>(resource_data.data() + block->OffsetToEntries + current_pos); + + //Check resource data length and entry length and some possible overflows + if(entry->Length < size_of_entry_headers + || !pe_utils::is_sum_safe(block->OffsetToEntries + current_pos, entry->Length) + || resource_data.length() < block->OffsetToEntries + current_pos + entry->Length + || entry->Length < size_of_entry_headers) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + if(entry->Flags & message_resource_unicode) + { + //If string is UNICODE + //Check its length + if(entry->Length % 2) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + //Add ID and string to message table +#ifdef PE_BLISS_WINDOWS + ret.insert(std::make_pair(curr_id, message_table_item( + std::wstring(reinterpret_cast<const wchar_t*>(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers), + (entry->Length - size_of_entry_headers) / 2) + ))); +#else + ret.insert(std::make_pair(curr_id, message_table_item( + pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers), + (entry->Length - size_of_entry_headers) / 2)) + ))); +#endif + } + else + { + //If string is ANSI + //Add ID and string to message table + ret.insert(std::make_pair(curr_id, message_table_item( + std::string(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers, + entry->Length - size_of_entry_headers) + ))); + } + + //Go to next entry + current_pos += entry->Length; + } + } + + return ret; +} + +//Returns message table data by ID and index in language directory (instead of language) +const resource_message_list resource_message_list_reader::get_message_table_by_id(uint32_t id, uint32_t index) const +{ + return parse_message_list(res_.get_resource_data_by_id(pe_resource_viewer::resource_message_table, id, index).get_data()); +} + +//Returns message table data by ID and language +const resource_message_list resource_message_list_reader::get_message_table_by_id_lang(uint32_t language, uint32_t id) const +{ + return parse_message_list(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_message_table, id).get_data()); +} +} diff --git a/tools/pe_bliss/resource_message_list_reader.h b/tools/pe_bliss/resource_message_list_reader.h new file mode 100644 index 0000000000..a0ac96eb8c --- /dev/null +++ b/tools/pe_bliss/resource_message_list_reader.h @@ -0,0 +1,49 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "message_table.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +//ID; message_table_item +typedef std::map<uint32_t, message_table_item> resource_message_list; + +class resource_message_list_reader +{ +public: + resource_message_list_reader(const pe_resource_viewer& res); + + //Returns message table data by ID and language + const resource_message_list get_message_table_by_id_lang(uint32_t language, uint32_t id) const; + //Returns message table data by ID and index in language directory (instead of language) + const resource_message_list get_message_table_by_id(uint32_t id, uint32_t index = 0) const; + + //Helper function of parsing message list table + //resource_data - raw message table resource data + static const resource_message_list parse_message_list(const std::string& resource_data); + +private: + const pe_resource_viewer& res_; +}; +} diff --git a/tools/pe_bliss/resource_string_table_reader.cpp b/tools/pe_bliss/resource_string_table_reader.cpp new file mode 100644 index 0000000000..8a51720e6a --- /dev/null +++ b/tools/pe_bliss/resource_string_table_reader.cpp @@ -0,0 +1,109 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "resource_string_table_reader.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +resource_string_table_reader::resource_string_table_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Returns string table data by ID and index in language directory (instead of language) +const resource_string_list resource_string_table_reader::get_string_table_by_id(uint32_t id, uint32_t index) const +{ + return parse_string_list(id, res_.get_resource_data_by_id(pe_resource_viewer::resource_string, id, index).get_data()); +} + +//Returns string table data by ID and language +const resource_string_list resource_string_table_reader::get_string_table_by_id_lang(uint32_t language, uint32_t id) const +{ + return parse_string_list(id, res_.get_resource_data_by_id(language, pe_resource_viewer::resource_string, id).get_data()); +} + +//Helper function of parsing string list table +const resource_string_list resource_string_table_reader::parse_string_list(uint32_t id, const std::string& resource_data) +{ + resource_string_list ret; + + //16 is maximum count of strings in a string table + static const unsigned long max_string_list_entries = 16; + unsigned long passed_bytes = 0; + for(unsigned long i = 0; i != max_string_list_entries; ++i) + { + //Check resource data length + if(resource_data.length() < sizeof(uint16_t) + passed_bytes) + throw pe_exception("Incorrect resource string table", pe_exception::resource_incorrect_string_table); + + //Get string length - the first WORD + uint16_t string_length = *reinterpret_cast<const uint16_t*>(resource_data.data() + passed_bytes); + passed_bytes += sizeof(uint16_t); //WORD containing string length + + //Check resource data length again + if(resource_data.length() < string_length + passed_bytes) + throw pe_exception("Incorrect resource string table", pe_exception::resource_incorrect_string_table); + + if(string_length) + { + //Create and save string (UNICODE) +#ifdef PE_BLISS_WINDOWS + ret.insert( + std::make_pair(static_cast<uint16_t>(((id - 1) << 4) + i), //ID of string is calculated such way + std::wstring(reinterpret_cast<const wchar_t*>(resource_data.data() + passed_bytes), string_length))); +#else + ret.insert( + std::make_pair(static_cast<uint16_t>(((id - 1) << 4) + i), //ID of string is calculated such way + pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + passed_bytes), string_length)))); +#endif + } + + //Go to next string + passed_bytes += string_length * 2; + } + + return ret; +} + +//Returns string from string table by ID and language +const std::wstring resource_string_table_reader::get_string_by_id_lang(uint32_t language, uint16_t id) const +{ + //List strings by string table id and language + const resource_string_list strings(get_string_table_by_id_lang(language, (id >> 4) + 1)); + resource_string_list::const_iterator it = strings.find(id); //Find string by id + if(it == strings.end()) + throw pe_exception("Resource string not found", pe_exception::resource_string_not_found); + + return (*it).second; +} + +//Returns string from string table by ID and index in language directory (instead of language) +const std::wstring resource_string_table_reader::get_string_by_id(uint16_t id, uint32_t index) const +{ + //List strings by string table id and index + const resource_string_list strings(get_string_table_by_id((id >> 4) + 1, index)); + resource_string_list::const_iterator it = strings.find(id); //Find string by id + if(it == strings.end()) + throw pe_exception("Resource string not found", pe_exception::resource_string_not_found); + + return (*it).second; +} +} diff --git a/tools/pe_bliss/resource_string_table_reader.h b/tools/pe_bliss/resource_string_table_reader.h new file mode 100644 index 0000000000..e3ded1da85 --- /dev/null +++ b/tools/pe_bliss/resource_string_table_reader.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <string> +#include <map> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +//ID; string +typedef std::map<uint16_t, std::wstring> resource_string_list; + +class resource_string_table_reader +{ +public: + resource_string_table_reader(const pe_resource_viewer& res); + +public: + //Returns string table data by ID and language + const resource_string_list get_string_table_by_id_lang(uint32_t language, uint32_t id) const; + //Returns string table data by ID and index in language directory (instead of language) + const resource_string_list get_string_table_by_id(uint32_t id, uint32_t index = 0) const; + //Returns string from string table by ID and language + const std::wstring get_string_by_id_lang(uint32_t language, uint16_t id) const; + //Returns string from string table by ID and index in language directory (instead of language) + const std::wstring get_string_by_id(uint16_t id, uint32_t index = 0) const; + +private: + const pe_resource_viewer& res_; + + //Helper function of parsing string list table + //Id of resource is needed to calculate string IDs correctly + //resource_data is raw string table resource data + static const resource_string_list parse_string_list(uint32_t id, const std::string& resource_data); +}; +} diff --git a/tools/pe_bliss/resource_version_info_reader.cpp b/tools/pe_bliss/resource_version_info_reader.cpp new file mode 100644 index 0000000000..8ad44c6856 --- /dev/null +++ b/tools/pe_bliss/resource_version_info_reader.cpp @@ -0,0 +1,311 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "resource_version_info_reader.h" +#include "utils.h" +#include "pe_exception.h" +#include "resource_internal.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Root version info block key value +const u16string resource_version_info_reader::version_info_key(U16TEXT("V\0S\0_\0V\0E\0R\0S\0I\0O\0N\0_\0I\0N\0F\0O\0\0")); + +resource_version_info_reader::resource_version_info_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Returns aligned version block value position +uint32_t resource_version_info_reader::get_version_block_value_pos(uint32_t base_pos, const unicode16_t* key) +{ + uint32_t string_length = static_cast<uint32_t>(u16string(key).length()); + uint32_t ret = pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 /* headers before Key data */ + + base_pos + + (string_length + 1 /* nullbyte */) * 2), + sizeof(uint32_t)); + + //Check possible overflows + if(ret < base_pos || ret < sizeof(uint16_t) * 3 || ret < (string_length + 1) * 2) + throw_incorrect_version_info(); + + return ret; +} + +//Returns aligned version block first child position +uint32_t resource_version_info_reader::get_version_block_first_child_pos(uint32_t base_pos, uint32_t value_length, const unicode16_t* key) +{ + uint32_t string_length = static_cast<uint32_t>(u16string(key).length()); + uint32_t ret = pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 /* headers before Key data */ + + base_pos + + (string_length + 1 /* nullbyte */) * 2), + sizeof(uint32_t)) + + pe_utils::align_up(value_length, sizeof(uint32_t)); + + //Check possible overflows + if(ret < base_pos || ret < value_length || ret < sizeof(uint16_t) * 3 || ret < (string_length + 1) * 2) + throw_incorrect_version_info(); + + return ret; +} + +//Throws an exception (id = resource_incorrect_version_info) +void resource_version_info_reader::throw_incorrect_version_info() +{ + throw pe_exception("Incorrect resource version info", pe_exception::resource_incorrect_version_info); +} + +//Returns full version information: +//file_version_info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +const file_version_info resource_version_info_reader::get_version_info(lang_string_values_map& string_values, translation_values_map& translations, const std::string& resource_data) const +{ + //Fixed file version info + file_version_info ret; + + //Check resource data length + if(resource_data.length() < sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Root version info block + const version_info_block* root_block = reinterpret_cast<const version_info_block*>(resource_data.data()); + + //Check root block key for null-termination and its name + if(!pe_utils::is_null_terminated(root_block->Key, resource_data.length() - sizeof(uint16_t) * 3 /* headers before Key data */) + || version_info_key != reinterpret_cast<const unicode16_t*>(root_block->Key)) + throw_incorrect_version_info(); + + //If file has fixed version info + if(root_block->ValueLength) + { + //Get root block value position + uint32_t value_pos = get_version_block_value_pos(0, reinterpret_cast<const unicode16_t*>(root_block->Key)); + //Check value length + if(resource_data.length() < value_pos + sizeof(vs_fixedfileinfo)) + throw_incorrect_version_info(); + + //Get VS_FIXEDFILEINFO structure pointer + const vs_fixedfileinfo* file_info = reinterpret_cast<const vs_fixedfileinfo*>(resource_data.data() + value_pos); + //Check its signature and some other fields + if(file_info->dwSignature != vs_ffi_signature || file_info->dwStrucVersion != vs_ffi_strucversion) //Don't check if file_info->dwFileFlagsMask == VS_FFI_FILEFLAGSMASK + throw_incorrect_version_info(); + + //Save fixed version info + ret = file_version_info(*file_info); + } + + //Iterate over child elements of VS_VERSIONINFO (StringFileInfo or VarFileInfo) + for(uint32_t child_pos = get_version_block_first_child_pos(0, root_block->ValueLength, reinterpret_cast<const unicode16_t*>(root_block->Key)); + child_pos < root_block->Length;) + { + //Check block position + if(!pe_utils::is_sum_safe(child_pos, sizeof(version_info_block)) + || resource_data.length() < child_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer + const version_info_block* block = reinterpret_cast<const version_info_block*>(resource_data.data() + child_pos); + + //Check its length + if(block->Length == 0) + throw_incorrect_version_info(); + + //Check block key for null-termination + if(!pe_utils::is_null_terminated(block->Key, resource_data.length() - child_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + u16string info_type(reinterpret_cast<const unicode16_t*>(block->Key)); + //If we encountered StringFileInfo... + if(info_type == StringFileInfo) + { + //Enumerate all string tables + for(uint32_t string_table_pos = get_version_block_first_child_pos(child_pos, block->ValueLength, reinterpret_cast<const unicode16_t*>(block->Key)); + string_table_pos - child_pos < block->Length;) + { + //Check string table block position + if(resource_data.length() < string_table_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer for string table + const version_info_block* string_table = reinterpret_cast<const version_info_block*>(resource_data.data() + string_table_pos); + + //Check its length + if(string_table->Length == 0) + throw_incorrect_version_info(); + + //Check string table key for null-termination + if(!pe_utils::is_null_terminated(string_table->Key, resource_data.length() - string_table_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + string_values_map new_values; + + //Enumerate all strings in the string table + for(uint32_t string_pos = get_version_block_first_child_pos(string_table_pos, string_table->ValueLength, reinterpret_cast<const unicode16_t*>(string_table->Key)); + string_pos - string_table_pos < string_table->Length;) + { + //Check string block position + if(resource_data.length() < string_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer for string block + const version_info_block* string_block = reinterpret_cast<const version_info_block*>(resource_data.data() + string_pos); + + //Check its length + if(string_block->Length == 0) + throw_incorrect_version_info(); + + //Check string block key for null-termination + if(!pe_utils::is_null_terminated(string_block->Key, resource_data.length() - string_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + u16string data; + //If string block has value + if(string_block->ValueLength != 0) + { + //Get value position + uint32_t value_pos = get_version_block_value_pos(string_pos, reinterpret_cast<const unicode16_t*>(string_block->Key)); + //Check it + if(resource_data.length() < value_pos + string_block->ValueLength) + throw pe_exception("Incorrect resource version info", pe_exception::resource_incorrect_version_info); + + //Get UNICODE string value + data = u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + value_pos), string_block->ValueLength); + pe_utils::strip_nullbytes(data); + } + + //Save name-value pair +#ifdef PE_BLISS_WINDOWS + new_values.insert(std::make_pair(reinterpret_cast<const unicode16_t*>(string_block->Key), data)); +#else + new_values.insert(std::make_pair(pe_utils::from_ucs2(reinterpret_cast<const unicode16_t*>(string_block->Key)), + pe_utils::from_ucs2(data))); +#endif + + //Navigate to next string block + string_pos += pe_utils::align_up(string_block->Length, sizeof(uint32_t)); + } + +#ifdef PE_BLISS_WINDOWS + string_values.insert(std::make_pair(reinterpret_cast<const unicode16_t*>(string_table->Key), new_values)); +#else + string_values.insert(std::make_pair(pe_utils::from_ucs2(reinterpret_cast<const unicode16_t*>(string_table->Key)), new_values)); +#endif + + //Navigate to next string table block + string_table_pos += pe_utils::align_up(string_table->Length, sizeof(uint32_t)); + } + } + else if(info_type == VarFileInfo) //If we encountered VarFileInfo + { + for(uint32_t var_table_pos = get_version_block_first_child_pos(child_pos, block->ValueLength, reinterpret_cast<const unicode16_t*>(block->Key)); + var_table_pos - child_pos < block->Length;) + { + //Check var block position + if(resource_data.length() < var_table_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer for var block + const version_info_block* var_table = reinterpret_cast<const version_info_block*>(resource_data.data() + var_table_pos); + + //Check its length + if(var_table->Length == 0) + throw_incorrect_version_info(); + + //Check its key for null-termination + if(!pe_utils::is_null_terminated(var_table->Key, resource_data.length() - var_table_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + //If block is "Translation" (actually, there's no other types possible in VarFileInfo) and it has value + if(u16string(reinterpret_cast<const unicode16_t*>(var_table->Key)) == Translation && var_table->ValueLength) + { + //Get its value position + uint32_t value_pos = get_version_block_value_pos(var_table_pos, reinterpret_cast<const unicode16_t*>(var_table->Key)); + //Cherck value length + if(resource_data.length() < value_pos + var_table->ValueLength) + throw_incorrect_version_info(); + + //Get list of translations: pairs of LANGUAGE_ID - CODEPAGE_ID + for(unsigned long i = 0; i < var_table->ValueLength; i += sizeof(uint16_t) * 2) + { + //Pair of WORDs + uint16_t lang_id = *reinterpret_cast<const uint16_t*>(resource_data.data() + value_pos + i); + uint16_t codepage_id = *reinterpret_cast<const uint16_t*>(resource_data.data() + value_pos + sizeof(uint16_t) + i); + //Save translation + translations.insert(std::make_pair(lang_id, codepage_id)); + } + } + + //Navigate to next var block + var_table_pos += pe_utils::align_up(var_table->Length, sizeof(uint32_t)); + } + } + else + { + throw_incorrect_version_info(); + } + + //Navigate to next element in root block + child_pos += pe_utils::align_up(block->Length, sizeof(uint32_t)); + } + + return ret; +} + +//Returns full version information: +//file_version info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +const file_version_info resource_version_info_reader::get_version_info_by_lang(lang_string_values_map& string_values, translation_values_map& translations, uint32_t language) const +{ + const std::string& resource_data = res_.get_root_directory() //Type directory + .entry_by_id(pe_resource_viewer::resource_version) + .get_resource_directory() //Name/ID directory + .entry_by_id(1) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry() //Data directory + .get_data(); + + return get_version_info(string_values, translations, resource_data); +} + +//Returns full version information: +//file_version_info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +const file_version_info resource_version_info_reader::get_version_info(lang_string_values_map& string_values, translation_values_map& translations, uint32_t index) const +{ + const resource_directory::entry_list& entries = res_.get_root_directory() //Type directory + .entry_by_id(pe_resource_viewer::resource_version) + .get_resource_directory() //Name/ID directory + .entry_by_id(1) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return get_version_info(string_values, translations, entries.at(index).get_data_entry().get_data()); //Data directory +} +} diff --git a/tools/pe_bliss/resource_version_info_reader.h b/tools/pe_bliss/resource_version_info_reader.h new file mode 100644 index 0000000000..c1dfbffdc2 --- /dev/null +++ b/tools/pe_bliss/resource_version_info_reader.h @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <map> +#include "file_version_info.h" +#include "pe_structures.h" +#include "version_info_types.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +class resource_version_info_reader +{ +public: //VERSION INFO + resource_version_info_reader(const pe_resource_viewer& res); + + //Returns full version information: + //file_version_info: versions and file info + //lang_lang_string_values_map: map of version info strings with encodings with encodings + //translation_values_map: map of translations + const file_version_info get_version_info(lang_string_values_map& string_values, translation_values_map& translations, uint32_t index = 0) const; + const file_version_info get_version_info_by_lang(lang_string_values_map& string_values, translation_values_map& translations, uint32_t language) const; + +public: + //L"VS_VERSION_INFO" key of root version info block + static const u16string version_info_key; + +private: + const pe_resource_viewer& res_; + + //VERSION INFO helpers + //Returns aligned version block value position + static uint32_t get_version_block_value_pos(uint32_t base_pos, const unicode16_t* key); + + //Returns aligned version block first child position + static uint32_t get_version_block_first_child_pos(uint32_t base_pos, uint32_t value_length, const unicode16_t* key); + + //Returns full version information: + //file_version_info: versions and file info + //lang_string_values_map: map of version info strings with encodings + //translation_values_map: map of translations + const file_version_info get_version_info(lang_string_values_map& string_values, translation_values_map& translations, const std::string& resource_data) const; + + //Throws an exception (id = resource_incorrect_version_info) + static void throw_incorrect_version_info(); +}; +} diff --git a/tools/pe_bliss/resource_version_info_writer.cpp b/tools/pe_bliss/resource_version_info_writer.cpp new file mode 100644 index 0000000000..ed95a0f7ea --- /dev/null +++ b/tools/pe_bliss/resource_version_info_writer.cpp @@ -0,0 +1,283 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "resource_version_info_writer.h" +#include "pe_structures.h" +#include "resource_internal.h" +#include "utils.h" +#include "pe_resource_manager.h" +#include "resource_version_info_reader.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_version_info_writer::resource_version_info_writer(pe_resource_manager& res) + :res_(res) +{} + +//Sets/replaces full version information: +//file_version_info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +void resource_version_info_writer::set_version_info(const file_version_info& file_info, + const lang_string_values_map& string_values, + const translation_values_map& translations, + uint32_t language, + uint32_t codepage, + uint32_t timestamp) +{ + std::string version_data; + + //Calculate total size of version resource data + uint32_t total_version_info_length = + static_cast<uint32_t>(sizeof(version_info_block) - sizeof(uint16_t) + sizeof(uint16_t) /* pading */ + + (resource_version_info_reader::version_info_key.length() + 1) * 2 + + sizeof(vs_fixedfileinfo)); + + //If we have any strings values + if(!string_values.empty()) + { + total_version_info_length += sizeof(version_info_block) - sizeof(uint16_t); //StringFileInfo block + total_version_info_length += SizeofStringFileInfo; //Name of block (key) + + //Add required size for version strings + for(lang_string_values_map::const_iterator table_it = string_values.begin(); table_it != string_values.end(); ++table_it) + { + total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 + ((*table_it).first.length() + 1) * 2), sizeof(uint32_t)); //Name of child block and block size (key of string table block) + + const string_values_map& values = (*table_it).second; + for(string_values_map::const_iterator it = values.begin(); it != values.end(); ++it) + { + total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 + ((*it).first.length() + 1) * 2), sizeof(uint32_t)); + total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(((*it).second.length() + 1) * 2), sizeof(uint32_t)); + } + } + } + + //If we have translations + if(!translations.empty()) + { + total_version_info_length += (sizeof(version_info_block) - sizeof(uint16_t)) * 2; //VarFileInfo and Translation blocks + total_version_info_length += SizeofVarFileInfoAligned; //DWORD-aligned VarFileInfo block name + total_version_info_length += SizeofTranslationAligned; //DWORD-aligned Translation block name + total_version_info_length += static_cast<uint32_t>(translations.size() * sizeof(uint16_t) * 2); + } + + //Resize version data buffer + version_data.resize(total_version_info_length); + + //Create root version block + version_info_block root_block = {0}; + root_block.ValueLength = sizeof(vs_fixedfileinfo); + root_block.Length = static_cast<uint16_t>(total_version_info_length); + + //Fill fixed file info + vs_fixedfileinfo fixed_info = {0}; + fixed_info.dwFileDateLS = file_info.get_file_date_ls(); + fixed_info.dwFileDateMS = file_info.get_file_date_ms(); + fixed_info.dwFileFlags = file_info.get_file_flags(); + fixed_info.dwFileFlagsMask = vs_ffi_fileflagsmask; + fixed_info.dwFileOS = file_info.get_file_os_raw(); + fixed_info.dwFileSubtype = file_info.get_file_subtype(); + fixed_info.dwFileType = file_info.get_file_type_raw(); + fixed_info.dwFileVersionLS = file_info.get_file_version_ls(); + fixed_info.dwFileVersionMS = file_info.get_file_version_ms(); + fixed_info.dwSignature = vs_ffi_signature; + fixed_info.dwStrucVersion = vs_ffi_strucversion; + fixed_info.dwProductVersionLS = file_info.get_product_version_ls(); + fixed_info.dwProductVersionMS = file_info.get_product_version_ms(); + + //Write root block and fixed file info to buffer + uint32_t data_ptr = 0; + memcpy(&version_data[data_ptr], &root_block, sizeof(version_info_block) - sizeof(uint16_t)); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + memcpy(&version_data[data_ptr], resource_version_info_reader::version_info_key.c_str(), (resource_version_info_reader::version_info_key.length() + 1) * sizeof(uint16_t)); + data_ptr += static_cast<uint32_t>((resource_version_info_reader::version_info_key.length() + 1) * sizeof(uint16_t)); + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + memcpy(&version_data[data_ptr], &fixed_info, sizeof(fixed_info)); + data_ptr += sizeof(fixed_info); + + //Write string values, if any + if(!string_values.empty()) + { + //Create string file info root block + version_info_block string_file_info_block = {0}; + string_file_info_block.Type = 1; //Block type is string + memcpy(&version_data[data_ptr], &string_file_info_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* string_file_info_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr1 = data_ptr; //Used to calculate string file info block length later + memcpy(&version_data[data_ptr], StringFileInfo, SizeofStringFileInfo); //Write block name + data_ptr += SizeofStringFileInfo; + + //Create string table root block (child of string file info) + version_info_block string_table_block = {0}; + string_table_block.Type = 1; //Block type is string + + for(lang_string_values_map::const_iterator table_it = string_values.begin(); table_it != string_values.end(); ++table_it) + { + const string_values_map& values = (*table_it).second; + + memcpy(&version_data[data_ptr], &string_table_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* string_table_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr2 = data_ptr; //Used to calculate string table block length later + uint32_t lang_key_length = static_cast<uint32_t>(((*table_it).first.length() + 1) * sizeof(uint16_t)); + +#ifdef PE_BLISS_WINDOWS + memcpy(&version_data[data_ptr], (*table_it).first.c_str(), lang_key_length); //Write block key +#else + { + u16string str(pe_utils::to_ucs2((*table_it).first)); + memcpy(&version_data[data_ptr], str.c_str(), lang_key_length); //Write block key + } +#endif + + data_ptr += lang_key_length; + //Align key if necessary + if((sizeof(uint16_t) * 3 + lang_key_length) % sizeof(uint32_t)) + { + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + } + + //Create string block (child of string table block) + version_info_block string_block = {0}; + string_block.Type = 1; //Block type is string + for(string_values_map::const_iterator it = values.begin(); it != values.end(); ++it) + { + //Calculate value length and key length of string block + string_block.ValueLength = static_cast<uint16_t>((*it).second.length() + 1); + uint32_t key_length = static_cast<uint32_t>(((*it).first.length() + 1) * sizeof(uint16_t)); + //Calculate length of block + string_block.Length = static_cast<uint16_t>(pe_utils::align_up(sizeof(uint16_t) * 3 + key_length, sizeof(uint32_t)) + string_block.ValueLength * sizeof(uint16_t)); + + //Write string block + memcpy(&version_data[data_ptr], &string_block, sizeof(version_info_block) - sizeof(uint16_t)); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + +#ifdef PE_BLISS_WINDOWS + memcpy(&version_data[data_ptr], (*it).first.c_str(), key_length); //Write block key +#else + { + u16string str(pe_utils::to_ucs2((*it).first)); + memcpy(&version_data[data_ptr], str.c_str(), key_length); //Write block key + } +#endif + + data_ptr += key_length; + //Align key if necessary + if((sizeof(uint16_t) * 3 + key_length) % sizeof(uint32_t)) + { + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + } + + //Write block data (value) +#ifdef PE_BLISS_WINDOWS + memcpy(&version_data[data_ptr], (*it).second.c_str(), string_block.ValueLength * sizeof(uint16_t)); +#else + { + u16string str(pe_utils::to_ucs2((*it).second)); + memcpy(&version_data[data_ptr], str.c_str(), string_block.ValueLength * sizeof(uint16_t)); + } +#endif + + data_ptr += string_block.ValueLength * 2; + //Align data if necessary + if((string_block.ValueLength * 2) % sizeof(uint32_t)) + { + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + } + } + + //Calculate string table and string file info blocks lengths + string_table_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr2 + sizeof(uint16_t) * 3); + } + + string_file_info_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr1 + sizeof(uint16_t) * 3); + } + + //If we have transactions + if(!translations.empty()) + { + //Create root var file info block + version_info_block var_file_info_block = {0}; + var_file_info_block.Type = 1; //Type of block is string + //Write block header + memcpy(&version_data[data_ptr], &var_file_info_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* var_file_info_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr1 = data_ptr; //Used to calculate var file info block length later + memcpy(&version_data[data_ptr], VarFileInfoAligned, SizeofVarFileInfoAligned); //Write block key (aligned) + data_ptr += SizeofVarFileInfoAligned; + + //Create root translation block (child of var file info block) + version_info_block translation_block = {0}; + //Write block header + memcpy(&version_data[data_ptr], &translation_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* translation_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr2 = data_ptr; //Used to calculate var file info block length later + memcpy(&version_data[data_ptr], TranslationAligned, SizeofTranslationAligned); //Write block key (aligned) + data_ptr += SizeofTranslationAligned; + + //Calculate translation block value length + translation_block_ptr->ValueLength = static_cast<uint16_t>(sizeof(uint16_t) * 2 * translations.size()); + + //Write translation values to block + for(translation_values_map::const_iterator it = translations.begin(); it != translations.end(); ++it) + { + uint16_t lang_id = (*it).first; //Language ID + uint16_t codepage_id = (*it).second; //Codepage ID + memcpy(&version_data[data_ptr], &lang_id, sizeof(lang_id)); + data_ptr += sizeof(lang_id); + memcpy(&version_data[data_ptr], &codepage_id, sizeof(codepage_id)); + data_ptr += sizeof(codepage_id); + } + + //Calculate Translation and VarFileInfo blocks lengths + translation_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr2 + sizeof(uint16_t) * 3); + var_file_info_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr1 + sizeof(uint16_t) * 3); + } + + //Add/replace version info resource + res_.add_resource(version_data, pe_resource_viewer::resource_version, 1, language, codepage, timestamp); +} + +//Removes version info by language (ID = 1) +bool resource_version_info_writer::remove_version_info(uint32_t language) +{ + return res_.remove_resource(pe_resource_viewer::resource_version, 1, language); +} +} diff --git a/tools/pe_bliss/resource_version_info_writer.h b/tools/pe_bliss/resource_version_info_writer.h new file mode 100644 index 0000000000..da279ddedb --- /dev/null +++ b/tools/pe_bliss/resource_version_info_writer.h @@ -0,0 +1,52 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "version_info_types.h" +#include "file_version_info.h" + +namespace pe_bliss +{ +class pe_resource_manager; + +class resource_version_info_writer +{ +public: + resource_version_info_writer(pe_resource_manager& res); + + //Sets/replaces full version information: + //file_version_info: versions and file info + //lang_string_values_map: map of version info strings with encodings + //translation_values_map: map of translations + void set_version_info(const file_version_info& file_info, + const lang_string_values_map& string_values, + const translation_values_map& translations, + uint32_t language, + uint32_t codepage = 0, + uint32_t timestamp = 0); + + //Removes version info by language (ID = 1) + bool remove_version_info(uint32_t language); + +private: + pe_resource_manager& res_; +}; +} diff --git a/tools/pe_bliss/stdint_defs.h b/tools/pe_bliss/stdint_defs.h new file mode 100644 index 0000000000..bbc003690a --- /dev/null +++ b/tools/pe_bliss/stdint_defs.h @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#ifdef _MSC_VER +#if _MSC_VER < 1600 +namespace pe_bliss +{ + //stdint.h definitions for MSVC 2008 and earlier, as + //it doesn't have them + typedef signed char int8_t; + typedef short int16_t; + typedef int int32_t; + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + + typedef long long int64_t; + typedef unsigned long long uint64_t; +} +#else +#include <stdint.h> +#endif +#else +#include <stdint.h> +#endif diff --git a/tools/pe_bliss/utils.cpp b/tools/pe_bliss/utils.cpp new file mode 100644 index 0000000000..e6a75d5497 --- /dev/null +++ b/tools/pe_bliss/utils.cpp @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <string.h> +#include "utils.h" +#include "pe_exception.h" + + +namespace pe_bliss +{ +const double pe_utils::log_2 = 1.44269504088896340736; //instead of using M_LOG2E + +//Returns stream size +std::streamoff pe_utils::get_file_size(std::istream& file) +{ + //Get old istream offset + std::streamoff old_offset = file.tellg(); + file.seekg(0, std::ios::end); + std::streamoff filesize = file.tellg(); + //Set old istream offset + file.seekg(old_offset); + return filesize; +} + +#ifndef PE_BLISS_WINDOWS +const u16string pe_utils::to_ucs2(const std::wstring& str) +{ + u16string ret; + if(str.empty()) + return ret; + + int len = str.length(); + + ret.resize(len); + + for(int i=0;i<len;i++) { + ret[i]=str[i]&0xFFFF; + } + + return ret; +} + +const std::wstring pe_utils::from_ucs2(const u16string& str) +{ + std::wstring ret; + if(str.empty()) + return ret; + + int len = str.length(); + ret.resize(str.length()); + + for(int i=0;i<len;i++) { + ret[i]=str[i]; + } + + return ret; +} +#endif + +bool operator==(const pe_win::guid& guid1, const pe_win::guid& guid2) +{ + return guid1.Data1 == guid2.Data1 + && guid1.Data2 == guid2.Data2 + && guid1.Data3 == guid2.Data3 + && !memcmp(guid1.Data4, guid2.Data4, sizeof(guid1.Data4)); +} +} diff --git a/tools/pe_bliss/utils.h b/tools/pe_bliss/utils.h new file mode 100644 index 0000000000..29125f8dc1 --- /dev/null +++ b/tools/pe_bliss/utils.h @@ -0,0 +1,105 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <istream> +#include <string> +#include "stdint_defs.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +class pe_utils +{ +public: + //Returns true if string "data" with maximum length "raw_length" is null-terminated + template<typename T> + static bool is_null_terminated(const T* data, size_t raw_length) + { + raw_length /= sizeof(T); + for(size_t l = 0; l < raw_length; l++) + { + if(data[l] == static_cast<T>(L'\0')) + return true; + } + + return false; + } + + //Helper template function to strip nullbytes in the end of string + template<typename T> + static void strip_nullbytes(std::basic_string<T>& str) + { + while(!*(str.end() - 1) && !str.empty()) + str.erase(str.length() - 1); + } + + //Helper function to determine if number is power of 2 + template<typename T> + static inline bool is_power_of_2(T x) + { + return !(x & (x - 1)); + } + + //Helper function to align number down + template<typename T> + static inline T align_down(T x, uint32_t align) + { + return x & ~(static_cast<T>(align) - 1); + } + + //Helper function to align number up + template<typename T> + static inline T align_up(T x, uint32_t align) + { + return (x & static_cast<T>(align - 1)) ? align_down(x, align) + static_cast<T>(align) : x; + } + + //Returns true if sum of two unsigned integers is safe (no overflow occurs) + static inline bool is_sum_safe(uint32_t a, uint32_t b) + { + return a <= static_cast<uint32_t>(-1) - b; + } + + //Two gigabytes value in bytes + static const uint32_t two_gb = 0x80000000; + static const uint32_t max_dword = 0xFFFFFFFF; + static const uint32_t max_word = 0x0000FFFF; + static const double log_2; //instead of using M_LOG2E + + //Returns stream size + static std::streamoff get_file_size(std::istream& file); + +#ifndef PE_BLISS_WINDOWS +public: + static const u16string to_ucs2(const std::wstring& str); + static const std::wstring from_ucs2(const u16string& str); +#endif + +private: + pe_utils(); + pe_utils(pe_utils&); + pe_utils& operator=(const pe_utils&); +}; + +//Windows GUID comparison +bool operator==(const pe_win::guid& guid1, const pe_win::guid& guid2); +} diff --git a/tools/pe_bliss/version_info_editor.cpp b/tools/pe_bliss/version_info_editor.cpp new file mode 100644 index 0000000000..199eebfd54 --- /dev/null +++ b/tools/pe_bliss/version_info_editor.cpp @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <sstream> +#include <iomanip> +#include "version_info_types.h" +#include "version_info_editor.h" +#include "version_info_viewer.h" + +namespace pe_bliss +{ +//Default constructor +//strings - version info strings with charsets +//translations - version info translations map +version_info_editor::version_info_editor(lang_string_values_map& strings, translation_values_map& translations) + :version_info_viewer(strings, translations), + strings_edit_(strings), + translations_edit_(translations) +{} + +//Below functions have parameter translation +//If it's empty, the default language translation will be taken +//If there's no default language translation, the first one will be taken + +//Sets company name +void version_info_editor::set_company_name(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"CompanyName", value, translation); +} + +//Sets file description +void version_info_editor::set_file_description(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"FileDescription", value, translation); +} + +//Sets file version +void version_info_editor::set_file_version(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"FileVersion", value, translation); +} + +//Sets internal file name +void version_info_editor::set_internal_name(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"InternalName", value, translation); +} + +//Sets legal copyright +void version_info_editor::set_legal_copyright(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"LegalCopyright", value, translation); +} + +//Sets original file name +void version_info_editor::set_original_filename(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"OriginalFilename", value, translation); +} + +//Sets product name +void version_info_editor::set_product_name(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"ProductName", value, translation); +} + +//Sets product version +void version_info_editor::set_product_version(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"ProductVersion", value, translation); +} + +//Sets version info property value +//property_name - property name +//value - property value +//If translation does not exist, it will be added +//If property does not exist, it will be added +void version_info_editor::set_property(const std::wstring& property_name, const std::wstring& value, const std::wstring& translation) +{ + lang_string_values_map::iterator it = strings_edit_.begin(); + + if(translation.empty()) + { + //If no translation was specified + it = strings_edit_.find(default_language_translation); //Find default translation table + if(it == strings_edit_.end()) //If there's no default translation table, take the first one + { + it = strings_edit_.begin(); + if(it == strings_edit_.end()) //If there's no any translation table, add default one + { + it = strings_edit_.insert(std::make_pair(default_language_translation, string_values_map())).first; + //Also add it to translations list + add_translation(default_language_translation); + } + } + } + else + { + it = strings_edit_.find(translation); //Find specified translation table + if(it == strings_edit_.end()) //If there's no translation, add it + { + it = strings_edit_.insert(std::make_pair(translation, string_values_map())).first; + //Also add it to translations list + add_translation(translation); + } + } + + //Change value of the required property + ((*it).second)[property_name] = value; +} + +//Adds translation to translation list +void version_info_editor::add_translation(const std::wstring& translation) +{ + std::pair<uint16_t, uint16_t> translation_ids(translation_from_string(translation)); + add_translation(translation_ids.first, translation_ids.second); +} + +void version_info_editor::add_translation(uint16_t language_id, uint16_t codepage_id) +{ + std::pair<translation_values_map::const_iterator, translation_values_map::const_iterator> + range(translations_edit_.equal_range(language_id)); + + //If translation already exists + for(translation_values_map::const_iterator it = range.first; it != range.second; ++it) + { + if((*it).second == codepage_id) + return; + } + + translations_edit_.insert(std::make_pair(language_id, codepage_id)); +} + +//Removes translation from translations and strings lists +void version_info_editor::remove_translation(const std::wstring& translation) +{ + std::pair<uint16_t, uint16_t> translation_ids(translation_from_string(translation)); + remove_translation(translation_ids.first, translation_ids.second); +} + +void version_info_editor::remove_translation(uint16_t language_id, uint16_t codepage_id) +{ + { + //Erase string table (if exists) + std::wstringstream ss; + ss << std::hex + << std::setw(4) << std::setfill(L'0') << language_id + << std::setw(4) << std::setfill(L'0') << codepage_id; + + strings_edit_.erase(ss.str()); + } + + //Find and erase translation from translations table + std::pair<translation_values_map::iterator, translation_values_map::iterator> + it_pair = translations_edit_.equal_range(language_id); + + for(translation_values_map::iterator it = it_pair.first; it != it_pair.second; ++it) + { + if((*it).second == codepage_id) + { + translations_edit_.erase(it); + break; + } + } +} +} diff --git a/tools/pe_bliss/version_info_editor.h b/tools/pe_bliss/version_info_editor.h new file mode 100644 index 0000000000..53d3dc62c1 --- /dev/null +++ b/tools/pe_bliss/version_info_editor.h @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include "version_info_types.h" +#include "version_info_viewer.h" + +namespace pe_bliss +{ + //Helper class to read and edit version information + //lang_string_values_map: map of version info strings with encodings + //translation_values_map: map of translations + class version_info_editor : public version_info_viewer + { + public: + //Default constructor + //strings - version info strings with charsets + //translations - version info translations map + version_info_editor(lang_string_values_map& strings, translation_values_map& translations); + + //Below functions have parameter translation + //If it's empty, the default language translation will be taken + //If there's no default language translation, the first one will be taken + + //Sets company name + void set_company_name(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets file description + void set_file_description(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets file version + void set_file_version(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets internal file name + void set_internal_name(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets legal copyright + void set_legal_copyright(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets original file name + void set_original_filename(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets product name + void set_product_name(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets product version + void set_product_version(const std::wstring& value, const std::wstring& translation = std::wstring()); + + //Sets version info property value + //property_name - property name + //value - property value + //If translation does not exist, it will be added to strings and translations lists + //If property does not exist, it will be added + void set_property(const std::wstring& property_name, const std::wstring& value, const std::wstring& translation = std::wstring()); + + //Adds translation to translation list + void add_translation(const std::wstring& translation); + void add_translation(uint16_t language_id, uint16_t codepage_id); + + //Removes translation from translations and strings lists + void remove_translation(const std::wstring& translation); + void remove_translation(uint16_t language_id, uint16_t codepage_id); + + private: + lang_string_values_map& strings_edit_; + translation_values_map& translations_edit_; + }; +} diff --git a/tools/pe_bliss/version_info_types.h b/tools/pe_bliss/version_info_types.h new file mode 100644 index 0000000000..6010c9691e --- /dev/null +++ b/tools/pe_bliss/version_info_types.h @@ -0,0 +1,38 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <map> +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ + //Typedef for version info functions: Name - Value + typedef std::map<std::wstring, std::wstring> string_values_map; + //Typedef for version info functions: Language string - String Values Map + //Language String consists of LangID and CharsetID + //E.g. 041904b0 for Russian UNICODE, 040004b0 for Process Default Language UNICODE + typedef std::map<std::wstring, string_values_map> lang_string_values_map; + + //Typedef for version info functions: Language - Character Set + typedef std::multimap<uint16_t, uint16_t> translation_values_map; +} diff --git a/tools/pe_bliss/version_info_viewer.cpp b/tools/pe_bliss/version_info_viewer.cpp new file mode 100644 index 0000000000..6e2d0d5c5b --- /dev/null +++ b/tools/pe_bliss/version_info_viewer.cpp @@ -0,0 +1,180 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include <iomanip> +#include <sstream> +#include "pe_exception.h" +#include "version_info_viewer.h" + +namespace pe_bliss +{ +//Default process language, UNICODE +const std::wstring version_info_viewer::default_language_translation(L"040904b0"); + +//Default constructor +//strings - version info strings with charsets +//translations - version info translations map +version_info_viewer::version_info_viewer(const lang_string_values_map& strings, const translation_values_map& translations) + :strings_(strings), translations_(translations) +{} + +//Below functions have parameter translation +//If it's empty, the default language translation will be taken +//If there's no default language translation, the first one will be taken + +//Returns company name +const std::wstring version_info_viewer::get_company_name(const std::wstring& translation) const +{ + return get_property(L"CompanyName", translation); +} + +//Returns file description +const std::wstring version_info_viewer::get_file_description(const std::wstring& translation) const +{ + return get_property(L"FileDescription", translation); +} + +//Returns file version +const std::wstring version_info_viewer::get_file_version(const std::wstring& translation) const +{ + return get_property(L"FileVersion", translation); +} + +//Returns internal file name +const std::wstring version_info_viewer::get_internal_name(const std::wstring& translation) const +{ + return get_property(L"InternalName", translation); +} + +//Returns legal copyright +const std::wstring version_info_viewer::get_legal_copyright(const std::wstring& translation) const +{ + return get_property(L"LegalCopyright", translation); +} + +//Returns original file name +const std::wstring version_info_viewer::get_original_filename(const std::wstring& translation) const +{ + return get_property(L"OriginalFilename", translation); +} + +//Returns product name +const std::wstring version_info_viewer::get_product_name(const std::wstring& translation) const +{ + return get_property(L"ProductName", translation); +} + +//Returns product version +const std::wstring version_info_viewer::get_product_version(const std::wstring& translation) const +{ + return get_property(L"ProductVersion", translation); +} + +//Returns list of translations in string representation +const version_info_viewer::translation_list version_info_viewer::get_translation_list() const +{ + translation_list ret; + + //Enumerate all translations + for(translation_values_map::const_iterator it = translations_.begin(); it != translations_.end(); ++it) + { + //Create string representation of translation value + std::wstringstream ss; + ss << std::hex + << std::setw(4) << std::setfill(L'0') << (*it).first + << std::setw(4) << std::setfill(L'0') << (*it).second; + + //Save it + ret.push_back(ss.str()); + } + + return ret; +} + +//Returns version info property value +//property_name - required property name +//If throw_if_absent = true, will throw exception if property does not exist +//If throw_if_absent = false, will return empty string if property does not exist +const std::wstring version_info_viewer::get_property(const std::wstring& property_name, const std::wstring& translation, bool throw_if_absent) const +{ + std::wstring ret; + + //If there're no strings + if(strings_.empty()) + { + if(throw_if_absent) + throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); + + return ret; + } + + lang_string_values_map::const_iterator it = strings_.begin(); + + if(translation.empty()) + { + //If no translation was specified + it = strings_.find(default_language_translation); //Find default translation table + if(it == strings_.end()) //If there's no default translation table, take the first one + it = strings_.begin(); + } + else + { + it = strings_.find(translation); //Find specified translation table + if(it == strings_.end()) + { + if(throw_if_absent) + throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); + + return ret; + } + } + + //Find value of the required property + string_values_map::const_iterator str_it = (*it).second.find(property_name); + + if(str_it == (*it).second.end()) + { + if(throw_if_absent) + throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); + + return ret; + } + + ret = (*str_it).second; + + return ret; +} + +//Converts translation HEX-string to pair of language ID and codepage ID +const version_info_viewer::translation_pair version_info_viewer::translation_from_string(const std::wstring& translation) +{ + uint32_t translation_id = 0; + + { + //Convert string to DWORD + std::wstringstream ss; + ss << std::hex << translation; + ss >> translation_id; + } + + return std::make_pair(static_cast<uint16_t>(translation_id >> 16), static_cast<uint16_t>(translation_id & 0xFFFF)); +} +} diff --git a/tools/pe_bliss/version_info_viewer.h b/tools/pe_bliss/version_info_viewer.h new file mode 100644 index 0000000000..bc2f6f2ba7 --- /dev/null +++ b/tools/pe_bliss/version_info_viewer.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* Copyright (c) 2015 dx, http://kaimi.ru */ +/* */ +/* Permission is hereby granted, free of charge, to any person */ +/* obtaining a copy of this software and associated documentation */ +/* files (the "Software"), to deal in the Software without */ +/* restriction, including without limitation the rights to use, */ +/* copy, modify, merge, publish, distribute, sublicense, and/or */ +/* sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#pragma once +#include <map> +#include <vector> +#include <string> +#include "pe_resource_viewer.h" +#include "pe_structures.h" +#include "version_info_types.h" + +namespace pe_bliss +{ +//Helper class to read version information +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +class version_info_viewer +{ +public: + //Useful typedefs + typedef std::pair<uint16_t, uint16_t> translation_pair; + typedef std::vector<std::wstring> translation_list; + +public: + //Default constructor + //strings - version info strings with charsets + //translations - version info translations map + version_info_viewer(const lang_string_values_map& strings, const translation_values_map& translations); + + //Below functions have parameter translation + //If it's empty, the default language translation will be taken + //If there's no default language translation, the first one will be taken + + //Returns company name + const std::wstring get_company_name(const std::wstring& translation = std::wstring()) const; + //Returns file description + const std::wstring get_file_description(const std::wstring& translation = std::wstring()) const; + //Returns file version + const std::wstring get_file_version(const std::wstring& translation = std::wstring()) const; + //Returns internal file name + const std::wstring get_internal_name(const std::wstring& translation = std::wstring()) const; + //Returns legal copyright + const std::wstring get_legal_copyright(const std::wstring& translation = std::wstring()) const; + //Returns original file name + const std::wstring get_original_filename(const std::wstring& translation = std::wstring()) const; + //Returns product name + const std::wstring get_product_name(const std::wstring& translation = std::wstring()) const; + //Returns product version + const std::wstring get_product_version(const std::wstring& translation = std::wstring()) const; + + //Returns list of translations in string representation + const translation_list get_translation_list() const; + + //Returns version info property value + //property_name - required property name + //If throw_if_absent = true, will throw exception if property does not exist + //If throw_if_absent = false, will return empty string if property does not exist + const std::wstring get_property(const std::wstring& property_name, const std::wstring& translation = std::wstring(), bool throw_if_absent = false) const; + + //Converts translation HEX-string to pair of language ID and codepage ID + static const translation_pair translation_from_string(const std::wstring& translation); + +public: + //Default process language, UNICODE + static const std::wstring default_language_translation; + +private: + const lang_string_values_map& strings_; + const translation_values_map& translations_; +}; +} diff --git a/tools/scripts/file-hex-array.py b/tools/scripts/file-hex-array.py new file mode 100755 index 0000000000..05352396f1 --- /dev/null +++ b/tools/scripts/file-hex-array.py @@ -0,0 +1,52 @@ +import binascii +import os.path +import sys + +def tof(filepath): + with open(filepath, 'r') as f: + content = f.read() + content = content.replace("0x","") + content = content.split(',') + for i in range(len(content)): + if len(content[i]) == 1: content[i] = "0" + content[i] + content = "".join(content) + with open(filepath+".file", 'wb') as f: + content = f.write(content.decode("hex")) + print(os.path.basename(filepath)+".file created.") + exit(0) + +def toa(filepath): + with open(filepath, 'rb') as f: + content = f.read() + content = binascii.hexlify(content) + content = [content[i:i+2] for i in range(0, len(content), 2)] + content = ",0x".join(content) + content = "0x" + content + content = content.replace("0x00","0x0") + with open(filepath+".array", 'w') as f: + content = f.write(content) + print(os.path.basename(filepath)+".array created.") + exit(0) + +def usage(): + print("========================================================\n\ +#\n\ +# Usage: python file-hex-array.py [action] [option]\n\ +#\n\ +# Arguments:\n\ +# action ==> toa # convert file to array [option is file path]\n\ +# tof # convert array to file [option is array file path]\n\ +#\n\ +# Example : python file-hex-array.py toa 1.png\n\ +#\n\ +========================================================") + exit(1) + +if len(sys.argv) != 3: + usage() +if sys.argv[1] == "toa" and os.path.isfile(sys.argv[2]): + toa(sys.argv[2]) +elif sys.argv[1] == "tof" and os.path.isfile(sys.argv[2]): + tof(sys.argv[2]) +else: + usage()
\ No newline at end of file |