diff --git a/CMakeLists.txt b/CMakeLists.txt index 63b061e..e35328f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,28 @@ SET(EXECNAME "AerofoilX" CACHE STRING "Defines the exec name") message(${CMAKE_BINARY_DIR}) +# Use Release build type by default +if("${CMAKE_BUILD_TYPE}" STREQUAL "") + set(CMAKE_BUILD_TYPE Release) + message("Build type unspecified, using Release") +endif() + +# Enable LTO by default if supported +if("${CMAKE_INTERPROCEDURAL_OPTIMIZATION}" STREQUAL "") + include(CheckIPOSupported) + check_ipo_supported(RESULT IPO_SUPPORTED) + if(IPO_SUPPORTED) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION On) + message("Compiler supports LTO, enabling automatically") + endif() +endif() + +# FIXME: Clang treats this as an error by default; fixing the source rather +# than downgrading the error to a warning would be a better solution. +add_compile_options( + $<$:-Wno-error=c++11-narrowing> + ) + find_package(SDL2 REQUIRED) if(PLATFORM STREQUAL "MAC") @@ -127,18 +149,18 @@ add_library(PortabilityLayer STATIC ) target_include_directories(PortabilityLayer PRIVATE - $ - $ - $ - $ - $ - $ - $ + Common + GpCommon + PortabilityLayer + zlib + rapidjson/include + MacRomanConversion + stb ) target_compile_options(PortabilityLayer PRIVATE -Wno-multichar) -target_link_libraries(PortabilityLayer zlib MacRomanConversion stb) +target_link_libraries(PortabilityLayer PRIVATE zlib MacRomanConversion stb) add_library(GpShell STATIC @@ -153,9 +175,9 @@ add_library(GpShell STATIC ) target_include_directories(GpShell PRIVATE - $ - $ - $ + Common + GpCommon + PortabilityLayer ) add_library(GpApp STATIC @@ -233,16 +255,15 @@ add_library(GpApp STATIC target_compile_options(GpApp PRIVATE -Wno-multichar) target_include_directories(GpApp PRIVATE - $ - $ - $ + Common + GpCommon + PortabilityLayer ) -target_link_libraries(GpApp PortabilityLayer) +target_link_libraries(GpApp PRIVATE PortabilityLayer) if(CMAKE_HOST_UNIX) - set(EXEC_SOURCES ) - list(APPEND EXEC_SOURCES + set(EXEC_SOURCES AerofoilPortable/GpSystemServices_POSIX.cpp AerofoilPortable/GpThreadEvent_Cpp11.cpp AerofoilPortable/GpAllocator_C.cpp @@ -260,21 +281,20 @@ if(CMAKE_HOST_UNIX) AerofoilX/GpFileSystem_X.cpp ) - set(EXEC_LIBS ) - list(APPEND EXEC_LIBS + set(EXEC_LIBS ${SDL2_LIBRARIES} GpApp GpShell + PortabilityLayer ) - set(EXEC_INC_DIRS ) - list(APPEND EXEC_INC_DIRS - $ - $ - $ - $ - $ - $ + set(EXEC_INC_DIRS + Common + GpCommon + GpShell + AerofoilSDL + AerofoilPortable + PortabilityLayer ${SDL2_INCLUDE_DIRS} ) if(PLATFORM STREQUAL "MAC") @@ -283,7 +303,7 @@ if(CMAKE_HOST_UNIX) AerofoilMac/AerofoilMac/MacInit.mm ) list(APPEND EXEC_INC_DIRS - $ + AerofoilMac/AerofoilMac ) list(APPEND EXEC_LIBS "-framework Cocoa" @@ -296,4 +316,418 @@ if(CMAKE_HOST_UNIX) endif() -install (TARGETS ${EXECNAME}) +add_executable(flattenmov EXCLUDE_FROM_ALL flattenmov/flattenmov.cpp AerofoilPortable/GpAllocator_C.cpp) +target_include_directories(flattenmov PRIVATE + Common + GpCommon + PortabilityLayer + AerofoilPortable + ) +target_link_libraries(flattenmov PortabilityLayer) + +add_executable(bin2gp EXCLUDE_FROM_ALL bin2gp/bin2gp.cpp AerofoilPortable/GpAllocator_C.cpp) +target_include_directories(bin2gp PRIVATE + Common + GpCommon + PortabilityLayer + AerofoilPortable + ) +target_link_libraries(bin2gp PortabilityLayer) + +add_executable(hqx2bin EXCLUDE_FROM_ALL hqx2bin/hqx2bin.cpp AerofoilPortable/GpAllocator_C.cpp) +target_include_directories(hqx2bin PRIVATE + Common + GpCommon + PortabilityLayer + AerofoilPortable + ) +target_link_libraries(hqx2bin PortabilityLayer) + +add_executable(hqx2gp EXCLUDE_FROM_ALL + hqx2gp/hqx2gp.cpp + AerofoilPortable/GpAllocator_C.cpp + WindowsUnicodeToolShim/UnixUnicodeToolShim.cpp + ) +target_include_directories(hqx2gp PRIVATE + Common + GpCommon + PortabilityLayer + AerofoilPortable + WindowsUnicodeToolShim + ) +target_link_libraries(hqx2gp PortabilityLayer) + +add_executable(gpr2gpa EXCLUDE_FROM_ALL + gpr2gpa/gpr2gpa.cpp + gpr2gpa/macedec.cpp + AerofoilPortable/GpAllocator_C.cpp + WindowsUnicodeToolShim/UnixUnicodeToolShim.cpp + ) +target_include_directories(gpr2gpa PRIVATE + Common + GpCommon + PortabilityLayer + AerofoilPortable + MacRomanConversion + WindowsUnicodeToolShim + rapidjson/include + zlib + ) +target_link_libraries(gpr2gpa PortabilityLayer MacRomanConversion zlib) + +add_executable(MakeTimestamp EXCLUDE_FROM_ALL MakeTimestamp/MakeTimestamp.cpp) +target_include_directories(MakeTimestamp PRIVATE PortabilityLayer) +target_link_libraries(MakeTimestamp PortabilityLayer) + +add_executable(FTagData EXCLUDE_FROM_ALL + FTagData/FTagData.cpp + WindowsUnicodeToolShim/UnixUnicodeToolShim.cpp + ) +target_include_directories(FTagData PRIVATE + Common + GpCommon + PortabilityLayer + AerofoilPortable + WindowsUnicodeToolShim + ) +target_link_libraries(FTagData PortabilityLayer) + +add_executable(MergeGPF EXCLUDE_FROM_ALL + MergeGPF/MergeGPF.cpp + AerofoilPortable/GpAllocator_C.cpp + WindowsUnicodeToolShim/UnixUnicodeToolShim.cpp + ) +target_include_directories(MergeGPF PRIVATE + Common + GpCommon + PortabilityLayer + AerofoilPortable + WindowsUnicodeToolShim + ) +target_link_libraries(MergeGPF PortabilityLayer) + +add_executable(MiniRez EXCLUDE_FROM_ALL MiniRez/MiniRez.cpp WindowsUnicodeToolShim/UnixUnicodeToolShim.cpp) +target_include_directories(MiniRez PRIVATE + Common + GpCommon + PortabilityLayer + WindowsUnicodeToolShim + ) +target_link_libraries(MiniRez PortabilityLayer) + +add_executable(HouseTool EXCLUDE_FROM_ALL + HouseTool/HouseTool.cpp + WindowsUnicodeToolShim/UnixUnicodeToolShim.cpp + ) +target_include_directories(HouseTool PRIVATE + Common + GpCommon + PortabilityLayer + MacRomanConversion + WindowsUnicodeToolShim + ) +target_link_libraries(HouseTool PortabilityLayer MacRomanConversion) + +add_executable(unpacktool EXCLUDE_FROM_ALL + unpacktool/ArchiveDescription.cpp + unpacktool/BWT.cpp + unpacktool/CompactProLZHDecompressor.cpp + unpacktool/CompactProLZHRLEDecompressor.cpp + unpacktool/CompactProParser.cpp + unpacktool/CompactProRLEDecompressor.cpp + unpacktool/CRC.cpp + unpacktool/CSInputBuffer.cpp + unpacktool/DecompressorProxyReader.cpp + unpacktool/LZSSDecompressor.cpp + unpacktool/LZW.cpp + unpacktool/LZWDecompressor.cpp + unpacktool/NullDecompressor.cpp + unpacktool/PrefixCode.cpp + unpacktool/RLE90Decompressor.cpp + unpacktool/StringCommon.cpp + unpacktool/StuffIt13Decompressor.cpp + unpacktool/StuffIt5Parser.cpp + unpacktool/StuffItArsenicDecompressor.cpp + unpacktool/StuffItCommon.cpp + unpacktool/StuffItHuffmanDecompressor.cpp + unpacktool/StuffItParser.cpp + unpacktool/unpacktool.cpp + WindowsUnicodeToolShim/UnixUnicodeToolShim.cpp + ) +target_include_directories(unpacktool PRIVATE + Common + GpCommon + PortabilityLayer + MacRomanConversion + WindowsUnicodeToolShim + ) +target_link_libraries(unpacktool PortabilityLayer MacRomanConversion) + + +find_package(Freetype) +if(FREETYPE_FOUND) + add_library(GpFontHandler_FreeType2 STATIC + GpFontHandler_FreeType2/GpFontHandler_FreeType2.cpp + ) + + target_include_directories(GpFontHandler_FreeType2 PRIVATE + Common + GpCommon + "${FREETYPE_INCLUDE_DIR_ft2build}" + ) + + target_link_libraries(GpFontHandler_FreeType2 PRIVATE Freetype::Freetype) + + + add_executable(GenerateFonts EXCLUDE_FROM_ALL + GenerateFonts/GenerateFonts.cpp + AerofoilPortable/GpAllocator_C.cpp + WindowsUnicodeToolShim/UnixUnicodeToolShim.cpp + ) + target_include_directories(GenerateFonts PRIVATE + Common + GpCommon + PortabilityLayer + AerofoilPortable + WindowsUnicodeToolShim + ) + target_link_libraries(GenerateFonts PortabilityLayer GpFontHandler_FreeType2) +endif() + +add_executable(ConvertColorCursors EXCLUDE_FROM_ALL + ConvertColorCursors/ConvertColorCursors.cpp + AerofoilPortable/GpAllocator_C.cpp + stb/stb_image_write.c + WindowsUnicodeToolShim/UnixUnicodeToolShim.cpp + ) +target_include_directories(ConvertColorCursors PRIVATE + Common + GpCommon + PortabilityLayer + AerofoilPortable + stb + WindowsUnicodeToolShim + ) +target_link_libraries(ConvertColorCursors PortabilityLayer) + + +add_custom_target(BuildDirs + BYPRODUCTS Packaged tmp + COMMAND "${CMAKE_COMMAND}" -E make_directory Packaged/Houses tmp + VERBATIM + ) + +set(DATA_FILES) + +function(add_data_file NAME) + list(APPEND DATA_FILES "Packaged/${NAME}") + set(DATA_FILES "${DATA_FILES}" PARENT_SCOPE) + + cmake_parse_arguments(PARSE_ARGV 1 ARG "" "" "") + set(TMPDIR "${CMAKE_CURRENT_BINARY_DIR}/tmp/${NAME}") + list(TRANSFORM ARG_UNPARSED_ARGUMENTS + REPLACE {TMPDIR} "${TMPDIR}" + ) + + add_custom_command( + OUTPUT "Packaged/${NAME}" + DEPENDS BuildDirs + COMMAND "${CMAKE_COMMAND}" -E make_directory "${TMPDIR}" + ${ARG_UNPARSED_ARGUMENTS} + COMMAND "${CMAKE_COMMAND}" -E rename + "${TMPDIR}/${NAME}" + "${CMAKE_CURRENT_BINARY_DIR}/Packaged/${NAME}" + COMMAND "${CMAKE_COMMAND}" -E rm -r -- "${TMPDIR}" + VERBATIM + ) +endfunction() + +add_data_file(ApplicationResources.gpf + DEPENDS + MiniRez gpr2gpa FTagData MergeGPF "GliderProData/Glider PRO.r" + ApplicationResourcePatches/manifest.json DefaultTimestamp.timestamp + COMMAND MiniRez + "GliderProData/Glider PRO.r" + "{TMPDIR}/ApplicationResources.gpr" + COMMAND gpr2gpa + "{TMPDIR}/ApplicationResources.gpr" + DefaultTimestamp.timestamp + "{TMPDIR}/ApplicationResources.gpa" + -patch ApplicationResourcePatches/manifest.json + COMMAND FTagData + DefaultTimestamp.timestamp + "{TMPDIR}/ApplicationResources.gpf" + data ozm5 0 0 locked + COMMAND MergeGPF + "{TMPDIR}/ApplicationResources.gpf" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + ) + +file(GLOB_RECURSE FONT_DEPS RELATIVE "${CMAKE_SOURCE_DIR}" CONFIGURE_DEPENDS Resources/Fonts/*) +add_data_file(Fonts.gpf + DEPENDS GenerateFonts MiniRez gpr2gpa FTagData MergeGPF ${FONT_DEPS} + COMMAND GenerateFonts "${CMAKE_SOURCE_DIR}/Resources" {TMPDIR} + COMMAND MiniRez "${CMAKE_SOURCE_DIR}/Empty.r" {TMPDIR}/Fonts.gpr + COMMAND gpr2gpa + {TMPDIR}/Fonts.gpr + "${CMAKE_SOURCE_DIR}/DefaultTimestamp.timestamp" + {TMPDIR}/Fonts.gpa + -patch {TMPDIR}/FontCacheManifest.json + COMMAND FTagData + DefaultTimestamp.timestamp + {TMPDIR}/Fonts.gpf + data ozm5 0 0 locked + COMMAND MergeGPF {TMPDIR}/Fonts.gpf + ) + +# These files are committed to the repo and aren't currently useful on non-Windows systems anyway. +#file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Aerofoil/ConvertedResources) +# +#set(CONVERTED_ICONS +# Aerofoil/ConvertedResources/Large128.ico +# Aerofoil/ConvertedResources/Large129.ico +# Aerofoil/ConvertedResources/Large130.ico +# Aerofoil/ConvertedResources/Large131.ico +# Aerofoil/ConvertedResources/Large132.ico +# Aerofoil/ConvertedResources/Large133.ico +# Aerofoil/ConvertedResources/Small128.ico +# Aerofoil/ConvertedResources/Small129.ico +# Aerofoil/ConvertedResources/Small130.ico +# Aerofoil/ConvertedResources/Small133.ico +# ) +# +#add_custom_command( +# OUTPUT ${CONVERTED_ICONS} +# DEPENDS ConvertColorCursors Packaged/ApplicationResources.gpr +# COMMAND ConvertColorCursors +# ) +#add_custom_target(Icons DEPENDS ${CONVERTED_ICONS}) + +set(HOUSE_FILES) + +function(add_house NAME) + cmake_parse_arguments(PARSE_ARGV 0 ARG + "" + PATCH + "" + ) + + if(ARG_PATCH) + set(PATCH_ARGS -patch "${CMAKE_SOURCE_DIR}/HousePatches/${ARG_PATCH}") + endif() + + set(BASE_PATH "Packaged/Houses/${NAME}") + list(APPEND HOUSE_FILES "${BASE_PATH}.gpf") + + set(BYPRODUCTS "${BASE_PATH}.gpr" "${BASE_PATH}.gpa" "${BASE_PATH}.gpd") + set(BINHEX_SRC "${CMAKE_SOURCE_DIR}/GliderProData/Houses/${NAME}.binhex") + set(TS "${CMAKE_SOURCE_DIR}/DefaultTimestamp.timestamp") + + add_custom_command( + OUTPUT + "${BASE_PATH}.gpf" + BYPRODUCTS + ${BYPRODUCTS} + DEPENDS hqx2gp gpr2gpa MergeGPF BuildDirs "${BINHEX_SRC}" "${TS}" + COMMAND hqx2gp + "${BINHEX_SRC}" + "${TS}" + "${BASE_PATH}" + COMMAND gpr2gpa + "${BASE_PATH}.gpr" + "${TS}" + "${BASE_PATH}.gpa" + ${PATCH_ARGS} + ${HOUSE_EXTRA_COMMANDS} + COMMAND MergeGPF + "${BASE_PATH}.gpf" + COMMAND "${CMAKE_COMMAND}" -E rm + ${BYPRODUCTS} + VERBATIM + ) + + set(MOV_GPA_SRC "${CMAKE_SOURCE_DIR}/GliderProData/ConvertedMovies/${NAME}.mov.gpa") + if(EXISTS "${MOV_GPA_SRC}") + list(APPEND HOUSE_FILES "${BASE_PATH}.mov.gpf") + + add_custom_command( + OUTPUT + "${BASE_PATH}.mov.gpf" + BYPRODUCTS + "${BASE_PATH}.mov.gpa" + DEPENDS FTagData MergeGPF BuildDirs "${MOV_GPA_SRC}" "${TS}" + COMMAND FTagData + "${TS}" + "${BASE_PATH}.mov.gpf" + MooV ozm5 0 0 locked + COMMAND "${CMAKE_COMMAND}" -E copy + -t "Packaged/Houses" + "${MOV_GPA_SRC}" + COMMAND MergeGPF + "${BASE_PATH}.mov.gpf" + COMMAND "${CMAKE_COMMAND}" -E rm + "${BASE_PATH}.mov.gpa" + VERBATIM + ) + endif() + set(HOUSE_FILES "${HOUSE_FILES}" PARENT_SCOPE) +endfunction() + +add_house("Art Museum") +add_house("California or Bust!") + +set(HOUSE_EXTRA_COMMANDS + DEPENDS HouseTool + COMMAND HouseTool + patch "Packaged/Houses/Castle o' the Air.gpd" .firstRoom 77 + ) +add_house("Castle o' the Air") +unset(HOUSE_EXTRA_COMMANDS) + +add_house("CD Demo House") +add_house("Davis Station") +add_house("Demo House") +add_house("Fun House") +add_house("Grand Prix" PATCH "GrandPrix.json") +add_house("ImagineHouse PRO II" PATCH "ImagineHousePROII.json") +add_house("In The Mirror" PATCH "InTheMirror.json") +add_house("Land of Illusion") +add_house("Leviathan" PATCH "Leviathan.json") +add_house("Metropolis") +add_house("Nemo's Market") +add_house("Rainbow's End" PATCH "RainbowsEnd.json") +add_house("Slumberland") +add_house("SpacePods") +add_house("Teddy World") +add_house("The Asylum Pro") +add_house("Titanic") + +add_custom_target(Executable DEPENDS "${EXECNAME}") + +add_custom_target(Resources ALL + DEPENDS + ${DATA_FILES} + ${HOUSE_FILES} + ) + +set(TOOL_EXES + flattenmov + bin2gp + hqx2bin + hqx2gp + MakeTimestamp + FTagData + gpr2gpa + unpacktool + MergeGPF + ) +add_custom_target(Tools ALL DEPENDS ${TOOL_EXES}) + + +list(TRANSFORM DATA_FILES PREPEND "${CMAKE_CURRENT_BINARY_DIR}/") +list(TRANSFORM HOUSE_FILES PREPEND "${CMAKE_CURRENT_BINARY_DIR}/") + +install(TARGETS "${EXECNAME}" COMPONENT Executable) +install(FILES ${DATA_FILES} DESTINATION lib/aerofoil/Packaged COMPONENT Resources) +install(FILES ${HOUSE_FILES} DESTINATION lib/aerofoil/Packaged/Houses COMPONENT Resources) +install(TARGETS ${TOOL_EXES} DESTINATION lib/aerofoil/tools COMPONENT Tools) diff --git a/ConvertColorCursors/ConvertColorCursors.cpp b/ConvertColorCursors/ConvertColorCursors.cpp index 4d8ee7e..3dd5664 100644 --- a/ConvertColorCursors/ConvertColorCursors.cpp +++ b/ConvertColorCursors/ConvertColorCursors.cpp @@ -8,8 +8,11 @@ #include "QDStandardPalette.h" #include "PLBigEndian.h" #include "PLDrivers.h" -#include +#include "WindowsUnicodeToolShim.h" +#include +#include +#include #include #include @@ -92,12 +95,12 @@ void ConvertIconFamily(PortabilityLayer::ResourceFile *resFile, int32_t iconBitm item.a = 0; } - char outPath[256]; - sprintf_s(outPath, "Aerofoil\\ConvertedResources\\%s%i.ico", prefix, resID); + std::string outPath = ( + std::ostringstream() << "Aerofoil/ConvertedResources/" << prefix << resID << ".ico" + ).str(); - FILE *outF = nullptr; - errno_t outErr = fopen_s(&outF, outPath, "wb"); - if (!outErr) + FILE *outF = fopen_utf8(outPath.c_str(), "wb"); + if (outF) { IconDir iconDir; iconDir.m_reserved = 0; @@ -127,6 +130,8 @@ void ConvertIconFamily(PortabilityLayer::ResourceFile *resFile, int32_t iconBitm fwrite(&iconDirEntry, 1, sizeof(IconDirEntry), outF); fclose(outF); } + else + perror(outPath.c_str()); delete[] pixelData; bwBlock.Dispose(); @@ -134,12 +139,14 @@ void ConvertIconFamily(PortabilityLayer::ResourceFile *resFile, int32_t iconBitm } } -int main(int argc, const char **argv) +int toolMain(int argc, const char **argv) { - FILE *f = nullptr; - errno_t err = fopen_s(&f, "Packaged\\ApplicationResources.gpr", "rb"); - if (err) - return err; + FILE *f = fopen_utf8("Packaged/ApplicationResources.gpr", "rb"); + if (!f) + { + perror("Cannot open Packaged/ApplicationResources.gpr"); + return -1; + } PortabilityLayer::CFileStream stream(f); diff --git a/ConvertResources.bat b/ConvertResources.bat index 0907a67..89c9392 100644 --- a/ConvertResources.bat +++ b/ConvertResources.bat @@ -8,7 +8,7 @@ x64\Release\gpr2gpa.exe "Packaged\ApplicationResources.gpr" "DefaultTimestamp.ti x64\Release\FTagData.exe "DefaultTimestamp.timestamp" "Packaged\ApplicationResources.gpf" data ozm5 0 0 locked x64\Release\MergeGPF.exe "Packaged\ApplicationResources.gpf" -x64\Release\GenerateFonts.exe +x64\Release\GenerateFonts.exe Resources Packaged x64\Release\MiniRez.exe "Empty.r" Packaged\Fonts.gpr x64\Release\gpr2gpa.exe "Packaged\Fonts.gpr" "DefaultTimestamp.timestamp" "Packaged\Fonts.gpa" -patch "Packaged\FontCacheManifest.json" diff --git a/FTagData/FTagData.cpp b/FTagData/FTagData.cpp index c0337df..77a7e0c 100644 --- a/FTagData/FTagData.cpp +++ b/FTagData/FTagData.cpp @@ -1,6 +1,6 @@ #include #include -#include + #include "MacFileInfo.h" #include "CFileStream.h" #include "CombinedTimestamp.h" diff --git a/GenerateFonts/GenerateFonts.cpp b/GenerateFonts/GenerateFonts.cpp index 53d1506..fdaa96f 100644 --- a/GenerateFonts/GenerateFonts.cpp +++ b/GenerateFonts/GenerateFonts.cpp @@ -1,3 +1,7 @@ +#include +#include +#include + #include "FontFamily.h" #include "FontManager.h" #include "FontRenderer.h" @@ -7,8 +11,6 @@ #include "GpAppInterface.h" #include "GpDriverIndex.h" #include "GpFontHandlerProperties.h" -#include "GpFileSystem_Win32.h" -#include "GpSystemServices_Win32.h" #include "CFileStream.h" #include "PLDrivers.h" @@ -17,7 +19,11 @@ #include "WindowsUnicodeToolShim.h" -extern "C" __declspec(dllimport) IGpFontHandler *GpDriver_CreateFontHandler_FreeType2(const GpFontHandlerProperties &properties); +extern "C" +#ifdef _MSC_VER +__declspec(dllimport) +#endif +IGpFontHandler *GpDriver_CreateFontHandler_FreeType2(const GpFontHandlerProperties &properties); class MemBufferStream final : public GpIOStream { @@ -168,6 +174,16 @@ bool KnownFontSpec::operator!=(const KnownFontSpec &other) const int toolMain(int argc, const char **argv) { + if (argc < 3) + { + fputs("Usage: GenerateFonts \n", stderr); + return -1; + } + std::string resourcesDir(argv[1]); + resourcesDir += '/'; + std::string outputDir(argv[2]); + outputDir += '/'; + IGpAllocator *alloc = GpAllocator_C::GetInstance(); GpFontHandlerProperties fhProperties; @@ -187,12 +203,13 @@ int toolMain(int argc, const char **argv) std::vector catalog; std::vector fontSpecs; - FILE *manifestF = fopen_utf8("Packaged/FontCacheManifest.json", "wb"); + std::string tmpPath(outputDir + "FontCacheManifest.json"); + FILE *manifestF = fopen_utf8(tmpPath.c_str(), "wb"); fprintf(manifestF, "{\n"); fprintf(manifestF, "\t\"add\" :\n"); fprintf(manifestF, "\t{\n"); - fprintf(manifestF, "\t\t\"RFCT/1000.bin\" : \"Packaged/FontCacheCatalog.bin\""); + fprintf(manifestF, "\t\t\"RFCT/1000.bin\" : \"%sFontCacheCatalog.bin\"", outputDir.c_str()); int numFontsEmitted = 0; for (int presetIndex = 0; presetIndex < PortabilityLayer::FontPresets::kCount; presetIndex++) @@ -220,8 +237,9 @@ int toolMain(int argc, const char **argv) fontSpecs.push_back(spec); - std::string resPath = std::string("Resources/") + path; - FILE *fontFile = fopen_utf8(resPath.c_str(), "rb"); + tmpPath = resourcesDir; + tmpPath += path; + FILE *fontFile = fopen_utf8(tmpPath.c_str(), "rb"); if (fontFile) { @@ -236,7 +254,7 @@ int toolMain(int argc, const char **argv) { char fontPath[1024]; - sprintf(fontPath, "Packaged/CachedFont%i.bin", numFontsEmitted); + sprintf(fontPath, "%sCachedFont%i.bin", outputDir.c_str(), numFontsEmitted); FILE *cachedFontF = fopen_utf8(fontPath, "wb"); PortabilityLayer::CFileStream cacheStream(cachedFontF); @@ -290,7 +308,9 @@ int toolMain(int argc, const char **argv) PortabilityLayer::RenderedFontCatalogHeader catHeader; - FILE *catF = fopen_utf8("Packaged/FontCacheCatalog.bin", "wb"); + tmpPath = outputDir; + tmpPath += "FontCacheCatalog.bin"; + FILE *catF = fopen_utf8(tmpPath.c_str(), "wb"); catHeader.m_version = PortabilityLayer::RenderedFontCatalogHeader::kVersion; catHeader.m_pathsOffset = static_cast(sizeof(PortabilityLayer::RenderedFontCatalogHeader) + paths.size() * sizeof(PortabilityLayer::RenderedFontCatalogPathEntry) + numFontsEmitted * sizeof(PortabilityLayer::RenderedFontCatalogRFontEntry)); diff --git a/GpCommon/GpRenderedGlyphMetrics.h b/GpCommon/GpRenderedGlyphMetrics.h index 91415a8..584d5cb 100644 --- a/GpCommon/GpRenderedGlyphMetrics.h +++ b/GpCommon/GpRenderedGlyphMetrics.h @@ -1,5 +1,6 @@ #pragma once +#include #include struct GpRenderedGlyphMetrics diff --git a/LINUX.txt b/LINUX.txt index 6c8ecce..7dc1513 100644 --- a/LINUX.txt +++ b/LINUX.txt @@ -1,18 +1,49 @@ -Linux support is IN ALPHA and may or may not work. Only Cygwin has been -tested so far. +Linux support is IN ALPHA and may or may not work. -You can attempt the following: -- Install CMake (https://cmake.org/download/) -- Install SDL 2.0.12 or higher, or build it from the included source -- Run "cmake ." in the Aerofoil source directory -- Run "make install" to build the AerofoilX program -- Unzip the the Windows build -- Copy the "Packaged" and "Resources" directories from the Windows build into - the "lib" folder above the "bin" folder that the AerofoilX program installed - to. For example, if it installed to /usr/local/bin/, then the data files - should go in /usr/local/lib/aerofoil/ -- Run AerofoilX +Use these basic steps to build and install Aerofoil: +- Ensure dependencies are installed - all of these are likely available from + your distribution's package manager: + - CMake 3.10 or later (https://cmake.org/download/) + - SDL 2.0.12 or later (https://libsdl.org) + - FreeType 2.10.1 or later (https://freetype.org/download.html) +- Change directory to the Aerofoil source root (the directory containing this file) +- Run "cmake -B build" to create a CMake build tree under "build" +- Run "cmake --build build" to build the AerofoilX program and its resources +- Run "cmake --install build" to install everything under /usr/local +- You can now run Aerofoil with the command "AerofoilX" +You can also build just the AerofoilX executable and use the prebuilt resources +from the Windows release: +- Follow the steps above, ignoring the FreeType dependency and stopping after + running "cmake -B build" +- Run "cmake --build build --target Executable" to build only the executable +- Run "cmake --install build --component Executable" to install AerofoilX in + /usr/local/bin +- Download the Windows build from + https://github.com/elasota/Aerofoil/releases/latest and unzip it +- Create the directory /usr/local/lib/aerofoil and copy the "Packaged" + directory from the Windows build into it + +To install under a prefix other than /usr/local add +"-DCMAKE_INSTALL_PREFIX=" to "cmake -B build" + +Tools for converting Glider PRO houses are installed under +/usr/local/lib/aerofoil/tools, see Documentation/userhouses.txt for +instructions on how to use them. + +The main AerofoilX executable, its required resources and the conversion tools +can be built and installed individually via these build targets and install +components: +- Executable +- Resources +- Tools + +For instance, you can use + cmake --build build --target Executable Resources +to build only Aerofoil and its resources, and + cmake --install build --component Executable + cmake --install build --component Resources +to install them. Please report any issues that you experience with this to the issue tracker on GitHub. diff --git a/MakeTimestamp/MakeTimestamp.cpp b/MakeTimestamp/MakeTimestamp.cpp index bbc9e4e..58694d8 100644 --- a/MakeTimestamp/MakeTimestamp.cpp +++ b/MakeTimestamp/MakeTimestamp.cpp @@ -1,6 +1,13 @@ +#ifdef _WIN32 #include -#include -#include + +#include +#else +#include +#include +#endif + +#include #include "CombinedTimestamp.h" @@ -13,6 +20,7 @@ int main(int argc, const char **argv) return -1; } +#ifdef _WIN32 SYSTEMTIME epochStart; epochStart.wYear = 1904; epochStart.wMonth = 1; @@ -34,13 +42,6 @@ int main(int argc, const char **argv) int64_t currentTime64 = (static_cast(timestampFT.dwLowDateTime) & 0xffffffff) | (static_cast(timestampFT.dwHighDateTime) << 32); int64_t timeDelta = (currentTime64 - epochStart64) / 10000000; - FILE *f = nullptr; - if (fopen_s(&f, argv[1], "wb")) - { - fprintf(stderr, "Error opening output file"); - return -1; - } - TIME_ZONE_INFORMATION tz; GetTimeZoneInformation(&tz); @@ -60,9 +61,36 @@ int main(int argc, const char **argv) ts.m_localHour = localST.wHour; ts.m_localMinute = localST.wMinute; ts.m_localSecond = localST.wSecond; +#else + time_t currentTimeUnix = time(nullptr); + + tm *currentTimeStruct = localtime(¤tTimeUnix); + if (currentTimeStruct == nullptr) { + fprintf(stderr, "Error converting system time to calendar format"); + return -1; + } + + PortabilityLayer::CombinedTimestamp ts; + ts.SetMacEpochTime(currentTimeUnix + ts.kMacEpochToUTC); + + ts.SetLocalYear(currentTimeStruct->tm_year + 1900); + ts.m_localMonth = currentTimeStruct->tm_mon + 1; + ts.m_localDay = currentTimeStruct->tm_mday; + + ts.m_localHour = currentTimeStruct->tm_hour; + ts.m_localMinute = currentTimeStruct->tm_min; + ts.m_localSecond = currentTimeStruct->tm_sec; +#endif memset(ts.m_padding, 0, sizeof(ts.m_padding)); + FILE *f = fopen(argv[1], "wb"); + if (!f) + { + perror("Error opening output file"); + return -1; + } + fwrite(&ts, sizeof(ts), 1, f); fclose(f); diff --git a/MiniRez/MiniRez.cpp b/MiniRez/MiniRez.cpp index 6b03b48..644a5f0 100644 --- a/MiniRez/MiniRez.cpp +++ b/MiniRez/MiniRez.cpp @@ -1,3 +1,4 @@ +#include #include #include #include diff --git a/WindowsUnicodeToolShim/UnixUnicodeToolShim.cpp b/WindowsUnicodeToolShim/UnixUnicodeToolShim.cpp new file mode 100644 index 0000000..65f09c6 --- /dev/null +++ b/WindowsUnicodeToolShim/UnixUnicodeToolShim.cpp @@ -0,0 +1,99 @@ +#include "WindowsUnicodeToolShim.h" + +#include +#include +#include +#include +#include + +#include +#include + +// Linux and macOS (and probably other non-Windows systems in general) do not +// have separate file system or command line APIs for different text encodings. +// On these systems UTF-8 is commonly the system encoding and, if so, argv will +// typically be UTF-8 encoded, though it is the calling process's responsibility +// to ensure this, as argv is passed verbatim as a sequence of byte strings. +// +// Filesystems on Unix-like systems are typically encoding-unaware, in which +// case the actual encoding used should match the system encoding, or else +// manual intervention is required. +// +// On macOS, HFS+ and APFS filenames are encoded as UTF-16 and UTF-8 +// respectively, and the C filesystem API accepts UTF-8 strings. There are some +// gotchas relating to Unicode normalization, where HFS+ enforces a specific +// form of normalization at the filesystem layer but APFS does not, and using +// the C API to access the filesystem may avoid automatic normalization that +// higher-level macOS API functions may perform. +// +// In summary, text encoding is still a hairy problem on every computer system, +// though on the major non-Windows systems, assuming UTF-8 encoding is +// reasonable. For now, this header simply maps the WindowsUnicodeToolShim +// functions to their regular C API counterparts. +int toolMain(int argc, const char **argv); +int main(int argc, const char **argv) { return toolMain(argc, argv); } + +FILE *fopen_utf8(const char *path, const char *options) { return fopen(path, options); } +int fputs_utf8(const char *str, FILE *f) { return fputs(str, f); } +int mkdir_utf8(const char *path) { return mkdir(path, 0777); } + +void TerminateDirectoryPath(std::string &path) +{ + const size_t len = path.length(); + + if (len == 0 || path[len - 1] != '/') + path.push_back('/'); +} + +void ScanDirectoryForExtension +( + std::vector &outPaths, + const char *path, + const char *ending, + bool recursive +) { + DIR *dir = opendir(path); + if (!dir) + { + return; + } + + size_t endingLen = strlen(ending); + + dirent *ent; + while ((ent = readdir(dir))) + { + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) + continue; + else if (recursive && ent->d_type == DT_DIR) + { + std::string tmpPath(path); + tmpPath.append("/"); + tmpPath.append(ent->d_name); + ScanDirectoryForExtension( + outPaths, + tmpPath.c_str(), + ending, + recursive + ); + } + else + { + size_t nameLen = strlen(ent->d_name); + + if (endingLen <= nameLen && memcmp + ( + ent->d_name + nameLen - endingLen, + ending, + endingLen + ) == 0 + ) { + std::string tmpPath(path); + tmpPath.append("/"); + tmpPath.append(ent->d_name); + outPaths.push_back(std::move(tmpPath)); + } + } + } + closedir(dir); +} diff --git a/WindowsUnicodeToolShim/WindowsUnicodeToolShim.h b/WindowsUnicodeToolShim/WindowsUnicodeToolShim.h index 8d94ab5..2396c69 100644 --- a/WindowsUnicodeToolShim/WindowsUnicodeToolShim.h +++ b/WindowsUnicodeToolShim/WindowsUnicodeToolShim.h @@ -8,6 +8,7 @@ int mkdir_utf8(const char *path); void TerminateDirectoryPath(std::string &path); void ScanDirectoryForExtension(std::vector& outPaths, const char *path, const char *ending, bool recursive); +#ifdef _WIN32 struct DirectoryScanContext; struct DirectoryScanEntry { @@ -17,3 +18,11 @@ struct DirectoryScanEntry DirectoryScanContext *opendir_utf8(const char *name); DirectoryScanEntry *readdir_utf8(DirectoryScanContext *dir); void closedir_utf8(DirectoryScanContext *context); + +#else + +inline int _fseeki64(FILE *stream, long offset, int whence) { + return fseek(stream, offset, whence); +} +inline long _ftelli64(FILE *stream) { return ftell(stream); }; +#endif diff --git a/gpr2gpa/gpr2gpa.cpp b/gpr2gpa/gpr2gpa.cpp index d1f4def..2c1ff73 100644 --- a/gpr2gpa/gpr2gpa.cpp +++ b/gpr2gpa/gpr2gpa.cpp @@ -31,9 +31,9 @@ #include #include #include +#include #include #include -#include "GpWindows.h" enum AudioCompressionCodecID { @@ -98,6 +98,11 @@ void AppendFmt(std::vector &array, const char *fmt, ...) if (resultSize <= 0) return; + // vsnprintf invalidates the va_list, so we need to + // reinit args so the next call doesn't print garbage. + va_end(args); + va_start(args, fmt); + size_t appendSize = static_cast(resultSize); if (SIZE_MAX == appendSize) @@ -1466,7 +1471,7 @@ bool DecompressSound(int compressionID, int channelCount, const void *sndData, s } } } - else if (compressionID = AudioCompressionCodecID_SixToOne) + else if (compressionID == AudioCompressionCodecID_SixToOne) { if (channelCount != 1) { @@ -2427,14 +2432,11 @@ int ConvertSingleFile(const char *resPath, const PortabilityLayer::CombinedTimes const size_t numRefs = typeList.m_numRefs; { - char subName[256]; - sprintf_s(subName, "%s", resTag.m_id); - PlannedEntry entry; - entry.m_name = subName; + entry.m_name = resTag.m_id; entry.m_isDirectory = true; - contents.push_back(entry); + contents.push_back(std::move(entry)); } for (size_t rlIndex = 0; rlIndex < numRefs; rlIndex++) @@ -2453,14 +2455,15 @@ int ConvertSingleFile(const char *resPath, const PortabilityLayer::CombinedTimes if (typeList.m_resType == pictTypeID || typeList.m_resType == dateTypeID) { - char resName[256]; - sprintf_s(resName, "%s/%i.bmp", resTag.m_id, static_cast(res.m_resID)); + std::string resName = ( + std::ostringstream() << resTag.m_id << '/' << res.m_resID << ".bmp" + ).str(); - if (ContainsName(reservedNames, resName)) + if (ContainsName(reservedNames, resName.c_str())) continue; PlannedEntry entry; - entry.m_name = resName; + entry.m_name = std::move(resName); entry.m_comment = resComment; if (ImportPICT(entry.m_uncompressedContents, resData, resSize, dumpqtDir, res.m_resID)) @@ -2470,14 +2473,15 @@ int ConvertSingleFile(const char *resPath, const PortabilityLayer::CombinedTimes } else if (typeList.m_resType == sndTypeID) { - char resName[256]; - sprintf_s(resName, "%s/%i.wav", resTag.m_id, static_cast(res.m_resID)); + std::string resName = ( + std::ostringstream() << resTag.m_id << '/' << res.m_resID << ".wav" + ).str(); - if (ContainsName(reservedNames, resName)) + if (ContainsName(reservedNames, resName.c_str())) continue; PlannedEntry entry; - entry.m_name = resName; + entry.m_name = std::move(resName); entry.m_comment = resComment; if (ImportSound(entry.m_uncompressedContents, resData, resSize, res.m_resID)) @@ -2487,14 +2491,15 @@ int ConvertSingleFile(const char *resPath, const PortabilityLayer::CombinedTimes } else if (typeList.m_resType == indexStringTypeID) { - char resName[256]; - sprintf_s(resName, "%s/%i.txt", resTag.m_id, static_cast(res.m_resID)); + std::string resName = ( + std::ostringstream() << resTag.m_id << '/' << res.m_resID << ".txt" + ).str(); - if (ContainsName(reservedNames, resName)) + if (ContainsName(reservedNames, resName.c_str())) continue; PlannedEntry entry; - entry.m_name = resName; + entry.m_name = std::move(resName); entry.m_comment = resComment; if (ImportIndexedString(entry.m_uncompressedContents, resData, resSize)) @@ -2502,14 +2507,15 @@ int ConvertSingleFile(const char *resPath, const PortabilityLayer::CombinedTimes } else if (typeList.m_resType == ditlTypeID) { - char resName[256]; - sprintf_s(resName, "%s/%i.json", resTag.m_id, static_cast(res.m_resID)); + std::string resName = ( + std::ostringstream() << resTag.m_id << '/' << res.m_resID << ".json" + ).str(); - if (ContainsName(reservedNames, resName)) + if (ContainsName(reservedNames, resName.c_str())) continue; PlannedEntry entry; - entry.m_name = resName; + entry.m_name = std::move(resName); entry.m_comment = resComment; if (ImportDialogItemTemplate(entry.m_uncompressedContents, resData, resSize)) @@ -2524,15 +2530,16 @@ int ConvertSingleFile(const char *resPath, const PortabilityLayer::CombinedTimes const IconTypeSpec &iconSpec = iconTypeSpecs[i]; if (typeList.m_resType == iconSpec.m_resTypeID) { - char resName[256]; - sprintf_s(resName, "%s/%i.bmp", resTag.m_id, static_cast(res.m_resID)); + std::string resName = ( + std::ostringstream() << resTag.m_id << '/' << res.m_resID << ".bmp" + ).str(); - if (!ContainsName(reservedNames, resName)) + if (!ContainsName(reservedNames, resName.c_str())) { isIcon = true; PlannedEntry entry; - entry.m_name = resName; + entry.m_name = std::move(resName); entry.m_comment = resComment; if (ImportIcon(entry.m_uncompressedContents, resData, resSize, iconSpec.m_width, iconSpec.m_height, iconSpec.m_bpp)) @@ -2545,15 +2552,16 @@ int ConvertSingleFile(const char *resPath, const PortabilityLayer::CombinedTimes if (!isIcon) { - char resName[256]; - sprintf_s(resName, "%s/%i.bin", resTag.m_id, static_cast(res.m_resID)); + std::string resName = ( + std::ostringstream() << resTag.m_id << '/' << res.m_resID << ".bin" + ).str(); - if (ContainsName(reservedNames, resName)) + if (ContainsName(reservedNames, resName.c_str())) continue; PlannedEntry entry; - entry.m_name = resName; + entry.m_name = std::move(resName); entry.m_comment = resComment; entry.m_uncompressedContents.resize(res.GetSize()); diff --git a/unpacktool/CompactProRLEDecompressor.h b/unpacktool/CompactProRLEDecompressor.h index 51586ea..af33fb7 100644 --- a/unpacktool/CompactProRLEDecompressor.h +++ b/unpacktool/CompactProRLEDecompressor.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "IDecompressor.h" class CompactProRLEDecompressor : public IDecompressor diff --git a/unpacktool/IDecompressor.h b/unpacktool/IDecompressor.h index b1a5f4c..8788ea4 100644 --- a/unpacktool/IDecompressor.h +++ b/unpacktool/IDecompressor.h @@ -1,6 +1,6 @@ #pragma once -#include +#include struct CSInputBuffer; diff --git a/unpacktool/IFileReader.h b/unpacktool/IFileReader.h index bd40ed7..a6ad14a 100644 --- a/unpacktool/IFileReader.h +++ b/unpacktool/IFileReader.h @@ -1,6 +1,7 @@ #pragma once -#include +#include +#include struct IFileReader { diff --git a/unpacktool/PrefixCode.cpp b/unpacktool/PrefixCode.cpp index 0546bad..01912c1 100644 --- a/unpacktool/PrefixCode.cpp +++ b/unpacktool/PrefixCode.cpp @@ -1,3 +1,5 @@ +#include + #include "PrefixCode.h" struct XADCodeTreeNode diff --git a/unpacktool/RLE90Decompressor.h b/unpacktool/RLE90Decompressor.h index cf81fea..77cd22a 100644 --- a/unpacktool/RLE90Decompressor.h +++ b/unpacktool/RLE90Decompressor.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "IDecompressor.h" class RLE90Decompressor : public IDecompressor diff --git a/unpacktool/unpacktool.cpp b/unpacktool/unpacktool.cpp index e82a84b..7defb39 100644 --- a/unpacktool/unpacktool.cpp +++ b/unpacktool/unpacktool.cpp @@ -28,9 +28,6 @@ #include -#include -#include - #include #include @@ -387,7 +384,7 @@ int ExtractItem(int depth, const ArchiveItem &item, const std::string &dirPath, { mkdir_utf8(path.c_str()); - path.append("\\"); + path.append("/"); int returnCode = RecursiveExtractFiles(depth + 1, item.m_children, path, pathParanoid, reader, ts); if (returnCode)