[OTR Archive] Store soh version in OTR files and verify on launch/ask to regenerate (#3218)

* parse sohver arg and store version file in otr

* parse args for soh.otr gen only

* pass soh version from built in extractor

* update launch scripts, cmake and extract steps to pass soh version

* check otr versions and error or ask to regenerate

* add wiiu core header for osfatal

* review feedback

* remove soh dummy version for lus change instead

* only configure linux script for linux

* change lus commit

* rename soh version to port version

* fix submodules

* bump OTRExporter

* clean up error messages for switch/wiiu

* strings not char array

* typo

* init wiiu before otr detection

* Add message for mac/linux extraction

* remove unneeded exits

* change version number types to u16 to fix 32bit devices

* bump otrexporter
This commit is contained in:
Adam Bird 2023-11-05 12:34:39 -05:00 committed by GitHub
parent 13a8a1a5cc
commit 7e9efeeadb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 187 additions and 27 deletions

View File

@ -24,7 +24,6 @@ if (CPACK_GENERATOR MATCHES "Bundle")
set(CPACK_BUNDLE_NAME "soh")
set(CPACK_BUNDLE_PLIST "macosx/Info.plist")
set(CPACK_BUNDLE_ICON "macosx/soh.icns")
set(CPACK_BUNDLE_STARTUP_COMMAND "../soh/macosx/soh-macos.sh")
set(CPACK_BUNDLE_STARTUP_COMMAND "macosx/soh-macos.sh")
set(CPACK_BUNDLE_APPLE_CERT_APP "-")
endif()

View File

@ -97,7 +97,7 @@ set_property(TARGET soh PROPERTY APPIMAGE_DESKTOP_FILE "${CMAKE_SOURCE_DIR}/scri
set_property(TARGET soh PROPERTY APPIMAGE_ICON_FILE "${CMAKE_BINARY_DIR}/sohIcon.png")
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
install(PROGRAMS "${CMAKE_SOURCE_DIR}/scripts/linux/appimage/soh.sh" DESTINATION . COMPONENT appimage)
install(PROGRAMS "${CMAKE_BINARY_DIR}/linux/soh.sh" DESTINATION . COMPONENT appimage)
install(FILES "${CMAKE_SOURCE_DIR}/soh.otr" DESTINATION . COMPONENT ship)
install(TARGETS ZAPD DESTINATION ./assets/extractor COMPONENT extractor)
install(DIRECTORY "${CMAKE_SOURCE_DIR}/soh/assets/extractor/" DESTINATION ./assets/extractor COMPONENT extractor)
@ -124,7 +124,7 @@ add_custom_target(
ExtractAssets
# CMake versions prior to 3.17 do not have the rm command, use remove instead for older versions
COMMAND ${CMAKE_COMMAND} -E $<IF:$<VERSION_LESS:${CMAKE_VERSION},3.17>,remove,rm> -f oot.otr oot-mq.otr soh.otr
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets.py -z "$<TARGET_FILE:ZAPD>" --non-interactive --xml-root ../soh/assets/xml --custom-otr-file soh.otr "--custom-assets-path" ${CMAKE_CURRENT_SOURCE_DIR}/soh/assets/custom
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets.py -z "$<TARGET_FILE:ZAPD>" --non-interactive --xml-root ../soh/assets/xml --custom-otr-file soh.otr "--custom-assets-path" ${CMAKE_CURRENT_SOURCE_DIR}/soh/assets/custom --port-ver "${CMAKE_PROJECT_VERSION}"
COMMAND ${CMAKE_COMMAND} -DSYSTEM_NAME=${CMAKE_SYSTEM_NAME} -DTARGET_DIR="$<TARGET_FILE_DIR:ZAPD>" -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -DBINARY_DIR=${CMAKE_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/copy-existing-otrs.cmake
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter
COMMENT "Running asset extraction..."
@ -146,7 +146,7 @@ add_custom_target(
GenerateSohOtr
# CMake versions prior to 3.17 do not have the rm command, use remove instead for older versions
COMMAND ${CMAKE_COMMAND} -E $<IF:$<VERSION_LESS:${CMAKE_VERSION},3.17>,remove,rm> -f soh.otr
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets.py -z "$<TARGET_FILE:ZAPD>" --norom --custom-otr-file soh.otr "--custom-assets-path" ${CMAKE_CURRENT_SOURCE_DIR}/soh/assets/custom
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets.py -z "$<TARGET_FILE:ZAPD>" --norom --custom-otr-file soh.otr "--custom-assets-path" ${CMAKE_CURRENT_SOURCE_DIR}/soh/assets/custom --port-ver "${CMAKE_PROJECT_VERSION}"
COMMAND ${CMAKE_COMMAND} -DSYSTEM_NAME=${CMAKE_SYSTEM_NAME} -DTARGET_DIR="$<TARGET_FILE_DIR:ZAPD>" -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -DBINARY_DIR=${CMAKE_BINARY_DIR} -DONLYSOHOTR=On -P ${CMAKE_CURRENT_SOURCE_DIR}/copy-existing-otrs.cmake
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter
COMMENT "Generating soh.otr..."

@ -1 +1 @@
Subproject commit f1bc0a726813d7e70ad471fdebd080e6fd77996a
Subproject commit cde9a3b655570370e4ed4928e8c9f3a0f631c52e

View File

@ -171,7 +171,7 @@ while [[ (! -e "$SHIP_HOME"/oot.otr) || (! -e "$SHIP_HOME"/oot-mq.otr) ]]; do
else
echo "Processing..."
fi
assets/extractor/ZAPD.out ed -eh -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR --otrfile "${OTRNAME}" > /dev/null 2>&1
assets/extractor/ZAPD.out ed -eh -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR --otrfile "${OTRNAME}" --portVer "@CMAKE_PROJECT_VERSION@" > /dev/null 2>&1
cp "$ASSETDIR"/"$OTRNAME" "$SHIP_HOME"
fi
else

View File

@ -757,11 +757,16 @@ INSTALL(FILES $<TARGET_PDB_FILE:soh> DESTINATION ./debug COMPONENT ship)
INSTALL(FILES ${CMAKE_BINARY_DIR}/soh/soh.otr DESTINATION . COMPONENT ship)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/../scripts/linux/appimage/soh.sh.in ${CMAKE_BINARY_DIR}/linux/soh.sh @ONLY)
endif()
find_program(CURL NAMES curl DOC "Path to the curl program. Used to download files.")
execute_process(COMMAND ${CURL} -sSfL https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt -o ${CMAKE_BINARY_DIR}/gamecontrollerdb.txt OUTPUT_VARIABLE RESULT)
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/macosx/Info.plist.in ${CMAKE_BINARY_DIR}/macosx/Info.plist @ONLY)
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/macosx/soh-macos.sh.in ${CMAKE_BINARY_DIR}/macosx/soh-macos.sh @ONLY)
INSTALL(FILES ${CMAKE_BINARY_DIR}/gamecontrollerdb.txt DESTINATION ../MacOS COMPONENT ship)
INSTALL(FILES ${CMAKE_BINARY_DIR}/soh/soh.otr DESTINATION ../Resources COMPONENT ship)
elseif(NOT "${CMAKE_SYSTEM_NAME}" MATCHES "NintendoSwitch|CafeOS")

View File

@ -46,9 +46,9 @@ extern "C"
extern OSViMode osViModeFpalLan1;
extern u32 __additional_scanline;
extern u8 gBuildVersion[];
extern s16 gBuildVersionMajor;
extern s16 gBuildVersionMinor;
extern s16 gBuildVersionPatch;
extern u16 gBuildVersionMajor;
extern u16 gBuildVersionMinor;
extern u16 gBuildVersionPatch;
extern u8 gBuildTeam[];
extern u8 gBuildDate[];
extern u8 gBuildMakeOption[];

View File

@ -229,7 +229,7 @@ if [ ! -e "$SHIP_HOME"/oot.otr ] || [ ! -e "$SHIP_HOME"/oot-mq.otr ]; then
fi
osascript -e 'display notification "Generating OTR..." with title "Ship Of Harkinian"'
assets/extractor/ZAPD.out ed -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR
assets/extractor/ZAPD.out ed -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR --portVer "@CMAKE_PROJECT_VERSION@"
if [ -e "$ASSETDIR"/oot.otr ]; then
osascript -e 'display notification "OTR successfully generated" with title "Ship Of Harkinian"'
cp "$ASSETDIR"/oot.otr "$SHIP_HOME"/"$OTRNAME"

View File

@ -7,6 +7,7 @@
#include "Extract.h"
#include "portable-file-dialogs.h"
#include <Utils/BitConverter.h>
#include "variables.h"
#ifdef unix
#include <dirent.h>
@ -557,9 +558,10 @@ std::string Extractor::Mkdtemp() {
extern "C" int zapd_main(int argc, char** argv);
bool Extractor::CallZapd(std::string installPath, std::string exportdir) {
constexpr int argc = 16;
constexpr int argc = 18;
char xmlPath[1024];
char confPath[1024];
char portVersion[18]; // 5 digits for int16_max (x3) + separators + terminator
std::array<const char*, argc> argv;
const char* version = GetZapdVerStr();
const char* otrFile = IsMasterQuest() ? "oot-mq.otr" : "oot.otr";
@ -581,6 +583,7 @@ bool Extractor::CallZapd(std::string installPath, std::string exportdir) {
snprintf(xmlPath, 1024, "assets/extractor/xmls/%s", version);
snprintf(confPath, 1024, "assets/extractor/Config_%s.xml", version);
snprintf(portVersion, 18, "%d.%d.%d", gBuildVersionMajor, gBuildVersionMinor, gBuildVersionPatch);
argv[0] = "ZAPD";
argv[1] = "ed";
@ -598,6 +601,8 @@ bool Extractor::CallZapd(std::string installPath, std::string exportdir) {
argv[13] = "OTR";
argv[14] = "--otrfile";
argv[15] = otrFile;
argv[16] = "--portVer";
argv[17] = portVersion;
#ifdef _WIN32
// Grab a handle to the command window.
@ -606,6 +611,9 @@ bool Extractor::CallZapd(std::string installPath, std::string exportdir) {
// Normally the command window is hidden. We want the window to be shown here so the user can see the progess of the extraction.
ShowWindow(cmdWindow, SW_SHOW);
SetWindowPos(cmdWindow, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
#else
// Show extraction in background message until linux/mac can have visual progress
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Extracting", "Extraction will now begin in the background.\n\nPlease be patient for the process to finish. Do not close the main program.", nullptr);
#endif
zapd_main(argc, (char**)argv.data());
@ -623,4 +631,3 @@ bool Extractor::CallZapd(std::string installPath, std::string exportdir) {
return 0;
}

View File

@ -66,6 +66,7 @@
#include <port/switch/SwitchImpl.h>
#elif defined(__WIIU__)
#include <port/wiiu/WiiUImpl.h>
#include <coreinit/debug.h> // OSFatal
#endif
@ -743,15 +744,156 @@ extern "C" void OTRExtScanner() {
}
}
typedef struct {
uint16_t major;
uint16_t minor;
uint16_t patch;
} OTRVersion;
// Read the port version from an OTR file
OTRVersion ReadPortVersionFromOTR(std::string otrPath) {
OTRVersion version = {};
// Use a temporary archive instance to load the otr and read the version file
auto archive = std::make_shared<LUS::Archive>(otrPath, "", std::unordered_set<uint32_t>(), false);
if (archive->IsMainMPQValid()) {
auto t = archive->LoadFile("portVersion", false);
if (t != nullptr && t->IsLoaded) {
auto stream = std::make_shared<LUS::MemoryStream>(t->Buffer.data(), t->Buffer.size());
auto reader = std::make_shared<LUS::BinaryReader>(stream);
LUS::Endianness endianness = (LUS::Endianness)reader->ReadUByte();
reader->SetEndianness(endianness);
version.major = reader->ReadUInt16();
version.minor = reader->ReadUInt16();
version.patch = reader->ReadUInt16();
}
}
archive = nullptr;
return version;
}
// Check that a soh.otr exists and matches the version of soh running
// Otherwise show a message and exit
void CheckSoHOTRVersion(std::string otrPath) {
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 soh.otr from the Ship of Harkinian download\nto your folder.\n\nPress and hold the power button to shutdown...";
#else
msg = "Please extract the soh.otr from the Ship of Harkinian download to your folder.\n\nExiting...";
#endif
if (!std::filesystem::exists(otrPath)) {
#if not defined(__SWITCH__) && not defined(__WIIU__)
Extractor::ShowErrorBox("soh.otr file is missing", msg.c_str());
exit(1);
#elif defined(__SWITCH__)
LUS::Switch::PrintErrorMessageToScreen(("\x1b[2;2HYou are missing the soh.otr file." + msg).c_str());
#elif defined(__WIIU__)
OSFatal(("You are missing the soh.otr file\n\n" + msg).c_str());
#endif
}
OTRVersion otrVersion = ReadPortVersionFromOTR(otrPath);
if (otrVersion.major != gBuildVersionMajor || otrVersion.minor != gBuildVersionMinor || otrVersion.patch != gBuildVersionPatch) {
#if not defined(__SWITCH__) && not defined(__WIIU__)
Extractor::ShowErrorBox("soh.otr file version does not match", msg.c_str());
exit(1);
#elif defined(__SWITCH__)
LUS::Switch::PrintErrorMessageToScreen(("\x1b[2;2HYou have an old soh.otr file." + msg).c_str());
#elif defined(__WIIU__)
OSFatal(("You have an old soh.otr file\n\n" + msg).c_str());
#endif
}
}
// Checks the program version stored in the otr and compares the major value to soh
// For Windows/Mac/Linux if the version doesn't match, offer to
void DetectOTRVersion(std::string fileName, bool isMQ) {
bool isOtrOld = false;
std::string otrPath = LUS::Context::LocateFileAcrossAppDirs(fileName, appShortName);
// Doesn't exist so nothing to do here
if (!std::filesystem::exists(otrPath)) {
return;
}
OTRVersion otrVersion = ReadPortVersionFromOTR(otrPath);
if (otrVersion.major != gBuildVersionMajor) {
isOtrOld = true;
}
if (isOtrOld) {
#if not defined(__SWITCH__) && not defined(__WIIU__)
char msgBuf[250];
char version[18]; // 5 digits for int16_max (x3) + separators + terminator
if (otrVersion.major != 0 || otrVersion.minor != 0 || otrVersion.patch != 0) {
snprintf(version, 18, "%d.%d.%d", otrVersion.major, otrVersion.minor, otrVersion.patch);
} else {
snprintf(version, 18, "no version found");
}
snprintf(msgBuf, 250,
"The %s file was generated with a different version of Ship of Harkinian.\nOTR version: %s\n\n"
"You must regenerate to be able to play, otherwise the program will exit.\nWould you like to regenerate it now?",
fileName.c_str(), version);
if (Extractor::ShowYesNoBox("Old OTR File Found", msgBuf) == IDYES) {
std::string installPath = LUS::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 OTR file.\n\nExiting...");
exit(1);
}
Extractor extract;
if (!extract.Run(isMQ ? RomSearchMode::MQ : RomSearchMode::Vanilla)) {
Extractor::ShowErrorBox("Error", "An error occured, no OTR file was generated.\n\nExiting...");
exit(1);
}
extract.CallZapd(installPath, LUS::Context::GetAppDirectoryPath(appShortName));
} else {
exit(1);
}
#elif defined(__SWITCH__)
LUS::Switch::PrintErrorMessageToScreen("\x1b[2;2HYou've launched the Ship with an old game OTR file."
"\x1b[4;2HPlease regenerate a new game OTR and relaunch."
"\x1b[6;2HPress the Home button to exit...");
#elif defined(__WIIU__)
OSFatal("You've launched the Ship with an old a game OTR file.\n\n"
"Please generate a game OTR and relaunch.\n\n"
"Press and hold the Power button to shutdown...");
#endif
}
}
extern "C" void InitOTR() {
#if not defined (__SWITCH__) && not defined(__WIIU__)
#ifdef __SWITCH__
LUS::Switch::Init(LUS::PreInitPhase);
#elif defined(__WIIU__)
LUS::WiiU::Init(appShortName);
#endif
CheckSoHOTRVersion(LUS::Context::GetPathRelativeToAppBundle("soh.otr"));
if (!std::filesystem::exists(LUS::Context::LocateFileAcrossAppDirs("oot-mq.otr", appShortName)) &&
!std::filesystem::exists(LUS::Context::LocateFileAcrossAppDirs("oot.otr", appShortName))){
#if not defined(__SWITCH__) && not defined(__WIIU__)
std::string installPath = LUS::Context::GetAppBundlePath();
if (!std::filesystem::exists(installPath + "/assets/extractor")) {
Extractor::ShowErrorBox("Extractor assets not found",
"No OTR files found. Missing assets/extractor folder needed to generate OTR file. Exiting...");
"No OTR files found. Missing assets/extractor folder needed to generate OTR file.\n\nExiting...");
exit(1);
}
@ -759,7 +901,7 @@ extern "C" void InitOTR() {
if (Extractor::ShowYesNoBox("No OTR Files", "No OTR files found. Generate one now?") == IDYES) {
Extractor extract;
if (!extract.Run()) {
Extractor::ShowErrorBox("Error", "An error occured, no OTR file was generated. Exiting...");
Extractor::ShowErrorBox("Error", "An error occured, no OTR file was generated.\n\nExiting...");
exit(1);
}
extract.CallZapd(installPath, LUS::Context::GetAppDirectoryPath(appShortName));
@ -770,19 +912,25 @@ extern "C" void InitOTR() {
if (Extractor::ShowYesNoBox("Extraction Complete", "ROM Extracted. Extract another?") == IDYES) {
Extractor extract;
if (!extract.Run(generatedOtrIsMQ ? RomSearchMode::Vanilla : RomSearchMode::MQ)) {
Extractor::ShowErrorBox("Error", "An error occured, an OTR file may have been generated by a different step. Continuing...");
Extractor::ShowErrorBox("Error", "An error occured, an OTR file may have been generated by a different step.\n\nContinuing...");
} else {
extract.CallZapd(installPath, LUS::Context::GetAppDirectoryPath(appShortName));
}
}
}
#endif
#ifdef __SWITCH__
LUS::Switch::Init(LUS::PreInitPhase);
#elif defined(__SWITCH__)
LUS::Switch::PrintErrorMessageToScreen("\x1b[2;2HYou've launched the Ship without a game OTR file."
"\x1b[4;2HPlease generate a game OTR and relaunch."
"\x1b[6;2HPress the Home button to exit...");
#elif defined(__WIIU__)
LUS::WiiU::Init("soh");
OSFatal("You've launched the Ship without a game OTR file.\n\n"
"Please generate a game OTR and relaunch.\n\n"
"Press and hold the Power button to shutdown...");
#endif
}
DetectOTRVersion("oot.otr", false);
DetectOTRVersion("oot-mq.otr", true);
OTRGlobals::Instance = new OTRGlobals();
CustomMessageManager::Instance = new CustomMessageManager();
@ -2276,4 +2424,4 @@ extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* repla
extern "C" void CheckTracker_OnMessageClose() {
CheckTracker::CheckTrackerDialogClosed();
}
}

View File

@ -1,8 +1,9 @@
#include <libultraship/libultra.h>
const char gBuildVersion[] = "@PROJECT_BUILD_NAME@ (@CMAKE_PROJECT_VERSION_MAJOR@.@CMAKE_PROJECT_VERSION_MINOR@.@CMAKE_PROJECT_VERSION_PATCH@)";
const int gBuildVersionMajor = @CMAKE_PROJECT_VERSION_MAJOR@;
const int gBuildVersionMinor = @CMAKE_PROJECT_VERSION_MINOR@;
const int gBuildVersionPatch = @CMAKE_PROJECT_VERSION_PATCH@;
const u16 gBuildVersionMajor = @CMAKE_PROJECT_VERSION_MAJOR@;
const u16 gBuildVersionMinor = @CMAKE_PROJECT_VERSION_MINOR@;
const u16 gBuildVersionPatch = @CMAKE_PROJECT_VERSION_PATCH@;
const char gBuildTeam[] = "@PROJECT_TEAM@";
const char gBuildDate[] = __DATE__ " " __TIME__;
const char gBuildMakeOption[] = "";