summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Godot.app/Contents/Resources/Godot.icnsbin294521 -> 260598 bytes
-rw-r--r--tools/SCsub3
-rw-r--r--tools/collada/SCsub2
-rw-r--r--tools/doc/SCsub2
-rw-r--r--tools/doc/doc_data.cpp2
-rw-r--r--tools/docdump/SCsub2
-rw-r--r--tools/docdump/makehtml.py19
-rw-r--r--tools/editor/SCsub11
-rw-r--r--tools/editor/animation_editor.cpp57
-rw-r--r--tools/editor/animation_editor.h8
-rw-r--r--tools/editor/code_editor.cpp4
-rw-r--r--tools/editor/editor_help.cpp613
-rw-r--r--tools/editor/editor_help.h91
-rw-r--r--tools/editor/editor_import_export.cpp87
-rw-r--r--tools/editor/editor_import_export.h19
-rw-r--r--tools/editor/editor_log.cpp13
-rw-r--r--tools/editor/editor_log.h4
-rw-r--r--tools/editor/editor_node.cpp270
-rw-r--r--tools/editor/editor_node.h6
-rw-r--r--tools/editor/editor_settings.cpp2
-rw-r--r--tools/editor/fileserver/SCsub4
-rw-r--r--tools/editor/icons/SCsub19
-rw-r--r--tools/editor/icons/icon_back.pngbin206 -> 180 bytes
-rw-r--r--tools/editor/icons/icon_class_list.pngbin0 -> 241 bytes
-rw-r--r--tools/editor/icons/icon_forward.pngbin199 -> 191 bytes
-rw-r--r--tools/editor/icons/icon_godot.pngbin0 -> 671 bytes
-rw-r--r--tools/editor/icons/icon_help.pngbin822 -> 841 bytes
-rw-r--r--tools/editor/icons/icon_multi_edit.pngbin0 -> 576 bytes
-rw-r--r--tools/editor/io_plugins/SCsub4
-rw-r--r--tools/editor/io_plugins/editor_atlas.cpp12
-rw-r--r--tools/editor/io_plugins/editor_sample_import_plugin.cpp93
-rw-r--r--tools/editor/io_plugins/editor_sample_import_plugin.h14
-rw-r--r--tools/editor/io_plugins/editor_scene_import_plugin.cpp4
-rw-r--r--tools/editor/io_plugins/editor_texture_import_plugin.h1
-rw-r--r--tools/editor/plugins/SCsub4
-rw-r--r--tools/editor/plugins/animation_player_editor_plugin.cpp171
-rw-r--r--tools/editor/plugins/animation_player_editor_plugin.h22
-rw-r--r--tools/editor/plugins/canvas_item_editor_plugin.cpp62
-rw-r--r--tools/editor/plugins/collision_polygon_editor_plugin.cpp5
-rw-r--r--tools/editor/plugins/collision_shape_2d_editor_plugin.cpp33
-rw-r--r--tools/editor/plugins/editor_preview_plugins.cpp8
-rw-r--r--tools/editor/plugins/mesh_editor_plugin.cpp15
-rw-r--r--tools/editor/plugins/multimesh_editor_plugin.cpp20
-rw-r--r--tools/editor/plugins/multimesh_editor_plugin.h8
-rw-r--r--tools/editor/plugins/polygon_2d_editor_plugin.cpp11
-rw-r--r--tools/editor/plugins/sample_editor_plugin.cpp8
-rw-r--r--tools/editor/plugins/sample_library_editor_plugin.cpp4
-rw-r--r--tools/editor/plugins/script_editor_plugin.cpp1370
-rw-r--r--tools/editor/plugins/script_editor_plugin.h49
-rw-r--r--tools/editor/plugins/shader_graph_editor_plugin.cpp2
-rw-r--r--tools/editor/plugins/spatial_editor_plugin.cpp52
-rw-r--r--tools/editor/plugins/theme_editor_plugin.cpp38
-rw-r--r--tools/editor/plugins/tile_set_editor_plugin.cpp12
-rw-r--r--tools/editor/project_export.cpp35
-rw-r--r--tools/editor/project_export.h6
-rw-r--r--tools/editor/project_manager.cpp37
-rw-r--r--tools/editor/property_editor.cpp1
-rw-r--r--tools/editor/scene_tree_dock.cpp9
-rw-r--r--tools/editor/scene_tree_editor.cpp16
-rw-r--r--tools/editor/scenes_dock.cpp28
-rw-r--r--tools/editor/script_editor_debugger.cpp1
-rw-r--r--tools/editor/spatial_editor_gizmos.cpp2
-rw-r--r--tools/export/blender25/godot_export_manager.py4
-rw-r--r--tools/freetype/SCsub4
-rw-r--r--tools/osx_template.app/Contents/Resources/icon.icnsbin294521 -> 260598 bytes
-rw-r--r--tools/pck/SCsub1
-rw-r--r--tools/pe_bliss/README84
-rw-r--r--tools/pe_bliss/SCsub5
-rw-r--r--tools/pe_bliss/entropy.cpp111
-rw-r--r--tools/pe_bliss/entropy.h51
-rw-r--r--tools/pe_bliss/file_version_info.cpp440
-rw-r--r--tools/pe_bliss/file_version_info.h199
-rw-r--r--tools/pe_bliss/message_table.cpp81
-rw-r--r--tools/pe_bliss/message_table.h56
-rw-r--r--tools/pe_bliss/pe_base.cpp1680
-rw-r--r--tools/pe_bliss/pe_base.h544
-rw-r--r--tools/pe_bliss/pe_bliss.h39
-rw-r--r--tools/pe_bliss/pe_bliss_godot.cpp118
-rw-r--r--tools/pe_bliss/pe_bliss_godot.h7
-rw-r--r--tools/pe_bliss/pe_bliss_resources.h36
-rw-r--r--tools/pe_bliss/pe_bound_import.cpp311
-rw-r--r--tools/pe_bliss/pe_bound_import.h108
-rw-r--r--tools/pe_bliss/pe_checksum.cpp103
-rw-r--r--tools/pe_bliss/pe_checksum.h30
-rw-r--r--tools/pe_bliss/pe_debug.cpp865
-rw-r--r--tools/pe_bliss/pe_debug.h324
-rw-r--r--tools/pe_bliss/pe_directory.cpp59
-rw-r--r--tools/pe_bliss/pe_directory.h50
-rw-r--r--tools/pe_bliss/pe_dotnet.cpp186
-rw-r--r--tools/pe_bliss/pe_dotnet.h97
-rw-r--r--tools/pe_bliss/pe_exception.cpp40
-rw-r--r--tools/pe_bliss/pe_exception.h130
-rw-r--r--tools/pe_bliss/pe_exception_directory.cpp177
-rw-r--r--tools/pe_bliss/pe_exception_directory.h88
-rw-r--r--tools/pe_bliss/pe_exports.cpp700
-rw-r--r--tools/pe_bliss/pe_exports.h184
-rw-r--r--tools/pe_bliss/pe_factory.cpp43
-rw-r--r--tools/pe_bliss/pe_factory.h39
-rw-r--r--tools/pe_bliss/pe_imports.cpp777
-rw-r--r--tools/pe_bliss/pe_imports.h208
-rw-r--r--tools/pe_bliss/pe_load_config.cpp557
-rw-r--r--tools/pe_bliss/pe_load_config.h184
-rw-r--r--tools/pe_bliss/pe_properties.cpp41
-rw-r--r--tools/pe_bliss/pe_properties.h236
-rw-r--r--tools/pe_bliss/pe_properties_generic.cpp645
-rw-r--r--tools/pe_bliss/pe_properties_generic.h277
-rw-r--r--tools/pe_bliss/pe_rebuilder.cpp214
-rw-r--r--tools/pe_bliss/pe_rebuilder.h40
-rw-r--r--tools/pe_bliss/pe_relocations.cpp320
-rw-r--r--tools/pe_bliss/pe_relocations.h122
-rw-r--r--tools/pe_bliss/pe_resource_manager.cpp286
-rw-r--r--tools/pe_bliss/pe_resource_manager.h113
-rw-r--r--tools/pe_bliss/pe_resource_viewer.cpp382
-rw-r--r--tools/pe_bliss/pe_resource_viewer.h153
-rw-r--r--tools/pe_bliss/pe_resources.cpp726
-rw-r--r--tools/pe_bliss/pe_resources.h245
-rw-r--r--tools/pe_bliss/pe_rich_data.cpp152
-rw-r--r--tools/pe_bliss/pe_rich_data.h58
-rw-r--r--tools/pe_bliss/pe_section.cpp303
-rw-r--r--tools/pe_bliss/pe_section.h158
-rw-r--r--tools/pe_bliss/pe_structures.h1028
-rw-r--r--tools/pe_bliss/pe_tls.cpp396
-rw-r--r--tools/pe_bliss/pe_tls.h122
-rw-r--r--tools/pe_bliss/resource_bitmap_reader.cpp86
-rw-r--r--tools/pe_bliss/resource_bitmap_reader.h50
-rw-r--r--tools/pe_bliss/resource_bitmap_writer.cpp75
-rw-r--r--tools/pe_bliss/resource_bitmap_writer.h47
-rw-r--r--tools/pe_bliss/resource_cursor_icon_reader.cpp521
-rw-r--r--tools/pe_bliss/resource_cursor_icon_reader.h84
-rw-r--r--tools/pe_bliss/resource_cursor_icon_writer.cpp447
-rw-r--r--tools/pe_bliss/resource_cursor_icon_writer.h94
-rw-r--r--tools/pe_bliss/resource_data_info.cpp48
-rw-r--r--tools/pe_bliss/resource_data_info.h48
-rw-r--r--tools/pe_bliss/resource_internal.h34
-rw-r--r--tools/pe_bliss/resource_message_list_reader.cpp131
-rw-r--r--tools/pe_bliss/resource_message_list_reader.h49
-rw-r--r--tools/pe_bliss/resource_string_table_reader.cpp109
-rw-r--r--tools/pe_bliss/resource_string_table_reader.h57
-rw-r--r--tools/pe_bliss/resource_version_info_reader.cpp311
-rw-r--r--tools/pe_bliss/resource_version_info_reader.h67
-rw-r--r--tools/pe_bliss/resource_version_info_writer.cpp283
-rw-r--r--tools/pe_bliss/resource_version_info_writer.h52
-rw-r--r--tools/pe_bliss/stdint_defs.h45
-rw-r--r--tools/pe_bliss/utils.cpp85
-rw-r--r--tools/pe_bliss/utils.h105
-rw-r--r--tools/pe_bliss/version_info_editor.cpp184
-rw-r--r--tools/pe_bliss/version_info_editor.h79
-rw-r--r--tools/pe_bliss/version_info_types.h38
-rw-r--r--tools/pe_bliss/version_info_viewer.cpp180
-rw-r--r--tools/pe_bliss/version_info_viewer.h89
-rwxr-xr-xtools/scripts/file-hex-array.py52
151 files changed, 20412 insertions, 1170 deletions
diff --git a/tools/Godot.app/Contents/Resources/Godot.icns b/tools/Godot.app/Contents/Resources/Godot.icns
index 18bc68d6ea..4a3dc0415a 100644
--- a/tools/Godot.app/Contents/Resources/Godot.icns
+++ b/tools/Godot.app/Contents/Resources/Godot.icns
Binary files differ
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
index d60e3b2640..f7e507d92b 100644
--- a/tools/editor/icons/icon_back.png
+++ b/tools/editor/icons/icon_back.png
Binary files differ
diff --git a/tools/editor/icons/icon_class_list.png b/tools/editor/icons/icon_class_list.png
new file mode 100644
index 0000000000..fb756c0fe1
--- /dev/null
+++ b/tools/editor/icons/icon_class_list.png
Binary files differ
diff --git a/tools/editor/icons/icon_forward.png b/tools/editor/icons/icon_forward.png
index ca6838ae9e..14e8bc9a5a 100644
--- a/tools/editor/icons/icon_forward.png
+++ b/tools/editor/icons/icon_forward.png
Binary files differ
diff --git a/tools/editor/icons/icon_godot.png b/tools/editor/icons/icon_godot.png
new file mode 100644
index 0000000000..e80820fc10
--- /dev/null
+++ b/tools/editor/icons/icon_godot.png
Binary files differ
diff --git a/tools/editor/icons/icon_help.png b/tools/editor/icons/icon_help.png
index 3f4f8453a7..d2085589ae 100644
--- a/tools/editor/icons/icon_help.png
+++ b/tools/editor/icons/icon_help.png
Binary files differ
diff --git a/tools/editor/icons/icon_multi_edit.png b/tools/editor/icons/icon_multi_edit.png
new file mode 100644
index 0000000000..70faee3d6a
--- /dev/null
+++ b/tools/editor/icons/icon_multi_edit.png
Binary files differ
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 5a871da9dd..e3f4edf967 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");
@@ -1476,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();
@@ -2408,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);
@@ -3305,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);
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 b39975dc84..7816efe89f 100644
--- a/tools/editor/plugins/spatial_editor_plugin.cpp
+++ b/tools/editor/plugins/spatial_editor_plugin.cpp
@@ -2064,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;
@@ -2860,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: {
@@ -3928,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");
@@ -4041,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/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
index 18bc68d6ea..4a3dc0415a 100644
--- a/tools/osx_template.app/Contents/Resources/icon.icns
+++ b/tools/osx_template.app/Contents/Resources/icon.icns
Binary files differ
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