diff options
| author | Archez <Archez@users.noreply.github.com> | 2024-11-09 21:15:10 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-11-09 20:15:10 -0600 |
| commit | 449a2ebc70d12a7f5c01d6833100435af152ee6c (patch) | |
| tree | a38b0e2ee32670ef60081145e17e9ae8184c39fe | |
| parent | be70a5a1236c0b7ef309cc239432fd61d7634f6b (diff) | |
Archive version checking and regeneration (#822)
* Add archive version checking and regeneration
* update building docs and copy assets for visual studio
| -rw-r--r-- | docs/BUILDING.md | 81 | ||||
| -rw-r--r-- | mm/2s2h/BenPort.cpp | 290 | ||||
| -rw-r--r-- | mm/2s2h/BenPort.h | 4 | ||||
| -rw-r--r-- | mm/2s2h/Extractor/Extract.cpp | 105 | ||||
| -rw-r--r-- | mm/2s2h/Extractor/Extract.h | 5 | ||||
| -rw-r--r-- | mm/CMakeLists.txt | 14 |
6 files changed, 296 insertions, 203 deletions
diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 8f6c7ec2e..34775cdd2 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -22,7 +22,7 @@ It is recommended that you install Python and Git standalone, the install proces _Note: Be sure to either clone with the ``--recursive`` flag or do ``git submodule update --init`` after cloning to pull in the libultraship submodule!_ -2. Place one or more [compatible](#compatible-roms) roms in the `OTRExporter` directory with namings of your choice +2. After setup and initial build, use the built-in O2R extraction to make your mm.o2r file. _Note: Instructions assume using powershell_ ```powershell @@ -30,22 +30,17 @@ _Note: Instructions assume using powershell_ cd 2ship2harkinian # Setup cmake project -& 'C:\Program Files\CMake\bin\cmake' -S . -B "build/x64" -G "Visual Studio 17 2022" -T v143 -A x64 # -DCMAKE_BUILD_TYPE:STRING=Release (if you're packaging) -# Extract assets & generate OTR (run this anytime you need to regenerate OTR) -& 'C:\Program Files\CMake\bin\cmake.exe' --build .\build\x64 --target ExtractAssets # --config Release (if you're packaging) -# Compile project -& 'C:\Program Files\CMake\bin\cmake.exe' --build .\build\x64 # --config Release (if you're packaging) - -# Now you can run the executable in .\build\x64 +# Add `-DCMAKE_BUILD_TYPE:STRING=Release` if you're packaging +& 'C:\Program Files\CMake\bin\cmake' -S . -B "build/x64" -G "Visual Studio 17 2022" -T v143 -A x64 -# If you need to clean the project you can run -& 'C:\Program Files\CMake\bin\cmake.exe' --build .\build\x64 --target clean +# Generate 2ship.o2r +& 'C:\Program Files\CMake\bin\cmake.exe' --build .\build\x64 --target Generate2ShipOtr -# If you need to regenerate the asset headers to check them into source -& 'C:\Program Files\CMake\bin\cmake.exe' --build .\build\x64 --target ExtractAssetHeaders +# Compile project +# Add `--config Release` if you're packaging +& 'C:\Program Files\CMake\bin\cmake.exe' --build .\build\x64 -# If you need a newer 2ship.o2r only -& 'C:\Program Files\CMake\bin\cmake.exe' --build .\build\x64 --target Generate2ShipOtr +# Now you can run the executable in .\build\x64 or run in Visual Studio ``` ### Developing 2S2H @@ -76,6 +71,19 @@ cd "build/x64" & 'C:\Program Files\CMake\bin\cpack.exe' -G ZIP ``` +### Additional CMake Targets +#### Clean +```powershell +# If you need to clean the project you can run +C:\Program Files\CMake\bin\cmake.exe --build build-cmake --target clean +``` + +#### Regenerate Asset Headers +```powershell +# If you need to regenerate the asset headers to check them into source +C:\Program Files\CMake\bin\cmake.exe --build build-cmake --target ExtractAssetHeaders +``` + ## Linux ### Install dependencies #### Debian/Ubuntu @@ -124,13 +132,16 @@ cd 2ship2harkinian git submodule update --init # Generate Ninja project -cmake -H. -Bbuild-cmake -GNinja # -DCMAKE_BUILD_TYPE:STRING=Release (if you're packaging) -DPython3_EXECUTABLE=$(which python3) (if you are using non-standard Python installations such as PyEnv) +# Add `-DCMAKE_BUILD_TYPE:STRING=Release` if you're packaging +# Add `-DPython3_EXECUTABLE=$(which python3)` if you are using non-standard Python installations such as PyEnv +cmake -H. -Bbuild-cmake -GNinja # Generate 2ship.o2r cmake --build build-cmake --target Generate2ShipOtr # Compile the project -cmake --build build-cmake # --config Release (if you're packaging) +# Add `--config Release` if you're packaging +cmake --build build-cmake # Now you can run the executable in ./build-cmake/mm/2s2h.elf # To develop the project open the repository in VSCode (or your preferred editor) @@ -157,7 +168,6 @@ cmake --build build-cmake --target clean #### Regenerate Asset Headers ```bash # If you need to regenerate the asset headers to check them into source -cp <path to your ROM> OTRExporter cmake --build build-cmake --target ExtractAssetHeaders ``` @@ -174,27 +184,21 @@ git clone https://github.com/HarbourMasters/2ship2harkinian.git cd 2ship2harkinian # Clone the submodule libultraship git submodule update --init -# Copy the baserom to the OTRExporter folder -cp <path to your ROM> OTRExporter + # Generate Ninja project -cmake -H. -Bbuild-cmake -GNinja # -DCMAKE_BUILD_TYPE:STRING=Release (if you're packaging) -# Extract assets & generate OTR (run this anytime you need to regenerate OTR) -cmake --build build-cmake --target ExtractAssets +# Add `-DCMAKE_BUILD_TYPE:STRING=Release` if you're packaging +cmake -H. -Bbuild-cmake -GNinja + +# Generate 2ship.o2r +cmake --build build-cmake --target Generate2ShipOtr + # Compile the project -cmake --build build-cmake # --config Release (if you're packaging) +# Add `--config Release` if you're packaging +cmake --build build-cmake # Now you can run the executable file: ./build-cmake/mm/2s2h-macos # To develop the project open the repository in VSCode (or your preferred editor) - -# If you need to clean the project you can run -cmake --build build-cmake --target clean - -# If you need to regenerate the asset headers to check them into source -cmake --build build-cmake --target ExtractAssetHeaders - -# If you need a newer 2ship.o2r only -cmake --build build-cmake --target Generate2ShipOtr ``` ### Generating a distributable @@ -206,6 +210,19 @@ cd build-cmake cpack ``` +### Additional CMake Targets +#### Clean +```bash +# If you need to clean the project you can run +cmake --build build-cmake --target clean +``` + +#### Regenerate Asset Headers +```bash +# If you need to regenerate the asset headers to check them into source +cmake --build build-cmake --target ExtractAssetHeaders +``` + # Compatible Roms See [`supportedHashes.json`](supportedHashes.json) diff --git a/mm/2s2h/BenPort.cpp b/mm/2s2h/BenPort.cpp index 7179d9db5..5764408ba 100644 --- a/mm/2s2h/BenPort.cpp +++ b/mm/2s2h/BenPort.cpp @@ -10,7 +10,6 @@ #include <File.h> #include <DisplayList.h> #include <Window.h> -#include <GameVersions.h> #include "z64animation.h" #include "z64bgcheck.h" @@ -27,6 +26,7 @@ #include "macros.h" #include <utils/StringHelper.h> #include <nlohmann/json.hpp> +#include "build.h" #include <Fast3D/gfx_pc.h> #include <Fast3D/gfx_rendering_api.h> @@ -145,10 +145,9 @@ OTRGlobals::OTRGlobals() { } } } - std::unordered_set<uint32_t> ValidHashes = { OOT_PAL_MQ, OOT_NTSC_JP_MQ, OOT_NTSC_US_MQ, OOT_PAL_GC_MQ_DBG, - OOT_NTSC_US_10, OOT_NTSC_US_11, OOT_NTSC_US_12, OOT_PAL_10, - OOT_PAL_11, OOT_NTSC_JP_GC_CE, OOT_NTSC_JP_GC, OOT_NTSC_US_GC, - OOT_PAL_GC, OOT_PAL_GC_DBG1, OOT_PAL_GC_DBG2 }; + + std::unordered_set<uint32_t> validHashes = { MM_NTSC_US_10, MM_NTSC_US_GC }; + // tell LUS to reserve 3 SoH specific threads (Game, Audio, Save) context = Ship::Context::CreateInstance("2 Ship 2 Harkinian", appShortName, "2ship2harkinian.json", archiveFiles, {}, 3, @@ -162,8 +161,6 @@ OTRGlobals::OTRGlobals() { (spdlog::level::level_enum)CVarGetInteger("gDeveloperTools.LogLevel", 1)); Ship::Context::GetInstance()->GetLogger()->set_pattern("[%H:%M:%S.%e] [%s:%#] [%l] %v"); - // context = Ship::Context::CreateUninitializedInstance("Ship of Harkinian", appShortName, "shipofharkinian.json"); - auto overlay = context->GetInstance()->GetWindow()->GetGui()->GetGameOverlay(); overlay->LoadFont("Press Start 2P", "fonts/PressStart2P-Regular.ttf", 12.0f); overlay->LoadFont("Fipps", "fonts/Fipps-Regular.otf", 32.0f); @@ -230,59 +227,22 @@ OTRGlobals::OTRGlobals() { // gSaveStateMgr = std::make_shared<SaveStateMgr>(); // gRandomizer = std::make_shared<Randomizer>(); - hasMasterQuest = hasOriginal = false; - - // Move the camera strings from read only memory onto the heap (writable memory) - // This is in OTRGlobals right now because this is a place that will only ever be run once at the beginning of - // startup. We should probably find some code in db_camera that does initialization and only run once, and then - // dealloc on deinitialization. - // cameraStrings = (char**)malloc(sizeof(constCameraStrings)); - // for (int32_t i = 0; i < sizeof(constCameraStrings) / sizeof(char*); i++) { - // // OTRTODO: never deallocated... - // auto dup = strdup(constCameraStrings[i]); - // cameraStrings[i] = dup; - //} auto versions = context->GetResourceManager()->GetArchiveManager()->GetGameVersions(); -#if 0 for (uint32_t version : versions) { - if (!ValidHashes.contains(version)) { + if (!validHashes.contains(version)) { #if defined(__SWITCH__) - SPDLOG_ERROR("Invalid OTR File!"); + SPDLOG_ERROR("Invalid O2R File!"); #elif defined(__WIIU__) Ship::WiiU::ThrowInvalidOTR(); #else - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Invalid OTR File", - "Attempted to load an invalid OTR file. Try regenerating.", nullptr); - SPDLOG_ERROR("Invalid OTR File!"); + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Invalid O2R File", + "Attempted to load an invalid O2R file. Try regenerating.", nullptr); + SPDLOG_ERROR("Invalid O2R File!"); #endif exit(1); } - switch (version) { - case OOT_PAL_MQ: - case OOT_NTSC_JP_MQ: - case OOT_NTSC_US_MQ: - case OOT_PAL_GC_MQ_DBG: - hasMasterQuest = true; - break; - case OOT_NTSC_US_10: - case OOT_NTSC_US_11: - case OOT_NTSC_US_12: - case OOT_PAL_10: - case OOT_PAL_11: - case OOT_NTSC_JP_GC_CE: - case OOT_NTSC_JP_GC: - case OOT_NTSC_US_GC: - case OOT_PAL_GC: - case OOT_PAL_GC_DBG1: - case OOT_PAL_GC_DBG2: - hasOriginal = true; - break; - default: - break; - } } -#endif fontMono = CreateFontWithSize(16.0f, "fonts/Inconsolata-Regular.ttf"); fontMonoLarger = CreateFontWithSize(20.0f, "fonts/Inconsolata-Regular.ttf"); @@ -296,14 +256,6 @@ OTRGlobals::OTRGlobals() { OTRGlobals::~OTRGlobals() { } -bool OTRGlobals::HasMasterQuest() { - return hasMasterQuest; -} - -bool OTRGlobals::HasOriginal() { - return hasOriginal; -} - uint32_t OTRGlobals::GetInterpolationFPS() { if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) { return CVarGetInteger("gInterpolationFPS", 20); @@ -479,23 +431,198 @@ void Ben_ProcessDroppedFiles(std::string filePath) { } } +typedef struct { + uint16_t major; + uint16_t minor; + uint16_t patch; +} ArchiveVersion; + +// Read the port version from an archive file +ArchiveVersion ReadPortVersionFromArchive(std::string archivePath, bool isO2rType) { + ArchiveVersion version = {}; + + // Use a temporary archive instance to load the archive appropriately and read the version file + std::shared_ptr<Ship::Archive> archive; + if (isO2rType) { + archive = make_shared<Ship::O2rArchive>(archivePath); + } else { + archive = make_shared<Ship::OtrArchive>(archivePath); + } + if (archive->Open()) { + auto t = archive->LoadFile("portVersion", std::make_shared<Ship::ResourceInitData>()); + if (t != nullptr && t->IsLoaded) { + auto stream = std::make_shared<Ship::MemoryStream>(t->Buffer->data(), t->Buffer->size()); + auto reader = std::make_shared<Ship::BinaryReader>(stream); + Ship::Endianness endianness = (Ship::Endianness)reader->ReadUByte(); + reader->SetEndianness(endianness); + version.major = reader->ReadUInt16(); + version.minor = reader->ReadUInt16(); + version.patch = reader->ReadUInt16(); + } + archive->Close(); + } + + return version; +} + +// Check that a 2ship.o2r exists and matches the version of 2ship running +// Otherwise show a message and exit +void Check2ShipArchiveVersion(std::string archivePath) { + std::string msg; + +#if defined(__SWITCH__) + msg = "\x1b[4;2HPlease re-extract it from the download." + "\x1b[6;2HPress the Home button to exit..."; +#elif defined(__WIIU__) + msg = "Please extract the 2ship.o2r from the 2 Ship 2 Harkinian download\nto your folder.\n\n" + "Press and hold the power button to shutdown..."; +#else + msg = "Please extract the 2ship.o2r from the 2 Ship 2 Harkinian download to your folder.\n\nExiting..."; +#endif + + if (!std::filesystem::exists(archivePath)) { +#if not defined(__SWITCH__) && not defined(__WIIU__) + Extractor::ShowErrorBox("2ship.o2r file is missing", msg.c_str()); + exit(1); +#elif defined(__SWITCH__) + Ship::Switch::PrintErrorMessageToScreen(("\x1b[2;2HYou are missing the 2ship.o2r file." + msg).c_str()); +#elif defined(__WIIU__) + OSFatal(("You are missing the 2ship.o2r file\n\n" + msg).c_str()); +#endif + } + + ArchiveVersion archiveVer = ReadPortVersionFromArchive(archivePath, true); + + if (archiveVer.major != gBuildVersionMajor || archiveVer.minor != gBuildVersionMinor || + archiveVer.patch != gBuildVersionPatch) { +#if not defined(__SWITCH__) && not defined(__WIIU__) + Extractor::ShowErrorBox("2ship.o2r file version does not match", msg.c_str()); + exit(1); +#elif defined(__SWITCH__) + Ship::Switch::PrintErrorMessageToScreen(("\x1b[2;2HYou have an old 2ship.o2r file." + msg).c_str()); +#elif defined(__WIIU__) + OSFatal(("You have an old 2ship.o2r file\n\n" + msg).c_str()); +#endif + } +} + +// Checks the program version stored in the o2r and compares the major/minor value to 2ship +// For Windows/Mac/Linux if the version doesn't match, offer to regenerate it +void DetectArchiveVersion(std::string fileName, bool isO2rType) { + bool isArchiveOld = false; + std::string archivePath = Ship::Context::LocateFileAcrossAppDirs(fileName, appShortName); + + // Doesn't exist so nothing to do here + if (!std::filesystem::exists(archivePath)) { + return; + } + + ArchiveVersion archiveVer = ReadPortVersionFromArchive(archivePath, isO2rType); + + // Check both major and minor for game archives + if (archiveVer.major != gBuildVersionMajor || archiveVer.minor != gBuildVersionMinor) { + isArchiveOld = true; + } + + if (isArchiveOld) { +#if not defined(__SWITCH__) && not defined(__WIIU__) + char msgBuf[250]; + char version[18]; // 5 digits for int16_max (x3) + separators + terminator + + if (archiveVer.major != 0 || archiveVer.minor != 0 || archiveVer.patch != 0) { + snprintf(version, 18, "%d.%d.%d", archiveVer.major, archiveVer.minor, archiveVer.patch); + } else { + snprintf(version, 18, "no version found"); + } + + snprintf(msgBuf, 250, + "The %s file was generated with a different version of 2 Ship 2 Harkinian.\n" + "O2R version: %s\n\n" + "You must regenerate to be able to play, otherwise the program will exit.\n" + "Would you like to regenerate it now?", + fileName.c_str(), version); + + if (Extractor::ShowYesNoBox("Old O2R File Found", msgBuf) == IDYES) { + std::string installPath = Ship::Context::GetAppBundlePath(); + if (!std::filesystem::exists(installPath + "/assets/extractor")) { + Extractor::ShowErrorBox( + "Extractor assets not found", + "Unable to regenerate. Missing assets/extractor folder needed to generate O2R file.\n\nExiting..."); + exit(1); + } + + Extractor extract; + if (!extract.Run(Ship::Context::GetAppDirectoryPath(appShortName))) { + Extractor::ShowErrorBox("Error", "An error occurred, no O2R file was generated.\n\nExiting..."); + exit(1); + } + + // We can only regenerate O2R archives, so we should just delete the old OTR file + if (!isO2rType) { + std::filesystem::remove(archivePath); + } + + extract.CallZapd(installPath, Ship::Context::GetAppDirectoryPath(appShortName)); + + // Rename the new O2R with the previously used extension + if (isO2rType) { + std::filesystem::rename(Ship::Context::LocateFileAcrossAppDirs("mm.o2r", appShortName), archivePath); + } + } else { + exit(1); + } + +#elif defined(__SWITCH__) + Ship::Switch::PrintErrorMessageToScreen("\x1b[2;2HYou've launched the 2Ship with an old game O2R file." + "\x1b[4;2HPlease regenerate a new game O2R and relaunch." + "\x1b[6;2HPress the Home button to exit..."); +#elif defined(__WIIU__) + OSFatal("You've launched the 2Ship with an old a game O2R file.\n\n" + "Please generate a game O2R and relaunch.\n\n" + "Press and hold the Power button to shutdown..."); +#endif + } +} + extern "C" void InitOTR() { + +#ifdef __SWITCH__ + Ship::Switch::Init(Ship::PreInitPhase); +#elif defined(__WIIU__) + Ship::WiiU::Init(appShortName); +#endif + + // BENTODO: OTRExporter is filling the version file with garbage. Uncomment once fixed. + // Check2ShipArchiveVersion(Ship::Context::GetPathRelativeToAppBundle("2ship.o2r")); + + std::string mmPathO2R = Ship::Context::LocateFileAcrossAppDirs("mm.o2r", appShortName); + std::string mmPathZIP = Ship::Context::LocateFileAcrossAppDirs("mm.zip", appShortName); + std::string mmPathOtr = Ship::Context::LocateFileAcrossAppDirs("mm.otr", appShortName); + + // Check game archives in preferred order + if (std::filesystem::exists(mmPathO2R)) { + DetectArchiveVersion("mm.o2r", true); + } else if (std::filesystem::exists(mmPathZIP)) { + DetectArchiveVersion("mm.zip", true); + } else if (std::filesystem::exists(mmPathOtr)) { + DetectArchiveVersion("mm.otr", false); + } + #if not defined(__SWITCH__) && not defined(__WIIU__) - if (!std::filesystem::exists(Ship::Context::LocateFileAcrossAppDirs("mm.o2r", appShortName)) && - !std::filesystem::exists(Ship::Context::LocateFileAcrossAppDirs("mm.zip", appShortName)) && - !std::filesystem::exists(Ship::Context::LocateFileAcrossAppDirs("mm.otr", appShortName))) { + if (!std::filesystem::exists(mmPathO2R) && !std::filesystem::exists(mmPathZIP) && + !std::filesystem::exists(mmPathOtr)) { std::string installPath = Ship::Context::GetAppBundlePath(); if (!std::filesystem::exists(installPath + "/assets/extractor")) { Extractor::ShowErrorBox( "Extractor assets not found", - "No game O2R files found. Missing assets/extractor folder needed to generate O2R file. Exiting..."); + "No game O2R file found. Missing assets/extractor folder needed to generate O2R file. Exiting..."); exit(1); } - if (Extractor::ShowYesNoBox("No O2R Files", "No O2R files found. Generate one now?") == IDYES) { + if (Extractor::ShowYesNoBox("No O2R File", "No O2R files found. Generate one now?") == IDYES) { Extractor extract; - if (!extract.Run()) { - Extractor::ShowErrorBox("Error", "An error occurred, no OTR file was generated. Exiting..."); + if (!extract.Run(Ship::Context::GetAppDirectoryPath(appShortName))) { + Extractor::ShowErrorBox("Error", "An error occurred, no O2R file was generated. Exiting..."); exit(1); } extract.CallZapd(installPath, Ship::Context::GetAppDirectoryPath(appShortName)); @@ -505,12 +632,6 @@ extern "C" void InitOTR() { } #endif -#ifdef __SWITCH__ - Ship::Switch::Init(Ship::PreInitPhase); -#elif defined(__WIIU__) - Ship::WiiU::Init("soh"); -#endif - OTRGlobals::Instance = new OTRGlobals(); GameInteractor::Instance = new GameInteractor(); LoadGuiTextures(); @@ -839,22 +960,8 @@ extern "C" uint32_t ResourceMgr_GetGamePlatform(int index) { Ship::Context::GetInstance()->GetResourceManager()->GetArchiveManager()->GetGameVersions()[index]; switch (version) { - case OOT_NTSC_US_10: - case OOT_NTSC_US_11: - case OOT_NTSC_US_12: - case OOT_PAL_10: - case OOT_PAL_11: case MM_NTSC_US_10: return GAME_PLATFORM_N64; - case OOT_NTSC_JP_GC: - case OOT_NTSC_US_GC: - case OOT_PAL_GC: - case OOT_NTSC_JP_MQ: - case OOT_NTSC_US_MQ: - case OOT_PAL_MQ: - case OOT_PAL_GC_DBG1: - case OOT_PAL_GC_DBG2: - case OOT_PAL_GC_MQ_DBG: case MM_NTSC_US_GC: return GAME_PLATFORM_GC; } @@ -865,24 +972,9 @@ extern "C" uint32_t ResourceMgr_GetGameRegion(int index) { Ship::Context::GetInstance()->GetResourceManager()->GetArchiveManager()->GetGameVersions()[index]; switch (version) { - case OOT_NTSC_US_10: - case OOT_NTSC_US_11: - case OOT_NTSC_US_12: - case OOT_NTSC_JP_GC: - case OOT_NTSC_US_GC: - case OOT_NTSC_JP_MQ: - case OOT_NTSC_US_MQ: case MM_NTSC_US_10: case MM_NTSC_US_GC: return GAME_REGION_NTSC; - case OOT_PAL_10: - case OOT_PAL_11: - case OOT_PAL_GC: - case OOT_PAL_MQ: - case OOT_PAL_GC_DBG1: - case OOT_PAL_GC_DBG2: - case OOT_PAL_GC_MQ_DBG: - return GAME_REGION_PAL; } } diff --git a/mm/2s2h/BenPort.h b/mm/2s2h/BenPort.h index f67607bbc..4c7791da1 100644 --- a/mm/2s2h/BenPort.h +++ b/mm/2s2h/BenPort.h @@ -36,16 +36,12 @@ class OTRGlobals { OTRGlobals(); ~OTRGlobals(); - bool HasMasterQuest(); - bool HasOriginal(); uint32_t GetInterpolationFPS(); std::shared_ptr<std::vector<std::string>> ListFiles(std::string path); private: ImFont* CreateFontWithSize(float size, std::string fontPath = ""); void CheckSaveFile(size_t sramSize) const; - bool hasMasterQuest; - bool hasOriginal; }; uint32_t IsGameMasterQuest(); diff --git a/mm/2s2h/Extractor/Extract.cpp b/mm/2s2h/Extractor/Extract.cpp index 16c823d38..1b6b23738 100644 --- a/mm/2s2h/Extractor/Extract.cpp +++ b/mm/2s2h/Extractor/Extract.cpp @@ -51,42 +51,18 @@ extern "C" uint32_t CRC32C(unsigned char* data, size_t dataSize); -static constexpr uint32_t OOT_PAL_GC = 0x09465AC3; -static constexpr uint32_t OOT_PAL_MQ = 0x1D4136F3; -static constexpr uint32_t OOT_PAL_GC_DBG1 = 0x871E1C92; // 03-21-2002 build -static constexpr uint32_t OOT_PAL_GC_DBG2 = 0x87121EFE; // 03-13-2002 build -static constexpr uint32_t OOT_PAL_GC_MQ_DBG = 0x917D18F6; -static constexpr uint32_t OOT_PAL_10 = 0xB044B569; -static constexpr uint32_t OOT_PAL_11 = 0xB2055FBD; - static constexpr uint32_t MM_US_10 = 0x5354631C; static constexpr uint32_t MM_US_GC = 0xB443EB08; static const std::unordered_map<uint32_t, const char*> verMap = { - { MM_US_10, "US 1.0" }, { MM_US_GC, "US GC" }, - //{ OOT_PAL_GC, "PAL Gamecube" }, - //{ OOT_PAL_MQ, "PAL MQ" }, - //{ OOT_PAL_GC_DBG1, "PAL Debug 1" }, - //{ OOT_PAL_GC_DBG2, "PAL Debug 2" }, - //{ OOT_PAL_GC_MQ_DBG, "PAL MQ Debug" }, - //{ OOT_PAL_10, "PAL N64 1.0" }, - //{ OOT_PAL_11, "PAL N64 1.1" }, + { MM_US_10, "US 1.0" }, + { MM_US_GC, "US GC" }, }; // TODO only check the first 54MB of the rom. static constexpr std::array<const uint32_t, 10> goodCrcs = { 0x96F49400, // MM US 1.0 32MB 0xBB434787, // MM GC - // 0xfa8c0555, // MQ DBG 64MB (Original overdump) - // 0x8652ac4c, // MQ DBG 64MB - // 0x5B8A1EB7, // MQ DBG 64MB (Empty overdump) - // 0x1f731ffe, // MQ DBG 54MB - // 0x044b3982, // NMQ DBG 54MB - // 0xEB15D7B9, // NMQ DBG 64MB - // 0xDA8E61BF, // GC PAL - // 0x7A2FAE68, // GC MQ PAL - // 0xFD9913B1, // N64 PAL 1.0 - // 0xE033FBBA, // N64 PAL 1.1 }; enum class ButtonId : int { @@ -112,7 +88,13 @@ void Extractor::ShowSizeErrorBox() const { } void Extractor::ShowCrcErrorBox() const { - ShowErrorBox("Rom CRC invalid", "Rom CRC did not match the list of known good roms. Please find another."); + ShowErrorBox("Rom CRC invalid", + "Rom CRC did not match the list of known compatible roms. Please find another.\n\n" + "Visit https://2ship.equipment/ to validate your ROM and see a list of compatible versions"); +} + +void Extractor::ShowCompressedErrorBox() const { + ShowErrorBox("File is Compressed", "The selected file appears to be compressed. Please extract before using."); } int Extractor::ShowRomPickBox(uint32_t verCrc) const { @@ -225,7 +207,7 @@ void Extractor::GetRoms(std::vector<std::string>& roms) { //} #elif unix // Open the directory of the app. - DIR* d = opendir("."); + DIR* d = opendir(mSearchPath.c_str()); struct dirent* dir; if (d != NULL) { @@ -247,7 +229,7 @@ void Extractor::GetRoms(std::vector<std::string>& roms) { } closedir(d); #else - for (const auto& file : std::filesystem::directory_iterator("./")) { + for (const auto& file : std::filesystem::directory_iterator(mSearchPath)) { if (file.is_directory()) continue; if ((file.path().extension() == ".n64") || (file.path().extension() == ".z64") || @@ -297,7 +279,7 @@ bool Extractor::GetRomPathFromBox() { } mCurrentRomPath = nameBuffer; #else - auto selection = pfd::open_file("Select a file", ".", { "N64 Roms", "*.z64 *.n64 *.v64" }).result(); + auto selection = pfd::open_file("Select a file", mSearchPath, { "N64 Roms", "*.z64 *.n64 *.v64" }).result(); if (selection.empty()) { return false; @@ -318,11 +300,6 @@ size_t Extractor::GetCurRomSize() const { } bool Extractor::ValidateAndFixRom() { - // The MQ debug rom sometimes has the header patched to look like a US rom. Change it back - if (GetRomVerCrc() == OOT_PAL_GC_MQ_DBG) { - mRomData[0x3E] = 'P'; - } - const uint32_t actualCrc = CRC32C(mRomData.get(), mCurRomSize); for (const uint32_t crc : goodCrcs) { @@ -333,6 +310,25 @@ bool Extractor::ValidateAndFixRom() { return false; } +// The file box will only allow selecting an n64 rom but typing in the file name will allow selecting anything. +bool Extractor::ValidateNotCompressed() const { + // ZIP file header + if (mRomData[0] == 'P' && mRomData[1] == 'K' && mRomData[2] == 0x03 && mRomData[3] == 0x04) { + return false; + } + // RAR file header. Only the first 4 bytes. + if (mRomData[0] == 'R' && mRomData[1] == 'a' && mRomData[2] == 'r' && mRomData[3] == 0x21) { + return false; + } + // 7z file header. 37 7A BC AF 27 1C + if (mRomData[0] == '7' && mRomData[1] == 'z' && mRomData[2] == 0xBC && mRomData[3] == 0xAF && mRomData[4] == 0x27 && + mRomData[5] == 0x1C) { + return false; + } + + return true; +} + bool Extractor::ValidateRomSize() const { if (mCurRomSize != MB32 && mCurRomSize != MB54 && mCurRomSize != MB64) { return false; @@ -341,6 +337,10 @@ bool Extractor::ValidateRomSize() const { } bool Extractor::ValidateRom(bool skipCrcTextBox) { + if (!ValidateNotCompressed()) { + ShowCompressedErrorBox(); + return false; + } if (!ValidateRomSize()) { ShowSizeErrorBox(); return false; @@ -410,10 +410,12 @@ bool Extractor::ManuallySearchForRomMatchingType(RomSearchMode searchMode) { return true; } -bool Extractor::Run(RomSearchMode searchMode) { +bool Extractor::Run(std::string searchPath, RomSearchMode searchMode) { std::vector<std::string> roms; std::ifstream inFile; + mSearchPath = searchPath; + GetRoms(roms); FilterRoms(roms, searchMode); @@ -456,8 +458,10 @@ bool Extractor::Run(RomSearchMode searchMode) { if (rom == roms.back()) { ShowCrcErrorBox(); } else { - ShowErrorBox("Rom CRC invalid", - "Rom CRC did not match the list of known good roms. Trying the next one..."); + ShowErrorBox( + "Rom CRC invalid", + "Rom CRC did not match the list of known compatible roms. Trying the next one...\n\n" + "Visit https://2ship.equipment/ to validate your ROM and see a list of compatible versions"); } continue; } @@ -480,34 +484,11 @@ bool Extractor::Run(RomSearchMode searchMode) { } bool Extractor::IsMasterQuest() const { - switch (GetRomVerCrc()) { - case OOT_PAL_MQ: - case OOT_PAL_GC_MQ_DBG: - return true; - case OOT_PAL_10: - case OOT_PAL_11: - case OOT_PAL_GC: - case OOT_PAL_GC_DBG1: - return false; - default: - UNREACHABLE; - } + return false; } const char* Extractor::GetZapdVerStr() const { switch (GetRomVerCrc()) { - case OOT_PAL_GC: - return "GC_NMQ_PAL_F"; - case OOT_PAL_MQ: - return "GC_MQ_PAL_F"; - case OOT_PAL_GC_DBG1: - return "GC_NMQ_D"; - case OOT_PAL_GC_MQ_DBG: - return "GC_MQ_D"; - case OOT_PAL_10: - return "N64_PAL_10"; - case OOT_PAL_11: - return "N64_PAL_11"; case MM_US_10: return "N64_US"; case MM_US_GC: diff --git a/mm/2s2h/Extractor/Extract.h b/mm/2s2h/Extractor/Extract.h index 4109c6196..ae4fa740a 100644 --- a/mm/2s2h/Extractor/Extract.h +++ b/mm/2s2h/Extractor/Extract.h @@ -28,6 +28,7 @@ enum class RomSearchMode { class Extractor { std::unique_ptr<unsigned char[]> mRomData = std::make_unique<unsigned char[]>(MB64); std::string mCurrentRomPath; + std::string mSearchPath; size_t mCurRomSize = 0; bool GetRomPathFromBox(); @@ -38,6 +39,7 @@ class Extractor { bool ValidateRomSize() const; bool ValidateRom(bool skipCrcBox = false); + bool ValidateNotCompressed() const; const char* GetZapdVerStr() const; void SetRomInfo(const std::string& path); @@ -46,6 +48,7 @@ class Extractor { void GetRoms(std::vector<std::string>& roms); void ShowSizeErrorBox() const; void ShowCrcErrorBox() const; + void ShowCompressedErrorBox() const; int ShowRomPickBox(uint32_t verCrc) const; bool ManuallySearchForRom(); bool ManuallySearchForRomMatchingType(RomSearchMode searchMode); @@ -56,7 +59,7 @@ class Extractor { static void ShowErrorBox(const char* title, const char* text); bool IsMasterQuest() const; - bool Run(RomSearchMode searchMode = RomSearchMode::Both); + bool Run(std::string searchPath, RomSearchMode searchMode = RomSearchMode::Both); bool CallZapd(std::string installPath, std::string exportdir); const char* GetZapdStr(); std::string Mkdtemp(); diff --git a/mm/CMakeLists.txt b/mm/CMakeLists.txt index 4640ff349..79bbab7fb 100644 --- a/mm/CMakeLists.txt +++ b/mm/CMakeLists.txt @@ -524,18 +524,22 @@ endif() ################################################################################ # Pre build events ################################################################################ +if (CMAKE_GENERATOR MATCHES "Visual Studio") + set(VS_COPY_ASSETS_CMD ${CMAKE_COMMAND} -E copy_directory $<TARGET_FILE_DIR:2ship>/assets ${CMAKE_BINARY_DIR}/mm/assets) +endif() if(NOT CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|CafeOS") add_custom_command( - TARGET ${PROJECT_NAME} - POST_BUILD - COMMENT "Copying asset xmls..." + TARGET ${PROJECT_NAME} + POST_BUILD + COMMENT "Copying asset xmls..." COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/mm/assets/extractor $<TARGET_FILE_DIR:2ship>/assets/extractor - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/mm/assets/xml $<TARGET_FILE_DIR:2ship>/assets/extractor/xmls + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/mm/assets/xml $<TARGET_FILE_DIR:2ship>/assets/extractor/xmls COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/OTRExporter/CFG/filelists $<TARGET_FILE_DIR:2ship>/assets/extractor/filelists COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_FILE_DIR:2ship>/assets/extractor/symbols COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/OTRExporter/CFG/ActorList_MM.txt $<TARGET_FILE_DIR:2ship>/assets/extractor/symbols COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/OTRExporter/CFG/ObjectList_MM.txt $<TARGET_FILE_DIR:2ship>/assets/extractor/symbols - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/OTRExporter/CFG/SymbolMap_MM.txt $<TARGET_FILE_DIR:2ship>/assets/extractor/symbols + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/OTRExporter/CFG/SymbolMap_MM.txt $<TARGET_FILE_DIR:2ship>/assets/extractor/symbols + COMMAND ${VS_COPY_ASSETS_CMD} ) endif() ################################################################################ |
