diff --git a/CMakeLists.txt b/CMakeLists.txt index b5523175b..03094c477 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,11 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # this is originally a Windows project, so it uses _DEBUG, not NDEBUG add_compile_definitions("$<$:_DEBUG>") -add_compile_options(-Werror=return-type -Wno-switch) +if (MSVC) + add_compile_options(/W4) +else() + add_compile_options(-Werror=return-type -Wno-switch) +endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_compile_options(-Werror=format -Wno-error=format-overflow -Wno-error=format-truncation -Wno-psabi) @@ -61,8 +65,12 @@ if (BUILD_LIBRETRO OR BUILD_APPLEN OR BUILD_SA2) add_subdirectory(source/frontends/common2) endif() -if (BUILD_APPLEN) - add_subdirectory(source/frontends/ncurses) +if (NOT MSVC) + # ncurses is not supported on Windows MSVC, only on MSYS2/MinGW + # TODO: add support for Windows MSVC via PDCurses + if (BUILD_APPLEN) + add_subdirectory(source/frontends/ncurses) + endif() endif() if (BUILD_LIBRETRO) @@ -71,6 +79,7 @@ endif() if (BUILD_SA2) add_subdirectory(source/frontends/sdl) + set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT sa2) endif() if (NOT WIN32) diff --git a/libyaml/CMakeLists.txt b/libyaml/CMakeLists.txt index 6dbf542cd..736a285f6 100644 --- a/libyaml/CMakeLists.txt +++ b/libyaml/CMakeLists.txt @@ -24,3 +24,7 @@ target_include_directories(yaml PRIVATE target_compile_definitions(yaml PRIVATE HAVE_CONFIG_H ) + +if (MSVC) + target_compile_definitions(yaml PUBLIC YAML_DECLARE_STATIC) +endif() \ No newline at end of file diff --git a/minizip/CMakeLists.txt b/minizip/CMakeLists.txt index eeea60c88..f0bef1a11 100644 --- a/minizip/CMakeLists.txt +++ b/minizip/CMakeLists.txt @@ -14,3 +14,7 @@ add_library(minizip STATIC target_include_directories(minizip INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/.. ) + +find_package(ZLIB REQUIRED) + +target_link_libraries(minizip PRIVATE ZLIB::ZLIB) \ No newline at end of file diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 162fd6c52..01e72b314 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -276,6 +276,7 @@ endif() target_link_libraries(appleii PUBLIC yaml minizip + apple2roms ) target_link_directories(appleii PUBLIC @@ -284,9 +285,19 @@ target_link_directories(appleii PUBLIC ${ZLIB_LIBRARY_DIRS} ) -target_compile_options(appleii PUBLIC - -Wno-multichar +if (MSVC) + target_compile_options(appleii PUBLIC + /wd4566 # disable multi-character constant warning ) + target_compile_definitions(appleii PRIVATE + NOMINMAX # to avoid conflicts with std::min/max + DISABLE_SPEECH_API # undefine because the original AppleWin speech API is not supported here + ) +else() + target_compile_options(appleii PUBLIC + -Wno-multichar + ) +endif() # wildcards (*.SYM) are not supported on Windows msys2 add_custom_command( diff --git a/source/StdAfx.h b/source/StdAfx.h index ace7cf68d..3ed48b7b9 100644 --- a/source/StdAfx.h +++ b/source/StdAfx.h @@ -45,8 +45,10 @@ #define SM_CXPADDEDBORDER 92 #endif -#ifndef __MINGW32__ -#define USE_SPEECH_API +#ifndef DISABLE_SPEECH_API +# ifndef __MINGW32__ +# define USE_SPEECH_API +# endif #endif #ifdef __MINGW32__ diff --git a/source/frontends/common2/CMakeLists.txt b/source/frontends/common2/CMakeLists.txt index f9b7ef306..cf8e7b542 100644 --- a/source/frontends/common2/CMakeLists.txt +++ b/source/frontends/common2/CMakeLists.txt @@ -45,6 +45,20 @@ target_link_libraries(common2 PRIVATE apple2roms ) +# ---- getopt for Windows (MSVC/MinGW on Win32) ---- +if (WIN32) + # Build the local getopt implementation and expose its headers to this target + target_sources(common2 PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/getopt.c + ) + target_include_directories(common2 PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ) + + # Prevent from defining min/max macros, which break numeric_limits::min/max + target_compile_definitions(common2 PRIVATE NOMINMAX) +endif() + if (NOT WIN32) target_link_libraries(common2 PUBLIC windows diff --git a/source/frontends/common2/argparser.cpp b/source/frontends/common2/argparser.cpp index 6a00062a8..a41209515 100644 --- a/source/frontends/common2/argparser.cpp +++ b/source/frontends/common2/argparser.cpp @@ -6,7 +6,11 @@ #include "Memory.h" -#include +#ifdef _WIN32 +# include "frontends/common2/getopt.h" +#else +# include +#endif #include #include #include diff --git a/source/frontends/common2/getopt.c b/source/frontends/common2/getopt.c new file mode 100644 index 000000000..cfb275aad --- /dev/null +++ b/source/frontends/common2/getopt.c @@ -0,0 +1,109 @@ +#include "getopt.h" +#include +#include + +// For Windows MSVC only + +char *optarg = NULL; +int optind = 1, opterr = 1, optopt = '?'; + +static int optpos = 1; + +static int +_parse_short(int argc, char * const argv[], const char *optstring) +{ + char c = argv[optind][optpos]; + const char *cp = strchr(optstring, c); + + if (!cp) { + if (opterr) + fprintf(stderr, "unknown option -- %c\n", c); + if (argv[optind][++optpos] == '\0') { + optind++; + optpos = 1; + } + optopt = c; + return '?'; + } + if (cp[1] == ':') { + if (argv[optind][optpos+1] != '\0') { + optarg = &argv[optind][optpos+1]; + optind++; + } else if (++optind < argc) { + optarg = argv[optind++]; + } else { + optopt = c; + return (optstring[0] == ':') ? ':' : '?'; + } + optpos = 1; + } else { + if (argv[optind][++optpos] == '\0') { + optpos = 1; + optind++; + } + optarg = NULL; + } + return c; +} + +int getopt(int argc, char * const argv[], const char *optstring) +{ + if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') + return -1; + if (strcmp(argv[optind], "--") == 0) { + optind++; + return -1; + } + return _parse_short(argc, argv, optstring); +} + +int getopt_long(int argc, char * const argv[], const char *optstring, + const struct option *longopts, int *longindex) +{ + if (optind >= argc) return -1; + + if (argv[optind][0] == '-' && argv[optind][1] == '-') { + const char *name = argv[optind] + 2; + const char *eq = strchr(name, '='); + size_t len = eq ? (size_t)(eq - name) : strlen(name); + + for (int i = 0; longopts[i].name; i++) { + if (strncmp(name, longopts[i].name, len) == 0 + && len == strlen(longopts[i].name)) { + if (longindex) *longindex = i; + if (longopts[i].has_arg == no_argument) { + optarg = NULL; + } else if (longopts[i].has_arg == required_argument) { + if (eq) + optarg = (char*)eq+1; + else if (optind+1 < argc) + optarg = argv[++optind]; + else + return optopt = longopts[i].val, '?'; + } else if (longopts[i].has_arg == optional_argument) { + optarg = eq ? (char*)eq+1 : NULL; + } + optind++; + if (longopts[i].flag) { + *(longopts[i].flag) = longopts[i].val; + return 0; + } else { + return longopts[i].val; + } + } + } + /* no match */ + if (opterr) + fprintf(stderr, "unrecognized option '--%s'\n", name); + optind++; + return '?'; + } + /* fallback to short option parsing */ + return getopt(argc, argv, optstring); +} + +int getopt_long_only(int argc, char * const argv[], const char *optstring, + const struct option *longopts, int *longindex) +{ + return getopt_long(argc, argv, optstring, longopts, longindex); +} diff --git a/source/frontends/common2/getopt.h b/source/frontends/common2/getopt.h new file mode 100644 index 000000000..6c71713d1 --- /dev/null +++ b/source/frontends/common2/getopt.h @@ -0,0 +1,39 @@ +#ifndef GETOPT_H +#define GETOPT_H + +// For Windows MSVC only + +#ifdef __cplusplus +extern "C" { +#endif + +/* argument flags */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +struct option { + const char *name; + int has_arg; + int *flag; + int val; +}; + +/* globals */ +extern char *optarg; +extern int optind, opterr, optopt; + +/* functions */ +int getopt(int argc, char * const argv[], const char *optstring); + +int getopt_long(int argc, char * const argv[], const char *optstring, + const struct option *longopts, int *longindex); + +int getopt_long_only(int argc, char * const argv[], const char *optstring, + const struct option *longopts, int *longindex); + +#ifdef __cplusplus +} +#endif + +#endif /* GETOPT_H */ diff --git a/source/frontends/docs/msvc.md b/source/frontends/docs/msvc.md new file mode 100644 index 000000000..fb96ba41c --- /dev/null +++ b/source/frontends/docs/msvc.md @@ -0,0 +1,29 @@ +# Microsoft VisualStudio support + +## Why + +The original AppleWin project is built with Visual Studio. We now provide support for Visual Studio when building 'sa2', the SDL version of AppleWin. + +## Status + +`sa2` compiles under VisualStudio 2022. + +## Building + +Very **important** to install `vcpkg`. Refer to the Microsoft documentation. + +Install using `vcpkg` the following packages: +``` +vcpkg install sdl2 sdl2-image sdl2-ttf +vcpkg install zlib +vcpkg install boost +``` +Then configure: +``` +cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=C:\PATH_TO_VCPKG\scripts\buildsystems\vcpkg.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DVCPKG_TARGET_TRIPLET=x64-windows -DBUILD_SA2=ON +``` +Then open the generated `build\applewin.sln` file in Visual Studio. + +## Running + +`sa2` should be the startup project that runs when you press F5. diff --git a/source/frontends/docs/xcode.md b/source/frontends/docs/xcode.md new file mode 100644 index 000000000..1a7fbe612 --- /dev/null +++ b/source/frontends/docs/xcode.md @@ -0,0 +1,23 @@ +# Apple Xcode support + +## Status + +Compiles cleanly under Xcode. + +## Building + +Configure: +Move to the top level directory of AppleWin +Create a `build` directory and configure (here to build the SDL version): +``` +mkdir build +cmake -G Xcode -B build -DBUILD_SA2=ON +``` +Then open the generated `build/applewin.xcodeproj` file in Xcode, or just type: +``` +open build/applewin.xcodeproj +``` + +## Running + +`sa2` should be inside build/Debug or build/Release depending on your choice. diff --git a/source/frontends/sdl/CMakeLists.txt b/source/frontends/sdl/CMakeLists.txt index 22af23242..180af28d8 100644 --- a/source/frontends/sdl/CMakeLists.txt +++ b/source/frontends/sdl/CMakeLists.txt @@ -10,6 +10,7 @@ find_package(SDL2 REQUIRED) # we should use find_package, but Ubuntu does not provide it for SDL2_image pkg_search_module(SDL2_IMAGE REQUIRED SDL2_image) +target_compile_features(common2 PUBLIC cxx_std_20) if ((${CMAKE_SYSTEM_NAME} MATCHES "Darwin") OR (${CMAKE_SYSTEM_NAME} MATCHES "Windows")) # only OpenGL supported on MacOS and Windows @@ -27,7 +28,6 @@ else() ) endif() - set(SOURCE_FILES main.cpp gamepad.cpp @@ -128,7 +128,21 @@ target_include_directories(sa2 PRIVATE target_compile_definitions(sa2 PRIVATE IMGUI_USER_CONFIG="frontends/sdl/imgui/imconfig.h" - ) +) + +if (MSVC) + target_compile_definitions(sa2 PRIVATE + NOMINMAX # to avoid conflicts with std::min/max + DISABLE_SPEECH_API # undefine because the original AppleWin speech API is not supported here + ) +endif() install(TARGETS sa2 - DESTINATION bin) + RUNTIME_DEPENDENCIES + PRE_EXCLUDE_REGEXES "api-ms-" "ext-ms-" # skip Windows system DLLs + POST_EXCLUDE_REGEXES ".*system32/.*" # skip more system DLLs + FRAMEWORK DESTINATION Frameworks + RUNTIME DESTINATION bin + LIBRARY DESTINATION bin + ARCHIVE DESTINATION lib +) diff --git a/source/frontends/sdl/processfile.cpp b/source/frontends/sdl/processfile.cpp index c2fdebaac..96791981e 100644 --- a/source/frontends/sdl/processfile.cpp +++ b/source/frontends/sdl/processfile.cpp @@ -14,6 +14,10 @@ #include #include +#ifdef _WIN32 +# define strcasecmp _stricmp +#endif + namespace {