EBLAN_ENGINE/editor/src/project_manager.h

178 lines
8.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <string>
#include <filesystem>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <windows.h>
namespace fs = std::filesystem;
struct EblanProject {
std::string title = "EBLAN_GAME";
std::string author = "Developer";
std::string version = "1.0.0";
std::string startupMap = "assets/map.dat";
std::string windowTitle = "EBLAN ENGINE 3D - Standalone Retro Game";
int defaultWidth = 1280;
int defaultHeight = 960;
bool Save(const std::string& path) const {
HANDLE hFile = CreateFileA(path.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE) return false;
char numBuf[32];
std::string content = "[Project]\r\n";
content += "Title=" + title + "\r\n";
content += "Author=" + author + "\r\n";
content += "Version=" + version + "\r\n";
content += "StartupMap=" + startupMap + "\r\n";
content += "WindowTitle=" + windowTitle + "\r\n";
snprintf(numBuf, sizeof(numBuf), "%d", defaultWidth);
content += std::string("DefaultWidth=") + numBuf + "\r\n";
snprintf(numBuf, sizeof(numBuf), "%d", defaultHeight);
content += std::string("DefaultHeight=") + numBuf + "\r\n";
DWORD written = 0;
WriteFile(hFile, content.data(), static_cast<DWORD>(content.size()), &written, nullptr);
CloseHandle(hFile);
return true;
}
bool Load(const std::string& path) {
HANDLE hFile = CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE) return false;
DWORD size = GetFileSize(hFile, nullptr);
if (size == INVALID_FILE_SIZE) { CloseHandle(hFile); return false; }
std::string buffer(size, '\0');
DWORD read = 0;
ReadFile(hFile, buffer.data(), size, &read, nullptr);
CloseHandle(hFile);
size_t start = 0;
while (start < buffer.size()) {
size_t end = buffer.find('\n', start);
std::string line = (end == std::string::npos) ? buffer.substr(start) : buffer.substr(start, end - start);
if (!line.empty() && line.back() == '\r') line.pop_back();
if (!line.empty() && line[0] != ';' && line[0] != '#') {
auto eq = line.find('=');
if (eq != std::string::npos) {
std::string key = line.substr(0, eq);
std::string val = line.substr(eq + 1);
if (key == "Title") title = val;
else if (key == "Author") author = val;
else if (key == "Version") version = val;
else if (key == "StartupMap") startupMap = val;
else if (key == "WindowTitle") windowTitle = val;
else if (key == "DefaultWidth") defaultWidth = std::atoi(val.c_str());
else if (key == "DefaultHeight") defaultHeight = std::atoi(val.c_str());
}
}
if (end == std::string::npos) break;
start = end + 1;
}
return true;
}
};
class StandaloneExporter {
public:
static bool ExportGame(const EblanProject& project, const std::string& destDir, const std::string& projectRoot, std::string& outMessage) {
std::error_code ec;
fs::path root(projectRoot);
fs::path dest(destDir);
if (!fs::exists(dest, ec)) {
fs::create_directories(dest, ec);
if (ec) {
outMessage = "Ошибка создания папки экспорта: " + ec.message();
return false;
}
}
// 1. Locate engine.exe
fs::path srcExe = root / "engine.exe";
if (!fs::exists(srcExe, ec)) {
srcExe = root.parent_path() / "engine.exe";
}
if (!fs::exists(srcExe, ec)) {
outMessage = "Не найден скомпилированный движок engine.exe. Сначала выполните компиляцию (F7).";
return false;
}
// 2. Sanitize executable name
std::string safeName = project.title;
for (char& c : safeName) {
if (c == ' ' || c == '\\' || c == '/' || c == ':' || c == '*' || c == '?' || c == '"' || c == '<' || c == '>' || c == '|') {
c = '_';
}
}
if (safeName.empty()) safeName = "Game";
fs::path targetExe = dest / (safeName + ".exe");
if (!CopyFileA(srcExe.string().c_str(), targetExe.string().c_str(), FALSE)) {
outMessage = "Ошибка копирования исполняемого файла игры.";
return false;
}
// 3. Copy assets directory
fs::path srcAssets = root / "assets";
if (!fs::exists(srcAssets, ec)) {
srcAssets = root.parent_path() / "assets";
}
if (fs::exists(srcAssets, ec)) {
fs::path targetAssets = dest / "assets";
fs::create_directories(targetAssets, ec);
for (const auto& entry : fs::recursive_directory_iterator(srcAssets, ec)) {
if (ec) break;
auto rel = fs::relative(entry.path(), srcAssets, ec);
fs::path targetPath = targetAssets / rel;
if (entry.is_directory()) {
fs::create_directories(targetPath, ec);
} else if (entry.is_regular_file()) {
fs::create_directories(targetPath.parent_path(), ec);
CopyFileA(entry.path().string().c_str(), targetPath.string().c_str(), FALSE);
}
}
}
// 4. Create launcher script PlayGame.bat
fs::path launcherPath = dest / "PlayGame.bat";
HANDLE hBat = CreateFileA(launcherPath.string().c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hBat != INVALID_HANDLE_VALUE) {
std::string batContent = "@echo off\r\ntitle " + safeName + "\r\ncd /d \"%~dp0\"\r\nstart \"\" \"" + safeName + ".exe\"\r\n";
DWORD written = 0;
WriteFile(hBat, batContent.data(), static_cast<DWORD>(batContent.size()), &written, nullptr);
CloseHandle(hBat);
}
// 5. Create README.txt
fs::path readmePath = dest / "README.txt";
HANDLE hReadme = CreateFileA(readmePath.string().c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hReadme != INVALID_HANDLE_VALUE) {
std::string readme = "==============================================================================\r\n";
readme += project.title + " (v" + project.version + ")\r\n";
readme += "Created with EblanStudio & EBLAN ENGINE 3D [Pure x86 Assembly]\r\n";
readme += "Author: " + project.author + "\r\n";
readme += "==============================================================================\r\n\r\n";
readme += "УПРАВЛЕНИЕ:\r\n";
readme += " W, A, S, D - Перемещение / Стрейф\r\n";
readme += " Стрелки - Поворот камеры\r\n";
readme += " Shift - Бег / Спринт\r\n";
readme += " ПРОБЕЛ/Ctrl - Огонь / Стрельба\r\n";
readme += " E - Использование / Открытие дверей / Переключатели\r\n";
readme += " M - Радар / Карта\r\n";
readme += " C - Перезапуск кат-сцены\r\n";
readme += " R - Перезапуск уровня при гибели\r\n";
readme += " F12 - Сделать скриншот (screenshot.bmp)\r\n";
readme += " ESC - Выход\r\n\r\n";
readme += "Zero External Dependencies: работает на любой версии Windows без DirectX/C++ runtimes!\r\n";
DWORD written = 0;
WriteFile(hReadme, readme.data(), static_cast<DWORD>(readme.size()), &written, nullptr);
CloseHandle(hReadme);
}
outMessage = "Игра успешно экспортирована в:\n" + dest.string() + "\n\nСоздан автономный пакет с исполняемым файлом " + safeName + ".exe, ассетами и лаунчером.";
return true;
}
};