#include #include #include #include #include #include #include #include #include #include namespace hex::plugin::builtin { void drawFileToolCombiner() { static std::vector files; static std::u8string outputPath; static u32 selectedIndex; static TaskHolder combinerTask; if (ImGui::BeginTable("files_table", 2, ImGuiTableFlags_SizingStretchProp)) { ImGui::TableSetupColumn("file list", ImGuiTableColumnFlags_NoHeaderLabel, 10); ImGui::TableSetupColumn("buttons", ImGuiTableColumnFlags_NoHeaderLabel, 1); ImGui::TableNextRow(); ImGui::TableNextColumn(); if (ImGui::BeginListBox("##files", { -FLT_MIN, 10 * ImGui::GetTextLineHeightWithSpacing() })) { u32 index = 0; for (auto &file : files) { if (ImGui::Selectable(wolv::util::toUTF8String(file).c_str(), index == selectedIndex)) selectedIndex = index; index++; } ImGui::EndListBox(); } ImGui::TableNextColumn(); ImGui::BeginDisabled(selectedIndex <= 0); { if (ImGui::ArrowButton("move_up", ImGuiDir_Up)) { std::iter_swap(files.begin() + selectedIndex, files.begin() + selectedIndex - 1); selectedIndex--; } } ImGui::EndDisabled(); ImGui::BeginDisabled(files.empty() || selectedIndex >= files.size() - 1); { if (ImGui::ArrowButton("move_down", ImGuiDir_Down)) { std::iter_swap(files.begin() + selectedIndex, files.begin() + selectedIndex + 1); selectedIndex++; } } ImGui::EndDisabled(); ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::BeginDisabled(combinerTask.isRunning()); { if (ImGui::Button("hex.builtin.tools.file_tools.combiner.add"_lang)) { fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) { files.push_back(path); }, "", true); } ImGui::SameLine(); ImGui::BeginDisabled(files.empty() || selectedIndex >= files.size()); if (ImGui::Button("hex.builtin.tools.file_tools.combiner.delete"_lang)) { files.erase(files.begin() + selectedIndex); if (selectedIndex > 0) selectedIndex--; } ImGui::EndDisabled(); ImGui::SameLine(); ImGui::BeginDisabled(files.empty()); if (ImGui::Button("hex.builtin.tools.file_tools.combiner.clear"_lang)) { files.clear(); } ImGui::EndDisabled(); } ImGui::EndDisabled(); ImGui::EndTable(); } ImGui::BeginDisabled(combinerTask.isRunning()); { ImGui::InputText("##output_path", outputPath); ImGui::SameLine(); if (ImGui::Button("...")) { fs::openFileBrowser(fs::DialogMode::Save, {}, [](const auto &path) { outputPath = path.u8string(); }); } ImGui::SameLine(); ImGui::TextUnformatted("hex.builtin.tools.file_tools.combiner.output"_lang); } ImGui::EndDisabled(); ImGui::BeginDisabled(files.empty() || outputPath.empty()); { if (combinerTask.isRunning()) ImGuiExt::TextSpinner("hex.builtin.tools.file_tools.combiner.combining"_lang); else { if (ImGui::Button("hex.builtin.tools.file_tools.combiner.combine"_lang)) { combinerTask = TaskManager::createTask("hex.builtin.tools.file_tools.combiner.combining", 0, [](auto &task) { wolv::io::File output(outputPath, wolv::io::File::Mode::Create); if (!output.isValid()) { ui::PopupError::open("hex.builtin.tools.file_tools.combiner.error.open_output"_lang); return; } task.setMaxValue(files.size()); u64 fileIndex = 0; for (const auto &file : files) { task.update(fileIndex); fileIndex++; wolv::io::File input(file, wolv::io::File::Mode::Read); if (!input.isValid()) { ui::PopupError::open(hex::format("hex.builtin.tools.file_tools.combiner.open_input"_lang, wolv::util::toUTF8String(file))); return; } constexpr static auto BufferSize = 0xFF'FFFF; auto inputSize = input.getSize(); for (u64 inputOffset = 0; inputOffset < inputSize; inputOffset += BufferSize) { output.writeVector(input.readVector(std::min(BufferSize, inputSize - inputOffset))); output.flush(); } } files.clear(); selectedIndex = 0; outputPath.clear(); ui::PopupInfo::open("hex.builtin.tools.file_tools.combiner.success"_lang); }); } } } ImGui::EndDisabled(); } }