Initial update to make the application build and run on Windows.

Much more work will be needed to get the Windows port working properly.
This commit is contained in:
Leon Styhre 2020-07-03 20:23:51 +02:00
parent e4fdd1e20d
commit 76aa239855
34 changed files with 3933 additions and 227 deletions

4
.gitignore vendored
View file

@ -6,6 +6,9 @@
# Compiled Dynamic libraries
*.so
# Windows Dynamic-link libraries
*.[dD][lL][lL]
# Compiled Static libraries
*.lai
*.la
@ -16,6 +19,7 @@
# Compiled executable
emulationstation
emulationstation.exe
# build directory
build

View file

@ -56,6 +56,9 @@ if(${GLSystem} MATCHES "Desktop OpenGL")
else()
find_package(OpenGLES REQUIRED)
endif()
# Skip package dependency checks if we're on Windows.
if (NOT WIN32)
find_package(CURL REQUIRED)
find_package(FreeImage REQUIRED)
find_package(Freetype REQUIRED)
@ -63,6 +66,7 @@ find_package(Pugixml REQUIRED)
find_package(RapidJSON REQUIRED)
find_package(SDL2 REQUIRED)
find_package(VLC REQUIRED)
endif()
# Add libCEC support.
if(CEC)
@ -101,14 +105,10 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
if (G++_VERSION VERSION_LESS 4.8)
message(SEND_ERROR "You need at least GCC 4.8 to compile EmulationStation-DE!")
endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
message("-- Compiler is MSVC (NOT TESTED)")
set(CMAKE_DEBUG_POSTFIX "d")
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE)
add_definitions(-DNOMINMAX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") # Multi-processor compilation.
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") # Multi-processor compilation.
if (WIN32)
set(CMAKE_CXX_FLAGS "-mwindows ${CMAKE_CXX_FLAGS}")
# set(CMAKE_CXX_FLAGS "-static-libstdc++ -static-libgcc ${CMAKE_CXX_FLAGS}")
endif()
endif()
# Set up compiler flags for debug or release builds.
@ -136,13 +136,24 @@ else()
add_definitions(-DUSE_OPENGLES_10)
endif()
# Assign the installation prefix to local $ES_INSTALL_PREFIX variable.
# For Unix systems, assign the installation prefix to local $ES_INSTALL_PREFIX variable.
if (NOT WIN32)
add_definitions(-DES_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}")
endif()
# Enable additional defines for the Debug build configuration.
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
# Handle additional (required) include files for dependency packages.
if (WIN32)
set(WIN32_INCLUDE_DIR "NOT_DEFINED" CACHE FILEPATH "")
if(NOT EXISTS ${WIN32_INCLUDE_DIR})
message(SEND_ERROR "Can't find WIN32 include directory: ${WIN32_INCLUDE_DIR}")
endif()
#include_directories(${WIN32_INCLUDE_DIR})
endif()
#---------------------------------------------------------------------------------------------------
# Add include directories.
set(COMMON_INCLUDE_DIRS
@ -157,6 +168,12 @@ set(COMMON_INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/es-core/src
)
if (WIN32)
set(COMMON_INCLUDE_DIRS
${COMMON_INCLUDE_DIRS}
${WIN32_INCLUDE_DIR})
endif()
# Add libCEC include directory.
if(DEFINED libCEC_FOUND)
LIST(APPEND COMMON_INCLUDE_DIRS
@ -200,6 +217,7 @@ elseif(DEFINED VERO4K)
"${CMAKE_FIND_ROOT_PATH}/opt/vero3/lib")
endif()
if (NOT WIN32)
set(COMMON_LIBRARIES
${CURL_LIBRARIES}
${FreeImage_LIBRARIES}
@ -208,6 +226,19 @@ set(COMMON_LIBRARIES
${SDL2_LIBRARY}
${VLC_LIBRARIES}
nanosvg)
elseif(WIN32)
set(COMMON_LIBRARIES
"${PROJECT_SOURCE_DIR}/libpugixml.dll"
"${PROJECT_SOURCE_DIR}/FreeImage.dll"
"${PROJECT_SOURCE_DIR}/libcurl-x64.dll"
"${PROJECT_SOURCE_DIR}/libvlc.dll"
"${PROJECT_SOURCE_DIR}/libfreetype.dll"
"Winmm.dll"
"mingw32"
"${PROJECT_SOURCE_DIR}/libSDL2main.a"
"${PROJECT_SOURCE_DIR}/SDL2.dll"
"nanosvg")
endif()
# Add libCEC libraries.
if(DEFINED libCEC_FOUND)

View file

@ -175,25 +175,177 @@ sudo apt-get install rpm
```
**On Windows:**
### On Windows:
[CMake](http://www.cmake.org/cmake/resources/software.html)
This is a strange legacy operating system. However it's still popular, so we need to support it for the time being.
[SDL2](http://www.libsdl.org/release/SDL2-devel-2.0.8-VC.zip)
I did a brief evaluation of the Microsoft Visual C++ compiler (MSVC) but as far as I'm concerned it's an abomination so I won't cover it here and it won't be supported.
[FreeImage](http://downloads.sourceforge.net/freeimage/FreeImage3154Win32.zip)
At the moment I have only built the software using GCC on Windows, but I may try to get Clang/LLVM working at a later date.
[FreeType2](http://download.savannah.gnu.org/releases/freetype/freetype-2.4.9.tar.bz2) (you'll need to compile)
Anyway, here's a brief summary of how to get a build environment up and running on Windows.
[cURL](http://curl.haxx.se/download.html) (you'll need to compile or get the pre-compiled DLL version)
**Install Git, CMake, MinGW and your code editor:**
[RapisJSON](https://github.com/tencent/rapidjson) (you need to add `include/rapidsjon` to your include path)
[Git](https://gitforwindows.org)
Remember to copy the necessary .DLL files into the same folder as the executable: probably FreeImage.dll, freetype6.dll, SDL2.dll, libcurl.dll, and zlib1.dll. Exact list depends on if you built your libraries in "static" mode or not.
[CMake](https://cmake.org/download)
[MinGW](https://gnutoolchains.com/mingw64)
Make a copy of `mingw64/bin/mingw32-make` to `make` just for convenience and make sure that the necessary paths are defined for the PATH environmental variable.
I won't get into the details on how to configure Git, but there are many resources available online to support with this. The `Git Bash` shell is very useful though as it's somewhat reproducing a Unix environment using MinGW/MSYS.
Install your editor of choice. As for VSCodium it's unfortunately broken or crippled under Windows, making some important extensions impossible to install. VSCode works fine, but make sure to disable all surveillance functionality (telemetry).
It's strongly recommended to set line breaks to linefeed (Unix-style) directly in the editor, although it can also be configured in Git for conversion during commit. The source code for EmulationStation-DE only uses Unix-style line breaks.
**Enable pretty printing for GDB:**
This is useful for displaying std::string values for example.
Adjust your paths accordingly, the below are just examples of course.
Save a file to
C:/Programming/mingw64/bin/pp.gdb with the following contents:
```
python
import sys
sys.path.insert(0, 'c:/Programming/mingw64/share/gcc-9.1.0/python/libstdcxx/v6')
from printers import register_libstdcxx_printers
register_libstdcxx_printers (None)
end
```
If using VSCode, add the following line to launch.json:
`"miDebuggerArgs": "-x c:/programming/mingw64/bin/pp.gdb",`
An equivalent setup should be possible on other code editors as well.
Note that most GDB builds for Windows have broken Python support so that pretty printing won't work. The MinGW installation recommended in the previous step seems to work fine though.
**Download the dependency packages:**
[FreeImage](https://sourceforge.net/projects/freeimage)
[cURL](https://curl.haxx.se/download.html)
[SDL2](https://www.libsdl.org/download-2.0.php)
[libVLC](https://ftp.lysator.liu.se/pub/videolan/vlc)
Uncompress the files to a suitable directory, for example C:/Programming/Dependencies/
The following packages are not readily available for Windows, so clone the repos and build them yourself:
[FreeType](https://www.freetype.org)
```
git clone git://git.savannah.gnu.org/freetype/freetype2.git
git checkout VER-2-10-2
mkdir build
cmake -G "MinGW Makefiles" -DBUILD_SHARED_LIBS=ON ..
make
```
[pugixml](https://pugixml.org)
```
git clone git://github.com/zeux/pugixml
git checkout v1.10
cmake -G "MinGW Makefiles" -DBUILD_SHARED_LIBS=ON .
make
```
As for RapidJSON, you don't need to compile it, you just need the include files:
[RapidJSON](http://rapidjson.org)
```
git clone git://github.com/Tencent/rapidjson
git checkout v1.1.0
```
**Clone the EmulationStation-DE repository:**
This works the same as in Unix, just run the following:
```
git clone https://gitlab.com/leonstyhre/emulationstation-de
```
**Setup the include directories:**
As there is no standardized include directory structure in Windows and no package manager, you need to provide the include files manually.
Make a directory in your build environment tree, for instance under `C:/Programming/include`.
Copy the include files from cURL, FreeImage, FreeType, pugixml, RapidJSON, SDL2 and VLC to this directory.
It should then look something like this:
```
$ ls -1 include/
curl/
FreeImage.h
freetype/
ft2build.h
pugiconfig.hpp
pugixml.hpp
rapidjson/
SDL2/
vlc/
```
**Copy the required DLL files to the EmulationStation build directory:**
As there's no package manager in Windows and no way to handle dependencies, we need to ship all the required shared libraries with the application.
Copy the following files to the `emulationstation-de` build directory. Most of them will come from the packages that were provided in the previous steps of this guide:
```
FreeImage.dll
libcrypto-1_1-x64.dll (from the OpenSSL package, located in Git MinGW/MSYS under /mingw/bin/)
libcurl-x64.dll
libfreetype.dll
libgcc_s_seh-1.dll (located in Git MinGW/MSYS under /mingw/bin/)
libpugixml.dll
libssl-1_1-x64.dll (from the OpenSSL package, located in Git MinGW under /mingw/bin/)
libstdc++-6.dll
libvlc.dll
libvlccore.dll
libwinpthread-1.dll (located in Git MinGW under /mingw/bin/)
SDL2.dll
libSDL2main.a
```
The files from the MinGW installation must correspond to the version used to compile the binary.
*So if the MinGW installation is upgraded to a newer version or so, make sure to copy the .dll files again, overwriting the old ones.*
**Building the application:**
For a release build:
cmake -G "MinGW Makefiles" -DWIN32_INCLUDE_DIR=../include .
Or for a debug build:
cmake -G "MinGW Makefiles" -DWIN32_INCLUDE_DIR=../include -DCMAKE_BUILD_TYPE=Debug .
For some reason defining the '../include' path doesn't work when running CMake from PowerShell (and no, changing to backslash doesn't help). Instead use Bash, by running from a `Git Bash` shell.
The make command works fine directly in PowerShell though so it can be run from the VSCode terminal.
Running `make -j6` (or whatever number of parallel jobs you prefer) should now build the binary.
Note that compilation time is much longer than on Unix, and linking time is excessive for a debug build. The debug binary is also much larger than on Unix.
A worthwhile endeavour could be to setup a cross-compilation environment using WLS/WLS2 (Linux), but I have not tried it.
Configuring
===========
Configuring EmulationStation-DE
===============================
**~/.emulationstation/es_systems.cfg:**
@ -320,7 +472,9 @@ Here's an overview of the file layout:
<extension>.smc .SMC .sfc .SFC .swc .SWC .fig .FIG .bs .BS .bin .BIN .mgd .MGD .7z .7Z .zip .ZIP</extension>
<!-- The command executed when a game is launched. A few special variables are replaced if found in a command, like %ROM% (see below).
This example would run RetroArch with the the snes9x_libretro core. -->
This example would run RetroArch with the the snes9x_libretro core.
If there are spaces in the path or file name, you must enclose them in quotation marks, for example:
retroarch -L "~/my configs/retroarch/cores/snes9x_libretro.so" %ROM% -->
<command>retroarch -L ~/.config/retroarch/cores/snes9x_libretro.so %ROM%</command>
<!-- The platform(s) to use when scraping. You can see the full list of accepted platforms in src/PlatformIds.cpp.

View file

@ -102,7 +102,7 @@ set(ES_SOURCES
#-------------------------------------------------------------------------------
# Define OS specific sources and headers.
if(MSVC)
if(WIN32)
LIST(APPEND ES_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/EmulationStation.rc
)
@ -114,18 +114,6 @@ include_directories(${COMMON_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/src)
add_executable(emulationstation ${ES_SOURCES} ${ES_HEADERS})
target_link_libraries(emulationstation ${COMMON_LIBRARIES} es-core)
# Special properties for Windows builds.
if(MSVC)
# Always compile with the "WINDOWS" subsystem to avoid console window flashing at startup
# when --debug is not set (see es-core/src/main.cpp for explanation).
# The console will still be shown if launched with --debug.
# Note that up to CMake 2.8.10 this feature is broken: http://public.kitware.com/Bug/view.php?id=12566
set_target_properties(emulationstation PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS")
set_target_properties(emulationstation PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS")
set_target_properties(emulationstation PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
set_target_properties(emulationstation PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
endif()
#-------------------------------------------------------------------------------
# Set up CPack install for `make install`.

View file

@ -436,7 +436,11 @@ void FileData::launchGame(Window* window)
AudioManager::getInstance()->deinit();
VolumeControl::getInstance()->deinit();
// window->deinit();
// TEMPORARY - Windows does not like it at all if you launch a game without
// first doing a deinit(). Need to fix this properly at a later date.
#ifdef _WIN64
window->deinit();
#endif
std::string command = "";
@ -467,7 +471,11 @@ void FileData::launchGame(Window* window)
Scripting::fireEvent("game-end");
// window->init();
// TEMPORARY - Windows does not like it at all if you launch a game without
// first doing a deinit(). Need to fix this properly at a later date.
#ifdef _WIN64
window->init();
#endif
VolumeControl::getInstance()->init();
window->normalizeNextUpdate();

View file

@ -20,11 +20,9 @@
#include "Settings.h"
#include "ThemeData.h"
#include "views/UIModeController.h"
#include <pugixml.hpp>
#include <fstream>
#ifdef WIN32
#include <Windows.h>
#endif
std::vector<SystemData*> SystemData::sSystemVector;

View file

@ -20,8 +20,14 @@
#include "PowerSaver.h"
#include "Sound.h"
#include "SystemData.h"
#include <unordered_map>
#include <time.h>
#ifdef _WIN64
#include <cstring>
#endif
#define FADE_TIME 300
SystemScreenSaver::SystemScreenSaver(

View file

@ -9,7 +9,8 @@
#include "math/Misc.h"
#include "Log.h"
#include "Settings.h"
#ifdef WIN32
#ifdef _WIN64
#include <mmdeviceapi.h>
#endif
@ -34,7 +35,7 @@ VolumeControl::VolumeControl()
mixerHandle(nullptr),
mixerElem(nullptr),
mixerSelemId(nullptr)
#elif defined(WIN32) || defined(_WIN32)
#elif defined(_WIN64)
, mixerHandle(nullptr),
endpointVolume(nullptr)
#endif
@ -56,7 +57,7 @@ VolumeControl::VolumeControl(
mixerHandle(nullptr),
mixerElem(nullptr),
mixerSelemId(nullptr)
#elif defined(WIN32) || defined(_WIN32)
#elif defined(_WIN64)
, mixerHandle(nullptr),
endpointVolume(nullptr)
#endif
@ -156,7 +157,7 @@ void VolumeControl::init()
LOG(LogError) << "VolumeControl::init() - Failed to open ALSA mixer!";
}
}
#elif defined(WIN32) || defined(_WIN32)
#elif defined(_WIN64)
// Get windows version information.
OSVERSIONINFOEXA osVer = {sizeof(OSVERSIONINFO)};
::GetVersionExA(reinterpret_cast<LPOSVERSIONINFOA>(&osVer));
@ -164,7 +165,11 @@ void VolumeControl::init()
if (osVer.dwMajorVersion < 6) {
// Windows older than Vista. use mixer API. open default mixer.
if (mixerHandle == nullptr) {
#if defined(_WIN64)
if (mixerOpen(&mixerHandle, 0, (DWORD_PTR)nullptr, 0, 0) == MMSYSERR_NOERROR) {
#else
if (mixerOpen(&mixerHandle, 0, nullptr, 0, 0) == MMSYSERR_NOERROR) {
#endif
// Retrieve info on the volume slider control for the "Speaker Out" line.
MIXERLINECONTROLS mixerLineControls;
mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
@ -239,7 +244,7 @@ void VolumeControl::deinit()
mixerHandle = nullptr;
mixerElem = nullptr;
}
#elif defined(WIN32) || defined(_WIN32)
#elif defined(_WIN64)
if (mixerHandle != nullptr) {
mixerClose(mixerHandle);
mixerHandle = nullptr;
@ -283,7 +288,7 @@ int VolumeControl::getVolume() const
LOG(LogError) << "VolumeControl::getVolume() - Failed to get volume range!";
}
}
#elif defined(WIN32) || defined(_WIN32)
#elif defined(_WIN64)
if (mixerHandle != nullptr) {
// Windows older than Vista. use mixer API. get volume from line control.
MIXERCONTROLDETAILS_UNSIGNED value;
@ -355,7 +360,7 @@ void VolumeControl::setVolume(int volume)
LOG(LogError) << "VolumeControl::getVolume() - Failed to get volume range!";
}
}
#elif defined(WIN32) || defined(_WIN32)
#elif defined(_WIN64)
if (mixerHandle != nullptr) {
// Windows older than Vista. use mixer API. get volume from line control.
MIXERCONTROLDETAILS_UNSIGNED value;

View file

@ -16,10 +16,10 @@
#include <unistd.h>
#include <fcntl.h>
#include <alsa/asoundlib.h>
#elif defined(WIN32) || defined(_WIN32)
#elif defined(_WIN64)
#include <Windows.h>
#include <endpointvolume.h>
#include <mmeapi.h>
//#include <mmeapi.h>
#endif
// Singleton pattern. Call getInstance() to get an object.
@ -34,7 +34,7 @@ class VolumeControl
snd_mixer_t* mixerHandle;
snd_mixer_elem_t* mixerElem;
snd_mixer_selem_id_t* mixerSelemId;
#elif defined(WIN32) || defined(_WIN32)
#elif defined(_WIN64)
HMIXER mixerHandle;
MIXERCONTROL mixerControl;
IAudioEndpointVolume * endpointVolume;

View file

@ -10,7 +10,7 @@
#include "components/NinePatchComponent.h"
#include "components/TextComponent.h"
#ifdef __linux__
#if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL_timer.h>
#else
#include "SDL_timer.h"

View file

@ -28,7 +28,7 @@
#include <algorithm>
#ifdef __linux__
#if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL_events.h>
#else
#include "SDL_events.h"

View file

@ -31,7 +31,12 @@
#include "SystemData.h"
#include "SystemScreenSaver.h"
#ifdef __linux__
#ifdef _WIN64
#include <cstring>
#include <windows.h>
#endif
#if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_main.h>
#include <SDL2/SDL_timer.h>
@ -44,9 +49,6 @@
#include <FreeImage.h>
#include <iostream>
#include <time.h>
#ifdef WIN32
#include <Windows.h>
#endif
enum eErrorCodes {
NO_ERRORS,
@ -54,10 +56,96 @@ enum eErrorCodes {
NO_ROMS
};
#ifdef _WIN64
enum eConsoleType {
NO_CONSOLE,
PARENT_CONSOLE,
ALLOCATED_CONSOLE
};
// Console output for Windows. The handling of consoles is a mess on this operating system,
// and this is the best solution I could find. EmulationStation is built using the WINDOWS
// subsystem (using the -mwindows compiler flag). The idea is to attach to or allocate a new
// console as needed. However some console types such as the 'Git Bash' shell simply doesn't
// work properly. Windows thinks it's attaching to a console but is unable to redirect the
// standard input and output. Output also can't be redirected or piped by the user for any
// console type and PowerShell behaves quite strange. Still, it works well enough to be
// somewhat usable, at least for the moment. If the allocConsole argument is set to true
// and there is no console available, a new console window will be spawned.
eConsoleType outputToConsole(bool allocConsole)
{
HANDLE outputHandle = nullptr;
HWND consoleWindow = nullptr;
eConsoleType ConsoleType = NO_CONSOLE;
// Try to attach to a parent console process.
if (AttachConsole(ATTACH_PARENT_PROCESS))
outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
// If there is a parent console process, then attempt to retrieve its handle.
if (outputHandle != INVALID_HANDLE_VALUE && outputHandle != nullptr) {
consoleWindow = GetConsoleWindow();
ConsoleType = PARENT_CONSOLE;
}
// If we couldn't retrieve the handle, it means we need to allocate a new console window.
if (!consoleWindow && allocConsole) {
AllocConsole();
ConsoleType = ALLOCATED_CONSOLE;
}
// If we are attached to the parent console or we have opened a new console window,
// then redirect stdin, stdout and stderr accordingly.
if (ConsoleType == PARENT_CONSOLE || ConsoleType == ALLOCATED_CONSOLE) {
FILE* fp = nullptr;
freopen_s(&fp, "CONIN$", "rb", stdin);
freopen_s(&fp, "CONOUT$", "wb", stdout);
setvbuf(stdout, NULL, _IONBF, 0);
freopen_s(&fp, "CONOUT$", "wb", stderr);
setvbuf(stderr, NULL, _IONBF, 0);
// Point the standard streams to the console.
std::ios::sync_with_stdio(true);
// Clear the error state for each standard stream.
std::wcout.clear();
std::cout.clear();
std::wcerr.clear();
std::cerr.clear();
std::wcin.clear();
std::cin.clear();
std::cout << "\n";
}
return ConsoleType;
}
void closeConsole()
{
FILE* fp;
// Redirect stdin, stdout and stderr to NUL.
freopen_s(&fp, "NUL:", "r", stdin);
freopen_s(&fp, "NUL:", "w", stdout);
freopen_s(&fp, "NUL:", "w", stderr);
FreeConsole();
}
#endif
bool parseArgs(int argc, char* argv[])
{
Utils::FileSystem::setExePath(argv[0]);
#ifdef _WIN64
// Print any command line output to the console.
if (argc > 1)
eConsoleType ConsoleType = outputToConsole(false);
#endif
// We need to process --home before any call to Settings::getInstance(),
// because settings are loaded from the home path.
for (int i = 1; i < argc; i++) {
@ -161,7 +249,6 @@ bool parseArgs(int argc, char* argv[])
}
else if (strcmp(argv[i], "--debug") == 0) {
Settings::getInstance()->setBool("Debug", true);
Settings::getInstance()->setBool("HideConsole", false);
Log::setReportingLevel(LogDebug);
}
else if (strcmp(argv[i], "--fullscreen-normal") == 0) {
@ -194,14 +281,6 @@ bool parseArgs(int argc, char* argv[])
return false;
}
else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
#ifdef WIN32
// This is a bit of a hack, but otherwise output will go to nowhere when the
// application is compiled with the "WINDOWS" subsystem (which we usually are).
// If you're an experienced Windows programmer and know how to do this
// the right way, please submit a pull request!
AttachConsole(ATTACH_PARENT_PROCESS);
freopen("CONOUT$", "wb", stdout);
#endif
std::cout <<
"EmulationStation Desktop Edition\n"
"An Emulator Front-end\n\n"
@ -212,11 +291,7 @@ bool parseArgs(int argc, char* argv[])
"--draw-framerate Display the framerate\n"
"--no-exit Don't show the exit option in the menu\n"
"--no-splash Don't show the splash screen\n"
#ifdef WIN32
"--debug Show console and print debug information\n"
#else
"--debug Print debug information\n"
#endif
"--windowed Windowed mode, should be combined with --resolution\n"
"--fullscreen-normal Normal fullscreen mode\n"
"--fullscreen-borderless Borderless fullscreen mode (always on top)\n"
@ -300,39 +375,17 @@ int main(int argc, char* argv[])
std::locale::global(std::locale("C"));
if (!parseArgs(argc, argv))
if (!parseArgs(argc, argv)) {
#ifdef _WIN64
closeConsole();
#endif
return 0;
}
// Only show the console on Windows if HideConsole is false.
#ifdef WIN32
// MSVC has a "SubSystem" option, with two primary options: "WINDOWS" and "CONSOLE".
// In "WINDOWS" mode, no console is automatically created for us. This is good,
// because we can choose to only create the console window if the user explicitly
// asks for it, preventing it from flashing open and then closing.
// In "CONSOLE" mode, a console is always automatically created for us before we
// enter main. In this case, we can only hide the console after the fact, which
// will leave a brief flash.
// TL;DR: You should compile ES under the "WINDOWS" subsystem.
// I have no idea how this works with non-MSVC compilers.
if (!Settings::getInstance()->getBool("HideConsole")) {
// We want to show the console.
// If we're compiled in "CONSOLE" mode, this is already done.
// If we're compiled in "WINDOWS" mode, no console is created for us automatically;
// the user asked for one, so make one and then hook stdin/stdout/sterr up to it.
if (AllocConsole()) { // Should only pass in "WINDOWS" mode.
freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "wb", stdout);
freopen("CONOUT$", "wb", stderr);
}
}
else {
// We want to hide the console.
// If we're compiled with the "WINDOWS" subsystem, this is already done.
// If we're compiled with the "CONSOLE" subsystem, a console is already created;
// it'll flash open, but we hide it nearly immediately.
if (GetConsoleWindow()) // Should only pass in "CONSOLE" mode.
ShowWindow(GetConsoleWindow(), SW_HIDE);
}
#ifdef _WIN64
// Send debug output to the console..
if (Settings::getInstance()->getBool("Debug"))
outputToConsole(true);
#endif
// Call this ONLY when linking with FreeImage as a static library.
@ -508,5 +561,9 @@ int main(int argc, char* argv[])
LOG(LogInfo) << "EmulationStation cleanly shutting down.";
#ifdef _WIN64
closeConsole();
#endif
return 0;
}

View file

@ -162,8 +162,7 @@ void screenscraper_generate_scraper_requests(const ScraperSearchParams& params,
auto last = std::unique(p_ids.begin(), p_ids.end());
p_ids.erase(last, p_ids.end());
for (auto platform = p_ids.cbegin(); platform != p_ids.cend(); platform++)
{
for (auto platform = p_ids.cbegin(); platform != p_ids.cend(); platform++) {
path += "&systemeid=";
path += HttpReq::urlEncode(std::to_string(*platform));
requests.push(std::unique_ptr<ScraperRequest>

View file

@ -10,7 +10,7 @@
#include "Settings.h"
#include "Sound.h"
#ifdef __linux__
#if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL.h>
#else
#include "SDL.h"

View file

@ -8,7 +8,7 @@
#ifndef ES_CORE_AUDIO_MANAGER_H
#define ES_CORE_AUDIO_MANAGER_H
#ifdef __linux__
#if defined(__linux__) || defined (_WIN64)
#include <SDL2/SDL_audio.h>
#else
#include "SDL_audio.h"

View file

@ -8,8 +8,10 @@
#include "HttpReq.h"
#include "resources/ResourceManager.h"
#include "utils/FileSystemUtil.h"
#include "Log.h"
#include <assert.h>
CURLM* HttpReq::s_multi_handle = curl_multi_init();
@ -48,6 +50,15 @@ HttpReq::HttpReq(const std::string& url) : mStatus(REQ_IN_PROGRESS), mHandle(nul
{
mHandle = curl_easy_init();
// On Windows, use the bundled cURL TLS/SSL certificates (which actually come from the
// Mozilla project). There is a possibility to use the OS provided Schannel certificates
// but I haven't been able to get this to work and it also seems to be problematic on
// older Windows versions.
#ifdef _WIN64
curl_easy_setopt(mHandle, CURLOPT_CAINFO, ResourceManager::getInstance()->
getResourcePath(":/certificates/curl-ca-bundle.crt").c_str());
#endif
if (mHandle == nullptr) {
mStatus = REQ_IO_ERROR;
onError("curl_easy_init failed");

View file

@ -13,7 +13,7 @@
#include <sstream>
#include <vector>
#ifdef __linux__
#if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL_joystick.h>
#include <SDL2/SDL_keyboard.h>
#else

View file

@ -15,7 +15,7 @@
#include "Scripting.h"
#include "Window.h"
#ifdef __linux__
#if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL.h>
#else
#include "SDL.h"

View file

@ -10,7 +10,7 @@
#ifndef ES_CORE_INPUT_MANAGER_H
#define ES_CORE_INPUT_MANAGER_H
#ifdef __linux__
#if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL_joystick.h>
#else
#include "SDL_joystick.h"

View file

@ -6,14 +6,15 @@
#include "Platform.h"
#ifdef __linux__
#if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL_events.h>
#else
#include "SDL_events.h"
#endif
#ifdef WIN32
#ifdef _WIN64
#include <codecvt>
#include <locale>
#else
#include <unistd.h>
#endif
@ -23,7 +24,7 @@
int runRebootCommand()
{
#ifdef WIN32 // Windows.
#ifdef _WIN64 // Windows.
return system("shutdown -r -t 0");
#else // macOS and Linux.
return system("shutdown --reboot now");
@ -32,7 +33,7 @@ int runRebootCommand()
int runPoweroffCommand()
{
#ifdef WIN32 // Windows.
#ifdef _WIN64 // Windows.
return system("shutdown -s -t 0");
#else // macOS and Linux.
return system("shutdown --poweroff now");
@ -41,7 +42,7 @@ int runPoweroffCommand()
int runSystemCommand(const std::string& cmd_utf8)
{
#ifdef WIN32
#ifdef _WIN64
// On Windows we use _wsystem to support non-ASCII paths
// which requires converting from UTF8 to a wstring.
typedef std::codecvt_utf8<wchar_t> convert_type;
@ -67,7 +68,7 @@ int quitES(QuitMode mode)
void touch(const std::string& filename)
{
#ifdef WIN32
#ifdef _WIN64
FILE* fp = fopen(filename.c_str(), "ab+");
if (fp != nullptr)
fclose(fp);

View file

@ -11,7 +11,7 @@
#include <string>
// Why the hell this naming inconsistency exists is well beyond me.
#ifdef WIN32
#ifdef _WIN64
#define sleep Sleep
#endif

View file

@ -25,7 +25,6 @@ Settings* Settings::sInstance = nullptr;
std::vector<const char*> settings_dont_save {
// These options can be set using command-line arguments:
"Debug", // --debug
"HideConsole", // Implicitly set via the --debug flag.
"ForceKid", // --force-kid
"ForceKiosk", // --force-kiosk
"IgnoreGamelist", // --ignore-gamelist
@ -185,7 +184,6 @@ void Settings::setDefaults()
// Options listed using --help
mBoolMap["Debug"] = false;
mBoolMap["HideConsole"] = true; // Implicitly set via the --debug flag.
mBoolMap["ForceKid"] = false;
mBoolMap["ForceKiosk"] = false;
mBoolMap["IgnoreGamelist"] = false;

View file

@ -9,7 +9,7 @@
#ifndef ES_CORE_SOUND_H
#define ES_CORE_SOUND_H
#ifdef __linux__
#if defined(__linux__) || defined (_WIN64)
#include <SDL2/SDL_audio.h>
#else
#include "SDL_audio.h"

View file

@ -593,12 +593,17 @@ std::map<std::string, ThemeSet> ThemeData::getThemeSets()
{
std::map<std::string, ThemeSet> sets;
// Check for themes under the data installation directory (install prefix), as well
// as under the user home directory.
// Check for themes under the data installation directory for Unix or under the
// executable directory for Windows, as well as under the user home directory regardless
// of operating system.
static const size_t pathCount = 2;
std::string paths[pathCount] =
{
Utils::FileSystem::getInstallPrefixPath() + "/emulationstation/themes",
#ifdef _WIN64
Utils::FileSystem::getExePath() + "/themes",
#else
Utils::FileSystem::getProgramDataPath() + "/themes",
#endif
Utils::FileSystem::getHomePath() + "/.emulationstation/themes"
};

View file

@ -12,7 +12,7 @@
#include "ThemeData.h"
#include "Window.h"
#ifdef __linux__
#if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL_timer.h>
#else
#include "SDL_timer.h"

View file

@ -12,7 +12,7 @@
#include "PowerSaver.h"
#include "Settings.h"
#ifdef __linux__
#if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_timer.h>
#else
@ -22,8 +22,9 @@
#include <vlc/vlc.h>
#ifdef WIN32
#ifdef _WIN64
#include <codecvt>
#include <cstring>
#endif
libvlc_instance_t* VideoVlcComponent::mVLC = nullptr;
@ -243,7 +244,7 @@ void VideoVlcComponent::startVideo()
mVideoWidth = 0;
mVideoHeight = 0;
#ifdef WIN32
#ifdef _WIN64
std::string path(Utils::String::replace(mVideoPath, "/", "\\"));
#else
std::string path(mVideoPath);

View file

@ -13,7 +13,7 @@
#include "Log.h"
#include "Settings.h"
#ifdef __linux__
#if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL.h>
#else
#include "SDL.h"

View file

@ -11,7 +11,7 @@
#include "Log.h"
#include "Settings.h"
#ifdef __linux__
#if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#else
@ -29,7 +29,7 @@ namespace Renderer
const GLenum errorCode = glGetError();
if (errorCode != GL_NO_ERROR) {
LOG(LogError) << "OpenGLES error: " << _funcName <<
LOG(LogError) << "OpenGL error: " << _funcName <<
" failed with error code: " << errorCode;
}
}

View file

@ -11,7 +11,7 @@
#include "Log.h"
#include "Settings.h"
#ifdef __linux__
#if defined(__linux__) || defined(_WIN64)
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengles.h>
#else

View file

@ -12,10 +12,6 @@
#include "utils/StringUtil.h"
#include "Log.h"
#ifdef WIN32
#include <Windows.h>
#endif
FT_Library Font::sLibrary = nullptr;
int Font::getSize() const { return mSize; }
@ -215,40 +211,6 @@ void Font::getTextureForNewGlyph(const Vector2i& glyphSize,
std::vector<std::string> getFallbackFontPaths()
{
#ifdef WIN32
// Windows
// TEMPORARY, remove this and use the bundled fallback fonts instead.
// Get this system's equivalent of "C:\Windows" (might be on a different drive or
// in a different folder) so we can check the Fonts subdirectory for fallback fonts.
TCHAR winDir[MAX_PATH];
GetWindowsDirectory(winDir, MAX_PATH);
std::string fontDir = winDir;
fontDir += "\\Fonts\\";
const char* fontNames[] = {
"meiryo.ttc", // Japanese.
"simhei.ttf", // Chinese.
"arial.ttf" // Latin.
};
// Prepend to font file names.
std::vector<std::string> fontPaths;
fontPaths.reserve(sizeof(fontNames) / sizeof(fontNames[0]));
for (unsigned int i = 0; i < sizeof(fontNames) / sizeof(fontNames[0]); i++) {
std::string path = fontDir + fontNames[i];
if (ResourceManager::getInstance()->fileExists(path))
fontPaths.push_back(path);
}
fontPaths.shrink_to_fit();
return fontPaths;
#else
// Linux.
std::vector<std::string> fontPaths;
// Vera sans Unicode:
@ -269,8 +231,6 @@ std::vector<std::string> getFallbackFontPaths()
fontPaths.shrink_to_fit();
return fontPaths;
#endif
}
FT_Face Font::getFaceForChar(unsigned int id)

View file

@ -38,9 +38,14 @@ std::string ResourceManager::getResourcePath(const std::string& path) const
if (Utils::FileSystem::exists(test))
return test;
// Check under the data installation directory (install prefix).
test = Utils::FileSystem::getInstallPrefixPath() +
"/emulationstation/resources/" + &path[2];
// Check for the resource under the data installation directory for Unix or under
// the executable directory for Windows.
#ifdef _WIN64
test = Utils::FileSystem::getExePath() + "/resources/" + &path[2];
#else
test = Utils::FileSystem::getProgramDataPath() + "/resources/" + &path[2];
#endif
if (Utils::FileSystem::exists(test))
return test;
}

View file

@ -13,7 +13,7 @@
#include <sys/stat.h>
#include <string.h>
#ifdef WIN32
#if defined(_WIN64)
// Because windows...
#include <direct.h>
#include <Windows.h>
@ -22,17 +22,17 @@
#define snprintf _snprintf
#define stat64 _stat64
#define unlink _unlink
#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
//#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
//#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#else
#include <dirent.h>
#include <unistd.h>
#endif
// Try to get the install prefix as defined when CMake was run.
// For Unix systems, try to get the install prefix as defined when CMake was run.
// The installPrefix directory is the value set for CMAKE_INSTALL_PREFIX during build.
// If not defined, the default prefix path '/usr/local' will be used.
#ifdef __unix__
#if defined(__unix__)
#ifdef ES_INSTALL_PREFIX
std::string installPrefix = ES_INSTALL_PREFIX;
#else
@ -47,7 +47,7 @@ namespace Utils
static std::string homePath = "";
static std::string exePath = "";
#if defined(_WIN32)
#if defined(_WIN64)
static std::string convertFromWideString(const std::wstring wstring)
{
int numBytes = WideCharToMultiByte(CP_UTF8, 0, wstring.c_str(),
@ -70,7 +70,7 @@ namespace Utils
// Only parse the directory, if it's a directory.
if (isDirectory(path)) {
#if defined(_WIN32)
#if defined(_WIN64)
WIN32_FIND_DATAW findData;
std::string wildcard = path + "/*";
HANDLE hFind = FindFirstFileW(std::wstring(wildcard.begin(),
@ -149,14 +149,7 @@ namespace Utils
if (homePath.length())
return homePath;
// Check for HOME environment variable.
if (!homePath.length()) {
std::string envHome = getenv("HOME");
if (envHome.length())
homePath = getGenericPath(envHome);
}
#if defined(_WIN32)
#if defined(_WIN64)
// On Windows we need to check HOMEDRIVE and HOMEPATH.
if (!homePath.length()) {
std::string envHomeDrive = getenv("HOMEDRIVE");
@ -164,7 +157,14 @@ namespace Utils
if (envHomeDrive.length() && envHomePath.length())
homePath = getGenericPath(envHomeDrive + "/" + envHomePath);
}
#endif // _WIN32
#else
// Check for HOME environment variable.
if (!homePath.length()) {
std::string envHome = getenv("HOME");
if (envHome.length())
homePath = getGenericPath(envHome);
}
#endif
// No homepath found, fall back to current working directory.
if (!homePath.length())
@ -184,7 +184,7 @@ namespace Utils
void setExePath(const std::string& _path)
{
constexpr int path_max = 32767;
#if defined(_WIN32)
#if defined(_WIN64)
std::wstring result(path_max, 0);
if (GetModuleFileNameW(nullptr, &result[0], path_max) != 0)
exePath = convertFromWideString(result);
@ -207,22 +207,28 @@ namespace Utils
return exePath;
}
std::string getInstallPrefixPath()
std::string getProgramDataPath()
{
// installPrefix should be populated by CMAKE from $CMAKE_INSTALL_PREFIX.
// But just as a precaution, let's check if this variable is blank, and if so
// set it to '/usr/local'. Just in case some make environments won't handle this
// correctly.
// For Unix systems, installPrefix should be populated by CMAKE from
// $CMAKE_INSTALL_PREFIX. But just as a precaution, let's check if this
// variable is blank, and if so set it to '/usr/local'.
// Just in case some build environments won't handle this correctly.
// For Windows it doesn't really work like that and the application could have
// been install to an arbitrary location, so this function won't be used on that OS.
#ifdef __unix__
if (!installPrefix.length())
installPrefix = "/usr/local";
return installPrefix + "/share";
return installPrefix + "/share/emulationstation";
#else
return "";
#endif
}
std::string getPreferredPath(const std::string& _path)
{
std::string path = _path;
size_t offset = std::string::npos;
#if defined(_WIN32)
#if defined(_WIN64)
// Convert '/' to '\\'
while ((offset = path.find('/')) != std::string::npos)
path.replace(offset, 1, "\\");
@ -258,7 +264,7 @@ namespace Utils
{
std::string path = getGenericPath(_path);
#if defined(_WIN32)
#if defined(_WIN64)
// Windows escapes stuff by just putting everything in quotes.
return '"' + getPreferredPath(path) + '"';
#else
@ -316,7 +322,7 @@ namespace Utils
continue;
}
#if defined(_WIN32)
#if defined(_WIN64)
// Append folder to path.
path += (path.size() == 0) ? (*it) : ("/" + (*it));
#else
@ -481,13 +487,15 @@ namespace Utils
std::string path = getGenericPath(_path);
std::string resolved;
#if defined(_WIN32)
#if defined(_WIN64)
HANDLE hFile = CreateFile(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ,
0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
/*
if (hFile != INVALID_HANDLE_VALUE) {
resolved.resize(GetFinalPathNameByHandle(hFile, nullptr, 0,
resolved.resize(GetFinalPathNameByHandle(hFile, nullptr, 0,
FILE_NAME_NORMALIZED) + 1);
if (GetFinalPathNameByHandle(hFile, (LPSTR)resolved.data(),
if (GetFinalPathNameByHandle(hFile, (LPSTR)resolved.data(),
(DWORD)resolved.size(), FILE_NAME_NORMALIZED) > 0) {
resolved.resize(resolved.size() - 1);
@ -495,6 +503,7 @@ namespace Utils
}
CloseHandle(hFile);
}
*/
#else
struct stat info;
@ -555,7 +564,7 @@ namespace Utils
{
std::string path = getGenericPath(_path);
#if defined(_WIN32)
#if defined(_WIN64)
return ((path.size() > 1) && (path[1] == ':'));
#else
return ((path.size() > 0) && (path[0] == '/'));
@ -590,7 +599,7 @@ namespace Utils
{
std::string path = getGenericPath(_path);
#if defined(_WIN32)
#if defined(_WIN64)
// Check for symlink attribute.
const DWORD Attributes = GetFileAttributes(path.c_str());
if ((Attributes != INVALID_FILE_ATTRIBUTES) &&
@ -614,12 +623,12 @@ namespace Utils
{
std::string path = getGenericPath(_path);
#if defined(_WIN32)
#if defined(_WIN64)
// Check for hidden attribute.
const DWORD Attributes = GetFileAttributes(path.c_str());
if ((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_HIDDEN))
return true;
#endif // _WIN32
#endif // _WIN64
// Filenames starting with . are hidden in Linux, but
// we do this check for windows as well.

View file

@ -27,7 +27,7 @@ namespace Utils
std::string getCWDPath();
void setExePath(const std::string& _path);
std::string getExePath();
std::string getInstallPrefixPath ();
std::string getProgramDataPath ();
std::string getPreferredPath(const std::string& _path);
std::string getGenericPath(const std::string& _path);
std::string getEscapedPath(const std::string& _path);

File diff suppressed because it is too large Load diff