From 02ea919a89de89569c036f9de11ff64ab91e8fc1 Mon Sep 17 00:00:00 2001 From: jo! Date: Thu, 8 Jun 2023 00:03:38 +0100 Subject: shipwright: init at 7.0.2 --- pkgs/games/shipwright/default.nix | 222 +++++++++++++++++++++++ pkgs/games/shipwright/lus-install-paths.patch | 146 +++++++++++++++ pkgs/games/shipwright/soh-misc-otr-patches.patch | 119 ++++++++++++ 3 files changed, 487 insertions(+) create mode 100644 pkgs/games/shipwright/default.nix create mode 100644 pkgs/games/shipwright/lus-install-paths.patch create mode 100644 pkgs/games/shipwright/soh-misc-otr-patches.patch (limited to 'pkgs/games') diff --git a/pkgs/games/shipwright/default.nix b/pkgs/games/shipwright/default.nix new file mode 100644 index 00000000000..182e2cf654a --- /dev/null +++ b/pkgs/games/shipwright/default.nix @@ -0,0 +1,222 @@ +{ stdenv +, cmake +, lsb-release +, ninja +, lib +, fetchFromGitHub +, fetchurl +, makeDesktopItem +, python3 +, libX11 +, libXrandr +, libXinerama +, libXcursor +, libXi +, libXext +, glew +, boost +, SDL2 +, SDL2_net +, pkg-config +, libpulseaudio +, libpng +, imagemagick +, requireFile + +, oot ? rec { + enable = true; + variant = "debug"; + + rom = requireFile { + name = "oot-${variant}.z64"; + message = '' + This nix expression requires that oot-${variant}.z64 is already part of the store. + To get this file you can dump your Ocarina of Time's cartridge to a file, + and add it to the nix store with nix-store --add-fixed sha1 , or override the package: + shipwright.override { oot = { enable = true; variant = "debug"; rom = path/to/oot-debug-mq.z64; } } + + The supported variants are: + - debug: Ocarina of Time Debug PAL GC (not Master Quest) + - pal-gc: Ocarina of Time PAL GameCube (may lead to crashes and instability) + + This is optional if you have imported an Ocarina of Time Master Quest ROM. + If so, please set oot.enable to false and ootMq.enable to true. + If both are enabled, Ship of Harkinian will be built with both ROMs. + ''; + + # From upstream: https://github.com/HarbourMasters/Shipwright/blob/e46c60a7a1396374e23f7a1f7122ddf9efcadff7/README.md#1-check-your-sha1 + sha1 = { + debug = "cee6bc3c2a634b41728f2af8da54d9bf8cc14099"; + pal-gc = "0227d7c0074f2d0ac935631990da8ec5914597b4"; + }.${variant} or (throw "Unsupported romVariant ${variant}. Valid options are 'debug' and 'pal-gc'."); + }; + } + +, ootMq ? rec { + enable = false; + variant = "debug-mq"; + + rom = requireFile { + name = "oot-${variant}.z64"; + message = '' + This nix expression requires that oot-${variant}.z64 is already part of the store. + To get this file you can dump your Ocarina of Time Master Quest's cartridge to a file, + and add it to the nix store with nix-store --add-fixed sha1 , or override the package: + shipwright.override { ootMq = { enable = true; variant = "debug-mq"; rom = path/to/oot-debug-mq.z64; } } + + The supported variants are: + - debug-mq: Ocarina of Time Debug PAL GC MQ (Dungeons will be Master Quest) + - debug-mq-alt: Alternate ROM, not produced by decompilation. + + This is optional if you have imported an Ocarina of Time ROM. + If so, please set oot.enable to true and ootMq.enable to false. + If both are enabled, Ship of Harkinian will be built with both ROMs. + ''; + + # From upstream: https://github.com/HarbourMasters/Shipwright/blob/e46c60a7a1396374e23f7a1f7122ddf9efcadff7/README.md#1-check-your-sha1 + sha1 = { + debug-mq = "079b855b943d6ad8bd1eb026c0ed169ecbdac7da"; + debug-mq-alt = "50bebedad9e0f10746a52b07239e47fa6c284d03"; + }.${variant} or (throw "Unsupported mqRomVariant ${variant}. Valid options are 'debug-mq' and 'debug-mq-alt'."); + }; + } +}: + +let + checkAttrs = attrs: + let + validAttrs = [ "enable" "rom" "variant" ]; + in + lib.all (name: lib.elem name validAttrs) (lib.attrNames attrs); +in +assert (lib.assertMsg (checkAttrs oot) "oot must have the attributes 'enable' and 'rom', and none other"); +assert (lib.assertMsg (checkAttrs ootMq) "ootMq must have the attributes 'enable' and 'rom', and none other"); +assert (lib.assertMsg (oot.enable || ootMq.enable) "At least one of 'oot.enable' and 'ootMq.enable' must be true"); + +stdenv.mkDerivation rec { + pname = "shipwright"; + version = "7.0.2"; + + src = fetchFromGitHub { + owner = "harbourmasters"; + repo = "shipwright"; + rev = version; + hash = "sha256-2VCcczGWSvp6hk8FTA1/T1E1KkrrvWyOdkEw8eiYYnY="; + fetchSubmodules = true; + }; + + # This would get fetched at build time otherwise, see: + # https://github.com/HarbourMasters/Shipwright/blob/e46c60a7a1396374e23f7a1f7122ddf9efcadff7/soh/CMakeLists.txt#L736 + gamecontrollerdb = fetchurl { + name = "gamecontrollerdb.txt"; + url = "https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/c5b4df0e1061175cb11e3ebbf8045178339864a5/gamecontrollerdb.txt"; + hash = "sha256-2VFCsaalXoe+JYWCH6IbgjnLXNKxe0UqSyJNGZMn5Ko="; + }; + + nativeBuildInputs = [ + cmake + ninja + pkg-config + lsb-release + python3 + imagemagick + ]; + + buildInputs = [ + boost + libX11 + libXrandr + libXinerama + libXcursor + libXi + libXext + glew + SDL2 + SDL2_net + libpulseaudio + libpng + ]; + + patches = [ + # These patches make soh look inside the nix store for data files (the controller database and the OTRs) + ./lus-install-paths.patch + ./soh-misc-otr-patches.patch + ]; + + cmakeFlags = [ + "-DCMAKE_INSTALL_PREFIX=${placeholder "out"}/lib" + ]; + + dontAddPrefix = true; + + # Linking fails without this + hardeningDisable = [ "format" ]; + + postBuild = '' + cp ${gamecontrollerdb} ${gamecontrollerdb.name} + + pushd ../OTRExporter + ${lib.optionalString oot.enable "python3 ./extract_assets.py -z ../build/ZAPD/ZAPD.out ${oot.rom}"} + ${lib.optionalString ootMq.enable "python3 ./extract_assets.py -z ../build/ZAPD/ZAPD.out ${ootMq.rom}"} + popd + ''; + + preInstall = '' + # Cmake likes it here for its install paths + cp ../OTRExporter/soh.otr .. + ''; + + postInstall = '' + mkdir -p $out/bin + + # Copy the extracted assets, required to be in the same directory as the executable + ${lib.optionalString oot.enable "cp ../OTRExporter/oot.otr $out/lib"} + ${lib.optionalString ootMq.enable "cp ../OTRExporter/oot-mq.otr $out/lib"} + + ln -s $out/lib/soh.elf $out/bin/soh + ''; + + desktopItems = [ + (makeDesktopItem { + name = "soh"; + icon = "soh"; + exec = "soh"; + genericName = "Ship of Harkinian"; + desktopName = "soh"; + categories = [ "Game" ]; + }) + ]; + + meta = with lib; { + homepage = "https://github.com/HarbourMasters/Shipwright"; + description = "A PC port of Ocarina of Time with modern controls, widescreen, high-resolution, and more"; + longDescription = '' + An PC port of Ocarina of Time with modern controls, widescreen, high-resolution and more, based off of decompilation. + Note that you must supply an OoT rom yourself to use this package because propietary assets are extracted from it. + + You can change the game variant like this: + shipwright.override { oot.enable = false; ootMq.enable = true } + + The default ROM variants for Oot and OotMq are debug and debug-mq respectively. + If you have a pal-gc rom, you should override like this: + shipwright.override { oot = { enable = true; variant = "pal-gc"; rom = path/to/oot-pal-gc.z64; } } + + The supported Oot variants are: + - debug: Ocarina of Time Debug PAL GC (not Master Quest) + - pal-gc: Ocarina of Time PAL GameCube (may lead to crashes and instability) + + The supported OotMq variants are: + - debug-mq: Ocarina of Time Debug PAL GC MQ (Dungeons will be Master Quest) + - debug-mq-alt: Alternate ROM, not produced by decompilation. + ''; + mainProgram = "soh"; + platforms = [ "x86_64-linux" ]; + maintainers = with maintainers; [ ivar j0lol ]; + license = with licenses; [ + # OTRExporter, OTRGui, ZAPDTR, libultraship + mit + # Ship of Harkinian itself + unfree + ]; + }; +} diff --git a/pkgs/games/shipwright/lus-install-paths.patch b/pkgs/games/shipwright/lus-install-paths.patch new file mode 100644 index 00000000000..c14ecccfed0 --- /dev/null +++ b/pkgs/games/shipwright/lus-install-paths.patch @@ -0,0 +1,146 @@ +Submodule libultraship contains modified content +diff --git a/libultraship/src/CMakeLists.txt b/libultraship/src/CMakeLists.txt +index f95c3c9..5b967b9 100644 +--- a/libultraship/src/CMakeLists.txt ++++ b/libultraship/src/CMakeLists.txt +@@ -74,7 +74,10 @@ target_sources(libultraship PRIVATE ${Source_Files__Controller}) + + #=================== Core =================== + ++configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/install_config.h.in ${CMAKE_BINARY_DIR}/install_config.h @ONLY) ++ + set(Source_Files__Core ++ ${CMAKE_BINARY_DIR}/install_config.h + ${CMAKE_CURRENT_SOURCE_DIR}/core/Window.h + ${CMAKE_CURRENT_SOURCE_DIR}/core/Window.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/core/ConsoleVariable.h +@@ -329,7 +332,7 @@ endif() + #=================== Packages & Includes =================== + + target_include_directories(libultraship +- PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../extern ++ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../extern ${CMAKE_BINARY_DIR} + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../extern/spdlog/include ${CMAKE_CURRENT_SOURCE_DIR}/../extern/stb + ) + +diff --git a/libultraship/src/core/Context.cpp b/libultraship/src/core/Context.cpp +index 776333e..fa546e6 100644 +--- a/libultraship/src/core/Context.cpp ++++ b/libultraship/src/core/Context.cpp +@@ -14,6 +14,7 @@ + #elif defined(__WIIU__) + #include "port/wiiu/WiiUImpl.h" + #endif ++#include "install_config.h" + + namespace LUS { + std::weak_ptr Context::mContext; +@@ -281,6 +282,18 @@ std::string Context::GetShortName() { + } + + std::string Context::GetAppBundlePath() { ++#ifdef CMAKE_INSTALL_PREFIX ++ static const std::string fpath = CMAKE_INSTALL_PREFIX; ++ static int exists = -1; ++ ++ if (exists == -1) { ++ exists = fpath.size() > 0 && std::filesystem::is_directory(fpath); ++ } ++ ++ if (exists) { ++ return fpath; ++ } ++#else + #ifdef __APPLE__ + FolderManager folderManager; + return folderManager.getMainBundlePath(); +@@ -291,6 +304,7 @@ std::string Context::GetAppBundlePath() { + if (fpath != NULL) { + return std::string(fpath); + } ++#endif + #endif + + return "."; +@@ -304,6 +318,13 @@ std::string Context::GetAppDirectoryPath() { + } + #endif + ++ char *prefpath = SDL_GetPrefPath(NULL, "soh"); ++ if (prefpath != NULL) { ++ std::string ret(prefpath); ++ SDL_free(prefpath); ++ return ret; ++ } ++ + return "."; + } + +@@ -315,7 +336,24 @@ std::string Context::GetPathRelativeToAppDirectory(const char* path) { + return GetAppDirectoryPath() + "/" + path; + } + ++std::string Context::FindFileFromAllAppDirectories(const char* path) { ++ std::string fpath; ++ ++ // app configuration dir (eg. ~/.local/share) ++ fpath = GetPathRelativeToAppDirectory(path); ++ if (std::filesystem::exists(fpath)) { ++ return fpath; ++ } ++ // app install dir (eg. /usr/) ++ fpath = GetPathRelativeToAppBundle(path); ++ if (std::filesystem::exists(fpath)) { ++ return fpath; ++ } ++ // current dir ++ return "./" + std::string(path); ++} ++ + bool Context::DoesOtrFileExist() { + return mOtrFileExists; + } +-} // namespace LUS +\ No newline at end of file ++} // namespace LUS +diff --git a/libultraship/src/core/Context.h b/libultraship/src/core/Context.h +index c32f4dd..a9f1639 100644 +--- a/libultraship/src/core/Context.h ++++ b/libultraship/src/core/Context.h +@@ -26,6 +26,7 @@ class Context { + static std::string GetAppDirectoryPath(); + static std::string GetPathRelativeToAppDirectory(const char* path); + static std::string GetPathRelativeToAppBundle(const char* path); ++ static std::string FindFileFromAllAppDirectories(const char* path); + + Context(std::string name, std::string shortName); + +diff --git a/libultraship/src/core/libultra/os.cpp b/libultraship/src/core/libultra/os.cpp +index 9058fe1..7d9387e 100644 +--- a/libultraship/src/core/libultra/os.cpp ++++ b/libultraship/src/core/libultra/os.cpp +@@ -21,8 +21,8 @@ int32_t osContInit(OSMesgQueue* mq, uint8_t* controllerBits, OSContStatus* statu + } + + #ifndef __SWITCH__ +- const char* controllerDb = "gamecontrollerdb.txt"; +- int mappingsAdded = SDL_GameControllerAddMappingsFromFile(controllerDb); ++ std::string controllerDb = LUS::Context::GetPathRelativeToAppBundle("gamecontrollerdb.txt"); ++ int mappingsAdded = SDL_GameControllerAddMappingsFromFile(controllerDb.c_str()); + if (mappingsAdded >= 0) { + SPDLOG_INFO("Added SDL game controllers from \"{}\" ({})", controllerDb, mappingsAdded); + } else { +@@ -90,4 +90,4 @@ int32_t osRecvMesg(OSMesgQueue* mq, OSMesg* msg, int32_t flag) { + mq->validCount--; + return 0; + } +-} +\ No newline at end of file ++} +diff --git a/libultraship/src/install_config.h.in b/libultraship/src/install_config.h.in +new file mode 100644 +index 0000000..029753c +--- /dev/null ++++ b/libultraship/src/install_config.h.in +@@ -0,0 +1 @@ ++#cmakedefine CMAKE_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@" diff --git a/pkgs/games/shipwright/soh-misc-otr-patches.patch b/pkgs/games/shipwright/soh-misc-otr-patches.patch new file mode 100644 index 00000000000..2be31092e28 --- /dev/null +++ b/pkgs/games/shipwright/soh-misc-otr-patches.patch @@ -0,0 +1,119 @@ +diff --git a/soh/soh/Extractor/Extract.cpp b/soh/soh/Extractor/Extract.cpp +index a9ddc4f4..f6d45fe2 100644 +--- a/soh/soh/Extractor/Extract.cpp ++++ b/soh/soh/Extractor/Extract.cpp +@@ -490,28 +490,27 @@ const char* Extractor::GetZapdVerStr() const { + + extern "C" int zapd_main(int argc, char** argv); + +-bool Extractor::CallZapd() { ++bool Extractor::CallZapd(std::string &assetPath) { + constexpr int argc = 16; +- char xmlPath[100]; +- char confPath[100]; + std::array argv; + const char* version = GetZapdVerStr(); + +- snprintf(xmlPath, 100, "assets/extractor/xmls/%s", version); +- snprintf(confPath, 100, "assets/extractor/Config_%s.xml", version); ++ std::string xmlPath = assetPath + "/assets/extractor/xmls/" + version; ++ std::string confPath = assetPath + "/assets/extractor/Config_" + version + ".xml"; ++ std::string fileListsPath = assetPath + "/assets/extractor/filelists"; + + argv[0] = "ZAPD"; + argv[1] = "ed"; + argv[2] = "-i"; +- argv[3] = xmlPath; ++ argv[3] = xmlPath.c_str(); + argv[4] = "-b"; + argv[5] = mCurrentRomPath.c_str(); + argv[6] = "-fl"; +- argv[7] = "assets/extractor/filelists"; ++ argv[7] = fileListsPath.c_str(); + argv[8] = "-gsf"; +- argv[9] = "1"; ++ argv[9] = "0"; + argv[10] = "-rconf"; +- argv[11] = confPath; ++ argv[11] = confPath.c_str(); + argv[12] = "-se"; + argv[13] = "OTR"; + argv[14] = "--otrfile"; +diff --git a/soh/soh/Extractor/Extract.h b/soh/soh/Extractor/Extract.h +index e4eb2e5b..3c95b025 100644 +--- a/soh/soh/Extractor/Extract.h ++++ b/soh/soh/Extractor/Extract.h +@@ -57,7 +57,7 @@ class Extractor { + bool IsMasterQuest() const; + + bool Run(RomSearchMode searchMode = RomSearchMode::Both); +- bool CallZapd(); ++ bool CallZapd(std::string& assetPath); + const char* GetZapdStr(); + }; + #endif +diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp +index d192de6a..cc516085 100644 +--- a/soh/soh/OTRGlobals.cpp ++++ b/soh/soh/OTRGlobals.cpp +@@ -201,11 +201,11 @@ const char* constCameraStrings[] = { + + OTRGlobals::OTRGlobals() { + std::vector OTRFiles; +- std::string mqPath = LUS::Context::GetPathRelativeToAppDirectory("oot-mq.otr"); ++ std::string mqPath = LUS::Context::FindFileFromAllAppDirectories("oot-mq.otr"); + if (std::filesystem::exists(mqPath)) { + OTRFiles.push_back(mqPath); + } +- std::string ootPath = LUS::Context::GetPathRelativeToAppDirectory("oot.otr"); ++ std::string ootPath = LUS::Context::FindFileFromAllAppDirectories("oot.otr"); + if (std::filesystem::exists(ootPath)) { + OTRFiles.push_back(ootPath); + } +@@ -213,7 +213,7 @@ OTRGlobals::OTRGlobals() { + if (std::filesystem::exists(sohOtrPath)) { + OTRFiles.push_back(sohOtrPath); + } +- std::string patchesPath = LUS::Context::GetPathRelativeToAppDirectory("mods"); ++ std::string patchesPath = LUS::Context::FindFileFromAllAppDirectories("mods"); + if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) { + if (std::filesystem::is_directory(patchesPath)) { + for (const auto& p : std::filesystem::recursive_directory_iterator(patchesPath)) { +@@ -709,8 +709,10 @@ extern "C" void OTRExtScanner() { + + extern "C" void InitOTR() { + #if not defined (__SWITCH__) && not defined(__WIIU__) +- if (!std::filesystem::exists(LUS::Context::GetPathRelativeToAppDirectory("oot-mq.otr")) && +- !std::filesystem::exists(LUS::Context::GetPathRelativeToAppDirectory("oot.otr"))){ ++ if (!std::filesystem::exists(LUS::Context::FindFileFromAllAppDirectories("oot-mq.otr")) && ++ !std::filesystem::exists(LUS::Context::FindFileFromAllAppDirectories("oot.otr"))){ ++ std::string exporterAssetPath = LUS::Context::GetAppBundlePath(); ++ + bool generatedOtrIsMQ = false; + if (Extractor::ShowYesNoBox("No OTR Files", "No OTR files found. Generate one now?") == IDYES) { + Extractor extract; +@@ -718,7 +720,7 @@ extern "C" void InitOTR() { + Extractor::ShowErrorBox("Error", "An error occured, no OTR file was generated. Exiting..."); + exit(1); + } +- extract.CallZapd(); ++ extract.CallZapd(exporterAssetPath); + generatedOtrIsMQ = extract.IsMasterQuest(); + } else { + exit(1); +@@ -728,7 +730,7 @@ extern "C" void InitOTR() { + 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..."); + } else { +- extract.CallZapd(); ++ extract.CallZapd(exporterAssetPath); + } + } + } +@@ -2018,4 +2020,4 @@ extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* repla + + extern "C" void SaveManager_ThreadPoolWait() { + SaveManager::Instance->ThreadPoolWait(); +-} +\ No newline at end of file ++} -- cgit 1.4.1