NOISSUE tabs -> spaces

This commit is contained in:
Petr Mrázek 2018-07-15 14:51:05 +02:00
parent 03280cc62e
commit bbb3b3e6f6
577 changed files with 51938 additions and 51938 deletions

View File

@ -1,5 +1,5 @@
{ {
"project_id": "MultiMC5", "project_id": "MultiMC5",
"conduit_uri": "http://ph.multimc.org" "conduit_uri": "http://ph.multimc.org"
} }

View File

@ -1,4 +1,4 @@
UseTab: true UseTab: false
IndentWidth: 4 IndentWidth: 4
TabWidth: 4 TabWidth: 4
ConstructorInitializerIndentWidth: 4 ConstructorInitializerIndentWidth: 4

8
.gitmodules vendored
View File

@ -1,6 +1,6 @@
[submodule "depends/libnbtplusplus"] [submodule "depends/libnbtplusplus"]
path = libraries/libnbtplusplus path = libraries/libnbtplusplus
url = https://github.com/MultiMC/libnbtplusplus.git url = https://github.com/MultiMC/libnbtplusplus.git
[submodule "libraries/quazip"] [submodule "libraries/quazip"]
path = libraries/quazip path = libraries/quazip
url = https://github.com/MultiMC/quazip.git url = https://github.com/MultiMC/quazip.git

View File

@ -2,12 +2,12 @@ cmake_minimum_required(VERSION 3.1)
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD) string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
if(IS_IN_SOURCE_BUILD) if(IS_IN_SOURCE_BUILD)
message(AUTHOR_WARNING "You are building MultiMC in-source. This is NOT recommended!") message(AUTHOR_WARNING "You are building MultiMC in-source. This is NOT recommended!")
endif() endif()
if(WIN32) if(WIN32)
# In Qt 5.1+ we have our own main() function, don't autolink to qtmain on Windows # In Qt 5.1+ we have our own main() function, don't autolink to qtmain on Windows
cmake_policy(SET CMP0020 OLD) cmake_policy(SET CMP0020 OLD)
endif() endif()
project(MultiMC) project(MultiMC)
@ -22,7 +22,7 @@ set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
# Output all executables and shared libs in the main build folder, not in subfolders. # Output all executables and shared libs in the main build folder, not in subfolders.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
if(UNIX) if(UNIX)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
endif() endif()
set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars) set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars)
@ -34,7 +34,7 @@ set(CMAKE_C_STANDARD 11)
include(GenerateExportHeader) include(GenerateExportHeader)
set(CMAKE_CXX_FLAGS " -Wall -pedantic -Werror -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS " -Wall -pedantic -Werror -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}")
if(UNIX AND APPLE) if(UNIX AND APPLE)
set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}")
endif() endif()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
@ -108,124 +108,124 @@ set(MultiMC_LAYOUT "auto" CACHE STRING "The layout for MultiMC installation (aut
set_property(CACHE MultiMC_LAYOUT PROPERTY STRINGS auto win-bundle lin-bundle lin-nodeps lin-system mac-bundle) set_property(CACHE MultiMC_LAYOUT PROPERTY STRINGS auto win-bundle lin-bundle lin-nodeps lin-system mac-bundle)
if(MultiMC_LAYOUT STREQUAL "auto") if(MultiMC_LAYOUT STREQUAL "auto")
if(UNIX AND APPLE) if(UNIX AND APPLE)
set(MultiMC_LAYOUT_REAL "mac-bundle") set(MultiMC_LAYOUT_REAL "mac-bundle")
elseif(UNIX) elseif(UNIX)
set(MultiMC_LAYOUT_REAL "lin-nodeps") set(MultiMC_LAYOUT_REAL "lin-nodeps")
elseif(WIN32) elseif(WIN32)
set(MultiMC_LAYOUT_REAL "win-bundle") set(MultiMC_LAYOUT_REAL "win-bundle")
else() else()
message(FATAL_ERROR "Cannot choose a sensible install layout for your platform.") message(FATAL_ERROR "Cannot choose a sensible install layout for your platform.")
endif() endif()
else() else()
set(MultiMC_LAYOUT_REAL ${MultiMC_LAYOUT}) set(MultiMC_LAYOUT_REAL ${MultiMC_LAYOUT})
endif() endif()
if(MultiMC_LAYOUT_REAL STREQUAL "mac-bundle") if(MultiMC_LAYOUT_REAL STREQUAL "mac-bundle")
set(BINARY_DEST_DIR "MultiMC.app/Contents/MacOS") set(BINARY_DEST_DIR "MultiMC.app/Contents/MacOS")
set(LIBRARY_DEST_DIR "MultiMC.app/Contents/MacOS") set(LIBRARY_DEST_DIR "MultiMC.app/Contents/MacOS")
set(PLUGIN_DEST_DIR "MultiMC.app/Contents/MacOS") set(PLUGIN_DEST_DIR "MultiMC.app/Contents/MacOS")
set(RESOURCES_DEST_DIR "MultiMC.app/Contents/Resources") set(RESOURCES_DEST_DIR "MultiMC.app/Contents/Resources")
set(JARS_DEST_DIR "MultiMC.app/Contents/MacOS/jars") set(JARS_DEST_DIR "MultiMC.app/Contents/MacOS/jars")
set(BUNDLE_DEST_DIR ".") set(BUNDLE_DEST_DIR ".")
# Apps to bundle # Apps to bundle
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.app") set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.app")
# Mac bundle settings # Mac bundle settings
set(MACOSX_BUNDLE_BUNDLE_NAME "MultiMC") set(MACOSX_BUNDLE_BUNDLE_NAME "MultiMC")
set(MACOSX_BUNDLE_INFO_STRING "MultiMC Minecraft launcher and management utility.") set(MACOSX_BUNDLE_INFO_STRING "MultiMC Minecraft launcher and management utility.")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.MultiMC5") set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.MultiMC5")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}") set(MACOSX_BUNDLE_BUNDLE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}") set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
set(MACOSX_BUNDLE_ICON_FILE MultiMC.icns) set(MACOSX_BUNDLE_ICON_FILE MultiMC.icns)
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2015-2018 MultiMC Contributors") set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2015-2018 MultiMC Contributors")
# directories to look for dependencies # directories to look for dependencies
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# install as bundle # install as bundle
set(INSTALL_BUNDLE "full") set(INSTALL_BUNDLE "full")
# Add the icon # Add the icon
install(FILES application/resources/MultiMC.icns DESTINATION ${RESOURCES_DEST_DIR}) install(FILES application/resources/MultiMC.icns DESTINATION ${RESOURCES_DEST_DIR})
elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-bundle") elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-bundle")
set(BINARY_DEST_DIR "bin") set(BINARY_DEST_DIR "bin")
set(LIBRARY_DEST_DIR "bin") set(LIBRARY_DEST_DIR "bin")
set(PLUGIN_DEST_DIR "plugins") set(PLUGIN_DEST_DIR "plugins")
set(BUNDLE_DEST_DIR ".") set(BUNDLE_DEST_DIR ".")
set(RESOURCES_DEST_DIR ".") set(RESOURCES_DEST_DIR ".")
set(JARS_DEST_DIR "bin/jars") set(JARS_DEST_DIR "bin/jars")
# Apps to bundle # Apps to bundle
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/MultiMC") set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/MultiMC")
# directories to look for dependencies # directories to look for dependencies
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# install as bundle # install as bundle
set(INSTALL_BUNDLE "full") set(INSTALL_BUNDLE "full")
# Set RPATH # Set RPATH
SET(MultiMC_BINARY_RPATH "$ORIGIN/") SET(MultiMC_BINARY_RPATH "$ORIGIN/")
# Install basic runner script # Install basic runner script
install(PROGRAMS application/package/linux/MultiMC DESTINATION ${BUNDLE_DEST_DIR}) install(PROGRAMS application/package/linux/MultiMC DESTINATION ${BUNDLE_DEST_DIR})
elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-nodeps") elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-nodeps")
set(BINARY_DEST_DIR "bin") set(BINARY_DEST_DIR "bin")
set(LIBRARY_DEST_DIR "bin") set(LIBRARY_DEST_DIR "bin")
set(PLUGIN_DEST_DIR "plugins") set(PLUGIN_DEST_DIR "plugins")
set(BUNDLE_DEST_DIR ".") set(BUNDLE_DEST_DIR ".")
set(RESOURCES_DEST_DIR ".") set(RESOURCES_DEST_DIR ".")
set(JARS_DEST_DIR "bin/jars") set(JARS_DEST_DIR "bin/jars")
# install as bundle with no dependencies included # install as bundle with no dependencies included
set(INSTALL_BUNDLE "nodeps") set(INSTALL_BUNDLE "nodeps")
# Set RPATH # Set RPATH
SET(MultiMC_BINARY_RPATH "$ORIGIN/") SET(MultiMC_BINARY_RPATH "$ORIGIN/")
# Install basic runner script # Install basic runner script
install(PROGRAMS application/package/linux/MultiMC DESTINATION ${BUNDLE_DEST_DIR}) install(PROGRAMS application/package/linux/MultiMC DESTINATION ${BUNDLE_DEST_DIR})
elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-system") elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-system")
set(MultiMC_APP_BINARY_NAME "multimc" CACHE STRING "Name of the MultiMC binary") set(MultiMC_APP_BINARY_NAME "multimc" CACHE STRING "Name of the MultiMC binary")
set(MultiMC_BINARY_DEST_DIR "bin" CACHE STRING "Path to the binary directory") set(MultiMC_BINARY_DEST_DIR "bin" CACHE STRING "Path to the binary directory")
set(MultiMC_LIBRARY_DEST_DIR "lib${LIB_SUFFIX}" CACHE STRING "Path to the library directory") set(MultiMC_LIBRARY_DEST_DIR "lib${LIB_SUFFIX}" CACHE STRING "Path to the library directory")
set(MultiMC_SHARE_DEST_DIR "share/multimc" CACHE STRING "Path to the shared data directory") set(MultiMC_SHARE_DEST_DIR "share/multimc" CACHE STRING "Path to the shared data directory")
set(JARS_DEST_DIR "${MultiMC_SHARE_DEST_DIR}/jars") set(JARS_DEST_DIR "${MultiMC_SHARE_DEST_DIR}/jars")
set(BINARY_DEST_DIR ${MultiMC_BINARY_DEST_DIR}) set(BINARY_DEST_DIR ${MultiMC_BINARY_DEST_DIR})
set(LIBRARY_DEST_DIR ${MultiMC_LIBRARY_DEST_DIR}) set(LIBRARY_DEST_DIR ${MultiMC_LIBRARY_DEST_DIR})
MESSAGE(STATUS "Compiling for linux system with ${MultiMC_SHARE_DEST_DIR} and MULTIMC_LINUX_DATADIR") MESSAGE(STATUS "Compiling for linux system with ${MultiMC_SHARE_DEST_DIR} and MULTIMC_LINUX_DATADIR")
SET(MultiMC_APP_BINARY_DEFS "-DMULTIMC_JARS_LOCATION=${CMAKE_INSTALL_PREFIX}/${JARS_DEST_DIR}" "-DMULTIMC_LINUX_DATADIR") SET(MultiMC_APP_BINARY_DEFS "-DMULTIMC_JARS_LOCATION=${CMAKE_INSTALL_PREFIX}/${JARS_DEST_DIR}" "-DMULTIMC_LINUX_DATADIR")
# install as bundle with no dependencies included # install as bundle with no dependencies included
set(INSTALL_BUNDLE "nodeps") set(INSTALL_BUNDLE "nodeps")
elseif(MultiMC_LAYOUT_REAL STREQUAL "win-bundle") elseif(MultiMC_LAYOUT_REAL STREQUAL "win-bundle")
set(BINARY_DEST_DIR ".") set(BINARY_DEST_DIR ".")
set(LIBRARY_DEST_DIR ".") set(LIBRARY_DEST_DIR ".")
set(PLUGIN_DEST_DIR ".") set(PLUGIN_DEST_DIR ".")
set(BUNDLE_DEST_DIR ".") set(BUNDLE_DEST_DIR ".")
set(RESOURCES_DEST_DIR ".") set(RESOURCES_DEST_DIR ".")
set(JARS_DEST_DIR "jars") set(JARS_DEST_DIR "jars")
# Apps to bundle # Apps to bundle
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.exe") set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.exe")
# directories to look for dependencies # directories to look for dependencies
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# install as bundle # install as bundle
set(INSTALL_BUNDLE "full") set(INSTALL_BUNDLE "full")
else() else()
message(FATAL_ERROR "No sensible install layout set.") message(FATAL_ERROR "No sensible install layout set.")
endif() endif()
################################ Included Libs ################################ ################################ Included Libs ################################

View File

@ -1,254 +1,254 @@
# MultiMC # MultiMC
Copyright 2012-2018 MultiMC Contributors Copyright 2012-2018 MultiMC Contributors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
# MinGW runtime (Windows) # MinGW runtime (Windows)
Copyright (c) 2012 MinGW.org project Copyright (c) 2012 MinGW.org project
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions: Software is furnished to do so, subject to the following conditions:
The above copyright notice, this permission notice and the below disclaimer The above copyright notice, this permission notice and the below disclaimer
shall be included in all copies or substantial portions of the Software. shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
# Qt 5 # Qt 5
Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
Contact: http://www.qt-project.org/legal Contact: http://www.qt-project.org/legal
Licensed under LGPL v2.1 Licensed under LGPL v2.1
# libnbt++ # libnbt++
libnbt++ - A library for the Minecraft Named Binary Tag format. libnbt++ - A library for the Minecraft Named Binary Tag format.
Copyright (C) 2013, 2015 ljfa-ag Copyright (C) 2013, 2015 ljfa-ag
libnbt++ is free software: you can redistribute it and/or modify libnbt++ is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
libnbt++ is distributed in the hope that it will be useful, libnbt++ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details. GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with libnbt++. If not, see <http://www.gnu.org/licenses/>. along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
# rainbow (KGuiAddons) # rainbow (KGuiAddons)
Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net> Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
Copyright (C) 2007 Olaf Schmidt <ojschmidt@kde.org> Copyright (C) 2007 Olaf Schmidt <ojschmidt@kde.org>
Copyright (C) 2007 Thomas Zander <zander@kde.org> Copyright (C) 2007 Thomas Zander <zander@kde.org>
Copyright (C) 2007 Zack Rusin <zack@kde.org> Copyright (C) 2007 Zack Rusin <zack@kde.org>
Copyright (C) 2015 Petr Mrazek <peterix@gmail.com> Copyright (C) 2015 Petr Mrazek <peterix@gmail.com>
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version. version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details. Library General Public License for more details.
You should have received a copy of the GNU Library General Public License You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. Boston, MA 02110-1301, USA.
# Hoedown # Hoedown
Copyright (c) 2008, Natacha Porté Copyright (c) 2008, Natacha Porté
Copyright (c) 2011, Vicent Martí Copyright (c) 2011, Vicent Martí
Copyright (c) 2014, Xavier Mendez, Devin Torres and the Hoedown authors Copyright (c) 2014, Xavier Mendez, Devin Torres and the Hoedown authors
Permission to use, copy, modify, and distribute this software for any Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies. copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# Batch icon set # Batch icon set
You are free to use Batch (the "icon set") or any part thereof (the "icons") You are free to use Batch (the "icon set") or any part thereof (the "icons")
in any personal, open-source or commercial work without obligation of payment in any personal, open-source or commercial work without obligation of payment
(monetary or otherwise) or attribution. Do not sell the icon set, host (monetary or otherwise) or attribution. Do not sell the icon set, host
the icon set or rent the icon set (either in existing or modified form). the icon set or rent the icon set (either in existing or modified form).
While attribution is optional, it is always appreciated. While attribution is optional, it is always appreciated.
Intellectual property rights are not transferred with the download of the icons. Intellectual property rights are not transferred with the download of the icons.
EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL ADAM WHITCROFT EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL ADAM WHITCROFT
BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL,
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
# Material Design Icons # Material Design Icons
Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/), Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/),
with Reserved Font Name Material Design Icons. with Reserved Font Name Material Design Icons.
Copyright (c) 2014, Google (http://www.google.com/design/) Copyright (c) 2014, Google (http://www.google.com/design/)
uses the license at https://github.com/google/material-design-icons/blob/master/LICENSE uses the license at https://github.com/google/material-design-icons/blob/master/LICENSE
This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL http://scripts.sil.org/OFL
# Pack200 # Pack200
The GNU General Public License (GPL) The GNU General Public License (GPL)
Version 2, June 1991 Version 2, June 1991
+ "CLASSPATH" EXCEPTION TO THE GPL + "CLASSPATH" EXCEPTION TO THE GPL
Certain source files distributed by Oracle America and/or its affiliates are Certain source files distributed by Oracle America and/or its affiliates are
subject to the following clarification and special exception to the GPL, but subject to the following clarification and special exception to the GPL, but
only where Oracle has expressly included in the particular source file's header only where Oracle has expressly included in the particular source file's header
the words "Oracle designates this particular file as subject to the "Classpath" the words "Oracle designates this particular file as subject to the "Classpath"
exception as provided by Oracle in the LICENSE file that accompanied this code." exception as provided by Oracle in the LICENSE file that accompanied this code."
Linking this library statically or dynamically with other modules is making Linking this library statically or dynamically with other modules is making
a combined work based on this library. Thus, the terms and conditions of a combined work based on this library. Thus, the terms and conditions of
the GNU General Public License cover the whole combination. the GNU General Public License cover the whole combination.
As a special exception, the copyright holders of this library give you As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent modules, executable, regardless of the license terms of these independent modules,
and to copy and distribute the resulting executable under terms of your and to copy and distribute the resulting executable under terms of your
choice, provided that you also meet, for each linked independent module, choice, provided that you also meet, for each linked independent module,
the terms and conditions of the license of that module. An independent the terms and conditions of the license of that module. An independent
module is a module which is not derived from or based on this library. If module is a module which is not derived from or based on this library. If
you modify this library, you may extend this exception to your version of you modify this library, you may extend this exception to your version of
the library, but you are not obligated to do so. If you do not wish to do the library, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version. so, delete this exception statement from your version.
# Quazip # Quazip
Copyright (C) 2005-2011 Sergey A. Tachenov Copyright (C) 2005-2011 Sergey A. Tachenov
This program is free software; you can redistribute it and/or modify it This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at the Free Software Foundation; either version 2 of the License, or (at
your option) any later version. your option) any later version.
This program is distributed in the hope that it will be useful, but This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
General Public License for more details. General Public License for more details.
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software Foundation, along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
See COPYING file for the full LGPL text. See COPYING file for the full LGPL text.
Original ZIP package is copyrighted by Gilles Vollant, see Original ZIP package is copyrighted by Gilles Vollant, see
quazip/(un)zip.h files for details, basically it's zlib license. quazip/(un)zip.h files for details, basically it's zlib license.
# xz-minidec # xz-minidec
XZ decompressor XZ decompressor
Authors: Lasse Collin <lasse.collin@tukaani.org> Authors: Lasse Collin <lasse.collin@tukaani.org>
Igor Pavlov <http://7-zip.org/> Igor Pavlov <http://7-zip.org/>
This file has been put into the public domain. This file has been put into the public domain.
You can do whatever you want with this file. You can do whatever you want with this file.
# ColumnResizer # ColumnResizer
Copyright (c) 2011-2016 Aurélien Gâteau and contributors. Copyright (c) 2011-2016 Aurélien Gâteau and contributors.
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the modification, are permitted (subject to the limitations in the
disclaimer below) provided that the following conditions are met: disclaimer below) provided that the following conditions are met:
* Redistributions of source code must retain the above copyright * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the documentation and/or other materials provided with the
distribution. distribution.
* The name of the contributors may not be used to endorse or * The name of the contributors may not be used to endorse or
promote products derived from this software without specific prior promote products derived from this software without specific prior
written permission. written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# lionshead # lionshead
Code has been taken from https://github.com/natefoo/lionshead and loosely Code has been taken from https://github.com/natefoo/lionshead and loosely
translated to C++ laced with Qt. translated to C++ laced with Qt.
MIT License MIT License
Copyright (c) 2017 Nate Coraor Copyright (c) 2017 Nate Coraor
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

View File

@ -1,17 +1,17 @@
project(MultiMC_gui LANGUAGES CXX) project(MultiMC_gui LANGUAGES CXX)
set(GUI_SOURCES set(GUI_SOURCES
DesktopServices.h DesktopServices.h
DesktopServices.cpp DesktopServices.cpp
# Icons # Icons
icons/MMCIcon.h icons/MMCIcon.h
icons/MMCIcon.cpp icons/MMCIcon.cpp
icons/IconList.h icons/IconList.h
icons/IconList.cpp icons/IconList.cpp
SkinUtils.cpp SkinUtils.cpp
SkinUtils.h SkinUtils.h
) )
################################ COMPILE ################################ ################################ COMPILE ################################
@ -28,7 +28,7 @@ target_include_directories(MultiMC_gui PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${C
# Install it # Install it
install( install(
TARGETS MultiMC_gui TARGETS MultiMC_gui
RUNTIME DESTINATION ${LIBRARY_DEST_DIR} RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
) )

View File

@ -17,132 +17,132 @@
template <typename T> template <typename T>
bool IndirectOpen(T callable, qint64 *pid_forked = nullptr) bool IndirectOpen(T callable, qint64 *pid_forked = nullptr)
{ {
auto pid = fork(); auto pid = fork();
if(pid_forked) if(pid_forked)
{ {
if(pid > 0) if(pid > 0)
*pid_forked = pid; *pid_forked = pid;
else else
*pid_forked = 0; *pid_forked = 0;
} }
if(pid == -1) if(pid == -1)
{ {
qWarning() << "IndirectOpen failed to fork: " << errno; qWarning() << "IndirectOpen failed to fork: " << errno;
return false; return false;
} }
// child - do the stuff // child - do the stuff
if(pid == 0) if(pid == 0)
{ {
// unset all this garbage so it doesn't get passed to the child process // unset all this garbage so it doesn't get passed to the child process
qunsetenv("LD_PRELOAD"); qunsetenv("LD_PRELOAD");
qunsetenv("LD_LIBRARY_PATH"); qunsetenv("LD_LIBRARY_PATH");
qunsetenv("LD_DEBUG"); qunsetenv("LD_DEBUG");
qunsetenv("QT_PLUGIN_PATH"); qunsetenv("QT_PLUGIN_PATH");
qunsetenv("QT_FONTPATH"); qunsetenv("QT_FONTPATH");
// open the URL // open the URL
auto status = callable(); auto status = callable();
// detach from the parent process group. // detach from the parent process group.
setsid(); setsid();
// die. now. do not clean up anything, it would just hang forever. // die. now. do not clean up anything, it would just hang forever.
_exit(status ? 0 : 1); _exit(status ? 0 : 1);
} }
else else
{ {
//parent - assume it worked. //parent - assume it worked.
int status; int status;
while (waitpid(pid, &status, 0)) while (waitpid(pid, &status, 0))
{ {
if(WIFEXITED(status)) if(WIFEXITED(status))
{ {
return WEXITSTATUS(status) == 0; return WEXITSTATUS(status) == 0;
} }
if(WIFSIGNALED(status)) if(WIFSIGNALED(status))
{ {
return false; return false;
} }
} }
return true; return true;
} }
} }
#endif #endif
namespace DesktopServices { namespace DesktopServices {
bool openDirectory(const QString &path, bool ensureExists) bool openDirectory(const QString &path, bool ensureExists)
{ {
qDebug() << "Opening directory" << path; qDebug() << "Opening directory" << path;
QDir parentPath; QDir parentPath;
QDir dir(path); QDir dir(path);
if (!dir.exists()) if (!dir.exists())
{ {
parentPath.mkpath(dir.absolutePath()); parentPath.mkpath(dir.absolutePath());
} }
auto f = [&]() auto f = [&]()
{ {
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
}; };
#if defined(Q_OS_LINUX) #if defined(Q_OS_LINUX)
return IndirectOpen(f); return IndirectOpen(f);
#else #else
return f(); return f();
#endif #endif
} }
bool openFile(const QString &path) bool openFile(const QString &path)
{ {
qDebug() << "Opening file" << path; qDebug() << "Opening file" << path;
auto f = [&]() auto f = [&]()
{ {
return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}; };
#if defined(Q_OS_LINUX) #if defined(Q_OS_LINUX)
return IndirectOpen(f); return IndirectOpen(f);
#else #else
return f(); return f();
#endif #endif
} }
bool openFile(const QString &application, const QString &path, const QString &workingDirectory, qint64 *pid) bool openFile(const QString &application, const QString &path, const QString &workingDirectory, qint64 *pid)
{ {
qDebug() << "Opening file" << path << "using" << application; qDebug() << "Opening file" << path << "using" << application;
#if defined(Q_OS_LINUX) #if defined(Q_OS_LINUX)
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
return IndirectOpen([&]() return IndirectOpen([&]()
{ {
return QProcess::startDetached(application, QStringList() << path, workingDirectory); return QProcess::startDetached(application, QStringList() << path, workingDirectory);
}, pid); }, pid);
#else #else
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid); return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
#endif #endif
} }
bool run(const QString &application, const QStringList &args, const QString &workingDirectory, qint64 *pid) bool run(const QString &application, const QStringList &args, const QString &workingDirectory, qint64 *pid)
{ {
qDebug() << "Running" << application << "with args" << args.join(' '); qDebug() << "Running" << application << "with args" << args.join(' ');
#if defined(Q_OS_LINUX) #if defined(Q_OS_LINUX)
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
return IndirectOpen([&]() return IndirectOpen([&]()
{ {
return QProcess::startDetached(application, args, workingDirectory); return QProcess::startDetached(application, args, workingDirectory);
}, pid); }, pid);
#else #else
return QProcess::startDetached(application, args, workingDirectory, pid); return QProcess::startDetached(application, args, workingDirectory, pid);
#endif #endif
} }
bool openUrl(const QUrl &url) bool openUrl(const QUrl &url)
{ {
qDebug() << "Opening URL" << url.toString(); qDebug() << "Opening URL" << url.toString();
auto f = [&]() auto f = [&]()
{ {
return QDesktopServices::openUrl(url); return QDesktopServices::openUrl(url);
}; };
#if defined(Q_OS_LINUX) #if defined(Q_OS_LINUX)
return IndirectOpen(f); return IndirectOpen(f);
#else #else
return f(); return f();
#endif #endif
} }

View File

@ -10,28 +10,28 @@
*/ */
namespace DesktopServices namespace DesktopServices
{ {
/** /**
* Open a file in whatever application is applicable * Open a file in whatever application is applicable
*/ */
MULTIMC_GUI_EXPORT bool openFile(const QString &path); MULTIMC_GUI_EXPORT bool openFile(const QString &path);
/** /**
* Open a file in the specified application * Open a file in the specified application
*/ */
MULTIMC_GUI_EXPORT bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0); MULTIMC_GUI_EXPORT bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0);
/** /**
* Run an application * Run an application
*/ */
MULTIMC_GUI_EXPORT bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0); MULTIMC_GUI_EXPORT bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0);
/** /**
* Open a directory * Open a directory
*/ */
MULTIMC_GUI_EXPORT bool openDirectory(const QString &path, bool ensureExists = false); MULTIMC_GUI_EXPORT bool openDirectory(const QString &path, bool ensureExists = false);
/** /**
* Open the URL, most likely in a browser. Maybe. * Open the URL, most likely in a browser. Maybe.
*/ */
MULTIMC_GUI_EXPORT bool openUrl(const QUrl &url); MULTIMC_GUI_EXPORT bool openUrl(const QUrl &url);
} }

View File

@ -29,19 +29,19 @@ namespace SkinUtils
*/ */
QPixmap getFaceFromCache(QString username, int height, int width) QPixmap getFaceFromCache(QString username, int height, int width)
{ {
QFile fskin(ENV.metacache() QFile fskin(ENV.metacache()
->resolveEntry("skins", username + ".png") ->resolveEntry("skins", username + ".png")
->getFullPath()); ->getFullPath());
if (fskin.exists()) if (fskin.exists())
{ {
QPixmap skin(fskin.fileName()); QPixmap skin(fskin.fileName());
if(!skin.isNull()) if(!skin.isNull())
{ {
return skin.copy(8, 8, 8, 8).scaled(height, width, Qt::KeepAspectRatio); return skin.copy(8, 8, 8, 8).scaled(height, width, Qt::KeepAspectRatio);
} }
} }
return QPixmap(); return QPixmap();
} }
} }

View File

@ -27,394 +27,394 @@
IconList::IconList(const QStringList &builtinPaths, QString path, QObject *parent) : QAbstractListModel(parent) IconList::IconList(const QStringList &builtinPaths, QString path, QObject *parent) : QAbstractListModel(parent)
{ {
QSet<QString> builtinNames; QSet<QString> builtinNames;
// add builtin icons // add builtin icons
for(auto & builtinPath: builtinPaths) for(auto & builtinPath: builtinPaths)
{ {
QDir instance_icons(builtinPath); QDir instance_icons(builtinPath);
auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name); auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name);
for (auto file_info : file_info_list) for (auto file_info : file_info_list)
{ {
builtinNames.insert(file_info.baseName()); builtinNames.insert(file_info.baseName());
} }
} }
for(auto & builtinName : builtinNames) for(auto & builtinName : builtinNames)
{ {
addThemeIcon(builtinName); addThemeIcon(builtinName);
} }
m_watcher.reset(new QFileSystemWatcher()); m_watcher.reset(new QFileSystemWatcher());
is_watching = false; is_watching = false;
connect(m_watcher.get(), SIGNAL(directoryChanged(QString)), connect(m_watcher.get(), SIGNAL(directoryChanged(QString)),
SLOT(directoryChanged(QString))); SLOT(directoryChanged(QString)));
connect(m_watcher.get(), SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString))); connect(m_watcher.get(), SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString)));
directoryChanged(path); directoryChanged(path);
} }
void IconList::directoryChanged(const QString &path) void IconList::directoryChanged(const QString &path)
{ {
QDir new_dir (path); QDir new_dir (path);
if(m_dir.absolutePath() != new_dir.absolutePath()) if(m_dir.absolutePath() != new_dir.absolutePath())
{ {
m_dir.setPath(path); m_dir.setPath(path);
m_dir.refresh(); m_dir.refresh();
if(is_watching) if(is_watching)
stopWatching(); stopWatching();
startWatching(); startWatching();
} }
if(!m_dir.exists()) if(!m_dir.exists())
if(!FS::ensureFolderPathExists(m_dir.absolutePath())) if(!FS::ensureFolderPathExists(m_dir.absolutePath()))
return; return;
m_dir.refresh(); m_dir.refresh();
auto new_list = m_dir.entryList(QDir::Files, QDir::Name); auto new_list = m_dir.entryList(QDir::Files, QDir::Name);
for (auto it = new_list.begin(); it != new_list.end(); it++) for (auto it = new_list.begin(); it != new_list.end(); it++)
{ {
QString &foo = (*it); QString &foo = (*it);
foo = m_dir.filePath(foo); foo = m_dir.filePath(foo);
} }
auto new_set = new_list.toSet(); auto new_set = new_list.toSet();
QList<QString> current_list; QList<QString> current_list;
for (auto &it : icons) for (auto &it : icons)
{ {
if (!it.has(IconType::FileBased)) if (!it.has(IconType::FileBased))
continue; continue;
current_list.push_back(it.m_images[IconType::FileBased].filename); current_list.push_back(it.m_images[IconType::FileBased].filename);
} }
QSet<QString> current_set = current_list.toSet(); QSet<QString> current_set = current_list.toSet();
QSet<QString> to_remove = current_set; QSet<QString> to_remove = current_set;
to_remove -= new_set; to_remove -= new_set;
QSet<QString> to_add = new_set; QSet<QString> to_add = new_set;
to_add -= current_set; to_add -= current_set;
for (auto remove : to_remove) for (auto remove : to_remove)
{ {
qDebug() << "Removing " << remove; qDebug() << "Removing " << remove;
QFileInfo rmfile(remove); QFileInfo rmfile(remove);
QString key = rmfile.baseName(); QString key = rmfile.baseName();
int idx = getIconIndex(key); int idx = getIconIndex(key);
if (idx == -1) if (idx == -1)
continue; continue;
icons[idx].remove(IconType::FileBased); icons[idx].remove(IconType::FileBased);
if (icons[idx].type() == IconType::ToBeDeleted) if (icons[idx].type() == IconType::ToBeDeleted)
{ {
beginRemoveRows(QModelIndex(), idx, idx); beginRemoveRows(QModelIndex(), idx, idx);
icons.remove(idx); icons.remove(idx);
reindex(); reindex();
endRemoveRows(); endRemoveRows();
} }
else else
{ {
dataChanged(index(idx), index(idx)); dataChanged(index(idx), index(idx));
} }
m_watcher->removePath(remove); m_watcher->removePath(remove);
emit iconUpdated(key); emit iconUpdated(key);
} }
for (auto add : to_add) for (auto add : to_add)
{ {
qDebug() << "Adding " << add; qDebug() << "Adding " << add;
QFileInfo addfile(add); QFileInfo addfile(add);
QString key = addfile.baseName(); QString key = addfile.baseName();
if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased))
{ {
m_watcher->addPath(add); m_watcher->addPath(add);
emit iconUpdated(key); emit iconUpdated(key);
} }
} }
} }
void IconList::fileChanged(const QString &path) void IconList::fileChanged(const QString &path)
{ {
qDebug() << "Checking " << path; qDebug() << "Checking " << path;
QFileInfo checkfile(path); QFileInfo checkfile(path);
if (!checkfile.exists()) if (!checkfile.exists())
return; return;
QString key = checkfile.baseName(); QString key = checkfile.baseName();
int idx = getIconIndex(key); int idx = getIconIndex(key);
if (idx == -1) if (idx == -1)
return; return;
QIcon icon(path); QIcon icon(path);
if (!icon.availableSizes().size()) if (!icon.availableSizes().size())
return; return;
icons[idx].m_images[IconType::FileBased].icon = icon; icons[idx].m_images[IconType::FileBased].icon = icon;
dataChanged(index(idx), index(idx)); dataChanged(index(idx), index(idx));
emit iconUpdated(key); emit iconUpdated(key);
} }
void IconList::SettingChanged(const Setting &setting, QVariant value) void IconList::SettingChanged(const Setting &setting, QVariant value)
{ {
if(setting.id() != "IconsDir") if(setting.id() != "IconsDir")
return; return;
directoryChanged(value.toString()); directoryChanged(value.toString());
} }
void IconList::startWatching() void IconList::startWatching()
{ {
auto abs_path = m_dir.absolutePath(); auto abs_path = m_dir.absolutePath();
FS::ensureFolderPathExists(abs_path); FS::ensureFolderPathExists(abs_path);
is_watching = m_watcher->addPath(abs_path); is_watching = m_watcher->addPath(abs_path);
if (is_watching) if (is_watching)
{ {
qDebug() << "Started watching " << abs_path; qDebug() << "Started watching " << abs_path;
} }
else else
{ {
qDebug() << "Failed to start watching " << abs_path; qDebug() << "Failed to start watching " << abs_path;
} }
} }
void IconList::stopWatching() void IconList::stopWatching()
{ {
m_watcher->removePaths(m_watcher->files()); m_watcher->removePaths(m_watcher->files());
m_watcher->removePaths(m_watcher->directories()); m_watcher->removePaths(m_watcher->directories());
is_watching = false; is_watching = false;
} }
QStringList IconList::mimeTypes() const QStringList IconList::mimeTypes() const
{ {
QStringList types; QStringList types;
types << "text/uri-list"; types << "text/uri-list";
return types; return types;
} }
Qt::DropActions IconList::supportedDropActions() const Qt::DropActions IconList::supportedDropActions() const
{ {
return Qt::CopyAction; return Qt::CopyAction;
} }
bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
const QModelIndex &parent) const QModelIndex &parent)
{ {
if (action == Qt::IgnoreAction) if (action == Qt::IgnoreAction)
return true; return true;
// check if the action is supported // check if the action is supported
if (!data || !(action & supportedDropActions())) if (!data || !(action & supportedDropActions()))
return false; return false;
// files dropped from outside? // files dropped from outside?
if (data->hasUrls()) if (data->hasUrls())
{ {
auto urls = data->urls(); auto urls = data->urls();
QStringList iconFiles; QStringList iconFiles;
for (auto url : urls) for (auto url : urls)
{ {
// only local files may be dropped... // only local files may be dropped...
if (!url.isLocalFile()) if (!url.isLocalFile())
continue; continue;
iconFiles += url.toLocalFile(); iconFiles += url.toLocalFile();
} }
installIcons(iconFiles); installIcons(iconFiles);
return true; return true;
} }
return false; return false;
} }
Qt::ItemFlags IconList::flags(const QModelIndex &index) const Qt::ItemFlags IconList::flags(const QModelIndex &index) const
{ {
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
if (index.isValid()) if (index.isValid())
return Qt::ItemIsDropEnabled | defaultFlags; return Qt::ItemIsDropEnabled | defaultFlags;
else else
return Qt::ItemIsDropEnabled | defaultFlags; return Qt::ItemIsDropEnabled | defaultFlags;
} }
QVariant IconList::data(const QModelIndex &index, int role) const QVariant IconList::data(const QModelIndex &index, int role) const
{ {
if (!index.isValid()) if (!index.isValid())
return QVariant(); return QVariant();
int row = index.row(); int row = index.row();
if (row < 0 || row >= icons.size()) if (row < 0 || row >= icons.size())
return QVariant(); return QVariant();
switch (role) switch (role)
{ {
case Qt::DecorationRole: case Qt::DecorationRole:
return icons[row].icon(); return icons[row].icon();
case Qt::DisplayRole: case Qt::DisplayRole:
return icons[row].name(); return icons[row].name();
case Qt::UserRole: case Qt::UserRole:
return icons[row].m_key; return icons[row].m_key;
default: default:
return QVariant(); return QVariant();
} }
} }
int IconList::rowCount(const QModelIndex &parent) const int IconList::rowCount(const QModelIndex &parent) const
{ {
return icons.size(); return icons.size();
} }
void IconList::installIcons(const QStringList &iconFiles) void IconList::installIcons(const QStringList &iconFiles)
{ {
for (QString file : iconFiles) for (QString file : iconFiles)
{ {
QFileInfo fileinfo(file); QFileInfo fileinfo(file);
if (!fileinfo.isReadable() || !fileinfo.isFile()) if (!fileinfo.isReadable() || !fileinfo.isFile())
continue; continue;
QString target = FS::PathCombine(m_dir.dirName(), fileinfo.fileName()); QString target = FS::PathCombine(m_dir.dirName(), fileinfo.fileName());
QString suffix = fileinfo.suffix(); QString suffix = fileinfo.suffix();
if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg") if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg")
continue; continue;
if (!QFile::copy(file, target)) if (!QFile::copy(file, target))
continue; continue;
} }
} }
void IconList::installIcon(const QString &file, const QString &name) void IconList::installIcon(const QString &file, const QString &name)
{ {
QFileInfo fileinfo(file); QFileInfo fileinfo(file);
if(!fileinfo.isReadable() || !fileinfo.isFile()) if(!fileinfo.isReadable() || !fileinfo.isFile())
return; return;
QString target = FS::PathCombine(m_dir.dirName(), name); QString target = FS::PathCombine(m_dir.dirName(), name);
QFile::copy(file, target); QFile::copy(file, target);
} }
bool IconList::iconFileExists(const QString &key) const bool IconList::iconFileExists(const QString &key) const
{ {
auto iconEntry = icon(key); auto iconEntry = icon(key);
if(!iconEntry) if(!iconEntry)
{ {
return false; return false;
} }
return iconEntry->has(IconType::FileBased); return iconEntry->has(IconType::FileBased);
} }
const MMCIcon *IconList::icon(const QString &key) const const MMCIcon *IconList::icon(const QString &key) const
{ {
int iconIdx = getIconIndex(key); int iconIdx = getIconIndex(key);
if (iconIdx == -1) if (iconIdx == -1)
return nullptr; return nullptr;
return &icons[iconIdx]; return &icons[iconIdx];
} }
bool IconList::deleteIcon(const QString &key) bool IconList::deleteIcon(const QString &key)
{ {
int iconIdx = getIconIndex(key); int iconIdx = getIconIndex(key);
if (iconIdx == -1) if (iconIdx == -1)
return false; return false;
auto &iconEntry = icons[iconIdx]; auto &iconEntry = icons[iconIdx];
if (iconEntry.has(IconType::FileBased)) if (iconEntry.has(IconType::FileBased))
{ {
return QFile::remove(iconEntry.m_images[IconType::FileBased].filename); return QFile::remove(iconEntry.m_images[IconType::FileBased].filename);
} }
return false; return false;
} }
bool IconList::addThemeIcon(const QString& key) bool IconList::addThemeIcon(const QString& key)
{ {
auto iter = name_index.find(key); auto iter = name_index.find(key);
if (iter != name_index.end()) if (iter != name_index.end())
{ {
auto &oldOne = icons[*iter]; auto &oldOne = icons[*iter];
oldOne.replace(Builtin, key); oldOne.replace(Builtin, key);
dataChanged(index(*iter), index(*iter)); dataChanged(index(*iter), index(*iter));
return true; return true;
} }
else else
{ {
// add a new icon // add a new icon
beginInsertRows(QModelIndex(), icons.size(), icons.size()); beginInsertRows(QModelIndex(), icons.size(), icons.size());
{ {
MMCIcon mmc_icon; MMCIcon mmc_icon;
mmc_icon.m_name = key; mmc_icon.m_name = key;
mmc_icon.m_key = key; mmc_icon.m_key = key;
mmc_icon.replace(Builtin, key); mmc_icon.replace(Builtin, key);
icons.push_back(mmc_icon); icons.push_back(mmc_icon);
name_index[key] = icons.size() - 1; name_index[key] = icons.size() - 1;
} }
endInsertRows(); endInsertRows();
return true; return true;
} }
} }
bool IconList::addIcon(const QString &key, const QString &name, const QString &path, const IconType type) bool IconList::addIcon(const QString &key, const QString &name, const QString &path, const IconType type)
{ {
// replace the icon even? is the input valid? // replace the icon even? is the input valid?
QIcon icon(path); QIcon icon(path);
if (icon.isNull()) if (icon.isNull())
return false; return false;
auto iter = name_index.find(key); auto iter = name_index.find(key);
if (iter != name_index.end()) if (iter != name_index.end())
{ {
auto &oldOne = icons[*iter]; auto &oldOne = icons[*iter];
oldOne.replace(type, icon, path); oldOne.replace(type, icon, path);
dataChanged(index(*iter), index(*iter)); dataChanged(index(*iter), index(*iter));
return true; return true;
} }
else else
{ {
// add a new icon // add a new icon
beginInsertRows(QModelIndex(), icons.size(), icons.size()); beginInsertRows(QModelIndex(), icons.size(), icons.size());
{ {
MMCIcon mmc_icon; MMCIcon mmc_icon;
mmc_icon.m_name = name; mmc_icon.m_name = name;
mmc_icon.m_key = key; mmc_icon.m_key = key;
mmc_icon.replace(type, icon, path); mmc_icon.replace(type, icon, path);
icons.push_back(mmc_icon); icons.push_back(mmc_icon);
name_index[key] = icons.size() - 1; name_index[key] = icons.size() - 1;
} }
endInsertRows(); endInsertRows();
return true; return true;
} }
} }
void IconList::saveIcon(const QString &key, const QString &path, const char * format) const void IconList::saveIcon(const QString &key, const QString &path, const char * format) const
{ {
auto icon = getIcon(key); auto icon = getIcon(key);
auto pixmap = icon.pixmap(128, 128); auto pixmap = icon.pixmap(128, 128);
pixmap.save(path, format); pixmap.save(path, format);
} }
void IconList::reindex() void IconList::reindex()
{ {
name_index.clear(); name_index.clear();
int i = 0; int i = 0;
for (auto &iter : icons) for (auto &iter : icons)
{ {
name_index[iter.m_key] = i; name_index[iter.m_key] = i;
i++; i++;
} }
} }
QIcon IconList::getIcon(const QString &key) const QIcon IconList::getIcon(const QString &key) const
{ {
int icon_index = getIconIndex(key); int icon_index = getIconIndex(key);
if (icon_index != -1) if (icon_index != -1)
return icons[icon_index].icon(); return icons[icon_index].icon();
// Fallback for icons that don't exist. // Fallback for icons that don't exist.
icon_index = getIconIndex("infinity"); icon_index = getIconIndex("infinity");
if (icon_index != -1) if (icon_index != -1)
return icons[icon_index].icon(); return icons[icon_index].icon();
return QIcon(); return QIcon();
} }
int IconList::getIconIndex(const QString &key) const int IconList::getIconIndex(const QString &key) const
{ {
auto iter = name_index.find(key == "default" ? "infinity" : key); auto iter = name_index.find(key == "default" ? "infinity" : key);
if (iter != name_index.end()) if (iter != name_index.end())
return *iter; return *iter;
return -1; return -1;
} }
QString IconList::getDirectory() const QString IconList::getDirectory() const
{ {
return m_dir.absolutePath(); return m_dir.absolutePath();
} }
//#include "IconList.moc" //#include "IconList.moc"

View File

@ -32,57 +32,57 @@ class QFileSystemWatcher;
class MULTIMC_GUI_EXPORT IconList : public QAbstractListModel, public IIconList class MULTIMC_GUI_EXPORT IconList : public QAbstractListModel, public IIconList
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit IconList(const QStringList &builtinPaths, QString path, QObject *parent = 0); explicit IconList(const QStringList &builtinPaths, QString path, QObject *parent = 0);
virtual ~IconList() {}; virtual ~IconList() {};
QIcon getIcon(const QString &key) const; QIcon getIcon(const QString &key) const;
int getIconIndex(const QString &key) const; int getIconIndex(const QString &key) const;
QString getDirectory() const; QString getDirectory() const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
bool addThemeIcon(const QString &key); bool addThemeIcon(const QString &key);
bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) override; bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) override;
void saveIcon(const QString &key, const QString &path, const char * format) const override; void saveIcon(const QString &key, const QString &path, const char * format) const override;
bool deleteIcon(const QString &key) override; bool deleteIcon(const QString &key) override;
bool iconFileExists(const QString &key) const override; bool iconFileExists(const QString &key) const override;
virtual QStringList mimeTypes() const override; virtual QStringList mimeTypes() const override;
virtual Qt::DropActions supportedDropActions() const override; virtual Qt::DropActions supportedDropActions() const override;
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
virtual Qt::ItemFlags flags(const QModelIndex &index) const override; virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
void installIcons(const QStringList &iconFiles) override; void installIcons(const QStringList &iconFiles) override;
void installIcon(const QString &file, const QString &name) override; void installIcon(const QString &file, const QString &name) override;
const MMCIcon * icon(const QString &key) const; const MMCIcon * icon(const QString &key) const;
void startWatching(); void startWatching();
void stopWatching(); void stopWatching();
signals: signals:
void iconUpdated(QString key); void iconUpdated(QString key);
private: private:
// hide copy constructor // hide copy constructor
IconList(const IconList &) = delete; IconList(const IconList &) = delete;
// hide assign op // hide assign op
IconList &operator=(const IconList &) = delete; IconList &operator=(const IconList &) = delete;
void reindex(); void reindex();
public slots: public slots:
void directoryChanged(const QString &path); void directoryChanged(const QString &path);
protected slots: protected slots:
void fileChanged(const QString &path); void fileChanged(const QString &path);
void SettingChanged(const Setting & setting, QVariant value); void SettingChanged(const Setting & setting, QVariant value);
private: private:
std::shared_ptr<QFileSystemWatcher> m_watcher; std::shared_ptr<QFileSystemWatcher> m_watcher;
bool is_watching; bool is_watching;
QMap<QString, int> name_index; QMap<QString, int> name_index;
QVector<MMCIcon> icons; QVector<MMCIcon> icons;
QDir m_dir; QDir m_dir;
}; };

View File

@ -19,86 +19,86 @@
IconType operator--(IconType &t, int) IconType operator--(IconType &t, int)
{ {
IconType temp = t; IconType temp = t;
switch (t) switch (t)
{ {
case IconType::Builtin: case IconType::Builtin:
t = IconType::ToBeDeleted; t = IconType::ToBeDeleted;
break; break;
case IconType::Transient: case IconType::Transient:
t = IconType::Builtin; t = IconType::Builtin;
break; break;
case IconType::FileBased: case IconType::FileBased:
t = IconType::Transient; t = IconType::Transient;
break; break;
default: default:
{ {
} }
} }
return temp; return temp;
} }
IconType MMCIcon::type() const IconType MMCIcon::type() const
{ {
return m_current_type; return m_current_type;
} }
QString MMCIcon::name() const QString MMCIcon::name() const
{ {
if (m_name.size()) if (m_name.size())
return m_name; return m_name;
return m_key; return m_key;
} }
bool MMCIcon::has(IconType _type) const bool MMCIcon::has(IconType _type) const
{ {
return m_images[_type].present(); return m_images[_type].present();
} }
QIcon MMCIcon::icon() const QIcon MMCIcon::icon() const
{ {
if (m_current_type == IconType::ToBeDeleted) if (m_current_type == IconType::ToBeDeleted)
return QIcon(); return QIcon();
auto & icon = m_images[m_current_type].icon; auto & icon = m_images[m_current_type].icon;
if(!icon.isNull()) if(!icon.isNull())
return icon; return icon;
// FIXME: inject this. // FIXME: inject this.
return XdgIcon::fromTheme(m_images[m_current_type].key); return XdgIcon::fromTheme(m_images[m_current_type].key);
} }
void MMCIcon::remove(IconType rm_type) void MMCIcon::remove(IconType rm_type)
{ {
m_images[rm_type].filename = QString(); m_images[rm_type].filename = QString();
m_images[rm_type].icon = QIcon(); m_images[rm_type].icon = QIcon();
for (auto iter = rm_type; iter != IconType::ToBeDeleted; iter--) for (auto iter = rm_type; iter != IconType::ToBeDeleted; iter--)
{ {
if (m_images[iter].present()) if (m_images[iter].present())
{ {
m_current_type = iter; m_current_type = iter;
return; return;
} }
} }
m_current_type = IconType::ToBeDeleted; m_current_type = IconType::ToBeDeleted;
} }
void MMCIcon::replace(IconType new_type, QIcon icon, QString path) void MMCIcon::replace(IconType new_type, QIcon icon, QString path)
{ {
if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted) if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted)
{ {
m_current_type = new_type; m_current_type = new_type;
} }
m_images[new_type].icon = icon; m_images[new_type].icon = icon;
m_images[new_type].filename = path; m_images[new_type].filename = path;
m_images[new_type].key = QString(); m_images[new_type].key = QString();
} }
void MMCIcon::replace(IconType new_type, const QString& key) void MMCIcon::replace(IconType new_type, const QString& key)
{ {
if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted) if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted)
{ {
m_current_type = new_type; m_current_type = new_type;
} }
m_images[new_type].icon = QIcon(); m_images[new_type].icon = QIcon();
m_images[new_type].filename = QString(); m_images[new_type].filename = QString();
m_images[new_type].key = key; m_images[new_type].key = key;
} }

View File

@ -23,27 +23,27 @@
struct MULTIMC_GUI_EXPORT MMCImage struct MULTIMC_GUI_EXPORT MMCImage
{ {
QIcon icon; QIcon icon;
QString key; QString key;
QString filename; QString filename;
bool present() const bool present() const
{ {
return !icon.isNull() || !key.isEmpty(); return !icon.isNull() || !key.isEmpty();
} }
}; };
struct MULTIMC_GUI_EXPORT MMCIcon struct MULTIMC_GUI_EXPORT MMCIcon
{ {
QString m_key; QString m_key;
QString m_name; QString m_name;
MMCImage m_images[ICONS_TOTAL]; MMCImage m_images[ICONS_TOTAL];
IconType m_current_type = ToBeDeleted; IconType m_current_type = ToBeDeleted;
IconType type() const; IconType type() const;
QString name() const; QString name() const;
bool has(IconType _type) const; bool has(IconType _type) const;
QIcon icon() const; QIcon icon() const;
void remove(IconType rm_type); void remove(IconType rm_type);
void replace(IconType new_type, QIcon icon, QString path = QString()); void replace(IconType new_type, QIcon icon, QString path = QString());
void replace(IconType new_type, const QString &key); void replace(IconType new_type, const QString &key);
}; };

View File

@ -25,37 +25,37 @@ BaseInstaller::BaseInstaller()
bool BaseInstaller::isApplied(MinecraftInstance *on) bool BaseInstaller::isApplied(MinecraftInstance *on)
{ {
return QFile::exists(filename(on->instanceRoot())); return QFile::exists(filename(on->instanceRoot()));
} }
bool BaseInstaller::add(MinecraftInstance *to) bool BaseInstaller::add(MinecraftInstance *to)
{ {
if (!patchesDir(to->instanceRoot()).exists()) if (!patchesDir(to->instanceRoot()).exists())
{ {
QDir(to->instanceRoot()).mkdir("patches"); QDir(to->instanceRoot()).mkdir("patches");
} }
if (isApplied(to)) if (isApplied(to))
{ {
if (!remove(to)) if (!remove(to))
{ {
return false; return false;
} }
} }
return true; return true;
} }
bool BaseInstaller::remove(MinecraftInstance *from) bool BaseInstaller::remove(MinecraftInstance *from)
{ {
return QFile::remove(filename(from->instanceRoot())); return QFile::remove(filename(from->instanceRoot()));
} }
QString BaseInstaller::filename(const QString &root) const QString BaseInstaller::filename(const QString &root) const
{ {
return patchesDir(root).absoluteFilePath(id() + ".json"); return patchesDir(root).absoluteFilePath(id() + ".json");
} }
QDir BaseInstaller::patchesDir(const QString &root) const QDir BaseInstaller::patchesDir(const QString &root) const
{ {
return QDir(root + "/patches/"); return QDir(root + "/patches/");
} }

View File

@ -30,17 +30,17 @@ typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
class MULTIMC_LOGIC_EXPORT BaseInstaller class MULTIMC_LOGIC_EXPORT BaseInstaller
{ {
public: public:
BaseInstaller(); BaseInstaller();
virtual ~BaseInstaller(){}; virtual ~BaseInstaller(){};
bool isApplied(MinecraftInstance *on); bool isApplied(MinecraftInstance *on);
virtual bool add(MinecraftInstance *to); virtual bool add(MinecraftInstance *to);
virtual bool remove(MinecraftInstance *from); virtual bool remove(MinecraftInstance *from);
virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersionPtr version, QObject *parent) = 0; virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersionPtr version, QObject *parent) = 0;
protected: protected:
virtual QString id() const = 0; virtual QString id() const = 0;
QString filename(const QString &root) const; QString filename(const QString &root) const;
QDir patchesDir(const QString &root) const; QDir patchesDir(const QString &root) const;
}; };

View File

@ -27,281 +27,281 @@
#include "Commandline.h" #include "Commandline.h"
BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
: QObject() : QObject()
{ {
m_settings = settings; m_settings = settings;
m_rootDir = rootDir; m_rootDir = rootDir;
m_settings->registerSetting("name", "Unnamed Instance"); m_settings->registerSetting("name", "Unnamed Instance");
m_settings->registerSetting("iconKey", "default"); m_settings->registerSetting("iconKey", "default");
m_settings->registerSetting("notes", ""); m_settings->registerSetting("notes", "");
m_settings->registerSetting("lastLaunchTime", 0); m_settings->registerSetting("lastLaunchTime", 0);
m_settings->registerSetting("totalTimePlayed", 0); m_settings->registerSetting("totalTimePlayed", 0);
// Custom Commands // Custom Commands
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false); auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting); m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting);
m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting); m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting);
m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting); m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting);
// Console // Console
auto consoleSetting = m_settings->registerSetting("OverrideConsole", false); auto consoleSetting = m_settings->registerSetting("OverrideConsole", false);
m_settings->registerOverride(globalSettings->getSetting("ShowConsole"), consoleSetting); m_settings->registerOverride(globalSettings->getSetting("ShowConsole"), consoleSetting);
m_settings->registerOverride(globalSettings->getSetting("AutoCloseConsole"), consoleSetting); m_settings->registerOverride(globalSettings->getSetting("AutoCloseConsole"), consoleSetting);
m_settings->registerOverride(globalSettings->getSetting("ShowConsoleOnError"), consoleSetting); m_settings->registerOverride(globalSettings->getSetting("ShowConsoleOnError"), consoleSetting);
m_settings->registerOverride(globalSettings->getSetting("LogPrePostOutput"), consoleSetting); m_settings->registerOverride(globalSettings->getSetting("LogPrePostOutput"), consoleSetting);
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr); m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr);
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr); m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr);
} }
QString BaseInstance::getPreLaunchCommand() QString BaseInstance::getPreLaunchCommand()
{ {
return settings()->get("PreLaunchCommand").toString(); return settings()->get("PreLaunchCommand").toString();
} }
QString BaseInstance::getWrapperCommand() QString BaseInstance::getWrapperCommand()
{ {
return settings()->get("WrapperCommand").toString(); return settings()->get("WrapperCommand").toString();
} }
QString BaseInstance::getPostExitCommand() QString BaseInstance::getPostExitCommand()
{ {
return settings()->get("PostExitCommand").toString(); return settings()->get("PostExitCommand").toString();
} }
int BaseInstance::getConsoleMaxLines() const int BaseInstance::getConsoleMaxLines() const
{ {
auto lineSetting = settings()->getSetting("ConsoleMaxLines"); auto lineSetting = settings()->getSetting("ConsoleMaxLines");
bool conversionOk = false; bool conversionOk = false;
int maxLines = lineSetting->get().toInt(&conversionOk); int maxLines = lineSetting->get().toInt(&conversionOk);
if(!conversionOk) if(!conversionOk)
{ {
maxLines = lineSetting->defValue().toInt(); maxLines = lineSetting->defValue().toInt();
qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines; qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
} }
return maxLines; return maxLines;
} }
bool BaseInstance::shouldStopOnConsoleOverflow() const bool BaseInstance::shouldStopOnConsoleOverflow() const
{ {
return settings()->get("ConsoleOverflowStop").toBool(); return settings()->get("ConsoleOverflowStop").toBool();
} }
void BaseInstance::iconUpdated(QString key) void BaseInstance::iconUpdated(QString key)
{ {
if(iconKey() == key) if(iconKey() == key)
{ {
emit propertiesChanged(this); emit propertiesChanged(this);
} }
} }
void BaseInstance::invalidate() void BaseInstance::invalidate()
{ {
changeStatus(Status::Gone); changeStatus(Status::Gone);
qDebug() << "Instance" << id() << "has been invalidated."; qDebug() << "Instance" << id() << "has been invalidated.";
} }
void BaseInstance::nuke() void BaseInstance::nuke()
{ {
changeStatus(Status::Gone); changeStatus(Status::Gone);
qDebug() << "Instance" << id() << "has been deleted by MultiMC."; qDebug() << "Instance" << id() << "has been deleted by MultiMC.";
FS::deletePath(instanceRoot()); FS::deletePath(instanceRoot());
} }
void BaseInstance::changeStatus(BaseInstance::Status newStatus) void BaseInstance::changeStatus(BaseInstance::Status newStatus)
{ {
Status status = currentStatus(); Status status = currentStatus();
if(status != newStatus) if(status != newStatus)
{ {
m_status = newStatus; m_status = newStatus;
emit statusChanged(status, newStatus); emit statusChanged(status, newStatus);
} }
} }
BaseInstance::Status BaseInstance::currentStatus() const BaseInstance::Status BaseInstance::currentStatus() const
{ {
return m_status; return m_status;
} }
QString BaseInstance::id() const QString BaseInstance::id() const
{ {
return QFileInfo(instanceRoot()).fileName(); return QFileInfo(instanceRoot()).fileName();
} }
bool BaseInstance::isRunning() const bool BaseInstance::isRunning() const
{ {
return m_isRunning; return m_isRunning;
} }
void BaseInstance::setRunning(bool running) void BaseInstance::setRunning(bool running)
{ {
if(running == m_isRunning) if(running == m_isRunning)
return; return;
m_isRunning = running; m_isRunning = running;
if(running) if(running)
{ {
m_timeStarted = QDateTime::currentDateTime(); m_timeStarted = QDateTime::currentDateTime();
} }
else else
{ {
qint64 current = settings()->get("totalTimePlayed").toLongLong(); qint64 current = settings()->get("totalTimePlayed").toLongLong();
QDateTime timeEnded = QDateTime::currentDateTime(); QDateTime timeEnded = QDateTime::currentDateTime();
settings()->set("totalTimePlayed", current + m_timeStarted.secsTo(timeEnded)); settings()->set("totalTimePlayed", current + m_timeStarted.secsTo(timeEnded));
emit propertiesChanged(this); emit propertiesChanged(this);
} }
emit runningStatusChanged(running); emit runningStatusChanged(running);
} }
int64_t BaseInstance::totalTimePlayed() const int64_t BaseInstance::totalTimePlayed() const
{ {
qint64 current = settings()->get("totalTimePlayed").toLongLong(); qint64 current = settings()->get("totalTimePlayed").toLongLong();
if(m_isRunning) if(m_isRunning)
{ {
QDateTime timeNow = QDateTime::currentDateTime(); QDateTime timeNow = QDateTime::currentDateTime();
return current + m_timeStarted.secsTo(timeNow); return current + m_timeStarted.secsTo(timeNow);
} }
return current; return current;
} }
void BaseInstance::resetTimePlayed() void BaseInstance::resetTimePlayed()
{ {
settings()->reset("totalTimePlayed"); settings()->reset("totalTimePlayed");
} }
QString BaseInstance::instanceType() const QString BaseInstance::instanceType() const
{ {
return m_settings->get("InstanceType").toString(); return m_settings->get("InstanceType").toString();
} }
QString BaseInstance::instanceRoot() const QString BaseInstance::instanceRoot() const
{ {
return m_rootDir; return m_rootDir;
} }
InstancePtr BaseInstance::getSharedPtr() InstancePtr BaseInstance::getSharedPtr()
{ {
return shared_from_this(); return shared_from_this();
} }
SettingsObjectPtr BaseInstance::settings() const SettingsObjectPtr BaseInstance::settings() const
{ {
return m_settings; return m_settings;
} }
bool BaseInstance::canLaunch() const bool BaseInstance::canLaunch() const
{ {
return (!hasVersionBroken() && !isRunning()); return (!hasVersionBroken() && !isRunning());
} }
bool BaseInstance::reloadSettings() bool BaseInstance::reloadSettings()
{ {
return m_settings->reload(); return m_settings->reload();
} }
qint64 BaseInstance::lastLaunch() const qint64 BaseInstance::lastLaunch() const
{ {
return m_settings->get("lastLaunchTime").value<qint64>(); return m_settings->get("lastLaunchTime").value<qint64>();
} }
void BaseInstance::setLastLaunch(qint64 val) void BaseInstance::setLastLaunch(qint64 val)
{ {
//FIXME: if no change, do not set. setting involves saving a file. //FIXME: if no change, do not set. setting involves saving a file.
m_settings->set("lastLaunchTime", val); m_settings->set("lastLaunchTime", val);
emit propertiesChanged(this); emit propertiesChanged(this);
} }
void BaseInstance::setGroupInitial(QString val) void BaseInstance::setGroupInitial(QString val)
{ {
if(m_group == val) if(m_group == val)
{ {
return; return;
} }
m_group = val; m_group = val;
emit propertiesChanged(this); emit propertiesChanged(this);
} }
void BaseInstance::setGroupPost(QString val) void BaseInstance::setGroupPost(QString val)
{ {
if(m_group == val) if(m_group == val)
{ {
return; return;
} }
setGroupInitial(val); setGroupInitial(val);
emit groupChanged(); emit groupChanged();
} }
QString BaseInstance::group() const QString BaseInstance::group() const
{ {
return m_group; return m_group;
} }
void BaseInstance::setNotes(QString val) void BaseInstance::setNotes(QString val)
{ {
//FIXME: if no change, do not set. setting involves saving a file. //FIXME: if no change, do not set. setting involves saving a file.
m_settings->set("notes", val); m_settings->set("notes", val);
} }
QString BaseInstance::notes() const QString BaseInstance::notes() const
{ {
return m_settings->get("notes").toString(); return m_settings->get("notes").toString();
} }
void BaseInstance::setIconKey(QString val) void BaseInstance::setIconKey(QString val)
{ {
//FIXME: if no change, do not set. setting involves saving a file. //FIXME: if no change, do not set. setting involves saving a file.
m_settings->set("iconKey", val); m_settings->set("iconKey", val);
emit propertiesChanged(this); emit propertiesChanged(this);
} }
QString BaseInstance::iconKey() const QString BaseInstance::iconKey() const
{ {
return m_settings->get("iconKey").toString(); return m_settings->get("iconKey").toString();
} }
void BaseInstance::setName(QString val) void BaseInstance::setName(QString val)
{ {
//FIXME: if no change, do not set. setting involves saving a file. //FIXME: if no change, do not set. setting involves saving a file.
m_settings->set("name", val); m_settings->set("name", val);
emit propertiesChanged(this); emit propertiesChanged(this);
} }
QString BaseInstance::name() const QString BaseInstance::name() const
{ {
return m_settings->get("name").toString(); return m_settings->get("name").toString();
} }
QString BaseInstance::windowTitle() const QString BaseInstance::windowTitle() const
{ {
return "MultiMC: " + name(); return "MultiMC: " + name();
} }
// FIXME: why is this here? move it to MinecraftInstance!!! // FIXME: why is this here? move it to MinecraftInstance!!!
QStringList BaseInstance::extraArguments() const QStringList BaseInstance::extraArguments() const
{ {
return Commandline::splitArgs(settings()->get("JvmArgs").toString()); return Commandline::splitArgs(settings()->get("JvmArgs").toString());
} }
std::shared_ptr<LaunchTask> BaseInstance::getLaunchTask() std::shared_ptr<LaunchTask> BaseInstance::getLaunchTask()
{ {
return m_launchProcess; return m_launchProcess;
} }
void BaseInstance::setProvider(BaseInstanceProvider* provider) void BaseInstance::setProvider(BaseInstanceProvider* provider)
{ {
// only once. // only once.
assert(!m_provider); assert(!m_provider);
if(m_provider) if(m_provider)
{ {
qWarning() << "Provider set more than once for instance" << id(); qWarning() << "Provider set more than once for instance" << id();
} }
m_provider = provider; m_provider = provider;
} }
BaseInstanceProvider* BaseInstance::provider() const BaseInstanceProvider* BaseInstance::provider() const
{ {
return m_provider; return m_provider;
} }

View File

@ -53,229 +53,229 @@ typedef std::shared_ptr<BaseInstance> InstancePtr;
*/ */
class MULTIMC_LOGIC_EXPORT BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance> class MULTIMC_LOGIC_EXPORT BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance>
{ {
Q_OBJECT Q_OBJECT
protected: protected:
/// no-touchy! /// no-touchy!
BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
public: /* types */ public: /* types */
enum class Status enum class Status
{ {
Present, Present,
Gone // either nuked or invalidated Gone // either nuked or invalidated
}; };
public: public:
/// virtual destructor to make sure the destruction is COMPLETE /// virtual destructor to make sure the destruction is COMPLETE
virtual ~BaseInstance() {}; virtual ~BaseInstance() {};
virtual void init() = 0; virtual void init() = 0;
virtual void saveNow() = 0; virtual void saveNow() = 0;
/// nuke thoroughly - deletes the instance contents, notifies the list/model which is /// nuke thoroughly - deletes the instance contents, notifies the list/model which is
/// responsible of cleaning up the husk /// responsible of cleaning up the husk
void nuke(); void nuke();
/*** /***
* the instance has been invalidated - it is no longer tracked by MultiMC for some reason, * the instance has been invalidated - it is no longer tracked by MultiMC for some reason,
* but it has not necessarily been deleted. * but it has not necessarily been deleted.
* *
* Happens when the instance folder changes to some other location, or the instance is removed by external means. * Happens when the instance folder changes to some other location, or the instance is removed by external means.
*/ */
void invalidate(); void invalidate();
/// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to /// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to
/// be unique. /// be unique.
virtual QString id() const; virtual QString id() const;
void setRunning(bool running); void setRunning(bool running);
bool isRunning() const; bool isRunning() const;
int64_t totalTimePlayed() const; int64_t totalTimePlayed() const;
void resetTimePlayed(); void resetTimePlayed();
void setProvider(BaseInstanceProvider * provider); void setProvider(BaseInstanceProvider * provider);
BaseInstanceProvider * provider() const; BaseInstanceProvider * provider() const;
/// get the type of this instance /// get the type of this instance
QString instanceType() const; QString instanceType() const;
/// Path to the instance's root directory. /// Path to the instance's root directory.
QString instanceRoot() const; QString instanceRoot() const;
QString name() const; QString name() const;
void setName(QString val); void setName(QString val);
/// Value used for instance window titles /// Value used for instance window titles
QString windowTitle() const; QString windowTitle() const;
QString iconKey() const; QString iconKey() const;
void setIconKey(QString val); void setIconKey(QString val);
QString notes() const; QString notes() const;
void setNotes(QString val); void setNotes(QString val);
QString group() const; QString group() const;
void setGroupInitial(QString val); void setGroupInitial(QString val);
void setGroupPost(QString val); void setGroupPost(QString val);
QString getPreLaunchCommand(); QString getPreLaunchCommand();
QString getPostExitCommand(); QString getPostExitCommand();
QString getWrapperCommand(); QString getWrapperCommand();
/// guess log level from a line of game log /// guess log level from a line of game log
virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level)
{ {
return level; return level;
}; };
virtual QStringList extraArguments() const; virtual QStringList extraArguments() const;
/// Traits. Normally inside the version, depends on instance implementation. /// Traits. Normally inside the version, depends on instance implementation.
virtual QSet <QString> traits() const = 0; virtual QSet <QString> traits() const = 0;
/** /**
* Gets the time that the instance was last launched. * Gets the time that the instance was last launched.
* Stored in milliseconds since epoch. * Stored in milliseconds since epoch.
*/ */
qint64 lastLaunch() const; qint64 lastLaunch() const;
/// Sets the last launched time to 'val' milliseconds since epoch /// Sets the last launched time to 'val' milliseconds since epoch
void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch()); void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch());
InstancePtr getSharedPtr(); InstancePtr getSharedPtr();
/*! /*!
* \brief Gets this instance's settings object. * \brief Gets this instance's settings object.
* This settings object stores instance-specific settings. * This settings object stores instance-specific settings.
* \return A pointer to this instance's settings object. * \return A pointer to this instance's settings object.
*/ */
virtual SettingsObjectPtr settings() const; virtual SettingsObjectPtr settings() const;
/// returns a valid update task /// returns a valid update task
virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0; virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0;
/// returns a valid launcher (task container) /// returns a valid launcher (task container)
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0; virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
/// returns the current launch task (if any) /// returns the current launch task (if any)
std::shared_ptr<LaunchTask> getLaunchTask(); std::shared_ptr<LaunchTask> getLaunchTask();
/*! /*!
* Create envrironment variables for running the instance * Create envrironment variables for running the instance
*/ */
virtual QProcessEnvironment createEnvironment() = 0; virtual QProcessEnvironment createEnvironment() = 0;
/*! /*!
* Returns a matcher that can maps relative paths within the instance to whether they are 'log files' * Returns a matcher that can maps relative paths within the instance to whether they are 'log files'
*/ */
virtual IPathMatcher::Ptr getLogFileMatcher() = 0; virtual IPathMatcher::Ptr getLogFileMatcher() = 0;
/*! /*!
* Returns the root folder to use for looking up log files * Returns the root folder to use for looking up log files
*/ */
virtual QString getLogFileRoot() = 0; virtual QString getLogFileRoot() = 0;
virtual QString getStatusbarDescription() = 0; virtual QString getStatusbarDescription() = 0;
/// FIXME: this really should be elsewhere... /// FIXME: this really should be elsewhere...
virtual QString instanceConfigFolder() const = 0; virtual QString instanceConfigFolder() const = 0;
/// get variables this instance exports /// get variables this instance exports
virtual QMap<QString, QString> getVariables() const = 0; virtual QMap<QString, QString> getVariables() const = 0;
virtual QString typeName() const = 0; virtual QString typeName() const = 0;
bool hasVersionBroken() const bool hasVersionBroken() const
{ {
return m_hasBrokenVersion; return m_hasBrokenVersion;
} }
void setVersionBroken(bool value) void setVersionBroken(bool value)
{ {
if(m_hasBrokenVersion != value) if(m_hasBrokenVersion != value)
{ {
m_hasBrokenVersion = value; m_hasBrokenVersion = value;
emit propertiesChanged(this); emit propertiesChanged(this);
} }
} }
bool hasUpdateAvailable() const bool hasUpdateAvailable() const
{ {
return m_hasUpdate; return m_hasUpdate;
} }
void setUpdateAvailable(bool value) void setUpdateAvailable(bool value)
{ {
if(m_hasUpdate != value) if(m_hasUpdate != value)
{ {
m_hasUpdate = value; m_hasUpdate = value;
emit propertiesChanged(this); emit propertiesChanged(this);
} }
} }
bool hasCrashed() const bool hasCrashed() const
{ {
return m_crashed; return m_crashed;
} }
void setCrashed(bool value) void setCrashed(bool value)
{ {
if(m_crashed != value) if(m_crashed != value)
{ {
m_crashed = value; m_crashed = value;
emit propertiesChanged(this); emit propertiesChanged(this);
} }
} }
virtual bool canLaunch() const; virtual bool canLaunch() const;
virtual bool canEdit() const = 0; virtual bool canEdit() const = 0;
virtual bool canExport() const = 0; virtual bool canExport() const = 0;
bool reloadSettings(); bool reloadSettings();
/** /**
* 'print' a verbose desription of the instance into a QStringList * 'print' a verbose desription of the instance into a QStringList
*/ */
virtual QStringList verboseDescription(AuthSessionPtr session) = 0; virtual QStringList verboseDescription(AuthSessionPtr session) = 0;
Status currentStatus() const; Status currentStatus() const;
int getConsoleMaxLines() const; int getConsoleMaxLines() const;
bool shouldStopOnConsoleOverflow() const; bool shouldStopOnConsoleOverflow() const;
protected: protected:
void changeStatus(Status newStatus); void changeStatus(Status newStatus);
signals: signals:
/*! /*!
* \brief Signal emitted when properties relevant to the instance view change * \brief Signal emitted when properties relevant to the instance view change
*/ */
void propertiesChanged(BaseInstance *inst); void propertiesChanged(BaseInstance *inst);
/*! /*!
* \brief Signal emitted when groups are affected in any way * \brief Signal emitted when groups are affected in any way
*/ */
void groupChanged(); void groupChanged();
void launchTaskChanged(std::shared_ptr<LaunchTask>); void launchTaskChanged(std::shared_ptr<LaunchTask>);
void runningStatusChanged(bool running); void runningStatusChanged(bool running);
void statusChanged(Status from, Status to); void statusChanged(Status from, Status to);
protected slots: protected slots:
void iconUpdated(QString key); void iconUpdated(QString key);
protected: /* data */ protected: /* data */
QString m_rootDir; QString m_rootDir;
QString m_group; QString m_group;
SettingsObjectPtr m_settings; SettingsObjectPtr m_settings;
// InstanceFlags m_flags; // InstanceFlags m_flags;
bool m_isRunning = false; bool m_isRunning = false;
std::shared_ptr<LaunchTask> m_launchProcess; std::shared_ptr<LaunchTask> m_launchProcess;
QDateTime m_timeStarted; QDateTime m_timeStarted;
BaseInstanceProvider * m_provider = nullptr; BaseInstanceProvider * m_provider = nullptr;
private: /* data */ private: /* data */
Status m_status = Status::Present; Status m_status = Status::Present;
bool m_crashed = false; bool m_crashed = false;
bool m_hasUpdate = false; bool m_hasUpdate = false;
bool m_hasBrokenVersion = false; bool m_hasBrokenVersion = false;
}; };
Q_DECLARE_METATYPE(std::shared_ptr<BaseInstance>) Q_DECLARE_METATYPE(std::shared_ptr<BaseInstance>)

View File

@ -12,46 +12,46 @@ using InstanceLocator = std::pair<InstancePtr, int>;
enum class InstCreateError enum class InstCreateError
{ {
NoCreateError = 0, NoCreateError = 0,
NoSuchVersion, NoSuchVersion,
UnknownCreateError, UnknownCreateError,
InstExists, InstExists,
CantCreateDir CantCreateDir
}; };
class MULTIMC_LOGIC_EXPORT BaseInstanceProvider : public QObject class MULTIMC_LOGIC_EXPORT BaseInstanceProvider : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
BaseInstanceProvider(SettingsObjectPtr settings) : m_globalSettings(settings) BaseInstanceProvider(SettingsObjectPtr settings) : m_globalSettings(settings)
{ {
// nil // nil
} }
public: public:
virtual QList<InstanceId> discoverInstances() = 0; virtual QList<InstanceId> discoverInstances() = 0;
virtual InstancePtr loadInstance(const InstanceId &id) = 0; virtual InstancePtr loadInstance(const InstanceId &id) = 0;
virtual void loadGroupList() = 0; virtual void loadGroupList() = 0;
virtual void saveGroupList() = 0; virtual void saveGroupList() = 0;
virtual QString getStagedInstancePath() virtual QString getStagedInstancePath()
{ {
return QString(); return QString();
} }
virtual bool commitStagedInstance(const QString & path, const QString& instanceName, const QString & groupName) virtual bool commitStagedInstance(const QString & path, const QString& instanceName, const QString & groupName)
{ {
return false; return false;
} }
virtual bool destroyStagingPath(const QString & path) virtual bool destroyStagingPath(const QString & path)
{ {
return true; return true;
} }
signals: signals:
// Emit this when the list of provided instances changed // Emit this when the list of provided instances changed
void instancesChanged(); void instancesChanged();
// Emit when the set of groups your provider supplies changes. // Emit when the set of groups your provider supplies changes.
void groupsChanged(QSet<QString> groups); void groupsChanged(QSet<QString> groups);
protected: protected:
SettingsObjectPtr m_globalSettings; SettingsObjectPtr m_globalSettings;
}; };

View File

@ -25,33 +25,33 @@
class BaseVersion class BaseVersion
{ {
public: public:
virtual ~BaseVersion() {} virtual ~BaseVersion() {}
/*! /*!
* A string used to identify this version in config files. * A string used to identify this version in config files.
* This should be unique within the version list or shenanigans will occur. * This should be unique within the version list or shenanigans will occur.
*/ */
virtual QString descriptor() = 0; virtual QString descriptor() = 0;
/*! /*!
* The name of this version as it is displayed to the user. * The name of this version as it is displayed to the user.
* For example: "1.5.1" * For example: "1.5.1"
*/ */
virtual QString name() = 0; virtual QString name() = 0;
/*! /*!
* This should return a string that describes * This should return a string that describes
* the kind of version this is (Stable, Beta, Snapshot, whatever) * the kind of version this is (Stable, Beta, Snapshot, whatever)
*/ */
virtual QString typeString() const = 0; virtual QString typeString() const = 0;
virtual bool operator<(BaseVersion &a) virtual bool operator<(BaseVersion &a)
{ {
return name() < a.name(); return name() < a.name();
}; };
virtual bool operator>(BaseVersion &a) virtual bool operator>(BaseVersion &a)
{ {
return name() > a.name(); return name() > a.name();
}; };
}; };
typedef std::shared_ptr<BaseVersion> BaseVersionPtr; typedef std::shared_ptr<BaseVersion> BaseVersionPtr;

View File

@ -22,78 +22,78 @@ BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent)
BaseVersionPtr BaseVersionList::findVersion(const QString &descriptor) BaseVersionPtr BaseVersionList::findVersion(const QString &descriptor)
{ {
for (int i = 0; i < count(); i++) for (int i = 0; i < count(); i++)
{ {
if (at(i)->descriptor() == descriptor) if (at(i)->descriptor() == descriptor)
return at(i); return at(i);
} }
return BaseVersionPtr(); return BaseVersionPtr();
} }
BaseVersionPtr BaseVersionList::getRecommended() const BaseVersionPtr BaseVersionList::getRecommended() const
{ {
if (count() <= 0) if (count() <= 0)
return BaseVersionPtr(); return BaseVersionPtr();
else else
return at(0); return at(0);
} }
QVariant BaseVersionList::data(const QModelIndex &index, int role) const QVariant BaseVersionList::data(const QModelIndex &index, int role) const
{ {
if (!index.isValid()) if (!index.isValid())
return QVariant(); return QVariant();
if (index.row() > count()) if (index.row() > count())
return QVariant(); return QVariant();
BaseVersionPtr version = at(index.row()); BaseVersionPtr version = at(index.row());
switch (role) switch (role)
{ {
case VersionPointerRole: case VersionPointerRole:
return qVariantFromValue(version); return qVariantFromValue(version);
case VersionRole: case VersionRole:
return version->name(); return version->name();
case VersionIdRole: case VersionIdRole:
return version->descriptor(); return version->descriptor();
case TypeRole: case TypeRole:
return version->typeString(); return version->typeString();
default: default:
return QVariant(); return QVariant();
} }
} }
BaseVersionList::RoleList BaseVersionList::providesRoles() const BaseVersionList::RoleList BaseVersionList::providesRoles() const
{ {
return {VersionPointerRole, VersionRole, VersionIdRole, TypeRole}; return {VersionPointerRole, VersionRole, VersionIdRole, TypeRole};
} }
int BaseVersionList::rowCount(const QModelIndex &parent) const int BaseVersionList::rowCount(const QModelIndex &parent) const
{ {
// Return count // Return count
return count(); return count();
} }
int BaseVersionList::columnCount(const QModelIndex &parent) const int BaseVersionList::columnCount(const QModelIndex &parent) const
{ {
return 1; return 1;
} }
QHash<int, QByteArray> BaseVersionList::roleNames() const QHash<int, QByteArray> BaseVersionList::roleNames() const
{ {
QHash<int, QByteArray> roles = QAbstractListModel::roleNames(); QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
roles.insert(VersionRole, "version"); roles.insert(VersionRole, "version");
roles.insert(VersionIdRole, "versionId"); roles.insert(VersionIdRole, "versionId");
roles.insert(ParentVersionRole, "parentGameVersion"); roles.insert(ParentVersionRole, "parentGameVersion");
roles.insert(RecommendedRole, "recommended"); roles.insert(RecommendedRole, "recommended");
roles.insert(LatestRole, "latest"); roles.insert(LatestRole, "latest");
roles.insert(TypeRole, "type"); roles.insert(TypeRole, "type");
roles.insert(BranchRole, "branch"); roles.insert(BranchRole, "branch");
roles.insert(PathRole, "path"); roles.insert(PathRole, "path");
roles.insert(ArchitectureRole, "architecture"); roles.insert(ArchitectureRole, "architecture");
return roles; return roles;
} }

View File

@ -38,85 +38,85 @@
*/ */
class MULTIMC_LOGIC_EXPORT BaseVersionList : public QAbstractListModel class MULTIMC_LOGIC_EXPORT BaseVersionList : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
public: public:
enum ModelRoles enum ModelRoles
{ {
VersionPointerRole = Qt::UserRole, VersionPointerRole = Qt::UserRole,
VersionRole, VersionRole,
VersionIdRole, VersionIdRole,
ParentVersionRole, ParentVersionRole,
RecommendedRole, RecommendedRole,
LatestRole, LatestRole,
TypeRole, TypeRole,
BranchRole, BranchRole,
PathRole, PathRole,
ArchitectureRole, ArchitectureRole,
SortRole SortRole
}; };
typedef QList<int> RoleList; typedef QList<int> RoleList;
explicit BaseVersionList(QObject *parent = 0); explicit BaseVersionList(QObject *parent = 0);
/*! /*!
* \brief Gets a task that will reload the version list. * \brief Gets a task that will reload the version list.
* Simply execute the task to load the list. * Simply execute the task to load the list.
* The task returned by this function should reset the model when it's done. * The task returned by this function should reset the model when it's done.
* \return A pointer to a task that reloads the version list. * \return A pointer to a task that reloads the version list.
*/ */
virtual shared_qobject_ptr<Task> getLoadTask() = 0; virtual shared_qobject_ptr<Task> getLoadTask() = 0;
//! Checks whether or not the list is loaded. If this returns false, the list should be //! Checks whether or not the list is loaded. If this returns false, the list should be
//loaded. //loaded.
virtual bool isLoaded() = 0; virtual bool isLoaded() = 0;
//! Gets the version at the given index. //! Gets the version at the given index.
virtual const BaseVersionPtr at(int i) const = 0; virtual const BaseVersionPtr at(int i) const = 0;
//! Returns the number of versions in the list. //! Returns the number of versions in the list.
virtual int count() const = 0; virtual int count() const = 0;
//////// List Model Functions //////// //////// List Model Functions ////////
QVariant data(const QModelIndex &index, int role) const override; QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override;
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
//! which roles are provided by this version list? //! which roles are provided by this version list?
virtual RoleList providesRoles() const; virtual RoleList providesRoles() const;
/*! /*!
* \brief Finds a version by its descriptor. * \brief Finds a version by its descriptor.
* \param descriptor The descriptor of the version to find. * \param descriptor The descriptor of the version to find.
* \return A const pointer to the version with the given descriptor. NULL if * \return A const pointer to the version with the given descriptor. NULL if
* one doesn't exist. * one doesn't exist.
*/ */
virtual BaseVersionPtr findVersion(const QString &descriptor); virtual BaseVersionPtr findVersion(const QString &descriptor);
/*! /*!
* \brief Gets the recommended version from this list * \brief Gets the recommended version from this list
* If the list doesn't support recommended versions, this works exactly as getLatestStable * If the list doesn't support recommended versions, this works exactly as getLatestStable
*/ */
virtual BaseVersionPtr getRecommended() const; virtual BaseVersionPtr getRecommended() const;
/*! /*!
* Sorts the version list. * Sorts the version list.
*/ */
virtual void sortVersions() = 0; virtual void sortVersions() = 0;
protected protected
slots: slots:
/*! /*!
* Updates this list with the given list of versions. * Updates this list with the given list of versions.
* This is done by copying each version in the given list and inserting it * This is done by copying each version in the given list and inserting it
* into this one. * into this one.
* We need to do this so that we can set the parents of the versions are set to this * We need to do this so that we can set the parents of the versions are set to this
* version list. This can't be done in the load task, because the versions the load * version list. This can't be done in the load task, because the versions the load
* task creates are on the load task's thread and Qt won't allow their parents * task creates are on the load task's thread and Qt won't allow their parents
* to be set to something created on another thread. * to be set to something created on another thread.
* To get around that problem, we invoke this method on the GUI thread, which * To get around that problem, we invoke this method on the GUI thread, which
* then copies the versions and sets their parents correctly. * then copies the versions and sets their parents correctly.
* \param versions List of versions whose parents should be set. * \param versions List of versions whose parents should be set.
*/ */
virtual void updateListData(QList<BaseVersionPtr> versions) = 0; virtual void updateListData(QList<BaseVersionPtr> versions) = 0;
}; };

View File

@ -3,446 +3,446 @@ project(MultiMC_logic)
include (UnitTest) include (UnitTest)
set(CORE_SOURCES set(CORE_SOURCES
# LOGIC - Base classes and infrastructure # LOGIC - Base classes and infrastructure
BaseInstaller.h BaseInstaller.h
BaseInstaller.cpp BaseInstaller.cpp
BaseVersionList.h BaseVersionList.h
BaseVersionList.cpp BaseVersionList.cpp
InstanceList.h InstanceList.h
InstanceList.cpp InstanceList.cpp
InstanceTask.h InstanceTask.h
InstanceTask.cpp InstanceTask.cpp
LoggedProcess.h LoggedProcess.h
LoggedProcess.cpp LoggedProcess.cpp
MessageLevel.cpp MessageLevel.cpp
MessageLevel.h MessageLevel.h
BaseInstanceProvider.h BaseInstanceProvider.h
FolderInstanceProvider.h FolderInstanceProvider.h
FolderInstanceProvider.cpp FolderInstanceProvider.cpp
BaseVersion.h BaseVersion.h
BaseInstance.h BaseInstance.h
BaseInstance.cpp BaseInstance.cpp
NullInstance.h NullInstance.h
MMCZip.h MMCZip.h
MMCZip.cpp MMCZip.cpp
MMCStrings.h MMCStrings.h
MMCStrings.cpp MMCStrings.cpp
# Basic instance manipulation tasks (derived from InstanceTask) # Basic instance manipulation tasks (derived from InstanceTask)
InstanceCreationTask.h InstanceCreationTask.h
InstanceCreationTask.cpp InstanceCreationTask.cpp
InstanceCopyTask.h InstanceCopyTask.h
InstanceCopyTask.cpp InstanceCopyTask.cpp
InstanceImportTask.h InstanceImportTask.h
InstanceImportTask.cpp InstanceImportTask.cpp
# Use tracking separate from memory management # Use tracking separate from memory management
Usable.h Usable.h
# Prefix tree where node names are strings between separators # Prefix tree where node names are strings between separators
SeparatorPrefixTree.h SeparatorPrefixTree.h
# WARNING: globals live here # WARNING: globals live here
Env.h Env.h
Env.cpp Env.cpp
# String filters # String filters
Filter.h Filter.h
Filter.cpp Filter.cpp
# JSON parsing helpers # JSON parsing helpers
Json.h Json.h
Json.cpp Json.cpp
FileSystem.h FileSystem.h
FileSystem.cpp FileSystem.cpp
Exception.h Exception.h
# RW lock protected map # RW lock protected map
RWStorage.h RWStorage.h
# A variable that has an implicit default value and keeps track of changes # A variable that has an implicit default value and keeps track of changes
DefaultVariable.h DefaultVariable.h
# a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms # a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms
QObjectPtr.h QObjectPtr.h
# Compression support # Compression support
GZip.h GZip.h
GZip.cpp GZip.cpp
# Command line parameter parsing # Command line parameter parsing
Commandline.h Commandline.h
Commandline.cpp Commandline.cpp
# Version number string support # Version number string support
Version.h Version.h
Version.cpp Version.cpp
# A Recursive file system watcher # A Recursive file system watcher
RecursiveFileSystemWatcher.h RecursiveFileSystemWatcher.h
RecursiveFileSystemWatcher.cpp RecursiveFileSystemWatcher.cpp
) )
add_unit_test(FileSystem add_unit_test(FileSystem
SOURCES FileSystem_test.cpp SOURCES FileSystem_test.cpp
LIBS MultiMC_logic LIBS MultiMC_logic
DATA testdata DATA testdata
) )
add_unit_test(GZip add_unit_test(GZip
SOURCES GZip_test.cpp SOURCES GZip_test.cpp
LIBS MultiMC_logic LIBS MultiMC_logic
) )
set(PATHMATCHER_SOURCES set(PATHMATCHER_SOURCES
# Path matchers # Path matchers
pathmatcher/FSTreeMatcher.h pathmatcher/FSTreeMatcher.h
pathmatcher/IPathMatcher.h pathmatcher/IPathMatcher.h
pathmatcher/MultiMatcher.h pathmatcher/MultiMatcher.h
pathmatcher/RegexpMatcher.h pathmatcher/RegexpMatcher.h
) )
set(NET_SOURCES set(NET_SOURCES
# network stuffs # network stuffs
net/ByteArraySink.h net/ByteArraySink.h
net/ChecksumValidator.h net/ChecksumValidator.h
net/Download.cpp net/Download.cpp
net/Download.h net/Download.h
net/FileSink.cpp net/FileSink.cpp
net/FileSink.h net/FileSink.h
net/HttpMetaCache.cpp net/HttpMetaCache.cpp
net/HttpMetaCache.h net/HttpMetaCache.h
net/MetaCacheSink.cpp net/MetaCacheSink.cpp
net/MetaCacheSink.h net/MetaCacheSink.h
net/NetAction.h net/NetAction.h
net/NetJob.cpp net/NetJob.cpp
net/NetJob.h net/NetJob.h
net/PasteUpload.cpp net/PasteUpload.cpp
net/PasteUpload.h net/PasteUpload.h
net/Sink.h net/Sink.h
net/URLConstants.cpp net/URLConstants.cpp
net/URLConstants.h net/URLConstants.h
net/Validator.h net/Validator.h
) )
# Game launch logic # Game launch logic
set(LAUNCH_SOURCES set(LAUNCH_SOURCES
launch/steps/PostLaunchCommand.cpp launch/steps/PostLaunchCommand.cpp
launch/steps/PostLaunchCommand.h launch/steps/PostLaunchCommand.h
launch/steps/PreLaunchCommand.cpp launch/steps/PreLaunchCommand.cpp
launch/steps/PreLaunchCommand.h launch/steps/PreLaunchCommand.h
launch/steps/TextPrint.cpp launch/steps/TextPrint.cpp
launch/steps/TextPrint.h launch/steps/TextPrint.h
launch/steps/Update.cpp launch/steps/Update.cpp
launch/steps/Update.h launch/steps/Update.h
launch/LaunchStep.cpp launch/LaunchStep.cpp
launch/LaunchStep.h launch/LaunchStep.h
launch/LaunchTask.cpp launch/LaunchTask.cpp
launch/LaunchTask.h launch/LaunchTask.h
launch/LogModel.cpp launch/LogModel.cpp
launch/LogModel.h launch/LogModel.h
) )
# Old update system # Old update system
set(UPDATE_SOURCES set(UPDATE_SOURCES
updater/GoUpdate.h updater/GoUpdate.h
updater/GoUpdate.cpp updater/GoUpdate.cpp
updater/UpdateChecker.h updater/UpdateChecker.h
updater/UpdateChecker.cpp updater/UpdateChecker.cpp
updater/DownloadTask.h updater/DownloadTask.h
updater/DownloadTask.cpp updater/DownloadTask.cpp
) )
add_unit_test(UpdateChecker add_unit_test(UpdateChecker
SOURCES updater/UpdateChecker_test.cpp SOURCES updater/UpdateChecker_test.cpp
LIBS MultiMC_logic LIBS MultiMC_logic
DATA updater/testdata DATA updater/testdata
) )
add_unit_test(DownloadTask add_unit_test(DownloadTask
SOURCES updater/DownloadTask_test.cpp SOURCES updater/DownloadTask_test.cpp
LIBS MultiMC_logic LIBS MultiMC_logic
DATA updater/testdata DATA updater/testdata
) )
# Rarely used notifications # Rarely used notifications
set(NOTIFICATIONS_SOURCES set(NOTIFICATIONS_SOURCES
# Notifications - short warning messages # Notifications - short warning messages
notifications/NotificationChecker.h notifications/NotificationChecker.h
notifications/NotificationChecker.cpp notifications/NotificationChecker.cpp
) )
# Backend for the news bar... there's usually no news. # Backend for the news bar... there's usually no news.
set(NEWS_SOURCES set(NEWS_SOURCES
# News System # News System
news/NewsChecker.h news/NewsChecker.h
news/NewsChecker.cpp news/NewsChecker.cpp
news/NewsEntry.h news/NewsEntry.h
news/NewsEntry.cpp news/NewsEntry.cpp
) )
# Icon interface # Icon interface
set(ICONS_SOURCES set(ICONS_SOURCES
# News System # News System
icons/IIconList.h icons/IIconList.h
icons/IIconList.cpp icons/IIconList.cpp
) )
# Minecraft services status checker # Minecraft services status checker
set(STATUS_SOURCES set(STATUS_SOURCES
# Status system # Status system
status/StatusChecker.h status/StatusChecker.h
status/StatusChecker.cpp status/StatusChecker.cpp
) )
# Support for Minecraft instances and launch # Support for Minecraft instances and launch
set(MINECRAFT_SOURCES set(MINECRAFT_SOURCES
# Minecraft support # Minecraft support
minecraft/auth/AuthSession.h minecraft/auth/AuthSession.h
minecraft/auth/AuthSession.cpp minecraft/auth/AuthSession.cpp
minecraft/auth/MojangAccountList.h minecraft/auth/MojangAccountList.h
minecraft/auth/MojangAccountList.cpp minecraft/auth/MojangAccountList.cpp
minecraft/auth/MojangAccount.h minecraft/auth/MojangAccount.h
minecraft/auth/MojangAccount.cpp minecraft/auth/MojangAccount.cpp
minecraft/auth/YggdrasilTask.h minecraft/auth/YggdrasilTask.h
minecraft/auth/YggdrasilTask.cpp minecraft/auth/YggdrasilTask.cpp
minecraft/auth/flows/AuthenticateTask.h minecraft/auth/flows/AuthenticateTask.h
minecraft/auth/flows/AuthenticateTask.cpp minecraft/auth/flows/AuthenticateTask.cpp
minecraft/auth/flows/RefreshTask.cpp minecraft/auth/flows/RefreshTask.cpp
minecraft/auth/flows/RefreshTask.cpp minecraft/auth/flows/RefreshTask.cpp
minecraft/auth/flows/ValidateTask.h minecraft/auth/flows/ValidateTask.h
minecraft/auth/flows/ValidateTask.cpp minecraft/auth/flows/ValidateTask.cpp
minecraft/update/AssetUpdateTask.h minecraft/update/AssetUpdateTask.h
minecraft/update/AssetUpdateTask.cpp minecraft/update/AssetUpdateTask.cpp
minecraft/update/FMLLibrariesTask.cpp minecraft/update/FMLLibrariesTask.cpp
minecraft/update/FMLLibrariesTask.h minecraft/update/FMLLibrariesTask.h
minecraft/update/FoldersTask.cpp minecraft/update/FoldersTask.cpp
minecraft/update/FoldersTask.h minecraft/update/FoldersTask.h
minecraft/update/LibrariesTask.cpp minecraft/update/LibrariesTask.cpp
minecraft/update/LibrariesTask.h minecraft/update/LibrariesTask.h
minecraft/launch/ClaimAccount.cpp minecraft/launch/ClaimAccount.cpp
minecraft/launch/ClaimAccount.h minecraft/launch/ClaimAccount.h
minecraft/launch/CreateServerResourcePacksFolder.cpp minecraft/launch/CreateServerResourcePacksFolder.cpp
minecraft/launch/CreateServerResourcePacksFolder.h minecraft/launch/CreateServerResourcePacksFolder.h
minecraft/launch/ModMinecraftJar.cpp minecraft/launch/ModMinecraftJar.cpp
minecraft/launch/ModMinecraftJar.h minecraft/launch/ModMinecraftJar.h
minecraft/launch/DirectJavaLaunch.cpp minecraft/launch/DirectJavaLaunch.cpp
minecraft/launch/DirectJavaLaunch.h minecraft/launch/DirectJavaLaunch.h
minecraft/launch/ExtractNatives.cpp minecraft/launch/ExtractNatives.cpp
minecraft/launch/ExtractNatives.h minecraft/launch/ExtractNatives.h
minecraft/launch/LauncherPartLaunch.cpp minecraft/launch/LauncherPartLaunch.cpp
minecraft/launch/LauncherPartLaunch.h minecraft/launch/LauncherPartLaunch.h
minecraft/launch/PrintInstanceInfo.cpp minecraft/launch/PrintInstanceInfo.cpp
minecraft/launch/PrintInstanceInfo.h minecraft/launch/PrintInstanceInfo.h
minecraft/legacy/LegacyModList.h minecraft/legacy/LegacyModList.h
minecraft/legacy/LegacyModList.cpp minecraft/legacy/LegacyModList.cpp
minecraft/legacy/LegacyInstance.h minecraft/legacy/LegacyInstance.h
minecraft/legacy/LegacyInstance.cpp minecraft/legacy/LegacyInstance.cpp
minecraft/legacy/LegacyUpgradeTask.h minecraft/legacy/LegacyUpgradeTask.h
minecraft/legacy/LegacyUpgradeTask.cpp minecraft/legacy/LegacyUpgradeTask.cpp
minecraft/GradleSpecifier.h minecraft/GradleSpecifier.h
minecraft/MinecraftInstance.cpp minecraft/MinecraftInstance.cpp
minecraft/MinecraftInstance.h minecraft/MinecraftInstance.h
minecraft/LaunchProfile.cpp minecraft/LaunchProfile.cpp
minecraft/LaunchProfile.h minecraft/LaunchProfile.h
minecraft/Component.cpp minecraft/Component.cpp
minecraft/Component.h minecraft/Component.h
minecraft/ComponentList.cpp minecraft/ComponentList.cpp
minecraft/ComponentList.h minecraft/ComponentList.h
minecraft/ComponentUpdateTask.cpp minecraft/ComponentUpdateTask.cpp
minecraft/ComponentUpdateTask.h minecraft/ComponentUpdateTask.h
minecraft/MinecraftLoadAndCheck.h minecraft/MinecraftLoadAndCheck.h
minecraft/MinecraftLoadAndCheck.cpp minecraft/MinecraftLoadAndCheck.cpp
minecraft/MinecraftUpdate.h minecraft/MinecraftUpdate.h
minecraft/MinecraftUpdate.cpp minecraft/MinecraftUpdate.cpp
minecraft/MojangVersionFormat.cpp minecraft/MojangVersionFormat.cpp
minecraft/MojangVersionFormat.h minecraft/MojangVersionFormat.h
minecraft/Rule.cpp minecraft/Rule.cpp
minecraft/Rule.h minecraft/Rule.h
minecraft/OneSixVersionFormat.cpp minecraft/OneSixVersionFormat.cpp
minecraft/OneSixVersionFormat.h minecraft/OneSixVersionFormat.h
minecraft/OpSys.cpp minecraft/OpSys.cpp
minecraft/OpSys.h minecraft/OpSys.h
minecraft/ParseUtils.cpp minecraft/ParseUtils.cpp
minecraft/ParseUtils.h minecraft/ParseUtils.h
minecraft/ProfileUtils.cpp minecraft/ProfileUtils.cpp
minecraft/ProfileUtils.h minecraft/ProfileUtils.h
minecraft/Library.cpp minecraft/Library.cpp
minecraft/Library.h minecraft/Library.h
minecraft/MojangDownloadInfo.h minecraft/MojangDownloadInfo.h
minecraft/VersionFile.cpp minecraft/VersionFile.cpp
minecraft/VersionFile.h minecraft/VersionFile.h
minecraft/VersionFilterData.h minecraft/VersionFilterData.h
minecraft/VersionFilterData.cpp minecraft/VersionFilterData.cpp
minecraft/Mod.h minecraft/Mod.h
minecraft/Mod.cpp minecraft/Mod.cpp
minecraft/ModsModel.h minecraft/ModsModel.h
minecraft/ModsModel.cpp minecraft/ModsModel.cpp
minecraft/SimpleModList.h minecraft/SimpleModList.h
minecraft/SimpleModList.cpp minecraft/SimpleModList.cpp
minecraft/World.h minecraft/World.h
minecraft/World.cpp minecraft/World.cpp
minecraft/WorldList.h minecraft/WorldList.h
minecraft/WorldList.cpp minecraft/WorldList.cpp
# Assets # Assets
minecraft/AssetsUtils.h minecraft/AssetsUtils.h
minecraft/AssetsUtils.cpp minecraft/AssetsUtils.cpp
# Forge and all things forge related # Forge and all things forge related
minecraft/forge/ForgeXzDownload.h minecraft/forge/ForgeXzDownload.h
minecraft/forge/ForgeXzDownload.cpp minecraft/forge/ForgeXzDownload.cpp
# Skin upload utilities # Skin upload utilities
minecraft/SkinUpload.cpp minecraft/SkinUpload.cpp
minecraft/SkinUpload.h minecraft/SkinUpload.h
) )
add_unit_test(GradleSpecifier add_unit_test(GradleSpecifier
SOURCES minecraft/GradleSpecifier_test.cpp SOURCES minecraft/GradleSpecifier_test.cpp
LIBS MultiMC_logic LIBS MultiMC_logic
) )
add_unit_test(MojangVersionFormat add_unit_test(MojangVersionFormat
SOURCES minecraft/MojangVersionFormat_test.cpp SOURCES minecraft/MojangVersionFormat_test.cpp
LIBS MultiMC_logic LIBS MultiMC_logic
DATA minecraft/testdata DATA minecraft/testdata
) )
add_unit_test(Library add_unit_test(Library
SOURCES minecraft/Library_test.cpp SOURCES minecraft/Library_test.cpp
LIBS MultiMC_logic LIBS MultiMC_logic
) )
# FIXME: shares data with FileSystem test # FIXME: shares data with FileSystem test
add_unit_test(SimpleModList add_unit_test(SimpleModList
SOURCES minecraft/SimpleModList_test.cpp SOURCES minecraft/SimpleModList_test.cpp
DATA testdata DATA testdata
LIBS MultiMC_logic LIBS MultiMC_logic
) )
add_unit_test(ParseUtils add_unit_test(ParseUtils
SOURCES minecraft/ParseUtils_test.cpp SOURCES minecraft/ParseUtils_test.cpp
LIBS MultiMC_logic LIBS MultiMC_logic
) )
# the screenshots feature # the screenshots feature
set(SCREENSHOTS_SOURCES set(SCREENSHOTS_SOURCES
screenshots/Screenshot.h screenshots/Screenshot.h
screenshots/ImgurUpload.h screenshots/ImgurUpload.h
screenshots/ImgurUpload.cpp screenshots/ImgurUpload.cpp
screenshots/ImgurAlbumCreation.h screenshots/ImgurAlbumCreation.h
screenshots/ImgurAlbumCreation.cpp screenshots/ImgurAlbumCreation.cpp
) )
set(TASKS_SOURCES set(TASKS_SOURCES
# Tasks # Tasks
tasks/Task.h tasks/Task.h
tasks/Task.cpp tasks/Task.cpp
tasks/SequentialTask.h tasks/SequentialTask.h
tasks/SequentialTask.cpp tasks/SequentialTask.cpp
) )
set(SETTINGS_SOURCES set(SETTINGS_SOURCES
# Settings # Settings
settings/INIFile.cpp settings/INIFile.cpp
settings/INIFile.h settings/INIFile.h
settings/INISettingsObject.cpp settings/INISettingsObject.cpp
settings/INISettingsObject.h settings/INISettingsObject.h
settings/OverrideSetting.cpp settings/OverrideSetting.cpp
settings/OverrideSetting.h settings/OverrideSetting.h
settings/PassthroughSetting.cpp settings/PassthroughSetting.cpp
settings/PassthroughSetting.h settings/PassthroughSetting.h
settings/Setting.cpp settings/Setting.cpp
settings/Setting.h settings/Setting.h
settings/SettingsObject.cpp settings/SettingsObject.cpp
settings/SettingsObject.h settings/SettingsObject.h
) )
add_unit_test(INIFile add_unit_test(INIFile
SOURCES settings/INIFile_test.cpp SOURCES settings/INIFile_test.cpp
LIBS MultiMC_logic LIBS MultiMC_logic
) )
set(JAVA_SOURCES set(JAVA_SOURCES
# Java related code # Java related code
java/launch/CheckJava.cpp java/launch/CheckJava.cpp
java/launch/CheckJava.h java/launch/CheckJava.h
java/JavaChecker.h java/JavaChecker.h
java/JavaChecker.cpp java/JavaChecker.cpp
java/JavaCheckerJob.h java/JavaCheckerJob.h
java/JavaCheckerJob.cpp java/JavaCheckerJob.cpp
java/JavaInstall.h java/JavaInstall.h
java/JavaInstall.cpp java/JavaInstall.cpp
java/JavaInstallList.h java/JavaInstallList.h
java/JavaInstallList.cpp java/JavaInstallList.cpp
java/JavaUtils.h java/JavaUtils.h
java/JavaUtils.cpp java/JavaUtils.cpp
java/JavaVersion.h java/JavaVersion.h
java/JavaVersion.cpp java/JavaVersion.cpp
) )
add_unit_test(JavaVersion add_unit_test(JavaVersion
SOURCES java/JavaVersion_test.cpp SOURCES java/JavaVersion_test.cpp
LIBS MultiMC_logic LIBS MultiMC_logic
) )
set(TRANSLATIONS_SOURCES set(TRANSLATIONS_SOURCES
translations/TranslationsModel.h translations/TranslationsModel.h
translations/TranslationsModel.cpp translations/TranslationsModel.cpp
) )
set(TOOLS_SOURCES set(TOOLS_SOURCES
# Tools # Tools
tools/BaseExternalTool.cpp tools/BaseExternalTool.cpp
tools/BaseExternalTool.h tools/BaseExternalTool.h
tools/BaseProfiler.cpp tools/BaseProfiler.cpp
tools/BaseProfiler.h tools/BaseProfiler.h
tools/JProfiler.cpp tools/JProfiler.cpp
tools/JProfiler.h tools/JProfiler.h
tools/JVisualVM.cpp tools/JVisualVM.cpp
tools/JVisualVM.h tools/JVisualVM.h
tools/MCEditTool.cpp tools/MCEditTool.cpp
tools/MCEditTool.h tools/MCEditTool.h
) )
set(META_SOURCES set(META_SOURCES
# Metadata sources # Metadata sources
meta/JsonFormat.cpp meta/JsonFormat.cpp
meta/JsonFormat.h meta/JsonFormat.h
meta/BaseEntity.cpp meta/BaseEntity.cpp
meta/BaseEntity.h meta/BaseEntity.h
meta/VersionList.cpp meta/VersionList.cpp
meta/VersionList.h meta/VersionList.h
meta/Version.cpp meta/Version.cpp
meta/Version.h meta/Version.h
meta/Index.cpp meta/Index.cpp
meta/Index.h meta/Index.h
) )
set(FTB_SOURCES set(FTB_SOURCES
modplatform/ftb/FtbPackFetchTask.h modplatform/ftb/FtbPackFetchTask.h
modplatform/ftb/FtbPackFetchTask.cpp modplatform/ftb/FtbPackFetchTask.cpp
modplatform/ftb/FtbPackInstallTask.h modplatform/ftb/FtbPackInstallTask.h
modplatform/ftb/FtbPackInstallTask.cpp modplatform/ftb/FtbPackInstallTask.cpp
modplatform/ftb/PackHelpers.h modplatform/ftb/PackHelpers.h
) )
set(FLAME_SOURCES set(FLAME_SOURCES
# Flame # Flame
modplatform/flame/PackManifest.h modplatform/flame/PackManifest.h
modplatform/flame/PackManifest.cpp modplatform/flame/PackManifest.cpp
modplatform/flame/FileResolvingTask.h modplatform/flame/FileResolvingTask.h
modplatform/flame/FileResolvingTask.cpp modplatform/flame/FileResolvingTask.cpp
) )
add_unit_test(Index add_unit_test(Index
SOURCES meta/Index_test.cpp SOURCES meta/Index_test.cpp
LIBS MultiMC_logic LIBS MultiMC_logic
) )
################################ COMPILE ################################ ################################ COMPILE ################################
@ -450,25 +450,25 @@ add_unit_test(Index
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
set(LOGIC_SOURCES set(LOGIC_SOURCES
${CORE_SOURCES} ${CORE_SOURCES}
${PATHMATCHER_SOURCES} ${PATHMATCHER_SOURCES}
${NET_SOURCES} ${NET_SOURCES}
${LAUNCH_SOURCES} ${LAUNCH_SOURCES}
${UPDATE_SOURCES} ${UPDATE_SOURCES}
${NOTIFICATIONS_SOURCES} ${NOTIFICATIONS_SOURCES}
${NEWS_SOURCES} ${NEWS_SOURCES}
${STATUS_SOURCES} ${STATUS_SOURCES}
${MINECRAFT_SOURCES} ${MINECRAFT_SOURCES}
${SCREENSHOTS_SOURCES} ${SCREENSHOTS_SOURCES}
${TASKS_SOURCES} ${TASKS_SOURCES}
${SETTINGS_SOURCES} ${SETTINGS_SOURCES}
${JAVA_SOURCES} ${JAVA_SOURCES}
${TRANSLATIONS_SOURCES} ${TRANSLATIONS_SOURCES}
${TOOLS_SOURCES} ${TOOLS_SOURCES}
${META_SOURCES} ${META_SOURCES}
${ICONS_SOURCES} ${ICONS_SOURCES}
${FTB_SOURCES} ${FTB_SOURCES}
${FLAME_SOURCES} ${FLAME_SOURCES}
) )
add_library(MultiMC_logic SHARED ${LOGIC_SOURCES}) add_library(MultiMC_logic SHARED ${LOGIC_SOURCES})
@ -485,7 +485,7 @@ target_include_directories(MultiMC_logic PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "$
# Install it # Install it
install( install(
TARGETS MultiMC_logic TARGETS MultiMC_logic
RUNTIME DESTINATION ${LIBRARY_DEST_DIR} RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
) )

View File

@ -27,453 +27,453 @@ namespace Commandline
// commandline splitter // commandline splitter
QStringList splitArgs(QString args) QStringList splitArgs(QString args)
{ {
QStringList argv; QStringList argv;
QString current; QString current;
bool escape = false; bool escape = false;
QChar inquotes; QChar inquotes;
for (int i = 0; i < args.length(); i++) for (int i = 0; i < args.length(); i++)
{ {
QChar cchar = args.at(i); QChar cchar = args.at(i);
// \ escaped // \ escaped
if (escape) if (escape)
{ {
current += cchar; current += cchar;
escape = false; escape = false;
// in "quotes" // in "quotes"
} }
else if (!inquotes.isNull()) else if (!inquotes.isNull())
{ {
if (cchar == '\\') if (cchar == '\\')
escape = true; escape = true;
else if (cchar == inquotes) else if (cchar == inquotes)
inquotes = 0; inquotes = 0;
else else
current += cchar; current += cchar;
// otherwise // otherwise
} }
else else
{ {
if (cchar == ' ') if (cchar == ' ')
{ {
if (!current.isEmpty()) if (!current.isEmpty())
{ {
argv << current; argv << current;
current.clear(); current.clear();
} }
} }
else if (cchar == '"' || cchar == '\'') else if (cchar == '"' || cchar == '\'')
inquotes = cchar; inquotes = cchar;
else else
current += cchar; current += cchar;
} }
} }
if (!current.isEmpty()) if (!current.isEmpty())
argv << current; argv << current;
return argv; return argv;
} }
Parser::Parser(FlagStyle::Enum flagStyle, ArgumentStyle::Enum argStyle) Parser::Parser(FlagStyle::Enum flagStyle, ArgumentStyle::Enum argStyle)
{ {
m_flagStyle = flagStyle; m_flagStyle = flagStyle;
m_argStyle = argStyle; m_argStyle = argStyle;
} }
// styles setter/getter // styles setter/getter
void Parser::setArgumentStyle(ArgumentStyle::Enum style) void Parser::setArgumentStyle(ArgumentStyle::Enum style)
{ {
m_argStyle = style; m_argStyle = style;
} }
ArgumentStyle::Enum Parser::argumentStyle() ArgumentStyle::Enum Parser::argumentStyle()
{ {
return m_argStyle; return m_argStyle;
} }
void Parser::setFlagStyle(FlagStyle::Enum style) void Parser::setFlagStyle(FlagStyle::Enum style)
{ {
m_flagStyle = style; m_flagStyle = style;
} }
FlagStyle::Enum Parser::flagStyle() FlagStyle::Enum Parser::flagStyle()
{ {
return m_flagStyle; return m_flagStyle;
} }
// setup methods // setup methods
void Parser::addSwitch(QString name, bool def) void Parser::addSwitch(QString name, bool def)
{ {
if (m_params.contains(name)) if (m_params.contains(name))
throw "Name not unique"; throw "Name not unique";
OptionDef *param = new OptionDef; OptionDef *param = new OptionDef;
param->type = otSwitch; param->type = otSwitch;
param->name = name; param->name = name;
param->metavar = QString("<%1>").arg(name); param->metavar = QString("<%1>").arg(name);
param->def = def; param->def = def;
m_options[name] = param; m_options[name] = param;
m_params[name] = (CommonDef *)param; m_params[name] = (CommonDef *)param;
m_optionList.append(param); m_optionList.append(param);
} }
void Parser::addOption(QString name, QVariant def) void Parser::addOption(QString name, QVariant def)
{ {
if (m_params.contains(name)) if (m_params.contains(name))
throw "Name not unique"; throw "Name not unique";
OptionDef *param = new OptionDef; OptionDef *param = new OptionDef;
param->type = otOption; param->type = otOption;
param->name = name; param->name = name;
param->metavar = QString("<%1>").arg(name); param->metavar = QString("<%1>").arg(name);
param->def = def; param->def = def;
m_options[name] = param; m_options[name] = param;
m_params[name] = (CommonDef *)param; m_params[name] = (CommonDef *)param;
m_optionList.append(param); m_optionList.append(param);
} }
void Parser::addArgument(QString name, bool required, QVariant def) void Parser::addArgument(QString name, bool required, QVariant def)
{ {
if (m_params.contains(name)) if (m_params.contains(name))
throw "Name not unique"; throw "Name not unique";
PositionalDef *param = new PositionalDef; PositionalDef *param = new PositionalDef;
param->name = name; param->name = name;
param->def = def; param->def = def;
param->required = required; param->required = required;
param->metavar = name; param->metavar = name;
m_positionals.append(param); m_positionals.append(param);
m_params[name] = (CommonDef *)param; m_params[name] = (CommonDef *)param;
} }
void Parser::addDocumentation(QString name, QString doc, QString metavar) void Parser::addDocumentation(QString name, QString doc, QString metavar)
{ {
if (!m_params.contains(name)) if (!m_params.contains(name))
throw "Name does not exist"; throw "Name does not exist";
CommonDef *param = m_params[name]; CommonDef *param = m_params[name];
param->doc = doc; param->doc = doc;
if (!metavar.isNull()) if (!metavar.isNull())
param->metavar = metavar; param->metavar = metavar;
} }
void Parser::addShortOpt(QString name, QChar flag) void Parser::addShortOpt(QString name, QChar flag)
{ {
if (!m_params.contains(name)) if (!m_params.contains(name))
throw "Name does not exist"; throw "Name does not exist";
if (!m_options.contains(name)) if (!m_options.contains(name))
throw "Name is not an Option or Swtich"; throw "Name is not an Option or Swtich";
OptionDef *param = m_options[name]; OptionDef *param = m_options[name];
m_flags[flag] = param; m_flags[flag] = param;
param->flag = flag; param->flag = flag;
} }
// help methods // help methods
QString Parser::compileHelp(QString progName, int helpIndent, bool useFlags) QString Parser::compileHelp(QString progName, int helpIndent, bool useFlags)
{ {
QStringList help; QStringList help;
help << compileUsage(progName, useFlags) << "\r\n"; help << compileUsage(progName, useFlags) << "\r\n";
// positionals // positionals
if (!m_positionals.isEmpty()) if (!m_positionals.isEmpty())
{ {
help << "\r\n"; help << "\r\n";
help << "Positional arguments:\r\n"; help << "Positional arguments:\r\n";
QListIterator<PositionalDef *> it2(m_positionals); QListIterator<PositionalDef *> it2(m_positionals);
while (it2.hasNext()) while (it2.hasNext())
{ {
PositionalDef *param = it2.next(); PositionalDef *param = it2.next();
help << " " << param->metavar; help << " " << param->metavar;
help << " " << QString(helpIndent - param->metavar.length() - 1, ' '); help << " " << QString(helpIndent - param->metavar.length() - 1, ' ');
help << param->doc << "\r\n"; help << param->doc << "\r\n";
} }
} }
// Options // Options
if (!m_optionList.isEmpty()) if (!m_optionList.isEmpty())
{ {
help << "\r\n"; help << "\r\n";
QString optPrefix, flagPrefix; QString optPrefix, flagPrefix;
getPrefix(optPrefix, flagPrefix); getPrefix(optPrefix, flagPrefix);
help << "Options & Switches:\r\n"; help << "Options & Switches:\r\n";
QListIterator<OptionDef *> it(m_optionList); QListIterator<OptionDef *> it(m_optionList);
while (it.hasNext()) while (it.hasNext())
{ {
OptionDef *option = it.next(); OptionDef *option = it.next();
help << " "; help << " ";
int nameLength = optPrefix.length() + option->name.length(); int nameLength = optPrefix.length() + option->name.length();
if (!option->flag.isNull()) if (!option->flag.isNull())
{ {
nameLength += 3 + flagPrefix.length(); nameLength += 3 + flagPrefix.length();
help << flagPrefix << option->flag << ", "; help << flagPrefix << option->flag << ", ";
} }
help << optPrefix << option->name; help << optPrefix << option->name;
if (option->type == otOption) if (option->type == otOption)
{ {
QString arg = QString("%1%2").arg( QString arg = QString("%1%2").arg(
((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar); ((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar);
nameLength += arg.length(); nameLength += arg.length();
help << arg; help << arg;
} }
help << " " << QString(helpIndent - nameLength - 1, ' '); help << " " << QString(helpIndent - nameLength - 1, ' ');
help << option->doc << "\r\n"; help << option->doc << "\r\n";
} }
} }
return help.join(""); return help.join("");
} }
QString Parser::compileUsage(QString progName, bool useFlags) QString Parser::compileUsage(QString progName, bool useFlags)
{ {
QStringList usage; QStringList usage;
usage << "Usage: " << progName; usage << "Usage: " << progName;
QString optPrefix, flagPrefix; QString optPrefix, flagPrefix;
getPrefix(optPrefix, flagPrefix); getPrefix(optPrefix, flagPrefix);
// options // options
QListIterator<OptionDef *> it(m_optionList); QListIterator<OptionDef *> it(m_optionList);
while (it.hasNext()) while (it.hasNext())
{ {
OptionDef *option = it.next(); OptionDef *option = it.next();
usage << " ["; usage << " [";
if (!option->flag.isNull() && useFlags) if (!option->flag.isNull() && useFlags)
usage << flagPrefix << option->flag; usage << flagPrefix << option->flag;
else else
usage << optPrefix << option->name; usage << optPrefix << option->name;
if (option->type == otOption) if (option->type == otOption)
usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar; usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar;
usage << "]"; usage << "]";
} }
// arguments // arguments
QListIterator<PositionalDef *> it2(m_positionals); QListIterator<PositionalDef *> it2(m_positionals);
while (it2.hasNext()) while (it2.hasNext())
{ {
PositionalDef *param = it2.next(); PositionalDef *param = it2.next();
usage << " " << (param->required ? "<" : "["); usage << " " << (param->required ? "<" : "[");
usage << param->metavar; usage << param->metavar;
usage << (param->required ? ">" : "]"); usage << (param->required ? ">" : "]");
} }
return usage.join(""); return usage.join("");
} }
// parsing // parsing
QHash<QString, QVariant> Parser::parse(QStringList argv) QHash<QString, QVariant> Parser::parse(QStringList argv)
{ {
QHash<QString, QVariant> map; QHash<QString, QVariant> map;
QStringListIterator it(argv); QStringListIterator it(argv);
QString programName = it.next(); QString programName = it.next();
QString optionPrefix; QString optionPrefix;
QString flagPrefix; QString flagPrefix;
QListIterator<PositionalDef *> positionals(m_positionals); QListIterator<PositionalDef *> positionals(m_positionals);
QStringList expecting; QStringList expecting;
getPrefix(optionPrefix, flagPrefix); getPrefix(optionPrefix, flagPrefix);
while (it.hasNext()) while (it.hasNext())
{ {
QString arg = it.next(); QString arg = it.next();
if (!expecting.isEmpty()) if (!expecting.isEmpty())
// we were expecting an argument // we were expecting an argument
{ {
QString name = expecting.first(); QString name = expecting.first();
/* /*
if (map.contains(name)) if (map.contains(name))
throw ParsingError( throw ParsingError(
QString("Option %2%1 was given multiple times").arg(name, optionPrefix)); QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
*/ */
map[name] = QVariant(arg); map[name] = QVariant(arg);
expecting.removeFirst(); expecting.removeFirst();
continue; continue;
} }
if (arg.startsWith(optionPrefix)) if (arg.startsWith(optionPrefix))
// we have an option // we have an option
{ {
// qDebug("Found option %s", qPrintable(arg)); // qDebug("Found option %s", qPrintable(arg));
QString name = arg.mid(optionPrefix.length()); QString name = arg.mid(optionPrefix.length());
QString equals; QString equals;
if ((m_argStyle == ArgumentStyle::Equals || if ((m_argStyle == ArgumentStyle::Equals ||
m_argStyle == ArgumentStyle::SpaceAndEquals) && m_argStyle == ArgumentStyle::SpaceAndEquals) &&
name.contains("=")) name.contains("="))
{ {
int i = name.indexOf("="); int i = name.indexOf("=");
equals = name.mid(i + 1); equals = name.mid(i + 1);
name = name.left(i); name = name.left(i);
} }
if (m_options.contains(name)) if (m_options.contains(name))
{ {
/* /*
if (map.contains(name)) if (map.contains(name))
throw ParsingError(QString("Option %2%1 was given multiple times") throw ParsingError(QString("Option %2%1 was given multiple times")
.arg(name, optionPrefix)); .arg(name, optionPrefix));
*/ */
OptionDef *option = m_options[name]; OptionDef *option = m_options[name];
if (option->type == otSwitch) if (option->type == otSwitch)
map[name] = true; map[name] = true;
else // if (option->type == otOption) else // if (option->type == otOption)
{ {
if (m_argStyle == ArgumentStyle::Space) if (m_argStyle == ArgumentStyle::Space)
expecting.append(name); expecting.append(name);
else if (!equals.isNull()) else if (!equals.isNull())
map[name] = equals; map[name] = equals;
else if (m_argStyle == ArgumentStyle::SpaceAndEquals) else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
expecting.append(name); expecting.append(name);
else else
throw ParsingError(QString("Option %2%1 reqires an argument.") throw ParsingError(QString("Option %2%1 reqires an argument.")
.arg(name, optionPrefix)); .arg(name, optionPrefix));
} }
continue; continue;
} }
throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix)); throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix));
} }
if (arg.startsWith(flagPrefix)) if (arg.startsWith(flagPrefix))
// we have (a) flag(s) // we have (a) flag(s)
{ {
// qDebug("Found flags %s", qPrintable(arg)); // qDebug("Found flags %s", qPrintable(arg));
QString flags = arg.mid(flagPrefix.length()); QString flags = arg.mid(flagPrefix.length());
QString equals; QString equals;
if ((m_argStyle == ArgumentStyle::Equals || if ((m_argStyle == ArgumentStyle::Equals ||
m_argStyle == ArgumentStyle::SpaceAndEquals) && m_argStyle == ArgumentStyle::SpaceAndEquals) &&
flags.contains("=")) flags.contains("="))
{ {
int i = flags.indexOf("="); int i = flags.indexOf("=");
equals = flags.mid(i + 1); equals = flags.mid(i + 1);
flags = flags.left(i); flags = flags.left(i);
} }
for (int i = 0; i < flags.length(); i++) for (int i = 0; i < flags.length(); i++)
{ {
QChar flag = flags.at(i); QChar flag = flags.at(i);
if (!m_flags.contains(flag)) if (!m_flags.contains(flag))
throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix)); throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix));
OptionDef *option = m_flags[flag]; OptionDef *option = m_flags[flag];
/* /*
if (map.contains(option->name)) if (map.contains(option->name))
throw ParsingError(QString("Option %2%1 was given multiple times") throw ParsingError(QString("Option %2%1 was given multiple times")
.arg(option->name, optionPrefix)); .arg(option->name, optionPrefix));
*/ */
if (option->type == otSwitch) if (option->type == otSwitch)
map[option->name] = true; map[option->name] = true;
else // if (option->type == otOption) else // if (option->type == otOption)
{ {
if (m_argStyle == ArgumentStyle::Space) if (m_argStyle == ArgumentStyle::Space)
expecting.append(option->name); expecting.append(option->name);
else if (!equals.isNull()) else if (!equals.isNull())
if (i == flags.length() - 1) if (i == flags.length() - 1)
map[option->name] = equals; map[option->name] = equals;
else else
throw ParsingError(QString("Flag %4%2 of Argument-requiring Option " throw ParsingError(QString("Flag %4%2 of Argument-requiring Option "
"%1 not last flag in %4%3") "%1 not last flag in %4%3")
.arg(option->name, flag, flags, flagPrefix)); .arg(option->name, flag, flags, flagPrefix));
else if (m_argStyle == ArgumentStyle::SpaceAndEquals) else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
expecting.append(option->name); expecting.append(option->name);
else else
throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)") throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)")
.arg(option->name, flag, flagPrefix)); .arg(option->name, flag, flagPrefix));
} }
} }
continue; continue;
} }
// must be a positional argument // must be a positional argument
if (!positionals.hasNext()) if (!positionals.hasNext())
throw ParsingError(QString("Don't know what to do with '%1'").arg(arg)); throw ParsingError(QString("Don't know what to do with '%1'").arg(arg));
PositionalDef *param = positionals.next(); PositionalDef *param = positionals.next();
map[param->name] = arg; map[param->name] = arg;
} }
// check if we're missing something // check if we're missing something
if (!expecting.isEmpty()) if (!expecting.isEmpty())
throw ParsingError(QString("Was still expecting arguments for %2%1").arg( throw ParsingError(QString("Was still expecting arguments for %2%1").arg(
expecting.join(QString(", ") + optionPrefix), optionPrefix)); expecting.join(QString(", ") + optionPrefix), optionPrefix));
while (positionals.hasNext()) while (positionals.hasNext())
{ {
PositionalDef *param = positionals.next(); PositionalDef *param = positionals.next();
if (param->required) if (param->required)
throw ParsingError( throw ParsingError(
QString("Missing required positional argument '%1'").arg(param->name)); QString("Missing required positional argument '%1'").arg(param->name));
else else
map[param->name] = param->def; map[param->name] = param->def;
} }
// fill out gaps // fill out gaps
QListIterator<OptionDef *> iter(m_optionList); QListIterator<OptionDef *> iter(m_optionList);
while (iter.hasNext()) while (iter.hasNext())
{ {
OptionDef *option = iter.next(); OptionDef *option = iter.next();
if (!map.contains(option->name)) if (!map.contains(option->name))
map[option->name] = option->def; map[option->name] = option->def;
} }
return map; return map;
} }
// clear defs // clear defs
void Parser::clear() void Parser::clear()
{ {
m_flags.clear(); m_flags.clear();
m_params.clear(); m_params.clear();
m_options.clear(); m_options.clear();
QMutableListIterator<OptionDef *> it(m_optionList); QMutableListIterator<OptionDef *> it(m_optionList);
while (it.hasNext()) while (it.hasNext())
{ {
OptionDef *option = it.next(); OptionDef *option = it.next();
it.remove(); it.remove();
delete option; delete option;
} }
QMutableListIterator<PositionalDef *> it2(m_positionals); QMutableListIterator<PositionalDef *> it2(m_positionals);
while (it2.hasNext()) while (it2.hasNext())
{ {
PositionalDef *arg = it2.next(); PositionalDef *arg = it2.next();
it2.remove(); it2.remove();
delete arg; delete arg;
} }
} }
// Destructor // Destructor
Parser::~Parser() Parser::~Parser()
{ {
clear(); clear();
} }
// getPrefix // getPrefix
void Parser::getPrefix(QString &opt, QString &flag) void Parser::getPrefix(QString &opt, QString &flag)
{ {
if (m_flagStyle == FlagStyle::Windows) if (m_flagStyle == FlagStyle::Windows)
opt = flag = "/"; opt = flag = "/";
else if (m_flagStyle == FlagStyle::Unix) else if (m_flagStyle == FlagStyle::Unix)
opt = flag = "-"; opt = flag = "-";
// else if (m_flagStyle == FlagStyle::GNU) // else if (m_flagStyle == FlagStyle::GNU)
else else
{ {
opt = "--"; opt = "--";
flag = "-"; flag = "-";
} }
} }
// ParsingError // ParsingError

View File

@ -51,13 +51,13 @@ namespace FlagStyle
{ {
enum Enum enum Enum
{ {
GNU, /**< --option and -o (GNU Style) */ GNU, /**< --option and -o (GNU Style) */
Unix, /**< -option and -o (Unix Style) */ Unix, /**< -option and -o (Unix Style) */
Windows, /**< /option and /o (Windows Style) */ Windows, /**< /option and /o (Windows Style) */
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
Default = Windows Default = Windows
#else #else
Default = GNU Default = GNU
#endif #endif
}; };
} }
@ -69,13 +69,13 @@ namespace ArgumentStyle
{ {
enum Enum enum Enum
{ {
Space, /**< --option=value */ Space, /**< --option=value */
Equals, /**< --option value */ Equals, /**< --option value */
SpaceAndEquals, /**< --option[= ]value */ SpaceAndEquals, /**< --option[= ]value */
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
Default = Equals Default = Equals
#else #else
Default = SpaceAndEquals Default = SpaceAndEquals
#endif #endif
}; };
} }
@ -86,7 +86,7 @@ enum Enum
class MULTIMC_LOGIC_EXPORT ParsingError : public std::runtime_error class MULTIMC_LOGIC_EXPORT ParsingError : public std::runtime_error
{ {
public: public:
ParsingError(const QString &what); ParsingError(const QString &what);
}; };
/** /**
@ -95,158 +95,158 @@ public:
class MULTIMC_LOGIC_EXPORT Parser class MULTIMC_LOGIC_EXPORT Parser
{ {
public: public:
/** /**
* @brief Parser constructor * @brief Parser constructor
* @param flagStyle the FlagStyle to use in this Parser * @param flagStyle the FlagStyle to use in this Parser
* @param argStyle the ArgumentStyle to use in this Parser * @param argStyle the ArgumentStyle to use in this Parser
*/ */
Parser(FlagStyle::Enum flagStyle = FlagStyle::Default, Parser(FlagStyle::Enum flagStyle = FlagStyle::Default,
ArgumentStyle::Enum argStyle = ArgumentStyle::Default); ArgumentStyle::Enum argStyle = ArgumentStyle::Default);
/** /**
* @brief set the flag style * @brief set the flag style
* @param style * @param style
*/ */
void setFlagStyle(FlagStyle::Enum style); void setFlagStyle(FlagStyle::Enum style);
/** /**
* @brief get the flag style * @brief get the flag style
* @return * @return
*/ */
FlagStyle::Enum flagStyle(); FlagStyle::Enum flagStyle();
/** /**
* @brief set the argument style * @brief set the argument style
* @param style * @param style
*/ */
void setArgumentStyle(ArgumentStyle::Enum style); void setArgumentStyle(ArgumentStyle::Enum style);
/** /**
* @brief get the argument style * @brief get the argument style
* @return * @return
*/ */
ArgumentStyle::Enum argumentStyle(); ArgumentStyle::Enum argumentStyle();
/** /**
* @brief define a boolean switch * @brief define a boolean switch
* @param name the parameter name * @param name the parameter name
* @param def the default value * @param def the default value
*/ */
void addSwitch(QString name, bool def = false); void addSwitch(QString name, bool def = false);
/** /**
* @brief define an option that takes an additional argument * @brief define an option that takes an additional argument
* @param name the parameter name * @param name the parameter name
* @param def the default value * @param def the default value
*/ */
void addOption(QString name, QVariant def = QVariant()); void addOption(QString name, QVariant def = QVariant());
/** /**
* @brief define a positional argument * @brief define a positional argument
* @param name the parameter name * @param name the parameter name
* @param required wether this argument is required * @param required wether this argument is required
* @param def the default value * @param def the default value
*/ */
void addArgument(QString name, bool required = true, QVariant def = QVariant()); void addArgument(QString name, bool required = true, QVariant def = QVariant());
/** /**
* @brief adds a flag to an existing parameter * @brief adds a flag to an existing parameter
* @param name the (existing) parameter name * @param name the (existing) parameter name
* @param flag the flag character * @param flag the flag character
* @see addSwitch addArgument addOption * @see addSwitch addArgument addOption
* Note: any one parameter can only have one flag * Note: any one parameter can only have one flag
*/ */
void addShortOpt(QString name, QChar flag); void addShortOpt(QString name, QChar flag);
/** /**
* @brief adds documentation to a Parameter * @brief adds documentation to a Parameter
* @param name the parameter name * @param name the parameter name
* @param metavar a string to be displayed as placeholder for the value * @param metavar a string to be displayed as placeholder for the value
* @param doc a QString containing the documentation * @param doc a QString containing the documentation
* Note: on positional arguments, metavar replaces the name as displayed. * Note: on positional arguments, metavar replaces the name as displayed.
* on options , metavar replaces the value placeholder * on options , metavar replaces the value placeholder
*/ */
void addDocumentation(QString name, QString doc, QString metavar = QString()); void addDocumentation(QString name, QString doc, QString metavar = QString());
/** /**
* @brief generate a help message * @brief generate a help message
* @param progName the program name to use in the help message * @param progName the program name to use in the help message
* @param helpIndent how much the parameter documentation should be indented * @param helpIndent how much the parameter documentation should be indented
* @param flagsInUsage whether we should use flags instead of options in the usage * @param flagsInUsage whether we should use flags instead of options in the usage
* @return a help message * @return a help message
*/ */
QString compileHelp(QString progName, int helpIndent = 22, bool flagsInUsage = true); QString compileHelp(QString progName, int helpIndent = 22, bool flagsInUsage = true);
/** /**
* @brief generate a short usage message * @brief generate a short usage message
* @param progName the program name to use in the usage message * @param progName the program name to use in the usage message
* @param useFlags whether we should use flags instead of options * @param useFlags whether we should use flags instead of options
* @return a usage message * @return a usage message
*/ */
QString compileUsage(QString progName, bool useFlags = true); QString compileUsage(QString progName, bool useFlags = true);
/** /**
* @brief parse * @brief parse
* @param argv a QStringList containing the program ARGV * @param argv a QStringList containing the program ARGV
* @return a QHash mapping argument names to their values * @return a QHash mapping argument names to their values
*/ */
QHash<QString, QVariant> parse(QStringList argv); QHash<QString, QVariant> parse(QStringList argv);
/** /**
* @brief clear all definitions * @brief clear all definitions
*/ */
void clear(); void clear();
~Parser(); ~Parser();
private: private:
FlagStyle::Enum m_flagStyle; FlagStyle::Enum m_flagStyle;
ArgumentStyle::Enum m_argStyle; ArgumentStyle::Enum m_argStyle;
enum OptionType enum OptionType
{ {
otSwitch, otSwitch,
otOption otOption
}; };
// Important: the common part MUST BE COMMON ON ALL THREE structs // Important: the common part MUST BE COMMON ON ALL THREE structs
struct CommonDef struct CommonDef
{ {
QString name; QString name;
QString doc; QString doc;
QString metavar; QString metavar;
QVariant def; QVariant def;
}; };
struct OptionDef struct OptionDef
{ {
// common // common
QString name; QString name;
QString doc; QString doc;
QString metavar; QString metavar;
QVariant def; QVariant def;
// option // option
OptionType type; OptionType type;
QChar flag; QChar flag;
}; };
struct PositionalDef struct PositionalDef
{ {
// common // common
QString name; QString name;
QString doc; QString doc;
QString metavar; QString metavar;
QVariant def; QVariant def;
// positional // positional
bool required; bool required;
}; };
QHash<QString, OptionDef *> m_options; QHash<QString, OptionDef *> m_options;
QHash<QChar, OptionDef *> m_flags; QHash<QChar, OptionDef *> m_flags;
QHash<QString, CommonDef *> m_params; QHash<QString, CommonDef *> m_params;
QList<PositionalDef *> m_positionals; QList<PositionalDef *> m_positionals;
QList<OptionDef *> m_optionList; QList<OptionDef *> m_optionList;
void getPrefix(QString &opt, QString &flag); void getPrefix(QString &opt, QString &flag);
}; };
} }

View File

@ -4,32 +4,32 @@ template <typename T>
class DefaultVariable class DefaultVariable
{ {
public: public:
DefaultVariable(const T & value) DefaultVariable(const T & value)
{ {
defaultValue = value; defaultValue = value;
} }
DefaultVariable<T> & operator =(const T & value) DefaultVariable<T> & operator =(const T & value)
{ {
currentValue = value; currentValue = value;
is_default = currentValue == defaultValue; is_default = currentValue == defaultValue;
is_explicit = true; is_explicit = true;
return *this; return *this;
} }
operator const T &() const operator const T &() const
{ {
return is_default ? defaultValue : currentValue; return is_default ? defaultValue : currentValue;
} }
bool isDefault() const bool isDefault() const
{ {
return is_default; return is_default;
} }
bool isExplicit() const bool isExplicit() const
{ {
return is_explicit; return is_explicit;
} }
private: private:
T currentValue; T currentValue;
T defaultValue; T defaultValue;
bool is_default = true; bool is_default = true;
bool is_explicit = false; bool is_explicit = false;
}; };

View File

@ -15,11 +15,11 @@
struct Env::Private struct Env::Private
{ {
QNetworkAccessManager m_qnam; QNetworkAccessManager m_qnam;
shared_qobject_ptr<HttpMetaCache> m_metacache; shared_qobject_ptr<HttpMetaCache> m_metacache;
std::shared_ptr<IIconList> m_iconlist; std::shared_ptr<IIconList> m_iconlist;
shared_qobject_ptr<Meta::Index> m_metadataIndex; shared_qobject_ptr<Meta::Index> m_metadataIndex;
QString m_jarsPath; QString m_jarsPath;
}; };
static Env * instance; static Env * instance;
@ -30,152 +30,152 @@ static Env * instance;
Env::Env() Env::Env()
{ {
d = new Private(); d = new Private();
} }
Env::~Env() Env::~Env()
{ {
delete d; delete d;
} }
Env& Env::Env::getInstance() Env& Env::Env::getInstance()
{ {
if(!instance) if(!instance)
{ {
instance = new Env(); instance = new Env();
} }
return *instance; return *instance;
} }
void Env::dispose() void Env::dispose()
{ {
delete instance; delete instance;
instance = nullptr; instance = nullptr;
} }
shared_qobject_ptr< HttpMetaCache > Env::metacache() shared_qobject_ptr< HttpMetaCache > Env::metacache()
{ {
return d->m_metacache; return d->m_metacache;
} }
QNetworkAccessManager& Env::qnam() const QNetworkAccessManager& Env::qnam() const
{ {
return d->m_qnam; return d->m_qnam;
} }
std::shared_ptr<IIconList> Env::icons() std::shared_ptr<IIconList> Env::icons()
{ {
return d->m_iconlist; return d->m_iconlist;
} }
void Env::registerIconList(std::shared_ptr<IIconList> iconlist) void Env::registerIconList(std::shared_ptr<IIconList> iconlist)
{ {
d->m_iconlist = iconlist; d->m_iconlist = iconlist;
} }
shared_qobject_ptr<Meta::Index> Env::metadataIndex() shared_qobject_ptr<Meta::Index> Env::metadataIndex()
{ {
if (!d->m_metadataIndex) if (!d->m_metadataIndex)
{ {
d->m_metadataIndex.reset(new Meta::Index()); d->m_metadataIndex.reset(new Meta::Index());
} }
return d->m_metadataIndex; return d->m_metadataIndex;
} }
void Env::initHttpMetaCache() void Env::initHttpMetaCache()
{ {
auto &m_metacache = d->m_metacache; auto &m_metacache = d->m_metacache;
m_metacache.reset(new HttpMetaCache("metacache")); m_metacache.reset(new HttpMetaCache("metacache"));
m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath()); m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath()); m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
m_metacache->addBase("versions", QDir("versions").absolutePath()); m_metacache->addBase("versions", QDir("versions").absolutePath());
m_metacache->addBase("libraries", QDir("libraries").absolutePath()); m_metacache->addBase("libraries", QDir("libraries").absolutePath());
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath()); m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath()); m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath()); m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
m_metacache->addBase("general", QDir("cache").absolutePath()); m_metacache->addBase("general", QDir("cache").absolutePath());
m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath()); m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath()); m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
m_metacache->addBase("root", QDir::currentPath()); m_metacache->addBase("root", QDir::currentPath());
m_metacache->addBase("translations", QDir("translations").absolutePath()); m_metacache->addBase("translations", QDir("translations").absolutePath());
m_metacache->addBase("icons", QDir("cache/icons").absolutePath()); m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
m_metacache->addBase("meta", QDir("meta").absolutePath()); m_metacache->addBase("meta", QDir("meta").absolutePath());
m_metacache->Load(); m_metacache->Load();
} }
void Env::updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password) void Env::updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password)
{ {
// Set the application proxy settings. // Set the application proxy settings.
if (proxyTypeStr == "SOCKS5") if (proxyTypeStr == "SOCKS5")
{ {
QNetworkProxy::setApplicationProxy( QNetworkProxy::setApplicationProxy(
QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password)); QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password));
} }
else if (proxyTypeStr == "HTTP") else if (proxyTypeStr == "HTTP")
{ {
QNetworkProxy::setApplicationProxy( QNetworkProxy::setApplicationProxy(
QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password)); QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password));
} }
else if (proxyTypeStr == "None") else if (proxyTypeStr == "None")
{ {
// If we have no proxy set, set no proxy and return. // If we have no proxy set, set no proxy and return.
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy)); QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
} }
else else
{ {
// If we have "Default" selected, set Qt to use the system proxy settings. // If we have "Default" selected, set Qt to use the system proxy settings.
QNetworkProxyFactory::setUseSystemConfiguration(true); QNetworkProxyFactory::setUseSystemConfiguration(true);
} }
qDebug() << "Detecting proxy settings..."; qDebug() << "Detecting proxy settings...";
QNetworkProxy proxy = QNetworkProxy::applicationProxy(); QNetworkProxy proxy = QNetworkProxy::applicationProxy();
d->m_qnam.setProxy(proxy); d->m_qnam.setProxy(proxy);
QString proxyDesc; QString proxyDesc;
if (proxy.type() == QNetworkProxy::NoProxy) if (proxy.type() == QNetworkProxy::NoProxy)
{ {
qDebug() << "Using no proxy is an option!"; qDebug() << "Using no proxy is an option!";
return; return;
} }
switch (proxy.type()) switch (proxy.type())
{ {
case QNetworkProxy::DefaultProxy: case QNetworkProxy::DefaultProxy:
proxyDesc = "Default proxy: "; proxyDesc = "Default proxy: ";
break; break;
case QNetworkProxy::Socks5Proxy: case QNetworkProxy::Socks5Proxy:
proxyDesc = "Socks5 proxy: "; proxyDesc = "Socks5 proxy: ";
break; break;
case QNetworkProxy::HttpProxy: case QNetworkProxy::HttpProxy:
proxyDesc = "HTTP proxy: "; proxyDesc = "HTTP proxy: ";
break; break;
case QNetworkProxy::HttpCachingProxy: case QNetworkProxy::HttpCachingProxy:
proxyDesc = "HTTP caching: "; proxyDesc = "HTTP caching: ";
break; break;
case QNetworkProxy::FtpCachingProxy: case QNetworkProxy::FtpCachingProxy:
proxyDesc = "FTP caching: "; proxyDesc = "FTP caching: ";
break; break;
default: default:
proxyDesc = "DERP proxy: "; proxyDesc = "DERP proxy: ";
break; break;
} }
proxyDesc += QString("%3@%1:%2 pass %4") proxyDesc += QString("%3@%1:%2 pass %4")
.arg(proxy.hostName()) .arg(proxy.hostName())
.arg(proxy.port()) .arg(proxy.port())
.arg(proxy.user()) .arg(proxy.user())
.arg(proxy.password()); .arg(proxy.password());
qDebug() << proxyDesc; qDebug() << proxyDesc;
} }
QString Env::getJarsPath() QString Env::getJarsPath()
{ {
if(d->m_jarsPath.isEmpty()) if(d->m_jarsPath.isEmpty())
{ {
return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars"); return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars");
} }
return d->m_jarsPath; return d->m_jarsPath;
} }
void Env::setJarsPath(const QString& path) void Env::setJarsPath(const QString& path)
{ {
d->m_jarsPath = path; d->m_jarsPath = path;
} }

View File

@ -20,40 +20,40 @@ class Index;
} }
#if defined(ENV) #if defined(ENV)
#undef ENV #undef ENV
#endif #endif
#define ENV (Env::getInstance()) #define ENV (Env::getInstance())
class MULTIMC_LOGIC_EXPORT Env class MULTIMC_LOGIC_EXPORT Env
{ {
friend class MultiMC; friend class MultiMC;
private: private:
struct Private; struct Private;
Env(); Env();
~Env(); ~Env();
static void dispose(); static void dispose();
public: public:
static Env& getInstance(); static Env& getInstance();
QNetworkAccessManager &qnam() const; QNetworkAccessManager &qnam() const;
shared_qobject_ptr<HttpMetaCache> metacache(); shared_qobject_ptr<HttpMetaCache> metacache();
std::shared_ptr<IIconList> icons(); std::shared_ptr<IIconList> icons();
/// init the cache. FIXME: possible future hook point /// init the cache. FIXME: possible future hook point
void initHttpMetaCache(); void initHttpMetaCache();
/// Updates the application proxy settings from the settings object. /// Updates the application proxy settings from the settings object.
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password); void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
void registerIconList(std::shared_ptr<IIconList> iconlist); void registerIconList(std::shared_ptr<IIconList> iconlist);
shared_qobject_ptr<Meta::Index> metadataIndex(); shared_qobject_ptr<Meta::Index> metadataIndex();
QString getJarsPath(); QString getJarsPath();
void setJarsPath(const QString & path); void setJarsPath(const QString & path);
protected: protected:
Private * d; Private * d;
}; };

View File

@ -11,24 +11,24 @@
class MULTIMC_LOGIC_EXPORT Exception : public std::exception class MULTIMC_LOGIC_EXPORT Exception : public std::exception
{ {
public: public:
Exception(const QString &message) : std::exception(), m_message(message) Exception(const QString &message) : std::exception(), m_message(message)
{ {
qCritical() << "Exception:" << message; qCritical() << "Exception:" << message;
} }
Exception(const Exception &other) Exception(const Exception &other)
: std::exception(), m_message(other.cause()) : std::exception(), m_message(other.cause())
{ {
} }
virtual ~Exception() noexcept {} virtual ~Exception() noexcept {}
const char *what() const noexcept const char *what() const noexcept
{ {
return m_message.toLatin1().constData(); return m_message.toLatin1().constData();
} }
QString cause() const QString cause() const
{ {
return m_message; return m_message;
} }
private: private:
QString m_message; QString m_message;
}; };

View File

@ -12,262 +12,262 @@
#include <QTextStream> #include <QTextStream>
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
#include <windows.h> #include <windows.h>
#include <string> #include <string>
#include <sys/utime.h> #include <sys/utime.h>
#include <winnls.h> #include <winnls.h>
#include <shobjidl.h> #include <shobjidl.h>
#include <objbase.h> #include <objbase.h>
#include <objidl.h> #include <objidl.h>
#include <shlguid.h> #include <shlguid.h>
#include <shlobj.h> #include <shlobj.h>
#else #else
#include <utime.h> #include <utime.h>
#endif #endif
namespace FS { namespace FS {
void ensureExists(const QDir &dir) void ensureExists(const QDir &dir)
{ {
if (!QDir().mkpath(dir.absolutePath())) if (!QDir().mkpath(dir.absolutePath()))
{ {
throw FileSystemException("Unable to create folder " + dir.dirName() + " (" + throw FileSystemException("Unable to create folder " + dir.dirName() + " (" +
dir.absolutePath() + ")"); dir.absolutePath() + ")");
} }
} }
void write(const QString &filename, const QByteArray &data) void write(const QString &filename, const QByteArray &data)
{ {
ensureExists(QFileInfo(filename).dir()); ensureExists(QFileInfo(filename).dir());
QSaveFile file(filename); QSaveFile file(filename);
if (!file.open(QSaveFile::WriteOnly)) if (!file.open(QSaveFile::WriteOnly))
{ {
throw FileSystemException("Couldn't open " + filename + " for writing: " + throw FileSystemException("Couldn't open " + filename + " for writing: " +
file.errorString()); file.errorString());
} }
if (data.size() != file.write(data)) if (data.size() != file.write(data))
{ {
throw FileSystemException("Error writing data to " + filename + ": " + throw FileSystemException("Error writing data to " + filename + ": " +
file.errorString()); file.errorString());
} }
if (!file.commit()) if (!file.commit())
{ {
throw FileSystemException("Error while committing data to " + filename + ": " + throw FileSystemException("Error while committing data to " + filename + ": " +
file.errorString()); file.errorString());
} }
} }
QByteArray read(const QString &filename) QByteArray read(const QString &filename)
{ {
QFile file(filename); QFile file(filename);
if (!file.open(QFile::ReadOnly)) if (!file.open(QFile::ReadOnly))
{ {
throw FileSystemException("Unable to open " + filename + " for reading: " + throw FileSystemException("Unable to open " + filename + " for reading: " +
file.errorString()); file.errorString());
} }
const qint64 size = file.size(); const qint64 size = file.size();
QByteArray data(int(size), 0); QByteArray data(int(size), 0);
const qint64 ret = file.read(data.data(), size); const qint64 ret = file.read(data.data(), size);
if (ret == -1 || ret != size) if (ret == -1 || ret != size)
{ {
throw FileSystemException("Error reading data from " + filename + ": " + throw FileSystemException("Error reading data from " + filename + ": " +
file.errorString()); file.errorString());
} }
return data; return data;
} }
bool updateTimestamp(const QString& filename) bool updateTimestamp(const QString& filename)
{ {
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
std::wstring filename_utf_16 = filename.toStdWString(); std::wstring filename_utf_16 = filename.toStdWString();
return (_wutime64(filename_utf_16.c_str(), nullptr) == 0); return (_wutime64(filename_utf_16.c_str(), nullptr) == 0);
#else #else
QByteArray filenameBA = QFile::encodeName(filename); QByteArray filenameBA = QFile::encodeName(filename);
return (utime(filenameBA.data(), nullptr) == 0); return (utime(filenameBA.data(), nullptr) == 0);
#endif #endif
} }
bool ensureFilePathExists(QString filenamepath) bool ensureFilePathExists(QString filenamepath)
{ {
QFileInfo a(filenamepath); QFileInfo a(filenamepath);
QDir dir; QDir dir;
QString ensuredPath = a.path(); QString ensuredPath = a.path();
bool success = dir.mkpath(ensuredPath); bool success = dir.mkpath(ensuredPath);
return success; return success;
} }
bool ensureFolderPathExists(QString foldernamepath) bool ensureFolderPathExists(QString foldernamepath)
{ {
QFileInfo a(foldernamepath); QFileInfo a(foldernamepath);
QDir dir; QDir dir;
QString ensuredPath = a.filePath(); QString ensuredPath = a.filePath();
bool success = dir.mkpath(ensuredPath); bool success = dir.mkpath(ensuredPath);
return success; return success;
} }
bool copy::operator()(const QString &offset) bool copy::operator()(const QString &offset)
{ {
//NOTE always deep copy on windows. the alternatives are too messy. //NOTE always deep copy on windows. the alternatives are too messy.
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
m_followSymlinks = true; m_followSymlinks = true;
#endif #endif
auto src = PathCombine(m_src.absolutePath(), offset); auto src = PathCombine(m_src.absolutePath(), offset);
auto dst = PathCombine(m_dst.absolutePath(), offset); auto dst = PathCombine(m_dst.absolutePath(), offset);
QFileInfo currentSrc(src); QFileInfo currentSrc(src);
if (!currentSrc.exists()) if (!currentSrc.exists())
return false; return false;
if(!m_followSymlinks && currentSrc.isSymLink()) if(!m_followSymlinks && currentSrc.isSymLink())
{ {
qDebug() << "creating symlink" << src << " - " << dst; qDebug() << "creating symlink" << src << " - " << dst;
if (!ensureFilePathExists(dst)) if (!ensureFilePathExists(dst))
{ {
qWarning() << "Cannot create path!"; qWarning() << "Cannot create path!";
return false; return false;
} }
return QFile::link(currentSrc.symLinkTarget(), dst); return QFile::link(currentSrc.symLinkTarget(), dst);
} }
else if(currentSrc.isFile()) else if(currentSrc.isFile())
{ {
qDebug() << "copying file" << src << " - " << dst; qDebug() << "copying file" << src << " - " << dst;
if (!ensureFilePathExists(dst)) if (!ensureFilePathExists(dst))
{ {
qWarning() << "Cannot create path!"; qWarning() << "Cannot create path!";
return false; return false;
} }
return QFile::copy(src, dst); return QFile::copy(src, dst);
} }
else if(currentSrc.isDir()) else if(currentSrc.isDir())
{ {
qDebug() << "recursing" << offset; qDebug() << "recursing" << offset;
if (!ensureFolderPathExists(dst)) if (!ensureFolderPathExists(dst))
{ {
qWarning() << "Cannot create path!"; qWarning() << "Cannot create path!";
return false; return false;
} }
QDir currentDir(src); QDir currentDir(src);
for(auto & f : currentDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) for(auto & f : currentDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System))
{ {
auto inner_offset = PathCombine(offset, f); auto inner_offset = PathCombine(offset, f);
// ignore and skip stuff that matches the blacklist. // ignore and skip stuff that matches the blacklist.
if(m_blacklist && m_blacklist->matches(inner_offset)) if(m_blacklist && m_blacklist->matches(inner_offset))
{ {
continue; continue;
} }
if(!operator()(inner_offset)) if(!operator()(inner_offset))
{ {
qWarning() << "Failed to copy" << inner_offset; qWarning() << "Failed to copy" << inner_offset;
return false; return false;
} }
} }
} }
else else
{ {
qCritical() << "Copy ERROR: Unknown filesystem object:" << src; qCritical() << "Copy ERROR: Unknown filesystem object:" << src;
return false; return false;
} }
return true; return true;
} }
bool deletePath(QString path) bool deletePath(QString path)
{ {
bool OK = true; bool OK = true;
QDir dir(path); QDir dir(path);
if (!dir.exists()) if (!dir.exists())
{ {
return OK; return OK;
} }
auto allEntries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | auto allEntries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden |
QDir::AllDirs | QDir::Files, QDir::AllDirs | QDir::Files,
QDir::DirsFirst); QDir::DirsFirst);
for(auto & info: allEntries) for(auto & info: allEntries)
{ {
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
QString nativePath = QDir::toNativeSeparators(info.absoluteFilePath()); QString nativePath = QDir::toNativeSeparators(info.absoluteFilePath());
auto wString = nativePath.toStdWString(); auto wString = nativePath.toStdWString();
DWORD dwAttrs = GetFileAttributesW(wString.c_str()); DWORD dwAttrs = GetFileAttributesW(wString.c_str());
// Windows: check for junctions, reparse points and other nasty things of that sort // Windows: check for junctions, reparse points and other nasty things of that sort
if(dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT) if(dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT)
{ {
if (info.isFile()) if (info.isFile())
{ {
OK &= QFile::remove(info.absoluteFilePath()); OK &= QFile::remove(info.absoluteFilePath());
} }
else if (info.isDir()) else if (info.isDir())
{ {
OK &= dir.rmdir(info.absoluteFilePath()); OK &= dir.rmdir(info.absoluteFilePath());
} }
} }
#else #else
// We do not trust Qt with reparse points, but do trust it with unix symlinks. // We do not trust Qt with reparse points, but do trust it with unix symlinks.
if(info.isSymLink()) if(info.isSymLink())
{ {
OK &= QFile::remove(info.absoluteFilePath()); OK &= QFile::remove(info.absoluteFilePath());
} }
#endif #endif
else if (info.isDir()) else if (info.isDir())
{ {
OK &= deletePath(info.absoluteFilePath()); OK &= deletePath(info.absoluteFilePath());
} }
else if (info.isFile()) else if (info.isFile())
{ {
OK &= QFile::remove(info.absoluteFilePath()); OK &= QFile::remove(info.absoluteFilePath());
} }
else else
{ {
OK = false; OK = false;
qCritical() << "Delete ERROR: Unknown filesystem object:" << info.absoluteFilePath(); qCritical() << "Delete ERROR: Unknown filesystem object:" << info.absoluteFilePath();
} }
} }
OK &= dir.rmdir(dir.absolutePath()); OK &= dir.rmdir(dir.absolutePath());
return OK; return OK;
} }
QString PathCombine(const QString & path1, const QString & path2) QString PathCombine(const QString & path1, const QString & path2)
{ {
if(!path1.size()) if(!path1.size())
return path2; return path2;
if(!path2.size()) if(!path2.size())
return path1; return path1;
return QDir::cleanPath(path1 + QDir::separator() + path2); return QDir::cleanPath(path1 + QDir::separator() + path2);
} }
QString PathCombine(const QString & path1, const QString & path2, const QString & path3) QString PathCombine(const QString & path1, const QString & path2, const QString & path3)
{ {
return PathCombine(PathCombine(path1, path2), path3); return PathCombine(PathCombine(path1, path2), path3);
} }
QString PathCombine(const QString & path1, const QString & path2, const QString & path3, const QString & path4) QString PathCombine(const QString & path1, const QString & path2, const QString & path3, const QString & path4)
{ {
return PathCombine(PathCombine(path1, path2, path3), path4); return PathCombine(PathCombine(path1, path2, path3), path4);
} }
QString AbsolutePath(QString path) QString AbsolutePath(QString path)
{ {
return QFileInfo(path).absolutePath(); return QFileInfo(path).absolutePath();
} }
QString ResolveExecutable(QString path) QString ResolveExecutable(QString path)
{ {
if (path.isEmpty()) if (path.isEmpty())
{ {
return QString(); return QString();
} }
if(!path.contains('/')) if(!path.contains('/'))
{ {
path = QStandardPaths::findExecutable(path); path = QStandardPaths::findExecutable(path);
} }
QFileInfo pathInfo(path); QFileInfo pathInfo(path);
if(!pathInfo.exists() || !pathInfo.isExecutable()) if(!pathInfo.exists() || !pathInfo.isExecutable())
{ {
return QString(); return QString();
} }
return pathInfo.absoluteFilePath(); return pathInfo.absoluteFilePath();
} }
/** /**
@ -278,66 +278,66 @@ QString ResolveExecutable(QString path)
*/ */
QString NormalizePath(QString path) QString NormalizePath(QString path)
{ {
QDir a = QDir::currentPath(); QDir a = QDir::currentPath();
QString currentAbsolute = a.absolutePath(); QString currentAbsolute = a.absolutePath();
QDir b(path); QDir b(path);
QString newAbsolute = b.absolutePath(); QString newAbsolute = b.absolutePath();
if (newAbsolute.startsWith(currentAbsolute)) if (newAbsolute.startsWith(currentAbsolute))
{ {
return a.relativeFilePath(newAbsolute); return a.relativeFilePath(newAbsolute);
} }
else else
{ {
return newAbsolute; return newAbsolute;
} }
} }
QString badFilenameChars = "\"\\/?<>:*|!"; QString badFilenameChars = "\"\\/?<>:*|!";
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith) QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
{ {
for (int i = 0; i < string.length(); i++) for (int i = 0; i < string.length(); i++)
{ {
if (badFilenameChars.contains(string[i])) if (badFilenameChars.contains(string[i]))
{ {
string[i] = replaceWith; string[i] = replaceWith;
} }
} }
return string; return string;
} }
QString DirNameFromString(QString string, QString inDir) QString DirNameFromString(QString string, QString inDir)
{ {
int num = 0; int num = 0;
QString baseName = RemoveInvalidFilenameChars(string, '-'); QString baseName = RemoveInvalidFilenameChars(string, '-');
QString dirName; QString dirName;
do do
{ {
if(num == 0) if(num == 0)
{ {
dirName = baseName; dirName = baseName;
} }
else else
{ {
dirName = baseName + QString::number(num);; dirName = baseName + QString::number(num);;
} }
// If it's over 9000 // If it's over 9000
if (num > 9000) if (num > 9000)
return ""; return "";
num++; num++;
} while (QFileInfo(PathCombine(inDir, dirName)).exists()); } while (QFileInfo(PathCombine(inDir, dirName)).exists());
return dirName; return dirName;
} }
// Does the folder path contain any '!'? If yes, return true, otherwise false. // Does the folder path contain any '!'? If yes, return true, otherwise false.
// (This is a problem for Java) // (This is a problem for Java)
bool checkProblemticPathJava(QDir folder) bool checkProblemticPathJava(QDir folder)
{ {
QString pathfoldername = folder.absolutePath(); QString pathfoldername = folder.absolutePath();
return pathfoldername.contains("!", Qt::CaseInsensitive); return pathfoldername.contains("!", Qt::CaseInsensitive);
} }
// Win32 crap // Win32 crap
@ -347,106 +347,106 @@ bool called_coinit = false;
HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args) HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args)
{ {
HRESULT hres; HRESULT hres;
if (!called_coinit) if (!called_coinit)
{ {
hres = CoInitialize(NULL); hres = CoInitialize(NULL);
called_coinit = true; called_coinit = true;
if (!SUCCEEDED(hres)) if (!SUCCEEDED(hres))
{ {
qWarning("Failed to initialize COM. Error 0x%08lX", hres); qWarning("Failed to initialize COM. Error 0x%08lX", hres);
return hres; return hres;
} }
} }
IShellLink *link; IShellLink *link;
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink,
(LPVOID *)&link); (LPVOID *)&link);
if (SUCCEEDED(hres)) if (SUCCEEDED(hres))
{ {
IPersistFile *persistFile; IPersistFile *persistFile;
link->SetPath(targetPath); link->SetPath(targetPath);
link->SetArguments(args); link->SetArguments(args);
hres = link->QueryInterface(IID_IPersistFile, (LPVOID *)&persistFile); hres = link->QueryInterface(IID_IPersistFile, (LPVOID *)&persistFile);
if (SUCCEEDED(hres)) if (SUCCEEDED(hres))
{ {
WCHAR wstr[MAX_PATH]; WCHAR wstr[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, linkPath, -1, wstr, MAX_PATH); MultiByteToWideChar(CP_ACP, 0, linkPath, -1, wstr, MAX_PATH);
hres = persistFile->Save(wstr, TRUE); hres = persistFile->Save(wstr, TRUE);
persistFile->Release(); persistFile->Release();
} }
link->Release(); link->Release();
} }
return hres; return hres;
} }
#endif #endif
QString getDesktopDir() QString getDesktopDir()
{ {
return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
} }
// Cross-platform Shortcut creation // Cross-platform Shortcut creation
bool createShortCut(QString location, QString dest, QStringList args, QString name, bool createShortCut(QString location, QString dest, QStringList args, QString name,
QString icon) QString icon)
{ {
#if defined Q_OS_LINUX #if defined Q_OS_LINUX
location = PathCombine(location, name + ".desktop"); location = PathCombine(location, name + ".desktop");
QFile f(location); QFile f(location);
f.open(QIODevice::WriteOnly | QIODevice::Text); f.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&f); QTextStream stream(&f);
QString argstring; QString argstring;
if (!args.empty()) if (!args.empty())
argstring = " '" + args.join("' '") + "'"; argstring = " '" + args.join("' '") + "'";
stream << "[Desktop Entry]" stream << "[Desktop Entry]"
<< "\n"; << "\n";
stream << "Type=Application" stream << "Type=Application"
<< "\n"; << "\n";
stream << "TryExec=" << dest.toLocal8Bit() << "\n"; stream << "TryExec=" << dest.toLocal8Bit() << "\n";
stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n"; stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n";
stream << "Name=" << name.toLocal8Bit() << "\n"; stream << "Name=" << name.toLocal8Bit() << "\n";
stream << "Icon=" << icon.toLocal8Bit() << "\n"; stream << "Icon=" << icon.toLocal8Bit() << "\n";
stream.flush(); stream.flush();
f.close(); f.close();
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup |
QFileDevice::ExeOther); QFileDevice::ExeOther);
return true; return true;
#elif defined Q_OS_WIN #elif defined Q_OS_WIN
// TODO: Fix // TODO: Fix
// QFile file(PathCombine(location, name + ".lnk")); // QFile file(PathCombine(location, name + ".lnk"));
// WCHAR *file_w; // WCHAR *file_w;
// WCHAR *dest_w; // WCHAR *dest_w;
// WCHAR *args_w; // WCHAR *args_w;
// file.fileName().toWCharArray(file_w); // file.fileName().toWCharArray(file_w);
// dest.toWCharArray(dest_w); // dest.toWCharArray(dest_w);
// QString argStr; // QString argStr;
// for (int i = 0; i < args.count(); i++) // for (int i = 0; i < args.count(); i++)
// { // {
// argStr.append(args[i]); // argStr.append(args[i]);
// argStr.append(" "); // argStr.append(" ");
// } // }
// argStr.toWCharArray(args_w); // argStr.toWCharArray(args_w);
// return SUCCEEDED(CreateLink(file_w, dest_w, args_w)); // return SUCCEEDED(CreateLink(file_w, dest_w, args_w));
return false; return false;
#else #else
qWarning("Desktop Shortcuts not supported on your platform!"); qWarning("Desktop Shortcuts not supported on your platform!");
return false; return false;
#endif #endif
} }
} }

View File

@ -15,7 +15,7 @@ namespace FS
class MULTIMC_LOGIC_EXPORT FileSystemException : public ::Exception class MULTIMC_LOGIC_EXPORT FileSystemException : public ::Exception
{ {
public: public:
FileSystemException(const QString &message) : Exception(message) {} FileSystemException(const QString &message) : Exception(message) {}
}; };
/** /**
@ -48,34 +48,34 @@ MULTIMC_LOGIC_EXPORT bool ensureFolderPathExists(QString filenamepath);
class MULTIMC_LOGIC_EXPORT copy class MULTIMC_LOGIC_EXPORT copy
{ {
public: public:
copy(const QString & src, const QString & dst) copy(const QString & src, const QString & dst)
{ {
m_src = src; m_src = src;
m_dst = dst; m_dst = dst;
} }
copy & followSymlinks(const bool follow) copy & followSymlinks(const bool follow)
{ {
m_followSymlinks = follow; m_followSymlinks = follow;
return *this; return *this;
} }
copy & blacklist(const IPathMatcher * filter) copy & blacklist(const IPathMatcher * filter)
{ {
m_blacklist = filter; m_blacklist = filter;
return *this; return *this;
} }
bool operator()() bool operator()()
{ {
return operator()(QString()); return operator()(QString());
} }
private: private:
bool operator()(const QString &offset); bool operator()(const QString &offset);
private: private:
bool m_followSymlinks = true; bool m_followSymlinks = true;
const IPathMatcher * m_blacklist = nullptr; const IPathMatcher * m_blacklist = nullptr;
QDir m_src; QDir m_src;
QDir m_dst; QDir m_dst;
}; };
/** /**

View File

@ -7,155 +7,155 @@
class FileSystemTest : public QObject class FileSystemTest : public QObject
{ {
Q_OBJECT Q_OBJECT
const QString bothSlash = "/foo/"; const QString bothSlash = "/foo/";
const QString trailingSlash = "foo/"; const QString trailingSlash = "foo/";
const QString leadingSlash = "/foo"; const QString leadingSlash = "/foo";
private private
slots: slots:
void test_pathCombine() void test_pathCombine()
{ {
QCOMPARE(QString("/foo/foo"), FS::PathCombine(bothSlash, bothSlash)); QCOMPARE(QString("/foo/foo"), FS::PathCombine(bothSlash, bothSlash));
QCOMPARE(QString("foo/foo"), FS::PathCombine(trailingSlash, trailingSlash)); QCOMPARE(QString("foo/foo"), FS::PathCombine(trailingSlash, trailingSlash));
QCOMPARE(QString("/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash)); QCOMPARE(QString("/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash));
QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(bothSlash, bothSlash, bothSlash)); QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(bothSlash, bothSlash, bothSlash));
QCOMPARE(QString("foo/foo/foo"), FS::PathCombine(trailingSlash, trailingSlash, trailingSlash)); QCOMPARE(QString("foo/foo/foo"), FS::PathCombine(trailingSlash, trailingSlash, trailingSlash));
QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash, leadingSlash)); QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash, leadingSlash));
} }
void test_PathCombine1_data() void test_PathCombine1_data()
{ {
QTest::addColumn<QString>("result"); QTest::addColumn<QString>("result");
QTest::addColumn<QString>("path1"); QTest::addColumn<QString>("path1");
QTest::addColumn<QString>("path2"); QTest::addColumn<QString>("path2");
QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc/def" << "ghi/jkl"; QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc/def" << "ghi/jkl";
QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/def/" << "ghi/jkl"; QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/def/" << "ghi/jkl";
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
QTest::newRow("win native, from C:") << "C:/abc" << "C:" << "abc"; QTest::newRow("win native, from C:") << "C:/abc" << "C:" << "abc";
QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def" << "ghi\\jkl"; QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def" << "ghi\\jkl";
QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def\\" << "ghi\\jkl"; QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def\\" << "ghi\\jkl";
#endif #endif
} }
void test_PathCombine1() void test_PathCombine1()
{ {
QFETCH(QString, result); QFETCH(QString, result);
QFETCH(QString, path1); QFETCH(QString, path1);
QFETCH(QString, path2); QFETCH(QString, path2);
QCOMPARE(FS::PathCombine(path1, path2), result); QCOMPARE(FS::PathCombine(path1, path2), result);
} }
void test_PathCombine2_data() void test_PathCombine2_data()
{ {
QTest::addColumn<QString>("result"); QTest::addColumn<QString>("result");
QTest::addColumn<QString>("path1"); QTest::addColumn<QString>("path1");
QTest::addColumn<QString>("path2"); QTest::addColumn<QString>("path2");
QTest::addColumn<QString>("path3"); QTest::addColumn<QString>("path3");
QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc" << "def" << "ghi/jkl"; QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc" << "def" << "ghi/jkl";
QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/" << "def" << "ghi/jkl"; QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/" << "def" << "ghi/jkl";
QTest::newRow("qt 3") << "/abc/def/ghi/jkl" << "/abc" << "def/" << "ghi/jkl"; QTest::newRow("qt 3") << "/abc/def/ghi/jkl" << "/abc" << "def/" << "ghi/jkl";
QTest::newRow("qt 4") << "/abc/def/ghi/jkl" << "/abc/" << "def/" << "ghi/jkl"; QTest::newRow("qt 4") << "/abc/def/ghi/jkl" << "/abc/" << "def/" << "ghi/jkl";
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def" << "ghi\\jkl"; QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def" << "ghi\\jkl";
QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl"; QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def\\" << "ghi\\jkl"; QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def\\" << "ghi\\jkl";
QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl"; QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
#endif #endif
} }
void test_PathCombine2() void test_PathCombine2()
{ {
QFETCH(QString, result); QFETCH(QString, result);
QFETCH(QString, path1); QFETCH(QString, path1);
QFETCH(QString, path2); QFETCH(QString, path2);
QFETCH(QString, path3); QFETCH(QString, path3);
QCOMPARE(FS::PathCombine(path1, path2, path3), result); QCOMPARE(FS::PathCombine(path1, path2, path3), result);
} }
void test_copy() void test_copy()
{ {
QString folder = QFINDTESTDATA("data/test_folder"); QString folder = QFINDTESTDATA("data/test_folder");
auto f = [&folder]() auto f = [&folder]()
{ {
QTemporaryDir tempDir; QTemporaryDir tempDir;
tempDir.setAutoRemove(true); tempDir.setAutoRemove(true);
qDebug() << "From:" << folder << "To:" << tempDir.path(); qDebug() << "From:" << folder << "To:" << tempDir.path();
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder")); QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
qDebug() << tempDir.path(); qDebug() << tempDir.path();
qDebug() << target_dir.path(); qDebug() << target_dir.path();
FS::copy c(folder, target_dir.path()); FS::copy c(folder, target_dir.path());
c(); c();
for(auto entry: target_dir.entryList()) for(auto entry: target_dir.entryList())
{ {
qDebug() << entry; qDebug() << entry;
} }
QVERIFY(target_dir.entryList().contains("pack.mcmeta")); QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
QVERIFY(target_dir.entryList().contains("assets")); QVERIFY(target_dir.entryList().contains("assets"));
}; };
// first try variant without trailing / // first try variant without trailing /
QVERIFY(!folder.endsWith('/')); QVERIFY(!folder.endsWith('/'));
f(); f();
// then variant with trailing / // then variant with trailing /
folder.append('/'); folder.append('/');
QVERIFY(folder.endsWith('/')); QVERIFY(folder.endsWith('/'));
f(); f();
} }
void test_getDesktop() void test_getDesktop()
{ {
QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
} }
// this is only valid on linux // this is only valid on linux
// FIXME: implement on windows, OSX, then test. // FIXME: implement on windows, OSX, then test.
#if defined(Q_OS_LINUX) #if defined(Q_OS_LINUX)
void test_createShortcut_data() void test_createShortcut_data()
{ {
QTest::addColumn<QString>("location"); QTest::addColumn<QString>("location");
QTest::addColumn<QString>("dest"); QTest::addColumn<QString>("dest");
QTest::addColumn<QStringList>("args"); QTest::addColumn<QStringList>("args");
QTest::addColumn<QString>("name"); QTest::addColumn<QString>("name");
QTest::addColumn<QString>("iconLocation"); QTest::addColumn<QString>("iconLocation");
QTest::addColumn<QByteArray>("result"); QTest::addColumn<QByteArray>("result");
QTest::newRow("unix") << QDir::currentPath() QTest::newRow("unix") << QDir::currentPath()
<< "asdfDest" << "asdfDest"
<< (QStringList() << "arg1" << "arg2") << (QStringList() << "arg1" << "arg2")
<< "asdf" << "asdf"
<< QString() << QString()
#if defined(Q_OS_LINUX) #if defined(Q_OS_LINUX)
<< MULTIMC_GET_TEST_FILE("data/FileSystem-test_createShortcut-unix") << MULTIMC_GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
#elif defined(Q_OS_WIN) #elif defined(Q_OS_WIN)
<< QByteArray() << QByteArray()
#endif #endif
; ;
} }
void test_createShortcut() void test_createShortcut()
{ {
QFETCH(QString, location); QFETCH(QString, location);
QFETCH(QString, dest); QFETCH(QString, dest);
QFETCH(QStringList, args); QFETCH(QStringList, args);
QFETCH(QString, name); QFETCH(QString, name);
QFETCH(QString, iconLocation); QFETCH(QString, iconLocation);
QFETCH(QByteArray, result); QFETCH(QByteArray, result);
QVERIFY(FS::createShortCut(location, dest, args, name, iconLocation)); QVERIFY(FS::createShortCut(location, dest, args, name, iconLocation));
QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result)); QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result));
//QDir().remove(location); //QDir().remove(location);
} }
#endif #endif
}; };

View File

@ -6,26 +6,26 @@ ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern){}
ContainsFilter::~ContainsFilter(){} ContainsFilter::~ContainsFilter(){}
bool ContainsFilter::accepts(const QString& value) bool ContainsFilter::accepts(const QString& value)
{ {
return value.contains(pattern); return value.contains(pattern);
} }
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern){} ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern){}
ExactFilter::~ExactFilter(){} ExactFilter::~ExactFilter(){}
bool ExactFilter::accepts(const QString& value) bool ExactFilter::accepts(const QString& value)
{ {
return value.contains(pattern); return value.contains(pattern);
} }
RegexpFilter::RegexpFilter(const QString& regexp, bool invert) RegexpFilter::RegexpFilter(const QString& regexp, bool invert)
:invert(invert) :invert(invert)
{ {
pattern.setPattern(regexp); pattern.setPattern(regexp);
pattern.optimize(); pattern.optimize();
} }
RegexpFilter::~RegexpFilter(){} RegexpFilter::~RegexpFilter(){}
bool RegexpFilter::accepts(const QString& value) bool RegexpFilter::accepts(const QString& value)
{ {
auto match = pattern.match(value); auto match = pattern.match(value);
bool matched = match.hasMatch(); bool matched = match.hasMatch();
return invert ? (!matched) : (matched); return invert ? (!matched) : (matched);
} }

View File

@ -8,37 +8,37 @@
class MULTIMC_LOGIC_EXPORT Filter class MULTIMC_LOGIC_EXPORT Filter
{ {
public: public:
virtual ~Filter(); virtual ~Filter();
virtual bool accepts(const QString & value) = 0; virtual bool accepts(const QString & value) = 0;
}; };
class MULTIMC_LOGIC_EXPORT ContainsFilter: public Filter class MULTIMC_LOGIC_EXPORT ContainsFilter: public Filter
{ {
public: public:
ContainsFilter(const QString &pattern); ContainsFilter(const QString &pattern);
virtual ~ContainsFilter(); virtual ~ContainsFilter();
bool accepts(const QString & value) override; bool accepts(const QString & value) override;
private: private:
QString pattern; QString pattern;
}; };
class MULTIMC_LOGIC_EXPORT ExactFilter: public Filter class MULTIMC_LOGIC_EXPORT ExactFilter: public Filter
{ {
public: public:
ExactFilter(const QString &pattern); ExactFilter(const QString &pattern);
virtual ~ExactFilter(); virtual ~ExactFilter();
bool accepts(const QString & value) override; bool accepts(const QString & value) override;
private: private:
QString pattern; QString pattern;
}; };
class MULTIMC_LOGIC_EXPORT RegexpFilter: public Filter class MULTIMC_LOGIC_EXPORT RegexpFilter: public Filter
{ {
public: public:
RegexpFilter(const QString &regexp, bool invert); RegexpFilter(const QString &regexp, bool invert);
virtual ~RegexpFilter(); virtual ~RegexpFilter();
bool accepts(const QString & value) override; bool accepts(const QString & value) override;
private: private:
QRegularExpression pattern; QRegularExpression pattern;
bool invert = false; bool invert = false;
}; };

View File

@ -18,322 +18,322 @@ const static int GROUP_FILE_FORMAT_VERSION = 1;
struct WatchLock struct WatchLock
{ {
WatchLock(QFileSystemWatcher * watcher, const QString& instDir) WatchLock(QFileSystemWatcher * watcher, const QString& instDir)
: m_watcher(watcher), m_instDir(instDir) : m_watcher(watcher), m_instDir(instDir)
{ {
m_watcher->removePath(m_instDir); m_watcher->removePath(m_instDir);
} }
~WatchLock() ~WatchLock()
{ {
m_watcher->addPath(m_instDir); m_watcher->addPath(m_instDir);
} }
QFileSystemWatcher * m_watcher; QFileSystemWatcher * m_watcher;
QString m_instDir; QString m_instDir;
}; };
FolderInstanceProvider::FolderInstanceProvider(SettingsObjectPtr settings, const QString& instDir) FolderInstanceProvider::FolderInstanceProvider(SettingsObjectPtr settings, const QString& instDir)
: BaseInstanceProvider(settings) : BaseInstanceProvider(settings)
{ {
// Create aand normalize path // Create aand normalize path
if (!QDir::current().exists(instDir)) if (!QDir::current().exists(instDir))
{ {
QDir::current().mkpath(instDir); QDir::current().mkpath(instDir);
} }
// NOTE: canonicalPath requires the path to exist. Do not move this above the creation block! // NOTE: canonicalPath requires the path to exist. Do not move this above the creation block!
m_instDir = QDir(instDir).canonicalPath(); m_instDir = QDir(instDir).canonicalPath();
m_watcher = new QFileSystemWatcher(this); m_watcher = new QFileSystemWatcher(this);
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &FolderInstanceProvider::instanceDirContentsChanged); connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &FolderInstanceProvider::instanceDirContentsChanged);
m_watcher->addPath(m_instDir); m_watcher->addPath(m_instDir);
} }
QList< InstanceId > FolderInstanceProvider::discoverInstances() QList< InstanceId > FolderInstanceProvider::discoverInstances()
{ {
QList<InstanceId> out; QList<InstanceId> out;
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks); QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks);
while (iter.hasNext()) while (iter.hasNext())
{ {
QString subDir = iter.next(); QString subDir = iter.next();
QFileInfo dirInfo(subDir); QFileInfo dirInfo(subDir);
if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists()) if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists())
continue; continue;
// if it is a symlink, ignore it if it goes to the instance folder // if it is a symlink, ignore it if it goes to the instance folder
if(dirInfo.isSymLink()) if(dirInfo.isSymLink())
{ {
QFileInfo targetInfo(dirInfo.symLinkTarget()); QFileInfo targetInfo(dirInfo.symLinkTarget());
QFileInfo instDirInfo(m_instDir); QFileInfo instDirInfo(m_instDir);
if(targetInfo.canonicalPath() == instDirInfo.canonicalFilePath()) if(targetInfo.canonicalPath() == instDirInfo.canonicalFilePath())
{ {
qDebug() << "Ignoring symlink" << subDir << "that leads into the instances folder"; qDebug() << "Ignoring symlink" << subDir << "that leads into the instances folder";
continue; continue;
} }
} }
auto id = dirInfo.fileName(); auto id = dirInfo.fileName();
out.append(id); out.append(id);
qDebug() << "Found instance ID" << id; qDebug() << "Found instance ID" << id;
} }
return out; return out;
} }
InstancePtr FolderInstanceProvider::loadInstance(const InstanceId& id) InstancePtr FolderInstanceProvider::loadInstance(const InstanceId& id)
{ {
if(!m_groupsLoaded) if(!m_groupsLoaded)
{ {
loadGroupList(); loadGroupList();
} }
auto instanceRoot = FS::PathCombine(m_instDir, id); auto instanceRoot = FS::PathCombine(m_instDir, id);
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg")); auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
InstancePtr inst; InstancePtr inst;
instanceSettings->registerSetting("InstanceType", "Legacy"); instanceSettings->registerSetting("InstanceType", "Legacy");
QString inst_type = instanceSettings->get("InstanceType").toString(); QString inst_type = instanceSettings->get("InstanceType").toString();
if (inst_type == "OneSix" || inst_type == "Nostalgia") if (inst_type == "OneSix" || inst_type == "Nostalgia")
{ {
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot)); inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
} }
else if (inst_type == "Legacy") else if (inst_type == "Legacy")
{ {
inst.reset(new LegacyInstance(m_globalSettings, instanceSettings, instanceRoot)); inst.reset(new LegacyInstance(m_globalSettings, instanceSettings, instanceRoot));
} }
else else
{ {
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot)); inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
} }
inst->init(); inst->init();
inst->setProvider(this); inst->setProvider(this);
auto iter = groupMap.find(id); auto iter = groupMap.find(id);
if (iter != groupMap.end()) if (iter != groupMap.end())
{ {
inst->setGroupInitial((*iter)); inst->setGroupInitial((*iter));
} }
connect(inst.get(), &BaseInstance::groupChanged, this, &FolderInstanceProvider::groupChanged); connect(inst.get(), &BaseInstance::groupChanged, this, &FolderInstanceProvider::groupChanged);
qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot(); qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
return inst; return inst;
} }
void FolderInstanceProvider::saveGroupList() void FolderInstanceProvider::saveGroupList()
{ {
WatchLock foo(m_watcher, m_instDir); WatchLock foo(m_watcher, m_instDir);
QString groupFileName = m_instDir + "/instgroups.json"; QString groupFileName = m_instDir + "/instgroups.json";
QMap<QString, QSet<QString>> reverseGroupMap; QMap<QString, QSet<QString>> reverseGroupMap;
for (auto iter = groupMap.begin(); iter != groupMap.end(); iter++) for (auto iter = groupMap.begin(); iter != groupMap.end(); iter++)
{ {
QString id = iter.key(); QString id = iter.key();
QString group = iter.value(); QString group = iter.value();
if (group.isEmpty()) if (group.isEmpty())
continue; continue;
if (!reverseGroupMap.count(group)) if (!reverseGroupMap.count(group))
{ {
QSet<QString> set; QSet<QString> set;
set.insert(id); set.insert(id);
reverseGroupMap[group] = set; reverseGroupMap[group] = set;
} }
else else
{ {
QSet<QString> &set = reverseGroupMap[group]; QSet<QString> &set = reverseGroupMap[group];
set.insert(id); set.insert(id);
} }
} }
QJsonObject toplevel; QJsonObject toplevel;
toplevel.insert("formatVersion", QJsonValue(QString("1"))); toplevel.insert("formatVersion", QJsonValue(QString("1")));
QJsonObject groupsArr; QJsonObject groupsArr;
for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++) for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++)
{ {
auto list = iter.value(); auto list = iter.value();
auto name = iter.key(); auto name = iter.key();
QJsonObject groupObj; QJsonObject groupObj;
QJsonArray instanceArr; QJsonArray instanceArr;
groupObj.insert("hidden", QJsonValue(QString("false"))); groupObj.insert("hidden", QJsonValue(QString("false")));
for (auto item : list) for (auto item : list)
{ {
instanceArr.append(QJsonValue(item)); instanceArr.append(QJsonValue(item));
} }
groupObj.insert("instances", instanceArr); groupObj.insert("instances", instanceArr);
groupsArr.insert(name, groupObj); groupsArr.insert(name, groupObj);
} }
toplevel.insert("groups", groupsArr); toplevel.insert("groups", groupsArr);
QJsonDocument doc(toplevel); QJsonDocument doc(toplevel);
try try
{ {
FS::write(groupFileName, doc.toJson()); FS::write(groupFileName, doc.toJson());
} }
catch (const FS::FileSystemException &e) catch (const FS::FileSystemException &e)
{ {
qCritical() << "Failed to write instance group file :" << e.cause(); qCritical() << "Failed to write instance group file :" << e.cause();
} }
} }
void FolderInstanceProvider::loadGroupList() void FolderInstanceProvider::loadGroupList()
{ {
QSet<QString> groupSet; QSet<QString> groupSet;
QString groupFileName = m_instDir + "/instgroups.json"; QString groupFileName = m_instDir + "/instgroups.json";
// if there's no group file, fail // if there's no group file, fail
if (!QFileInfo(groupFileName).exists()) if (!QFileInfo(groupFileName).exists())
return; return;
QByteArray jsonData; QByteArray jsonData;
try try
{ {
jsonData = FS::read(groupFileName); jsonData = FS::read(groupFileName);
} }
catch (const FS::FileSystemException &e) catch (const FS::FileSystemException &e)
{ {
qCritical() << "Failed to read instance group file :" << e.cause(); qCritical() << "Failed to read instance group file :" << e.cause();
return; return;
} }
QJsonParseError error; QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error); QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
// if the json was bad, fail // if the json was bad, fail
if (error.error != QJsonParseError::NoError) if (error.error != QJsonParseError::NoError)
{ {
qCritical() << QString("Failed to parse instance group file: %1 at offset %2") qCritical() << QString("Failed to parse instance group file: %1 at offset %2")
.arg(error.errorString(), QString::number(error.offset)) .arg(error.errorString(), QString::number(error.offset))
.toUtf8(); .toUtf8();
return; return;
} }
// if the root of the json wasn't an object, fail // if the root of the json wasn't an object, fail
if (!jsonDoc.isObject()) if (!jsonDoc.isObject())
{ {
qWarning() << "Invalid group file. Root entry should be an object."; qWarning() << "Invalid group file. Root entry should be an object.";
return; return;
} }
QJsonObject rootObj = jsonDoc.object(); QJsonObject rootObj = jsonDoc.object();
// Make sure the format version matches, otherwise fail. // Make sure the format version matches, otherwise fail.
if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION) if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION)
return; return;
// Get the groups. if it's not an object, fail // Get the groups. if it's not an object, fail
if (!rootObj.value("groups").isObject()) if (!rootObj.value("groups").isObject())
{ {
qWarning() << "Invalid group list JSON: 'groups' should be an object."; qWarning() << "Invalid group list JSON: 'groups' should be an object.";
return; return;
} }
groupMap.clear(); groupMap.clear();
// Iterate through all the groups. // Iterate through all the groups.
QJsonObject groupMapping = rootObj.value("groups").toObject(); QJsonObject groupMapping = rootObj.value("groups").toObject();
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++)
{ {
QString groupName = iter.key(); QString groupName = iter.key();
// If not an object, complain and skip to the next one. // If not an object, complain and skip to the next one.
if (!iter.value().isObject()) if (!iter.value().isObject())
{ {
qWarning() << QString("Group '%1' in the group list should " qWarning() << QString("Group '%1' in the group list should "
"be an object.") "be an object.")
.arg(groupName) .arg(groupName)
.toUtf8(); .toUtf8();
continue; continue;
} }
QJsonObject groupObj = iter.value().toObject(); QJsonObject groupObj = iter.value().toObject();
if (!groupObj.value("instances").isArray()) if (!groupObj.value("instances").isArray())
{ {
qWarning() << QString("Group '%1' in the group list is invalid. " qWarning() << QString("Group '%1' in the group list is invalid. "
"It should contain an array " "It should contain an array "
"called 'instances'.") "called 'instances'.")
.arg(groupName) .arg(groupName)
.toUtf8(); .toUtf8();
continue; continue;
} }
// keep a list/set of groups for choosing // keep a list/set of groups for choosing
groupSet.insert(groupName); groupSet.insert(groupName);
// Iterate through the list of instances in the group. // Iterate through the list of instances in the group.
QJsonArray instancesArray = groupObj.value("instances").toArray(); QJsonArray instancesArray = groupObj.value("instances").toArray();
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end();
iter2++) iter2++)
{ {
groupMap[(*iter2).toString()] = groupName; groupMap[(*iter2).toString()] = groupName;
} }
} }
m_groupsLoaded = true; m_groupsLoaded = true;
emit groupsChanged(groupSet); emit groupsChanged(groupSet);
} }
void FolderInstanceProvider::groupChanged() void FolderInstanceProvider::groupChanged()
{ {
// save the groups. save all of them. // save the groups. save all of them.
auto instance = (BaseInstance *) QObject::sender(); auto instance = (BaseInstance *) QObject::sender();
auto id = instance->id(); auto id = instance->id();
groupMap[id] = instance->group(); groupMap[id] = instance->group();
emit groupsChanged({instance->group()}); emit groupsChanged({instance->group()});
saveGroupList(); saveGroupList();
} }
void FolderInstanceProvider::instanceDirContentsChanged(const QString& path) void FolderInstanceProvider::instanceDirContentsChanged(const QString& path)
{ {
Q_UNUSED(path); Q_UNUSED(path);
emit instancesChanged(); emit instancesChanged();
} }
void FolderInstanceProvider::on_InstFolderChanged(const Setting &setting, QVariant value) void FolderInstanceProvider::on_InstFolderChanged(const Setting &setting, QVariant value)
{ {
QString newInstDir = QDir(value.toString()).canonicalPath(); QString newInstDir = QDir(value.toString()).canonicalPath();
if(newInstDir != m_instDir) if(newInstDir != m_instDir)
{ {
if(m_groupsLoaded) if(m_groupsLoaded)
{ {
saveGroupList(); saveGroupList();
} }
m_instDir = newInstDir; m_instDir = newInstDir;
m_groupsLoaded = false; m_groupsLoaded = false;
emit instancesChanged(); emit instancesChanged();
} }
} }
template <typename T> template <typename T>
static void clamp(T& current, T min, T max) static void clamp(T& current, T min, T max)
{ {
if (current < min) if (current < min)
{ {
current = min; current = min;
} }
else if(current > max) else if(current > max)
{ {
current = max; current = max;
} }
} }
// List of numbers from min to max. Next is exponent times bigger than previous. // List of numbers from min to max. Next is exponent times bigger than previous.
class ExponentialSeries class ExponentialSeries
{ {
public: public:
ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2) ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2)
{ {
m_current = m_min = min; m_current = m_min = min;
m_max = max; m_max = max;
m_exponent = exponent; m_exponent = exponent;
} }
void reset() void reset()
{ {
m_current = m_min; m_current = m_min;
} }
unsigned operator()() unsigned operator()()
{ {
unsigned retval = m_current; unsigned retval = m_current;
m_current *= m_exponent; m_current *= m_exponent;
clamp(m_current, m_min, m_max); clamp(m_current, m_min, m_max);
return retval; return retval;
} }
unsigned m_current; unsigned m_current;
unsigned m_min; unsigned m_min;
unsigned m_max; unsigned m_max;
unsigned m_exponent; unsigned m_exponent;
}; };
/* /*
@ -344,121 +344,121 @@ public:
class FolderInstanceStaging : public Task class FolderInstanceStaging : public Task
{ {
Q_OBJECT Q_OBJECT
const unsigned minBackoff = 1; const unsigned minBackoff = 1;
const unsigned maxBackoff = 16; const unsigned maxBackoff = 16;
public: public:
FolderInstanceStaging ( FolderInstanceStaging (
FolderInstanceProvider * parent, FolderInstanceProvider * parent,
Task * child, Task * child,
const QString & stagingPath, const QString & stagingPath,
const QString& instanceName, const QString& instanceName,
const QString& groupName ) const QString& groupName )
: backoff(minBackoff, maxBackoff) : backoff(minBackoff, maxBackoff)
{ {
m_parent = parent; m_parent = parent;
m_child.reset(child); m_child.reset(child);
connect(child, &Task::succeeded, this, &FolderInstanceStaging::childSucceded); connect(child, &Task::succeeded, this, &FolderInstanceStaging::childSucceded);
connect(child, &Task::failed, this, &FolderInstanceStaging::childFailed); connect(child, &Task::failed, this, &FolderInstanceStaging::childFailed);
connect(child, &Task::status, this, &FolderInstanceStaging::setStatus); connect(child, &Task::status, this, &FolderInstanceStaging::setStatus);
connect(child, &Task::progress, this, &FolderInstanceStaging::setProgress); connect(child, &Task::progress, this, &FolderInstanceStaging::setProgress);
m_instanceName = instanceName; m_instanceName = instanceName;
m_groupName = groupName; m_groupName = groupName;
m_stagingPath = stagingPath; m_stagingPath = stagingPath;
m_backoffTimer.setSingleShot(true); m_backoffTimer.setSingleShot(true);
connect(&m_backoffTimer, &QTimer::timeout, this, &FolderInstanceStaging::childSucceded); connect(&m_backoffTimer, &QTimer::timeout, this, &FolderInstanceStaging::childSucceded);
} }
virtual ~FolderInstanceStaging() {}; virtual ~FolderInstanceStaging() {};
protected: protected:
virtual void executeTask() override virtual void executeTask() override
{ {
m_child->start(); m_child->start();
} }
QStringList warnings() const override QStringList warnings() const override
{ {
return m_child->warnings(); return m_child->warnings();
} }
private slots: private slots:
void childSucceded() void childSucceded()
{ {
unsigned sleepTime = backoff(); unsigned sleepTime = backoff();
if(m_parent->commitStagedInstance(m_stagingPath, m_instanceName, m_groupName)) if(m_parent->commitStagedInstance(m_stagingPath, m_instanceName, m_groupName))
{ {
emitSucceeded(); emitSucceeded();
return; return;
} }
// we actually failed, retry? // we actually failed, retry?
if(sleepTime == maxBackoff) if(sleepTime == maxBackoff)
{ {
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something.")); emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
return; return;
} }
qDebug() << "Failed to commit instance" << m_instanceName << "Initiating backoff:" << sleepTime; qDebug() << "Failed to commit instance" << m_instanceName << "Initiating backoff:" << sleepTime;
m_backoffTimer.start(sleepTime * 500); m_backoffTimer.start(sleepTime * 500);
} }
void childFailed(const QString & reason) void childFailed(const QString & reason)
{ {
m_parent->destroyStagingPath(m_stagingPath); m_parent->destroyStagingPath(m_stagingPath);
emitFailed(reason); emitFailed(reason);
} }
private: private:
ExponentialSeries backoff; ExponentialSeries backoff;
QString m_stagingPath; QString m_stagingPath;
FolderInstanceProvider * m_parent; FolderInstanceProvider * m_parent;
unique_qobject_ptr<Task> m_child; unique_qobject_ptr<Task> m_child;
QString m_instanceName; QString m_instanceName;
QString m_groupName; QString m_groupName;
QTimer m_backoffTimer; QTimer m_backoffTimer;
}; };
#include "InstanceTask.h" #include "InstanceTask.h"
Task * FolderInstanceProvider::wrapInstanceTask(InstanceTask * task) Task * FolderInstanceProvider::wrapInstanceTask(InstanceTask * task)
{ {
auto stagingPath = getStagedInstancePath(); auto stagingPath = getStagedInstancePath();
task->setStagingPath(stagingPath); task->setStagingPath(stagingPath);
task->setParentSettings(m_globalSettings); task->setParentSettings(m_globalSettings);
return new FolderInstanceStaging(this, task, stagingPath, task->name(), task->group()); return new FolderInstanceStaging(this, task, stagingPath, task->name(), task->group());
} }
QString FolderInstanceProvider::getStagedInstancePath() QString FolderInstanceProvider::getStagedInstancePath()
{ {
QString key = QUuid::createUuid().toString(); QString key = QUuid::createUuid().toString();
QString relPath = FS::PathCombine("_MMC_TEMP/" , key); QString relPath = FS::PathCombine("_MMC_TEMP/" , key);
QDir rootPath(m_instDir); QDir rootPath(m_instDir);
auto path = FS::PathCombine(m_instDir, relPath); auto path = FS::PathCombine(m_instDir, relPath);
if(!rootPath.mkpath(relPath)) if(!rootPath.mkpath(relPath))
{ {
return QString(); return QString();
} }
return path; return path;
} }
bool FolderInstanceProvider::commitStagedInstance(const QString& path, const QString& instanceName, const QString& groupName) bool FolderInstanceProvider::commitStagedInstance(const QString& path, const QString& instanceName, const QString& groupName)
{ {
QDir dir; QDir dir;
QString instID = FS::DirNameFromString(instanceName, m_instDir); QString instID = FS::DirNameFromString(instanceName, m_instDir);
{ {
WatchLock lock(m_watcher, m_instDir); WatchLock lock(m_watcher, m_instDir);
QString destination = FS::PathCombine(m_instDir, instID); QString destination = FS::PathCombine(m_instDir, instID);
if(!dir.rename(path, destination)) if(!dir.rename(path, destination))
{ {
qWarning() << "Failed to move" << path << "to" << destination; qWarning() << "Failed to move" << path << "to" << destination;
return false; return false;
} }
groupMap[instID] = groupName; groupMap[instID] = groupName;
emit groupsChanged({groupName}); emit groupsChanged({groupName});
emit instancesChanged(); emit instancesChanged();
} }
saveGroupList(); saveGroupList();
return true; return true;
} }
bool FolderInstanceProvider::destroyStagingPath(const QString& keyPath) bool FolderInstanceProvider::destroyStagingPath(const QString& keyPath)
{ {
return FS::deletePath(keyPath); return FS::deletePath(keyPath);
} }
#include "FolderInstanceProvider.moc" #include "FolderInstanceProvider.moc"

View File

@ -8,68 +8,68 @@ class InstanceTask;
class MULTIMC_LOGIC_EXPORT FolderInstanceProvider : public BaseInstanceProvider class MULTIMC_LOGIC_EXPORT FolderInstanceProvider : public BaseInstanceProvider
{ {
Q_OBJECT Q_OBJECT
public: public:
FolderInstanceProvider(SettingsObjectPtr settings, const QString & instDir); FolderInstanceProvider(SettingsObjectPtr settings, const QString & instDir);
public: public:
/// used by InstanceList to @return a list of plausible IDs to probe for /// used by InstanceList to @return a list of plausible IDs to probe for
QList<InstanceId> discoverInstances() override; QList<InstanceId> discoverInstances() override;
/// used by InstanceList to (re)load an instance with the given @id. /// used by InstanceList to (re)load an instance with the given @id.
InstancePtr loadInstance(const InstanceId& id) override; InstancePtr loadInstance(const InstanceId& id) override;
/* /*
// create instance in this provider // create instance in this provider
Task * creationTask(BaseVersionPtr version, const QString &instName, const QString &instGroup, const QString &instIcon); Task * creationTask(BaseVersionPtr version, const QString &instName, const QString &instGroup, const QString &instIcon);
// copy instance to this provider // copy instance to this provider
Task * copyTask(const InstancePtr &oldInstance, const QString& instName, const QString& instGroup, const QString& instIcon, bool copySaves); Task * copyTask(const InstancePtr &oldInstance, const QString& instName, const QString& instGroup, const QString& instIcon, bool copySaves);
// import zipped instance into this provider // import zipped instance into this provider
Task * zipImportTask(const QUrl sourceUrl, const QString &instName, const QString &instGroup, const QString &instIcon); Task * zipImportTask(const QUrl sourceUrl, const QString &instName, const QString &instGroup, const QString &instIcon);
//create FtbInstance //create FtbInstance
Task * ftbCreationTask(FtbPackDownloader *downloader, const QString &instName, const QString &instGroup, const QString &instIcon); Task * ftbCreationTask(FtbPackDownloader *downloader, const QString &instName, const QString &instGroup, const QString &instIcon);
// migrate an instance to the current format // migrate an instance to the current format
Task * legacyUpgradeTask(const InstancePtr& oldInstance); Task * legacyUpgradeTask(const InstancePtr& oldInstance);
*/ */
// Wrap an instance creation task in some more task machinery and make it ready to be used // Wrap an instance creation task in some more task machinery and make it ready to be used
Task * wrapInstanceTask(InstanceTask * task); Task * wrapInstanceTask(InstanceTask * task);
/** /**
* Create a new empty staging area for instance creation and @return a path/key top commit it later. * Create a new empty staging area for instance creation and @return a path/key top commit it later.
* Used by instance manipulation tasks. * Used by instance manipulation tasks.
*/ */
QString getStagedInstancePath() override; QString getStagedInstancePath() override;
/** /**
* Commit the staging area given by @keyPath to the provider - used when creation succeeds. * Commit the staging area given by @keyPath to the provider - used when creation succeeds.
* Used by instance manipulation tasks. * Used by instance manipulation tasks.
*/ */
bool commitStagedInstance(const QString & keyPath, const QString& instanceName, const QString & groupName) override; bool commitStagedInstance(const QString & keyPath, const QString& instanceName, const QString & groupName) override;
/** /**
* Destroy a previously created staging area given by @keyPath - used when creation fails. * Destroy a previously created staging area given by @keyPath - used when creation fails.
* Used by instance manipulation tasks. * Used by instance manipulation tasks.
*/ */
bool destroyStagingPath(const QString & keyPath) override; bool destroyStagingPath(const QString & keyPath) override;
public slots: public slots:
void on_InstFolderChanged(const Setting &setting, QVariant value); void on_InstFolderChanged(const Setting &setting, QVariant value);
private slots: private slots:
void instanceDirContentsChanged(const QString &path); void instanceDirContentsChanged(const QString &path);
void groupChanged(); void groupChanged();
private: /* methods */ private: /* methods */
void loadGroupList() override; void loadGroupList() override;
void saveGroupList() override; void saveGroupList() override;
private: /* data */ private: /* data */
QString m_instDir; QString m_instDir;
QFileSystemWatcher * m_watcher; QFileSystemWatcher * m_watcher;
QMap<QString, QString> groupMap; QMap<QString, QString> groupMap;
bool m_groupsLoaded = false; bool m_groupsLoaded = false;
}; };

View File

@ -4,112 +4,112 @@
bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes) bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes)
{ {
if (compressedBytes.size() == 0) if (compressedBytes.size() == 0)
{ {
uncompressedBytes = compressedBytes; uncompressedBytes = compressedBytes;
return true; return true;
} }
unsigned uncompLength = compressedBytes.size(); unsigned uncompLength = compressedBytes.size();
uncompressedBytes.clear(); uncompressedBytes.clear();
uncompressedBytes.resize(uncompLength); uncompressedBytes.resize(uncompLength);
z_stream strm; z_stream strm;
memset(&strm, 0, sizeof(strm)); memset(&strm, 0, sizeof(strm));
strm.next_in = (Bytef *)compressedBytes.data(); strm.next_in = (Bytef *)compressedBytes.data();
strm.avail_in = compressedBytes.size(); strm.avail_in = compressedBytes.size();
bool done = false; bool done = false;
if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK)
{ {
return false; return false;
} }
int err = Z_OK; int err = Z_OK;
while (!done) while (!done)
{ {
// If our output buffer is too small // If our output buffer is too small
if (strm.total_out >= uncompLength) if (strm.total_out >= uncompLength)
{ {
uncompressedBytes.resize(uncompLength * 2); uncompressedBytes.resize(uncompLength * 2);
uncompLength *= 2; uncompLength *= 2;
} }
strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out); strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
strm.avail_out = uncompLength - strm.total_out; strm.avail_out = uncompLength - strm.total_out;
// Inflate another chunk. // Inflate another chunk.
err = inflate(&strm, Z_SYNC_FLUSH); err = inflate(&strm, Z_SYNC_FLUSH);
if (err == Z_STREAM_END) if (err == Z_STREAM_END)
done = true; done = true;
else if (err != Z_OK) else if (err != Z_OK)
{ {
break; break;
} }
} }
if (inflateEnd(&strm) != Z_OK || !done) if (inflateEnd(&strm) != Z_OK || !done)
{ {
return false; return false;
} }
uncompressedBytes.resize(strm.total_out); uncompressedBytes.resize(strm.total_out);
return true; return true;
} }
bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes) bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
{ {
if (uncompressedBytes.size() == 0) if (uncompressedBytes.size() == 0)
{ {
compressedBytes = uncompressedBytes; compressedBytes = uncompressedBytes;
return true; return true;
} }
unsigned compLength = std::min(uncompressedBytes.size(), 16); unsigned compLength = std::min(uncompressedBytes.size(), 16);
compressedBytes.clear(); compressedBytes.clear();
compressedBytes.resize(compLength); compressedBytes.resize(compLength);
z_stream zs; z_stream zs;
memset(&zs, 0, sizeof(zs)); memset(&zs, 0, sizeof(zs));
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK) if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK)
{ {
return false; return false;
} }
zs.next_in = (Bytef*)uncompressedBytes.data(); zs.next_in = (Bytef*)uncompressedBytes.data();
zs.avail_in = uncompressedBytes.size(); zs.avail_in = uncompressedBytes.size();
int ret; int ret;
compressedBytes.resize(uncompressedBytes.size()); compressedBytes.resize(uncompressedBytes.size());
unsigned offset = 0; unsigned offset = 0;
unsigned temp = 0; unsigned temp = 0;
do do
{ {
auto remaining = compressedBytes.size() - offset; auto remaining = compressedBytes.size() - offset;
if(remaining < 1) if(remaining < 1)
{ {
compressedBytes.resize(compressedBytes.size() * 2); compressedBytes.resize(compressedBytes.size() * 2);
} }
zs.next_out = (Bytef *) (compressedBytes.data() + offset); zs.next_out = (Bytef *) (compressedBytes.data() + offset);
temp = zs.avail_out = compressedBytes.size() - offset; temp = zs.avail_out = compressedBytes.size() - offset;
ret = deflate(&zs, Z_FINISH); ret = deflate(&zs, Z_FINISH);
offset += temp - zs.avail_out; offset += temp - zs.avail_out;
} while (ret == Z_OK); } while (ret == Z_OK);
compressedBytes.resize(offset); compressedBytes.resize(offset);
if (deflateEnd(&zs) != Z_OK) if (deflateEnd(&zs) != Z_OK)
{ {
return false; return false;
} }
if (ret != Z_STREAM_END) if (ret != Z_STREAM_END)
{ {
return false; return false;
} }
return true; return true;
} }

View File

@ -6,7 +6,7 @@
class MULTIMC_LOGIC_EXPORT GZip class MULTIMC_LOGIC_EXPORT GZip
{ {
public: public:
static bool unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes); static bool unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes);
static bool zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes); static bool zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes);
}; };

View File

@ -6,50 +6,50 @@
void fib(int &prev, int &cur) void fib(int &prev, int &cur)
{ {
auto ret = prev + cur; auto ret = prev + cur;
prev = cur; prev = cur;
cur = ret; cur = ret;
} }
class GZipTest : public QObject class GZipTest : public QObject
{ {
Q_OBJECT Q_OBJECT
private private
slots: slots:
void test_Through() void test_Through()
{ {
// test up to 10 MB // test up to 10 MB
static const int size = 10 * 1024 * 1024; static const int size = 10 * 1024 * 1024;
QByteArray random; QByteArray random;
QByteArray compressed; QByteArray compressed;
QByteArray decompressed; QByteArray decompressed;
std::default_random_engine eng((std::random_device())()); std::default_random_engine eng((std::random_device())());
std::uniform_int_distribution<uint8_t> idis(0, std::numeric_limits<uint8_t>::max()); std::uniform_int_distribution<uint8_t> idis(0, std::numeric_limits<uint8_t>::max());
// initialize random buffer // initialize random buffer
for(int i = 0; i < size; i++) for(int i = 0; i < size; i++)
{ {
random.append((char)idis(eng)); random.append((char)idis(eng));
} }
// initialize fibonacci // initialize fibonacci
int prev = 1; int prev = 1;
int cur = 1; int cur = 1;
// test if fibonacci long random buffers pass through GZip // test if fibonacci long random buffers pass through GZip
do do
{ {
QByteArray copy = random; QByteArray copy = random;
copy.resize(cur); copy.resize(cur);
compressed.clear(); compressed.clear();
decompressed.clear(); decompressed.clear();
QVERIFY(GZip::zip(copy, compressed)); QVERIFY(GZip::zip(copy, compressed));
QVERIFY(GZip::unzip(compressed, decompressed)); QVERIFY(GZip::unzip(compressed, decompressed));
QCOMPARE(decompressed, copy); QCOMPARE(decompressed, copy);
fib(prev, cur); fib(prev, cur);
} while (cur < size); } while (cur < size);
} }
}; };
QTEST_GUILESS_MAIN(GZipTest) QTEST_GUILESS_MAIN(GZipTest)

View File

@ -8,50 +8,50 @@
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves) InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves)
{ {
m_origInstance = origInstance; m_origInstance = origInstance;
if(!copySaves) if(!copySaves)
{ {
// FIXME: get this from the original instance type... // FIXME: get this from the original instance type...
auto matcherReal = new RegexpMatcher("[.]?minecraft/saves"); auto matcherReal = new RegexpMatcher("[.]?minecraft/saves");
matcherReal->caseSensitive(false); matcherReal->caseSensitive(false);
m_matcher.reset(matcherReal); m_matcher.reset(matcherReal);
} }
} }
void InstanceCopyTask::executeTask() void InstanceCopyTask::executeTask()
{ {
setStatus(tr("Copying instance %1").arg(m_origInstance->name())); setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
folderCopy.followSymlinks(false).blacklist(m_matcher.get()); folderCopy.followSymlinks(false).blacklist(m_matcher.get());
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy); m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished); connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted); connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
m_copyFutureWatcher.setFuture(m_copyFuture); m_copyFutureWatcher.setFuture(m_copyFuture);
} }
void InstanceCopyTask::copyFinished() void InstanceCopyTask::copyFinished()
{ {
auto successful = m_copyFuture.result(); auto successful = m_copyFuture.result();
if(!successful) if(!successful)
{ {
emitFailed(tr("Instance folder copy failed.")); emitFailed(tr("Instance folder copy failed."));
return; return;
} }
// FIXME: shouldn't this be able to report errors? // FIXME: shouldn't this be able to report errors?
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg")); auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
instanceSettings->registerSetting("InstanceType", "Legacy"); instanceSettings->registerSetting("InstanceType", "Legacy");
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath)); InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
inst->setName(m_instName); inst->setName(m_instName);
inst->setIconKey(m_instIcon); inst->setIconKey(m_instIcon);
emitSucceeded(); emitSucceeded();
} }
void InstanceCopyTask::copyAborted() void InstanceCopyTask::copyAborted()
{ {
emitFailed(tr("Instance folder copy has been aborted.")); emitFailed(tr("Instance folder copy has been aborted."));
return; return;
} }

View File

@ -15,19 +15,19 @@ class BaseInstanceProvider;
class MULTIMC_LOGIC_EXPORT InstanceCopyTask : public InstanceTask class MULTIMC_LOGIC_EXPORT InstanceCopyTask : public InstanceTask
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves); explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves);
protected: protected:
//! Entry point for tasks. //! Entry point for tasks.
virtual void executeTask() override; virtual void executeTask() override;
void copyFinished(); void copyFinished();
void copyAborted(); void copyAborted();
private: /* data */ private: /* data */
InstancePtr m_origInstance; InstancePtr m_origInstance;
QFuture<bool> m_copyFuture; QFuture<bool> m_copyFuture;
QFutureWatcher<bool> m_copyFutureWatcher; QFutureWatcher<bool> m_copyFutureWatcher;
std::unique_ptr<IPathMatcher> m_matcher; std::unique_ptr<IPathMatcher> m_matcher;
}; };

View File

@ -9,25 +9,25 @@
InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version) InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version)
{ {
m_version = version; m_version = version;
} }
void InstanceCreationTask::executeTask() void InstanceCreationTask::executeTask()
{ {
setStatus(tr("Creating instance from version %1").arg(m_version->name())); setStatus(tr("Creating instance from version %1").arg(m_version->name()));
{ {
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg")); auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
instanceSettings->suspendSave(); instanceSettings->suspendSave();
instanceSettings->registerSetting("InstanceType", "Legacy"); instanceSettings->registerSetting("InstanceType", "Legacy");
instanceSettings->set("InstanceType", "OneSix"); instanceSettings->set("InstanceType", "OneSix");
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath); MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
auto components = inst.getComponentList(); auto components = inst.getComponentList();
components->buildingFromScratch(); components->buildingFromScratch();
components->setComponentVersion("net.minecraft", m_version->descriptor(), true); components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
inst.setName(m_instName); inst.setName(m_instName);
inst.setIconKey(m_instIcon); inst.setIconKey(m_instIcon);
inst.init(); inst.init();
instanceSettings->resumeSave(); instanceSettings->resumeSave();
} }
emitSucceeded(); emitSucceeded();
} }

View File

@ -10,14 +10,14 @@
class MULTIMC_LOGIC_EXPORT InstanceCreationTask : public InstanceTask class MULTIMC_LOGIC_EXPORT InstanceCreationTask : public InstanceTask
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceCreationTask(BaseVersionPtr version); explicit InstanceCreationTask(BaseVersionPtr version);
protected: protected:
//! Entry point for tasks. //! Entry point for tasks.
virtual void executeTask() override; virtual void executeTask() override;
private: /* data */ private: /* data */
BaseVersionPtr m_version; BaseVersionPtr m_version;
}; };

View File

@ -18,394 +18,394 @@
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl) InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
{ {
m_sourceUrl = sourceUrl; m_sourceUrl = sourceUrl;
} }
void InstanceImportTask::executeTask() void InstanceImportTask::executeTask()
{ {
InstancePtr newInstance; InstancePtr newInstance;
if (m_sourceUrl.isLocalFile()) if (m_sourceUrl.isLocalFile())
{ {
m_archivePath = m_sourceUrl.toLocalFile(); m_archivePath = m_sourceUrl.toLocalFile();
processZipPack(); processZipPack();
} }
else else
{ {
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString())); setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
m_downloadRequired = true; m_downloadRequired = true;
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path(); const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
auto entry = ENV.metacache()->resolveEntry("general", path); auto entry = ENV.metacache()->resolveEntry("general", path);
entry->setStale(true); entry->setStale(true);
m_filesNetJob.reset(new NetJob(tr("Modpack download"))); m_filesNetJob.reset(new NetJob(tr("Modpack download")));
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
m_archivePath = entry->getFullPath(); m_archivePath = entry->getFullPath();
auto job = m_filesNetJob.get(); auto job = m_filesNetJob.get();
connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed); connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
m_filesNetJob->start(); m_filesNetJob->start();
} }
} }
void InstanceImportTask::downloadSucceeded() void InstanceImportTask::downloadSucceeded()
{ {
processZipPack(); processZipPack();
m_filesNetJob.reset(); m_filesNetJob.reset();
} }
void InstanceImportTask::downloadFailed(QString reason) void InstanceImportTask::downloadFailed(QString reason)
{ {
emitFailed(reason); emitFailed(reason);
m_filesNetJob.reset(); m_filesNetJob.reset();
} }
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total) void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
{ {
setProgress(current / 2, total); setProgress(current / 2, total);
} }
void InstanceImportTask::processZipPack() void InstanceImportTask::processZipPack()
{ {
setStatus(tr("Extracting modpack")); setStatus(tr("Extracting modpack"));
QDir extractDir(m_stagingPath); QDir extractDir(m_stagingPath);
qDebug() << "Attempting to create instance from" << m_archivePath; qDebug() << "Attempting to create instance from" << m_archivePath;
// open the zip and find relevant files in it // open the zip and find relevant files in it
m_packZip.reset(new QuaZip(m_archivePath)); m_packZip.reset(new QuaZip(m_archivePath));
if (!m_packZip->open(QuaZip::mdUnzip)) if (!m_packZip->open(QuaZip::mdUnzip))
{ {
emitFailed(tr("Unable to open supplied modpack zip file.")); emitFailed(tr("Unable to open supplied modpack zip file."));
return; return;
} }
QStringList blacklist = {"instance.cfg", "manifest.json"}; QStringList blacklist = {"instance.cfg", "manifest.json"};
QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg"); QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json"); QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
QString root; QString root;
if(!mmcFound.isNull()) if(!mmcFound.isNull())
{ {
// process as MultiMC instance/pack // process as MultiMC instance/pack
qDebug() << "MultiMC:" << mmcFound; qDebug() << "MultiMC:" << mmcFound;
root = mmcFound; root = mmcFound;
m_modpackType = ModpackType::MultiMC; m_modpackType = ModpackType::MultiMC;
} }
else if(!flameFound.isNull()) else if(!flameFound.isNull())
{ {
// process as Flame pack // process as Flame pack
qDebug() << "Flame:" << flameFound; qDebug() << "Flame:" << flameFound;
root = flameFound; root = flameFound;
m_modpackType = ModpackType::Flame; m_modpackType = ModpackType::Flame;
} }
if(m_modpackType == ModpackType::Unknown) if(m_modpackType == ModpackType::Unknown)
{ {
emitFailed(tr("Archive does not contain a recognized modpack type.")); emitFailed(tr("Archive does not contain a recognized modpack type."));
return; return;
} }
// make sure we extract just the pack // make sure we extract just the pack
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath()); m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished); connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted); connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
m_extractFutureWatcher.setFuture(m_extractFuture); m_extractFutureWatcher.setFuture(m_extractFuture);
} }
void InstanceImportTask::extractFinished() void InstanceImportTask::extractFinished()
{ {
m_packZip.reset(); m_packZip.reset();
if (m_extractFuture.result().isEmpty()) if (m_extractFuture.result().isEmpty())
{ {
emitFailed(tr("Failed to extract modpack")); emitFailed(tr("Failed to extract modpack"));
return; return;
} }
QDir extractDir(m_stagingPath); QDir extractDir(m_stagingPath);
qDebug() << "Fixing permissions for extracted pack files..."; qDebug() << "Fixing permissions for extracted pack files...";
QDirIterator it(extractDir, QDirIterator::Subdirectories); QDirIterator it(extractDir, QDirIterator::Subdirectories);
while (it.hasNext()) while (it.hasNext())
{ {
auto filepath = it.next(); auto filepath = it.next();
QFileInfo file(filepath); QFileInfo file(filepath);
auto permissions = QFile::permissions(filepath); auto permissions = QFile::permissions(filepath);
auto origPermissions = permissions; auto origPermissions = permissions;
if(file.isDir()) if(file.isDir())
{ {
// Folder +rwx for current user // Folder +rwx for current user
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser; permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
} }
else else
{ {
// File +rw for current user // File +rw for current user
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
} }
if(origPermissions != permissions) if(origPermissions != permissions)
{ {
if(!QFile::setPermissions(filepath, permissions)) if(!QFile::setPermissions(filepath, permissions))
{ {
logWarning(tr("Could not fix permissions for %1").arg(filepath)); logWarning(tr("Could not fix permissions for %1").arg(filepath));
} }
else else
{ {
qDebug() << "Fixed" << filepath; qDebug() << "Fixed" << filepath;
} }
} }
} }
switch(m_modpackType) switch(m_modpackType)
{ {
case ModpackType::Flame: case ModpackType::Flame:
processFlame(); processFlame();
return; return;
case ModpackType::MultiMC: case ModpackType::MultiMC:
processMultiMC(); processMultiMC();
return; return;
case ModpackType::Unknown: case ModpackType::Unknown:
emitFailed(tr("Archive does not contain a recognized modpack type.")); emitFailed(tr("Archive does not contain a recognized modpack type."));
return; return;
} }
} }
void InstanceImportTask::extractAborted() void InstanceImportTask::extractAborted()
{ {
emitFailed(tr("Instance import has been aborted.")); emitFailed(tr("Instance import has been aborted."));
return; return;
} }
void InstanceImportTask::processFlame() void InstanceImportTask::processFlame()
{ {
const static QMap<QString,QString> forgemap = { const static QMap<QString,QString> forgemap = {
{"1.2.5", "3.4.9.171"}, {"1.2.5", "3.4.9.171"},
{"1.4.2", "6.0.1.355"}, {"1.4.2", "6.0.1.355"},
{"1.4.7", "6.6.2.534"}, {"1.4.7", "6.6.2.534"},
{"1.5.2", "7.8.1.737"} {"1.5.2", "7.8.1.737"}
}; };
Flame::Manifest pack; Flame::Manifest pack;
try try
{ {
QString configPath = FS::PathCombine(m_stagingPath, "manifest.json"); QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
Flame::loadManifest(pack, configPath); Flame::loadManifest(pack, configPath);
QFile::remove(configPath); QFile::remove(configPath);
} }
catch (const JSONValidationError &e) catch (const JSONValidationError &e)
{ {
emitFailed(tr("Could not understand pack manifest:\n") + e.cause()); emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
return; return;
} }
if(!pack.overrides.isEmpty()) if(!pack.overrides.isEmpty())
{ {
QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides); QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
if (QFile::exists(overridePath)) if (QFile::exists(overridePath))
{ {
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft"); QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
if (!QFile::rename(overridePath, mcPath)) if (!QFile::rename(overridePath, mcPath))
{ {
emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides); emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
return; return;
} }
} }
else else
{ {
logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides)); logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
} }
} }
QString forgeVersion; QString forgeVersion;
for(auto &loader: pack.minecraft.modLoaders) for(auto &loader: pack.minecraft.modLoaders)
{ {
auto id = loader.id; auto id = loader.id;
if(id.startsWith("forge-")) if(id.startsWith("forge-"))
{ {
id.remove("forge-"); id.remove("forge-");
forgeVersion = id; forgeVersion = id;
continue; continue;
} }
logWarning(tr("Unknown mod loader in manifest: %1").arg(id)); logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
} }
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(configPath); auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
instanceSettings->registerSetting("InstanceType", "Legacy"); instanceSettings->registerSetting("InstanceType", "Legacy");
instanceSettings->set("InstanceType", "OneSix"); instanceSettings->set("InstanceType", "OneSix");
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath); MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
auto mcVersion = pack.minecraft.version; auto mcVersion = pack.minecraft.version;
// Hack to correct some 'special sauce'... // Hack to correct some 'special sauce'...
if(mcVersion.endsWith('.')) if(mcVersion.endsWith('.'))
{ {
mcVersion.remove(QRegExp("[.]+$")); mcVersion.remove(QRegExp("[.]+$"));
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack.")); logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
} }
auto components = instance.getComponentList(); auto components = instance.getComponentList();
components->buildingFromScratch(); components->buildingFromScratch();
components->setComponentVersion("net.minecraft", mcVersion, true); components->setComponentVersion("net.minecraft", mcVersion, true);
if(!forgeVersion.isEmpty()) if(!forgeVersion.isEmpty())
{ {
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata. // FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
if(forgeVersion == "recommended") if(forgeVersion == "recommended")
{ {
if(forgemap.contains(mcVersion)) if(forgemap.contains(mcVersion))
{ {
forgeVersion = forgemap[mcVersion]; forgeVersion = forgemap[mcVersion];
} }
else else
{ {
logWarning(tr("Could not map recommended forge version for Minecraft %1").arg(mcVersion)); logWarning(tr("Could not map recommended forge version for Minecraft %1").arg(mcVersion));
} }
} }
components->setComponentVersion("net.minecraftforge", forgeVersion); components->setComponentVersion("net.minecraftforge", forgeVersion);
} }
if (m_instIcon != "default") if (m_instIcon != "default")
{ {
instance.setIconKey(m_instIcon); instance.setIconKey(m_instIcon);
} }
else else
{ {
if(pack.name.contains("Direwolf20")) if(pack.name.contains("Direwolf20"))
{ {
instance.setIconKey("steve"); instance.setIconKey("steve");
} }
else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast")) else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
{ {
instance.setIconKey("ftb_logo"); instance.setIconKey("ftb_logo");
} }
else else
{ {
// default to something other than the MultiMC default to distinguish these // default to something other than the MultiMC default to distinguish these
instance.setIconKey("flame"); instance.setIconKey("flame");
} }
} }
instance.init(); instance.init();
QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods"); QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
QFileInfo jarmodsInfo(jarmodsPath); QFileInfo jarmodsInfo(jarmodsPath);
if(jarmodsInfo.isDir()) if(jarmodsInfo.isDir())
{ {
// install all the jar mods // install all the jar mods
qDebug() << "Found jarmods:"; qDebug() << "Found jarmods:";
QDir jarmodsDir(jarmodsPath); QDir jarmodsDir(jarmodsPath);
QStringList jarMods; QStringList jarMods;
for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
{ {
qDebug() << info.fileName(); qDebug() << info.fileName();
jarMods.push_back(info.absoluteFilePath()); jarMods.push_back(info.absoluteFilePath());
} }
auto profile = instance.getComponentList(); auto profile = instance.getComponentList();
profile->installJarMods(jarMods); profile->installJarMods(jarMods);
// nuke the original files // nuke the original files
FS::deletePath(jarmodsPath); FS::deletePath(jarmodsPath);
} }
instance.setName(m_instName); instance.setName(m_instName);
m_modIdResolver.reset(new Flame::FileResolvingTask(pack)); m_modIdResolver.reset(new Flame::FileResolvingTask(pack));
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]() connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
{ {
auto results = m_modIdResolver->getResults(); auto results = m_modIdResolver->getResults();
m_filesNetJob.reset(new NetJob(tr("Mod download"))); m_filesNetJob.reset(new NetJob(tr("Mod download")));
for(auto result: results.files) for(auto result: results.files)
{ {
QString filename = result.fileName; QString filename = result.fileName;
if(!result.required) if(!result.required)
{ {
filename += ".disabled"; filename += ".disabled";
} }
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename); auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
auto path = FS::PathCombine(m_stagingPath , relpath); auto path = FS::PathCombine(m_stagingPath , relpath);
switch(result.type) switch(result.type)
{ {
case Flame::File::Type::Folder: case Flame::File::Type::Folder:
{ {
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath)); logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
// fall-through intentional, we treat these as plain old mods and dump them wherever. // fall-through intentional, we treat these as plain old mods and dump them wherever.
} }
case Flame::File::Type::SingleFile: case Flame::File::Type::SingleFile:
case Flame::File::Type::Mod: case Flame::File::Type::Mod:
{ {
qDebug() << "Will download" << result.url << "to" << path; qDebug() << "Will download" << result.url << "to" << path;
auto dl = Net::Download::makeFile(result.url, path); auto dl = Net::Download::makeFile(result.url, path);
m_filesNetJob->addNetAction(dl); m_filesNetJob->addNetAction(dl);
break; break;
} }
case Flame::File::Type::Modpack: case Flame::File::Type::Modpack:
logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath)); logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
break; break;
case Flame::File::Type::Cmod2: case Flame::File::Type::Cmod2:
case Flame::File::Type::Ctoc: case Flame::File::Type::Ctoc:
case Flame::File::Type::Unknown: case Flame::File::Type::Unknown:
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath)); logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
break; break;
} }
} }
m_modIdResolver.reset(); m_modIdResolver.reset();
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
{ {
m_filesNetJob.reset(); m_filesNetJob.reset();
emitSucceeded(); emitSucceeded();
} }
); );
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
{ {
m_filesNetJob.reset(); m_filesNetJob.reset();
emitFailed(reason); emitFailed(reason);
}); });
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
{ {
setProgress(current, total); setProgress(current, total);
}); });
setStatus(tr("Downloading mods...")); setStatus(tr("Downloading mods..."));
m_filesNetJob->start(); m_filesNetJob->start();
} }
); );
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason) connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
{ {
m_modIdResolver.reset(); m_modIdResolver.reset();
emitFailed(tr("Unable to resolve mod IDs:\n") + reason); emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
}); });
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total) connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
{ {
setProgress(current, total); setProgress(current, total);
}); });
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status) connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
{ {
setStatus(status); setStatus(status);
}); });
m_modIdResolver->start(); m_modIdResolver->start();
} }
void InstanceImportTask::processMultiMC() void InstanceImportTask::processMultiMC()
{ {
// FIXME: copy from FolderInstanceProvider!!! FIX IT!!! // FIXME: copy from FolderInstanceProvider!!! FIX IT!!!
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(configPath); auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
instanceSettings->registerSetting("InstanceType", "Legacy"); instanceSettings->registerSetting("InstanceType", "Legacy");
NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath); NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
// reset time played on import... because packs. // reset time played on import... because packs.
instance.resetTimePlayed(); instance.resetTimePlayed();
// set a new nice name // set a new nice name
instance.setName(m_instName); instance.setName(m_instName);
// if the icon was specified by user, use that. otherwise pull icon from the pack // if the icon was specified by user, use that. otherwise pull icon from the pack
if (m_instIcon != "default") if (m_instIcon != "default")
{ {
instance.setIconKey(m_instIcon); instance.setIconKey(m_instIcon);
} }
else else
{ {
m_instIcon = instance.iconKey(); m_instIcon = instance.iconKey();
auto importIconPath = FS::PathCombine(instance.instanceRoot(), m_instIcon + ".png"); auto importIconPath = FS::PathCombine(instance.instanceRoot(), m_instIcon + ".png");
if (QFile::exists(importIconPath)) if (QFile::exists(importIconPath))
{ {
// import icon // import icon
auto iconList = ENV.icons(); auto iconList = ENV.icons();
if (iconList->iconFileExists(m_instIcon)) if (iconList->iconFileExists(m_instIcon))
{ {
iconList->deleteIcon(m_instIcon); iconList->deleteIcon(m_instIcon);
} }
iconList->installIcons({importIconPath}); iconList->installIcons({importIconPath});
} }
} }
emitSucceeded(); emitSucceeded();
} }

View File

@ -13,43 +13,43 @@ class QuaZip;
class BaseInstanceProvider; class BaseInstanceProvider;
namespace Flame namespace Flame
{ {
class FileResolvingTask; class FileResolvingTask;
} }
class MULTIMC_LOGIC_EXPORT InstanceImportTask : public InstanceTask class MULTIMC_LOGIC_EXPORT InstanceImportTask : public InstanceTask
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceImportTask(const QUrl sourceUrl); explicit InstanceImportTask(const QUrl sourceUrl);
protected: protected:
//! Entry point for tasks. //! Entry point for tasks.
virtual void executeTask() override; virtual void executeTask() override;
private: private:
void processZipPack(); void processZipPack();
void processMultiMC(); void processMultiMC();
void processFlame(); void processFlame();
private slots: private slots:
void downloadSucceeded(); void downloadSucceeded();
void downloadFailed(QString reason); void downloadFailed(QString reason);
void downloadProgressChanged(qint64 current, qint64 total); void downloadProgressChanged(qint64 current, qint64 total);
void extractFinished(); void extractFinished();
void extractAborted(); void extractAborted();
private: /* data */ private: /* data */
NetJobPtr m_filesNetJob; NetJobPtr m_filesNetJob;
shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver; shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
QUrl m_sourceUrl; QUrl m_sourceUrl;
QString m_archivePath; QString m_archivePath;
bool m_downloadRequired = false; bool m_downloadRequired = false;
std::unique_ptr<QuaZip> m_packZip; std::unique_ptr<QuaZip> m_packZip;
QFuture<QStringList> m_extractFuture; QFuture<QStringList> m_extractFuture;
QFutureWatcher<QStringList> m_extractFutureWatcher; QFutureWatcher<QStringList> m_extractFutureWatcher;
enum class ModpackType{ enum class ModpackType{
Unknown, Unknown,
MultiMC, MultiMC,
Flame Flame
} m_modpackType = ModpackType::Unknown; } m_modpackType = ModpackType::Unknown;
}; };

View File

@ -27,9 +27,9 @@
#include "FolderInstanceProvider.h" #include "FolderInstanceProvider.h"
InstanceList::InstanceList(QObject *parent) InstanceList::InstanceList(QObject *parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)
{ {
resumeWatch(); resumeWatch();
} }
InstanceList::~InstanceList() InstanceList::~InstanceList()
@ -38,310 +38,310 @@ InstanceList::~InstanceList()
int InstanceList::rowCount(const QModelIndex &parent) const int InstanceList::rowCount(const QModelIndex &parent) const
{ {
Q_UNUSED(parent); Q_UNUSED(parent);
return m_instances.count(); return m_instances.count();
} }
QModelIndex InstanceList::index(int row, int column, const QModelIndex &parent) const QModelIndex InstanceList::index(int row, int column, const QModelIndex &parent) const
{ {
Q_UNUSED(parent); Q_UNUSED(parent);
if (row < 0 || row >= m_instances.size()) if (row < 0 || row >= m_instances.size())
return QModelIndex(); return QModelIndex();
return createIndex(row, column, (void *)m_instances.at(row).get()); return createIndex(row, column, (void *)m_instances.at(row).get());
} }
QVariant InstanceList::data(const QModelIndex &index, int role) const QVariant InstanceList::data(const QModelIndex &index, int role) const
{ {
if (!index.isValid()) if (!index.isValid())
{ {
return QVariant(); return QVariant();
} }
BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer()); BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
switch (role) switch (role)
{ {
case InstancePointerRole: case InstancePointerRole:
{ {
QVariant v = qVariantFromValue((void *)pdata); QVariant v = qVariantFromValue((void *)pdata);
return v; return v;
} }
case InstanceIDRole: case InstanceIDRole:
{ {
return pdata->id(); return pdata->id();
} }
case Qt::DisplayRole: case Qt::DisplayRole:
{ {
return pdata->name(); return pdata->name();
} }
case Qt::ToolTipRole: case Qt::ToolTipRole:
{ {
return pdata->instanceRoot(); return pdata->instanceRoot();
} }
case Qt::DecorationRole: case Qt::DecorationRole:
{ {
return pdata->iconKey(); return pdata->iconKey();
} }
// HACK: see GroupView.h in gui! // HACK: see GroupView.h in gui!
case GroupRole: case GroupRole:
{ {
return pdata->group(); return pdata->group();
} }
default: default:
break; break;
} }
return QVariant(); return QVariant();
} }
Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const
{ {
Qt::ItemFlags f; Qt::ItemFlags f;
if (index.isValid()) if (index.isValid())
{ {
f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable); f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable);
} }
return f; return f;
} }
QStringList InstanceList::getGroups() QStringList InstanceList::getGroups()
{ {
return m_groups.toList(); return m_groups.toList();
} }
void InstanceList::deleteGroup(const QString& name) void InstanceList::deleteGroup(const QString& name)
{ {
for(auto & instance: m_instances) for(auto & instance: m_instances)
{ {
auto instGroupName = instance->group(); auto instGroupName = instance->group();
if(instGroupName == name) if(instGroupName == name)
{ {
instance->setGroupPost(QString()); instance->setGroupPost(QString());
} }
} }
} }
static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &list) static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &list)
{ {
QMap<InstanceId, InstanceLocator> out; QMap<InstanceId, InstanceLocator> out;
int i = 0; int i = 0;
for(auto & item: list) for(auto & item: list)
{ {
auto id = item->id(); auto id = item->id();
if(out.contains(id)) if(out.contains(id))
{ {
qWarning() << "Duplicate ID" << id << "in instance list"; qWarning() << "Duplicate ID" << id << "in instance list";
} }
out[id] = std::make_pair(item, i); out[id] = std::make_pair(item, i);
i++; i++;
} }
return out; return out;
} }
InstanceList::InstListError InstanceList::loadList(bool complete) InstanceList::InstListError InstanceList::loadList(bool complete)
{ {
auto existingIds = getIdMapping(m_instances); auto existingIds = getIdMapping(m_instances);
QList<InstancePtr> newList; QList<InstancePtr> newList;
auto processIds = [&](BaseInstanceProvider * provider, QList<InstanceId> ids) auto processIds = [&](BaseInstanceProvider * provider, QList<InstanceId> ids)
{ {
for(auto & id: ids) for(auto & id: ids)
{ {
if(existingIds.contains(id)) if(existingIds.contains(id))
{ {
auto instPair = existingIds[id]; auto instPair = existingIds[id];
/* /*
auto & instPtr = instPair.first; auto & instPtr = instPair.first;
auto & instIdx = instPair.second; auto & instIdx = instPair.second;
*/ */
existingIds.remove(id); existingIds.remove(id);
qDebug() << "Should keep and soft-reload" << id; qDebug() << "Should keep and soft-reload" << id;
} }
else else
{ {
InstancePtr instPtr = provider->loadInstance(id); InstancePtr instPtr = provider->loadInstance(id);
if(instPtr) if(instPtr)
{ {
newList.append(instPtr); newList.append(instPtr);
} }
} }
} }
}; };
if(complete) if(complete)
{ {
for(auto & item: m_providers) for(auto & item: m_providers)
{ {
processIds(item.get(), item->discoverInstances()); processIds(item.get(), item->discoverInstances());
} }
} }
else else
{ {
for (auto & item: m_updatedProviders) for (auto & item: m_updatedProviders)
{ {
processIds(item, item->discoverInstances()); processIds(item, item->discoverInstances());
} }
} }
// TODO: looks like a general algorithm with a few specifics inserted. Do something about it. // TODO: looks like a general algorithm with a few specifics inserted. Do something about it.
if(!existingIds.isEmpty()) if(!existingIds.isEmpty())
{ {
// get the list of removed instances and sort it by their original index, from last to first // get the list of removed instances and sort it by their original index, from last to first
auto deadList = existingIds.values(); auto deadList = existingIds.values();
auto orderSortPredicate = [](const InstanceLocator & a, const InstanceLocator & b) -> bool auto orderSortPredicate = [](const InstanceLocator & a, const InstanceLocator & b) -> bool
{ {
return a.second > b.second; return a.second > b.second;
}; };
std::sort(deadList.begin(), deadList.end(), orderSortPredicate); std::sort(deadList.begin(), deadList.end(), orderSortPredicate);
// remove the contiguous ranges of rows // remove the contiguous ranges of rows
int front_bookmark = -1; int front_bookmark = -1;
int back_bookmark = -1; int back_bookmark = -1;
int currentItem = -1; int currentItem = -1;
auto removeNow = [&]() auto removeNow = [&]()
{ {
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark); beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1); m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
endRemoveRows(); endRemoveRows();
front_bookmark = -1; front_bookmark = -1;
back_bookmark = currentItem; back_bookmark = currentItem;
}; };
for(auto & removedItem: deadList) for(auto & removedItem: deadList)
{ {
auto instPtr = removedItem.first; auto instPtr = removedItem.first;
if(!complete && !m_updatedProviders.contains(instPtr->provider())) if(!complete && !m_updatedProviders.contains(instPtr->provider()))
{ {
continue; continue;
} }
instPtr->invalidate(); instPtr->invalidate();
currentItem = removedItem.second; currentItem = removedItem.second;
if(back_bookmark == -1) if(back_bookmark == -1)
{ {
// no bookmark yet // no bookmark yet
back_bookmark = currentItem; back_bookmark = currentItem;
} }
else if(currentItem == front_bookmark - 1) else if(currentItem == front_bookmark - 1)
{ {
// part of contiguous sequence, continue // part of contiguous sequence, continue
} }
else else
{ {
// seam between previous and current item // seam between previous and current item
removeNow(); removeNow();
} }
front_bookmark = currentItem; front_bookmark = currentItem;
} }
if(back_bookmark != -1) if(back_bookmark != -1)
{ {
removeNow(); removeNow();
} }
} }
if(newList.size()) if(newList.size())
{ {
add(newList); add(newList);
} }
m_updatedProviders.clear(); m_updatedProviders.clear();
return NoError; return NoError;
} }
void InstanceList::saveNow() void InstanceList::saveNow()
{ {
for(auto & item: m_instances) for(auto & item: m_instances)
{ {
item->saveNow(); item->saveNow();
} }
} }
void InstanceList::add(const QList<InstancePtr> &t) void InstanceList::add(const QList<InstancePtr> &t)
{ {
beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1); beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1);
m_instances.append(t); m_instances.append(t);
for(auto & ptr : t) for(auto & ptr : t)
{ {
connect(ptr.get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged); connect(ptr.get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged);
} }
endInsertRows(); endInsertRows();
} }
void InstanceList::resumeWatch() void InstanceList::resumeWatch()
{ {
if(m_watchLevel > 0) if(m_watchLevel > 0)
{ {
qWarning() << "Bad suspend level resume in instance list"; qWarning() << "Bad suspend level resume in instance list";
return; return;
} }
m_watchLevel++; m_watchLevel++;
if(m_watchLevel > 0 && !m_updatedProviders.isEmpty()) if(m_watchLevel > 0 && !m_updatedProviders.isEmpty())
{ {
loadList(); loadList();
} }
} }
void InstanceList::suspendWatch() void InstanceList::suspendWatch()
{ {
m_watchLevel --; m_watchLevel --;
} }
void InstanceList::providerUpdated() void InstanceList::providerUpdated()
{ {
auto provider = dynamic_cast<BaseInstanceProvider *>(QObject::sender()); auto provider = dynamic_cast<BaseInstanceProvider *>(QObject::sender());
if(!provider) if(!provider)
{ {
qWarning() << "InstanceList::providerUpdated triggered by a non-provider"; qWarning() << "InstanceList::providerUpdated triggered by a non-provider";
return; return;
} }
m_updatedProviders.insert(provider); m_updatedProviders.insert(provider);
if(m_watchLevel == 1) if(m_watchLevel == 1)
{ {
loadList(); loadList();
} }
} }
void InstanceList::groupsPublished(QSet<QString> newGroups) void InstanceList::groupsPublished(QSet<QString> newGroups)
{ {
m_groups.unite(newGroups); m_groups.unite(newGroups);
} }
void InstanceList::addInstanceProvider(BaseInstanceProvider* provider) void InstanceList::addInstanceProvider(BaseInstanceProvider* provider)
{ {
connect(provider, &BaseInstanceProvider::instancesChanged, this, &InstanceList::providerUpdated); connect(provider, &BaseInstanceProvider::instancesChanged, this, &InstanceList::providerUpdated);
connect(provider, &BaseInstanceProvider::groupsChanged, this, &InstanceList::groupsPublished); connect(provider, &BaseInstanceProvider::groupsChanged, this, &InstanceList::groupsPublished);
m_providers.append(provider); m_providers.append(provider);
} }
InstancePtr InstanceList::getInstanceById(QString instId) const InstancePtr InstanceList::getInstanceById(QString instId) const
{ {
if(instId.isEmpty()) if(instId.isEmpty())
return InstancePtr(); return InstancePtr();
for(auto & inst: m_instances) for(auto & inst: m_instances)
{ {
if (inst->id() == instId) if (inst->id() == instId)
{ {
return inst; return inst;
} }
} }
return InstancePtr(); return InstancePtr();
} }
QModelIndex InstanceList::getInstanceIndexById(const QString &id) const QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
{ {
return index(getInstIndex(getInstanceById(id).get())); return index(getInstIndex(getInstanceById(id).get()));
} }
int InstanceList::getInstIndex(BaseInstance *inst) const int InstanceList::getInstIndex(BaseInstance *inst) const
{ {
int count = m_instances.count(); int count = m_instances.count();
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
if (inst == m_instances[i].get()) if (inst == m_instances[i].get())
{ {
return i; return i;
} }
} }
return -1; return -1;
} }
void InstanceList::propertiesChanged(BaseInstance *inst) void InstanceList::propertiesChanged(BaseInstance *inst)
{ {
int i = getInstIndex(inst); int i = getInstIndex(inst);
if (i != -1) if (i != -1)
{ {
emit dataChanged(index(i), index(i)); emit dataChanged(index(i), index(i));
} }
} }

View File

@ -31,75 +31,75 @@ class BaseInstance;
class MULTIMC_LOGIC_EXPORT InstanceList : public QAbstractListModel class MULTIMC_LOGIC_EXPORT InstanceList : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceList(QObject *parent = 0); explicit InstanceList(QObject *parent = 0);
virtual ~InstanceList(); virtual ~InstanceList();
public: public:
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const; int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const; Qt::ItemFlags flags(const QModelIndex &index) const;
enum AdditionalRoles enum AdditionalRoles
{ {
GroupRole = Qt::UserRole, GroupRole = Qt::UserRole,
InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance
InstanceIDRole = 0x34B1CB49 ///< Return id if the instance InstanceIDRole = 0x34B1CB49 ///< Return id if the instance
}; };
/*! /*!
* \brief Error codes returned by functions in the InstanceList class. * \brief Error codes returned by functions in the InstanceList class.
* NoError Indicates that no error occurred. * NoError Indicates that no error occurred.
* UnknownError indicates that an unspecified error occurred. * UnknownError indicates that an unspecified error occurred.
*/ */
enum InstListError enum InstListError
{ {
NoError = 0, NoError = 0,
UnknownError UnknownError
}; };
InstancePtr at(int i) const InstancePtr at(int i) const
{ {
return m_instances.at(i); return m_instances.at(i);
} }
int count() const int count() const
{ {
return m_instances.count(); return m_instances.count();
} }
InstListError loadList(bool complete = false); InstListError loadList(bool complete = false);
void saveNow(); void saveNow();
/// Add an instance provider. Takes ownership of it. Should only be done before the first load. /// Add an instance provider. Takes ownership of it. Should only be done before the first load.
void addInstanceProvider(BaseInstanceProvider * provider); void addInstanceProvider(BaseInstanceProvider * provider);
InstancePtr getInstanceById(QString id) const; InstancePtr getInstanceById(QString id) const;
QModelIndex getInstanceIndexById(const QString &id) const; QModelIndex getInstanceIndexById(const QString &id) const;
QStringList getGroups(); QStringList getGroups();
void deleteGroup(const QString & name); void deleteGroup(const QString & name);
signals: signals:
void dataIsInvalid(); void dataIsInvalid();
private slots: private slots:
void propertiesChanged(BaseInstance *inst); void propertiesChanged(BaseInstance *inst);
void groupsPublished(QSet<QString>); void groupsPublished(QSet<QString>);
void providerUpdated(); void providerUpdated();
private: private:
int getInstIndex(BaseInstance *inst) const; int getInstIndex(BaseInstance *inst) const;
void suspendWatch(); void suspendWatch();
void resumeWatch(); void resumeWatch();
void add(const QList<InstancePtr> &list); void add(const QList<InstancePtr> &list);
protected: protected:
int m_watchLevel = 0; int m_watchLevel = 0;
QSet<BaseInstanceProvider *> m_updatedProviders; QSet<BaseInstanceProvider *> m_updatedProviders;
QList<InstancePtr> m_instances; QList<InstancePtr> m_instances;
QSet<QString> m_groups; QSet<QString> m_groups;
QVector<shared_qobject_ptr<BaseInstanceProvider>> m_providers; QVector<shared_qobject_ptr<BaseInstanceProvider>> m_providers;
}; };

View File

@ -8,48 +8,48 @@ class BaseInstanceProvider;
class MULTIMC_LOGIC_EXPORT InstanceTask : public Task class MULTIMC_LOGIC_EXPORT InstanceTask : public Task
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceTask(); explicit InstanceTask();
virtual ~InstanceTask(); virtual ~InstanceTask();
void setParentSettings(SettingsObjectPtr settings) void setParentSettings(SettingsObjectPtr settings)
{ {
m_globalSettings = settings; m_globalSettings = settings;
} }
void setStagingPath(const QString &stagingPath) void setStagingPath(const QString &stagingPath)
{ {
m_stagingPath = stagingPath; m_stagingPath = stagingPath;
} }
void setName(const QString &name) void setName(const QString &name)
{ {
m_instName = name; m_instName = name;
} }
QString name() const QString name() const
{ {
return m_instName; return m_instName;
} }
void setIcon(const QString &icon) void setIcon(const QString &icon)
{ {
m_instIcon = icon; m_instIcon = icon;
} }
void setGroup(const QString &group) void setGroup(const QString &group)
{ {
m_instGroup = group; m_instGroup = group;
} }
QString group() const QString group() const
{ {
return m_instGroup; return m_instGroup;
} }
protected: /* data */ protected: /* data */
SettingsObjectPtr m_globalSettings; SettingsObjectPtr m_globalSettings;
QString m_instName; QString m_instName;
QString m_instIcon; QString m_instIcon;
QString m_instGroup; QString m_instGroup;
QString m_stagingPath; QString m_stagingPath;
}; };

View File

@ -11,262 +11,262 @@ namespace Json
{ {
void write(const QJsonDocument &doc, const QString &filename) void write(const QJsonDocument &doc, const QString &filename)
{ {
FS::write(filename, doc.toJson()); FS::write(filename, doc.toJson());
} }
void write(const QJsonObject &object, const QString &filename) void write(const QJsonObject &object, const QString &filename)
{ {
write(QJsonDocument(object), filename); write(QJsonDocument(object), filename);
} }
void write(const QJsonArray &array, const QString &filename) void write(const QJsonArray &array, const QString &filename)
{ {
write(QJsonDocument(array), filename); write(QJsonDocument(array), filename);
} }
QByteArray toBinary(const QJsonObject &obj) QByteArray toBinary(const QJsonObject &obj)
{ {
return QJsonDocument(obj).toBinaryData(); return QJsonDocument(obj).toBinaryData();
} }
QByteArray toBinary(const QJsonArray &array) QByteArray toBinary(const QJsonArray &array)
{ {
return QJsonDocument(array).toBinaryData(); return QJsonDocument(array).toBinaryData();
} }
QByteArray toText(const QJsonObject &obj) QByteArray toText(const QJsonObject &obj)
{ {
return QJsonDocument(obj).toJson(QJsonDocument::Compact); return QJsonDocument(obj).toJson(QJsonDocument::Compact);
} }
QByteArray toText(const QJsonArray &array) QByteArray toText(const QJsonArray &array)
{ {
return QJsonDocument(array).toJson(QJsonDocument::Compact); return QJsonDocument(array).toJson(QJsonDocument::Compact);
} }
static bool isBinaryJson(const QByteArray &data) static bool isBinaryJson(const QByteArray &data)
{ {
decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag; decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag;
return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0; return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0;
} }
QJsonDocument requireDocument(const QByteArray &data, const QString &what) QJsonDocument requireDocument(const QByteArray &data, const QString &what)
{ {
if (isBinaryJson(data)) if (isBinaryJson(data))
{ {
QJsonDocument doc = QJsonDocument::fromBinaryData(data); QJsonDocument doc = QJsonDocument::fromBinaryData(data);
if (doc.isNull()) if (doc.isNull())
{ {
throw JsonException(what + ": Invalid JSON (binary JSON detected)"); throw JsonException(what + ": Invalid JSON (binary JSON detected)");
} }
return doc; return doc;
} }
else else
{ {
QJsonParseError error; QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(data, &error); QJsonDocument doc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) if (error.error != QJsonParseError::NoError)
{ {
throw JsonException(what + ": Error parsing JSON: " + error.errorString()); throw JsonException(what + ": Error parsing JSON: " + error.errorString());
} }
return doc; return doc;
} }
} }
QJsonDocument requireDocument(const QString &filename, const QString &what) QJsonDocument requireDocument(const QString &filename, const QString &what)
{ {
return requireDocument(FS::read(filename), what); return requireDocument(FS::read(filename), what);
} }
QJsonObject requireObject(const QJsonDocument &doc, const QString &what) QJsonObject requireObject(const QJsonDocument &doc, const QString &what)
{ {
if (!doc.isObject()) if (!doc.isObject())
{ {
throw JsonException(what + " is not an object"); throw JsonException(what + " is not an object");
} }
return doc.object(); return doc.object();
} }
QJsonArray requireArray(const QJsonDocument &doc, const QString &what) QJsonArray requireArray(const QJsonDocument &doc, const QString &what)
{ {
if (!doc.isArray()) if (!doc.isArray())
{ {
throw JsonException(what + " is not an array"); throw JsonException(what + " is not an array");
} }
return doc.array(); return doc.array();
} }
void writeString(QJsonObject &to, const QString &key, const QString &value) void writeString(QJsonObject &to, const QString &key, const QString &value)
{ {
if (!value.isEmpty()) if (!value.isEmpty())
{ {
to.insert(key, value); to.insert(key, value);
} }
} }
void writeStringList(QJsonObject &to, const QString &key, const QStringList &values) void writeStringList(QJsonObject &to, const QString &key, const QStringList &values)
{ {
if (!values.isEmpty()) if (!values.isEmpty())
{ {
QJsonArray array; QJsonArray array;
for(auto value: values) for(auto value: values)
{ {
array.append(value); array.append(value);
} }
to.insert(key, array); to.insert(key, array);
} }
} }
template<> template<>
QJsonValue toJson<QUrl>(const QUrl &url) QJsonValue toJson<QUrl>(const QUrl &url)
{ {
return QJsonValue(url.toString(QUrl::FullyEncoded)); return QJsonValue(url.toString(QUrl::FullyEncoded));
} }
template<> template<>
QJsonValue toJson<QByteArray>(const QByteArray &data) QJsonValue toJson<QByteArray>(const QByteArray &data)
{ {
return QJsonValue(QString::fromLatin1(data.toHex())); return QJsonValue(QString::fromLatin1(data.toHex()));
} }
template<> template<>
QJsonValue toJson<QDateTime>(const QDateTime &datetime) QJsonValue toJson<QDateTime>(const QDateTime &datetime)
{ {
return QJsonValue(datetime.toString(Qt::ISODate)); return QJsonValue(datetime.toString(Qt::ISODate));
} }
template<> template<>
QJsonValue toJson<QDir>(const QDir &dir) QJsonValue toJson<QDir>(const QDir &dir)
{ {
return QDir::current().relativeFilePath(dir.absolutePath()); return QDir::current().relativeFilePath(dir.absolutePath());
} }
template<> template<>
QJsonValue toJson<QUuid>(const QUuid &uuid) QJsonValue toJson<QUuid>(const QUuid &uuid)
{ {
return uuid.toString(); return uuid.toString();
} }
template<> template<>
QJsonValue toJson<QVariant>(const QVariant &variant) QJsonValue toJson<QVariant>(const QVariant &variant)
{ {
return QJsonValue::fromVariant(variant); return QJsonValue::fromVariant(variant);
} }
template<> QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what) template<> QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what)
{ {
const QString string = ensureIsType<QString>(value, what); const QString string = ensureIsType<QString>(value, what);
// ensure that the string can be safely cast to Latin1 // ensure that the string can be safely cast to Latin1
if (string != QString::fromLatin1(string.toLatin1())) if (string != QString::fromLatin1(string.toLatin1()))
{ {
throw JsonException(what + " is not encodable as Latin1"); throw JsonException(what + " is not encodable as Latin1");
} }
return QByteArray::fromHex(string.toLatin1()); return QByteArray::fromHex(string.toLatin1());
} }
template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what) template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what)
{ {
if (!value.isArray()) if (!value.isArray())
{ {
throw JsonException(what + " is not an array"); throw JsonException(what + " is not an array");
} }
return value.toArray(); return value.toArray();
} }
template<> QString requireIsType<QString>(const QJsonValue &value, const QString &what) template<> QString requireIsType<QString>(const QJsonValue &value, const QString &what)
{ {
if (!value.isString()) if (!value.isString())
{ {
throw JsonException(what + " is not a string"); throw JsonException(what + " is not a string");
} }
return value.toString(); return value.toString();
} }
template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what) template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what)
{ {
if (!value.isBool()) if (!value.isBool())
{ {
throw JsonException(what + " is not a bool"); throw JsonException(what + " is not a bool");
} }
return value.toBool(); return value.toBool();
} }
template<> double requireIsType<double>(const QJsonValue &value, const QString &what) template<> double requireIsType<double>(const QJsonValue &value, const QString &what)
{ {
if (!value.isDouble()) if (!value.isDouble())
{ {
throw JsonException(what + " is not a double"); throw JsonException(what + " is not a double");
} }
return value.toDouble(); return value.toDouble();
} }
template<> int requireIsType<int>(const QJsonValue &value, const QString &what) template<> int requireIsType<int>(const QJsonValue &value, const QString &what)
{ {
const double doubl = requireIsType<double>(value, what); const double doubl = requireIsType<double>(value, what);
if (fmod(doubl, 1) != 0) if (fmod(doubl, 1) != 0)
{ {
throw JsonException(what + " is not an integer"); throw JsonException(what + " is not an integer");
} }
return int(doubl); return int(doubl);
} }
template<> QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what) template<> QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what)
{ {
const QString string = requireIsType<QString>(value, what); const QString string = requireIsType<QString>(value, what);
const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate); const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate);
if (!datetime.isValid()) if (!datetime.isValid())
{ {
throw JsonException(what + " is not a ISO formatted date/time value"); throw JsonException(what + " is not a ISO formatted date/time value");
} }
return datetime; return datetime;
} }
template<> QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what) template<> QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what)
{ {
const QString string = ensureIsType<QString>(value, what); const QString string = ensureIsType<QString>(value, what);
if (string.isEmpty()) if (string.isEmpty())
{ {
return QUrl(); return QUrl();
} }
const QUrl url = QUrl(string, QUrl::StrictMode); const QUrl url = QUrl(string, QUrl::StrictMode);
if (!url.isValid()) if (!url.isValid())
{ {
throw JsonException(what + " is not a correctly formatted URL"); throw JsonException(what + " is not a correctly formatted URL");
} }
return url; return url;
} }
template<> QDir requireIsType<QDir>(const QJsonValue &value, const QString &what) template<> QDir requireIsType<QDir>(const QJsonValue &value, const QString &what)
{ {
const QString string = requireIsType<QString>(value, what); const QString string = requireIsType<QString>(value, what);
// FIXME: does not handle invalid characters! // FIXME: does not handle invalid characters!
return QDir::current().absoluteFilePath(string); return QDir::current().absoluteFilePath(string);
} }
template<> QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what) template<> QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what)
{ {
const QString string = requireIsType<QString>(value, what); const QString string = requireIsType<QString>(value, what);
const QUuid uuid = QUuid(string); const QUuid uuid = QUuid(string);
if (uuid.toString() != string) // converts back => valid if (uuid.toString() != string) // converts back => valid
{ {
throw JsonException(what + " is not a valid UUID"); throw JsonException(what + " is not a valid UUID");
} }
return uuid; return uuid;
} }
template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what) template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what)
{ {
if (!value.isObject()) if (!value.isObject())
{ {
throw JsonException(what + " is not an object"); throw JsonException(what + " is not an object");
} }
return value.toObject(); return value.toObject();
} }
template<> QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what) template<> QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what)
{ {
if (value.isNull() || value.isUndefined()) if (value.isNull() || value.isUndefined())
{ {
throw JsonException(what + " is null or undefined"); throw JsonException(what + " is null or undefined");
} }
return value.toVariant(); return value.toVariant();
} }
template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what) template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what)
{ {
if (value.isNull() || value.isUndefined()) if (value.isNull() || value.isUndefined())
{ {
throw JsonException(what + " is null or undefined"); throw JsonException(what + " is null or undefined");
} }
return value; return value;
} }
} }

View File

@ -19,7 +19,7 @@ namespace Json
class MULTIMC_LOGIC_EXPORT JsonException : public ::Exception class MULTIMC_LOGIC_EXPORT JsonException : public ::Exception
{ {
public: public:
JsonException(const QString &message) : Exception(message) {} JsonException(const QString &message) : Exception(message) {}
}; };
/// @throw FileSystemException /// @throw FileSystemException
@ -51,7 +51,7 @@ void writeStringList(QJsonObject & to, const QString &key, const QStringList &va
template<typename T> template<typename T>
QJsonValue toJson(const T &t) QJsonValue toJson(const T &t)
{ {
return QJsonValue(t); return QJsonValue(t);
} }
template<> template<>
QJsonValue toJson<QUrl>(const QUrl &url); QJsonValue toJson<QUrl>(const QUrl &url);
@ -69,12 +69,12 @@ QJsonValue toJson<QVariant>(const QVariant &variant);
template<typename T> template<typename T>
QJsonArray toJsonArray(const QList<T> &container) QJsonArray toJsonArray(const QList<T> &container)
{ {
QJsonArray array; QJsonArray array;
for (const T item : container) for (const T item : container)
{ {
array.append(toJson<T>(item)); array.append(toJson<T>(item));
} }
return array; return array;
} }
////////////////// READING //////////////////// ////////////////// READING ////////////////////
@ -115,119 +115,119 @@ template<> MULTIMC_LOGIC_EXPORT QUrl requireIsType<QUrl>(const QJsonValue &value
template <typename T> template <typename T>
T ensureIsType(const QJsonValue &value, const T default_ = T(), const QString &what = "Value") T ensureIsType(const QJsonValue &value, const T default_ = T(), const QString &what = "Value")
{ {
if (value.isUndefined() || value.isNull()) if (value.isUndefined() || value.isNull())
{ {
return default_; return default_;
} }
try try
{ {
return requireIsType<T>(value, what); return requireIsType<T>(value, what);
} }
catch (const JsonException &) catch (const JsonException &)
{ {
return default_; return default_;
} }
} }
/// @throw JsonException /// @throw JsonException
template <typename T> template <typename T>
T requireIsType(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") T requireIsType(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__")
{ {
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key)) if (!parent.contains(key))
{ {
throw JsonException(localWhat + "s parent does not contain " + localWhat); throw JsonException(localWhat + "s parent does not contain " + localWhat);
} }
return requireIsType<T>(parent.value(key), localWhat); return requireIsType<T>(parent.value(key), localWhat);
} }
template <typename T> template <typename T>
T ensureIsType(const QJsonObject &parent, const QString &key, const T default_ = T(), const QString &what = "__placeholder__") T ensureIsType(const QJsonObject &parent, const QString &key, const T default_ = T(), const QString &what = "__placeholder__")
{ {
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key)) if (!parent.contains(key))
{ {
return default_; return default_;
} }
return ensureIsType<T>(parent.value(key), default_, localWhat); return ensureIsType<T>(parent.value(key), default_, localWhat);
} }
template <typename T> template <typename T>
QVector<T> requireIsArrayOf(const QJsonDocument &doc) QVector<T> requireIsArrayOf(const QJsonDocument &doc)
{ {
const QJsonArray array = requireArray(doc); const QJsonArray array = requireArray(doc);
QVector<T> out; QVector<T> out;
for (const QJsonValue val : array) for (const QJsonValue val : array)
{ {
out.append(requireIsType<T>(val, "Document")); out.append(requireIsType<T>(val, "Document"));
} }
return out; return out;
} }
template <typename T> template <typename T>
QVector<T> ensureIsArrayOf(const QJsonValue &value, const QString &what = "Value") QVector<T> ensureIsArrayOf(const QJsonValue &value, const QString &what = "Value")
{ {
const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what); const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what);
QVector<T> out; QVector<T> out;
for (const QJsonValue val : array) for (const QJsonValue val : array)
{ {
out.append(requireIsType<T>(val, what)); out.append(requireIsType<T>(val, what));
} }
return out; return out;
} }
template <typename T> template <typename T>
QVector<T> ensureIsArrayOf(const QJsonValue &value, const QVector<T> default_, const QString &what = "Value") QVector<T> ensureIsArrayOf(const QJsonValue &value, const QVector<T> default_, const QString &what = "Value")
{ {
if (value.isUndefined()) if (value.isUndefined())
{ {
return default_; return default_;
} }
return ensureIsArrayOf<T>(value, what); return ensureIsArrayOf<T>(value, what);
} }
/// @throw JsonException /// @throw JsonException
template <typename T> template <typename T>
QVector<T> requireIsArrayOf(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") QVector<T> requireIsArrayOf(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__")
{ {
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key)) if (!parent.contains(key))
{ {
throw JsonException(localWhat + "s parent does not contain " + localWhat); throw JsonException(localWhat + "s parent does not contain " + localWhat);
} }
return ensureIsArrayOf<T>(parent.value(key), localWhat); return ensureIsArrayOf<T>(parent.value(key), localWhat);
} }
template <typename T> template <typename T>
QVector<T> ensureIsArrayOf(const QJsonObject &parent, const QString &key, QVector<T> ensureIsArrayOf(const QJsonObject &parent, const QString &key,
const QVector<T> &default_ = QVector<T>(), const QString &what = "__placeholder__") const QVector<T> &default_ = QVector<T>(), const QString &what = "__placeholder__")
{ {
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key)) if (!parent.contains(key))
{ {
return default_; return default_;
} }
return ensureIsArrayOf<T>(parent.value(key), default_, localWhat); return ensureIsArrayOf<T>(parent.value(key), default_, localWhat);
} }
// this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers // this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers
#define JSON_HELPERFUNCTIONS(NAME, TYPE) \ #define JSON_HELPERFUNCTIONS(NAME, TYPE) \
inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \ inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \
{ \ { \
return requireIsType<TYPE>(value, what); \ return requireIsType<TYPE>(value, what); \
} \ } \
inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \ inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \
{ \ { \
return ensureIsType<TYPE>(value, default_, what); \ return ensureIsType<TYPE>(value, default_, what); \
} \ } \
inline TYPE require##NAME(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") \ inline TYPE require##NAME(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") \
{ \ { \
return requireIsType<TYPE>(parent, key, what); \ return requireIsType<TYPE>(parent, key, what); \
} \ } \
inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_ = TYPE(), const QString &what = "__placeholder") \ inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_ = TYPE(), const QString &what = "__placeholder") \
{ \ { \
return ensureIsType<TYPE>(parent, key, default_, what); \ return ensureIsType<TYPE>(parent, key, default_, what); \
} }
JSON_HELPERFUNCTIONS(Array, QJsonArray) JSON_HELPERFUNCTIONS(Array, QJsonArray)
JSON_HELPERFUNCTIONS(Object, QJsonObject) JSON_HELPERFUNCTIONS(Object, QJsonObject)

View File

@ -4,157 +4,157 @@
LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent) LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
{ {
// QProcess has a strange interface... let's map a lot of those into a few. // QProcess has a strange interface... let's map a lot of those into a few.
connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut); connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr); connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr);
connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus))); connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus)));
connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError))); connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError)));
connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange); connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange);
} }
LoggedProcess::~LoggedProcess() LoggedProcess::~LoggedProcess()
{ {
if(m_is_detachable) if(m_is_detachable)
{ {
setProcessState(QProcess::NotRunning); setProcessState(QProcess::NotRunning);
} }
} }
QStringList reprocess(const QByteArray & data, QString & leftover) QStringList reprocess(const QByteArray & data, QString & leftover)
{ {
QString str = leftover + QString::fromLocal8Bit(data); QString str = leftover + QString::fromLocal8Bit(data);
str.remove('\r'); str.remove('\r');
QStringList lines = str.split("\n"); QStringList lines = str.split("\n");
leftover = lines.takeLast(); leftover = lines.takeLast();
return lines; return lines;
} }
void LoggedProcess::on_stdErr() void LoggedProcess::on_stdErr()
{ {
auto lines = reprocess(readAllStandardError(), m_err_leftover); auto lines = reprocess(readAllStandardError(), m_err_leftover);
emit log(lines, MessageLevel::StdErr); emit log(lines, MessageLevel::StdErr);
} }
void LoggedProcess::on_stdOut() void LoggedProcess::on_stdOut()
{ {
auto lines = reprocess(readAllStandardOutput(), m_out_leftover); auto lines = reprocess(readAllStandardOutput(), m_out_leftover);
emit log(lines, MessageLevel::StdOut); emit log(lines, MessageLevel::StdOut);
} }
void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status) void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status)
{ {
// save the exit code // save the exit code
m_exit_code = exit_code; m_exit_code = exit_code;
// Flush console window // Flush console window
if (!m_err_leftover.isEmpty()) if (!m_err_leftover.isEmpty())
{ {
emit log({m_err_leftover}, MessageLevel::StdErr); emit log({m_err_leftover}, MessageLevel::StdErr);
m_err_leftover.clear(); m_err_leftover.clear();
} }
if (!m_out_leftover.isEmpty()) if (!m_out_leftover.isEmpty())
{ {
emit log({m_err_leftover}, MessageLevel::StdOut); emit log({m_err_leftover}, MessageLevel::StdOut);
m_out_leftover.clear(); m_out_leftover.clear();
} }
// based on state, send signals // based on state, send signals
if (!m_is_aborting) if (!m_is_aborting)
{ {
if (status == QProcess::NormalExit) if (status == QProcess::NormalExit)
{ {
//: Message displayed on instance exit //: Message displayed on instance exit
emit log({tr("Process exited with code %1.").arg(exit_code)}, MessageLevel::MultiMC); emit log({tr("Process exited with code %1.").arg(exit_code)}, MessageLevel::MultiMC);
changeState(LoggedProcess::Finished); changeState(LoggedProcess::Finished);
} }
else else
{ {
//: Message displayed on instance crashed //: Message displayed on instance crashed
if(exit_code == -1) if(exit_code == -1)
emit log({tr("Process crashed.")}, MessageLevel::MultiMC); emit log({tr("Process crashed.")}, MessageLevel::MultiMC);
else else
emit log({tr("Process crashed with exitcode %1.").arg(exit_code)}, MessageLevel::MultiMC); emit log({tr("Process crashed with exitcode %1.").arg(exit_code)}, MessageLevel::MultiMC);
changeState(LoggedProcess::Crashed); changeState(LoggedProcess::Crashed);
} }
} }
else else
{ {
//: Message displayed after the instance exits due to kill request //: Message displayed after the instance exits due to kill request
emit log({tr("Process was killed by user.")}, MessageLevel::Error); emit log({tr("Process was killed by user.")}, MessageLevel::Error);
changeState(LoggedProcess::Aborted); changeState(LoggedProcess::Aborted);
} }
} }
void LoggedProcess::on_error(QProcess::ProcessError error) void LoggedProcess::on_error(QProcess::ProcessError error)
{ {
switch(error) switch(error)
{ {
case QProcess::FailedToStart: case QProcess::FailedToStart:
{ {
emit log({tr("The process failed to start.")}, MessageLevel::Fatal); emit log({tr("The process failed to start.")}, MessageLevel::Fatal);
changeState(LoggedProcess::FailedToStart); changeState(LoggedProcess::FailedToStart);
break; break;
} }
// we'll just ignore those... never needed them // we'll just ignore those... never needed them
case QProcess::Crashed: case QProcess::Crashed:
case QProcess::ReadError: case QProcess::ReadError:
case QProcess::Timedout: case QProcess::Timedout:
case QProcess::UnknownError: case QProcess::UnknownError:
case QProcess::WriteError: case QProcess::WriteError:
break; break;
} }
} }
void LoggedProcess::kill() void LoggedProcess::kill()
{ {
m_is_aborting = true; m_is_aborting = true;
QProcess::kill(); QProcess::kill();
} }
int LoggedProcess::exitCode() const int LoggedProcess::exitCode() const
{ {
return m_exit_code; return m_exit_code;
} }
void LoggedProcess::changeState(LoggedProcess::State state) void LoggedProcess::changeState(LoggedProcess::State state)
{ {
if(state == m_state) if(state == m_state)
return; return;
m_state = state; m_state = state;
emit stateChanged(m_state); emit stateChanged(m_state);
} }
LoggedProcess::State LoggedProcess::state() const LoggedProcess::State LoggedProcess::state() const
{ {
return m_state; return m_state;
} }
void LoggedProcess::on_stateChange(QProcess::ProcessState state) void LoggedProcess::on_stateChange(QProcess::ProcessState state)
{ {
switch(state) switch(state)
{ {
case QProcess::NotRunning: case QProcess::NotRunning:
break; // let's not - there are too many that handle this already. break; // let's not - there are too many that handle this already.
case QProcess::Starting: case QProcess::Starting:
{ {
if(m_state != LoggedProcess::NotRunning) if(m_state != LoggedProcess::NotRunning)
{ {
qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Starting; qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Starting;
} }
changeState(LoggedProcess::Starting); changeState(LoggedProcess::Starting);
return; return;
} }
case QProcess::Running: case QProcess::Running:
{ {
if(m_state != LoggedProcess::Starting) if(m_state != LoggedProcess::Starting)
{ {
qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Running; qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Running;
} }
changeState(LoggedProcess::Running); changeState(LoggedProcess::Running);
return; return;
} }
} }
} }
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
@ -172,5 +172,5 @@ qint64 LoggedProcess::processId() const
void LoggedProcess::setDetachable(bool detachable) void LoggedProcess::setDetachable(bool detachable)
{ {
m_is_detachable = detachable; m_is_detachable = detachable;
} }

View File

@ -27,54 +27,54 @@ class MULTIMC_LOGIC_EXPORT LoggedProcess : public QProcess
{ {
Q_OBJECT Q_OBJECT
public: public:
enum State enum State
{ {
NotRunning, NotRunning,
Starting, Starting,
FailedToStart, FailedToStart,
Running, Running,
Finished, Finished,
Crashed, Crashed,
Aborted Aborted
}; };
public: public:
explicit LoggedProcess(QObject* parent = 0); explicit LoggedProcess(QObject* parent = 0);
virtual ~LoggedProcess(); virtual ~LoggedProcess();
State state() const; State state() const;
int exitCode() const; int exitCode() const;
qint64 processId() const; qint64 processId() const;
void setDetachable(bool detachable); void setDetachable(bool detachable);
signals: signals:
void log(QStringList lines, MessageLevel::Enum level); void log(QStringList lines, MessageLevel::Enum level);
void stateChanged(LoggedProcess::State state); void stateChanged(LoggedProcess::State state);
public slots: public slots:
/** /**
* @brief kill the process - equivalent to kill -9 * @brief kill the process - equivalent to kill -9
*/ */
void kill(); void kill();
private slots: private slots:
void on_stdErr(); void on_stdErr();
void on_stdOut(); void on_stdOut();
void on_exit(int exit_code, QProcess::ExitStatus status); void on_exit(int exit_code, QProcess::ExitStatus status);
void on_error(QProcess::ProcessError error); void on_error(QProcess::ProcessError error);
void on_stateChange(QProcess::ProcessState); void on_stateChange(QProcess::ProcessState);
private: private:
void changeState(LoggedProcess::State state); void changeState(LoggedProcess::State state);
private: private:
QString m_err_leftover; QString m_err_leftover;
QString m_out_leftover; QString m_out_leftover;
bool m_killed = false; bool m_killed = false;
State m_state = NotRunning; State m_state = NotRunning;
int m_exit_code = 0; int m_exit_code = 0;
bool m_is_aborting = false; bool m_is_aborting = false;
bool m_is_detachable = false; bool m_is_detachable = false;
}; };

View File

@ -3,74 +3,74 @@
/// TAKEN FROM Qt, because it doesn't expose it intelligently /// TAKEN FROM Qt, because it doesn't expose it intelligently
static inline QChar getNextChar(const QString &s, int location) static inline QChar getNextChar(const QString &s, int location)
{ {
return (location < s.length()) ? s.at(location) : QChar(); return (location < s.length()) ? s.at(location) : QChar();
} }
/// TAKEN FROM Qt, because it doesn't expose it intelligently /// TAKEN FROM Qt, because it doesn't expose it intelligently
int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs) int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs)
{ {
for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2)
{ {
// skip spaces, tabs and 0's // skip spaces, tabs and 0's
QChar c1 = getNextChar(s1, l1); QChar c1 = getNextChar(s1, l1);
while (c1.isSpace()) while (c1.isSpace())
c1 = getNextChar(s1, ++l1); c1 = getNextChar(s1, ++l1);
QChar c2 = getNextChar(s2, l2); QChar c2 = getNextChar(s2, l2);
while (c2.isSpace()) while (c2.isSpace())
c2 = getNextChar(s2, ++l2); c2 = getNextChar(s2, ++l2);
if (c1.isDigit() && c2.isDigit()) if (c1.isDigit() && c2.isDigit())
{ {
while (c1.digitValue() == 0) while (c1.digitValue() == 0)
c1 = getNextChar(s1, ++l1); c1 = getNextChar(s1, ++l1);
while (c2.digitValue() == 0) while (c2.digitValue() == 0)
c2 = getNextChar(s2, ++l2); c2 = getNextChar(s2, ++l2);
int lookAheadLocation1 = l1; int lookAheadLocation1 = l1;
int lookAheadLocation2 = l2; int lookAheadLocation2 = l2;
int currentReturnValue = 0; int currentReturnValue = 0;
// find the last digit, setting currentReturnValue as we go if it isn't equal // find the last digit, setting currentReturnValue as we go if it isn't equal
for (QChar lookAhead1 = c1, lookAhead2 = c2; for (QChar lookAhead1 = c1, lookAhead2 = c2;
(lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length()); (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
lookAhead1 = getNextChar(s1, ++lookAheadLocation1), lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
lookAhead2 = getNextChar(s2, ++lookAheadLocation2)) lookAhead2 = getNextChar(s2, ++lookAheadLocation2))
{ {
bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit(); bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit(); bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
if (!is1ADigit && !is2ADigit) if (!is1ADigit && !is2ADigit)
break; break;
if (!is1ADigit) if (!is1ADigit)
return -1; return -1;
if (!is2ADigit) if (!is2ADigit)
return 1; return 1;
if (currentReturnValue == 0) if (currentReturnValue == 0)
{ {
if (lookAhead1 < lookAhead2) if (lookAhead1 < lookAhead2)
{ {
currentReturnValue = -1; currentReturnValue = -1;
} }
else if (lookAhead1 > lookAhead2) else if (lookAhead1 > lookAhead2)
{ {
currentReturnValue = 1; currentReturnValue = 1;
} }
} }
} }
if (currentReturnValue != 0) if (currentReturnValue != 0)
return currentReturnValue; return currentReturnValue;
} }
if (cs == Qt::CaseInsensitive) if (cs == Qt::CaseInsensitive)
{ {
if (!c1.isLower()) if (!c1.isLower())
c1 = c1.toLower(); c1 = c1.toLower();
if (!c2.isLower()) if (!c2.isLower())
c2 = c2.toLower(); c2 = c2.toLower();
} }
int r = QString::localeAwareCompare(c1, c2); int r = QString::localeAwareCompare(c1, c2);
if (r < 0) if (r < 0)
return -1; return -1;
if (r > 0) if (r > 0)
return 1; return 1;
} }
// The two strings are the same (02 == 2) so fall back to the normal sort // The two strings are the same (02 == 2) so fall back to the normal sort
return QString::compare(s1, s2, cs); return QString::compare(s1, s2, cs);
} }

View File

@ -6,5 +6,5 @@
namespace Strings namespace Strings
{ {
int MULTIMC_LOGIC_EXPORT naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs); int MULTIMC_LOGIC_EXPORT naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
} }

View File

@ -25,231 +25,231 @@
// ours // ours
bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, const JlCompress::FilterFunction filter) bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, const JlCompress::FilterFunction filter)
{ {
QuaZip modZip(from.filePath()); QuaZip modZip(from.filePath());
modZip.open(QuaZip::mdUnzip); modZip.open(QuaZip::mdUnzip);
QuaZipFile fileInsideMod(&modZip); QuaZipFile fileInsideMod(&modZip);
QuaZipFile zipOutFile(into); QuaZipFile zipOutFile(into);
for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile()) for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile())
{ {
QString filename = modZip.getCurrentFileName(); QString filename = modZip.getCurrentFileName();
if (filter && !filter(filename)) if (filter && !filter(filename))
{ {
qDebug() << "Skipping file " << filename << " from " qDebug() << "Skipping file " << filename << " from "
<< from.fileName() << " - filtered"; << from.fileName() << " - filtered";
continue; continue;
} }
if (contained.contains(filename)) if (contained.contains(filename))
{ {
qDebug() << "Skipping already contained file " << filename << " from " qDebug() << "Skipping already contained file " << filename << " from "
<< from.fileName(); << from.fileName();
continue; continue;
} }
contained.insert(filename); contained.insert(filename);
if (!fileInsideMod.open(QIODevice::ReadOnly)) if (!fileInsideMod.open(QIODevice::ReadOnly))
{ {
qCritical() << "Failed to open " << filename << " from " << from.fileName(); qCritical() << "Failed to open " << filename << " from " << from.fileName();
return false; return false;
} }
QuaZipNewInfo info_out(fileInsideMod.getActualFileName()); QuaZipNewInfo info_out(fileInsideMod.getActualFileName());
if (!zipOutFile.open(QIODevice::WriteOnly, info_out)) if (!zipOutFile.open(QIODevice::WriteOnly, info_out))
{ {
qCritical() << "Failed to open " << filename << " in the jar"; qCritical() << "Failed to open " << filename << " in the jar";
fileInsideMod.close(); fileInsideMod.close();
return false; return false;
} }
if (!JlCompress::copyData(fileInsideMod, zipOutFile)) if (!JlCompress::copyData(fileInsideMod, zipOutFile))
{ {
zipOutFile.close(); zipOutFile.close();
fileInsideMod.close(); fileInsideMod.close();
qCritical() << "Failed to copy data of " << filename << " into the jar"; qCritical() << "Failed to copy data of " << filename << " into the jar";
return false; return false;
} }
zipOutFile.close(); zipOutFile.close();
fileInsideMod.close(); fileInsideMod.close();
} }
return true; return true;
} }
// ours // ours
bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods) bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods)
{ {
QuaZip zipOut(targetJarPath); QuaZip zipOut(targetJarPath);
if (!zipOut.open(QuaZip::mdCreate)) if (!zipOut.open(QuaZip::mdCreate))
{ {
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to open the minecraft.jar for modding"; qCritical() << "Failed to open the minecraft.jar for modding";
return false; return false;
} }
// Files already added to the jar. // Files already added to the jar.
// These files will be skipped. // These files will be skipped.
QSet<QString> addedFiles; QSet<QString> addedFiles;
// Modify the jar // Modify the jar
QListIterator<Mod> i(mods); QListIterator<Mod> i(mods);
i.toBack(); i.toBack();
while (i.hasPrevious()) while (i.hasPrevious())
{ {
const Mod &mod = i.previous(); const Mod &mod = i.previous();
// do not merge disabled mods. // do not merge disabled mods.
if (!mod.enabled()) if (!mod.enabled())
continue; continue;
if (mod.type() == Mod::MOD_ZIPFILE) if (mod.type() == Mod::MOD_ZIPFILE)
{ {
if (!mergeZipFiles(&zipOut, mod.filename(), addedFiles)) if (!mergeZipFiles(&zipOut, mod.filename(), addedFiles))
{ {
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar."; qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
return false; return false;
} }
} }
else if (mod.type() == Mod::MOD_SINGLEFILE) else if (mod.type() == Mod::MOD_SINGLEFILE)
{ {
// FIXME: buggy - does not work with addedFiles // FIXME: buggy - does not work with addedFiles
auto filename = mod.filename(); auto filename = mod.filename();
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName()))
{ {
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar."; qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
return false; return false;
} }
addedFiles.insert(filename.fileName()); addedFiles.insert(filename.fileName());
} }
else if (mod.type() == Mod::MOD_FOLDER) else if (mod.type() == Mod::MOD_FOLDER)
{ {
// FIXME: buggy - does not work with addedFiles // FIXME: buggy - does not work with addedFiles
auto filename = mod.filename(); auto filename = mod.filename();
QString what_to_zip = filename.absoluteFilePath(); QString what_to_zip = filename.absoluteFilePath();
QDir dir(what_to_zip); QDir dir(what_to_zip);
dir.cdUp(); dir.cdUp();
QString parent_dir = dir.absolutePath(); QString parent_dir = dir.absolutePath();
if (!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, addedFiles)) if (!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, addedFiles))
{ {
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar."; qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
return false; return false;
} }
qDebug() << "Adding folder " << filename.fileName() << " from " qDebug() << "Adding folder " << filename.fileName() << " from "
<< filename.absoluteFilePath(); << filename.absoluteFilePath();
} }
else else
{ {
// Make sure we do not continue launching when something is missing or undefined... // Make sure we do not continue launching when something is missing or undefined...
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add unknown mod type" << mod.filename().fileName() << "to the jar."; qCritical() << "Failed to add unknown mod type" << mod.filename().fileName() << "to the jar.";
return false; return false;
} }
} }
if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key){return !key.contains("META-INF");})) if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key){return !key.contains("META-INF");}))
{ {
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to insert minecraft.jar contents."; qCritical() << "Failed to insert minecraft.jar contents.";
return false; return false;
} }
// Recompress the jar // Recompress the jar
zipOut.close(); zipOut.close();
if (zipOut.getZipError() != 0) if (zipOut.getZipError() != 0)
{ {
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to finalize minecraft.jar!"; qCritical() << "Failed to finalize minecraft.jar!";
return false; return false;
} }
return true; return true;
} }
// ours // ours
QString MMCZip::findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root) QString MMCZip::findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root)
{ {
QuaZipDir rootDir(zip, root); QuaZipDir rootDir(zip, root);
for(auto fileName: rootDir.entryList(QDir::Files)) for(auto fileName: rootDir.entryList(QDir::Files))
{ {
if(fileName == what) if(fileName == what)
return root; return root;
} }
for(auto fileName: rootDir.entryList(QDir::Dirs)) for(auto fileName: rootDir.entryList(QDir::Dirs))
{ {
QString result = findFolderOfFileInZip(zip, what, root + fileName); QString result = findFolderOfFileInZip(zip, what, root + fileName);
if(!result.isEmpty()) if(!result.isEmpty())
{ {
return result; return result;
} }
} }
return QString(); return QString();
} }
// ours // ours
bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root) bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root)
{ {
QuaZipDir rootDir(zip, root); QuaZipDir rootDir(zip, root);
for(auto fileName: rootDir.entryList(QDir::Files)) for(auto fileName: rootDir.entryList(QDir::Files))
{ {
if(fileName == what) if(fileName == what)
{ {
result.append(root); result.append(root);
return true; return true;
} }
} }
for(auto fileName: rootDir.entryList(QDir::Dirs)) for(auto fileName: rootDir.entryList(QDir::Dirs))
{ {
findFilesInZip(zip, what, result, root + fileName); findFilesInZip(zip, what, result, root + fileName);
} }
return !result.isEmpty(); return !result.isEmpty();
} }
// ours // ours
QStringList MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target) QStringList MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target)
{ {
QDir directory(target); QDir directory(target);
QStringList extracted; QStringList extracted;
qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target; qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target;
if (!zip->goToFirstFile()) if (!zip->goToFirstFile())
{ {
qWarning() << "Failed to seek to first file in zip"; qWarning() << "Failed to seek to first file in zip";
return QStringList(); return QStringList();
} }
do do
{ {
QString name = zip->getCurrentFileName(); QString name = zip->getCurrentFileName();
if(!name.startsWith(subdir)) if(!name.startsWith(subdir))
{ {
continue; continue;
} }
name.remove(0, subdir.size()); name.remove(0, subdir.size());
QString absFilePath = directory.absoluteFilePath(name); QString absFilePath = directory.absoluteFilePath(name);
if(name.isEmpty()) if(name.isEmpty())
{ {
absFilePath += "/"; absFilePath += "/";
} }
if (!JlCompress::extractFile(zip, "", absFilePath)) if (!JlCompress::extractFile(zip, "", absFilePath))
{ {
qWarning() << "Failed to extract file" << name << "to" << absFilePath; qWarning() << "Failed to extract file" << name << "to" << absFilePath;
JlCompress::removeFile(extracted); JlCompress::removeFile(extracted);
return QStringList(); return QStringList();
} }
extracted.append(absFilePath); extracted.append(absFilePath);
qDebug() << "Extracted file" << name; qDebug() << "Extracted file" << name;
} while (zip->goToNextFile()); } while (zip->goToNextFile());
return extracted; return extracted;
} }
// ours // ours
QStringList MMCZip::extractDir(QString fileCompressed, QString dir) QStringList MMCZip::extractDir(QString fileCompressed, QString dir)
{ {
QuaZip zip(fileCompressed); QuaZip zip(fileCompressed);
if (!zip.open(QuaZip::mdUnzip)) if (!zip.open(QuaZip::mdUnzip))
{ {
return {}; return {};
} }
return MMCZip::extractSubDir(&zip, "", dir); return MMCZip::extractSubDir(&zip, "", dir);
} }

View File

@ -28,44 +28,44 @@
namespace MMCZip namespace MMCZip
{ {
/** /**
* Merge two zip files, using a filter function * Merge two zip files, using a filter function
*/ */
bool MULTIMC_LOGIC_EXPORT mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, bool MULTIMC_LOGIC_EXPORT mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
const JlCompress::FilterFunction filter = nullptr); const JlCompress::FilterFunction filter = nullptr);
/** /**
* take a source jar, add mods to it, resulting in target jar * take a source jar, add mods to it, resulting in target jar
*/ */
bool MULTIMC_LOGIC_EXPORT createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods); bool MULTIMC_LOGIC_EXPORT createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods);
/** /**
* Find a single file in archive by file name (not path) * Find a single file in archive by file name (not path)
* *
* \return the path prefix where the file is * \return the path prefix where the file is
*/ */
QString MULTIMC_LOGIC_EXPORT findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root = QString("")); QString MULTIMC_LOGIC_EXPORT findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root = QString(""));
/** /**
* Find a multiple files of the same name in archive by file name * Find a multiple files of the same name in archive by file name
* If a file is found in a path, no deeper paths are searched * If a file is found in a path, no deeper paths are searched
* *
* \return true if anything was found * \return true if anything was found
*/ */
bool MULTIMC_LOGIC_EXPORT findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString()); bool MULTIMC_LOGIC_EXPORT findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString());
/** /**
* Extract a subdirectory from an archive * Extract a subdirectory from an archive
*/ */
QStringList MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target); QStringList MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
/** /**
* Extract a whole archive. * Extract a whole archive.
* *
* \param fileCompressed The name of the archive. * \param fileCompressed The name of the archive.
* \param dir The directory to extract to, the current directory if left empty. * \param dir The directory to extract to, the current directory if left empty.
* \return The list of the full paths of the files extracted, empty on failure. * \return The list of the full paths of the files extracted, empty on failure.
*/ */
QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir); QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir);
} }

View File

@ -2,35 +2,35 @@
MessageLevel::Enum MessageLevel::getLevel(const QString& levelName) MessageLevel::Enum MessageLevel::getLevel(const QString& levelName)
{ {
if (levelName == "MultiMC") if (levelName == "MultiMC")
return MessageLevel::MultiMC; return MessageLevel::MultiMC;
else if (levelName == "Debug") else if (levelName == "Debug")
return MessageLevel::Debug; return MessageLevel::Debug;
else if (levelName == "Info") else if (levelName == "Info")
return MessageLevel::Info; return MessageLevel::Info;
else if (levelName == "Message") else if (levelName == "Message")
return MessageLevel::Message; return MessageLevel::Message;
else if (levelName == "Warning") else if (levelName == "Warning")
return MessageLevel::Warning; return MessageLevel::Warning;
else if (levelName == "Error") else if (levelName == "Error")
return MessageLevel::Error; return MessageLevel::Error;
else if (levelName == "Fatal") else if (levelName == "Fatal")
return MessageLevel::Fatal; return MessageLevel::Fatal;
// Skip PrePost, it's not exposed to !![]! // Skip PrePost, it's not exposed to !![]!
// Also skip StdErr and StdOut // Also skip StdErr and StdOut
else else
return MessageLevel::Unknown; return MessageLevel::Unknown;
} }
MessageLevel::Enum MessageLevel::fromLine(QString &line) MessageLevel::Enum MessageLevel::fromLine(QString &line)
{ {
// Level prefix // Level prefix
int endmark = line.indexOf("]!"); int endmark = line.indexOf("]!");
if (line.startsWith("!![") && endmark != -1) if (line.startsWith("!![") && endmark != -1)
{ {
auto level = MessageLevel::getLevel(line.left(endmark).mid(3)); auto level = MessageLevel::getLevel(line.left(endmark).mid(3));
line = line.mid(endmark + 2); line = line.mid(endmark + 2);
return level; return level;
} }
return MessageLevel::Unknown; return MessageLevel::Unknown;
} }

View File

@ -10,16 +10,16 @@ namespace MessageLevel
{ {
enum Enum enum Enum
{ {
Unknown, /**< No idea what this is or where it came from */ Unknown, /**< No idea what this is or where it came from */
StdOut, /**< Undetermined stderr messages */ StdOut, /**< Undetermined stderr messages */
StdErr, /**< Undetermined stdout messages */ StdErr, /**< Undetermined stdout messages */
MultiMC, /**< MultiMC Messages */ MultiMC, /**< MultiMC Messages */
Debug, /**< Debug Messages */ Debug, /**< Debug Messages */
Info, /**< Info Messages */ Info, /**< Info Messages */
Message, /**< Standard Messages */ Message, /**< Standard Messages */
Warning, /**< Warnings */ Warning, /**< Warnings */
Error, /**< Errors */ Error, /**< Errors */
Fatal, /**< Fatal Errors */ Fatal, /**< Fatal Errors */
}; };
MessageLevel::Enum getLevel(const QString &levelName); MessageLevel::Enum getLevel(const QString &levelName);

View File

@ -4,74 +4,74 @@
class NullInstance: public BaseInstance class NullInstance: public BaseInstance
{ {
public: public:
NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir)
:BaseInstance(globalSettings, settings, rootDir) :BaseInstance(globalSettings, settings, rootDir)
{ {
setVersionBroken(true); setVersionBroken(true);
} }
virtual ~NullInstance() {}; virtual ~NullInstance() {};
virtual void init() override virtual void init() override
{ {
} }
virtual void saveNow() override virtual void saveNow() override
{ {
} }
virtual QString getStatusbarDescription() override virtual QString getStatusbarDescription() override
{ {
return tr("Unknown instance type"); return tr("Unknown instance type");
}; };
virtual QSet< QString > traits() const override virtual QSet< QString > traits() const override
{ {
return {}; return {};
}; };
virtual QString instanceConfigFolder() const override virtual QString instanceConfigFolder() const override
{ {
return instanceRoot(); return instanceRoot();
}; };
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override
{ {
return nullptr; return nullptr;
} }
virtual shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override virtual shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override
{ {
return nullptr; return nullptr;
} }
virtual QProcessEnvironment createEnvironment() override virtual QProcessEnvironment createEnvironment() override
{ {
return QProcessEnvironment(); return QProcessEnvironment();
} }
virtual QMap<QString, QString> getVariables() const override virtual QMap<QString, QString> getVariables() const override
{ {
return QMap<QString, QString>(); return QMap<QString, QString>();
} }
virtual IPathMatcher::Ptr getLogFileMatcher() override virtual IPathMatcher::Ptr getLogFileMatcher() override
{ {
return nullptr; return nullptr;
} }
virtual QString getLogFileRoot() override virtual QString getLogFileRoot() override
{ {
return instanceRoot(); return instanceRoot();
} }
virtual QString typeName() const override virtual QString typeName() const override
{ {
return "Null"; return "Null";
} }
bool canExport() const override bool canExport() const override
{ {
return false; return false;
} }
bool canEdit() const override bool canEdit() const override
{ {
return false; return false;
} }
bool canLaunch() const override bool canLaunch() const override
{ {
return false; return false;
} }
QStringList verboseDescription(AuthSessionPtr session) override QStringList verboseDescription(AuthSessionPtr session) override
{ {
QStringList out; QStringList out;
out << "Null instance - placeholder."; out << "Null instance - placeholder.";
return out; return out;
} }
}; };

View File

@ -4,46 +4,46 @@
enum class ProblemSeverity enum class ProblemSeverity
{ {
None, None,
Warning, Warning,
Error Error
}; };
struct PatchProblem struct PatchProblem
{ {
ProblemSeverity m_severity; ProblemSeverity m_severity;
QString m_description; QString m_description;
}; };
class MULTIMC_LOGIC_EXPORT ProblemProvider class MULTIMC_LOGIC_EXPORT ProblemProvider
{ {
public: public:
virtual ~ProblemProvider() {}; virtual ~ProblemProvider() {};
virtual const QList<PatchProblem> getProblems() const = 0; virtual const QList<PatchProblem> getProblems() const = 0;
virtual ProblemSeverity getProblemSeverity() const = 0; virtual ProblemSeverity getProblemSeverity() const = 0;
}; };
class MULTIMC_LOGIC_EXPORT ProblemContainer : public ProblemProvider class MULTIMC_LOGIC_EXPORT ProblemContainer : public ProblemProvider
{ {
public: public:
const QList<PatchProblem> getProblems() const override const QList<PatchProblem> getProblems() const override
{ {
return m_problems; return m_problems;
} }
ProblemSeverity getProblemSeverity() const override ProblemSeverity getProblemSeverity() const override
{ {
return m_problemSeverity; return m_problemSeverity;
} }
virtual void addProblem(ProblemSeverity severity, const QString &description) virtual void addProblem(ProblemSeverity severity, const QString &description)
{ {
if(severity > m_problemSeverity) if(severity > m_problemSeverity)
{ {
m_problemSeverity = severity; m_problemSeverity = severity;
} }
m_problems.append({severity, description}); m_problems.append({severity, description});
} }
private: private:
QList<PatchProblem> m_problems; QList<PatchProblem> m_problems;
ProblemSeverity m_problemSeverity = ProblemSeverity::None; ProblemSeverity m_problemSeverity = ProblemSeverity::None;
}; };

View File

@ -8,10 +8,10 @@ namespace details
{ {
struct DeleteQObjectLater struct DeleteQObjectLater
{ {
void operator()(QObject *obj) const void operator()(QObject *obj) const
{ {
obj->deleteLater(); obj->deleteLater();
} }
}; };
} }
/** /**
@ -28,56 +28,56 @@ template <typename T>
class shared_qobject_ptr class shared_qobject_ptr
{ {
public: public:
shared_qobject_ptr(){} shared_qobject_ptr(){}
shared_qobject_ptr(T * wrap) shared_qobject_ptr(T * wrap)
{ {
reset(wrap); reset(wrap);
} }
shared_qobject_ptr(const shared_qobject_ptr<T>& other) shared_qobject_ptr(const shared_qobject_ptr<T>& other)
{ {
m_ptr = other.m_ptr; m_ptr = other.m_ptr;
} }
template<typename Derived> template<typename Derived>
shared_qobject_ptr(const shared_qobject_ptr<Derived> &other) shared_qobject_ptr(const shared_qobject_ptr<Derived> &other)
{ {
m_ptr = other.unwrap(); m_ptr = other.unwrap();
} }
public: public:
void reset(T * wrap) void reset(T * wrap)
{ {
using namespace std::placeholders; using namespace std::placeholders;
m_ptr.reset(wrap, std::bind(&QObject::deleteLater, _1)); m_ptr.reset(wrap, std::bind(&QObject::deleteLater, _1));
} }
void reset(const shared_qobject_ptr<T> &other) void reset(const shared_qobject_ptr<T> &other)
{ {
m_ptr = other.m_ptr; m_ptr = other.m_ptr;
} }
void reset() void reset()
{ {
m_ptr.reset(); m_ptr.reset();
} }
T * get() const T * get() const
{ {
return m_ptr.get(); return m_ptr.get();
} }
T * operator->() const T * operator->() const
{ {
return m_ptr.get(); return m_ptr.get();
} }
T & operator*() const T & operator*() const
{ {
return *m_ptr.get(); return *m_ptr.get();
} }
operator bool() const operator bool() const
{ {
return m_ptr.get() != nullptr; return m_ptr.get() != nullptr;
} }
const std::shared_ptr <T> unwrap() const const std::shared_ptr <T> unwrap() const
{ {
return m_ptr; return m_ptr;
} }
private: private:
std::shared_ptr <T> m_ptr; std::shared_ptr <T> m_ptr;
}; };

View File

@ -5,59 +5,59 @@ template <typename K, typename V>
class RWStorage class RWStorage
{ {
public: public:
void add(K key, V value) void add(K key, V value)
{ {
QWriteLocker l(&lock); QWriteLocker l(&lock);
cache[key] = value; cache[key] = value;
stale_entries.remove(key); stale_entries.remove(key);
} }
V get(K key) V get(K key)
{ {
QReadLocker l(&lock); QReadLocker l(&lock);
if(cache.contains(key)) if(cache.contains(key))
{ {
return cache[key]; return cache[key];
} }
else return V(); else return V();
} }
bool get(K key, V& value) bool get(K key, V& value)
{ {
QReadLocker l(&lock); QReadLocker l(&lock);
if(cache.contains(key)) if(cache.contains(key))
{ {
value = cache[key]; value = cache[key];
return true; return true;
} }
else return false; else return false;
} }
bool has(K key) bool has(K key)
{ {
QReadLocker l(&lock); QReadLocker l(&lock);
return cache.contains(key); return cache.contains(key);
} }
bool stale(K key) bool stale(K key)
{ {
QReadLocker l(&lock); QReadLocker l(&lock);
if(!cache.contains(key)) if(!cache.contains(key))
return true; return true;
return stale_entries.contains(key); return stale_entries.contains(key);
} }
void setStale(K key) void setStale(K key)
{ {
QWriteLocker l(&lock); QWriteLocker l(&lock);
if(cache.contains(key)) if(cache.contains(key))
{ {
stale_entries.insert(key); stale_entries.insert(key);
} }
} }
void clear() void clear()
{ {
QWriteLocker l(&lock); QWriteLocker l(&lock);
cache.clear(); cache.clear();
stale_entries.clear(); stale_entries.clear();
} }
private: private:
QReadWriteLock lock; QReadWriteLock lock;
QMap<K, V> cache; QMap<K, V> cache;
QSet<K> stale_entries; QSet<K> stale_entries;
}; };

View File

@ -4,108 +4,108 @@
#include <QDebug> #include <QDebug>
RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject *parent) RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject *parent)
: QObject(parent), m_watcher(new QFileSystemWatcher(this)) : QObject(parent), m_watcher(new QFileSystemWatcher(this))
{ {
connect(m_watcher, &QFileSystemWatcher::fileChanged, this, connect(m_watcher, &QFileSystemWatcher::fileChanged, this,
&RecursiveFileSystemWatcher::fileChange); &RecursiveFileSystemWatcher::fileChange);
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, connect(m_watcher, &QFileSystemWatcher::directoryChanged, this,
&RecursiveFileSystemWatcher::directoryChange); &RecursiveFileSystemWatcher::directoryChange);
} }
void RecursiveFileSystemWatcher::setRootDir(const QDir &root) void RecursiveFileSystemWatcher::setRootDir(const QDir &root)
{ {
bool wasEnabled = m_isEnabled; bool wasEnabled = m_isEnabled;
disable(); disable();
m_root = root; m_root = root;
setFiles(scanRecursive(m_root)); setFiles(scanRecursive(m_root));
if (wasEnabled) if (wasEnabled)
{ {
enable(); enable();
} }
} }
void RecursiveFileSystemWatcher::setWatchFiles(const bool watchFiles) void RecursiveFileSystemWatcher::setWatchFiles(const bool watchFiles)
{ {
bool wasEnabled = m_isEnabled; bool wasEnabled = m_isEnabled;
disable(); disable();
m_watchFiles = watchFiles; m_watchFiles = watchFiles;
if (wasEnabled) if (wasEnabled)
{ {
enable(); enable();
} }
} }
void RecursiveFileSystemWatcher::enable() void RecursiveFileSystemWatcher::enable()
{ {
if (m_isEnabled) if (m_isEnabled)
{ {
return; return;
} }
Q_ASSERT(m_root != QDir::root()); Q_ASSERT(m_root != QDir::root());
addFilesToWatcherRecursive(m_root); addFilesToWatcherRecursive(m_root);
m_isEnabled = true; m_isEnabled = true;
} }
void RecursiveFileSystemWatcher::disable() void RecursiveFileSystemWatcher::disable()
{ {
if (!m_isEnabled) if (!m_isEnabled)
{ {
return; return;
} }
m_isEnabled = false; m_isEnabled = false;
m_watcher->removePaths(m_watcher->files()); m_watcher->removePaths(m_watcher->files());
m_watcher->removePaths(m_watcher->directories()); m_watcher->removePaths(m_watcher->directories());
} }
void RecursiveFileSystemWatcher::setFiles(const QStringList &files) void RecursiveFileSystemWatcher::setFiles(const QStringList &files)
{ {
if (files != m_files) if (files != m_files)
{ {
m_files = files; m_files = files;
emit filesChanged(); emit filesChanged();
} }
} }
void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir &dir) void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir &dir)
{ {
m_watcher->addPath(dir.absolutePath()); m_watcher->addPath(dir.absolutePath());
for (const QString &directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) for (const QString &directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
{ {
addFilesToWatcherRecursive(dir.absoluteFilePath(directory)); addFilesToWatcherRecursive(dir.absoluteFilePath(directory));
} }
if (m_watchFiles) if (m_watchFiles)
{ {
for (const QFileInfo &info : dir.entryInfoList(QDir::Files)) for (const QFileInfo &info : dir.entryInfoList(QDir::Files))
{ {
m_watcher->addPath(info.absoluteFilePath()); m_watcher->addPath(info.absoluteFilePath());
} }
} }
} }
QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir &directory) QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir &directory)
{ {
QStringList ret; QStringList ret;
if(!m_matcher) if(!m_matcher)
{ {
return {}; return {};
} }
for (const QString &dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden)) for (const QString &dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden))
{ {
ret.append(scanRecursive(directory.absoluteFilePath(dir))); ret.append(scanRecursive(directory.absoluteFilePath(dir)));
} }
for (const QString &file : directory.entryList(QDir::Files | QDir::Hidden)) for (const QString &file : directory.entryList(QDir::Files | QDir::Hidden))
{ {
auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file)); auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file));
if (m_matcher->matches(relPath)) if (m_matcher->matches(relPath))
{ {
ret.append(relPath); ret.append(relPath);
} }
} }
return ret; return ret;
} }
void RecursiveFileSystemWatcher::fileChange(const QString &path) void RecursiveFileSystemWatcher::fileChange(const QString &path)
{ {
emit fileChanged(path); emit fileChanged(path);
} }
void RecursiveFileSystemWatcher::directoryChange(const QString &path) void RecursiveFileSystemWatcher::directoryChange(const QString &path)
{ {
setFiles(scanRecursive(m_root)); setFiles(scanRecursive(m_root));
} }

View File

@ -8,56 +8,56 @@
class MULTIMC_LOGIC_EXPORT RecursiveFileSystemWatcher : public QObject class MULTIMC_LOGIC_EXPORT RecursiveFileSystemWatcher : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
RecursiveFileSystemWatcher(QObject *parent); RecursiveFileSystemWatcher(QObject *parent);
void setRootDir(const QDir &root); void setRootDir(const QDir &root);
QDir rootDir() const QDir rootDir() const
{ {
return m_root; return m_root;
} }
// WARNING: setting this to true may be bad for performance // WARNING: setting this to true may be bad for performance
void setWatchFiles(const bool watchFiles); void setWatchFiles(const bool watchFiles);
bool watchFiles() const bool watchFiles() const
{ {
return m_watchFiles; return m_watchFiles;
} }
void setMatcher(IPathMatcher::Ptr matcher) void setMatcher(IPathMatcher::Ptr matcher)
{ {
m_matcher = matcher; m_matcher = matcher;
} }
QStringList files() const QStringList files() const
{ {
return m_files; return m_files;
} }
signals: signals:
void filesChanged(); void filesChanged();
void fileChanged(const QString &path); void fileChanged(const QString &path);
public slots: public slots:
void enable(); void enable();
void disable(); void disable();
private: private:
QDir m_root; QDir m_root;
bool m_watchFiles = false; bool m_watchFiles = false;
bool m_isEnabled = false; bool m_isEnabled = false;
IPathMatcher::Ptr m_matcher; IPathMatcher::Ptr m_matcher;
QFileSystemWatcher *m_watcher; QFileSystemWatcher *m_watcher;
QStringList m_files; QStringList m_files;
void setFiles(const QStringList &files); void setFiles(const QStringList &files);
void addFilesToWatcherRecursive(const QDir &dir); void addFilesToWatcherRecursive(const QDir &dir);
QStringList scanRecursive(const QDir &dir); QStringList scanRecursive(const QDir &dir);
private slots: private slots:
void fileChange(const QString &path); void fileChange(const QString &path);
void directoryChange(const QString &path); void directoryChange(const QString &path);
}; };

View File

@ -7,292 +7,292 @@ template <char Tseparator>
class SeparatorPrefixTree class SeparatorPrefixTree
{ {
public: public:
SeparatorPrefixTree(QStringList paths) SeparatorPrefixTree(QStringList paths)
{ {
insert(paths); insert(paths);
} }
SeparatorPrefixTree(bool contained = false) SeparatorPrefixTree(bool contained = false)
{ {
m_contained = contained; m_contained = contained;
} }
void insert(QStringList paths) void insert(QStringList paths)
{ {
for(auto &path: paths) for(auto &path: paths)
{ {
insert(path); insert(path);
} }
} }
/// insert an exact path into the tree /// insert an exact path into the tree
SeparatorPrefixTree & insert(QString path) SeparatorPrefixTree & insert(QString path)
{ {
auto sepIndex = path.indexOf(Tseparator); auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1) if(sepIndex == -1)
{ {
children[path] = SeparatorPrefixTree(true); children[path] = SeparatorPrefixTree(true);
return children[path]; return children[path];
} }
else else
{ {
auto prefix = path.left(sepIndex); auto prefix = path.left(sepIndex);
if(!children.contains(prefix)) if(!children.contains(prefix))
{ {
children[prefix] = SeparatorPrefixTree(false); children[prefix] = SeparatorPrefixTree(false);
} }
return children[prefix].insert(path.mid(sepIndex + 1)); return children[prefix].insert(path.mid(sepIndex + 1));
} }
} }
/// is the path fully contained in the tree? /// is the path fully contained in the tree?
bool contains(QString path) const bool contains(QString path) const
{ {
auto node = find(path); auto node = find(path);
return node != nullptr; return node != nullptr;
} }
/// does the tree cover a path? That means the prefix of the path is contained in the tree /// does the tree cover a path? That means the prefix of the path is contained in the tree
bool covers(QString path) const bool covers(QString path) const
{ {
// if we found some valid node, it's good enough. the tree covers the path // if we found some valid node, it's good enough. the tree covers the path
if(m_contained) if(m_contained)
{ {
return true; return true;
} }
auto sepIndex = path.indexOf(Tseparator); auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1) if(sepIndex == -1)
{ {
auto found = children.find(path); auto found = children.find(path);
if(found == children.end()) if(found == children.end())
{ {
return false; return false;
} }
return (*found).covers(QString()); return (*found).covers(QString());
} }
else else
{ {
auto prefix = path.left(sepIndex); auto prefix = path.left(sepIndex);
auto found = children.find(prefix); auto found = children.find(prefix);
if(found == children.end()) if(found == children.end())
{ {
return false; return false;
} }
return (*found).covers(path.mid(sepIndex + 1)); return (*found).covers(path.mid(sepIndex + 1));
} }
} }
/// return the contained path that covers the path specified /// return the contained path that covers the path specified
QString cover(QString path) const QString cover(QString path) const
{ {
// if we found some valid node, it's good enough. the tree covers the path // if we found some valid node, it's good enough. the tree covers the path
if(m_contained) if(m_contained)
{ {
return QString(""); return QString("");
} }
auto sepIndex = path.indexOf(Tseparator); auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1) if(sepIndex == -1)
{ {
auto found = children.find(path); auto found = children.find(path);
if(found == children.end()) if(found == children.end())
{ {
return QString(); return QString();
} }
auto nested = (*found).cover(QString()); auto nested = (*found).cover(QString());
if(nested.isNull()) if(nested.isNull())
{ {
return nested; return nested;
} }
if(nested.isEmpty()) if(nested.isEmpty())
return path; return path;
return path + Tseparator + nested; return path + Tseparator + nested;
} }
else else
{ {
auto prefix = path.left(sepIndex); auto prefix = path.left(sepIndex);
auto found = children.find(prefix); auto found = children.find(prefix);
if(found == children.end()) if(found == children.end())
{ {
return QString(); return QString();
} }
auto nested = (*found).cover(path.mid(sepIndex + 1)); auto nested = (*found).cover(path.mid(sepIndex + 1));
if(nested.isNull()) if(nested.isNull())
{ {
return nested; return nested;
} }
if(nested.isEmpty()) if(nested.isEmpty())
return prefix; return prefix;
return prefix + Tseparator + nested; return prefix + Tseparator + nested;
} }
} }
/// Does the path-specified node exist in the tree? It does not have to be contained. /// Does the path-specified node exist in the tree? It does not have to be contained.
bool exists(QString path) const bool exists(QString path) const
{ {
auto sepIndex = path.indexOf(Tseparator); auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1) if(sepIndex == -1)
{ {
auto found = children.find(path); auto found = children.find(path);
if(found == children.end()) if(found == children.end())
{ {
return false; return false;
} }
return true; return true;
} }
else else
{ {
auto prefix = path.left(sepIndex); auto prefix = path.left(sepIndex);
auto found = children.find(prefix); auto found = children.find(prefix);
if(found == children.end()) if(found == children.end())
{ {
return false; return false;
} }
return (*found).exists(path.mid(sepIndex + 1)); return (*found).exists(path.mid(sepIndex + 1));
} }
} }
/// find a node in the tree by name /// find a node in the tree by name
const SeparatorPrefixTree * find(QString path) const const SeparatorPrefixTree * find(QString path) const
{ {
auto sepIndex = path.indexOf(Tseparator); auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1) if(sepIndex == -1)
{ {
auto found = children.find(path); auto found = children.find(path);
if(found == children.end()) if(found == children.end())
{ {
return nullptr; return nullptr;
} }
return &(*found); return &(*found);
} }
else else
{ {
auto prefix = path.left(sepIndex); auto prefix = path.left(sepIndex);
auto found = children.find(prefix); auto found = children.find(prefix);
if(found == children.end()) if(found == children.end())
{ {
return nullptr; return nullptr;
} }
return (*found).find(path.mid(sepIndex + 1)); return (*found).find(path.mid(sepIndex + 1));
} }
} }
/// is this a leaf node? /// is this a leaf node?
bool leaf() const bool leaf() const
{ {
return children.isEmpty(); return children.isEmpty();
} }
/// is this node actually contained in the tree, or is it purely structural? /// is this node actually contained in the tree, or is it purely structural?
bool contained() const bool contained() const
{ {
return m_contained; return m_contained;
} }
/// Remove a path from the tree /// Remove a path from the tree
bool remove(QString path) bool remove(QString path)
{ {
return removeInternal(path) != Failed; return removeInternal(path) != Failed;
} }
/// Clear all children of this node tree node /// Clear all children of this node tree node
void clear() void clear()
{ {
children.clear(); children.clear();
} }
QStringList toStringList() const QStringList toStringList() const
{ {
QStringList collected; QStringList collected;
// collecting these is more expensive. // collecting these is more expensive.
auto iter = children.begin(); auto iter = children.begin();
while(iter != children.end()) while(iter != children.end())
{ {
QStringList list = iter.value().toStringList(); QStringList list = iter.value().toStringList();
for(int i = 0; i < list.size(); i++) for(int i = 0; i < list.size(); i++)
{ {
list[i] = iter.key() + Tseparator + list[i]; list[i] = iter.key() + Tseparator + list[i];
} }
collected.append(list); collected.append(list);
if((*iter).m_contained) if((*iter).m_contained)
{ {
collected.append(iter.key()); collected.append(iter.key());
} }
iter++; iter++;
} }
return collected; return collected;
} }
private: private:
enum Removal enum Removal
{ {
Failed, Failed,
Succeeded, Succeeded,
HasChildren HasChildren
}; };
Removal removeInternal(QString path = QString()) Removal removeInternal(QString path = QString())
{ {
if(path.isEmpty()) if(path.isEmpty())
{ {
if(!m_contained) if(!m_contained)
{ {
// remove all children - we are removing a prefix // remove all children - we are removing a prefix
clear(); clear();
return Succeeded; return Succeeded;
} }
m_contained = false; m_contained = false;
if(children.size()) if(children.size())
{ {
return HasChildren; return HasChildren;
} }
return Succeeded; return Succeeded;
} }
Removal remStatus = Failed; Removal remStatus = Failed;
QString childToRemove; QString childToRemove;
auto sepIndex = path.indexOf(Tseparator); auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1) if(sepIndex == -1)
{ {
childToRemove = path; childToRemove = path;
auto found = children.find(childToRemove); auto found = children.find(childToRemove);
if(found == children.end()) if(found == children.end())
{ {
return Failed; return Failed;
} }
remStatus = (*found).removeInternal(); remStatus = (*found).removeInternal();
} }
else else
{ {
childToRemove = path.left(sepIndex); childToRemove = path.left(sepIndex);
auto found = children.find(childToRemove); auto found = children.find(childToRemove);
if(found == children.end()) if(found == children.end())
{ {
return Failed; return Failed;
} }
remStatus = (*found).removeInternal(path.mid(sepIndex + 1)); remStatus = (*found).removeInternal(path.mid(sepIndex + 1));
} }
switch (remStatus) switch (remStatus)
{ {
case Failed: case Failed:
case HasChildren: case HasChildren:
{ {
return remStatus; return remStatus;
} }
case Succeeded: case Succeeded:
{ {
children.remove(childToRemove); children.remove(childToRemove);
if(m_contained) if(m_contained)
{ {
return HasChildren; return HasChildren;
} }
if(children.size()) if(children.size())
{ {
return HasChildren; return HasChildren;
} }
return Succeeded; return Succeeded;
} }
} }
return Failed; return Failed;
} }
private: private:
QMap<QString,SeparatorPrefixTree<Tseparator>> children; QMap<QString,SeparatorPrefixTree<Tseparator>> children;
bool m_contained = false; bool m_contained = false;
}; };

View File

@ -12,27 +12,27 @@ class Usable;
*/ */
class Usable class Usable
{ {
friend class UseLock; friend class UseLock;
public: public:
std::size_t useCount() std::size_t useCount()
{ {
return m_useCount; return m_useCount;
} }
bool isInUse() bool isInUse()
{ {
return m_useCount > 0; return m_useCount > 0;
} }
protected: protected:
virtual void decrementUses() virtual void decrementUses()
{ {
m_useCount--; m_useCount--;
} }
virtual void incrementUses() virtual void incrementUses()
{ {
m_useCount++; m_useCount++;
} }
private: private:
std::size_t m_useCount = 0; std::size_t m_useCount = 0;
}; };
/** /**
@ -43,16 +43,16 @@ private:
class UseLock class UseLock
{ {
public: public:
UseLock(std::shared_ptr<Usable> usable) UseLock(std::shared_ptr<Usable> usable)
: m_usable(usable) : m_usable(usable)
{ {
// this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate. // this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate.
m_usable->incrementUses(); m_usable->incrementUses();
} }
~UseLock() ~UseLock()
{ {
m_usable->decrementUses(); m_usable->decrementUses();
} }
private: private:
std::shared_ptr<Usable> m_usable; std::shared_ptr<Usable> m_usable;
}; };

View File

@ -7,79 +7,79 @@
Version::Version(const QString &str) : m_string(str) Version::Version(const QString &str) : m_string(str)
{ {
parse(); parse();
} }
bool Version::operator<(const Version &other) const bool Version::operator<(const Version &other) const
{ {
const int size = qMax(m_sections.size(), other.m_sections.size()); const int size = qMax(m_sections.size(), other.m_sections.size());
for (int i = 0; i < size; ++i) for (int i = 0; i < size; ++i)
{ {
const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i); const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
const Section sec2 = const Section sec2 =
(i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i); (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
if (sec1 != sec2) if (sec1 != sec2)
{ {
return sec1 < sec2; return sec1 < sec2;
} }
} }
return false; return false;
} }
bool Version::operator<=(const Version &other) const bool Version::operator<=(const Version &other) const
{ {
return *this < other || *this == other; return *this < other || *this == other;
} }
bool Version::operator>(const Version &other) const bool Version::operator>(const Version &other) const
{ {
const int size = qMax(m_sections.size(), other.m_sections.size()); const int size = qMax(m_sections.size(), other.m_sections.size());
for (int i = 0; i < size; ++i) for (int i = 0; i < size; ++i)
{ {
const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i); const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
const Section sec2 = const Section sec2 =
(i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i); (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
if (sec1 != sec2) if (sec1 != sec2)
{ {
return sec1 > sec2; return sec1 > sec2;
} }
} }
return false; return false;
} }
bool Version::operator>=(const Version &other) const bool Version::operator>=(const Version &other) const
{ {
return *this > other || *this == other; return *this > other || *this == other;
} }
bool Version::operator==(const Version &other) const bool Version::operator==(const Version &other) const
{ {
const int size = qMax(m_sections.size(), other.m_sections.size()); const int size = qMax(m_sections.size(), other.m_sections.size());
for (int i = 0; i < size; ++i) for (int i = 0; i < size; ++i)
{ {
const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i); const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
const Section sec2 = const Section sec2 =
(i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i); (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
if (sec1 != sec2) if (sec1 != sec2)
{ {
return false; return false;
} }
} }
return true; return true;
} }
bool Version::operator!=(const Version &other) const bool Version::operator!=(const Version &other) const
{ {
return !operator==(other); return !operator==(other);
} }
void Version::parse() void Version::parse()
{ {
m_sections.clear(); m_sections.clear();
// FIXME: this is bad. versions can contain a lot more separators... // FIXME: this is bad. versions can contain a lot more separators...
QStringList parts = m_string.split('.'); QStringList parts = m_string.split('.');
for (const auto part : parts) for (const auto part : parts)
{ {
m_sections.append(Section(part)); m_sections.append(Section(part));
} }
} }

View File

@ -10,98 +10,98 @@ class QUrl;
class MULTIMC_LOGIC_EXPORT Version class MULTIMC_LOGIC_EXPORT Version
{ {
public: public:
Version(const QString &str); Version(const QString &str);
Version() {} Version() {}
bool operator<(const Version &other) const; bool operator<(const Version &other) const;
bool operator<=(const Version &other) const; bool operator<=(const Version &other) const;
bool operator>(const Version &other) const; bool operator>(const Version &other) const;
bool operator>=(const Version &other) const; bool operator>=(const Version &other) const;
bool operator==(const Version &other) const; bool operator==(const Version &other) const;
bool operator!=(const Version &other) const; bool operator!=(const Version &other) const;
QString toString() const QString toString() const
{ {
return m_string; return m_string;
} }
private: private:
QString m_string; QString m_string;
struct Section struct Section
{ {
explicit Section(const QString &fullString) explicit Section(const QString &fullString)
{ {
m_fullString = fullString; m_fullString = fullString;
int cutoff = m_fullString.size(); int cutoff = m_fullString.size();
for(int i = 0; i < m_fullString.size(); i++) for(int i = 0; i < m_fullString.size(); i++)
{ {
if(!m_fullString[i].isDigit()) if(!m_fullString[i].isDigit())
{ {
cutoff = i; cutoff = i;
break; break;
} }
} }
auto numPart = m_fullString.leftRef(cutoff); auto numPart = m_fullString.leftRef(cutoff);
if(numPart.size()) if(numPart.size())
{ {
numValid = true; numValid = true;
m_numPart = numPart.toInt(); m_numPart = numPart.toInt();
} }
auto stringPart = m_fullString.midRef(cutoff); auto stringPart = m_fullString.midRef(cutoff);
if(stringPart.size()) if(stringPart.size())
{ {
m_stringPart = stringPart.toString(); m_stringPart = stringPart.toString();
} }
} }
explicit Section() {} explicit Section() {}
bool numValid = false; bool numValid = false;
int m_numPart = 0; int m_numPart = 0;
QString m_stringPart; QString m_stringPart;
QString m_fullString; QString m_fullString;
inline bool operator!=(const Section &other) const inline bool operator!=(const Section &other) const
{ {
if(numValid && other.numValid) if(numValid && other.numValid)
{ {
return m_numPart != other.m_numPart || m_stringPart != other.m_stringPart; return m_numPart != other.m_numPart || m_stringPart != other.m_stringPart;
} }
else else
{ {
return m_fullString != other.m_fullString; return m_fullString != other.m_fullString;
} }
} }
inline bool operator<(const Section &other) const inline bool operator<(const Section &other) const
{ {
if(numValid && other.numValid) if(numValid && other.numValid)
{ {
if(m_numPart < other.m_numPart) if(m_numPart < other.m_numPart)
return true; return true;
if(m_numPart == other.m_numPart && m_stringPart < other.m_stringPart) if(m_numPart == other.m_numPart && m_stringPart < other.m_stringPart)
return true; return true;
return false; return false;
} }
else else
{ {
return m_fullString < other.m_fullString; return m_fullString < other.m_fullString;
} }
} }
inline bool operator>(const Section &other) const inline bool operator>(const Section &other) const
{ {
if(numValid && other.numValid) if(numValid && other.numValid)
{ {
if(m_numPart > other.m_numPart) if(m_numPart > other.m_numPart)
return true; return true;
if(m_numPart == other.m_numPart && m_stringPart > other.m_stringPart) if(m_numPart == other.m_numPart && m_stringPart > other.m_stringPart)
return true; return true;
return false; return false;
} }
else else
{ {
return m_fullString > other.m_fullString; return m_fullString > other.m_fullString;
} }
} }
}; };
QList<Section> m_sections; QList<Section> m_sections;
void parse(); void parse();
}; };

View File

@ -20,64 +20,64 @@
class ModUtilsTest : public QObject class ModUtilsTest : public QObject
{ {
Q_OBJECT Q_OBJECT
void setupVersions() void setupVersions()
{ {
QTest::addColumn<QString>("first"); QTest::addColumn<QString>("first");
QTest::addColumn<QString>("second"); QTest::addColumn<QString>("second");
QTest::addColumn<bool>("lessThan"); QTest::addColumn<bool>("lessThan");
QTest::addColumn<bool>("equal"); QTest::addColumn<bool>("equal");
QTest::newRow("equal, explicit") << "1.2.0" << "1.2.0" << false << true; QTest::newRow("equal, explicit") << "1.2.0" << "1.2.0" << false << true;
QTest::newRow("equal, implicit 1") << "1.2" << "1.2.0" << false << true; QTest::newRow("equal, implicit 1") << "1.2" << "1.2.0" << false << true;
QTest::newRow("equal, implicit 2") << "1.2.0" << "1.2" << false << true; QTest::newRow("equal, implicit 2") << "1.2.0" << "1.2" << false << true;
QTest::newRow("equal, two-digit") << "1.42" << "1.42" << false << true; QTest::newRow("equal, two-digit") << "1.42" << "1.42" << false << true;
QTest::newRow("lessThan, explicit 1") << "1.2.0" << "1.2.1" << true << false; QTest::newRow("lessThan, explicit 1") << "1.2.0" << "1.2.1" << true << false;
QTest::newRow("lessThan, explicit 2") << "1.2.0" << "1.3.0" << true << false; QTest::newRow("lessThan, explicit 2") << "1.2.0" << "1.3.0" << true << false;
QTest::newRow("lessThan, explicit 3") << "1.2.0" << "2.2.0" << true << false; QTest::newRow("lessThan, explicit 3") << "1.2.0" << "2.2.0" << true << false;
QTest::newRow("lessThan, implicit 1") << "1.2" << "1.2.1" << true << false; QTest::newRow("lessThan, implicit 1") << "1.2" << "1.2.1" << true << false;
QTest::newRow("lessThan, implicit 2") << "1.2" << "1.3.0" << true << false; QTest::newRow("lessThan, implicit 2") << "1.2" << "1.3.0" << true << false;
QTest::newRow("lessThan, implicit 3") << "1.2" << "2.2.0" << true << false; QTest::newRow("lessThan, implicit 3") << "1.2" << "2.2.0" << true << false;
QTest::newRow("lessThan, two-digit") << "1.41" << "1.42" << true << false; QTest::newRow("lessThan, two-digit") << "1.41" << "1.42" << true << false;
QTest::newRow("greaterThan, explicit 1") << "1.2.1" << "1.2.0" << false << false; QTest::newRow("greaterThan, explicit 1") << "1.2.1" << "1.2.0" << false << false;
QTest::newRow("greaterThan, explicit 2") << "1.3.0" << "1.2.0" << false << false; QTest::newRow("greaterThan, explicit 2") << "1.3.0" << "1.2.0" << false << false;
QTest::newRow("greaterThan, explicit 3") << "2.2.0" << "1.2.0" << false << false; QTest::newRow("greaterThan, explicit 3") << "2.2.0" << "1.2.0" << false << false;
QTest::newRow("greaterThan, implicit 1") << "1.2.1" << "1.2" << false << false; QTest::newRow("greaterThan, implicit 1") << "1.2.1" << "1.2" << false << false;
QTest::newRow("greaterThan, implicit 2") << "1.3.0" << "1.2" << false << false; QTest::newRow("greaterThan, implicit 2") << "1.3.0" << "1.2" << false << false;
QTest::newRow("greaterThan, implicit 3") << "2.2.0" << "1.2" << false << false; QTest::newRow("greaterThan, implicit 3") << "2.2.0" << "1.2" << false << false;
QTest::newRow("greaterThan, two-digit") << "1.42" << "1.41" << false << false; QTest::newRow("greaterThan, two-digit") << "1.42" << "1.41" << false << false;
} }
private slots: private slots:
void initTestCase() void initTestCase()
{ {
} }
void cleanupTestCase() void cleanupTestCase()
{ {
} }
void test_versionCompare_data() void test_versionCompare_data()
{ {
setupVersions(); setupVersions();
} }
void test_versionCompare() void test_versionCompare()
{ {
QFETCH(QString, first); QFETCH(QString, first);
QFETCH(QString, second); QFETCH(QString, second);
QFETCH(bool, lessThan); QFETCH(bool, lessThan);
QFETCH(bool, equal); QFETCH(bool, equal);
const auto v1 = Version(first); const auto v1 = Version(first);
const auto v2 = Version(second); const auto v2 = Version(second);
QCOMPARE(v1 < v2, lessThan); QCOMPARE(v1 < v2, lessThan);
QCOMPARE(v1 > v2, !lessThan && !equal); QCOMPARE(v1 > v2, !lessThan && !equal);
QCOMPARE(v1 == v2, equal); QCOMPARE(v1 == v2, equal);
} }
}; };
QTEST_GUILESS_MAIN(ModUtilsTest) QTEST_GUILESS_MAIN(ModUtilsTest)

View File

@ -6,21 +6,21 @@
enum IconType : unsigned enum IconType : unsigned
{ {
Builtin, Builtin,
Transient, Transient,
FileBased, FileBased,
ICONS_TOTAL, ICONS_TOTAL,
ToBeDeleted ToBeDeleted
}; };
class MULTIMC_LOGIC_EXPORT IIconList class MULTIMC_LOGIC_EXPORT IIconList
{ {
public: public:
virtual ~IIconList(); virtual ~IIconList();
virtual bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) = 0; virtual bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) = 0;
virtual bool deleteIcon(const QString &key) = 0; virtual bool deleteIcon(const QString &key) = 0;
virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0; virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0;
virtual bool iconFileExists(const QString &key) const = 0; virtual bool iconFileExists(const QString &key) const = 0;
virtual void installIcons(const QStringList &iconFiles) = 0; virtual void installIcons(const QStringList &iconFiles) = 0;
virtual void installIcon(const QString &file, const QString &name) = 0; virtual void installIcon(const QString &file, const QString &name) = 0;
}; };

View File

@ -16,149 +16,149 @@ JavaChecker::JavaChecker(QObject *parent) : QObject(parent)
void JavaChecker::performCheck() void JavaChecker::performCheck()
{ {
QString checkerJar = FS::PathCombine(ENV.getJarsPath(), "JavaCheck.jar"); QString checkerJar = FS::PathCombine(ENV.getJarsPath(), "JavaCheck.jar");
QStringList args; QStringList args;
process.reset(new QProcess()); process.reset(new QProcess());
if(m_args.size()) if(m_args.size())
{ {
auto extraArgs = Commandline::splitArgs(m_args); auto extraArgs = Commandline::splitArgs(m_args);
args.append(extraArgs); args.append(extraArgs);
} }
if(m_minMem != 0) if(m_minMem != 0)
{ {
args << QString("-Xms%1m").arg(m_minMem); args << QString("-Xms%1m").arg(m_minMem);
} }
if(m_maxMem != 0) if(m_maxMem != 0)
{ {
args << QString("-Xmx%1m").arg(m_maxMem); args << QString("-Xmx%1m").arg(m_maxMem);
} }
if(m_permGen != 64) if(m_permGen != 64)
{ {
args << QString("-XX:PermSize=%1m").arg(m_permGen); args << QString("-XX:PermSize=%1m").arg(m_permGen);
} }
args.append({"-jar", checkerJar}); args.append({"-jar", checkerJar});
process->setArguments(args); process->setArguments(args);
process->setProgram(m_path); process->setProgram(m_path);
process->setProcessChannelMode(QProcess::SeparateChannels); process->setProcessChannelMode(QProcess::SeparateChannels);
process->setProcessEnvironment(CleanEnviroment()); process->setProcessEnvironment(CleanEnviroment());
qDebug() << "Running java checker: " + m_path + args.join(" ");; qDebug() << "Running java checker: " + m_path + args.join(" ");;
connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus))); connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus)));
connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError))); connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError)));
connect(process.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(stdoutReady())); connect(process.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(stdoutReady()));
connect(process.get(), SIGNAL(readyReadStandardError()), this, SLOT(stderrReady())); connect(process.get(), SIGNAL(readyReadStandardError()), this, SLOT(stderrReady()));
connect(&killTimer, SIGNAL(timeout()), SLOT(timeout())); connect(&killTimer, SIGNAL(timeout()), SLOT(timeout()));
killTimer.setSingleShot(true); killTimer.setSingleShot(true);
killTimer.start(15000); killTimer.start(15000);
process->start(); process->start();
} }
void JavaChecker::stdoutReady() void JavaChecker::stdoutReady()
{ {
QByteArray data = process->readAllStandardOutput(); QByteArray data = process->readAllStandardOutput();
QString added = QString::fromLocal8Bit(data); QString added = QString::fromLocal8Bit(data);
added.remove('\r'); added.remove('\r');
m_stdout += added; m_stdout += added;
} }
void JavaChecker::stderrReady() void JavaChecker::stderrReady()
{ {
QByteArray data = process->readAllStandardError(); QByteArray data = process->readAllStandardError();
QString added = QString::fromLocal8Bit(data); QString added = QString::fromLocal8Bit(data);
added.remove('\r'); added.remove('\r');
m_stderr += added; m_stderr += added;
} }
void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
{ {
killTimer.stop(); killTimer.stop();
QProcessPtr _process; QProcessPtr _process;
_process.swap(process); _process.swap(process);
JavaCheckResult result; JavaCheckResult result;
{ {
result.path = m_path; result.path = m_path;
result.id = m_id; result.id = m_id;
} }
result.errorLog = m_stderr; result.errorLog = m_stderr;
result.outLog = m_stdout; result.outLog = m_stdout;
qDebug() << "STDOUT" << m_stdout; qDebug() << "STDOUT" << m_stdout;
qWarning() << "STDERR" << m_stderr; qWarning() << "STDERR" << m_stderr;
qDebug() << "Java checker finished with status " << status << " exit code " << exitcode; qDebug() << "Java checker finished with status " << status << " exit code " << exitcode;
if (status == QProcess::CrashExit || exitcode == 1) if (status == QProcess::CrashExit || exitcode == 1)
{ {
result.validity = JavaCheckResult::Validity::Errored; result.validity = JavaCheckResult::Validity::Errored;
emit checkFinished(result); emit checkFinished(result);
return; return;
} }
bool success = true; bool success = true;
QMap<QString, QString> results; QMap<QString, QString> results;
QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts); QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts);
for(QString line : lines) for(QString line : lines)
{ {
line = line.trimmed(); line = line.trimmed();
auto parts = line.split('=', QString::SkipEmptyParts); auto parts = line.split('=', QString::SkipEmptyParts);
if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty())
{ {
success = false; success = false;
} }
else else
{ {
results.insert(parts[0], parts[1]); results.insert(parts[0], parts[1]);
} }
} }
if(!results.contains("os.arch") || !results.contains("java.version") || !success) if(!results.contains("os.arch") || !results.contains("java.version") || !success)
{ {
result.validity = JavaCheckResult::Validity::ReturnedInvalidData; result.validity = JavaCheckResult::Validity::ReturnedInvalidData;
emit checkFinished(result); emit checkFinished(result);
return; return;
} }
auto os_arch = results["os.arch"]; auto os_arch = results["os.arch"];
auto java_version = results["java.version"]; auto java_version = results["java.version"];
bool is_64 = os_arch == "x86_64" || os_arch == "amd64"; bool is_64 = os_arch == "x86_64" || os_arch == "amd64";
result.validity = JavaCheckResult::Validity::Valid; result.validity = JavaCheckResult::Validity::Valid;
result.is_64bit = is_64; result.is_64bit = is_64;
result.mojangPlatform = is_64 ? "64" : "32"; result.mojangPlatform = is_64 ? "64" : "32";
result.realPlatform = os_arch; result.realPlatform = os_arch;
result.javaVersion = java_version; result.javaVersion = java_version;
qDebug() << "Java checker succeeded."; qDebug() << "Java checker succeeded.";
emit checkFinished(result); emit checkFinished(result);
} }
void JavaChecker::error(QProcess::ProcessError err) void JavaChecker::error(QProcess::ProcessError err)
{ {
if(err == QProcess::FailedToStart) if(err == QProcess::FailedToStart)
{ {
killTimer.stop(); killTimer.stop();
qDebug() << "Java checker has failed to start."; qDebug() << "Java checker has failed to start.";
JavaCheckResult result; JavaCheckResult result;
{ {
result.path = m_path; result.path = m_path;
result.id = m_id; result.id = m_id;
} }
emit checkFinished(result); emit checkFinished(result);
return; return;
} }
} }
void JavaChecker::timeout() void JavaChecker::timeout()
{ {
// NO MERCY. NO ABUSE. // NO MERCY. NO ABUSE.
if(process) if(process)
{ {
qDebug() << "Java checker has been killed by timeout."; qDebug() << "Java checker has been killed by timeout.";
process->kill(); process->kill();
} }
} }

View File

@ -11,50 +11,50 @@ class JavaChecker;
struct MULTIMC_LOGIC_EXPORT JavaCheckResult struct MULTIMC_LOGIC_EXPORT JavaCheckResult
{ {
QString path; QString path;
QString mojangPlatform; QString mojangPlatform;
QString realPlatform; QString realPlatform;
JavaVersion javaVersion; JavaVersion javaVersion;
QString outLog; QString outLog;
QString errorLog; QString errorLog;
bool is_64bit = false; bool is_64bit = false;
int id; int id;
enum class Validity enum class Validity
{ {
Errored, Errored,
ReturnedInvalidData, ReturnedInvalidData,
Valid Valid
} validity = Validity::Errored; } validity = Validity::Errored;
}; };
typedef std::shared_ptr<QProcess> QProcessPtr; typedef std::shared_ptr<QProcess> QProcessPtr;
typedef std::shared_ptr<JavaChecker> JavaCheckerPtr; typedef std::shared_ptr<JavaChecker> JavaCheckerPtr;
class MULTIMC_LOGIC_EXPORT JavaChecker : public QObject class MULTIMC_LOGIC_EXPORT JavaChecker : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit JavaChecker(QObject *parent = 0); explicit JavaChecker(QObject *parent = 0);
void performCheck(); void performCheck();
QString m_path; QString m_path;
QString m_args; QString m_args;
int m_id = 0; int m_id = 0;
int m_minMem = 0; int m_minMem = 0;
int m_maxMem = 0; int m_maxMem = 0;
int m_permGen = 64; int m_permGen = 64;
signals: signals:
void checkFinished(JavaCheckResult result); void checkFinished(JavaCheckResult result);
private: private:
QProcessPtr process; QProcessPtr process;
QTimer killTimer; QTimer killTimer;
QString m_stdout; QString m_stdout;
QString m_stderr; QString m_stderr;
public public
slots: slots:
void timeout(); void timeout();
void finished(int exitcode, QProcess::ExitStatus); void finished(int exitcode, QProcess::ExitStatus);
void error(QProcess::ProcessError); void error(QProcess::ProcessError);
void stdoutReady(); void stdoutReady();
void stderrReady(); void stderrReady();
}; };

View File

@ -19,26 +19,26 @@
void JavaCheckerJob::partFinished(JavaCheckResult result) void JavaCheckerJob::partFinished(JavaCheckResult result)
{ {
num_finished++; num_finished++;
qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/" qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/"
<< javacheckers.size(); << javacheckers.size();
setProgress(num_finished, javacheckers.size()); setProgress(num_finished, javacheckers.size());
javaresults.replace(result.id, result); javaresults.replace(result.id, result);
if (num_finished == javacheckers.size()) if (num_finished == javacheckers.size())
{ {
emitSucceeded(); emitSucceeded();
} }
} }
void JavaCheckerJob::executeTask() void JavaCheckerJob::executeTask()
{ {
qDebug() << m_job_name.toLocal8Bit() << " started."; qDebug() << m_job_name.toLocal8Bit() << " started.";
for (auto iter : javacheckers) for (auto iter : javacheckers)
{ {
javaresults.append(JavaCheckResult()); javaresults.append(JavaCheckResult());
connect(iter.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult))); connect(iter.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult)));
iter->performCheck(); iter->performCheck();
} }
} }

View File

@ -25,37 +25,37 @@ typedef std::shared_ptr<JavaCheckerJob> JavaCheckerJobPtr;
// FIXME: this just seems horribly redundant // FIXME: this just seems horribly redundant
class JavaCheckerJob : public Task class JavaCheckerJob : public Task
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {}; explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {};
virtual ~JavaCheckerJob() {}; virtual ~JavaCheckerJob() {};
bool addJavaCheckerAction(JavaCheckerPtr base) bool addJavaCheckerAction(JavaCheckerPtr base)
{ {
javacheckers.append(base); javacheckers.append(base);
// if this is already running, the action needs to be started right away! // if this is already running, the action needs to be started right away!
if (isRunning()) if (isRunning())
{ {
setProgress(num_finished, javacheckers.size()); setProgress(num_finished, javacheckers.size());
connect(base.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished); connect(base.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished);
base->performCheck(); base->performCheck();
} }
return true; return true;
} }
QList<JavaCheckResult> getResults() QList<JavaCheckResult> getResults()
{ {
return javaresults; return javaresults;
} }
private slots: private slots:
void partFinished(JavaCheckResult result); void partFinished(JavaCheckResult result);
protected: protected:
virtual void executeTask() override; virtual void executeTask() override;
private: private:
QString m_job_name; QString m_job_name;
QList<JavaCheckerPtr> javacheckers; QList<JavaCheckerPtr> javacheckers;
QList<JavaCheckResult> javaresults; QList<JavaCheckResult> javaresults;
int num_finished = 0; int num_finished = 0;
}; };

View File

@ -3,26 +3,26 @@
bool JavaInstall::operator<(const JavaInstall &rhs) bool JavaInstall::operator<(const JavaInstall &rhs)
{ {
auto archCompare = Strings::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive); auto archCompare = Strings::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
if(archCompare != 0) if(archCompare != 0)
return archCompare < 0; return archCompare < 0;
if(id < rhs.id) if(id < rhs.id)
{ {
return true; return true;
} }
if(id > rhs.id) if(id > rhs.id)
{ {
return false; return false;
} }
return Strings::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0; return Strings::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;
} }
bool JavaInstall::operator==(const JavaInstall &rhs) bool JavaInstall::operator==(const JavaInstall &rhs)
{ {
return arch == rhs.arch && id == rhs.id && path == rhs.path; return arch == rhs.arch && id == rhs.id && path == rhs.path;
} }
bool JavaInstall::operator>(const JavaInstall &rhs) bool JavaInstall::operator>(const JavaInstall &rhs)
{ {
return (!operator<(rhs)) && (!operator==(rhs)); return (!operator<(rhs)) && (!operator==(rhs));
} }

View File

@ -5,34 +5,34 @@
struct JavaInstall : public BaseVersion struct JavaInstall : public BaseVersion
{ {
JavaInstall(){} JavaInstall(){}
JavaInstall(QString id, QString arch, QString path) JavaInstall(QString id, QString arch, QString path)
: id(id), arch(arch), path(path) : id(id), arch(arch), path(path)
{ {
} }
virtual QString descriptor() virtual QString descriptor()
{ {
return id.toString(); return id.toString();
} }
virtual QString name() virtual QString name()
{ {
return id.toString(); return id.toString();
} }
virtual QString typeString() const virtual QString typeString() const
{ {
return arch; return arch;
} }
bool operator<(const JavaInstall & rhs); bool operator<(const JavaInstall & rhs);
bool operator==(const JavaInstall & rhs); bool operator==(const JavaInstall & rhs);
bool operator>(const JavaInstall & rhs); bool operator>(const JavaInstall & rhs);
JavaVersion id; JavaVersion id;
QString arch; QString arch;
QString path; QString path;
bool recommended = false; bool recommended = false;
}; };
typedef std::shared_ptr<JavaInstall> JavaInstallPtr; typedef std::shared_ptr<JavaInstall> JavaInstallPtr;

View File

@ -31,111 +31,111 @@ JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent)
shared_qobject_ptr<Task> JavaInstallList::getLoadTask() shared_qobject_ptr<Task> JavaInstallList::getLoadTask()
{ {
load(); load();
return getCurrentTask(); return getCurrentTask();
} }
shared_qobject_ptr<Task> JavaInstallList::getCurrentTask() shared_qobject_ptr<Task> JavaInstallList::getCurrentTask()
{ {
if(m_status == Status::InProgress) if(m_status == Status::InProgress)
{ {
return m_loadTask; return m_loadTask;
} }
return nullptr; return nullptr;
} }
void JavaInstallList::load() void JavaInstallList::load()
{ {
if(m_status != Status::InProgress) if(m_status != Status::InProgress)
{ {
m_status = Status::InProgress; m_status = Status::InProgress;
m_loadTask = new JavaListLoadTask(this); m_loadTask = new JavaListLoadTask(this);
m_loadTask->start(); m_loadTask->start();
} }
} }
const BaseVersionPtr JavaInstallList::at(int i) const const BaseVersionPtr JavaInstallList::at(int i) const
{ {
return m_vlist.at(i); return m_vlist.at(i);
} }
bool JavaInstallList::isLoaded() bool JavaInstallList::isLoaded()
{ {
return m_status == JavaInstallList::Status::Done; return m_status == JavaInstallList::Status::Done;
} }
int JavaInstallList::count() const int JavaInstallList::count() const
{ {
return m_vlist.count(); return m_vlist.count();
} }
QVariant JavaInstallList::data(const QModelIndex &index, int role) const QVariant JavaInstallList::data(const QModelIndex &index, int role) const
{ {
if (!index.isValid()) if (!index.isValid())
return QVariant(); return QVariant();
if (index.row() > count()) if (index.row() > count())
return QVariant(); return QVariant();
auto version = std::dynamic_pointer_cast<JavaInstall>(m_vlist[index.row()]); auto version = std::dynamic_pointer_cast<JavaInstall>(m_vlist[index.row()]);
switch (role) switch (role)
{ {
case VersionPointerRole: case VersionPointerRole:
return qVariantFromValue(m_vlist[index.row()]); return qVariantFromValue(m_vlist[index.row()]);
case VersionIdRole: case VersionIdRole:
return version->descriptor(); return version->descriptor();
case VersionRole: case VersionRole:
return version->id.toString(); return version->id.toString();
case RecommendedRole: case RecommendedRole:
return version->recommended; return version->recommended;
case PathRole: case PathRole:
return version->path; return version->path;
case ArchitectureRole: case ArchitectureRole:
return version->arch; return version->arch;
default: default:
return QVariant(); return QVariant();
} }
} }
BaseVersionList::RoleList JavaInstallList::providesRoles() const BaseVersionList::RoleList JavaInstallList::providesRoles() const
{ {
return {VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole}; return {VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole};
} }
void JavaInstallList::updateListData(QList<BaseVersionPtr> versions) void JavaInstallList::updateListData(QList<BaseVersionPtr> versions)
{ {
beginResetModel(); beginResetModel();
m_vlist = versions; m_vlist = versions;
sortVersions(); sortVersions();
if(m_vlist.size()) if(m_vlist.size())
{ {
auto best = std::dynamic_pointer_cast<JavaInstall>(m_vlist[0]); auto best = std::dynamic_pointer_cast<JavaInstall>(m_vlist[0]);
best->recommended = true; best->recommended = true;
} }
endResetModel(); endResetModel();
m_status = Status::Done; m_status = Status::Done;
m_loadTask.reset(); m_loadTask.reset();
} }
bool sortJavas(BaseVersionPtr left, BaseVersionPtr right) bool sortJavas(BaseVersionPtr left, BaseVersionPtr right)
{ {
auto rleft = std::dynamic_pointer_cast<JavaInstall>(left); auto rleft = std::dynamic_pointer_cast<JavaInstall>(left);
auto rright = std::dynamic_pointer_cast<JavaInstall>(right); auto rright = std::dynamic_pointer_cast<JavaInstall>(right);
return (*rleft) > (*rright); return (*rleft) > (*rright);
} }
void JavaInstallList::sortVersions() void JavaInstallList::sortVersions()
{ {
beginResetModel(); beginResetModel();
std::sort(m_vlist.begin(), m_vlist.end(), sortJavas); std::sort(m_vlist.begin(), m_vlist.end(), sortJavas);
endResetModel(); endResetModel();
} }
JavaListLoadTask::JavaListLoadTask(JavaInstallList *vlist) : Task() JavaListLoadTask::JavaListLoadTask(JavaInstallList *vlist) : Task()
{ {
m_list = vlist; m_list = vlist;
m_currentRecommended = NULL; m_currentRecommended = NULL;
} }
JavaListLoadTask::~JavaListLoadTask() JavaListLoadTask::~JavaListLoadTask()
@ -144,65 +144,65 @@ JavaListLoadTask::~JavaListLoadTask()
void JavaListLoadTask::executeTask() void JavaListLoadTask::executeTask()
{ {
setStatus(tr("Detecting Java installations...")); setStatus(tr("Detecting Java installations..."));
JavaUtils ju; JavaUtils ju;
QList<QString> candidate_paths = ju.FindJavaPaths(); QList<QString> candidate_paths = ju.FindJavaPaths();
m_job = std::shared_ptr<JavaCheckerJob>(new JavaCheckerJob("Java detection")); m_job = std::shared_ptr<JavaCheckerJob>(new JavaCheckerJob("Java detection"));
connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished); connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished);
connect(m_job.get(), &Task::progress, this, &Task::setProgress); connect(m_job.get(), &Task::progress, this, &Task::setProgress);
qDebug() << "Probing the following Java paths: "; qDebug() << "Probing the following Java paths: ";
int id = 0; int id = 0;
for(QString candidate : candidate_paths) for(QString candidate : candidate_paths)
{ {
qDebug() << " " << candidate; qDebug() << " " << candidate;
auto candidate_checker = new JavaChecker(); auto candidate_checker = new JavaChecker();
candidate_checker->m_path = candidate; candidate_checker->m_path = candidate;
candidate_checker->m_id = id; candidate_checker->m_id = id;
m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker)); m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker));
id++; id++;
} }
m_job->start(); m_job->start();
} }
void JavaListLoadTask::javaCheckerFinished() void JavaListLoadTask::javaCheckerFinished()
{ {
QList<JavaInstallPtr> candidates; QList<JavaInstallPtr> candidates;
auto results = m_job->getResults(); auto results = m_job->getResults();
qDebug() << "Found the following valid Java installations:"; qDebug() << "Found the following valid Java installations:";
for(JavaCheckResult result : results) for(JavaCheckResult result : results)
{ {
if(result.validity == JavaCheckResult::Validity::Valid) if(result.validity == JavaCheckResult::Validity::Valid)
{ {
JavaInstallPtr javaVersion(new JavaInstall()); JavaInstallPtr javaVersion(new JavaInstall());
javaVersion->id = result.javaVersion; javaVersion->id = result.javaVersion;
javaVersion->arch = result.mojangPlatform; javaVersion->arch = result.mojangPlatform;
javaVersion->path = result.path; javaVersion->path = result.path;
candidates.append(javaVersion); candidates.append(javaVersion);
qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path; qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path;
} }
} }
QList<BaseVersionPtr> javas_bvp; QList<BaseVersionPtr> javas_bvp;
for (auto java : candidates) for (auto java : candidates)
{ {
//qDebug() << java->id << java->arch << " at " << java->path; //qDebug() << java->id << java->arch << " at " << java->path;
BaseVersionPtr bp_java = std::dynamic_pointer_cast<BaseVersion>(java); BaseVersionPtr bp_java = std::dynamic_pointer_cast<BaseVersion>(java);
if (bp_java) if (bp_java)
{ {
javas_bvp.append(java); javas_bvp.append(java);
} }
} }
m_list->updateListData(javas_bvp); m_list->updateListData(javas_bvp);
emitSucceeded(); emitSucceeded();
} }

View File

@ -30,52 +30,52 @@ class JavaListLoadTask;
class MULTIMC_LOGIC_EXPORT JavaInstallList : public BaseVersionList class MULTIMC_LOGIC_EXPORT JavaInstallList : public BaseVersionList
{ {
Q_OBJECT Q_OBJECT
enum class Status enum class Status
{ {
NotDone, NotDone,
InProgress, InProgress,
Done Done
}; };
public: public:
explicit JavaInstallList(QObject *parent = 0); explicit JavaInstallList(QObject *parent = 0);
shared_qobject_ptr<Task> getLoadTask() override; shared_qobject_ptr<Task> getLoadTask() override;
bool isLoaded() override; bool isLoaded() override;
const BaseVersionPtr at(int i) const override; const BaseVersionPtr at(int i) const override;
int count() const override; int count() const override;
void sortVersions() override; void sortVersions() override;
QVariant data(const QModelIndex &index, int role) const override; QVariant data(const QModelIndex &index, int role) const override;
RoleList providesRoles() const override; RoleList providesRoles() const override;
public slots: public slots:
void updateListData(QList<BaseVersionPtr> versions) override; void updateListData(QList<BaseVersionPtr> versions) override;
protected: protected:
void load(); void load();
shared_qobject_ptr<Task> getCurrentTask(); shared_qobject_ptr<Task> getCurrentTask();
protected: protected:
Status m_status = Status::NotDone; Status m_status = Status::NotDone;
shared_qobject_ptr<JavaListLoadTask> m_loadTask; shared_qobject_ptr<JavaListLoadTask> m_loadTask;
QList<BaseVersionPtr> m_vlist; QList<BaseVersionPtr> m_vlist;
}; };
class JavaListLoadTask : public Task class JavaListLoadTask : public Task
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit JavaListLoadTask(JavaInstallList *vlist); explicit JavaListLoadTask(JavaInstallList *vlist);
virtual ~JavaListLoadTask(); virtual ~JavaListLoadTask();
void executeTask() override; void executeTask() override;
public slots: public slots:
void javaCheckerFinished(); void javaCheckerFinished();
protected: protected:
std::shared_ptr<JavaCheckerJob> m_job; std::shared_ptr<JavaCheckerJob> m_job;
JavaInstallList *m_list; JavaInstallList *m_list;
JavaInstall *m_currentRecommended; JavaInstall *m_currentRecommended;
}; };

View File

@ -34,312 +34,312 @@ JavaUtils::JavaUtils()
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
static QString processLD_LIBRARY_PATH(const QString & LD_LIBRARY_PATH) static QString processLD_LIBRARY_PATH(const QString & LD_LIBRARY_PATH)
{ {
QDir mmcBin(QCoreApplication::applicationDirPath()); QDir mmcBin(QCoreApplication::applicationDirPath());
auto items = LD_LIBRARY_PATH.split(':'); auto items = LD_LIBRARY_PATH.split(':');
QStringList final; QStringList final;
for(auto & item: items) for(auto & item: items)
{ {
QDir test(item); QDir test(item);
if(test == mmcBin) if(test == mmcBin)
{ {
qDebug() << "Env:LD_LIBRARY_PATH ignoring path" << item; qDebug() << "Env:LD_LIBRARY_PATH ignoring path" << item;
continue; continue;
} }
final.append(item); final.append(item);
} }
return final.join(':'); return final.join(':');
} }
#endif #endif
QProcessEnvironment CleanEnviroment() QProcessEnvironment CleanEnviroment()
{ {
// prepare the process environment // prepare the process environment
QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment(); QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment();
QProcessEnvironment env; QProcessEnvironment env;
QStringList ignored = QStringList ignored =
{ {
"JAVA_ARGS", "JAVA_ARGS",
"CLASSPATH", "CLASSPATH",
"CONFIGPATH", "CONFIGPATH",
"JAVA_HOME", "JAVA_HOME",
"JRE_HOME", "JRE_HOME",
"_JAVA_OPTIONS", "_JAVA_OPTIONS",
"JAVA_OPTIONS", "JAVA_OPTIONS",
"JAVA_TOOL_OPTIONS" "JAVA_TOOL_OPTIONS"
}; };
for(auto key: rawenv.keys()) for(auto key: rawenv.keys())
{ {
auto value = rawenv.value(key); auto value = rawenv.value(key);
// filter out dangerous java crap // filter out dangerous java crap
if(ignored.contains(key)) if(ignored.contains(key))
{ {
qDebug() << "Env: ignoring" << key << value; qDebug() << "Env: ignoring" << key << value;
continue; continue;
} }
// filter MultiMC-related things // filter MultiMC-related things
if(key.startsWith("QT_")) if(key.startsWith("QT_"))
{ {
qDebug() << "Env: ignoring" << key << value; qDebug() << "Env: ignoring" << key << value;
continue; continue;
} }
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
// Do not pass LD_* variables to java. They were intended for MultiMC // Do not pass LD_* variables to java. They were intended for MultiMC
if(key.startsWith("LD_")) if(key.startsWith("LD_"))
{ {
qDebug() << "Env: ignoring" << key << value; qDebug() << "Env: ignoring" << key << value;
continue; continue;
} }
// Strip IBus // Strip IBus
// IBus is a Linux IME framework. For some reason, it breaks MC? // IBus is a Linux IME framework. For some reason, it breaks MC?
if (key == "XMODIFIERS" && value.contains(IBUS)) if (key == "XMODIFIERS" && value.contains(IBUS))
{ {
QString save = value; QString save = value;
value.replace(IBUS, ""); value.replace(IBUS, "");
qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value; qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value;
} }
if(key == "GAME_PRELOAD") if(key == "GAME_PRELOAD")
{ {
env.insert("LD_PRELOAD", value); env.insert("LD_PRELOAD", value);
continue; continue;
} }
if(key == "GAME_LIBRARY_PATH") if(key == "GAME_LIBRARY_PATH")
{ {
env.insert("LD_LIBRARY_PATH", processLD_LIBRARY_PATH(value)); env.insert("LD_LIBRARY_PATH", processLD_LIBRARY_PATH(value));
continue; continue;
} }
#endif #endif
// qDebug() << "Env: " << key << value; // qDebug() << "Env: " << key << value;
env.insert(key, value); env.insert(key, value);
} }
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
// HACK: Workaround for QTBUG42500 // HACK: Workaround for QTBUG42500
if(!env.contains("LD_LIBRARY_PATH")) if(!env.contains("LD_LIBRARY_PATH"))
{ {
env.insert("LD_LIBRARY_PATH", ""); env.insert("LD_LIBRARY_PATH", "");
} }
#endif #endif
return env; return env;
} }
JavaInstallPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch) JavaInstallPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch)
{ {
JavaInstallPtr javaVersion(new JavaInstall()); JavaInstallPtr javaVersion(new JavaInstall());
javaVersion->id = id; javaVersion->id = id;
javaVersion->arch = arch; javaVersion->arch = arch;
javaVersion->path = path; javaVersion->path = path;
return javaVersion; return javaVersion;
} }
JavaInstallPtr JavaUtils::GetDefaultJava() JavaInstallPtr JavaUtils::GetDefaultJava()
{ {
JavaInstallPtr javaVersion(new JavaInstall()); JavaInstallPtr javaVersion(new JavaInstall());
javaVersion->id = "java"; javaVersion->id = "java";
javaVersion->arch = "unknown"; javaVersion->arch = "unknown";
#if defined(Q_OS_WIN32) #if defined(Q_OS_WIN32)
javaVersion->path = "javaw"; javaVersion->path = "javaw";
#else #else
javaVersion->path = "java"; javaVersion->path = "java";
#endif #endif
return javaVersion; return javaVersion;
} }
#if defined(Q_OS_WIN32) #if defined(Q_OS_WIN32)
QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName) QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName)
{ {
QList<JavaInstallPtr> javas; QList<JavaInstallPtr> javas;
QString archType = "unknown"; QString archType = "unknown";
if (keyType == KEY_WOW64_64KEY) if (keyType == KEY_WOW64_64KEY)
archType = "64"; archType = "64";
else if (keyType == KEY_WOW64_32KEY) else if (keyType == KEY_WOW64_32KEY)
archType = "32"; archType = "32";
HKEY jreKey; HKEY jreKey;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName.toStdString().c_str(), 0, if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName.toStdString().c_str(), 0,
KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS) KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS)
{ {
// Read the current type version from the registry. // Read the current type version from the registry.
// This will be used to find any key that contains the JavaHome value. // This will be used to find any key that contains the JavaHome value.
char *value = new char[0]; char *value = new char[0];
DWORD valueSz = 0; DWORD valueSz = 0;
if (RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz) == if (RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz) ==
ERROR_MORE_DATA) ERROR_MORE_DATA)
{ {
value = new char[valueSz]; value = new char[valueSz];
RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz); RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz);
} }
QString recommended = value; QString recommended = value;
TCHAR subKeyName[255]; TCHAR subKeyName[255];
DWORD subKeyNameSize, numSubKeys, retCode; DWORD subKeyNameSize, numSubKeys, retCode;
// Get the number of subkeys // Get the number of subkeys
RegQueryInfoKey(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, RegQueryInfoKey(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL,
NULL, NULL); NULL, NULL);
// Iterate until RegEnumKeyEx fails // Iterate until RegEnumKeyEx fails
if (numSubKeys > 0) if (numSubKeys > 0)
{ {
for (DWORD i = 0; i < numSubKeys; i++) for (DWORD i = 0; i < numSubKeys; i++)
{ {
subKeyNameSize = 255; subKeyNameSize = 255;
retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL,
NULL); NULL);
if (retCode == ERROR_SUCCESS) if (retCode == ERROR_SUCCESS)
{ {
// Now open the registry key for the version that we just got. // Now open the registry key for the version that we just got.
QString newKeyName = keyName + "\\" + subKeyName; QString newKeyName = keyName + "\\" + subKeyName;
HKEY newKey; HKEY newKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, newKeyName.toStdString().c_str(), 0, if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, newKeyName.toStdString().c_str(), 0,
KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS) KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS)
{ {
// Read the JavaHome value to find where Java is installed. // Read the JavaHome value to find where Java is installed.
value = new char[0]; value = new char[0];
valueSz = 0; valueSz = 0;
if (RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value, if (RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value,
&valueSz) == ERROR_MORE_DATA) &valueSz) == ERROR_MORE_DATA)
{ {
value = new char[valueSz]; value = new char[valueSz];
RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value, RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value,
&valueSz); &valueSz);
// Now, we construct the version object and add it to the list. // Now, we construct the version object and add it to the list.
JavaInstallPtr javaVersion(new JavaInstall()); JavaInstallPtr javaVersion(new JavaInstall());
javaVersion->id = subKeyName; javaVersion->id = subKeyName;
javaVersion->arch = archType; javaVersion->arch = archType;
javaVersion->path = javaVersion->path =
QDir(FS::PathCombine(value, "bin")).absoluteFilePath("javaw.exe"); QDir(FS::PathCombine(value, "bin")).absoluteFilePath("javaw.exe");
javas.append(javaVersion); javas.append(javaVersion);
} }
RegCloseKey(newKey); RegCloseKey(newKey);
} }
} }
} }
} }
RegCloseKey(jreKey); RegCloseKey(jreKey);
} }
return javas; return javas;
} }
QList<QString> JavaUtils::FindJavaPaths() QList<QString> JavaUtils::FindJavaPaths()
{ {
QList<JavaInstallPtr> java_candidates; QList<JavaInstallPtr> java_candidates;
QList<JavaInstallPtr> JRE64s = this->FindJavaFromRegistryKey( QList<JavaInstallPtr> JRE64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
QList<JavaInstallPtr> JDK64s = this->FindJavaFromRegistryKey( QList<JavaInstallPtr> JDK64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
QList<JavaInstallPtr> JRE32s = this->FindJavaFromRegistryKey( QList<JavaInstallPtr> JRE32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
QList<JavaInstallPtr> JDK32s = this->FindJavaFromRegistryKey( QList<JavaInstallPtr> JDK32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
java_candidates.append(JRE64s); java_candidates.append(JRE64s);
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe"));
java_candidates.append(JDK64s); java_candidates.append(JDK64s);
java_candidates.append(JRE32s); java_candidates.append(JRE32s);
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe"));
java_candidates.append(JDK32s); java_candidates.append(JDK32s);
java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path)); java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path));
QList<QString> candidates; QList<QString> candidates;
for(JavaInstallPtr java_candidate : java_candidates) for(JavaInstallPtr java_candidate : java_candidates)
{ {
if(!candidates.contains(java_candidate->path)) if(!candidates.contains(java_candidate->path))
{ {
candidates.append(java_candidate->path); candidates.append(java_candidate->path);
} }
} }
return candidates; return candidates;
} }
#elif defined(Q_OS_MAC) #elif defined(Q_OS_MAC)
QList<QString> JavaUtils::FindJavaPaths() QList<QString> JavaUtils::FindJavaPaths()
{ {
QList<QString> javas; QList<QString> javas;
javas.append(this->GetDefaultJava()->path); javas.append(this->GetDefaultJava()->path);
javas.append("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java"); javas.append("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java");
javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"); javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java");
javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java"); javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java");
QDir libraryJVMDir("/Library/Java/JavaVirtualMachines/"); QDir libraryJVMDir("/Library/Java/JavaVirtualMachines/");
QStringList libraryJVMJavas = libraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); QStringList libraryJVMJavas = libraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach (const QString &java, libraryJVMJavas) { foreach (const QString &java, libraryJVMJavas) {
javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/jre/bin/java"); javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/jre/bin/java");
} }
QDir systemLibraryJVMDir("/System/Library/Java/JavaVirtualMachines/"); QDir systemLibraryJVMDir("/System/Library/Java/JavaVirtualMachines/");
QStringList systemLibraryJVMJavas = systemLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); QStringList systemLibraryJVMJavas = systemLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach (const QString &java, systemLibraryJVMJavas) { foreach (const QString &java, systemLibraryJVMJavas) {
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
} }
return javas; return javas;
} }
#elif defined(Q_OS_LINUX) #elif defined(Q_OS_LINUX)
QList<QString> JavaUtils::FindJavaPaths() QList<QString> JavaUtils::FindJavaPaths()
{ {
qDebug() << "Linux Java detection incomplete - defaulting to \"java\""; qDebug() << "Linux Java detection incomplete - defaulting to \"java\"";
QList<QString> javas; QList<QString> javas;
javas.append(this->GetDefaultJava()->path); javas.append(this->GetDefaultJava()->path);
auto scanJavaDir = [&](const QString & dirPath) auto scanJavaDir = [&](const QString & dirPath)
{ {
QDir dir(dirPath); QDir dir(dirPath);
if(!dir.exists()) if(!dir.exists())
return; return;
auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
for(auto & entry: entries) for(auto & entry: entries)
{ {
QString prefix; QString prefix;
if(entry.isAbsolute()) if(entry.isAbsolute())
{ {
prefix = entry.absoluteFilePath(); prefix = entry.absoluteFilePath();
} }
else else
{ {
prefix = entry.filePath(); prefix = entry.filePath();
} }
javas.append(FS::PathCombine(prefix, "jre/bin/java")); javas.append(FS::PathCombine(prefix, "jre/bin/java"));
javas.append(FS::PathCombine(prefix, "bin/java")); javas.append(FS::PathCombine(prefix, "bin/java"));
} }
}; };
// oracle RPMs // oracle RPMs
scanJavaDir("/usr/java"); scanJavaDir("/usr/java");
// general locations used by distro packaging // general locations used by distro packaging
scanJavaDir("/usr/lib/jvm"); scanJavaDir("/usr/lib/jvm");
scanJavaDir("/usr/lib32/jvm"); scanJavaDir("/usr/lib32/jvm");
// javas stored in MultiMC's folder // javas stored in MultiMC's folder
scanJavaDir("java"); scanJavaDir("java");
return javas; return javas;
} }
#else #else
QList<QString> JavaUtils::FindJavaPaths() QList<QString> JavaUtils::FindJavaPaths()
{ {
qDebug() << "Unknown operating system build - defaulting to \"java\""; qDebug() << "Unknown operating system build - defaulting to \"java\"";
QList<QString> javas; QList<QString> javas;
javas.append(this->GetDefaultJava()->path); javas.append(this->GetDefaultJava()->path);
return javas; return javas;
} }
#endif #endif

View File

@ -30,15 +30,15 @@ QProcessEnvironment CleanEnviroment();
class MULTIMC_LOGIC_EXPORT JavaUtils : public QObject class MULTIMC_LOGIC_EXPORT JavaUtils : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
JavaUtils(); JavaUtils();
JavaInstallPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown"); JavaInstallPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown");
QList<QString> FindJavaPaths(); QList<QString> FindJavaPaths();
JavaInstallPtr GetDefaultJava(); JavaInstallPtr GetDefaultJava();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
QList<JavaInstallPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName); QList<JavaInstallPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName);
#endif #endif
}; };

View File

@ -6,116 +6,116 @@
JavaVersion & JavaVersion::operator=(const QString & javaVersionString) JavaVersion & JavaVersion::operator=(const QString & javaVersionString)
{ {
m_string = javaVersionString; m_string = javaVersionString;
auto getCapturedInteger = [](const QRegularExpressionMatch & match, const QString &what) -> int auto getCapturedInteger = [](const QRegularExpressionMatch & match, const QString &what) -> int
{ {
auto str = match.captured(what); auto str = match.captured(what);
if(str.isEmpty()) if(str.isEmpty())
{ {
return 0; return 0;
} }
return str.toInt(); return str.toInt();
}; };
QRegularExpression pattern; QRegularExpression pattern;
if(javaVersionString.startsWith("1.")) if(javaVersionString.startsWith("1."))
{ {
pattern = QRegularExpression ("1[.](?<major>[0-9]+)([.](?<minor>[0-9]+))?(_(?<security>[0-9]+)?)?(-(?<prerelease>[a-zA-Z0-9]+))?"); pattern = QRegularExpression ("1[.](?<major>[0-9]+)([.](?<minor>[0-9]+))?(_(?<security>[0-9]+)?)?(-(?<prerelease>[a-zA-Z0-9]+))?");
} }
else else
{ {
pattern = QRegularExpression("(?<major>[0-9]+)([.](?<minor>[0-9]+))?([.](?<security>[0-9]+))?(-(?<prerelease>[a-zA-Z0-9]+))?"); pattern = QRegularExpression("(?<major>[0-9]+)([.](?<minor>[0-9]+))?([.](?<security>[0-9]+))?(-(?<prerelease>[a-zA-Z0-9]+))?");
} }
auto match = pattern.match(m_string); auto match = pattern.match(m_string);
m_parseable = match.hasMatch(); m_parseable = match.hasMatch();
m_major = getCapturedInteger(match, "major"); m_major = getCapturedInteger(match, "major");
m_minor = getCapturedInteger(match, "minor"); m_minor = getCapturedInteger(match, "minor");
m_security = getCapturedInteger(match, "security"); m_security = getCapturedInteger(match, "security");
m_prerelease = match.captured("prerelease"); m_prerelease = match.captured("prerelease");
return *this; return *this;
} }
JavaVersion::JavaVersion(const QString &rhs) JavaVersion::JavaVersion(const QString &rhs)
{ {
operator=(rhs); operator=(rhs);
} }
QString JavaVersion::toString() QString JavaVersion::toString()
{ {
return m_string; return m_string;
} }
bool JavaVersion::requiresPermGen() bool JavaVersion::requiresPermGen()
{ {
if(m_parseable) if(m_parseable)
{ {
return m_major < 8; return m_major < 8;
} }
return true; return true;
} }
bool JavaVersion::operator<(const JavaVersion &rhs) bool JavaVersion::operator<(const JavaVersion &rhs)
{ {
if(m_parseable && rhs.m_parseable) if(m_parseable && rhs.m_parseable)
{ {
auto major = m_major; auto major = m_major;
auto rmajor = rhs.m_major; auto rmajor = rhs.m_major;
// HACK: discourage using java 9 // HACK: discourage using java 9
if(major > 8) if(major > 8)
major = -major; major = -major;
if(rmajor > 8) if(rmajor > 8)
rmajor = -rmajor; rmajor = -rmajor;
if(major < rmajor) if(major < rmajor)
return true; return true;
if(major > rmajor) if(major > rmajor)
return false; return false;
if(m_minor < rhs.m_minor) if(m_minor < rhs.m_minor)
return true; return true;
if(m_minor > rhs.m_minor) if(m_minor > rhs.m_minor)
return false; return false;
if(m_security < rhs.m_security) if(m_security < rhs.m_security)
return true; return true;
if(m_security > rhs.m_security) if(m_security > rhs.m_security)
return false; return false;
// everything else being equal, consider prerelease status // everything else being equal, consider prerelease status
bool thisPre = !m_prerelease.isEmpty(); bool thisPre = !m_prerelease.isEmpty();
bool rhsPre = !rhs.m_prerelease.isEmpty(); bool rhsPre = !rhs.m_prerelease.isEmpty();
if(thisPre && !rhsPre) if(thisPre && !rhsPre)
{ {
// this is a prerelease and the other one isn't -> lesser // this is a prerelease and the other one isn't -> lesser
return true; return true;
} }
else if(!thisPre && rhsPre) else if(!thisPre && rhsPre)
{ {
// this isn't a prerelease and the other one is -> greater // this isn't a prerelease and the other one is -> greater
return false; return false;
} }
else if(thisPre && rhsPre) else if(thisPre && rhsPre)
{ {
// both are prereleases - use natural compare... // both are prereleases - use natural compare...
return Strings::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0; return Strings::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0;
} }
// neither is prerelease, so they are the same -> this cannot be less than rhs // neither is prerelease, so they are the same -> this cannot be less than rhs
return false; return false;
} }
else return Strings::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0; else return Strings::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0;
} }
bool JavaVersion::operator==(const JavaVersion &rhs) bool JavaVersion::operator==(const JavaVersion &rhs)
{ {
if(m_parseable && rhs.m_parseable) if(m_parseable && rhs.m_parseable)
{ {
return m_major == rhs.m_major && m_minor == rhs.m_minor && m_security == rhs.m_security && m_prerelease == rhs.m_prerelease; return m_major == rhs.m_major && m_minor == rhs.m_minor && m_security == rhs.m_security && m_prerelease == rhs.m_prerelease;
} }
return m_string == rhs.m_string; return m_string == rhs.m_string;
} }
bool JavaVersion::operator>(const JavaVersion &rhs) bool JavaVersion::operator>(const JavaVersion &rhs)
{ {
return (!operator<(rhs)) && (!operator==(rhs)); return (!operator<(rhs)) && (!operator==(rhs));
} }

View File

@ -5,46 +5,46 @@
// NOTE: apparently the GNU C library pollutes the global namespace with these... undef them. // NOTE: apparently the GNU C library pollutes the global namespace with these... undef them.
#ifdef major #ifdef major
#undef major #undef major
#endif #endif
#ifdef minor #ifdef minor
#undef minor #undef minor
#endif #endif
class MULTIMC_LOGIC_EXPORT JavaVersion class MULTIMC_LOGIC_EXPORT JavaVersion
{ {
friend class JavaVersionTest; friend class JavaVersionTest;
public: public:
JavaVersion() {}; JavaVersion() {};
JavaVersion(const QString & rhs); JavaVersion(const QString & rhs);
JavaVersion & operator=(const QString & rhs); JavaVersion & operator=(const QString & rhs);
bool operator<(const JavaVersion & rhs); bool operator<(const JavaVersion & rhs);
bool operator==(const JavaVersion & rhs); bool operator==(const JavaVersion & rhs);
bool operator>(const JavaVersion & rhs); bool operator>(const JavaVersion & rhs);
bool requiresPermGen(); bool requiresPermGen();
QString toString(); QString toString();
int major() int major()
{ {
return m_major; return m_major;
} }
int minor() int minor()
{ {
return m_minor; return m_minor;
} }
int security() int security()
{ {
return m_security; return m_security;
} }
private: private:
QString m_string; QString m_string;
int m_major = 0; int m_major = 0;
int m_minor = 0; int m_minor = 0;
int m_security = 0; int m_security = 0;
bool m_parseable = false; bool m_parseable = false;
QString m_prerelease; QString m_prerelease;
}; };

View File

@ -5,110 +5,110 @@
class JavaVersionTest : public QObject class JavaVersionTest : public QObject
{ {
Q_OBJECT Q_OBJECT
private private
slots: slots:
void test_Parse_data() void test_Parse_data()
{ {
QTest::addColumn<QString>("string"); QTest::addColumn<QString>("string");
QTest::addColumn<int>("major"); QTest::addColumn<int>("major");
QTest::addColumn<int>("minor"); QTest::addColumn<int>("minor");
QTest::addColumn<int>("security"); QTest::addColumn<int>("security");
QTest::addColumn<QString>("prerelease"); QTest::addColumn<QString>("prerelease");
QTest::newRow("old format") << "1.6.0_33" << 6 << 0 << 33 << QString(); QTest::newRow("old format") << "1.6.0_33" << 6 << 0 << 33 << QString();
QTest::newRow("old format prerelease") << "1.9.0_1-ea" << 9 << 0 << 1 << "ea"; QTest::newRow("old format prerelease") << "1.9.0_1-ea" << 9 << 0 << 1 << "ea";
QTest::newRow("new format major") << "9" << 9 << 0 << 0 << QString(); QTest::newRow("new format major") << "9" << 9 << 0 << 0 << QString();
QTest::newRow("new format minor") << "9.1" << 9 << 1 << 0 << QString(); QTest::newRow("new format minor") << "9.1" << 9 << 1 << 0 << QString();
QTest::newRow("new format security") << "9.0.1" << 9 << 0 << 1 << QString(); QTest::newRow("new format security") << "9.0.1" << 9 << 0 << 1 << QString();
QTest::newRow("new format prerelease") << "9-ea" << 9 << 0 << 0 << "ea"; QTest::newRow("new format prerelease") << "9-ea" << 9 << 0 << 0 << "ea";
QTest::newRow("new format long prerelease") << "9.0.1-ea" << 9 << 0 << 1 << "ea"; QTest::newRow("new format long prerelease") << "9.0.1-ea" << 9 << 0 << 1 << "ea";
} }
void test_Parse() void test_Parse()
{ {
QFETCH(QString, string); QFETCH(QString, string);
QFETCH(int, major); QFETCH(int, major);
QFETCH(int, minor); QFETCH(int, minor);
QFETCH(int, security); QFETCH(int, security);
QFETCH(QString, prerelease); QFETCH(QString, prerelease);
JavaVersion test(string); JavaVersion test(string);
QCOMPARE(test.m_string, string); QCOMPARE(test.m_string, string);
QCOMPARE(test.toString(), string); QCOMPARE(test.toString(), string);
QCOMPARE(test.m_major, major); QCOMPARE(test.m_major, major);
QCOMPARE(test.m_minor, minor); QCOMPARE(test.m_minor, minor);
QCOMPARE(test.m_security, security); QCOMPARE(test.m_security, security);
QCOMPARE(test.m_prerelease, prerelease); QCOMPARE(test.m_prerelease, prerelease);
} }
void test_Sort_data() void test_Sort_data()
{ {
QTest::addColumn<QString>("lhs"); QTest::addColumn<QString>("lhs");
QTest::addColumn<QString>("rhs"); QTest::addColumn<QString>("rhs");
QTest::addColumn<bool>("smaller"); QTest::addColumn<bool>("smaller");
QTest::addColumn<bool>("equal"); QTest::addColumn<bool>("equal");
QTest::addColumn<bool>("bigger"); QTest::addColumn<bool>("bigger");
// old format and new format equivalence // old format and new format equivalence
QTest::newRow("1.6.0_33 == 6.0.33") << "1.6.0_33" << "6.0.33" << false << true << false; QTest::newRow("1.6.0_33 == 6.0.33") << "1.6.0_33" << "6.0.33" << false << true << false;
// old format major version // old format major version
QTest::newRow("1.5.0_33 < 1.6.0_33") << "1.5.0_33" << "1.6.0_33" << true << false << false; QTest::newRow("1.5.0_33 < 1.6.0_33") << "1.5.0_33" << "1.6.0_33" << true << false << false;
// new format - first release vs first security patch // new format - first release vs first security patch
QTest::newRow("9 < 9.0.1") << "9" << "9.0.1" << true << false << false; QTest::newRow("9 < 9.0.1") << "9" << "9.0.1" << true << false << false;
QTest::newRow("9.0.1 > 9") << "9.0.1" << "9" << false << false << true; QTest::newRow("9.0.1 > 9") << "9.0.1" << "9" << false << false << true;
// new format - first minor vs first release/security patch // new format - first minor vs first release/security patch
QTest::newRow("9.1 > 9.0.1") << "9.1" << "9.0.1" << false << false << true; QTest::newRow("9.1 > 9.0.1") << "9.1" << "9.0.1" << false << false << true;
QTest::newRow("9.0.1 < 9.1") << "9.0.1" << "9.1" << true << false << false; QTest::newRow("9.0.1 < 9.1") << "9.0.1" << "9.1" << true << false << false;
QTest::newRow("9.1 > 9") << "9.1" << "9" << false << false << true; QTest::newRow("9.1 > 9") << "9.1" << "9" << false << false << true;
QTest::newRow("9 > 9.1") << "9" << "9.1" << true << false << false; QTest::newRow("9 > 9.1") << "9" << "9.1" << true << false << false;
// new format - omitted numbers // new format - omitted numbers
QTest::newRow("9 == 9.0") << "9" << "9.0" << false << true << false; QTest::newRow("9 == 9.0") << "9" << "9.0" << false << true << false;
QTest::newRow("9 == 9.0.0") << "9" << "9.0.0" << false << true << false; QTest::newRow("9 == 9.0.0") << "9" << "9.0.0" << false << true << false;
QTest::newRow("9.0 == 9.0.0") << "9.0" << "9.0.0" << false << true << false; QTest::newRow("9.0 == 9.0.0") << "9.0" << "9.0.0" << false << true << false;
// early access and prereleases compared to final release // early access and prereleases compared to final release
QTest::newRow("9-ea < 9") << "9-ea" << "9" << true << false << false; QTest::newRow("9-ea < 9") << "9-ea" << "9" << true << false << false;
QTest::newRow("9 < 9.0.1-ea") << "9" << "9.0.1-ea" << true << false << false; QTest::newRow("9 < 9.0.1-ea") << "9" << "9.0.1-ea" << true << false << false;
QTest::newRow("9.0.1-ea > 9") << "9.0.1-ea" << "9" << false << false << true; QTest::newRow("9.0.1-ea > 9") << "9.0.1-ea" << "9" << false << false << true;
// prerelease difference only testing // prerelease difference only testing
QTest::newRow("9-1 == 9-1") << "9-1" << "9-1" << false << true << false; QTest::newRow("9-1 == 9-1") << "9-1" << "9-1" << false << true << false;
QTest::newRow("9-1 < 9-2") << "9-1" << "9-2" << true << false << false; QTest::newRow("9-1 < 9-2") << "9-1" << "9-2" << true << false << false;
QTest::newRow("9-5 < 9-20") << "9-5" << "9-20" << true << false << false; QTest::newRow("9-5 < 9-20") << "9-5" << "9-20" << true << false << false;
QTest::newRow("9-rc1 < 9-rc2") << "9-rc1" << "9-rc2" << true << false << false; QTest::newRow("9-rc1 < 9-rc2") << "9-rc1" << "9-rc2" << true << false << false;
QTest::newRow("9-rc5 < 9-rc20") << "9-rc5" << "9-rc20" << true << false << false; QTest::newRow("9-rc5 < 9-rc20") << "9-rc5" << "9-rc20" << true << false << false;
QTest::newRow("9-rc < 9-rc2") << "9-rc" << "9-rc2" << true << false << false; QTest::newRow("9-rc < 9-rc2") << "9-rc" << "9-rc2" << true << false << false;
QTest::newRow("9-ea < 9-rc") << "9-ea" << "9-rc" << true << false << false; QTest::newRow("9-ea < 9-rc") << "9-ea" << "9-rc" << true << false << false;
} }
void test_Sort() void test_Sort()
{ {
QFETCH(QString, lhs); QFETCH(QString, lhs);
QFETCH(QString, rhs); QFETCH(QString, rhs);
QFETCH(bool, smaller); QFETCH(bool, smaller);
QFETCH(bool, equal); QFETCH(bool, equal);
QFETCH(bool, bigger); QFETCH(bool, bigger);
JavaVersion lver(lhs); JavaVersion lver(lhs);
JavaVersion rver(rhs); JavaVersion rver(rhs);
QCOMPARE(lver < rver, smaller); QCOMPARE(lver < rver, smaller);
QCOMPARE(lver == rver, equal); QCOMPARE(lver == rver, equal);
QCOMPARE(lver > rver, bigger); QCOMPARE(lver > rver, bigger);
} }
void test_PermGen_data() void test_PermGen_data()
{ {
QTest::addColumn<QString>("version"); QTest::addColumn<QString>("version");
QTest::addColumn<bool>("needs_permgen"); QTest::addColumn<bool>("needs_permgen");
QTest::newRow("1.6.0_33") << "1.6.0_33" << true; QTest::newRow("1.6.0_33") << "1.6.0_33" << true;
QTest::newRow("1.7.0_60") << "1.7.0_60" << true; QTest::newRow("1.7.0_60") << "1.7.0_60" << true;
QTest::newRow("1.8.0_22") << "1.8.0_22" << false; QTest::newRow("1.8.0_22") << "1.8.0_22" << false;
QTest::newRow("9-ea") << "9-ea" << false; QTest::newRow("9-ea") << "9-ea" << false;
QTest::newRow("9.2.4") << "9.2.4" << false; QTest::newRow("9.2.4") << "9.2.4" << false;
} }
void test_PermGen() void test_PermGen()
{ {
QFETCH(QString, version); QFETCH(QString, version);
QFETCH(bool, needs_permgen); QFETCH(bool, needs_permgen);
JavaVersion v(version); JavaVersion v(version);
QCOMPARE(needs_permgen, v.requiresPermGen()); QCOMPARE(needs_permgen, v.requiresPermGen());
} }
}; };
QTEST_GUILESS_MAIN(JavaVersionTest) QTEST_GUILESS_MAIN(JavaVersionTest)

View File

@ -22,115 +22,115 @@
void CheckJava::executeTask() void CheckJava::executeTask()
{ {
auto instance = m_parent->instance(); auto instance = m_parent->instance();
auto settings = instance->settings(); auto settings = instance->settings();
m_javaPath = FS::ResolveExecutable(settings->get("JavaPath").toString()); m_javaPath = FS::ResolveExecutable(settings->get("JavaPath").toString());
bool perInstance = settings->get("OverrideJava").toBool() || settings->get("OverrideJavaLocation").toBool(); bool perInstance = settings->get("OverrideJava").toBool() || settings->get("OverrideJavaLocation").toBool();
auto realJavaPath = QStandardPaths::findExecutable(m_javaPath); auto realJavaPath = QStandardPaths::findExecutable(m_javaPath);
if (realJavaPath.isEmpty()) if (realJavaPath.isEmpty())
{ {
if (perInstance) if (perInstance)
{ {
emit logLine( emit logLine(
tr("The java binary \"%1\" couldn't be found. Please fix the java path " tr("The java binary \"%1\" couldn't be found. Please fix the java path "
"override in the instance's settings or disable it.").arg(m_javaPath), "override in the instance's settings or disable it.").arg(m_javaPath),
MessageLevel::Warning); MessageLevel::Warning);
} }
else else
{ {
emit logLine(tr("The java binary \"%1\" couldn't be found. Please set up java in " emit logLine(tr("The java binary \"%1\" couldn't be found. Please set up java in "
"the settings.").arg(m_javaPath), "the settings.").arg(m_javaPath),
MessageLevel::Warning); MessageLevel::Warning);
} }
emitFailed(tr("Java path is not valid.")); emitFailed(tr("Java path is not valid."));
return; return;
} }
else else
{ {
emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::MultiMC); emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::MultiMC);
} }
QFileInfo javaInfo(realJavaPath); QFileInfo javaInfo(realJavaPath);
qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch(); qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch();
auto storedUnixTime = settings->get("JavaTimestamp").toLongLong(); auto storedUnixTime = settings->get("JavaTimestamp").toLongLong();
auto storedArchitecture = settings->get("JavaArchitecture").toString(); auto storedArchitecture = settings->get("JavaArchitecture").toString();
auto storedVersion = settings->get("JavaVersion").toString(); auto storedVersion = settings->get("JavaVersion").toString();
m_javaUnixTime = javaUnixTime; m_javaUnixTime = javaUnixTime;
// if timestamps are not the same, or something is missing, check! // if timestamps are not the same, or something is missing, check!
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0) if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0)
{ {
m_JavaChecker = std::make_shared<JavaChecker>(); m_JavaChecker = std::make_shared<JavaChecker>();
emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC); emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC);
connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished); connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
m_JavaChecker->m_path = realJavaPath; m_JavaChecker->m_path = realJavaPath;
m_JavaChecker->performCheck(); m_JavaChecker->performCheck();
return; return;
} }
else else
{ {
auto verString = instance->settings()->get("JavaVersion").toString(); auto verString = instance->settings()->get("JavaVersion").toString();
auto archString = instance->settings()->get("JavaArchitecture").toString(); auto archString = instance->settings()->get("JavaArchitecture").toString();
printJavaInfo(verString, archString); printJavaInfo(verString, archString);
} }
emitSucceeded(); emitSucceeded();
} }
void CheckJava::checkJavaFinished(JavaCheckResult result) void CheckJava::checkJavaFinished(JavaCheckResult result)
{ {
switch (result.validity) switch (result.validity)
{ {
case JavaCheckResult::Validity::Errored: case JavaCheckResult::Validity::Errored:
{ {
// Error message displayed if java can't start // Error message displayed if java can't start
emit logLine(tr("Could not start java:"), MessageLevel::Error); emit logLine(tr("Could not start java:"), MessageLevel::Error);
emit logLines(result.errorLog.split('\n'), MessageLevel::Error); emit logLines(result.errorLog.split('\n'), MessageLevel::Error);
emit logLine("\nCheck your MultiMC Java settings.", MessageLevel::MultiMC); emit logLine("\nCheck your MultiMC Java settings.", MessageLevel::MultiMC);
printSystemInfo(false, false); printSystemInfo(false, false);
emitFailed(tr("Could not start java!")); emitFailed(tr("Could not start java!"));
return; return;
} }
case JavaCheckResult::Validity::ReturnedInvalidData: case JavaCheckResult::Validity::ReturnedInvalidData:
{ {
emit logLine(tr("Java checker returned some invalid data MultiMC doesn't understand:"), MessageLevel::Error); emit logLine(tr("Java checker returned some invalid data MultiMC doesn't understand:"), MessageLevel::Error);
emit logLines(result.outLog.split('\n'), MessageLevel::Warning); emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
emit logLine("\nMinecraft might not start properly.", MessageLevel::MultiMC); emit logLine("\nMinecraft might not start properly.", MessageLevel::MultiMC);
printSystemInfo(false, false); printSystemInfo(false, false);
emitSucceeded(); emitSucceeded();
return; return;
} }
case JavaCheckResult::Validity::Valid: case JavaCheckResult::Validity::Valid:
{ {
auto instance = m_parent->instance(); auto instance = m_parent->instance();
printJavaInfo(result.javaVersion.toString(), result.mojangPlatform); printJavaInfo(result.javaVersion.toString(), result.mojangPlatform);
instance->settings()->set("JavaVersion", result.javaVersion.toString()); instance->settings()->set("JavaVersion", result.javaVersion.toString());
instance->settings()->set("JavaArchitecture", result.mojangPlatform); instance->settings()->set("JavaArchitecture", result.mojangPlatform);
instance->settings()->set("JavaTimestamp", m_javaUnixTime); instance->settings()->set("JavaTimestamp", m_javaUnixTime);
emitSucceeded(); emitSucceeded();
return; return;
} }
} }
} }
void CheckJava::printJavaInfo(const QString& version, const QString& architecture) void CheckJava::printJavaInfo(const QString& version, const QString& architecture)
{ {
emit logLine(tr("Java is version %1, using %2-bit architecture.\n\n").arg(version, architecture), MessageLevel::MultiMC); emit logLine(tr("Java is version %1, using %2-bit architecture.\n\n").arg(version, architecture), MessageLevel::MultiMC);
printSystemInfo(true, architecture == "64"); printSystemInfo(true, architecture == "64");
} }
void CheckJava::printSystemInfo(bool javaIsKnown, bool javaIs64bit) void CheckJava::printSystemInfo(bool javaIsKnown, bool javaIs64bit)
{ {
auto cpu64 = Sys::isCPU64bit(); auto cpu64 = Sys::isCPU64bit();
auto system64 = Sys::isSystem64bit(); auto system64 = Sys::isSystem64bit();
if(cpu64 != system64) if(cpu64 != system64)
{ {
emit logLine(tr("Your CPU architecture is not matching your system architecture. You might want to install a 64bit Operating System.\n\n"), MessageLevel::Error); emit logLine(tr("Your CPU architecture is not matching your system architecture. You might want to install a 64bit Operating System.\n\n"), MessageLevel::Error);
} }
if(javaIsKnown) if(javaIsKnown)
{ {
if(javaIs64bit != system64) if(javaIs64bit != system64)
{ {
emit logLine(tr("Your Java architecture is not matching your system architecture. You might want to install a 64bit Java version.\n\n"), MessageLevel::Error); emit logLine(tr("Your Java architecture is not matching your system architecture. You might want to install a 64bit Java version.\n\n"), MessageLevel::Error);
} }
} }
} }

View File

@ -21,25 +21,25 @@
class CheckJava: public LaunchStep class CheckJava: public LaunchStep
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit CheckJava(LaunchTask *parent) :LaunchStep(parent){}; explicit CheckJava(LaunchTask *parent) :LaunchStep(parent){};
virtual ~CheckJava() {}; virtual ~CheckJava() {};
virtual void executeTask(); virtual void executeTask();
virtual bool canAbort() const virtual bool canAbort() const
{ {
return false; return false;
} }
private slots: private slots:
void checkJavaFinished(JavaCheckResult result); void checkJavaFinished(JavaCheckResult result);
private: private:
void printJavaInfo(const QString & version, const QString & architecture); void printJavaInfo(const QString & version, const QString & architecture);
void printSystemInfo(bool javaIsKnown, bool javaIs64bit); void printSystemInfo(bool javaIsKnown, bool javaIs64bit);
private: private:
QString m_javaPath; QString m_javaPath;
qlonglong m_javaUnixTime; qlonglong m_javaUnixTime;
JavaCheckerPtr m_JavaChecker; JavaCheckerPtr m_JavaChecker;
}; };

View File

@ -18,10 +18,10 @@
void LaunchStep::bind(LaunchTask *parent) void LaunchStep::bind(LaunchTask *parent)
{ {
m_parent = parent; m_parent = parent;
connect(this, &LaunchStep::readyForLaunch, parent, &LaunchTask::onReadyForLaunch); connect(this, &LaunchStep::readyForLaunch, parent, &LaunchTask::onReadyForLaunch);
connect(this, &LaunchStep::logLine, parent, &LaunchTask::onLogLine); connect(this, &LaunchStep::logLine, parent, &LaunchTask::onLogLine);
connect(this, &LaunchStep::logLines, parent, &LaunchTask::onLogLines); connect(this, &LaunchStep::logLines, parent, &LaunchTask::onLogLines);
connect(this, &LaunchStep::finished, parent, &LaunchTask::onStepFinished); connect(this, &LaunchStep::finished, parent, &LaunchTask::onStepFinished);
connect(this, &LaunchStep::progressReportingRequest, parent, &LaunchTask::onProgressReportingRequested); connect(this, &LaunchStep::progressReportingRequest, parent, &LaunchTask::onProgressReportingRequested);
} }

View File

@ -23,28 +23,28 @@
class LaunchTask; class LaunchTask;
class LaunchStep: public Task class LaunchStep: public Task
{ {
Q_OBJECT Q_OBJECT
public: /* methods */ public: /* methods */
explicit LaunchStep(LaunchTask *parent):Task(nullptr), m_parent(parent) explicit LaunchStep(LaunchTask *parent):Task(nullptr), m_parent(parent)
{ {
bind(parent); bind(parent);
}; };
virtual ~LaunchStep() {}; virtual ~LaunchStep() {};
protected: /* methods */ protected: /* methods */
virtual void bind(LaunchTask *parent); virtual void bind(LaunchTask *parent);
signals: signals:
void logLines(QStringList lines, MessageLevel::Enum level); void logLines(QStringList lines, MessageLevel::Enum level);
void logLine(QString line, MessageLevel::Enum level); void logLine(QString line, MessageLevel::Enum level);
void readyForLaunch(); void readyForLaunch();
void progressReportingRequest(); void progressReportingRequest();
public slots: public slots:
virtual void proceed() {}; virtual void proceed() {};
// called in the opposite order than the Task launch(), used to clean up or otherwise undo things after the launch ends // called in the opposite order than the Task launch(), used to clean up or otherwise undo things after the launch ends
virtual void finalize() {}; virtual void finalize() {};
protected: /* data */ protected: /* data */
LaunchTask *m_parent; LaunchTask *m_parent;
}; };

View File

@ -30,14 +30,14 @@
void LaunchTask::init() void LaunchTask::init()
{ {
m_instance->setRunning(true); m_instance->setRunning(true);
} }
std::shared_ptr<LaunchTask> LaunchTask::create(InstancePtr inst) std::shared_ptr<LaunchTask> LaunchTask::create(InstancePtr inst)
{ {
std::shared_ptr<LaunchTask> proc(new LaunchTask(inst)); std::shared_ptr<LaunchTask> proc(new LaunchTask(inst));
proc->init(); proc->init();
return proc; return proc;
} }
LaunchTask::LaunchTask(InstancePtr instance): m_instance(instance) LaunchTask::LaunchTask(InstancePtr instance): m_instance(instance)
@ -46,235 +46,235 @@ LaunchTask::LaunchTask(InstancePtr instance): m_instance(instance)
void LaunchTask::appendStep(std::shared_ptr<LaunchStep> step) void LaunchTask::appendStep(std::shared_ptr<LaunchStep> step)
{ {
m_steps.append(step); m_steps.append(step);
} }
void LaunchTask::prependStep(std::shared_ptr<LaunchStep> step) void LaunchTask::prependStep(std::shared_ptr<LaunchStep> step)
{ {
m_steps.prepend(step); m_steps.prepend(step);
} }
void LaunchTask::executeTask() void LaunchTask::executeTask()
{ {
m_instance->setCrashed(false); m_instance->setCrashed(false);
if(!m_steps.size()) if(!m_steps.size())
{ {
state = LaunchTask::Finished; state = LaunchTask::Finished;
emitSucceeded(); emitSucceeded();
} }
state = LaunchTask::Running; state = LaunchTask::Running;
onStepFinished(); onStepFinished();
} }
void LaunchTask::onReadyForLaunch() void LaunchTask::onReadyForLaunch()
{ {
state = LaunchTask::Waiting; state = LaunchTask::Waiting;
emit readyForLaunch(); emit readyForLaunch();
} }
void LaunchTask::onStepFinished() void LaunchTask::onStepFinished()
{ {
// initial -> just start the first step // initial -> just start the first step
if(currentStep == -1) if(currentStep == -1)
{ {
currentStep ++; currentStep ++;
m_steps[currentStep]->start(); m_steps[currentStep]->start();
return; return;
} }
auto step = m_steps[currentStep]; auto step = m_steps[currentStep];
if(step->wasSuccessful()) if(step->wasSuccessful())
{ {
// end? // end?
if(currentStep == m_steps.size() - 1) if(currentStep == m_steps.size() - 1)
{ {
finalizeSteps(true, QString()); finalizeSteps(true, QString());
} }
else else
{ {
currentStep ++; currentStep ++;
step = m_steps[currentStep]; step = m_steps[currentStep];
step->start(); step->start();
} }
} }
else else
{ {
finalizeSteps(false, step->failReason()); finalizeSteps(false, step->failReason());
} }
} }
void LaunchTask::finalizeSteps(bool successful, const QString& error) void LaunchTask::finalizeSteps(bool successful, const QString& error)
{ {
for(auto step = currentStep; step >= 0; step--) for(auto step = currentStep; step >= 0; step--)
{ {
m_steps[step]->finalize(); m_steps[step]->finalize();
} }
if(successful) if(successful)
{ {
emitSucceeded(); emitSucceeded();
} }
else else
{ {
emitFailed(error); emitFailed(error);
} }
} }
void LaunchTask::onProgressReportingRequested() void LaunchTask::onProgressReportingRequested()
{ {
state = LaunchTask::Waiting; state = LaunchTask::Waiting;
emit requestProgress(m_steps[currentStep].get()); emit requestProgress(m_steps[currentStep].get());
} }
void LaunchTask::setCensorFilter(QMap<QString, QString> filter) void LaunchTask::setCensorFilter(QMap<QString, QString> filter)
{ {
m_censorFilter = filter; m_censorFilter = filter;
} }
QString LaunchTask::censorPrivateInfo(QString in) QString LaunchTask::censorPrivateInfo(QString in)
{ {
auto iter = m_censorFilter.begin(); auto iter = m_censorFilter.begin();
while (iter != m_censorFilter.end()) while (iter != m_censorFilter.end())
{ {
in.replace(iter.key(), iter.value()); in.replace(iter.key(), iter.value());
iter++; iter++;
} }
return in; return in;
} }
void LaunchTask::proceed() void LaunchTask::proceed()
{ {
if(state != LaunchTask::Waiting) if(state != LaunchTask::Waiting)
{ {
return; return;
} }
m_steps[currentStep]->proceed(); m_steps[currentStep]->proceed();
} }
bool LaunchTask::canAbort() const bool LaunchTask::canAbort() const
{ {
switch(state) switch(state)
{ {
case LaunchTask::Aborted: case LaunchTask::Aborted:
case LaunchTask::Failed: case LaunchTask::Failed:
case LaunchTask::Finished: case LaunchTask::Finished:
return false; return false;
case LaunchTask::NotStarted: case LaunchTask::NotStarted:
return true; return true;
case LaunchTask::Running: case LaunchTask::Running:
case LaunchTask::Waiting: case LaunchTask::Waiting:
{ {
auto step = m_steps[currentStep]; auto step = m_steps[currentStep];
return step->canAbort(); return step->canAbort();
} }
} }
return false; return false;
} }
bool LaunchTask::abort() bool LaunchTask::abort()
{ {
switch(state) switch(state)
{ {
case LaunchTask::Aborted: case LaunchTask::Aborted:
case LaunchTask::Failed: case LaunchTask::Failed:
case LaunchTask::Finished: case LaunchTask::Finished:
return true; return true;
case LaunchTask::NotStarted: case LaunchTask::NotStarted:
{ {
state = LaunchTask::Aborted; state = LaunchTask::Aborted;
emitFailed("Aborted"); emitFailed("Aborted");
return true; return true;
} }
case LaunchTask::Running: case LaunchTask::Running:
case LaunchTask::Waiting: case LaunchTask::Waiting:
{ {
auto step = m_steps[currentStep]; auto step = m_steps[currentStep];
if(!step->canAbort()) if(!step->canAbort())
{ {
return false; return false;
} }
if(step->abort()) if(step->abort())
{ {
state = LaunchTask::Aborted; state = LaunchTask::Aborted;
return true; return true;
} }
} }
default: default:
break; break;
} }
return false; return false;
} }
shared_qobject_ptr<LogModel> LaunchTask::getLogModel() shared_qobject_ptr<LogModel> LaunchTask::getLogModel()
{ {
if(!m_logModel) if(!m_logModel)
{ {
m_logModel.reset(new LogModel()); m_logModel.reset(new LogModel());
m_logModel->setMaxLines(m_instance->getConsoleMaxLines()); m_logModel->setMaxLines(m_instance->getConsoleMaxLines());
m_logModel->setStopOnOverflow(m_instance->shouldStopOnConsoleOverflow()); m_logModel->setStopOnOverflow(m_instance->shouldStopOnConsoleOverflow());
// FIXME: should this really be here? // FIXME: should this really be here?
m_logModel->setOverflowMessage(tr("MultiMC stopped watching the game log because the log length surpassed %1 lines.\n" m_logModel->setOverflowMessage(tr("MultiMC stopped watching the game log because the log length surpassed %1 lines.\n"
"You may have to fix your mods because the game is still logging to files and" "You may have to fix your mods because the game is still logging to files and"
" likely wasting harddrive space at an alarming rate!").arg(m_logModel->getMaxLines())); " likely wasting harddrive space at an alarming rate!").arg(m_logModel->getMaxLines()));
} }
return m_logModel; return m_logModel;
} }
void LaunchTask::onLogLines(const QStringList &lines, MessageLevel::Enum defaultLevel) void LaunchTask::onLogLines(const QStringList &lines, MessageLevel::Enum defaultLevel)
{ {
for (auto & line: lines) for (auto & line: lines)
{ {
onLogLine(line, defaultLevel); onLogLine(line, defaultLevel);
} }
} }
void LaunchTask::onLogLine(QString line, MessageLevel::Enum level) void LaunchTask::onLogLine(QString line, MessageLevel::Enum level)
{ {
// if the launcher part set a log level, use it // if the launcher part set a log level, use it
auto innerLevel = MessageLevel::fromLine(line); auto innerLevel = MessageLevel::fromLine(line);
if(innerLevel != MessageLevel::Unknown) if(innerLevel != MessageLevel::Unknown)
{ {
level = innerLevel; level = innerLevel;
} }
// If the level is still undetermined, guess level // If the level is still undetermined, guess level
if (level == MessageLevel::StdErr || level == MessageLevel::StdOut || level == MessageLevel::Unknown) if (level == MessageLevel::StdErr || level == MessageLevel::StdOut || level == MessageLevel::Unknown)
{ {
level = m_instance->guessLevel(line, level); level = m_instance->guessLevel(line, level);
} }
// censor private user info // censor private user info
line = censorPrivateInfo(line); line = censorPrivateInfo(line);
auto &model = *getLogModel(); auto &model = *getLogModel();
model.append(level, line); model.append(level, line);
} }
void LaunchTask::emitSucceeded() void LaunchTask::emitSucceeded()
{ {
m_instance->setRunning(false); m_instance->setRunning(false);
Task::emitSucceeded(); Task::emitSucceeded();
} }
void LaunchTask::emitFailed(QString reason) void LaunchTask::emitFailed(QString reason)
{ {
m_instance->setRunning(false); m_instance->setRunning(false);
m_instance->setCrashed(true); m_instance->setCrashed(true);
Task::emitFailed(reason); Task::emitFailed(reason);
} }
QString LaunchTask::substituteVariables(const QString &cmd) const QString LaunchTask::substituteVariables(const QString &cmd) const
{ {
QString out = cmd; QString out = cmd;
auto variables = m_instance->getVariables(); auto variables = m_instance->getVariables();
for (auto it = variables.begin(); it != variables.end(); ++it) for (auto it = variables.begin(); it != variables.end(); ++it)
{ {
out.replace("$" + it.key(), it.value()); out.replace("$" + it.key(), it.value());
} }
auto env = QProcessEnvironment::systemEnvironment(); auto env = QProcessEnvironment::systemEnvironment();
for (auto var : env.keys()) for (auto var : env.keys())
{ {
out.replace("$" + var, env.value(var)); out.replace("$" + var, env.value(var));
} }
return out; return out;
} }

View File

@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -28,98 +28,98 @@
class MULTIMC_LOGIC_EXPORT LaunchTask: public Task class MULTIMC_LOGIC_EXPORT LaunchTask: public Task
{ {
Q_OBJECT Q_OBJECT
protected: protected:
explicit LaunchTask(InstancePtr instance); explicit LaunchTask(InstancePtr instance);
void init(); void init();
public: public:
enum State enum State
{ {
NotStarted, NotStarted,
Running, Running,
Waiting, Waiting,
Failed, Failed,
Aborted, Aborted,
Finished Finished
}; };
public: /* methods */ public: /* methods */
static std::shared_ptr<LaunchTask> create(InstancePtr inst); static std::shared_ptr<LaunchTask> create(InstancePtr inst);
virtual ~LaunchTask() {}; virtual ~LaunchTask() {};
void appendStep(std::shared_ptr<LaunchStep> step); void appendStep(std::shared_ptr<LaunchStep> step);
void prependStep(std::shared_ptr<LaunchStep> step); void prependStep(std::shared_ptr<LaunchStep> step);
void setCensorFilter(QMap<QString, QString> filter); void setCensorFilter(QMap<QString, QString> filter);
InstancePtr instance() InstancePtr instance()
{ {
return m_instance; return m_instance;
} }
void setPid(qint64 pid) void setPid(qint64 pid)
{ {
m_pid = pid; m_pid = pid;
} }
qint64 pid() qint64 pid()
{ {
return m_pid; return m_pid;
} }
/** /**
* @brief prepare the process for launch (for multi-stage launch) * @brief prepare the process for launch (for multi-stage launch)
*/ */
virtual void executeTask() override; virtual void executeTask() override;
/** /**
* @brief launch the armed instance * @brief launch the armed instance
*/ */
void proceed(); void proceed();
/** /**
* @brief abort launch * @brief abort launch
*/ */
bool abort() override; bool abort() override;
bool canAbort() const override; bool canAbort() const override;
shared_qobject_ptr<LogModel> getLogModel(); shared_qobject_ptr<LogModel> getLogModel();
public: public:
QString substituteVariables(const QString &cmd) const; QString substituteVariables(const QString &cmd) const;
QString censorPrivateInfo(QString in); QString censorPrivateInfo(QString in);
protected: /* methods */ protected: /* methods */
virtual void emitFailed(QString reason) override; virtual void emitFailed(QString reason) override;
virtual void emitSucceeded() override; virtual void emitSucceeded() override;
signals: signals:
/** /**
* @brief emitted when the launch preparations are done * @brief emitted when the launch preparations are done
*/ */
void readyForLaunch(); void readyForLaunch();
void requestProgress(Task *task); void requestProgress(Task *task);
void requestLogging(); void requestLogging();
public slots: public slots:
void onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC); void onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
void onLogLine(QString line, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC); void onLogLine(QString line, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
void onReadyForLaunch(); void onReadyForLaunch();
void onStepFinished(); void onStepFinished();
void onProgressReportingRequested(); void onProgressReportingRequested();
private: /*methods */ private: /*methods */
void finalizeSteps(bool successful, const QString & error); void finalizeSteps(bool successful, const QString & error);
protected: /* data */ protected: /* data */
InstancePtr m_instance; InstancePtr m_instance;
shared_qobject_ptr<LogModel> m_logModel; shared_qobject_ptr<LogModel> m_logModel;
QList <std::shared_ptr<LaunchStep>> m_steps; QList <std::shared_ptr<LaunchStep>> m_steps;
QMap<QString, QString> m_censorFilter; QMap<QString, QString> m_censorFilter;
int currentStep = -1; int currentStep = -1;
State state = NotStarted; State state = NotStarted;
qint64 m_pid = -1; qint64 m_pid = -1;
}; };

View File

@ -2,166 +2,166 @@
LogModel::LogModel(QObject *parent):QAbstractListModel(parent) LogModel::LogModel(QObject *parent):QAbstractListModel(parent)
{ {
m_content.resize(m_maxLines); m_content.resize(m_maxLines);
} }
int LogModel::rowCount(const QModelIndex &parent) const int LogModel::rowCount(const QModelIndex &parent) const
{ {
if (parent.isValid()) if (parent.isValid())
return 0; return 0;
return m_numLines; return m_numLines;
} }
QVariant LogModel::data(const QModelIndex &index, int role) const QVariant LogModel::data(const QModelIndex &index, int role) const
{ {
if (index.row() < 0 || index.row() >= m_numLines) if (index.row() < 0 || index.row() >= m_numLines)
return QVariant(); return QVariant();
auto row = index.row(); auto row = index.row();
auto realRow = (row + m_firstLine) % m_maxLines; auto realRow = (row + m_firstLine) % m_maxLines;
if (role == Qt::DisplayRole || role == Qt::EditRole) if (role == Qt::DisplayRole || role == Qt::EditRole)
{ {
return m_content[realRow].line; return m_content[realRow].line;
} }
if(role == LevelRole) if(role == LevelRole)
{ {
return m_content[realRow].level; return m_content[realRow].level;
} }
return QVariant(); return QVariant();
} }
void LogModel::append(MessageLevel::Enum level, QString line) void LogModel::append(MessageLevel::Enum level, QString line)
{ {
if(m_suspended) if(m_suspended)
{ {
return; return;
} }
int lineNum = (m_firstLine + m_numLines) % m_maxLines; int lineNum = (m_firstLine + m_numLines) % m_maxLines;
// overflow // overflow
if(m_numLines == m_maxLines) if(m_numLines == m_maxLines)
{ {
if(m_stopOnOverflow) if(m_stopOnOverflow)
{ {
// nothing more to do, the buffer is full // nothing more to do, the buffer is full
return; return;
} }
beginRemoveRows(QModelIndex(), 0, 0); beginRemoveRows(QModelIndex(), 0, 0);
m_firstLine = (m_firstLine + 1) % m_maxLines; m_firstLine = (m_firstLine + 1) % m_maxLines;
m_numLines --; m_numLines --;
endRemoveRows(); endRemoveRows();
} }
else if (m_numLines == m_maxLines - 1 && m_stopOnOverflow) else if (m_numLines == m_maxLines - 1 && m_stopOnOverflow)
{ {
level = MessageLevel::Fatal; level = MessageLevel::Fatal;
line = m_overflowMessage; line = m_overflowMessage;
} }
beginInsertRows(QModelIndex(), m_numLines, m_numLines); beginInsertRows(QModelIndex(), m_numLines, m_numLines);
m_numLines ++; m_numLines ++;
m_content[lineNum].level = level; m_content[lineNum].level = level;
m_content[lineNum].line = line; m_content[lineNum].line = line;
endInsertRows(); endInsertRows();
} }
void LogModel::suspend(bool suspend) void LogModel::suspend(bool suspend)
{ {
m_suspended = suspend; m_suspended = suspend;
} }
bool LogModel::suspended() bool LogModel::suspended()
{ {
return m_suspended; return m_suspended;
} }
void LogModel::clear() void LogModel::clear()
{ {
beginResetModel(); beginResetModel();
m_firstLine = 0; m_firstLine = 0;
m_numLines = 0; m_numLines = 0;
endResetModel(); endResetModel();
} }
QString LogModel::toPlainText() QString LogModel::toPlainText()
{ {
QString out; QString out;
out.reserve(m_numLines * 80); out.reserve(m_numLines * 80);
for(int i = 0; i < m_numLines; i++) for(int i = 0; i < m_numLines; i++)
{ {
QString & line = m_content[(m_firstLine + i) % m_maxLines].line; QString & line = m_content[(m_firstLine + i) % m_maxLines].line;
out.append(line + '\n'); out.append(line + '\n');
} }
out.squeeze(); out.squeeze();
return out; return out;
} }
void LogModel::setMaxLines(int maxLines) void LogModel::setMaxLines(int maxLines)
{ {
// no-op // no-op
if(maxLines == m_maxLines) if(maxLines == m_maxLines)
{ {
return; return;
} }
// if it all still fits in the buffer, just resize it // if it all still fits in the buffer, just resize it
if(m_firstLine + m_numLines < m_maxLines) if(m_firstLine + m_numLines < m_maxLines)
{ {
m_maxLines = maxLines; m_maxLines = maxLines;
m_content.resize(maxLines); m_content.resize(maxLines);
return; return;
} }
// otherwise, we need to reorganize the data because it crosses the wrap boundary // otherwise, we need to reorganize the data because it crosses the wrap boundary
QVector<entry> newContent; QVector<entry> newContent;
newContent.resize(maxLines); newContent.resize(maxLines);
if(m_numLines <= maxLines) if(m_numLines <= maxLines)
{ {
// if it all fits in the new buffer, just copy it over // if it all fits in the new buffer, just copy it over
for(int i = 0; i < m_numLines; i++) for(int i = 0; i < m_numLines; i++)
{ {
newContent[i] = m_content[(m_firstLine + i) % m_maxLines]; newContent[i] = m_content[(m_firstLine + i) % m_maxLines];
} }
m_content.swap(newContent); m_content.swap(newContent);
} }
else else
{ {
// if it doesn't fit, part of the data needs to be thrown away (the oldest log messages) // if it doesn't fit, part of the data needs to be thrown away (the oldest log messages)
int lead = m_numLines - maxLines; int lead = m_numLines - maxLines;
beginRemoveRows(QModelIndex(), 0, lead - 1); beginRemoveRows(QModelIndex(), 0, lead - 1);
for(int i = 0; i < maxLines; i++) for(int i = 0; i < maxLines; i++)
{ {
newContent[i] = m_content[(m_firstLine + lead + i) % m_maxLines]; newContent[i] = m_content[(m_firstLine + lead + i) % m_maxLines];
} }
m_numLines = m_maxLines; m_numLines = m_maxLines;
m_content.swap(newContent); m_content.swap(newContent);
endRemoveRows(); endRemoveRows();
} }
m_firstLine = 0; m_firstLine = 0;
m_maxLines = maxLines; m_maxLines = maxLines;
} }
int LogModel::getMaxLines() int LogModel::getMaxLines()
{ {
return m_maxLines; return m_maxLines;
} }
void LogModel::setStopOnOverflow(bool stop) void LogModel::setStopOnOverflow(bool stop)
{ {
m_stopOnOverflow = stop; m_stopOnOverflow = stop;
} }
void LogModel::setOverflowMessage(const QString& overflowMessage) void LogModel::setOverflowMessage(const QString& overflowMessage)
{ {
m_overflowMessage = overflowMessage; m_overflowMessage = overflowMessage;
} }
void LogModel::setLineWrap(bool state) void LogModel::setLineWrap(bool state)
{ {
if(m_lineWrap != state) if(m_lineWrap != state)
{ {
m_lineWrap = state; m_lineWrap = state;
} }
} }
bool LogModel::wrapLines() const bool LogModel::wrapLines() const
{ {
return m_lineWrap; return m_lineWrap;
} }

View File

@ -8,53 +8,53 @@
class MULTIMC_LOGIC_EXPORT LogModel : public QAbstractListModel class MULTIMC_LOGIC_EXPORT LogModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit LogModel(QObject *parent = 0); explicit LogModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const; int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;
void append(MessageLevel::Enum, QString line); void append(MessageLevel::Enum, QString line);
void clear(); void clear();
void suspend(bool suspend); void suspend(bool suspend);
bool suspended(); bool suspended();
QString toPlainText(); QString toPlainText();
int getMaxLines(); int getMaxLines();
void setMaxLines(int maxLines); void setMaxLines(int maxLines);
void setStopOnOverflow(bool stop); void setStopOnOverflow(bool stop);
void setOverflowMessage(const QString & overflowMessage); void setOverflowMessage(const QString & overflowMessage);
void setLineWrap(bool state); void setLineWrap(bool state);
bool wrapLines() const; bool wrapLines() const;
enum Roles enum Roles
{ {
LevelRole = Qt::UserRole LevelRole = Qt::UserRole
}; };
private /* types */: private /* types */:
struct entry struct entry
{ {
MessageLevel::Enum level; MessageLevel::Enum level;
QString line; QString line;
}; };
private: /* data */ private: /* data */
QVector <entry> m_content; QVector <entry> m_content;
int m_maxLines = 1000; int m_maxLines = 1000;
// first line in the circular buffer // first line in the circular buffer
int m_firstLine = 0; int m_firstLine = 0;
// number of lines occupied in the circular buffer // number of lines occupied in the circular buffer
int m_numLines = 0; int m_numLines = 0;
bool m_stopOnOverflow = false; bool m_stopOnOverflow = false;
QString m_overflowMessage = "OVERFLOW"; QString m_overflowMessage = "OVERFLOW";
bool m_suspended = false; bool m_suspended = false;
bool m_lineWrap = true; bool m_lineWrap = true;
private: private:
Q_DISABLE_COPY(LogModel) Q_DISABLE_COPY(LogModel)
}; };

View File

@ -18,67 +18,67 @@
PostLaunchCommand::PostLaunchCommand(LaunchTask *parent) : LaunchStep(parent) PostLaunchCommand::PostLaunchCommand(LaunchTask *parent) : LaunchStep(parent)
{ {
auto instance = m_parent->instance(); auto instance = m_parent->instance();
m_command = instance->getPostExitCommand(); m_command = instance->getPostExitCommand();
m_process.setProcessEnvironment(instance->createEnvironment()); m_process.setProcessEnvironment(instance->createEnvironment());
connect(&m_process, &LoggedProcess::log, this, &PostLaunchCommand::logLines); connect(&m_process, &LoggedProcess::log, this, &PostLaunchCommand::logLines);
connect(&m_process, &LoggedProcess::stateChanged, this, &PostLaunchCommand::on_state); connect(&m_process, &LoggedProcess::stateChanged, this, &PostLaunchCommand::on_state);
} }
void PostLaunchCommand::executeTask() void PostLaunchCommand::executeTask()
{ {
QString postlaunch_cmd = m_parent->substituteVariables(m_command); QString postlaunch_cmd = m_parent->substituteVariables(m_command);
emit logLine(tr("Running Post-Launch command: %1").arg(postlaunch_cmd), MessageLevel::MultiMC); emit logLine(tr("Running Post-Launch command: %1").arg(postlaunch_cmd), MessageLevel::MultiMC);
m_process.start(postlaunch_cmd); m_process.start(postlaunch_cmd);
} }
void PostLaunchCommand::on_state(LoggedProcess::State state) void PostLaunchCommand::on_state(LoggedProcess::State state)
{ {
auto getError = [&]() auto getError = [&]()
{ {
return tr("Post-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); return tr("Post-Launch command failed with code %1.\n\n").arg(m_process.exitCode());
}; };
switch(state) switch(state)
{ {
case LoggedProcess::Aborted: case LoggedProcess::Aborted:
case LoggedProcess::Crashed: case LoggedProcess::Crashed:
case LoggedProcess::FailedToStart: case LoggedProcess::FailedToStart:
{ {
auto error = getError(); auto error = getError();
emit logLine(error, MessageLevel::Fatal); emit logLine(error, MessageLevel::Fatal);
emitFailed(error); emitFailed(error);
return; return;
} }
case LoggedProcess::Finished: case LoggedProcess::Finished:
{ {
if(m_process.exitCode() != 0) if(m_process.exitCode() != 0)
{ {
auto error = getError(); auto error = getError();
emit logLine(error, MessageLevel::Fatal); emit logLine(error, MessageLevel::Fatal);
emitFailed(error); emitFailed(error);
} }
else else
{ {
emit logLine(tr("Post-Launch command ran successfully.\n\n"), MessageLevel::MultiMC); emit logLine(tr("Post-Launch command ran successfully.\n\n"), MessageLevel::MultiMC);
emitSucceeded(); emitSucceeded();
} }
} }
default: default:
break; break;
} }
} }
void PostLaunchCommand::setWorkingDirectory(const QString &wd) void PostLaunchCommand::setWorkingDirectory(const QString &wd)
{ {
m_process.setWorkingDirectory(wd); m_process.setWorkingDirectory(wd);
} }
bool PostLaunchCommand::abort() bool PostLaunchCommand::abort()
{ {
auto state = m_process.state(); auto state = m_process.state();
if (state == LoggedProcess::Running || state == LoggedProcess::Starting) if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
{ {
m_process.kill(); m_process.kill();
} }
return true; return true;
} }

View File

@ -20,22 +20,22 @@
class PostLaunchCommand: public LaunchStep class PostLaunchCommand: public LaunchStep
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit PostLaunchCommand(LaunchTask *parent); explicit PostLaunchCommand(LaunchTask *parent);
virtual ~PostLaunchCommand() {}; virtual ~PostLaunchCommand() {};
virtual void executeTask(); virtual void executeTask();
virtual bool abort(); virtual bool abort();
virtual bool canAbort() const virtual bool canAbort() const
{ {
return true; return true;
} }
void setWorkingDirectory(const QString &wd); void setWorkingDirectory(const QString &wd);
private slots: private slots:
void on_state(LoggedProcess::State state); void on_state(LoggedProcess::State state);
private: private:
LoggedProcess m_process; LoggedProcess m_process;
QString m_command; QString m_command;
}; };

View File

@ -18,68 +18,68 @@
PreLaunchCommand::PreLaunchCommand(LaunchTask *parent) : LaunchStep(parent) PreLaunchCommand::PreLaunchCommand(LaunchTask *parent) : LaunchStep(parent)
{ {
auto instance = m_parent->instance(); auto instance = m_parent->instance();
m_command = instance->getPreLaunchCommand(); m_command = instance->getPreLaunchCommand();
m_process.setProcessEnvironment(instance->createEnvironment()); m_process.setProcessEnvironment(instance->createEnvironment());
connect(&m_process, &LoggedProcess::log, this, &PreLaunchCommand::logLines); connect(&m_process, &LoggedProcess::log, this, &PreLaunchCommand::logLines);
connect(&m_process, &LoggedProcess::stateChanged, this, &PreLaunchCommand::on_state); connect(&m_process, &LoggedProcess::stateChanged, this, &PreLaunchCommand::on_state);
} }
void PreLaunchCommand::executeTask() void PreLaunchCommand::executeTask()
{ {
//FIXME: where to put this? //FIXME: where to put this?
QString prelaunch_cmd = m_parent->substituteVariables(m_command); QString prelaunch_cmd = m_parent->substituteVariables(m_command);
emit logLine(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd), MessageLevel::MultiMC); emit logLine(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd), MessageLevel::MultiMC);
m_process.start(prelaunch_cmd); m_process.start(prelaunch_cmd);
} }
void PreLaunchCommand::on_state(LoggedProcess::State state) void PreLaunchCommand::on_state(LoggedProcess::State state)
{ {
auto getError = [&]() auto getError = [&]()
{ {
return tr("Pre-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); return tr("Pre-Launch command failed with code %1.\n\n").arg(m_process.exitCode());
}; };
switch(state) switch(state)
{ {
case LoggedProcess::Aborted: case LoggedProcess::Aborted:
case LoggedProcess::Crashed: case LoggedProcess::Crashed:
case LoggedProcess::FailedToStart: case LoggedProcess::FailedToStart:
{ {
auto error = getError(); auto error = getError();
emit logLine(error, MessageLevel::Fatal); emit logLine(error, MessageLevel::Fatal);
emitFailed(error); emitFailed(error);
return; return;
} }
case LoggedProcess::Finished: case LoggedProcess::Finished:
{ {
if(m_process.exitCode() != 0) if(m_process.exitCode() != 0)
{ {
auto error = getError(); auto error = getError();
emit logLine(error, MessageLevel::Fatal); emit logLine(error, MessageLevel::Fatal);
emitFailed(error); emitFailed(error);
} }
else else
{ {
emit logLine(tr("Pre-Launch command ran successfully.\n\n"), MessageLevel::MultiMC); emit logLine(tr("Pre-Launch command ran successfully.\n\n"), MessageLevel::MultiMC);
emitSucceeded(); emitSucceeded();
} }
} }
default: default:
break; break;
} }
} }
void PreLaunchCommand::setWorkingDirectory(const QString &wd) void PreLaunchCommand::setWorkingDirectory(const QString &wd)
{ {
m_process.setWorkingDirectory(wd); m_process.setWorkingDirectory(wd);
} }
bool PreLaunchCommand::abort() bool PreLaunchCommand::abort()
{ {
auto state = m_process.state(); auto state = m_process.state();
if (state == LoggedProcess::Running || state == LoggedProcess::Starting) if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
{ {
m_process.kill(); m_process.kill();
} }
return true; return true;
} }

View File

@ -20,22 +20,22 @@
class PreLaunchCommand: public LaunchStep class PreLaunchCommand: public LaunchStep
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit PreLaunchCommand(LaunchTask *parent); explicit PreLaunchCommand(LaunchTask *parent);
virtual ~PreLaunchCommand() {}; virtual ~PreLaunchCommand() {};
virtual void executeTask(); virtual void executeTask();
virtual bool abort(); virtual bool abort();
virtual bool canAbort() const virtual bool canAbort() const
{ {
return true; return true;
} }
void setWorkingDirectory(const QString &wd); void setWorkingDirectory(const QString &wd);
private slots: private slots:
void on_state(LoggedProcess::State state); void on_state(LoggedProcess::State state);
private: private:
LoggedProcess m_process; LoggedProcess m_process;
QString m_command; QString m_command;
}; };

View File

@ -2,28 +2,28 @@
TextPrint::TextPrint(LaunchTask * parent, const QStringList &lines, MessageLevel::Enum level) : LaunchStep(parent) TextPrint::TextPrint(LaunchTask * parent, const QStringList &lines, MessageLevel::Enum level) : LaunchStep(parent)
{ {
m_lines = lines; m_lines = lines;
m_level = level; m_level = level;
} }
TextPrint::TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level) : LaunchStep(parent) TextPrint::TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level) : LaunchStep(parent)
{ {
m_lines.append(line); m_lines.append(line);
m_level = level; m_level = level;
} }
void TextPrint::executeTask() void TextPrint::executeTask()
{ {
emit logLines(m_lines, m_level); emit logLines(m_lines, m_level);
emitSucceeded(); emitSucceeded();
} }
bool TextPrint::canAbort() const bool TextPrint::canAbort() const
{ {
return true; return true;
} }
bool TextPrint::abort() bool TextPrint::abort()
{ {
emitFailed("Aborted."); emitFailed("Aborted.");
return true; return true;
} }

View File

@ -27,17 +27,17 @@
class MULTIMC_LOGIC_EXPORT TextPrint: public LaunchStep class MULTIMC_LOGIC_EXPORT TextPrint: public LaunchStep
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit TextPrint(LaunchTask *parent, const QStringList &lines, MessageLevel::Enum level); explicit TextPrint(LaunchTask *parent, const QStringList &lines, MessageLevel::Enum level);
explicit TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level); explicit TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level);
virtual ~TextPrint(){}; virtual ~TextPrint(){};
virtual void executeTask(); virtual void executeTask();
virtual bool canAbort() const; virtual bool canAbort() const;
virtual bool abort(); virtual bool abort();
private: private:
QStringList m_lines; QStringList m_lines;
MessageLevel::Enum m_level; MessageLevel::Enum m_level;
}; };

View File

@ -18,63 +18,63 @@
void Update::executeTask() void Update::executeTask()
{ {
if(m_aborted) if(m_aborted)
{ {
emitFailed(tr("Task aborted.")); emitFailed(tr("Task aborted."));
return; return;
} }
m_updateTask.reset(m_parent->instance()->createUpdateTask(m_mode)); m_updateTask.reset(m_parent->instance()->createUpdateTask(m_mode));
if(m_updateTask) if(m_updateTask)
{ {
connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished())); connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished()));
connect(m_updateTask.get(), &Task::progress, this, &Task::setProgress); connect(m_updateTask.get(), &Task::progress, this, &Task::setProgress);
connect(m_updateTask.get(), &Task::status, this, &Task::setStatus); connect(m_updateTask.get(), &Task::status, this, &Task::setStatus);
emit progressReportingRequest(); emit progressReportingRequest();
return; return;
} }
emitSucceeded(); emitSucceeded();
} }
void Update::proceed() void Update::proceed()
{ {
m_updateTask->start(); m_updateTask->start();
} }
void Update::updateFinished() void Update::updateFinished()
{ {
if(m_updateTask->wasSuccessful()) if(m_updateTask->wasSuccessful())
{ {
m_updateTask.reset(); m_updateTask.reset();
emitSucceeded(); emitSucceeded();
} }
else else
{ {
QString reason = tr("Instance update failed because: %1.\n\n").arg(m_updateTask->failReason()); QString reason = tr("Instance update failed because: %1.\n\n").arg(m_updateTask->failReason());
m_updateTask.reset(); m_updateTask.reset();
emit logLine(reason, MessageLevel::Fatal); emit logLine(reason, MessageLevel::Fatal);
emitFailed(reason); emitFailed(reason);
} }
} }
bool Update::canAbort() const bool Update::canAbort() const
{ {
if(m_updateTask) if(m_updateTask)
{ {
return m_updateTask->canAbort(); return m_updateTask->canAbort();
} }
return true; return true;
} }
bool Update::abort() bool Update::abort()
{ {
m_aborted = true; m_aborted = true;
if(m_updateTask) if(m_updateTask)
{ {
if(m_updateTask->canAbort()) if(m_updateTask->canAbort())
{ {
return m_updateTask->abort(); return m_updateTask->abort();
} }
} }
return true; return true;
} }

View File

@ -24,22 +24,22 @@
// FIXME: stupid. should be defined by the instance type? or even completely abstracted away... // FIXME: stupid. should be defined by the instance type? or even completely abstracted away...
class Update: public LaunchStep class Update: public LaunchStep
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit Update(LaunchTask *parent, Net::Mode mode):LaunchStep(parent), m_mode(mode) {}; explicit Update(LaunchTask *parent, Net::Mode mode):LaunchStep(parent), m_mode(mode) {};
virtual ~Update() {}; virtual ~Update() {};
void executeTask() override; void executeTask() override;
bool canAbort() const override; bool canAbort() const override;
void proceed() override; void proceed() override;
public slots: public slots:
bool abort() override; bool abort() override;
private slots: private slots:
void updateFinished(); void updateFinished();
private: private:
shared_qobject_ptr<Task> m_updateTask; shared_qobject_ptr<Task> m_updateTask;
bool m_aborted = false; bool m_aborted = false;
Net::Mode m_mode = Net::Mode::Offline; Net::Mode m_mode = Net::Mode::Offline;
}; };

View File

@ -27,45 +27,45 @@
class ParsingValidator : public Net::Validator class ParsingValidator : public Net::Validator
{ {
public: /* con/des */ public: /* con/des */
ParsingValidator(Meta::BaseEntity *entity) : m_entity(entity) ParsingValidator(Meta::BaseEntity *entity) : m_entity(entity)
{ {
}; };
virtual ~ParsingValidator() virtual ~ParsingValidator()
{ {
}; };
public: /* methods */ public: /* methods */
bool init(QNetworkRequest &) override bool init(QNetworkRequest &) override
{ {
return true; return true;
} }
bool write(QByteArray & data) override bool write(QByteArray & data) override
{ {
this->data.append(data); this->data.append(data);
return true; return true;
} }
bool abort() override bool abort() override
{ {
return true; return true;
} }
bool validate(QNetworkReply &) override bool validate(QNetworkReply &) override
{ {
auto fname = m_entity->localFilename(); auto fname = m_entity->localFilename();
try try
{ {
m_entity->parse(Json::requireObject(Json::requireDocument(data, fname), fname)); m_entity->parse(Json::requireObject(Json::requireDocument(data, fname), fname));
return true; return true;
} }
catch (const Exception &e) catch (const Exception &e)
{ {
qWarning() << "Unable to parse response:" << e.cause(); qWarning() << "Unable to parse response:" << e.cause();
return false; return false;
} }
} }
private: /* data */ private: /* data */
QByteArray data; QByteArray data;
Meta::BaseEntity *m_entity; Meta::BaseEntity *m_entity;
}; };
Meta::BaseEntity::~BaseEntity() Meta::BaseEntity::~BaseEntity()
@ -74,89 +74,89 @@ Meta::BaseEntity::~BaseEntity()
QUrl Meta::BaseEntity::url() const QUrl Meta::BaseEntity::url() const
{ {
return QUrl("https://v1.meta.multimc.org").resolved(localFilename()); return QUrl("https://v1.meta.multimc.org").resolved(localFilename());
} }
bool Meta::BaseEntity::loadLocalFile() bool Meta::BaseEntity::loadLocalFile()
{ {
const QString fname = QDir("meta").absoluteFilePath(localFilename()); const QString fname = QDir("meta").absoluteFilePath(localFilename());
if (!QFile::exists(fname)) if (!QFile::exists(fname))
{ {
return false; return false;
} }
// TODO: check if the file has the expected checksum // TODO: check if the file has the expected checksum
try try
{ {
parse(Json::requireObject(Json::requireDocument(fname, fname), fname)); parse(Json::requireObject(Json::requireDocument(fname, fname), fname));
return true; return true;
} }
catch (const Exception &e) catch (const Exception &e)
{ {
qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause()); qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause());
// just make sure it's gone and we never consider it again. // just make sure it's gone and we never consider it again.
QFile::remove(fname); QFile::remove(fname);
return false; return false;
} }
} }
void Meta::BaseEntity::load(Net::Mode loadType) void Meta::BaseEntity::load(Net::Mode loadType)
{ {
// load local file if nothing is loaded yet // load local file if nothing is loaded yet
if(!isLoaded()) if(!isLoaded())
{ {
if(loadLocalFile()) if(loadLocalFile())
{ {
m_loadStatus = LoadStatus::Local; m_loadStatus = LoadStatus::Local;
} }
} }
// if we need remote update, run the update task // if we need remote update, run the update task
if(loadType == Net::Mode::Offline || !shouldStartRemoteUpdate()) if(loadType == Net::Mode::Offline || !shouldStartRemoteUpdate())
{ {
return; return;
} }
NetJob *job = new NetJob(QObject::tr("Download of meta file %1").arg(localFilename())); NetJob *job = new NetJob(QObject::tr("Download of meta file %1").arg(localFilename()));
auto url = this->url(); auto url = this->url();
auto entry = ENV.metacache()->resolveEntry("meta", localFilename()); auto entry = ENV.metacache()->resolveEntry("meta", localFilename());
entry->setStale(true); entry->setStale(true);
auto dl = Net::Download::makeCached(url, entry); auto dl = Net::Download::makeCached(url, entry);
/* /*
* The validator parses the file and loads it into the object. * The validator parses the file and loads it into the object.
* If that fails, the file is not written to storage. * If that fails, the file is not written to storage.
*/ */
dl->addValidator(new ParsingValidator(this)); dl->addValidator(new ParsingValidator(this));
job->addNetAction(dl); job->addNetAction(dl);
m_updateStatus = UpdateStatus::InProgress; m_updateStatus = UpdateStatus::InProgress;
m_updateTask.reset(job); m_updateTask.reset(job);
QObject::connect(job, &NetJob::succeeded, [&]() QObject::connect(job, &NetJob::succeeded, [&]()
{ {
m_loadStatus = LoadStatus::Remote; m_loadStatus = LoadStatus::Remote;
m_updateStatus = UpdateStatus::Succeeded; m_updateStatus = UpdateStatus::Succeeded;
m_updateTask.reset(); m_updateTask.reset();
}); });
QObject::connect(job, &NetJob::failed, [&]() QObject::connect(job, &NetJob::failed, [&]()
{ {
m_updateStatus = UpdateStatus::Failed; m_updateStatus = UpdateStatus::Failed;
m_updateTask.reset(); m_updateTask.reset();
}); });
m_updateTask->start(); m_updateTask->start();
} }
bool Meta::BaseEntity::isLoaded() const bool Meta::BaseEntity::isLoaded() const
{ {
return m_loadStatus > LoadStatus::NotLoaded; return m_loadStatus > LoadStatus::NotLoaded;
} }
bool Meta::BaseEntity::shouldStartRemoteUpdate() const bool Meta::BaseEntity::shouldStartRemoteUpdate() const
{ {
// TODO: version-locks and offline mode? // TODO: version-locks and offline mode?
return m_updateStatus != UpdateStatus::InProgress; return m_updateStatus != UpdateStatus::InProgress;
} }
shared_qobject_ptr<Task> Meta::BaseEntity::getCurrentTask() shared_qobject_ptr<Task> Meta::BaseEntity::getCurrentTask()
{ {
if(m_updateStatus == UpdateStatus::InProgress) if(m_updateStatus == UpdateStatus::InProgress)
{ {
return m_updateTask; return m_updateTask;
} }
return nullptr; return nullptr;
} }

View File

@ -28,41 +28,41 @@ namespace Meta
class MULTIMC_LOGIC_EXPORT BaseEntity class MULTIMC_LOGIC_EXPORT BaseEntity
{ {
public: /* types */ public: /* types */
using Ptr = std::shared_ptr<BaseEntity>; using Ptr = std::shared_ptr<BaseEntity>;
enum class LoadStatus enum class LoadStatus
{ {
NotLoaded, NotLoaded,
Local, Local,
Remote Remote
}; };
enum class UpdateStatus enum class UpdateStatus
{ {
NotDone, NotDone,
InProgress, InProgress,
Failed, Failed,
Succeeded Succeeded
}; };
public: public:
virtual ~BaseEntity(); virtual ~BaseEntity();
virtual void parse(const QJsonObject &obj) = 0; virtual void parse(const QJsonObject &obj) = 0;
virtual QString localFilename() const = 0; virtual QString localFilename() const = 0;
virtual QUrl url() const; virtual QUrl url() const;
bool isLoaded() const; bool isLoaded() const;
bool shouldStartRemoteUpdate() const; bool shouldStartRemoteUpdate() const;
void load(Net::Mode loadType); void load(Net::Mode loadType);
shared_qobject_ptr<Task> getCurrentTask(); shared_qobject_ptr<Task> getCurrentTask();
protected: /* methods */ protected: /* methods */
bool loadLocalFile(); bool loadLocalFile();
private: private:
LoadStatus m_loadStatus = LoadStatus::NotLoaded; LoadStatus m_loadStatus = LoadStatus::NotLoaded;
UpdateStatus m_updateStatus = UpdateStatus::NotDone; UpdateStatus m_updateStatus = UpdateStatus::NotDone;
shared_qobject_ptr<Task> m_updateTask; shared_qobject_ptr<Task> m_updateTask;
}; };
} }

Some files were not shown because too many files have changed in this diff Show More