├── .clang-format ├── .git-blame-ignore-revs ├── .gitattributes ├── .github └── workflows │ ├── build.yml │ └── linting.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CMakeLists.txt ├── bootstrap.bat ├── bootstrap.ps1 ├── licenses ├── 7zip.txt ├── AntlrBuildTask.txt ├── BY-SA-v3.0.txt ├── Castle.txt ├── DXTex.txt ├── GNU-LGPL-v2.1.txt ├── GPL-v2.0.txt ├── GPL-v3.0.txt ├── LGPL-v3.0.txt ├── ValveFileVDF.txt ├── boost.txt ├── cpptoml.txt ├── fmt.txt ├── openssl.txt ├── python.txt ├── sip.txt ├── spdlog.txt ├── udis86.txt └── zlib.txt ├── mob.ini ├── readme.md ├── src ├── CMakeLists.txt ├── cmd │ ├── build.cpp │ ├── cmake_config.cpp │ ├── commands.cpp │ ├── commands.h │ ├── git.cpp │ ├── list.cpp │ ├── pch.h │ ├── pr.cpp │ ├── release.cpp │ └── tx.cpp ├── core │ ├── conf.cpp │ ├── conf.h │ ├── context.cpp │ ├── context.h │ ├── env.cpp │ ├── env.h │ ├── formatters.h │ ├── ini.cpp │ ├── ini.h │ ├── op.cpp │ ├── op.h │ ├── paths.cpp │ ├── paths.h │ ├── pch.h │ ├── pipe.cpp │ ├── pipe.h │ ├── process.cpp │ └── process.h ├── main.cpp ├── net.cpp ├── net.h ├── pch.cpp ├── pch.h ├── tasks │ ├── explorerpp.cpp │ ├── installer.cpp │ ├── licenses.cpp │ ├── modorganizer.cpp │ ├── pch.h │ ├── stylesheets.cpp │ ├── task.cpp │ ├── task.h │ ├── task_manager.cpp │ ├── task_manager.h │ ├── tasks.h │ ├── translations.cpp │ └── usvfs.cpp ├── tools │ ├── cmake.cpp │ ├── cmake.h │ ├── downloader.cpp │ ├── extractor.cpp │ ├── git.cpp │ ├── git.h │ ├── msbuild.cpp │ ├── msbuild.h │ ├── pch.h │ ├── process_runner.cpp │ ├── tools.cpp │ └── tools.h ├── utility.cpp ├── utility.h └── utility │ ├── algo.h │ ├── assert.cpp │ ├── assert.h │ ├── enum.h │ ├── fs.cpp │ ├── fs.h │ ├── io.cpp │ ├── io.h │ ├── pch.h │ ├── string.cpp │ ├── string.h │ ├── threading.cpp │ └── threading.h └── third-party ├── bin ├── 7z.dll ├── 7z.exe ├── jom.exe ├── msys-2.0.dll ├── nasm.exe ├── nuget.exe ├── patch.exe ├── tx.exe └── vswhere.exe ├── include ├── clipp.h ├── curl │ ├── curl.h │ ├── curlver.h │ ├── easy.h │ ├── mprintf.h │ ├── multi.h │ ├── stdcheaders.h │ ├── system.h │ ├── typecheck-gcc.h │ └── urlapi.h └── nlohmann │ └── json.hpp └── lib ├── libcurl-d.lib ├── libcurl.lib ├── zlib.lib └── zlibd.lib /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # We'll use defaults from the LLVM style, but with 4 columns indentation. 3 | BasedOnStyle: LLVM 4 | IndentWidth: 4 5 | --- 6 | Language: Cpp 7 | # Force pointers to the type for C++. 8 | DerivePointerAlignment: false 9 | PointerAlignment: Left 10 | AlignConsecutiveAssignments: true 11 | AllowShortFunctionsOnASingleLine: Inline 12 | AllowShortIfStatementsOnASingleLine: Never 13 | AllowShortLambdasOnASingleLine: Empty 14 | AlwaysBreakTemplateDeclarations: Yes 15 | AccessModifierOffset: -4 16 | AlignTrailingComments: true 17 | SpacesBeforeTrailingComments: 2 18 | NamespaceIndentation: All 19 | MaxEmptyLinesToKeep: 1 20 | BreakBeforeBraces: Stroustrup 21 | ColumnLimit: 88 22 | IncludeBlocks: Preserve 23 | IncludeCategories: 24 | - Regex: '^"pch.h"$' 25 | Priority: -1 26 | SortPriority: -1 27 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | d0913d07d33929d7b753a3a09a0dac70e5befbc1 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Explicitly declare text files you want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.cpp text eol=crlf 7 | *.h text eol=crlf 8 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Mob 2 | 3 | on: 4 | push: 5 | branches: master 6 | pull_request: 7 | types: [opened, synchronize, reopened] 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-2022 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Build Mob 15 | shell: pwsh 16 | run: ./bootstrap.ps1 -Verbose 17 | -------------------------------------------------------------------------------- /.github/workflows/linting.yml: -------------------------------------------------------------------------------- 1 | name: Lint Mob 2 | 3 | on: 4 | push: 5 | pull_request: 6 | types: [opened, synchronize, reopened] 7 | 8 | jobs: 9 | lint: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Check format 14 | uses: ModOrganizer2/check-formatting-action@master 15 | with: 16 | check-path: "." 17 | exclude-regex: "third-party" 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # mob-specific files 2 | /build 3 | /vs/obj 4 | /vs/.vs 5 | /vs/*.user 6 | /mob.exe 7 | /mob.log 8 | 9 | # Files built by mob 10 | /*/build 11 | /*/downloads 12 | /*/install 13 | /*/releases 14 | /*/mob.ini 15 | /*/mob.log 16 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: trailing-whitespace 6 | exclude: '^(third-party|licenses)/.*$' 7 | - id: end-of-file-fixer 8 | exclude: '^(third-party|licenses)/.*$' 9 | - id: check-merge-conflict 10 | exclude: '^(third-party|licenses)/.*$' 11 | - id: check-case-conflict 12 | exclude: '^(third-party|licenses)/.*$' 13 | - repo: https://github.com/pre-commit/mirrors-clang-format 14 | rev: v19.1.5 15 | hooks: 16 | - id: clang-format 17 | 'types_or': [c++, c] 18 | exclude: '^third-party/.*$' 19 | 20 | ci: 21 | autofix_commit_msg: "[pre-commit.ci] Auto fixes from pre-commit.com hooks." 22 | autofix_prs: true 23 | autoupdate_commit_msg: "[pre-commit.ci] Pre-commit autoupdate." 24 | autoupdate_schedule: quarterly 25 | submodules: false 26 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | project(mob) 4 | add_subdirectory(src) 5 | 6 | set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT mob) 7 | -------------------------------------------------------------------------------- /bootstrap.bat: -------------------------------------------------------------------------------- 1 | @type bootstrap.ps1 | powershell - 2 | -------------------------------------------------------------------------------- /bootstrap.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [switch] 3 | $Verbose, 4 | [ValidateSet("Debug", "RelWithDebInfo", "Release")] 5 | [string] 6 | $Config = "Release" 7 | ) 8 | 9 | $root = $PSScriptRoot 10 | if (!$root) { 11 | $root = "." 12 | } 13 | 14 | $output = if ($Verbose) { "Out-Default" } else { "Out-Null" } 15 | 16 | cmake -B $root/build -G "Visual Studio 17 2022" $root | & $output 17 | 18 | $installationPath = & $root\third-party\bin\vswhere.exe -products * -nologo -prerelease -latest -property installationPath 19 | if (! $?) { 20 | Write-Error "vswhere returned $LastExitCode" 21 | exit $LastExitCode 22 | } 23 | 24 | if (! $installationPath) { 25 | Write-Error "Empty installation path" 26 | exit 1 27 | } 28 | 29 | $opts = "" 30 | $opts += " $root\build\mob.sln" 31 | $opts += " -m" 32 | $opts += " -p:Configuration=${Config}" 33 | $opts += " -noLogo" 34 | $opts += " -p:UseMultiToolTask=true" 35 | $opts += " -p:EnforceProcessCountAcrossBuilds=true" 36 | 37 | if (!$Verbose) { 38 | $opts += " -clp:ErrorsOnly;Verbosity=minimal" 39 | } 40 | 41 | $vsDevCmd = "$installationPath\Common7\Tools\VsDevCmd.bat" 42 | if (!(Test-Path "$vsDevCmd")) { 43 | Write-Error "VdDevCmd.bat not found at $vsDevCmd" 44 | exit 1 45 | } 46 | 47 | & "${env:COMSPEC}" /c "`"$vsDevCmd`" -no_logo -arch=amd64 -host_arch=amd64 && msbuild $opts" 48 | 49 | if (! $?) { 50 | Write-Error "Build failed" 51 | exit $LastExitCode 52 | } 53 | 54 | Copy-Item "$root\build\src\${Config}\mob.exe" "$root\mob.exe" 55 | Write-Output "run ``.\mob -d prefix/path build`` to start building" 56 | -------------------------------------------------------------------------------- /licenses/7zip.txt: -------------------------------------------------------------------------------- 1 | 7-Zip source code 2 | ~~~~~~~~~~~~~~~~~ 3 | License for use and distribution 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | 7-Zip Copyright (C) 1999-2024 Igor Pavlov. 7 | 8 | The licenses for files are: 9 | 10 | - CPP/7zip/Compress/Rar* files: the "GNU LGPL" with "unRAR license restriction" 11 | - CPP/7zip/Compress/LzfseDecoder.cpp: the "BSD 3-clause License" 12 | - C/ZstdDec.c: the "BSD 3-clause License" 13 | - C/Xxh64.c: the "BSD 2-clause License" 14 | - Some files are "public domain" files, if "public domain" status is stated in source file. 15 | - the "GNU LGPL" for all other files. If there is no license information in 16 | some source file, that file is under the "GNU LGPL". 17 | 18 | The "GNU LGPL" with "unRAR license restriction" means that you must follow both 19 | "GNU LGPL" rules and "unRAR license restriction" rules. 20 | 21 | 22 | 23 | 24 | GNU LGPL information 25 | -------------------- 26 | 27 | This library is free software; you can redistribute it and/or 28 | modify it under the terms of the GNU Lesser General Public 29 | License as published by the Free Software Foundation; either 30 | version 2.1 of the License, or (at your option) any later version. 31 | 32 | This library is distributed in the hope that it will be useful, 33 | but WITHOUT ANY WARRANTY; without even the implied warranty of 34 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 35 | Lesser General Public License for more details. 36 | 37 | You should have received a copy of the GNU Lesser General Public 38 | License along with this library; if not, 39 | you can get a copy of the GNU Lesser General Public License from 40 | http://www.gnu.org/ 41 | 42 | 43 | 44 | 45 | BSD 3-clause License in 7-Zip code 46 | ---------------------------------- 47 | 48 | The "BSD 3-clause License" is used for the following code in 7z.dll 49 | 1) LZFSE data decompression. 50 | CPP/7zip/Compress/LzfseDecoder.cpp. 51 | That code was derived from the code in the "LZFSE compression library" developed by Apple Inc, 52 | that also uses the "BSD 3-clause License". 53 | 2) ZSTD data decompression. 54 | C/ZstdDec.c 55 | that code was developed using original zstd decoder code as reference code. 56 | The original zstd decoder code was developed by Facebook Inc, 57 | that also uses the "BSD 3-clause License". 58 | 59 | Copyright (c) 2015-2016, Apple Inc. All rights reserved. 60 | Copyright (c) Facebook, Inc. All rights reserved. 61 | Copyright (c) 2023-2024 Igor Pavlov. 62 | 63 | Text of the "BSD 3-clause License" 64 | ---------------------------------- 65 | 66 | Redistribution and use in source and binary forms, with or without modification, 67 | are permitted provided that the following conditions are met: 68 | 69 | 1. Redistributions of source code must retain the above copyright notice, this 70 | list of conditions and the following disclaimer. 71 | 72 | 2. Redistributions in binary form must reproduce the above copyright notice, 73 | this list of conditions and the following disclaimer in the documentation 74 | and/or other materials provided with the distribution. 75 | 76 | 3. Neither the name of the copyright holder nor the names of its contributors may 77 | be used to endorse or promote products derived from this software without 78 | specific prior written permission. 79 | 80 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 81 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 82 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 83 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 84 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 85 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 86 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 87 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 88 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 89 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 90 | 91 | --- 92 | 93 | 94 | 95 | 96 | BSD 2-clause License in 7-Zip code 97 | ---------------------------------- 98 | 99 | The "BSD 2-clause License" is used for the XXH64 code in 7-Zip. 100 | C/Xxh64.c 101 | 102 | XXH64 code in 7-Zip was derived from the original XXH64 code developed by Yann Collet. 103 | 104 | Copyright (c) 2012-2021 Yann Collet. 105 | Copyright (c) 2023-2024 Igor Pavlov. 106 | 107 | Text of the "BSD 2-clause License" 108 | ---------------------------------- 109 | 110 | Redistribution and use in source and binary forms, with or without modification, 111 | are permitted provided that the following conditions are met: 112 | 113 | 1. Redistributions of source code must retain the above copyright notice, this 114 | list of conditions and the following disclaimer. 115 | 116 | 2. Redistributions in binary form must reproduce the above copyright notice, 117 | this list of conditions and the following disclaimer in the documentation 118 | and/or other materials provided with the distribution. 119 | 120 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 121 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 122 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 123 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 124 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 125 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 126 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 127 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 128 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 129 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 130 | 131 | --- 132 | 133 | 134 | 135 | 136 | unRAR license restriction 137 | ------------------------- 138 | 139 | The decompression engine for RAR archives was developed using source 140 | code of unRAR program. 141 | All copyrights to original unRAR code are owned by Alexander Roshal. 142 | 143 | The license for original unRAR code has the following restriction: 144 | 145 | The unRAR sources cannot be used to re-create the RAR compression algorithm, 146 | which is proprietary. Distribution of modified unRAR sources in separate form 147 | or as a part of other software is permitted, provided that it is clearly 148 | stated in the documentation and source comments that the code may 149 | not be used to develop a RAR (WinRAR) compatible archiver. 150 | 151 | -- 152 | -------------------------------------------------------------------------------- /licenses/AntlrBuildTask.txt: -------------------------------------------------------------------------------- 1 | [The "BSD license"] 2 | Copyright (c) 2011 Terence Parr 3 | C# Port (c) 2011 Sam Harwell, Tunnel Vision Laboratories, LLC 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions 8 | are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 3. The name of the author may not be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /licenses/Castle.txt: -------------------------------------------------------------------------------- 1 | Copyright 2004-2014 Castle Project - http://www.castleproject.org/ 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /licenses/DXTex.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2020 Microsoft Corp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | software and associated documentation files (the "Software"), to deal in the Software 7 | without restriction, including without limitation the rights to use, copy, modify, 8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all copies 13 | or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 17 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 18 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 19 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 20 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /licenses/LGPL-v3.0.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /licenses/ValveFileVDF.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Matthias Moeller 2016 m_moeller@live.de 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /licenses/boost.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /licenses/cpptoml.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Chase Geigle 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /licenses/fmt.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | --- Optional exception to the license --- 23 | 24 | As an exception, if, as a result of your compiling your source code, portions 25 | of this Software are embedded into a machine-executable object form of such 26 | source code, you may redistribute such embedded portions in such object form 27 | without including the above copyright and permission notices. 28 | -------------------------------------------------------------------------------- /licenses/sip.txt: -------------------------------------------------------------------------------- 1 | Copyright 2024 Phil Thompson 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /licenses/spdlog.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Gabi Melman. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -- NOTE: Third party dependency used by this software -- 24 | This software depends on the fmt lib (MIT License), 25 | and users must comply to its license: https://raw.githubusercontent.com/fmtlib/fmt/master/LICENSE 26 | 27 | -------------------------------------------------------------------------------- /licenses/udis86.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2002-2012, Vivek Thampi 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /licenses/zlib.txt: -------------------------------------------------------------------------------- 1 | Copyright notice: 2 | 3 | (C) 1995-2022 Jean-loup Gailly and Mark Adler 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgment in the product documentation would be 16 | appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 3. This notice may not be removed or altered from any source distribution. 20 | 21 | Jean-loup Gailly Mark Adler 22 | jloup@gzip.org madler@alumni.caltech.edu 23 | -------------------------------------------------------------------------------- /mob.ini: -------------------------------------------------------------------------------- 1 | [global] 2 | dry = false 3 | redownload = false 4 | reextract = false 5 | reconfigure = false 6 | rebuild = false 7 | clean_task = true 8 | fetch_task = true 9 | build_task = true 10 | output_log_level = 3 11 | file_log_level = 5 12 | log_file = mob.log 13 | ignore_uncommitted = false 14 | github_key = 15 | 16 | [cmake] 17 | install_message = never 18 | host = 19 | 20 | [aliases] 21 | super = cmake_common modorganizer* githubpp 22 | plugins = check_fnis bsapacker bsa_extractor diagnose_basic installer_* plugin_python preview_base preview_bsa tool_* game_* 23 | 24 | [task] 25 | enabled = true 26 | mo_org = ModOrganizer2 27 | mo_branch = master 28 | mo_fallback = 29 | no_pull = false 30 | ignore_ts = false 31 | revert_ts = false 32 | configuration = RelWithDebInfo 33 | 34 | git_url_prefix = https://github.com/ 35 | git_shallow = true 36 | git_username = 37 | git_email = 38 | 39 | set_origin_remote = false 40 | remote_org = 41 | remote_key = 42 | remote_no_push_upstream = false 43 | remote_push_default_origin = false 44 | 45 | [super:task] 46 | git_shallow = false 47 | 48 | [usvfs:task] 49 | git_shallow = false 50 | configuration = Release 51 | 52 | [installer:task] 53 | enabled = false 54 | git_shallow = false 55 | 56 | [translations:task] 57 | enabled = false 58 | 59 | [tools] 60 | sevenz = 7z.exe 61 | jom = jom.exe 62 | patch = patch.exe 63 | git = git.exe 64 | cmake = cmake.exe 65 | perl = perl.exe 66 | devenv = devenv.exe 67 | msbuild = msbuild.exe 68 | nmake = nmake.exe 69 | nuget = nuget.exe 70 | vswhere = vswhere.exe 71 | nasm = nasm.exe 72 | tx = tx.exe 73 | lrelease = lrelease.exe 74 | iscc = ISCC.exe 75 | vcvars = 76 | 77 | [transifex] 78 | enabled = true 79 | key = 80 | team = mod-organizer-2-team 81 | project = mod-organizer-2 82 | url = https://app.transifex.com 83 | minimum = 60 84 | force = false 85 | configure = true 86 | pull = true 87 | 88 | [versions] 89 | vs = 17 90 | vs_year = 2022 91 | vs_toolset = 14.3 92 | sdk = 10.0.26100.0 93 | pyqt = 6.7.1 94 | qt = 6.7.3 95 | qt_vs = 2022 96 | usvfs = master 97 | explorerpp = 1.4.0 98 | ss_paper_lad_6788 = 7.2 99 | ss_paper_automata_6788 = 3.2 100 | ss_paper_mono_6788 = 3.2 101 | ss_dark_mode_1809_6788 = 3.0 102 | ss_morrowind_trosski = 1.1 103 | ss_skyrim_trosski = v1.1 104 | ss_starfield_trosski = V1.11 105 | ss_fallout3_trosski = v1.11 106 | ss_fallout4_trosski = v1.11 107 | 108 | [paths] 109 | third_party = 110 | prefix = 111 | cache = 112 | licenses = 113 | build = 114 | install = 115 | install_bin = 116 | install_installer = 117 | install_libs = 118 | install_pdbs = 119 | install_dlls = 120 | install_plugins = 121 | install_stylesheets = 122 | install_licenses = 123 | install_translations = 124 | vs = 125 | vcpkg = 126 | qt_install = 127 | qt_bin = 128 | qt_translations = 129 | pf_x86 = 130 | pf_x64 = 131 | temp_dir = 132 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | file(GLOB_RECURSE source_files *.cpp) 4 | file(GLOB_RECURSE header_files *.h) 5 | 6 | add_executable(mob ${source_files} ${header_files}) 7 | set_target_properties(mob PROPERTIES CXX_STANDARD 20) 8 | 9 | target_compile_definitions(mob PUBLIC NOMINMAX) 10 | target_compile_options(mob PUBLIC "/MT") 11 | target_include_directories(mob PUBLIC ${CMAKE_SOURCE_DIR}/third-party/include) 12 | target_link_libraries(mob PUBLIC 13 | wsock32 ws2_32 crypt32 wldap32 dbghelp shlwapi version 14 | optimized ${CMAKE_SOURCE_DIR}/third-party/lib/libcurl.lib 15 | debug ${CMAKE_SOURCE_DIR}/third-party/lib/libcurl-d.lib 16 | optimized ${CMAKE_SOURCE_DIR}/third-party/lib/zlib.lib 17 | debug ${CMAKE_SOURCE_DIR}/third-party/lib/zlibd.lib) 18 | 19 | source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} 20 | PREFIX src 21 | FILES ${source_files} ${header_files}) 22 | set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT mob) 23 | -------------------------------------------------------------------------------- /src/cmd/build.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "../core/conf.h" 3 | #include "../core/context.h" 4 | #include "../core/ini.h" 5 | #include "../core/op.h" 6 | #include "../tasks/task_manager.h" 7 | #include "commands.h" 8 | 9 | namespace mob { 10 | 11 | build_command::build_command() : command(requires_options | handle_sigint) {} 12 | 13 | command::meta_t build_command::meta() const 14 | { 15 | return {"build", "builds tasks"}; 16 | } 17 | 18 | clipp::group build_command::do_group() 19 | { 20 | return (clipp::command("build")).set(picked_), 21 | 22 | (clipp::option("-h", "--help") >> help_) % ("shows this message"), 23 | 24 | (clipp::option("-g", "--redownload") >> redownload_) % 25 | "redownloads archives, see --reextract", 26 | 27 | (clipp::option("-e", "--reextract") >> reextract_) % 28 | "deletes source directories and re-extracts archives", 29 | 30 | (clipp::option("-c", "--reconfigure") >> reconfigure_) % 31 | "reconfigures the task by running cmake, configure scripts, " 32 | "etc.; some tasks might have to delete the whole source " 33 | "directory", 34 | 35 | (clipp::option("-b", "--rebuild") >> rebuild_) % 36 | "cleans and rebuilds projects; some tasks might have to " 37 | "delete the whole source directory", 38 | 39 | (clipp::option("-n", "--new") >> new_) % 40 | "deletes everything and starts from scratch", 41 | 42 | (clipp::option("--clean-task").call([&] { 43 | clean_ = true; 44 | }) | 45 | clipp::option("--no-clean-task").call([&] { 46 | clean_ = false; 47 | })) % 48 | "sets whether tasks are cleaned", 49 | 50 | (clipp::option("--fetch-task").call([&] { 51 | fetch_ = true; 52 | }) | 53 | clipp::option("--no-fetch-task").call([&] { 54 | fetch_ = false; 55 | })) % 56 | "sets whether tasks are fetched", 57 | 58 | (clipp::option("--build-task").call([&] { 59 | build_ = true; 60 | }) | 61 | clipp::option("--no-build-task").call([&] { 62 | build_ = false; 63 | })) % 64 | "sets whether tasks are built", 65 | 66 | (clipp::option("--pull").call([&] { 67 | nopull_ = false; 68 | }) | 69 | clipp::option("--no-pull").call([&] { 70 | nopull_ = true; 71 | })) % 72 | "whether to pull repos that are already cloned; global override", 73 | 74 | (clipp::option("--revert-ts").call([&] { 75 | revert_ts_ = true; 76 | }) | 77 | clipp::option("--no-revert-ts").call([&] { 78 | revert_ts_ = false; 79 | })) % 80 | "whether to revert all the .ts files in a repo before pulling to " 81 | "avoid merge errors; global override", 82 | 83 | (clipp::option("--ignore-uncommitted-changes") >> ignore_uncommitted_) % 84 | "when --reextract is given, directories controlled by git will " 85 | "be deleted even if they contain uncommitted changes", 86 | 87 | (clipp::option("--keep-msbuild") >> keep_msbuild_) % 88 | "don't terminate msbuild.exe instances after building", 89 | 90 | (clipp::opt_values(clipp::match::prefix_not("-"), "task", tasks_)) % 91 | "tasks to run; specify 'super' to only build modorganizer " 92 | "projects"; 93 | } 94 | 95 | void build_command::convert_cl_to_conf() 96 | { 97 | command::convert_cl_to_conf(); 98 | 99 | if (redownload_ || new_) 100 | common.options.push_back("global/redownload=true"); 101 | 102 | if (reextract_ || new_) 103 | common.options.push_back("global/reextract=true"); 104 | 105 | if (reconfigure_ || new_) 106 | common.options.push_back("global/reconfigure=true"); 107 | 108 | if (rebuild_ || new_) 109 | common.options.push_back("global/rebuild=true"); 110 | 111 | if (ignore_uncommitted_) 112 | common.options.push_back("global/ignore_uncommitted=true"); 113 | 114 | if (clean_) { 115 | if (*clean_) 116 | common.options.push_back("global/clean_task=true"); 117 | else 118 | common.options.push_back("global/clean_task=false"); 119 | } 120 | 121 | if (fetch_) { 122 | if (*fetch_) 123 | common.options.push_back("global/fetch_task=true"); 124 | else 125 | common.options.push_back("global/fetch_task=false"); 126 | } 127 | 128 | if (build_) { 129 | if (*build_) 130 | common.options.push_back("global/build_task=true"); 131 | else 132 | common.options.push_back("global/build_task=false"); 133 | } 134 | 135 | if (nopull_) { 136 | if (*nopull_) 137 | common.options.push_back("_override:task/no_pull=true"); 138 | else 139 | common.options.push_back("_override:task/no_pull=false"); 140 | } 141 | 142 | if (revert_ts_) { 143 | if (*revert_ts_) 144 | common.options.push_back("_override:task/revert_ts=true"); 145 | else 146 | common.options.push_back("_override:task/revert_ts=false"); 147 | } 148 | 149 | if (!tasks_.empty()) 150 | set_task_enabled_flags(tasks_); 151 | } 152 | 153 | int build_command::do_run() 154 | { 155 | try { 156 | create_prefix_ini(); 157 | 158 | task_manager::instance().run_all(); 159 | 160 | if (!keep_msbuild_) 161 | terminate_msbuild(); 162 | 163 | mob::gcx().info(mob::context::generic, "mob done"); 164 | return 0; 165 | } 166 | catch (bailed&) { 167 | gcx().error(context::generic, "bailing out"); 168 | return 1; 169 | } 170 | } 171 | 172 | void build_command::create_prefix_ini() 173 | { 174 | const auto prefix = conf().path().prefix(); 175 | 176 | // creating prefix 177 | if (!exists(prefix)) 178 | op::create_directories(gcx(), prefix); 179 | 180 | const auto ini = prefix / default_ini_filename(); 181 | if (!exists(ini)) { 182 | std::ofstream(ini) << "[paths]\n" 183 | << "prefix = .\n"; 184 | } 185 | } 186 | 187 | void build_command::terminate_msbuild() 188 | { 189 | if (conf().global().dry()) 190 | return; 191 | 192 | system("taskkill /im msbuild.exe /f > NUL 2>&1"); 193 | } 194 | 195 | } // namespace mob 196 | -------------------------------------------------------------------------------- /src/cmd/cmake_config.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "../core/conf.h" 4 | #include "../tasks/tasks.h" 5 | 6 | #include "commands.h" 7 | 8 | namespace mob { 9 | 10 | cmake_config_command::cmake_config_command() : command(requires_options) {} 11 | 12 | command::meta_t cmake_config_command::meta() const 13 | { 14 | return {"cmake-config", "print CMake configuration variables"}; 15 | } 16 | 17 | clipp::group cmake_config_command::do_group() 18 | { 19 | return clipp::group( 20 | clipp::command("cmake-config").set(picked_), 21 | (clipp::option("-h", "--help") >> help_) % ("shows this message"), 22 | clipp::command("prefix-path").set(var_, variable::prefix_path) | 23 | clipp::command("install-prefix").set(var_, variable::install_prefix)); 24 | } 25 | 26 | std::string cmake_config_command::do_doc() 27 | { 28 | return "Print CMake variables to be used when configuring projects.\n"; 29 | } 30 | 31 | int cmake_config_command::do_run() 32 | { 33 | switch (var_) { 34 | case variable::prefix_path: 35 | u8cout << tasks::modorganizer::cmake_prefix_path(); 36 | break; 37 | case variable::install_prefix: 38 | u8cout << conf().path().install().string(); 39 | break; 40 | } 41 | return 0; 42 | } 43 | } // namespace mob 44 | -------------------------------------------------------------------------------- /src/cmd/git.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "../tasks/tasks.h" 3 | #include "../tools/tools.h" 4 | #include "commands.h" 5 | 6 | namespace mob { 7 | 8 | git_command::git_command() : command(requires_options) {} 9 | 10 | command::meta_t git_command::meta() const 11 | { 12 | return {"git", "manages the git repos"}; 13 | } 14 | 15 | clipp::group git_command::do_group() 16 | { 17 | return clipp::group( 18 | clipp::command("git").set(picked_), 19 | 20 | (clipp::option("-h", "--help") >> help_) % ("shows this message"), 21 | 22 | "set-remotes" % 23 | (clipp::command("set-remotes").set(mode_, modes::set_remotes), 24 | (clipp::required("-u", "--username") & 25 | clipp::value("USERNAME") >> username_) % 26 | "git username", 27 | 28 | (clipp::required("-e", "--email") & 29 | clipp::value("EMAIL") >> email_) % 30 | "git email", 31 | 32 | (clipp::option("-k", "--key") & clipp::value("PATH") >> key_) % 33 | "path to putty key", 34 | 35 | (clipp::option("-s", "--no-push").set(nopush_) % 36 | "disables pushing to 'upstream' by changing the push url " 37 | "to 'nopushurl' to avoid accidental pushes"), 38 | 39 | (clipp::option("-p", "--push-origin").set(push_default_) % 40 | "sets the new 'origin' remote as the default push target"), 41 | 42 | (clipp::opt_value("path") >> path_) % "only use this repo") 43 | 44 | | 45 | 46 | "add-remote" % 47 | (clipp::command("add-remote").set(mode_, modes::add_remote), 48 | (clipp::required("-n", "--name") & 49 | clipp::value("NAME") >> remote_) % 50 | "name of new remote", 51 | 52 | (clipp::required("-u", "--username") & 53 | clipp::value("USERNAME") >> username_) % 54 | "git username", 55 | 56 | (clipp::option("-k", "--key") & clipp::value("PATH") >> key_) % 57 | "path to putty key", 58 | 59 | (clipp::option("-p", "--push-origin").set(push_default_) % 60 | "sets this new remote as the default push target"), 61 | 62 | (clipp::opt_value("path") >> path_) % "only use this repo") 63 | 64 | | 65 | 66 | "ignore-ts" % (clipp::command("ignore-ts").set(mode_, modes::ignore_ts), 67 | (clipp::command("on").set(tson_, true) | 68 | clipp::command("off").set(tson_, false))) 69 | 70 | | 71 | 72 | "branches" % (clipp::command("branches").set(mode_, modes::branches), 73 | clipp::option("-a", "--all").set(all_branches_) % 74 | "shows all branches, including those on master")); 75 | } 76 | 77 | int git_command::do_run() 78 | { 79 | switch (mode_) { 80 | case modes::set_remotes: { 81 | do_set_remotes(); 82 | break; 83 | } 84 | 85 | case modes::add_remote: { 86 | do_add_remote(); 87 | break; 88 | } 89 | 90 | case modes::ignore_ts: { 91 | do_ignore_ts(); 92 | break; 93 | } 94 | 95 | case modes::branches: { 96 | do_branches(); 97 | break; 98 | } 99 | 100 | case modes::none: 101 | default: 102 | u8cerr << "bad git mode " << static_cast(mode_) << "\n"; 103 | throw bailed(); 104 | } 105 | 106 | return 0; 107 | } 108 | 109 | std::string git_command::do_doc() 110 | { 111 | return "All the commands will go through all modorganizer repos, plus usvfs\n" 112 | "and NCC.\n" 113 | "\n" 114 | "Commands:\n" 115 | "set-remotes\n" 116 | " For each repo, this first sets the username and email. Then, it\n" 117 | " will rename the remote 'origin' to 'upstream' and create a new\n" 118 | " remote 'origin' with the given information. If the remote\n" 119 | " 'upstream' already exists in a repo, nothing happens.\n" 120 | "\n" 121 | "add-remote\n" 122 | " For each repo, adds a new remote with the given information. If a\n" 123 | " remote with the same name already exists, nothing happens.\n" 124 | "\n" 125 | "ignore-ts\n" 126 | " Toggles the --assume-changed status of all .ts files in all repos.\n" 127 | "\n" 128 | "branches\n" 129 | " Lists all git repos that are not on master. With -a, show all \n" 130 | " repos and their current branch."; 131 | } 132 | 133 | void git_command::do_set_remotes() 134 | { 135 | if (path_.empty()) { 136 | const auto repos = get_repos(); 137 | 138 | for (auto&& r : repos) 139 | do_set_remotes(r); 140 | } 141 | else { 142 | do_set_remotes(path_); 143 | } 144 | } 145 | 146 | void git_command::do_set_remotes(const fs::path& r) 147 | { 148 | u8cout << "setting up " << path_to_utf8(r.filename()) << "\n"; 149 | 150 | git_wrap(r).set_credentials(username_, email_); 151 | 152 | git_wrap(r).set_origin_and_upstream_remotes(username_, key_, nopush_, 153 | push_default_); 154 | } 155 | 156 | void git_command::do_add_remote() 157 | { 158 | u8cout << "adding remote '" << remote_ << "' " 159 | << "from '" << username_ << "' to repos\n"; 160 | 161 | if (path_.empty()) { 162 | const auto repos = get_repos(); 163 | 164 | for (auto&& r : repos) 165 | do_add_remote(r); 166 | } 167 | else { 168 | do_add_remote(path_); 169 | } 170 | } 171 | 172 | void git_command::do_add_remote(const fs::path& r) 173 | { 174 | u8cout << path_to_utf8(r.filename()) << "\n"; 175 | git_wrap(r).add_remote(remote_, username_, key_, push_default_); 176 | } 177 | 178 | void git_command::do_ignore_ts() 179 | { 180 | if (tson_) 181 | u8cout << "ignoring .ts files\n"; 182 | else 183 | u8cout << "un-ignoring .ts files\n"; 184 | 185 | if (path_.empty()) { 186 | const auto repos = get_repos(); 187 | 188 | for (auto&& r : repos) 189 | do_ignore_ts(r); 190 | } 191 | else { 192 | do_ignore_ts(path_); 193 | } 194 | } 195 | 196 | void git_command::do_ignore_ts(const fs::path& r) 197 | { 198 | u8cout << path_to_utf8(r.filename()) << "\n"; 199 | git_wrap(r).ignore_ts(tson_); 200 | } 201 | 202 | void git_command::do_branches() 203 | { 204 | std::vector> v; 205 | 206 | for (auto&& r : get_repos()) { 207 | const auto b = git_wrap(r).current_branch(); 208 | if (b == "master" && !all_branches_) 209 | continue; 210 | 211 | if (b.empty()) 212 | v.push_back({r.filename().string(), "detached head"}); 213 | else 214 | v.push_back({r.filename().string(), b}); 215 | } 216 | 217 | u8cout << table(v, 0, 3) << "\n"; 218 | } 219 | 220 | std::vector git_command::get_repos() const 221 | { 222 | std::vector v; 223 | 224 | // usvfs 225 | if (fs::exists(tasks::usvfs::source_path())) 226 | v.push_back(tasks::usvfs::source_path()); 227 | 228 | const auto super = tasks::modorganizer::super_path(); 229 | 230 | // all directories in super except for those starting with a dot 231 | if (fs::exists(super)) { 232 | for (auto e : fs::directory_iterator(super)) { 233 | if (!e.is_directory()) 234 | continue; 235 | 236 | const auto p = e.path(); 237 | if (path_to_utf8(p.filename()).starts_with(".")) 238 | continue; 239 | 240 | v.push_back(p); 241 | } 242 | } 243 | 244 | return v; 245 | } 246 | 247 | } // namespace mob 248 | -------------------------------------------------------------------------------- /src/cmd/list.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "../tasks/task.h" 3 | #include "../tasks/task_manager.h" 4 | #include "../utility/io.h" 5 | #include "commands.h" 6 | 7 | namespace mob { 8 | 9 | command::meta_t list_command::meta() const 10 | { 11 | return {"list", "lists available tasks"}; 12 | } 13 | 14 | clipp::group list_command::do_group() 15 | { 16 | return clipp::group( 17 | clipp::command("list").set(picked_), 18 | 19 | (clipp::option("-h", "--help") >> help_) % "shows this message", 20 | 21 | (clipp::option("-a", "--all") >> all_) % 22 | "shows all the tasks, including pseudo parallel tasks", 23 | 24 | (clipp::option("-i", "--aliases") >> aliases_) % "shows only aliases", 25 | 26 | (clipp::opt_values(clipp::match::prefix_not("-"), "task", tasks_)) % 27 | "with -a; when given, acts like the tasks given to `build` and " 28 | "shows only the tasks that would run"); 29 | } 30 | 31 | int list_command::do_run() 32 | { 33 | auto& tm = task_manager::instance(); 34 | 35 | if (aliases_) { 36 | load_options(); 37 | dump_aliases(); 38 | } 39 | else { 40 | if (all_) { 41 | if (!tasks_.empty()) 42 | set_task_enabled_flags(tasks_); 43 | 44 | load_options(); 45 | dump(tm.top_level(), 0); 46 | 47 | u8cout << "\n\naliases:\n"; 48 | dump_aliases(); 49 | } 50 | else { 51 | for (auto&& t : tm.all()) 52 | u8cout << " - " << join(t->names(), ", ") << "\n"; 53 | } 54 | } 55 | 56 | return 0; 57 | } 58 | 59 | void list_command::dump(const std::vector& v, std::size_t indent) const 60 | { 61 | for (auto&& t : v) { 62 | if (!t->enabled()) 63 | continue; 64 | 65 | u8cout << std::string(indent * 4, ' ') << " - " << join(t->names(), ",") 66 | << "\n"; 67 | 68 | if (auto* pt = dynamic_cast(t)) 69 | dump(pt->children(), indent + 1); 70 | } 71 | } 72 | 73 | void list_command::dump_aliases() const 74 | { 75 | const auto v = task_manager::instance().aliases(); 76 | if (v.empty()) 77 | return; 78 | 79 | for (auto&& [k, patterns] : v) 80 | u8cout << " - " << k << ": " << join(patterns, ", ") << "\n"; 81 | } 82 | 83 | } // namespace mob 84 | -------------------------------------------------------------------------------- /src/cmd/pch.h: -------------------------------------------------------------------------------- 1 | // for intellisense 2 | #include "../pch.h" 3 | -------------------------------------------------------------------------------- /src/cmd/tx.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "../core/conf.h" 3 | #include "../core/context.h" 4 | #include "../core/env.h" 5 | #include "../net.h" 6 | #include "../tasks/tasks.h" 7 | #include "../utility.h" 8 | #include "commands.h" 9 | 10 | namespace mob { 11 | 12 | tx_command::tx_command() : command(requires_options) {} 13 | 14 | command::meta_t tx_command::meta() const 15 | { 16 | return {"tx", "manages transifex translations"}; 17 | } 18 | 19 | clipp::group tx_command::do_group() 20 | { 21 | return clipp::group( 22 | clipp::command("tx").set(picked_), 23 | 24 | (clipp::option("-h", "--help") >> help_) % ("shows this message"), 25 | 26 | "get" % (clipp::command("get").set(mode_, modes::get), 27 | (clipp::option("-k", "--key") & clipp::value("APIKEY") >> key_) % 28 | "API key", 29 | 30 | (clipp::option("-t", "--team") & clipp::value("TEAM") >> team_) % 31 | "team name", 32 | 33 | (clipp::option("-p", "--project") & 34 | clipp::value("PROJECT") >> project_) % 35 | "project name", 36 | 37 | (clipp::option("-u", "--url") & clipp::value("URL") >> url_) % 38 | "project URL", 39 | 40 | (clipp::option("-m", "--minimum") & 41 | clipp::value("PERCENT").set(min_)) % 42 | "minimum translation threshold to download [0-100]", 43 | 44 | (clipp::option("-f", "--force").call([&] { 45 | force_ = true; 46 | })) % 47 | "don't check timestamps, re-download all translation files", 48 | 49 | (clipp::value("path") >> path_) % 50 | "path that will contain the .tx directory") 51 | 52 | | 53 | 54 | "build" % (clipp::command("build").set(mode_, modes::build), 55 | 56 | (clipp::value("source") >> path_) % 57 | "path that contains the translation directories", 58 | 59 | (clipp::value("destination") >> dest_) % 60 | "path that will contain the .qm files")); 61 | } 62 | 63 | void tx_command::convert_cl_to_conf() 64 | { 65 | command::convert_cl_to_conf(); 66 | 67 | if (!key_.empty()) 68 | common.options.push_back("transifex/key=" + key_); 69 | 70 | if (!team_.empty()) 71 | common.options.push_back("transifex/team=" + team_); 72 | 73 | if (!project_.empty()) 74 | common.options.push_back("transifex/project=" + project_); 75 | 76 | if (!url_.empty()) 77 | common.options.push_back("transifex/url=" + url_); 78 | 79 | if (min_ >= 0) 80 | common.options.push_back("transifex/minimum=" + std::to_string(min_)); 81 | 82 | if (force_) 83 | common.options.push_back("transifex/force=" + std::to_string(*force_)); 84 | } 85 | 86 | int tx_command::do_run() 87 | { 88 | switch (mode_) { 89 | case modes::get: 90 | do_get(); 91 | break; 92 | 93 | case modes::build: 94 | do_build(); 95 | break; 96 | 97 | case modes::none: 98 | default: 99 | u8cerr << "bad tx mode " << static_cast(mode_) << "\n"; 100 | throw bailed(); 101 | } 102 | 103 | return 0; 104 | } 105 | 106 | std::string tx_command::do_doc() 107 | { 108 | return "Some values will be taken from the INI file if not specified.\n" 109 | "\n" 110 | "Commands:\n" 111 | "get\n" 112 | " Initializes a Transifex project in the given directory if\n" 113 | " necessary and pulls all the translation files.\n" 114 | "\n" 115 | "build\n" 116 | " Builds all .qm files. The path can either be the transifex\n" 117 | " project (where .tx is) or the `translations` directory (where the\n" 118 | " individual translation directories are)."; 119 | } 120 | 121 | void tx_command::do_get() 122 | { 123 | const url u = conf().transifex().get("url") + "/" + 124 | conf().transifex().get("team") + "/" + 125 | conf().transifex().get("project"); 126 | 127 | const std::string key = conf().transifex().get("key"); 128 | 129 | if (key.empty() && !this_env::get_opt("TX_TOKEN")) { 130 | u8cout << "(no key was in the INI, --key wasn't given and TX_TOKEN env\n" 131 | "variable doesn't exist, this will probably fail)\n\n"; 132 | } 133 | 134 | // copy the global context, the tools will modify it 135 | context cxcopy = gcx(); 136 | 137 | u8cout << "initializing\n"; 138 | transifex(transifex::init).root(path_).run(cxcopy); 139 | 140 | u8cout << "configuring\n"; 141 | transifex(transifex::config) 142 | .stdout_level(context::level::info) 143 | .root(path_) 144 | .api_key(key) 145 | .url(u) 146 | .run(cxcopy); 147 | 148 | u8cout << "pulling\n"; 149 | transifex(transifex::pull) 150 | .stdout_level(context::level::info) 151 | .root(path_) 152 | .api_key(key) 153 | .minimum(conf().transifex().get("minimum")) 154 | .force(conf().transifex().get("force")) 155 | .run(cxcopy); 156 | } 157 | 158 | void tx_command::do_build() 159 | { 160 | fs::path root = path_; 161 | if (fs::exists(root / ".tx") && fs::exists(root / "translations")) 162 | root = root / "translations"; 163 | 164 | tasks::translations::projects ps(root); 165 | 166 | fs::path dest = dest_; 167 | op::create_directories(gcx(), dest, op::unsafe); 168 | 169 | for (auto&& w : ps.warnings()) 170 | u8cerr << w << "\n"; 171 | 172 | thread_pool tp; 173 | 174 | for (auto& p : ps.get()) { 175 | for (auto& lg : p.langs) { 176 | // copy the global context, each thread must have its own 177 | tp.add([&, cxcopy = gcx()]() mutable { 178 | lrelease() 179 | .project(p.name) 180 | .sources(lg.ts_files) 181 | .out(dest) 182 | .run(cxcopy); 183 | }); 184 | } 185 | } 186 | } 187 | 188 | } // namespace mob 189 | -------------------------------------------------------------------------------- /src/core/env.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../utility.h" 4 | 5 | namespace mob { 6 | 7 | // a set of environment variables; copy-on-write because this gets copied a lot 8 | // 9 | class env { 10 | public: 11 | using map = std::map; 12 | 13 | // used in set(); replaces, appends or prepends to a variable if it already 14 | // exists 15 | // 16 | enum flags { replace = 1, append, prepend }; 17 | 18 | // Visual Studio environment variables for 32-bit 19 | // 20 | static env vs_x86(); 21 | 22 | // Visual Studio environment variables for 64-bit 23 | // 24 | static env vs_x64(); 25 | 26 | // Visual Studio environment variables for the given architecture 27 | // 28 | static env vs(arch a); 29 | 30 | // empty set 31 | // 32 | env(); 33 | 34 | // handle ref count 35 | // 36 | env(const env& e); 37 | env(env&& e); 38 | env& operator=(const env& e); 39 | env& operator=(env&& e); 40 | 41 | // prepends to PATH 42 | // 43 | env& prepend_path(const fs::path& p); 44 | env& prepend_path(const std::vector& v); 45 | 46 | // appends to PATH 47 | // 48 | env& append_path(const fs::path& p); 49 | env& append_path(const std::vector& v); 50 | 51 | // sets k=v 52 | // 53 | env& set(std::string_view k, std::string_view v, flags f = replace); 54 | env& set(std::wstring k, std::wstring v, flags f = replace); 55 | 56 | // returns the variable' value, empty if not found 57 | // 58 | std::string get(std::string_view k) const; 59 | 60 | // map of variables 61 | // 62 | map get_map() const; 63 | 64 | // passed to CreateProcess() in the process class; returns a pointer to a 65 | // block of utf16 strings, owned by this, created on demand 66 | // 67 | void* get_unicode_pointers() const; 68 | 69 | private: 70 | // shared between copies 71 | // 72 | struct data { 73 | std::mutex m; 74 | map vars; 75 | 76 | // unicode strings, see get_unicode_pointers() 77 | mutable std::wstring sys; 78 | }; 79 | 80 | // shared data 81 | std::shared_ptr data_; 82 | 83 | // whether this instance owns the data, set to true in copy_for_write() 84 | // when the data must be modified 85 | bool own_; 86 | 87 | // creates the unicode strings 88 | // 89 | void create_sys() const; 90 | 91 | // returns a pointer inside the map, null if not found 92 | // 93 | std::wstring* find(std::wstring_view name); 94 | const std::wstring* find(std::wstring_view name) const; 95 | 96 | // called by set(), sets the value in the map 97 | // 98 | void set_impl(std::wstring k, std::wstring v, flags f); 99 | 100 | // duplicates the data, sets own_=true 101 | // 102 | void copy_for_write(); 103 | 104 | // called by the various *_path() functions, actually changes the PATH 105 | // value 106 | // 107 | env& change_path(const std::vector& v, flags f); 108 | }; 109 | 110 | // represents mob's environment variables 111 | // 112 | struct this_env { 113 | // sets a variable 114 | // 115 | static void set(const std::string& k, const std::string& v, 116 | env::flags f = env::replace); 117 | 118 | // changes PATH 119 | // 120 | static void prepend_to_path(const fs::path& p); 121 | static void append_to_path(const fs::path& p); 122 | 123 | // returns mob's environment variables 124 | // 125 | static env get(); 126 | 127 | // returns a specific variable; bails out if it doesn't exist 128 | // 129 | static std::string get(const std::string& k); 130 | 131 | // returns a specific variable, or empty if it doesn't exist 132 | // 133 | static std::optional get_opt(const std::string& k); 134 | 135 | private: 136 | // used by get() and get_opt(), does the actual work 137 | // 138 | static std::optional get_impl(const std::string& k); 139 | }; 140 | 141 | } // namespace mob 142 | -------------------------------------------------------------------------------- /src/core/formatters.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../utility/string.h" 4 | 5 | template <> 6 | struct std::formatter : std::formatter { 7 | template 8 | FmtContext::iterator format(std::wstring const& s, FmtContext& ctx) const 9 | { 10 | return std::formatter::format(mob::utf16_to_utf8(s), ctx); 11 | } 12 | }; 13 | 14 | template <> 15 | struct std::formatter 16 | : std::formatter, char> { 17 | template 18 | FmtContext::iterator format(std::filesystem::path const& s, FmtContext& ctx) const 19 | { 20 | return std::formatter, 21 | char>::format(s.native(), ctx); 22 | } 23 | }; 24 | 25 | template 26 | requires std::is_enum_v 27 | struct std::formatter 28 | : std::formatter, CharT> { 29 | template 30 | FmtContext::iterator format(Enum v, FmtContext& ctx) const 31 | { 32 | return std::formatter, CharT>::format( 33 | static_cast>(v), ctx); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/core/ini.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mob { 4 | 5 | std::string default_ini_filename(); 6 | 7 | std::vector 8 | find_inis(bool auto_detect, const std::vector& from_cl, bool verbose); 9 | 10 | struct ini_data { 11 | using alias_patterns = std::vector; 12 | using aliases_map = std::map; 13 | 14 | using kv_map = std::map; 15 | using sections_vector = std::vector>; 16 | 17 | fs::path path; 18 | aliases_map aliases; 19 | sections_vector sections; 20 | 21 | kv_map& get_section(std::string_view name); 22 | void set(std::string_view section, std::string key, std::string value); 23 | }; 24 | 25 | ini_data parse_ini(const fs::path& ini); 26 | std::string default_ini_filename(); 27 | 28 | } // namespace mob 29 | -------------------------------------------------------------------------------- /src/core/op.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../utility.h" 4 | 5 | namespace mob { 6 | class context; 7 | } 8 | 9 | namespace mob::op { 10 | 11 | // filesystem operations, also handle --dry 12 | // 13 | // for functions that end with _if_better(): the source is considered better 14 | // than the destination if: 15 | // 1) the destination doesn't exist, or 16 | // 2) the size is different, or 17 | // 3) the date is newer 18 | 19 | // various flags for the operations below, only some of them are used by some 20 | // functions 21 | // 22 | enum flags { 23 | noflags = 0x00, 24 | 25 | // the operation is optional, don't bail out if it fails 26 | optional = 0x01, 27 | 28 | // used by copy_glob_to_dir_if_better() to decide if files and/or 29 | // directories are copied 30 | copy_files = 0x02, 31 | copy_dirs = 0x04, 32 | 33 | // operations will typically fail early if paths are empty or if they're not 34 | // inside a list of approved locations, like the prefix, %TEMP%, etc. 35 | // 36 | // this is to prevent mob from going on a deletion spree in case of bugs 37 | unsafe = 0x08 38 | }; 39 | 40 | MOB_ENUM_OPERATORS(flags); 41 | 42 | // creates the given file if it doesn't exist 43 | // 44 | void touch(const context& cx, const fs::path& p, flags f = noflags); 45 | 46 | // creates all the directories in the given path 47 | // 48 | void create_directories(const context& cx, const fs::path& p, flags f = noflags); 49 | 50 | // deletes the given directory, recursive 51 | // 52 | // if deletion fails because of access denied, attempts to remove the readonly 53 | // flag on all files and tries again; this happens with some archives like 7z 54 | // 55 | // if the directory is controlled by git, prefer git_wrap::delete_directory(), 56 | // which checks for uncommitted changes before 57 | // 58 | void delete_directory(const context& cx, const fs::path& p, flags f = noflags); 59 | 60 | // deletes the given file 61 | // 62 | void delete_file(const context& cx, const fs::path& p, flags f = noflags); 63 | 64 | // deletes all files matching the glob in the glob's parent directory 65 | // 66 | void delete_file_glob(const context& cx, const fs::path& glob, flags f = noflags); 67 | 68 | // removes the readonly flag for all files in `dir`, recursive 69 | // 70 | void remove_readonly(const context& cx, const fs::path& dir, flags f = noflags); 71 | 72 | // renames `src` to `dest`, files or directories; fails if it already exists 73 | // 74 | void rename(const context& cx, const fs::path& src, const fs::path& dest, 75 | flags f = noflags); 76 | 77 | // moves a file or directory `src` into dir `dest_dir`, using the same name 78 | // (renames src to dest_dir/src.filename()); fails if it already exists 79 | // 80 | void move_to_directory(const context& cx, const fs::path& src, 81 | const fs::path& dest_dir, flags f = noflags); 82 | 83 | // copies a single file `file` into `dest_dir`; if the file already exists, only 84 | // copies it if it's considered better (see comment on top); doesn't support 85 | // globs or directories 86 | // 87 | void copy_file_to_dir_if_better(const context& cx, const fs::path& file, 88 | const fs::path& dest_dir, flags f = noflags); 89 | 90 | // same as copy_file_to_dir_if_better(), but the `dest_file` contains the 91 | // target filename instead of being constructed from dest_dir/src.filename() 92 | // 93 | void copy_file_to_file_if_better(const context& cx, const fs::path& src_file, 94 | const fs::path& dest_file, flags f = noflags); 95 | 96 | // basically calls copy_file_to_dir_if_better() for every file matching the 97 | // glob; recursive 98 | // 99 | void copy_glob_to_dir_if_better(const context& cx, const fs::path& src_glob, 100 | const fs::path& dest_dir, flags f); 101 | 102 | // renames `dest` to `src`, deleting `src` if it exists; if `backup` is given, 103 | // `src` is first renamed to it 104 | // 105 | // this attempts an atomic rename with ReplaceFile(), falls back to non-atomic 106 | // renames if it fails 107 | // 108 | void replace_file(const context& cx, const fs::path& src, const fs::path& dest, 109 | const fs::path& backup = {}, flags f = noflags); 110 | 111 | // reads the given file, converts it to utf8 from the given encoding, returns 112 | // the utf8 string; if `e` is `dont_know`, returns the bytes as-is 113 | // 114 | std::string read_text_file(const context& cx, encodings e, const fs::path& p, 115 | flags f = noflags); 116 | 117 | // creates file `p`, writes the given utf8 string into it, converting the string 118 | // to the given encoding; if `e` is dont_know, the bytes are written as-is 119 | // 120 | void write_text_file(const context& cx, encodings e, const fs::path& p, 121 | std::string_view utf8, flags f = noflags); 122 | 123 | // creates an archive `dest_file` and puts all the files matching `src_glob` 124 | // into it, ignoring any file in `ignore` by name 125 | // 126 | // uses tools::archiver 127 | // 128 | void archive_from_glob(const context& cx, const fs::path& src_glob, 129 | const fs::path& dest_file, 130 | const std::vector& ignore, flags f = noflags); 131 | 132 | // creates an archive `dest_file` and puts all the files from `files` in it, 133 | // resolving relative paths against `files_root` 134 | // 135 | void archive_from_files(const context& cx, const std::vector& files, 136 | const fs::path& files_root, const fs::path& dest_file, 137 | flags f = noflags); 138 | 139 | } // namespace mob::op 140 | -------------------------------------------------------------------------------- /src/core/paths.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mob { 4 | 5 | // returns the path to mob.exe that's currently running, including filename; 6 | // bails on failure 7 | // 8 | fs::path mob_exe_path(); 9 | 10 | // returns mob's root directory, contains third-party/, etc.; bails on failure 11 | // 12 | // this is not necessarily the parent of mob_exe_path(), it mob could be running 13 | // from the build directory 14 | // 15 | fs::path find_root(bool verbose = false); 16 | 17 | // resolves the given relative path against find_root(); bails when not found 18 | // 19 | fs::path find_in_root(const fs::path& file); 20 | 21 | // returns the absolute path of mob's third-party directory; bails when not 22 | // found 23 | // 24 | fs::path find_third_party_directory(); 25 | 26 | // returns the absolute path to x86/x64 program files directory 27 | // 28 | fs::path find_program_files_x86(); 29 | fs::path find_program_files_x64(); 30 | 31 | // returns the absolute path to visual studio's root directory, the one that 32 | // contains Common7, VC, etc.; bails if not found 33 | // 34 | fs::path find_vs(); 35 | 36 | // returns the absolute path to VCPKG root directory to be used as VCPKG_ROOT when 37 | // building 38 | // 39 | fs::path find_vcpkg(); 40 | 41 | // returns the absolute path to Qt's root directory, the one that contains 42 | // bin, include, etc.; bails if not found 43 | // 44 | fs::path find_qt(); 45 | 46 | // returns the absolute path to iscc.exe; bails if not found 47 | // 48 | fs::path find_iscc(); 49 | 50 | // returns the absolute path to the system's temp directory; bails on error 51 | // 52 | fs::path find_temp_dir(); 53 | 54 | // returns the absolute path to the vcvars batch file, bails if not found 55 | // 56 | fs::path find_vcvars(); 57 | 58 | } // namespace mob 59 | -------------------------------------------------------------------------------- /src/core/pch.h: -------------------------------------------------------------------------------- 1 | // for intellisense 2 | #include "../pch.h" 3 | -------------------------------------------------------------------------------- /src/core/pipe.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../utility.h" 4 | 5 | namespace mob { 6 | 7 | // a pipe connected to a process's stdout or stderr, it is read from 8 | // 9 | class async_pipe_stdout { 10 | public: 11 | async_pipe_stdout(const context& cx); 12 | 13 | // a pipe has two ends: one that's given to the process so it can write to 14 | // it, and another that's kept so it can be read from 15 | // 16 | // this creates both ends and returns the handle that should be given to 17 | // the process 18 | // 19 | handle_ptr create(); 20 | 21 | // reads from the pipe and returns bytes, if any 22 | // 23 | // if `finish` is true (happens when the process has terminated) and nothing 24 | // is available in the pipe, closed() will return true 25 | // 26 | // this may start an async read, so read() must be called repeatedly until 27 | // the process terminates 28 | // 29 | std::string_view read(bool finish); 30 | 31 | // if this returns true, everything has been read from the pipe 32 | // 33 | bool closed() const; 34 | 35 | private: 36 | // the maximum number of bytes that can be put in the pipe 37 | static const std::size_t buffer_size = 50'000; 38 | 39 | // calling context, used for logging 40 | const context& cx_; 41 | 42 | // end of the pipe that is read from 43 | handle_ptr pipe_; 44 | 45 | // an event that's given to pipe for overlapped reads, signalled when data 46 | // is available 47 | handle_ptr event_; 48 | 49 | // internal buffer of `buffer_size` bytes, the data from the pipe is put 50 | // in there 51 | std::unique_ptr buffer_; 52 | 53 | // used for async reads 54 | OVERLAPPED ov_; 55 | 56 | // whether the last read attempt said an async operation was started 57 | bool pending_; 58 | 59 | // whether the last read attempt had `finished` true and nothing was 60 | // available in the pipe; in this case, the pipe is considered closed 61 | bool closed_; 62 | 63 | // creates the actual pipe, sets stdout_ and returns the other end so it 64 | // can be given to the process 65 | // 66 | HANDLE create_named_pipe(); 67 | 68 | // called when pending_ is false; tries to read from the pipe, which may 69 | // start an async operation, in which case pending_ is set to true and an 70 | // empty string is returned 71 | // 72 | // if the read operation was completed synchronously, returns the bytes 73 | // that were read 74 | // 75 | std::string_view try_read(); 76 | 77 | // called when pending_ is true; if the async operation is finished, resets 78 | // pending_ and returns the bytes that were read 79 | // 80 | std::string_view check_pending(); 81 | }; 82 | 83 | // a pipe connected to a process's stdin, it is written to; this pipe is 84 | // synchronous and does not keep a copy of the given buffer, see write() 85 | // 86 | class async_pipe_stdin { 87 | public: 88 | async_pipe_stdin(const context& cx); 89 | 90 | handle_ptr create(); 91 | 92 | // tries to send all of `s` down the pipe, returns the number of bytes 93 | // actually written 94 | // 95 | std::size_t write(std::string_view s); 96 | 97 | // closes the pipe, should be called as soon as everything has been written 98 | // 99 | void close(); 100 | 101 | private: 102 | // calling context, used for logging 103 | const context& cx_; 104 | 105 | // end of the pipe that is written to 106 | handle_ptr pipe_; 107 | }; 108 | 109 | } // namespace mob 110 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "cmd/commands.h" 3 | #include "core/conf.h" 4 | #include "core/op.h" 5 | #include "net.h" 6 | #include "tasks/task_manager.h" 7 | #include "tasks/tasks.h" 8 | #include "tools/tools.h" 9 | #include "utility.h" 10 | #include "utility/threading.h" 11 | 12 | namespace mob { 13 | 14 | void add_tasks() 15 | { 16 | using namespace tasks; 17 | 18 | // add new tasks here 19 | // 20 | // top level tasks are run sequentially, tasks added to a parallel_tasks will 21 | // run in parallel; which tasks are run in parallel is somewhat arbitrary when 22 | // there's no dependency, the goal is just to saturate the cpu 23 | // 24 | // mob doesn't have a concept of task dependencies, just task ordering, so 25 | // if a task depends on another, it has to be earlier in the order 26 | 27 | // super tasks 28 | 29 | using mo = modorganizer; 30 | 31 | // most of the alternate names below are from the transifex slugs, which 32 | // are sometimes different from the project names, for whatever reason 33 | 34 | add_task().add_task().add_task("cmake_common"); 35 | 36 | add_task("modorganizer-uibase"); 37 | 38 | add_task() 39 | .add_task("modorganizer-archive") 40 | .add_task("modorganizer-lootcli") 41 | .add_task("modorganizer-esptk") 42 | .add_task("modorganizer-bsatk") 43 | .add_task("modorganizer-nxmhandler") 44 | .add_task("modorganizer-helper") 45 | .add_task("modorganizer-game_bethesda"); 46 | 47 | add_task() 48 | .add_task({"modorganizer-bsapacker", "bsa_packer"}) 49 | .add_task({"modorganizer-tool_inieditor", "inieditor"}) 50 | .add_task({"modorganizer-tool_inibakery", "inibakery"}) 51 | .add_task("modorganizer-preview_bsa") 52 | .add_task("modorganizer-preview_base") 53 | .add_task("modorganizer-diagnose_basic") 54 | .add_task("modorganizer-check_fnis") 55 | .add_task("modorganizer-installer_bain") 56 | .add_task("modorganizer-installer_manual") 57 | .add_task("modorganizer-installer_bundle") 58 | .add_task("modorganizer-installer_quick") 59 | .add_task("modorganizer-installer_fomod") 60 | .add_task("modorganizer-installer_fomod_csharp") 61 | .add_task("modorganizer-installer_omod") 62 | .add_task("modorganizer-installer_wizard") 63 | .add_task("modorganizer-bsa_extractor") 64 | .add_task("modorganizer-plugin_python"); 65 | 66 | add_task() 67 | .add_task() 68 | .add_task() 69 | .add_task() 70 | .add_task({"modorganizer-tool_configurator", "pycfg"}) 71 | .add_task("modorganizer-fnistool") 72 | .add_task("modorganizer-basic_games") 73 | .add_task({"modorganizer-script_extender_plugin_checker", 74 | "scriptextenderpluginchecker"}) 75 | .add_task({"modorganizer-form43_checker", "form43checker"}) 76 | .add_task({"modorganizer-preview_dds", "ddspreview"}) 77 | .add_task({"modorganizer", "organizer"}); 78 | 79 | // other tasks 80 | add_task(); 81 | add_task(); 82 | } 83 | 84 | // figures out which command to run and returns it, if any 85 | // 86 | std::shared_ptr handle_command_line(const std::vector& args) 87 | { 88 | auto help = std::make_shared(); 89 | auto build = std::make_shared(); 90 | 91 | // available commands 92 | std::vector> commands = { 93 | help, 94 | std::make_unique(), 95 | std::make_unique(), 96 | build, 97 | std::make_unique(), 98 | std::make_unique(), 99 | std::make_unique(), 100 | std::make_unique(), 101 | std::make_unique(), 102 | std::make_unique(), 103 | std::make_unique()}; 104 | 105 | // commands are shown in the help 106 | help->set_commands(commands); 107 | 108 | // root group with all the command groups 109 | clipp::group all_groups; 110 | 111 | // not sure, actually 112 | all_groups.scoped(false); 113 | 114 | // child groups are exclusive, that is, only one command can be given 115 | all_groups.exclusive(true); 116 | 117 | for (auto& c : commands) 118 | all_groups.push_back(c->group()); 119 | 120 | // vs reports a no-op on the left side of the command, which is incorrect 121 | #pragma warning(suppress : 4548) 122 | auto cli = (all_groups, command::common_options_group()); 123 | auto pr = clipp::parse(args, cli); 124 | 125 | if (!pr) { 126 | // if a command was picked, show its help instead of the main one 127 | for (auto&& c : commands) { 128 | if (c->picked()) { 129 | c->force_help(); 130 | return std::move(c); 131 | } 132 | } 133 | 134 | // bad command line 135 | help->force_exit_code(1); 136 | return help; 137 | } 138 | 139 | for (auto&& c : commands) { 140 | if (c->picked()) 141 | return std::move(c); 142 | } 143 | 144 | return {}; 145 | } 146 | 147 | int run(const std::vector& args) 148 | { 149 | font_restorer fr; 150 | curl_init curl; 151 | 152 | try { 153 | add_tasks(); 154 | 155 | auto c = handle_command_line(args); 156 | if (!c) 157 | return 1; 158 | 159 | return c->run(); 160 | } 161 | catch (bailed&) { 162 | // silent 163 | return 1; 164 | } 165 | } 166 | 167 | } // namespace mob 168 | 169 | int wmain(int argc, wchar_t** argv) 170 | { 171 | // makes streams unicode 172 | mob::set_std_streams(); 173 | 174 | // outputs stacktrace on crash 175 | mob::set_thread_exception_handlers(); 176 | 177 | std::vector args; 178 | for (int i = 1; i < argc; ++i) 179 | args.push_back(mob::utf16_to_utf8(argv[i])); 180 | 181 | int r = mob::run(args); 182 | mob::dump_logs(); 183 | 184 | return r; 185 | } 186 | -------------------------------------------------------------------------------- /src/net.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utility.h" 4 | 5 | namespace mob { 6 | 7 | class context; 8 | 9 | // curl global init/cleanup 10 | // 11 | struct curl_init { 12 | curl_init(); 13 | ~curl_init(); 14 | 15 | curl_init(const curl_init&) = delete; 16 | curl_init& operator=(const curl_init&) = delete; 17 | }; 18 | 19 | // wrapper around curl_url 20 | // 21 | class url { 22 | public: 23 | url(const char* p); 24 | url(std::string s = {}); 25 | 26 | const char* c_str() const; 27 | const std::string& string() const; 28 | bool empty() const; 29 | 30 | // component of the path after last separator 31 | // 32 | std::string filename() const; 33 | 34 | private: 35 | std::string s_; 36 | }; 37 | 38 | // threaded downloader 39 | // 40 | class curl_downloader { 41 | public: 42 | using headers = std::vector>; 43 | 44 | curl_downloader(const context* cx = nullptr); 45 | 46 | // convenience: starts a thread, downloads url into given file 47 | // 48 | void start(const mob::url& u, const fs::path& file); 49 | 50 | // sets the url to download from 51 | // 52 | curl_downloader& url(const mob::url& u); 53 | 54 | // sets the output file 55 | // 56 | curl_downloader& file(const fs::path& file); 57 | 58 | // adds a header 59 | // 60 | curl_downloader& header(std::string name, std::string value); 61 | 62 | // starts the download in a thread 63 | // 64 | curl_downloader& start(); 65 | 66 | // joins download thread 67 | // 68 | curl_downloader& join(); 69 | 70 | // async interrupt 71 | // 72 | void interrupt(); 73 | 74 | // whether the file was downloaded correctly; only valid after join() 75 | // 76 | bool ok() const; 77 | 78 | // if file() wasn't called, returns the content that was retrieved 79 | // 80 | const std::string& output(); 81 | std::string steal_output(); 82 | 83 | private: 84 | const context& cx_; 85 | mob::url url_; 86 | fs::path path_; 87 | handle_ptr file_; 88 | std::thread thread_; 89 | std::size_t bytes_; 90 | std::atomic interrupt_; 91 | bool ok_; 92 | std::string output_; 93 | headers headers_; 94 | 95 | void run(); 96 | bool create_file(); 97 | bool write_file(char* ptr, size_t size); 98 | bool write_string(char* ptr, size_t size); 99 | 100 | static size_t on_write_static(char* ptr, size_t size, size_t nmemb, 101 | void* user) noexcept; 102 | 103 | void on_write(char* ptr, std::size_t n) noexcept; 104 | 105 | static int on_progress_static(void* user, double dltotal, double dlnow, 106 | double ultotal, double ulnow) noexcept; 107 | 108 | static int on_xfer_static(void* user, curl_off_t dltotal, curl_off_t dlnow, 109 | curl_off_t ultotal, curl_off_t ulnow) noexcept; 110 | 111 | static int on_debug_static(CURL* handle, curl_infotype type, char* data, 112 | size_t size, void* user) noexcept; 113 | 114 | void on_debug(curl_infotype type, std::string_view s); 115 | }; 116 | 117 | } // namespace mob 118 | 119 | template <> 120 | struct std::formatter : std::formatter { 121 | template 122 | FmtContext::iterator format(mob::url const& u, FmtContext& ctx) const 123 | { 124 | return std::formatter::format(u.string(), ctx); 125 | } 126 | }; 127 | -------------------------------------------------------------------------------- /src/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /src/pch.h: -------------------------------------------------------------------------------- 1 | // global warnings 2 | #pragma warning(disable : 4464) // relative include path 3 | #pragma warning(disable : 4820) // padding added 4 | #pragma warning(disable : 4623) // implicitly defined as deleted 5 | #pragma warning(disable : 4625) // implicitly defined as deleted 6 | #pragma warning(disable : 4626) // implicitly defined as deleted 7 | #pragma warning(disable : 5026) // implicitly defined as deleted 8 | #pragma warning(disable : 5027) // implicitly defined as deleted 9 | #pragma warning(disable : 4514) // unreferenced inline 10 | #pragma warning(disable : 4866) // may not enforce left-to-right evaluation 11 | #pragma warning(disable : 4868) // may not enforce left-to-right evaluation 12 | #pragma warning(disable : 4711) // selected for automatic inline expansion 13 | #pragma warning(disable : 4251) // needs to have dll-interface 14 | #pragma warning(disable : 4571) // catch semantics 15 | #pragma warning(disable : 4686) // change in UDT return calling convention 16 | #pragma warning(disable : 5045) // spectre 17 | #pragma warning(disable : 4710) // function not inlined 18 | #pragma warning(disable : 4435) // /vd2 19 | #pragma warning(disable : 5052) // requires use of /std:c++latest 20 | 21 | // popped by warnings_pop.h 22 | #pragma warning(push, 3) 23 | #pragma warning(disable : 4355) // this used in initializer list 24 | #pragma warning(disable : 4668) // not defined as a preprocessor macro 25 | #pragma warning(disable : 4619) // there is no warning number 'x' 26 | #pragma warning(disable : 5031) // warning state pushed in different file 27 | #pragma warning(disable : 4643) // forward declaring in namespace std 28 | #pragma warning(disable : 4365) // signed/unsigned mismatch 29 | #pragma warning(disable : 4061) // enumerator is not explicitly handled 30 | #pragma warning(disable : 4265) // destructor is not virtual 31 | #pragma warning(disable : 4623) // default constructor implicitly deleted 32 | #pragma warning(disable : 4266) // no override available 33 | #pragma warning(disable : 4267) // conversion 34 | #pragma warning(disable : 4774) // format string 35 | #pragma warning(disable : 4371) // layout of class may have changed 36 | #pragma warning(disable : 5039) // throwing function passed to extern C 37 | #pragma warning(disable : 4388) // signed/unsigned mismatch 38 | #pragma warning(disable : 4582) // constructor is not implicitly called 39 | #pragma warning(disable : 4574) // macro is defined to be 'value' 40 | #pragma warning(disable : 4201) // nameless struct/union 41 | #pragma warning(disable : 4127) // conditional expression is constant 42 | #pragma warning(disable : 4100) // unreferenced parameter 43 | #pragma warning(disable : 4242) // possible loss of data 44 | #pragma warning(disable : 4244) // possible loss of data 45 | #pragma warning(disable : 4275) // non dll-interface base 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | 71 | #include 72 | #include 73 | #include 74 | 75 | #pragma warning(pop) 76 | 77 | namespace mob { 78 | 79 | namespace fs = std::filesystem; 80 | using hr_clock = std::chrono::high_resolution_clock; 81 | 82 | } // namespace mob 83 | -------------------------------------------------------------------------------- /src/tasks/explorerpp.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "tasks.h" 3 | 4 | namespace mob::tasks { 5 | 6 | namespace { 7 | 8 | url source_url() 9 | { 10 | return "https://download.explorerplusplus.com/stable/" + 11 | explorerpp::version() + "/explorerpp_x64.zip"; 12 | } 13 | 14 | } // namespace 15 | 16 | explorerpp::explorerpp() : basic_task("explorerpp", "explorer++") {} 17 | 18 | std::string explorerpp::version() 19 | { 20 | return conf().version().get("explorerpp"); 21 | } 22 | 23 | bool explorerpp::prebuilt() 24 | { 25 | // always prebuilt, direct download 26 | return false; 27 | } 28 | 29 | fs::path explorerpp::source_path() 30 | { 31 | return conf().path().build() / "explorer++"; 32 | } 33 | 34 | void explorerpp::do_clean(clean c) 35 | { 36 | // delete download 37 | if (is_set(c, clean::redownload)) 38 | run_tool(downloader(source_url(), downloader::clean)); 39 | 40 | // delete the whole directory 41 | if (is_set(c, clean::reextract)) { 42 | cx().trace(context::reextract, "deleting {}", source_path()); 43 | op::delete_directory(cx(), source_path(), op::optional); 44 | } 45 | } 46 | 47 | void explorerpp::do_fetch() 48 | { 49 | const auto file = run_tool(downloader(source_url())); 50 | 51 | run_tool(extractor().file(file).output(source_path())); 52 | 53 | // copy everything to install/bin/explorer++ 54 | op::copy_glob_to_dir_if_better(cx(), source_path() / "*", 55 | conf().path().install_bin() / "explorer++", 56 | op::copy_files); 57 | } 58 | 59 | } // namespace mob::tasks 60 | -------------------------------------------------------------------------------- /src/tasks/installer.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "tasks.h" 3 | 4 | namespace mob::tasks { 5 | 6 | installer::installer() : basic_task("installer") {} 7 | 8 | bool installer::prebuilt() 9 | { 10 | return false; 11 | } 12 | 13 | std::string installer::version() 14 | { 15 | return {}; 16 | } 17 | 18 | fs::path installer::source_path() 19 | { 20 | return modorganizer::super_path() / "installer"; 21 | } 22 | 23 | void installer::do_clean(clean c) 24 | { 25 | // delete the git clone directory 26 | if (is_set(c, clean::reclone)) 27 | git_wrap::delete_directory(cx(), source_path()); 28 | 29 | // the installer script outputs directly in the installer directory, delete 30 | // it 31 | if (is_set(c, clean::rebuild)) 32 | op::delete_directory(cx(), conf().path().install_installer()); 33 | } 34 | 35 | void installer::do_fetch() 36 | { 37 | const std::string repo = "modorganizer-Installer"; 38 | 39 | // find the best suitable branch 40 | const auto fallback = task_conf().mo_fallback_branch(); 41 | auto branch = task_conf().mo_branch(); 42 | if (!fallback.empty() && 43 | !git_wrap::remote_branch_exists(make_git_url(task_conf().mo_org(), repo), 44 | branch)) { 45 | cx().warning(context::generic, 46 | "{} has no remote {} branch, switching to {}", repo, branch, 47 | fallback); 48 | branch = fallback; 49 | } 50 | 51 | run_tool(make_git() 52 | .url(make_git_url(task_conf().mo_org(), repo)) 53 | .branch(branch) 54 | .root(source_path())); 55 | } 56 | 57 | void installer::do_build_and_install() 58 | { 59 | run_tool(source_path() / "dist" / "MO2-Installer.iss"); 60 | } 61 | 62 | } // namespace mob::tasks 63 | -------------------------------------------------------------------------------- /src/tasks/licenses.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "tasks.h" 3 | 4 | namespace mob::tasks { 5 | 6 | licenses::licenses() : task("licenses") {} 7 | 8 | void licenses::do_build_and_install() 9 | { 10 | // copy all files from mob's license directory to install/bin/licenses 11 | op::copy_glob_to_dir_if_better(cx(), conf().path().licenses() / "*", 12 | conf().path().install_licenses(), 13 | op::copy_files | op::copy_dirs); 14 | } 15 | 16 | } // namespace mob::tasks 17 | -------------------------------------------------------------------------------- /src/tasks/modorganizer.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "tasks.h" 3 | 4 | namespace mob::tasks { 5 | 6 | // given a vector of names (some projects have more than one, see add_tasks() in 7 | // main.cpp), this prepends the simplified name to the vector and returns it 8 | // 9 | // most MO project names are something like "modorganizer-uibase" on github and 10 | // the simplified name is used for two main reasons: 11 | // 12 | // 1) individual directories in modorganizer_super have historically used the 13 | // simplified name only 14 | // 15 | // 2) it's useful to have an simplified name for use on the command line 16 | // 17 | std::vector make_names(std::vector names) 18 | { 19 | // first name in the list might be a "modorganizer-something" 20 | const auto main_name = names[0]; 21 | 22 | const auto dash = main_name.find("-"); 23 | if (dash != std::string::npos) { 24 | // remove the part before the dash and the dash 25 | names.insert(names.begin(), main_name.substr(dash + 1)); 26 | } 27 | 28 | return names; 29 | } 30 | 31 | // creates the repo in modorganizer_super, used to add submodules 32 | // 33 | // only one task will end up past the mutex and the flag, so it's only done 34 | // once 35 | // 36 | void initialize_super(context& cx, const fs::path& super_root) 37 | { 38 | static std::mutex mutex; 39 | static bool initialized = false; 40 | 41 | std::scoped_lock lock(mutex); 42 | if (initialized) 43 | return; 44 | 45 | initialized = true; 46 | 47 | cx.trace(context::generic, "checking super"); 48 | 49 | git_wrap g(super_root); 50 | 51 | // happens when running mob again in the same build tree 52 | if (g.is_git_repo()) { 53 | cx.debug(context::generic, "super already initialized"); 54 | return; 55 | } 56 | 57 | // create empty repo 58 | cx.trace(context::generic, "initializing super"); 59 | g.init_repo(); 60 | } 61 | 62 | modorganizer::modorganizer(std::string long_name) 63 | : modorganizer(std::vector{long_name}) 64 | { 65 | } 66 | 67 | modorganizer::modorganizer(std::vector names) 68 | : modorganizer(std::vector(names.begin(), names.end())) 69 | { 70 | } 71 | 72 | modorganizer::modorganizer(std::vector names) 73 | : task(make_names(names)), repo_(names[0]) 74 | { 75 | if (names.size() > 1) { 76 | project_ = names[1]; 77 | } 78 | else { 79 | project_ = make_names(names)[0]; 80 | } 81 | } 82 | 83 | std::string modorganizer::cmake_prefix_path() 84 | { 85 | return conf().path().qt_install().string() + ";" + 86 | (modorganizer::super_path() / "cmake_common").string() + ";" + 87 | (conf().path().install() / "lib" / "cmake").string(); 88 | } 89 | 90 | fs::path modorganizer::source_path() const 91 | { 92 | // something like build/modorganizer_super/uibase 93 | return super_path() / name(); 94 | } 95 | 96 | fs::path modorganizer::super_path() 97 | { 98 | return conf().path().build(); 99 | } 100 | 101 | url modorganizer::git_url() const 102 | { 103 | return make_git_url(task_conf().mo_org(), repo_); 104 | } 105 | 106 | std::string modorganizer::org() const 107 | { 108 | return task_conf().mo_org(); 109 | } 110 | 111 | std::string modorganizer::repo() const 112 | { 113 | return repo_; 114 | } 115 | 116 | void modorganizer::do_clean(clean c) 117 | { 118 | // delete the whole directory 119 | if (is_set(c, clean::reclone)) { 120 | git_wrap::delete_directory(cx(), source_path()); 121 | 122 | // no need to do anything else 123 | return; 124 | } 125 | 126 | // cmake clean 127 | if (is_set(c, clean::reconfigure)) 128 | run_tool(cmake(cmake::clean).root(source_path())); 129 | } 130 | 131 | void modorganizer::do_fetch() 132 | { 133 | // make sure the super directory is initialized, only done once 134 | initialize_super(cx(), super_path()); 135 | 136 | // find the best suitable branch 137 | const auto fallback = task_conf().mo_fallback_branch(); 138 | auto branch = task_conf().mo_branch(); 139 | if (!fallback.empty() && !git_wrap::remote_branch_exists(git_url(), branch)) { 140 | cx().warning(context::generic, 141 | "{} has no remote {} branch, switching to {}", repo_, branch, 142 | fallback); 143 | branch = fallback; 144 | } 145 | 146 | // clone/pull 147 | run_tool(make_git().url(git_url()).branch(branch).root(source_path())); 148 | } 149 | 150 | void modorganizer::do_build_and_install() 151 | { 152 | // adds a git submodule in build for this project; note that 153 | // git_submodule_adder runs a thread because adding submodules is slow, but 154 | // can happen while stuff is building 155 | git_submodule_adder::instance().queue( 156 | std::move(git_submodule() 157 | .url(git_url()) 158 | .branch(task_conf().mo_branch()) 159 | .submodule(name()) 160 | .root(super_path()))); 161 | 162 | // not all modorganizer projects need to actually be built, such as 163 | // cmake_common, so don't try if there's no cmake file 164 | if (!exists(source_path() / "CMakeLists.txt")) { 165 | cx().trace(context::generic, "{} has no CMakeLists.txt, not building", 166 | repo_); 167 | 168 | return; 169 | } 170 | 171 | // if there is a CMakeLists.txt, there must be a CMakePresets.json otherwise 172 | // we cannot build 173 | if (!exists(source_path() / "CMakePresets.json")) { 174 | gcx().bail_out(context::generic, 175 | "{} has no CMakePresets.txt, aborting build", repo_); 176 | } 177 | 178 | // run cmake 179 | run_tool(cmake(cmake::generate) 180 | .generator(cmake::vs) 181 | .def("CMAKE_INSTALL_PREFIX:PATH", conf().path().install()) 182 | .def("CMAKE_PREFIX_PATH", cmake_prefix_path()) 183 | .configuration_types({task_conf().configuration()}) 184 | .preset("vs2022-windows") 185 | .root(source_path())); 186 | 187 | // run cmake --build with default target 188 | // TODO: handle rebuild by adding `--clean-first` 189 | // TODO: have a way to specify the `--parallel` value - 16 is useful to build 190 | // game_bethesda that has 15 games, so 15 projects 191 | run_tool(cmake(cmake::build) 192 | .root(source_path()) 193 | .arg("--parallel") 194 | .arg("16") 195 | .configuration(task_conf().configuration())); 196 | 197 | // run cmake --install 198 | run_tool(cmake(cmake::build) 199 | .root(source_path()) 200 | .targets("INSTALL") 201 | .configuration(task_conf().configuration())); 202 | } 203 | 204 | } // namespace mob::tasks 205 | -------------------------------------------------------------------------------- /src/tasks/pch.h: -------------------------------------------------------------------------------- 1 | // for intellisense 2 | #include "../pch.h" 3 | -------------------------------------------------------------------------------- /src/tasks/stylesheets.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "tasks.h" 3 | 4 | namespace mob::tasks { 5 | 6 | std::vector releases() 7 | { 8 | return {{"6788-00", "paper-light-and-dark", 9 | conf().version().get("ss_paper_lad_6788"), "paper-light-and-dark", ""}, 10 | 11 | {"6788-00", "paper-automata", 12 | conf().version().get("ss_paper_automata_6788"), "paper-automata", ""}, 13 | 14 | {"6788-00", "paper-mono", conf().version().get("ss_paper_mono_6788"), 15 | "paper-mono", ""}, 16 | 17 | {"6788-00", "1809-dark-mode", 18 | conf().version().get("ss_dark_mode_1809_6788"), "1809", ""}, 19 | 20 | {"Trosski", "ModOrganizer_Style_Morrowind", 21 | conf().version().get("ss_morrowind_trosski"), 22 | "Morrowind-MO2-Stylesheet", ""}, 23 | 24 | {"Trosski", "Mod-Organizer-2-Skyrim-Stylesheet", 25 | conf().version().get("ss_skyrim_trosski"), "Skyrim-MO2-Stylesheet", 26 | ""}, 27 | 28 | {"Trosski", "ModOrganizer_Style_Fallout3", 29 | conf().version().get("ss_fallout3_trosski"), "Fallout3-MO2-Stylesheet", 30 | ""}, 31 | 32 | {"Trosski", "Mod-Organizer2-Fallout-4-Stylesheet", 33 | conf().version().get("ss_fallout4_trosski"), "Fallout4-MO2-Stylesheet", 34 | ""}, 35 | 36 | {"Trosski", "Starfield_MO2_Stylesheet", 37 | conf().version().get("ss_starfield_trosski"), 38 | "Starfield.MO2.Stylsheet", ""}}; 39 | } 40 | 41 | stylesheets::stylesheets() : task("ss", "stylesheets") {} 42 | 43 | bool stylesheets::prebuilt() 44 | { 45 | return false; 46 | } 47 | 48 | void stylesheets::do_clean(clean c) 49 | { 50 | // delete download file for each release 51 | if (is_set(c, clean::redownload)) { 52 | for (auto&& r : releases()) 53 | run_tool(make_downloader_tool(r, downloader::clean)); 54 | } 55 | 56 | // delete directory for each release 57 | if (is_set(c, clean::reextract)) { 58 | for (auto&& r : releases()) { 59 | const auto p = release_build_path(r); 60 | 61 | cx().trace(context::reextract, "deleting {}", p); 62 | op::delete_directory(cx(), p, op::optional); 63 | } 64 | } 65 | } 66 | 67 | void stylesheets::do_fetch() 68 | { 69 | // download and extract file for each release 70 | for (auto&& r : releases()) { 71 | const auto file = run_tool(make_downloader_tool(r)); 72 | 73 | run_tool(extractor().file(file).output(release_build_path(r))); 74 | } 75 | } 76 | 77 | fs::path stylesheets::release_build_path(const release& r) const 78 | { 79 | // something like build/paper-mono-v2.1 80 | return conf().path().build() / "stylesheets" / (r.repo + "-" + r.version); 81 | } 82 | 83 | downloader stylesheets::make_downloader_tool(const release& r, 84 | downloader::ops o) const 85 | { 86 | url u = "https://github.com/" + r.user + "/" + r.repo + 87 | "/releases/" 88 | "download/" + 89 | r.version + "/" + r.file + ".7z"; 90 | 91 | return std::move( 92 | downloader(o).url(u).file(conf().path().cache() / (r.repo + ".7z"))); 93 | } 94 | 95 | void stylesheets::do_build_and_install() 96 | { 97 | for (auto&& r : releases()) { 98 | // copy all the files and directories from the source directory directly 99 | // into install/bin/stylesheets 100 | op::copy_glob_to_dir_if_better( 101 | cx(), 102 | r.top_level_folder.size() 103 | ? release_build_path(r) / r.top_level_folder / "*" 104 | : release_build_path(r) / "*", 105 | conf().path().install_stylesheets(), op::copy_files | op::copy_dirs); 106 | } 107 | } 108 | 109 | } // namespace mob::tasks 110 | -------------------------------------------------------------------------------- /src/tasks/task_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "task_manager.h" 3 | #include "../core/context.h" 4 | #include "task.h" 5 | 6 | namespace mob { 7 | 8 | task_manager::task_manager() : interrupt_(false) {} 9 | 10 | task_manager& task_manager::instance() 11 | { 12 | static task_manager m; 13 | return m; 14 | } 15 | 16 | void task_manager::add(std::unique_ptr t) 17 | { 18 | top_level_.push_back(std::move(t)); 19 | } 20 | 21 | void task_manager::register_task(task* t) 22 | { 23 | all_.push_back(t); 24 | } 25 | 26 | std::vector task_manager::find(std::string_view pattern) 27 | { 28 | auto tasks = find_by_pattern(pattern); 29 | 30 | if (tasks.empty()) 31 | tasks = find_by_alias(pattern); 32 | 33 | return tasks; 34 | } 35 | 36 | task* task_manager::find_one(std::string_view pattern, bool verbose) 37 | { 38 | const auto tasks = find(pattern); 39 | 40 | // okay, only one match 41 | if (tasks.size() == 1) 42 | return tasks[0]; 43 | 44 | // bad 45 | 46 | if (tasks.empty()) { 47 | if (verbose) 48 | u8cerr << "no task matches '" << pattern << "'\n"; 49 | } 50 | else if (tasks.size() > 1) { 51 | if (verbose) { 52 | u8cerr << "found " << tasks.size() << " matches for pattern " 53 | << "'" << pattern << "'\n" 54 | << "the pattern must only match one task\n"; 55 | } 56 | } 57 | 58 | return nullptr; 59 | } 60 | 61 | std::vector task_manager::all() 62 | { 63 | return all_; 64 | } 65 | 66 | std::vector task_manager::top_level() 67 | { 68 | std::vector v; 69 | 70 | for (auto&& t : top_level_) 71 | v.push_back(t.get()); 72 | 73 | return v; 74 | } 75 | 76 | void task_manager::add_alias(std::string name, std::vector names) 77 | { 78 | auto itor = aliases_.find(name); 79 | if (itor != aliases_.end()) { 80 | gcx().warning(context::generic, "alias {} already exists", name); 81 | return; 82 | } 83 | 84 | aliases_.emplace(std::move(name), std::move(names)); 85 | } 86 | 87 | const task_manager::alias_map& task_manager::aliases() 88 | { 89 | return aliases_; 90 | } 91 | 92 | void task_manager::run_all() 93 | { 94 | try { 95 | for (auto&& t : top_level_) { 96 | t->run(); 97 | 98 | if (interrupt_) 99 | break; 100 | } 101 | } 102 | catch (interrupted&) { 103 | } 104 | 105 | for (auto&& t : top_level_) { 106 | t->check_bailed(); 107 | } 108 | } 109 | 110 | void task_manager::interrupt_all() 111 | { 112 | // handles multiple tasks failing simultaneously 113 | std::scoped_lock lock(interrupt_mutex_); 114 | 115 | if (!interrupt_) { 116 | interrupt_ = true; 117 | for (auto&& t : top_level_) 118 | t->interrupt(); 119 | } 120 | } 121 | 122 | std::vector task_manager::find_by_pattern(std::string_view pattern) 123 | { 124 | std::vector tasks; 125 | 126 | for (auto&& t : all_) { 127 | if (t->name_matches(pattern)) 128 | tasks.push_back(t); 129 | } 130 | 131 | return tasks; 132 | } 133 | 134 | std::vector task_manager::find_by_alias(std::string_view alias_name) 135 | { 136 | std::vector v; 137 | 138 | auto itor = aliases_.find(alias_name); 139 | if (itor == aliases_.end()) 140 | return v; 141 | 142 | for (auto&& a : itor->second) { 143 | const auto temp = find_by_pattern(a); 144 | v.insert(v.end(), temp.begin(), temp.end()); 145 | } 146 | 147 | return v; 148 | } 149 | 150 | bool task_manager::valid_task_name(std::string_view pattern) 151 | { 152 | if (!find(pattern).empty()) 153 | return true; 154 | 155 | if (pattern == "_override") 156 | return true; 157 | 158 | return false; 159 | } 160 | 161 | } // namespace mob 162 | -------------------------------------------------------------------------------- /src/tasks/task_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mob { 4 | 5 | class task; 6 | 7 | // thrown by tasks or within the task_manager when they're interrupted because 8 | // of failure or sigint 9 | // 10 | class interrupted {}; 11 | 12 | // contains the tasks and aliases, singleton 13 | // 14 | // the manager owns the top level tasks added with add() but also has pointers 15 | // to all tasks except for parallel_tasks, added by calling register_task() in 16 | // task's constructor 17 | // 18 | class task_manager { 19 | public: 20 | // map of alias -> patterns 21 | using alias_map = std::map, std::less<>>; 22 | 23 | task_manager(); 24 | static task_manager& instance(); 25 | 26 | // adds a top-level task, used for running or interrupting tasks 27 | // 28 | void add(std::unique_ptr t); 29 | 30 | // called by task::task() for all tasks except parallel_tasks, used for 31 | // find tasks by name 32 | // 33 | void register_task(task* t); 34 | 35 | // returns all tasks matching the glob 36 | // 37 | std::vector find(std::string_view pattern); 38 | 39 | // returns one task that matches the glob; returns null if multiple or no 40 | // tasks match the pattern, outputs errors when `verbose` is true (mostly 41 | // for the command line) 42 | // 43 | task* find_one(std::string_view pattern, bool verbose = true); 44 | 45 | // whether the given pattern matches at least one task or is "_override", 46 | // should only be used when parsing inis or command line options 47 | // 48 | bool valid_task_name(std::string_view pattern); 49 | 50 | // returns all tasks except for parallel_tasks 51 | // 52 | std::vector all(); 53 | 54 | // returns all top-level tasks, that is, tasks added with add() 55 | // 56 | std::vector top_level(); 57 | 58 | // adds an alias 59 | // 60 | void add_alias(std::string name, std::vector patterns); 61 | 62 | // returns all aliases 63 | // 64 | const alias_map& aliases(); 65 | 66 | // runs all top-level tasks sequentially, disabled tasks won't run 67 | // 68 | void run_all(); 69 | 70 | // interrupts all tasks 71 | // 72 | void interrupt_all(); 73 | 74 | private: 75 | // top-level tasks 76 | std::vector> top_level_; 77 | 78 | // all tasks except for parallel_tasks 79 | std::vector all_; 80 | 81 | // set to true in interrupt_all(), checked in run_all() to stop the loop 82 | std::atomic interrupt_; 83 | 84 | // locked in interrupt_all() in case multiple tasks fail at the same time 85 | std::mutex interrupt_mutex_; 86 | 87 | // alias map 88 | alias_map aliases_; 89 | 90 | // used by find(), returns tasks matching the given glob 91 | // 92 | std::vector find_by_pattern(std::string_view pattern); 93 | 94 | // used by find(), looks for an alias with the given name and returns 95 | // matching tasks 96 | // 97 | std::vector find_by_alias(std::string_view alias_name); 98 | }; 99 | 100 | // convenience, calls task_manager::add() 101 | // 102 | template 103 | Task& add_task(Args&&... args) 104 | { 105 | auto t = std::make_unique(std::forward(args)...); 106 | auto& ref = *t; 107 | 108 | task_manager::instance().add(std::move(t)); 109 | 110 | return ref; 111 | } 112 | 113 | } // namespace mob 114 | -------------------------------------------------------------------------------- /src/tasks/tasks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/conf.h" 4 | #include "../core/op.h" 5 | #include "../net.h" 6 | #include "../tools/tools.h" 7 | #include "../utility.h" 8 | #include "task.h" 9 | 10 | namespace mob::tasks { 11 | 12 | // single header for all the tasks, not worth having a header per task 13 | 14 | class explorerpp : public basic_task { 15 | public: 16 | explorerpp(); 17 | 18 | static std::string version(); 19 | static bool prebuilt(); 20 | static fs::path source_path(); 21 | 22 | protected: 23 | void do_clean(clean c) override; 24 | void do_fetch() override; 25 | }; 26 | 27 | class installer : public basic_task { 28 | public: 29 | installer(); 30 | 31 | static bool prebuilt(); 32 | static std::string version(); 33 | static fs::path source_path(); 34 | 35 | protected: 36 | void do_clean(clean c) override; 37 | void do_fetch() override; 38 | void do_build_and_install() override; 39 | }; 40 | 41 | class licenses : public task { 42 | public: 43 | licenses(); 44 | 45 | protected: 46 | void do_build_and_install() override; 47 | }; 48 | 49 | // a task for all modorganizer projects except for the installer, which is 50 | // technically an MO project but is built differently 51 | // 52 | // note that this doesn't inherit from basic_task because it doesn't have the 53 | // usual static functions for source_path() and prebuilt() since there's a 54 | // variety of modorganizer objects, one per project 55 | // 56 | class modorganizer : public task { 57 | public: 58 | // build CMAKE_PREFIX_PATH for MO2 tasks 59 | // 60 | static std::string cmake_prefix_path(); 61 | 62 | // path of the root modorganizer_super directory 63 | // 64 | static fs::path super_path(); 65 | 66 | // creates the same cmake tool as the one used to build the various MO 67 | // projects, used internally, but also by the cmake command 68 | // 69 | static cmake create_cmake_tool(const fs::path& root, 70 | cmake::ops o = cmake::generate, 71 | config config = config::relwithdebinfo); 72 | 73 | // some mo tasks have more than one name, mostly because the transifex slugs 74 | // are different than the names on github; the std::string and const char* 75 | // overloads are because they're constructed from initializer lists and it's 76 | // more convenient that way 77 | modorganizer(std::string name); 78 | modorganizer(std::vector names); 79 | modorganizer(std::vector names); 80 | 81 | // url to the git repo 82 | // 83 | url git_url() const; 84 | 85 | // `mo_org` setting from the ini (typically ModOrganizer2) 86 | // 87 | std::string org() const; 88 | 89 | // name of the repo on github (first name given in the constructor, 90 | // something like "cmake_common" or "modorganizer-uibase") 91 | // 92 | std::string repo() const; 93 | 94 | // directory for the project's source, something like 95 | // "build/modorganizer_super/modorganizer" 96 | // 97 | fs::path source_path() const; 98 | 99 | protected: 100 | void do_clean(clean c) override; 101 | void do_fetch() override; 102 | void do_build_and_install() override; 103 | 104 | private: 105 | std::string repo_; 106 | std::string project_; 107 | }; 108 | 109 | class stylesheets : public task { 110 | public: 111 | struct release { 112 | std::string user; 113 | std::string repo; 114 | std::string version; 115 | std::string file; 116 | std::string top_level_folder; 117 | }; 118 | 119 | stylesheets(); 120 | 121 | static bool prebuilt(); 122 | 123 | protected: 124 | void do_clean(clean c) override; 125 | void do_fetch() override; 126 | void do_build_and_install() override; 127 | 128 | private: 129 | downloader make_downloader_tool(const release& r, 130 | downloader::ops = downloader::download) const; 131 | 132 | fs::path release_build_path(const release& r) const; 133 | }; 134 | 135 | // see translations.cpp for more info 136 | // 137 | class translations : public task { 138 | public: 139 | // given the root translations folder, will have one `project` object per 140 | // directory 141 | // 142 | // each project will have a list of `lang` objects, each lang has the list 143 | // of .ts files that need to be compiled to create the .qm file 144 | // 145 | class projects { 146 | public: 147 | // a language for a project 148 | struct lang { 149 | // language name 150 | std::string name; 151 | 152 | // .ts files that need to be compiled 153 | std::vector ts_files; 154 | 155 | lang(std::string n); 156 | 157 | // if `name` has an underscore, returns the part before and after 158 | // it; if there is no underscore, first is `name`, second is empty 159 | // 160 | std::pair split() const; 161 | }; 162 | 163 | // a project that contains languages 164 | struct project { 165 | // project name 166 | std::string name; 167 | 168 | // list of languages 169 | std::vector langs; 170 | 171 | project(std::string n = {}); 172 | }; 173 | 174 | // walks the given directory and figures out projects and languages 175 | // 176 | projects(fs::path root); 177 | 178 | // list of projects found, one per directory in the root 179 | // 180 | const std::vector& get() const; 181 | 182 | // any warnings that happened while walking the directories 183 | // 184 | const std::vector& warnings() const; 185 | 186 | // return a project by name 187 | // 188 | std::optional find(std::string_view name) const; 189 | 190 | private: 191 | // translations directory 192 | const fs::path root_; 193 | 194 | // projects 195 | std::vector projects_; 196 | 197 | // warnings 198 | std::vector warnings_; 199 | 200 | // set of files for which a warning was added to warnings_, avoids 201 | // duplicate warnings 202 | std::set warned_; 203 | 204 | // parses the directory name, walks all the .ts files, returns a project 205 | // object for them 206 | // 207 | project create_project(const fs::path& dir); 208 | 209 | // returns a lang object that contains at least the given main_ts_file, 210 | // but might contain more if it's a gamebryo plugin 211 | // 212 | lang create_lang(const std::string& project_name, 213 | const fs::path& main_ts_file); 214 | }; 215 | 216 | translations(); 217 | static fs::path source_path(); 218 | 219 | protected: 220 | void do_clean(clean c) override; 221 | void do_fetch() override; 222 | void do_build_and_install() override; 223 | 224 | private: 225 | // copy builtin qt .qm files 226 | void copy_builtin_qt_translations(const projects::project& organizer_project, 227 | const fs::path& dest); 228 | }; 229 | 230 | class usvfs : public basic_task { 231 | public: 232 | usvfs(); 233 | 234 | static std::string version(); 235 | static bool prebuilt(); 236 | static fs::path source_path(); 237 | 238 | protected: 239 | void do_clean(clean c) override; 240 | void do_fetch() override; 241 | void do_build_and_install() override; 242 | 243 | private: 244 | void fetch_from_source(); 245 | void build_and_install_from_source(); 246 | 247 | cmake create_cmake_tool(arch, cmake::ops = cmake::generate) const; 248 | msbuild create_msbuild_tool(arch, msbuild::ops = msbuild::build, 249 | config = config::release) const; 250 | }; 251 | 252 | } // namespace mob::tasks 253 | -------------------------------------------------------------------------------- /src/tasks/usvfs.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "tasks.h" 3 | 4 | // note that usvfs is the only project that has to be built for both 32- and 5 | // 64-bit architectures 6 | // 7 | // prebuilts are typically only used when building on appveyor, so they reuse 8 | // whatever was last built with contiguous integration 9 | // 10 | // the files generated by CI are called "artifacts", they can be seen on 11 | // https://ci.appveyor.com/project/ModOrganizer2/usvfs, by clicking on a 12 | // platform at the bottom, then the "Artifacts" tab 13 | 14 | namespace mob::tasks { 15 | 16 | usvfs::usvfs() : basic_task("usvfs") {} 17 | 18 | std::string usvfs::version() 19 | { 20 | return conf().version().get("usvfs"); 21 | } 22 | 23 | bool usvfs::prebuilt() 24 | { 25 | return false; 26 | } 27 | 28 | fs::path usvfs::source_path() 29 | { 30 | return conf().path().build() / "usvfs"; 31 | } 32 | 33 | void usvfs::do_clean(clean c) 34 | { 35 | // delete the whole directory 36 | if (is_set(c, clean::reclone)) { 37 | git_wrap::delete_directory(cx(), source_path()); 38 | 39 | // nothing more to do 40 | return; 41 | } 42 | 43 | if (is_set(c, clean::reconfigure)) { 44 | run_tool(create_cmake_tool(arch::x64)); 45 | run_tool(create_cmake_tool(arch::x86)); 46 | } 47 | 48 | if (is_set(c, clean::rebuild)) { 49 | // msbuild clean 50 | run_tool(create_msbuild_tool(arch::x86, msbuild::clean, 51 | task_conf().configuration())); 52 | run_tool(create_msbuild_tool(arch::x64, msbuild::clean, 53 | task_conf().configuration())); 54 | } 55 | } 56 | 57 | void usvfs::do_fetch() 58 | { 59 | fetch_from_source(); 60 | } 61 | 62 | void usvfs::do_build_and_install() 63 | { 64 | build_and_install_from_source(); 65 | } 66 | 67 | void usvfs::fetch_from_source() 68 | { 69 | run_tool(make_git() 70 | .url(make_git_url(task_conf().mo_org(), "usvfs")) 71 | .branch(version()) 72 | .root(source_path())); 73 | } 74 | 75 | void usvfs::build_and_install_from_source() 76 | { 77 | run_tool(create_cmake_tool(arch::x64)); 78 | run_tool(create_cmake_tool(arch::x86)); 79 | run_tool(create_msbuild_tool(arch::x64, msbuild::build, 80 | task_conf().configuration())); 81 | run_tool(create_msbuild_tool(arch::x86, msbuild::build, 82 | task_conf().configuration())); 83 | } 84 | 85 | cmake usvfs::create_cmake_tool(arch a, cmake::ops o) const 86 | { 87 | return std::move( 88 | cmake(o) 89 | .root(source_path()) 90 | .def("CMAKE_INSTALL_PREFIX:PATH", conf().path().install()) 91 | .generator(cmake::vs) 92 | .preset(a == arch::x64 ? "vs2022-windows-x64" : "vs2022-windows-x86") 93 | .arg("-DBUILD_TESTING=OFF")); 94 | } 95 | 96 | msbuild usvfs::create_msbuild_tool(arch a, msbuild::ops o, config c) const 97 | { 98 | const std::string vsbuild = (a == arch::x64 ? "vsbuild64" : "vsbuild32"); 99 | return std::move(msbuild(o).architecture(a).configuration(c).solution( 100 | source_path() / vsbuild / "usvfs.sln")); 101 | } 102 | 103 | } // namespace mob::tasks 104 | -------------------------------------------------------------------------------- /src/tools/cmake.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mob { 4 | 5 | // a tool that runs `cmake ..` by default in a given directory 6 | // 7 | // supports either visual studio or jom/nmake and x86/x64 architectures 8 | // 9 | class cmake : public basic_process_runner { 10 | public: 11 | // path to cmake 12 | // 13 | static fs::path binary(); 14 | 15 | // type of build files generated 16 | // 17 | enum class generators { 18 | // generates build files for visual studio 19 | vs = 0x01, 20 | 21 | // generates build files for jom/nmake 22 | jom = 0x02 23 | }; 24 | using enum generators; 25 | 26 | // what run() will do 27 | // 28 | enum class ops { 29 | // generates the build files 30 | generate = 1, 31 | 32 | // build 33 | build, 34 | 35 | // install 36 | install, 37 | 38 | // cleans the build files so they're regenerated from scratch 39 | clean 40 | }; 41 | using enum ops; 42 | 43 | cmake(ops o = generate); 44 | 45 | // sets the generator, defaults to jom 46 | // 47 | cmake& generator(generators g); 48 | 49 | // sets the generator string passed to -G, output() must be set before 50 | // run() because the output path is only created automatically for known 51 | // generators from the enum 52 | // 53 | cmake& generator(const std::string& g); 54 | 55 | // directory where CMakeLists.txt is 56 | // 57 | // by default, the tool will create a build directory in the root with a 58 | // name based on the generator and architecture (see output()), then cd into 59 | // it and invoke `cmake ..` 60 | // 61 | cmake& root(const fs::path& p); 62 | 63 | // set the targets for build 64 | // 65 | cmake& targets(const std::string& target); 66 | cmake& targets(const std::vector& target); 67 | 68 | // set the configuration to build or install 69 | // 70 | cmake& configuration(mob::config config); 71 | 72 | // set the configuration types available when generating 73 | cmake& configuration_types(const std::vector& configs); 74 | 75 | // overrides the directory in which cmake will write build files 76 | // 77 | // by default, this is a directory inside what was given in root() with a 78 | // name based on the generator and architecture (such as "vsbuild" or 79 | // "vsbuild_32") 80 | // 81 | // if generator() was called with a string, output() must be called 82 | // 83 | cmake& output(const fs::path& p); 84 | 85 | // if not empty, the path is passed to cmake with 86 | // `-DCMAKE_INSTALL_PREFIX=path` 87 | // 88 | cmake& prefix(const fs::path& s); 89 | 90 | // adds a variable definition, passed as `-Dname=value` 91 | // 92 | cmake& def(const std::string& name, const std::string& value); 93 | cmake& def(const std::string& name, const fs::path& p); 94 | cmake& def(const std::string& name, const char* s); 95 | 96 | // set a preset to run with cmake --preset 97 | // 98 | cmake& preset(const std::string& s); 99 | 100 | // adds an arbitrary argument, passed verbatim 101 | // 102 | cmake& arg(std::string s); 103 | 104 | // sets the architecture, used along with the generator to create the 105 | // output directory name, but also to get the proper vcvars environment 106 | // variables for the build environment 107 | // 108 | cmake& architecture(arch a); 109 | 110 | // by default, the tool invokes `cmake ..` in the output directory, setting 111 | // this will invoke `cmake cmd` instead 112 | // 113 | cmake& cmd(const std::string& s); 114 | 115 | // returns the path given in output(), if it was set 116 | // 117 | // if not, returns the build path based on the parameters (for example, 118 | // `vsbuild_32/` for a 32-bit arch with the vs generator 119 | // 120 | fs::path build_path() const; 121 | 122 | // returns build_path(), used by task::run_tool() 123 | // 124 | fs::path result() const; 125 | 126 | protected: 127 | // calls either do_clean() or do_generate() 128 | // 129 | void do_run() override; 130 | 131 | private: 132 | // information about a generator available in the `generators` enum, 133 | // returned by all_generators() below 134 | // 135 | // used to generate build directory names and command line parameters 136 | // 137 | struct gen_info { 138 | // name of build directory, "_32" is appended to it for x86 139 | // architectures 140 | std::string dir; 141 | 142 | // generator name, passed to -G 143 | std::string name; 144 | 145 | // names for 32- and 64-bit architectures, passed to -A 146 | std::string x86; 147 | std::string x64; 148 | 149 | // if the corresponding string in `x86` and `x64` just above is not 150 | // empty, returns "-A xxx" depending on the given architecture; returns 151 | // empty otherwise 152 | // 153 | std::string get_arch(arch a) const; 154 | 155 | // for generator that supports it, returns a toolset configuration to set 156 | // the host as specified in the configuration 157 | // 158 | // for VS generator, this returns "-T host=XXX" if conf_host is not empty, 159 | // otherwise returns an empty string 160 | // 161 | std::string get_host(std::string_view conf_host) const; 162 | 163 | // return either `dir` for 64-bit or `dir` + "_32" for 32-bit 164 | // 165 | std::string output_dir(arch a) const; 166 | }; 167 | 168 | // what run() does 169 | ops op_; 170 | 171 | // preset to run 172 | std::string preset_; 173 | 174 | // directory where CMakeLists.txt is 175 | fs::path root_; 176 | 177 | // generator used, either from the enum or as a string 178 | generators gen_; 179 | std::string genstring_; 180 | 181 | // passed as -DCMAKE_INSTALL_PREFIX 182 | fs::path prefix_; 183 | 184 | // targets 185 | std::vector targets_; 186 | 187 | // configuration 188 | mob::config config_{mob::config::relwithdebinfo}; 189 | 190 | // configuration types 191 | std::vector config_types_; 192 | 193 | // passed verbatim 194 | std::vector args_; 195 | 196 | // overrides build directory name 197 | fs::path output_; 198 | 199 | // architecture, used for build directory name and command line 200 | arch arch_; 201 | 202 | // overrides `..` on the command line 203 | std::string cmd_; 204 | 205 | // deletes the build directory 206 | // 207 | void do_clean(); 208 | 209 | // runs cmake 210 | // 211 | void do_generate(); 212 | void do_build(); 213 | void do_install(); 214 | 215 | // returns a list of generators handled by this tool, same ones as in the 216 | // `generators` enum on top 217 | // 218 | static const std::map& all_generators(); 219 | 220 | // returns the generator info for the given enum value 221 | // 222 | static const gen_info& get_generator(generators g); 223 | }; 224 | 225 | } // namespace mob 226 | -------------------------------------------------------------------------------- /src/tools/downloader.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "tools.h" 3 | 4 | namespace mob { 5 | 6 | downloader::downloader(ops o) : tool("dl"), op_(o) {} 7 | 8 | downloader::downloader(mob::url u, ops o) : downloader(o) 9 | { 10 | urls_.push_back(std::move(u)); 11 | } 12 | 13 | downloader& downloader::url(const mob::url& u) 14 | { 15 | urls_.push_back(u); 16 | return *this; 17 | } 18 | 19 | downloader& downloader::file(const fs::path& p) 20 | { 21 | file_ = p; 22 | return *this; 23 | } 24 | 25 | fs::path downloader::result() const 26 | { 27 | return file_; 28 | } 29 | 30 | void downloader::do_run() 31 | { 32 | switch (op_) { 33 | case clean: { 34 | do_clean(); 35 | break; 36 | } 37 | 38 | case download: { 39 | do_download(); 40 | break; 41 | } 42 | 43 | default: { 44 | cx().bail_out(context::net, "bad downloader op {}", op_); 45 | } 46 | } 47 | } 48 | 49 | void downloader::do_download() 50 | { 51 | dl_.reset(new curl_downloader(&cx())); 52 | 53 | cx().trace(context::net, "looking for already downloaded files"); 54 | if (use_existing()) { 55 | cx().trace(context::bypass, "using {}", file_); 56 | return; 57 | } 58 | 59 | cx().trace(context::net, "no cached downloads were found, will try:"); 60 | for (auto&& u : urls_) 61 | cx().trace(context::net, " . {}", u); 62 | 63 | // try them in order 64 | for (auto&& u : urls_) { 65 | if (try_download(u)) { 66 | // done 67 | return; 68 | } 69 | } 70 | 71 | if (interrupted()) { 72 | cx().trace(context::interruption, "interrupted"); 73 | return; 74 | } 75 | 76 | // all failed 77 | cx().bail_out(context::net, "all urls failed to download"); 78 | } 79 | 80 | bool downloader::try_download(const mob::url& u) 81 | { 82 | // when file() wasn't called, the output file is created from the url 83 | if (file_.empty()) 84 | file_ = path_for_url(u); 85 | 86 | // downloading 87 | cx().trace(context::net, "trying {} into {}", u, file_); 88 | dl_->start(u, file_); 89 | 90 | cx().trace(context::net, "waiting for download"); 91 | dl_->join(); 92 | 93 | if (dl_->ok()) { 94 | // done 95 | cx().trace(context::net, "file {} downloaded", file_); 96 | return true; 97 | } 98 | 99 | cx().debug(context::net, "download failed"); 100 | return false; 101 | } 102 | 103 | void downloader::do_clean() 104 | { 105 | if (file_.empty()) { 106 | // file() wasn't called, delete all the files that would be created 107 | // depending on the urls given 108 | 109 | for (auto&& u : urls_) { 110 | const auto file = path_for_url(u); 111 | 112 | cx().debug(context::redownload, "deleting {}", file); 113 | op::delete_file(cx(), file, op::optional); 114 | } 115 | } 116 | else { 117 | // delete the given output file 118 | cx().debug(context::redownload, "deleting {}", file_); 119 | op::delete_file(cx(), file_, op::optional); 120 | } 121 | } 122 | 123 | void downloader::do_interrupt() 124 | { 125 | if (dl_) 126 | dl_->interrupt(); 127 | } 128 | 129 | bool downloader::use_existing() 130 | { 131 | if (file_.empty()) { 132 | // check if one of the files that would be created by a url exists 133 | for (auto&& u : urls_) { 134 | const auto file = path_for_url(u); 135 | 136 | if (fs::exists(file)) { 137 | // take it 138 | file_ = file; 139 | return true; 140 | } 141 | } 142 | } 143 | else { 144 | // file() was called, check if it exists 145 | if (fs::exists(file_)) 146 | return true; 147 | } 148 | 149 | return false; 150 | } 151 | 152 | fs::path downloader::path_for_url(const mob::url& u) const 153 | { 154 | std::string filename; 155 | 156 | std::string url_string = u.string(); 157 | 158 | if (url_string.find("sourceforge.net") != std::string::npos) { 159 | // sf downloads end with /download, strip it to get the filename 160 | const std::string strip = "/download"; 161 | 162 | cx().trace(context::net, "url {} is sourceforge, stripping {} for filename", 163 | u, strip); 164 | 165 | if (url_string.ends_with(strip)) 166 | url_string = url_string.substr(0, url_string.size() - strip.size()); 167 | else 168 | cx().trace(context::net, "no need to strip {}", u); 169 | 170 | filename = mob::url(url_string).filename(); 171 | } 172 | else { 173 | filename = u.filename(); 174 | } 175 | 176 | // downloaded files go in the cache, typically build/downloads/ 177 | return conf().path().cache() / filename; 178 | } 179 | 180 | } // namespace mob 181 | -------------------------------------------------------------------------------- /src/tools/msbuild.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "../core/conf.h" 3 | #include "../core/env.h" 4 | #include "../core/process.h" 5 | #include "tools.h" 6 | 7 | namespace mob { 8 | 9 | msbuild::msbuild(ops o) 10 | : basic_process_runner("msbuild"), op_(o), config_(config::release), 11 | arch_(arch::def), flags_(noflags) 12 | { 13 | } 14 | 15 | fs::path msbuild::binary() 16 | { 17 | return conf().tool().get("msbuild"); 18 | } 19 | 20 | std::string msbuild::configuration_name(config c) 21 | { 22 | switch (c) { 23 | case config::debug: 24 | return "Debug"; 25 | case config::release: 26 | return "Release"; 27 | case config::relwithdebinfo: 28 | [[fallthrough]]; 29 | default: 30 | return "RelWithDebInfo"; 31 | } 32 | } 33 | 34 | msbuild& msbuild::solution(const fs::path& sln) 35 | { 36 | sln_ = sln; 37 | return *this; 38 | } 39 | 40 | msbuild& msbuild::targets(const std::vector& names) 41 | { 42 | targets_ = names; 43 | return *this; 44 | } 45 | 46 | msbuild& msbuild::properties(const std::vector& props) 47 | { 48 | props_ = props; 49 | return *this; 50 | } 51 | 52 | msbuild& msbuild::configuration(config c) 53 | { 54 | config_ = c; 55 | return *this; 56 | } 57 | 58 | msbuild& msbuild::platform(const std::string& s) 59 | { 60 | platform_ = s; 61 | return *this; 62 | } 63 | 64 | msbuild& msbuild::architecture(arch a) 65 | { 66 | arch_ = a; 67 | return *this; 68 | } 69 | 70 | msbuild& msbuild::flags(flags_t f) 71 | { 72 | flags_ = f; 73 | return *this; 74 | } 75 | 76 | msbuild& msbuild::env(const mob::env& e) 77 | { 78 | env_ = e; 79 | return *this; 80 | } 81 | 82 | int msbuild::result() const 83 | { 84 | return exit_code(); 85 | } 86 | 87 | void msbuild::do_run() 88 | { 89 | switch (op_) { 90 | case clean: { 91 | do_clean(); 92 | break; 93 | } 94 | 95 | case build: { 96 | do_build(); 97 | break; 98 | } 99 | 100 | default: { 101 | cx().bail_out(context::generic, "bad msbuild op {}", op_); 102 | } 103 | } 104 | } 105 | 106 | void msbuild::do_build() 107 | { 108 | run_for_targets(targets_); 109 | } 110 | 111 | std::string msbuild::platform_property() const 112 | { 113 | if (!platform_.empty()) 114 | return platform_; 115 | 116 | switch (arch_) { 117 | case arch::x86: 118 | return "Win32"; 119 | 120 | case arch::x64: 121 | return "x64"; 122 | 123 | case arch::dont_care: 124 | default: 125 | cx().bail_out(context::generic, "msbuild::do_run(): bad arch"); 126 | } 127 | } 128 | 129 | void msbuild::run_for_targets(const std::vector& targets) 130 | { 131 | // 14.2 to v142 132 | const auto toolset = "v" + replace_all(vs::toolset(), ".", ""); 133 | 134 | process p; 135 | 136 | if (is_set(flags_, allow_failure)) { 137 | // make sure errors are not displayed and mob doesn't bail out 138 | p.stderr_level(context::level::trace).flags(process::allow_failure); 139 | } 140 | else { 141 | p.stdout_filter([&](auto& f) { 142 | // ": error C2065" 143 | // ": error MSB1009" 144 | static std::regex re(": error [A-Z]"); 145 | 146 | // ghetto attempt at showing errors on the console, since stdout 147 | // has all the compiler output 148 | if (std::regex_search(f.line.begin(), f.line.end(), re)) 149 | f.lv = context::level::error; 150 | }); 151 | } 152 | 153 | // msbuild will use the console's encoding, so by invoking `chcp 65001` 154 | // (the utf8 "codepage"), stdout and stderr are utf8 155 | p.binary(binary()) 156 | .chcp(65001) 157 | .stdout_encoding(encodings::utf8) 158 | .stderr_encoding(encodings::utf8) 159 | .arg("-nologo"); 160 | 161 | if (!is_set(flags_, single_job)) { 162 | // multi-process 163 | p.arg("-maxCpuCount") 164 | .arg("-property:UseMultiToolTask=true") 165 | .arg("-property:EnforceProcessCountAcrossBuilds=true"); 166 | } 167 | 168 | p.arg("-property:Configuration=", configuration_name(config_), process::quote) 169 | .arg("-property:PlatformToolset=" + toolset) 170 | .arg("-property:WindowsTargetPlatformVersion=" + vs::sdk()) 171 | .arg("-property:Platform=", platform_property(), process::quote) 172 | .arg("-verbosity:minimal", process::log_quiet) 173 | .arg("-consoleLoggerParameters:ErrorsOnly", process::log_quiet); 174 | 175 | // some projects have code analysis turned on and can fail on preview 176 | // versions, make sure it's never run 177 | p.arg("-property:RunCodeAnalysis=false"); 178 | 179 | // targets 180 | if (!targets.empty()) 181 | p.arg("-target:" + mob::join(targets, ";")); 182 | 183 | // properties 184 | for (auto&& prop : props_) 185 | p.arg("-property:" + prop); 186 | 187 | p.arg(sln_).cwd(sln_.parent_path()).env(env_ ? *env_ : env::vs(arch_)); 188 | 189 | execute_and_join(p); 190 | } 191 | 192 | void msbuild::do_clean() 193 | { 194 | flags_ |= allow_failure; 195 | run_for_targets(map(targets_, [&](auto&& t) { 196 | return t + ":Clean"; 197 | })); 198 | } 199 | 200 | } // namespace mob 201 | -------------------------------------------------------------------------------- /src/tools/msbuild.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../core/conf.h" 4 | #include "../core/env.h" 5 | #include "cmake.h" 6 | 7 | namespace mob { 8 | 9 | // tool that runs `msbuild`, see the jom tool for explanations on errors with 10 | // parallel builds and the single_job/allow_failure flags, the same thing 11 | // happens with msbuild 12 | // 13 | class msbuild : public basic_process_runner { 14 | public: 15 | // path to the msbuild binary 16 | // 17 | static fs::path binary(); 18 | 19 | // retrieve the name of the configuration for the given config 20 | // 21 | static std::string configuration_name(config c); 22 | 23 | enum class flags_t { noflags = 0x00, single_job = 0x01, allow_failure = 0x02 }; 24 | using enum flags_t; 25 | 26 | // what run() should do 27 | // 28 | enum class ops { build = 1, clean }; 29 | using enum ops; 30 | 31 | msbuild(ops o = build); 32 | 33 | // .sln file 34 | // 35 | msbuild& solution(const fs::path& sln); 36 | 37 | // adds a "-target:string` for each string given 38 | // 39 | msbuild& targets(const std::vector& names); 40 | 41 | // adds a "-property:string" for every string given 42 | // 43 | msbuild& properties(const std::vector& props); 44 | 45 | // sets "-property:Configuration=s" 46 | // 47 | msbuild& configuration(config config); 48 | 49 | // sets "-property:Platform=s"; if not set, uses architecture() to figure 50 | // it out 51 | // 52 | msbuild& platform(const std::string& s); 53 | 54 | // used by 55 | // 1) the vsvars environment variables 56 | // 2) the "-property:Platform" property if platform() wasn't called 57 | // 58 | msbuild& architecture(arch a); 59 | 60 | // flags 61 | msbuild& flags(flags_t f); 62 | 63 | // override the environment variables, which normally defaults to env::vs() 64 | // for the arch given in architecture() 65 | // 66 | msbuild& env(const mob::env& e); 67 | 68 | // exit code 69 | // 70 | int result() const; 71 | 72 | protected: 73 | void do_run() override; 74 | 75 | private: 76 | ops op_; 77 | fs::path sln_; 78 | std::vector targets_; 79 | std::vector props_; 80 | config config_; 81 | std::string platform_; 82 | arch arch_; 83 | flags_t flags_; 84 | std::optional env_; 85 | std::vector prepend_path_; 86 | 87 | // runs msbuild with ":Clean" for each target given in targets(), giving 88 | // something like "-target:modorganizer:Clean" 89 | // 90 | void do_clean(); 91 | 92 | // runs msbuild 93 | // 94 | void do_build(); 95 | 96 | // called by both do_clean() and do_build 97 | // 98 | void run_for_targets(const std::vector& targets); 99 | 100 | std::string platform_property() const; 101 | }; 102 | 103 | MOB_ENUM_OPERATORS(msbuild::flags_t); 104 | 105 | } // namespace mob 106 | -------------------------------------------------------------------------------- /src/tools/pch.h: -------------------------------------------------------------------------------- 1 | // for intellisense 2 | #include "../pch.h" 3 | -------------------------------------------------------------------------------- /src/tools/process_runner.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "../core/conf.h" 3 | #include "../core/process.h" 4 | #include "tools.h" 5 | 6 | namespace mob { 7 | 8 | basic_process_runner::basic_process_runner(std::string name) 9 | : tool(std::move(name)), p_(nullptr), code_(0) 10 | { 11 | } 12 | 13 | // anchors 14 | basic_process_runner::basic_process_runner(basic_process_runner&& r) = default; 15 | basic_process_runner::~basic_process_runner() = default; 16 | 17 | void basic_process_runner::do_interrupt() 18 | { 19 | if (p_) 20 | p_->interrupt(); 21 | } 22 | 23 | int basic_process_runner::execute_and_join(process& p) 24 | { 25 | // remember the process for do_interrupt() 26 | p_ = &p; 27 | 28 | // use the process' name for this tool 29 | set_name(p.name()); 30 | 31 | // use this tool's log context for the process 32 | p.set_context(&cx()); 33 | 34 | // run, remember the code because the process object might be destroyed 35 | code_ = p.run_and_join(); 36 | 37 | return code_; 38 | } 39 | 40 | int basic_process_runner::exit_code() const 41 | { 42 | return code_; 43 | } 44 | 45 | process_runner::process_runner(process& p) : tool(p.name()), p_(p) {} 46 | 47 | // anchor 48 | process_runner::~process_runner() = default; 49 | 50 | void process_runner::do_run() 51 | { 52 | // use the process' name for this tool 53 | set_name(p_.name()); 54 | 55 | // use this tool's log context for the process 56 | p_.set_context(&cx()); 57 | 58 | // run 59 | p_.run_and_join(); 60 | } 61 | 62 | int process_runner::result() const 63 | { 64 | return p_.exit_code(); 65 | } 66 | 67 | void process_runner::do_interrupt() 68 | { 69 | p_.interrupt(); 70 | } 71 | 72 | } // namespace mob 73 | -------------------------------------------------------------------------------- /src/utility.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "utility.h" 3 | #include "core/conf.h" 4 | #include "core/context.h" 5 | #include "core/op.h" 6 | #include "net.h" 7 | 8 | namespace mob { 9 | 10 | std::string mob_version() 11 | { 12 | return "mob 5.0"; 13 | } 14 | 15 | url make_prebuilt_url(const std::string& filename) 16 | { 17 | return "https://github.com/ModOrganizer2/mob/" 18 | "releases/download/2.5-dependencies/" + 19 | filename; 20 | } 21 | 22 | url make_appveyor_artifact_url(arch a, const std::string& project, 23 | const std::string& filename) 24 | { 25 | std::string arch_s; 26 | 27 | switch (a) { 28 | case arch::x86: 29 | arch_s = "x86"; 30 | break; 31 | 32 | case arch::x64: 33 | arch_s = "x64"; 34 | break; 35 | 36 | case arch::dont_care: 37 | default: 38 | gcx().bail_out(context::generic, "bad arch"); 39 | } 40 | 41 | return "https://ci.appveyor.com/api/projects/Modorganizer2/" + project + 42 | "/artifacts/" + filename + "?job=Platform:%20" + arch_s; 43 | } 44 | 45 | } // namespace mob 46 | -------------------------------------------------------------------------------- /src/utility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utility/algo.h" 4 | #include "utility/enum.h" 5 | #include "utility/fs.h" 6 | #include "utility/io.h" 7 | #include "utility/string.h" 8 | #include "utility/threading.h" 9 | 10 | namespace mob { 11 | 12 | // time since mob started 13 | // 14 | std::chrono::nanoseconds timestamp(); 15 | 16 | // exception thrown when a task failed 17 | // 18 | class bailed { 19 | public: 20 | bailed(std::string s = {}) : s_(std::move(s)) {} 21 | 22 | const char* what() const { return s_.c_str(); } 23 | 24 | private: 25 | std::string s_; 26 | }; 27 | 28 | // executes the given function in the destructor 29 | // 30 | template 31 | class guard { 32 | public: 33 | guard(F f) : f_(f) {} 34 | 35 | ~guard() { f_(); } 36 | 37 | private: 38 | F f_; 39 | }; 40 | 41 | enum class arch { 42 | x86 = 1, 43 | x64, 44 | dont_care, 45 | 46 | def = x64 47 | }; 48 | 49 | enum class config { debug, relwithdebinfo, release }; 50 | 51 | class url; 52 | 53 | // returns "mob x.y" 54 | // 55 | std::string mob_version(); 56 | 57 | } // namespace mob 58 | -------------------------------------------------------------------------------- /src/utility/algo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mob { 4 | 5 | template 6 | class repeat_iterator { 7 | public: 8 | repeat_iterator(const T& s) : s_(s) {} 9 | 10 | bool operator==(const repeat_iterator&) const { return false; } 11 | 12 | const T& operator*() const { return s_; } 13 | 14 | repeat_iterator& operator++() 15 | { 16 | // no-op 17 | return *this; 18 | } 19 | 20 | private: 21 | const T& s_; 22 | }; 23 | 24 | template 25 | class repeat_range { 26 | public: 27 | using value_type = T; 28 | using const_iterator = repeat_iterator; 29 | 30 | repeat_range(const T& s) : s_(s) {} 31 | 32 | const_iterator begin() const { return const_iterator(s_); } 33 | 34 | const_iterator end() const { return const_iterator(s_); } 35 | 36 | private: 37 | T s_; 38 | }; 39 | 40 | template 41 | repeat_iterator begin(const repeat_range& r) 42 | { 43 | return r.begin(); 44 | } 45 | 46 | template 47 | repeat_iterator end(const repeat_range& r) 48 | { 49 | return r.end(); 50 | } 51 | 52 | template 53 | struct repeat_converter { 54 | using value_type = T; 55 | }; 56 | 57 | template <> 58 | struct repeat_converter { 59 | using value_type = std::string; 60 | }; 61 | 62 | template 63 | struct repeat_converter { 64 | using value_type = std::string; 65 | }; 66 | 67 | template 68 | struct repeat_converter { 69 | using value_type = std::string; 70 | }; 71 | 72 | // a range that infinitely return `s`, can be used with zip() to create a pair 73 | // with a repeating value 74 | // 75 | template 76 | auto repeat(const T& s) 77 | { 78 | return repeat_range::value_type>(s); 79 | } 80 | 81 | // returns a container of pairs from both ranges; if the ranges are not the 82 | // same size, truncates to the smallest one 83 | // 84 | template >> 87 | Container zip(const Range1& range1, const Range2& range2) 88 | { 89 | Container out; 90 | 91 | auto itor1 = begin(range1); 92 | auto end1 = end(range1); 93 | 94 | auto itor2 = begin(range2); 95 | auto end2 = end(range2); 96 | 97 | for (;;) { 98 | if (itor1 == end1 || itor2 == end2) 99 | break; 100 | 101 | out.push_back({*itor1, *itor2}); 102 | ++itor1; 103 | ++itor2; 104 | } 105 | 106 | return out; 107 | } 108 | 109 | // returns a vector containing the result of `f(e)` for each element `e` of `v` 110 | // 111 | template 112 | auto map(const Cont& v, F&& f) 113 | { 114 | using mapped_type = decltype(f(std::declval())); 115 | std::vector out; 116 | 117 | for (auto&& e : v) 118 | out.push_back(f(e)); 119 | 120 | return out; 121 | } 122 | 123 | } // namespace mob 124 | -------------------------------------------------------------------------------- /src/utility/assert.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "assert.h" 3 | #include "../core/context.h" 4 | 5 | namespace mob { 6 | 7 | void mob_assertion_failed(const char* message, const char* exp, const wchar_t* file, 8 | int line, const char* func) 9 | { 10 | if (message) { 11 | gcx().error(context::generic, "assertion failed: {}:{} {}: {} ({})", 12 | std::wstring(file), line, func, message, exp); 13 | } 14 | else { 15 | gcx().error(context::generic, "assertion failed: {}:{} {}: '{}'", 16 | std::wstring(file), line, func, exp); 17 | } 18 | 19 | if (IsDebuggerPresent()) 20 | DebugBreak(); 21 | else 22 | std::exit(1); 23 | } 24 | 25 | } // namespace mob 26 | -------------------------------------------------------------------------------- /src/utility/assert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mob { 4 | 5 | #define MOB_WIDEN2(x) L##x 6 | #define MOB_WIDEN(x) MOB_WIDEN2(x) 7 | #define MOB_FILE_UTF16 MOB_WIDEN(__FILE__) 8 | 9 | #define MOB_ASSERT(x, ...) \ 10 | mob_assert(x, __VA_ARGS__, #x, MOB_FILE_UTF16, __LINE__, __FUNCSIG__); 11 | 12 | void mob_assertion_failed(const char* message, const char* exp, const wchar_t* file, 13 | int line, const char* func); 14 | 15 | template 16 | inline void mob_assert(X&& x, const char* message, const char* exp, 17 | const wchar_t* file, int line, const char* func) 18 | { 19 | if (!(x)) 20 | mob_assertion_failed(message, exp, file, line, func); 21 | } 22 | 23 | template 24 | inline void mob_assert(X&& x, const char* exp, const wchar_t* file, int line, 25 | const char* func) 26 | { 27 | if (!(x)) 28 | mob_assertion_failed(nullptr, exp, file, line, func); 29 | } 30 | 31 | } // namespace mob 32 | -------------------------------------------------------------------------------- /src/utility/enum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mob { 4 | 5 | #define MOB_ENUM_OPERATORS(E) \ 6 | inline E operator|(E e1, E e2) \ 7 | { \ 8 | return (E)((int)e1 | (int)e2); \ 9 | } \ 10 | inline E operator&(E e1, E e2) \ 11 | { \ 12 | return (E)((int)e1 & (int)e2); \ 13 | } \ 14 | inline E operator|=(E& e1, E e2) \ 15 | { \ 16 | e1 = e1 | e2; \ 17 | return e1; \ 18 | } 19 | 20 | #define MOB_ENUM_FRIEND_OPERATORS(E) \ 21 | inline friend E operator|(E e1, E e2) \ 22 | { \ 23 | return (E)((int)e1 | (int)e2); \ 24 | } \ 25 | inline friend E operator&(E e1, E e2) \ 26 | { \ 27 | return (E)((int)e1 & (int)e2); \ 28 | } \ 29 | inline friend E operator|=(E& e1, E e2) \ 30 | { \ 31 | e1 = e1 | e2; \ 32 | return e1; \ 33 | } 34 | 35 | template 36 | bool is_set(E e, E v) 37 | { 38 | static_assert(std::is_enum_v, "this is for enums"); 39 | return (e & v) == v; 40 | } 41 | 42 | template 43 | bool is_any_set(E e, E v) 44 | { 45 | static_assert(std::is_enum_v, "this is for enums"); 46 | return (e & v) != E(0); 47 | } 48 | 49 | } // namespace mob 50 | -------------------------------------------------------------------------------- /src/utility/fs.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "fs.h" 3 | #include "../core/conf.h" 4 | #include "../core/context.h" 5 | #include "../core/op.h" 6 | #include "../utility.h" 7 | 8 | namespace mob { 9 | 10 | fs::path make_temp_file() 11 | { 12 | static fs::path dir = conf().path().temp_dir(); 13 | 14 | wchar_t name[MAX_PATH + 1] = {}; 15 | if (GetTempFileNameW(dir.native().c_str(), L"mob", 0, name) == 0) { 16 | const auto e = GetLastError(); 17 | 18 | gcx().bail_out(context::conf, "can't create temp file in {}, {}", dir, 19 | error_message(e)); 20 | } 21 | 22 | return dir / name; 23 | } 24 | 25 | file_deleter::file_deleter(const context& cx, fs::path p) 26 | : cx_(cx), p_(std::move(p)), delete_(true) 27 | { 28 | cx_.trace(context::fs, "will delete {} if things go bad", p_); 29 | } 30 | 31 | file_deleter::~file_deleter() 32 | { 33 | if (delete_) 34 | delete_now(); 35 | } 36 | 37 | void file_deleter::delete_now() 38 | { 39 | cx_.debug(context::fs, "something went bad, deleting {}", p_); 40 | op::delete_file(cx_, p_, op::optional); 41 | } 42 | 43 | void file_deleter::cancel() 44 | { 45 | cx_.trace(context::fs, "everything okay, keeping {}", p_); 46 | delete_ = false; 47 | } 48 | 49 | directory_deleter::directory_deleter(const context& cx, fs::path p) 50 | : cx_(cx), p_(std::move(p)), delete_(true) 51 | { 52 | cx_.trace(context::fs, "will delete {} if things go bad", p_); 53 | } 54 | 55 | directory_deleter::~directory_deleter() 56 | { 57 | if (delete_) 58 | delete_now(); 59 | } 60 | 61 | void directory_deleter::delete_now() 62 | { 63 | cx_.debug(context::fs, "something went bad, deleting {}", p_); 64 | op::delete_directory(cx_, p_, op::optional); 65 | } 66 | 67 | void directory_deleter::cancel() 68 | { 69 | cx_.trace(context::fs, "everything okay, keeping {}", p_); 70 | delete_ = false; 71 | } 72 | 73 | interruption_file::interruption_file(const context& cx, fs::path dir, 74 | std::string name) 75 | : cx_(cx), dir_(std::move(dir)), name_(std::move(name)) 76 | { 77 | if (fs::exists(file())) 78 | cx_.trace(context::interruption, "found interrupt file {}", file()); 79 | } 80 | 81 | bool interruption_file::exists() const 82 | { 83 | return fs::exists(file()); 84 | } 85 | 86 | fs::path interruption_file::file() const 87 | { 88 | return dir_ / ("_mo_interrupted_" + name_); 89 | } 90 | 91 | void interruption_file::create() 92 | { 93 | cx_.trace(context::interruption, "creating interrupt file {}", file()); 94 | op::touch(cx_, file()); 95 | } 96 | 97 | void interruption_file::remove() 98 | { 99 | cx_.trace(context::interruption, "removing interrupt file {}", file()); 100 | op::delete_file(cx_, file()); 101 | } 102 | 103 | bypass_file::bypass_file(const context& cx, fs::path dir, std::string name) 104 | : cx_(cx), file_(dir / ("_mob_" + name)) 105 | { 106 | } 107 | 108 | bool bypass_file::exists() const 109 | { 110 | if (fs::exists(file_)) { 111 | if (conf().global().rebuild()) { 112 | cx_.trace(context::rebuild, "bypass file {} exists, deleting", file_); 113 | 114 | op::delete_file(cx_, file_, op::optional); 115 | 116 | return false; 117 | } 118 | else { 119 | cx_.trace(context::bypass, "bypass file {} exists", file_); 120 | return true; 121 | } 122 | } 123 | else { 124 | cx_.trace(context::bypass, "bypass file {} not found", file_); 125 | return false; 126 | } 127 | } 128 | 129 | void bypass_file::create() 130 | { 131 | cx_.trace(context::bypass, "create bypass file {}", file_); 132 | op::touch(cx_, file_); 133 | } 134 | 135 | } // namespace mob 136 | -------------------------------------------------------------------------------- /src/utility/fs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mob { 4 | 5 | class context; 6 | 7 | // returns a unique filename in the temp directory; requires inis to be loaded 8 | // 9 | fs::path make_temp_file(); 10 | 11 | struct handle_closer { 12 | using pointer = HANDLE; 13 | 14 | void operator()(HANDLE h) 15 | { 16 | if (h != INVALID_HANDLE_VALUE) 17 | ::CloseHandle(h); 18 | } 19 | }; 20 | 21 | using handle_ptr = std::unique_ptr; 22 | 23 | struct file_closer { 24 | void operator()(std::FILE* f) 25 | { 26 | if (f) 27 | std::fclose(f); 28 | } 29 | }; 30 | 31 | using file_ptr = std::unique_ptr; 32 | 33 | // deletes the given file in the destructor unless cancel() is called 34 | // 35 | class file_deleter { 36 | public: 37 | file_deleter(const context& cx, fs::path p); 38 | file_deleter(const file_deleter&) = delete; 39 | file_deleter& operator=(const file_deleter&) = delete; 40 | ~file_deleter(); 41 | 42 | void delete_now(); 43 | void cancel(); 44 | 45 | private: 46 | const context& cx_; 47 | fs::path p_; 48 | bool delete_; 49 | }; 50 | 51 | // deletes the given directory in the destructor unless cancel is called 52 | // 53 | class directory_deleter { 54 | public: 55 | directory_deleter(const context& cx, fs::path p); 56 | directory_deleter(const directory_deleter&) = delete; 57 | directory_deleter& operator=(const directory_deleter&) = delete; 58 | ~directory_deleter(); 59 | 60 | void delete_now(); 61 | void cancel(); 62 | 63 | private: 64 | const context& cx_; 65 | fs::path p_; 66 | bool delete_; 67 | }; 68 | 69 | // creates a temporary file in the given directory that is used to detect 70 | // crashes or interruptions; some prefix is added to the filename to try and 71 | // make it unlikely to clash 72 | // 73 | // for example: 74 | // 75 | // interruption_file ifile("some/dir", "some action"); 76 | // 77 | // if (ifile.exists()) 78 | // { 79 | // // action was previously interrupted, do something about it 80 | // } 81 | // 82 | // // create interruption file 83 | // ifile.create(); 84 | // 85 | // // do stuff that might fail and throw or return early 86 | // 87 | // // success, remove 88 | // ifile.remove(); 89 | // 90 | class interruption_file { 91 | public: 92 | interruption_file(const context& cx, fs::path dir, std::string name); 93 | 94 | // path to the interruption file 95 | // 96 | fs::path file() const; 97 | 98 | // whether the file exists 99 | // 100 | bool exists() const; 101 | 102 | // creates the interruption file 103 | // 104 | void create(); 105 | 106 | // removes the interruption file 107 | // 108 | void remove(); 109 | 110 | private: 111 | const context& cx_; 112 | fs::path dir_; 113 | std::string name_; 114 | }; 115 | 116 | // creates a file in the given directory that is used to bypass an operation 117 | // in the future; some prefix is added to the filename to try and make it 118 | // unlikely to clash 119 | // 120 | // for example: 121 | // 122 | // bypass_file built(cx, "some/dir/", "built"); 123 | // 124 | // if (built.exists()) 125 | // { 126 | // // already built, bypass 127 | // return; 128 | // } 129 | // 130 | // // do the build process 131 | // 132 | // // bypass next time 133 | // built.create(); 134 | // 135 | class bypass_file { 136 | public: 137 | bypass_file(const context& cx, fs::path dir, std::string name); 138 | 139 | // whether the bypass file exists 140 | // 141 | bool exists() const; 142 | 143 | // creates the bypass file 144 | // 145 | void create(); 146 | 147 | private: 148 | const context& cx_; 149 | fs::path file_; 150 | }; 151 | 152 | } // namespace mob 153 | -------------------------------------------------------------------------------- /src/utility/io.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "io.h" 3 | #include "string.h" 4 | 5 | namespace mob { 6 | 7 | enum class color_methods { none = 0, ansi, console }; 8 | 9 | // returns whether the given standard handle is for a console or is redirected 10 | // somewhere else 11 | // 12 | bool is_handle_console(DWORD handle) 13 | { 14 | DWORD d = 0; 15 | 16 | if (GetConsoleMode(GetStdHandle(handle), &d)) { 17 | // this is a console 18 | return true; 19 | } 20 | 21 | return false; 22 | } 23 | 24 | // figures out if the terminal supports ansi color codes; the old conhost 25 | // doesn't, but the new terminal does 26 | // 27 | // returns color_methods::none if the output is not a console 28 | // 29 | color_methods get_color_method() 30 | { 31 | DWORD d = 0; 32 | 33 | if (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &d)) { 34 | if ((d & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0) 35 | return color_methods::console; 36 | else 37 | return color_methods::ansi; 38 | } 39 | 40 | // not a console 41 | return color_methods::none; 42 | } 43 | 44 | // global output mutex, avoids interleaving 45 | static std::mutex g_output_mutex; 46 | 47 | // streams 48 | extern u8stream u8cout(false); 49 | extern u8stream u8cerr(true); 50 | 51 | // whether stdout and stderr are for console, only check once 52 | static bool stdout_console = is_handle_console(STD_OUTPUT_HANDLE); 53 | static bool stderr_console = is_handle_console(STD_ERROR_HANDLE); 54 | 55 | // color method supported by terminal, only check once 56 | static color_methods g_color_method = get_color_method(); 57 | 58 | void set_std_streams() 59 | { 60 | // only set to utf16 when the output is the console 61 | 62 | if (stdout_console) 63 | _setmode(_fileno(stdout), _O_U16TEXT); 64 | 65 | if (stderr_console) 66 | _setmode(_fileno(stderr), _O_U16TEXT); 67 | } 68 | 69 | std::mutex& global_output_mutex() 70 | { 71 | return g_output_mutex; 72 | } 73 | 74 | yn ask_yes_no(const std::string& text, yn def) 75 | { 76 | u8cout << text << (text.empty() ? "" : " ") 77 | << (def == yn::yes ? "[Y/n]" : "[y/N]") << " "; 78 | 79 | // stdin is not utf8 80 | std::string line; 81 | std::getline(std::cin, line); 82 | 83 | // ctrl+c 84 | if (!std::cin) 85 | return yn::cancelled; 86 | 87 | if (line.empty()) 88 | return def; 89 | else if (line == "y" || line == "Y") 90 | return yn::yes; 91 | else if (line == "n" || line == "N") 92 | return yn::no; 93 | else 94 | return yn::cancelled; 95 | } 96 | 97 | void u8stream::do_output(const std::string& s) 98 | { 99 | std::scoped_lock lock(g_output_mutex); 100 | 101 | if (err_) { 102 | if (stderr_console) 103 | std::wcerr << utf8_to_utf16(s); 104 | else 105 | std::cerr << s; 106 | } 107 | else { 108 | if (stdout_console) 109 | std::wcout << utf8_to_utf16(s); 110 | else 111 | std::cout << s; 112 | } 113 | } 114 | 115 | void u8stream::write_ln(std::string_view utf8) 116 | { 117 | std::scoped_lock lock(g_output_mutex); 118 | 119 | if (err_) { 120 | if (stderr_console) 121 | std::wcerr << utf8_to_utf16(utf8) << L"\n"; 122 | else 123 | std::cerr << utf8 << "\n"; 124 | } 125 | else { 126 | if (stdout_console) 127 | std::wcout << utf8_to_utf16(utf8) << L"\n"; 128 | else 129 | std::cout << utf8 << "\n"; 130 | } 131 | } 132 | 133 | console_color::console_color() : reset_(false), old_atts_(0) {} 134 | 135 | console_color::console_color(colors c) : reset_(false), old_atts_(0) 136 | { 137 | if (g_color_method == color_methods::ansi) { 138 | switch (c) { 139 | case colors::white: 140 | break; 141 | 142 | case colors::grey: 143 | reset_ = true; 144 | u8cout << "\033[38;2;150;150;150m"; 145 | break; 146 | 147 | case colors::yellow: 148 | reset_ = true; 149 | u8cout << "\033[38;2;240;240;50m"; 150 | break; 151 | 152 | case colors::red: 153 | reset_ = true; 154 | u8cout << "\033[38;2;240;50;50m"; 155 | break; 156 | } 157 | } 158 | else if (g_color_method == color_methods::console) { 159 | CONSOLE_SCREEN_BUFFER_INFO bi = {}; 160 | GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &bi); 161 | old_atts_ = bi.wAttributes; 162 | 163 | WORD atts = 0; 164 | 165 | switch (c) { 166 | case colors::white: 167 | break; 168 | 169 | case colors::grey: 170 | reset_ = true; 171 | atts = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; 172 | break; 173 | 174 | case colors::yellow: 175 | reset_ = true; 176 | atts = FOREGROUND_GREEN | FOREGROUND_RED; 177 | break; 178 | 179 | case colors::red: 180 | reset_ = true; 181 | atts = FOREGROUND_RED; 182 | break; 183 | } 184 | 185 | if (atts != 0) 186 | SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), atts); 187 | } 188 | } 189 | 190 | console_color::~console_color() 191 | { 192 | if (!reset_) 193 | return; 194 | 195 | if (g_color_method == color_methods::ansi) { 196 | u8cout << "\033[39m\033[49m"; 197 | } 198 | else if (g_color_method == color_methods::console) { 199 | SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), old_atts_); 200 | } 201 | } 202 | 203 | font_restorer::font_restorer() : restore_(false) 204 | { 205 | std::memset(&old_, 0, sizeof(old_)); 206 | old_.cbSize = sizeof(old_); 207 | 208 | if (GetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &old_)) 209 | restore_ = true; 210 | } 211 | 212 | font_restorer::~font_restorer() 213 | { 214 | if (!restore_) 215 | return; 216 | 217 | CONSOLE_FONT_INFOEX now = {}; 218 | now.cbSize = sizeof(now); 219 | 220 | if (!GetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &now)) 221 | return; 222 | 223 | if (std::wcsncmp(old_.FaceName, now.FaceName, LF_FACESIZE) != 0) 224 | restore(); 225 | } 226 | 227 | void font_restorer::restore() 228 | { 229 | ::SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &old_); 230 | } 231 | 232 | } // namespace mob 233 | -------------------------------------------------------------------------------- /src/utility/io.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mob { 4 | 5 | // sets the current console color in the constructor, restores it in the 6 | // destructor 7 | // 8 | class console_color { 9 | public: 10 | enum colors { white, grey, yellow, red }; 11 | 12 | // no-op 13 | // 14 | console_color(); 15 | 16 | // sets the given color in the console 17 | // 18 | console_color(colors c); 19 | 20 | // resets the color 21 | // 22 | ~console_color(); 23 | 24 | // non-copyable 25 | console_color(const console_color&) = delete; 26 | console_color& operator=(const console_color&) = delete; 27 | 28 | private: 29 | // true when the color was changed and must be reset in the destructor 30 | bool reset_; 31 | 32 | // old color 33 | WORD old_atts_; 34 | }; 35 | 36 | // a stream that accepts utf8 strings and writes them to stdout/stderr 37 | // 38 | // if stdout/stderr is a console, converts to utf16 and outputs; if it's 39 | // redirected, outputs utf8 directly 40 | // 41 | // thread-safe, no interleaving 42 | // 43 | class u8stream { 44 | public: 45 | // use stderr when err is true, stdout otherwise 46 | // 47 | u8stream(bool err) : err_(err) {} 48 | 49 | // outputs arguments without a newline 50 | // 51 | template 52 | u8stream& operator<<(Args&&... args) 53 | { 54 | std::ostringstream oss; 55 | ((oss << std::forward(args)), ...); 56 | 57 | do_output(oss.str()); 58 | 59 | return *this; 60 | } 61 | 62 | // outputs given string followed by a newline 63 | // 64 | void write_ln(std::string_view utf8); 65 | 66 | private: 67 | // whether this is stdout or stderr 68 | bool err_; 69 | 70 | // outputs the given utf8 string 71 | // 72 | void do_output(const std::string& utf8); 73 | }; 74 | 75 | // stdout 76 | extern u8stream u8cout; 77 | 78 | // stderr 79 | extern u8stream u8cerr; 80 | 81 | // called from main(), changes the standard output to utf-16 82 | // 83 | void set_std_streams(); 84 | 85 | // the global mutex used to avoid interleaving 86 | // 87 | std::mutex& global_output_mutex(); 88 | 89 | enum class yn { no = 0, yes, cancelled }; 90 | 91 | // asks the user for y/n 92 | // 93 | yn ask_yes_no(const std::string& text, yn def); 94 | 95 | // see https://github.com/isanae/mob/issues/4 96 | // 97 | // this saves the current console font in the constructor and restores it in 98 | // the destructor if it changed 99 | // 100 | class font_restorer { 101 | public: 102 | font_restorer(); 103 | ~font_restorer(); 104 | 105 | void restore(); 106 | 107 | private: 108 | CONSOLE_FONT_INFOEX old_; 109 | bool restore_; 110 | }; 111 | 112 | } // namespace mob 113 | -------------------------------------------------------------------------------- /src/utility/pch.h: -------------------------------------------------------------------------------- 1 | // for intellisense 2 | #include "../pch.h" 3 | -------------------------------------------------------------------------------- /src/utility/threading.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "threading.h" 3 | #include "../utility.h" 4 | 5 | namespace mob { 6 | 7 | constexpr std::size_t max_name_length = 1000; 8 | constexpr std::size_t max_frames = 100; 9 | const std::size_t exception_message_length = 5000; 10 | 11 | static void* frame_addresses[max_frames]; 12 | static wchar_t undecorated_name[max_name_length + 1] = {}; 13 | static unsigned char sym_buffer[sizeof(SYMBOL_INFOW) + max_name_length]; 14 | static SYMBOL_INFOW* sym = (SYMBOL_INFOW*)sym_buffer; 15 | static wchar_t exception_message[exception_message_length + 1] = {}; 16 | 17 | static LPTOP_LEVEL_EXCEPTION_FILTER g_previous_handler = nullptr; 18 | 19 | void dump_stacktrace(const wchar_t* what) 20 | { 21 | // don't use 8ucout, don't lock the global out mutex, this can be called 22 | // while the mutex is locked 23 | std::wcerr << what << "\n\nmob has crashed\n" 24 | << L"*****************************\n\n" 25 | << what << L"\n\n"; 26 | 27 | HANDLE process = INVALID_HANDLE_VALUE; 28 | 29 | DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), 30 | &process, 0, false, DUPLICATE_SAME_ACCESS); 31 | 32 | SymSetOptions(SymGetOptions() & (~SYMOPT_UNDNAME)); 33 | SymInitializeW(process, NULL, TRUE); 34 | 35 | const std::size_t frame_count = 36 | CaptureStackBackTrace(0, max_frames, frame_addresses, nullptr); 37 | 38 | for (std::size_t i = 0; i < frame_count; ++i) { 39 | DWORD disp = 0; 40 | IMAGEHLP_LINEW64 line = {0}; 41 | line.SizeOfStruct = sizeof(line); 42 | 43 | std::wcerr << frame_addresses[i] << L" "; 44 | 45 | if (SymGetLineFromAddrW64(process, 46 | reinterpret_cast(frame_addresses[i]), 47 | &disp, &line)) { 48 | std::wcerr << line.FileName << L":" << line.LineNumber << L" "; 49 | } 50 | 51 | DWORD64 disp2 = 0; 52 | 53 | sym->MaxNameLen = max_name_length; 54 | sym->SizeOfStruct = sizeof(SYMBOL_INFOW); 55 | 56 | if (SymFromAddrW(process, reinterpret_cast(frame_addresses[i]), 57 | &disp2, sym)) { 58 | const DWORD und_length = UnDecorateSymbolNameW( 59 | sym->Name, undecorated_name, max_name_length, UNDNAME_COMPLETE); 60 | 61 | std::wcerr << undecorated_name; 62 | } 63 | 64 | std::wcerr << L"\n"; 65 | } 66 | 67 | if (IsDebuggerPresent()) 68 | DebugBreak(); 69 | else 70 | TerminateProcess(GetCurrentProcess(), 0xffff); 71 | } 72 | 73 | void terminate_handler() noexcept 74 | { 75 | try { 76 | std::rethrow_exception(std::current_exception()); 77 | } 78 | catch (std::exception& e) { 79 | auto* p = exception_message; 80 | std::size_t remaining = exception_message_length; 81 | 82 | const int n = _snwprintf(p, remaining, L"%s", L"unhandled exception: "); 83 | 84 | if (n >= 0) { 85 | p += n; 86 | remaining -= n; 87 | } 88 | 89 | ::MultiByteToWideChar(CP_ACP, 0, e.what(), -1, p, 90 | static_cast(remaining)); 91 | } 92 | catch (...) { 93 | auto* p = exception_message; 94 | std::size_t remaining = exception_message_length; 95 | 96 | _snwprintf(p, remaining, L"%s", L"unhandled exception"); 97 | } 98 | 99 | dump_stacktrace(exception_message); 100 | } 101 | 102 | const wchar_t* error_code_name(DWORD code) 103 | { 104 | switch (code) { 105 | case EXCEPTION_ACCESS_VIOLATION: 106 | return L"EXCEPTION_ACCESS_VIOLATION"; 107 | case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: 108 | return L"EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; 109 | case EXCEPTION_BREAKPOINT: 110 | return L"EXCEPTION_BREAKPOINT"; 111 | case EXCEPTION_DATATYPE_MISALIGNMENT: 112 | return L"EXCEPTION_DATATYPE_MISALIGNMENT"; 113 | case EXCEPTION_FLT_DENORMAL_OPERAND: 114 | return L"EXCEPTION_FLT_DENORMAL_OPERAND"; 115 | case EXCEPTION_FLT_DIVIDE_BY_ZERO: 116 | return L"EXCEPTION_FLT_DIVIDE_BY_ZERO"; 117 | case EXCEPTION_FLT_INEXACT_RESULT: 118 | return L"EXCEPTION_FLT_INEXACT_RESULT"; 119 | case EXCEPTION_FLT_INVALID_OPERATION: 120 | return L"EXCEPTION_FLT_INVALID_OPERATION"; 121 | case EXCEPTION_FLT_OVERFLOW: 122 | return L"EXCEPTION_FLT_OVERFLOW"; 123 | case EXCEPTION_FLT_STACK_CHECK: 124 | return L"EXCEPTION_FLT_STACK_CHECK"; 125 | case EXCEPTION_FLT_UNDERFLOW: 126 | return L"EXCEPTION_FLT_UNDERFLOW"; 127 | case EXCEPTION_ILLEGAL_INSTRUCTION: 128 | return L"EXCEPTION_ILLEGAL_INSTRUCTION"; 129 | case EXCEPTION_IN_PAGE_ERROR: 130 | return L"EXCEPTION_IN_PAGE_ERROR"; 131 | case EXCEPTION_INT_DIVIDE_BY_ZERO: 132 | return L"EXCEPTION_INT_DIVIDE_BY_ZERO"; 133 | case EXCEPTION_INT_OVERFLOW: 134 | return L"EXCEPTION_INT_OVERFLOW"; 135 | case EXCEPTION_INVALID_DISPOSITION: 136 | return L"EXCEPTION_INVALID_DISPOSITION"; 137 | case EXCEPTION_NONCONTINUABLE_EXCEPTION: 138 | return L"EXCEPTION_NONCONTINUABLE_EXCEPTION"; 139 | case EXCEPTION_PRIV_INSTRUCTION: 140 | return L"EXCEPTION_PRIV_INSTRUCTION"; 141 | case EXCEPTION_SINGLE_STEP: 142 | return L"EXCEPTION_SINGLE_STEP"; 143 | case EXCEPTION_STACK_OVERFLOW: 144 | return L"EXCEPTION_STACK_OVERFLOW"; 145 | default: 146 | return L"unknown exception"; 147 | } 148 | } 149 | 150 | LONG WINAPI unhandled_exception_handler(LPEXCEPTION_POINTERS ep) noexcept 151 | { 152 | if (ep->ExceptionRecord->ExceptionCode == 0xE06D7363) { 153 | if (g_previous_handler) 154 | return g_previous_handler(ep); 155 | ; 156 | } 157 | 158 | wchar_t* p = exception_message; 159 | std::size_t remaining = exception_message_length; 160 | 161 | const auto n = GetModuleFileNameW(GetModuleHandleW(nullptr), exception_message, 162 | exception_message_length); 163 | 164 | p += n; 165 | remaining -= n; 166 | 167 | const auto n2 = _snwprintf( 168 | p, remaining, L": exception thrown at 0x%p: 0x%lX %s", 169 | ep->ExceptionRecord->ExceptionAddress, ep->ExceptionRecord->ExceptionCode, 170 | error_code_name(ep->ExceptionRecord->ExceptionCode)); 171 | 172 | p += n2; 173 | remaining -= n2; 174 | 175 | dump_stacktrace(exception_message); 176 | return EXCEPTION_CONTINUE_SEARCH; 177 | } 178 | 179 | void set_thread_exception_handlers() 180 | { 181 | g_previous_handler = 182 | SetUnhandledExceptionFilter(mob::unhandled_exception_handler); 183 | 184 | std::set_terminate(mob::terminate_handler); 185 | } 186 | 187 | std::size_t make_thread_count(std::optional count) 188 | { 189 | static const auto def = std::thread::hardware_concurrency(); 190 | return std::max(1, count.value_or(def)); 191 | } 192 | 193 | thread_pool::thread_pool(std::optional count) 194 | : count_(make_thread_count(count)) 195 | { 196 | for (std::size_t i = 0; i < count_; ++i) 197 | threads_.emplace_back(std::make_unique()); 198 | } 199 | 200 | thread_pool::~thread_pool() 201 | { 202 | join(); 203 | } 204 | 205 | void thread_pool::join() 206 | { 207 | for (auto&& t : threads_) { 208 | if (t->thread.joinable()) 209 | t->thread.join(); 210 | } 211 | } 212 | 213 | void thread_pool::add(fun thread_fun) 214 | { 215 | for (;;) { 216 | if (try_add(thread_fun)) 217 | break; 218 | 219 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 220 | } 221 | } 222 | 223 | bool thread_pool::try_add(fun thread_fun) 224 | { 225 | for (auto& t : threads_) { 226 | if (t->running) 227 | continue; 228 | 229 | // found one 230 | 231 | if (t->thread.joinable()) 232 | t->thread.join(); 233 | 234 | t->running = true; 235 | t->thread_fun = thread_fun; 236 | 237 | t->thread = start_thread([&] { 238 | t->thread_fun(); 239 | t->running = false; 240 | }); 241 | 242 | return true; 243 | } 244 | 245 | return false; 246 | } 247 | 248 | } // namespace mob 249 | -------------------------------------------------------------------------------- /src/utility/threading.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mob { 4 | 5 | // sets unhandled exception and std::terminate handlers for the current thread 6 | // 7 | void set_thread_exception_handlers(); 8 | 9 | // starts a thread with exception handlers set up 10 | // 11 | template 12 | std::thread start_thread(F&& f) 13 | { 14 | return std::thread([f] { 15 | set_thread_exception_handlers(); 16 | f(); 17 | }); 18 | } 19 | 20 | // executes a function in a thread, blocks if there are too many 21 | // 22 | class thread_pool { 23 | public: 24 | typedef std::function fun; 25 | 26 | thread_pool(std::optional count = {}); 27 | 28 | // joins 29 | // 30 | ~thread_pool(); 31 | 32 | // non-copyable 33 | thread_pool(const thread_pool&) = delete; 34 | thread_pool& operator=(const thread_pool&) = delete; 35 | 36 | // runs the given function in a thread; if there no threads available, 37 | // blocks until another thread finishes 38 | // 39 | void add(fun f); 40 | 41 | // blocks until all threads are finished 42 | // 43 | void join(); 44 | 45 | private: 46 | struct thread_info { 47 | std::atomic running = false; 48 | fun thread_fun; 49 | std::thread thread; 50 | }; 51 | 52 | const std::size_t count_; 53 | std::vector> threads_; 54 | 55 | // tries to find an available thread, returns false if none are found 56 | // 57 | bool try_add(fun thread_fun); 58 | }; 59 | 60 | } // namespace mob 61 | -------------------------------------------------------------------------------- /third-party/bin/7z.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/mob/621daac2ec23a364e135605d32c7b5296411d58a/third-party/bin/7z.dll -------------------------------------------------------------------------------- /third-party/bin/7z.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/mob/621daac2ec23a364e135605d32c7b5296411d58a/third-party/bin/7z.exe -------------------------------------------------------------------------------- /third-party/bin/jom.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/mob/621daac2ec23a364e135605d32c7b5296411d58a/third-party/bin/jom.exe -------------------------------------------------------------------------------- /third-party/bin/msys-2.0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/mob/621daac2ec23a364e135605d32c7b5296411d58a/third-party/bin/msys-2.0.dll -------------------------------------------------------------------------------- /third-party/bin/nasm.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/mob/621daac2ec23a364e135605d32c7b5296411d58a/third-party/bin/nasm.exe -------------------------------------------------------------------------------- /third-party/bin/nuget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/mob/621daac2ec23a364e135605d32c7b5296411d58a/third-party/bin/nuget.exe -------------------------------------------------------------------------------- /third-party/bin/patch.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/mob/621daac2ec23a364e135605d32c7b5296411d58a/third-party/bin/patch.exe -------------------------------------------------------------------------------- /third-party/bin/tx.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/mob/621daac2ec23a364e135605d32c7b5296411d58a/third-party/bin/tx.exe -------------------------------------------------------------------------------- /third-party/bin/vswhere.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/mob/621daac2ec23a364e135605d32c7b5296411d58a/third-party/bin/vswhere.exe -------------------------------------------------------------------------------- /third-party/include/curl/curlver.h: -------------------------------------------------------------------------------- 1 | #ifndef CURLINC_CURLVER_H 2 | #define CURLINC_CURLVER_H 3 | /*************************************************************************** 4 | * _ _ ____ _ 5 | * Project ___| | | | _ \| | 6 | * / __| | | | |_) | | 7 | * | (__| |_| | _ <| |___ 8 | * \___|\___/|_| \_\_____| 9 | * 10 | * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. 11 | * 12 | * This software is licensed as described in the file COPYING, which 13 | * you should have received as part of this distribution. The terms 14 | * are also available at https://curl.haxx.se/docs/copyright.html. 15 | * 16 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell 17 | * copies of the Software, and permit persons to whom the Software is 18 | * furnished to do so, under the terms of the COPYING file. 19 | * 20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 21 | * KIND, either express or implied. 22 | * 23 | ***************************************************************************/ 24 | 25 | /* This header file contains nothing but libcurl version info, generated by 26 | a script at release-time. This was made its own header file in 7.11.2 */ 27 | 28 | /* This is the global package copyright */ 29 | #define LIBCURL_COPYRIGHT "1996 - 2019 Daniel Stenberg, ." 30 | 31 | /* This is the version number of the libcurl package from which this header 32 | file origins: */ 33 | #define LIBCURL_VERSION "7.68.0-DEV" 34 | 35 | /* The numeric version number is also available "in parts" by using these 36 | defines: */ 37 | #define LIBCURL_VERSION_MAJOR 7 38 | #define LIBCURL_VERSION_MINOR 68 39 | #define LIBCURL_VERSION_PATCH 0 40 | 41 | /* This is the numeric version of the libcurl version number, meant for easier 42 | parsing and comparions by programs. The LIBCURL_VERSION_NUM define will 43 | always follow this syntax: 44 | 45 | 0xXXYYZZ 46 | 47 | Where XX, YY and ZZ are the main version, release and patch numbers in 48 | hexadecimal (using 8 bits each). All three numbers are always represented 49 | using two digits. 1.2 would appear as "0x010200" while version 9.11.7 50 | appears as "0x090b07". 51 | 52 | This 6-digit (24 bits) hexadecimal number does not show pre-release number, 53 | and it is always a greater number in a more recent release. It makes 54 | comparisons with greater than and less than work. 55 | 56 | Note: This define is the full hex number and _does not_ use the 57 | CURL_VERSION_BITS() macro since curl's own configure script greps for it 58 | and needs it to contain the full number. 59 | */ 60 | #define LIBCURL_VERSION_NUM 0x074400 61 | 62 | /* 63 | * This is the date and time when the full source package was created. The 64 | * timestamp is not stored in git, as the timestamp is properly set in the 65 | * tarballs by the maketgz script. 66 | * 67 | * The format of the date follows this template: 68 | * 69 | * "2007-11-23" 70 | */ 71 | #define LIBCURL_TIMESTAMP "[unreleased]" 72 | 73 | #define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z)) 74 | #define CURL_AT_LEAST_VERSION(x,y,z) \ 75 | (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z)) 76 | 77 | #endif /* CURLINC_CURLVER_H */ 78 | -------------------------------------------------------------------------------- /third-party/include/curl/easy.h: -------------------------------------------------------------------------------- 1 | #ifndef CURLINC_EASY_H 2 | #define CURLINC_EASY_H 3 | /*************************************************************************** 4 | * _ _ ____ _ 5 | * Project ___| | | | _ \| | 6 | * / __| | | | |_) | | 7 | * | (__| |_| | _ <| |___ 8 | * \___|\___/|_| \_\_____| 9 | * 10 | * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. 11 | * 12 | * This software is licensed as described in the file COPYING, which 13 | * you should have received as part of this distribution. The terms 14 | * are also available at https://curl.haxx.se/docs/copyright.html. 15 | * 16 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell 17 | * copies of the Software, and permit persons to whom the Software is 18 | * furnished to do so, under the terms of the COPYING file. 19 | * 20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 21 | * KIND, either express or implied. 22 | * 23 | ***************************************************************************/ 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | CURL_EXTERN CURL *curl_easy_init(void); 29 | CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); 30 | CURL_EXTERN CURLcode curl_easy_perform(CURL *curl); 31 | CURL_EXTERN void curl_easy_cleanup(CURL *curl); 32 | 33 | /* 34 | * NAME curl_easy_getinfo() 35 | * 36 | * DESCRIPTION 37 | * 38 | * Request internal information from the curl session with this function. The 39 | * third argument MUST be a pointer to a long, a pointer to a char * or a 40 | * pointer to a double (as the documentation describes elsewhere). The data 41 | * pointed to will be filled in accordingly and can be relied upon only if the 42 | * function returns CURLE_OK. This function is intended to get used *AFTER* a 43 | * performed transfer, all results from this function are undefined until the 44 | * transfer is completed. 45 | */ 46 | CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); 47 | 48 | 49 | /* 50 | * NAME curl_easy_duphandle() 51 | * 52 | * DESCRIPTION 53 | * 54 | * Creates a new curl session handle with the same options set for the handle 55 | * passed in. Duplicating a handle could only be a matter of cloning data and 56 | * options, internal state info and things like persistent connections cannot 57 | * be transferred. It is useful in multithreaded applications when you can run 58 | * curl_easy_duphandle() for each new thread to avoid a series of identical 59 | * curl_easy_setopt() invokes in every thread. 60 | */ 61 | CURL_EXTERN CURL *curl_easy_duphandle(CURL *curl); 62 | 63 | /* 64 | * NAME curl_easy_reset() 65 | * 66 | * DESCRIPTION 67 | * 68 | * Re-initializes a CURL handle to the default values. This puts back the 69 | * handle to the same state as it was in when it was just created. 70 | * 71 | * It does keep: live connections, the Session ID cache, the DNS cache and the 72 | * cookies. 73 | */ 74 | CURL_EXTERN void curl_easy_reset(CURL *curl); 75 | 76 | /* 77 | * NAME curl_easy_recv() 78 | * 79 | * DESCRIPTION 80 | * 81 | * Receives data from the connected socket. Use after successful 82 | * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. 83 | */ 84 | CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, 85 | size_t *n); 86 | 87 | /* 88 | * NAME curl_easy_send() 89 | * 90 | * DESCRIPTION 91 | * 92 | * Sends data over the connected socket. Use after successful 93 | * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. 94 | */ 95 | CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer, 96 | size_t buflen, size_t *n); 97 | 98 | 99 | /* 100 | * NAME curl_easy_upkeep() 101 | * 102 | * DESCRIPTION 103 | * 104 | * Performs connection upkeep for the given session handle. 105 | */ 106 | CURL_EXTERN CURLcode curl_easy_upkeep(CURL *curl); 107 | 108 | #ifdef __cplusplus 109 | } 110 | #endif 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /third-party/include/curl/mprintf.h: -------------------------------------------------------------------------------- 1 | #ifndef CURLINC_MPRINTF_H 2 | #define CURLINC_MPRINTF_H 3 | /*************************************************************************** 4 | * _ _ ____ _ 5 | * Project ___| | | | _ \| | 6 | * / __| | | | |_) | | 7 | * | (__| |_| | _ <| |___ 8 | * \___|\___/|_| \_\_____| 9 | * 10 | * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. 11 | * 12 | * This software is licensed as described in the file COPYING, which 13 | * you should have received as part of this distribution. The terms 14 | * are also available at https://curl.haxx.se/docs/copyright.html. 15 | * 16 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell 17 | * copies of the Software, and permit persons to whom the Software is 18 | * furnished to do so, under the terms of the COPYING file. 19 | * 20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 21 | * KIND, either express or implied. 22 | * 23 | ***************************************************************************/ 24 | 25 | #include 26 | #include /* needed for FILE */ 27 | #include "curl.h" /* for CURL_EXTERN */ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | CURL_EXTERN int curl_mprintf(const char *format, ...); 34 | CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...); 35 | CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...); 36 | CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength, 37 | const char *format, ...); 38 | CURL_EXTERN int curl_mvprintf(const char *format, va_list args); 39 | CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args); 40 | CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args); 41 | CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, 42 | const char *format, va_list args); 43 | CURL_EXTERN char *curl_maprintf(const char *format, ...); 44 | CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | 50 | #endif /* CURLINC_MPRINTF_H */ 51 | -------------------------------------------------------------------------------- /third-party/include/curl/stdcheaders.h: -------------------------------------------------------------------------------- 1 | #ifndef CURLINC_STDCHEADERS_H 2 | #define CURLINC_STDCHEADERS_H 3 | /*************************************************************************** 4 | * _ _ ____ _ 5 | * Project ___| | | | _ \| | 6 | * / __| | | | |_) | | 7 | * | (__| |_| | _ <| |___ 8 | * \___|\___/|_| \_\_____| 9 | * 10 | * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. 11 | * 12 | * This software is licensed as described in the file COPYING, which 13 | * you should have received as part of this distribution. The terms 14 | * are also available at https://curl.haxx.se/docs/copyright.html. 15 | * 16 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell 17 | * copies of the Software, and permit persons to whom the Software is 18 | * furnished to do so, under the terms of the COPYING file. 19 | * 20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 21 | * KIND, either express or implied. 22 | * 23 | ***************************************************************************/ 24 | 25 | #include 26 | 27 | size_t fread(void *, size_t, size_t, FILE *); 28 | size_t fwrite(const void *, size_t, size_t, FILE *); 29 | 30 | int strcasecmp(const char *, const char *); 31 | int strncasecmp(const char *, const char *, size_t); 32 | 33 | #endif /* CURLINC_STDCHEADERS_H */ 34 | -------------------------------------------------------------------------------- /third-party/include/curl/urlapi.h: -------------------------------------------------------------------------------- 1 | #ifndef CURLINC_URLAPI_H 2 | #define CURLINC_URLAPI_H 3 | /*************************************************************************** 4 | * _ _ ____ _ 5 | * Project ___| | | | _ \| | 6 | * / __| | | | |_) | | 7 | * | (__| |_| | _ <| |___ 8 | * \___|\___/|_| \_\_____| 9 | * 10 | * Copyright (C) 2018 - 2019, Daniel Stenberg, , et al. 11 | * 12 | * This software is licensed as described in the file COPYING, which 13 | * you should have received as part of this distribution. The terms 14 | * are also available at https://curl.haxx.se/docs/copyright.html. 15 | * 16 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell 17 | * copies of the Software, and permit persons to whom the Software is 18 | * furnished to do so, under the terms of the COPYING file. 19 | * 20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 21 | * KIND, either express or implied. 22 | * 23 | ***************************************************************************/ 24 | 25 | #include "curl.h" 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | /* the error codes for the URL API */ 32 | typedef enum { 33 | CURLUE_OK, 34 | CURLUE_BAD_HANDLE, /* 1 */ 35 | CURLUE_BAD_PARTPOINTER, /* 2 */ 36 | CURLUE_MALFORMED_INPUT, /* 3 */ 37 | CURLUE_BAD_PORT_NUMBER, /* 4 */ 38 | CURLUE_UNSUPPORTED_SCHEME, /* 5 */ 39 | CURLUE_URLDECODE, /* 6 */ 40 | CURLUE_OUT_OF_MEMORY, /* 7 */ 41 | CURLUE_USER_NOT_ALLOWED, /* 8 */ 42 | CURLUE_UNKNOWN_PART, /* 9 */ 43 | CURLUE_NO_SCHEME, /* 10 */ 44 | CURLUE_NO_USER, /* 11 */ 45 | CURLUE_NO_PASSWORD, /* 12 */ 46 | CURLUE_NO_OPTIONS, /* 13 */ 47 | CURLUE_NO_HOST, /* 14 */ 48 | CURLUE_NO_PORT, /* 15 */ 49 | CURLUE_NO_QUERY, /* 16 */ 50 | CURLUE_NO_FRAGMENT /* 17 */ 51 | } CURLUcode; 52 | 53 | typedef enum { 54 | CURLUPART_URL, 55 | CURLUPART_SCHEME, 56 | CURLUPART_USER, 57 | CURLUPART_PASSWORD, 58 | CURLUPART_OPTIONS, 59 | CURLUPART_HOST, 60 | CURLUPART_PORT, 61 | CURLUPART_PATH, 62 | CURLUPART_QUERY, 63 | CURLUPART_FRAGMENT, 64 | CURLUPART_ZONEID /* added in 7.65.0 */ 65 | } CURLUPart; 66 | 67 | #define CURLU_DEFAULT_PORT (1<<0) /* return default port number */ 68 | #define CURLU_NO_DEFAULT_PORT (1<<1) /* act as if no port number was set, 69 | if the port number matches the 70 | default for the scheme */ 71 | #define CURLU_DEFAULT_SCHEME (1<<2) /* return default scheme if 72 | missing */ 73 | #define CURLU_NON_SUPPORT_SCHEME (1<<3) /* allow non-supported scheme */ 74 | #define CURLU_PATH_AS_IS (1<<4) /* leave dot sequences */ 75 | #define CURLU_DISALLOW_USER (1<<5) /* no user+password allowed */ 76 | #define CURLU_URLDECODE (1<<6) /* URL decode on get */ 77 | #define CURLU_URLENCODE (1<<7) /* URL encode on set */ 78 | #define CURLU_APPENDQUERY (1<<8) /* append a form style part */ 79 | #define CURLU_GUESS_SCHEME (1<<9) /* legacy curl-style guessing */ 80 | #define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the 81 | scheme is unknown. */ 82 | 83 | typedef struct Curl_URL CURLU; 84 | 85 | /* 86 | * curl_url() creates a new CURLU handle and returns a pointer to it. 87 | * Must be freed with curl_url_cleanup(). 88 | */ 89 | CURL_EXTERN CURLU *curl_url(void); 90 | 91 | /* 92 | * curl_url_cleanup() frees the CURLU handle and related resources used for 93 | * the URL parsing. It will not free strings previously returned with the URL 94 | * API. 95 | */ 96 | CURL_EXTERN void curl_url_cleanup(CURLU *handle); 97 | 98 | /* 99 | * curl_url_dup() duplicates a CURLU handle and returns a new copy. The new 100 | * handle must also be freed with curl_url_cleanup(). 101 | */ 102 | CURL_EXTERN CURLU *curl_url_dup(CURLU *in); 103 | 104 | /* 105 | * curl_url_get() extracts a specific part of the URL from a CURLU 106 | * handle. Returns error code. The returned pointer MUST be freed with 107 | * curl_free() afterwards. 108 | */ 109 | CURL_EXTERN CURLUcode curl_url_get(CURLU *handle, CURLUPart what, 110 | char **part, unsigned int flags); 111 | 112 | /* 113 | * curl_url_set() sets a specific part of the URL in a CURLU handle. Returns 114 | * error code. The passed in string will be copied. Passing a NULL instead of 115 | * a part string, clears that part. 116 | */ 117 | CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what, 118 | const char *part, unsigned int flags); 119 | 120 | 121 | #ifdef __cplusplus 122 | } /* end of extern "C" */ 123 | #endif 124 | 125 | #endif /* CURLINC_URLAPI_H */ 126 | -------------------------------------------------------------------------------- /third-party/lib/libcurl-d.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/mob/621daac2ec23a364e135605d32c7b5296411d58a/third-party/lib/libcurl-d.lib -------------------------------------------------------------------------------- /third-party/lib/libcurl.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/mob/621daac2ec23a364e135605d32c7b5296411d58a/third-party/lib/libcurl.lib -------------------------------------------------------------------------------- /third-party/lib/zlib.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/mob/621daac2ec23a364e135605d32c7b5296411d58a/third-party/lib/zlib.lib -------------------------------------------------------------------------------- /third-party/lib/zlibd.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/mob/621daac2ec23a364e135605d32c7b5296411d58a/third-party/lib/zlibd.lib --------------------------------------------------------------------------------