Make Windows version buildable on MSVC (closes #10)

This commit is contained in:
Matt Pharoah
2021-02-03 21:06:33 -05:00
parent 625b180b7c
commit a82cc1fe4a
20 changed files with 164 additions and 139 deletions

2
.gitignore vendored
View File

@@ -14,7 +14,5 @@
/win32
/win64
/System Volume Information
/parallel-launcher_setup_win32.exe
/parallel-launcher_setup_win64.exe
/doc
/data/parallel-launcher-lsjs

View File

@@ -1,6 +1,6 @@
# Maintainer: Matt Pharoah <mtpharoah@gmail.com>
pkgname=parallel-launcher
pkgver=2.0.0
pkgver=2.0.1
pkgrel=0
epoch=
pkgdesc='A simple easy-to-use launcher for the ParallelN64 and Mupen64Plus-Next emulators'
@@ -31,9 +31,9 @@ backup=()
options=()
install=
changelog=
source=('parallel-launcher-2.0.0.tar.gz')
source=('parallel-launcher-2.0.1.tar.gz')
noextract=()
md5sums=('88a9f4bbe8986a63223b7ed3133f9cdc')
md5sums=('03e6d509387fd6e816cb1785cd90f163')
validpgpkeys=()
build() {

110
README.md
View File

@@ -12,8 +12,11 @@ Parallel Launcher aims to give you the best of both worlds by providing a very s
### Building on Linux
- Install the dev dependencies. For Debian-based systems this can be done with:
`sudo apt install build-essential qt5-qmake qtdeclarative5-dev libsdl2-dev`
- Install the dev dependencies.
- Debian/Ubuntu/Mint: `sudo apt install build-essential qt5-qmake qtdeclarative5-dev libsdl2-dev`
- OpenSUSE: `sudo zypper install gcc9 gcc9-c++ libqt5-qtbase-common-devel libqt5-qtdeclarative-devel libSDL2-devel findutils`
- Fedora/CentOS: `sudo yum install gcc9 gcc9-c++ libqt5-qtbase-common-devel libqt5-qtdeclarative-devel libSDL2-devel findutils`
- Arch/Manjaro: `sudo pacman -S gcc qt5-declarative make sdl2 findutils`
- Build the makefile with `./qmake-release.sh` (or `./qmake-debug.sh` for the debug build)
- Run `make` to build
@@ -21,71 +24,38 @@ If you are editing the UI, you will also want to install either Qt Designer or Q
### Building on Windows
- Firstly, you're going to need to install and setup msys2
- Download and install from https://www.msys2.org/
- Follow the 7 steps listed on the linked website to setup the package manager
- Now, we need to install mingw-w64 and Qt
- Open MSYS2
- Install gcc, make, and qmake
- 64-bit: `pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-make mingw-w64-x86_64-qt5`
- 32-bit: `pacman -S mingw-w64-i686-gcc mingw-w64-i686-make mingw-w64-i686-qt5`
- [Optional] Install gdb:
- 64-bit: `pacman -S mingw-w64-x86_64-gdb`
- 32-bit: `pacman -S mingw-w64-i686-gdb`
- Next, we need to add the required dlls to the directory where we will build
- Open MSYS2 MinGW 64-bit (or MSYS2 MinGW 32-bit if you are building 32-bit)
- Navigate to the source directory
- Create a win64 (or win32 for 32-bit) directory: `mkdir win64`
- Copy the following dll files from `C:\msys64\mingw64\bin` (or `C:\msys64\mingw32\bin` for 32-bit builds) into your newly created win64/win32 directory:
- libbrotildec.dll
- libbrotilicommon.dll
- libbz2-1.dll
- libdouble-conversion.dll
- libfreetype-6.dll
- libgcc_s_seh-1.dll
- libglib-2.0-0.dll (or libgcc_s_dw2-1.dll for 32-bit)
- libgraphite2.dll
- libharfbuzz-0.dll
- libiconv-2.dll
- libicudt67.dll
- libicuin67.dll
- libicuuc67.dll
- libintl-8.dll
- libpcre-1.dll
- libpcre2-16-0.dll
- libpng16-16.dll
- libstdc++-6.dll
- libwinpthread-1.dll
- libzstd.dll
- zlib1.dll
- Manually install the SDL2 development library
- Download the SDL2 development libraries for Windows MinGW release from https://www.libsdl.org/download-2.0.php
- Extract the tarball and go to the `x86_64-w64-mingw32` folder (or the `i686-w64-mingw32` folder for 32-bit)
- Copy `bin/SDL2.dll` to your win64/win32 directory with the rest of the dlls you added in the previous steps
- Merge the `bin`, `include`, `lib`, and `share` folders into your `C:\msys64\mingw64` directory (or `C:\msys64\mingw32` for 32-bit)
- Now, we will build the app for the first time.
- Run `./qmake-windows.sh` (or `./qmake-windows-debug.sh` for a debug build) to create a makefile
- Run `mingw32-make` to build. Compilation takes much longer on Windows than on Linux, so be patient
- [Optional] Build the theme plugins
- Clone the repository hosted at https://github.com/qt/qtstyleplugins into some temporary directory
- Build the plugins using qmake and make
- Add a `style` folder inside your `win32`/`win64` folder and place the plugin dlls inside
- Finally, we need to add the last few things Qt requires on Windows
- Move the built application into the win64/win32 folder: `mv parallel-launcher.exe win64`
- Enter the win64/win32 directory: `cd win64`
- Run `windeployqt parallel-launcher.exe`
- Now that the application has been built, we can build the installer
- Download and install Inno Setup (https://jrsoftware.org/isinfo.php)
- Open `win64-installer.iss` (or `win32-installer.iss` for a 32-bit build) in the source directory
- Click the green play button to build the installer
If you are editing the UI, you will also want to install either Qt Designer or Qt Creator
Note that you will only get output on the command line when running Parallel
Launcher through MSYS2 MinGW. You will not see any command line output if you
use Command Prompt or Powershell to open it. Additionally, sometimes even with
MSYS2 you still don't get output when you should.
You can debug on Windows by using gdb in MSYS2, though it is EXTREMELY slow on
Windows. If the error you are debugging is not specific to Windows, I strongly
recommend debugging on Linux for your own sanity.
#### Setup
- Install Visual Studio 2019
- Install "Desktop development with C++" from the Visual Studio installer
- Download Qt from http://download.qt.io/official_releases/qt/5.12/5.12.10/qt-opensource-windows-x86-5.12.10.exe
- Run the installer and select the MSVC 2017 component to install as well as the defaults
- In Qt Creator, go to Tools -> Options, then set your cmake location if it was not set automatically
- Create a `win64` folder (or `win32` for building 32-bit) in your checkout directory and put an `include` and `lib` directory inside it.
- Copy the library file for OneCore to your `lib` directory (should be found somewhere like `C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64`)
- Download the SDL2 development libraries for Visual C++ from https://www.libsdl.org/download-2.0.php
- Copy the `SDL2.dll` file into your `release` and `debug` folders in your checkout directory
- Copy the `SDL2.lib` file into your `win64/lib` (or `win32/lib`) folder
- Make an `SDL2` directory in your `win64/lib` or `win32/lib` directory, and copy all of the SDL header files into it
- Open the `app.pro` file in Qt Creator
- Set the build directory to your checkout directory and set your build configuration to release (or debug)
- Build the project for the first time in Qt Creator
- Run `windeployqt release/parallel-launcher.exe`
- Copy the C++ runtimes DLLs from your system to the `release` directory (they will be located in a directory path something like `C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Redist\MSVC\14.28.29325\onecore\x64`)
- Optionally, you can also build the style plugins to make more themes available (see below)
#### Building
- If you made changes to the ui files or changed build configurations, run qmake again
- Build the project from Qt Creator.
#### Building the Styles/Themes (Optional)
- Clone the repository hosted at https://github.com/qt/qtstyleplugins into some temporary directory
- Open the project file in Qt Creator
- Go to the Projects tab and set the build configuration to Release
- Run qmake and build from Qt Creator
- Copy the built plugin DLL files to the `style` directory in your debug/release folder for parallel-launcher
#### Building the Installer
- Download and install Inno Setup (https://jrsoftware.org/isinfo.php)
- Before running the installer, remove the `qrc_resources.cpp` file from your release folder
- Open win64-installer.iss (or win32-installer.iss for a 32-bit build) in the source directory
- Click the green play button to build the installer

55
app.pro
View File

@@ -3,7 +3,7 @@ TEMPLATE = app
TARGET = "parallel-launcher"
CONFIG += qt object_parallel_to_source
CONFIG += qt
CONFIG(debug, debug|release){
OBJECTS_DIR = "build/debug/obj"
MOC_DIR = "build/debug/moc"
@@ -19,51 +19,60 @@ CONFIG(release, debug|release){
UI_DIR = "build/release/ui"
}
QMAKE_CXXFLAGS += -std=c++17 -Werror
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += src/main.cpp src/core/*.cpp src/ui/*.cpp src/polyfill/*.cpp src/input/*.cpp
HEADERS += src/types.hpp src/core/*.hpp src/ui/*.hpp src/polyfill/*.hpp src/input/*.hpp
FORMS += src/ui/designer/*.ui
RESOURCES = data/resources.qrc
LIBS += -lstdc++fs -lSDL2 -lSDL2main -lpthread
win32 {
win32:contains(QMAKE_HOST.arch, x86_64) {
WIN_DIR = win64
} else {
WIN_DIR = win32
}
CONFIG(debug, debug|release){
WIN_BUILD_DIR = debug
}
CONFIG(release, debug|release){
WIN_BUILD_DIR = release
}
CONFIG += embed_manifest_exe
VERSION = 2.0.0
QMAKE_CXXFLAGS += /std:c++17 /WX /utf-8 /Zc:preprocessor /Wv:18
LIBS += -L$${WIN_DIR}/lib -lonecore -lSDL2
INCLUDEPATH += $${WIN_DIR}/include
VERSION = 2.0.1
RC_ICONS = "data\\appicon.ico"
RC_LANG = "EN-CA"
QMAKE_TARGET_COMPANY = "Matt Pharoah"
QMAKE_TARGET_COPYRIGHT = "GNU GENERAL PUBLIC LICENSE Version 2"
QMAKE_TARGET_PRODUCT = "Parallel Launcher"
INCLUDEPATH += .
INCLUDEPATH += $MOC_DIR
LIBS += -lole32 -luuid
lsjsTarget.target = parallel-launcher-lsjs.exe
lsjsTarget.depends = FORCE
lsjsTarget.commands = cl /WX /Wv:18 /O2 /I $${WIN_DIR}\\include lsjs.c /link $${WIN_DIR}\\lib\\SDL2.lib /out:$${WIN_BUILD_DIR}\\parallel-launcher-lsjs.exe
lsjsStub.target = data/parallel-launcher-lsjs
lsjsStub.depends = FORCE
lsjsStub.commands = type nul > data\\parallel-launcher-lsjs
PRE_TARGETDEPS += parallel-launcher-lsjs.exe data/parallel-launcher-lsjs
QMAKE_EXTRA_TARGETS += lsjsTarget lsjsStub
}
!win32 {
CONFIG += object_parallel_to_source
QMAKE_CXXFLAGS += -std=c++17 -Werror
QMAKE_LFLAGS += -no-pie -fno-pie
LIBS += -lstdc++fs -lSDL2 -lpthread
lsjsTarget.target = data/parallel-launcher-lsjs
lsjsTarget.depends = FORCE
lsjsTarget.commands = gcc -std=c99 -O3 lsjs.c -o data/parallel-launcher-lsjs -lSDL2
PRE_TARGETDEPS += data/parallel-launcher-lsjs
QMAKE_EXTRA_TARGETS += lsjsTarget
program.path = "/usr/local/bin"
program.files = "parallel-launcher"
icon.path = "/usr/local/share/parallel-launcher"
icon.files = "data/appicon.svg"
launcher.path = "/usr/local/share/applications"
launcher.files = "local/parallel-launcher.desktop"
launcher_post.path = "."
launcher_post.files = ""
launcher_post.extra = "update-desktop-database /usr/local/share/applications"
INSTALLS += program icon launcher launcher_post
}

View File

@@ -2,7 +2,7 @@
[Desktop Entry]
Type=Application
Version=2.0.0
Version=2.0.1
Name=Parallel Launcher
Exec=/usr/local/bin/parallel-launcher "%f"
Icon=/usr/local/share/parallel-launcher/appicon.svg

16
lsjs.c
View File

@@ -29,7 +29,23 @@ int main( int argc, char *argv[] ) {
const int numConnected = SDL_NumJoysticks();
for( int i = 0; i < numConnected; i++ ) {
writeUuid( SDL_JoystickGetDeviceGUID( i ).data, uuid );
#ifdef _WIN32
/* Fun fact: Windows Defender is so hilariously inept at virus detection
* that calling printf, a function provided by their own signed library,
* makes it inexplicably think the program is a trojan when compiled as
* a 32-bit application (64-bit is fine).
*
* This is not a meme.
* Windows Defender is actually that stupid.
*
* Printing it one character at a time, however, doesn't trigger a false
* positive, so I do that instead.
*/
for( int j = 0; j < 36; j++ ) putc( uuid[j], stdout );
putc( '\n', stdout );
#else
printf( "%s\n", uuid );
#endif
}
SDL_Quit();

View File

@@ -2,7 +2,7 @@
[Desktop Entry]
Type=Application
Version=2.0.0
Version=2.0.1
Name=Parallel Launcher
Exec=/usr/bin/parallel-launcher "%f"
Icon=/usr/share/parallel-launcher/appicon.svg

View File

@@ -1,11 +1,11 @@
Name: parallel-launcher
Version: 2.0.0
Version: 2.0.1
Release: 0%{?dist}
Summary: A simple easy-to-use launcher for the ParallelN64 emulator
License: GPLv2
Group: games
URL: https://blueprint64.ca/parallel-launcher
Source: https://gitlab.com/mpharoah/parallel-launcher/-/archive/v2.0-0/parallel-launcher-v2.0-0.tar.bz2
Source: https://gitlab.com/mpharoah/parallel-launcher/-/archive/v2.0-1/parallel-launcher-v2.0-1.tar.bz2
Vendor: Matt Pharoah
BuildRequires: gcc9

View File

@@ -1,6 +0,0 @@
#!/bin/sh
rm data/parallel-launcher-lsjs 2> /dev/null
touch data/parallel-launcher-lsjs
rm qrc_resources.cpp 2> /dev/null
qmake app.pro -spec win32-g++ CONFIG+=debug CONFIG+=qml_debug
sed -i 's/\.\.\///g' Makefile

View File

@@ -1,6 +0,0 @@
#!/bin/sh
rm data/parallel-launcher-lsjs 2> /dev/null
touch data/parallel-launcher-lsjs
rm qrc_resources.cpp 2> /dev/null
qmake app.pro -spec win32-g++ CONFIG+=release CONFIG+=qml_release
sed -i 's/\.\.\///g' Makefile

View File

@@ -17,6 +17,10 @@ template<class T> class FileCache {
void commit() const {
try {
std::ofstream file( m_filePath.string(), std::ios_base::out | std::ios_base::trunc );
#ifdef _WIN32
char buffer[8096];
file.rdbuf()->pubsetbuf( buffer, 8096 );
#endif
JsonWriter writer( &file, true );
m_setter( writer, m_value );
file << std::flush;
@@ -45,6 +49,10 @@ template<class T> class FileCache {
m_value = defaultValue;
} else try {
std::ifstream file( filePath.string() );
#ifdef _WIN32
char buffer[8096];
file.rdbuf()->pubsetbuf( buffer, 8096 );
#endif
m_value = getter( Json::parse( file ) );
} catch( const std::exception &exception ) {
string msg = "Failed to load data from '"s + m_filePath.string() + "'.\n";

View File

@@ -365,7 +365,7 @@ void JsonWriter::writeRawValue( const string &value ) {
state.empty = false;
if( m_pretty ) {
m_out->put( '\n' );
indent( m_out, m_state.size() - 1 );
indent( m_out, (int)m_state.size() - 1 );
}
m_out->write( value.c_str(), value.size() );
break;
@@ -387,7 +387,7 @@ void JsonWriter::writePropertyName( const string &name ) {
objectState.empty = false;
if( m_pretty ) {
m_out->put( '\n' );
indent( m_out, m_state.size() - 1 );
indent( m_out, (int)m_state.size() - 1 );
}
const string &propertyName = quoteAndEscape( name );
@@ -417,7 +417,7 @@ void JsonWriter::writeObjectEnd() {
m_state.pop();
m_out->put( '\n' );
indent( m_out, m_state.size() - 1 );
indent( m_out, (int)m_state.size() - 1 );
m_out->put( '}' );
}
@@ -440,6 +440,6 @@ void JsonWriter::writeArrayEnd() {
m_state.pop();
m_out->put( '\n' );
indent( m_out, m_state.size() - 1 );
indent( m_out, (int)m_state.size() - 1 );
m_out->put( ']' );
}

View File

@@ -299,7 +299,7 @@ static inline HashMap<Uuid,std::queue<int>> getDevicePorts( [[maybe_unused]] con
numMapped = (int)fallbackPortOrder.size();
for( size_t i = 0; i < fallbackPortOrder.size(); i++ ) {
devicePorts[fallbackPortOrder[i].info.uuid].push( i );
devicePorts[fallbackPortOrder[i].info.uuid].push( (int)i );
}
}

View File

@@ -211,16 +211,29 @@ SaveFile SaveFile::read( std::istream &saveFile ) {
return saveData;
}
static inline int bitcount( uint flags ) {
#ifdef _WIN32
int count = 0;
while( flags != 0 ) {
flags &= flags - 1;
count++;
}
return count;
#else
return __builtin_popcount( flags );
#endif
}
int SaveSlot::countStars() const {
int starCount = 0;
uint *wStarFlags = (uint*)starFlags;
for( int i = 0; i < 6; i++ ) {
starCount += __builtin_popcount( wStarFlags[i] & 0x7F7F7F7Fu );
starCount += bitcount( wStarFlags[i] & 0x7F7F7F7Fu );
}
starCount += __builtin_popcount( (uint)starFlags[24] & 0x7Fu );
starCount += __builtin_popcount( (uint)castleStars & 0x7Fu );
starCount += bitcount( (uint)starFlags[24] & 0x7Fu );
starCount += bitcount( (uint)castleStars & 0x7Fu );
return starCount;
}

View File

@@ -158,10 +158,10 @@ static void recursiveSearchWorker(
taskQueueLock.lock();
}
taskQueueLock.unlock();
SearchTaskInfo task = taskQueue.front();
taskQueue.pop();
taskQueueLock.unlock();
activeWorkers++;
lock.unlock();

View File

@@ -119,13 +119,10 @@ AsyncProcess::AsyncProcess( const string &process, const std::vector<string> &ar
cmd += quoteAndEscape( arg );
}
char lpCommandLine[cmd.length() + 1];
cmd.copy( lpCommandLine, cmd.length() );
lpCommandLine[cmd.length()] = '\0';
string lpCommandLine = cmd;
if( !CreateProcessA(
nullptr,
lpCommandLine,
lpCommandLine.data(),
nullptr,
nullptr,
false,
@@ -194,13 +191,10 @@ static bool tryGetOutputHelper(
}
si.dwFlags |= STARTF_USESTDHANDLES;
char lpCommandLine[command.length() + 1];
command.copy( lpCommandLine, command.length() );
lpCommandLine[command.length()] = '\0';
string lpCommandLine = command;
if( !CreateProcessA(
nullptr,
lpCommandLine,
lpCommandLine.data(),
nullptr,
nullptr,
true,

View File

@@ -25,7 +25,7 @@
</font>
</property>
<property name="text">
<string>Parallel Launcher v2.0.0</string>
<string>Parallel Launcher v2.0.1</string>
</property>
</widget>
</item>
@@ -271,7 +271,7 @@ min-width: 10000;
}</string>
</property>
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<property name="tabBarAutoHide">
<bool>false</bool>
@@ -394,7 +394,7 @@ min-width: 10000;
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<widget class="QGroupBox" name="mupenPluginGroup">
<property name="title">
<string>Graphics Plugin</string>
</property>

View File

@@ -122,6 +122,7 @@ void MainWindow::romSelectionChanged() {
m_ui->overclockCpuCheckbox->setEnabled( romSelected );
m_ui->overclockViCheckbox->setEnabled( romSelected );
m_ui->pluginGroup->setEnabled( romSelected );
m_ui->mupenPluginGroup->setEnabled( romSelected );
m_ui->playSingleplayerButton->setEnabled( romSelected );
m_ui->playMultiplayerButton->setEnabled( romSelected );

View File

@@ -211,6 +211,7 @@ QVariant RomListModel::headerData( int section, Qt::Orientation orientation, int
}
QModelIndex RomListModel::index( int row, int column, const QModelIndex &parent ) const {
if( m_groups.empty() ) return QModelIndex();
if( parent == QModelIndex() ) {
if( column != 0 ) return QModelIndex();
return createIndex( row, column, (void*)&m_groups.at( row ).self );
@@ -221,6 +222,7 @@ QModelIndex RomListModel::index( int row, int column, const QModelIndex &parent
}
QModelIndex RomListModel::parent( const QModelIndex &index ) const {
if( m_groups.empty() ) return QModelIndex();
const Index *i = static_cast<const Index*>( index.internalPointer() );
if( i == nullptr || i->row < 0 ) return QModelIndex();
const Index *parentIndex = &m_groups.at( i->group ).self;

View File

@@ -1,9 +1,35 @@
#define MyAppName "Parallel Launcher"
#define MyAppVersion "2.0.0"
#define MyAppVersion "2.0.1"
#define MyAppPublisher "Matt Pharoah"
#define MyAppURL "https://blueprint64.ca/parallel-launcher"
#define MyAppExeName "parallel-launcher.exe"
[InstallDelete]
Type: files; Name: "{app}\libbrotlidec.dll"
Type: files; Name: "{app}\libbrotlicommon.dll"
Type: files; Name: "{app}\libbrotilicommon.dll"
Type: files; Name: "{app}\libbz2-1.dll"
Type: files; Name: "{app}\libdouble-conversion.dll"
Type: files; Name: "{app}\libfreetype-6.dll"
Type: files; Name: "{app}\libgcc_s_seh-1.dll"
Type: files; Name: "{app}\libglib-2.0-0.dll"
Type: files; Name: "{app}\libgcc_s_dw2-1.dll"
Type: files; Name: "{app}\libgraphite2.dll"
Type: files; Name: "{app}\libharfbuzz-0.dll"
Type: files; Name: "{app}\libiconv-2.dll"
Type: files; Name: "{app}\libicudt67.dll"
Type: files; Name: "{app}\libicuin67.dll"
Type: files; Name: "{app}\libicuuc67.dll"
Type: files; Name: "{app}\libintl-8.dll"
Type: files; Name: "{app}\libpcre-1.dll"
Type: files; Name: "{app}\libpcre2-16-0.dll"
Type: files; Name: "{app}\libpng16-16.dll"
Type: files; Name: "{app}\libstdc++-6.dll"
Type: files; Name: "{app}\libwinpthread-1.dll"
Type: files; Name: "{app}\libzstd.dll"
Type: files; Name: "{app}\zlib1.dll"
Type: files; Name: "{app}\qrc_resources.cpp"
[Setup]
AppId=2C94867F-0911-4237-B447-CE6E30AD7F55
AppName={#MyAppName}
@@ -31,7 +57,7 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkedonce
[Files]
Source: "{#SourcePath}\{#TargetArch}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
Source: "{#SourcePath}\release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"