summaryrefslogtreecommitdiff
path: root/src/port/GameExtractor.cpp
diff options
context:
space:
mode:
authorMegaMech <MegaMech@users.noreply.github.com>2025-12-05 17:54:04 -0700
committerGitHub <noreply@github.com>2025-12-06 01:54:04 +0100
commiteec7d650482134352b7cde4801b979ce1ed2044e (patch)
treee3bef8b2ef25207f3003b61c42c29bebbfb0ce36 /src/port/GameExtractor.cpp
parent92d2a2a7b93014b4ee14e8c5e8a7b9270a7509eb (diff)
Auto-detect Game (#579)
* Update GameExtractor.cpp * Update GameExtractor.h * Update GameExtractor.cpp * Update GameExtractor.cpp * Update GameExtractor.cpp
Diffstat (limited to 'src/port/GameExtractor.cpp')
-rw-r--r--src/port/GameExtractor.cpp161
1 files changed, 145 insertions, 16 deletions
diff --git a/src/port/GameExtractor.cpp b/src/port/GameExtractor.cpp
index 9e942333d..f330c53cf 100644
--- a/src/port/GameExtractor.cpp
+++ b/src/port/GameExtractor.cpp
@@ -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() {
-#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;
+ 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;
+ }
}
- auto selection = pfd::open_file("Select a file", ".", { "N64 Roms", "*.z64" }).result();
+#if !defined(__IOS__) && !defined(__ANDROID__) && !defined(__SWITCH__)
+ // 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;
+ }
- if (selection.empty()) {
+ auto selection = pfd::open_file("Select a file", ".", { "N64 Roms", "*.z64" }).result();
+ if (selection.empty()) return false;
+
+ romPath = selection[0];
+ }
+#else
+ // Mobile: fallback to baserom.us.z64
+ if (!foundGame && !std::filesystem::exists(Ship::Context::GetPathRelativeToAppDirectory("baserom.us.z64"))) {
+ SPDLOG_ERROR("baserom not found");
return false;
}
- this->mGamePath = selection[0];
-#else
- this->mGamePath = Ship::Context::GetPathRelativeToAppDirectory("baserom.us.z64");
+ 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();