ソースを参照

The installer now can use original FFVII ISO images

Iñigo Valentin 1 ヶ月 前
コミット
ac6bd06def

+ 1 - 0
.gitignore

@@ -26,3 +26,4 @@ doc/FFVII/RAW/
 .directory
 v-gears-new*.png
 /.vscode/c_cpp_properties.json
+src/installer/MainWindow.ui.autosave

+ 26 - 6
src/installer/MainWindow.cpp

@@ -182,20 +182,39 @@ void MainWindow::on_btn_data_run_clicked(){
         QMessageBox::critical(
           this, tr("Input error"),
           tr(
-            "The installation needs the original game data.\n\n"
-            "Select a directory with the extracted data from Final Fantasy VII "
-            "(PC version, install disk)."
+            "The installation needs a Final Fantasy VII ISO image.\n\n"
+            "Select the ISO from the PC install disk."
           )
         );
     }
+    if (release.isValid() == false){
+        QMessageBox::critical(
+          this, tr("Input error"),
+          tr(
+            "The selected ISO is not a valid Final Fantasy VII game."
+            "\n\nSelect a valid ISO image of Final Fantasy VII.")
+        );
+    }
     else if (main_window_->line_data_dst->text().isEmpty())
         QMessageBox::critical(this, tr("Output error"), tr("No output path provided"));
     else{
-        // Normalize the paths so its in / format separators.
-        QString input = QDir::fromNativeSeparators(main_window_->line_data_src->text());
-        if (!input.endsWith("/")) input += "/";
+      // Normalize the output path and extract to original_data.
         QString output = QDir::fromNativeSeparators(main_window_->line_data_dst->text());
         if (!output.endsWith("/")) output += "/";
+
+        bool extraction_success = release.extractIso(output.toStdString());
+        if (!extraction_success) {
+            QMessageBox::critical(this, tr("Extraction error"), tr("Failed to extract ISO file."));
+            return;
+        }
+
+      QString input = QDir::fromNativeSeparators(QString::fromStdString(release.getContentPath()));
+      if (input.isEmpty()) {
+        QMessageBox::critical(this, tr("Extraction error"), tr("Extracted data path is invalid."));
+        return;
+      }
+      if (!input.endsWith("/")) input += "/";
+
         // TODO: Enumerate files or find some better way to do this.
         const std::vector<std::string> required_files = {
           "data/field/char.lgp",
@@ -215,6 +234,7 @@ void MainWindow::on_btn_data_run_clicked(){
           "data/wm/world_us.lgp",
           "ff7.exe"
         };
+
         // Ensure required files are in the input dir
         for (auto& file : required_files){
             QString full_path = input + QString::fromStdString(file);

+ 2 - 2
src/installer/MainWindow.ui

@@ -100,7 +100,7 @@
           <item>
            <widget class="QLabel" name="isoData">
             <property name="text">
-             <string>ISO Info: </string>
+             <string/>
             </property>
             <property name="margin">
              <number>0</number>
@@ -110,7 +110,7 @@
           <item>
            <widget class="QLabel" name="isoError">
             <property name="text">
-             <string>Message</string>
+             <string/>
             </property>
            </widget>
           </item>

+ 584 - 246
src/installer/Release.cpp

@@ -20,319 +20,657 @@
 #include <archive.h>
 #include <archive_entry.h>
 #include "Release.h"
+#include <QtCore/qstring.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qglobal.h>
+#include <algorithm>
+#include <cctype>
+#include <cstdio>
+#include <fstream>
+#include <regex>
+#include <stdexcept>
+#include <sys/stat.h>
+#include <vector>
 
 Release::Release(){
-    id = "";
+    id = "Unknown ISO file";
     platform = PLATFORM_UNKNOWN;
     region = REGION_UNKNOWN;
     language = LANGUAGE_UNKNOWN;
     disk = DISK_UNKNOWN;
     valid = false;
     supported = false;
-    error_message = "";
+    error_message = "The selected file is not a valid Final Fantasy VII ISO file.";
     warning_message = "";
 };
 
-
-
-Release::Release(std::string iso_path){
-    Release();
+Release::Release(std::string iso_path) : Release() {
     struct archive* a = archive_read_new();
     struct archive_entry* entry;
     archive_read_support_format_iso9660(a);
-    archive_read_support_format_all(a); // Fail-safe fallback
+    archive_read_support_format_all(a);
+    archive_read_support_filter_all(a);
 
-    if (archive_read_open_filename(a, iso_path.c_str(), 10240) != ARCHIVE_OK) {
+    if (archive_read_open_filename(a, iso_path.c_str(), 2048) != ARCHIVE_OK) {
+        const char* archive_error = archive_error_string(a);
+        const std::string open_error = archive_error ? archive_error : "Unknown archive error";
+        // Error reading iso
+        std::cerr << "Failed to read ISO file: " << open_error << std::endl;
+        error_message = "Failed to read ISO file: " + open_error;
         archive_read_free(a);
-        // Error readding iso
-        error_message = "Failed to read ISO file: " + std::string(archive_error_string(a));
         return;
     }
     std::string game_id = "";
 
-    // Scan headers without extracting data to disk
-    while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
+    auto to_upper = [](std::string value) {
+        std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c){ return std::toupper(c); });
+        return value;
+    };
+
+    // Normalize a path from the ISO9660 filesystem, removing version suffixes and trailing dots.
+    auto normalize_iso_path = [&](std::string value) {
+        std::replace(value.begin(), value.end(), '\\', '/');
+        while (
+          !value.empty()
+          && (value.back() == '\n' || value.back() == '\r' || value.back() == ' ' || value.back() == '\t')
+        ) {
+            value.pop_back();
+        }
+        while (!value.empty() && (value.front() == ' ' || value.front() == '\t')) {
+            value.erase(value.begin());
+        }
+        // ISO9660 listings often include version suffixes like ";1".
+        std::size_t semi = value.find(';');
+        if (semi != std::string::npos) {
+            bool version_suffix = true;
+            for (std::size_t i = semi + 1; i < value.size(); ++i) {
+                if (!std::isdigit(static_cast<unsigned char>(value[i]))) {
+                    version_suffix = false;
+                    break;
+                }
+            }
+            if (version_suffix) {
+                value = value.substr(0, semi);
+            }
+        }
+        // Some tools emit trailing dots for ISO9660 short names.
+        while (!value.empty() && value.back() == '.') {
+            value.pop_back();
+        }
+        if (!value.empty() && value.front() == '/') {
+            value.erase(value.begin());
+        }
+        std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c){
+            return std::toupper(c);
+        });
+        return value;
+    };
+
+    auto apply_pc_release_from_path = [&](const std::string& normalized) {
+        if (
+          normalized == "FF7CONFIG.EXE" || normalized == "FF7CONFI.EXE"
+          || normalized == "FF7CON~1.EXE" || normalized == "FF7INST.EXE"
+        ) {
+            this->iso_path = iso_path;
+            id = "Final Fantasy VII (PC, 1998, USA) Install Disk";
+            platform = PLATFORM_PC;
+            region = REGION_NORTH_AMERICA;
+            language = LANGUAGE_ENGLISH;
+            disk = DISK_INSTALL;
+            supported = true;
+            valid = true;
+            error_message = "";
+            warning_message = "";
+            return true;
+        }
+        if (normalized == "FF7/MOVIES/BIKE.AVI") {
+            this->iso_path = iso_path;
+            id = "Final Fantasy VII (PC, 1998, USA) Disk 1";
+            platform = PLATFORM_PC;
+            region = REGION_NORTH_AMERICA;
+            language = LANGUAGE_ENGLISH;
+            disk = DISK_1;
+            supported = false;
+            valid = false;
+            error_message = "To install from a PC release disk, use the install disk instead.";
+            warning_message = "";
+            return true;
+        }
+        if (normalized == "FF7/MOVIES/BIGLIGHT.AVI") {
+            this->iso_path = iso_path;
+            id = "Final Fantasy VII (PC, 1998, USA) Disk 2";
+            platform = PLATFORM_PC;
+            region = REGION_NORTH_AMERICA;
+            language = LANGUAGE_ENGLISH;
+            disk = DISK_2;
+            supported = false;
+            valid = false;
+            error_message = "To install from a PC release disk, use the install disk instead.";
+            warning_message = "";
+            return true;
+        }
+        if (normalized == "FF7/MOVIES/ENDING1.AVI") {
+            this->iso_path = iso_path;
+            id = "Final Fantasy VII (PC, 1998, USA) Disk 3";
+            platform = PLATFORM_PC;
+            region = REGION_NORTH_AMERICA;
+            language = LANGUAGE_ENGLISH;
+            disk = DISK_3;
+            supported = false;
+            valid = false;
+            error_message = "To install from a PC release disk, use the install disk instead.";
+            warning_message = "";
+            return true;
+        }
+        return false;
+    };
+
+    bool pc_marker_found = false;
+    int r = ARCHIVE_OK;
+    bool saw_header = false;
+    while (true) {
+        std::cout << "Reading next header from ISO..." << std::endl;
+        r = archive_read_next_header(a, &entry);
+        if (r == ARCHIVE_EOF) {
+            std::cout << "Reached end of ISO archive." << std::endl;
+            break;
+        }
+        if (r != ARCHIVE_OK && r != ARCHIVE_WARN) {
+            std::cerr << "archive_read_next_header error: "
+              << archive_error_string(a) << " (code=" << r << ")" << std::endl;
+            break;
+        }
+        if (r == ARCHIVE_WARN) {
+            std::cerr << "archive_read_next_header warning: " << archive_error_string(a) << std::endl;
+        }
+        saw_header = true;
+
         std::string path = archive_entry_pathname(entry);
-        
-        // Convert to uppercase to handle varied ISO naming conventions
-        for (char &c : path) c = std::toupper(c);
+        std::cout << "Found file in ISO: " << path << std::endl;
+
+        // Look for know files in the PC release disks.
+        const std::string normalized = normalize_iso_path(path);
+        if (apply_pc_release_from_path(normalized)) {
+            pc_marker_found = true;
+            break;
+        }
+        std::cout << "Finished checking PC releases." << std::endl;
 
-        // We only care about SYSTEM.CNF at the root
-        if (path == "SYSTEM.CNF" || path == "/SYSTEM.CNF" || path.find("SYSTEM.CNF;") != std::string::npos) {
+        // If we haven't found the PC install marker, look for SYSTEM.CNF
+        // to determine if it's a PSX release and extract the game ID.
+        std::string upper_path = to_upper(path);
+        if (
+          upper_path == "SYSTEM.CNF" || upper_path == "/SYSTEM.CNF"
+          || upper_path.find("SYSTEM.CNF;") != std::string::npos
+        ) {
+            std::cout << "SYSTEM.CNF FOUND." << std::endl;
             size_t size = archive_entry_size(entry);
             if (size > 0) {
                 std::vector<char> buffer(size);
-                archive_read_data(a, buffer.data(), size);
-                
-                std::string cnf_content(buffer.begin(), buffer.end());
-                // Get the game ID from the SYSTEM.CNF content using regex
-                // Looks for patterns like SLUS_123.45, SCUS_944.44, SLES_001.23, etc.
-                std::regex code_regex(R"(([A-Z]{4})_(\d{3})\.(\d{2}))");
-                std::smatch match;
-
-                if (std::regex_search(cnf_content, match, code_regex)) {
-                    // match[1] = ABCD, match[2] = 123, match[3] = 45
-                    // Standardize it to the common "ABCD-12345" format
-                    game_id = match[1].str() + "-" + match[2].str() + match[3].str();
+                la_ssize_t bytes_read = archive_read_data(a, buffer.data(), size);
+                if (bytes_read > 0) {
+                    std::string cnf_content(buffer.begin(), buffer.begin() + bytes_read);
+                    std::cout << "SYSTEM.CNF content:\n" << cnf_content << std::endl;
+                    std::regex code_regex(R"(([A-Z]{4})_(\d{3})\.(\d{2}))");
+                    std::smatch match;
+                    if (std::regex_search(cnf_content, match, code_regex)) {
+                        game_id = match[1].str() + "-" + match[2].str() + match[3].str();
+                        std::cout << "Detected game ID from SYSTEM.CNF: " << game_id << std::endl;
+                        if (game_id == "SCUS-94163") {
+                            id = "Final Fantasy VII (USA) Disc 1";
+                            platform = PLATFORM_PS1;
+                            region = REGION_NORTH_AMERICA;
+                            language = LANGUAGE_ENGLISH;
+                            disk = DISK_1;
+                            supported = true;
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCUS-94164") {
+                            id = "Final Fantasy VII (USA) Disc 2";
+                            platform = PLATFORM_PS1;
+                            region = REGION_NORTH_AMERICA;
+                            language = LANGUAGE_ENGLISH;
+                            disk = DISK_2;
+                            supported = true;
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCUS-94165") {
+                            id = "Final Fantasy VII (USA) Disc 3";
+                            platform = PLATFORM_PS1;
+                            region = REGION_NORTH_AMERICA;
+                            language = LANGUAGE_ENGLISH;
+                            disk = DISK_3;
+                            supported = true;
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-00867") {
+                            id = "Final Fantasy VII (Europe, English) Disc 1";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_ENGLISH;
+                            disk = DISK_1;
+                            supported = true;
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-10867") {
+                            id = "Final Fantasy VII (Europe, English) Disc 2";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_ENGLISH;
+                            disk = DISK_2;
+                            supported = true;
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-20867") {
+                            id = "Final Fantasy VII (Europe, English) Disc 3";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_ENGLISH;
+                            disk = DISK_3;
+                            supported = true;
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-00868") {
+                            id = "Final Fantasy VII (Europe, French) Disc 1";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_FRENCH;
+                            disk = DISK_1;
+                            supported = false;
+                            warning_message = "Spanish language is not fully supported in V-Gears.";
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-10868") {
+                            id = "Final Fantasy VII (Europe, French) Disc 2";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_FRENCH;
+                            disk = DISK_2;
+                            supported = false;
+                            warning_message = "Spanish language is not fully supported in V-Gears.";
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-20868") {
+                            id = "Final Fantasy VII (Europe, French) Disc 3";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_FRENCH;
+                            disk = DISK_3;
+                            supported = false;
+                            warning_message = "Spanish language is not fully supported in V-Gears.";
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-00900") {
+                            id = "Final Fantasy VII (Europe, Spanish) Disc 1";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_SPANISH;
+                            disk = DISK_1;
+                            supported = true;supported = false;
+                            warning_message = "Spanish language is not fully supported in V-Gears.";
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-10900") {
+                            id = "Final Fantasy VII (Europe, Spanish) Disc 2";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_SPANISH;
+                            disk = DISK_2;
+                            supported = false;
+                            warning_message = "Spanish language is not fully supported in V-Gears.";
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-20900") {
+                            id = "Final Fantasy VII (Europe, Spanish) Disc 3";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_SPANISH;
+                            disk = DISK_3;
+                            supported = false;
+                            warning_message = "Spanish language is not fully supported in V-Gears.";
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-00869") {
+                            id = "Final Fantasy VII (Europe, German) Disc 1";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_GERMAN;
+                            disk = DISK_1;
+                            supported = true;supported = false;
+                            warning_message = "German language is not fully supported in V-Gears.";
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-10869") {
+                            id = "Final Fantasy VII (Europe, German) Disc 2";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_GERMAN;
+                            disk = DISK_2;
+                            supported = false;
+                            warning_message = "German language is not fully supported in V-Gears.";
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-20869") {
+                            id = "Final Fantasy VII (Europe, German) Disc 3";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_GERMAN;
+                            disk = DISK_3;
+                            supported = false;
+                            warning_message = "German language is not fully supported in V-Gears.";
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-00901") {
+                            id = "Final Fantasy VII (Europe, Italian) Disc 1";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_ITALIAN;
+                            disk = DISK_1;
+                            supported = true;supported = false;
+                            warning_message = "Italian language is not fully supported in V-Gears.";
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-10901") {
+                            id = "Final Fantasy VII (Europe, Italian) Disc 2";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_ITALIAN;
+                            disk = DISK_2;
+                            supported = false;
+                            warning_message = "Italian language is not fully supported in V-Gears.";
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else if (game_id == "SCES-20901") {
+                            id = "Final Fantasy VII (Europe, Italian) Disc 3";
+                            platform = PLATFORM_PS1;
+                            region = REGION_EUROPE;
+                            language = LANGUAGE_ITALIAN;
+                            disk = DISK_3;
+                            supported = false;
+                            warning_message = "Italian language is not fully supported in V-Gears.";
+                            valid = false;
+                            error_message
+                            = "Play Station releases are not yet supported in V-Gears."
+                            "Use the PC release instead.";
+                        }
+                        else {
+                            id = "Unknown Release";
+                            platform = PLATFORM_UNKNOWN;
+                            region = REGION_UNKNOWN;
+                            language = LANGUAGE_UNKNOWN;
+                            disk = DISK_UNKNOWN;
+                            supported = false;
+                            valid = false;
+                            error_message = "Unrecognized game ID: " + game_id + ".";
+                        }
+                    }
                 }
-                else game_id = "";
             }
-            break; // Found a PC release
-            std::string path = archive_entry_pathname(entry);
-            if (path.find("diski.x") != std::string::npos) {
-                id = "Final Fantasy VII (PC) Disc 1";
-                platform = PLATFORM_PC;
-                region = REGION_UNKNOWN;
-                language = LANGUAGE_UNKNOWN;
-                disk = DISK_1;
-                supported = true;
-                valid = true;
+            break;
+        }
+    }
+
+    if (!saw_header && r == ARCHIVE_EOF) {
+        std::cout << "No entries found by libarchive, trying isoinfo listing..." << std::endl;
+
+        auto escape_shell = [](const std::string& value) {
+            std::string out;
+            for (char c : value) {
+                if (c == '"' || c == '\\') {
+                    out.push_back('\\');
+                }
+                out.push_back(c);
+            }
+            return out;
+        };
+
+        const std::string escaped_iso_path = escape_shell(iso_path);
+        const std::vector<std::string> list_cmds = {
+            "isoinfo -J -f -i \"" + escaped_iso_path + "\" 2>/dev/null",
+            "isoinfo -R -J -f -i \"" + escaped_iso_path + "\" 2>/dev/null",
+            "isoinfo -R -f -i \"" + escaped_iso_path + "\" 2>/dev/null",
+            "isoinfo -f -i \"" + escaped_iso_path + "\" 2>/dev/null"
+        };
+
+        char buffer[4096];
+        for (const auto& list_cmd : list_cmds) {
+            FILE* list_pipe = popen(list_cmd.c_str(), "r");
+            if (!list_pipe) {
+                continue;
             }
-            else if (path.find("diskii.x") != std::string::npos) {
-                id = "Final Fantasy VII (PC) Disc 3";
-                platform = PLATFORM_PC;
-                region = REGION_UNKNOWN;
-                language = LANGUAGE_UNKNOWN;
-                disk = DISK_3;
-                supported = true;
-                valid = true;
+
+            while (fgets(buffer, sizeof(buffer), list_pipe)) {
+                std::string line(buffer);
+                while (!line.empty() && (line.back() == '\n' || line.back() == '\r')) {
+                    line.pop_back();
+                }
+                if (line.empty()) {
+                    continue;
+                }
+                if (apply_pc_release_from_path(normalize_iso_path(line))) {
+                    pc_marker_found = true;
+                    break;
+                }
             }
-            else if (path.find("diskiii.x") != std::string::npos) {
-                id = "Final Fantasy VII (PC) Disc 3";
-                platform = PLATFORM_PC;
-                region = REGION_UNKNOWN;
-                language = LANGUAGE_UNKNOWN;
-                disk = DISK_3;
-                supported = true;
-                valid = true;
+
+            pclose(list_pipe);
+            if (pc_marker_found) {
+                break;
             }
-            return;
         }
     }
+
+    std::cout << "Finished checking releases." << std::endl;
+
     archive_read_close(a);
     archive_read_free(a);
+    return;
+};
 
-    // If it's a PC release, no need to check the game ID further
-    if (platform == PLATFORM_PC) {
-        return;
-    }
+Release::~Release(){};
 
-    if (game_id.empty()) {
-        error_message = "Game ID not found in ISO file: " + iso_path;
-        return;
-    }
+bool Release::extractIso(std::string installation_path){
+    struct archive* a;
+    struct archive* ext;
+    struct archive_entry* entry;
+    int flags;
+    int r;
+
+    // Attributes to restore
+    flags = ARCHIVE_EXTRACT_TIME;
+    flags |= ARCHIVE_EXTRACT_PERM;
+    flags |= ARCHIVE_EXTRACT_ACL;
+    flags |= ARCHIVE_EXTRACT_FFLAGS;
+
+    a = archive_read_new();
+    archive_read_support_format_iso9660(a);
+    archive_read_support_format_all(a); // Backup to support UDF ISOs
+    archive_read_support_filter_all(a); // Support compressed/filtered streams
     
-    if (game_id == "SCUS-94163") {
-        id = "Final Fantasy VII (USA) Disc 1";
-        platform = PLATFORM_PS1;
-        region = REGION_NORTH_AMERICA;
-        language = LANGUAGE_ENGLISH;
-        disk = DISK_1;
-        supported = true;
-        valid = true;
-    }
-    else if (game_id == "SCUS-94164") {
-        id = "Final Fantasy VII (USA) Disc 2";
-        platform = PLATFORM_PS1;
-        region = REGION_NORTH_AMERICA;
-        language = LANGUAGE_ENGLISH;
-        disk = DISK_2;
-        supported = true;
-        valid = true;
-    }
-    else if (game_id == "SCUS-94165") {
-        id = "Final Fantasy VII (USA) Disc 3";
-        platform = PLATFORM_PS1;
-        region = REGION_NORTH_AMERICA;
-        language = LANGUAGE_ENGLISH;
-        disk = DISK_3;
-        supported = true;
-        valid = true;
-    }
-    else if (game_id == "SCES-00867") {
-        id = "Final Fantasy VII (Europe, English) Disc 1";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_ENGLISH;
-        disk = DISK_1;
-        supported = true;
-        valid = true;
-    }
-    else if (game_id == "SCES-10867") {
-        id = "Final Fantasy VII (Europe, English) Disc 2";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_ENGLISH;
-        disk = DISK_2;
-        supported = true;
-        valid = true;
-    }
-    else if (game_id == "SCES-20867") {
-        id = "Final Fantasy VII (Europe, English) Disc 3";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_ENGLISH;
-        disk = DISK_3;
-        supported = true;
-        valid = true;
-    }
-    else if (game_id == "SCES-00868") {
-        id = "Final Fantasy VII (Europe, French) Disc 1";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_FRENCH;
-        disk = DISK_1;
-        supported = false;
-        warning_message = "Spanish language is not fully supported in V-Gears. Some features may not work as expected.";
-        valid = true;
-    }
-    else if (game_id == "SCES-10868") {
-        id = "Final Fantasy VII (Europe, French) Disc 2";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_FRENCH;
-        disk = DISK_2;
-        supported = false;
-        warning_message = "Spanish language is not fully supported in V-Gears. Some features may not work as expected.";
-        valid = true;
-    }
-    else if (game_id == "SCES-20868") {
-        id = "Final Fantasy VII (Europe, French) Disc 3";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_FRENCH;
-        disk = DISK_3;
-        supported = false;
-        warning_message = "Spanish language is not fully supported in V-Gears. Some features may not work as expected.";
-        valid = true;
-    }
-    else if (game_id == "SCES-00900") {
-        id = "Final Fantasy VII (Europe, Spanish) Disc 1";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_SPANISH;
-        disk = DISK_1;
-        supported = true;supported = false;
-        warning_message = "Spanish language is not fully supported in V-Gears. Some features may not work as expected.";
-        valid = true;
-    }
-    else if (game_id == "SCES-10900") {
-        id = "Final Fantasy VII (Europe, Spanish) Disc 2";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_SPANISH;
-        disk = DISK_2;
-        supported = false;
-        warning_message = "Spanish language is not fully supported in V-Gears. Some features may not work as expected.";
-        valid = true;
-    }
-    else if (game_id == "SCES-20900") {
-        id = "Final Fantasy VII (Europe, Spanish) Disc 3";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_SPANISH;
-        disk = DISK_3;
-        supported = false;
-        warning_message = "Spanish language is not fully supported in V-Gears. Some features may not work as expected.";
-        valid = true;
-    }
-    else if (game_id == "SCES-00869") {
-        id = "Final Fantasy VII (Europe, German) Disc 1";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_GERMAN;
-        disk = DISK_1;
-        supported = true;supported = false;
-        warning_message = "German language is not fully supported in V-Gears. Some features may not work as expected.";
-        valid = true;
-    }
-    else if (game_id == "SCES-10869") {
-        id = "Final Fantasy VII (Europe, German) Disc 2";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_GERMAN;
-        disk = DISK_2;
-        supported = false;
-        warning_message = "German language is not fully supported in V-Gears. Some features may not work as expected.";
-        valid = true;
+    ext = archive_write_disk_new();
+    archive_write_disk_set_options(ext, flags);
+    archive_write_disk_set_standard_lookup(ext);
+
+    // Create the required output directory if it doesn't exist
+    std::string data_dir = installation_path + "/original_data/" + (id.empty() ? "ff7" : id);
+    QString target = QString::fromStdString(data_dir);
+    QDir dir(target);
+    QString parent = QFileInfo(target).absolutePath();
+    QDir parent_dir(parent);
+    if (!parent_dir.exists()) {
+        if (!parent_dir.mkpath(".")) {
+            std::cerr << "Failed to create extraction parent directory: " << parent.toStdString() << std::endl;
+            return false;
+        }
     }
-    else if (game_id == "SCES-20869") {
-        id = "Final Fantasy VII (Europe, German) Disc 3";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_GERMAN;
-        disk = DISK_3;
-        supported = false;
-        warning_message = "German language is not fully supported in V-Gears. Some features may not work as expected.";
-        valid = true;
+    if (!dir.exists()) {
+        if (!dir.mkpath(".")) {
+            std::cerr << "Failed to create extraction directory: " << data_dir << std::endl;
+            return false;
+        }
     }
-    else if (game_id == "SCES-00901") {
-        id = "Final Fantasy VII (Europe, Italian) Disc 1";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_ITALIAN;
-        disk = DISK_1;
-        supported = true;supported = false;
-        warning_message = "Italian language is not fully supported in V-Gears. Some features may not work as expected.";
-        valid = true;
+
+    // Open the ISO file
+    if ((r = archive_read_open_filename(a, iso_path.c_str(), 10240))) {
+        std::cerr << "Error: Could not open ISO file: " << archive_error_string(a) << std::endl;
+        return false;
     }
-    else if (game_id == "SCES-10901") {
-        id = "Final Fantasy VII (Europe, Italian) Disc 2";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_ITALIAN;
-        disk = DISK_2;
-        supported = false;
-        warning_message = "Italian language is not fully supported in V-Gears. Some features may not work as expected.";
-        valid = true;
+
+    // Read through the archive entries
+    while ((r = archive_read_next_header(a, &entry)) == ARCHIVE_OK || r == ARCHIVE_WARN) {
+        if (r == ARCHIVE_WARN) {
+            std::cerr << "archive_read_next_header warning during extraction: " << archive_error_string(a) << std::endl;
+        }
+        if (!entry) {
+            std::cerr << "Skipping null archive entry" << std::endl;
+            continue;
+        }
+        const char* entry_path = archive_entry_pathname(entry);
+        if (!entry_path) {
+            std::cerr << "Skipping entry without a pathname" << std::endl;
+            archive_write_finish_entry(ext);
+            continue;
+        }
+        // Prepend destination directory to the current entry path
+        std::string current_file_path = entry_path;
+        std::string full_output_path = data_dir + "/" + current_file_path;
+        archive_entry_set_pathname(entry, full_output_path.c_str());
+
+        // Write the header
+        r = archive_write_header(ext, entry);
+        if (r < ARCHIVE_OK) {
+            std::cerr << "Warning (Header): " << archive_error_string(ext) << std::endl;
+        }
+        else if (archive_entry_size(entry) > 0) {
+            // Copy data from the ISO to the disk
+            const void* buff;
+            size_t size;
+            la_int64_t offset;
+
+            while (true) {
+                r = archive_read_data_block(a, &buff, &size, &offset);
+                if (r == ARCHIVE_EOF) break;
+                if (r < ARCHIVE_OK) {
+                    std::cerr << "Error reading data block: " << archive_error_string(a) << std::endl;
+                    break;
+                }
+                r = archive_write_data_block(ext, buff, size, offset);
+                if (r < ARCHIVE_OK) {
+                    std::cerr << "Error writing data block: " << archive_error_string(ext) << std::endl;
+                    break;
+                }
+            }
+        }
+        archive_write_finish_entry(ext);
     }
-    else if (game_id == "SCES-20901") {
-        id = "Final Fantasy VII (Europe, Italian) Disc 3";
-        platform = PLATFORM_PS1;
-        region = REGION_EUROPE;
-        language = LANGUAGE_ITALIAN;
-        disk = DISK_3;
-        supported = false;
-        warning_message = "Italian language is not fully supported in V-Gears. Some features may not work as expected.";
-        valid = true;
+    if (r != ARCHIVE_EOF && r != ARCHIVE_OK && r != ARCHIVE_WARN) {
+        std::cerr << "archive_read_next_header error during extraction: " << archive_error_string(a) << " (code=" << r << ")" << std::endl;
+        // continue cleanup and return failure
+        archive_read_close(a);
+        archive_read_free(a);
+        archive_write_close(ext);
+        archive_write_free(ext);
+        return false;
     }
-    return;
-};
 
-Release::~Release(){};
+    // Clean up
+    archive_read_close(a);
+    archive_read_free(a);
+    archive_write_close(ext);
+    archive_write_free(ext);
 
-std::string Release::getId(){
+    content_path = data_dir;
+    return true;
+}
+
+const std::string Release::getId(){
     return id;
 };
 
-Release::Platform Release::getPlatform(){
+const Release::Platform Release::getPlatform(){
     return platform;
 };
 
-Release::Region Release::getRegion(){
+const Release::Region Release::getRegion(){
     return region;
 };
 
-Release::Language Release::getLanguage(){
+const Release::Language Release::getLanguage(){
     return language;
 };
 
-Release::Disk Release::getDisk(){
+const Release::Disk Release::getDisk(){
     return disk;
 };
 
-bool Release::isValid(){
+const bool Release::isValid(){
     return valid;
 };
 
-bool Release::isSupported(){
+const bool Release::isSupported(){
     return supported;
 };
 
-std::string Release::getErrorMessage(){
+const std::string Release::getErrorMessage(){
     return error_message;
 };
 
-std::string Release::getWarningMessage(){
+const std::string Release::getWarningMessage(){
     return warning_message;
 };
+
+std::string const Release::getContentPath() {
+    return content_path;
+}

+ 61 - 16
src/installer/Release.h

@@ -74,6 +74,8 @@ struct Release{
          * The disk for which the release is intended.
          */
         enum Disk{
+            /** The release is for the install disk (PC only). */
+            DISK_INSTALL,
             /** The release is for disk 1. */
             DISK_1,
             /** The release is for disk 2. */
@@ -103,90 +105,128 @@ struct Release{
          */
         ~Release();
 
+        std::string getIsoPath() const { return iso_path; }
+
         /**
          * Gets the human readable ID of the release.
          *
          * @return The ID of the release.
          */
-        std::string getId();
+        std::string const getId();
 
         /**
          * Gets the platform for which the release is intended.
          *
          * @return The platform of the release.
          */
-        Platform getPlatform();
+        Platform const getPlatform();
 
         /**
          * Gets the region for which the release is intended.
          *
          * @return The region of the release.
          */
-        Region getRegion();
+        Region const getRegion();
 
         /**
          * Gets the language for which the release is intended.
          *
          * @return The language of the release.
          */
-        Language getLanguage();
+        Language const getLanguage();
 
         /**
          * Gets the disk for which the release is intended.
          *
          * @return The disk of the release.
          */
-        Disk getDisk();
+        Disk const getDisk();
 
         /**
          * Checks if the release is a valid Final Fantasy VII release.
          *
          * @return True if the release is valid, false otherwise.
          */
-        bool isValid();
+        bool const isValid();
 
         /**
          * Gets the error message for the release.
          *
          * @return The error message.
          */
-        std::string getErrorMessage();
+        std::string const getErrorMessage();
 
         /**
          * Checks if the release is supported and tested in V-Gears.
          *
          * @return True if the release is supported, false otherwise.
          */
-        bool isSupported();
+        bool const isSupported();
 
         /**
          * Gets a warning in case the release is not fully supported.
          *
          * @return The warning message.
          */
-        std::string getWarningMessage();
+        std::string const getWarningMessage();
+
+        /**
+         * Gets the path where the content has been extracted.
+         * 
+         * @return The path to the extracted content, null if the content has
+         * not been extracted yet.
+         */
+        std::string const getContentPath();
+
+        /**
+         * Extracts the ISO file to the specified output path. The files will
+         * be extracted to the "original_data" subdirectory of the output path.
+         *
+         * @param installation_path The v-gears installation directory.
+         * @return True if the extraction was successful, false otherwise.
+         */
+        bool extractIso(std::string installation_path);
 
     private:
 
-        /** A human readable ID of the release. */
+        /**
+         * The path to the ISO file.
+         */
+        std::string iso_path;
+
+        /**
+         * A human readable ID of the release.
+         */
         std::string id;
 
-        /** The platform for which the release is intended. */
+        /**
+         * The platform for which the release is intended.
+         */
         Platform platform;
 
-        /** The region for which the release is intended. */
+        /**
+         * The region for which the release is intended.
+         */
         Region region;
 
-        /** The language for which the release is intended. */
+        /**
+         * The language for which the release is intended.
+         */
         Language language;
 
-        /** The disk for which the release is intended. */
+        /**
+         * The disk for which the release is intended.
+         */
         Disk disk;
 
-        /** Whether the release is valid. */
+        /**
+         * Whether the release is valid.
+         */
         bool valid;
 
-        /** Whether the release is supported. */
+        /**
+         * Whether the release is supported.
+         */
         bool supported;
 
         /**
@@ -198,4 +238,9 @@ struct Release{
          * The warning message for the release. Empty if no warning.
          */
         std::string warning_message;
+
+        /**
+         * The path where the content has been extracted.
+         */
+        std::string content_path;
 };

+ 8 - 6
src/installer/ui_MainWindow.h

@@ -126,7 +126,7 @@ public:
         label_4->setObjectName(QString::fromUtf8("label_4"));
         label_4->setMinimumSize(QSize(200, 0));
         label_4->setMaximumSize(QSize(200, 50));
-        label_4->setAlignment(Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter);
+        label_4->setAlignment(Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter);
 
         isoLocator->addWidget(label_4);
 
@@ -162,8 +162,10 @@ public:
         isoInfo = new QVBoxLayout();
         isoInfo->setSpacing(6);
         isoInfo->setObjectName(QString::fromUtf8("isoInfo"));
+        isoInfo->setContentsMargins(30, -1, -1, -1);
         isoData = new QLabel(tab_installer);
         isoData->setObjectName(QString::fromUtf8("isoData"));
+        isoData->setMargin(0);
 
         isoInfo->addWidget(isoData);
 
@@ -182,7 +184,7 @@ public:
         label_3->setObjectName(QString::fromUtf8("label_3"));
         label_3->setMinimumSize(QSize(200, 0));
         label_3->setMaximumSize(QSize(200, 50));
-        label_3->setAlignment(Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter);
+        label_3->setAlignment(Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter);
 
         output->addWidget(label_3);
 
@@ -492,16 +494,16 @@ public:
     {
         MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "VGears Installer", nullptr));
         actionExit->setText(QCoreApplication::translate("MainWindow", "Exit", nullptr));
-        label_4->setText(QCoreApplication::translate("MainWindow", "Original FFVII extracted data:", nullptr));
+        label_4->setText(QCoreApplication::translate("MainWindow", "Final Fantasy VII ISO file: ", nullptr));
         btn_data_src->setText(QCoreApplication::translate("MainWindow", "...", nullptr));
 #if QT_CONFIG(tooltip)
         help_data_src->setToolTip(QCoreApplication::translate("MainWindow", "\n"
-"              <html><head/><body><b>Path to the extracted FFVII (PC version) data.</b><br><br>It must contain (at least) the folders 'kernel' and 'field'.</body></html>\n"
+"              <html><head/><body><b>Select a Final Fantasy VII ISO file.</b><br><br>It must contain (at least) the folders 'kernel' and 'field'.</body></html>\n"
 "             ", nullptr));
 #endif // QT_CONFIG(tooltip)
         help_data_src->setText(QString());
-        isoData->setText(QCoreApplication::translate("MainWindow", "ISO Info: ", nullptr));
-        isoError->setText(QCoreApplication::translate("MainWindow", "Message", nullptr));
+        isoData->setText(QString());
+        isoError->setText(QString());
         label_3->setText(QCoreApplication::translate("MainWindow", "VGears installation directory:", nullptr));
         btn_data_dst->setText(QCoreApplication::translate("MainWindow", "...", nullptr));
 #if QT_CONFIG(tooltip)