mirror of
https://github.com/HarbourMasters/2ship2harkinian.git
synced 2024-11-22 21:49:54 +00:00
Archive version checking and regeneration (#822)
* Add archive version checking and regeneration * update building docs and copy assets for visual studio
This commit is contained in:
parent
be70a5a123
commit
449a2ebc70
@ -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)
|
||||
# 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
|
||||
|
||||
# Now you can run the executable in .\build\x64
|
||||
|
||||
# If you need to clean the project you can run
|
||||
& 'C:\Program Files\CMake\bin\cmake.exe' --build .\build\x64 --target clean
|
||||
|
||||
# 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
|
||||
|
||||
# If you need a newer 2ship.o2r only
|
||||
# Generate 2ship.o2r
|
||||
& 'C:\Program Files\CMake\bin\cmake.exe' --build .\build\x64 --target Generate2ShipOtr
|
||||
|
||||
# Compile project
|
||||
# Add `--config Release` if you're packaging
|
||||
& 'C:\Program Files\CMake\bin\cmake.exe' --build .\build\x64
|
||||
|
||||
# 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)
|
||||
|
||||
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void InitOTR() {
|
||||
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__)
|
||||
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))) {
|
||||
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(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
################################################################################
|
||||
|
Loading…
Reference in New Issue
Block a user