├── icon ├── openrct2.ico ├── openrct2launcher.rc └── openrct2.icns ├── resources ├── splash.png └── resources.qrc ├── src ├── archives │ ├── tarextractor.h │ ├── zipextractor.h │ ├── gzipreadfilter.h │ ├── gzipreadfilter.cpp │ ├── tarextractor.cpp │ └── zipextractor.cpp ├── main.cpp ├── mainwindow.h ├── configuration.h ├── updater.h ├── configuration_data.h ├── platform.h ├── mainwindow.cpp ├── mainwindow.ui ├── configuration.cpp ├── updater.cpp └── configuration.ui ├── LICENSE ├── README.md ├── appveyor.yml ├── qt5-mac.sh ├── .gitignore ├── installer.iss ├── .travis.yml └── CMakeLists.txt /icon/openrct2.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRFLEW/OpenRCT2Launcher/HEAD/icon/openrct2.ico -------------------------------------------------------------------------------- /icon/openrct2launcher.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "openrct2.ico" 2 | -------------------------------------------------------------------------------- /icon/openrct2.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRFLEW/OpenRCT2Launcher/HEAD/icon/openrct2.icns -------------------------------------------------------------------------------- /resources/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRFLEW/OpenRCT2Launcher/HEAD/resources/splash.png -------------------------------------------------------------------------------- /resources/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | splash.png 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/archives/tarextractor.h: -------------------------------------------------------------------------------- 1 | #ifndef TAREXTRACTOR_H 2 | #define TAREXTRACTOR_H 3 | 4 | #include 5 | #include 6 | 7 | bool extractTar(QIODevice *in, QDir extractLoc); 8 | 9 | #endif // TAREXTRACTOR_H 10 | -------------------------------------------------------------------------------- /src/archives/zipextractor.h: -------------------------------------------------------------------------------- 1 | #ifndef ZIPEXTRACTOR_H 2 | #define ZIPEXTRACTOR_H 3 | 4 | #include 5 | #include 6 | 7 | bool extractZip(QIODevice *in, QDir extractLoc); 8 | 9 | #endif // ZIPEXTRACTOR_H 10 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | 8 | QCoreApplication::setOrganizationName(QStringLiteral("OpenRCT2")); 9 | QCoreApplication::setOrganizationDomain(QStringLiteral("OpenRCT2.org")); 10 | QCoreApplication::setApplicationName(QStringLiteral("OpenRCT2Launcher")); 11 | 12 | MainWindow w; 13 | w.show(); 14 | 15 | return a.exec(); 16 | } 17 | -------------------------------------------------------------------------------- /src/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include "updater.h" 6 | 7 | namespace Ui { 8 | class MainWindow; 9 | } 10 | 11 | class MainWindow : public QMainWindow 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit MainWindow(QWidget *parent = 0); 17 | ~MainWindow(); 18 | 19 | public slots: 20 | void launch(); 21 | void on_optionsButton_clicked(); 22 | 23 | private: 24 | Ui::MainWindow *ui; 25 | Updater updater; 26 | }; 27 | 28 | #endif // MAINWINDOW_H 29 | -------------------------------------------------------------------------------- /src/archives/gzipreadfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef GZIPFILTER_H 2 | #define GZIPFILTER_H 3 | 4 | #include 5 | #include 6 | 7 | class GZipReadFilter : public QIODevice 8 | { 9 | public: 10 | GZipReadFilter(QIODevice *in); 11 | GZipReadFilter(QIODevice *in, QObject *parent); 12 | 13 | virtual bool open(OpenMode mode); 14 | bool isSequential() const; 15 | bool atEnd() const; 16 | 17 | protected: 18 | virtual qint64 readData(char *data, qint64 maxlen); 19 | virtual qint64 writeData(const char *data, qint64 len); 20 | 21 | private: 22 | QIODevice *device; 23 | z_stream strm; 24 | bool done = false; 25 | QByteArray buffer; 26 | }; 27 | 28 | #endif // GZIPFILTER_H 29 | -------------------------------------------------------------------------------- /src/configuration.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIGURATION_H 2 | #define CONFIGURATION_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace Ui { 9 | class Configuration; 10 | } 11 | 12 | class Configuration : public QDialog 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | explicit Configuration(QSettings *mainSettings, QString file, QWidget *parent = 0); 18 | ~Configuration(); 19 | 20 | signals: 21 | void redownload(); 22 | 23 | public slots: 24 | void on_locateRCT2_clicked(); 25 | 26 | private: 27 | Ui::Configuration *ui; 28 | void setComboBoxData(); 29 | QSettings config; 30 | QSettings *mainSettings; 31 | QMap langEquiv; 32 | }; 33 | 34 | #endif // CONFIGURATION_H 35 | -------------------------------------------------------------------------------- /src/updater.h: -------------------------------------------------------------------------------- 1 | #ifndef UPDATER_H 2 | #define UPDATER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class Updater : public QObject 12 | { 13 | Q_OBJECT 14 | public: 15 | explicit Updater(QObject *parent = 0); 16 | QSettings settings; 17 | 18 | signals: 19 | void installed(); 20 | void error(QString error); 21 | void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); 22 | 23 | public slots: 24 | void download(); 25 | 26 | private slots: 27 | void receivedUpdate(); 28 | void receivedAPI(); 29 | void receivedBundle(); 30 | 31 | private: 32 | bool extract(QByteArray &data, QDir &bin); 33 | void queryDownloads(QString flavor); 34 | 35 | QNetworkAccessManager net; 36 | int size; 37 | QByteArray hash; 38 | QByteArray githash; 39 | QString version; 40 | 41 | QNetworkReply *update = nullptr; 42 | QNetworkReply *api = nullptr; 43 | QNetworkReply *bundle = nullptr; 44 | }; 45 | 46 | #endif // UPDATER_H 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 LRFLEW 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenRCT2 Launcher 2 | An Open Source Launcher & Updater for OpenRCT2 3 | 4 | This is a new launcher for [OpenRCT2](https://github.com/OpenRCT2/OpenRCT2). It's written in C++ using the Qt Framework and supports Windows, Mac OS X and Linux. 5 | 6 | ## Dependencies 7 | 8 | To build this project, you need [Qt 5](http://www.qt.io/download-open-source/). Note that Qt 4 will not work. You also need Zlib to compile and run this, but since Qt 5 is already dependent on it, the compiler will attempt to use the version that comes with Qt and fallback to a system-installed version if need be. Lastly, this project needs OpenSSL installed to communicate properly with the server. 9 | 10 | For building on Windows, you should just grab the [Official Qt 5 Installer](http://www.qt.io/download-open-source/). It will include Zlib, but not OpenSSL. There are no official pre-build binaries of OpenSSL, but [there are some unofficial ones you can use](https://wiki.openssl.org/index.php/Binaries). 11 | 12 | For OS X, it is recommended that you install Qt5 with brew using `brew install qt5`. The other dependencies are included with OS X by default. It's recommended that you download and use [Qt Creator](http://www.qt.io/download-open-source/#section-6) as your IDE. 13 | 14 | For Linux, search your distro's package manager for Qt5 (and OpenSSL if it's not already installed). If you're using Ubuntu, follow [these instructions](https://wiki.qt.io/Install_Qt_5_on_Ubuntu) for getting Qt5 installed. 15 | -------------------------------------------------------------------------------- /src/configuration_data.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIGURATION_DATA_H 2 | #define CONFIGURATION_DATA_H 3 | 4 | #include "configuration.h" 5 | #include "ui_configuration.h" 6 | 7 | // This can't be done from the UI file, so I'm doing it here 8 | void Configuration::setComboBoxData() { 9 | ui->languages->setItemData(0, QStringLiteral("en-GB")); 10 | ui->languages->setItemData(1, QStringLiteral("en-US")); 11 | ui->languages->setItemData(2, QStringLiteral("de-DE")); 12 | ui->languages->setItemData(3, QStringLiteral("nl-NL")); 13 | ui->languages->setItemData(4, QStringLiteral("fr-FR")); 14 | ui->languages->setItemData(5, QStringLiteral("hu-HU")); 15 | ui->languages->setItemData(6, QStringLiteral("pl-PL")); 16 | ui->languages->setItemData(7, QStringLiteral("es-ES")); 17 | ui->languages->setItemData(8, QStringLiteral("sv-SE")); 18 | ui->languages->setItemData(9, QStringLiteral("it-IT")); 19 | ui->languages->setItemData(10, QStringLiteral("pt-BR")); 20 | ui->languages->setItemData(11, QStringLiteral("zh-Hant")); 21 | ui->languages->setItemData(12, QStringLiteral("zh-Hans")); 22 | ui->languages->setItemData(13, QStringLiteral("fi-FI")); 23 | ui->languages->setItemData(14, QStringLiteral("ko")); 24 | ui->languages->setItemData(15, QStringLiteral("ru-RU")); 25 | ui->languages->setItemData(16, QStringLiteral("ca-CA")); 26 | ui->languages->setItemData(17, QStringLiteral("jp-JP")); 27 | 28 | ui->screenshotFormat->setItemData(0, QStringLiteral("BMP")); 29 | ui->screenshotFormat->setItemData(1, QStringLiteral("PNG")); 30 | 31 | langEquiv.insert("en-CA", "en-US"); 32 | langEquiv.insert("zh-CN", "zh-Hant"); 33 | langEquiv.insert("zh-TW", "zh-Hans"); 34 | } 35 | 36 | #endif // CONFIGURATION_DATA_H 37 | -------------------------------------------------------------------------------- /src/archives/gzipreadfilter.cpp: -------------------------------------------------------------------------------- 1 | #include "gzipreadfilter.h" 2 | 3 | #define FILTER_BUFFER_SIZE 16384 4 | 5 | GZipReadFilter::GZipReadFilter(QIODevice *in) : 6 | device(in) 7 | { 8 | buffer.resize(FILTER_BUFFER_SIZE); 9 | } 10 | 11 | GZipReadFilter::GZipReadFilter(QIODevice *in, QObject *parent) : 12 | QIODevice(parent), device(in) 13 | { } 14 | 15 | bool GZipReadFilter::open(OpenMode mode) { 16 | if (mode & OpenModeFlag::WriteOnly) 17 | return false; 18 | if (!device->open(mode)) 19 | return false; 20 | 21 | strm.zalloc = Z_NULL; 22 | strm.zfree = Z_NULL; 23 | strm.opaque = Z_NULL; 24 | strm.next_in = Z_NULL; 25 | strm.avail_in = 0; 26 | int ret = inflateInit2(&strm, 16 + MAX_WBITS); 27 | if (ret != Z_OK) 28 | return false; 29 | 30 | return QIODevice::open(mode); 31 | } 32 | 33 | bool GZipReadFilter::isSequential() const { 34 | return true; 35 | } 36 | 37 | bool GZipReadFilter::atEnd() const { 38 | if (done) return QIODevice::atEnd(); 39 | return false; 40 | } 41 | 42 | qint64 GZipReadFilter::writeData(const char *data, qint64 len) { 43 | // Do Nothing, as it won't happen 44 | Q_UNUSED(data); 45 | Q_UNUSED(len); 46 | return 0; 47 | } 48 | 49 | qint64 GZipReadFilter::readData(char *data, qint64 maxlen) { 50 | int ret = Z_OK; 51 | strm.next_out = (Bytef *) data; 52 | strm.avail_out = maxlen; 53 | while (ret == Z_OK && strm.avail_out > 0) { 54 | if (strm.avail_in == 0) { 55 | strm.avail_in = device->read(buffer.data(), FILTER_BUFFER_SIZE); 56 | strm.next_in = (Bytef *) buffer.data(); 57 | } 58 | ret = inflate(&strm, Z_NO_FLUSH); 59 | } 60 | if (ret == Z_STREAM_END) done = true; 61 | return maxlen - strm.avail_out; 62 | } 63 | -------------------------------------------------------------------------------- /src/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_H 2 | #define PLATFORM_H 3 | 4 | #include 5 | #include 6 | 7 | #ifndef Q_PROCESSOR_X86 8 | #error Needs x86 to run OpenRCT2 9 | #endif 10 | 11 | #if defined(Q_OS_WIN) 12 | 13 | #ifdef Q_PROCESSOR_X86_64 14 | #define OPENRCT2_FLAVOR "6" 15 | #else 16 | #define OPENRCT2_FLAVOR "1" 17 | #endif 18 | 19 | // Qt uses forward slashes for all OS's and converts to backslashes internally 20 | #define OPENRCT2_BASE "OpenRCT2/" 21 | #define OPENRCT2_EXEC_NAME "openrct2.exe" 22 | 23 | #elif defined(Q_OS_OSX) 24 | 25 | // Only one flavor for macOS 26 | #define OPENRCT2_FLAVOR "3" 27 | 28 | #define OPENRCT2_BASE "Library/Application Support/OpenRCT2/" 29 | #define OPENRCT2_EXEC_NAME "OpenRCT2.app/Contents/MacOS/OpenRCT2" 30 | 31 | #elif defined(Q_OS_LINUX) 32 | 33 | #ifdef Q_PROCESSOR_X86_64 34 | #define OPENRCT2_FLAVOR "9" 35 | #else 36 | #define OPENRCT2_FLAVOR "4" 37 | #endif 38 | 39 | #define OPENRCT2_BASE ".config/OpenRCT2/" 40 | #define OPENRCT2_EXEC_NAME "openrct2" 41 | 42 | #else 43 | 44 | #error Unsupported Platform 45 | 46 | #endif 47 | 48 | #if defined(Q_OS_OSX) // macOS doesn't differenciate bit-ness 49 | #define OPENRCT2_BIT_SUFFIX 50 | #elif defined(Q_PROCESSOR_X86_64) 51 | #define OPENRCT2_BIT_SUFFIX " x64" 52 | #else 53 | #define OPENRCT2_BIT_SUFFIX " x86" 54 | #endif 55 | 56 | #define OPENRCT2_BIN OPENRCT2_BASE "bin/" 57 | #define OPENRCT2_EXEC_LOCATION OPENRCT2_BIN OPENRCT2_EXEC_NAME 58 | 59 | #ifdef Q_OS_WIN 60 | #include 61 | #define OPENRCT2_HOMEPATH (QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)) 62 | #define OPENRCT2_HOMEDIR (QDir(OPENRCT2_HOMEPATH)) 63 | #else 64 | #define OPENRCT2_HOMEPATH (QDir::homePath()) 65 | #define OPENRCT2_HOMEDIR (QDir::home()) 66 | #endif 67 | 68 | #endif // PLATFORM_H 69 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.0.7#{build} 2 | image: Visual Studio 2017 3 | environment: 4 | matrix: 5 | - VC_ARCH: x86 6 | QT_PATH: C:\Qt\5.12.4\msvc2017 7 | SSL_BUILD: OpenSSL_1_0_2s/OpenSSL-VC-WIN32.zip 8 | INNO_ARGS: '' 9 | ARTIFACT_SUFFIX: _x86 10 | - VC_ARCH: amd64 11 | QT_PATH: C:\Qt\5.12.4\msvc2017_64 12 | SSL_BUILD: OpenSSL_1_0_2s/OpenSSL-VC-WIN64A.zip 13 | INNO_ARGS: /DBUILD64 14 | install: 15 | - ps: >- 16 | choco install innosetup 17 | 18 | [Environment]::CurrentDirectory = $PWD 19 | 20 | $opensslDownloadUrl = "https://github.com/LRFLEW/openssl/releases/download/$Env:SSL_BUILD" 21 | 22 | $wc = New-Object net.webclient 23 | 24 | $wc.Downloadfile($opensslDownloadUrl, "OpenSSL-DLLs.zip") 25 | 26 | 7z e "OpenSSL-DLLs.zip" 27 | build_script: 28 | - cmd: >- 29 | "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" %VC_ARCH% 30 | 31 | SET PATH=%PATH%;%QT_PATH%\bin;"C:\Program Files (x86)\Inno Setup 5" 32 | 33 | qmake OpenRCT2Launcher.pro 34 | 35 | nmake 36 | 37 | mkdir build-Windows 38 | 39 | copy release\OpenRCT2.exe build-Windows\OpenRCT2.exe 40 | 41 | windeployqt build-Windows\OpenRCT2.exe 42 | 43 | copy libeay32.dll build-Windows\ 44 | 45 | copy ssleay32.dll build-Windows\ 46 | 47 | pushd build-Windows & 7z a -tzip ..\OpenRCT2Launcher-win%ARTIFACT_SUFFIX%.zip * & popd 48 | 49 | iscc %INNO_ARGS% installer.iss 50 | artifacts: 51 | - path: OpenRCT2Launcher-win*.zip 52 | name: zip-archive 53 | - path: OpenRCT2Launcher-win*.exe 54 | name: exe-installer 55 | deploy: 56 | - provider: GitHub 57 | auth_token: 58 | secure: US2B1AJBegrxUhjKUfbocWUqavl636DxnaUnl8a7/waGexpd5Dp/mN6JBZIS+6Ee 59 | artifact: zip-archive, exe-installer 60 | on: 61 | appveyor_repo_tag: true -------------------------------------------------------------------------------- /qt5-mac.sh: -------------------------------------------------------------------------------- 1 | 2 | QT5_MAJOR=12 3 | QT5_MINOR=4 4 | QT5_PATCH=0-201906140148 5 | 6 | # Qt likes to change how their filenames work 7 | QT5_SHORT=5${QT5_MAJOR}${QT5_MINOR} 8 | QT5_DIR=5.${QT5_MAJOR}.${QT5_MINOR} 9 | QT5_OS_VER=MacOS-MacOS_10_13 10 | QT5_DIR_PREFIX=qt.qt5 11 | 12 | if (( $# < 2 )); then 13 | echo "Usage: [component...]" 14 | (( "$#" == 0 )) 15 | exit $?; 16 | fi 17 | 18 | for x in ${@:2}; do 19 | if [[ $x = extra-* ]]; then 20 | DOWNLOAD_URL="https://download.qt.io/online/qtsdkrepository/mac_x64/desktop/qt5_${QT5_SHORT}/${QT5_DIR_PREFIX}.${QT5_SHORT}.qt${x/#extra-/}.clang_64/${QT5_DIR}-${QT5_PATCH}qt${x/#extra-/}-${QT5_OS_VER}-Clang-${QT5_OS_VER}-X86_64.7z" 21 | else 22 | DOWNLOAD_URL="https://download.qt.io/online/qtsdkrepository/mac_x64/desktop/qt5_${QT5_SHORT}/${QT5_DIR_PREFIX}.${QT5_SHORT}.clang_64/${QT5_DIR}-${QT5_PATCH}qt${x}-${QT5_OS_VER}-Clang-${QT5_OS_VER}-X86_64.7z" 23 | fi 24 | 25 | DOWNLOAD_CHECK=$(curl -f ${DOWNLOAD_URL}.sha1 2>/dev/null) 26 | if [[ -z $DOWNLOAD_CHECK ]]; then 27 | echo "ERROR: Unknown package $x" 28 | exit 1 29 | fi 30 | 31 | curl -L -o /tmp/qt5.7z $DOWNLOAD_URL 32 | DOWNLOAD_HASH=$(shasum -a 1 /tmp/qt5.7z) 33 | if [[ $DOWNLOAD_HASH != $DOWNLOAD_HASH ]]; then 34 | echo "ERROR: Hash missmatch for $x" 1>&2 35 | exit 1 36 | fi 37 | 38 | 7z x -aoa "-o$1" /tmp/qt5.7z 1>&2 39 | rm /tmp/qt5.7z 40 | done 41 | 42 | # Minimal Qt Configuration File 43 | echo "[Paths]" > $1/${QT5_DIR}/clang_64/bin/qt.conf 44 | echo "Prefix=.." >> $1/${QT5_DIR}/clang_64/bin/qt.conf 45 | 46 | # Why does Qt default to Enterprise Licence? 47 | sed -i "" -E 's/^[[:space:]]*QT_EDITION[[:space:]]*=.*$/QT_EDITION = OpenSource/' $1/${QT5_DIR}/clang_64/mkspecs/qconfig.pri 48 | sed -i "" -E 's/^[[:space:]]*QT_LICHECK[[:space:]]*=.*$//' $1/${QT5_DIR}/clang_64/mkspecs/qconfig.pri 49 | sed -i "" -E 's/^[[:space:]]*QT_RELEASE_DATE[[:space:]]*=.*$//' $1/${QT5_DIR}/clang_64/mkspecs/qconfig.pri 50 | 51 | echo $1/${QT5_DIR}/clang_64/bin 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/qt,linux,macos,windows 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | ### macOS ### 20 | *.DS_Store 21 | .AppleDouble 22 | .LSOverride 23 | 24 | # Icon must end with two \r 25 | Icon 26 | 27 | # Thumbnails 28 | ._* 29 | 30 | # Files that might appear in the root of a volume 31 | .DocumentRevisions-V100 32 | .fseventsd 33 | .Spotlight-V100 34 | .TemporaryItems 35 | .Trashes 36 | .VolumeIcon.icns 37 | .com.apple.timemachine.donotpresent 38 | 39 | # Directories potentially created on remote AFP share 40 | .AppleDB 41 | .AppleDesktop 42 | Network Trash Folder 43 | Temporary Items 44 | .apdisk 45 | 46 | ### Qt ### 47 | # C++ objects and libs 48 | 49 | *.slo 50 | *.lo 51 | *.o 52 | *.a 53 | *.la 54 | *.lai 55 | *.so 56 | *.dll 57 | *.dylib 58 | 59 | # Qt-es 60 | 61 | /.qmake.cache 62 | /.qmake.stash 63 | *.pro.user 64 | *.pro.user.* 65 | *.qbs.user 66 | *.qbs.user.* 67 | *.moc 68 | moc_*.cpp 69 | moc_*.h 70 | qrc_*.cpp 71 | ui_*.h 72 | Makefile* 73 | *build-* 74 | 75 | # QtCreator 76 | 77 | *.autosave 78 | 79 | # QtCtreator Qml 80 | *.qmlproject.user 81 | *.qmlproject.user.* 82 | 83 | # QtCtreator CMake 84 | CMakeLists.txt.user* 85 | 86 | 87 | ### Windows ### 88 | # Windows thumbnail cache files 89 | Thumbs.db 90 | ehthumbs.db 91 | ehthumbs_vista.db 92 | 93 | # Folder config file 94 | Desktop.ini 95 | 96 | # Recycle Bin used on file shares 97 | $RECYCLE.BIN/ 98 | 99 | # Windows Installer files 100 | *.cab 101 | *.msi 102 | *.msm 103 | *.msp 104 | 105 | # Windows shortcuts 106 | *.lnk 107 | 108 | # End of https://www.gitignore.io/api/qt,linux,macos,windows 109 | -------------------------------------------------------------------------------- /src/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | #include "platform.h" 4 | #include "configuration.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | MainWindow::MainWindow(QWidget *parent) : 11 | QMainWindow(parent), 12 | ui(new Ui::MainWindow) 13 | { 14 | ui->setupUi(this); 15 | 16 | ui->progressBar->setHidden(true); 17 | connect(ui->launchButton, &QPushButton::clicked, this, &MainWindow::launch); 18 | 19 | connect(&updater, &Updater::installed, [this]{ ui->progressBar->setHidden(true); ui->launchButton->setEnabled(true); }); 20 | connect(&updater, &Updater::error, [this](QString error){ 21 | qDebug() << error; ui->errorLabel->setText(error); ui->progressBar->setHidden(true); ui->launchButton->setEnabled(true); }); 22 | connect(&updater, &Updater::downloadProgress, [this](qint64 bytesReceived, qint64 bytesTotal){ 23 | ui->progressBar->setHidden(false); ui->progressBar->setMaximum(bytesTotal); ui->progressBar->setValue(bytesReceived); }); 24 | 25 | updater.download(); 26 | } 27 | 28 | MainWindow::~MainWindow() 29 | { 30 | delete ui; 31 | } 32 | 33 | void MainWindow::on_optionsButton_clicked() { 34 | QDir dir = OPENRCT2_HOMEDIR; 35 | if (dir.cd(QStringLiteral(OPENRCT2_BASE))) { 36 | if (!dir.exists()) dir.mkpath(QStringLiteral(OPENRCT2_BASE)); 37 | } else { 38 | dir.mkpath(QStringLiteral(OPENRCT2_BASE)); 39 | dir.cd(QStringLiteral(OPENRCT2_BASE)); 40 | } 41 | 42 | Configuration config(&updater.settings, dir.filePath(QStringLiteral("config.ini"))); 43 | connect(&config, &Configuration::redownload, [this]{ ui->launchButton->setEnabled(false); 44 | ui->errorLabel->setText(QStringLiteral("")); updater.download(); ui->progressBar->setHidden(true); }); 45 | config.exec(); 46 | } 47 | 48 | void MainWindow::launch() { 49 | if (QProcess::startDetached(OPENRCT2_HOMEDIR.filePath(QStringLiteral(OPENRCT2_EXEC_LOCATION)), QStringList(), OPENRCT2_HOMEPATH)) { 50 | QApplication::quit(); 51 | } else { 52 | ui->errorLabel->setText(tr("Unable to Launch Game")); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /installer.iss: -------------------------------------------------------------------------------- 1 | ; Script generated by the Inno Setup Script Wizard. 2 | ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! 3 | 4 | #define MyAppName "OpenRCT2 Launcher" 5 | #define MyAppVersion "0.0.7" 6 | #define MyAppPublisher "OpenRCT2" 7 | #define MyAppURL "http://www.github.com/LRFLEW/OpenRCT2Launcher" 8 | #define MyAppExeName "OpenRCT2.exe" 9 | 10 | #ifdef BUILD64 11 | #define REDIST "vcredist_x64.exe" 12 | #else 13 | #define REDIST "vcredist_x86.exe" 14 | #endif 15 | 16 | [Setup] 17 | ; NOTE: The value of AppId uniquely identifies this application. 18 | ; Do not use the same AppId value in installers for other applications. 19 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) 20 | AppId={{D71D87CE-20E7-4DB6-A0D8-E6DE57051B35} 21 | AppName={#MyAppName} 22 | AppVersion={#MyAppVersion} 23 | ;AppVerName={#MyAppName} {#MyAppVersion} 24 | AppPublisher={#MyAppPublisher} 25 | AppPublisherURL={#MyAppURL} 26 | DefaultDirName={pf}\{#MyAppName} 27 | DisableProgramGroupPage=yes 28 | OutputDir=. 29 | Compression=lzma 30 | SolidCompression=yes 31 | 32 | #ifdef BUILD64 33 | OutputBaseFilename=OpenRCT2Launcher-win 34 | ArchitecturesInstallIn64BitMode=x64 35 | ArchitecturesAllowed=x64 36 | #else 37 | OutputBaseFilename=OpenRCT2Launcher-win-x86 38 | 39 | [Code] 40 | function InitializeSetup(): Boolean; 41 | begin 42 | if ProcessorArchitecture = paX64 then 43 | Result := SuppressibleMsgBox('It appears that you are installing this 32-bit version on a 64-bit machine.' #13#10 #13#10 44 | 'It''s recommended you use the 64-bit version of the launcher instead.' #13#10 #13#10 45 | 'Continue Anyways?', mbInformation, MB_YESNO or MB_DEFBUTTON2, 0) = IDYES 46 | else Result := True; 47 | end; 48 | #endif 49 | 50 | [Languages] 51 | Name: "english"; MessagesFile: "compiler:Default.isl" 52 | 53 | [Tasks] 54 | Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}" 55 | 56 | [Files] 57 | Source: "build-Windows\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs 58 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files 59 | 60 | [Icons] 61 | Name: "{commonprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" 62 | Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon 63 | 64 | [Run] 65 | Filename: "{app}\{#REDIST}"; Parameters: "/passive /norestart" 66 | Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent 67 | 68 | -------------------------------------------------------------------------------- /src/archives/tarextractor.cpp: -------------------------------------------------------------------------------- 1 | #include "tarextractor.h" 2 | 3 | #define READ_BUFFER_SIZE 16384 4 | 5 | bool extractTar(QIODevice *in, QDir extractLoc) { 6 | in->open(QIODevice::ReadOnly); 7 | QByteArray buffer, headbuf; 8 | buffer.resize(READ_BUFFER_SIZE); 9 | headbuf.resize(512); 10 | 11 | while (true) { 12 | char type; 13 | bool good; 14 | 15 | in->read(headbuf.data(), 512); 16 | 17 | if (headbuf[0] == '\0') return true; // Consider empty file name as end (close enough) 18 | 19 | if (qstrncmp(headbuf.data() + 257, "ustar", 5) != 0) return false; // Should be either POSIX or GNU tar 20 | 21 | // Terrible, terrible checksum 22 | // Will fail under odd edge-cases (fingers crossed it won't happen) 23 | quint32 checksum = headbuf.mid(148, 8).toUInt(&good, 8); 24 | if (!good) return false; 25 | headbuf.replace(148, 8, " ", 8); 26 | quint32 sum = 0; 27 | for (quint8 c : headbuf) sum += c; 28 | if (sum != checksum) return false; 29 | 30 | QString name = QString::fromUtf8(headbuf.data(), qstrnlen(headbuf.data(), 100)); 31 | if (name.startsWith("OpenRCT2/")) name.remove(0, 9); 32 | 33 | quint16 mode = headbuf.mid(100, 8).toUShort(&good, 16); // Use Base-16 for efficiency with QFile 34 | if (!good) return false; 35 | 36 | quint32 size = headbuf.mid(124, 12).toUInt(&good, 8); 37 | if (!good) return false; 38 | 39 | type = headbuf[156]; 40 | 41 | if (type == '5') { 42 | if (size != 0) return false; 43 | if (name.isEmpty()) continue; 44 | if (!name.endsWith("/")) return false; 45 | extractLoc.mkdir(name); 46 | } else if (type == '0' || type == '\0') { 47 | if (name.endsWith("/")) return false; 48 | 49 | int length = size; 50 | QFile file(extractLoc.filePath(name)); 51 | file.open(QFile::WriteOnly); 52 | 53 | while (length > 0) { 54 | int read = in->read(buffer.data(), qMin(length, (int) READ_BUFFER_SIZE)); 55 | file.write(buffer.data(), read); 56 | length -= read; 57 | } 58 | 59 | file.close(); 60 | 61 | file.setPermissions(file.permissions() | static_cast((mode & 0x0FFF) | ((mode & 0x0F00) << 4))); 62 | } else return false; // Unsupported type 63 | 64 | int padding = (0x200 - (size & 0x1FF)) & 0x1FF; 65 | if (padding > 0) { 66 | in->read(headbuf.data(), padding); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c++ 2 | deploy: 3 | provider: releases 4 | api_key: 5 | secure: > 6 | 0HIS6srOsKsti3klaZ0XyADQZrXJJ/QQ8GO8sRQA2gmPaWB+7anEzN1kpuus7F2 7 | Tqg0O3F3txh/nmdpAtJJFgIp60yDBjllwf9FYKwLofU7aP5FIWYkYIlJ2x1OFO8 8 | 7X+Dyoc5iNv/mDcmRVGbcCCHXz5RcLmUIBfwlGCE3ubc7i/4Kma+Ei0gJgoWVVt 9 | N+h1s47EEqYPVDv5rKbL39Hki013EjFSl4JEjSts9oksL2fQsBFgcTA3SXTIxGN 10 | SfVh6lZrO1JC8VLHwo1dwdZ7rbepE9xxYf/c1A2bVs6Hi3q0BTrkI4aecHoDwf3 11 | 4GARB+T6816mhOMM9HKlCpMSrLrbiit/kTVQjcL9GeHwEukjullHWpUEFD1nelD 12 | cR3Dz9vk7YSc9ERk9Yg+GhFHrLYmZ0HpLnwsAsUL1Zc6ZlzWzoBCdy6sa8YDoVo 13 | eWN559khUwVnHDh2xmGQfb8IXMYa+EsaniaU+V7bAcQdbyYNpC8J4pm4Qowhnfq 14 | sWC8Jiu8zEC0y5aDnrXbxZeUvSFslX8L0DUQAoBW1egiUOHqAKXBEyZmkHeJ3Nc 15 | O0piaePyfA80VqdnLB40LFxt+0hFkwsRc1aLqAWEr8F/e3N/54Sz0niOB7Ww6p5 16 | UAldJGGhUguL6vkjiyHKqBq45MzJJ/k/E4gNn2yG3Pvu19CxPugMs= 17 | file: $DEPLOY_FILE 18 | skip_cleanup: true 19 | on: 20 | tags: true 21 | 22 | before_install: 23 | - if [[ -z "$DOCKER_IMAGE" ]]; then 24 | brew update ; 25 | else 26 | docker pull "$DOCKER_IMAGE" && 27 | docker run -dit -v "$(pwd)":/home/launcher --name launcher "$DOCKER_IMAGE" && 28 | docker exec launcher apt-get update ; 29 | fi 30 | install: 31 | - if [[ -z "$DOCKER_IMAGE" ]]; then 32 | brew install p7zip && 33 | PATH="$(./qt5-mac.sh . base tools):$PATH" ; 34 | else 35 | docker exec launcher apt-get install --no-install-recommends -y 36 | build-essential qt5-default qt5-qmake qtbase5-dev-tools zlib1g-dev ; 37 | fi 38 | script: 39 | - if [[ -z "$DOCKER_IMAGE" ]]; then 40 | qmake $QMAKE_ARGS && make ; 41 | else 42 | docker exec -w /home/launcher launcher qmake && 43 | docker exec -w /home/launcher launcher make ; 44 | fi 45 | after_script: 46 | - if [[ -n "$DOCKER_IMAGE" ]]; then docker stop launcher; fi 47 | before_deploy: 48 | - if [[ -z "$DOCKER_IMAGE" ]]; then 49 | macdeployqt OpenRCT2.app && 50 | zip -ryq "$DEPLOY_FILE" OpenRCT2.app ; 51 | else 52 | tar -czf "$DEPLOY_FILE" OpenRCT2 ; 53 | fi 54 | 55 | matrix: 56 | include: 57 | - os: osx 58 | osx_image: xcode10.2 59 | env: 60 | - DEPLOY_FILE=OpenRCT2Launcher-macos.zip 61 | - os: linux 62 | dist: xenial 63 | services: docker 64 | env: 65 | - DEPLOY_FILE=OpenRCT2Launcher-Linux-x64.tar.gz 66 | - DOCKER_IMAGE=amd64/ubuntu:bionic 67 | - os: linux 68 | dist: xenial 69 | services: docker 70 | env: 71 | - DEPLOY_FILE=OpenRCT2Launcher-Linux-x86.tar.gz 72 | - DOCKER_IMAGE=i386/ubuntu:bionic 73 | 74 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(OpenRCT2Launcher VERSION 0.0.7 LANGUAGES CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | 7 | set(CMAKE_AUTOUIC ON) 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | 11 | set(CMAKE_CXX_STANDARD 11) 12 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 13 | 14 | find_package(Qt5 COMPONENTS Core Gui Widgets Network REQUIRED) 15 | find_package(Qt5 OPTIONAL_COMPONENTS Zlib) 16 | if (Qt5Zlib_FOUND) 17 | set(ZLIB_LINK Qt5::Zlib) 18 | else() 19 | find_package(ZLIB REQUIRED) 20 | set(ZLIB_LINK ZLIB::ZLIB) 21 | endif() 22 | 23 | set(PROJECT_SOURCES 24 | ${CMAKE_CURRENT_SOURCE_DIR}/resources/resources.qrc 25 | ${CMAKE_CURRENT_SOURCE_DIR}/src/configuration.cpp 26 | ${CMAKE_CURRENT_SOURCE_DIR}/src/configuration.h 27 | ${CMAKE_CURRENT_SOURCE_DIR}/src/configuration.ui 28 | ${CMAKE_CURRENT_SOURCE_DIR}/src/configuration_data.h 29 | ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp 30 | ${CMAKE_CURRENT_SOURCE_DIR}/src/mainwindow.cpp 31 | ${CMAKE_CURRENT_SOURCE_DIR}/src/mainwindow.h 32 | ${CMAKE_CURRENT_SOURCE_DIR}/src/mainwindow.ui 33 | ${CMAKE_CURRENT_SOURCE_DIR}/src/platform.h 34 | ${CMAKE_CURRENT_SOURCE_DIR}/src/updater.cpp 35 | ${CMAKE_CURRENT_SOURCE_DIR}/src/updater.h 36 | ) 37 | 38 | if (UNIX AND NOT APPLE) 39 | list(APPEND PROJECT_SOURCES 40 | ${CMAKE_CURRENT_SOURCE_DIR}/src/archives/gzipreadfilter.cpp 41 | ${CMAKE_CURRENT_SOURCE_DIR}/src/archives/gzipreadfilter.h 42 | ${CMAKE_CURRENT_SOURCE_DIR}/src/archives/tarextractor.cpp 43 | ${CMAKE_CURRENT_SOURCE_DIR}/src/archives/tarextractor.h 44 | ) 45 | else() 46 | list(APPEND PROJECT_SOURCES 47 | ${CMAKE_CURRENT_SOURCE_DIR}/src/archives/zipextractor.cpp 48 | ${CMAKE_CURRENT_SOURCE_DIR}/src/archives/zipextractor.h 49 | ) 50 | endif() 51 | 52 | if (WIN32) 53 | list(APPEND PROJECT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/icon/openrct2launcher.rc) 54 | elseif(APPLE) 55 | set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) 56 | set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) 57 | set(MACOSX_BUNDLE_COPYRIGHT "© LRFLEW - Released Under the MIT License") 58 | set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.lrflew.OpenRCT2") 59 | set(MACOSX_BUNDLE_ICON_FILE openrct2.icns) 60 | set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}) 61 | 62 | set(APP_ICON_MACOSX ${CMAKE_CURRENT_SOURCE_DIR}/icon/openrct2.icns) 63 | set_source_files_properties(${APP_ICON_MACOSX} PROPERTIES 64 | MACOSX_PACKAGE_LOCATION "Resources") 65 | list(APPEND PROJECT_SOURCES ${APP_ICON_MACOSX}) 66 | endif() 67 | 68 | add_executable(OpenRCT2Launcher WIN32 MACOSX_BUNDLE ${PROJECT_SOURCES}) 69 | target_compile_definitions(OpenRCT2Launcher PUBLIC APP_VERSION="${PROJECT_VERSION}") 70 | 71 | target_link_libraries(OpenRCT2Launcher PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network ${ZLIB_LINK}) 72 | -------------------------------------------------------------------------------- /src/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 420 10 | 280 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 420 22 | 280 23 | 24 | 25 | 26 | 27 | 420 28 | 280 29 | 30 | 31 | 32 | OpenRCT2 Launcher 33 | 34 | 35 | 36 | 37 | 0 38 | 39 | 40 | 0 41 | 42 | 43 | 0 44 | 45 | 46 | 0 47 | 48 | 49 | 0 50 | 51 | 52 | 53 | 54 | :/splash.png 55 | 56 | 57 | true 58 | 59 | 60 | Qt::AlignBottom|Qt::AlignHCenter 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 0 69 | 0 70 | 71 | 72 | 73 | 74 | 0 75 | 40 76 | 77 | 78 | 79 | 80 | 12 81 | 82 | 83 | 6 84 | 85 | 86 | 12 87 | 88 | 89 | 6 90 | 91 | 92 | 93 | 94 | false 95 | 96 | 97 | Play 98 | 99 | 100 | true 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 40 109 | 0 110 | 111 | 112 | 113 | 114 | 80 115 | 16777215 116 | 117 | 118 | 119 | 0 120 | 121 | 122 | 123 | 124 | 125 | 126 | true 127 | 128 | 129 | color: rgb(255, 0, 0); 130 | 131 | 132 | 133 | 134 | 135 | 136 | Qt::Horizontal 137 | 138 | 139 | 140 | 40 141 | 20 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | Options 150 | 151 | 152 | false 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /src/configuration.cpp: -------------------------------------------------------------------------------- 1 | #include "configuration.h" 2 | #include "ui_configuration.h" 3 | #include "configuration_data.h" 4 | #include "platform.h" 5 | 6 | #include 7 | 8 | Configuration::Configuration(QSettings *mainSettings, QString file, QWidget *parent) : 9 | QDialog(parent), 10 | ui(new Ui::Configuration), 11 | config(file, QSettings::IniFormat), 12 | mainSettings(mainSettings) 13 | { 14 | ui->setupUi(this); 15 | setComboBoxData(); 16 | 17 | ui->launcherVersionMsg->setText(tr("Launcher Version: ") + QStringLiteral(APP_VERSION OPENRCT2_BIT_SUFFIX)); 18 | 19 | QVariant hash = mainSettings->value(QStringLiteral("gitHash")); 20 | if (hash.isValid()) ui->buildHashMsg->setText(tr("OpenRCT2 Git Hash: ") + hash.toByteArray().left(4).toHex().left(7)); 21 | 22 | QVariant stable = mainSettings->value(QStringLiteral("stable")); 23 | if (stable.isValid() && stable.toBool()) ui->stableButton->setChecked(true); 24 | 25 | for (QLineEdit *w : ui->tabWidget->findChildren()) { 26 | QVariant setting = w->property("config"); 27 | if (setting.isValid()) { 28 | QVariant cvalue = config.value(setting.toString()); 29 | if (cvalue.isValid()) w->setText(cvalue.toString()); 30 | } 31 | } 32 | 33 | for (QCheckBox *w : ui->tabWidget->findChildren()) { 34 | QVariant setting = w->property("config"); 35 | if (setting.isValid()) { 36 | QVariant cvalue = config.value(setting.toString()); 37 | if (cvalue.convert(QMetaType::Bool)) w->setChecked(cvalue.toBool()); 38 | } 39 | } 40 | 41 | for (QSpinBox *w : ui->tabWidget->findChildren()) { 42 | QVariant setting = w->property("config"); 43 | if (setting.isValid()) { 44 | QVariant cvalue = config.value(setting.toString()); 45 | if (cvalue.convert(QMetaType::Int)) w->setValue(cvalue.toInt()); 46 | } 47 | } 48 | 49 | for (QComboBox *w : ui->tabWidget->findChildren()) { 50 | QVariant setting = w->property("config"); 51 | if (setting.isValid()) { 52 | QString configName = setting.toString(); 53 | QVariant cvalue = config.value(setting.toString()); 54 | if (cvalue.isValid()) { 55 | int index = -1; 56 | QString configValue = cvalue.toString(); 57 | if (cvalue.convert(QMetaType::Int)) { 58 | index = cvalue.toInt(); 59 | QVariant offset = w->property("configOffset"); 60 | if (offset.isValid()) index -= offset.toInt(); 61 | } else { 62 | index = w->findData(configValue); 63 | } 64 | if (index >= 0) w->setCurrentIndex(index); 65 | } 66 | } 67 | } 68 | 69 | // Locale Stuff 70 | { 71 | QLocale locale; 72 | if (!config.value(QStringLiteral("language")).isValid()) { 73 | QString name = locale.name(); 74 | name = name.replace('_', '-'); 75 | int ind = ui->languages->findData(langEquiv.value(name, name), Qt::UserRole, Qt::MatchExactly); 76 | 77 | if (ind < 0) { 78 | QStringList languages = locale.uiLanguages(); 79 | for (QString lang : languages) { 80 | lang = lang.replace('_', '-'); 81 | ind = ui->languages->findData(langEquiv.value(lang, lang), Qt::UserRole, Qt::MatchExactly); 82 | if (ind >= 0) break; 83 | } 84 | } 85 | 86 | if (ind < 0) { 87 | QStringList languages = locale.uiLanguages(); 88 | for (QString lang : languages) { 89 | lang = lang.replace('_', '-'); 90 | if (lang.contains('-')) lang.truncate(lang.indexOf('-')); 91 | ind = ui->languages->findData(lang, Qt::UserRole, Qt::MatchStartsWith); 92 | if (ind >= 0) break; 93 | } 94 | } 95 | 96 | ui->languages->setCurrentIndex(ind); 97 | } 98 | } 99 | } 100 | 101 | Configuration::~Configuration() 102 | { 103 | for (QLineEdit *w : ui->tabWidget->findChildren()) { 104 | QVariant setting = w->property("config"); 105 | if (setting.isValid()) { 106 | config.setValue(setting.toString(), w->text()); 107 | } 108 | } 109 | 110 | for (QCheckBox *w : ui->tabWidget->findChildren()) { 111 | QVariant setting = w->property("config"); 112 | if (setting.isValid()) { 113 | config.setValue(setting.toString(), w->isChecked()); 114 | } 115 | } 116 | 117 | for (QSpinBox *w : ui->tabWidget->findChildren()) { 118 | QVariant setting = w->property("config"); 119 | if (setting.isValid()) { 120 | config.setValue(setting.toString(), w->value()); 121 | } 122 | } 123 | 124 | for (QComboBox *w : ui->tabWidget->findChildren()) { 125 | QVariant setting = w->property("config"); 126 | if (setting.isValid()) { 127 | if (w->currentData().isValid()) { 128 | config.setValue(setting.toString(), w->currentData()); 129 | } else { 130 | QVariant offset = w->property("configOffset"); 131 | if (offset.isValid()) config.setValue(setting.toString(), w->currentIndex() + offset.toInt()); 132 | else config.setValue(setting.toString(), w->currentIndex()); 133 | } 134 | } 135 | } 136 | 137 | QVariant stableVar = mainSettings->value(QStringLiteral("stable")); 138 | bool stable = stableVar.isValid() && stableVar.toBool(); 139 | bool newStable = ui->stableButton->isChecked(); 140 | if (stable != newStable) { 141 | mainSettings->setValue(QStringLiteral("stable"), newStable); 142 | mainSettings->sync(); 143 | emit redownload(); 144 | } 145 | 146 | delete ui; 147 | } 148 | 149 | void Configuration::on_locateRCT2_clicked() { 150 | QString rct2 = QFileDialog::getExistingDirectory(this, tr("Select RCT2 Install Location"), 151 | OPENRCT2_HOMEPATH, QFileDialog::ShowDirsOnly); 152 | if (!rct2.isEmpty()) ui->rct2Path->setText(rct2); 153 | } 154 | -------------------------------------------------------------------------------- /src/archives/zipextractor.cpp: -------------------------------------------------------------------------------- 1 | #include "zipextractor.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define READ_BUFFER_SIZE 16384 8 | #define NAME_BUFFER_SIZE 64 9 | 10 | static const quint32 ZipLocalMagic = 0x04034B50; 11 | static const quint32 ZipCentralMagic = 0x02014B50; 12 | static const quint32 ZipEndMagic = 0x06054B50; 13 | #define qcrc32(crc, buf, len) ((quint32) crc32(crc, (const Bytef *) buf, len)) 14 | 15 | static inline QFile::Permissions getPermissionsFromUnix(quint16 type) { 16 | return static_cast(((type & 0700) << 6) | ((type & 0700) << 2) | ((type & 0070) << 1) | (type & 0007)); 17 | } 18 | 19 | bool extractZip(QIODevice *in, QDir extractLoc) { 20 | in->open(QIODevice::ReadOnly); 21 | QDataStream stream(in); 22 | stream.setByteOrder(QDataStream::LittleEndian); // Just to be sure 23 | 24 | QByteArray buffer, cbuffer, namebuf; 25 | buffer.resize(READ_BUFFER_SIZE); 26 | cbuffer.resize(READ_BUFFER_SIZE); 27 | namebuf.reserve(NAME_BUFFER_SIZE); 28 | 29 | while (true) { 30 | quint32 magic; 31 | stream >> magic; 32 | 33 | if (magic == ZipLocalMagic) { 34 | 35 | quint8 zver, ztype; 36 | stream >> zver >> ztype; 37 | if (ztype != 0 || zver > 20) return false; // Unsupported Zip Version 38 | 39 | quint16 flags; 40 | stream >> flags; 41 | if (flags & 0xF7F9) return false; // Unsupported Flags 42 | 43 | quint16 comp; 44 | stream >> comp; 45 | if (comp != 0 && comp != 8) return false; // Unsupported Compression 46 | 47 | stream.skipRawData(4); // Ignore Modification Date and Time 48 | 49 | quint32 crc; 50 | stream >> crc; 51 | 52 | quint32 csize, esize; 53 | stream >> csize >> esize; 54 | 55 | quint16 namelen, extralen; 56 | stream >> namelen >> extralen; 57 | 58 | namebuf.resize(namelen); 59 | stream.readRawData(namebuf.data(), namelen); 60 | QString name = QString::fromUtf8(namebuf, namelen); 61 | 62 | stream.skipRawData(extralen); // Ignore Extra Field 63 | 64 | if (csize == 0 && esize == 0 && name.endsWith("/")) { // Is a Directory (Even on Windows) 65 | extractLoc.mkdir(name); 66 | 67 | } else if (comp == 0) { // No Compression (I Don't expect to see it) 68 | quint32 length = esize; 69 | quint32 rcrc = qcrc32(0, Z_NULL, 0); 70 | QFile file(extractLoc.filePath(name)); 71 | file.open(QFile::WriteOnly); 72 | 73 | while (length > 0) { 74 | int read = stream.readRawData(buffer.data(), qMin(length, (quint32) READ_BUFFER_SIZE)); 75 | file.write(buffer.data(), read); 76 | rcrc = qcrc32(rcrc, buffer.data(), read); 77 | length -= read; 78 | } 79 | 80 | file.close(); 81 | if (rcrc != crc) return false; // CRC-32 Mismatch 82 | 83 | } else { // Standard DEFLATE 84 | 85 | z_stream strm; 86 | strm.zalloc = Z_NULL; 87 | strm.zfree = Z_NULL; 88 | strm.opaque = Z_NULL; 89 | strm.next_in = Z_NULL; 90 | strm.avail_in = 0; 91 | int ret = inflateInit2(&strm, -MAX_WBITS); 92 | if (ret != Z_OK) 93 | return ret; 94 | 95 | quint32 length = csize; 96 | quint32 written = 0; 97 | quint32 rcrc = qcrc32(0, Z_NULL, 0); 98 | QFile file(extractLoc.filePath(name)); 99 | file.open(QFile::WriteOnly); 100 | 101 | while (length > 0 && ret == Z_OK) { 102 | int read = stream.readRawData(cbuffer.data(), qMin(length, (quint32) READ_BUFFER_SIZE)); 103 | strm.next_in = (Bytef *) cbuffer.data(); 104 | strm.avail_in = read; 105 | 106 | do { 107 | strm.next_out = (Bytef *) buffer.data(); 108 | strm.avail_out = READ_BUFFER_SIZE; 109 | ret = inflate(&strm, Z_NO_FLUSH); 110 | if (ret >= 0) { 111 | quint32 towrite = READ_BUFFER_SIZE - strm.avail_out; 112 | file.write(buffer.data(), towrite); 113 | rcrc = qcrc32(rcrc, buffer.data(), towrite); 114 | written += towrite; 115 | } 116 | } while (ret == Z_OK && strm.avail_in > 0); 117 | length -= read; 118 | } 119 | 120 | file.close(); 121 | ret ^= 1; 122 | ret |= inflateEnd(&strm); 123 | if (ret != Z_OK) return false; 124 | if (written != esize) return false; // Unexpected File Size 125 | if (length != 0) return false; // Unexpected End Of Stream 126 | if (rcrc != crc) return false; // CRC-32 Mismatch 127 | } 128 | 129 | } else if (magic == ZipCentralMagic) { 130 | stream.skipRawData(1); // Skip Writer Version 131 | 132 | quint8 ztype; 133 | stream >> ztype; 134 | 135 | stream.skipRawData(22); 136 | 137 | quint16 namelen, extralen, commentlen; 138 | stream >> namelen >> extralen >> commentlen; 139 | 140 | if (ztype == 3) { 141 | stream.skipRawData(6); 142 | 143 | quint16 type; 144 | stream >> type; 145 | 146 | stream.skipRawData(4); 147 | 148 | namebuf.resize(namelen); 149 | stream.readRawData(namebuf.data(), namelen); 150 | QString name = QString::fromUtf8(namebuf, namelen); 151 | 152 | if ((type >> 12) == 8 || (type >> 12) == 4) { 153 | if (name.endsWith("/") != ((type >> 12) == 4)) return false; // Type != Written 154 | QFile file(extractLoc.filePath(name)); 155 | file.setPermissions(file.permissions() | getPermissionsFromUnix(type)); 156 | } 157 | 158 | stream.skipRawData(extralen + commentlen); 159 | } else { 160 | stream.skipRawData(12 + namelen + extralen + commentlen); 161 | } 162 | } else if (magic == ZipEndMagic) { 163 | // End of File 164 | return true; 165 | } else { 166 | return false; // Unknown Magic 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/updater.cpp: -------------------------------------------------------------------------------- 1 | #include "updater.h" 2 | #include "platform.h" 3 | 4 | #ifdef Q_OS_LINUX 5 | #include "archives/gzipreadfilter.h" 6 | #include "archives/tarextractor.h" 7 | #else 8 | #include "archives/zipextractor.h" 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | Updater::Updater(QObject *parent) : QObject(parent) 26 | { 27 | connect(&net, &QNetworkAccessManager::sslErrors, [](QNetworkReply * reply, const QList & errors){Q_UNUSED(reply); qDebug() << errors;}); 28 | 29 | QNetworkRequest urequest(QStringLiteral("https://api.github.com/repos/LRFLEW/OpenRCT2Launcher/releases/latest")); 30 | update = net.get(urequest); 31 | connect(update, &QNetworkReply::finished, this, &Updater::receivedUpdate); 32 | } 33 | 34 | void Updater::queryDownloads(QString flavor) { 35 | QUrl url(QStringLiteral("https://openrct2.org/altapi/")); 36 | 37 | QUrlQuery query; 38 | query.addQueryItem(QStringLiteral("command"), QStringLiteral("get-latest-download")); 39 | query.addQueryItem(QStringLiteral("flavourId"), flavor); 40 | 41 | QVariant stableVar = settings.value(QStringLiteral("stable")); 42 | bool stable = stableVar.isValid() && stableVar.toBool(); 43 | query.addQueryItem(QStringLiteral("gitBranch"), stable ? QStringLiteral("master") : QStringLiteral("develop")); 44 | 45 | url.setQuery(query.query()); 46 | 47 | QNetworkRequest request(url); 48 | api = net.get(request); 49 | connect(api, &QNetworkReply::finished, this, &Updater::receivedAPI); 50 | } 51 | 52 | void Updater::download() { 53 | if (api != nullptr) { 54 | api->abort(); 55 | api->deleteLater(); 56 | api = nullptr; 57 | } 58 | if (bundle != nullptr) { 59 | bundle->abort(); 60 | bundle->deleteLater(); 61 | bundle = nullptr; 62 | } 63 | 64 | queryDownloads(QStringLiteral(OPENRCT2_FLAVOR)); 65 | } 66 | 67 | void Updater::receivedUpdate() { 68 | if (update->error() != QNetworkReply::NoError) { 69 | // Don't emit, so the error is mostly silent 70 | qDebug() << update->errorString(); 71 | update->deleteLater(); 72 | update = nullptr; 73 | return; 74 | } 75 | 76 | QByteArray data = update->readAll(); 77 | update->close(); 78 | update->deleteLater(); 79 | update = nullptr; 80 | 81 | QJsonDocument response = QJsonDocument::fromJson(data); 82 | QJsonObject robj = response.object(); 83 | QString tag = robj[QStringLiteral("tag_name")].toString(); 84 | 85 | if (tag.startsWith('v')) { 86 | QStringRef ver = tag.midRef(1); 87 | if (ver.compare(QLatin1String(APP_VERSION)) > 0) { 88 | // Show Update Notification 89 | QMessageBox msg; 90 | msg.setText(QStringLiteral("") % tr("New Update is Available") % QStringLiteral("")); 91 | msg.setInformativeText( 92 | QStringLiteral("

") % 93 | tr("There is a new update available for the launcher.") % 94 | QStringLiteral("
") % 95 | tr("It is recomended that you update.") % 96 | QStringLiteral("

") % 97 | tr("Installed Version: ") % 98 | QStringLiteral(APP_VERSION) % 99 | QStringLiteral("
") % 100 | tr("Latest Version: ") % 101 | ver % 102 | QStringLiteral("

") % 105 | tr("Download Page") % 106 | QStringLiteral("

")); 107 | msg.setMinimumWidth(1000); 108 | msg.exec(); 109 | } 110 | } 111 | } 112 | 113 | void Updater::receivedAPI() { 114 | if (api->error() != QNetworkReply::NoError) { 115 | if (api->error() != QNetworkReply::OperationCanceledError) { 116 | emit error(api->errorString()); 117 | api->deleteLater(); 118 | api = nullptr; 119 | } 120 | return; 121 | } 122 | 123 | QByteArray data = api->readAll(); 124 | api->close(); 125 | api->deleteLater(); 126 | api = nullptr; 127 | 128 | QJsonDocument response = QJsonDocument::fromJson(data); 129 | QJsonObject robj = response.object(); 130 | if (robj[QStringLiteral("error")].toInt() != 0) { 131 | emit error(robj[QStringLiteral("errorMessage")].toString()); 132 | return; 133 | } 134 | 135 | QString flavor = settings.value(QStringLiteral("downloadFlavour")).toString(); 136 | QString have = settings.value(QStringLiteral("downloadId")).toString(); 137 | version = robj[QStringLiteral("downloadId")].toString(); 138 | 139 | if (flavor == OPENRCT2_FLAVOR && have == version && OPENRCT2_HOMEDIR.cd(QStringLiteral(OPENRCT2_BIN))) { 140 | emit installed(); 141 | } else { 142 | size = robj[QStringLiteral("fileSize")].toInt(); 143 | hash = QByteArray::fromHex(robj[QStringLiteral("fileHash")].toString().toLatin1()); 144 | githash = QByteArray::fromHex(robj[QStringLiteral("gitHash")].toString().toLatin1()); 145 | 146 | QNetworkRequest request(robj[QStringLiteral("url")].toString()); 147 | request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); 148 | bundle = net.get(request); 149 | connect(bundle, &QNetworkReply::finished, this, &Updater::receivedBundle); 150 | connect(bundle, &QNetworkReply::downloadProgress, this, &Updater::downloadProgress); 151 | } 152 | } 153 | 154 | void Updater::receivedBundle() { 155 | if (bundle->error() != QNetworkReply::NoError) { 156 | if (bundle->error() != QNetworkReply::OperationCanceledError) { 157 | emit error(bundle->errorString()); 158 | bundle->deleteLater(); 159 | bundle = nullptr; 160 | } 161 | return; 162 | } 163 | 164 | QByteArray data = bundle->readAll(); 165 | bundle->close(); 166 | bundle->deleteLater(); 167 | bundle = nullptr; 168 | 169 | if (size > 0 && data.size() != size) { 170 | emit error(tr("Invalid Download Size")); 171 | return; 172 | } 173 | 174 | if (hash.size() > 0) { 175 | QByteArray fhash = QCryptographicHash::hash(data, QCryptographicHash::Algorithm::Sha256); 176 | if (fhash != hash) { 177 | emit error(tr("Invalid Download Hash")); 178 | return; 179 | } 180 | } 181 | 182 | QDir bin = OPENRCT2_HOMEDIR; 183 | if (bin.cd(QStringLiteral(OPENRCT2_BIN))) { 184 | bin.removeRecursively(); 185 | if (!bin.mkpath(QStringLiteral("."))) { 186 | emit error(tr("bin dir not created")); 187 | return; 188 | } 189 | } else { 190 | bin.mkpath(QStringLiteral(OPENRCT2_BIN)); 191 | if (!bin.cd(QStringLiteral(OPENRCT2_BIN))) { 192 | emit error(tr("bin dir not created")); 193 | return; 194 | } 195 | } 196 | 197 | if (extract(data, bin)) { 198 | emit installed(); 199 | 200 | settings.setValue(QStringLiteral("downloadFlavour"), OPENRCT2_FLAVOR); 201 | settings.setValue(QStringLiteral("downloadId"), version); 202 | settings.setValue(QStringLiteral("gitHash"), githash); 203 | settings.sync(); 204 | } else { 205 | emit error(tr("Error extracting archive")); 206 | } 207 | } 208 | 209 | bool Updater::extract(QByteArray &data, QDir &bin) { 210 | QBuffer buffer(&data); 211 | #ifdef Q_OS_LINUX 212 | GZipReadFilter gzip(&buffer); 213 | return extractTar(&gzip, bin); 214 | #else 215 | return extractZip(&buffer, bin); 216 | #endif 217 | } 218 | -------------------------------------------------------------------------------- /src/configuration.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Configuration 4 | 5 | 6 | 7 | 0 8 | 0 9 | 480 10 | 480 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 480 22 | 480 23 | 24 | 25 | 26 | 27 | 480 28 | 480 29 | 30 | 31 | 32 | Options 33 | 34 | 35 | 36 | 37 | 38 | 0 39 | 40 | 41 | 42 | Launcher 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | RCT2 Location: 51 | 52 | 53 | 54 | 55 | 56 | 57 | game_path 58 | 59 | 60 | 61 | 62 | 63 | 64 | Locate 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Branch: 76 | 77 | 78 | 79 | 80 | 81 | 82 | Qt::Horizontal 83 | 84 | 85 | QSizePolicy::Fixed 86 | 87 | 88 | 89 | 60 90 | 20 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | Release 99 | 100 | 101 | true 102 | 103 | 104 | 105 | 106 | 107 | 108 | Develop 109 | 110 | 111 | true 112 | 113 | 114 | 115 | 116 | 117 | 118 | Qt::Horizontal 119 | 120 | 121 | 122 | 40 123 | 20 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | Qt::Vertical 134 | 135 | 136 | 137 | 20 138 | 40 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | Launcher Version: X.X.X 147 | 148 | 149 | Qt::AlignCenter 150 | 151 | 152 | Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse 153 | 154 | 155 | 156 | 157 | 158 | 159 | OpenRCT2 Git Hash: n/a 160 | 161 | 162 | Qt::AlignCenter 163 | 164 | 165 | Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse 166 | 167 | 168 | 169 | 170 | 171 | 172 | Qt::Vertical 173 | 174 | 175 | 176 | 20 177 | 40 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | Hidden Settings 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | Language: 195 | 196 | 197 | 198 | 199 | 200 | 201 | 1 202 | 203 | 204 | language 205 | 206 | 207 | 208 | English (UK) 209 | 210 | 211 | 212 | 213 | English (US) 214 | 215 | 216 | 217 | 218 | Deutsch 219 | 220 | 221 | 222 | 223 | Nederlands 224 | 225 | 226 | 227 | 228 | Français 229 | 230 | 231 | 232 | 233 | Magyar 234 | 235 | 236 | 237 | 238 | Polski 239 | 240 | 241 | 242 | 243 | Español 244 | 245 | 246 | 247 | 248 | Svenska 249 | 250 | 251 | 252 | 253 | Italiano 254 | 255 | 256 | 257 | 258 | Português (BR) 259 | 260 | 261 | 262 | 263 | Chinese (Traditional) 264 | 265 | 266 | 267 | 268 | Chinese (Simplified) 269 | 270 | 271 | 272 | 273 | Suomi 274 | 275 | 276 | 277 | 278 | Korean 279 | 280 | 281 | 282 | 283 | Russian 284 | 285 | 286 | 287 | 288 | Czech 289 | 290 | 291 | 292 | 293 | Japanese 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | Windowed Size: 306 | 307 | 308 | 309 | 310 | 311 | 312 | Qt::Horizontal 313 | 314 | 315 | 316 | 40 317 | 20 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | Width 326 | 327 | 328 | 329 | 330 | 331 | 332 | 999999 333 | 334 | 335 | 720 336 | 337 | 338 | window_width 339 | 340 | 341 | 342 | 343 | 344 | 345 | Height 346 | 347 | 348 | 349 | 350 | 351 | 352 | 999999 353 | 354 | 355 | 480 356 | 357 | 358 | window_height 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | Use small font in debug console 368 | 369 | 370 | interface/console_small_font 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | Window edge snapping distance: 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 0 388 | 0 389 | 390 | 391 | 392 | 393 | 120 394 | 0 395 | 396 | 397 | 398 | 255 399 | 400 | 401 | 5 402 | 403 | 404 | window_snap_proximity 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | Don't let testing rides crash 414 | 415 | 416 | no_test_crashes 417 | 418 | 419 | 420 | 421 | 422 | 423 | Allow loading files with incorrect checksums 424 | 425 | 426 | allow_loading_with_incorrect_checksum 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | Screenshot format: 436 | 437 | 438 | 439 | 440 | 441 | 442 | 1 443 | 444 | 445 | screenshot_format 446 | 447 | 448 | 449 | BMP 450 | 451 | 452 | 453 | 454 | PNG 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | Always prompt for save location 465 | 466 | 467 | confirmation_prompt 468 | 469 | 470 | 471 | 472 | 473 | 474 | Show game intro 475 | 476 | 477 | play_intro 478 | 479 | 480 | 481 | 482 | 483 | 484 | Hide RCT1 Mega Park when not unlocked 485 | 486 | 487 | true 488 | 489 | 490 | scenario_hide_mega_park 491 | 492 | 493 | 494 | 495 | 496 | 497 | Qt::Vertical 498 | 499 | 500 | 501 | 20 502 | 40 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | Multiplayer 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | Qt::Horizontal 520 | 521 | 522 | 523 | 40 524 | 20 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | Player Name: 533 | 534 | 535 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 0 544 | 0 545 | 546 | 547 | 548 | 549 | 160 550 | 0 551 | 552 | 553 | 554 | Player 555 | 556 | 557 | network/player_name 558 | 559 | 560 | 561 | 562 | 563 | 564 | Qt::Horizontal 565 | 566 | 567 | 568 | 40 569 | 20 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | Server Name: 578 | 579 | 580 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 0 589 | 0 590 | 591 | 592 | 593 | 594 | 160 595 | 0 596 | 597 | 598 | 599 | Server 600 | 601 | 602 | network/server_name 603 | 604 | 605 | 606 | 607 | 608 | 609 | Server Password: 610 | 611 | 612 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 0 621 | 0 622 | 623 | 624 | 625 | 626 | 160 627 | 0 628 | 629 | 630 | 631 | 632 | 633 | 634 | QLineEdit::Password 635 | 636 | 637 | network/default_password 638 | 639 | 640 | 641 | 642 | 643 | 644 | Server Port: 645 | 646 | 647 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 0 656 | 0 657 | 658 | 659 | 660 | 661 | 160 662 | 0 663 | 664 | 665 | 666 | 65535 667 | 668 | 669 | 11753 670 | 671 | 672 | network/default_port 673 | 674 | 675 | 676 | 677 | 678 | 679 | Max number of player on server: 680 | 681 | 682 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 0 691 | 0 692 | 693 | 694 | 695 | 696 | 160 697 | 0 698 | 699 | 700 | 701 | 1 702 | 703 | 704 | 255 705 | 706 | 707 | 16 708 | 709 | 710 | network/maxplayers 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | Advertise server on global server list 720 | 721 | 722 | network/advertise 723 | 724 | 725 | 726 | 727 | 728 | 729 | Qt::Horizontal 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | Server Description: 739 | 740 | 741 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 742 | 743 | 744 | 745 | 746 | 747 | 748 | network/server_description 749 | 750 | 751 | 752 | 753 | 754 | 755 | Provider Name: 756 | 757 | 758 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 759 | 760 | 761 | 762 | 763 | 764 | 765 | network/provider_name 766 | 767 | 768 | 769 | 770 | 771 | 772 | Provider Email: 773 | 774 | 775 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 776 | 777 | 778 | 779 | 780 | 781 | 782 | network/provider_email 783 | 784 | 785 | 786 | 787 | 788 | 789 | Provider Website: 790 | 791 | 792 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 793 | 794 | 795 | 796 | 797 | 798 | 799 | network/provider_website 800 | 801 | 802 | 803 | 804 | 805 | 806 | Master Server Override: 807 | 808 | 809 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 810 | 811 | 812 | 813 | 814 | 815 | 816 | network/master_server_url 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | Qt::Vertical 826 | 827 | 828 | 829 | 20 830 | 40 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | Qt::Horizontal 843 | 844 | 845 | QDialogButtonBox::Ok 846 | 847 | 848 | 849 | 850 | 851 | 852 | tabWidget 853 | rct2Path 854 | locateRCT2 855 | stableButton 856 | developButton 857 | languages 858 | spinBox_5 859 | spinBox_7 860 | checkBox_44 861 | spinBox_6 862 | checkBox_41 863 | checkBox_30 864 | screenshotFormat 865 | checkBox_24 866 | checkBox_38 867 | checkBox_42 868 | lineEdit_3 869 | lineEdit_5 870 | lineEdit_4 871 | spinBox 872 | spinBox_2 873 | checkBox_40 874 | lineEdit_6 875 | lineEdit_7 876 | lineEdit_8 877 | lineEdit_9 878 | lineEdit_10 879 | 880 | 881 | 882 | 883 | buttonBox 884 | accepted() 885 | Configuration 886 | accept() 887 | 888 | 889 | 260 890 | 335 891 | 892 | 893 | 157 894 | 274 895 | 896 | 897 | 898 | 899 | buttonBox 900 | rejected() 901 | Configuration 902 | reject() 903 | 904 | 905 | 328 906 | 335 907 | 908 | 909 | 286 910 | 274 911 | 912 | 913 | 914 | 915 | 916 | --------------------------------------------------------------------------------