Auto-detect Game (#579)

* Update GameExtractor.cpp

* Update GameExtractor.h

* Update GameExtractor.cpp

* Update GameExtractor.cpp

* Update GameExtractor.cpp
This commit is contained in:
MegaMech
2025-12-05 17:54:04 -07:00
committed by GitHub
parent 92d2a2a7b9
commit eec7d65048
2 changed files with 149 additions and 19 deletions
+148 -19
View File
@@ -1,5 +1,12 @@
#ifdef _WIN32
#include <Windows.h>
#include <winuser.h>
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
#endif
#include "GameExtractor.h"
#include <unordered_map>
#include <Companion.h>
#include <fstream>
@@ -7,6 +14,13 @@
#include "spdlog/spdlog.h"
#include <port/Engine.h>
#ifdef unix
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
#if !defined(__IOS__) && !defined(__ANDROID__) && !defined(__SWITCH__)
#include "portable-file-dialogs.h"
#endif
@@ -16,33 +30,148 @@ std::unordered_map<std::string, std::string> mGameList = {
};
bool GameExtractor::SelectGameFromUI() {
std::vector<std::string> roms;
GetRoms(roms);
bool foundGame = false;
// Store both path and already-read data
std::string romPath;
std::vector<uint8_t> romData;
// Auto detect first baserom with valid hash
for (const auto& rom : roms) {
if (!std::filesystem::exists(rom)) continue;
std::ifstream inFile(rom, std::ios::binary);
if (!inFile.is_open()) {
SPDLOG_INFO("Failed to open ROM at path: {}, continuing", rom);
continue;
}
inFile.seekg(0, std::ios::end);
size_t fileSize = inFile.tellg();
inFile.seekg(0, std::ios::beg);
std::vector<uint8_t> data(fileSize);
if (!inFile.read(reinterpret_cast<char*>(data.data()), fileSize)) {
SPDLOG_INFO("Failed to read ROM at path: {}, continuing", rom);
continue;
}
inFile.close();
std::string hash = Companion::CalculateHash(data);
if (mGameList.find(hash) != mGameList.end()) {
romPath = rom;
romData = std::move(data);
foundGame = true;
break;
}
}
#if !defined(__IOS__) && !defined(__ANDROID__) && !defined(__SWITCH__)
if (!pfd::settings::available()) {
SPDLOG_ERROR(
"portable-file-dialogs is not available on this system. Check "
"https://github.com/samhocevar/portable-file-dialogs for runtime "
"requirements."
);
return false;
// Desktop: fallback to file dialogue if no baserom found
if (!foundGame) {
if (!pfd::settings::available()) {
SPDLOG_ERROR(
"portable-file-dialogs is not available on this system."
);
return false;
}
auto selection = pfd::open_file("Select a file", ".", { "N64 Roms", "*.z64" }).result();
if (selection.empty()) return false;
romPath = selection[0];
}
auto selection = pfd::open_file("Select a file", ".", { "N64 Roms", "*.z64" }).result();
if (selection.empty()) {
return false;
}
this->mGamePath = selection[0];
#else
this->mGamePath = Ship::Context::GetPathRelativeToAppDirectory("baserom.us.z64");
// Mobile: fallback to baserom.us.z64
if (!foundGame && !std::filesystem::exists(Ship::Context::GetPathRelativeToAppDirectory("baserom.us.z64"))) {
SPDLOG_ERROR("baserom not found");
return false;
}
if (!foundGame) {
romPath = Ship::Context::GetPathRelativeToAppDirectory("baserom.us.z64");
}
#endif
std::ifstream file(this->mGamePath, std::ios::binary);
this->mGameData = std::vector<uint8_t>( std::istreambuf_iterator( file ), {} );
file.close();
// Load file if it is not already open
if (romData.empty()) {
if (!std::filesystem::exists(romPath)) {
SPDLOG_ERROR("Failed to find ROM at path: {}", romPath);
return false;
}
std::ifstream inFile(romPath, std::ios::binary);
if (!inFile.is_open()) return false;
romData = std::vector<uint8_t>(std::istreambuf_iterator<char>(inFile), {});
inFile.close();
}
this->mGamePath = romPath;
this->mGameData = std::move(romData);
return true;
}
void GameExtractor::GetRoms(std::vector<std::string>& roms) {
#ifdef _WIN32
WIN32_FIND_DATAA ffd;
HANDLE h = FindFirstFileA(".\\*", &ffd);
do {
if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
char* ext = PathFindExtensionA(ffd.cFileName);
// Check for any standard N64 rom file extensions.
if ((strcmp(ext, ".z64") == 0))
roms.push_back(ffd.cFileName);
}
} while (FindNextFileA(h, &ffd) != 0);
// if (h != nullptr) {
// CloseHandle(h);
//}
#elif unix
// Open the directory of the app.
DIR* d = opendir(".");
struct dirent* dir;
if (d != NULL) {
// Go through each file in the directory
while ((dir = readdir(d)) != NULL) {
struct stat path;
auto fullPath = std::filesystem::path(".") / dir->d_name;
auto fullPathString = fullPath.string();
const char* fullPathCStr = fullPathString.c_str();
// Check if current entry is not folder
stat(fullPathCStr, &path);
if (S_ISREG(path.st_mode)) {
// Get the position of the extension character.
char* ext = strrchr(dir->d_name, '.');
if (ext != NULL && (strcmp(ext, ".z64") == 0)) {
roms.push_back(fullPathCStr);
}
}
}
}
closedir(d);
#else
for (const auto& file : std::filesystem::directory_iterator(".")) {
if (file.is_directory())
continue;
if (file.path().extension() == ".z64") {
roms.push_back((file.path()));
}
}
#endif
}
std::optional<std::string> GameExtractor::ValidateChecksum() const {
const auto rom = new N64::Cartridge(this->mGameData);
rom->Initialize();
+1
View File
@@ -10,6 +10,7 @@ public:
static bool GenAssetFile();
std::optional<std::string> ValidateChecksum() const;
bool SelectGameFromUI();
void GetRoms(std::vector<std::string>& roms);
bool GenerateOTR() const;
private:
fs::path mGamePath;