//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;
        if (read_size >= reloc_size)
            break;

        reloc_table = pe.section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true);
    }

    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;
}
//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(pe_utils::align_up(needed_size + current_reloc_data_pos, pe.get_file_alignment())); //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;
}