Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions include/DLLoader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ class DLLoader {
public:
explicit DLLoader(const std::string& filepath) : _handle(nullptr) {
_handle = dlopen(filepath.c_str(), RTLD_LAZY);
if (!_handle)
throw ARCError(dlerror());
if (!_handle) {
const char* error = dlerror();
throw ARCError(std::string("dlopen error: ") + (error ? error : "unknown error"));
}
}

~DLLoader() {
Expand All @@ -35,30 +37,45 @@ class DLLoader {
if (this != &other) {
if (_handle)
dlclose(_handle);
_handle = other._handle = nullptr;
_handle = other._handle = other._handle = nullptr;
}
return *this;
}

bool hasSymbol(const std::string& symbolName) const {
return dlsym(_handle, symbolName.c_str()) != nullptr;
void* sym = dlsym(_handle, symbolName.c_str());
if (sym) return true;

std::string macOSName = "_" + symbolName;
sym = dlsym(_handle, macOSName.c_str());
return sym != nullptr;
}

std::unique_ptr<T> getInstance(const std::string& entryPointName = "entryPoint") {
dlerror();

void* sym = dlsym(_handle, entryPointName.c_str());

if (!sym) {
std::string macOSName = "_" + entryPointName;
sym = dlsym(_handle, macOSName.c_str());
}

const char *dlsym_error = dlerror();
if (dlsym_error)
throw ARCError(std::string("dlsym error : ") + dlsym_error);
throw ARCError(std::string("dlsym error: ") + dlsym_error);

if (!sym)
throw ARCError(std::string("cannot find symbol: ") + entryPointName);

using EntryPointFunc = T* (*)();
EntryPointFunc createFunc = reinterpret_cast<EntryPointFunc>(sym);

if (!createFunc)
throw ARCError(std::string("cannot find entryPointName: ") + entryPointName);
throw ARCError(std::string("invalid function pointer for: ") + entryPointName);

return std::unique_ptr<T>(createFunc());
}
};

}
9 changes: 6 additions & 3 deletions include/DirectoryScanner.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
#include <vector>
#include <string>
#include <filesystem>
#include "Errors.hpp"
#include "DLLoader.hpp"
#include "Errors.hpp"

namespace Arcade {
class DirectoryScanner {
Expand All @@ -18,8 +18,11 @@ class DirectoryScanner {
return;

for (const auto& entry : std::filesystem::directory_iterator(path)) {
if (entry.path().extension() == ".so") {
std::string extension = entry.path().extension().string();

if (extension == ".so" || extension == ".dylib") {
std::string filepath = entry.path().string();

try {
DLLoader<int> inspector(filepath);

Expand All @@ -28,7 +31,7 @@ class DirectoryScanner {
} else if (inspector.hasSymbol("createGraphics")) {
graphicLibs.push_back(filepath);
} else {
throw ARCError("Invalid symbol");
throw ARCError("Invalid symbol.");
}
} catch (const ARCError& e) {
std::cerr << "Error loading " << filepath << ": " << e.what() << std::endl;
Expand Down
35 changes: 35 additions & 0 deletions include/MenuSelector.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef MENUSELECTOR_HPP
#define MENUSELECTOR_HPP

#include <string>
#include <vector>
#include <functional>
#include <iostream>

namespace Arcade {

class MenuSelector {
public:
struct MenuOption {
std::string name;
std::string path;
std::function<void()> action;
};

MenuSelector();
~MenuSelector();

std::string run(const std::vector<std::string>& availableLibs);

private:
void displayMenu(const std::vector<MenuOption>& options, int selectedIdx);
void clearScreen();
int getchNonBlock();

bool _running;
int _selectedIdx;
};

} // namespace Arcade

#endif
2 changes: 1 addition & 1 deletion src/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,4 @@ void Core::run() {
}
}

}
}
160 changes: 160 additions & 0 deletions src/MenuSelector.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#include "MenuSelector.hpp"
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <cstdlib>

namespace Arcade {

MenuSelector::MenuSelector() : _running(true), _selectedIdx(0) {
struct termios term;
tcgetattr(STDIN_FILENO, &term);
term.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}

MenuSelector::~MenuSelector() {
struct termios term;
tcgetattr(STDIN_FILENO, &term);
term.c_lflag |= (ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}

void MenuSelector::clearScreen() {
std::cout << "\033[2J\033[H";
}

int MenuSelector::getchNonBlock() {
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
int ch = getchar();
fcntl(STDIN_FILENO, F_SETFL, flags);
return ch;
}

void MenuSelector::displayMenu(const std::vector<MenuOption>& options, int selectedIdx) {
clearScreen();

std::cout << "\n";
std::cout << " ╔══════════════════════════════════════╗\n";
std::cout << " ║ ARCADE - MENU PRINCIPAL ║\n";
std::cout << " ╠══════════════════════════════════════╣\n";
std::cout << " ║ ║\n";
std::cout << " ║ Choisissez une librairie graphique: ║\n";
std::cout << " ║ ║\n";

for (size_t i = 0; i < options.size(); ++i) {
if (static_cast<int>(i) == selectedIdx) {
std::cout << " ║ \033[7m";
} else {
std::cout << " ║ ";
}

std::string displayName = options[i].name;
size_t lastSlash = displayName.find_last_of('/');
if (lastSlash != std::string::npos) {
displayName = displayName.substr(lastSlash + 1);
}
size_t dotPos = displayName.find(".so");
if (dotPos != std::string::npos) {
displayName = displayName.substr(0, dotPos);
}
dotPos = displayName.find(".dylib");
if (dotPos != std::string::npos) {
displayName = displayName.substr(0, dotPos);
}

printf(" ► %-31s", displayName.c_str());

if (static_cast<int>(i) == selectedIdx) {
std::cout << "\033[0m";
}
std::cout << "║\n";
}

std::cout << " ║ ║\n";

if (selectedIdx == static_cast<int>(options.size())) {
std::cout << " ║ \033[7m ► Quitter\033[0m ║\n";
} else {
std::cout << " ║ ► Quitter ║\n";
}

std::cout << " ║ ║\n";
std::cout << " ╚══════════════════════════════════════╝\n";
std::cout << "\n";
std::cout << " Utilisez les flèches ↑/↓ pour naviguer\n";
std::cout << " Appuyez sur Entrée pour sélectionner\n";
std::cout << " Appuyez sur 'q' pour quitter\n";

fflush(stdout);
}

std::string MenuSelector::run(const std::vector<std::string>& availableLibs) {
std::vector<MenuOption> options;

for (const auto& lib : availableLibs) {
MenuOption opt;
opt.name = lib;
opt.path = lib;
options.push_back(opt);
}

_selectedIdx = 0;
_running = true;

displayMenu(options, _selectedIdx);

while (_running) {
int ch = getchNonBlock();

if (ch == EOF) {
usleep(10000);
continue;
}

switch (ch) {
case 27:
ch = getchNonBlock();
if (ch == 91) {
ch = getchNonBlock();
switch (ch) {
case 65:
_selectedIdx--;
if (_selectedIdx < 0)
_selectedIdx = options.size();
displayMenu(options, _selectedIdx);
break;
case 66:
_selectedIdx++;
if (_selectedIdx > static_cast<int>(options.size()))
_selectedIdx = 0;
displayMenu(options, _selectedIdx);
break;
}
}
break;

case '\n':
case '\r':
if (_selectedIdx == static_cast<int>(options.size())) {
return "";
} else if (_selectedIdx >= 0 && _selectedIdx < static_cast<int>(options.size())) {
return options[_selectedIdx].path;
}
break;

case 'q':
case 'Q':
return "";
break;
}

usleep(10000);
}

return "";
}

}
57 changes: 38 additions & 19 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
/*
** EPITECH PROJECT, 2026
** arcade
** File description:
** arcade
*/

#include "Core.hpp"
#include "Errors.hpp"
#include "MenuSelector.hpp"
#include "DirectoryScanner.hpp"
#include <iostream>

#define SUCCESS 0
#define HELP 2026
#define ERROR 84

static unsigned int handle_args(int argc, const char *argv[]) {
if (argc != 2 || !argv || !argv[1]) {
std::cerr <<
"Usage:\n\tarcade [options] <file>\nOptions:\n\t-h, --help Show this help" <<
std::endl;
if (argc > 2) {
std::cerr << "Usage:\n\t./arcade\n\t./arcade [graphical_library.so]\n\t./arcade -h, --help" << std::endl;
return ERROR;
}

std::string flag(argv[1]);
if (flag == "--help" || flag == "-h") {
std::cout <<
"Usage:\n\tarcade [options] <file>\nOptions:\n\t-h, --help Show this help" <<
std::endl;
return HELP;
if (argc == 2) {
std::string flag(argv[1]);
if (flag == "--help" || flag == "-h") {
std::cout << "Usage:\n\t./arcade [graphical_library.so]\n\n"
"If no library is specified, a menu will let you choose.\n\n"
"Options:\n\t-h, --help Show this help" << std::endl;
return HELP;
}
}
return SUCCESS;
}
Expand All @@ -35,16 +34,36 @@ int main(int argc, const char *argv[]) {
return ERROR;
default:
try {
Arcade::Core core(argv[1]);
std::string selectedLib;

if (argc == 1) {
std::vector<std::string> gameLibs;
std::vector<std::string> graphicalLibs;
Arcade::DirectoryScanner::scan("./lib/", gameLibs, graphicalLibs);

if (graphicalLibs.empty()) {
throw Arcade::ARCError("No graphical library found in ./lib/");
}

Arcade::MenuSelector menu;
selectedLib = menu.run(graphicalLibs);

if (selectedLib.empty()) { return SUCCESS; }
} else { selectedLib = argv[1]; }

Arcade::Core core(selectedLib);

core.run();
return SUCCESS;
} catch (const Arcade::ARCError &error) {
std::cerr << "Error: " << error.what() << std::endl;
return ERROR;
} catch (const std::exception& e) {
std::cerr << "Uncaught Error: " << e.what() << std::endl;
return ERROR;
} catch (...) {
std::cerr << "Uncaught Error." << std::endl;
return ERROR;
}
}
}
}
Loading