summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArchez <Archez@users.noreply.github.com>2024-11-09 21:15:10 -0500
committerGitHub <noreply@github.com>2024-11-09 20:15:10 -0600
commit449a2ebc70d12a7f5c01d6833100435af152ee6c (patch)
treea38b0e2ee32670ef60081145e17e9ae8184c39fe
parentbe70a5a1236c0b7ef309cc239432fd61d7634f6b (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.md81
-rw-r--r--mm/2s2h/BenPort.cpp290
-rw-r--r--mm/2s2h/BenPort.h4
-rw-r--r--mm/2s2h/Extractor/Extract.cpp105
-rw-r--r--mm/2s2h/Extractor/Extract.h5
-rw-r--r--mm/CMakeLists.txt14
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()
################################################################################