diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ace1e0bb..3eeea0e8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -17,8 +17,8 @@ jobs: build-test-linux: strategy: matrix: - build_arch: [x86, x64] - toolset: [gcc, clang] + build_arch: [ x86, x64 ] + toolset: [ gcc, clang ] include: - build_arch: x86 gtk_packages: libgtk-4-dev:i386 libwebkitgtk-6.0-dev:i386 @@ -105,13 +105,14 @@ jobs: ./ObjWritingTests ./ParserTests ./SystemTests + ./UnlinkingTests ./ZoneCodeGeneratorLibTests ./ZoneCommonTests build-test-windows: strategy: matrix: - build_arch: [x86, x64] + build_arch: [ x86, x64 ] include: - build_arch: x86 msbuild_config: Win32 @@ -200,6 +201,8 @@ jobs: $combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE) ./SystemTests $combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE) + ./UnlinkingTests + $combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE) ./ZoneCodeGeneratorLibTests $combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE) ./ZoneCommonTests diff --git a/premake5.lua b/premake5.lua index 0cf82dae..8513343d 100644 --- a/premake5.lua +++ b/premake5.lua @@ -208,6 +208,7 @@ include "test/ObjWritingTests.lua" include "test/ParserTestUtils.lua" include "test/ParserTests.lua" include "test/SystemTests.lua" +include "test/UnlinkingTests.lua" include "test/ZoneCodeGeneratorLibTests.lua" include "test/ZoneCommonTests.lua" @@ -223,6 +224,7 @@ group "Tests" ParserTestUtils:project() ParserTests:project() SystemTests:project() + UnlinkingTests:project() ZoneCodeGeneratorLibTests:project() ZoneCommonTests:project() group "" diff --git a/src/Unlinking/UnlinkerArgs.cpp b/src/Unlinking/UnlinkerArgs.cpp index 18ea2d53..9e2bac87 100644 --- a/src/Unlinking/UnlinkerArgs.cpp +++ b/src/Unlinking/UnlinkerArgs.cpp @@ -66,7 +66,9 @@ const CommandLineOption* const OPTION_OUTPUT_FOLDER = CommandLineOption::Builder::Create() .WithShortName("o") .WithLongName("output-folder") - .WithDescription(std::format("Specifies the output folder containing the contents of the unlinked zones. Defaults to \"{}\"", UnlinkerArgs::DEFAULT_OUTPUT_FOLDER)) + .WithDescription(std::format("Specifies the output folder containing the contents of the unlinked zones. Supports ?game? and ?zone? placeholders. " + "Defaults to \"{}\"", + UnlinkerArgs::DEFAULT_OUTPUT_FOLDER)) .WithParameter("outputFolderPath") .Build(); @@ -148,6 +150,7 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ UnlinkerArgs::UnlinkerArgs() : m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v), + m_game_pattern(R"(\?game\?)"), m_zone_pattern(R"(\?zone\?)"), m_task(ProcessingTask::DUMP), m_minimal_zone_def(false), @@ -390,5 +393,10 @@ bool UnlinkerArgs::ParseArgs(const int argc, const char** argv, bool& shouldCont std::string UnlinkerArgs::GetOutputFolderPathForZone(const Zone& zone) const { - return std::regex_replace(m_output_folder, m_zone_pattern, zone.m_name); + auto outputPath = std::regex_replace(m_output_folder, m_zone_pattern, zone.m_name); + + auto gameName = IGame::GetGameById(zone.m_game_id)->GetShortName(); + utils::MakeStringLowerCase(gameName); + + return std::regex_replace(outputPath, m_game_pattern, gameName); } diff --git a/src/Unlinking/UnlinkerArgs.h b/src/Unlinking/UnlinkerArgs.h index 459cbc60..c699713f 100644 --- a/src/Unlinking/UnlinkerArgs.h +++ b/src/Unlinking/UnlinkerArgs.h @@ -17,6 +17,7 @@ public: private: ArgumentParser m_argument_parser; + std::regex m_game_pattern; std::regex m_zone_pattern; /** diff --git a/test/UnlinkingTests.lua b/test/UnlinkingTests.lua new file mode 100644 index 00000000..078a4d4c --- /dev/null +++ b/test/UnlinkingTests.lua @@ -0,0 +1,58 @@ +UnlinkingTests = {} + +function UnlinkingTests:include(includes) + if includes:handle(self:name()) then + includedirs { + "%{wks.location}/src/Unlinking", + path.join(TestFolder(), "UnlinkingTests") + } + end +end + +function UnlinkingTests:link(links) + +end + +function UnlinkingTests:use() + +end + +function UnlinkingTests:name() + return "UnlinkingTests" +end + +function UnlinkingTests:project() + local folder = TestFolder() + local includes = Includes:create() + local links = Links:create() + + project(self:name()) + targetdir(TargetDirectoryTest) + location "%{wks.location}/test/%{prj.name}" + kind "ConsoleApp" + language "C++" + + files { + path.join(folder, "UnlinkingTests/**.h"), + path.join(folder, "UnlinkingTests/**.cpp") + } + + vpaths { + ["*"] = { + path.join(folder, "UnlinkingTests") + } + } + + self:include(includes) + Catch2Common:include(includes) + ObjCommonTestUtils:include(includes) + Unlinking:include(includes) + catch2:include(includes) + + links:linkto(ObjCommonTestUtils) + links:linkto(ParserTestUtils) + links:linkto(Unlinking) + links:linkto(catch2) + links:linkto(Catch2Common) + links:linkall() +end diff --git a/test/UnlinkingTests/UnlinkerArgsTests.cpp b/test/UnlinkingTests/UnlinkerArgsTests.cpp new file mode 100644 index 00000000..f3184574 --- /dev/null +++ b/test/UnlinkingTests/UnlinkerArgsTests.cpp @@ -0,0 +1,45 @@ +#include "UnlinkerArgs.h" + +#include +#include + +namespace +{ + TEST_CASE("Unlinker uses the default output folder when none is specified", "[unlinker][arguments]") + { + const char* argStrings[]{ + "Unlinker", + "test.ff", + }; + + UnlinkerArgs args; + bool shouldContinue = true; + + REQUIRE(args.ParseArgs(std::extent_v, argStrings, shouldContinue)); + REQUIRE(shouldContinue); + REQUIRE(args.m_output_folder == UnlinkerArgs::DEFAULT_OUTPUT_FOLDER); + + const Zone zone("test_zone", 0, GameId::IW4, GamePlatform::PC); + REQUIRE(args.GetOutputFolderPathForZone(zone) == "zone_dump/zone_raw/test_zone"); + } + + TEST_CASE("Unlinker output folder remains unchanged without placeholders", "[unlinker][arguments]") + { + UnlinkerArgs args; + args.m_output_folder = "zone_dump/custom"; + + const Zone zone("test_zone", 0, GameId::IW4, GamePlatform::PC); + + REQUIRE(args.GetOutputFolderPathForZone(zone) == "zone_dump/custom"); + } + + TEST_CASE("Unlinker output folder replaces game and zone placeholders", "[unlinker][arguments]") + { + UnlinkerArgs args; + args.m_output_folder = "zone_dump/?game?/?zone?"; + + const Zone zone("test_zone", 0, GameId::IW4, GamePlatform::PC); + + REQUIRE(args.GetOutputFolderPathForZone(zone) == "zone_dump/iw4/test_zone"); + } +} // namespace