mirror of
https://github.com/antopilo/Nuake.git
synced 2026-09-09 20:08:57 +03:00
304 lines
8.1 KiB
C++
304 lines
8.1 KiB
C++
#include "MapImporterWindow.h"
|
|
|
|
#include "../Misc/InterfaceFonts.h"
|
|
|
|
#include <Engine.h>
|
|
#include <Nuake/Core/Logger.h>
|
|
#include <Nuake/FileSystem/File.h>
|
|
#include <Nuake/Threading/JobSystem.h>
|
|
#include <Nuake/UI/ImUI.h>
|
|
#include <Nuake/FileSystem/FileDialog.h>
|
|
#include <Nuake/Core/String.h>
|
|
#include <Nuake/Resource/Project.h>
|
|
|
|
#include <imgui/imgui.h>
|
|
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <regex>
|
|
|
|
using namespace Nuake;
|
|
|
|
void MapImporterWindow::Draw()
|
|
{
|
|
auto& io = ImGui::GetIO();
|
|
ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(600, 400), ImVec2(1920, 1080));
|
|
if (ImGui::Begin("Map Importer"))
|
|
{
|
|
ImGui::PushFont(FontManager::GetFont(Bold));
|
|
ImGui::Text("What is this?");
|
|
ImGui::PopFont();
|
|
|
|
ImGui::TextWrapped("This imported will convert any Quake .map file into a .map that uses Nuake Material system.");
|
|
|
|
ImGui::PushFont(FontManager::GetFont(Bold));
|
|
ImGui::Text("1. Select a quake .map to import");
|
|
ImGui::PopFont();
|
|
|
|
std::string buttonLbl = "Empty";
|
|
|
|
if (m_MapToImport)
|
|
{
|
|
buttonLbl = m_MapToImport->GetRelativePath();
|
|
}
|
|
|
|
using namespace Nuake;
|
|
if (ImGui::Button(buttonLbl.c_str()))
|
|
{
|
|
std::string selectedFile = FileDialog::OpenFile("*.map");
|
|
std::string relativePath = FileSystem::AbsoluteToRelative(selectedFile);
|
|
if (!selectedFile.empty() && FileSystem::FileExists(relativePath))
|
|
{
|
|
if (Ref<File> file = FileSystem::GetFile(relativePath); file)
|
|
{
|
|
m_MapToImport = file;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ImGui::BeginDragDropTarget())
|
|
{
|
|
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("_Map"))
|
|
{
|
|
std::string fullPath = std::string(reinterpret_cast<char*>(payload->Data), 256);
|
|
std::string relPath = FileSystem::AbsoluteToRelative(fullPath);
|
|
if (FileSystem::FileExists(relPath))
|
|
{
|
|
if (Ref<File> file = FileSystem::GetFile(relPath); file)
|
|
{
|
|
m_MapToImport = file;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Logger::Log("Failed to import map file. File doesn't exists", "map importer", CRITICAL);
|
|
}
|
|
}
|
|
ImGui::EndDragDropTarget();
|
|
}
|
|
|
|
if (Nuake::UI::PrimaryButton("Scan WADs"))
|
|
{
|
|
m_MapToImport = nullptr;
|
|
}
|
|
|
|
//for (auto& w : ScanUsedWads())
|
|
//{
|
|
// auto pathSplits = String::Split(std::string(w.begin(), w.end() - 4), '/');
|
|
// std::string WadName = pathSplits[std::size(pathSplits) - 1];
|
|
// std::string TargetDirectory = "/Materials/" + WadName + "/";
|
|
//}
|
|
|
|
if (m_Working)
|
|
{
|
|
ImGui::BeginDisabled();
|
|
}
|
|
|
|
if (Nuake::UI::PrimaryButton("Export"))
|
|
{
|
|
if (m_OutputFile.empty())
|
|
{
|
|
std::string outputFile = FileDialog::SaveFile("*.map");
|
|
if (!String::EndsWith(outputFile, ".map"))
|
|
{
|
|
outputFile += ".map";
|
|
}
|
|
m_OutputFile = FileSystem::AbsoluteToRelative(outputFile);
|
|
}
|
|
|
|
m_Percentage = 0;
|
|
|
|
std::string mapContent = m_MapToImport->Read();
|
|
JobSystem::Get().Dispatch([this, mapContent]() {
|
|
m_Working = true;
|
|
std::string fileContent = mapContent;
|
|
std::istringstream stream(fileContent);
|
|
|
|
std::ostringstream modifiedMapContent;
|
|
|
|
std::string pattern = R"(\(\s*-?\d+(\.\d+)?\s*-?\d+(\.\d+)?\s*-?\d+(\.\d+)?\s*\)\s*\(\s*-?\d+(\.\d+)?\s*-?\d+(\.\d+)?\s*-?\d+(\.\d+)?\s*\)\s*\(\s*-?\d+(\.\d+)?\s*-?\d+(\.\d+)?\s*-?\d+(\.\d+)?\s*\)\s*([^\s]+))";
|
|
std::regex regexPattern(pattern);
|
|
|
|
std::string line;
|
|
|
|
int lines = std::count(fileContent.begin(), fileContent.end(), '\n');
|
|
|
|
int lineEdited = 0;
|
|
|
|
while (std::getline(stream, line))
|
|
{
|
|
if (String::BeginsWith(line, "// Game:"))
|
|
{
|
|
line = "// Game: " + String::RemoveWhiteSpace(Nuake::Engine::GetProject()->Name);
|
|
}
|
|
else
|
|
{
|
|
std::smatch match;
|
|
if (std::regex_search(line, match, regexPattern))
|
|
{
|
|
// If a match is found, return the captured value
|
|
std::string result = match[10].str();
|
|
std::string result2 = match[0].str();
|
|
result2.erase(result2.end() - result.length(), result2.end());
|
|
|
|
std::string transformWad = GetTransformedWadPath(result);
|
|
if (transformWad.empty())
|
|
{
|
|
Logger::Log("Couldn't find texture: " + result, "map importer", CRITICAL);
|
|
}
|
|
|
|
line = result2 + std::regex_replace(line, regexPattern, transformWad);
|
|
}
|
|
}
|
|
|
|
modifiedMapContent << line << std::endl;
|
|
lineEdited++;
|
|
|
|
int newPercentage = ((float)lineEdited / (float)lines) * 100;
|
|
if (newPercentage != m_Percentage)
|
|
{
|
|
m_Percentage.store(newPercentage, std::memory_order_relaxed);
|
|
}
|
|
}
|
|
|
|
FileSystem::BeginWriteFile(m_OutputFile);
|
|
FileSystem::WriteLine(modifiedMapContent.str());
|
|
FileSystem::EndWriteFile();
|
|
|
|
}, [this]() {
|
|
m_Working = false;
|
|
});
|
|
|
|
m_MapToImport = nullptr;
|
|
}
|
|
|
|
if (m_Working)
|
|
{
|
|
ImGui::EndDisabled();
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::TextUnformatted(m_OutputFile.c_str());
|
|
|
|
if (ImGui::IsItemClicked())
|
|
{
|
|
std::string outputFile = FileDialog::SaveFile("*.map");
|
|
if (!String::EndsWith(outputFile, ".map"))
|
|
{
|
|
outputFile += ".map";
|
|
}
|
|
|
|
m_OutputFile = FileSystem::AbsoluteToRelative(outputFile);
|
|
}
|
|
|
|
if (m_Working)
|
|
{
|
|
ImGui::SameLine();
|
|
|
|
ImGui::TextUnformatted((std::string("Progress: ") + std::to_string(m_Percentage) + "%").c_str());
|
|
}
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
std::vector<std::string> MapImporterWindow::ScanUsedWads()
|
|
{
|
|
if (!m_MapToImport)
|
|
{
|
|
return std::vector<std::string>();
|
|
}
|
|
|
|
m_DetectedWads.clear();
|
|
|
|
using namespace Nuake;
|
|
|
|
const std::string mapContent = m_MapToImport->Read();
|
|
|
|
std::vector<std::string> usedWads;
|
|
std::regex pattern("\"wad\"\\s*\"([^\"]+)\"");
|
|
std::smatch match;
|
|
|
|
// Search the input string using the regular expression
|
|
if (std::regex_search(mapContent, match, pattern))
|
|
{
|
|
const std::string fullString = match[1].str();
|
|
|
|
// Split multiple wads in case.
|
|
auto separatedWads = String::Split(fullString, ';');
|
|
for (auto& wad : separatedWads)
|
|
{
|
|
usedWads.push_back(wad);
|
|
}
|
|
}
|
|
|
|
// If no match is found, return an empty string
|
|
return usedWads;
|
|
}
|
|
|
|
std::string MapImporterWindow::GetTransformedWadPath(const std::string& path)
|
|
{
|
|
const std::string& baseTextureDir = "/Textures/";
|
|
|
|
using namespace Nuake;
|
|
if (m_WadToMaterialMap.find(path) != m_WadToMaterialMap.end())
|
|
{
|
|
const std::string resolvedPath = m_WadToMaterialMap[path];
|
|
Logger::Log("Map importer found matching material: " + resolvedPath, "map importer", VERBOSE);
|
|
return resolvedPath;
|
|
return resolvedPath;
|
|
}
|
|
|
|
std::regex backslash_pattern("\\\\");
|
|
for (const auto& entry : std::filesystem::recursive_directory_iterator(FileSystem::Root + baseTextureDir))
|
|
{
|
|
// Check if the current entry is a regular file and matches the file name
|
|
const std::string stem = String::ToLower(entry.path().stem().string());
|
|
std::string upperInput = String::ToLower(path);
|
|
|
|
upperInput = String::Split(upperInput, '/')[String::Split(upperInput, '/').size() - 1];
|
|
|
|
std::string prefix = "";
|
|
if (String::BeginsWith(upperInput, "*"))
|
|
{
|
|
prefix = "star_";
|
|
}
|
|
else if (String::BeginsWith(upperInput, "+"))
|
|
{
|
|
prefix = "plus_";
|
|
}
|
|
else if (String::BeginsWith(upperInput, "-"))
|
|
{
|
|
prefix = "minu_";
|
|
}
|
|
else if (String::BeginsWith(upperInput, "/"))
|
|
{
|
|
prefix = "divd_";
|
|
}
|
|
|
|
if (!prefix.empty())
|
|
{
|
|
upperInput = prefix + std::string(upperInput.begin() + 1, upperInput.end());
|
|
}
|
|
|
|
if (entry.is_regular_file() && stem == upperInput)
|
|
{
|
|
std::filesystem::path relativePath = std::filesystem::relative(entry.path(), FileSystem::Root + "/Textures/");
|
|
std::filesystem::path pathWithoutExtension = relativePath;
|
|
pathWithoutExtension = pathWithoutExtension.parent_path(); // Remove the file name
|
|
|
|
pathWithoutExtension /= relativePath.stem(); // Add the file name without extension
|
|
Logger::Log("Map importer found matching material: " + pathWithoutExtension.string(), "map importer", VERBOSE);
|
|
|
|
const std::string& foundMaterialPath = std::regex_replace(pathWithoutExtension.string(), backslash_pattern, "/");
|
|
m_WadToMaterialMap[path] = foundMaterialPath;
|
|
return foundMaterialPath;
|
|
}
|
|
}
|
|
|
|
Logger::Log("Map importer failed to find material: " + path, "map importer", VERBOSE);
|
|
return std::string();
|
|
}
|