aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/src/mpt
diff options
context:
space:
mode:
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/src/mpt')
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/.clang-format169
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/LICENSE.BSD-3-Clause.txt25
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/LICENSE.BSL-1.0.txt23
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/audio/sample.hpp35
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/audio/span.hpp403
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/algorithm.hpp61
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/aligned_array.hpp99
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/alloc.hpp180
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/arithmetic_shift.hpp119
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/array.hpp82
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/bit.hpp400
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/check_platform.hpp38
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/compiletime_warning.hpp42
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/constexpr_throw.hpp56
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/detect.hpp16
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_compiler.hpp190
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_libc.hpp42
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_libcxx.hpp49
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_os.hpp117
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_quirks.hpp147
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/floatingpoint.hpp93
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/integer.hpp33
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/macros.hpp119
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/math.hpp88
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/memory.hpp273
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/namespace.hpp81
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/numbers.hpp177
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/numeric.hpp95
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/pointer.hpp54
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/preprocessor.hpp19
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/saturate_cast.hpp81
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/saturate_round.hpp48
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/secure.hpp198
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/semantic_version.hpp97
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/source_location.hpp115
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/span.hpp196
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_arithmetic_shift.hpp328
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_bit.hpp227
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_math.hpp55
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_saturate_cast.hpp112
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_saturate_round.hpp65
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_wrapping_divide.hpp178
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/utility.hpp179
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/version.hpp15
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/base/wrapping_divide.hpp36
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/binary/base64.hpp137
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/binary/base64url.hpp140
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/binary/hex.hpp89
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/binary/tests/tests_binary.hpp115
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/check/libc.hpp41
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/check/mfc.hpp19
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/check/windows.hpp25
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/crc/crc.hpp205
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/crc/tests/tests_crc.hpp49
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/crypto/exception.hpp139
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/crypto/hash.hpp180
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/crypto/jwk.hpp531
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/crypto/tests/tests_crypto.hpp106
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/detect/dl.hpp27
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/detect/ltdl.hpp27
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/detect/mfc.hpp22
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/detect/nlohmann_json.hpp25
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/endian/floatingpoint.hpp485
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/endian/int24.hpp122
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/endian/integer.hpp493
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/endian/tests/tests_endian_floatingpoint.hpp81
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/endian/tests/tests_endian_integer.hpp133
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/environment/environment.hpp61
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/exception_text/exception_text.hpp74
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/default_floatingpoint.hpp91
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/default_formatter.hpp37
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/default_integer.hpp114
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/default_string.hpp28
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/helpers.hpp106
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/join.hpp42
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/message.hpp332
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/message_macros.hpp37
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/simple.hpp166
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/simple_floatingpoint.hpp250
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/simple_integer.hpp251
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/simple_spec.hpp221
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/tests/tests_format_message.hpp78
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/format/tests/tests_format_simple.hpp189
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io/base.hpp122
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io/io.hpp367
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io/io_span.hpp146
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io/io_stdstream.hpp332
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io/io_virtual_wrapper.hpp405
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io/tests/tests_io.hpp573
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/callbackstream.hpp51
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor.hpp419
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_callbackstream.hpp50
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_filename_traits.hpp63
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_memory.hpp45
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_stdstream.hpp50
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_traits_filedata.hpp67
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_traits_memory.hpp65
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata.hpp77
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base.hpp119
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base_buffered.hpp122
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base_seekable.hpp101
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base_unseekable.hpp174
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_callbackstream.hpp199
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_memory.hpp101
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_stdstream.hpp117
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filereader.hpp594
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/io_write/buffer.hpp202
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/json/json.hpp73
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/library/library.hpp465
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/mutex/mutex.hpp211
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/osinfo/class.hpp80
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/osinfo/windows_version.hpp452
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/out_of_memory/out_of_memory.hpp81
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/parse/parse.hpp107
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/parse/split.hpp44
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/parse/tests/tests_parse.hpp82
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/path/path.hpp506
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/random/crand.hpp64
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/random/default_engines.hpp37
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/random/device.hpp304
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/random/engine.hpp160
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/random/engine_lcg.hpp85
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/random/random.hpp113
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/random/seed.hpp49
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/random/tests/tests_random.hpp85
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/string/buffer.hpp344
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/string/tests/tests_string_buffer.hpp87
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/string/tests/tests_string_utility.hpp59
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/string/types.hpp396
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/string/utility.hpp359
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/string_transcode/macros.hpp24
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/string_transcode/tests/tests_string_transcode.hpp246
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/string_transcode/transcode.hpp1282
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/system_error/system_error.hpp170
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/test/test.hpp564
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/test/test_macros.hpp84
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/uuid/guid.hpp286
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/uuid/tests/tests_uuid.hpp102
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/uuid/uuid.hpp419
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/uuid_namespace/tests/tests_uuid_namespace.hpp52
-rw-r--r--Src/external_dependencies/openmpt-trunk/src/mpt/uuid_namespace/uuid_namespace.hpp73
141 files changed, 22629 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/.clang-format b/Src/external_dependencies/openmpt-trunk/src/mpt/.clang-format
new file mode 100644
index 00000000..058b40db
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/.clang-format
@@ -0,0 +1,169 @@
+# clang-format 14
+
+Language: Cpp
+Standard: c++20
+
+AccessModifierOffset: -4 #?
+AlignAfterOpenBracket: AlwaysBreak
+AlignArrayOfStructures: Left
+AlignConsecutiveAssignments: false
+AlignConsecutiveBitFields: false
+AlignConsecutiveDeclarations: false
+AlignConsecutiveMacros: true
+AlignEscapedNewlines: DontAlign
+AlignOperands: AlignAfterOperator
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllConstructorInitializersOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortEnumsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLambdasOnASingleLine: Inline
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: true
+AlwaysBreakTemplateDeclarations: Yes
+AttributeMacros: []
+BinPackArguments: true
+BinPackParameters: false
+BitFieldColonSpacing: Both
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: false
+ AfterControlStatement: MultiLine
+ AfterEnum: false
+ AfterFunction: false
+ AfterNamespace: false
+ #AfterObjCDeclaration
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: false
+ BeforeCatch: false
+ BeforeElse: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: true
+#BreakAfterJavaFieldAnnotations
+BreakBeforeBinaryOperators: NonAssignment
+BreakBeforeBraces: Custom
+BreakBeforeConceptDeclarations: true
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeComma
+BreakInheritanceList: BeforeComma
+BreakStringLiterals: false
+ColumnLimit: 0
+CommentPragmas: '' #?
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 4 #?
+ContinuationIndentWidth: 4 #?
+Cpp11BracedListStyle: true
+DeriveLineEnding: true
+DerivePointerAlignment: false
+EmptyLineAfterAccessModifier: Leave
+EmptyLineBeforeAccessModifier: Leave
+FixNamespaceComments: true
+ForEachMacros: []
+IfMacros: ['MPT_MAYBE_CONSTANT_IF']
+IncludeBlocks: Preserve
+IncludeCategories: [] #?
+IncludeIsMainRegex: '' #?
+IncludeIsMainSourceRegex: '' #?
+IndentAccessModifiers: false
+IndentCaseLabels: true
+IndentCaseBlocks: true
+IndentExternBlock: NoIndent
+IndentGotoLabels: false
+IndentPPDirectives: None
+#IndentRequiresClause: true
+InsertTrailingCommas: None
+#BeforeHash
+IndentWidth: 4
+IndentWrappedFunctionNames: true
+#JavaImportGroups
+#JavaScriptQuotes
+#JavaScriptWrapImports
+KeepEmptyLinesAtTheStartOfBlocks: true
+LambdaBodyIndentation: OuterScope
+MacroBlockBegin: '^MPT_TEST_GROUP_BEGIN$' #?
+MacroBlockEnd: '^MPT_TEST_GROUP_END$' #?
+MaxEmptyLinesToKeep: 5
+NamespaceIndentation: None
+NamespaceMacros: [] #?
+#ObjCBinPackProtocolList
+#ObjCBlockIndentWidth
+#ObjCBreakBeforeNestedBlockParam
+#ObjCSpaceAfterProperty
+#ObjCSpaceBeforeProtocolList
+PackConstructorInitializers: Never
+#PenaltyBreakAssignment
+#PenaltyBreakBeforeFirstCallParameter
+#PenaltyBreakComment
+#PenaltyBreakFirstLessLess
+#PenaltyBreakOpenParenthesis
+#PenaltyBreakString
+#PenaltyBreakTemplateDeclaration
+#PenaltyExcessCharacter
+#PenaltyIndentedWhitespace
+#PenaltyReturnTypeOnItsOwnLine
+PointerAlignment: Middle
+PPIndentWidth: -1
+#RawStringFormats
+QualifierAlignment: Leave
+#QualifierOrder: ['static', 'inline', 'constexpr', 'volatile', 'const', 'restrict', 'type']
+ReferenceAlignment: Pointer
+ReflowComments: false
+RemoveBracesLLVM: false
+SeparateDefinitionBlocks: Leave
+ShortNamespaceLines: 1
+SortIncludes: false
+#SortJavaStaticImport
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: true
+SpaceAroundPointerQualifiers: Default
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeParensOptions:
+ AfterControlStatements: true
+ AfterForeachMacros: true
+ AfterFunctionDeclarationName: false
+ AfterFunctionDefinitionName: false
+ AfterIfMacros: true
+ AfterOverloadedOperator: false
+ #AfterRequiresInClause: false
+ #AfterRequiresInExpression: false
+ BeforeNonEmptyParentheses: false
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceBeforeSquareBrackets: false
+SpaceInEmptyBlock: true
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+SpacesInConditionalStatement: false
+SpacesInContainerLiterals: true
+SpacesInLineCommentPrefix:
+ Minimum: 1
+ Maximum: -1
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+StatementAttributeLikeMacros: []
+StatementMacros: [ '_Pragma', '__pragma', 'MPT_WARNING', 'MPT_TEST_GROUP_INLINE_IDENTIFIER', 'MPT_TEST_GROUP_INLINE', 'MPT_TEST_GROUP_STATIC' ] #?
+TabWidth: 4
+TypenameMacros: [] #?
+UseCRLF: false
+UseTab: ForContinuationAndIndentation
+WhitespaceSensitiveMacros:
+ - MPT_PP_STRINGIFY
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/LICENSE.BSD-3-Clause.txt b/Src/external_dependencies/openmpt-trunk/src/mpt/LICENSE.BSD-3-Clause.txt
new file mode 100644
index 00000000..7c06acae
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/LICENSE.BSD-3-Clause.txt
@@ -0,0 +1,25 @@
+Copyright (c) 2004-2022, OpenMPT Project Developers and Contributors
+Copyright (c) 1997-2003, Olivier Lapicque
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the OpenMPT project nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/LICENSE.BSL-1.0.txt b/Src/external_dependencies/openmpt-trunk/src/mpt/LICENSE.BSL-1.0.txt
new file mode 100644
index 00000000..1dad8e93
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/LICENSE.BSL-1.0.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/audio/sample.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/audio/sample.hpp
new file mode 100644
index 00000000..ef347e7e
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/audio/sample.hpp
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_AUDIO_SAMPLE_HPP
+#define MPT_AUDIO_SAMPLE_HPP
+
+
+
+#include "mpt/base/floatingpoint.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <type_traits>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+using audio_sample_int = int16;
+using audio_sample_float = nativefloat;
+
+using audio_sample = std::conditional<mpt::float_traits<audio_sample_float>::is_hard, audio_sample_float, audio_sample_int>::type;
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BINARY_HEX_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/audio/span.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/audio/span.hpp
new file mode 100644
index 00000000..5653f8d8
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/audio/span.hpp
@@ -0,0 +1,403 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_AUDIO_SPAN_HPP
+#define MPT_AUDIO_SPAN_HPP
+
+
+
+#include "mpt/base/macros.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <cassert>
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+// LxxxLxxxLxxxLxxxLxxx xRxxxRxxxRxxxRxxxRxx
+template <typename SampleType>
+struct audio_span_planar_strided {
+public:
+ using sample_type = SampleType;
+
+private:
+ SampleType * const * m_buffers;
+ std::ptrdiff_t m_frame_stride;
+ std::size_t m_channels;
+ std::size_t m_frames;
+
+public:
+ constexpr audio_span_planar_strided(SampleType * const * buffers, std::size_t channels, std::size_t frames, std::ptrdiff_t frame_stride) noexcept
+ : m_buffers(buffers)
+ , m_frame_stride(frame_stride)
+ , m_channels(channels)
+ , m_frames(frames) {
+ return;
+ }
+ SampleType * const * data_planar() const noexcept {
+ return m_buffers;
+ }
+ SampleType * data() const noexcept {
+ return nullptr;
+ }
+ SampleType & operator()(std::size_t channel, std::size_t frame) const {
+ return m_buffers[channel][static_cast<std::ptrdiff_t>(frame) * m_frame_stride];
+ }
+ bool is_contiguous() const noexcept {
+ return false;
+ }
+ bool channels_are_contiguous() const noexcept {
+ return false;
+ }
+ bool frames_are_contiguous() const noexcept {
+ return false;
+ }
+ std::size_t size_channels() const noexcept {
+ return m_channels;
+ }
+ std::size_t size_frames() const noexcept {
+ return m_frames;
+ }
+ std::size_t size_samples() const noexcept {
+ return m_channels * m_frames;
+ }
+ std::ptrdiff_t frame_stride() const noexcept {
+ return m_frame_stride;
+ }
+};
+
+
+// LLLLL RRRRR
+template <typename SampleType>
+struct audio_span_planar {
+public:
+ using sample_type = SampleType;
+
+private:
+ SampleType * const * m_buffers;
+ std::size_t m_channels;
+ std::size_t m_frames;
+
+public:
+ constexpr audio_span_planar(SampleType * const * buffers, std::size_t channels, std::size_t frames) noexcept
+ : m_buffers(buffers)
+ , m_channels(channels)
+ , m_frames(frames) {
+ return;
+ }
+ SampleType * const * data_planar() const noexcept {
+ return m_buffers;
+ }
+ SampleType * data() const noexcept {
+ return nullptr;
+ }
+ SampleType & operator()(std::size_t channel, std::size_t frame) const {
+ return m_buffers[channel][frame];
+ }
+ bool is_contiguous() const noexcept {
+ return false;
+ }
+ bool channels_are_contiguous() const noexcept {
+ return false;
+ }
+ bool frames_are_contiguous() const noexcept {
+ return false;
+ }
+ std::size_t size_channels() const noexcept {
+ return m_channels;
+ }
+ std::size_t size_frames() const noexcept {
+ return m_frames;
+ }
+ std::size_t size_samples() const noexcept {
+ return m_channels * m_frames;
+ }
+};
+
+
+// LLLLLRRRRR
+template <typename SampleType>
+struct audio_span_contiguous {
+public:
+ using sample_type = SampleType;
+
+private:
+ SampleType * const m_buffer;
+ std::size_t m_channels;
+ std::size_t m_frames;
+
+public:
+ constexpr audio_span_contiguous(SampleType * buffer, std::size_t channels, std::size_t frames) noexcept
+ : m_buffer(buffer)
+ , m_channels(channels)
+ , m_frames(frames) {
+ return;
+ }
+ SampleType * const * data_planar() const noexcept {
+ return nullptr;
+ }
+ SampleType * data() const noexcept {
+ return m_buffer;
+ }
+ SampleType & operator()(std::size_t channel, std::size_t frame) const {
+ return m_buffer[(m_frames * channel) + frame];
+ }
+ bool is_contiguous() const noexcept {
+ return true;
+ }
+ bool channels_are_contiguous() const noexcept {
+ return true;
+ }
+ bool frames_are_contiguous() const noexcept {
+ return false;
+ }
+ std::size_t size_channels() const noexcept {
+ return m_channels;
+ }
+ std::size_t size_frames() const noexcept {
+ return m_frames;
+ }
+ std::size_t size_samples() const noexcept {
+ return m_channels * m_frames;
+ }
+};
+
+
+// LRLRLRLRLR
+template <typename SampleType>
+struct audio_span_interleaved {
+public:
+ using sample_type = SampleType;
+
+private:
+ SampleType * const m_buffer;
+ std::size_t m_channels;
+ std::size_t m_frames;
+
+public:
+ constexpr audio_span_interleaved(SampleType * buffer, std::size_t channels, std::size_t frames) noexcept
+ : m_buffer(buffer)
+ , m_channels(channels)
+ , m_frames(frames) {
+ return;
+ }
+ SampleType * const * data_planar() const noexcept {
+ return nullptr;
+ }
+ SampleType * data() const noexcept {
+ return m_buffer;
+ }
+ SampleType & operator()(std::size_t channel, std::size_t frame) const {
+ return m_buffer[m_channels * frame + channel];
+ }
+ bool is_contiguous() const noexcept {
+ return true;
+ }
+ bool channels_are_contiguous() const noexcept {
+ return false;
+ }
+ bool frames_are_contiguous() const noexcept {
+ return true;
+ }
+ std::size_t size_channels() const noexcept {
+ return m_channels;
+ }
+ std::size_t size_frames() const noexcept {
+ return m_frames;
+ }
+ std::size_t size_samples() const noexcept {
+ return m_channels * m_frames;
+ }
+};
+
+
+struct audio_span_frames_are_contiguous_t {
+};
+
+struct audio_span_channels_are_contiguous_t {
+};
+
+struct audio_span_channels_are_planar_t {
+};
+
+struct audio_span_channels_are_planar_and_strided_t {
+};
+
+// LRLRLRLRLR
+inline constexpr audio_span_frames_are_contiguous_t audio_span_frames_are_contiguous;
+
+// LLLLLRRRRR
+inline constexpr audio_span_channels_are_contiguous_t audio_span_channels_are_contiguous;
+
+// LLLLL RRRRR
+inline constexpr audio_span_channels_are_planar_t audio_span_channels_are_planar;
+
+// LxxxLxxxLxxxLxxxLxxx xRxxxRxxxRxxxRxxxRxx
+inline constexpr audio_span_channels_are_planar_and_strided_t audio_span_channels_are_planar_and_strided;
+
+template <typename SampleType>
+struct audio_span {
+public:
+ using sample_type = SampleType;
+
+private:
+ union {
+ SampleType * const contiguous;
+ SampleType * const * const planes;
+ } m_buffer;
+ std::ptrdiff_t m_frame_stride;
+ std::ptrdiff_t m_channel_stride;
+ std::size_t m_channels;
+ std::size_t m_frames;
+
+public:
+ constexpr audio_span(audio_span_interleaved<SampleType> buffer) noexcept
+ : m_frame_stride(static_cast<std::ptrdiff_t>(buffer.size_channels()))
+ , m_channel_stride(1)
+ , m_channels(buffer.size_channels())
+ , m_frames(buffer.size_frames()) {
+ m_buffer.contiguous = buffer.data();
+ }
+ constexpr audio_span(SampleType * buffer, std::size_t channels, std::size_t frames, audio_span_frames_are_contiguous_t) noexcept
+ : m_frame_stride(static_cast<std::ptrdiff_t>(channels))
+ , m_channel_stride(1)
+ , m_channels(channels)
+ , m_frames(frames) {
+ m_buffer.contiguous = buffer;
+ }
+ constexpr audio_span(audio_span_contiguous<SampleType> buffer) noexcept
+ : m_frame_stride(1)
+ , m_channel_stride(buffer.size_frames())
+ , m_channels(buffer.size_channels())
+ , m_frames(buffer.size_frames()) {
+ m_buffer.contiguous = buffer.data();
+ }
+ constexpr audio_span(SampleType * buffer, std::size_t channels, std::size_t frames, audio_span_channels_are_contiguous_t) noexcept
+ : m_frame_stride(1)
+ , m_channel_stride(static_cast<std::ptrdiff_t>(frames))
+ , m_channels(channels)
+ , m_frames(frames) {
+ m_buffer.contiguous = buffer;
+ }
+ constexpr audio_span(audio_span_planar<SampleType> buffer) noexcept
+ : m_frame_stride(1)
+ , m_channel_stride(0)
+ , m_channels(buffer.size_channels())
+ , m_frames(buffer.size_frames()) {
+ m_buffer.planes = buffer.data_planar();
+ }
+ constexpr audio_span(SampleType * const * planes, std::size_t channels, std::size_t frames, audio_span_channels_are_planar_t) noexcept
+ : m_frame_stride(1)
+ , m_channel_stride(0)
+ , m_channels(channels)
+ , m_frames(frames) {
+ m_buffer.planes = planes;
+ }
+ constexpr audio_span(audio_span_planar_strided<SampleType> buffer) noexcept
+ : m_frame_stride(static_cast<std::ptrdiff_t>(buffer.frame_stride()))
+ , m_channel_stride(0)
+ , m_channels(buffer.size_channels())
+ , m_frames(buffer.size_frames()) {
+ m_buffer.planes = buffer.data_planar();
+ }
+ constexpr audio_span(SampleType * const * planes, std::size_t channels, std::size_t frames, std::ptrdiff_t frame_stride, audio_span_channels_are_planar_and_strided_t) noexcept
+ : m_frame_stride(frame_stride)
+ , m_channel_stride(0)
+ , m_channels(channels)
+ , m_frames(frames) {
+ m_buffer.planes = planes;
+ }
+ bool is_contiguous() const noexcept {
+ return (m_channel_stride != 0);
+ }
+ SampleType * const * data_planar() const noexcept {
+ return (!is_contiguous()) ? m_buffer.planes : nullptr;
+ }
+ SampleType * data() const noexcept {
+ return is_contiguous() ? m_buffer.contiguous : nullptr;
+ }
+ SampleType & operator()(std::size_t channel, std::size_t frame) const {
+ return is_contiguous() ? m_buffer.contiguous[(m_channel_stride * static_cast<std::ptrdiff_t>(channel)) + (m_frame_stride * static_cast<std::ptrdiff_t>(frame))] : m_buffer.planes[channel][frame * static_cast<std::ptrdiff_t>(m_frame_stride)];
+ }
+ bool channels_are_contiguous() const noexcept {
+ return (m_channel_stride == static_cast<std::ptrdiff_t>(m_frames));
+ }
+ bool frames_are_contiguous() const noexcept {
+ return (m_frame_stride == static_cast<std::ptrdiff_t>(m_channels));
+ }
+ std::size_t size_channels() const noexcept {
+ return m_channels;
+ }
+ std::size_t size_frames() const noexcept {
+ return m_frames;
+ }
+ std::size_t size_samples() const noexcept {
+ return m_channels * m_frames;
+ }
+};
+
+
+template <typename Taudio_span>
+struct audio_span_with_offset {
+public:
+ using sample_type = typename Taudio_span::sample_type;
+
+private:
+ Taudio_span m_buffer;
+ std::size_t m_offset;
+
+public:
+ constexpr audio_span_with_offset(Taudio_span buffer, std::size_t offsetFrames) noexcept
+ : m_buffer(buffer)
+ , m_offset(offsetFrames) {
+ return;
+ }
+ sample_type * data() const noexcept {
+ if (!is_contiguous()) {
+ return nullptr;
+ }
+ return m_buffer.data() + (size_channels() * m_offset);
+ }
+ sample_type & operator()(std::size_t channel, std::size_t frame) const {
+ return m_buffer(channel, m_offset + frame);
+ }
+ bool is_contiguous() const noexcept {
+ return m_buffer.is_contiguous() && m_buffer.frames_are_contiguous();
+ }
+ bool channels_are_contiguous() const noexcept {
+ return m_buffer.channels_are_contiguous();
+ }
+ bool frames_are_contiguous() const noexcept {
+ return m_buffer.frames_are_contiguous();
+ }
+ std::size_t size_channels() const noexcept {
+ return m_buffer.size_channels();
+ }
+ std::size_t size_frames() const noexcept {
+ return m_buffer.size_frames() - m_offset;
+ }
+ std::size_t size_samples() const noexcept {
+ return size_channels() * size_frames();
+ }
+};
+
+
+
+template <typename BufferType>
+inline audio_span_with_offset<BufferType> make_audio_span_with_offset(BufferType buf, std::size_t offsetFrames) noexcept {
+ assert(offsetFrames <= buf.size_frames());
+ return audio_span_with_offset<BufferType>{buf, offsetFrames};
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_AUDIO_SPAN_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/algorithm.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/algorithm.hpp
new file mode 100644
index 00000000..8bb7a786
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/algorithm.hpp
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_ALGORITHM_HPP
+#define MPT_BASE_ALGORITHM_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include "mpt/base/saturate_cast.hpp"
+
+#include <algorithm>
+#include <limits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+// Grows x with an exponential factor suitable for increasing buffer sizes.
+// Clamps the result at limit.
+// And avoids integer overflows while doing its business.
+// The growth factor is 1.5, rounding down, execpt for the initial x==1 case.
+template <typename T, typename Tlimit>
+inline T exponential_grow(const T & x, const Tlimit & limit) {
+ if (x <= 1) {
+ return 2;
+ }
+ T add = std::min(x >> 1, std::numeric_limits<T>::max() - x);
+ return std::min(x + add, mpt::saturate_cast<T>(limit));
+}
+
+template <typename T>
+inline T exponential_grow(const T & x) {
+ return mpt::exponential_grow(x, std::numeric_limits<T>::max());
+}
+
+
+// Check if val is in [lo,hi] without causing compiler warnings
+// if theses checks are always true due to the domain of T.
+// GCC does not warn if the type is templated.
+template <typename T, typename C>
+constexpr bool is_in_range(const T & val, const C & lo, const C & hi) {
+ return lo <= val && val <= hi;
+}
+
+
+template <typename Tcontainer, typename Tval>
+MPT_CONSTEXPR20_FUN bool contains(const Tcontainer & container, const Tval & value) noexcept(noexcept(std::find(std::begin(container), std::end(container), value))) {
+ return std::find(std::begin(container), std::end(container), value) != std::end(container);
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_ALGORITHM_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/aligned_array.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/aligned_array.hpp
new file mode 100644
index 00000000..218c9062
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/aligned_array.hpp
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_ALIGNED_ARRAY_HPP
+#define MPT_BASE_ALIGNED_ARRAY_HPP
+
+
+
+#include "mpt/base/bit.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <array>
+#include <memory>
+#include <new>
+
+#include <cassert>
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+template <typename T, std::size_t count, std::align_val_t alignment>
+struct alignas(static_cast<std::size_t>(alignment)) aligned_array
+ : std::array<T, count> {
+ static_assert(static_cast<std::size_t>(alignment) >= alignof(T));
+ static_assert(((count * sizeof(T)) % static_cast<std::size_t>(alignment)) == 0);
+ static_assert(sizeof(std::array<T, count>) == (sizeof(T) * count));
+};
+
+static_assert(sizeof(mpt::aligned_array<float, 4, std::align_val_t{sizeof(float) * 4}>) == sizeof(std::array<float, 4>));
+
+
+
+template <std::size_t alignment_elements, std::size_t expected_elements, typename T, std::size_t N>
+T * align_elements(std::array<T, N> & a) {
+ static_assert(mpt::has_single_bit(alignof(T)));
+ static_assert(mpt::has_single_bit(sizeof(T)));
+ static_assert(mpt::has_single_bit(alignment_elements));
+ static_assert((expected_elements + alignment_elements - 1) <= N);
+ void * buf = a.data();
+ std::size_t size = N * sizeof(T);
+ void * result = std::align(alignment_elements * sizeof(T), expected_elements * sizeof(T), buf, size);
+ assert(result);
+ return reinterpret_cast<T *>(result);
+}
+
+template <std::size_t alignment_elements, std::size_t expected_elements, typename T, std::size_t N>
+T * align_elements(T (&a)[N]) {
+ static_assert(mpt::has_single_bit(alignof(T)));
+ static_assert(mpt::has_single_bit(sizeof(T)));
+ static_assert(mpt::has_single_bit(alignment_elements));
+ static_assert((expected_elements + alignment_elements - 1) <= N);
+ void * buf = a;
+ std::size_t size = N * sizeof(T);
+ void * result = std::align(alignment_elements * sizeof(T), expected_elements * sizeof(T), buf, size);
+ assert(result);
+ return reinterpret_cast<T *>(result);
+}
+
+
+
+template <std::size_t alignment_bytes, std::size_t expected_elements, typename T, std::size_t N>
+T * align_bytes(std::array<T, N> & a) {
+ static_assert(mpt::has_single_bit(alignof(T)));
+ static_assert(mpt::has_single_bit(sizeof(T)));
+ static_assert(mpt::has_single_bit(alignment_bytes));
+ static_assert((alignment_bytes % alignof(T)) == 0);
+ static_assert(((expected_elements * sizeof(T)) + alignment_bytes - 1) <= (N * sizeof(T)));
+ void * buf = a.data();
+ std::size_t size = N * sizeof(T);
+ void * result = std::align(alignment_bytes, expected_elements * sizeof(T), buf, size);
+ assert(result);
+ return reinterpret_cast<T *>(result);
+}
+
+template <std::size_t alignment_bytes, std::size_t expected_elements, typename T, std::size_t N>
+T * align_bytes(T (&a)[N]) {
+ static_assert(mpt::has_single_bit(alignof(T)));
+ static_assert(mpt::has_single_bit(sizeof(T)));
+ static_assert(mpt::has_single_bit(alignment_bytes));
+ static_assert((alignment_bytes % alignof(T)) == 0);
+ static_assert(((expected_elements * sizeof(T)) + alignment_bytes - 1) <= (N * sizeof(T)));
+ void * buf = a;
+ std::size_t size = N * sizeof(T);
+ void * result = std::align(alignment_bytes, expected_elements * sizeof(T), buf, size);
+ assert(result);
+ return reinterpret_cast<T *>(result);
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_ALIGNED_ARRAY_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/alloc.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/alloc.hpp
new file mode 100644
index 00000000..075dd035
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/alloc.hpp
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_ALLOC_HPP
+#define MPT_BASE_ALLOC_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+
+#include "mpt/base/memory.hpp"
+#include "mpt/base/span.hpp"
+
+#include <iterator>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+template <typename T>
+inline mpt::span<T> as_span(std::vector<T> & cont) {
+ return mpt::span<T>(cont.data(), cont.data() + cont.size());
+}
+
+template <typename T>
+inline mpt::span<const T> as_span(const std::vector<T> & cont) {
+ return mpt::span<const T>(cont.data(), cont.data() + cont.size());
+}
+
+template <typename T>
+inline span<T> as_span(std::basic_string<T> & str) {
+ return span<T>(str.data(), str.size());
+}
+
+template <typename T>
+inline span<const T> as_span(const std::basic_string<T> & str) {
+ return span<const T>(str.data(), str.size());
+}
+
+
+
+template <typename T>
+inline std::vector<typename std::remove_const<T>::type> make_vector(T * beg, T * end) {
+ return std::vector<typename std::remove_const<T>::type>(beg, end);
+}
+
+template <typename T>
+inline std::vector<typename std::remove_const<T>::type> make_vector(T * data, std::size_t size) {
+ return std::vector<typename std::remove_const<T>::type>(data, data + size);
+}
+
+template <typename T>
+inline std::vector<typename std::remove_const<T>::type> make_vector(mpt::span<T> data) {
+ return std::vector<typename std::remove_const<T>::type>(data.data(), data.data() + data.size());
+}
+
+template <typename T, std::size_t N>
+inline std::vector<typename std::remove_const<T>::type> make_vector(T (&arr)[N]) {
+ return std::vector<typename std::remove_const<T>::type>(std::begin(arr), std::end(arr));
+}
+
+template <typename T>
+inline std::vector<typename std::remove_const<T>::type> make_vector(const std::basic_string<T> & str) {
+ return std::vector<typename std::remove_const<T>::type>(str.begin(), str.end());
+}
+
+
+
+template <typename T>
+inline std::basic_string<typename std::remove_const<T>::type> make_basic_string(T * beg, T * end) {
+ return std::basic_string<typename std::remove_const<T>::type>(beg, end);
+}
+
+template <typename T>
+inline std::basic_string<typename std::remove_const<T>::type> make_basic_string(T * data, std::size_t size) {
+ return std::basic_string<typename std::remove_const<T>::type>(data, data + size);
+}
+
+template <typename T>
+inline std::basic_string<typename std::remove_const<T>::type> make_basic_string(mpt::span<T> data) {
+ return std::basic_string<typename std::remove_const<T>::type>(data.data(), data.data() + data.size());
+}
+
+template <typename T, std::size_t N>
+inline std::basic_string<typename std::remove_const<T>::type> make_basic_string(T (&arr)[N]) {
+ return std::basic_string<typename std::remove_const<T>::type>(std::begin(arr), std::end(arr));
+}
+
+template <typename T>
+inline std::basic_string<typename std::remove_const<T>::type> make_basic_string(const std::vector<T> & str) {
+ return std::vector<typename std::remove_const<T>::type>(str.begin(), str.end());
+}
+
+
+
+template <typename Tcont2, typename Tcont1>
+inline Tcont1 & append(Tcont1 & cont1, const Tcont2 & cont2) {
+ cont1.insert(cont1.end(), cont2.begin(), cont2.end());
+ return cont1;
+}
+
+template <typename Tit2, typename Tcont1>
+inline Tcont1 & append(Tcont1 & cont1, Tit2 beg, Tit2 end) {
+ cont1.insert(cont1.end(), beg, end);
+ return cont1;
+}
+
+
+
+template <typename Tdst, typename Tsrc>
+struct buffer_cast_impl {
+ inline Tdst operator()(const Tsrc & src) const {
+ return Tdst(mpt::byte_cast<const typename Tdst::value_type *>(src.data()), mpt::byte_cast<const typename Tdst::value_type *>(src.data()) + src.size());
+ }
+};
+
+// casts between vector<->string of byte-castable types
+template <typename Tdst, typename Tsrc>
+inline Tdst buffer_cast(Tsrc src) {
+ return buffer_cast_impl<Tdst, Tsrc>()(src);
+}
+
+
+
+template <typename T>
+struct as_raw_memory_impl<std::vector<T>> {
+ inline mpt::const_byte_span operator()(const std::vector<T> & v) const {
+ static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
+ return mpt::as_span(reinterpret_cast<const std::byte *>(v.data()), v.size() * sizeof(T));
+ }
+ inline mpt::byte_span operator()(std::vector<T> & v) const {
+ static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
+ return mpt::as_span(reinterpret_cast<std::byte *>(v.data()), v.size() * sizeof(T));
+ }
+};
+
+template <typename T>
+struct as_raw_memory_impl<const std::vector<T>> {
+ inline mpt::const_byte_span operator()(const std::vector<T> & v) const {
+ static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
+ return mpt::as_span(reinterpret_cast<const std::byte *>(v.data()), v.size() * sizeof(T));
+ }
+};
+
+
+
+template <typename T>
+class heap_value {
+private:
+ std::unique_ptr<T> m_value{};
+
+public:
+ template <typename... Targs>
+ heap_value(Targs &&... args)
+ : m_value(std::make_unique<T>(std::forward<Targs>(args)...)) {
+ return;
+ }
+ const T & operator*() const {
+ return *m_value;
+ }
+ T & operator*() {
+ return *m_value;
+ }
+};
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_ALLOC_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/arithmetic_shift.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/arithmetic_shift.hpp
new file mode 100644
index 00000000..65a6951f
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/arithmetic_shift.hpp
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_ARITHMETIC_SHIFT_HPP
+#define MPT_BASE_ARITHMETIC_SHIFT_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include "mpt/base/saturate_cast.hpp"
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+// mpt::rshift_signed
+// mpt::lshift_signed
+// Shift a signed integer value in a well-defined manner.
+// Does the same thing as MSVC would do. This is verified by the test suite.
+
+template <typename T>
+constexpr auto rshift_signed_standard(T x, int y) noexcept -> decltype(x >> y) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::numeric_limits<T>::is_signed);
+ using result_type = decltype(x >> y);
+ using unsigned_result_type = typename std::make_unsigned<result_type>::type;
+ const unsigned_result_type roffset = static_cast<unsigned_result_type>(1) << ((sizeof(result_type) * 8) - 1);
+ result_type rx = x;
+ unsigned_result_type urx = static_cast<unsigned_result_type>(rx);
+ urx += roffset;
+ urx >>= y;
+ urx -= roffset >> y;
+ return static_cast<result_type>(urx);
+}
+
+template <typename T>
+constexpr auto lshift_signed_standard(T x, int y) noexcept -> decltype(x << y) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::numeric_limits<T>::is_signed);
+ using result_type = decltype(x << y);
+ using unsigned_result_type = typename std::make_unsigned<result_type>::type;
+ const unsigned_result_type roffset = static_cast<unsigned_result_type>(1) << ((sizeof(result_type) * 8) - 1);
+ result_type rx = x;
+ unsigned_result_type urx = static_cast<unsigned_result_type>(rx);
+ urx += roffset;
+ urx <<= y;
+ urx -= roffset << y;
+ return static_cast<result_type>(urx);
+}
+
+#if MPT_COMPILER_SHIFT_SIGNED
+
+template <typename T>
+constexpr auto rshift_signed_undefined(T x, int y) noexcept -> decltype(x >> y) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::numeric_limits<T>::is_signed);
+ return x >> y;
+}
+
+template <typename T>
+constexpr auto lshift_signed_undefined(T x, int y) noexcept -> decltype(x << y) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::numeric_limits<T>::is_signed);
+ return x << y;
+}
+
+template <typename T>
+constexpr auto rshift_signed(T x, int y) noexcept -> decltype(x >> y) {
+ return mpt::rshift_signed_undefined(x, y);
+}
+
+template <typename T>
+constexpr auto lshift_signed(T x, int y) noexcept -> decltype(x << y) {
+ return mpt::lshift_signed_undefined(x, y);
+}
+
+#else
+
+template <typename T>
+constexpr auto rshift_signed(T x, int y) noexcept -> decltype(x >> y) {
+ return mpt::rshift_signed_standard(x, y);
+}
+
+template <typename T>
+constexpr auto lshift_signed(T x, int y) noexcept -> decltype(x << y) {
+ return mpt::lshift_signed_standard(x, y);
+}
+
+#endif
+
+template <typename T>
+constexpr auto arithmetic_shift_right(T x, int y) noexcept -> decltype(x >> y) {
+ return mpt::rshift_signed(x, y);
+}
+
+template <typename T>
+constexpr auto arithmetic_shift_right(T x, int y) noexcept -> decltype(x << y) {
+ return mpt::lshift_signed(x, y);
+}
+
+template <typename T>
+constexpr auto sar(T x, int y) noexcept -> decltype(x >> y) {
+ return mpt::rshift_signed(x, y);
+}
+
+template <typename T>
+constexpr auto sal(T x, int y) noexcept -> decltype(x << y) {
+ return mpt::lshift_signed(x, y);
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_ARITHMETIC_SHIFT_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/array.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/array.hpp
new file mode 100644
index 00000000..255aff46
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/array.hpp
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_ARRAY_HPP
+#define MPT_BASE_ARRAY_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <array>
+#include <type_traits>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+template <typename T>
+struct stdarray_extent : std::integral_constant<std::size_t, 0> { };
+
+template <typename T, std::size_t N>
+struct stdarray_extent<std::array<T, N>> : std::integral_constant<std::size_t, N> { };
+
+template <typename T>
+struct is_stdarray : std::false_type { };
+
+template <typename T, std::size_t N>
+struct is_stdarray<std::array<T, N>> : std::true_type { };
+
+// mpt::extent is the same as std::extent,
+// but also works for std::array,
+// and asserts that the given type is actually an array type instead of returning 0.
+// use as:
+// mpt::extent<decltype(expr)>()
+// mpt::extent<decltype(variable)>()
+// mpt::extent<decltype(type)>()
+// mpt::extent<type>()
+template <typename T>
+constexpr std::size_t extent() noexcept {
+ using Tarray = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+ static_assert(std::is_array<Tarray>::value || mpt::is_stdarray<Tarray>::value);
+ if constexpr (mpt::is_stdarray<Tarray>::value) {
+ return mpt::stdarray_extent<Tarray>();
+ } else {
+ return std::extent<Tarray>();
+ }
+}
+
+template <typename>
+struct array_size;
+
+template <typename T, std::size_t N>
+struct array_size<std::array<T, N>> {
+ static constexpr std::size_t size = N;
+};
+
+template <typename T, std::size_t N>
+struct array_size<T[N]> {
+ static constexpr std::size_t size = N;
+};
+
+
+template <typename T, std::size_t N, typename Tx>
+constexpr std::array<T, N> init_array(const Tx & x) {
+ std::array<T, N> result{};
+ for (std::size_t i = 0; i < N; ++i) {
+ result[i] = x;
+ }
+ return result;
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_ARRAY_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/bit.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/bit.hpp
new file mode 100644
index 00000000..3bfa98eb
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/bit.hpp
@@ -0,0 +1,400 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_BIT_HPP
+#define MPT_BASE_BIT_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/macros.hpp"
+
+#if MPT_CXX_AT_LEAST(20)
+#include <bit>
+#else // !C++20
+#include <array>
+#include <limits>
+#endif // C++20
+#include <type_traits>
+
+#include <cstddef>
+#if MPT_CXX_BEFORE(20)
+#include <cstring>
+#endif // !C++20
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+#if MPT_CXX_AT_LEAST(20) && !MPT_CLANG_BEFORE(14, 0, 0)
+using std::bit_cast;
+#else // !C++20
+// C++2a compatible bit_cast.
+// Not implementing constexpr because this is not easily possible pre C++20.
+template <typename Tdst, typename Tsrc>
+MPT_FORCEINLINE typename std::enable_if<(sizeof(Tdst) == sizeof(Tsrc)) && std::is_trivially_copyable<Tsrc>::value && std::is_trivially_copyable<Tdst>::value, Tdst>::type bit_cast(const Tsrc & src) noexcept {
+ Tdst dst{};
+ std::memcpy(&dst, &src, sizeof(Tdst));
+ return dst;
+}
+#endif // C++20
+
+
+
+#if MPT_CXX_AT_LEAST(20)
+
+using std::endian;
+
+static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported");
+
+constexpr mpt::endian get_endian() noexcept {
+ return mpt::endian::native;
+}
+
+constexpr bool endian_is_little() noexcept {
+ return get_endian() == mpt::endian::little;
+}
+
+constexpr bool endian_is_big() noexcept {
+ return get_endian() == mpt::endian::big;
+}
+
+constexpr bool endian_is_weird() noexcept {
+ return !endian_is_little() && !endian_is_big();
+}
+
+#else // !C++20
+
+#if !MPT_COMPILER_GENERIC
+
+#if MPT_COMPILER_MSVC
+#define MPT_PLATFORM_LITTLE_ENDIAN
+#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define MPT_PLATFORM_BIG_ENDIAN
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define MPT_PLATFORM_LITTLE_ENDIAN
+#endif
+#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && defined(__ORDER_LITTLE_ENDIAN__)
+#if __ORDER_BIG_ENDIAN__ != __ORDER_LITTLE_ENDIAN__
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define MPT_PLATFORM_BIG_ENDIAN
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define MPT_PLATFORM_LITTLE_ENDIAN
+#endif
+#endif
+#endif
+
+// fallback:
+#if !defined(MPT_PLATFORM_BIG_ENDIAN) && !defined(MPT_PLATFORM_LITTLE_ENDIAN)
+#if (defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) \
+ || (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) \
+ || (defined(_STLP_BIG_ENDIAN) && !defined(_STLP_LITTLE_ENDIAN))
+#define MPT_PLATFORM_BIG_ENDIAN
+#elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) \
+ || (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) \
+ || (defined(_STLP_LITTLE_ENDIAN) && !defined(_STLP_BIG_ENDIAN))
+#define MPT_PLATFORM_LITTLE_ENDIAN
+#elif defined(__hpux) || defined(__hppa) \
+ || defined(_MIPSEB) \
+ || defined(__s390__)
+#define MPT_PLATFORM_BIG_ENDIAN
+#elif defined(__i386__) || defined(_M_IX86) \
+ || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
+ || defined(__bfin__)
+#define MPT_PLATFORM_LITTLE_ENDIAN
+#endif
+#endif
+
+#endif // !MPT_COMPILER_GENERIC
+
+enum class endian {
+ little = 0x78563412u,
+ big = 0x12345678u,
+ weird = 1u,
+#if MPT_COMPILER_GENERIC
+ native = 0u,
+#elif defined(MPT_PLATFORM_LITTLE_ENDIAN)
+ native = little,
+#elif defined(MPT_PLATFORM_BIG_ENDIAN)
+ native = big,
+#else
+ native = 0u,
+#endif
+};
+
+static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported");
+
+MPT_FORCEINLINE mpt::endian endian_probe() noexcept {
+ using endian_probe_type = uint32;
+ static_assert(sizeof(endian_probe_type) == 4);
+ constexpr endian_probe_type endian_probe_big = 0x12345678u;
+ constexpr endian_probe_type endian_probe_little = 0x78563412u;
+ const std::array<std::byte, sizeof(endian_probe_type)> probe{
+ {std::byte{0x12}, std::byte{0x34}, std::byte{0x56}, std::byte{0x78}}
+ };
+ const endian_probe_type test = mpt::bit_cast<endian_probe_type>(probe);
+ mpt::endian result = mpt::endian::native;
+ switch (test) {
+ case endian_probe_big:
+ result = mpt::endian::big;
+ break;
+ case endian_probe_little:
+ result = mpt::endian::little;
+ break;
+ default:
+ result = mpt::endian::weird;
+ break;
+ }
+ return result;
+}
+
+MPT_FORCEINLINE mpt::endian get_endian() noexcept {
+#if MPT_COMPILER_MSVC
+#pragma warning(push)
+#pragma warning(disable : 6285) // false-positive: (<non-zero constant> || <non-zero constant>) is always a non-zero constant.
+#endif // MPT_COMPILER_MSVC
+ if constexpr ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) {
+ return mpt::endian::native;
+ } else {
+ return mpt::endian_probe();
+ }
+#if MPT_COMPILER_MSVC
+#pragma warning(pop)
+#endif // MPT_COMPILER_MSVC
+}
+
+MPT_FORCEINLINE bool endian_is_little() noexcept {
+ return get_endian() == mpt::endian::little;
+}
+
+MPT_FORCEINLINE bool endian_is_big() noexcept {
+ return get_endian() == mpt::endian::big;
+}
+
+MPT_FORCEINLINE bool endian_is_weird() noexcept {
+ return !endian_is_little() && !endian_is_big();
+}
+
+#endif // C++20
+
+
+
+#if MPT_CXX_AT_LEAST(20) && MPT_MSVC_AT_LEAST(2022, 1) && !MPT_CLANG_BEFORE(12, 0, 0)
+
+// Disabled for VS2022.0 because of
+// <https://developercommunity.visualstudio.com/t/vs2022-cl-193030705-generates-non-universally-avai/1578571>
+// / <https://github.com/microsoft/STL/issues/2330> with fix already queued
+// (<https://github.com/microsoft/STL/pull/2333>).
+
+using std::bit_ceil;
+using std::bit_floor;
+using std::bit_width;
+using std::countl_one;
+using std::countl_zero;
+using std::countr_one;
+using std::countr_zero;
+using std::has_single_bit;
+using std::popcount;
+using std::rotl;
+using std::rotr;
+
+#else // !C++20
+
+// C++20 <bit> header.
+// Note that we do not use SFINAE here but instead rely on static_assert.
+
+template <typename T>
+constexpr int popcount(T val) noexcept {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::is_unsigned<T>::value);
+ int result = 0;
+ while (val > 0) {
+ if (val & 0x1) {
+ result++;
+ }
+ val >>= 1;
+ }
+ return result;
+}
+
+template <typename T>
+constexpr bool has_single_bit(T x) noexcept {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::is_unsigned<T>::value);
+ return mpt::popcount(x) == 1;
+}
+
+template <typename T>
+constexpr T bit_ceil(T x) noexcept {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::is_unsigned<T>::value);
+ T result = 1;
+ while (result < x) {
+ T newresult = result << 1;
+ if (newresult < result) {
+ return 0;
+ }
+ result = newresult;
+ }
+ return result;
+}
+
+template <typename T>
+constexpr T bit_floor(T x) noexcept {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::is_unsigned<T>::value);
+ if (x == 0) {
+ return 0;
+ }
+ T result = 1;
+ do {
+ T newresult = result << 1;
+ if (newresult < result) {
+ return result;
+ }
+ result = newresult;
+ } while (result <= x);
+ return result >> 1;
+}
+
+template <typename T>
+constexpr T bit_width(T x) noexcept {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::is_unsigned<T>::value);
+ T result = 0;
+ while (x > 0) {
+ x >>= 1;
+ result += 1;
+ }
+ return result;
+}
+
+template <typename T>
+constexpr int countl_zero(T x) noexcept {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::is_unsigned<T>::value);
+ int count = 0;
+ for (int bit = std::numeric_limits<T>::digits - 1; bit >= 0; --bit) {
+ if ((x & (1u << bit)) == 0u) {
+ count++;
+ } else {
+ break;
+ }
+ }
+ return count;
+}
+
+template <typename T>
+constexpr int countl_one(T x) noexcept {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::is_unsigned<T>::value);
+ int count = 0;
+ for (int bit = std::numeric_limits<T>::digits - 1; bit >= 0; --bit) {
+ if ((x & (1u << bit)) != 0u) {
+ count++;
+ } else {
+ break;
+ }
+ }
+ return count;
+}
+
+template <typename T>
+constexpr int countr_zero(T x) noexcept {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::is_unsigned<T>::value);
+ int count = 0;
+ for (int bit = 0; bit < std::numeric_limits<T>::digits; ++bit) {
+ if ((x & (1u << bit)) == 0u) {
+ count++;
+ } else {
+ break;
+ }
+ }
+ return count;
+}
+
+template <typename T>
+constexpr int countr_one(T x) noexcept {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::is_unsigned<T>::value);
+ int count = 0;
+ for (int bit = 0; bit < std::numeric_limits<T>::digits; ++bit) {
+ if ((x & (1u << bit)) != 0u) {
+ count++;
+ } else {
+ break;
+ }
+ }
+ return count;
+}
+
+template <typename T>
+constexpr T rotl_impl(T x, int r) noexcept {
+ auto N = std::numeric_limits<T>::digits;
+ return (x >> (N - r)) | (x << r);
+}
+
+template <typename T>
+constexpr T rotr_impl(T x, int r) noexcept {
+ auto N = std::numeric_limits<T>::digits;
+ return (x << (N - r)) | (x >> r);
+}
+
+template <typename T>
+constexpr T rotl(T x, int s) noexcept {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::is_unsigned<T>::value);
+ auto N = std::numeric_limits<T>::digits;
+ auto r = s % N;
+ return (s < 0) ? mpt::rotr_impl(x, -s) : ((x >> (N - r)) | (x << r));
+}
+
+template <typename T>
+constexpr T rotr(T x, int s) noexcept {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(std::is_unsigned<T>::value);
+ auto N = std::numeric_limits<T>::digits;
+ auto r = s % N;
+ return (s < 0) ? mpt::rotl_impl(x, -s) : ((x << (N - r)) | (x >> r));
+}
+
+#endif // C++20
+
+
+
+template <typename T>
+constexpr int lower_bound_entropy_bits(T x_) {
+ typename std::make_unsigned<T>::type x = static_cast<typename std::make_unsigned<T>::type>(x_);
+ return mpt::bit_width(x) == static_cast<typename std::make_unsigned<T>::type>(mpt::popcount(x)) ? mpt::bit_width(x) : mpt::bit_width(x) - 1;
+}
+
+
+template <typename T>
+constexpr bool is_mask(T x) {
+ static_assert(std::is_integral<T>::value);
+ typedef typename std::make_unsigned<T>::type unsigned_T;
+ unsigned_T ux = static_cast<unsigned_T>(x);
+ unsigned_T mask = 0;
+ for (std::size_t bits = 0; bits <= (sizeof(unsigned_T) * 8); ++bits) {
+ mask = (mask << 1) | 1u;
+ if (ux == mask) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_BIT_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/check_platform.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/check_platform.hpp
new file mode 100644
index 00000000..04f7f53b
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/check_platform.hpp
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_CHECK_PLATFORM_HPP
+#define MPT_BASE_CHECK_PLATFORM_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/pointer.hpp"
+
+#include <limits>
+
+#include <cstddef>
+#include <cstdint>
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+static_assert(sizeof(std::uintptr_t) == sizeof(void *));
+static_assert(std::numeric_limits<unsigned char>::digits == 8);
+
+static_assert(sizeof(char) == 1);
+
+static_assert(sizeof(std::byte) == 1);
+static_assert(alignof(std::byte) == 1);
+
+static_assert(mpt::arch_bits == static_cast<int>(mpt::pointer_size) * 8);
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_CHECK_PLATFORM_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/compiletime_warning.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/compiletime_warning.hpp
new file mode 100644
index 00000000..5f450021
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/compiletime_warning.hpp
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_COMPILETIME_WARNING_HPP
+#define MPT_BASE_COMPILETIME_WARNING_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/preprocessor.hpp"
+
+
+
+#if MPT_COMPILER_MSVC
+
+#define MPT_WARNING(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text))
+#define MPT_WARNING_STATEMENT(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text))
+
+#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG
+
+#define MPT_WARNING(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text))
+#define MPT_WARNING_STATEMENT(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text))
+
+#else
+
+// portable #pragma message or #warning replacement
+#define MPT_WARNING(text) \
+ static inline int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME)() noexcept { \
+ int warning [[deprecated("Warning: " text)]] = 0; \
+ return warning; \
+ } \
+/**/
+#define MPT_WARNING_STATEMENT(text) \
+ int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) = []() { \
+ int warning [[deprecated("Warning: " text)]] = 0; \
+ return warning; \
+ }() /**/
+
+#endif
+
+
+
+#endif // MPT_BASE_COMPILETIME_WARNING_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/constexpr_throw.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/constexpr_throw.hpp
new file mode 100644
index 00000000..961268f2
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/constexpr_throw.hpp
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_CONSTEXPR_THROW_HPP
+#define MPT_BASE_CONSTEXPR_THROW_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <utility>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+// Work-around for the requirement of at least 1 non-throwing function argument combination in C++ (17,2a).
+
+template <typename Exception>
+constexpr bool constexpr_throw_helper(Exception && e, bool really = true) {
+ //return !really ? really : throw std::forward<Exception>(e);
+ if (really) {
+ throw std::forward<Exception>(e);
+ }
+ // cppcheck-suppress identicalConditionAfterEarlyExit
+ return really;
+}
+
+template <typename Exception>
+constexpr bool constexpr_throw(Exception && e) {
+ return mpt::constexpr_throw_helper(std::forward<Exception>(e));
+}
+
+template <typename T, typename Exception>
+constexpr T constexpr_throw_helper(Exception && e, bool really = true) {
+ //return !really ? really : throw std::forward<Exception>(e);
+ if (really) {
+ throw std::forward<Exception>(e);
+ }
+ return T{};
+}
+
+template <typename T, typename Exception>
+constexpr T constexpr_throw(Exception && e) {
+ return mpt::constexpr_throw_helper<T>(std::forward<Exception>(e));
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_CONSTEXPR_THROW_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect.hpp
new file mode 100644
index 00000000..c7b1c15c
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect.hpp
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_DETECT_HPP
+#define MPT_BASE_DETECT_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/detect_libc.hpp"
+#include "mpt/base/detect_libcxx.hpp"
+#include "mpt/base/detect_os.hpp"
+#include "mpt/base/detect_quirks.hpp"
+
+
+
+#endif // MPT_BASE_DETECT_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_compiler.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_compiler.hpp
new file mode 100644
index 00000000..93628670
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_compiler.hpp
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_DETECT_COMPILER_HPP
+#define MPT_BASE_DETECT_COMPILER_HPP
+
+
+
+#define MPT_COMPILER_MAKE_VERSION2(version, sp) ((version)*100 + (sp))
+#define MPT_COMPILER_MAKE_VERSION3(major, minor, patch) ((major)*10000 + (minor)*100 + (patch))
+
+
+
+#if defined(MPT_COMPILER_GENERIC)
+
+#undef MPT_COMPILER_GENERIC
+#define MPT_COMPILER_GENERIC 1
+
+#elif defined(__clang__) && defined(_MSC_VER) && defined(__c2__)
+
+#error "Clang/C2 is not supported. Please use Clang/LLVM for Windows instead."
+
+#elif defined(__clang__)
+
+#define MPT_COMPILER_CLANG 1
+#define MPT_COMPILER_CLANG_VERSION MPT_COMPILER_MAKE_VERSION3(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#define MPT_CLANG_AT_LEAST(major, minor, patch) (MPT_COMPILER_CLANG_VERSION >= MPT_COMPILER_MAKE_VERSION3((major), (minor), (patch)))
+#define MPT_CLANG_BEFORE(major, minor, patch) (MPT_COMPILER_CLANG_VERSION < MPT_COMPILER_MAKE_VERSION3((major), (minor), (patch)))
+
+#if MPT_CLANG_BEFORE(7, 0, 0)
+#error "clang version 7 required"
+#endif
+
+#if defined(__clang_analyzer__)
+#ifndef MPT_BUILD_ANALYZED
+#define MPT_BUILD_ANALYZED
+#endif
+#endif
+
+#elif defined(__GNUC__)
+
+#define MPT_COMPILER_GCC 1
+#define MPT_COMPILER_GCC_VERSION MPT_COMPILER_MAKE_VERSION3(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#define MPT_GCC_AT_LEAST(major, minor, patch) (MPT_COMPILER_GCC_VERSION >= MPT_COMPILER_MAKE_VERSION3((major), (minor), (patch)))
+#define MPT_GCC_BEFORE(major, minor, patch) (MPT_COMPILER_GCC_VERSION < MPT_COMPILER_MAKE_VERSION3((major), (minor), (patch)))
+
+#if MPT_GCC_BEFORE(8, 1, 0)
+#error "GCC version 8.1 required"
+#endif
+
+#elif defined(_MSC_VER)
+
+#define MPT_COMPILER_MSVC 1
+#if (_MSC_VER >= 1933)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 3)
+#elif (_MSC_VER >= 1932)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 2)
+#elif (_MSC_VER >= 1931)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 1)
+#elif (_MSC_VER >= 1930)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 0)
+#elif (_MSC_VER >= 1929)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 10)
+#elif (_MSC_VER >= 1928)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 8)
+#elif (_MSC_VER >= 1927)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 7)
+#elif (_MSC_VER >= 1926)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 6)
+#elif (_MSC_VER >= 1925)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 5)
+#elif (_MSC_VER >= 1924)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 4)
+#elif (_MSC_VER >= 1923)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 3)
+#elif (_MSC_VER >= 1922)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 2)
+#elif (_MSC_VER >= 1921)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 1)
+#elif (_MSC_VER >= 1920)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 0)
+#elif (_MSC_VER >= 1916)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 9)
+#elif (_MSC_VER >= 1915)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 8)
+#elif (_MSC_VER >= 1914)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 7)
+#elif (_MSC_VER >= 1913)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 6)
+#elif (_MSC_VER >= 1912)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 5)
+#elif (_MSC_VER >= 1911)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 3)
+#elif (_MSC_VER >= 1910)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 0)
+#elif (_MSC_VER >= 1900) && defined(_MSVC_LANG)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2015, 3)
+#elif (_MSC_VER >= 1900)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2015, 0)
+#elif (_MSC_VER >= 1800)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2013, 0)
+#elif (_MSC_VER >= 1700)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2012, 0)
+#elif (_MSC_VER >= 1600)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2010, 0)
+#elif (_MSC_VER >= 1500)
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2008, 0)
+#else
+#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2005, 0)
+#endif
+#define MPT_MSVC_AT_LEAST(version, sp) (MPT_COMPILER_MSVC_VERSION >= MPT_COMPILER_MAKE_VERSION2((version), (sp)))
+#define MPT_MSVC_BEFORE(version, sp) (MPT_COMPILER_MSVC_VERSION < MPT_COMPILER_MAKE_VERSION2((version), (sp)))
+
+#if MPT_MSVC_BEFORE(2017, 9)
+#error "MSVC version 2017 15.9 required"
+#endif
+
+#if defined(_PREFAST_)
+#ifndef MPT_BUILD_ANALYZED
+#define MPT_BUILD_ANALYZED
+#endif
+#endif
+
+#else
+
+#define MPT_COMPILER_GENERIC 1
+
+#endif
+
+
+
+#ifndef MPT_COMPILER_GENERIC
+#define MPT_COMPILER_GENERIC 0
+#endif
+#ifndef MPT_COMPILER_CLANG
+#define MPT_COMPILER_CLANG 0
+#define MPT_CLANG_AT_LEAST(major, minor, patch) 0
+#define MPT_CLANG_BEFORE(major, minor, patch) 0
+#endif
+#ifndef MPT_COMPILER_GCC
+#define MPT_COMPILER_GCC 0
+#define MPT_GCC_AT_LEAST(major, minor, patch) 0
+#define MPT_GCC_BEFORE(major, minor, patch) 0
+#endif
+#ifndef MPT_COMPILER_MSVC
+#define MPT_COMPILER_MSVC 0
+#define MPT_MSVC_AT_LEAST(version, sp) 0
+#define MPT_MSVC_BEFORE(version, sp) 0
+#endif
+
+
+
+#if MPT_COMPILER_GENERIC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG
+
+#if (__cplusplus >= 202002)
+// Support for C++20 is lacking across all compilers.
+// Only assume C++17 for non-MSVC, even when in C++20 mode.
+#define MPT_CXX 17
+#elif (__cplusplus >= 201703)
+#define MPT_CXX 17
+#else
+#define MPT_CXX 17
+#endif
+
+#elif MPT_COMPILER_MSVC
+
+#if MPT_MSVC_AT_LEAST(2019, 10) && (_MSVC_LANG >= 201705)
+#define MPT_CXX 20
+#elif (_MSVC_LANG >= 201703)
+#define MPT_CXX 17
+#else
+#define MPT_CXX 17
+#endif
+
+#else
+
+#define MPT_CXX 17
+
+#endif
+
+// MPT_CXX is stricter than just using __cplusplus directly.
+// We will only claim a language version as supported IFF all core language and
+// library fatures that we need are actually supported AND working correctly
+// (to our needs).
+
+#define MPT_CXX_AT_LEAST(version) (MPT_CXX >= (version))
+#define MPT_CXX_BEFORE(version) (MPT_CXX < (version))
+
+
+
+#endif // MPT_BASE_DETECT_COMPILER_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_libc.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_libc.hpp
new file mode 100644
index 00000000..4cc811e7
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_libc.hpp
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_DETECT_LIBC_HPP
+#define MPT_BASE_DETECT_LIBC_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/detect_os.hpp"
+
+#include <cstddef>
+
+
+
+// order of checks is important!
+#if MPT_COMPILER_GENERIC
+#define MPT_LIBC_GENERIC 1
+#elif MPT_COMPILER_GCC && (defined(__MINGW32__) || defined(__MINGW64__))
+#define MPT_LIBC_MS 1
+#elif defined(__GLIBC__)
+#define MPT_LIBC_GLIBC 1
+#elif MPT_COMPILER_MSVC
+#define MPT_LIBC_MS 1
+#elif MPT_COMPILER_CLANG && MPT_OS_WINDOWS
+#define MPT_LIBC_MS 1
+#else
+#define MPT_LIBC_GENERIC 1
+#endif
+
+#ifndef MPT_LIBC_GENERIC
+#define MPT_LIBC_GENERIC 0
+#endif
+#ifndef MPT_LIBC_GLIBC
+#define MPT_LIBC_GLIBC 0
+#endif
+#ifndef MPT_LIBC_MS
+#define MPT_LIBC_MS 0
+#endif
+
+
+
+#endif // MPT_BASE_DETECT_LIBC_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_libcxx.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_libcxx.hpp
new file mode 100644
index 00000000..7f21564f
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_libcxx.hpp
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_DETECT_LIBCXX_HPP
+#define MPT_BASE_DETECT_LIBCXX_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/detect_os.hpp"
+
+#if MPT_CXX_AT_LEAST(20)
+#include <version>
+#else // !C++20
+#include <array>
+#endif // C++20
+
+
+
+// order of checks is important!
+#if MPT_COMPILER_GENERIC
+#define MPT_LIBCXX_GENERIC 1
+#elif defined(_LIBCPP_VERSION)
+#define MPT_LIBCXX_LLVM 1
+#elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
+#define MPT_LIBCXX_GNU 1
+#elif MPT_COMPILER_MSVC
+#define MPT_LIBCXX_MS 1
+#elif MPT_COMPILER_CLANG && MPT_OS_WINDOWS
+#define MPT_LIBCXX_MS 1
+#else
+#define MPT_LIBCXX_GENERIC 1
+#endif
+
+#ifndef MPT_LIBCXX_GENERIC
+#define MPT_LIBCXX_GENERIC 0
+#endif
+#ifndef MPT_LIBCXX_LLVM
+#define MPT_LIBCXX_LLVM 0
+#endif
+#ifndef MPT_LIBCXX_GNU
+#define MPT_LIBCXX_GNU 0
+#endif
+#ifndef MPT_LIBCXX_MS
+#define MPT_LIBCXX_MS 0
+#endif
+
+
+
+#endif // MPT_BASE_DETECT_LIBCXX_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_os.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_os.hpp
new file mode 100644
index 00000000..507cce88
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_os.hpp
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_DETECT_OS_HPP
+#define MPT_BASE_DETECT_OS_HPP
+
+
+
+// The order of the checks matters!
+#if defined(__DJGPP__)
+#define MPT_OS_DJGPP 1
+#elif defined(__EMSCRIPTEN__)
+#define MPT_OS_EMSCRIPTEN 1
+#if !defined(__EMSCRIPTEN_major__) || !defined(__EMSCRIPTEN_minor__) || !defined(__EMSCRIPTEN_tiny__)
+#include <emscripten/version.h>
+#endif
+#if defined(__EMSCRIPTEN_major__) && defined(__EMSCRIPTEN_minor__)
+#if (__EMSCRIPTEN_major__ > 1)
+// ok
+#elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ > 39)
+// ok
+#elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ == 39) && (__EMSCRIPTEN_tiny__ >= 7)
+// ok
+#else
+#error "Emscripten >= 1.39.7 is required."
+#endif
+#endif
+#elif defined(_WIN32)
+#define MPT_OS_WINDOWS 1
+#if defined(WINAPI_FAMILY)
+#include <winapifamily.h>
+#if (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
+#define MPT_OS_WINDOWS_WINRT 0
+#else
+#define MPT_OS_WINDOWS_WINRT 1
+#endif
+#else // !WINAPI_FAMILY
+#define MPT_OS_WINDOWS_WINRT 0
+#endif // WINAPI_FAMILY
+#elif defined(__APPLE__)
+#define MPT_OS_MACOSX_OR_IOS 1
+#include <TargetConditionals.h>
+#if defined(TARGET_OS_OSX)
+#if (TARGET_OS_OSX != 0)
+#include <AvailabilityMacros.h>
+#endif
+#endif
+//#if TARGET_IPHONE_SIMULATOR
+//#elif TARGET_OS_IPHONE
+//#elif TARGET_OS_MAC
+//#else
+//#endif
+#elif defined(__HAIKU__)
+#define MPT_OS_HAIKU 1
+#elif defined(__ANDROID__) || defined(ANDROID)
+#define MPT_OS_ANDROID 1
+#elif defined(__linux__)
+#define MPT_OS_LINUX 1
+#elif defined(__DragonFly__)
+#define MPT_OS_DRAGONFLYBSD 1
+#elif defined(__FreeBSD__)
+#define MPT_OS_FREEBSD 1
+#elif defined(__OpenBSD__)
+#define MPT_OS_OPENBSD 1
+#elif defined(__NetBSD__)
+#define MPT_OS_NETBSD 1
+#elif defined(__unix__)
+#define MPT_OS_GENERIC_UNIX 1
+#else
+#define MPT_OS_UNKNOWN 1
+#endif
+
+#ifndef MPT_OS_DJGPP
+#define MPT_OS_DJGPP 0
+#endif
+#ifndef MPT_OS_EMSCRIPTEN
+#define MPT_OS_EMSCRIPTEN 0
+#endif
+#ifndef MPT_OS_WINDOWS
+#define MPT_OS_WINDOWS 0
+#endif
+#ifndef MPT_OS_WINDOWS_WINRT
+#define MPT_OS_WINDOWS_WINRT 0
+#endif
+#ifndef MPT_OS_MACOSX_OR_IOS
+#define MPT_OS_MACOSX_OR_IOS 0
+#endif
+#ifndef MPT_OS_HAIKU
+#define MPT_OS_HAIKU 0
+#endif
+#ifndef MPT_OS_ANDROID
+#define MPT_OS_ANDROID 0
+#endif
+#ifndef MPT_OS_LINUX
+#define MPT_OS_LINUX 0
+#endif
+#ifndef MPT_OS_DRAGONFLYBSD
+#define MPT_OS_DRAGONFLYBSD 0
+#endif
+#ifndef MPT_OS_FREEBSD
+#define MPT_OS_FREEBSD 0
+#endif
+#ifndef MPT_OS_OPENBSD
+#define MPT_OS_OPENBSD 0
+#endif
+#ifndef MPT_OS_NETBSD
+#define MPT_OS_NETBSD 0
+#endif
+#ifndef MPT_OS_GENERIC_UNIX
+#define MPT_OS_GENERIC_UNIX 0
+#endif
+#ifndef MPT_OS_UNKNOWN
+#define MPT_OS_UNKNOWN 0
+#endif
+
+
+
+#endif // MPT_BASE_DETECT_OS_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_quirks.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_quirks.hpp
new file mode 100644
index 00000000..ab7b076a
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/detect_quirks.hpp
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_DETECT_QUIRKS_HPP
+#define MPT_BASE_DETECT_QUIRKS_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/detect_libcxx.hpp"
+#include "mpt/base/detect_os.hpp"
+
+
+
+#if MPT_COMPILER_MSVC
+// Compiler has multiplication/division semantics when shifting signed integers.
+#define MPT_COMPILER_SHIFT_SIGNED 1
+#endif
+
+#ifndef MPT_COMPILER_SHIFT_SIGNED
+#define MPT_COMPILER_SHIFT_SIGNED 0
+#endif
+
+
+
+// This should really be based on __STDCPP_THREADS__, but that is not defined by
+// GCC or clang. Stupid.
+// Just assume multithreaded and disable for platforms we know are
+// singlethreaded later on.
+#define MPT_PLATFORM_MULTITHREADED 1
+
+#if MPT_OS_DJGPP
+#undef MPT_PLATFORM_MULTITHREADED
+#define MPT_PLATFORM_MULTITHREADED 0
+#endif
+
+#if (MPT_OS_EMSCRIPTEN && !defined(__EMSCRIPTEN_PTHREADS__))
+#undef MPT_PLATFORM_MULTITHREADED
+#define MPT_PLATFORM_MULTITHREADED 0
+#endif
+
+
+
+#if MPT_OS_WINDOWS && MPT_COMPILER_MSVC
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) // _WIN32_WINNT_VISTA
+#define MPT_COMPILER_QUIRK_COMPLEX_STD_MUTEX
+#endif
+#endif
+
+
+
+#if MPT_OS_EMSCRIPTEN && defined(MPT_BUILD_AUDIOWORKLETPROCESSOR)
+#define MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK
+#endif
+
+
+
+#if MPT_OS_EMSCRIPTEN && defined(MPT_BUILD_AUDIOWORKLETPROCESSOR)
+#define MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
+#endif
+
+
+
+#if MPT_OS_DJGPP
+#define MPT_COMPILER_QUIRK_NO_WCHAR
+#endif
+
+
+
+#if MPT_OS_WINDOWS && MPT_GCC_BEFORE(9, 1, 0)
+// GCC C++ library has no wchar_t overloads
+#define MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR
+#endif
+
+
+
+#if defined(__arm__)
+
+#if defined(__SOFTFP__)
+#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 1
+#else
+#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0
+#endif
+#if defined(__VFP_FP__)
+// native-endian IEEE754
+#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 0
+#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0
+#elif defined(__MAVERICK__)
+// little-endian IEEE754, we assume native-endian though
+#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 1
+#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0
+#else
+// not IEEE754
+#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 1
+#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 1
+#endif
+
+#elif defined(__mips__)
+
+#if defined(__mips_soft_float)
+#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 1
+#else
+#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0
+#endif
+
+#endif
+
+#if MPT_OS_EMSCRIPTEN
+#define MPT_COMPILER_QUIRK_FLOAT_PREFER64 1
+#endif
+
+#ifndef MPT_COMPILER_QUIRK_FLOAT_PREFER32
+#define MPT_COMPILER_QUIRK_FLOAT_PREFER32 0
+#endif
+#ifndef MPT_COMPILER_QUIRK_FLOAT_PREFER64
+#define MPT_COMPILER_QUIRK_FLOAT_PREFER64 0
+#endif
+#ifndef MPT_COMPILER_QUIRK_FLOAT_EMULATED
+#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0
+#endif
+#ifndef MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN
+#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 0
+#endif
+#ifndef MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754
+#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0
+#endif
+
+
+
+#if MPT_OS_MACOSX_OR_IOS
+#if defined(TARGET_OS_OSX)
+#if TARGET_OS_OSX
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_15)
+#define MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT
+#endif
+#endif
+#endif
+#endif
+
+
+
+#if (MPT_LIBCXX_MS && (MPT_MSVC_BEFORE(2019, 4) || !MPT_COMPILER_MSVC)) || (MPT_LIBCXX_GNU && (MPT_GCC_BEFORE(11, 0, 0) || !MPT_COMPILER_GCC)) || MPT_LIBCXX_LLVM || MPT_LIBCXX_GENERIC
+#define MPT_LIBCXX_QUIRK_NO_TO_CHARS_FLOAT
+#endif
+
+
+
+#endif // MPT_BASE_DETECT_QUIRKS_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/floatingpoint.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/floatingpoint.hpp
new file mode 100644
index 00000000..efce0ab2
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/floatingpoint.hpp
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_FLOATINGPOINT_HPP
+#define MPT_BASE_FLOATINGPOINT_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <limits>
+#include <type_traits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+// fp half
+// n/a
+
+// fp single
+using single = float;
+namespace float_literals {
+constexpr single operator"" _fs(long double lit) noexcept {
+ return static_cast<single>(lit);
+}
+} // namespace float_literals
+
+// fp double
+namespace float_literals {
+constexpr double operator"" _fd(long double lit) noexcept {
+ return static_cast<double>(lit);
+}
+} // namespace float_literals
+
+// fp extended
+namespace float_literals {
+constexpr long double operator"" _fe(long double lit) noexcept {
+ return static_cast<long double>(lit);
+}
+} // namespace float_literals
+
+// fp quad
+// n/a
+
+using float32 = std::conditional<sizeof(float) == 4, float, std::conditional<sizeof(double) == 4, double, std::conditional<sizeof(long double) == 4, long double, float>::type>::type>::type;
+namespace float_literals {
+constexpr float32 operator"" _f32(long double lit) noexcept {
+ return static_cast<float32>(lit);
+}
+} // namespace float_literals
+
+using float64 = std::conditional<sizeof(float) == 8, float, std::conditional<sizeof(double) == 8, double, std::conditional<sizeof(long double) == 8, long double, double>::type>::type>::type;
+namespace float_literals {
+constexpr float64 operator"" _f64(long double lit) noexcept {
+ return static_cast<float64>(lit);
+}
+} // namespace float_literals
+
+template <typename T>
+struct float_traits {
+ static constexpr bool is_float = !std::numeric_limits<T>::is_integer;
+ static constexpr bool is_hard = is_float && !MPT_COMPILER_QUIRK_FLOAT_EMULATED;
+ static constexpr bool is_soft = is_float && MPT_COMPILER_QUIRK_FLOAT_EMULATED;
+ static constexpr bool is_float32 = is_float && (sizeof(T) == 4);
+ static constexpr bool is_float64 = is_float && (sizeof(T) == 8);
+ static constexpr bool is_native_endian = is_float && !MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN;
+ static constexpr bool is_ieee754_binary = is_float && std::numeric_limits<T>::is_iec559 && !MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754;
+ static constexpr bool is_ieee754_binary32 = is_float && is_ieee754_binary && is_float32;
+ static constexpr bool is_ieee754_binary64 = is_float && is_ieee754_binary && is_float64;
+ static constexpr bool is_ieee754_binary32ne = is_float && is_ieee754_binary && is_float32 && is_native_endian;
+ static constexpr bool is_ieee754_binary64ne = is_float && is_ieee754_binary && is_float64 && is_native_endian;
+ static constexpr bool is_preferred = is_float && ((is_float32 && MPT_COMPILER_QUIRK_FLOAT_PREFER32) || (is_float64 && MPT_COMPILER_QUIRK_FLOAT_PREFER64));
+};
+
+// prefer smaller floats, but try to use IEEE754 floats
+using nativefloat =
+ std::conditional<mpt::float_traits<float32>::is_preferred, float32, std::conditional<mpt::float_traits<float64>::is_preferred, float64, std::conditional<std::numeric_limits<float>::is_iec559, float, std::conditional<std::numeric_limits<double>::is_iec559, double, std::conditional<std::numeric_limits<long double>::is_iec559, long double, float>::type>::type>::type>::type>::type;
+namespace float_literals {
+constexpr nativefloat operator"" _nf(long double lit) noexcept {
+ return static_cast<nativefloat>(lit);
+}
+} // namespace float_literals
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_FLOATINGPOINT_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/integer.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/integer.hpp
new file mode 100644
index 00000000..0e681fe5
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/integer.hpp
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_INTEGER_HPP
+#define MPT_BASE_INTEGER_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+
+#include <cstdint>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+using int8 = std::int8_t;
+using int16 = std::int16_t;
+using int32 = std::int32_t;
+using int64 = std::int64_t;
+using uint8 = std::uint8_t;
+using uint16 = std::uint16_t;
+using uint32 = std::uint32_t;
+using uint64 = std::uint64_t;
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_INTEGER_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/macros.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/macros.hpp
new file mode 100644
index 00000000..c54ebf92
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/macros.hpp
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_MACROS_HPP
+#define MPT_BASE_MACROS_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+
+#include <type_traits>
+
+#if MPT_COMPILER_MSVC && MPT_OS_WINDOWS
+#include <windows.h>
+#endif // MPT_COMPILER_MSVC && MPT_OS_WINDOWS
+
+
+
+// Advanced inline attributes
+#if MPT_COMPILER_MSVC
+#define MPT_FORCEINLINE __forceinline
+#define MPT_NOINLINE __declspec(noinline)
+#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG
+#define MPT_FORCEINLINE __attribute__((always_inline)) inline
+#define MPT_NOINLINE __attribute__((noinline))
+#else
+#define MPT_FORCEINLINE inline
+#define MPT_NOINLINE
+#endif
+
+
+
+// constexpr
+#define MPT_CONSTEXPRINLINE constexpr MPT_FORCEINLINE
+#if MPT_CXX_AT_LEAST(20)
+#define MPT_CONSTEXPR20_FUN constexpr MPT_FORCEINLINE
+#define MPT_CONSTEXPR20_VAR constexpr
+#else // !C++20
+#define MPT_CONSTEXPR20_FUN MPT_FORCEINLINE
+#define MPT_CONSTEXPR20_VAR const
+#endif // C++20
+
+
+
+#define MPT_FORCE_CONSTEXPR(expr) [&]() { \
+ constexpr auto x = (expr); \
+ return x; \
+}()
+
+
+
+#if MPT_CXX_AT_LEAST(20)
+#define MPT_IS_CONSTANT_EVALUATED20() std::is_constant_evaluated()
+#define MPT_IS_CONSTANT_EVALUATED() std::is_constant_evaluated()
+#else // !C++20
+#define MPT_IS_CONSTANT_EVALUATED20() false
+// this pessimizes the case for C++17 by always assuming constexpr context, which implies always running constexpr-friendly code
+#define MPT_IS_CONSTANT_EVALUATED() true
+#endif // C++20
+
+
+
+#if MPT_COMPILER_MSVC
+#define MPT_MAYBE_CONSTANT_IF(x) \
+ __pragma(warning(push)) \
+ __pragma(warning(disable : 4127)) \
+ if (x) \
+ __pragma(warning(pop)) \
+/**/
+#endif
+
+#if MPT_COMPILER_GCC
+#define MPT_MAYBE_CONSTANT_IF(x) \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \
+ if (x) \
+ _Pragma("GCC diagnostic pop") \
+/**/
+#endif
+
+#if MPT_COMPILER_CLANG
+#define MPT_MAYBE_CONSTANT_IF(x) \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") \
+ _Pragma("clang diagnostic ignored \"-Wtype-limits\"") \
+ _Pragma("clang diagnostic ignored \"-Wtautological-constant-out-of-range-compare\"") \
+ if (x) \
+ _Pragma("clang diagnostic pop") \
+/**/
+#endif
+
+#if !defined(MPT_MAYBE_CONSTANT_IF)
+// MPT_MAYBE_CONSTANT_IF disables compiler warnings for conditions that may in some case be either always false or always true (this may turn out to be useful in ASSERTions in some cases).
+#define MPT_MAYBE_CONSTANT_IF(x) if (x)
+#endif
+
+
+
+#if MPT_COMPILER_MSVC && MPT_OS_WINDOWS
+#define MPT_UNUSED(x) UNREFERENCED_PARAMETER(x)
+#else
+#define MPT_UNUSED(x) static_cast<void>(x)
+#endif
+
+
+
+#define MPT_DISCARD(expr) static_cast<void>(expr)
+
+
+
+// Use MPT_RESTRICT to indicate that a pointer is guaranteed to not be aliased.
+#if MPT_COMPILER_MSVC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG
+#define MPT_RESTRICT __restrict
+#else
+#define MPT_RESTRICT
+#endif
+
+
+
+#endif // MPT_BASE_MACROS_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/math.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/math.hpp
new file mode 100644
index 00000000..23e50d73
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/math.hpp
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_EMPTY_HPP
+#define MPT_BASE_EMPTY_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <algorithm>
+#include <type_traits>
+
+#include <cmath>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+#if MPT_OS_DJGPP
+
+inline long double log2(const long double val) {
+ return static_cast<long double>(::log2(static_cast<double>(val)));
+}
+
+inline double log2(const double val) {
+ return ::log2(val);
+}
+
+inline float log2(const float val) {
+ return ::log2f(val);
+}
+
+#else // !MPT_OS_DJGPP
+
+// C++11 std::log2
+using std::log2;
+
+#endif // MPT_OS_DJGPP
+
+
+#if MPT_OS_DJGPP
+
+inline long double round(const long double val) {
+ return ::roundl(val);
+}
+
+inline double round(const double val) {
+ return ::round(val);
+}
+
+inline float round(const float val) {
+ return ::roundf(val);
+}
+
+#else // !MPT_OS_DJGPP
+
+// C++11 std::round
+using std::round;
+
+#endif // MPT_OS_DJGPP
+
+
+template <typename T>
+inline T sanitize_nan(T val) {
+ static_assert(std::is_floating_point<T>::value);
+ if (std::isnan(val)) {
+ return T(0.0);
+ }
+ return val;
+}
+
+
+template <typename T>
+inline T safe_clamp(T v, T lo, T hi) {
+ static_assert(std::is_floating_point<T>::value);
+ return std::clamp(mpt::sanitize_nan(v), lo, hi);
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_EMPTY_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/memory.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/memory.hpp
new file mode 100644
index 00000000..1ee93378
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/memory.hpp
@@ -0,0 +1,273 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_MEMORY_HPP
+#define MPT_BASE_MEMORY_HPP
+
+
+
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/span.hpp"
+
+#include <type_traits>
+
+#include <cstddef>
+#include <cstring>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+using byte_span = mpt::span<std::byte>;
+using const_byte_span = mpt::span<const std::byte>;
+
+
+
+// Tell which types are safe for mpt::byte_cast.
+// signed char is actually not allowed to alias into an object representation,
+// which means that, if the actual type is not itself signed char but char or
+// unsigned char instead, dereferencing the signed char pointer is undefined
+// behaviour.
+template <typename T>
+struct is_byte_castable : public std::false_type { };
+template <>
+struct is_byte_castable<char> : public std::true_type { };
+template <>
+struct is_byte_castable<unsigned char> : public std::true_type { };
+template <>
+struct is_byte_castable<std::byte> : public std::true_type { };
+template <>
+struct is_byte_castable<const char> : public std::true_type { };
+template <>
+struct is_byte_castable<const unsigned char> : public std::true_type { };
+template <>
+struct is_byte_castable<const std::byte> : public std::true_type { };
+
+
+template <typename T>
+struct is_byte : public std::false_type { };
+template <>
+struct is_byte<std::byte> : public std::true_type { };
+template <>
+struct is_byte<const std::byte> : public std::true_type { };
+
+
+template <typename T>
+constexpr bool declare_binary_safe(const T &) noexcept {
+ return false;
+}
+
+constexpr bool declare_binary_safe(const char &) noexcept {
+ return true;
+}
+constexpr bool declare_binary_safe(const uint8 &) noexcept {
+ return true;
+}
+constexpr bool declare_binary_safe(const int8 &) noexcept {
+ return true;
+}
+constexpr bool declare_binary_safe(const std::byte &) noexcept {
+ return true;
+}
+
+// Tell which types are safe to binary write into files.
+// By default, no types are safe.
+// When a safe type gets defined,
+// also specialize this template so that IO functions will work.
+template <typename T>
+struct is_binary_safe : public std::conditional<declare_binary_safe(T{}), std::true_type, std::false_type>::type { };
+
+// Generic Specialization for arrays.
+template <typename T, std::size_t N>
+struct is_binary_safe<T[N]> : public is_binary_safe<T> { };
+template <typename T, std::size_t N>
+struct is_binary_safe<const T[N]> : public is_binary_safe<T> { };
+template <typename T, std::size_t N>
+struct is_binary_safe<std::array<T, N>> : public is_binary_safe<T> { };
+template <typename T, std::size_t N>
+struct is_binary_safe<const std::array<T, N>> : public is_binary_safe<T> { };
+
+
+template <typename T>
+constexpr bool check_binary_size(std::size_t size) noexcept {
+ return true
+ && (sizeof(T) == size)
+ && (alignof(T) == 1)
+ && std::is_standard_layout<T>::value
+ && std::has_unique_object_representations<T>::value
+ && mpt::is_binary_safe<T>::value;
+}
+
+
+template <typename Tdst, typename Tsrc>
+struct byte_cast_impl {
+ inline Tdst operator()(Tsrc src) const noexcept {
+ static_assert(sizeof(Tsrc) == sizeof(std::byte));
+ static_assert(sizeof(Tdst) == sizeof(std::byte));
+ // not checking is_byte_castable here because we are actually
+ // doing a static_cast and converting the value
+ static_assert(std::is_integral<Tsrc>::value || mpt::is_byte<Tsrc>::value);
+ static_assert(std::is_integral<Tdst>::value || mpt::is_byte<Tdst>::value);
+ return static_cast<Tdst>(src);
+ }
+};
+
+template <typename Tdst, typename Tsrc>
+struct byte_cast_impl<mpt::span<Tdst>, mpt::span<Tsrc>> {
+ inline mpt::span<Tdst> operator()(mpt::span<Tsrc> src) const noexcept {
+ static_assert(sizeof(Tsrc) == sizeof(std::byte));
+ static_assert(sizeof(Tdst) == sizeof(std::byte));
+ static_assert(mpt::is_byte_castable<Tsrc>::value);
+ static_assert(mpt::is_byte_castable<Tdst>::value);
+ static_assert(std::is_integral<Tsrc>::value || mpt::is_byte<Tsrc>::value);
+ static_assert(std::is_integral<Tdst>::value || mpt::is_byte<Tdst>::value);
+ return mpt::as_span(mpt::byte_cast_impl<Tdst *, Tsrc *>()(src.data()), mpt::byte_cast_impl<Tdst *, Tsrc *>()(src.data() + src.size()));
+ }
+};
+
+template <typename Tdst, typename Tsrc>
+struct byte_cast_impl<Tdst *, Tsrc *> {
+ inline Tdst * operator()(Tsrc * src) const noexcept {
+ static_assert(sizeof(Tsrc) == sizeof(std::byte));
+ static_assert(sizeof(Tdst) == sizeof(std::byte));
+ static_assert(mpt::is_byte_castable<Tsrc>::value);
+ static_assert(mpt::is_byte_castable<Tdst>::value);
+ static_assert(std::is_integral<Tsrc>::value || mpt::is_byte<Tsrc>::value);
+ static_assert(std::is_integral<Tdst>::value || mpt::is_byte<Tdst>::value);
+ return reinterpret_cast<Tdst *>(src);
+ }
+};
+
+template <typename Tdst, typename Tsrc>
+struct void_cast_impl;
+
+template <typename Tdst>
+struct void_cast_impl<Tdst *, void *> {
+ inline Tdst * operator()(void * src) const noexcept {
+ static_assert(sizeof(Tdst) == sizeof(std::byte));
+ static_assert(mpt::is_byte_castable<Tdst>::value);
+ static_assert(std::is_integral<Tdst>::value || mpt::is_byte<Tdst>::value);
+ return reinterpret_cast<Tdst *>(src);
+ }
+};
+
+template <typename Tdst>
+struct void_cast_impl<Tdst *, const void *> {
+ inline Tdst * operator()(const void * src) const noexcept {
+ static_assert(sizeof(Tdst) == sizeof(std::byte));
+ static_assert(mpt::is_byte_castable<Tdst>::value);
+ static_assert(std::is_integral<Tdst>::value || mpt::is_byte<Tdst>::value);
+ return reinterpret_cast<Tdst *>(src);
+ }
+};
+
+template <typename Tsrc>
+struct void_cast_impl<void *, Tsrc *> {
+ inline void * operator()(Tsrc * src) const noexcept {
+ static_assert(sizeof(Tsrc) == sizeof(std::byte));
+ static_assert(mpt::is_byte_castable<Tsrc>::value);
+ static_assert(std::is_integral<Tsrc>::value || mpt::is_byte<Tsrc>::value);
+ return reinterpret_cast<void *>(src);
+ }
+};
+
+template <typename Tsrc>
+struct void_cast_impl<const void *, Tsrc *> {
+ inline const void * operator()(Tsrc * src) const noexcept {
+ static_assert(sizeof(Tsrc) == sizeof(std::byte));
+ static_assert(mpt::is_byte_castable<Tsrc>::value);
+ static_assert(std::is_integral<Tsrc>::value || mpt::is_byte<Tsrc>::value);
+ return reinterpret_cast<const void *>(src);
+ }
+};
+
+// casts between different byte (char) types or pointers to these types
+template <typename Tdst, typename Tsrc>
+inline Tdst byte_cast(Tsrc src) noexcept {
+ return byte_cast_impl<Tdst, Tsrc>()(src);
+}
+
+// casts between pointers to void and pointers to byte
+template <typename Tdst, typename Tsrc>
+inline Tdst void_cast(Tsrc src) noexcept {
+ return void_cast_impl<Tdst, Tsrc>()(src);
+}
+
+
+
+template <typename T>
+MPT_CONSTEXPRINLINE std::byte as_byte(T src) noexcept {
+ static_assert(std::is_integral<T>::value);
+ return static_cast<std::byte>(static_cast<uint8>(src));
+}
+
+
+
+template <typename T>
+struct as_raw_memory_impl {
+ inline mpt::const_byte_span operator()(const T & v) const {
+ static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
+ return mpt::as_span(reinterpret_cast<const std::byte *>(&v), sizeof(T));
+ }
+ inline mpt::byte_span operator()(T & v) const {
+ static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
+ return mpt::as_span(reinterpret_cast<std::byte *>(&v), sizeof(T));
+ }
+};
+
+template <typename T, std::size_t N>
+struct as_raw_memory_impl<T[N]> {
+ inline mpt::const_byte_span operator()(const T (&v)[N]) const {
+ static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
+ return mpt::as_span(reinterpret_cast<const std::byte *>(v), N * sizeof(T));
+ }
+ inline mpt::byte_span operator()(T (&v)[N]) const {
+ static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
+ return mpt::as_span(reinterpret_cast<std::byte *>(v), N * sizeof(T));
+ }
+};
+
+template <typename T, std::size_t N>
+struct as_raw_memory_impl<const T[N]> {
+ inline mpt::const_byte_span operator()(const T (&v)[N]) const {
+ static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
+ return mpt::as_span(reinterpret_cast<const std::byte *>(v), N * sizeof(T));
+ }
+};
+
+// In order to be able to partially specialize it,
+// as_raw_memory is implemented via a class template.
+// Do not overload or specialize as_raw_memory directly.
+// Using a wrapper (by default just around a cast to const std::byte *),
+// allows for implementing raw memory access
+// via on-demand generating a cached serialized representation.
+template <typename T>
+inline mpt::const_byte_span as_raw_memory(const T & v) {
+ return mpt::as_raw_memory_impl<T>()(v);
+}
+template <typename T>
+inline mpt::byte_span as_raw_memory(T & v) {
+ return mpt::as_raw_memory_impl<T>()(v);
+}
+
+
+
+template <class T>
+inline void memclear(T & x) {
+ static_assert(std::is_standard_layout<T>::value);
+ static_assert((std::is_trivially_default_constructible<T>::value && std::is_trivially_copyable<T>::value) || mpt::is_binary_safe<T>::value);
+ std::memset(&x, 0, sizeof(T));
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_MEMORY_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/namespace.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/namespace.hpp
new file mode 100644
index 00000000..36b7e5cc
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/namespace.hpp
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_NAMESPACE_HPP
+#define MPT_BASE_NAMESPACE_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/version.hpp"
+#include "mpt/base/compiletime_warning.hpp"
+
+
+
+#if !defined(MPT_INLINE_NS)
+
+#define MPT_BUILD_VERSION_NAMESPACE_IMPL(a, b, c, d) v##a##_##b##_##c##_##d
+#define MPT_BUILD_VERSION_NAMESPACE(a, b, c, d) MPT_BUILD_VERSION_NAMESPACE_IMPL(a, b, c, d)
+
+#define MPT_VERSION_NAMESPACE MPT_BUILD_VERSION_NAMESPACE(MPT_VERSION_MAJOR, MPT_VERSION_MINOR, MPT_VERSION_PATCH, MPT_VERSION_BUILD)
+
+#if MPT_OS_WINDOWS
+#ifdef UNICODE
+#define MPT_VERSION_ABI_OS u
+#else
+#define MPT_VERSION_ABI_OS 8
+#endif
+#else
+#define MPT_VERSION_ABI_OS _
+#endif
+
+#if MPT_LIBC_GENERIC
+#define MPT_VERSION_ABI_LIBC _
+#elif MPT_LIBC_MS
+#ifdef _DLL
+#ifdef _DEBUG
+#define MPT_VERSION_ABI_LIBC MDd
+#else
+#define MPT_VERSION_ABI_LIBC MDr
+#endif
+#else
+#ifdef _DEBUG
+#define MPT_VERSION_ABI_LIBC MTd
+#else
+#define MPT_VERSION_ABI_LIBC MTr
+#endif
+#endif
+#elif MPT_LIBC_GLIBC
+#define MPT_VERSION_ABI_LIBC G
+#else
+#define MPT_VERSION_ABI_LIBC _
+#endif
+
+#define MPT_BUILD_ABI_NAMESPACE_IMPL(a, b) ABI_##a##_##b
+#define MPT_BUILD_ABI_NAMESPACE(a, b) MPT_BUILD_ABI_NAMESPACE_IMPL(a, b)
+
+#define MPT_ABI_NAMESPACE MPT_BUILD_ABI_NAMESPACE(MPT_VERSION_ABI_OS, MPT_VERSION_ABI_LIBC)
+
+#if !defined(MPT_PROJECT_NAMESPACE)
+MPT_WARNING("Please #define MPT_PROJECT_NAMESPACE or #define MPT_INLINE_NS in build configuration.")
+#define MPT_PROJECT_NAMESPACE x
+#endif // !MPT_PROJECT_NAMESPACE
+
+#define MPT_BUILD_INLINE_NS_IMPL(a, b, c) a##_##b##_##c
+#define MPT_BUILD_INLINE_NS(a, b, c) MPT_BUILD_INLINE_NS_IMPL(a, b, c)
+
+#define MPT_INLINE_NS MPT_BUILD_INLINE_NS(MPT_VERSION_NAMESPACE, MPT_ABI_NAMESPACE, MPT_PROJECT_NAMESPACE)
+
+#endif // !MPT_INLINE_NS
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_NAMESPACE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/numbers.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/numbers.hpp
new file mode 100644
index 00000000..a69f10ee
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/numbers.hpp
@@ -0,0 +1,177 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_NUMBRES_HPP
+#define MPT_BASE_NUMBRES_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/namespace.hpp"
+
+#if MPT_CXX_AT_LEAST(20)
+#include <numbers>
+#else
+#include <type_traits>
+#include <cmath>
+#include <math.h>
+#endif
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace numbers {
+
+#if MPT_CXX_AT_LEAST(20)
+
+template <typename T>
+inline constexpr T e_v = std::numbers::e_v<T>;
+template <typename T>
+inline constexpr T log2e_v = std::numbers::log2e_v<T>;
+template <typename T>
+inline constexpr T log10e_v = std::numbers::log10e_v<T>;
+template <typename T>
+inline constexpr T pi_v = std::numbers::pi_v<T>;
+template <typename T>
+inline constexpr T inv_pi_v = std::numbers::inv_pi_v<T>;
+template <typename T>
+inline constexpr T inv_sqrtpi_v = std::numbers::inv_sqrtpi_v<T>;
+template <typename T>
+inline constexpr T ln2_v = std::numbers::ln2_v<T>;
+template <typename T>
+inline constexpr T ln10_v = std::numbers::ln10_v<T>;
+template <typename T>
+inline constexpr T sqrt2_v = std::numbers::sqrt2_v<T>;
+template <typename T>
+inline constexpr T sqrt3_v = std::numbers::sqrt3_v<T>;
+template <typename T>
+inline constexpr T inv_sqrt3_v = std::numbers::inv_sqrt3_v<T>;
+template <typename T>
+inline constexpr T egamma_v = std::numbers::egamma_v<T>;
+template <typename T>
+inline constexpr T phi_v = std::numbers::phi_v<T>;
+
+inline constexpr double e = e_v<double>;
+inline constexpr double log2e = log2e_v<double>;
+inline constexpr double log10e = log10e_v<double>;
+inline constexpr double pi = pi_v<double>;
+inline constexpr double inv_pi = inv_pi_v<double>;
+inline constexpr double inv_sqrtpi = inv_sqrtpi_v<double>;
+inline constexpr double ln2 = ln2_v<double>;
+inline constexpr double ln10 = ln10_v<double>;
+inline constexpr double sqrt2 = sqrt2_v<double>;
+inline constexpr double sqrt3 = sqrt3_v<double>;
+inline constexpr double inv_sqrt3 = inv_sqrt3_v<double>;
+inline constexpr double egamma = egamma_v<double>;
+inline constexpr double phi = phi_v<double>;
+
+#else
+
+#ifdef M_E
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T e_v = static_cast<T>(M_E);
+#else
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T e_v = static_cast<T>(2.71828182845904523536);
+#endif
+
+#ifdef M_LOG2E
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T log2e_v = static_cast<T>(M_LOG2E);
+#else
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T log2e_v = static_cast<T>(1.44269504088896340736);
+#endif
+
+#ifdef M_LOG10E
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T log10e_v = static_cast<T>(M_LOG10E);
+#else
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T log10e_v = static_cast<T>(0.434294481903251827651);
+#endif
+
+#ifdef M_PI
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T pi_v = static_cast<T>(M_PI);
+#else
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T pi_v = static_cast<T>(3.14159265358979323846);
+#endif
+
+#ifdef M_1_PI
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T inv_pi_v = static_cast<T>(M_1_PI);
+#else
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T inv_pi_v = static_cast<T>(0.318309886183790671538);
+#endif
+
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T inv_sqrtpi_v = static_cast<T>(0.564189583547756286948079451560772586);
+
+#ifdef M_LN2
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T ln2_v = static_cast<T>(M_LN2);
+#else
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T ln2_v = static_cast<T>(0.693147180559945309417);
+#endif
+
+#ifdef M_LN10
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T ln10_v = static_cast<T>(M_LN10);
+#else
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T ln10_v = static_cast<T>(2.30258509299404568402);
+#endif
+
+#ifdef M_SQRT2
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T sqrt2_v = static_cast<T>(M_SQRT2);
+#else
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T sqrt2_v = static_cast<T>(1.41421356237309504880);
+#endif
+
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T sqrt3_v = static_cast<T>(1.732050807568877293527446341505872367);
+
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T inv_sqrt3_v = static_cast<T>(0.577350269189625764509148780501957456);
+
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T egamma_v = static_cast<T>(0.577215664901532860606512090082402431);
+
+template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
+inline constexpr T phi_v = static_cast<T>(1.618033988749894848204586834365638118);
+
+inline constexpr double e = e_v<double>;
+inline constexpr double log2e = log2e_v<double>;
+inline constexpr double log10e = log10e_v<double>;
+inline constexpr double pi = pi_v<double>;
+inline constexpr double inv_pi = inv_pi_v<double>;
+inline constexpr double inv_sqrtpi = inv_sqrtpi_v<double>;
+inline constexpr double ln2 = ln2_v<double>;
+inline constexpr double ln10 = ln10_v<double>;
+inline constexpr double sqrt2 = sqrt2_v<double>;
+inline constexpr double sqrt3 = sqrt3_v<double>;
+inline constexpr double inv_sqrt3 = inv_sqrt3_v<double>;
+inline constexpr double egamma = egamma_v<double>;
+inline constexpr double phi = phi_v<double>;
+
+#endif
+
+} // namespace numbers
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_NUMBRES_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/numeric.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/numeric.hpp
new file mode 100644
index 00000000..014f06fa
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/numeric.hpp
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_NUMERIC_HPP
+#define MPT_BASE_NUMERIC_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include "mpt/base/bit.hpp"
+#include "mpt/base/saturate_cast.hpp"
+
+#include <algorithm>
+#include <limits>
+#include <type_traits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+template <typename Tmod, Tmod m>
+struct ModIfNotZeroImpl {
+ template <typename Tval>
+ constexpr Tval mod(Tval x) {
+ static_assert(std::numeric_limits<Tmod>::is_integer);
+ static_assert(!std::numeric_limits<Tmod>::is_signed);
+ static_assert(std::numeric_limits<Tval>::is_integer);
+ static_assert(!std::numeric_limits<Tval>::is_signed);
+ return static_cast<Tval>(x % m);
+ }
+};
+template <>
+struct ModIfNotZeroImpl<uint8, 0> {
+ template <typename Tval>
+ constexpr Tval mod(Tval x) {
+ return x;
+ }
+};
+template <>
+struct ModIfNotZeroImpl<uint16, 0> {
+ template <typename Tval>
+ constexpr Tval mod(Tval x) {
+ return x;
+ }
+};
+template <>
+struct ModIfNotZeroImpl<uint32, 0> {
+ template <typename Tval>
+ constexpr Tval mod(Tval x) {
+ return x;
+ }
+};
+template <>
+struct ModIfNotZeroImpl<uint64, 0> {
+ template <typename Tval>
+ constexpr Tval mod(Tval x) {
+ return x;
+ }
+};
+
+// Returns x % m if m != 0, x otherwise.
+// i.e. "return (m == 0) ? x : (x % m);", but without causing a warning with stupid older compilers
+template <typename Tmod, Tmod m, typename Tval>
+constexpr Tval modulo_if_not_zero(Tval x) {
+ return ModIfNotZeroImpl<Tmod, m>().mod(x);
+}
+
+// rounds x up to multiples of target
+template <typename T>
+constexpr T align_up(T x, T target) {
+ return ((x + (target - 1)) / target) * target;
+}
+
+// rounds x down to multiples of target
+template <typename T>
+constexpr T align_down(T x, T target) {
+ return (x / target) * target;
+}
+
+// Returns sign of a number (-1 for negative numbers, 1 for positive numbers, 0 for 0)
+template <class T>
+constexpr int signum(T value) {
+ return (value > T(0)) - (value < T(0));
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_ALGORITHM_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/pointer.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/pointer.hpp
new file mode 100644
index 00000000..b6fd3f28
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/pointer.hpp
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_POINTER_HPP
+#define MPT_BASE_POINTER_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+inline constexpr int arch_bits = sizeof(void *) * 8;
+inline constexpr std::size_t pointer_size = sizeof(void *);
+
+
+template <typename Tdst, typename Tsrc>
+struct pointer_cast_helper {
+ static constexpr Tdst cast(const Tsrc & src) noexcept {
+ return src;
+ }
+};
+
+template <typename Tdst, typename Tptr>
+struct pointer_cast_helper<Tdst, const Tptr *> {
+ static constexpr Tdst cast(const Tptr * const & src) noexcept {
+ return reinterpret_cast<const Tdst>(src);
+ }
+};
+template <typename Tdst, typename Tptr>
+struct pointer_cast_helper<Tdst, Tptr *> {
+ static constexpr Tdst cast(const Tptr * const & src) noexcept {
+ return reinterpret_cast<const Tdst>(src);
+ }
+};
+
+
+template <typename Tdst, typename Tsrc>
+constexpr Tdst pointer_cast(const Tsrc & src) noexcept {
+ return pointer_cast_helper<Tdst, Tsrc>::cast(src);
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_POINTER_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/preprocessor.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/preprocessor.hpp
new file mode 100644
index 00000000..fe400102
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/preprocessor.hpp
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_PREPROCESSOR_HPP
+#define MPT_BASE_PREPROCESSOR_HPP
+
+
+
+#define MPT_PP_DEFER(m, ...) m(__VA_ARGS__)
+
+#define MPT_PP_STRINGIFY(x) #x
+
+#define MPT_PP_JOIN_HELPER(a, b) a##b
+#define MPT_PP_JOIN(a, b) MPT_PP_JOIN_HELPER(a, b)
+
+#define MPT_PP_UNIQUE_IDENTIFIER(prefix) MPT_PP_JOIN(prefix, __LINE__)
+
+
+
+#endif // MPT_BASE_PREPROCESSOR_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/saturate_cast.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/saturate_cast.hpp
new file mode 100644
index 00000000..6ddd604c
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/saturate_cast.hpp
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_SATURATE_CAST_HPP
+#define MPT_BASE_SATURATE_CAST_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+
+#include <limits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+// Saturate the value of src to the domain of Tdst
+template <typename Tdst, typename Tsrc>
+constexpr Tdst saturate_cast(Tsrc src) noexcept {
+ // This code tries not only to obviously avoid overflows but also to avoid signed/unsigned comparison warnings and type truncation warnings (which in fact would be safe here) by explicit casting.
+ static_assert(std::numeric_limits<Tdst>::is_integer);
+ static_assert(std::numeric_limits<Tsrc>::is_integer);
+ if constexpr (std::numeric_limits<Tdst>::is_signed && std::numeric_limits<Tsrc>::is_signed) {
+ if constexpr (sizeof(Tdst) >= sizeof(Tsrc)) {
+ return static_cast<Tdst>(src);
+ } else {
+ return static_cast<Tdst>(std::max(static_cast<Tsrc>(std::numeric_limits<Tdst>::min()), std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max()))));
+ }
+ } else if constexpr (!std::numeric_limits<Tdst>::is_signed && !std::numeric_limits<Tsrc>::is_signed) {
+ if constexpr (sizeof(Tdst) >= sizeof(Tsrc)) {
+ return static_cast<Tdst>(src);
+ } else {
+ return static_cast<Tdst>(std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max())));
+ }
+ } else if constexpr (std::numeric_limits<Tdst>::is_signed && !std::numeric_limits<Tsrc>::is_signed) {
+ if constexpr (sizeof(Tdst) > sizeof(Tsrc)) {
+ return static_cast<Tdst>(src);
+ } else if constexpr (sizeof(Tdst) == sizeof(Tsrc)) {
+ return static_cast<Tdst>(std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max())));
+ } else {
+ return static_cast<Tdst>(std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max())));
+ }
+ } else { // Tdst unsigned, Tsrc signed
+ if constexpr (sizeof(Tdst) >= sizeof(Tsrc)) {
+ return static_cast<Tdst>(std::max(static_cast<Tsrc>(0), src));
+ } else {
+ return static_cast<Tdst>(std::max(static_cast<Tsrc>(0), std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max()))));
+ }
+ }
+}
+
+template <typename Tdst>
+constexpr Tdst saturate_cast(double src) {
+ if (src >= static_cast<double>(std::numeric_limits<Tdst>::max())) {
+ return std::numeric_limits<Tdst>::max();
+ }
+ if (src <= static_cast<double>(std::numeric_limits<Tdst>::min())) {
+ return std::numeric_limits<Tdst>::min();
+ }
+ return static_cast<Tdst>(src);
+}
+
+template <typename Tdst>
+constexpr Tdst saturate_cast(float src) {
+ if (src >= static_cast<float>(std::numeric_limits<Tdst>::max())) {
+ return std::numeric_limits<Tdst>::max();
+ }
+ if (src <= static_cast<float>(std::numeric_limits<Tdst>::min())) {
+ return std::numeric_limits<Tdst>::min();
+ }
+ return static_cast<Tdst>(src);
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_SATURATE_CAST_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/saturate_round.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/saturate_round.hpp
new file mode 100644
index 00000000..7680608b
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/saturate_round.hpp
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_SATURATE_ROUND_HPP
+#define MPT_BASE_SATURATE_ROUND_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+
+#include "mpt/base/math.hpp"
+#include "mpt/base/saturate_cast.hpp"
+
+#include <type_traits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+// Rounds given double value to nearest integer value of type T.
+// Out-of-range values are saturated to the specified integer type's limits.
+
+template <typename T>
+inline T saturate_round(float val) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::saturate_cast<T>(mpt::round(val));
+}
+
+template <typename T>
+inline T saturate_round(double val) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::saturate_cast<T>(mpt::round(val));
+}
+
+template <typename T>
+inline T saturate_round(long double val) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::saturate_cast<T>(mpt::round(val));
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_SATURATE_ROUND_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/secure.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/secure.hpp
new file mode 100644
index 00000000..80b3519d
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/secure.hpp
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_SECURE_HPP
+#define MPT_BASE_SECURE_HPP
+
+
+
+#include "mpt/base/integer.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <atomic>
+#include <utility>
+#include <vector>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace secure {
+
+
+
+inline MPT_NOINLINE void memzero(std::byte * const dst, std::size_t const len) noexcept {
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ volatile std::byte * volatile p = static_cast<volatile std::byte *>(dst);
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ for (volatile std::size_t i = 0; i < len; ++i) {
+ p[i] = std::byte{0};
+ }
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+}
+
+inline MPT_NOINLINE void memzero(void * const dst, std::size_t const len) noexcept {
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ volatile std::byte * volatile p = static_cast<volatile std::byte *>(dst);
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ for (volatile std::size_t i = 0; i < len; ++i) {
+ p[i] = std::byte{0};
+ }
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+}
+
+inline MPT_NOINLINE void memzero(char * const dst, std::size_t const len) noexcept {
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ volatile std::byte * volatile p = reinterpret_cast<volatile std::byte *>(dst);
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ for (volatile std::size_t i = 0; i < len; ++i) {
+ p[i] = std::byte{0};
+ }
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+}
+
+inline MPT_NOINLINE void memzero(uint8 * const dst, std::size_t const len) noexcept {
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ volatile std::byte * volatile p = reinterpret_cast<volatile std::byte *>(dst);
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ for (volatile std::size_t i = 0; i < len; ++i) {
+ p[i] = std::byte{0};
+ }
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+}
+
+
+
+template <typename T>
+inline MPT_NOINLINE void clear(T & val) {
+ std::atomic_signal_fence(std::memory_order_seq_cst);
+ volatile T * volatile v = &val;
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ *v = T{};
+ std::atomic_signal_fence(std::memory_order_seq_cst);
+}
+
+
+
+class byte {
+private:
+ std::byte value;
+
+public:
+ byte() noexcept
+ : value(std::byte{0}) {
+ return;
+ }
+ explicit byte(std::byte value) noexcept
+ : value(value) {
+ return;
+ }
+ byte(const byte & other) noexcept
+ : value(other.value) {
+ return;
+ }
+ byte(byte && other) noexcept
+ : value(std::move(other.value)) {
+ mpt::secure::clear(other.value);
+ }
+ byte & operator=(const byte & other) noexcept {
+ if (&other == this) {
+ return *this;
+ }
+ value = other.value;
+ return *this;
+ }
+ byte & operator==(byte && other) noexcept {
+ if (&other == this) {
+ return *this;
+ }
+ value = std::move(other.value);
+ mpt::secure::clear(other.value);
+ return *this;
+ }
+ explicit operator std::byte() const noexcept {
+ return value;
+ }
+ ~byte() {
+ mpt::secure::clear(value);
+ }
+};
+
+
+
+class buffer {
+private:
+ std::vector<std::byte> m_data;
+
+public:
+ buffer()
+ : m_data(0) {
+ return;
+ }
+ explicit buffer(const std::vector<std::byte> & data)
+ : m_data(data) {
+ return;
+ }
+ explicit buffer(const std::byte * beg, const std::byte * end)
+ : m_data(beg, end) {
+ return;
+ }
+ buffer(const buffer & other)
+ : m_data(other.m_data) {
+ return;
+ }
+ buffer(buffer && other) noexcept
+ : m_data(std::move(other.m_data)) {
+ mpt::secure::memzero(other.m_data.data(), other.m_data.size());
+ }
+ buffer & operator=(const buffer & other) {
+ if (&other == this) {
+ return *this;
+ }
+ m_data = other.m_data;
+ return *this;
+ }
+ buffer & operator=(buffer && other) noexcept {
+ if (&other == this) {
+ return *this;
+ }
+ m_data = std::move(other.m_data);
+ mpt::secure::memzero(other.m_data.data(), other.m_data.size());
+ return *this;
+ }
+ ~buffer() {
+ mpt::secure::memzero(m_data.data(), m_data.size());
+ m_data.resize(0);
+ m_data.shrink_to_fit();
+ }
+ explicit operator std::vector<std::byte>() const {
+ return m_data;
+ }
+ const std::byte * data() const {
+ return m_data.data();
+ }
+ std::byte * data() {
+ return m_data.data();
+ }
+ std::size_t size() const {
+ return m_data.size();
+ }
+};
+
+
+
+} // namespace secure
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_SECURE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/semantic_version.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/semantic_version.hpp
new file mode 100644
index 00000000..826ac059
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/semantic_version.hpp
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_SEMANTIC_VERSION_HPP
+#define MPT_BASE_SEMANTIC_VERSION_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/version.hpp"
+
+
+
+#include <tuple>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+struct semantic_version {
+ unsigned long long major = 0;
+ unsigned long long minor = 0;
+ unsigned long long patch = 0;
+ constexpr std::tuple<unsigned long long, unsigned long long, unsigned long long> as_tuple() const noexcept {
+ return std::make_tuple(major, minor, patch);
+ }
+};
+
+constexpr bool operator==(const semantic_version a, const semantic_version b) noexcept {
+ return a.as_tuple() == b.as_tuple();
+}
+constexpr bool operator!=(const semantic_version a, const semantic_version b) noexcept {
+ return a.as_tuple() != b.as_tuple();
+}
+constexpr bool operator<(const semantic_version a, const semantic_version b) noexcept {
+ return a.as_tuple() < b.as_tuple();
+}
+constexpr bool operator>(const semantic_version a, const semantic_version b) noexcept {
+ return a.as_tuple() > b.as_tuple();
+}
+constexpr bool operator<=(const semantic_version a, const semantic_version b) noexcept {
+ return a.as_tuple() <= b.as_tuple();
+}
+constexpr bool operator>=(const semantic_version a, const semantic_version b) noexcept {
+ return a.as_tuple() >= b.as_tuple();
+}
+
+struct version_info {
+ semantic_version semver{};
+ unsigned long long build = 0;
+ constexpr std::tuple<std::tuple<unsigned long long, unsigned long long, unsigned long long>, unsigned long long> as_tuple() const noexcept {
+ return std::make_tuple(semver.as_tuple(), build);
+ }
+ template <typename Tostream>
+ friend Tostream & operator<<(Tostream & os, const version_info vi) {
+ if (vi.build > 0) {
+ os << vi.semver.major << "." << vi.semver.minor << "." << vi.semver.patch << "+build." << vi.build;
+ } else {
+ os << vi.semver.major << "." << vi.semver.minor << "." << vi.semver.patch;
+ }
+ return os;
+ }
+};
+
+constexpr bool operator==(const version_info a, const version_info b) noexcept {
+ return a.as_tuple() == b.as_tuple();
+}
+constexpr bool operator!=(const version_info a, const version_info b) noexcept {
+ return a.as_tuple() != b.as_tuple();
+}
+constexpr bool operator<(const version_info a, const version_info b) noexcept {
+ return a.as_tuple() < b.as_tuple();
+}
+constexpr bool operator>(const version_info a, const version_info b) noexcept {
+ return a.as_tuple() > b.as_tuple();
+}
+constexpr bool operator<=(const version_info a, const version_info b) noexcept {
+ return a.as_tuple() <= b.as_tuple();
+}
+constexpr bool operator>=(const version_info a, const version_info b) noexcept {
+ return a.as_tuple() >= b.as_tuple();
+}
+
+constexpr inline version_info Version = {
+ {MPT_VERSION_MAJOR, MPT_VERSION_MINOR, MPT_VERSION_PATCH},
+ MPT_VERSION_BUILD
+};
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_SEMANTIC_VERSION_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/source_location.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/source_location.hpp
new file mode 100644
index 00000000..90be04bc
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/source_location.hpp
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_SOURCE_LOCATION_HPP
+#define MPT_BASE_SOURCE_LOCATION_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+
+#if MPT_CXX_AT_LEAST(20) && !MPT_MSVC_BEFORE(2022, 0) && !MPT_COMPILER_CLANG
+#include <source_location>
+#endif // C++20
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+#if MPT_CXX_AT_LEAST(20) && !MPT_MSVC_BEFORE(2022, 0) && !MPT_COMPILER_CLANG
+
+using std::source_location;
+
+#define MPT_SOURCE_LOCATION_CURRENT() std::source_location::current()
+
+#else // !C++20
+
+#if MPT_COMPILER_MSVC && MPT_MSVC_AT_LEAST(2019, 6)
+
+#define MPT_SOURCE_LOCATION_FILE __builtin_FILE()
+#define MPT_SOURCE_LOCATION_FUNCTION __builtin_FUNCTION()
+#define MPT_SOURCE_LOCATION_LINE __builtin_LINE()
+#define MPT_SOURCE_LOCATION_COLUMN __builtin_COLUMN()
+
+#elif MPT_COMPILER_GCC
+
+#define MPT_SOURCE_LOCATION_FILE __builtin_FILE()
+#define MPT_SOURCE_LOCATION_FUNCTION __builtin_FUNCTION()
+#define MPT_SOURCE_LOCATION_LINE __builtin_LINE()
+#define MPT_SOURCE_LOCATION_COLUMN 0
+
+#elif MPT_COMPILER_CLANG && MPT_CLANG_AT_LEAST(9, 0, 0)
+
+#define MPT_SOURCE_LOCATION_FILE __builtin_FILE()
+#define MPT_SOURCE_LOCATION_FUNCTION __builtin_FUNCTION()
+#define MPT_SOURCE_LOCATION_LINE __builtin_LINE()
+#define MPT_SOURCE_LOCATION_COLUMN __builtin_COLUMN()
+
+#else
+
+#define MPT_SOURCE_LOCATION_FILE __FILE__
+#define MPT_SOURCE_LOCATION_FUNCTION ""
+#define MPT_SOURCE_LOCATION_LINE __LINE__
+#define MPT_SOURCE_LOCATION_COLUMN 0
+
+#endif
+
+// compatible with C++20 std::source_location
+struct source_location {
+private:
+ const char * m_file_name;
+ const char * m_function_name;
+ uint32 m_line;
+ uint32 m_column;
+
+public:
+ constexpr source_location() noexcept
+ : m_file_name("")
+ , m_function_name("")
+ , m_line(0)
+ , m_column(0) {
+ }
+ constexpr source_location(const char * file, const char * function, uint32 line, uint32 column) noexcept
+ : m_file_name(file)
+ , m_function_name(function)
+ , m_line(line)
+ , m_column(column) {
+ }
+ source_location(const source_location &) = default;
+ source_location(source_location &&) = default;
+ static constexpr source_location current(const char * file = MPT_SOURCE_LOCATION_FILE, const char * function = MPT_SOURCE_LOCATION_FUNCTION, uint32 line = MPT_SOURCE_LOCATION_LINE, uint32 column = MPT_SOURCE_LOCATION_COLUMN) noexcept {
+ return source_location(file, function, line, column);
+ }
+ constexpr uint32 line() const noexcept {
+ return m_line;
+ }
+ constexpr uint32 column() const noexcept {
+ return m_column;
+ }
+ constexpr const char * file_name() const noexcept {
+ return m_file_name;
+ }
+ constexpr const char * function_name() const noexcept {
+ return m_function_name;
+ }
+};
+
+
+#if (MPT_COMPILER_MSVC && MPT_MSVC_AT_LEAST(2019, 6)) || MPT_COMPILER_GCC || (MPT_COMPILER_CLANG && MPT_CLANG_AT_LEAST(9, 0, 0))
+#define MPT_SOURCE_LOCATION_CURRENT() mpt::source_location::current()
+#else
+#define MPT_SOURCE_LOCATION_CURRENT() mpt::source_location::current(__FILE__, __func__, __LINE__, 0)
+#endif
+
+#endif // C++20
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_SOURCE_LOCATION_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/span.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/span.hpp
new file mode 100644
index 00000000..4a4d9c66
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/span.hpp
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_SPAN_HPP
+#define MPT_BASE_SPAN_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <array>
+#if MPT_CXX_AT_LEAST(20)
+#include <span>
+#else // !C++20
+#include <iterator>
+#include <limits>
+#include <type_traits>
+#endif // C++20
+
+#if MPT_CXX_BEFORE(20)
+#include <cstddef>
+#endif // !C++20
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+#if MPT_CXX_AT_LEAST(20)
+
+using std::dynamic_extent;
+using std::span;
+
+#else // !C++20
+
+// Simplified version of gsl::span.
+// Non-owning read-only or read-write view into a contiguous block of T
+// objects, i.e. equivalent to a (beg,end) or (data,size) tuple.
+// Can eventually be replaced without further modifications with a full C++20
+// std::span.
+
+inline constexpr std::size_t dynamic_extent = std::numeric_limits<std::size_t>::max();
+
+template <typename T>
+class span {
+
+public:
+ using element_type = T;
+ using value_type = typename std::remove_cv<T>::type;
+ using index_type = std::size_t;
+ using pointer = T *;
+ using const_pointer = const T *;
+ using reference = T &;
+ using const_reference = const T &;
+
+ using iterator = pointer;
+
+ using difference_type = typename std::iterator_traits<iterator>::difference_type;
+
+private:
+ T * m_data;
+ std::size_t m_size;
+
+public:
+ span() noexcept
+ : m_data(nullptr)
+ , m_size(0) {
+ }
+
+ span(pointer beg, pointer end)
+ : m_data(beg)
+ , m_size(end - beg) {
+ }
+
+ span(pointer data, index_type size)
+ : m_data(data)
+ , m_size(size) {
+ }
+
+ template <std::size_t N>
+ span(element_type (&arr)[N])
+ : m_data(arr)
+ , m_size(N) {
+ }
+
+ template <std::size_t N>
+ span(std::array<value_type, N> & arr)
+ : m_data(arr.data())
+ , m_size(arr.size()) {
+ }
+
+ template <std::size_t N>
+ span(const std::array<value_type, N> & arr)
+ : m_data(arr.data())
+ , m_size(arr.size()) {
+ }
+
+ span(const span & other) noexcept = default;
+
+ template <typename U>
+ span(const span<U> & other)
+ : m_data(other.data())
+ , m_size(other.size()) {
+ }
+
+ span & operator=(const span & other) noexcept = default;
+
+ iterator begin() const {
+ return iterator(m_data);
+ }
+
+ iterator end() const {
+ return iterator(m_data + m_size);
+ }
+
+ reference operator[](index_type index) {
+ return m_data[index];
+ }
+
+ const_reference operator[](index_type index) const {
+ return m_data[index];
+ }
+
+ bool operator==(const span & other) const noexcept {
+ return size() == other.size() && (m_data == other.m_data || std::equal(begin(), end(), other.begin()));
+ }
+
+ bool operator!=(const span & other) const noexcept {
+ return !(*this == other);
+ }
+
+ pointer data() const noexcept {
+ return m_data;
+ }
+
+ bool empty() const noexcept {
+ return size() == 0;
+ }
+
+ index_type size() const noexcept {
+ return m_size;
+ }
+
+ index_type length() const noexcept {
+ return size();
+ }
+
+ span subspan(std::size_t offset, std::size_t count = mpt::dynamic_extent) const {
+ return span(data() + offset, (count == mpt::dynamic_extent) ? (size() - offset) : count);
+ }
+
+ span first(std::size_t count) const {
+ return span(data(), count);
+ }
+
+ span last(std::size_t count) const {
+ return span(data() + (size() - count), count);
+ }
+
+}; // class span
+
+#endif // C++20
+
+template <typename T>
+inline span<T> as_span(T * beg, T * end) {
+ return span<T>(beg, end);
+}
+
+template <typename T>
+inline span<T> as_span(T * data, std::size_t size) {
+ return span<T>(data, size);
+}
+
+template <typename T, std::size_t N>
+inline span<T> as_span(T (&arr)[N]) {
+ return span<T>(std::begin(arr), std::end(arr));
+}
+
+template <typename T, std::size_t N>
+inline span<T> as_span(std::array<T, N> & cont) {
+ return span<T>(cont);
+}
+
+template <typename T, std::size_t N>
+inline span<const T> as_span(const std::array<T, N> & cont) {
+ return span<const T>(cont);
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_SPAN_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_arithmetic_shift.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_arithmetic_shift.hpp
new file mode 100644
index 00000000..d0a4330b
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_arithmetic_shift.hpp
@@ -0,0 +1,328 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_TESTS_ARITHMETIC_SHIFT_HPP
+#define MPT_BASE_TESTS_ARITHMETIC_SHIFT_HPP
+
+
+
+#include "mpt/base/arithmetic_shift.hpp"
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <limits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace base {
+namespace arithmetic_shift {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/base/arithmetic_shift")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32768, 1), mpt::rshift_signed_standard<int16>(-32768, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32767, 1), mpt::rshift_signed_standard<int16>(-32767, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32766, 1), mpt::rshift_signed_standard<int16>(-32766, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-2, 1), mpt::rshift_signed_standard<int16>(-2, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-1, 1), mpt::rshift_signed_standard<int16>(-1, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(0, 1), mpt::rshift_signed_standard<int16>(0, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(1, 1), mpt::rshift_signed_standard<int16>(1, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(2, 1), mpt::rshift_signed_standard<int16>(2, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32766, 1), mpt::rshift_signed_standard<int16>(32766, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32767, 1), mpt::rshift_signed_standard<int16>(32767, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32768, 14), mpt::rshift_signed_standard<int16>(-32768, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32767, 14), mpt::rshift_signed_standard<int16>(-32767, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32766, 14), mpt::rshift_signed_standard<int16>(-32766, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-2, 14), mpt::rshift_signed_standard<int16>(-2, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-1, 14), mpt::rshift_signed_standard<int16>(-1, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(0, 14), mpt::rshift_signed_standard<int16>(0, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(1, 14), mpt::rshift_signed_standard<int16>(1, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(2, 14), mpt::rshift_signed_standard<int16>(2, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32766, 14), mpt::rshift_signed_standard<int16>(32766, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32767, 14), mpt::rshift_signed_standard<int16>(32767, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32768, 15), mpt::rshift_signed_standard<int16>(-32768, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32767, 15), mpt::rshift_signed_standard<int16>(-32767, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32766, 15), mpt::rshift_signed_standard<int16>(-32766, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-2, 15), mpt::rshift_signed_standard<int16>(-2, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-1, 15), mpt::rshift_signed_standard<int16>(-1, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(0, 15), mpt::rshift_signed_standard<int16>(0, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(1, 15), mpt::rshift_signed_standard<int16>(1, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(2, 15), mpt::rshift_signed_standard<int16>(2, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32766, 15), mpt::rshift_signed_standard<int16>(32766, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32767, 15), mpt::rshift_signed_standard<int16>(32767, 15));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32768, 1), mpt::lshift_signed_standard<int16>(-32768, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32767, 1), mpt::lshift_signed_standard<int16>(-32767, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32766, 1), mpt::lshift_signed_standard<int16>(-32766, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-2, 1), mpt::lshift_signed_standard<int16>(-2, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-1, 1), mpt::lshift_signed_standard<int16>(-1, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(0, 1), mpt::lshift_signed_standard<int16>(0, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(1, 1), mpt::lshift_signed_standard<int16>(1, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(2, 1), mpt::lshift_signed_standard<int16>(2, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32766, 1), mpt::lshift_signed_standard<int16>(32766, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32767, 1), mpt::lshift_signed_standard<int16>(32767, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32768, 14), mpt::lshift_signed_standard<int16>(-32768, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32767, 14), mpt::lshift_signed_standard<int16>(-32767, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32766, 14), mpt::lshift_signed_standard<int16>(-32766, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-2, 14), mpt::lshift_signed_standard<int16>(-2, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-1, 14), mpt::lshift_signed_standard<int16>(-1, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(0, 14), mpt::lshift_signed_standard<int16>(0, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(1, 14), mpt::lshift_signed_standard<int16>(1, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(2, 14), mpt::lshift_signed_standard<int16>(2, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32766, 14), mpt::lshift_signed_standard<int16>(32766, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32767, 14), mpt::lshift_signed_standard<int16>(32767, 14));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32768, 15), mpt::lshift_signed_standard<int16>(-32768, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32767, 15), mpt::lshift_signed_standard<int16>(-32767, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32766, 15), mpt::lshift_signed_standard<int16>(-32766, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-2, 15), mpt::lshift_signed_standard<int16>(-2, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-1, 15), mpt::lshift_signed_standard<int16>(-1, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(0, 15), mpt::lshift_signed_standard<int16>(0, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(1, 15), mpt::lshift_signed_standard<int16>(1, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(2, 15), mpt::lshift_signed_standard<int16>(2, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32766, 15), mpt::lshift_signed_standard<int16>(32766, 15));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32767, 15), mpt::lshift_signed_standard<int16>(32767, 15));
+
+#if MPT_COMPILER_SHIFT_SIGNED
+
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32768, 1), (-32768) >> 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32767, 1), (-32767) >> 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32766, 1), (-32766) >> 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-2, 1), (-2) >> 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-1, 1), (-1) >> 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(0, 1), (0) >> 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(1, 1), (1) >> 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(2, 1), (2) >> 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32766, 1), (32766) >> 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32767, 1), (32767) >> 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32768, 14), (-32768) >> 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32767, 14), (-32767) >> 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32766, 14), (-32766) >> 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-2, 14), (-2) >> 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-1, 14), (-1) >> 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(0, 14), (0) >> 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(1, 14), (1) >> 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(2, 14), (2) >> 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32766, 14), (32766) >> 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32767, 14), (32767) >> 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32768, 15), (-32768) >> 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32767, 15), (-32767) >> 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32766, 15), (-32766) >> 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-2, 15), (-2) >> 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-1, 15), (-1) >> 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(0, 15), (0) >> 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(1, 15), (1) >> 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(2, 15), (2) >> 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32766, 15), (32766) >> 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32767, 15), (32767) >> 15);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32768, 1), (-32768) << 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32767, 1), (-32767) << 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32766, 1), (-32766) << 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-2, 1), (-2) << 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-1, 1), (-1) << 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(0, 1), (0) << 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(1, 1), (1) << 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(2, 1), (2) << 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32766, 1), (32766) << 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32767, 1), (32767) << 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32768, 14), (-32768) << 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32767, 14), (-32767) << 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32766, 14), (-32766) << 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-2, 14), (-2) << 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-1, 14), (-1) << 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(0, 14), (0) << 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(1, 14), (1) << 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(2, 14), (2) << 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32766, 14), (32766) << 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32767, 14), (32767) << 14);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32768, 15), (-32768) << 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32767, 15), (-32767) << 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32766, 15), (-32766) << 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-2, 15), (-2) << 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-1, 15), (-1) << 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(0, 15), (0) << 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(1, 15), (1) << 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(2, 15), (2) << 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32766, 15), (32766) << 15);
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32767, 15), (32767) << 15);
+
+#endif
+
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0 - 0x80000000, 1), mpt::rshift_signed_standard<int32>(0 - 0x80000000, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7fffffff, 1), mpt::rshift_signed_standard<int32>(-0x7fffffff, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7ffffffe, 1), mpt::rshift_signed_standard<int32>(-0x7ffffffe, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-1, 1), mpt::rshift_signed_standard<int32>(-1, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0, 1), mpt::rshift_signed_standard<int32>(0, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(1, 1), mpt::rshift_signed_standard<int32>(1, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7ffffffe, 1), mpt::rshift_signed_standard<int32>(0x7ffffffe, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7fffffff, 1), mpt::rshift_signed_standard<int32>(0x7fffffff, 1));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0 - 0x80000000, 31), mpt::rshift_signed_standard<int32>(0 - 0x80000000, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7fffffff, 31), mpt::rshift_signed_standard<int32>(-0x7fffffff, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7ffffffe, 31), mpt::rshift_signed_standard<int32>(-0x7ffffffe, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-1, 31), mpt::rshift_signed_standard<int32>(-1, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0, 31), mpt::rshift_signed_standard<int32>(0, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(1, 31), mpt::rshift_signed_standard<int32>(1, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7ffffffe, 31), mpt::rshift_signed_standard<int32>(0x7ffffffe, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7fffffff, 31), mpt::rshift_signed_standard<int32>(0x7fffffff, 31));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0 - 0x80000000, 1), mpt::lshift_signed_standard<int32>(0 - 0x80000000, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7fffffff, 1), mpt::lshift_signed_standard<int32>(-0x7fffffff, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7ffffffe, 1), mpt::lshift_signed_standard<int32>(-0x7ffffffe, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-1, 1), mpt::lshift_signed_standard<int32>(-1, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0, 1), mpt::lshift_signed_standard<int32>(0, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(1, 1), mpt::lshift_signed_standard<int32>(1, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7ffffffe, 1), mpt::lshift_signed_standard<int32>(0x7ffffffe, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7fffffff, 1), mpt::lshift_signed_standard<int32>(0x7fffffff, 1));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0 - 0x80000000, 31), mpt::lshift_signed_standard<int32>(0 - 0x80000000, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7fffffff, 31), mpt::lshift_signed_standard<int32>(-0x7fffffff, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7ffffffe, 31), mpt::lshift_signed_standard<int32>(-0x7ffffffe, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-1, 31), mpt::lshift_signed_standard<int32>(-1, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0, 31), mpt::lshift_signed_standard<int32>(0, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(1, 31), mpt::lshift_signed_standard<int32>(1, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7ffffffe, 31), mpt::lshift_signed_standard<int32>(0x7ffffffe, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7fffffff, 31), mpt::lshift_signed_standard<int32>(0x7fffffff, 31));
+
+#if MPT_COMPILER_SHIFT_SIGNED
+
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0 - 0x80000000, 1), mpt::rshift_signed_undefined<int32>(0 - 0x80000000, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7fffffff, 1), mpt::rshift_signed_undefined<int32>(-0x7fffffff, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7ffffffe, 1), mpt::rshift_signed_undefined<int32>(-0x7ffffffe, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-1, 1), mpt::rshift_signed_undefined<int32>(-1, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0, 1), mpt::rshift_signed_undefined<int32>(0, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(1, 1), mpt::rshift_signed_undefined<int32>(1, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7ffffffe, 1), mpt::rshift_signed_undefined<int32>(0x7ffffffe, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7fffffff, 1), mpt::rshift_signed_undefined<int32>(0x7fffffff, 1));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0 - 0x80000000, 31), mpt::rshift_signed_undefined<int32>(0 - 0x80000000, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7fffffff, 31), mpt::rshift_signed_undefined<int32>(-0x7fffffff, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7ffffffe, 31), mpt::rshift_signed_undefined<int32>(-0x7ffffffe, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-1, 31), mpt::rshift_signed_undefined<int32>(-1, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0, 31), mpt::rshift_signed_undefined<int32>(0, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(1, 31), mpt::rshift_signed_undefined<int32>(1, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7ffffffe, 31), mpt::rshift_signed_undefined<int32>(0x7ffffffe, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7fffffff, 31), mpt::rshift_signed_undefined<int32>(0x7fffffff, 31));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0 - 0x80000000, 1), mpt::lshift_signed_undefined<int32>(0 - 0x80000000, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7fffffff, 1), mpt::lshift_signed_undefined<int32>(-0x7fffffff, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7ffffffe, 1), mpt::lshift_signed_undefined<int32>(-0x7ffffffe, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-1, 1), mpt::lshift_signed_undefined<int32>(-1, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0, 1), mpt::lshift_signed_undefined<int32>(0, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(1, 1), mpt::lshift_signed_undefined<int32>(1, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7ffffffe, 1), mpt::lshift_signed_undefined<int32>(0x7ffffffe, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7fffffff, 1), mpt::lshift_signed_undefined<int32>(0x7fffffff, 1));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0 - 0x80000000, 31), mpt::lshift_signed_undefined<int32>(0 - 0x80000000, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7fffffff, 31), mpt::lshift_signed_undefined<int32>(-0x7fffffff, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7ffffffe, 31), mpt::lshift_signed_undefined<int32>(-0x7ffffffe, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-1, 31), mpt::lshift_signed_undefined<int32>(-1, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0, 31), mpt::lshift_signed_undefined<int32>(0, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(1, 31), mpt::lshift_signed_undefined<int32>(1, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7ffffffe, 31), mpt::lshift_signed_undefined<int32>(0x7ffffffe, 31));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7fffffff, 31), mpt::lshift_signed_undefined<int32>(0x7fffffff, 31));
+
+#endif
+
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ull - 0x8000000000000000ull, 1), mpt::rshift_signed_standard<int64>(0ull - 0x8000000000000000ull, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7fffffffffffffffll, 1), mpt::rshift_signed_standard<int64>(-0x7fffffffffffffffll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7ffffffffffffffell, 1), mpt::rshift_signed_standard<int64>(-0x7ffffffffffffffell, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-1ll, 1), mpt::rshift_signed_standard<int64>(-1ll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ll, 1), mpt::rshift_signed_standard<int64>(0ll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(1ll, 1), mpt::rshift_signed_standard<int64>(1ll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7ffffffffffffffell, 1), mpt::rshift_signed_standard<int64>(0x7ffffffffffffffell, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7fffffffffffffffll, 1), mpt::rshift_signed_standard<int64>(0x7fffffffffffffffll, 1));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ull - 0x8000000000000000ull, 63), mpt::rshift_signed_standard<int64>(0ull - 0x8000000000000000ull, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7fffffffffffffffll, 63), mpt::rshift_signed_standard<int64>(-0x7fffffffffffffffll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7ffffffffffffffell, 63), mpt::rshift_signed_standard<int64>(-0x7ffffffffffffffell, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-1ll, 63), mpt::rshift_signed_standard<int64>(-1ll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ll, 63), mpt::rshift_signed_standard<int64>(0ll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(1ll, 63), mpt::rshift_signed_standard<int64>(1ll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7ffffffffffffffell, 63), mpt::rshift_signed_standard<int64>(0x7ffffffffffffffell, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7fffffffffffffffll, 63), mpt::rshift_signed_standard<int64>(0x7fffffffffffffffll, 63));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ull - 0x8000000000000000ull, 1), mpt::lshift_signed_standard<int64>(0ull - 0x8000000000000000ull, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7fffffffffffffffll, 1), mpt::lshift_signed_standard<int64>(-0x7fffffffffffffffll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7ffffffffffffffell, 1), mpt::lshift_signed_standard<int64>(-0x7ffffffffffffffell, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-1ll, 1), mpt::lshift_signed_standard<int64>(-1ll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ll, 1), mpt::lshift_signed_standard<int64>(0ll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(1ll, 1), mpt::lshift_signed_standard<int64>(1ll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7ffffffffffffffell, 1), mpt::lshift_signed_standard<int64>(0x7ffffffffffffffell, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7fffffffffffffffll, 1), mpt::lshift_signed_standard<int64>(0x7fffffffffffffffll, 1));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ull - 0x8000000000000000ull, 63), mpt::lshift_signed_standard<int64>(0ull - 0x8000000000000000ull, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7fffffffffffffffll, 63), mpt::lshift_signed_standard<int64>(-0x7fffffffffffffffll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7ffffffffffffffell, 63), mpt::lshift_signed_standard<int64>(-0x7ffffffffffffffell, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-1ll, 63), mpt::lshift_signed_standard<int64>(-1ll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ll, 63), mpt::lshift_signed_standard<int64>(0ll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(1ll, 63), mpt::lshift_signed_standard<int64>(1ll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7ffffffffffffffell, 63), mpt::lshift_signed_standard<int64>(0x7ffffffffffffffell, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7fffffffffffffffll, 63), mpt::lshift_signed_standard<int64>(0x7fffffffffffffffll, 63));
+
+#if MPT_COMPILER_SHIFT_SIGNED
+
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ull - 0x8000000000000000ull, 1), mpt::rshift_signed_undefined<int64>(0ull - 0x8000000000000000ull, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7fffffffffffffffll, 1), mpt::rshift_signed_undefined<int64>(-0x7fffffffffffffffll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7ffffffffffffffell, 1), mpt::rshift_signed_undefined<int64>(-0x7ffffffffffffffell, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-1ll, 1), mpt::rshift_signed_undefined<int64>(-1ll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ll, 1), mpt::rshift_signed_undefined<int64>(0ll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(1ll, 1), mpt::rshift_signed_undefined<int64>(1ll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7ffffffffffffffell, 1), mpt::rshift_signed_undefined<int64>(0x7ffffffffffffffell, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7fffffffffffffffll, 1), mpt::rshift_signed_undefined<int64>(0x7fffffffffffffffll, 1));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ull - 0x8000000000000000ull, 63), mpt::rshift_signed_undefined<int64>(0ull - 0x8000000000000000ull, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7fffffffffffffffll, 63), mpt::rshift_signed_undefined<int64>(-0x7fffffffffffffffll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7ffffffffffffffell, 63), mpt::rshift_signed_undefined<int64>(-0x7ffffffffffffffell, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-1ll, 63), mpt::rshift_signed_undefined<int64>(-1ll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ll, 63), mpt::rshift_signed_undefined<int64>(0ll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(1ll, 63), mpt::rshift_signed_undefined<int64>(1ll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7ffffffffffffffell, 63), mpt::rshift_signed_undefined<int64>(0x7ffffffffffffffell, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7fffffffffffffffll, 63), mpt::rshift_signed_undefined<int64>(0x7fffffffffffffffll, 63));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ull - 0x8000000000000000ull, 1), mpt::lshift_signed_undefined<int64>(0ull - 0x8000000000000000ull, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7fffffffffffffffll, 1), mpt::lshift_signed_undefined<int64>(-0x7fffffffffffffffll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7ffffffffffffffell, 1), mpt::lshift_signed_undefined<int64>(-0x7ffffffffffffffell, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-1ll, 1), mpt::lshift_signed_undefined<int64>(-1ll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ll, 1), mpt::lshift_signed_undefined<int64>(0ll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(1ll, 1), mpt::lshift_signed_undefined<int64>(1ll, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7ffffffffffffffell, 1), mpt::lshift_signed_undefined<int64>(0x7ffffffffffffffell, 1));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7fffffffffffffffll, 1), mpt::lshift_signed_undefined<int64>(0x7fffffffffffffffll, 1));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ull - 0x8000000000000000ull, 63), mpt::lshift_signed_undefined<int64>(0ull - 0x8000000000000000ull, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7fffffffffffffffll, 63), mpt::lshift_signed_undefined<int64>(-0x7fffffffffffffffll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7ffffffffffffffell, 63), mpt::lshift_signed_undefined<int64>(-0x7ffffffffffffffell, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-1ll, 63), mpt::lshift_signed_undefined<int64>(-1ll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ll, 63), mpt::lshift_signed_undefined<int64>(0ll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(1ll, 63), mpt::lshift_signed_undefined<int64>(1ll, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7ffffffffffffffell, 63), mpt::lshift_signed_undefined<int64>(0x7ffffffffffffffell, 63));
+ MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7fffffffffffffffll, 63), mpt::lshift_signed_undefined<int64>(0x7fffffffffffffffll, 63));
+
+#endif
+}
+
+} // namespace arithmetic_shift
+} // namespace base
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_TESTS_ARITHMETIC_SHIFT_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_bit.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_bit.hpp
new file mode 100644
index 00000000..7aed082c
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_bit.hpp
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_TESTS_BASE_BIT_HPP
+#define MPT_BASE_TESTS_BASE_BIT_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace base {
+namespace bit {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/base/bit")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+#if MPT_CXX_BEFORE(20)
+ MPT_TEST_EXPECT_EQUAL(mpt::get_endian(), mpt::endian_probe());
+#endif
+ MPT_MAYBE_CONSTANT_IF (mpt::endian_is_little()) {
+ MPT_TEST_EXPECT_EQUAL(mpt::get_endian(), mpt::endian::little);
+ MPT_MAYBE_CONSTANT_IF ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) {
+ MPT_TEST_EXPECT_EQUAL(mpt::endian::native, mpt::endian::little);
+ }
+#if MPT_CXX_BEFORE(20)
+ MPT_TEST_EXPECT_EQUAL(mpt::endian_probe(), mpt::endian::little);
+#endif
+ }
+ MPT_MAYBE_CONSTANT_IF (mpt::endian_is_big()) {
+ MPT_TEST_EXPECT_EQUAL(mpt::get_endian(), mpt::endian::big);
+ MPT_MAYBE_CONSTANT_IF ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) {
+ MPT_TEST_EXPECT_EQUAL(mpt::endian::native, mpt::endian::big);
+ }
+#if MPT_CXX_BEFORE(20)
+ MPT_TEST_EXPECT_EQUAL(mpt::endian_probe(), mpt::endian::big);
+#endif
+ }
+
+ MPT_TEST_EXPECT_EQUAL(mpt::popcount(static_cast<uint32>(int32(-1))), 32);
+ MPT_TEST_EXPECT_EQUAL(mpt::popcount(0u), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::popcount(1u), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::popcount(2u), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::popcount(3u), 2);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(0u), false);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(1u), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(2u), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(3u), false);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(4u), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(5u), false);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(6u), false);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(7u), false);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(8u), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(9u), false);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0x7fffffffu)), false);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0x80000000u)), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0x80000001u)), false);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0xfffffffeu)), false);
+ MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0xffffffffu)), false);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(0u), 1u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(1u), 1u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(2u), 2u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(3u), 4u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(4u), 4u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(5u), 8u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(6u), 8u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(7u), 8u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(8u), 8u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(9u), 16u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0x7fffffffu)), 0x80000000u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0x80000000u)), 0x80000000u);
+ //MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0x80000001u)), 0u);
+ //MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0xfffffffeu)), 0u);
+ //MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0xffffffffu)), 0u);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(0u), 0u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(1u), 1u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(2u), 2u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(3u), 2u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(4u), 4u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(5u), 4u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(6u), 4u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(7u), 4u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(8u), 8u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(9u), 8u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0x7fffffffu)), 0x40000000u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0x80000000u)), 0x80000000u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0x80000001u)), 0x80000000u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0xfffffffeu)), 0x80000000u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0xffffffffu)), 0x80000000u);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(0u), 0u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(1u), 1u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(2u), 2u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(3u), 2u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(4u), 3u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(5u), 3u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(6u), 3u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(7u), 3u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(8u), 4u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(9u), 4u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0x7fffffffu)), 31u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0x80000000u)), 32u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0x80000001u)), 32u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0xfffffffeu)), 32u);
+ MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0xffffffffu)), 32u);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000001)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000011)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000111)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00001111)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00011111)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00111111)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b01111111)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11111111)), 8);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11111110)), 7);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11111100)), 6);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11111000)), 5);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11110000)), 4);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11100000)), 3);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11000000)), 2);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b10000000)), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000000)), 0);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000000)), 8);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000001)), 7);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000011)), 6);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000111)), 5);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00001111)), 4);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00011111)), 3);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00111111)), 2);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b01111111)), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11111111)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11111110)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11111100)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11111000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11110000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11100000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11000000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b10000000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000000)), 8);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000001)), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000011)), 2);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000111)), 3);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00001111)), 4);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00011111)), 5);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00111111)), 6);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b01111111)), 7);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11111111)), 8);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11111110)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11111100)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11111000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11110000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11100000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11000000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b10000000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000000)), 0);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000000)), 8);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000001)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000011)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000111)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00001111)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00011111)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00111111)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b01111111)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11111111)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11111110)), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11111100)), 2);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11111000)), 3);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11110000)), 4);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11100000)), 5);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11000000)), 6);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b10000000)), 7);
+ MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000000)), 8);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0xffffffffu), 32);
+ MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0xfffffffeu), 31);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x80000000u), 31);
+ MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x7fffffffu), 31);
+ MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x7ffffffeu), 30);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000007u), 3);
+ MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000006u), 2);
+ MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000005u), 2);
+ MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000004u), 2);
+ MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000003u), 2);
+ MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000002u), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000001u), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000000u), 0);
+}
+
+} // namespace bit
+} // namespace base
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_TESTS_BASE_BIT_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_math.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_math.hpp
new file mode 100644
index 00000000..ba22dc1b
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_math.hpp
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_TESTS_MATH_HPP
+#define MPT_BASE_TESTS_MATH_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/math.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace base {
+namespace math {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/base/math")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ MPT_TEST_EXPECT_EQUAL(mpt::round(1.99), 2.0);
+ MPT_TEST_EXPECT_EQUAL(mpt::round(1.5), 2.0);
+ MPT_TEST_EXPECT_EQUAL(mpt::round(1.1), 1.0);
+ MPT_TEST_EXPECT_EQUAL(mpt::round(-0.1), 0.0);
+ MPT_TEST_EXPECT_EQUAL(mpt::round(-0.5), -1.0);
+ MPT_TEST_EXPECT_EQUAL(mpt::round(-0.9), -1.0);
+ MPT_TEST_EXPECT_EQUAL(mpt::round(-1.4), -1.0);
+ MPT_TEST_EXPECT_EQUAL(mpt::round(-1.7), -2.0);
+}
+
+} // namespace math
+} // namespace base
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_TESTS_MATH_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_saturate_cast.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_saturate_cast.hpp
new file mode 100644
index 00000000..e2d69ad9
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_saturate_cast.hpp
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_TESTS_SATURATE_CAST_HPP
+#define MPT_BASE_TESTS_SATURATE_CAST_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/saturate_cast.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <limits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace base {
+namespace saturate_cast {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/base/saturate_cast")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ // trivials
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int>(-1), -1);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int>(0), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int>(1), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int>(std::numeric_limits<int>::min()), std::numeric_limits<int>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int>(std::numeric_limits<int>::max()), std::numeric_limits<int>::max());
+
+ // signed / unsigned
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(std::numeric_limits<uint16>::min()), std::numeric_limits<uint16>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(std::numeric_limits<uint16>::max()), std::numeric_limits<int16>::max());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(std::numeric_limits<uint32>::min()), (int32)std::numeric_limits<uint32>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(std::numeric_limits<uint32>::max()), std::numeric_limits<int32>::max());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int64>(std::numeric_limits<uint64>::min()), (int64)std::numeric_limits<uint64>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int64>(std::numeric_limits<uint64>::max()), std::numeric_limits<int64>::max());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(std::numeric_limits<int16>::min()), std::numeric_limits<uint16>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(std::numeric_limits<int16>::max()), std::numeric_limits<int16>::max());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(std::numeric_limits<int32>::min()), std::numeric_limits<uint32>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(std::numeric_limits<int32>::max()), (uint32)std::numeric_limits<int32>::max());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint64>(std::numeric_limits<int64>::min()), std::numeric_limits<uint64>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint64>(std::numeric_limits<int64>::max()), (uint64)std::numeric_limits<int64>::max());
+
+ // overflow
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(std::numeric_limits<int16>::min() - 1), std::numeric_limits<int16>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(std::numeric_limits<int16>::max() + 1), std::numeric_limits<int16>::max());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(std::numeric_limits<int32>::min() - int64(1)), std::numeric_limits<int32>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(std::numeric_limits<int32>::max() + int64(1)), std::numeric_limits<int32>::max());
+
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(std::numeric_limits<int16>::min() - 1), std::numeric_limits<uint16>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(std::numeric_limits<int16>::max() + 1), (uint16)std::numeric_limits<int16>::max() + 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(std::numeric_limits<int32>::min() - int64(1)), std::numeric_limits<uint32>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(std::numeric_limits<int32>::max() + int64(1)), (uint32)std::numeric_limits<int32>::max() + 1);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int8>(int16(32000)), 127);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int8>(int16(-32000)), -128);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int8>(uint16(32000)), 127);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int8>(uint16(64000)), 127);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint8>(int16(32000)), 255);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint8>(int16(-32000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint8>(uint16(32000)), 255);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint8>(uint16(64000)), 255);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(int16(32000)), 32000);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(int16(-32000)), -32000);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(uint16(32000)), 32000);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(uint16(64000)), 32767);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(int16(32000)), 32000);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(int16(-32000)), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(uint16(32000)), 32000);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(uint16(64000)), 64000);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(int16(32000)), 32000);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(int16(-32000)), -32000);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(uint16(32000)), 32000);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(uint16(64000)), 64000);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(int16(32000)), 32000u);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(int16(-32000)), 0u);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(uint16(32000)), 32000u);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(uint16(64000)), 64000u);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(std::numeric_limits<int64>::max() - 1), std::numeric_limits<uint32>::max());
+
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(std::numeric_limits<uint64>::max() - 1), std::numeric_limits<int32>::max());
+
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(static_cast<double>(std::numeric_limits<int64>::max())), std::numeric_limits<uint32>::max());
+}
+
+} // namespace saturate_cast
+} // namespace base
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_TESTS_SATURATE_CAST_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_saturate_round.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_saturate_round.hpp
new file mode 100644
index 00000000..27f753e7
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_saturate_round.hpp
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_TESTS_SATURATE_ROUND_HPP
+#define MPT_BASE_TESTS_SATURATE_ROUND_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/saturate_round.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <limits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace base {
+namespace saturate_round {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/base/saturate_round")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int32>(std::numeric_limits<int32>::max() + 0.1), std::numeric_limits<int32>::max());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int32>(std::numeric_limits<int32>::max() - 0.4), std::numeric_limits<int32>::max());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int32>(std::numeric_limits<int32>::min() + 0.1), std::numeric_limits<int32>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int32>(std::numeric_limits<int32>::min() - 0.1), std::numeric_limits<int32>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<uint32>(std::numeric_limits<uint32>::max() + 0.499), std::numeric_limits<uint32>::max());
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int8>(110.1), 110);
+ MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int8>(-110.1), -110);
+
+ // These should fail to compile
+ //mpt::saturate_round<std::string>(1.0);
+ //mpt::saturate_round<int64>(1.0);
+ //mpt::saturate_round<uint64>(1.0);
+
+ // This should trigger assert in Round.
+ //MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int8>(-129), 0);
+}
+
+} // namespace saturate_round
+} // namespace base
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_TESTS_SATURATE_ROUND_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_wrapping_divide.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_wrapping_divide.hpp
new file mode 100644
index 00000000..02b07185
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/tests/tests_base_wrapping_divide.hpp
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_TESTS_WRAPPING_DIVIDE_HPP
+#define MPT_BASE_TESTS_WRAPPING_DIVIDE_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/wrapping_divide.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <limits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace base {
+namespace wrapping_divide {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/base/wrapping_divide")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-25, 12), 11);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-24, 12), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-23, 12), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-8, 7), 6);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-7, 7), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-6, 7), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-5, 7), 2);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-4, 7), 3);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-3, 7), 4);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-2, 7), 5);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-1, 7), 6);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(0, 12), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(0, 7), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(1, 7), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(2, 7), 2);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(3, 7), 3);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(4, 7), 4);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(5, 7), 5);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(6, 7), 6);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(7, 7), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(8, 7), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(23, 12), 11);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(24, 12), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(25, 12), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(uint32(0x7fffffff), uint32(0x80000000)), uint32(0x7fffffff));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0x7ffffffe), int32(0x7fffffff)), int32(0x7ffffffe));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(1)), int32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(2)), int32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(1)), int32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(2)), int32(1));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(1)), int32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(2)), int32(0));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(0x7fffffff)), int32(0x7ffffffe));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(0x7fffffff)), int32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(0x7fffffff)), int32(1));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(0x7ffffffe)), int32(0x7ffffffc));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(0x7ffffffe)), int32(0x7ffffffd));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(0x7ffffffe)), int32(0));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(0x7ffffffd)), int32(0x7ffffffa));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(0x7ffffffd)), int32(0x7ffffffb));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(0x7ffffffd)), int32(0x7ffffffc));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0), int32(0x7fffffff)), int32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-1), int32(0x7fffffff)), int32(0x7ffffffe));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-2), int32(0x7fffffff)), int32(0x7ffffffd));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0), int32(0x7ffffffe)), int32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-1), int32(0x7ffffffe)), int32(0x7ffffffd));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-2), int32(0x7ffffffe)), int32(0x7ffffffc));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(1)), uint32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(2)), uint32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(1)), uint32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(2)), uint32(1));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(1)), uint32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(2)), uint32(0));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x40000001), uint32(0xffffffff)), uint32(0xbffffffe));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x40000000), uint32(0xffffffff)), uint32(0xbfffffff));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x3fffffff), uint32(0xffffffff)), uint32(0xc0000000));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x80000000)), uint32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x80000000)), uint32(1));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x80000000)), uint32(2));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x80000001)), uint32(1));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x80000001)), uint32(2));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x80000001)), uint32(3));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x80000000)), uint32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x80000000)), uint32(1));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x80000000)), uint32(2));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x7fffffff)), uint32(0x7ffffffe));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x7fffffff)), uint32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x7fffffff)), uint32(1));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x7ffffffe)), uint32(0x7ffffffc));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x7ffffffe)), uint32(0x7ffffffd));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x7ffffffe)), uint32(0));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x7ffffffd)), uint32(0x7ffffffa));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x7ffffffd)), uint32(0x7ffffffb));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x7ffffffd)), uint32(0x7ffffffc));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0), uint32(0x7fffffff)), uint32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-1), uint32(0x7fffffff)), uint32(0x7ffffffe));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-2), uint32(0x7fffffff)), uint32(0x7ffffffd));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0), uint32(0x7ffffffe)), uint32(0));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-1), uint32(0x7ffffffe)), uint32(0x7ffffffd));
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-2), uint32(0x7ffffffe)), uint32(0x7ffffffc));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-15, 7), -3);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-14, 7), -2);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-13, 7), -2);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-12, 7), -2);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-11, 7), -2);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-10, 7), -2);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-9, 7), -2);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-8, 7), -2);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-7, 7), -1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-6, 7), -1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-5, 7), -1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-4, 7), -1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-3, 7), -1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-2, 7), -1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-1, 7), -1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(0, 7), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(1, 7), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(2, 7), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(3, 7), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(4, 7), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(5, 7), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(6, 7), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(7, 7), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(8, 7), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(9, 7), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(10, 7), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(11, 7), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(12, 7), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(13, 7), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(14, 7), 2);
+ MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(15, 7), 2);
+}
+
+} // namespace wrapping_divide
+} // namespace base
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_TESTS_WRAPPING_DIVIDE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/utility.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/utility.hpp
new file mode 100644
index 00000000..7087a6a2
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/utility.hpp
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_UTILITY_HPP
+#define MPT_BASE_UTILITY_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/namespace.hpp"
+
+#if MPT_CXX_BEFORE(20)
+#include "mpt/base/saturate_cast.hpp"
+#endif
+
+#include <type_traits>
+#include <utility>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+#if MPT_CXX_AT_LEAST(20) && !MPT_CLANG_BEFORE(13, 0, 0)
+
+using std::in_range;
+
+#else
+
+// Returns true iff Tdst can represent the value val.
+// Use as if(mpt::in_range<uint8>(-1)).
+template <typename Tdst, typename Tsrc>
+constexpr bool in_range(Tsrc val) {
+ return (static_cast<Tsrc>(mpt::saturate_cast<Tdst>(val)) == val);
+}
+
+#endif
+
+
+#if MPT_CXX_AT_LEAST(23)
+
+using std::to_underlying;
+
+#else
+
+template <typename T>
+constexpr std::underlying_type_t<T> to_underlying(T value) noexcept {
+ return static_cast<typename std::underlying_type<T>::type>(value);
+}
+
+#endif
+
+
+
+template <typename T>
+struct value_initializer {
+ inline void operator()(T & x) {
+ x = T{};
+ }
+};
+
+template <typename T, std::size_t N>
+struct value_initializer<T[N]> {
+ inline void operator()(T (&a)[N]) {
+ for (auto & e : a)
+ {
+ value_initializer<T>{}(e);
+ }
+ }
+};
+
+template <typename T>
+inline void reset(T & x) {
+ value_initializer<T>{}(x);
+}
+
+
+
+#if MPT_CXX_AT_LEAST(20) && !MPT_CLANG_BEFORE(13, 0, 0)
+
+using std::cmp_equal;
+using std::cmp_greater;
+using std::cmp_greater_equal;
+using std::cmp_less;
+using std::cmp_less_equal;
+using std::cmp_not_equal;
+
+#else
+
+template <typename Ta, typename Tb>
+constexpr bool cmp_equal(Ta a, Tb b) noexcept {
+ using UTa = typename std::make_unsigned<Ta>::type;
+ using UTb = typename std::make_unsigned<Tb>::type;
+ if constexpr (std::is_signed<Ta>::value == std::is_signed<Tb>::value) {
+ return a == b;
+ } else if constexpr (std::is_signed<Ta>::value) {
+ return (a < 0) ? false : static_cast<UTa>(a) == b;
+ } else {
+ return (b < 0) ? false : a == static_cast<UTb>(b);
+ }
+}
+
+template <typename Ta, typename Tb>
+constexpr bool cmp_not_equal(Ta a, Tb b) noexcept {
+ using UTa = typename std::make_unsigned<Ta>::type;
+ using UTb = typename std::make_unsigned<Tb>::type;
+ if constexpr (std::is_signed<Ta>::value == std::is_signed<Tb>::value) {
+ return a != b;
+ } else if constexpr (std::is_signed<Ta>::value) {
+ return (a < 0) ? true : static_cast<UTa>(a) != b;
+ } else {
+ return (b < 0) ? true : a != static_cast<UTb>(b);
+ }
+}
+
+template <typename Ta, typename Tb>
+constexpr bool cmp_less(Ta a, Tb b) noexcept {
+ using UTa = typename std::make_unsigned<Ta>::type;
+ using UTb = typename std::make_unsigned<Tb>::type;
+ if constexpr (std::is_signed<Ta>::value == std::is_signed<Tb>::value) {
+ return a < b;
+ } else if constexpr (std::is_signed<Ta>::value) {
+ return (a < 0) ? true : static_cast<UTa>(a) < b;
+ } else {
+ return (b < 0) ? false : a < static_cast<UTb>(b);
+ }
+}
+
+template <typename Ta, typename Tb>
+constexpr bool cmp_greater(Ta a, Tb b) noexcept {
+ using UTa = typename std::make_unsigned<Ta>::type;
+ using UTb = typename std::make_unsigned<Tb>::type;
+ if constexpr (std::is_signed<Ta>::value == std::is_signed<Tb>::value) {
+ return a > b;
+ } else if constexpr (std::is_signed<Ta>::value) {
+ return (a < 0) ? false : static_cast<UTa>(a) > b;
+ } else {
+ return (b < 0) ? true : a > static_cast<UTb>(b);
+ }
+}
+
+template <typename Ta, typename Tb>
+constexpr bool cmp_less_equal(Ta a, Tb b) noexcept {
+ using UTa = typename std::make_unsigned<Ta>::type;
+ using UTb = typename std::make_unsigned<Tb>::type;
+ if constexpr (std::is_signed<Ta>::value == std::is_signed<Tb>::value) {
+ return a <= b;
+ } else if constexpr (std::is_signed<Ta>::value) {
+ return (a < 0) ? true : static_cast<UTa>(a) <= b;
+ } else {
+ return (b < 0) ? false : a <= static_cast<UTb>(b);
+ }
+}
+
+template <typename Ta, typename Tb>
+constexpr bool cmp_greater_equal(Ta a, Tb b) noexcept {
+ using UTa = typename std::make_unsigned<Ta>::type;
+ using UTb = typename std::make_unsigned<Tb>::type;
+ if constexpr (std::is_signed<Ta>::value == std::is_signed<Tb>::value) {
+ return a >= b;
+ } else if constexpr (std::is_signed<Ta>::value) {
+ return (a < 0) ? false : static_cast<UTa>(a) >= b;
+ } else {
+ return (b < 0) ? true : a >= static_cast<UTb>(b);
+ }
+}
+
+#endif
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_UTILITY_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/version.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/version.hpp
new file mode 100644
index 00000000..1ac14ed9
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/version.hpp
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_VERSION_HPP
+#define MPT_BASE_VERSION_HPP
+
+
+
+#define MPT_VERSION_MAJOR 0
+#define MPT_VERSION_MINOR 0
+#define MPT_VERSION_PATCH 0
+#define MPT_VERSION_BUILD 0
+
+
+
+#endif // MPT_BASE_VERSION_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/base/wrapping_divide.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/base/wrapping_divide.hpp
new file mode 100644
index 00000000..a0c4acdb
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/base/wrapping_divide.hpp
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_WRAPPING_DIVIDE_HPP
+#define MPT_BASE_WRAPPING_DIVIDE_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+// Modulo with more intuitive behaviour for some contexts:
+// Instead of being symmetrical around 0, the pattern for positive numbers is repeated in the negative range.
+// For example, wrapping_modulo(-1, m) == (m - 1).
+// Behaviour is undefined if m<=0.
+template <typename T, typename M>
+constexpr auto wrapping_modulo(T x, M m) -> decltype(x % m) {
+ return (x >= 0) ? (x % m) : (m - 1 - ((-1 - x) % m));
+}
+
+template <typename T, typename D>
+constexpr auto wrapping_divide(T x, D d) -> decltype(x / d) {
+ return (x >= 0) ? (x / d) : (((x + 1) / d) - 1);
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_WRAPPING_DIVIDE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/binary/base64.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/binary/base64.hpp
new file mode 100644
index 00000000..579f6710
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/binary/base64.hpp
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BINARY_BASE64_HPP
+#define MPT_BINARY_BASE64_HPP
+
+
+
+#include "mpt/base/integer.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/string/types.hpp"
+
+#include <array>
+#include <stdexcept>
+#include <vector>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+class base64_parse_error : public std::runtime_error {
+public:
+ base64_parse_error()
+ : std::runtime_error("invalid Base64 encoding") {
+ }
+};
+
+
+inline constexpr std::array<mpt::uchar, 64> base64 = {
+ {MPT_UCHAR('A'), MPT_UCHAR('B'), MPT_UCHAR('C'), MPT_UCHAR('D'), MPT_UCHAR('E'), MPT_UCHAR('F'), MPT_UCHAR('G'), MPT_UCHAR('H'), MPT_UCHAR('I'), MPT_UCHAR('J'), MPT_UCHAR('K'), MPT_UCHAR('L'), MPT_UCHAR('M'), MPT_UCHAR('N'), MPT_UCHAR('O'), MPT_UCHAR('P'),
+ MPT_UCHAR('Q'), MPT_UCHAR('R'), MPT_UCHAR('S'), MPT_UCHAR('T'), MPT_UCHAR('U'), MPT_UCHAR('V'), MPT_UCHAR('W'), MPT_UCHAR('X'), MPT_UCHAR('Y'), MPT_UCHAR('Z'), MPT_UCHAR('a'), MPT_UCHAR('b'), MPT_UCHAR('c'), MPT_UCHAR('d'), MPT_UCHAR('e'), MPT_UCHAR('f'),
+ MPT_UCHAR('g'), MPT_UCHAR('h'), MPT_UCHAR('i'), MPT_UCHAR('j'), MPT_UCHAR('k'), MPT_UCHAR('l'), MPT_UCHAR('m'), MPT_UCHAR('n'), MPT_UCHAR('o'), MPT_UCHAR('p'), MPT_UCHAR('q'), MPT_UCHAR('r'), MPT_UCHAR('s'), MPT_UCHAR('t'), MPT_UCHAR('u'), MPT_UCHAR('v'),
+ MPT_UCHAR('w'), MPT_UCHAR('x'), MPT_UCHAR('y'), MPT_UCHAR('z'), MPT_UCHAR('0'), MPT_UCHAR('1'), MPT_UCHAR('2'), MPT_UCHAR('3'), MPT_UCHAR('4'), MPT_UCHAR('5'), MPT_UCHAR('6'), MPT_UCHAR('7'), MPT_UCHAR('8'), MPT_UCHAR('9'), MPT_UCHAR('+'), MPT_UCHAR('/')}
+};
+
+
+template <typename Tbyte>
+inline mpt::ustring encode_base64(mpt::span<Tbyte> src_) {
+ mpt::const_byte_span src = mpt::byte_cast<mpt::const_byte_span>(src_);
+ mpt::ustring result;
+ result.reserve(4 * ((src.size() + 2) / 3));
+ uint32 bits = 0;
+ std::size_t bytes = 0;
+ for (std::byte byte : src) {
+ bits <<= 8;
+ bits |= mpt::byte_cast<uint8>(byte);
+ bytes++;
+ if (bytes == 3) {
+ result.push_back(base64[(bits >> 18) & 0x3f]);
+ result.push_back(base64[(bits >> 12) & 0x3f]);
+ result.push_back(base64[(bits >> 6) & 0x3f]);
+ result.push_back(base64[(bits >> 0) & 0x3f]);
+ bits = 0;
+ bytes = 0;
+ }
+ }
+ std::size_t padding = 0;
+ while (bytes != 0) {
+ bits <<= 8;
+ padding++;
+ bytes++;
+ if (bytes == 3) {
+ result.push_back(base64[(bits >> 18) & 0x3f]);
+ result.push_back(base64[(bits >> 12) & 0x3f]);
+ if (padding > 1) {
+ result.push_back(MPT_UCHAR('='));
+ } else {
+ result.push_back(base64[(bits >> 6) & 0x3f]);
+ }
+ if (padding > 0) {
+ result.push_back(MPT_UCHAR('='));
+ } else {
+ result.push_back(base64[(bits >> 0) & 0x3f]);
+ }
+ bits = 0;
+ bytes = 0;
+ }
+ }
+ return result;
+}
+
+inline uint8 decode_base64_bits(mpt::uchar c) {
+ for (uint8 i = 0; i < 64; ++i) {
+ if (base64[i] == c) {
+ return i;
+ }
+ }
+ throw base64_parse_error();
+}
+
+
+inline std::vector<std::byte> decode_base64(const mpt::ustring & src) {
+ std::vector<std::byte> result;
+ result.reserve(3 * (src.length() / 4));
+ uint32 bits = 0;
+ std::size_t chars = 0;
+ std::size_t padding = 0;
+ for (mpt::uchar c : src) {
+ bits <<= 6;
+ if (c == MPT_UCHAR('=')) {
+ padding++;
+ } else {
+ bits |= decode_base64_bits(c);
+ }
+ chars++;
+ if (chars == 4) {
+ result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 16) & 0xff)));
+ if (padding < 2) {
+ result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 8) & 0xff)));
+ }
+ if (padding < 1) {
+ result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 0) & 0xff)));
+ }
+ bits = 0;
+ chars = 0;
+ padding = 0;
+ }
+ }
+ if (chars != 0) {
+ throw base64_parse_error();
+ }
+ return result;
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BINARY_BASE64_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/binary/base64url.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/binary/base64url.hpp
new file mode 100644
index 00000000..04859f87
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/binary/base64url.hpp
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BINARY_BASE64URL_HPP
+#define MPT_BINARY_BASE64URL_HPP
+
+
+
+#include "mpt/base/integer.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/string/types.hpp"
+
+#include <array>
+#include <stdexcept>
+#include <vector>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+class base64url_parse_error : public std::runtime_error {
+public:
+ base64url_parse_error()
+ : std::runtime_error("invalid Base64URL encoding") {
+ }
+};
+
+
+inline constexpr std::array<mpt::uchar, 64> base64url = {
+ {MPT_UCHAR('A'), MPT_UCHAR('B'), MPT_UCHAR('C'), MPT_UCHAR('D'), MPT_UCHAR('E'), MPT_UCHAR('F'), MPT_UCHAR('G'), MPT_UCHAR('H'), MPT_UCHAR('I'), MPT_UCHAR('J'), MPT_UCHAR('K'), MPT_UCHAR('L'), MPT_UCHAR('M'), MPT_UCHAR('N'), MPT_UCHAR('O'), MPT_UCHAR('P'),
+ MPT_UCHAR('Q'), MPT_UCHAR('R'), MPT_UCHAR('S'), MPT_UCHAR('T'), MPT_UCHAR('U'), MPT_UCHAR('V'), MPT_UCHAR('W'), MPT_UCHAR('X'), MPT_UCHAR('Y'), MPT_UCHAR('Z'), MPT_UCHAR('a'), MPT_UCHAR('b'), MPT_UCHAR('c'), MPT_UCHAR('d'), MPT_UCHAR('e'), MPT_UCHAR('f'),
+ MPT_UCHAR('g'), MPT_UCHAR('h'), MPT_UCHAR('i'), MPT_UCHAR('j'), MPT_UCHAR('k'), MPT_UCHAR('l'), MPT_UCHAR('m'), MPT_UCHAR('n'), MPT_UCHAR('o'), MPT_UCHAR('p'), MPT_UCHAR('q'), MPT_UCHAR('r'), MPT_UCHAR('s'), MPT_UCHAR('t'), MPT_UCHAR('u'), MPT_UCHAR('v'),
+ MPT_UCHAR('w'), MPT_UCHAR('x'), MPT_UCHAR('y'), MPT_UCHAR('z'), MPT_UCHAR('0'), MPT_UCHAR('1'), MPT_UCHAR('2'), MPT_UCHAR('3'), MPT_UCHAR('4'), MPT_UCHAR('5'), MPT_UCHAR('6'), MPT_UCHAR('7'), MPT_UCHAR('8'), MPT_UCHAR('9'), MPT_UCHAR('-'), MPT_UCHAR('_')}
+};
+
+template <typename Tbyte>
+inline mpt::ustring encode_base64url(mpt::span<Tbyte> src_) {
+ mpt::const_byte_span src = mpt::byte_cast<mpt::const_byte_span>(src_);
+ mpt::ustring result;
+ result.reserve(4 * ((src.size() + 2) / 3));
+ uint32 bits = 0;
+ std::size_t bytes = 0;
+ for (std::byte byte : src) {
+ bits <<= 8;
+ bits |= mpt::byte_cast<uint8>(byte);
+ bytes++;
+ if (bytes == 3) {
+ result.push_back(base64url[(bits >> 18) & 0x3f]);
+ result.push_back(base64url[(bits >> 12) & 0x3f]);
+ result.push_back(base64url[(bits >> 6) & 0x3f]);
+ result.push_back(base64url[(bits >> 0) & 0x3f]);
+ bits = 0;
+ bytes = 0;
+ }
+ }
+ std::size_t padding = 0;
+ while (bytes != 0) {
+ bits <<= 8;
+ padding++;
+ bytes++;
+ if (bytes == 3) {
+ result.push_back(base64url[(bits >> 18) & 0x3f]);
+ result.push_back(base64url[(bits >> 12) & 0x3f]);
+ if (padding <= 1) {
+ result.push_back(base64url[(bits >> 6) & 0x3f]);
+ }
+ if (padding <= 0) {
+ result.push_back(base64url[(bits >> 0) & 0x3f]);
+ }
+ bits = 0;
+ bytes = 0;
+ }
+ }
+ return result;
+}
+
+inline uint8 decode_base64url_bits(mpt::uchar c) {
+ for (uint8 i = 0; i < 64; ++i)
+ {
+ if (base64url[i] == c)
+ {
+ return i;
+ }
+ }
+ throw base64url_parse_error();
+}
+
+inline std::vector<std::byte> decode_base64url(const mpt::ustring & src) {
+ std::vector<std::byte> result;
+ result.reserve(3 * ((src.length() + 2) / 4));
+ uint32 bits = 0;
+ std::size_t chars = 0;
+ for (mpt::uchar c : src) {
+ bits <<= 6;
+ bits |= decode_base64url_bits(c);
+ chars++;
+ if (chars == 4) {
+ result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 16) & 0xff)));
+ result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 8) & 0xff)));
+ result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 0) & 0xff)));
+ bits = 0;
+ chars = 0;
+ }
+ }
+ uint32 padding = 0;
+ if (chars != 0 && chars < 2) {
+ throw base64url_parse_error();
+ }
+ while (chars != 0) {
+ bits <<= 6;
+ padding++;
+ chars++;
+ if (chars == 4) {
+ result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 16) & 0xff)));
+ if (padding < 2) {
+ result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 8) & 0xff)));
+ }
+ if (padding < 1) {
+ result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 0) & 0xff)));
+ }
+ bits = 0;
+ chars = 0;
+ padding = 0;
+ }
+ }
+ return result;
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BINARY_BASE64URL_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/binary/hex.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/binary/hex.hpp
new file mode 100644
index 00000000..09a518b4
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/binary/hex.hpp
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BINARY_HEX_HPP
+#define MPT_BINARY_HEX_HPP
+
+
+
+#include "mpt/base/integer.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/string/types.hpp"
+
+#include <array>
+#include <vector>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+inline constexpr std::array<mpt::uchar, 16> encode_nibble = {
+ {MPT_UCHAR('0'), MPT_UCHAR('1'), MPT_UCHAR('2'), MPT_UCHAR('3'),
+ MPT_UCHAR('4'), MPT_UCHAR('5'), MPT_UCHAR('6'), MPT_UCHAR('7'),
+ MPT_UCHAR('8'), MPT_UCHAR('9'), MPT_UCHAR('A'), MPT_UCHAR('B'),
+ MPT_UCHAR('C'), MPT_UCHAR('D'), MPT_UCHAR('E'), MPT_UCHAR('F')}
+};
+
+
+inline bool decode_byte(uint8 & byte, mpt::uchar c1, mpt::uchar c2) {
+ byte = 0;
+ if (MPT_UCHAR('0') <= c1 && c1 <= MPT_UCHAR('9')) {
+ byte += static_cast<uint8>((c1 - MPT_UCHAR('0')) << 4);
+ } else if (MPT_UCHAR('A') <= c1 && c1 <= MPT_UCHAR('F')) {
+ byte += static_cast<uint8>((c1 - MPT_UCHAR('A') + 10) << 4);
+ } else if (MPT_UCHAR('a') <= c1 && c1 <= MPT_UCHAR('f')) {
+ byte += static_cast<uint8>((c1 - MPT_UCHAR('a') + 10) << 4);
+ } else {
+ return false;
+ }
+ if (MPT_UCHAR('0') <= c2 && c2 <= MPT_UCHAR('9')) {
+ byte += static_cast<uint8>(c2 - MPT_UCHAR('0'));
+ } else if (MPT_UCHAR('A') <= c2 && c2 <= MPT_UCHAR('F')) {
+ byte += static_cast<uint8>(c2 - MPT_UCHAR('A') + 10);
+ } else if (MPT_UCHAR('a') <= c2 && c2 <= MPT_UCHAR('f')) {
+ byte += static_cast<uint8>(c2 - MPT_UCHAR('a') + 10);
+ } else {
+ return false;
+ }
+ return true;
+}
+
+
+template <typename Tbyte>
+inline mpt::ustring encode_hex(mpt::span<Tbyte> src_) {
+ mpt::const_byte_span src = mpt::byte_cast<mpt::const_byte_span>(src_);
+ mpt::ustring result;
+ result.reserve(src.size() * 2);
+ for (std::byte byte : src) {
+ result.push_back(encode_nibble[(mpt::byte_cast<uint8>(byte) & 0xf0) >> 4]);
+ result.push_back(encode_nibble[mpt::byte_cast<uint8>(byte) & 0x0f]);
+ }
+ return result;
+}
+
+inline std::vector<std::byte> decode_hex(const mpt::ustring & src) {
+ std::vector<std::byte> result;
+ result.reserve(src.size() / 2);
+ for (std::size_t i = 0; (i + 1) < src.size(); i += 2) {
+ uint8 byte = 0;
+ if (!decode_byte(byte, src[i], src[i + 1])) {
+ return result;
+ }
+ result.push_back(mpt::byte_cast<std::byte>(byte));
+ }
+ return result;
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BINARY_HEX_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/binary/tests/tests_binary.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/binary/tests/tests_binary.hpp
new file mode 100644
index 00000000..daf9c979
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/binary/tests/tests_binary.hpp
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_TESTS_BINARY_HPP
+#define MPT_BASE_TESTS_BINARY_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/span.hpp"
+#include "mpt/binary/base64.hpp"
+#include "mpt/binary/base64url.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <string>
+#include <vector>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace binary {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/binary")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ {
+ std::string expecteds = std::string("pleasure.");
+ std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
+ MPT_TEST_EXPECT_EQUAL(mpt::encode_base64(mpt::as_span(expected)), MPT_USTRING("cGxlYXN1cmUu"));
+ }
+ {
+ std::string expecteds = std::string("leasure.");
+ std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
+ MPT_TEST_EXPECT_EQUAL(mpt::encode_base64(mpt::as_span(expected)), MPT_USTRING("bGVhc3VyZS4="));
+ }
+ {
+ std::string expecteds = std::string("easure.");
+ std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
+ MPT_TEST_EXPECT_EQUAL(mpt::encode_base64(mpt::as_span(expected)), MPT_USTRING("ZWFzdXJlLg=="));
+ }
+ {
+ std::string expecteds = std::string("pleasure.");
+ std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
+ MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64(MPT_USTRING("cGxlYXN1cmUu")));
+ }
+ {
+ std::string expecteds = std::string("leasure.");
+ std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
+ MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64(MPT_USTRING("bGVhc3VyZS4=")));
+ }
+ {
+ std::string expecteds = std::string("easure.");
+ std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
+ MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64(MPT_USTRING("ZWFzdXJlLg==")));
+ }
+
+ {
+ std::string expecteds = std::string("pleasure.");
+ std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
+ MPT_TEST_EXPECT_EQUAL(mpt::encode_base64url(mpt::as_span(expected)), MPT_USTRING("cGxlYXN1cmUu"));
+ }
+ {
+ std::string expecteds = std::string("leasure.");
+ std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
+ MPT_TEST_EXPECT_EQUAL(mpt::encode_base64url(mpt::as_span(expected)), MPT_USTRING("bGVhc3VyZS4"));
+ }
+ {
+ std::string expecteds = std::string("easure.");
+ std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
+ MPT_TEST_EXPECT_EQUAL(mpt::encode_base64url(mpt::as_span(expected)), MPT_USTRING("ZWFzdXJlLg"));
+ }
+ {
+ std::string expecteds = std::string("pleasure.");
+ std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
+ MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64url(MPT_USTRING("cGxlYXN1cmUu")));
+ }
+ {
+ std::string expecteds = std::string("leasure.");
+ std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
+ MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64url(MPT_USTRING("bGVhc3VyZS4")));
+ }
+ {
+ std::string expecteds = std::string("easure.");
+ std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
+ MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64url(MPT_USTRING("ZWFzdXJlLg")));
+ }
+}
+
+} // namespace binary
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_TESTS_BINARY_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/check/libc.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/check/libc.hpp
new file mode 100644
index 00000000..89a97834
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/check/libc.hpp
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_CHECK_LIBC_HPP
+#define MPT_CHECK_LIBC_HPP
+
+#include "mpt/base/detect_os.hpp"
+#include "mpt/base/compiletime_warning.hpp"
+
+#ifndef __STDC_CONSTANT_MACROS
+#ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_STDC_CONSTANT_MACROS
+MPT_WARNING("C stdlib does not provide constant macros. Please #define __STDC_CONSTANT_MACROS.")
+#endif
+#endif
+
+#ifndef __STDC_FORMAT_MACROS
+#ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_STDC_FORMAT_MACROS
+MPT_WARNING("C stdlib does not provide limit macros. Please #define __STDC_FORMAT_MACROS.")
+#endif
+#endif
+
+#ifndef __STDC_LIMIT_MACROS
+#ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_STDC_LIMIT_MACROS
+MPT_WARNING("C stdlib does not provide limit macros. Please #define __STDC_LIMIT_MACROS.")
+#endif
+#endif
+
+#ifndef _USE_MATH_DEFINES
+#ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_USE_MATH_DEFINES
+MPT_WARNING("C stdlib does not provide math constants. Please #define _USE_MATH_DEFINES.")
+#endif
+#endif
+
+#if MPT_LIBC_GLIBC
+#if !defined(_FILE_OFFSET_BITS)
+#ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_FILE_OFFSET_BITS
+MPT_WARNING("C stdlib may not provide 64bit std::FILE access. Please #define _FILE_OFFSET_BITS=64.")
+#endif
+#endif
+#endif
+
+#endif // MPT_CHECK_LIBC_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/check/mfc.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/check/mfc.hpp
new file mode 100644
index 00000000..a0f7014d
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/check/mfc.hpp
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_CHECK_MFC_HPP
+#define MPT_CHECK_MFC_HPP
+
+#include "mpt/base/compiletime_warning.hpp"
+#include "mpt/detect/mfc.hpp"
+
+#if MPT_DETECTED_MFC
+
+#ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
+#ifndef MPT_CHECK_MFC_IGNORE_WARNING_NO_CSTRING_DISABLE_NARROW_WIDE_CONVERSION
+MPT_WARNING("MFC uses CString with automatic encoding conversions. Please #define _CSTRING_DISABLE_NARROW_WIDE_CONVERSION.")
+#endif
+#endif
+
+#endif // MPT_DETECTED_MFC
+
+#endif // MPT_CHECK_MFC_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/check/windows.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/check/windows.hpp
new file mode 100644
index 00000000..b6a05f92
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/check/windows.hpp
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_CHECK_WINDOWS_HPP
+#define MPT_CHECK_WINDOWS_HPP
+
+#include "mpt/base/detect_os.hpp"
+#include "mpt/base/compiletime_warning.hpp"
+
+#if MPT_OS_WINDOWS
+
+#ifndef UNICODE
+#ifndef MPT_CHECK_WINDOWS_IGNORE_WARNING_NO_UNICODE
+MPT_WARNING("windows.h uses MBCS TCHAR. Please #define UNICODE.")
+#endif
+#endif
+
+#ifndef NOMINMAX
+#ifndef MPT_CHECK_WINDOWS_IGNORE_WARNING_NO_NOMINMAX
+MPT_WARNING("windows.h defines min and max which conflicts with C++. Please #define NOMINMAX.")
+#endif
+#endif
+
+#endif // MPT_OS_WINDOWS
+
+#endif // MPT_CHECK_WINDOWS_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/crc/crc.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/crc/crc.hpp
new file mode 100644
index 00000000..5d6e851f
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/crc/crc.hpp
@@ -0,0 +1,205 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_CRC_CRC_HPP
+#define MPT_CRC_CRC_HPP
+
+
+
+#include "mpt/base/array.hpp"
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include "mpt/base/integer.hpp"
+#include "mpt/base/memory.hpp"
+
+#include <array>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+template <typename T, T polynomial, T initial, T resultXOR, bool reverseData>
+class crc {
+
+public:
+ using self_type = crc;
+ using value_type = T;
+ using byte_type = uint8;
+
+ static constexpr std::size_t size_bytes = sizeof(value_type);
+ static constexpr std::size_t size_bits = sizeof(value_type) * 8;
+ static constexpr value_type top_bit = static_cast<value_type>(1) << ((sizeof(value_type) * 8) - 1);
+
+private:
+ template <typename Tint>
+ static constexpr Tint reverse(Tint value) noexcept {
+ const std::size_t bits = sizeof(Tint) * 8;
+ Tint result = 0;
+ for (std::size_t i = 0; i < bits; ++i) {
+ result <<= 1;
+ result |= static_cast<Tint>(value & 0x1);
+ value >>= 1;
+ }
+ return result;
+ }
+
+ static constexpr value_type calculate_table_entry(byte_type pos) noexcept {
+ value_type value = 0;
+ value = (static_cast<value_type>(reverseData ? reverse(pos) : pos) << (size_bits - 8));
+ for (std::size_t bit = 0; bit < 8; ++bit) {
+ if (value & top_bit) {
+ value = (value << 1) ^ polynomial;
+ } else {
+ value = (value << 1);
+ }
+ }
+ value = (reverseData ? reverse(value) : value);
+ return value;
+ }
+
+private:
+ static constexpr std::array<value_type, 256> calculate_table() noexcept {
+ std::array<value_type, 256> t = mpt::init_array<value_type, 256>(value_type{});
+ for (std::size_t i = 0; i < 256; ++i) {
+ t[i] = calculate_table_entry(static_cast<byte_type>(i));
+ }
+ return t;
+ }
+
+ static constexpr std::array<value_type, 256> table = calculate_table();
+
+private:
+ constexpr value_type read_table(byte_type pos) const noexcept {
+ return table[pos];
+ }
+
+private:
+ value_type value;
+
+public:
+ constexpr crc() noexcept
+ : value(initial) {
+ return;
+ }
+
+ constexpr void processByte(byte_type byte) noexcept {
+ if constexpr (reverseData) {
+ value = (value >> 8) ^ read_table(static_cast<byte_type>((value & 0xff) ^ byte));
+ } else {
+ value = (value << 8) ^ read_table(static_cast<byte_type>(((value >> (size_bits - 8)) & 0xff) ^ byte));
+ }
+ }
+
+ constexpr value_type result() const noexcept {
+ return (value ^ resultXOR);
+ }
+
+public:
+ constexpr operator value_type() const noexcept {
+ return result();
+ }
+
+ inline crc & process(char c) noexcept {
+ processByte(mpt::byte_cast<byte_type>(c));
+ return *this;
+ }
+
+ inline crc & process(signed char c) noexcept {
+ processByte(static_cast<byte_type>(c));
+ return *this;
+ }
+
+ inline crc & process(unsigned char c) noexcept {
+ processByte(mpt::byte_cast<byte_type>(c));
+ return *this;
+ }
+
+ inline crc & process(std::byte c) noexcept {
+ processByte(mpt::byte_cast<byte_type>(c));
+ return *this;
+ }
+
+ template <typename InputIt>
+ inline crc & process(InputIt beg, InputIt end) {
+ for (InputIt it = beg; it != end; ++it) {
+ static_assert(sizeof(*it) == 1, "1 byte type required");
+ process(*it);
+ }
+ return *this;
+ }
+
+ template <typename Container>
+ inline crc & process(const Container & data) {
+ operator()(data.begin(), data.end());
+ return *this;
+ }
+
+ inline crc & operator()(char c) noexcept {
+ processByte(mpt::byte_cast<byte_type>(c));
+ return *this;
+ }
+
+ inline crc & operator()(signed char c) noexcept {
+ processByte(static_cast<byte_type>(c));
+ return *this;
+ }
+
+ inline crc & operator()(unsigned char c) noexcept {
+ processByte(mpt::byte_cast<byte_type>(c));
+ return *this;
+ }
+
+ inline crc & operator()(std::byte c) noexcept {
+ processByte(mpt::byte_cast<byte_type>(c));
+ return *this;
+ }
+
+ template <typename InputIt>
+ crc & operator()(InputIt beg, InputIt end) {
+ for (InputIt it = beg; it != end; ++it) {
+ static_assert(sizeof(*it) == 1, "1 byte type required");
+ operator()(*it);
+ }
+ return *this;
+ }
+
+ template <typename Container>
+ inline crc & operator()(const Container & data) {
+ operator()(data.begin(), data.end());
+ return *this;
+ }
+
+ template <typename InputIt>
+ crc(InputIt beg, InputIt end)
+ : value(initial) {
+ for (InputIt it = beg; it != end; ++it) {
+ static_assert(sizeof(*it) == 1, "1 byte type required");
+ process(*it);
+ }
+ }
+
+ template <typename Container>
+ inline crc(const Container & data)
+ : value(initial) {
+ process(data.begin(), data.end());
+ }
+};
+
+using crc16 = crc<uint16, 0x8005, 0, 0, true>;
+using crc32 = crc<uint32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true>;
+using crc32_ogg = crc<uint32, 0x04C11DB7, 0, 0, false>;
+using crc32c = crc<uint32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true>;
+using crc64_jones = crc<uint64, 0xAD93D23594C935A9ull, 0xFFFFFFFFFFFFFFFFull, 0, true>;
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_CRC_CRC_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/crc/tests/tests_crc.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/crc/tests/tests_crc.hpp
new file mode 100644
index 00000000..c09616eb
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/crc/tests/tests_crc.hpp
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_TESTS_CRC_HPP
+#define MPT_BASE_TESTS_CRC_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/crc/crc.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <string>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace crc {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/crc")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ MPT_TEST_EXPECT_EQUAL(mpt::crc32(std::string("123456789")), 0xCBF43926u);
+ MPT_TEST_EXPECT_EQUAL(mpt::crc32_ogg(std::string("123456789")), 0x89a1897fu);
+}
+
+} // namespace crc
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_TESTS_CRC_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/crypto/exception.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/crypto/exception.hpp
new file mode 100644
index 00000000..6e7de510
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/crypto/exception.hpp
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_CRYPTO_EXCEPTION_HPP
+#define MPT_CRYPTO_EXCEPTION_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/format/simple.hpp"
+#include "mpt/out_of_memory/out_of_memory.hpp"
+
+#include <stdexcept>
+#include <string>
+
+#if MPT_OS_WINDOWS
+#include <windows.h> // must be before wincrypt.h for clang-cl
+#include <wincrypt.h> // must be before ncrypt.h
+#include <ncrypt.h>
+#endif // MPT_OS_WINDOWS
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+namespace crypto {
+
+
+
+#if MPT_OS_WINDOWS
+
+class exception
+ : public std::runtime_error {
+private:
+ NTSTATUS m_Status;
+
+public:
+ exception(NTSTATUS status)
+ : std::runtime_error(std::string("crypto error: NTSTATUS ") + mpt::format<std::string>::hex0<8>(static_cast<DWORD>(status)))
+ , m_Status(status) {
+ return;
+ }
+
+ exception(NTSTATUS status, const std::string & function)
+ : std::runtime_error(std::string("crypto error: ") + function + std::string(" NTSTATUS ") + mpt::format<std::string>::hex0<8>(static_cast<DWORD>(status)))
+ , m_Status(status) {
+ return;
+ }
+
+public:
+ NTSTATUS code() const noexcept {
+ return m_Status;
+ }
+};
+
+
+class security_exception
+ : public std::runtime_error {
+private:
+ SECURITY_STATUS m_Status;
+
+public:
+ security_exception(SECURITY_STATUS status)
+ : std::runtime_error(std::string("crypto error: SECURITY_STATUS ") + mpt::format<std::string>::hex0<8>(static_cast<DWORD>(status)))
+ , m_Status(status) {
+ return;
+ }
+
+ security_exception(SECURITY_STATUS status, const std::string & function)
+ : std::runtime_error(std::string("crypto error: ") + function + std::string(" SECURITY_STATUS ") + mpt::format<std::string>::hex0<8>(static_cast<DWORD>(status)))
+ , m_Status(status) {
+ return;
+ }
+
+public:
+ SECURITY_STATUS code() const noexcept {
+ return m_Status;
+ }
+};
+
+
+inline void CheckNTSTATUS(NTSTATUS status) {
+ if (status >= 0) {
+ return;
+ } else if (static_cast<DWORD>(status) == STATUS_NO_MEMORY) {
+ mpt::throw_out_of_memory();
+ } else {
+ throw exception(status);
+ }
+}
+
+
+inline void CheckNTSTATUS(NTSTATUS status, const std::string & function) {
+ if (status >= 0) {
+ return;
+ } else if (static_cast<DWORD>(status) == STATUS_NO_MEMORY) {
+ mpt::throw_out_of_memory();
+ } else {
+ throw exception(status, function);
+ }
+}
+
+
+inline void CheckSECURITY_STATUS(SECURITY_STATUS status) {
+ if (status == ERROR_SUCCESS) {
+ return;
+ } else if (status == NTE_NO_MEMORY) {
+ mpt::throw_out_of_memory();
+ } else {
+ throw security_exception(status);
+ }
+}
+
+
+inline void CheckSECURITY_STATUS(SECURITY_STATUS status, const std::string & function) {
+ if (status == ERROR_SUCCESS) {
+ return;
+ } else if (status == NTE_NO_MEMORY) {
+ mpt::throw_out_of_memory();
+ } else {
+ throw security_exception(status, function);
+ }
+}
+
+
+#endif // MPT_OS_WINDOWS
+
+
+
+} // namespace crypto
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_CRYPTO_EXCEPTION_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/crypto/hash.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/crypto/hash.hpp
new file mode 100644
index 00000000..e8b4d4cb
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/crypto/hash.hpp
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_CRYPTO_HASH_HPP
+#define MPT_CRYPTO_HASH_HPP
+
+
+
+#include "mpt/base/array.hpp"
+#include "mpt/base/detect.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/saturate_cast.hpp"
+#include "mpt/crypto/exception.hpp"
+
+#include <algorithm>
+#include <vector>
+
+#include <cassert>
+#include <cstddef>
+
+#if MPT_OS_WINDOWS
+#include <windows.h> // must be before wincrypt.h for clang-cl
+#include <bcrypt.h>
+#endif // MPT_OS_WINDOWS
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+namespace crypto {
+
+
+
+#if MPT_OS_WINDOWS
+
+
+namespace hash {
+
+
+struct hash_traits_md5 {
+ static constexpr std::size_t output_bits = 128;
+ static constexpr std::size_t output_bytes = output_bits / 8;
+ static constexpr const wchar_t * bcrypt_name = BCRYPT_MD5_ALGORITHM;
+};
+
+struct hash_traits_sha1 {
+ static constexpr std::size_t output_bits = 160;
+ static constexpr std::size_t output_bytes = output_bits / 8;
+ static constexpr const wchar_t * bcrypt_name = BCRYPT_SHA1_ALGORITHM;
+};
+
+struct hash_traits_sha256 {
+ static constexpr std::size_t output_bits = 256;
+ static constexpr std::size_t output_bytes = output_bits / 8;
+ static constexpr const wchar_t * bcrypt_name = BCRYPT_SHA256_ALGORITHM;
+};
+
+struct hash_traits_sha512 {
+ static constexpr std::size_t output_bits = 512;
+ static constexpr std::size_t output_bytes = output_bits / 8;
+ static constexpr const wchar_t * bcrypt_name = BCRYPT_SHA512_ALGORITHM;
+};
+
+template <typename Traits>
+class hash_impl {
+
+public:
+ using traits = Traits;
+
+ using result_type = std::array<std::byte, traits::output_bytes>;
+
+private:
+ BCRYPT_ALG_HANDLE hAlg = NULL;
+ std::vector<BYTE> hashState;
+ std::vector<BYTE> hashResult;
+ BCRYPT_HASH_HANDLE hHash = NULL;
+
+private:
+ void init() {
+ CheckNTSTATUS(BCryptOpenAlgorithmProvider(&hAlg, traits::bcrypt_name, NULL, 0), "BCryptOpenAlgorithmProvider");
+ if (!hAlg) {
+ throw exception(0);
+ }
+ DWORD hashStateSize = 0;
+ DWORD hashStateSizeSize = 0;
+ CheckNTSTATUS(BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&hashStateSize, sizeof(DWORD), &hashStateSizeSize, 0), "BCryptGetProperty");
+ if (hashStateSizeSize != sizeof(DWORD)) {
+ throw exception(0);
+ }
+ if (hashStateSize <= 0) {
+ throw exception(0);
+ }
+ hashState.resize(hashStateSize);
+ DWORD hashResultSize = 0;
+ DWORD hashResultSizeSize = 0;
+ CheckNTSTATUS(BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PBYTE)&hashResultSize, sizeof(DWORD), &hashResultSizeSize, 0), "BCryptGetProperty");
+ if (hashResultSizeSize != sizeof(DWORD)) {
+ throw exception(0);
+ }
+ if (hashResultSize <= 0) {
+ throw exception(0);
+ }
+ if (hashResultSize != mpt::extent<result_type>()) {
+ throw exception(0);
+ }
+ hashResult.resize(hashResultSize);
+ CheckNTSTATUS(BCryptCreateHash(hAlg, &hHash, hashState.data(), hashStateSize, NULL, 0, 0), "BCryptCreateHash");
+ if (!hHash) {
+ throw exception(0);
+ }
+ }
+
+ void cleanup() {
+ if (hHash) {
+ BCryptDestroyHash(hHash);
+ hHash = NULL;
+ }
+ hashResult.resize(0);
+ hashResult.shrink_to_fit();
+ hashState.resize(0);
+ hashState.shrink_to_fit();
+ if (hAlg) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ hAlg = NULL;
+ }
+ }
+
+public:
+ hash_impl() {
+ try {
+ init();
+ } catch (...) {
+ cleanup();
+ throw;
+ }
+ }
+ hash_impl(const hash_impl &) = delete;
+ hash_impl & operator=(const hash_impl &) = delete;
+ ~hash_impl() {
+ cleanup();
+ }
+
+public:
+ hash_impl & process(mpt::const_byte_span data) {
+ CheckNTSTATUS(BCryptHashData(hHash, const_cast<UCHAR *>(mpt::byte_cast<const UCHAR *>(data.data())), mpt::saturate_cast<ULONG>(data.size()), 0), "BCryptHashData");
+ return *this;
+ }
+
+ result_type result() {
+ result_type res = mpt::init_array<std::byte, traits::output_bytes>(std::byte{0});
+ CheckNTSTATUS(BCryptFinishHash(hHash, hashResult.data(), mpt::saturate_cast<ULONG>(hashResult.size()), 0), "BCryptFinishHash");
+ assert(hashResult.size() == mpt::extent<result_type>());
+ std::transform(hashResult.begin(), hashResult.end(), res.begin(), [](BYTE b) { return mpt::as_byte(b); });
+ return res;
+ }
+};
+
+using MD5 = hash_impl<hash_traits_md5>;
+using SHA1 = hash_impl<hash_traits_sha1>;
+using SHA256 = hash_impl<hash_traits_sha256>;
+using SHA512 = hash_impl<hash_traits_sha512>;
+
+
+} // namespace hash
+
+
+#endif // MPT_OS_WINDOWS
+
+
+
+} // namespace crypto
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_CRYPTO_HASH_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/crypto/jwk.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/crypto/jwk.hpp
new file mode 100644
index 00000000..dd54cb04
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/crypto/jwk.hpp
@@ -0,0 +1,531 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_CRYPTO_JWK_HPP
+#define MPT_CRYPTO_JWK_HPP
+
+
+
+#include "mpt/base/alloc.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/saturate_cast.hpp"
+#include "mpt/base/span.hpp"
+#include "mpt/binary/base64url.hpp"
+#include "mpt/crypto/exception.hpp"
+#include "mpt/crypto/hash.hpp"
+#include "mpt/detect/nlohmann_json.hpp"
+#include "mpt/json/json.hpp"
+#include "mpt/out_of_memory/out_of_memory.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/string/utility.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+
+#include <algorithm>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+
+#if MPT_OS_WINDOWS
+#include <windows.h> // must be before wincrypt.h for clang-cl
+#include <bcrypt.h>
+#include <wincrypt.h> // must be before ncrypt.h
+#include <ncrypt.h>
+#endif // MPT_OS_WINDOWS
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace crypto {
+
+
+
+#if MPT_OS_WINDOWS && MPT_DETECTED_NLOHMANN_JSON
+
+
+
+class keystore {
+public:
+ enum class domain {
+ system = 1,
+ user = 2,
+ };
+
+private:
+ NCRYPT_PROV_HANDLE hProv = NULL;
+ domain ProvDomain = domain::user;
+
+private:
+ void cleanup() {
+ if (hProv) {
+ NCryptFreeObject(hProv);
+ hProv = NULL;
+ }
+ }
+
+public:
+ keystore(domain d)
+ : ProvDomain(d) {
+ try {
+ CheckSECURITY_STATUS(NCryptOpenStorageProvider(&hProv, MS_KEY_STORAGE_PROVIDER, 0), "NCryptOpenStorageProvider");
+ } catch (...) {
+ cleanup();
+ throw;
+ }
+ }
+ ~keystore() {
+ return;
+ }
+ operator NCRYPT_PROV_HANDLE() {
+ return hProv;
+ }
+ keystore::domain store_domain() const {
+ return ProvDomain;
+ }
+};
+
+
+
+namespace asymmetric {
+
+
+
+class signature_verification_failed
+ : public std::runtime_error {
+public:
+ signature_verification_failed()
+ : std::runtime_error("Signature Verification failed.") {
+ return;
+ }
+};
+
+
+
+inline std::vector<mpt::ustring> jws_get_keynames(const mpt::ustring & jws_) {
+ std::vector<mpt::ustring> result;
+ nlohmann::json jws = nlohmann::json::parse(mpt::transcode<std::string>(mpt::common_encoding::utf8, jws_));
+ for (const auto & s : jws["signatures"]) {
+ result.push_back(s["header"]["kid"]);
+ }
+ return result;
+}
+
+
+
+struct RSASSA_PSS_SHA512_traits {
+ using hash_type = mpt::crypto::hash::SHA512;
+ static constexpr const char * jwk_alg = "PS512";
+};
+
+
+
+template <typename Traits = RSASSA_PSS_SHA512_traits, std::size_t keysize = 4096>
+class rsassa_pss {
+
+public:
+ using hash_type = typename Traits::hash_type;
+ static constexpr const char * jwk_alg = Traits::jwk_alg;
+
+ struct public_key_data {
+
+ mpt::ustring name;
+ uint32 length = 0;
+ std::vector<std::byte> public_exp;
+ std::vector<std::byte> modulus;
+
+ std::vector<std::byte> as_cng_blob() const {
+ BCRYPT_RSAKEY_BLOB rsakey_blob{};
+ rsakey_blob.Magic = BCRYPT_RSAPUBLIC_MAGIC;
+ rsakey_blob.BitLength = length;
+ rsakey_blob.cbPublicExp = mpt::saturate_cast<ULONG>(public_exp.size());
+ rsakey_blob.cbModulus = mpt::saturate_cast<ULONG>(modulus.size());
+ std::vector<std::byte> result(sizeof(BCRYPT_RSAKEY_BLOB) + public_exp.size() + modulus.size());
+ std::memcpy(result.data(), &rsakey_blob, sizeof(BCRYPT_RSAKEY_BLOB));
+ std::memcpy(result.data() + sizeof(BCRYPT_RSAKEY_BLOB), public_exp.data(), public_exp.size());
+ std::memcpy(result.data() + sizeof(BCRYPT_RSAKEY_BLOB) + public_exp.size(), modulus.data(), modulus.size());
+ return result;
+ }
+
+ mpt::ustring as_jwk() const {
+ nlohmann::json json = nlohmann::json::object();
+ json["kid"] = name;
+ json["kty"] = "RSA";
+ json["alg"] = jwk_alg;
+ json["use"] = "sig";
+ json["e"] = mpt::encode_base64url(mpt::as_span(public_exp));
+ json["n"] = mpt::encode_base64url(mpt::as_span(modulus));
+ return mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, json.dump());
+ }
+
+ static public_key_data from_jwk(const mpt::ustring & jwk) {
+ public_key_data result;
+ try {
+ nlohmann::json json = nlohmann::json::parse(mpt::transcode<std::string>(mpt::common_encoding::utf8, jwk));
+ if (json["kty"] != "RSA") {
+ throw std::runtime_error("Cannot parse RSA public key JWK.");
+ }
+ if (json["alg"] != jwk_alg) {
+ throw std::runtime_error("Cannot parse RSA public key JWK.");
+ }
+ if (json["use"] != "sig") {
+ throw std::runtime_error("Cannot parse RSA public key JWK.");
+ }
+ result.name = json["kid"].get<mpt::ustring>();
+ result.public_exp = mpt::decode_base64url(json["e"]);
+ result.modulus = mpt::decode_base64url(json["n"]);
+ result.length = mpt::saturate_cast<uint32>(result.modulus.size() * 8);
+ } catch (mpt::out_of_memory e) {
+ mpt::rethrow_out_of_memory(e);
+ } catch (...) {
+ throw std::runtime_error("Cannot parse RSA public key JWK.");
+ }
+ return result;
+ }
+
+ static public_key_data from_cng_blob(const mpt::ustring & name, const std::vector<std::byte> & blob) {
+ public_key_data result;
+ BCRYPT_RSAKEY_BLOB rsakey_blob{};
+ if (blob.size() < sizeof(BCRYPT_RSAKEY_BLOB)) {
+ throw std::runtime_error("Cannot parse RSA public key blob.");
+ }
+ std::memcpy(&rsakey_blob, blob.data(), sizeof(BCRYPT_RSAKEY_BLOB));
+ if (rsakey_blob.Magic != BCRYPT_RSAPUBLIC_MAGIC) {
+ throw std::runtime_error("Cannot parse RSA public key blob.");
+ }
+ if (blob.size() != sizeof(BCRYPT_RSAKEY_BLOB) + rsakey_blob.cbPublicExp + rsakey_blob.cbModulus) {
+ throw std::runtime_error("Cannot parse RSA public key blob.");
+ }
+ result.name = name;
+ result.length = rsakey_blob.BitLength;
+ result.public_exp = std::vector<std::byte>(blob.data() + sizeof(BCRYPT_RSAKEY_BLOB), blob.data() + sizeof(BCRYPT_RSAKEY_BLOB) + rsakey_blob.cbPublicExp);
+ result.modulus = std::vector<std::byte>(blob.data() + sizeof(BCRYPT_RSAKEY_BLOB) + rsakey_blob.cbPublicExp, blob.data() + sizeof(BCRYPT_RSAKEY_BLOB) + rsakey_blob.cbPublicExp + rsakey_blob.cbModulus);
+ return result;
+ }
+ };
+
+
+
+ static std::vector<public_key_data> parse_jwk_set(const mpt::ustring & jwk_set_) {
+ std::vector<public_key_data> result;
+ nlohmann::json jwk_set = nlohmann::json::parse(mpt::transcode<std::string>(mpt::common_encoding::utf8, jwk_set_));
+ for (const auto & k : jwk_set["keys"]) {
+ try {
+ result.push_back(public_key_data::from_jwk(mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, k.dump())));
+ } catch (...) {
+ // nothing
+ }
+ }
+ return result;
+ }
+
+
+
+ class public_key {
+
+ private:
+ mpt::ustring name;
+ BCRYPT_ALG_HANDLE hSignAlg = NULL;
+ BCRYPT_KEY_HANDLE hKey = NULL;
+
+ private:
+ void cleanup() {
+ if (hKey) {
+ BCryptDestroyKey(hKey);
+ hKey = NULL;
+ }
+ if (hSignAlg) {
+ BCryptCloseAlgorithmProvider(hSignAlg, 0);
+ hSignAlg = NULL;
+ }
+ }
+
+ public:
+ public_key(const public_key_data & data) {
+ try {
+ name = data.name;
+ CheckNTSTATUS(BCryptOpenAlgorithmProvider(&hSignAlg, BCRYPT_RSA_ALGORITHM, NULL, 0), "BCryptOpenAlgorithmProvider");
+ std::vector<std::byte> blob = data.as_cng_blob();
+ CheckNTSTATUS(BCryptImportKeyPair(hSignAlg, NULL, BCRYPT_RSAPUBLIC_BLOB, &hKey, mpt::byte_cast<UCHAR *>(blob.data()), mpt::saturate_cast<ULONG>(blob.size()), 0), "BCryptImportKeyPair");
+ } catch (...) {
+ cleanup();
+ throw;
+ }
+ }
+
+ public_key(const public_key & other)
+ : public_key(other.get_public_key_data()) {
+ return;
+ }
+
+ public_key & operator=(const public_key & other) {
+ if (&other == this) {
+ return *this;
+ }
+ public_key copy(other);
+ {
+ using std::swap;
+ swap(copy.name, name);
+ swap(copy.hSignAlg, hSignAlg);
+ swap(copy.hKey, hKey);
+ }
+ return *this;
+ }
+
+ ~public_key() {
+ cleanup();
+ }
+
+ mpt::ustring get_name() const {
+ return name;
+ }
+
+ public_key_data get_public_key_data() const {
+ DWORD bytes = 0;
+ CheckNTSTATUS(BCryptExportKey(hKey, NULL, BCRYPT_RSAPUBLIC_BLOB, NULL, 0, &bytes, 0), "BCryptExportKey");
+ std::vector<std::byte> blob(bytes);
+ CheckNTSTATUS(BCryptExportKey(hKey, NULL, BCRYPT_RSAPUBLIC_BLOB, mpt::byte_cast<BYTE *>(blob.data()), mpt::saturate_cast<DWORD>(blob.size()), &bytes, 0), "BCryptExportKey");
+ return public_key_data::from_cng_blob(name, blob);
+ }
+
+ void verify_hash(typename hash_type::result_type hash, std::vector<std::byte> signature) {
+ BCRYPT_PSS_PADDING_INFO paddinginfo;
+ paddinginfo.pszAlgId = hash_type::traits::bcrypt_name;
+ paddinginfo.cbSalt = mpt::saturate_cast<ULONG>(hash_type::traits::output_bytes);
+ NTSTATUS result = BCryptVerifySignature(hKey, &paddinginfo, mpt::byte_cast<UCHAR *>(hash.data()), mpt::saturate_cast<ULONG>(hash.size()), mpt::byte_cast<UCHAR *>(signature.data()), mpt::saturate_cast<ULONG>(signature.size()), BCRYPT_PAD_PSS);
+ if (result == 0x00000000 /*STATUS_SUCCESS*/) {
+ return;
+ }
+ if (result == 0xC000A000 /*STATUS_INVALID_SIGNATURE*/) {
+ throw signature_verification_failed();
+ }
+ CheckNTSTATUS(result, "BCryptVerifySignature");
+ throw signature_verification_failed();
+ }
+
+ void verify(mpt::const_byte_span payload, const std::vector<std::byte> & signature) {
+ verify_hash(hash_type().process(payload).result(), signature);
+ }
+
+ std::vector<std::byte> jws_verify(const mpt::ustring & jws_) {
+ nlohmann::json jws = nlohmann::json::parse(mpt::transcode<std::string>(mpt::common_encoding::utf8, jws_));
+ std::vector<std::byte> payload = mpt::decode_base64url(jws["payload"]);
+ nlohmann::json jsignature = nlohmann::json::object();
+ bool sigfound = false;
+ for (const auto & s : jws["signatures"]) {
+ if (s["header"]["kid"] == mpt::transcode<std::string>(mpt::common_encoding::utf8, name)) {
+ jsignature = s;
+ sigfound = true;
+ }
+ }
+ if (!sigfound) {
+ throw signature_verification_failed();
+ }
+ std::vector<std::byte> protectedheaderraw = mpt::decode_base64url(jsignature["protected"]);
+ std::vector<std::byte> signature = mpt::decode_base64url(jsignature["signature"]);
+ nlohmann::json header = nlohmann::json::parse(mpt::buffer_cast<std::string>(protectedheaderraw));
+ if (header["typ"] != "JWT") {
+ throw signature_verification_failed();
+ }
+ if (header["alg"] != jwk_alg) {
+ throw signature_verification_failed();
+ }
+ verify_hash(hash_type().process(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(mpt::transcode<std::string>(mpt::common_encoding::utf8, mpt::encode_base64url(mpt::as_span(protectedheaderraw)) + MPT_USTRING(".") + mpt::encode_base64url(mpt::as_span(payload)))))).result(), signature);
+ return payload;
+ }
+
+ std::vector<std::byte> jws_compact_verify(const mpt::ustring & jws) {
+ std::vector<mpt::ustring> parts = mpt::split<mpt::ustring>(jws, MPT_USTRING("."));
+ if (parts.size() != 3) {
+ throw signature_verification_failed();
+ }
+ std::vector<std::byte> protectedheaderraw = mpt::decode_base64url(parts[0]);
+ std::vector<std::byte> payload = mpt::decode_base64url(parts[1]);
+ std::vector<std::byte> signature = mpt::decode_base64url(parts[2]);
+ nlohmann::json header = nlohmann::json::parse(mpt::buffer_cast<std::string>(protectedheaderraw));
+ if (header["typ"] != "JWT") {
+ throw signature_verification_failed();
+ }
+ if (header["alg"] != jwk_alg) {
+ throw signature_verification_failed();
+ }
+ verify_hash(hash_type().process(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(mpt::transcode<std::string>(mpt::common_encoding::utf8, mpt::encode_base64url(mpt::as_span(protectedheaderraw)) + MPT_USTRING(".") + mpt::encode_base64url(mpt::as_span(payload)))))).result(), signature);
+ return payload;
+ }
+ };
+
+
+
+ static inline void jws_verify_at_least_one(std::vector<public_key> & keys, const std::vector<std::byte> & expectedPayload, const mpt::ustring & signature) {
+ std::vector<mpt::ustring> keynames = mpt::crypto::asymmetric::jws_get_keynames(signature);
+ bool sigchecked = false;
+ for (const auto & keyname : keynames) {
+ for (auto & key : keys) {
+ if (key.get_name() == keyname) {
+ if (expectedPayload != key.jws_verify(signature)) {
+ throw mpt::crypto::asymmetric::signature_verification_failed();
+ }
+ sigchecked = true;
+ }
+ }
+ }
+ if (!sigchecked) {
+ throw mpt::crypto::asymmetric::signature_verification_failed();
+ }
+ }
+
+
+
+ static inline std::vector<std::byte> jws_verify_at_least_one(std::vector<public_key> & keys, const mpt::ustring & signature) {
+ std::vector<mpt::ustring> keynames = mpt::crypto::asymmetric::jws_get_keynames(signature);
+ for (const auto & keyname : keynames) {
+ for (auto & key : keys) {
+ if (key.get_name() == keyname) {
+ return key.jws_verify(signature);
+ }
+ }
+ }
+ throw mpt::crypto::asymmetric::signature_verification_failed();
+ }
+
+
+
+ class managed_private_key {
+
+ private:
+ mpt::ustring name;
+ NCRYPT_KEY_HANDLE hKey = NULL;
+
+ private:
+ void cleanup() {
+ if (hKey) {
+ NCryptFreeObject(hKey);
+ hKey = NULL;
+ }
+ }
+
+ public:
+ managed_private_key() = delete;
+
+ managed_private_key(const managed_private_key &) = delete;
+
+ managed_private_key & operator=(const managed_private_key &) = delete;
+
+ managed_private_key(keystore & keystore) {
+ try {
+ CheckSECURITY_STATUS(NCryptCreatePersistedKey(keystore, &hKey, BCRYPT_RSA_ALGORITHM, NULL, 0, 0), "NCryptCreatePersistedKey");
+ } catch (...) {
+ cleanup();
+ throw;
+ }
+ }
+
+ managed_private_key(keystore & keystore, const mpt::ustring & name_)
+ : name(name_) {
+ try {
+ SECURITY_STATUS openKeyStatus = NCryptOpenKey(keystore, &hKey, mpt::transcode<std::wstring>(name).c_str(), 0, (keystore.store_domain() == keystore::domain::system ? NCRYPT_MACHINE_KEY_FLAG : 0));
+ if (openKeyStatus == NTE_BAD_KEYSET) {
+ CheckSECURITY_STATUS(NCryptCreatePersistedKey(keystore, &hKey, BCRYPT_RSA_ALGORITHM, mpt::transcode<std::wstring>(name).c_str(), 0, (keystore.store_domain() == keystore::domain::system ? NCRYPT_MACHINE_KEY_FLAG : 0)), "NCryptCreatePersistedKey");
+ DWORD length = mpt::saturate_cast<DWORD>(keysize);
+ CheckSECURITY_STATUS(NCryptSetProperty(hKey, NCRYPT_LENGTH_PROPERTY, (PBYTE)&length, mpt::saturate_cast<DWORD>(sizeof(DWORD)), 0), "NCryptSetProperty");
+ CheckSECURITY_STATUS(NCryptFinalizeKey(hKey, 0), "NCryptFinalizeKey");
+ } else {
+ CheckSECURITY_STATUS(openKeyStatus, "NCryptOpenKey");
+ }
+ } catch (...) {
+ cleanup();
+ throw;
+ }
+ }
+
+ ~managed_private_key() {
+ cleanup();
+ }
+
+ void destroy() {
+ CheckSECURITY_STATUS(NCryptDeleteKey(hKey, 0), "NCryptDeleteKey");
+ name = mpt::ustring();
+ hKey = NULL;
+ }
+
+ public:
+ public_key_data get_public_key_data() const {
+ DWORD bytes = 0;
+ CheckSECURITY_STATUS(NCryptExportKey(hKey, NULL, BCRYPT_RSAPUBLIC_BLOB, NULL, NULL, 0, &bytes, 0), "NCryptExportKey");
+ std::vector<std::byte> blob(bytes);
+ CheckSECURITY_STATUS(NCryptExportKey(hKey, NULL, BCRYPT_RSAPUBLIC_BLOB, NULL, mpt::byte_cast<BYTE *>(blob.data()), mpt::saturate_cast<DWORD>(blob.size()), &bytes, 0), "NCryptExportKey");
+ return public_key_data::from_cng_blob(name, blob);
+ }
+
+ std::vector<std::byte> sign_hash(typename hash_type::result_type hash) {
+ BCRYPT_PSS_PADDING_INFO paddinginfo;
+ paddinginfo.pszAlgId = hash_type::traits::bcrypt_name;
+ paddinginfo.cbSalt = mpt::saturate_cast<ULONG>(hash_type::traits::output_bytes);
+ DWORD bytes = 0;
+ CheckSECURITY_STATUS(NCryptSignHash(hKey, &paddinginfo, mpt::byte_cast<BYTE *>(hash.data()), mpt::saturate_cast<DWORD>(hash.size()), NULL, 0, &bytes, BCRYPT_PAD_PSS), "NCryptSignHash");
+ std::vector<std::byte> result(bytes);
+ CheckSECURITY_STATUS(NCryptSignHash(hKey, &paddinginfo, mpt::byte_cast<BYTE *>(hash.data()), mpt::saturate_cast<DWORD>(hash.size()), mpt::byte_cast<BYTE *>(result.data()), mpt::saturate_cast<DWORD>(result.size()), &bytes, BCRYPT_PAD_PSS), "NCryptSignHash");
+ return result;
+ }
+
+ std::vector<std::byte> sign(mpt::const_byte_span payload) {
+ return sign_hash(hash_type().process(payload).result());
+ }
+
+ mpt::ustring jws_compact_sign(mpt::const_byte_span payload) {
+ nlohmann::json protectedheader = nlohmann::json::object();
+ protectedheader["typ"] = "JWT";
+ protectedheader["alg"] = jwk_alg;
+ std::string protectedheaderstring = protectedheader.dump();
+ std::vector<std::byte> signature = sign_hash(hash_type().process(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(mpt::transcode<std::string>(mpt::common_encoding::utf8, mpt::encode_base64url(mpt::as_span(protectedheaderstring)) + MPT_USTRING(".") + mpt::encode_base64url(payload))))).result());
+ return mpt::encode_base64url(mpt::as_span(protectedheaderstring)) + MPT_USTRING(".") + mpt::encode_base64url(payload) + MPT_USTRING(".") + mpt::encode_base64url(mpt::as_span(signature));
+ }
+
+ mpt::ustring jws_sign(mpt::const_byte_span payload) {
+ nlohmann::json protectedheader = nlohmann::json::object();
+ protectedheader["typ"] = "JWT";
+ protectedheader["alg"] = jwk_alg;
+ std::string protectedheaderstring = protectedheader.dump();
+ nlohmann::json header = nlohmann::json::object();
+ header["kid"] = name;
+ std::vector<std::byte> signature = sign_hash(hash_type().process(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(mpt::transcode<std::string>(mpt::common_encoding::utf8, mpt::encode_base64url(mpt::as_span(protectedheaderstring)) + MPT_USTRING(".") + mpt::encode_base64url(payload))))).result());
+ nlohmann::json jws = nlohmann::json::object();
+ jws["payload"] = mpt::encode_base64url(payload);
+ jws["signatures"] = nlohmann::json::array();
+ nlohmann::json jsignature = nlohmann::json::object();
+ jsignature["header"] = header;
+ jsignature["protected"] = mpt::encode_base64url(mpt::as_span(protectedheaderstring));
+ jsignature["signature"] = mpt::encode_base64url(mpt::as_span(signature));
+ jws["signatures"].push_back(jsignature);
+ return mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, jws.dump());
+ }
+ };
+
+}; // class rsassa_pss
+
+
+
+} // namespace asymmetric
+
+
+
+#endif // MPT_OS_WINDOWS && MPT_DETECTED_NLOHMANN_JSON
+
+
+
+} // namespace crypto
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_CRYPTO_JWK_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/crypto/tests/tests_crypto.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/crypto/tests/tests_crypto.hpp
new file mode 100644
index 00000000..97053777
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/crypto/tests/tests_crypto.hpp
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_TESTS_CRYPTO_HPP
+#define MPT_BASE_TESTS_CRYPTO_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/span.hpp"
+#include "mpt/crypto/exception.hpp"
+#include "mpt/crypto/hash.hpp"
+#include "mpt/crypto/jwk.hpp"
+#include "mpt/detect/nlohmann_json.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <string>
+#include <vector>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace crypto {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/crypto")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+#if MPT_OS_WINDOWS
+ mpt::crypto::hash::SHA512::result_type sha512_abc{
+ std::byte{0xdd}, std::byte{0xaf}, std::byte{0x35}, std::byte{0xa1}, std::byte{0x93}, std::byte{0x61}, std::byte{0x7a}, std::byte{0xba},
+ std::byte{0xcc}, std::byte{0x41}, std::byte{0x73}, std::byte{0x49}, std::byte{0xae}, std::byte{0x20}, std::byte{0x41}, std::byte{0x31},
+ std::byte{0x12}, std::byte{0xe6}, std::byte{0xfa}, std::byte{0x4e}, std::byte{0x89}, std::byte{0xa9}, std::byte{0x7e}, std::byte{0xa2},
+ std::byte{0x0a}, std::byte{0x9e}, std::byte{0xee}, std::byte{0xe6}, std::byte{0x4b}, std::byte{0x55}, std::byte{0xd3}, std::byte{0x9a},
+ std::byte{0x21}, std::byte{0x92}, std::byte{0x99}, std::byte{0x2a}, std::byte{0x27}, std::byte{0x4f}, std::byte{0xc1}, std::byte{0xa8},
+ std::byte{0x36}, std::byte{0xba}, std::byte{0x3c}, std::byte{0x23}, std::byte{0xa3}, std::byte{0xfe}, std::byte{0xeb}, std::byte{0xbd},
+ std::byte{0x45}, std::byte{0x4d}, std::byte{0x44}, std::byte{0x23}, std::byte{0x64}, std::byte{0x3c}, std::byte{0xe8}, std::byte{0x0e},
+ std::byte{0x2a}, std::byte{0x9a}, std::byte{0xc9}, std::byte{0x4f}, std::byte{0xa5}, std::byte{0x4c}, std::byte{0xa4}, std::byte{0x9f}};
+ MPT_TEST_EXPECT_EQUAL(mpt::crypto::hash::SHA512().process(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(std::string("abc")))).result(), sha512_abc);
+
+#endif // MPT_OS_WINDOWS
+
+#if MPT_OS_WINDOWS && MPT_DETECTED_NLOHMANN_JSON
+
+ {
+
+ std::vector<std::byte> data = {std::byte{0x11}, std::byte{0x12}, std::byte{0x13}, std::byte{0x14}};
+
+ mpt::crypto::keystore keystore(mpt::crypto::keystore::domain::user);
+
+ mpt::crypto::asymmetric::rsassa_pss<>::managed_private_key key(keystore, U_("OpenMPT Test Key 1"));
+
+ auto publickeydata = key.get_public_key_data();
+
+ mpt::crypto::asymmetric::rsassa_pss<>::public_key pk{publickeydata};
+ mpt::crypto::asymmetric::rsassa_pss<>::public_key pk_copy{pk};
+ mpt::ustring jwk = publickeydata.as_jwk();
+
+ std::vector<std::byte> signature = key.sign(mpt::as_span(data));
+ mpt::ustring jws = key.jws_sign(mpt::as_span(data));
+ mpt::ustring jws_compact = key.jws_compact_sign(mpt::as_span(data));
+
+ try {
+ pk.verify(mpt::as_span(data), signature);
+ auto verifieddata1 = mpt::crypto::asymmetric::rsassa_pss<>::public_key(mpt::crypto::asymmetric::rsassa_pss<>::public_key_data::from_jwk(jwk)).jws_verify(jws);
+ auto verifieddata2 = mpt::crypto::asymmetric::rsassa_pss<>::public_key(mpt::crypto::asymmetric::rsassa_pss<>::public_key_data::from_jwk(jwk)).jws_compact_verify(jws_compact);
+ MPT_TEST_EXPECT_EQUAL(true, true);
+ MPT_TEST_EXPECT_EQUAL(data, verifieddata1);
+ MPT_TEST_EXPECT_EQUAL(data, verifieddata2);
+ } catch (const mpt::crypto::asymmetric::signature_verification_failed &) {
+ MPT_TEST_EXPECT_EQUAL(true, false);
+ }
+
+ key.destroy();
+ }
+
+#endif // MPT_OS_WINDOWS && MPT_DETECTED_NLOHMANN_JSON
+}
+
+
+} // namespace crypto
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_TESTS_CRYPTO_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/detect/dl.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/detect/dl.hpp
new file mode 100644
index 00000000..da24272e
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/detect/dl.hpp
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_DETECT_DL_HPP
+#define MPT_DETECT_DL_HPP
+
+#include "mpt/base/compiletime_warning.hpp"
+
+#if defined(MPT_WITH_DL)
+#if !defined(CPPCHECK)
+#if !__has_include(<dlfcn.h>)
+#error "MPT_WITH_DL defined but <dlfcn.h> not found."
+#endif
+#endif
+#define MPT_DETECTED_DL 1
+#else
+#if defined(CPPCHECK)
+#define MPT_DETECTED_DL 1
+#else
+#if __has_include(<dlfcn.h>)
+#define MPT_DETECTED_DL 1
+#else
+#define MPT_DETECTED_DL 0
+#endif
+#endif
+#endif
+
+#endif // MPT_DETECT_DL_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/detect/ltdl.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/detect/ltdl.hpp
new file mode 100644
index 00000000..bdd1d986
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/detect/ltdl.hpp
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_DETECT_LTDL_HPP
+#define MPT_DETECT_LTDL_HPP
+
+#include "mpt/base/compiletime_warning.hpp"
+
+#if defined(MPT_WITH_LTDL)
+#if !defined(CPPCHECK)
+#if !__has_include(<ltdl.h>)
+#error "MPT_WITH_LTDL defined but <ltdl.h> not found."
+#endif
+#endif
+#define MPT_DETECTED_LTDL 1
+#else
+#if defined(CPPCHECK)
+#define MPT_DETECTED_LTDL 1
+#else
+#if __has_include(<ltdl.h>)
+#define MPT_DETECTED_LTDL 1
+#else
+#define MPT_DETECTED_LTDL 0
+#endif
+#endif
+#endif
+
+#endif // MPT_DETECT_LTDL_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/detect/mfc.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/detect/mfc.hpp
new file mode 100644
index 00000000..2cdfd149
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/detect/mfc.hpp
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_DETECT_MFC_HPP
+#define MPT_DETECT_MFC_HPP
+
+#include "mpt/base/compiletime_warning.hpp"
+
+#if defined(MPT_WITH_MFC)
+#if !defined(CPPCHECK)
+#if !__has_include(<afx.h>)
+#error "MPT_WITH_MFC defined but <afx.h> not found."
+#endif
+#endif
+#if !MPT_COMPILER_GENERIC && !MPT_COMPILER_MSVC && !MPT_COMPILER_CLANG
+MPT_WARNING("Using MFC with unsupported compiler.")
+#endif
+#define MPT_DETECTED_MFC 1
+#else
+#define MPT_DETECTED_MFC 0
+#endif
+
+#endif // MPT_DETECT_MFC_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/detect/nlohmann_json.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/detect/nlohmann_json.hpp
new file mode 100644
index 00000000..299d6126
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/detect/nlohmann_json.hpp
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_DETECT_NLOHMANN_JSON_HPP
+#define MPT_DETECT_NLOHMANN_JSON_HPP
+
+#if defined(MPT_WITH_NLOHMANN_JSON)
+#if !defined(CPPCHECK)
+#if !__has_include(<nlohmann/json.hpp>)
+#error "MPT_WITH_NLOHMANN_JSON defined but <nlohmann/json.hpp> not found."
+#endif
+#endif
+#define MPT_DETECTED_NLOHMANN_JSON 1
+#else
+#if defined(CPPCHECK)
+#define MPT_DETECTED_NLOHMANN_JSON 1
+#else
+#if __has_include(<nlohmann/json.hpp>)
+#define MPT_DETECTED_NLOHMANN_JSON 1
+#else
+#define MPT_DETECTED_NLOHMANN_JSON 0
+#endif
+#endif
+#endif
+
+#endif // MPT_DETECT_NLOHMANN_JSON_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/endian/floatingpoint.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/endian/floatingpoint.hpp
new file mode 100644
index 00000000..7eaff159
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/endian/floatingpoint.hpp
@@ -0,0 +1,485 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_ENDIAN_FLOATINGPOINT_HPP
+#define MPT_ENDIAN_FLOATINGPOINT_HPP
+
+
+
+#include "mpt/base/bit.hpp"
+#include "mpt/base/floatingpoint.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <limits>
+
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+// 1.0f --> 0x3f800000u
+MPT_FORCEINLINE uint32 EncodeIEEE754binary32(float32 f) {
+ if constexpr (mpt::float_traits<float32>::is_ieee754_binary32ne) {
+ return mpt::bit_cast<uint32>(f);
+ } else {
+ int e = 0;
+ float m = std::frexp(f, &e);
+ if (e == 0 && std::fabs(m) == 0.0f) {
+ uint32 expo = 0u;
+ uint32 sign = std::signbit(m) ? 0x01u : 0x00u;
+ uint32 mant = 0u;
+ uint32 i = 0u;
+ i |= (mant << 0) & 0x007fffffu;
+ i |= (expo << 23) & 0x7f800000u;
+ i |= (sign << 31) & 0x80000000u;
+ return i;
+ } else {
+ uint32 expo = e + 127 - 1;
+ uint32 sign = std::signbit(m) ? 0x01u : 0x00u;
+ uint32 mant = static_cast<uint32>(std::fabs(std::ldexp(m, 24)));
+ uint32 i = 0u;
+ i |= (mant << 0) & 0x007fffffu;
+ i |= (expo << 23) & 0x7f800000u;
+ i |= (sign << 31) & 0x80000000u;
+ return i;
+ }
+ }
+}
+
+MPT_FORCEINLINE uint64 EncodeIEEE754binary64(float64 f) {
+ if constexpr (mpt::float_traits<float64>::is_ieee754_binary64ne) {
+ return mpt::bit_cast<uint64>(f);
+ } else {
+ int e = 0;
+ double m = std::frexp(f, &e);
+ if (e == 0 && std::fabs(m) == 0.0) {
+ uint64 expo = 0u;
+ uint64 sign = std::signbit(m) ? 0x01u : 0x00u;
+ uint64 mant = 0u;
+ uint64 i = 0u;
+ i |= (mant << 0) & 0x000fffffffffffffull;
+ i |= (expo << 52) & 0x7ff0000000000000ull;
+ i |= (sign << 63) & 0x8000000000000000ull;
+ return i;
+ } else {
+ uint64 expo = static_cast<int64>(e) + 1023 - 1;
+ uint64 sign = std::signbit(m) ? 0x01u : 0x00u;
+ uint64 mant = static_cast<uint64>(std::fabs(std::ldexp(m, 53)));
+ uint64 i = 0u;
+ i |= (mant << 0) & 0x000fffffffffffffull;
+ i |= (expo << 52) & 0x7ff0000000000000ull;
+ i |= (sign << 63) & 0x8000000000000000ull;
+ return i;
+ }
+ }
+}
+
+// 0x3f800000u --> 1.0f
+MPT_FORCEINLINE float32 DecodeIEEE754binary32(uint32 i) {
+ if constexpr (mpt::float_traits<float32>::is_ieee754_binary32ne) {
+ return mpt::bit_cast<float32>(i);
+ } else {
+ uint32 mant = (i & 0x007fffffu) >> 0;
+ uint32 expo = (i & 0x7f800000u) >> 23;
+ uint32 sign = (i & 0x80000000u) >> 31;
+ if (expo == 0) {
+ float m = sign ? -static_cast<float>(mant) : static_cast<float>(mant);
+ int e = static_cast<int>(expo) - 127 + 1 - 24;
+ float f = std::ldexp(m, e);
+ return static_cast<float32>(f);
+ } else {
+ mant |= 0x00800000u;
+ float m = sign ? -static_cast<float>(mant) : static_cast<float>(mant);
+ int e = static_cast<int>(expo) - 127 + 1 - 24;
+ float f = std::ldexp(m, e);
+ return static_cast<float32>(f);
+ }
+ }
+}
+
+MPT_FORCEINLINE float64 DecodeIEEE754binary64(uint64 i) {
+ if constexpr (mpt::float_traits<float64>::is_ieee754_binary64ne) {
+ return mpt::bit_cast<float64>(i);
+ } else {
+ uint64 mant = (i & 0x000fffffffffffffull) >> 0;
+ uint64 expo = (i & 0x7ff0000000000000ull) >> 52;
+ uint64 sign = (i & 0x8000000000000000ull) >> 63;
+ if (expo == 0) {
+ double m = sign ? -static_cast<double>(mant) : static_cast<double>(mant);
+ int e = static_cast<int>(expo) - 1023 + 1 - 53;
+ double f = std::ldexp(m, e);
+ return static_cast<float64>(f);
+ } else {
+ mant |= 0x0010000000000000ull;
+ double m = sign ? -static_cast<double>(mant) : static_cast<double>(mant);
+ int e = static_cast<int>(expo) - 1023 + 1 - 53;
+ double f = std::ldexp(m, e);
+ return static_cast<float64>(f);
+ }
+ }
+}
+
+
+// template parameters are byte indices corresponding to the individual bytes of iee754 in memory
+template <std::size_t hihi, std::size_t hilo, std::size_t lohi, std::size_t lolo>
+struct IEEE754binary32Emulated {
+public:
+ using self_t = IEEE754binary32Emulated<hihi, hilo, lohi, lolo>;
+ std::byte bytes[4];
+
+public:
+ MPT_FORCEINLINE std::byte GetByte(std::size_t i) const {
+ return bytes[i];
+ }
+ IEEE754binary32Emulated() = default;
+ MPT_FORCEINLINE explicit IEEE754binary32Emulated(float32 f) {
+ SetInt32(EncodeIEEE754binary32(f));
+ }
+ // b0...b3 are in memory order, i.e. depend on the endianness of this type
+ // little endian: (0x00,0x00,0x80,0x3f)
+ // big endian: (0x3f,0x80,0x00,0x00)
+ MPT_FORCEINLINE explicit IEEE754binary32Emulated(std::byte b0, std::byte b1, std::byte b2, std::byte b3) {
+ bytes[0] = b0;
+ bytes[1] = b1;
+ bytes[2] = b2;
+ bytes[3] = b3;
+ }
+ MPT_FORCEINLINE operator float32() const {
+ return DecodeIEEE754binary32(GetInt32());
+ }
+ MPT_FORCEINLINE self_t & SetInt32(uint32 i) {
+ bytes[hihi] = static_cast<std::byte>(i >> 24);
+ bytes[hilo] = static_cast<std::byte>(i >> 16);
+ bytes[lohi] = static_cast<std::byte>(i >> 8);
+ bytes[lolo] = static_cast<std::byte>(i >> 0);
+ return *this;
+ }
+ MPT_FORCEINLINE uint32 GetInt32() const {
+ return 0u
+ | (static_cast<uint32>(bytes[hihi]) << 24)
+ | (static_cast<uint32>(bytes[hilo]) << 16)
+ | (static_cast<uint32>(bytes[lohi]) << 8)
+ | (static_cast<uint32>(bytes[lolo]) << 0);
+ }
+ MPT_FORCEINLINE bool operator==(const self_t & cmp) const {
+ return true
+ && bytes[0] == cmp.bytes[0]
+ && bytes[1] == cmp.bytes[1]
+ && bytes[2] == cmp.bytes[2]
+ && bytes[3] == cmp.bytes[3];
+ }
+ MPT_FORCEINLINE bool operator!=(const self_t & cmp) const {
+ return !(*this == cmp);
+ }
+};
+template <std::size_t hihihi, std::size_t hihilo, std::size_t hilohi, std::size_t hilolo, std::size_t lohihi, std::size_t lohilo, std::size_t lolohi, std::size_t lololo>
+struct IEEE754binary64Emulated {
+public:
+ using self_t = IEEE754binary64Emulated<hihihi, hihilo, hilohi, hilolo, lohihi, lohilo, lolohi, lololo>;
+ std::byte bytes[8];
+
+public:
+ MPT_FORCEINLINE std::byte GetByte(std::size_t i) const {
+ return bytes[i];
+ }
+ IEEE754binary64Emulated() = default;
+ MPT_FORCEINLINE explicit IEEE754binary64Emulated(float64 f) {
+ SetInt64(EncodeIEEE754binary64(f));
+ }
+ MPT_FORCEINLINE explicit IEEE754binary64Emulated(std::byte b0, std::byte b1, std::byte b2, std::byte b3, std::byte b4, std::byte b5, std::byte b6, std::byte b7) {
+ bytes[0] = b0;
+ bytes[1] = b1;
+ bytes[2] = b2;
+ bytes[3] = b3;
+ bytes[4] = b4;
+ bytes[5] = b5;
+ bytes[6] = b6;
+ bytes[7] = b7;
+ }
+ MPT_FORCEINLINE operator float64() const {
+ return DecodeIEEE754binary64(GetInt64());
+ }
+ MPT_FORCEINLINE self_t & SetInt64(uint64 i) {
+ bytes[hihihi] = static_cast<std::byte>(i >> 56);
+ bytes[hihilo] = static_cast<std::byte>(i >> 48);
+ bytes[hilohi] = static_cast<std::byte>(i >> 40);
+ bytes[hilolo] = static_cast<std::byte>(i >> 32);
+ bytes[lohihi] = static_cast<std::byte>(i >> 24);
+ bytes[lohilo] = static_cast<std::byte>(i >> 16);
+ bytes[lolohi] = static_cast<std::byte>(i >> 8);
+ bytes[lololo] = static_cast<std::byte>(i >> 0);
+ return *this;
+ }
+ MPT_FORCEINLINE uint64 GetInt64() const {
+ return 0u
+ | (static_cast<uint64>(bytes[hihihi]) << 56)
+ | (static_cast<uint64>(bytes[hihilo]) << 48)
+ | (static_cast<uint64>(bytes[hilohi]) << 40)
+ | (static_cast<uint64>(bytes[hilolo]) << 32)
+ | (static_cast<uint64>(bytes[lohihi]) << 24)
+ | (static_cast<uint64>(bytes[lohilo]) << 16)
+ | (static_cast<uint64>(bytes[lolohi]) << 8)
+ | (static_cast<uint64>(bytes[lololo]) << 0);
+ }
+ MPT_FORCEINLINE bool operator==(const self_t & cmp) const {
+ return true
+ && bytes[0] == cmp.bytes[0]
+ && bytes[1] == cmp.bytes[1]
+ && bytes[2] == cmp.bytes[2]
+ && bytes[3] == cmp.bytes[3]
+ && bytes[4] == cmp.bytes[4]
+ && bytes[5] == cmp.bytes[5]
+ && bytes[6] == cmp.bytes[6]
+ && bytes[7] == cmp.bytes[7];
+ }
+ MPT_FORCEINLINE bool operator!=(const self_t & cmp) const {
+ return !(*this == cmp);
+ }
+};
+
+using IEEE754binary32EmulatedBE = IEEE754binary32Emulated<0, 1, 2, 3>;
+using IEEE754binary32EmulatedLE = IEEE754binary32Emulated<3, 2, 1, 0>;
+using IEEE754binary64EmulatedBE = IEEE754binary64Emulated<0, 1, 2, 3, 4, 5, 6, 7>;
+using IEEE754binary64EmulatedLE = IEEE754binary64Emulated<7, 6, 5, 4, 3, 2, 1, 0>;
+
+constexpr bool declare_binary_safe(const IEEE754binary32EmulatedBE &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const IEEE754binary32EmulatedLE &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const IEEE754binary64EmulatedBE &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const IEEE754binary64EmulatedLE &) {
+ return true;
+}
+
+static_assert(mpt::check_binary_size<IEEE754binary32EmulatedBE>(4));
+static_assert(mpt::check_binary_size<IEEE754binary32EmulatedLE>(4));
+static_assert(mpt::check_binary_size<IEEE754binary64EmulatedBE>(8));
+static_assert(mpt::check_binary_size<IEEE754binary64EmulatedLE>(8));
+
+template <mpt::endian endian = mpt::endian::native>
+struct IEEE754binary32Native {
+public:
+ float32 value;
+
+public:
+ MPT_FORCEINLINE std::byte GetByte(std::size_t i) const {
+ static_assert(endian == mpt::endian::little || endian == mpt::endian::big);
+ if constexpr (endian == mpt::endian::little) {
+ return static_cast<std::byte>(EncodeIEEE754binary32(value) >> (i * 8));
+ }
+ if constexpr (endian == mpt::endian::big) {
+ return static_cast<std::byte>(EncodeIEEE754binary32(value) >> ((4 - 1 - i) * 8));
+ }
+ }
+ IEEE754binary32Native() = default;
+ MPT_FORCEINLINE explicit IEEE754binary32Native(float32 f) {
+ value = f;
+ }
+ // b0...b3 are in memory order, i.e. depend on the endianness of this type
+ // little endian: (0x00,0x00,0x80,0x3f)
+ // big endian: (0x3f,0x80,0x00,0x00)
+ MPT_FORCEINLINE explicit IEEE754binary32Native(std::byte b0, std::byte b1, std::byte b2, std::byte b3) {
+ static_assert(endian == mpt::endian::little || endian == mpt::endian::big);
+ if constexpr (endian == mpt::endian::little) {
+ value = DecodeIEEE754binary32(0u | (static_cast<uint32>(b0) << 0) | (static_cast<uint32>(b1) << 8) | (static_cast<uint32>(b2) << 16) | (static_cast<uint32>(b3) << 24));
+ }
+ if constexpr (endian == mpt::endian::big) {
+ value = DecodeIEEE754binary32(0u | (static_cast<uint32>(b0) << 24) | (static_cast<uint32>(b1) << 16) | (static_cast<uint32>(b2) << 8) | (static_cast<uint32>(b3) << 0));
+ }
+ }
+ MPT_FORCEINLINE operator float32() const {
+ return value;
+ }
+ MPT_FORCEINLINE IEEE754binary32Native & SetInt32(uint32 i) {
+ value = DecodeIEEE754binary32(i);
+ return *this;
+ }
+ MPT_FORCEINLINE uint32 GetInt32() const {
+ return EncodeIEEE754binary32(value);
+ }
+ MPT_FORCEINLINE bool operator==(const IEEE754binary32Native & cmp) const {
+ return value == cmp.value;
+ }
+ MPT_FORCEINLINE bool operator!=(const IEEE754binary32Native & cmp) const {
+ return value != cmp.value;
+ }
+};
+
+template <mpt::endian endian = mpt::endian::native>
+struct IEEE754binary64Native {
+public:
+ float64 value;
+
+public:
+ MPT_FORCEINLINE std::byte GetByte(std::size_t i) const {
+ static_assert(endian == mpt::endian::little || endian == mpt::endian::big);
+ if constexpr (endian == mpt::endian::little) {
+ return mpt::byte_cast<std::byte>(static_cast<uint8>(EncodeIEEE754binary64(value) >> (i * 8)));
+ }
+ if constexpr (endian == mpt::endian::big) {
+ return mpt::byte_cast<std::byte>(static_cast<uint8>(EncodeIEEE754binary64(value) >> ((8 - 1 - i) * 8)));
+ }
+ }
+ IEEE754binary64Native() = default;
+ MPT_FORCEINLINE explicit IEEE754binary64Native(float64 f) {
+ value = f;
+ }
+ MPT_FORCEINLINE explicit IEEE754binary64Native(std::byte b0, std::byte b1, std::byte b2, std::byte b3, std::byte b4, std::byte b5, std::byte b6, std::byte b7) {
+ static_assert(endian == mpt::endian::little || endian == mpt::endian::big);
+ if constexpr (endian == mpt::endian::little) {
+ value = DecodeIEEE754binary64(0ull | (static_cast<uint64>(b0) << 0) | (static_cast<uint64>(b1) << 8) | (static_cast<uint64>(b2) << 16) | (static_cast<uint64>(b3) << 24) | (static_cast<uint64>(b4) << 32) | (static_cast<uint64>(b5) << 40) | (static_cast<uint64>(b6) << 48) | (static_cast<uint64>(b7) << 56));
+ }
+ if constexpr (endian == mpt::endian::big) {
+ value = DecodeIEEE754binary64(0ull | (static_cast<uint64>(b0) << 56) | (static_cast<uint64>(b1) << 48) | (static_cast<uint64>(b2) << 40) | (static_cast<uint64>(b3) << 32) | (static_cast<uint64>(b4) << 24) | (static_cast<uint64>(b5) << 16) | (static_cast<uint64>(b6) << 8) | (static_cast<uint64>(b7) << 0));
+ }
+ }
+ MPT_FORCEINLINE operator float64() const {
+ return value;
+ }
+ MPT_FORCEINLINE IEEE754binary64Native & SetInt64(uint64 i) {
+ value = DecodeIEEE754binary64(i);
+ return *this;
+ }
+ MPT_FORCEINLINE uint64 GetInt64() const {
+ return EncodeIEEE754binary64(value);
+ }
+ MPT_FORCEINLINE bool operator==(const IEEE754binary64Native & cmp) const {
+ return value == cmp.value;
+ }
+ MPT_FORCEINLINE bool operator!=(const IEEE754binary64Native & cmp) const {
+ return value != cmp.value;
+ }
+};
+
+static_assert((sizeof(IEEE754binary32Native<>) == 4));
+static_assert((sizeof(IEEE754binary64Native<>) == 8));
+
+constexpr bool declare_binary_safe(const IEEE754binary32Native<> &) noexcept {
+ return true;
+}
+constexpr bool declare_binary_safe(const IEEE754binary64Native<> &) noexcept {
+ return true;
+}
+
+template <bool is_ieee754, mpt::endian endian = mpt::endian::native>
+struct IEEE754binary_types {
+ using IEEE754binary32LE = IEEE754binary32EmulatedLE;
+ using IEEE754binary32BE = IEEE754binary32EmulatedBE;
+ using IEEE754binary64LE = IEEE754binary64EmulatedLE;
+ using IEEE754binary64BE = IEEE754binary64EmulatedBE;
+};
+template <>
+struct IEEE754binary_types<true, mpt::endian::little> {
+ using IEEE754binary32LE = IEEE754binary32Native<>;
+ using IEEE754binary32BE = IEEE754binary32EmulatedBE;
+ using IEEE754binary64LE = IEEE754binary64Native<>;
+ using IEEE754binary64BE = IEEE754binary64EmulatedBE;
+};
+template <>
+struct IEEE754binary_types<true, mpt::endian::big> {
+ using IEEE754binary32LE = IEEE754binary32EmulatedLE;
+ using IEEE754binary32BE = IEEE754binary32Native<>;
+ using IEEE754binary64LE = IEEE754binary64EmulatedLE;
+ using IEEE754binary64BE = IEEE754binary64Native<>;
+};
+
+using IEEE754binary32LE = IEEE754binary_types<mpt::float_traits<float32>::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32LE;
+using IEEE754binary32BE = IEEE754binary_types<mpt::float_traits<float32>::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32BE;
+using IEEE754binary64LE = IEEE754binary_types<mpt::float_traits<float64>::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64LE;
+using IEEE754binary64BE = IEEE754binary_types<mpt::float_traits<float64>::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64BE;
+
+static_assert(sizeof(IEEE754binary32LE) == 4);
+static_assert(sizeof(IEEE754binary32BE) == 4);
+static_assert(sizeof(IEEE754binary64LE) == 8);
+static_assert(sizeof(IEEE754binary64BE) == 8);
+
+
+// unaligned
+
+using float32le = IEEE754binary32EmulatedLE;
+using float32be = IEEE754binary32EmulatedBE;
+using float64le = IEEE754binary64EmulatedLE;
+using float64be = IEEE754binary64EmulatedBE;
+
+static_assert(sizeof(float32le) == 4);
+static_assert(sizeof(float32be) == 4);
+static_assert(sizeof(float64le) == 8);
+static_assert(sizeof(float64be) == 8);
+
+
+// potentially aligned
+
+using float32le_fast = IEEE754binary32LE;
+using float32be_fast = IEEE754binary32BE;
+using float64le_fast = IEEE754binary64LE;
+using float64be_fast = IEEE754binary64BE;
+
+static_assert(sizeof(float32le_fast) == 4);
+static_assert(sizeof(float32be_fast) == 4);
+static_assert(sizeof(float64le_fast) == 8);
+static_assert(sizeof(float64be_fast) == 8);
+
+
+
+template <typename Tfloat>
+struct make_float_be {
+};
+
+template <>
+struct make_float_be<double> {
+ using type = IEEE754binary64BE;
+};
+
+template <>
+struct make_float_be<float> {
+ using type = IEEE754binary32BE;
+};
+
+template <typename Tfloat>
+struct make_float_le {
+};
+
+template <>
+struct make_float_le<double> {
+ using type = IEEE754binary64LE;
+};
+
+template <>
+struct make_float_le<float> {
+ using type = IEEE754binary32LE;
+};
+
+template <mpt::endian endian, typename T>
+struct make_float_endian {
+};
+
+template <typename T>
+struct make_float_endian<mpt::endian::little, T> {
+ using type = typename make_float_le<typename std::remove_const<T>::type>::type;
+};
+
+template <typename T>
+struct make_float_endian<mpt::endian::big, T> {
+ using type = typename make_float_be<typename std::remove_const<T>::type>::type;
+};
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_ENDIAN_FLOATINGPOINT_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/endian/int24.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/endian/int24.hpp
new file mode 100644
index 00000000..cdab8617
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/endian/int24.hpp
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_ENDIAN_INT24_HPP
+#define MPT_ENDIAN_INT24_HPP
+
+
+
+#include "mpt/base/bit.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <array>
+#include <limits>
+#include <type_traits>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+struct uint24 {
+ std::array<std::byte, 3> bytes;
+ uint24() = default;
+ template <typename T, typename std::enable_if<std::is_integral<T>::value, bool>::type = true>
+ explicit uint24(T other) noexcept {
+ using Tunsigned = typename std::make_unsigned<T>::type;
+ MPT_MAYBE_CONSTANT_IF (mpt::endian_is_big()) {
+ bytes[0] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 16) & 0xff));
+ bytes[1] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 8) & 0xff));
+ bytes[2] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 0) & 0xff));
+ } else {
+ bytes[0] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 0) & 0xff));
+ bytes[1] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 8) & 0xff));
+ bytes[2] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 16) & 0xff));
+ }
+ }
+ operator int() const noexcept {
+ MPT_MAYBE_CONSTANT_IF (mpt::endian_is_big()) {
+ return (mpt::byte_cast<uint8>(bytes[0]) * 65536) + (mpt::byte_cast<uint8>(bytes[1]) * 256) + mpt::byte_cast<uint8>(bytes[2]);
+ } else {
+ return (mpt::byte_cast<uint8>(bytes[2]) * 65536) + (mpt::byte_cast<uint8>(bytes[1]) * 256) + mpt::byte_cast<uint8>(bytes[0]);
+ }
+ }
+};
+
+static_assert(sizeof(uint24) == 3);
+
+
+struct int24 {
+ std::array<std::byte, 3> bytes;
+ int24() = default;
+ template <typename T, typename std::enable_if<std::is_integral<T>::value, bool>::type = true>
+ explicit int24(T other) noexcept {
+ using Tunsigned = typename std::make_unsigned<T>::type;
+ MPT_MAYBE_CONSTANT_IF (mpt::endian_is_big()) {
+ bytes[0] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 16) & 0xff));
+ bytes[1] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 8) & 0xff));
+ bytes[2] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 0) & 0xff));
+ } else {
+ bytes[0] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 0) & 0xff));
+ bytes[1] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 8) & 0xff));
+ bytes[2] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 16) & 0xff));
+ }
+ }
+ operator int() const noexcept {
+ MPT_MAYBE_CONSTANT_IF (mpt::endian_is_big()) {
+ return (static_cast<int8>(mpt::byte_cast<uint8>(bytes[0])) * 65536) + (mpt::byte_cast<uint8>(bytes[1]) * 256) + mpt::byte_cast<uint8>(bytes[2]);
+ } else {
+ return (static_cast<int8>(mpt::byte_cast<uint8>(bytes[2])) * 65536) + (mpt::byte_cast<uint8>(bytes[1]) * 256) + mpt::byte_cast<uint8>(bytes[0]);
+ }
+ }
+};
+
+static_assert(sizeof(int24) == 3);
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#if !defined(CPPCHECK)
+// work-around crash in cppcheck 2.4.1
+namespace std {
+template <>
+class numeric_limits<mpt::uint24> : public std::numeric_limits<mpt::uint32> {
+public:
+ static constexpr mpt::uint32 min() noexcept {
+ return 0;
+ }
+ static constexpr mpt::uint32 lowest() noexcept {
+ return 0;
+ }
+ static constexpr mpt::uint32 max() noexcept {
+ return 0x00ffffff;
+ }
+};
+template <>
+class numeric_limits<mpt::int24> : public std::numeric_limits<mpt::int32> {
+public:
+ static constexpr mpt::int32 min() noexcept {
+ return 0 - 0x00800000;
+ }
+ static constexpr mpt::int32 lowest() noexcept {
+ return 0 - 0x00800000;
+ }
+ static constexpr mpt::int32 max() noexcept {
+ return 0 + 0x007fffff;
+ }
+};
+} // namespace std
+#endif // !CPPCHECK
+
+
+
+#endif // MPT_ENDIAN_INT24_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/endian/integer.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/endian/integer.hpp
new file mode 100644
index 00000000..abc54b73
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/endian/integer.hpp
@@ -0,0 +1,493 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_ENDIAN_INTEGER_HPP
+#define MPT_ENDIAN_INTEGER_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/bit.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <array>
+#include <limits>
+#include <type_traits>
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+
+#if MPT_COMPILER_MSVC
+#include <intrin.h>
+#endif
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+struct BigEndian_tag {
+ static constexpr mpt::endian endian = mpt::endian::big;
+};
+
+struct LittleEndian_tag {
+ static constexpr mpt::endian endian = mpt::endian::little;
+};
+
+
+
+constexpr inline uint16 constexpr_bswap16(uint16 x) noexcept {
+ return uint16(0)
+ | ((x >> 8) & 0x00FFu)
+ | ((x << 8) & 0xFF00u);
+}
+
+constexpr inline uint32 constexpr_bswap32(uint32 x) noexcept {
+ return uint32(0)
+ | ((x & 0x000000FFu) << 24)
+ | ((x & 0x0000FF00u) << 8)
+ | ((x & 0x00FF0000u) >> 8)
+ | ((x & 0xFF000000u) >> 24);
+}
+
+constexpr inline uint64 constexpr_bswap64(uint64 x) noexcept {
+ return uint64(0)
+ | (((x >> 0) & 0xffull) << 56)
+ | (((x >> 8) & 0xffull) << 48)
+ | (((x >> 16) & 0xffull) << 40)
+ | (((x >> 24) & 0xffull) << 32)
+ | (((x >> 32) & 0xffull) << 24)
+ | (((x >> 40) & 0xffull) << 16)
+ | (((x >> 48) & 0xffull) << 8)
+ | (((x >> 56) & 0xffull) << 0);
+}
+
+#if MPT_COMPILER_GCC
+#define MPT_bswap16 __builtin_bswap16
+#define MPT_bswap32 __builtin_bswap32
+#define MPT_bswap64 __builtin_bswap64
+#elif MPT_COMPILER_MSVC
+#define MPT_bswap16 _byteswap_ushort
+#define MPT_bswap32 _byteswap_ulong
+#define MPT_bswap64 _byteswap_uint64
+#endif
+
+// No intrinsics available
+#ifndef MPT_bswap16
+#define MPT_bswap16(x) mpt::constexpr_bswap16(x)
+#endif
+#ifndef MPT_bswap32
+#define MPT_bswap32(x) mpt::constexpr_bswap32(x)
+#endif
+#ifndef MPT_bswap64
+#define MPT_bswap64(x) mpt::constexpr_bswap64(x)
+#endif
+
+
+
+template <typename T, typename Tendian, std::size_t size>
+MPT_CONSTEXPRINLINE std::array<std::byte, size> EndianEncode(T val) noexcept {
+ static_assert(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big);
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(!std::numeric_limits<T>::is_signed);
+ static_assert(sizeof(T) == size);
+ using base_type = T;
+ using unsigned_base_type = typename std::make_unsigned<base_type>::type;
+ using endian_type = Tendian;
+ unsigned_base_type uval = static_cast<unsigned_base_type>(val);
+ std::array<std::byte, size> data{};
+ if constexpr (endian_type::endian == mpt::endian::little) {
+ for (std::size_t i = 0; i < sizeof(base_type); ++i) {
+ data[i] = static_cast<std::byte>(static_cast<uint8>((uval >> (i * 8)) & 0xffu));
+ }
+ } else {
+ for (std::size_t i = 0; i < sizeof(base_type); ++i) {
+ data[(sizeof(base_type) - 1) - i] = static_cast<std::byte>(static_cast<uint8>((uval >> (i * 8)) & 0xffu));
+ }
+ }
+ return data;
+}
+
+template <typename T, typename Tendian, std::size_t size>
+MPT_CONSTEXPRINLINE T EndianDecode(std::array<std::byte, size> data) noexcept {
+ static_assert(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big);
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(!std::numeric_limits<T>::is_signed);
+ static_assert(sizeof(T) == size);
+ using base_type = T;
+ using unsigned_base_type = typename std::make_unsigned<base_type>::type;
+ using endian_type = Tendian;
+ base_type val = base_type();
+ unsigned_base_type uval = unsigned_base_type();
+ if constexpr (endian_type::endian == mpt::endian::little) {
+ for (std::size_t i = 0; i < sizeof(base_type); ++i) {
+ uval |= static_cast<unsigned_base_type>(static_cast<uint8>(data[i])) << (i * 8);
+ }
+ } else {
+ for (std::size_t i = 0; i < sizeof(base_type); ++i) {
+ uval |= static_cast<unsigned_base_type>(static_cast<uint8>(data[(sizeof(base_type) - 1) - i])) << (i * 8);
+ }
+ }
+ val = static_cast<base_type>(uval);
+ return val;
+}
+
+
+MPT_CONSTEXPR20_FUN uint64 SwapBytesImpl(uint64 value) noexcept {
+ MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
+ return mpt::constexpr_bswap64(value);
+ } else {
+ return MPT_bswap64(value);
+ }
+}
+
+MPT_CONSTEXPR20_FUN uint32 SwapBytesImpl(uint32 value) noexcept {
+ MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
+ return mpt::constexpr_bswap32(value);
+ } else {
+ return MPT_bswap32(value);
+ }
+}
+
+MPT_CONSTEXPR20_FUN uint16 SwapBytesImpl(uint16 value) noexcept {
+ MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
+ return mpt::constexpr_bswap16(value);
+ } else {
+ return MPT_bswap16(value);
+ }
+}
+
+MPT_CONSTEXPR20_FUN int64 SwapBytesImpl(int64 value) noexcept {
+ MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
+ return mpt::constexpr_bswap64(value);
+ } else {
+ return MPT_bswap64(value);
+ }
+}
+
+MPT_CONSTEXPR20_FUN int32 SwapBytesImpl(int32 value) noexcept {
+ MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
+ return mpt::constexpr_bswap32(value);
+ } else {
+ return MPT_bswap32(value);
+ }
+}
+
+MPT_CONSTEXPR20_FUN int16 SwapBytesImpl(int16 value) noexcept {
+ MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
+ return mpt::constexpr_bswap16(value);
+ } else {
+ return MPT_bswap16(value);
+ }
+}
+
+// Do NOT remove these overloads, even if they seem useless.
+// We do not want risking to extend 8bit integers to int and then
+// endian-converting and casting back to int.
+// Thus these overloads.
+
+MPT_CONSTEXPR20_FUN uint8 SwapBytesImpl(uint8 value) noexcept {
+ return value;
+}
+
+MPT_CONSTEXPR20_FUN int8 SwapBytesImpl(int8 value) noexcept {
+ return value;
+}
+
+MPT_CONSTEXPR20_FUN char SwapBytesImpl(char value) noexcept {
+ return value;
+}
+
+#undef MPT_bswap16
+#undef MPT_bswap32
+#undef MPT_bswap64
+
+
+
+// On-disk integer types with defined endianness and no alignemnt requirements
+// Note: To easily debug module loaders (and anything else that uses this
+// wrapper struct), you can use the Debugger Visualizers available in
+// build/vs/debug/ to conveniently view the wrapped contents.
+
+template <typename T, typename Tendian>
+struct packed {
+public:
+ using base_type = T;
+ using endian_type = Tendian;
+
+public:
+ std::array<std::byte, sizeof(base_type)> data;
+
+public:
+ MPT_CONSTEXPR20_FUN void set(base_type val) noexcept {
+ static_assert(std::numeric_limits<T>::is_integer);
+ MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
+ if constexpr (endian_type::endian == mpt::endian::big) {
+ typename std::make_unsigned<base_type>::type uval = val;
+ for (std::size_t i = 0; i < sizeof(base_type); ++i) {
+ data[i] = static_cast<std::byte>((uval >> (8 * (sizeof(base_type) - 1 - i))) & 0xffu);
+ }
+ } else {
+ typename std::make_unsigned<base_type>::type uval = val;
+ for (std::size_t i = 0; i < sizeof(base_type); ++i) {
+ data[i] = static_cast<std::byte>((uval >> (8 * i)) & 0xffu);
+ }
+ }
+ } else {
+ if constexpr (mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) {
+ if constexpr (mpt::endian::native != endian_type::endian) {
+ val = mpt::SwapBytesImpl(val);
+ }
+ std::memcpy(data.data(), &val, sizeof(val));
+ } else {
+ using unsigned_base_type = typename std::make_unsigned<base_type>::type;
+ data = EndianEncode<unsigned_base_type, Tendian, sizeof(T)>(val);
+ }
+ }
+ }
+ MPT_CONSTEXPR20_FUN base_type get() const noexcept {
+ static_assert(std::numeric_limits<T>::is_integer);
+ MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
+ if constexpr (endian_type::endian == mpt::endian::big) {
+ typename std::make_unsigned<base_type>::type uval = 0;
+ for (std::size_t i = 0; i < sizeof(base_type); ++i) {
+ uval |= static_cast<typename std::make_unsigned<base_type>::type>(data[i]) << (8 * (sizeof(base_type) - 1 - i));
+ }
+ return static_cast<base_type>(uval);
+ } else {
+ typename std::make_unsigned<base_type>::type uval = 0;
+ for (std::size_t i = 0; i < sizeof(base_type); ++i) {
+ uval |= static_cast<typename std::make_unsigned<base_type>::type>(data[i]) << (8 * i);
+ }
+ return static_cast<base_type>(uval);
+ }
+ } else {
+ if constexpr (mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) {
+ base_type val = base_type();
+ std::memcpy(&val, data.data(), sizeof(val));
+ if constexpr (mpt::endian::native != endian_type::endian) {
+ val = mpt::SwapBytesImpl(val);
+ }
+ return val;
+ } else {
+ using unsigned_base_type = typename std::make_unsigned<base_type>::type;
+ return EndianDecode<unsigned_base_type, Tendian, sizeof(T)>(data);
+ }
+ }
+ }
+ MPT_CONSTEXPR20_FUN packed & operator=(const base_type & val) noexcept {
+ set(val);
+ return *this;
+ }
+ MPT_CONSTEXPR20_FUN operator base_type() const noexcept {
+ return get();
+ }
+
+public:
+ MPT_CONSTEXPR20_FUN packed & operator&=(base_type val) noexcept {
+ set(get() & val);
+ return *this;
+ }
+ MPT_CONSTEXPR20_FUN packed & operator|=(base_type val) noexcept {
+ set(get() | val);
+ return *this;
+ }
+ MPT_CONSTEXPR20_FUN packed & operator^=(base_type val) noexcept {
+ set(get() ^ val);
+ return *this;
+ }
+ MPT_CONSTEXPR20_FUN packed & operator+=(base_type val) noexcept {
+ set(get() + val);
+ return *this;
+ }
+ MPT_CONSTEXPR20_FUN packed & operator-=(base_type val) noexcept {
+ set(get() - val);
+ return *this;
+ }
+ MPT_CONSTEXPR20_FUN packed & operator*=(base_type val) noexcept {
+ set(get() * val);
+ return *this;
+ }
+ MPT_CONSTEXPR20_FUN packed & operator/=(base_type val) noexcept {
+ set(get() / val);
+ return *this;
+ }
+ MPT_CONSTEXPR20_FUN packed & operator%=(base_type val) noexcept {
+ set(get() % val);
+ return *this;
+ }
+ MPT_CONSTEXPR20_FUN packed & operator++() noexcept { // prefix
+ set(get() + 1);
+ return *this;
+ }
+ MPT_CONSTEXPR20_FUN packed & operator--() noexcept { // prefix
+ set(get() - 1);
+ return *this;
+ }
+ MPT_CONSTEXPR20_FUN base_type operator++(int) noexcept { // postfix
+ base_type old = get();
+ set(old + 1);
+ return old;
+ }
+ MPT_CONSTEXPR20_FUN base_type operator--(int) noexcept { // postfix
+ base_type old = get();
+ set(old - 1);
+ return old;
+ }
+};
+
+using int64le = packed<int64, LittleEndian_tag>;
+using int32le = packed<int32, LittleEndian_tag>;
+using int16le = packed<int16, LittleEndian_tag>;
+using int8le = packed<int8, LittleEndian_tag>;
+using uint64le = packed<uint64, LittleEndian_tag>;
+using uint32le = packed<uint32, LittleEndian_tag>;
+using uint16le = packed<uint16, LittleEndian_tag>;
+using uint8le = packed<uint8, LittleEndian_tag>;
+
+using int64be = packed<int64, BigEndian_tag>;
+using int32be = packed<int32, BigEndian_tag>;
+using int16be = packed<int16, BigEndian_tag>;
+using int8be = packed<int8, BigEndian_tag>;
+using uint64be = packed<uint64, BigEndian_tag>;
+using uint32be = packed<uint32, BigEndian_tag>;
+using uint16be = packed<uint16, BigEndian_tag>;
+using uint8be = packed<uint8, BigEndian_tag>;
+
+constexpr bool declare_binary_safe(const int64le &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const int32le &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const int16le &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const int8le &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const uint64le &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const uint32le &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const uint16le &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const uint8le &) {
+ return true;
+}
+
+constexpr bool declare_binary_safe(const int64be &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const int32be &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const int16be &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const int8be &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const uint64be &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const uint32be &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const uint16be &) {
+ return true;
+}
+constexpr bool declare_binary_safe(const uint8be &) {
+ return true;
+}
+
+static_assert(mpt::check_binary_size<int64le>(8));
+static_assert(mpt::check_binary_size<int32le>(4));
+static_assert(mpt::check_binary_size<int16le>(2));
+static_assert(mpt::check_binary_size<int8le>(1));
+static_assert(mpt::check_binary_size<uint64le>(8));
+static_assert(mpt::check_binary_size<uint32le>(4));
+static_assert(mpt::check_binary_size<uint16le>(2));
+static_assert(mpt::check_binary_size<uint8le>(1));
+
+static_assert(mpt::check_binary_size<int64be>(8));
+static_assert(mpt::check_binary_size<int32be>(4));
+static_assert(mpt::check_binary_size<int16be>(2));
+static_assert(mpt::check_binary_size<int8be>(1));
+static_assert(mpt::check_binary_size<uint64be>(8));
+static_assert(mpt::check_binary_size<uint32be>(4));
+static_assert(mpt::check_binary_size<uint16be>(2));
+static_assert(mpt::check_binary_size<uint8be>(1));
+
+
+
+template <typename T>
+struct make_le {
+ using type = packed<typename std::remove_const<T>::type, LittleEndian_tag>;
+};
+
+template <typename T>
+struct make_be {
+ using type = packed<typename std::remove_const<T>::type, BigEndian_tag>;
+};
+
+template <mpt::endian endian, typename T>
+struct make_endian {
+};
+
+template <typename T>
+struct make_endian<mpt::endian::little, T> {
+ using type = packed<typename std::remove_const<T>::type, LittleEndian_tag>;
+};
+
+template <typename T>
+struct make_endian<mpt::endian::big, T> {
+ using type = packed<typename std::remove_const<T>::type, BigEndian_tag>;
+};
+
+template <typename T>
+MPT_CONSTEXPR20_FUN auto as_le(T v) noexcept -> typename mpt::make_le<typename std::remove_const<T>::type>::type {
+ typename mpt::make_le<typename std::remove_const<T>::type>::type res{};
+ res = v;
+ return res;
+}
+
+template <typename T>
+MPT_CONSTEXPR20_FUN auto as_be(T v) noexcept -> typename mpt::make_be<typename std::remove_const<T>::type>::type {
+ typename mpt::make_be<typename std::remove_const<T>::type>::type res{};
+ res = v;
+ return res;
+}
+
+template <typename Tpacked>
+MPT_CONSTEXPR20_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept {
+ Tpacked res{};
+ res = v;
+ return res;
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+namespace std {
+template <typename T, typename Tendian>
+class numeric_limits<mpt::packed<T, Tendian>> : public std::numeric_limits<T> { };
+template <typename T, typename Tendian>
+class numeric_limits<const mpt::packed<T, Tendian>> : public std::numeric_limits<const T> { };
+} // namespace std
+
+
+
+#endif // MPT_ENDIAN_INTEGER_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/endian/tests/tests_endian_floatingpoint.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/endian/tests/tests_endian_floatingpoint.hpp
new file mode 100644
index 00000000..b8a6094b
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/endian/tests/tests_endian_floatingpoint.hpp
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_ENDIAN_TESTS_ENDIAN_FLOATINGPOINT_HPP
+#define MPT_ENDIAN_TESTS_ENDIAN_FLOATINGPOINT_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/endian/floatingpoint.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace endian {
+namespace floatingpoint {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/endian/floatingpoint")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ MPT_TEST_EXPECT_EQUAL(mpt::EncodeIEEE754binary32(1.0f), 0x3f800000u);
+ MPT_TEST_EXPECT_EQUAL(mpt::EncodeIEEE754binary32(-1.0f), 0xbf800000u);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x00000000u), 0.0f);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x41840000u), 16.5f);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x3faa0000u), 1.328125f);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0xbfaa0000u), -1.328125f);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x3f800000u), 1.0f);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x00000000u), 0.0f);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0xbf800000u), -1.0f);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x3f800000u), 1.0f);
+ MPT_TEST_EXPECT_EQUAL(IEEE754binary32LE(1.0f).GetInt32(), 0x3f800000u);
+ MPT_TEST_EXPECT_EQUAL(IEEE754binary32BE(1.0f).GetInt32(), 0x3f800000u);
+ MPT_TEST_EXPECT_EQUAL(IEEE754binary32LE(mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x80), mpt::as_byte(0x3f)), 1.0f);
+ MPT_TEST_EXPECT_EQUAL(IEEE754binary32BE(mpt::as_byte(0x3f), mpt::as_byte(0x80), mpt::as_byte(0x00), mpt::as_byte(0x00)), 1.0f);
+ MPT_TEST_EXPECT_EQUAL(IEEE754binary32LE(1.0f), IEEE754binary32LE(mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x80), mpt::as_byte(0x3f)));
+ MPT_TEST_EXPECT_EQUAL(IEEE754binary32BE(1.0f), IEEE754binary32BE(mpt::as_byte(0x3f), mpt::as_byte(0x80), mpt::as_byte(0x00), mpt::as_byte(0x00)));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::EncodeIEEE754binary64(1.0), 0x3ff0000000000000ull);
+ MPT_TEST_EXPECT_EQUAL(mpt::EncodeIEEE754binary64(-1.0), 0xbff0000000000000ull);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x0000000000000000ull), 0.0);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x4030800000000000ull), 16.5);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x3FF5400000000000ull), 1.328125);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0xBFF5400000000000ull), -1.328125);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x3ff0000000000000ull), 1.0);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x0000000000000000ull), 0.0);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0xbff0000000000000ull), -1.0);
+ MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x3ff0000000000000ull), 1.0);
+ MPT_TEST_EXPECT_EQUAL(IEEE754binary64LE(1.0).GetInt64(), 0x3ff0000000000000ull);
+ MPT_TEST_EXPECT_EQUAL(IEEE754binary64BE(1.0).GetInt64(), 0x3ff0000000000000ull);
+ MPT_TEST_EXPECT_EQUAL(IEEE754binary64LE(mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0xf0), mpt::as_byte(0x3f)), 1.0);
+ MPT_TEST_EXPECT_EQUAL(IEEE754binary64BE(mpt::as_byte(0x3f), mpt::as_byte(0xf0), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00)), 1.0);
+ MPT_TEST_EXPECT_EQUAL(IEEE754binary64LE(1.0), IEEE754binary64LE(mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0xf0), mpt::as_byte(0x3f)));
+ MPT_TEST_EXPECT_EQUAL(IEEE754binary64BE(1.0), IEEE754binary64BE(mpt::as_byte(0x3f), mpt::as_byte(0xf0), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00)));
+}
+
+} // namespace floatingpoint
+} // namespace endian
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_ENDIAN_TESTS_ENDIAN_FLOATINGPOINT_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/endian/tests/tests_endian_integer.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/endian/tests/tests_endian_integer.hpp
new file mode 100644
index 00000000..0511642e
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/endian/tests/tests_endian_integer.hpp
@@ -0,0 +1,133 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_ENDIAN_TESTS_ENDIAN_INTEGER_HPP
+#define MPT_ENDIAN_TESTS_ENDIAN_INTEGER_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/endian/integer.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <limits>
+
+#include <cstring>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace endian {
+namespace integer {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/endian/integer")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ static_assert(std::numeric_limits<int8le>::min() == std::numeric_limits<int8>::min());
+ static_assert(std::numeric_limits<uint8le>::min() == std::numeric_limits<uint8>::min());
+
+ static_assert(std::numeric_limits<int16le>::min() == std::numeric_limits<int16>::min());
+ static_assert(std::numeric_limits<uint16le>::min() == std::numeric_limits<uint16>::min());
+
+ static_assert(std::numeric_limits<int32le>::min() == std::numeric_limits<int32>::min());
+ static_assert(std::numeric_limits<uint32le>::min() == std::numeric_limits<uint32>::min());
+
+ static_assert(std::numeric_limits<int64le>::min() == std::numeric_limits<int64>::min());
+ static_assert(std::numeric_limits<uint64le>::min() == std::numeric_limits<uint64>::min());
+
+ static_assert(std::numeric_limits<int8le>::max() == std::numeric_limits<int8>::max());
+ static_assert(std::numeric_limits<uint8le>::max() == std::numeric_limits<uint8>::max());
+
+ static_assert(std::numeric_limits<int16le>::max() == std::numeric_limits<int16>::max());
+ static_assert(std::numeric_limits<uint16le>::max() == std::numeric_limits<uint16>::max());
+
+ static_assert(std::numeric_limits<int32le>::max() == std::numeric_limits<int32>::max());
+ static_assert(std::numeric_limits<uint32le>::max() == std::numeric_limits<uint32>::max());
+
+ static_assert(std::numeric_limits<int64le>::max() == std::numeric_limits<int64>::max());
+ static_assert(std::numeric_limits<uint64le>::max() == std::numeric_limits<uint64>::max());
+
+ struct test_endian_constexpr {
+ static MPT_CONSTEXPR20_FUN int32le test(uint32 x) {
+ int32le foo{};
+ foo = x;
+ return foo;
+ }
+ };
+
+ MPT_CONSTEXPR20_VAR int32le foo = test_endian_constexpr::test(23);
+ static_cast<void>(foo);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(uint8(0x12)), 0x12);
+ MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(uint16(0x1234)), 0x3412);
+ MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(uint32(0x12345678u)), 0x78563412u);
+ MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(uint64(0x123456789abcdef0ull)), 0xf0debc9a78563412ull);
+
+ MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(int8(std::numeric_limits<int8>::min())), std::numeric_limits<int8>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(int16(std::numeric_limits<int16>::min())), int16(0x80));
+ MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(int32(std::numeric_limits<int32>::min())), int32(0x80));
+ MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(int64(std::numeric_limits<int64>::min())), int64(0x80));
+
+ // Packed integers with defined endianness
+ {
+ int8le le8;
+ le8.set(-128);
+ int8be be8;
+ be8.set(-128);
+ MPT_TEST_EXPECT_EQUAL(le8, -128);
+ MPT_TEST_EXPECT_EQUAL(be8, -128);
+ MPT_TEST_EXPECT_EQUAL(std::memcmp(&le8, "\x80", 1), 0);
+ MPT_TEST_EXPECT_EQUAL(std::memcmp(&be8, "\x80", 1), 0);
+ int16le le16;
+ le16.set(0x1234);
+ int16be be16;
+ be16.set(0x1234);
+ MPT_TEST_EXPECT_EQUAL(le16, 0x1234);
+ MPT_TEST_EXPECT_EQUAL(be16, 0x1234);
+ MPT_TEST_EXPECT_EQUAL(std::memcmp(&le16, "\x34\x12", 2), 0);
+ MPT_TEST_EXPECT_EQUAL(std::memcmp(&be16, "\x12\x34", 2), 0);
+ uint32le le32;
+ le32.set(0xFFEEDDCCu);
+ uint32be be32;
+ be32.set(0xFFEEDDCCu);
+ MPT_TEST_EXPECT_EQUAL(le32, 0xFFEEDDCCu);
+ MPT_TEST_EXPECT_EQUAL(be32, 0xFFEEDDCCu);
+ MPT_TEST_EXPECT_EQUAL(std::memcmp(&le32, "\xCC\xDD\xEE\xFF", 4), 0);
+ MPT_TEST_EXPECT_EQUAL(std::memcmp(&be32, "\xFF\xEE\xDD\xCC", 4), 0);
+ uint64le le64;
+ le64.set(0xDEADC0DE15C0FFEEull);
+ uint64be be64;
+ be64.set(0xDEADC0DE15C0FFEEull);
+ MPT_TEST_EXPECT_EQUAL(le64, 0xDEADC0DE15C0FFEEull);
+ MPT_TEST_EXPECT_EQUAL(be64, 0xDEADC0DE15C0FFEEull);
+ MPT_TEST_EXPECT_EQUAL(std::memcmp(&le64, "\xEE\xFF\xC0\x15\xDE\xC0\xAD\xDE", 8), 0);
+ MPT_TEST_EXPECT_EQUAL(std::memcmp(&be64, "\xDE\xAD\xC0\xDE\x15\xC0\xFF\xEE", 8), 0);
+ }
+}
+
+} // namespace integer
+} // namespace endian
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_ENDIAN_TESTS_ENDIAN_INTEGER_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/environment/environment.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/environment/environment.hpp
new file mode 100644
index 00000000..4fb021b1
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/environment/environment.hpp
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_ENVIRONMENT_ENVIRONMENT_HPP
+#define MPT_ENVIRONMENT_ENVIRONMENT_HPP
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+#include "mpt/system_error/system_error.hpp"
+
+#include <optional>
+#if MPT_OS_WINDOWS
+#if defined(UNICODE) && !MPT_OS_WINDOWS_WINRT
+#include <vector>
+#endif // !MPT_OS_WINDOWS_WINRT
+#endif // MPT_OS_WINDOWS
+
+#include <cstdlib>
+
+#if MPT_OS_WINDOWS
+#include <windows.h>
+#endif // MPT_OS_WINDOWS
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+inline std::optional<mpt::ustring> getenv(const mpt::ustring & env_var) {
+#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
+ MPT_UNUSED(env_var);
+ return std::nullopt;
+#elif MPT_OS_WINDOWS && defined(UNICODE)
+ std::vector<WCHAR> buf(32767);
+ DWORD size = GetEnvironmentVariable(mpt::transcode<std::wstring>(env_var).c_str(), buf.data(), 32767);
+ if (size == 0) {
+ mpt::windows::ExpectError(ERROR_ENVVAR_NOT_FOUND);
+ return std::nullopt;
+ }
+ return mpt::transcode<mpt::ustring>(buf.data());
+#else
+ const char * val = std::getenv(mpt::transcode<std::string>(mpt::environment_encoding, env_var).c_str());
+ if (!val) {
+ return std::nullopt;
+ }
+ return mpt::transcode<mpt::ustring>(mpt::environment_encoding, val);
+#endif
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_ENVIRONMENT_ENVIRONMENT_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/exception_text/exception_text.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/exception_text/exception_text.hpp
new file mode 100644
index 00000000..26d18494
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/exception_text/exception_text.hpp
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_EXCEPTION_TEXT_EXCEPTION_TEXT_HPP
+#define MPT_EXCEPTION_TEXT_EXCEPTION_TEXT_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+
+#include <exception>
+
+#include <cstring>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+template <typename Tstring>
+inline Tstring get_exception_text(const std::exception & e) {
+ if (e.what() && (std::strlen(e.what()) > 0)) {
+ return mpt::transcode<Tstring>(mpt::exception_string{e.what()});
+ } else if (typeid(e).name() && (std::strlen(typeid(e).name()) > 0)) {
+ return mpt::transcode<Tstring>(mpt::source_string{typeid(e).name()});
+ } else {
+ return mpt::transcode<Tstring>(mpt::source_string{"unknown exception name"});
+ }
+}
+
+template <>
+inline std::string get_exception_text<std::string>(const std::exception & e) {
+ if (e.what() && (std::strlen(e.what()) > 0)) {
+ return std::string{e.what()};
+ } else if (typeid(e).name() && (std::strlen(typeid(e).name()) > 0)) {
+ return std::string{typeid(e).name()};
+ } else {
+ return std::string{"unknown exception name"};
+ }
+}
+
+
+template <typename Tstring>
+inline Tstring get_current_exception_text() {
+ try {
+ throw;
+ } catch (const std::exception & e) {
+ return mpt::get_exception_text<Tstring>(e);
+ } catch (...) {
+ return mpt::transcode<Tstring>(mpt::source_string{"unknown exception"});
+ }
+}
+
+template <>
+inline std::string get_current_exception_text<std::string>() {
+ try {
+ throw;
+ } catch (const std::exception & e) {
+ return mpt::get_exception_text<std::string>(e);
+ } catch (...) {
+ return std::string{"unknown exception"};
+ }
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_EXCEPTION_TEXT_EXCEPTION_TEXT_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/default_floatingpoint.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/default_floatingpoint.hpp
new file mode 100644
index 00000000..c308b623
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/default_floatingpoint.hpp
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_DETAULT_FLOATINGPOINT_HPP
+#define MPT_FORMAT_DETAULT_FLOATINGPOINT_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+
+#if !defined(MPT_LIBCXX_QUIRK_NO_TO_CHARS_FLOAT)
+#define MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 1
+#else
+#define MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 0
+#endif
+
+#if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17
+#include "mpt/base/algorithm.hpp"
+#endif
+#include "mpt/base/namespace.hpp"
+#include "mpt/format/helpers.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+
+#if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17
+#include <charconv>
+#endif
+#if !MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17
+#include <iomanip>
+#include <ios>
+#include <limits>
+#include <locale>
+#include <sstream>
+#endif
+#include <string>
+#if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17
+#include <system_error>
+#endif
+#include <type_traits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+#if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17
+template <typename Tstring, typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
+inline Tstring to_chars_string(const T & x) {
+ std::string str(1, '\0');
+ bool done = false;
+ while (!done) {
+ std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x);
+ if (result.ec != std::errc{}) {
+ str.resize(mpt::exponential_grow(str.size()), '\0');
+ } else {
+ str.resize(result.ptr - str.data());
+ done = true;
+ }
+ }
+ return mpt::convert_formatted_simple<Tstring>(str);
+}
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
+inline Tstring format_value_default(const T & x) {
+ return mpt::transcode<Tstring>(mpt::to_chars_string<typename mpt::select_format_string_type<Tstring>::type>(x));
+}
+#endif
+
+
+#if !MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17
+template <typename Tstring, typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
+inline Tstring to_stream_string(const T & x) {
+ using stream_char_type = typename mpt::select_format_char_type<typename Tstring::value_type>::type;
+ std::basic_ostringstream<stream_char_type> s;
+ s.imbue(std::locale::classic());
+ s << std::setprecision(std::numeric_limits<T>::max_digits10) << x;
+ return mpt::convert_formatted_simple<Tstring>(s.str());
+}
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
+inline Tstring format_value_default(const T & x) {
+ return mpt::transcode<Tstring>(mpt::to_stream_string<typename mpt::select_format_string_type<Tstring>::type>(x));
+}
+#endif
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_FORMAT_DETAULT_FLOATINGPOINT_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/default_formatter.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/default_formatter.hpp
new file mode 100644
index 00000000..27946a5a
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/default_formatter.hpp
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_DEFAULT_FORMATTER_HPP
+#define MPT_FORMAT_DEFAULT_FORMATTER_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+#include "mpt/format/default_floatingpoint.hpp"
+#include "mpt/format/default_integer.hpp"
+#include "mpt/format/default_string.hpp"
+
+#include <type_traits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+struct default_formatter {
+ template <typename Tstring, typename T>
+ static inline Tstring format(const T & value) {
+ using namespace mpt;
+ return format_value_default<Tstring>(value);
+ }
+};
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_FORMAT_DEFAULT_FORMATTER_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/default_integer.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/default_integer.hpp
new file mode 100644
index 00000000..fedd2dc0
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/default_integer.hpp
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_DEFAULT_INTEGER_HPP
+#define MPT_FORMAT_DEFAULT_INTEGER_HPP
+
+
+#include "mpt/base/detect.hpp"
+
+#if !defined(MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT)
+#define MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 1
+#else // MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT
+#define MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 0
+#endif // !MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT
+
+#if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
+#include "mpt/base/algorithm.hpp"
+#endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/utility.hpp"
+#include "mpt/format/helpers.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+
+#if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
+#include <charconv>
+#endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
+#if !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
+#include <locale>
+#include <sstream>
+#endif // !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
+#include <string>
+#if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
+#include <system_error>
+#endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
+#include <type_traits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+#if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
+inline Tstring to_chars_string(const T & x) {
+ std::string str(1, '\0');
+ bool done = false;
+ while (!done) {
+ if constexpr (std::is_same<T, bool>::value) {
+ std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), static_cast<int>(x));
+ if (result.ec != std::errc{}) {
+ str.resize(mpt::exponential_grow(str.size()), '\0');
+ } else {
+ str.resize(result.ptr - str.data());
+ done = true;
+ }
+ } else {
+ std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x);
+ if (result.ec != std::errc{}) {
+ str.resize(mpt::exponential_grow(str.size()), '\0');
+ } else {
+ str.resize(result.ptr - str.data());
+ done = true;
+ }
+ }
+ }
+ return mpt::convert_formatted_simple<Tstring>(str);
+}
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
+inline Tstring format_value_default(const T & x) {
+ return mpt::transcode<Tstring>(mpt::to_chars_string<typename mpt::select_format_string_type<Tstring>::type>(x));
+}
+
+#endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
+
+
+#if !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
+inline Tstring to_stream_string(const T & x) {
+ using stream_char_type = typename mpt::select_format_char_type<typename Tstring::value_type>::type;
+ std::basic_ostringstream<stream_char_type> s;
+ s.imbue(std::locale::classic());
+ if constexpr (std::is_same<T, bool>::value) {
+ s << static_cast<int>(x);
+ } else if constexpr (mpt::is_character<T>::value) {
+ s << (x + 0); // force integral promotion
+ } else {
+ s << x;
+ }
+ return mpt::convert_formatted_simple<Tstring>(s.str());
+}
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
+inline Tstring format_value_default(const T & x) {
+ return mpt::transcode<Tstring>(mpt::to_stream_string<typename mpt::select_format_string_type<Tstring>::type>(x));
+}
+
+#endif // !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
+
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_enum<T>::value, bool> = true>
+inline Tstring format_value_default(const T & x) {
+ return mpt::format_value_default<Tstring>(mpt::to_underlying(x));
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_FORMAT_DEFAULT_INTEGER_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/default_string.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/default_string.hpp
new file mode 100644
index 00000000..c8bf7734
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/default_string.hpp
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_DEFAULT_STRING_HPP
+#define MPT_FORMAT_DEFAULT_STRING_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+template <typename Tstring, typename T>
+inline auto format_value_default(const T & x) -> decltype(mpt::transcode<Tstring>(x)) {
+ return mpt::transcode<Tstring>(x);
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_FORMAT_DEFAULT_STRING_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/helpers.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/helpers.hpp
new file mode 100644
index 00000000..a8ff89fd
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/helpers.hpp
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_HELPERS_HPP
+#define MPT_FORMAT_HELPERS_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+
+#include <string>
+#include <type_traits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+template <typename Tdststring, typename Tsrcstring>
+inline Tdststring convert_formatted_simple(const Tsrcstring & src) {
+ if constexpr (std::is_same<Tdststring, Tsrcstring>::value) {
+ return src;
+ } else {
+ Tdststring dst;
+ dst.reserve(src.length());
+ for (std::size_t i = 0; i < src.length(); ++i) {
+ dst.push_back(mpt::unsafe_char_convert<typename Tdststring::value_type>(src[i]));
+ }
+ return dst;
+ }
+}
+
+
+template <typename Tchar>
+struct select_format_char_type {
+ using type = char;
+};
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+template <>
+struct select_format_char_type<wchar_t> {
+ using type = wchar_t;
+};
+#if MPT_USTRING_MODE_WIDE
+#if MPT_CXX_AT_LEAST(20)
+template <>
+struct select_format_char_type<char8_t> {
+ using type = wchar_t;
+};
+#endif // C++20
+template <>
+struct select_format_char_type<char16_t> {
+ using type = wchar_t;
+};
+template <>
+struct select_format_char_type<char32_t> {
+ using type = wchar_t;
+};
+#endif // MPT_USTRING_MODE_WIDE
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
+
+template <typename Tstring>
+struct select_format_string_type {
+ using type = mpt::ustring;
+};
+
+template <>
+struct select_format_string_type<std::string> {
+ using type = std::string;
+};
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+template <>
+struct select_format_string_type<std::wstring> {
+ using type = std::wstring;
+};
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
+#if MPT_CXX_AT_LEAST(20)
+template <>
+struct select_format_string_type<std::u8string> {
+ using type = std::u8string;
+};
+#endif // C++20
+
+template <>
+struct select_format_string_type<std::u16string> {
+ using type = std::u16string;
+};
+
+template <>
+struct select_format_string_type<std::u32string> {
+ using type = std::u32string;
+};
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_FORMAT_HELPERS_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/join.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/join.hpp
new file mode 100644
index 00000000..76e5ef74
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/join.hpp
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_JOIN_HPP
+#define MPT_FORMAT_JOIN_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+#include "mpt/format/simple.hpp"
+#include "mpt/string/utility.hpp"
+
+#include <vector>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+template <typename Tstring, typename T>
+Tstring join_format(const std::vector<T> & vals, const Tstring & sep = Tstring(1, char_constants<typename Tstring::value_type>::comma)) {
+ Tstring str;
+ for (std::size_t i = 0; i < vals.size(); ++i) {
+ if (i > 0) {
+ str += sep;
+ }
+ str += mpt::format<Tstring>::val(vals[i]);
+ }
+ return str;
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_FORMAT_JOIN_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/message.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/message.hpp
new file mode 100644
index 00000000..b4b0e914
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/message.hpp
@@ -0,0 +1,332 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_MESSAGE_HPP
+#define MPT_FORMAT_MESSAGE_HPP
+
+
+
+#include "mpt/base/macros.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/span.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/string/utility.hpp"
+
+#include <array>
+#include <stdexcept>
+#include <utility>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+class format_message_syntax_error
+ : public std::domain_error {
+public:
+ format_message_syntax_error()
+ : std::domain_error("format string syntax error") {
+ return;
+ }
+};
+
+template <typename Tformatter, typename Tformat>
+class message_formatter {
+
+public:
+ using Tstring = typename mpt::make_string_type<Tformat>::type;
+
+private:
+ Tstring format;
+
+private:
+ MPT_NOINLINE Tstring do_format(const mpt::span<const Tstring> vals) const {
+ using traits = typename mpt::string_traits<Tstring>;
+ using char_type = typename traits::char_type;
+ using size_type = typename traits::size_type;
+ Tstring result;
+ const size_type len = traits::length(format);
+ traits::reserve(result, len);
+ std::size_t max_arg = 0;
+ //std::size_t args = 0;
+ bool success = true;
+ enum class state : int {
+ error = -1,
+ text = 0,
+ open_seen = 1,
+ number_seen = 2,
+ close_seen = 3,
+ };
+ state state = state::text;
+ bool numbered_args = false;
+ bool unnumbered_args = false;
+ std::size_t last_arg = 0;
+ std::size_t this_arg = 0;
+ std::size_t current_arg = 0;
+ for (size_type pos = 0; pos != len; ++pos) {
+ char_type c = format[pos];
+ switch (state) {
+ case state::text:
+ if (c == char_type('{')) {
+ state = state::open_seen;
+ } else if (c == char_type('}')) {
+ state = state::close_seen;
+ } else {
+ state = state::text;
+ traits::append(result, 1, c); // output c here
+ }
+ break;
+ case state::open_seen:
+ if (c == char_type('{')) {
+ state = state::text;
+ traits::append(result, 1, char_type('{')); // output { here
+ } else if (c == char_type('}')) {
+ state = state::text;
+ unnumbered_args = true;
+ last_arg++;
+ this_arg = last_arg;
+ { // output this_arg here
+ const std::size_t n = this_arg - 1;
+ if (n < std::size(vals)) {
+ traits::append(result, vals[n]);
+ }
+ }
+ if (this_arg > max_arg) {
+ max_arg = this_arg;
+ }
+ //args += 1;
+ } else if (char_type('0') <= c && c <= char_type('9')) {
+ state = state::number_seen;
+ numbered_args = true;
+ current_arg = c - char_type('0');
+ } else {
+ state = state::error;
+ }
+ break;
+ case state::number_seen:
+ if (c == char_type('{')) {
+ state = state::error;
+ } else if (c == char_type('}')) {
+ state = state::text;
+ this_arg = current_arg + 1;
+ { // output this_arg here
+ const std::size_t n = this_arg - 1;
+ if (n < std::size(vals)) {
+ traits::append(result, vals[n]);
+ }
+ }
+ if (this_arg > max_arg) {
+ max_arg = this_arg;
+ }
+ //args += 1;
+ } else if (char_type('0') <= c && c <= char_type('9')) {
+ state = state::number_seen;
+ numbered_args = true;
+ current_arg = (current_arg * 10) + (c - char_type('0'));
+ } else {
+ state = state::error;
+ }
+ break;
+ case state::close_seen:
+ if (c == char_type('{')) {
+ state = state::error;
+ } else if (c == char_type('}')) {
+ state = state::text;
+ traits::append(result, 1, char_type('}')); // output } here
+ } else {
+ state = state::error;
+ }
+ break;
+ case state::error:
+ state = state::error;
+ break;
+ }
+ }
+ if (state == state::error) {
+ success = false;
+ }
+ if (state != state::text) {
+ success = false;
+ }
+ if (numbered_args && unnumbered_args) {
+ success = false;
+ }
+ if (!success) {
+ throw format_message_syntax_error();
+ }
+ return result;
+ }
+
+public:
+ MPT_FORCEINLINE message_formatter(Tstring format_)
+ : format(std::move(format_)) {
+ }
+
+public:
+ template <typename... Ts>
+ MPT_NOINLINE Tstring operator()(Ts &&... xs) const {
+ const std::array<Tstring, sizeof...(xs)> vals{{Tformatter::template format<Tstring>(std::forward<Ts>(xs))...}};
+ return do_format(mpt::as_span(vals));
+ }
+
+}; // struct message_formatter<Tformat>
+
+
+template <typename Tformatter, std::ptrdiff_t N, typename Tchar, typename Tstring>
+class message_formatter_counted {
+
+private:
+ message_formatter<Tformatter, Tstring> formatter;
+
+public:
+ template <std::size_t literal_length>
+ inline message_formatter_counted(const Tchar (&format)[literal_length])
+ : formatter(Tstring(format)) {
+ return;
+ }
+
+public:
+ template <typename... Ts>
+ inline Tstring operator()(Ts &&... xs) const {
+ static_assert(static_cast<std::ptrdiff_t>(sizeof...(xs)) == N);
+ return formatter(std::forward<Ts>(xs)...);
+ }
+
+}; // struct message_formatter_counted<Tformat>
+
+
+template <typename Tchar>
+MPT_CONSTEXPRINLINE std::ptrdiff_t parse_format_string_argument_count_impl(const Tchar * const format, const std::size_t len) {
+ std::size_t max_arg = 0;
+ std::size_t args = 0;
+ bool success = true;
+ enum class state : int {
+ error = -1,
+ text = 0,
+ open_seen = 1,
+ number_seen = 2,
+ close_seen = 3,
+ };
+ state state = state::text;
+ bool numbered_args = false;
+ bool unnumbered_args = false;
+ std::size_t last_arg = 0;
+ std::size_t this_arg = 0;
+ std::size_t current_arg = 0;
+ for (std::size_t pos = 0; pos != len; ++pos) {
+ Tchar c = format[pos];
+ switch (state) {
+ case state::text:
+ if (c == Tchar('{')) {
+ state = state::open_seen;
+ } else if (c == Tchar('}')) {
+ state = state::close_seen;
+ } else {
+ state = state::text;
+ // output c here
+ }
+ break;
+ case state::open_seen:
+ if (c == Tchar('{')) {
+ state = state::text;
+ // output { here
+ } else if (c == Tchar('}')) {
+ state = state::text;
+ unnumbered_args = true;
+ last_arg++;
+ this_arg = last_arg;
+ // output this_arg here
+ if (this_arg > max_arg)
+ {
+ max_arg = this_arg;
+ }
+ args += 1;
+ } else if (Tchar('0') <= c && c <= Tchar('9')) {
+ state = state::number_seen;
+ numbered_args = true;
+ current_arg = c - Tchar('0');
+ } else {
+ state = state::error;
+ }
+ break;
+ case state::number_seen:
+ if (c == Tchar('{')) {
+ state = state::error;
+ } else if (c == Tchar('}')) {
+ state = state::text;
+ this_arg = current_arg + 1;
+ // output this_arg here
+ if (this_arg > max_arg) {
+ max_arg = this_arg;
+ }
+ args += 1;
+ } else if (Tchar('0') <= c && c <= Tchar('9')) {
+ state = state::number_seen;
+ numbered_args = true;
+ current_arg = (current_arg * 10) + (c - Tchar('0'));
+ } else {
+ state = state::error;
+ }
+ break;
+ case state::close_seen:
+ if (c == Tchar('{')) {
+ state = state::error;
+ } else if (c == Tchar('}')) {
+ state = state::text;
+ // output } here
+ } else {
+ state = state::error;
+ }
+ break;
+ case state::error:
+ state = state::error;
+ break;
+ }
+ }
+ if (state == state::error) {
+ success = false;
+ }
+ if (state != state::text) {
+ success = false;
+ }
+ if (numbered_args && unnumbered_args) {
+ success = false;
+ }
+ if (!success) {
+ throw format_message_syntax_error();
+ }
+ if (max_arg != args) {
+ throw format_message_syntax_error();
+ }
+ return max_arg;
+}
+
+
+template <typename Tchar, std::size_t literal_length>
+MPT_CONSTEXPRINLINE std::ptrdiff_t parse_format_string_argument_count(const Tchar (&format)[literal_length]) {
+ return parse_format_string_argument_count_impl(format, literal_length - 1);
+}
+
+
+template <typename Tformatter, std::size_t args, typename Tchar, std::size_t N>
+inline auto format_message(const Tchar (&format)[N]) {
+ using Tstring = typename mpt::make_string_type<const Tchar *>::type;
+ return message_formatter_counted<Tformatter, args, Tchar, Tstring>(format);
+}
+
+template <typename Tformatter, std::size_t args, typename Tstring, typename Tchar, std::size_t N>
+inline auto format_message_typed(const Tchar (&format)[N]) {
+ return message_formatter_counted<Tformatter, args, Tchar, Tstring>(format);
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_FORMAT_MESSAGE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/message_macros.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/message_macros.hpp
new file mode 100644
index 00000000..c4d9bdb5
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/message_macros.hpp
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_MESSAGE_MACROS_HPP
+#define MPT_FORMAT_MESSAGE_MACROS_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/detect/mfc.hpp"
+#include "mpt/format/default_formatter.hpp"
+#include "mpt/format/message.hpp"
+
+
+
+#define MPT_AFORMAT_MESSAGE(f) mpt::format_message<mpt::default_formatter, mpt::parse_format_string_argument_count(f)>(f)
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+#define MPT_WFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(L##f), std::wstring>(L##f)
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
+#define MPT_UFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(MPT_ULITERAL(f)), mpt::ustring>(MPT_ULITERAL(f))
+
+#define MPT_LFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(f), mpt::lstring>(f)
+
+#if MPT_OS_WINDOWS
+#define MPT_TFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(TEXT(f)), mpt::tstring>(TEXT(f))
+#endif // MPT_OS_WINDOWS
+
+#if MPT_DETECTED_MFC
+#define MPT_CWFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(L##f), CStringW>(L##f)
+#define MPT_CAFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(f), CStringA>(f)
+#define MPT_CFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(TEXT(f)), CString>(TEXT(f))
+#endif // MPT_DETECTED_MFC
+
+
+
+#endif // MPT_FORMAT_MESSAGE_MACROS_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/simple.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/simple.hpp
new file mode 100644
index 00000000..2ca40dce
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/simple.hpp
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_SIMPLE_HPP
+#define MPT_FORMAT_SIMPLE_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/pointer.hpp"
+#include "mpt/format/default_formatter.hpp"
+#include "mpt/format/simple_floatingpoint.hpp"
+#include "mpt/format/simple_integer.hpp"
+#include "mpt/format/simple_spec.hpp"
+#include "mpt/string/utility.hpp"
+
+#include <type_traits>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+template <typename Tstring>
+struct format : format_simple_base {
+
+ template <typename T>
+ static inline Tstring val(const T & x) {
+ return mpt::default_formatter::format<Tstring>(x);
+ }
+
+ template <typename T>
+ static inline Tstring fmt(const T & x, const format_simple_spec & f) {
+ return mpt::format_simple<Tstring>(x, f);
+ }
+
+ template <typename T>
+ static inline Tstring dec(const T & x) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().BaseDec().FillOff());
+ }
+ template <int width, typename T>
+ static inline Tstring dec0(const T & x) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().BaseDec().FillNul().Width(width));
+ }
+
+ template <typename T>
+ static inline Tstring dec(unsigned int g, char s, const T & x) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().BaseDec().FillOff().Group(g).GroupSep(s));
+ }
+ template <int width, typename T>
+ static inline Tstring dec0(unsigned int g, char s, const T & x) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().BaseDec().FillNul().Width(width).Group(g).GroupSep(s));
+ }
+
+ template <typename T>
+ static inline Tstring hex(const T & x) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseLow().FillOff());
+ }
+ template <typename T>
+ static inline Tstring HEX(const T & x) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseUpp().FillOff());
+ }
+ template <int width, typename T>
+ static inline Tstring hex0(const T & x) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseLow().FillNul().Width(width));
+ }
+ template <int width, typename T>
+ static inline Tstring HEX0(const T & x) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseUpp().FillNul().Width(width));
+ }
+
+ template <typename T>
+ static inline Tstring hex(unsigned int g, char s, const T & x) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseLow().FillOff().Group(g).GroupSep(s));
+ }
+ template <typename T>
+ static inline Tstring HEX(unsigned int g, char s, const T & x) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseUpp().FillOff().Group(g).GroupSep(s));
+ }
+ template <int width, typename T>
+ static inline Tstring hex0(unsigned int g, char s, const T & x) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseLow().FillNul().Width(width).Group(g).GroupSep(s));
+ }
+ template <int width, typename T>
+ static inline Tstring HEX0(unsigned int g, char s, const T & x) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseUpp().FillNul().Width(width).Group(g).GroupSep(s));
+ }
+
+ template <typename T>
+ static inline Tstring flt(const T & x, int precision = -1) {
+ static_assert(std::is_floating_point<T>::value);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().NotaNrm().FillOff().Precision(precision));
+ }
+ template <typename T>
+ static inline Tstring fix(const T & x, int precision = -1) {
+ static_assert(std::is_floating_point<T>::value);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().NotaFix().FillOff().Precision(precision));
+ }
+ template <typename T>
+ static inline Tstring sci(const T & x, int precision = -1) {
+ static_assert(std::is_floating_point<T>::value);
+ return mpt::format_simple<Tstring>(x, format_simple_spec().NotaSci().FillOff().Precision(precision));
+ }
+
+ template <typename T>
+ static inline Tstring ptr(const T & x) {
+ static_assert(std::is_pointer<T>::value || std::is_same<T, std::uintptr_t>::value || std::is_same<T, std::intptr_t>::value, "");
+ return hex0<mpt::pointer_size * 2>(mpt::pointer_cast<const std::uintptr_t>(x));
+ }
+ template <typename T>
+ static inline Tstring PTR(const T & x) {
+ static_assert(std::is_pointer<T>::value || std::is_same<T, std::uintptr_t>::value || std::is_same<T, std::intptr_t>::value, "");
+ return HEX0<mpt::pointer_size * 2>(mpt::pointer_cast<const std::uintptr_t>(x));
+ }
+
+ static inline Tstring pad_left(std::size_t width_, const Tstring & str) {
+ typedef mpt::string_traits<Tstring> traits;
+ typename traits::size_type width = static_cast<typename traits::size_type>(width_);
+ return traits::pad(str, width, 0);
+ }
+ static inline Tstring pad_right(std::size_t width_, const Tstring & str) {
+ typedef mpt::string_traits<Tstring> traits;
+ typename traits::size_type width = static_cast<typename traits::size_type>(width_);
+ return traits::pad(str, 0, width);
+ }
+ static inline Tstring left(std::size_t width_, const Tstring & str) {
+ typedef mpt::string_traits<Tstring> traits;
+ typename traits::size_type width = static_cast<typename traits::size_type>(width_);
+ return (traits::length(str) < width) ? traits::pad(str, 0, width - traits::length(str)) : str;
+ }
+ static inline Tstring right(std::size_t width_, const Tstring & str) {
+ typedef mpt::string_traits<Tstring> traits;
+ typename traits::size_type width = static_cast<typename traits::size_type>(width_);
+ return (traits::length(str) < width) ? traits::pad(str, width - traits::length(str), 0) : str;
+ }
+ static inline Tstring center(std::size_t width_, const Tstring & str) {
+ typedef mpt::string_traits<Tstring> traits;
+ typename traits::size_type width = static_cast<typename traits::size_type>(width_);
+ return (traits::length(str) < width) ? traits::pad(str, (width - traits::length(str)) / 2, (width - traits::length(str) + 1) / 2) : str;
+ }
+
+}; // struct format
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_FORMAT_SIMPLE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/simple_floatingpoint.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/simple_floatingpoint.hpp
new file mode 100644
index 00000000..d1ccb32b
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/simple_floatingpoint.hpp
@@ -0,0 +1,250 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_SIMPLE_FLOATINGPOINT_HPP
+#define MPT_FORMAT_SIMPLE_FLOATINGPOINT_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+
+#if !defined(MPT_LIBCXX_QUIRK_NO_TO_CHARS_FLOAT)
+#define MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 1
+#else
+#define MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 0
+#endif
+
+#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
+#include "mpt/base/algorithm.hpp"
+#endif
+#include "mpt/base/namespace.hpp"
+#include "mpt/format/default_floatingpoint.hpp"
+#include "mpt/format/helpers.hpp"
+#include "mpt/format/simple_spec.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+
+#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
+#include <charconv>
+#endif
+#if !MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
+#include <iomanip>
+#include <ios>
+#endif
+#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
+#include <iterator>
+#endif
+#if !MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
+#include <limits>
+#include <locale>
+#include <sstream>
+#endif
+#include <string>
+#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
+#include <system_error>
+#endif
+#include <type_traits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
+
+
+template <typename Tstring, typename T>
+inline Tstring format_simple_floatingpoint_to_chars(const T & x, std::chars_format fmt) {
+ std::string str(1, '\0');
+ bool done = false;
+ while (!done) {
+ std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x, fmt);
+ if (result.ec != std::errc{}) {
+ str.resize(mpt::exponential_grow(str.size()), '\0');
+ } else {
+ str.resize(result.ptr - str.data());
+ done = true;
+ }
+ }
+ return mpt::convert_formatted_simple<Tstring>(str);
+}
+
+
+template <typename Tstring, typename T>
+inline Tstring format_simple_floatingpoint_to_chars(const T & x, std::chars_format fmt, int precision) {
+ std::string str(1, '\0');
+ bool done = false;
+ while (!done) {
+ std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x, fmt, precision);
+ if (result.ec != std::errc{}) {
+ str.resize(mpt::exponential_grow(str.size()), '\0');
+ } else {
+ str.resize(result.ptr - str.data());
+ done = true;
+ }
+ }
+ return mpt::convert_formatted_simple<Tstring>(str);
+}
+
+template <typename Tstring>
+inline Tstring format_simple_floatingpoint_postprocess_width(Tstring str, const format_simple_spec & format) {
+ format_simple_flags f = format.GetFlags();
+ std::size_t width = format.GetWidth();
+ if (f & format_simple_base::FillNul) {
+ auto pos = str.begin();
+ if (str.length() > 0) {
+ if (str[0] == mpt::unsafe_char_convert<typename Tstring::value_type>('+')) {
+ pos++;
+ width++;
+ } else if (str[0] == mpt::unsafe_char_convert<typename Tstring::value_type>('-')) {
+ pos++;
+ width++;
+ }
+ }
+ if (str.length() - std::distance(str.begin(), pos) < width) {
+ str.insert(pos, width - str.length() - std::distance(str.begin(), pos), '0');
+ }
+ } else {
+ if (str.length() < width) {
+ str.insert(0, width - str.length(), ' ');
+ }
+ }
+ return str;
+}
+
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
+inline Tstring format_simple(const T & x, const format_simple_spec & f) {
+ using format_string_type = typename mpt::select_format_string_type<Tstring>::type;
+ if (f.GetPrecision() != -1) {
+ if (f.GetFlags() & format_simple_base::NotaSci) {
+ return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars<format_string_type>(x, std::chars_format::scientific, f.GetPrecision()), f));
+ } else if (f.GetFlags() & format_simple_base::NotaFix) {
+ return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars<format_string_type>(x, std::chars_format::fixed, f.GetPrecision()), f));
+ } else {
+ return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars<format_string_type>(x, std::chars_format::general, f.GetPrecision()), f));
+ }
+ } else {
+ if (f.GetFlags() & format_simple_base::NotaSci) {
+ return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars<format_string_type>(x, std::chars_format::scientific), f));
+ } else if (f.GetFlags() & format_simple_base::NotaFix) {
+ return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars<format_string_type>(x, std::chars_format::fixed), f));
+ } else {
+ return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars<format_string_type>(x, std::chars_format::general), f));
+ }
+ }
+}
+
+
+#else // !MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
+
+
+template <typename Tchar>
+struct NumPunct : std::numpunct<Tchar> {
+private:
+ unsigned int group;
+ char sep;
+
+public:
+ NumPunct(unsigned int g, char s)
+ : group(g)
+ , sep(s) { }
+ std::string do_grouping() const override {
+ return std::string(1, static_cast<char>(group));
+ }
+ Tchar do_thousands_sep() const override {
+ return static_cast<Tchar>(sep);
+ }
+};
+
+template <typename Tostream, typename T>
+inline void format_simple_floatingpoint_apply_stream_format(Tostream & o, const format_simple_spec & format, const T &) {
+ if (format.GetGroup() > 0)
+ {
+ o.imbue(std::locale(o.getloc(), new NumPunct<typename Tostream::char_type>(format.GetGroup(), format.GetGroupSep())));
+ }
+ format_simple_flags f = format.GetFlags();
+ std::size_t width = format.GetWidth();
+ int precision = format.GetPrecision();
+ if (precision != -1 && width != 0 && !(f & format_simple_base::NotaFix) && !(f & format_simple_base::NotaSci))
+ {
+ // fixup:
+ // precision behaves differently from .#
+ // avoid default format when precision and width are set
+ f &= ~format_simple_base::NotaNrm;
+ f |= format_simple_base::NotaFix;
+ }
+ if (f & format_simple_base::BaseDec) {
+ o << std::dec;
+ } else if (f & format_simple_base::BaseHex) {
+ o << std::hex;
+ }
+ if (f & format_simple_base::NotaNrm) { /*nothing*/
+ } else if (f & format_simple_base::NotaFix) {
+ o << std::setiosflags(std::ios::fixed);
+ } else if (f & format_simple_base::NotaSci) {
+ o << std::setiosflags(std::ios::scientific);
+ }
+ if (f & format_simple_base::CaseLow) {
+ o << std::nouppercase;
+ } else if (f & format_simple_base::CaseUpp) {
+ o << std::uppercase;
+ }
+ if (f & format_simple_base::FillOff) { /* nothing */
+ } else if (f & format_simple_base::FillNul) {
+ o << std::setw(width) << std::setfill(typename Tostream::char_type('0'));
+ }
+ if (precision != -1)
+ {
+ o << std::setprecision(precision);
+ } else
+ {
+ if constexpr (std::is_floating_point<T>::value)
+ {
+ if (f & format_simple_base::NotaNrm)
+ {
+ o << std::setprecision(std::numeric_limits<T>::max_digits10);
+ } else if (f & format_simple_base::NotaFix)
+ {
+ o << std::setprecision(std::numeric_limits<T>::digits10);
+ } else if (f & format_simple_base::NotaSci)
+ {
+ o << std::setprecision(std::numeric_limits<T>::max_digits10 - 1);
+ } else
+ {
+ o << std::setprecision(std::numeric_limits<T>::max_digits10);
+ }
+ }
+ }
+}
+
+
+template <typename Tstring, typename T>
+inline Tstring format_simple_floatingpoint_stream(const T & x, const format_simple_spec & f) {
+ using stream_char_type = typename mpt::select_format_char_type<typename Tstring::value_type>::type;
+ std::basic_ostringstream<stream_char_type> s;
+ s.imbue(std::locale::classic());
+ mpt::format_simple_floatingpoint_apply_stream_format(s, f, x);
+ s << x;
+ return mpt::convert_formatted_simple<Tstring>(s.str());
+}
+
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
+inline Tstring format_simple(const T & x, const format_simple_spec & format) {
+ return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_stream<typename mpt::select_format_string_type<Tstring>::type>(x, format));
+}
+
+
+
+#endif // MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_FORMAT_SIMPLE_FLOATINGPOINT_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/simple_integer.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/simple_integer.hpp
new file mode 100644
index 00000000..addecf8f
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/simple_integer.hpp
@@ -0,0 +1,251 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_SIMPLE_INTEGER_HPP
+#define MPT_FORMAT_SIMPLE_INTEGER_HPP
+
+
+#include "mpt/base/detect.hpp"
+
+#if !defined(MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT)
+#define MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 1
+#else // MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT
+#define MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 0
+#endif // !MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT
+
+#include "mpt/base/algorithm.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/format/helpers.hpp"
+#include "mpt/format/simple_spec.hpp"
+#include "mpt/string/types.hpp"
+
+#if !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+#include <algorithm>
+#endif // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+#if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+#include <charconv>
+#endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+#if !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+#include <ios>
+#include <locale>
+#endif // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+#if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+#include <string>
+#endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+#if !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+#include <sstream>
+#endif // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+#if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+#include <system_error>
+#endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+#include <type_traits>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+#if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
+inline Tstring format_simple_integer_to_chars(const T & x, int base) {
+ std::string str(1, '\0');
+ bool done = false;
+ while (!done) {
+ if constexpr (std::is_same<T, bool>::value) {
+ std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), static_cast<int>(x), base);
+ if (result.ec != std::errc{}) {
+ str.resize(mpt::exponential_grow(str.size()), '\0');
+ } else {
+ str.resize(result.ptr - str.data());
+ done = true;
+ }
+ } else {
+ std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x, base);
+ if (result.ec != std::errc{}) {
+ str.resize(mpt::exponential_grow(str.size()), '\0');
+ } else {
+ str.resize(result.ptr - str.data());
+ done = true;
+ }
+ }
+ }
+ return mpt::convert_formatted_simple<Tstring>(str);
+}
+
+#else // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
+inline Tstring format_simple_integer_to_stream(const T & x, int base) {
+ using stream_char_type = typename mpt::select_format_char_type<typename Tstring::value_type>::type;
+ if ((base == 10) || ((base == 16) && std::is_unsigned<T>::value) || ((base == 8) && std::is_unsigned<T>::value)) {
+ // fast path
+ std::basic_ostringstream<stream_char_type> s;
+ s.imbue(std::locale::classic());
+ if (base == 16) {
+ s << std::hex;
+ } else if (base == 8) {
+ s << std::oct;
+ }
+ if constexpr (std::is_same<T, bool>::value) {
+ s << static_cast<int>(x);
+ } else if constexpr (mpt::is_character<T>::value) {
+ s << (x + 0); // force integral promotion
+ } else {
+ s << x;
+ }
+ return mpt::convert_formatted_simple<Tstring>(s.str());
+ } else {
+ if constexpr (std::is_same<T, bool>::value) {
+ return x ? Tstring(1, mpt::unsafe_char_convert<typename Tstring::value_type>('1')) : Tstring(1, mpt::unsafe_char_convert<typename Tstring::value_type>('0'));
+ } else if constexpr (std::is_unsigned<T>::value) {
+ Tstring result;
+ T val = x;
+ if (val == 0) {
+ result += Tstring(1, mpt::unsafe_char_convert<typename Tstring::value_type>('0'));
+ } else {
+ using Tunsigned = typename std::make_unsigned<T>::type;
+ while (val > 0) {
+ Tunsigned digit = static_cast<Tunsigned>(val % static_cast<unsigned int>(base));
+ val = static_cast<Tunsigned>(val / static_cast<unsigned int>(base));
+ if (digit >= 10) {
+ result += Tstring(1, static_cast<typename Tstring::value_type>(mpt::unsafe_char_convert<typename Tstring::value_type>('a') - 10 + digit));
+ } else {
+ result += Tstring(1, static_cast<typename Tstring::value_type>(mpt::unsafe_char_convert<typename Tstring::value_type>('0') + digit));
+ }
+ }
+ std::reverse(result.begin(), result.end());
+ }
+ return result;
+ } else {
+ Tstring result;
+ if (x == 0) {
+ result += Tstring(1, mpt::unsafe_char_convert<typename Tstring::value_type>('0'));
+ } else {
+ using Tunsigned = typename std::make_unsigned<T>::type;
+ Tunsigned val = (x != -x) ? ((x >= 0) ? x : -x) : (static_cast<Tunsigned>(-(x + 1)) + 1);
+ while (val > 0) {
+ Tunsigned digit = static_cast<Tunsigned>(val % static_cast<unsigned int>(base));
+ val = static_cast<Tunsigned>(val / static_cast<unsigned int>(base));
+ if (digit >= 10) {
+ result += Tstring(1, static_cast<typename Tstring::value_type>(mpt::unsafe_char_convert<typename Tstring::value_type>('a') - 10 + digit));
+ } else {
+ result += Tstring(1, static_cast<typename Tstring::value_type>(mpt::unsafe_char_convert<typename Tstring::value_type>('0') + digit));
+ }
+ }
+ if (x < 0) {
+ result += Tstring(1, mpt::unsafe_char_convert<typename Tstring::value_type>('-'));
+ }
+ std::reverse(result.begin(), result.end());
+ }
+ return result;
+ }
+ }
+}
+
+#endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+
+
+template <typename Tstring>
+inline Tstring format_simple_integer_postprocess_case(Tstring str, const format_simple_spec & format) {
+ format_simple_flags f = format.GetFlags();
+ if (f & format_simple_base::CaseUpp) {
+ for (auto & c : str) {
+ if (mpt::unsafe_char_convert<typename Tstring::value_type>('a') <= c && c <= mpt::unsafe_char_convert<typename Tstring::value_type>('z')) {
+ c -= mpt::unsafe_char_convert<typename Tstring::value_type>('a') - mpt::unsafe_char_convert<typename Tstring::value_type>('A');
+ }
+ }
+ }
+ return str;
+}
+
+
+template <typename Tstring>
+inline Tstring format_simple_integer_postprocess_digits(Tstring str, const format_simple_spec & format) {
+ format_simple_flags f = format.GetFlags();
+ std::size_t width = format.GetWidth();
+ if (f & format_simple_base::FillNul) {
+ auto pos = str.begin();
+ if (str.length() > 0) {
+ if (str[0] == mpt::unsafe_char_convert<typename Tstring::value_type>('+')) {
+ pos++;
+ width++;
+ } else if (str[0] == mpt::unsafe_char_convert<typename Tstring::value_type>('-')) {
+ pos++;
+ width++;
+ }
+ }
+ if (str.length() < width) {
+ str.insert(pos, width - str.length(), mpt::unsafe_char_convert<typename Tstring::value_type>('0'));
+ }
+ }
+ return str;
+}
+
+
+#if MPT_COMPILER_MSVC
+#pragma warning(push)
+#pragma warning(disable : 4723) // potential divide by 0
+#endif // MPT_COMPILER_MSVC
+template <typename Tstring>
+inline Tstring format_simple_integer_postprocess_group(Tstring str, const format_simple_spec & format) {
+ if (format.GetGroup() > 0) {
+ const unsigned int groupSize = format.GetGroup();
+ const char groupSep = format.GetGroupSep();
+ std::size_t len = str.length();
+ for (std::size_t n = 0; n < len; ++n) {
+ if (n > 0 && (n % groupSize) == 0) {
+ if (!(n == (len - 1) && (str[0] == mpt::unsafe_char_convert<typename Tstring::value_type>('+') || str[0] == mpt::unsafe_char_convert<typename Tstring::value_type>('-')))) {
+ str.insert(str.begin() + (len - n), 1, mpt::unsafe_char_convert<typename Tstring::value_type>(groupSep));
+ }
+ }
+ }
+ }
+ return str;
+}
+#if MPT_COMPILER_MSVC
+#pragma warning(pop)
+#endif // MPT_COMPILER_MSVC
+
+
+#if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
+inline Tstring format_simple(const T & x, const format_simple_spec & format) {
+ int base = 10;
+ if (format.GetFlags() & format_simple_base::BaseDec) {
+ base = 10;
+ }
+ if (format.GetFlags() & format_simple_base::BaseHex) {
+ base = 16;
+ }
+ using format_string_type = typename mpt::select_format_string_type<Tstring>::type;
+ return mpt::transcode<Tstring>(mpt::format_simple_integer_postprocess_group(mpt::format_simple_integer_postprocess_digits(mpt::format_simple_integer_postprocess_case(mpt::format_simple_integer_to_chars<format_string_type>(x, base), format), format), format));
+}
+
+#else // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
+inline Tstring format_simple(const T & x, const format_simple_spec & format) {
+ int base = 10;
+ if (format.GetFlags() & format_simple_base::BaseDec) {
+ base = 10;
+ }
+ if (format.GetFlags() & format_simple_base::BaseHex) {
+ base = 16;
+ }
+ using format_string_type = typename mpt::select_format_string_type<Tstring>::type;
+ return mpt::transcode<Tstring>(mpt::format_simple_integer_postprocess_group(mpt::format_simple_integer_postprocess_digits(mpt::format_simple_integer_postprocess_case(mpt::format_simple_integer_to_stream<format_string_type>(x, base), format), format), format));
+}
+
+#endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_FORMAT_SIMPLE_INTEGER_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/simple_spec.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/simple_spec.hpp
new file mode 100644
index 00000000..797a6199
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/simple_spec.hpp
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_SIMPLE_SPEC_HPP
+#define MPT_FORMAT_SIMPLE_SPEC_HPP
+
+
+
+#include "mpt/base/macros.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+struct format_simple_base {
+
+ enum FormatFlagsEnum {
+ BaseDec = 0x0001, // base 10 (integers only) // int+float
+ BaseHex = 0x0002, // base 16 (integers only) // int+float
+ CaseLow = 0x0010, // lower case hex digits // int+float
+ CaseUpp = 0x0020, // upper case hex digits // int+float
+ FillOff = 0x0100, // do not fill up width // int+float
+ FillNul = 0x0400, // fill up width with zeros // int+float
+ NotaNrm = 0x1000, // float: normal/default notation // float
+ NotaFix = 0x2000, // float: fixed point notation // float
+ NotaSci = 0x4000, // float: scientific notation // float
+ };
+
+}; // struct format_simple_base
+
+using format_simple_flags = unsigned int;
+
+static_assert(sizeof(format_simple_flags) >= sizeof(format_simple_base::FormatFlagsEnum));
+
+
+class format_simple_spec {
+private:
+ format_simple_flags flags;
+ std::size_t width; // int+float
+ int precision; // float
+ unsigned int group; // int
+ char group_sep; // int
+public:
+ MPT_CONSTEXPRINLINE format_simple_spec() noexcept
+ : flags(0)
+ , width(0)
+ , precision(-1)
+ , group(0)
+ , group_sep(',') { }
+ MPT_CONSTEXPRINLINE format_simple_flags GetFlags() const noexcept {
+ return flags;
+ }
+ MPT_CONSTEXPRINLINE std::size_t GetWidth() const noexcept {
+ return width;
+ }
+ MPT_CONSTEXPRINLINE int GetPrecision() const noexcept {
+ return precision;
+ }
+ MPT_CONSTEXPRINLINE unsigned int GetGroup() const noexcept {
+ return group;
+ }
+ MPT_CONSTEXPRINLINE char GetGroupSep() const noexcept {
+ return group_sep;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & SetFlags(format_simple_flags f) noexcept {
+ flags = f;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & SetWidth(std::size_t w) noexcept {
+ width = w;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & SetPrecision(int p) noexcept {
+ precision = p;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & SetGroup(unsigned int g) noexcept {
+ group = g;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & SetGroupSep(char s) noexcept {
+ group_sep = s;
+ return *this;
+ }
+
+public:
+ MPT_CONSTEXPRINLINE format_simple_spec & BaseDec() noexcept {
+ flags &= ~(format_simple_base::BaseDec | format_simple_base::BaseHex);
+ flags |= format_simple_base::BaseDec;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & BaseHex() noexcept {
+ flags &= ~(format_simple_base::BaseDec | format_simple_base::BaseHex);
+ flags |= format_simple_base::BaseHex;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & CaseLow() noexcept {
+ flags &= ~(format_simple_base::CaseLow | format_simple_base::CaseUpp);
+ flags |= format_simple_base::CaseLow;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & CaseUpp() noexcept {
+ flags &= ~(format_simple_base::CaseLow | format_simple_base::CaseUpp);
+ flags |= format_simple_base::CaseUpp;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & FillOff() noexcept {
+ flags &= ~(format_simple_base::FillOff | format_simple_base::FillNul);
+ flags |= format_simple_base::FillOff;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & FillNul() noexcept {
+ flags &= ~(format_simple_base::FillOff | format_simple_base::FillNul);
+ flags |= format_simple_base::FillNul;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & NotaNrm() noexcept {
+ flags &= ~(format_simple_base::NotaNrm | format_simple_base::NotaFix | format_simple_base::NotaSci);
+ flags |= format_simple_base::NotaNrm;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & NotaFix() noexcept {
+ flags &= ~(format_simple_base::NotaNrm | format_simple_base::NotaFix | format_simple_base::NotaSci);
+ flags |= format_simple_base::NotaFix;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & NotaSci() noexcept {
+ flags &= ~(format_simple_base::NotaNrm | format_simple_base::NotaFix | format_simple_base::NotaSci);
+ flags |= format_simple_base::NotaSci;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Width(std::size_t w) noexcept {
+ width = w;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Prec(int p) noexcept {
+ precision = p;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Group(unsigned int g) noexcept {
+ group = g;
+ return *this;
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & GroupSep(char s) noexcept {
+ group_sep = s;
+ return *this;
+ }
+
+public:
+ MPT_CONSTEXPRINLINE format_simple_spec & Dec() noexcept {
+ return BaseDec();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Hex() noexcept {
+ return BaseHex();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Low() noexcept {
+ return CaseLow();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Upp() noexcept {
+ return CaseUpp();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Off() noexcept {
+ return FillOff();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Nul() noexcept {
+ return FillNul();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Nrm() noexcept {
+ return NotaNrm();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Fix() noexcept {
+ return NotaFix();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Sci() noexcept {
+ return NotaSci();
+ }
+
+public:
+ MPT_CONSTEXPRINLINE format_simple_spec & Decimal() noexcept {
+ return BaseDec();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Hexadecimal() noexcept {
+ return BaseHex();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Lower() noexcept {
+ return CaseLow();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Upper() noexcept {
+ return CaseUpp();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & FillNone() noexcept {
+ return FillOff();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & FillZero() noexcept {
+ return FillNul();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & FloatNormal() noexcept {
+ return NotaNrm();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & FloatFixed() noexcept {
+ return NotaFix();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & FloatScientific() noexcept {
+ return NotaSci();
+ }
+ MPT_CONSTEXPRINLINE format_simple_spec & Precision(int p) noexcept {
+ return Prec(p);
+ }
+};
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_FORMAT_SIMPLE_SPEC_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/tests/tests_format_message.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/tests/tests_format_message.hpp
new file mode 100644
index 00000000..5de2dc13
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/tests/tests_format_message.hpp
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_TESTS_FORMAT_MESSAGE_HPP
+#define MPT_FORMAT_TESTS_FORMAT_MESSAGE_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/format/message.hpp"
+#include "mpt/format/message_macros.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace format {
+namespace message {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/format/message")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ static_assert(mpt::parse_format_string_argument_count("") == 0);
+ static_assert(mpt::parse_format_string_argument_count("{{") == 0);
+ static_assert(mpt::parse_format_string_argument_count("}}") == 0);
+ static_assert(mpt::parse_format_string_argument_count("{}") == 1);
+ static_assert(mpt::parse_format_string_argument_count("{}{}") == 2);
+ static_assert(mpt::parse_format_string_argument_count("{0}{1}") == 2);
+
+ // basic
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}{}{}")(1, 2, 3), "123");
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{2}{1}{0}")(1, 2, 3), "321");
+
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{2}{1}{0}{4}{3}{6}{5}{7}{10}{9}{8}")(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "a"), "21043657a98");
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE(L"{}{}{}")(1, 2, 3), L"123");
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
+ // escaping behviour
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%")(), "%");
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%")(), "%");
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%%")(), "%%");
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}")("a"), "a");
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}%")("a"), "a%");
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}%")("a"), "a%");
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}%%")("a"), "a%%");
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%1")(), "%1");
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%{}")("a"), "%a");
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%b")(), "%b");
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{{}}")(), "{}");
+ MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{{{}}}")("a"), "{a}");
+}
+
+} // namespace message
+} // namespace format
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_FORMAT_TESTS_FORMAT_MESSAGE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/format/tests/tests_format_simple.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/format/tests/tests_format_simple.hpp
new file mode 100644
index 00000000..8956d0d6
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/format/tests/tests_format_simple.hpp
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_FORMAT_TESTS_FORMAT_SIMPLE_HPP
+#define MPT_FORMAT_TESTS_FORMAT_SIMPLE_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/format/simple.hpp"
+#include "mpt/format/simple_integer.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <limits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace format {
+namespace simple {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/format/simple")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(1.5f), "1.5");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(true), "1");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(false), "0");
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(0), "0");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(-23), "-23");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(42), "42");
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex0<3>((int32)-1), "-001");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex((int32)-1), "-1");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex(-0xabcde), "-abcde");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex(std::numeric_limits<int32>::min()), "-80000000");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex(std::numeric_limits<int32>::min() + 1), "-7fffffff");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex(0x123e), "123e");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex0<6>(0x123e), "00123e");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex0<2>(0x123e), "123e");
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<0>(1), "1");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<1>(1), "1");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<2>(1), "01");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<3>(1), "001");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<0>(11), "11");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<1>(11), "11");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<2>(11), "11");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<3>(11), "011");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<0>(-1), "-1");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<1>(-1), "-1");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<2>(-1), "-01");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<3>(-1), "-001");
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<7>(0xa2345678), MPT_USTRING("A2345678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<8>(0xa2345678), MPT_USTRING("A2345678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<9>(0xa2345678), MPT_USTRING("0A2345678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<10>(0xa2345678), MPT_USTRING("00A2345678"));
+
+#if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_chars<std::string>(std::numeric_limits<int16>::min(), 10), "-32768");
+ MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_chars<std::string>(std::numeric_limits<int16>::max(), 10), "32767");
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_chars<std::string>(std::numeric_limits<int16>::min(), 7), "-164351");
+ MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_chars<std::string>(std::numeric_limits<int16>::min() + 1, 7), "-164350");
+ MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_chars<std::string>(std::numeric_limits<int16>::max(), 7), "164350");
+
+#else // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_stream<std::string>(std::numeric_limits<int16>::min(), 10), "-32768");
+ MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_stream<std::string>(std::numeric_limits<int16>::max(), 10), "32767");
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_stream<std::string>(std::numeric_limits<int16>::min(), 7), "-164351");
+ MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_stream<std::string>(std::numeric_limits<int16>::min() + 1, 7), "-164350");
+ MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_stream<std::string>(std::numeric_limits<int16>::max(), 7), "164350");
+
+#endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::wstring>::hex(0x123e), L"123e");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::wstring>::hex0<6>(0x123e), L"00123e");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::wstring>::hex0<2>(0x123e), L"123e");
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(-87.0f), "-87");
+ if (mpt::format<std::string>::val(-0.5e-6) != "-5e-007"
+ && mpt::format<std::string>::val(-0.5e-6) != "-5e-07"
+ && mpt::format<std::string>::val(-0.5e-6) != "-5e-7"
+ && mpt::format<std::string>::val(-0.5e-6) != "-4.9999999999999998e-7"
+ && mpt::format<std::string>::val(-0.5e-6) != "-4.9999999999999998e-07"
+ && mpt::format<std::string>::val(-0.5e-6) != "-4.9999999999999998e-007")
+ {
+ MPT_TEST_EXPECT_EQUAL(true, false);
+ }
+ if (mpt::format<std::string>::val(-1.0 / 65536.0) != "-1.52587890625e-005"
+ && mpt::format<std::string>::val(-1.0 / 65536.0) != "-1.52587890625e-05"
+ && mpt::format<std::string>::val(-1.0 / 65536.0) != "-1.52587890625e-5")
+ {
+ MPT_TEST_EXPECT_EQUAL(true, false);
+ }
+ if (mpt::format<std::string>::val(-1.0f / 65536.0f) != "-1.52587891e-005"
+ && mpt::format<std::string>::val(-1.0f / 65536.0f) != "-1.52587891e-05"
+ && mpt::format<std::string>::val(-1.0f / 65536.0f) != "-1.52587891e-5"
+ && mpt::format<std::string>::val(-1.0f / 65536.0f) != "-1.5258789e-005"
+ && mpt::format<std::string>::val(-1.0f / 65536.0f) != "-1.5258789e-05"
+ && mpt::format<std::string>::val(-1.0f / 65536.0f) != "-1.5258789e-5")
+ {
+ MPT_TEST_EXPECT_EQUAL(true, false);
+ }
+ if (mpt::format<std::string>::val(58.65403492763) != "58.654034927630001"
+ && mpt::format<std::string>::val(58.65403492763) != "58.65403492763")
+ {
+ MPT_TEST_EXPECT_EQUAL(true, false);
+ }
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::flt(58.65403492763, 6), "58.654");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::fix(23.42, 1), "23.4");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::fix(234.2, 1), "234.2");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::fix(2342.0, 1), "2342.0");
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec(2, ';', 2345678), std::string("2;34;56;78"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec(2, ';', 12345678), std::string("12;34;56;78"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex(3, ':', 0xa2345678), std::string("a2:345:678"));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::dec(2, ';', 12345678), MPT_USTRING("12;34;56;78"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::hex(3, ':', 0xa2345678), MPT_USTRING("a2:345:678"));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<7>(3, ':', 0xa2345678), MPT_USTRING("A2:345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<8>(3, ':', 0xa2345678), MPT_USTRING("A2:345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<9>(3, ':', 0xa2345678), MPT_USTRING("0A2:345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<10>(3, ':', 0xa2345678), MPT_USTRING("0:0A2:345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<11>(3, ':', 0xa2345678), MPT_USTRING("00:0A2:345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<12>(3, ':', 0xa2345678), MPT_USTRING("000:0A2:345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<7>(3, ':', -0x12345678), MPT_USTRING("-12:345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<8>(3, ':', -0x12345678), MPT_USTRING("-12:345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<9>(3, ':', -0x12345678), MPT_USTRING("-012:345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<10>(3, ':', -0x12345678), MPT_USTRING("-0:012:345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<11>(3, ':', -0x12345678), MPT_USTRING("-00:012:345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<12>(3, ':', -0x12345678), MPT_USTRING("-000:012:345:678"));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<5>(3, ':', 0x345678), MPT_USTRING("345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<6>(3, ':', 0x345678), MPT_USTRING("345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<7>(3, ':', 0x345678), MPT_USTRING("0:345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<5>(3, ':', -0x345678), MPT_USTRING("-345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<6>(3, ':', -0x345678), MPT_USTRING("-345:678"));
+ MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<7>(3, ':', -0x345678), MPT_USTRING("-0:345:678"));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::left(3, "a"), "a ");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::right(3, "a"), " a");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::center(3, "a"), " a ");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::center(4, "a"), " a ");
+
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::flt(6.12345, 3), "6.12");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::fix(6.12345, 3), "6.123");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::flt(6.12345, 4), "6.123");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::fix(6.12345, 4), "6.1235");
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::wstring>::flt(6.12345, 3), L"6.12");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::wstring>::fix(6.12345, 3), L"6.123");
+ MPT_TEST_EXPECT_EQUAL(mpt::format<std::wstring>::flt(6.12345, 4), L"6.123");
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+}
+
+} // namespace simple
+} // namespace format
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_FORMAT_TESTS_FORMAT_SIMPLE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io/base.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io/base.hpp
new file mode 100644
index 00000000..1ef286fd
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io/base.hpp
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_BASE_HPP
+#define MPT_IO_BASE_HPP
+
+
+
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+using Offset = int64;
+
+inline constexpr std::size_t BUFFERSIZE_MINUSCULE = 1 * 256; // on stack usage, tuned for single word/line buffers
+inline constexpr std::size_t BUFFERSIZE_TINY = 1 * 1024; // on stack usage
+inline constexpr std::size_t BUFFERSIZE_SMALL = 4 * 1024; // on heap
+inline constexpr std::size_t BUFFERSIZE_NORMAL = 64 * 1024; // FILE I/O
+inline constexpr std::size_t BUFFERSIZE_LARGE = 1024 * 1024;
+
+
+
+template <typename Tfile, typename Enable = void>
+struct FileOperations {
+};
+
+template <typename Tfile>
+inline FileOperations<Tfile> FileOps(Tfile & f) {
+ ;
+ return FileOperations<Tfile>{f};
+}
+
+
+
+template <typename Tfile>
+inline bool IsValid(Tfile & f) {
+ return FileOps(f).IsValid();
+}
+
+template <typename Tfile>
+inline bool IsReadSeekable(Tfile & f) {
+ return FileOps(f).IsReadSeekable();
+}
+
+template <typename Tfile>
+inline bool IsWriteSeekable(Tfile & f) {
+ return FileOps(f).IsWriteSeekable();
+}
+
+template <typename Tfile>
+inline IO::Offset TellRead(Tfile & f) {
+ return FileOps(f).TellRead();
+}
+
+template <typename Tfile>
+inline IO::Offset TellWrite(Tfile & f) {
+ return FileOps(f).TellWrite();
+}
+
+template <typename Tfile>
+inline bool SeekBegin(Tfile & f) {
+ return FileOps(f).SeekBegin();
+}
+
+template <typename Tfile>
+inline bool SeekEnd(Tfile & f) {
+ return FileOps(f).SeekEnd();
+}
+
+template <typename Tfile>
+inline bool SeekAbsolute(Tfile & f, IO::Offset pos) {
+ return FileOps(f).SeekAbsolute(pos);
+}
+
+template <typename Tfile>
+inline bool SeekRelative(Tfile & f, IO::Offset off) {
+ return FileOps(f).SeekRelative(off);
+}
+
+template <typename Tfile>
+inline mpt::byte_span ReadRawImpl(Tfile & f, mpt::byte_span data) {
+ return FileOps(f).ReadRawImpl(data);
+}
+
+template <typename Tfile>
+inline bool WriteRawImpl(Tfile & f, mpt::const_byte_span data) {
+ return FileOps(f).WriteRawImpl(data);
+}
+
+template <typename Tfile>
+inline bool IsEof(Tfile & f) {
+ return FileOps(f).IsEof();
+}
+
+template <typename Tfile>
+inline bool Flush(Tfile & f) {
+ return FileOps(f).Flush();
+}
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_BASE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io/io.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io/io.hpp
new file mode 100644
index 00000000..1379f1e5
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io/io.hpp
@@ -0,0 +1,367 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_IO_HPP
+#define MPT_IO_IO_HPP
+
+
+
+#include "mpt/base/array.hpp"
+#include "mpt/base/bit.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/span.hpp"
+#include "mpt/endian/integer.hpp"
+#include "mpt/io/base.hpp"
+
+#include <algorithm>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include <cassert>
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+template <typename Tbyte, typename Tfile>
+inline mpt::byte_span ReadRaw(Tfile & f, Tbyte * data, std::size_t size) {
+ return mpt::IO::ReadRawImpl(f, mpt::as_span(mpt::byte_cast<std::byte *>(data), size));
+}
+
+template <typename Tbyte, typename Tfile>
+inline mpt::byte_span ReadRaw(Tfile & f, mpt::span<Tbyte> data) {
+ return mpt::IO::ReadRawImpl(f, mpt::byte_cast<mpt::byte_span>(data));
+}
+
+template <typename Tbyte, typename Tfile>
+inline bool WriteRaw(Tfile & f, const Tbyte * data, std::size_t size) {
+ return mpt::IO::WriteRawImpl(f, mpt::as_span(mpt::byte_cast<const std::byte *>(data), size));
+}
+
+template <typename Tbyte, typename Tfile>
+inline bool WriteRaw(Tfile & f, mpt::span<Tbyte> data) {
+ return mpt::IO::WriteRawImpl(f, mpt::byte_cast<mpt::const_byte_span>(data));
+}
+
+template <typename Tbinary, typename Tfile>
+inline bool Read(Tfile & f, Tbinary & v) {
+ return mpt::IO::ReadRaw(f, mpt::as_raw_memory(v)).size() == mpt::as_raw_memory(v).size();
+}
+
+template <typename Tbinary, typename Tfile>
+inline bool Write(Tfile & f, const Tbinary & v) {
+ return mpt::IO::WriteRaw(f, mpt::as_raw_memory(v));
+}
+
+template <typename Tbinary, typename Tfile>
+inline bool Write(Tfile & f, const std::vector<Tbinary> & v) {
+ static_assert(mpt::is_binary_safe<Tbinary>::value);
+ return mpt::IO::WriteRaw(f, mpt::as_raw_memory(v));
+}
+
+template <typename T, typename Tfile>
+inline bool WritePartial(Tfile & f, const T & v, std::size_t size = sizeof(T)) {
+ assert(size <= sizeof(T));
+ return mpt::IO::WriteRaw(f, mpt::as_span(mpt::as_raw_memory(v).data(), size));
+}
+
+template <typename Tfile>
+inline bool ReadByte(Tfile & f, std::byte & v) {
+ bool result = false;
+ std::byte byte = mpt::as_byte(0);
+ const std::size_t readResult = mpt::IO::ReadRaw(f, &byte, sizeof(std::byte)).size();
+ result = (readResult == sizeof(std::byte));
+ v = byte;
+ return result;
+}
+
+template <typename T, typename Tfile>
+inline bool ReadBinaryTruncatedLE(Tfile & f, T & v, std::size_t size) {
+ bool result = false;
+ static_assert(std::numeric_limits<T>::is_integer);
+ std::array<uint8, sizeof(T)> bytes = mpt::init_array<uint8, sizeof(T)>(uint8{0});
+ const std::size_t readResult = mpt::IO::ReadRaw(f, bytes.data(), std::min(size, sizeof(T))).size();
+ result = (readResult == std::min(size, sizeof(T)));
+ v = mpt::bit_cast<typename mpt::make_le<T>::type>(bytes);
+ return result;
+}
+
+template <typename T, typename Tfile>
+inline bool ReadIntLE(Tfile & f, T & v) {
+ bool result = false;
+ static_assert(std::numeric_limits<T>::is_integer);
+ std::array<uint8, sizeof(T)> bytes = mpt::init_array<uint8, sizeof(T)>(uint8{0});
+ const std::size_t readResult = mpt::IO::ReadRaw(f, mpt::as_span(bytes)).size();
+ result = (readResult == sizeof(T));
+ v = mpt::bit_cast<typename mpt::make_le<T>::type>(bytes);
+ return result;
+}
+
+template <typename T, typename Tfile>
+inline bool ReadIntBE(Tfile & f, T & v) {
+ bool result = false;
+ static_assert(std::numeric_limits<T>::is_integer);
+ std::array<uint8, sizeof(T)> bytes = mpt::init_array<uint8, sizeof(T)>(uint8{0});
+ const std::size_t readResult = mpt::IO::ReadRaw(f, mpt::as_span(bytes)).size();
+ result = (readResult == sizeof(T));
+ v = mpt::bit_cast<typename mpt::make_be<T>::type>(bytes);
+ return result;
+}
+
+template <typename Tfile>
+inline bool ReadAdaptiveInt16LE(Tfile & f, uint16 & v) {
+ bool result = true;
+ uint8 byte = 0;
+ std::size_t additionalBytes = 0;
+ v = 0;
+ byte = 0;
+ if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
+ result = false;
+ }
+ additionalBytes = (byte & 0x01);
+ v = byte >> 1;
+ for (std::size_t i = 0; i < additionalBytes; ++i) {
+ byte = 0;
+ if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
+ result = false;
+ }
+ v |= (static_cast<uint16>(byte) << (((i + 1) * 8) - 1));
+ }
+ return result;
+}
+
+template <typename Tfile>
+inline bool ReadAdaptiveInt32LE(Tfile & f, uint32 & v) {
+ bool result = true;
+ uint8 byte = 0;
+ std::size_t additionalBytes = 0;
+ v = 0;
+ byte = 0;
+ if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
+ result = false;
+ }
+ additionalBytes = (byte & 0x03);
+ v = byte >> 2;
+ for (std::size_t i = 0; i < additionalBytes; ++i) {
+ byte = 0;
+ if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
+ result = false;
+ }
+ v |= (static_cast<uint32>(byte) << (((i + 1) * 8) - 2));
+ }
+ return result;
+}
+
+template <typename Tfile>
+inline bool ReadAdaptiveInt64LE(Tfile & f, uint64 & v) {
+ bool result = true;
+ uint8 byte = 0;
+ std::size_t additionalBytes = 0;
+ v = 0;
+ byte = 0;
+ if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
+ result = false;
+ }
+ additionalBytes = (1 << (byte & 0x03)) - 1;
+ v = byte >> 2;
+ for (std::size_t i = 0; i < additionalBytes; ++i) {
+ byte = 0;
+ if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
+ result = false;
+ }
+ v |= (static_cast<uint64>(byte) << (((i + 1) * 8) - 2));
+ }
+ return result;
+}
+
+template <typename Tsize, typename Tfile>
+inline bool ReadSizedStringLE(Tfile & f, std::string & str, Tsize maxSize = std::numeric_limits<Tsize>::max()) {
+ static_assert(std::numeric_limits<Tsize>::is_integer);
+ str.clear();
+ Tsize size = 0;
+ if (!mpt::IO::ReadIntLE(f, size)) {
+ return false;
+ }
+ if (size > maxSize) {
+ return false;
+ }
+ for (Tsize i = 0; i != size; ++i) {
+ char c = '\0';
+ if (!mpt::IO::ReadIntLE(f, c)) {
+ return false;
+ }
+ str.push_back(c);
+ }
+ return true;
+}
+
+
+
+template <typename T, typename Tfile>
+inline bool WriteIntLE(Tfile & f, const T v) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::IO::Write(f, mpt::as_le(v));
+}
+
+template <typename T, typename Tfile>
+inline bool WriteIntBE(Tfile & f, const T v) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ return mpt::IO::Write(f, mpt::as_be(v));
+}
+
+template <typename Tfile>
+inline bool WriteAdaptiveInt16LE(Tfile & f, const uint16 v, std::size_t fixedSize = 0) {
+ std::size_t minSize = fixedSize;
+ std::size_t maxSize = fixedSize;
+ assert(minSize == 0 || minSize == 1 || minSize == 2);
+ assert(maxSize == 0 || maxSize == 1 || maxSize == 2);
+ assert(maxSize == 0 || maxSize >= minSize);
+ if (maxSize == 0) {
+ maxSize = 2;
+ }
+ if (v < 0x80 && minSize <= 1 && 1 <= maxSize) {
+ return mpt::IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 1) | 0x00);
+ } else if (v < 0x8000 && minSize <= 2 && 2 <= maxSize) {
+ return mpt::IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 1) | 0x01);
+ } else {
+ assert(false);
+ return false;
+ }
+}
+
+template <typename Tfile>
+inline bool WriteAdaptiveInt32LE(Tfile & f, const uint32 v, std::size_t fixedSize = 0) {
+ std::size_t minSize = fixedSize;
+ std::size_t maxSize = fixedSize;
+ assert(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 3 || minSize == 4);
+ assert(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 3 || maxSize == 4);
+ assert(maxSize == 0 || maxSize >= minSize);
+ if (maxSize == 0) {
+ maxSize = 4;
+ }
+ if (v < 0x40 && minSize <= 1 && 1 <= maxSize) {
+ return mpt::IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 2) | 0x00);
+ } else if (v < 0x4000 && minSize <= 2 && 2 <= maxSize) {
+ return mpt::IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 2) | 0x01);
+ } else if (v < 0x400000 && minSize <= 3 && 3 <= maxSize) {
+ uint32 value = static_cast<uint32>(v << 2) | 0x02;
+ std::byte bytes[3];
+ bytes[0] = static_cast<std::byte>(value >> 0);
+ bytes[1] = static_cast<std::byte>(value >> 8);
+ bytes[2] = static_cast<std::byte>(value >> 16);
+ return mpt::IO::WriteRaw(f, bytes, 3);
+ } else if (v < 0x40000000 && minSize <= 4 && 4 <= maxSize) {
+ return mpt::IO::WriteIntLE<uint32>(f, static_cast<uint32>(v << 2) | 0x03);
+ } else {
+ assert(false);
+ return false;
+ }
+}
+
+template <typename Tfile>
+inline bool WriteAdaptiveInt64LE(Tfile & f, const uint64 v, std::size_t fixedSize = 0) {
+ std::size_t minSize = fixedSize;
+ std::size_t maxSize = fixedSize;
+ assert(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 4 || minSize == 8);
+ assert(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 4 || maxSize == 8);
+ assert(maxSize == 0 || maxSize >= minSize);
+ if (maxSize == 0) {
+ maxSize = 8;
+ }
+ if (v < 0x40 && minSize <= 1 && 1 <= maxSize) {
+ return mpt::IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 2) | 0x00);
+ } else if (v < 0x4000 && minSize <= 2 && 2 <= maxSize) {
+ return mpt::IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 2) | 0x01);
+ } else if (v < 0x40000000 && minSize <= 4 && 4 <= maxSize) {
+ return mpt::IO::WriteIntLE<uint32>(f, static_cast<uint32>(v << 2) | 0x02);
+ } else if (v < 0x4000000000000000ull && minSize <= 8 && 8 <= maxSize) {
+ return mpt::IO::WriteIntLE<uint64>(f, static_cast<uint64>(v << 2) | 0x03);
+ } else {
+ assert(false);
+ return false;
+ }
+}
+
+// Write a variable-length integer, as found in MIDI files. The number of written bytes is placed in the bytesWritten parameter.
+template <typename Tfile, typename T>
+bool WriteVarInt(Tfile & f, const T v, std::size_t * bytesWritten = nullptr) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(!std::numeric_limits<T>::is_signed);
+ std::byte out[(sizeof(T) * 8 + 6) / 7];
+ std::size_t numBytes = 0;
+ for (uint32 n = (sizeof(T) * 8) / 7; n > 0; n--) {
+ if (v >= (static_cast<T>(1) << (n * 7u))) {
+ out[numBytes++] = static_cast<std::byte>(((v >> (n * 7u)) & 0x7F) | 0x80);
+ }
+ }
+ out[numBytes++] = static_cast<std::byte>(v & 0x7F);
+ assert(numBytes <= std::size(out));
+ if (bytesWritten != nullptr) {
+ *bytesWritten = numBytes;
+ }
+ return mpt::IO::WriteRaw(f, out, numBytes);
+}
+
+template <typename Tsize, typename Tfile>
+inline bool WriteSizedStringLE(Tfile & f, const std::string & str) {
+ static_assert(std::numeric_limits<Tsize>::is_integer);
+ if (str.size() > std::numeric_limits<Tsize>::max()) {
+ return false;
+ }
+ Tsize size = static_cast<Tsize>(str.size());
+ if (!mpt::IO::WriteIntLE(f, size)) {
+ return false;
+ }
+ if (!mpt::IO::WriteRaw(f, str.data(), str.size())) {
+ return false;
+ }
+ return true;
+}
+
+template <typename Tfile>
+inline bool WriteText(Tfile & f, const std::string & s) {
+ return mpt::IO::WriteRaw(f, s.data(), s.size());
+}
+
+template <typename Tfile>
+inline bool WriteTextCRLF(Tfile & f) {
+ return mpt::IO::WriteText(f, "\r\n");
+}
+
+template <typename Tfile>
+inline bool WriteTextLF(Tfile & f) {
+ return mpt::IO::WriteText(f, "\n");
+}
+
+template <typename Tfile>
+inline bool WriteTextCRLF(Tfile & f, const std::string & s) {
+ return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextCRLF(f);
+}
+
+template <typename Tfile>
+inline bool WriteTextLF(Tfile & f, const std::string & s) {
+ return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextLF(f);
+}
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_IO_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io/io_span.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io/io_span.hpp
new file mode 100644
index 00000000..94cdaa99
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io/io_span.hpp
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_IO_SPAN_HPP
+#define MPT_IO_IO_SPAN_HPP
+
+
+
+#include "mpt/base/macros.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/saturate_cast.hpp"
+#include "mpt/base/span.hpp"
+#include "mpt/io/base.hpp"
+
+#include <algorithm>
+#include <utility>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+template <typename Tbyte>
+struct FileOperations<std::pair<mpt::span<Tbyte>, IO::Offset>> {
+
+private:
+ std::pair<mpt::span<Tbyte>, IO::Offset> & f;
+
+public:
+ FileOperations(std::pair<mpt::span<Tbyte>, IO::Offset> & f_)
+ : f(f_) {
+ return;
+ }
+
+public:
+ inline bool IsValid() {
+ return (f.second >= 0);
+ }
+
+ inline bool IsReadSeekable() {
+ MPT_UNUSED(f);
+ return true;
+ }
+
+ inline bool IsWriteSeekable() {
+ MPT_UNUSED(f);
+ return true;
+ }
+
+ inline IO::Offset TellRead() {
+ return f.second;
+ }
+
+ inline IO::Offset TellWrite() {
+ return f.second;
+ }
+
+ inline bool SeekBegin() {
+ f.second = 0;
+ return true;
+ }
+
+ inline bool SeekEnd() {
+ f.second = f.first.size();
+ return true;
+ }
+
+ inline bool SeekAbsolute(IO::Offset pos) {
+ f.second = pos;
+ return true;
+ }
+
+ inline bool SeekRelative(IO::Offset off) {
+ if (f.second < 0)
+ {
+ return false;
+ }
+ f.second += off;
+ return true;
+ }
+
+ inline mpt::byte_span ReadRawImpl(mpt::byte_span data) {
+ if (f.second < 0)
+ {
+ return data.first(0);
+ }
+ if (f.second >= static_cast<IO::Offset>(f.first.size()))
+ {
+ return data.first(0);
+ }
+ std::size_t num = mpt::saturate_cast<std::size_t>(std::min(static_cast<IO::Offset>(f.first.size()) - f.second, static_cast<IO::Offset>(data.size())));
+ std::copy(mpt::byte_cast<const std::byte *>(f.first.data() + f.second), mpt::byte_cast<const std::byte *>(f.first.data() + f.second + num), data.data());
+ f.second += num;
+ return data.first(num);
+ }
+
+ inline bool WriteRawImpl(mpt::const_byte_span data) {
+ if (f.second < 0)
+ {
+ return false;
+ }
+ if (f.second > static_cast<IO::Offset>(f.first.size()))
+ {
+ return false;
+ }
+ std::size_t num = mpt::saturate_cast<std::size_t>(std::min(static_cast<IO::Offset>(f.first.size()) - f.second, static_cast<IO::Offset>(data.size())));
+ if (num != data.size())
+ {
+ return false;
+ }
+ std::copy(data.data(), data.data() + num, mpt::byte_cast<std::byte *>(f.first.data() + f.second));
+ f.second += num;
+ return true;
+ }
+
+ inline bool IsEof() {
+ return (f.second >= static_cast<IO::Offset>(f.first.size()));
+ }
+
+ inline bool Flush() {
+ MPT_UNUSED(f);
+ return true;
+ }
+};
+
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_IO_SPAN_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io/io_stdstream.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io/io_stdstream.hpp
new file mode 100644
index 00000000..23f6602f
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io/io_stdstream.hpp
@@ -0,0 +1,332 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_IO_STDSTREAM_HPP
+#define MPT_IO_IO_STDSTREAM_HPP
+
+
+
+#include "mpt/base/macros.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/utility.hpp"
+#include "mpt/io/base.hpp"
+
+#include <ios>
+#include <istream>
+#include <ostream>
+#include <type_traits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+//static_assert(sizeof(std::streamoff) == 8); // Assert 64bit file support.
+
+struct FileOperationsStdIos {
+
+private:
+ std::ios & f;
+
+public:
+ FileOperationsStdIos(std::ios & f_)
+ : f(f_) {
+ return;
+ }
+
+public:
+ inline bool IsValid() {
+ return !f.fail();
+ }
+};
+
+struct FileOperationsStdIstream
+ : public FileOperationsStdIos {
+
+private:
+ std::istream & f;
+
+public:
+ FileOperationsStdIstream(std::istream & f_)
+ : FileOperationsStdIos(f_)
+ , f(f_) {
+ return;
+ }
+
+public:
+ inline bool IsReadSeekable() {
+ f.clear();
+ std::streampos oldpos = f.tellg();
+ if (f.fail() || oldpos == std::streampos(-1))
+ {
+ f.clear();
+ return false;
+ }
+ f.seekg(0, std::ios::beg);
+ if (f.fail())
+ {
+ f.clear();
+ f.seekg(oldpos);
+ f.clear();
+ return false;
+ }
+ f.seekg(0, std::ios::end);
+ if (f.fail())
+ {
+ f.clear();
+ f.seekg(oldpos);
+ f.clear();
+ return false;
+ }
+ std::streampos length = f.tellg();
+ if (f.fail() || length == std::streampos(-1))
+ {
+ f.clear();
+ f.seekg(oldpos);
+ f.clear();
+ return false;
+ }
+ f.seekg(oldpos);
+ f.clear();
+ return true;
+ }
+
+ inline IO::Offset TellRead() {
+ return f.tellg();
+ }
+
+ inline bool SeekBegin() {
+ f.seekg(0);
+ return !f.fail();
+ }
+
+ inline bool SeekEnd() {
+ f.seekg(0, std::ios::end);
+ return !f.fail();
+ }
+
+ inline bool SeekAbsolute(IO::Offset pos) {
+ if (!mpt::in_range<std::streamoff>(pos))
+ {
+ return false;
+ }
+ f.seekg(static_cast<std::streamoff>(pos), std::ios::beg);
+ return !f.fail();
+ }
+
+ inline bool SeekRelative(IO::Offset off) {
+ if (!mpt::in_range<std::streamoff>(off))
+ {
+ return false;
+ }
+ f.seekg(static_cast<std::streamoff>(off), std::ios::cur);
+ return !f.fail();
+ }
+
+ inline mpt::byte_span ReadRawImpl(mpt::byte_span data) {
+ f.read(mpt::byte_cast<char *>(data.data()), data.size());
+ return data.first(mpt::saturate_cast<std::size_t>(f.gcount()));
+ }
+
+ inline bool IsEof() {
+ return f.eof();
+ }
+};
+
+struct FileOperationsStdOstream
+ : public FileOperationsStdIos {
+
+private:
+ std::ostream & f;
+
+public:
+ FileOperationsStdOstream(std::ostream & f_)
+ : FileOperationsStdIos(f_)
+ , f(f_) {
+ return;
+ }
+
+public:
+ inline bool IsWriteSeekable() {
+ f.clear();
+ std::streampos oldpos = f.tellp();
+ if (f.fail() || oldpos == std::streampos(-1))
+ {
+ f.clear();
+ return false;
+ }
+ f.seekp(0, std::ios::beg);
+ if (f.fail())
+ {
+ f.clear();
+ f.seekp(oldpos);
+ f.clear();
+ return false;
+ }
+ f.seekp(0, std::ios::end);
+ if (f.fail())
+ {
+ f.clear();
+ f.seekp(oldpos);
+ f.clear();
+ return false;
+ }
+ std::streampos length = f.tellp();
+ if (f.fail() || length == std::streampos(-1))
+ {
+ f.clear();
+ f.seekp(oldpos);
+ f.clear();
+ return false;
+ }
+ f.seekp(oldpos);
+ f.clear();
+ return true;
+ }
+
+ inline IO::Offset TellWrite() {
+ return f.tellp();
+ }
+
+ inline bool SeekBegin() {
+ f.seekp(0);
+ return !f.fail();
+ }
+
+ inline bool SeekEnd() {
+ f.seekp(0, std::ios::end);
+ return !f.fail();
+ }
+
+ inline bool SeekAbsolute(IO::Offset pos) {
+ if (!mpt::in_range<std::streamoff>(pos))
+ {
+ return false;
+ }
+ f.seekp(static_cast<std::streamoff>(pos), std::ios::beg);
+ return !f.fail();
+ }
+
+ inline bool SeekRelative(IO::Offset off) {
+ if (!mpt::in_range<std::streamoff>(off))
+ {
+ return false;
+ }
+ f.seekp(static_cast<std::streamoff>(off), std::ios::cur);
+ return !f.fail();
+ }
+
+ inline bool WriteRawImpl(mpt::const_byte_span data) {
+ f.write(mpt::byte_cast<const char *>(data.data()), data.size());
+ return !f.fail();
+ }
+
+ inline bool Flush() {
+ f.flush();
+ return !f.fail();
+ }
+};
+
+struct FileOperationsStdIOstream
+ : public FileOperationsStdIstream
+ , public FileOperationsStdOstream {
+
+private:
+ std::iostream & f;
+
+public:
+ FileOperationsStdIOstream(std::iostream & f_)
+ : FileOperationsStdIstream(f_)
+ , FileOperationsStdOstream(f_)
+ , f(f_) {
+ return;
+ }
+
+public:
+ inline bool SeekBegin() {
+ FileOperationsStdIstream::SeekBegin();
+ FileOperationsStdOstream::SeekBegin();
+ return !f.fail();
+ }
+
+ inline bool SeekEnd() {
+ FileOperationsStdIstream::SeekEnd();
+ FileOperationsStdOstream::SeekEnd();
+ return !f.fail();
+ }
+
+ inline bool SeekAbsolute(IO::Offset pos) {
+ if (!mpt::in_range<std::streamoff>(pos))
+ {
+ return false;
+ }
+ FileOperationsStdIstream::SeekAbsolute(pos);
+ FileOperationsStdOstream::SeekAbsolute(pos);
+ return !f.fail();
+ }
+
+ inline bool SeekRelative(IO::Offset off) {
+ if (!mpt::in_range<std::streamoff>(off))
+ {
+ return false;
+ }
+ FileOperationsStdIstream::SeekRelative(off);
+ FileOperationsStdOstream::SeekRelative(off);
+ return !f.fail();
+ }
+};
+
+
+
+template <typename Tstream>
+struct FileOperations<Tstream, typename std::enable_if_t<std::is_base_of<std::iostream, Tstream>::value>>
+ : public FileOperationsStdIOstream {
+public:
+ FileOperations(Tstream & f)
+ : FileOperationsStdIOstream(f) {
+ return;
+ }
+};
+
+
+
+template <typename Tstream>
+struct FileOperations<Tstream, typename std::enable_if_t<std::is_base_of<std::istream, Tstream>::value && !std::is_base_of<std::iostream, Tstream>::value>>
+ : public FileOperationsStdIstream {
+public:
+ FileOperations(Tstream & f)
+ : FileOperationsStdIstream(f) {
+ return;
+ }
+};
+
+
+
+template <typename Tstream>
+struct FileOperations<Tstream, typename std::enable_if_t<std::is_base_of<std::ostream, Tstream>::value && !std::is_base_of<std::iostream, Tstream>::value>>
+ : public FileOperationsStdOstream {
+public:
+ FileOperations(Tstream & f)
+ : FileOperationsStdOstream(f) {
+ return;
+ }
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_IO_STDSTREAM_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io/io_virtual_wrapper.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io/io_virtual_wrapper.hpp
new file mode 100644
index 00000000..ad65c548
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io/io_virtual_wrapper.hpp
@@ -0,0 +1,405 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_IO_VIRTUAL_WRAPPER_HPP
+#define MPT_IO_IO_VIRTUAL_WRAPPER_HPP
+
+
+
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/io/base.hpp"
+
+#include <type_traits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+class IFileBase {
+protected:
+ IFileBase() = default;
+ virtual ~IFileBase() = default;
+
+public:
+ virtual bool IsValid() = 0;
+ virtual bool IsReadSeekable() = 0;
+ virtual IO::Offset TellRead() = 0;
+ virtual bool SeekBegin() = 0;
+ virtual bool SeekEnd() = 0;
+ virtual bool SeekAbsolute(IO::Offset pos) = 0;
+ virtual bool SeekRelative(IO::Offset off) = 0;
+ virtual mpt::byte_span ReadRawImpl(mpt::byte_span data) = 0;
+ virtual bool IsEof() = 0;
+};
+
+template <typename Tfile>
+class IFile
+ : public IFileBase {
+private:
+ Tfile & f;
+
+public:
+ IFile(Tfile & f_)
+ : f(f_) {
+ }
+ ~IFile() override = default;
+
+public:
+ bool IsValid() override {
+ return mpt::IO::IsValid(f);
+ }
+ bool IsReadSeekable() override {
+ return mpt::IO::IsReadSeekable(f);
+ }
+ IO::Offset TellRead() override {
+ return mpt::IO::TellRead(f);
+ }
+ bool SeekBegin() override {
+ return mpt::IO::SeekBegin(f);
+ }
+ bool SeekEnd() override {
+ return mpt::IO::SeekEnd(f);
+ }
+ bool SeekAbsolute(IO::Offset pos) override {
+ return mpt::IO::SeekAbsolute(f, pos);
+ }
+ bool SeekRelative(IO::Offset off) override {
+ return mpt::IO::SeekRelative(f, off);
+ }
+ mpt::byte_span ReadRawImpl(mpt::byte_span data) override {
+ return mpt::IO::ReadRawImpl(f, data);
+ }
+ bool IsEof() override {
+ return mpt::IO::IsEof(f);
+ }
+};
+
+
+
+class OFileBase {
+protected:
+ OFileBase() = default;
+ virtual ~OFileBase() = default;
+
+public:
+ virtual bool IsValid() = 0;
+ virtual bool IsWriteSeekable() = 0;
+ virtual IO::Offset TellWrite() = 0;
+ virtual bool SeekBegin() = 0;
+ virtual bool SeekEnd() = 0;
+ virtual bool SeekAbsolute(IO::Offset pos) = 0;
+ virtual bool SeekRelative(IO::Offset off) = 0;
+ virtual bool WriteRawImpl(mpt::const_byte_span data) = 0;
+ virtual bool Flush() = 0;
+};
+
+template <typename Tfile>
+class OFile
+ : public OFileBase {
+private:
+ Tfile & f;
+
+public:
+ OFile(Tfile & f_)
+ : f(f_) {
+ }
+ ~OFile() override = default;
+
+public:
+ bool IsValid() override {
+ return mpt::IO::IsValid(f);
+ }
+ bool IsWriteSeekable() override {
+ return mpt::IO::IsWriteSeekable(f);
+ }
+ IO::Offset TellWrite() override {
+ return mpt::IO::TellWrite(f);
+ }
+ bool SeekBegin() override {
+ return mpt::IO::SeekBegin(f);
+ }
+ bool SeekEnd() override {
+ return mpt::IO::SeekEnd(f);
+ }
+ bool SeekAbsolute(IO::Offset pos) override {
+ return mpt::IO::SeekAbsolute(f, pos);
+ }
+ bool SeekRelative(IO::Offset off) override {
+ return mpt::IO::SeekRelative(f, off);
+ }
+ bool WriteRawImpl(mpt::const_byte_span data) override {
+ return mpt::IO::WriteRawImpl(f, data);
+ }
+ bool Flush() override {
+ return mpt::IO::Flush(f);
+ }
+};
+
+
+
+class IOFileBase {
+protected:
+ IOFileBase() = default;
+ virtual ~IOFileBase() = default;
+
+public:
+ virtual bool IsValid() = 0;
+ virtual bool IsReadSeekable() = 0;
+ virtual bool IsWriteSeekable() = 0;
+ virtual IO::Offset TellRead() = 0;
+ virtual IO::Offset TellWrite() = 0;
+ virtual bool SeekBegin() = 0;
+ virtual bool SeekEnd() = 0;
+ virtual bool SeekAbsolute(IO::Offset pos) = 0;
+ virtual bool SeekRelative(IO::Offset off) = 0;
+ virtual mpt::byte_span ReadRawImpl(mpt::byte_span data) = 0;
+ virtual bool WriteRawImpl(mpt::const_byte_span data) = 0;
+ virtual bool IsEof() = 0;
+ virtual bool Flush() = 0;
+};
+
+template <typename Tfile>
+class IOFile
+ : public IOFileBase {
+private:
+ Tfile & f;
+
+public:
+ IOFile(Tfile & f_)
+ : f(f_) {
+ }
+ ~IOFile() override = default;
+
+public:
+ bool IsValid() override {
+ return mpt::IO::IsValid(f);
+ }
+ bool IsReadSeekable() override {
+ return mpt::IO::IsReadSeekable(f);
+ }
+ bool IsWriteSeekable() override {
+ return mpt::IO::IsWriteSeekable(f);
+ }
+ IO::Offset TellRead() override {
+ return mpt::IO::TellRead(f);
+ }
+ IO::Offset TellWrite() override {
+ return mpt::IO::TellWrite(f);
+ }
+ bool SeekBegin() override {
+ return mpt::IO::SeekBegin(f);
+ }
+ bool SeekEnd() override {
+ return mpt::IO::SeekEnd(f);
+ }
+ bool SeekAbsolute(IO::Offset pos) override {
+ return mpt::IO::SeekAbsolute(f, pos);
+ }
+ bool SeekRelative(IO::Offset off) override {
+ return mpt::IO::SeekRelative(f, off);
+ }
+ mpt::byte_span ReadRawImpl(mpt::byte_span data) override {
+ return mpt::IO::ReadRawImpl(f, data);
+ }
+ bool WriteRawImpl(mpt::const_byte_span data) override {
+ return mpt::IO::WriteRawImpl(f, data);
+ }
+ bool IsEof() override {
+ return mpt::IO::IsEof(f);
+ }
+ bool Flush() override {
+ return mpt::IO::Flush(f);
+ }
+};
+
+
+
+template <typename Tfile>
+struct FileOperations<Tfile, typename std::enable_if_t<std::is_base_of<IFileBase, Tfile>::value>> {
+
+private:
+ IFileBase & f;
+
+public:
+ FileOperations(IFileBase & f_)
+ : f(f_) {
+ return;
+ }
+
+public:
+ inline bool IsValid() {
+ return f.IsValid();
+ }
+
+ inline bool IsReadSeekable() {
+ return f.IsReadSeekable();
+ }
+
+ inline IO::Offset TellRead() {
+ return f.TellRead();
+ }
+
+ inline bool SeekBegin() {
+ return f.SeekBegin();
+ }
+
+ inline bool SeekEnd() {
+ return f.SeekEnd();
+ }
+
+ inline bool SeekAbsolute(IO::Offset pos) {
+ return f.SeekAbsolute(pos);
+ }
+
+ inline bool SeekRelative(IO::Offset off) {
+ return f.SeekRelative(off);
+ }
+
+ inline mpt::byte_span ReadRawImpl(mpt::byte_span data) {
+ return f.ReadRawImpl(data);
+ }
+
+ inline bool IsEof() {
+ return f.IsEof();
+ }
+};
+
+
+
+template <typename Tfile>
+struct FileOperations<Tfile, typename std::enable_if_t<std::is_base_of<OFileBase, Tfile>::value>> {
+
+private:
+ OFileBase & f;
+
+public:
+ FileOperations(OFileBase & f_)
+ : f(f_) {
+ return;
+ }
+
+public:
+ inline bool IsValid() {
+ return f.IsValid();
+ }
+
+ inline bool IsWriteSeekable() {
+ return f.IsWriteSeekable();
+ }
+
+ inline IO::Offset TellWrite() {
+ return f.TellWrite();
+ }
+
+ inline bool SeekBegin() {
+ return f.SeekBegin();
+ }
+
+ inline bool SeekEnd() {
+ return f.SeekEnd();
+ }
+
+ inline bool SeekAbsolute(IO::Offset pos) {
+ return f.SeekAbsolute(pos);
+ }
+
+ inline bool SeekRelative(IO::Offset off) {
+ return f.SeekRelative(off);
+ }
+
+ inline bool WriteRawImpl(mpt::const_byte_span data) {
+ return f.WriteRawImpl(data);
+ }
+
+ inline bool Flush() {
+ return f.Flush();
+ }
+};
+
+
+
+template <typename Tfile>
+struct FileOperations<Tfile, typename std::enable_if_t<std::is_base_of<IOFileBase, Tfile>::value>> {
+
+private:
+ IOFileBase & f;
+
+public:
+ FileOperations(IOFileBase & f_)
+ : f(f_) {
+ return;
+ }
+
+public:
+ inline bool IsValid() {
+ return f.IsValid();
+ }
+
+ inline bool IsReadSeekable() {
+ return f.IsReadSeekable();
+ }
+
+ inline bool IsWriteSeekable() {
+ return f.IsWriteSeekable();
+ }
+
+ inline IO::Offset TellRead() {
+ return f.TellRead();
+ }
+
+ inline IO::Offset TellWrite() {
+ return f.TellWrite();
+ }
+
+ inline bool SeekBegin() {
+ return f.SeekBegin();
+ }
+
+ inline bool SeekEnd() {
+ return f.SeekEnd();
+ }
+
+ inline bool SeekAbsolute(IO::Offset pos) {
+ return f.SeekAbsolute(pos);
+ }
+
+ inline bool SeekRelative(IO::Offset off) {
+ return f.SeekRelative(off);
+ }
+
+ inline mpt::byte_span ReadRawImpl(mpt::byte_span data) {
+ return f.ReadRawImpl(data);
+ }
+
+ inline bool WriteRawImpl(mpt::const_byte_span data) {
+ return f.WriteRawImpl(data);
+ }
+
+ inline bool IsEof() {
+ return f.IsEof();
+ }
+
+ inline bool Flush() {
+ return f.Flush();
+ }
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_IO_VIRTUAL_WRAPPER_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io/tests/tests_io.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io/tests/tests_io.hpp
new file mode 100644
index 00000000..38e2a695
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io/tests/tests_io.hpp
@@ -0,0 +1,573 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_TESTS_IO_HPP
+#define MPT_IO_TESTS_IO_HPP
+
+
+
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/utility.hpp"
+#include "mpt/endian/integer.hpp"
+#include "mpt/io/base.hpp"
+#include "mpt/io/io.hpp"
+#include "mpt/io/io_stdstream.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <ios>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace io {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/io")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+
+ // check that empty stringstream behaves correctly with our MSVC workarounds when using iostream interface directly
+
+ {
+ std::ostringstream ss;
+ MPT_TEST_EXPECT_EQUAL(ss.tellp(), std::streampos(0));
+ }
+ {
+ std::ostringstream ss;
+ ss.seekp(0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
+ }
+ {
+ std::ostringstream ss;
+ ss.seekp(0, std::ios_base::beg);
+ MPT_TEST_EXPECT_EQUAL(!ss.fail(), true);
+ }
+ {
+ std::ostringstream ss;
+ ss.seekp(0, std::ios_base::cur);
+ MPT_TEST_EXPECT_EQUAL(!ss.fail(), true);
+ }
+ {
+ std::istringstream ss;
+ MPT_TEST_EXPECT_EQUAL(ss.tellg(), std::streampos(0));
+ }
+ {
+ std::istringstream ss;
+ ss.seekg(0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
+ }
+ {
+ std::istringstream ss;
+ ss.seekg(0, std::ios_base::beg);
+ MPT_TEST_EXPECT_EQUAL(!ss.fail(), true);
+ }
+ {
+ std::istringstream ss;
+ ss.seekg(0, std::ios_base::cur);
+ MPT_TEST_EXPECT_EQUAL(!ss.fail(), true);
+ }
+
+ {
+ std::ostringstream s;
+ char b = 23;
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(0));
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ s.seekp(0, std::ios_base::beg);
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(0));
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ s.write(&b, 1);
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(1));
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ s.seekp(0, std::ios_base::beg);
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(0));
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ s.seekp(0, std::ios_base::end);
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(1));
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.str(), std::string(1, b));
+ }
+
+ {
+ std::istringstream s;
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ s.seekg(0, std::ios_base::beg);
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ s.seekg(0, std::ios_base::end);
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ }
+
+ {
+ std::istringstream s("a");
+ char a = 0;
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ s.seekg(0, std::ios_base::beg);
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ s.read(&a, 1);
+ MPT_TEST_EXPECT_EQUAL(a, 'a');
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(1));
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ s.seekg(0, std::ios_base::beg);
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ s.seekg(0, std::ios_base::end);
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(1));
+ MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
+ MPT_TEST_EXPECT_EQUAL(std::string(1, a), std::string(1, 'a'));
+ }
+
+ // check that empty native and fixed stringstream both behaves correctly with out IO functions
+
+ {
+ std::ostringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(ss), 0);
+ }
+ {
+ std::ostringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true);
+ }
+ {
+ std::ostringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
+ }
+ {
+ std::ostringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true);
+ }
+ {
+ std::istringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(ss), 0);
+ }
+ {
+ std::istringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true);
+ }
+ {
+ std::istringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
+ }
+ {
+ std::istringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true);
+ }
+
+ {
+ std::ostringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(ss), 0);
+ }
+ {
+ std::ostringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true);
+ }
+ {
+ std::ostringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
+ }
+ {
+ std::ostringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true);
+ }
+ {
+ std::istringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(ss), 0);
+ }
+ {
+ std::istringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true);
+ }
+ {
+ std::istringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
+ }
+ {
+ std::istringstream ss;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true);
+ }
+
+ {
+ std::ostringstream s;
+ char b = 23;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteRaw(s, &b, 1), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(s.str(), std::string(1, b));
+ }
+
+ {
+ std::istringstream s;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ }
+
+ {
+ std::istringstream s("a");
+ char a = 0;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadRaw(s, &a, 1).size(), 1u);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(std::string(1, a), std::string(1, 'a'));
+ }
+
+ {
+ std::ostringstream s;
+ char b = 23;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteRaw(s, &b, 1), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(s.str(), std::string(1, b));
+ }
+
+ {
+ std::istringstream s;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ }
+
+ {
+ std::istringstream s("a");
+ char a = 0;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadRaw(s, &a, 1).size(), 1u);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
+ MPT_TEST_EXPECT_EQUAL(std::string(1, a), std::string(1, 'a'));
+ }
+
+ // General file I/O tests
+ {
+ // Verify that writing arrays does not confuse the compiler.
+ // This is both, compile-time and run-time cheking.
+ // Run-time in case some weird compiler gets confused by our templates
+ // and only writes the first array element.
+ std::ostringstream f;
+ uint16be data[2];
+ mpt::reset(data);
+ data[0] = 0x1234;
+ data[1] = 0x5678;
+ mpt::IO::Write(f, data);
+ MPT_TEST_EXPECT_EQUAL(f.str(), std::string("\x12\x34\x56\x78"));
+ }
+ {
+ std::ostringstream f;
+ std::vector<int16be> data;
+ data.resize(3);
+ data[0] = 0x1234;
+ data[1] = 0x5678;
+ data[2] = 0x1234;
+ mpt::IO::Write(f, data);
+ MPT_TEST_EXPECT_EQUAL(f.str(), std::string("\x12\x34\x56\x78\x12\x34"));
+ }
+ {
+ std::ostringstream f;
+ int16be data[3];
+ mpt::reset(data);
+ data[0] = 0x1234;
+ data[1] = 0x5678;
+ data[2] = 0x1234;
+ mpt::IO::Write(f, data);
+ MPT_TEST_EXPECT_EQUAL(f.str(), std::string("\x12\x34\x56\x78\x12\x34"));
+ }
+
+ {
+ auto TestAdaptive16 = [&](uint16 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) {
+ std::stringstream f;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteAdaptiveInt16LE(f, value, fixedSize), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(f), expected_size);
+ if (bytes) {
+ mpt::IO::SeekBegin(f);
+ for (mpt::IO::Offset i = 0; i < expected_size; ++i) {
+ uint8 val = 0;
+ mpt::IO::ReadIntLE<uint8>(f, val);
+ MPT_TEST_EXPECT_EQUAL(val, static_cast<uint8>(bytes[i]));
+ }
+ }
+ mpt::IO::SeekBegin(f);
+ uint16 result = 0;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadAdaptiveInt16LE(f, result), true);
+ MPT_TEST_EXPECT_EQUAL(result, value);
+ };
+ auto TestAdaptive32 = [&](uint32 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) {
+ std::stringstream f;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteAdaptiveInt32LE(f, value, fixedSize), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(f), expected_size);
+ if (bytes) {
+ mpt::IO::SeekBegin(f);
+ for (mpt::IO::Offset i = 0; i < expected_size; ++i) {
+ uint8 val = 0;
+ mpt::IO::ReadIntLE<uint8>(f, val);
+ MPT_TEST_EXPECT_EQUAL(val, static_cast<uint8>(bytes[i]));
+ }
+ }
+ mpt::IO::SeekBegin(f);
+ uint32 result = 0;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadAdaptiveInt32LE(f, result), true);
+ MPT_TEST_EXPECT_EQUAL(result, value);
+ };
+ auto TestAdaptive64 = [&](uint64 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) {
+ std::stringstream f;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteAdaptiveInt64LE(f, value, fixedSize), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(f), expected_size);
+ if (bytes) {
+ mpt::IO::SeekBegin(f);
+ for (mpt::IO::Offset i = 0; i < expected_size; ++i) {
+ uint8 val = 0;
+ mpt::IO::ReadIntLE<uint8>(f, val);
+ MPT_TEST_EXPECT_EQUAL(val, static_cast<uint8>(bytes[i]));
+ }
+ }
+ mpt::IO::SeekBegin(f);
+ uint64 result = 0;
+ MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadAdaptiveInt64LE(f, result), true);
+ MPT_TEST_EXPECT_EQUAL(result, value);
+ };
+ TestAdaptive16(0, 1, 0, "\x00");
+ TestAdaptive16(1, 1, 0, "\x02");
+ TestAdaptive16(2, 1, 0, nullptr);
+ TestAdaptive16(0x7f, 1, 0, nullptr);
+ TestAdaptive16(0x80, 2, 0, "\x01\x01");
+ TestAdaptive16(0x81, 2, 0, "\x03\x01");
+ TestAdaptive16(0x7fff, 2, 0, "\xff\xff");
+ TestAdaptive16(0, 1, 1, nullptr);
+ TestAdaptive16(1, 1, 1, nullptr);
+ TestAdaptive16(2, 1, 1, nullptr);
+ TestAdaptive16(0x7f, 1, 1, nullptr);
+ TestAdaptive16(0x80, 2, 0, nullptr);
+ TestAdaptive16(0x81, 2, 0, nullptr);
+ TestAdaptive16(0x7fff, 2, 0, nullptr);
+ TestAdaptive16(0, 2, 2, "\x01\x00");
+ TestAdaptive16(1, 2, 2, "\x03\x00");
+ TestAdaptive16(2, 2, 2, nullptr);
+ TestAdaptive16(0x7f, 2, 2, nullptr);
+ TestAdaptive16(0x80, 2, 2, nullptr);
+ TestAdaptive16(0x81, 2, 2, nullptr);
+ TestAdaptive16(0x7fff, 2, 2, nullptr);
+
+ TestAdaptive32(0, 1, 0, "\x00");
+ TestAdaptive32(1, 1, 0, nullptr);
+ TestAdaptive32(2, 1, 0, nullptr);
+ TestAdaptive32(0x3f, 1, 0, nullptr);
+ TestAdaptive32(0x40, 2, 0, "\x01\x01");
+ TestAdaptive32(0x41, 2, 0, "\x05\x01");
+ TestAdaptive32(0x7f, 2, 0, nullptr);
+ TestAdaptive32(0x80, 2, 0, nullptr);
+ TestAdaptive32(0x3fff, 2, 0, nullptr);
+ TestAdaptive32(0x4000, 3, 0, "\x02\x00\x01");
+ TestAdaptive32(0x4001, 3, 0, nullptr);
+ TestAdaptive32(0x3fffff, 3, 0, nullptr);
+ TestAdaptive32(0x400000, 4, 0, "\x03\x00\x00\x01");
+ TestAdaptive32(0x400001, 4, 0, nullptr);
+ TestAdaptive32(0x3fffffff, 4, 0, "\xff\xff\xff\xff");
+ TestAdaptive32(0, 2, 2, nullptr);
+ TestAdaptive32(1, 2, 2, nullptr);
+ TestAdaptive32(2, 2, 2, nullptr);
+ TestAdaptive32(0x3f, 2, 2, nullptr);
+ TestAdaptive32(0x40, 2, 2, nullptr);
+ TestAdaptive32(0x41, 2, 2, nullptr);
+ TestAdaptive32(0x7f, 2, 2, nullptr);
+ TestAdaptive32(0x80, 2, 2, nullptr);
+ TestAdaptive32(0x3fff, 2, 2, nullptr);
+ TestAdaptive32(0, 3, 3, nullptr);
+ TestAdaptive32(1, 3, 3, nullptr);
+ TestAdaptive32(2, 3, 3, nullptr);
+ TestAdaptive32(0x3f, 3, 3, nullptr);
+ TestAdaptive32(0x40, 3, 3, nullptr);
+ TestAdaptive32(0x41, 3, 3, nullptr);
+ TestAdaptive32(0x7f, 3, 3, nullptr);
+ TestAdaptive32(0x80, 3, 3, nullptr);
+ TestAdaptive32(0x3fff, 3, 3, nullptr);
+ TestAdaptive32(0x4000, 3, 3, nullptr);
+ TestAdaptive32(0x4001, 3, 3, nullptr);
+ TestAdaptive32(0x3fffff, 3, 3, nullptr);
+ TestAdaptive32(0, 4, 4, nullptr);
+ TestAdaptive32(1, 4, 4, nullptr);
+ TestAdaptive32(2, 4, 4, nullptr);
+ TestAdaptive32(0x3f, 4, 4, nullptr);
+ TestAdaptive32(0x40, 4, 4, nullptr);
+ TestAdaptive32(0x41, 4, 4, nullptr);
+ TestAdaptive32(0x7f, 4, 4, nullptr);
+ TestAdaptive32(0x80, 4, 4, nullptr);
+ TestAdaptive32(0x3fff, 4, 4, nullptr);
+ TestAdaptive32(0x4000, 4, 4, nullptr);
+ TestAdaptive32(0x4001, 4, 4, nullptr);
+ TestAdaptive32(0x3fffff, 4, 4, nullptr);
+ TestAdaptive32(0x400000, 4, 4, nullptr);
+ TestAdaptive32(0x400001, 4, 4, nullptr);
+ TestAdaptive32(0x3fffffff, 4, 4, nullptr);
+
+ TestAdaptive64(0, 1, 0, nullptr);
+ TestAdaptive64(1, 1, 0, nullptr);
+ TestAdaptive64(2, 1, 0, nullptr);
+ TestAdaptive64(0x3f, 1, 0, nullptr);
+ TestAdaptive64(0x40, 2, 0, nullptr);
+ TestAdaptive64(0x41, 2, 0, nullptr);
+ TestAdaptive64(0x7f, 2, 0, nullptr);
+ TestAdaptive64(0x80, 2, 0, nullptr);
+ TestAdaptive64(0x3fff, 2, 0, nullptr);
+ TestAdaptive64(0x4000, 4, 0, nullptr);
+ TestAdaptive64(0x4001, 4, 0, nullptr);
+ TestAdaptive64(0x3fffff, 4, 0, nullptr);
+ TestAdaptive64(0x400000, 4, 0, nullptr);
+ TestAdaptive64(0x400001, 4, 0, nullptr);
+ TestAdaptive64(0x3fffffff, 4, 0, nullptr);
+ TestAdaptive64(0x40000000, 8, 0, nullptr);
+ TestAdaptive64(0x40000001, 8, 0, nullptr);
+ TestAdaptive64(0x3fffffffffffffffull, 8, 0, nullptr);
+ TestAdaptive64(0, 2, 2, nullptr);
+ TestAdaptive64(1, 2, 2, nullptr);
+ TestAdaptive64(2, 2, 2, nullptr);
+ TestAdaptive64(0x3f, 2, 2, nullptr);
+ TestAdaptive64(0, 4, 4, nullptr);
+ TestAdaptive64(1, 4, 4, nullptr);
+ TestAdaptive64(2, 4, 4, nullptr);
+ TestAdaptive64(0x3f, 4, 4, nullptr);
+ TestAdaptive64(0x40, 4, 4, nullptr);
+ TestAdaptive64(0x41, 4, 4, nullptr);
+ TestAdaptive64(0x7f, 4, 4, nullptr);
+ TestAdaptive64(0x80, 4, 4, nullptr);
+ TestAdaptive64(0x3fff, 4, 4, nullptr);
+ TestAdaptive64(0, 8, 8, nullptr);
+ TestAdaptive64(1, 8, 8, nullptr);
+ TestAdaptive64(2, 8, 8, nullptr);
+ TestAdaptive64(0x3f, 8, 8, nullptr);
+ TestAdaptive64(0x40, 8, 8, nullptr);
+ TestAdaptive64(0x41, 8, 8, nullptr);
+ TestAdaptive64(0x7f, 8, 8, nullptr);
+ TestAdaptive64(0x80, 8, 8, nullptr);
+ TestAdaptive64(0x3fff, 8, 8, nullptr);
+ TestAdaptive64(0x4000, 8, 8, nullptr);
+ TestAdaptive64(0x4001, 8, 8, nullptr);
+ TestAdaptive64(0x3fffff, 8, 8, nullptr);
+ TestAdaptive64(0x400000, 8, 8, nullptr);
+ TestAdaptive64(0x400001, 8, 8, nullptr);
+ TestAdaptive64(0x3fffffff, 8, 8, nullptr);
+ TestAdaptive64(0x40000000, 8, 8, nullptr);
+ TestAdaptive64(0x40000001, 8, 8, nullptr);
+ TestAdaptive64(0x3fffffffffffffffull, 8, 8, nullptr);
+ }
+}
+
+} // namespace io
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_TESTS_IO_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/callbackstream.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/callbackstream.hpp
new file mode 100644
index 00000000..330c2dab
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/callbackstream.hpp
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_CALLBACKSTREAM_HPP
+#define MPT_IO_READ_CALLBACKSTREAM_HPP
+
+
+
+#include "mpt/base/integer.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+template <typename Tstream>
+struct CallbackStreamTemplate {
+ enum : int {
+ SeekSet = 0,
+ SeekCur = 1,
+ SeekEnd = 2
+ };
+ Tstream stream;
+ std::size_t (*read)(Tstream stream, void * dst, std::size_t bytes);
+ int (*seek)(Tstream stream, int64 offset, int whence);
+ int64 (*tell)(Tstream stream);
+};
+
+using CallbackStream = CallbackStreamTemplate<void *>;
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_CALLBACKSTREAM_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor.hpp
new file mode 100644
index 00000000..d2d6c62b
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor.hpp
@@ -0,0 +1,419 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILECURSOR_HPP
+#define MPT_IO_READ_FILECURSOR_HPP
+
+
+
+#include "mpt/base/alloc.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/span.hpp"
+
+#include <algorithm>
+#include <optional>
+#include <vector>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+// change to show warnings for functions which trigger pre-caching the whole file for unseekable streams
+//#define MPT_FILECURSOR_DEPRECATED [[deprecated]]
+#define MPT_FILECURSOR_DEPRECATED
+
+
+
+template <typename Ttraits, typename Tfilenametraits>
+class FileCursor {
+
+private:
+ using traits_type = Ttraits;
+ using filename_traits_type = Tfilenametraits;
+
+public:
+ using pos_type = typename traits_type::pos_type;
+
+ using data_type = typename traits_type::data_type;
+ using ref_data_type = typename traits_type::ref_data_type;
+ using shared_data_type = typename traits_type::shared_data_type;
+ using value_data_type = typename traits_type::value_data_type;
+
+ using filename_type = typename filename_traits_type::filename_type;
+ using shared_filename_type = typename filename_traits_type::shared_filename_type;
+
+protected:
+ shared_data_type SharedDataContainer() const {
+ return traits_type::get_shared(m_data);
+ }
+ ref_data_type DataContainer() const {
+ return traits_type::get_ref(m_data);
+ }
+
+ static value_data_type DataInitializer() {
+ return traits_type::make_data();
+ }
+ static value_data_type DataInitializer(mpt::const_byte_span data) {
+ return traits_type::make_data(data);
+ }
+
+ static value_data_type CreateChunkImpl(shared_data_type data, pos_type position, pos_type size) {
+ return traits_type::make_chunk(data, position, size);
+ }
+
+private:
+ data_type m_data;
+
+ pos_type streamPos; // Cursor location in the file
+
+ shared_filename_type m_fileName; // Filename that corresponds to this FileCursor. It is only set if this FileCursor represents the whole contents of fileName. May be nullopt.
+
+public:
+ // Initialize invalid file reader object.
+ FileCursor()
+ : m_data(DataInitializer())
+ , streamPos(0)
+ , m_fileName(nullptr) {
+ return;
+ }
+
+ // Initialize file reader object with pointer to data and data length.
+ template <typename Tbyte>
+ explicit FileCursor(mpt::span<Tbyte> bytedata, shared_filename_type filename = shared_filename_type{})
+ : m_data(DataInitializer(mpt::byte_cast<mpt::const_byte_span>(bytedata)))
+ , streamPos(0)
+ , m_fileName(std::move(filename)) {
+ return;
+ }
+
+ // Initialize file reader object based on an existing file reader object window.
+ explicit FileCursor(value_data_type other, shared_filename_type filename = shared_filename_type{})
+ : m_data(std::move(other))
+ , streamPos(0)
+ , m_fileName(std::move(filename)) {
+ return;
+ }
+
+public:
+ std::optional<filename_type> GetOptionalFileName() const {
+ return filename_traits_type::get_optional_filename(m_fileName);
+ }
+
+ // Returns true if the object points to a valid (non-empty) stream.
+ operator bool() const {
+ return IsValid();
+ }
+
+ // Returns true if the object points to a valid (non-empty) stream.
+ bool IsValid() const {
+ return DataContainer().IsValid();
+ }
+
+ // Reset cursor to first byte in file.
+ void Rewind() {
+ streamPos = 0;
+ }
+
+ // Seek to a position in the mapped file.
+ // Returns false if position is invalid.
+ bool Seek(pos_type position) {
+ if (position <= streamPos) {
+ streamPos = position;
+ return true;
+ }
+ if (position <= DataContainer().GetLength()) {
+ streamPos = position;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // Increases position by skipBytes.
+ // Returns true if skipBytes could be skipped or false if the file end was reached earlier.
+ bool Skip(pos_type skipBytes) {
+ if (CanRead(skipBytes)) {
+ streamPos += skipBytes;
+ return true;
+ } else {
+ streamPos = DataContainer().GetLength();
+ return false;
+ }
+ }
+
+ // Decreases position by skipBytes.
+ // Returns true if skipBytes could be skipped or false if the file start was reached earlier.
+ bool SkipBack(pos_type skipBytes) {
+ if (streamPos >= skipBytes) {
+ streamPos -= skipBytes;
+ return true;
+ } else {
+ streamPos = 0;
+ return false;
+ }
+ }
+
+ // Returns cursor position in the mapped file.
+ pos_type GetPosition() const {
+ return streamPos;
+ }
+
+ // Return true IFF seeking and GetLength() is fast.
+ // In particular, it returns false for unseekable stream where GetLength()
+ // requires pre-caching.
+ bool HasFastGetLength() const {
+ return DataContainer().HasFastGetLength();
+ }
+
+ // Returns size of the mapped file in bytes.
+ MPT_FILECURSOR_DEPRECATED pos_type GetLength() const {
+ // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file
+ return DataContainer().GetLength();
+ }
+
+ // Return byte count between cursor position and end of file, i.e. how many bytes can still be read.
+ MPT_FILECURSOR_DEPRECATED pos_type BytesLeft() const {
+ // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file
+ return DataContainer().GetLength() - streamPos;
+ }
+
+ bool EndOfFile() const {
+ return !CanRead(1);
+ }
+
+ bool NoBytesLeft() const {
+ return !CanRead(1);
+ }
+
+ // Check if "amount" bytes can be read from the current position in the stream.
+ bool CanRead(pos_type amount) const {
+ return DataContainer().CanRead(streamPos, amount);
+ }
+
+ // Check if file size is at least size, without potentially caching the whole file to query the exact file length.
+ bool LengthIsAtLeast(pos_type size) const {
+ return DataContainer().CanRead(0, size);
+ }
+
+ // Check if file size is exactly size, without potentially caching the whole file if it is larger.
+ bool LengthIs(pos_type size) const {
+ return DataContainer().CanRead(0, size) && !DataContainer().CanRead(size, 1);
+ }
+
+protected:
+ FileCursor CreateChunk(pos_type position, pos_type length) const {
+ pos_type readableLength = DataContainer().GetReadableLength(position, length);
+ if (readableLength == 0)
+ {
+ return FileCursor();
+ }
+ return FileCursor(CreateChunkImpl(SharedDataContainer(), position, std::min(length, DataContainer().GetLength() - position)));
+ }
+
+public:
+ // Create a new FileCursor object for parsing a sub chunk at a given position with a given length.
+ // The file cursor is not modified.
+ FileCursor GetChunkAt(pos_type position, pos_type length) const {
+ return CreateChunk(position, length);
+ }
+
+ // Create a new FileCursor object for parsing a sub chunk at the current position with a given length.
+ // The file cursor is not advanced.
+ FileCursor GetChunk(pos_type length) {
+ return CreateChunk(streamPos, length);
+ }
+ // Create a new FileCursor object for parsing a sub chunk at the current position with a given length.
+ // The file cursor is advanced by "length" bytes.
+ FileCursor ReadChunk(pos_type length) {
+ pos_type position = streamPos;
+ Skip(length);
+ return CreateChunk(position, length);
+ }
+
+ class PinnedView {
+ private:
+ std::size_t size_;
+ const std::byte * pinnedData;
+ std::vector<std::byte> cache;
+
+ private:
+ void Init(const FileCursor & file, std::size_t size) {
+ size_ = 0;
+ pinnedData = nullptr;
+ if (!file.CanRead(size)) {
+ size = file.BytesLeft();
+ }
+ size_ = size;
+ if (file.DataContainer().HasPinnedView()) {
+ pinnedData = file.DataContainer().GetRawData() + file.GetPosition();
+ } else {
+ cache.resize(size_);
+ if (!cache.empty()) {
+ file.GetRaw(mpt::as_span(cache));
+ }
+ }
+ }
+
+ public:
+ PinnedView()
+ : size_(0)
+ , pinnedData(nullptr) {
+ }
+ PinnedView(const FileCursor & file) {
+ Init(file, file.BytesLeft());
+ }
+ PinnedView(const FileCursor & file, std::size_t size) {
+ Init(file, size);
+ }
+ PinnedView(FileCursor & file, bool advance) {
+ Init(file, file.BytesLeft());
+ if (advance)
+ {
+ file.Skip(size_);
+ }
+ }
+ PinnedView(FileCursor & file, std::size_t size, bool advance) {
+ Init(file, size);
+ if (advance) {
+ file.Skip(size_);
+ }
+ }
+
+ public:
+ mpt::const_byte_span GetSpan() const {
+ if (pinnedData) {
+ return mpt::as_span(pinnedData, size_);
+ } else if (!cache.empty()) {
+ return mpt::as_span(cache);
+ } else {
+ return mpt::const_byte_span();
+ }
+ }
+ mpt::const_byte_span span() const {
+ return GetSpan();
+ }
+ void invalidate() {
+ size_ = 0;
+ pinnedData = nullptr;
+ cache = std::vector<std::byte>();
+ }
+ const std::byte * data() const {
+ return span().data();
+ }
+ std::size_t size() const {
+ return size_;
+ }
+ mpt::const_byte_span::pointer begin() const {
+ return span().data();
+ }
+ mpt::const_byte_span::pointer end() const {
+ return span().data() + span().size();
+ }
+ mpt::const_byte_span::const_pointer cbegin() const {
+ return span().data();
+ }
+ mpt::const_byte_span::const_pointer cend() const {
+ return span().data() + span().size();
+ }
+ };
+
+ // Returns a pinned view into the remaining raw data from cursor position.
+ PinnedView GetPinnedView() const {
+ return PinnedView(*this);
+ }
+ // Returns a pinned view into the remeining raw data from cursor position, clamped at size.
+ PinnedView GetPinnedView(std::size_t size) const {
+ return PinnedView(*this, size);
+ }
+
+ // Returns a pinned view into the remeining raw data from cursor position.
+ // File cursor is advaned by the size of the returned pinned view.
+ PinnedView ReadPinnedView() {
+ return PinnedView(*this, true);
+ }
+ // Returns a pinned view into the remeining raw data from cursor position, clamped at size.
+ // File cursor is advaned by the size of the returned pinned view.
+ PinnedView ReadPinnedView(std::size_t size) {
+ return PinnedView(*this, size, true);
+ }
+
+ // Returns raw stream data at cursor position.
+ // Should only be used if absolutely necessary, for example for sample reading, or when used with a small chunk of the file retrieved by ReadChunk().
+ // Use GetPinnedView(size) whenever possible.
+ MPT_FILECURSOR_DEPRECATED mpt::const_byte_span GetRawData() const {
+ // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file
+ return mpt::span(DataContainer().GetRawData() + streamPos, DataContainer().GetLength() - streamPos);
+ }
+ template <typename T>
+ MPT_FILECURSOR_DEPRECATED mpt::span<const T> GetRawData() const {
+ // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file
+ return mpt::span(mpt::byte_cast<const T *>(DataContainer().GetRawData() + streamPos), DataContainer().GetLength() - streamPos);
+ }
+
+ mpt::byte_span GetRawWithOffset(std::size_t offset, mpt::byte_span dst) const {
+ return DataContainer().Read(streamPos + offset, dst);
+ }
+ template <typename Tspan>
+ Tspan GetRawWithOffset(std::size_t offset, Tspan dst) const {
+ return mpt::byte_cast<Tspan>(DataContainer().Read(streamPos + offset, mpt::byte_cast<mpt::byte_span>(dst)));
+ }
+
+ mpt::byte_span GetRaw(mpt::byte_span dst) const {
+ return DataContainer().Read(streamPos, dst);
+ }
+ template <typename Tspan>
+ Tspan GetRaw(Tspan dst) const {
+ return mpt::byte_cast<Tspan>(DataContainer().Read(streamPos, mpt::byte_cast<mpt::byte_span>(dst)));
+ }
+
+ mpt::byte_span ReadRaw(mpt::byte_span dst) {
+ mpt::byte_span result = DataContainer().Read(streamPos, dst);
+ streamPos += result.size();
+ return result;
+ }
+ template <typename Tspan>
+ Tspan ReadRaw(Tspan dst) {
+ Tspan result = mpt::byte_cast<Tspan>(DataContainer().Read(streamPos, mpt::byte_cast<mpt::byte_span>(dst)));
+ streamPos += result.size();
+ return result;
+ }
+
+ std::vector<std::byte> GetRawDataAsByteVector() const {
+ PinnedView view = GetPinnedView();
+ return mpt::make_vector(view.span());
+ }
+ std::vector<std::byte> ReadRawDataAsByteVector() {
+ PinnedView view = ReadPinnedView();
+ return mpt::make_vector(view.span());
+ }
+ std::vector<std::byte> GetRawDataAsByteVector(std::size_t size) const {
+ PinnedView view = GetPinnedView(size);
+ return mpt::make_vector(view.span());
+ }
+ std::vector<std::byte> ReadRawDataAsByteVector(std::size_t size) {
+ PinnedView view = ReadPinnedView(size);
+ return mpt::make_vector(view.span());
+ }
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILECURSOR_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_callbackstream.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_callbackstream.hpp
new file mode 100644
index 00000000..14fccef7
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_callbackstream.hpp
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILECURSOR_CALLBACKSTREAM_HPP
+#define MPT_IO_READ_FILECURSOR_CALLBACKSTREAM_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+#include "mpt/io_read/callbackstream.hpp"
+#include "mpt/io_read/filecursor.hpp"
+#include "mpt/io_read/filecursor_traits_filedata.hpp"
+#include "mpt/io_read/filecursor_filename_traits.hpp"
+#include "mpt/io_read/filedata_callbackstream.hpp"
+
+#include <memory>
+#include <utility>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+// Initialize file reader object with a CallbackStream.
+template <typename Tpath, typename Tstream>
+inline FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>> make_FileCursor(CallbackStreamTemplate<Tstream> s, std::shared_ptr<Tpath> filename = nullptr) {
+ if (FileDataCallbackStreamTemplate<Tstream>::IsSeekable(s)) {
+ return FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>>(std::static_pointer_cast<IFileData>(std::make_shared<FileDataCallbackStreamSeekableTemplate<Tstream>>(s)), std::move(filename));
+ } else {
+ return FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>>(std::static_pointer_cast<IFileData>(std::make_shared<FileDataCallbackStreamUnseekableTemplate<Tstream>>(s)), std::move(filename));
+ }
+}
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILECURSOR_CALLBACKSTREAM_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_filename_traits.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_filename_traits.hpp
new file mode 100644
index 00000000..b1621266
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_filename_traits.hpp
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILECURSOR_FILENAME_TRAITS_HPP
+#define MPT_IO_READ_FILECURSOR_FILENAME_TRAITS_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+
+#include <memory>
+#include <optional>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+class FileCursorFilenameTraitsNone {
+
+public:
+ struct empty_type { };
+
+ using filename_type = empty_type;
+ using shared_filename_type = empty_type;
+
+ static std::optional<filename_type> get_optional_filename(shared_filename_type /* filename */) {
+ return std::nullopt;
+ }
+};
+
+template <typename Tpath>
+class FileCursorFilenameTraits {
+
+public:
+ using filename_type = Tpath;
+ using shared_filename_type = std::shared_ptr<Tpath>;
+
+ static std::optional<filename_type> get_optional_filename(shared_filename_type filename) {
+ if (!filename) {
+ return std::nullopt;
+ }
+ return *filename;
+ }
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILECURSOR_FILENAME_TRAITS_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_memory.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_memory.hpp
new file mode 100644
index 00000000..78d954d1
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_memory.hpp
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILECURSOR_MEMORY_HPP
+#define MPT_IO_READ_FILECURSOR_MEMORY_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/span.hpp"
+#include "mpt/io_read/filecursor.hpp"
+#include "mpt/io_read/filecursor_traits_filedata.hpp"
+#include "mpt/io_read/filecursor_filename_traits.hpp"
+
+#include <memory>
+#include <utility>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+// Initialize file reader object with pointer to data and data length.
+template <typename Tpath, typename Tbyte>
+inline FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>> make_FileCursor(mpt::span<Tbyte> bytedata, std::shared_ptr<Tpath> filename = nullptr) {
+ return FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>>(mpt::byte_cast<mpt::const_byte_span>(bytedata), std::move(filename));
+}
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILECURSOR_STDSTREAM_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_stdstream.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_stdstream.hpp
new file mode 100644
index 00000000..b68d9e9a
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_stdstream.hpp
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILECURSOR_STDSTREAM_HPP
+#define MPT_IO_READ_FILECURSOR_STDSTREAM_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+#include "mpt/io_read/filecursor.hpp"
+#include "mpt/io_read/filecursor_traits_filedata.hpp"
+#include "mpt/io_read/filecursor_filename_traits.hpp"
+#include "mpt/io_read/filedata_stdstream.hpp"
+
+#include <istream>
+#include <memory>
+#include <utility>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+// Initialize file reader object with a std::istream.
+template <typename Tpath>
+inline FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>> make_FileCursor(std::istream & s, std::shared_ptr<Tpath> filename = nullptr) {
+ if (FileDataStdStream::IsSeekable(s)) {
+ return FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>>(std::static_pointer_cast<IFileData>(std::make_shared<FileDataStdStreamSeekable>(s)), std::move(filename));
+ } else {
+ return FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>>(std::static_pointer_cast<IFileData>(std::make_shared<FileDataStdStreamUnseekable>(s)), std::move(filename));
+ }
+}
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILECURSOR_STDSTREAM_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_traits_filedata.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_traits_filedata.hpp
new file mode 100644
index 00000000..2d3ef1f9
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_traits_filedata.hpp
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILECURSOR_TRAITS_FILEDATA_HPP
+#define MPT_IO_READ_FILECURSOR_TRAITS_FILEDATA_HPP
+
+
+
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/io_read/filedata.hpp"
+#include "mpt/io_read/filedata_base.hpp"
+#include "mpt/io_read/filedata_memory.hpp"
+
+#include <memory>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+class FileCursorTraitsFileData {
+
+public:
+ using pos_type = IFileData::pos_type;
+
+ using data_type = std::shared_ptr<const IFileData>;
+ using ref_data_type = const IFileData &;
+ using shared_data_type = std::shared_ptr<const IFileData>;
+ using value_data_type = std::shared_ptr<const IFileData>;
+
+ static shared_data_type get_shared(const data_type & data) {
+ return data;
+ }
+ static ref_data_type get_ref(const data_type & data) {
+ return *data;
+ }
+
+ static value_data_type make_data() {
+ return std::make_shared<FileDataDummy>();
+ }
+ static value_data_type make_data(mpt::const_byte_span data) {
+ return std::make_shared<FileDataMemory>(data);
+ }
+
+ static value_data_type make_chunk(shared_data_type data, pos_type position, pos_type size) {
+ return std::static_pointer_cast<IFileData>(std::make_shared<FileDataWindow>(data, position, size));
+ }
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILECURSOR_TRAITS_FILEDATA_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_traits_memory.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_traits_memory.hpp
new file mode 100644
index 00000000..254fc640
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filecursor_traits_memory.hpp
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILECURSOR_TRAITS_MEMORY_HPP
+#define MPT_IO_READ_FILECURSOR_TRAITS_MEMORY_HPP
+
+
+
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/span.hpp"
+#include "mpt/io_read/filedata.hpp"
+#include "mpt/io_read/filedata_memory.hpp"
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+class FileCursorTraitsMemory {
+
+public:
+ using pos_type = FileDataMemory::pos_type;
+
+ using data_type = FileDataMemory;
+ using ref_data_type = const FileDataMemory &;
+ using shared_data_type = const FileDataMemory &;
+ using value_data_type = FileDataMemory;
+
+ static shared_data_type get_shared(const data_type & data) {
+ return data;
+ }
+ static ref_data_type get_ref(const data_type & data) {
+ return data;
+ }
+
+ static value_data_type make_data() {
+ return mpt::const_byte_span();
+ }
+ static value_data_type make_data(mpt::const_byte_span data) {
+ return data;
+ }
+
+ static value_data_type make_chunk(shared_data_type data, pos_type position, pos_type size) {
+ return mpt::as_span(data.GetRawData() + position, size);
+ }
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILECURSOR_TRAITS_MEMORY_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata.hpp
new file mode 100644
index 00000000..afaf8db9
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata.hpp
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILEDATA_HPP
+#define MPT_IO_READ_FILEDATA_HPP
+
+
+
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+
+#include <algorithm>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+class IFileData {
+public:
+ typedef std::size_t pos_type;
+
+protected:
+ IFileData() = default;
+
+public:
+ IFileData(const IFileData &) = default;
+ IFileData & operator=(const IFileData &) = default;
+ virtual ~IFileData() = default;
+
+public:
+ virtual bool IsValid() const = 0;
+ virtual bool HasFastGetLength() const = 0;
+ virtual bool HasPinnedView() const = 0;
+ virtual const std::byte * GetRawData() const = 0;
+ virtual pos_type GetLength() const = 0;
+ virtual mpt::byte_span Read(pos_type pos, mpt::byte_span dst) const = 0;
+
+ virtual bool CanRead(pos_type pos, std::size_t length) const {
+ pos_type dataLength = GetLength();
+ if ((pos == dataLength) && (length == 0)) {
+ return true;
+ }
+ if (pos >= dataLength) {
+ return false;
+ }
+ return length <= dataLength - pos;
+ }
+
+ virtual std::size_t GetReadableLength(pos_type pos, std::size_t length) const {
+ pos_type dataLength = GetLength();
+ if (pos >= dataLength) {
+ return 0;
+ }
+ return std::min(length, dataLength - pos);
+ }
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILEDATA_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base.hpp
new file mode 100644
index 00000000..fba96a8f
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base.hpp
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILEDATA_BASE_HPP
+#define MPT_IO_READ_FILEDATA_BASE_HPP
+
+
+
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/io_read/filedata.hpp"
+
+#include <algorithm>
+#include <memory>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+class FileDataDummy : public IFileData {
+public:
+ FileDataDummy() { }
+
+public:
+ bool IsValid() const override {
+ return false;
+ }
+
+ bool HasFastGetLength() const override {
+ return true;
+ }
+
+ bool HasPinnedView() const override {
+ return true;
+ }
+
+ const std::byte * GetRawData() const override {
+ return nullptr;
+ }
+
+ pos_type GetLength() const override {
+ return 0;
+ }
+ mpt::byte_span Read(pos_type /* pos */, mpt::byte_span dst) const override {
+ return dst.first(0);
+ }
+};
+
+
+class FileDataWindow : public IFileData {
+private:
+ std::shared_ptr<const IFileData> data;
+ const pos_type dataOffset;
+ const pos_type dataLength;
+
+public:
+ FileDataWindow(std::shared_ptr<const IFileData> src, pos_type off, pos_type len)
+ : data(src)
+ , dataOffset(off)
+ , dataLength(len) { }
+
+ bool IsValid() const override {
+ return data->IsValid();
+ }
+ bool HasFastGetLength() const override {
+ return data->HasFastGetLength();
+ }
+ bool HasPinnedView() const override {
+ return data->HasPinnedView();
+ }
+ const std::byte * GetRawData() const override {
+ return data->GetRawData() + dataOffset;
+ }
+ pos_type GetLength() const override {
+ return dataLength;
+ }
+ mpt::byte_span Read(pos_type pos, mpt::byte_span dst) const override {
+ if (pos >= dataLength) {
+ return dst.first(0);
+ }
+ return data->Read(dataOffset + pos, dst.first(std::min(dst.size(), dataLength - pos)));
+ }
+ bool CanRead(pos_type pos, std::size_t length) const override {
+ if ((pos == dataLength) && (length == 0)) {
+ return true;
+ }
+ if (pos >= dataLength) {
+ return false;
+ }
+ return (length <= dataLength - pos);
+ }
+ pos_type GetReadableLength(pos_type pos, std::size_t length) const override {
+ if (pos >= dataLength) {
+ return 0;
+ }
+ return std::min(length, dataLength - pos);
+ }
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILEDATA_BASE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base_buffered.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base_buffered.hpp
new file mode 100644
index 00000000..637be25c
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base_buffered.hpp
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILEDATA_BASE_BUFFERED_HPP
+#define MPT_IO_READ_FILEDATA_BASE_BUFFERED_HPP
+
+
+
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/numeric.hpp"
+#include "mpt/io/base.hpp"
+#include "mpt/io_read/filedata.hpp"
+#include "mpt/io_read/filedata_base_seekable.hpp"
+
+#include <algorithm>
+#include <vector>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+class FileDataSeekableBuffered : public FileDataSeekable {
+
+private:
+ enum : std::size_t {
+ CHUNK_SIZE = mpt::IO::BUFFERSIZE_SMALL,
+ BUFFER_SIZE = mpt::IO::BUFFERSIZE_NORMAL
+ };
+ enum : std::size_t {
+ NUM_CHUNKS = BUFFER_SIZE / CHUNK_SIZE
+ };
+ struct chunk_info {
+ pos_type ChunkOffset = 0;
+ pos_type ChunkLength = 0;
+ bool ChunkValid = false;
+ };
+ mutable std::vector<std::byte> m_Buffer = std::vector<std::byte>(BUFFER_SIZE);
+
+ mpt::byte_span chunk_data(std::size_t chunkIndex) const {
+ return mpt::byte_span(m_Buffer.data() + (chunkIndex * CHUNK_SIZE), CHUNK_SIZE);
+ }
+
+ mutable std::array<chunk_info, NUM_CHUNKS> m_ChunkInfo = {};
+ mutable std::array<std::size_t, NUM_CHUNKS> m_ChunkIndexLRU = {};
+
+ std::size_t InternalFillPageAndReturnIndex(pos_type pos) const {
+ pos = mpt::align_down(pos, static_cast<pos_type>(CHUNK_SIZE));
+ for (std::size_t chunkLRUIndex = 0; chunkLRUIndex < NUM_CHUNKS; ++chunkLRUIndex) {
+ std::size_t chunkIndex = m_ChunkIndexLRU[chunkLRUIndex];
+ if (m_ChunkInfo[chunkIndex].ChunkValid && (m_ChunkInfo[chunkIndex].ChunkOffset == pos)) {
+ std::size_t chunk = std::move(m_ChunkIndexLRU[chunkLRUIndex]);
+ std::move_backward(m_ChunkIndexLRU.begin(), m_ChunkIndexLRU.begin() + chunkLRUIndex, m_ChunkIndexLRU.begin() + (chunkLRUIndex + 1));
+ m_ChunkIndexLRU[0] = std::move(chunk);
+ return chunkIndex;
+ }
+ }
+ {
+ std::size_t chunk = std::move(m_ChunkIndexLRU[NUM_CHUNKS - 1]);
+ std::move_backward(m_ChunkIndexLRU.begin(), m_ChunkIndexLRU.begin() + (NUM_CHUNKS - 1), m_ChunkIndexLRU.begin() + NUM_CHUNKS);
+ m_ChunkIndexLRU[0] = std::move(chunk);
+ }
+ std::size_t chunkIndex = m_ChunkIndexLRU[0];
+ chunk_info & chunk = m_ChunkInfo[chunkIndex];
+ chunk.ChunkOffset = pos;
+ chunk.ChunkLength = InternalReadBuffered(pos, chunk_data(chunkIndex)).size();
+ chunk.ChunkValid = true;
+ return chunkIndex;
+ }
+
+protected:
+ FileDataSeekableBuffered(pos_type streamLength_)
+ : FileDataSeekable(streamLength_) {
+ return;
+ }
+
+private:
+ mpt::byte_span InternalReadSeekable(pos_type pos, mpt::byte_span dst) const override {
+ pos_type totalRead = 0;
+ std::byte * pdst = dst.data();
+ std::size_t count = dst.size();
+ while (count > 0) {
+ std::size_t chunkIndex = InternalFillPageAndReturnIndex(pos);
+ pos_type pageSkip = pos - m_ChunkInfo[chunkIndex].ChunkOffset;
+ pos_type chunkWanted = std::min(static_cast<pos_type>(CHUNK_SIZE) - pageSkip, count);
+ pos_type chunkGot = (m_ChunkInfo[chunkIndex].ChunkLength > pageSkip) ? (m_ChunkInfo[chunkIndex].ChunkLength - pageSkip) : 0;
+ pos_type chunk = std::min(chunkWanted, chunkGot);
+ std::copy(chunk_data(chunkIndex).data() + pageSkip, chunk_data(chunkIndex).data() + pageSkip + chunk, pdst);
+ pos += chunk;
+ pdst += chunk;
+ totalRead += chunk;
+ count -= chunk;
+ if (chunkWanted > chunk) {
+ return dst.first(totalRead);
+ }
+ }
+ return dst.first(totalRead);
+ }
+
+ virtual mpt::byte_span InternalReadBuffered(pos_type pos, mpt::byte_span dst) const = 0;
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILEDATA_BASE_BUFFERED_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base_seekable.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base_seekable.hpp
new file mode 100644
index 00000000..b2324c7d
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base_seekable.hpp
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILEDATA_BASE_SEEKABLE_HPP
+#define MPT_IO_READ_FILEDATA_BASE_SEEKABLE_HPP
+
+
+
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/io_read/filedata.hpp"
+
+#include <algorithm>
+#include <vector>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+class FileDataSeekable : public IFileData {
+
+private:
+ pos_type streamLength;
+
+ mutable bool cached;
+ mutable std::vector<std::byte> cache;
+
+protected:
+ FileDataSeekable(pos_type streamLength_)
+ : streamLength(streamLength_)
+ , cached(false) {
+ return;
+ }
+
+
+private:
+ void CacheStream() const {
+ if (cached) {
+ return;
+ }
+ cache.resize(streamLength);
+ InternalReadSeekable(0, mpt::as_span(cache));
+ cached = true;
+ }
+
+public:
+ bool IsValid() const override {
+ return true;
+ }
+
+ bool HasFastGetLength() const override {
+ return true;
+ }
+
+ bool HasPinnedView() const override {
+ return cached;
+ }
+
+ const std::byte * GetRawData() const override {
+ CacheStream();
+ return cache.data();
+ }
+
+ pos_type GetLength() const override {
+ return streamLength;
+ }
+
+ mpt::byte_span Read(pos_type pos, mpt::byte_span dst) const override {
+ if (cached) {
+ IFileData::pos_type cache_avail = std::min(IFileData::pos_type(cache.size()) - pos, dst.size());
+ std::copy(cache.begin() + pos, cache.begin() + pos + cache_avail, dst.data());
+ return dst.first(cache_avail);
+ } else {
+ return InternalReadSeekable(pos, dst);
+ }
+ }
+
+private:
+ virtual mpt::byte_span InternalReadSeekable(pos_type pos, mpt::byte_span dst) const = 0;
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILEDATA_BASE_SEEKABLE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base_unseekable.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base_unseekable.hpp
new file mode 100644
index 00000000..78d9ca45
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_base_unseekable.hpp
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILEDATA_BASE_UNSEEKABLE_HPP
+#define MPT_IO_READ_FILEDATA_BASE_UNSEEKABLE_HPP
+
+
+
+#include "mpt/base/algorithm.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/numeric.hpp"
+#include "mpt/io/base.hpp"
+#include "mpt/io_read/filedata.hpp"
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+class FileDataUnseekable : public IFileData {
+
+private:
+ mutable std::vector<std::byte> cache;
+ mutable std::size_t cachesize;
+ mutable bool streamFullyCached;
+
+protected:
+ FileDataUnseekable()
+ : cachesize(0)
+ , streamFullyCached(false) {
+ return;
+ }
+
+private:
+ enum : std::size_t {
+ QUANTUM_SIZE = mpt::IO::BUFFERSIZE_SMALL,
+ BUFFER_SIZE = mpt::IO::BUFFERSIZE_NORMAL
+ };
+
+ void EnsureCacheBuffer(std::size_t requiredbuffersize) const {
+ if (cache.size() >= cachesize + requiredbuffersize) {
+ return;
+ }
+ if (cache.size() == 0) {
+ cache.resize(mpt::align_up<std::size_t>(cachesize + requiredbuffersize, BUFFER_SIZE));
+ } else if (mpt::exponential_grow(cache.size()) < cachesize + requiredbuffersize) {
+ cache.resize(mpt::align_up<std::size_t>(cachesize + requiredbuffersize, BUFFER_SIZE));
+ } else {
+ cache.resize(mpt::exponential_grow(cache.size()));
+ }
+ }
+
+ void CacheStream() const {
+ if (streamFullyCached) {
+ return;
+ }
+ while (!InternalEof()) {
+ EnsureCacheBuffer(BUFFER_SIZE);
+ std::size_t readcount = InternalReadUnseekable(mpt::span(&cache[cachesize], BUFFER_SIZE)).size();
+ cachesize += readcount;
+ }
+ streamFullyCached = true;
+ }
+
+
+ void CacheStreamUpTo(pos_type pos, pos_type length) const {
+ if (streamFullyCached) {
+ return;
+ }
+ if (length > std::numeric_limits<pos_type>::max() - pos) {
+ length = std::numeric_limits<pos_type>::max() - pos;
+ }
+ std::size_t target = mpt::saturate_cast<std::size_t>(pos + length);
+ if (target <= cachesize) {
+ return;
+ }
+ std::size_t alignedpos = mpt::align_up<std::size_t>(target, QUANTUM_SIZE);
+ std::size_t needcount = alignedpos - cachesize;
+ EnsureCacheBuffer(needcount);
+ std::size_t readcount = InternalReadUnseekable(mpt::span(&cache[cachesize], alignedpos - cachesize)).size();
+ cachesize += readcount;
+ if (!InternalEof()) {
+ // can read further
+ return;
+ }
+ streamFullyCached = true;
+ }
+
+private:
+ void ReadCached(pos_type pos, mpt::byte_span dst) const {
+ std::copy(cache.begin() + pos, cache.begin() + pos + dst.size(), dst.data());
+ }
+
+public:
+ bool IsValid() const override {
+ return true;
+ }
+
+ bool HasFastGetLength() const override {
+ return false;
+ }
+
+ bool HasPinnedView() const override {
+ return true; // we have the cache which is required for seeking anyway
+ }
+
+ const std::byte * GetRawData() const override {
+ CacheStream();
+ return cache.data();
+ }
+
+ pos_type GetLength() const override {
+ CacheStream();
+ return cachesize;
+ }
+
+ mpt::byte_span Read(pos_type pos, mpt::byte_span dst) const override {
+ CacheStreamUpTo(pos, dst.size());
+ if (pos >= IFileData::pos_type(cachesize)) {
+ return dst.first(0);
+ }
+ IFileData::pos_type cache_avail = std::min(IFileData::pos_type(cachesize) - pos, dst.size());
+ ReadCached(pos, dst.subspan(0, cache_avail));
+ return dst.subspan(0, cache_avail);
+ }
+
+ bool CanRead(pos_type pos, std::size_t length) const override {
+ CacheStreamUpTo(pos, length);
+ if ((pos == IFileData::pos_type(cachesize)) && (length == 0)) {
+ return true;
+ }
+ if (pos >= IFileData::pos_type(cachesize)) {
+ return false;
+ }
+ return length <= IFileData::pos_type(cachesize) - pos;
+ }
+
+ std::size_t GetReadableLength(pos_type pos, std::size_t length) const override {
+ CacheStreamUpTo(pos, length);
+ if (pos >= cachesize) {
+ return 0;
+ }
+ return std::min(static_cast<IFileData::pos_type>(cachesize) - pos, length);
+ }
+
+private:
+ virtual bool InternalEof() const = 0;
+ virtual mpt::byte_span InternalReadUnseekable(mpt::byte_span dst) const = 0;
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILEDATA_BASE_UNSEEKABLE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_callbackstream.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_callbackstream.hpp
new file mode 100644
index 00000000..ae72ab59
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_callbackstream.hpp
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILEDATA_CALLBACKSTREAM_HPP
+#define MPT_IO_READ_FILEDATA_CALLBACKSTREAM_HPP
+
+
+
+#include "mpt/base/integer.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/io_read/callbackstream.hpp"
+#include "mpt/io_read/filedata.hpp"
+#include "mpt/io_read/filedata_base_seekable.hpp"
+#include "mpt/io_read/filedata_base_unseekable.hpp"
+
+#include <algorithm>
+#include <memory>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+template <typename Tstream>
+class FileDataCallbackStreamTemplate {
+public:
+ static bool IsSeekable(CallbackStreamTemplate<Tstream> stream) {
+ if (!stream.stream) {
+ return false;
+ }
+ if (!stream.seek) {
+ return false;
+ }
+ if (!stream.tell) {
+ return false;
+ }
+ int64 oldpos = stream.tell(stream.stream);
+ if (oldpos < 0) {
+ return false;
+ }
+ if (stream.seek(stream.stream, 0, CallbackStream::SeekSet) < 0) {
+ stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
+ return false;
+ }
+ if (stream.seek(stream.stream, 0, CallbackStream::SeekEnd) < 0) {
+ stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
+ return false;
+ }
+ int64 length = stream.tell(stream.stream);
+ if (length < 0) {
+ stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
+ return false;
+ }
+ stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
+ return true;
+ }
+
+ static IFileData::pos_type GetLength(CallbackStreamTemplate<Tstream> stream) {
+ if (!stream.stream) {
+ return 0;
+ }
+ if (!stream.seek) {
+ return false;
+ }
+ if (!stream.tell) {
+ return false;
+ }
+ int64 oldpos = stream.tell(stream.stream);
+ if (oldpos < 0) {
+ return 0;
+ }
+ if (stream.seek(stream.stream, 0, CallbackStream::SeekSet) < 0) {
+ stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
+ return 0;
+ }
+ if (stream.seek(stream.stream, 0, CallbackStream::SeekEnd) < 0) {
+ stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
+ return 0;
+ }
+ int64 length = stream.tell(stream.stream);
+ if (length < 0) {
+ stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
+ return 0;
+ }
+ stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
+ return mpt::saturate_cast<IFileData::pos_type>(length);
+ }
+};
+
+using FileDataCallbackStream = FileDataCallbackStreamTemplate<void *>;
+
+
+
+template <typename Tstream>
+class FileDataCallbackStreamSeekableTemplate : public FileDataSeekable {
+private:
+ CallbackStreamTemplate<Tstream> stream;
+
+public:
+ FileDataCallbackStreamSeekableTemplate(CallbackStreamTemplate<Tstream> s)
+ : FileDataSeekable(FileDataCallbackStream::GetLength(s))
+ , stream(s) {
+ return;
+ }
+
+private:
+ mpt::byte_span InternalReadSeekable(pos_type pos, mpt::byte_span dst) const override {
+ if (!stream.read) {
+ return dst.first(0);
+ }
+ if (stream.seek(stream.stream, pos, CallbackStream::SeekSet) < 0) {
+ return dst.first(0);
+ }
+ int64 totalread = 0;
+ std::byte * pdst = dst.data();
+ std::size_t count = dst.size();
+ while (count > 0) {
+ int64 readcount = stream.read(stream.stream, pdst, count);
+ if (readcount <= 0) {
+ break;
+ }
+ pdst += static_cast<std::size_t>(readcount);
+ count -= static_cast<std::size_t>(readcount);
+ totalread += readcount;
+ }
+ return dst.first(static_cast<std::size_t>(totalread));
+ }
+};
+
+using FileDataCallbackStreamSeekable = FileDataCallbackStreamSeekableTemplate<void *>;
+
+
+
+template <typename Tstream>
+class FileDataCallbackStreamUnseekableTemplate : public FileDataUnseekable {
+private:
+ CallbackStreamTemplate<Tstream> stream;
+ mutable bool eof_reached;
+
+public:
+ FileDataCallbackStreamUnseekableTemplate(CallbackStreamTemplate<Tstream> s)
+ : FileDataUnseekable()
+ , stream(s)
+ , eof_reached(false) {
+ return;
+ }
+
+private:
+ bool InternalEof() const override {
+ return eof_reached;
+ }
+
+ mpt::byte_span InternalReadUnseekable(mpt::byte_span dst) const override {
+ if (eof_reached) {
+ return dst.first(0);
+ }
+ if (!stream.read) {
+ eof_reached = true;
+ return dst.first(0);
+ }
+ int64 totalread = 0;
+ std::byte * pdst = dst.data();
+ std::size_t count = dst.size();
+ while (count > 0) {
+ int64 readcount = stream.read(stream.stream, pdst, count);
+ if (readcount <= 0) {
+ eof_reached = true;
+ break;
+ }
+ pdst += static_cast<std::size_t>(readcount);
+ count -= static_cast<std::size_t>(readcount);
+ totalread += readcount;
+ }
+ return dst.first(static_cast<std::size_t>(totalread));
+ }
+};
+
+using FileDataCallbackStreamUnseekable = FileDataCallbackStreamUnseekableTemplate<void *>;
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILEDATA_CALLBACKSTREAM_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_memory.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_memory.hpp
new file mode 100644
index 00000000..004104c3
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_memory.hpp
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILEDATA_MEMORY_HPP
+#define MPT_IO_READ_FILEDATA_MEMORY_HPP
+
+
+
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/io_read/filedata.hpp"
+
+#include <algorithm>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+class FileDataMemory
+ : public IFileData {
+
+private:
+ const std::byte * streamData; // Pointer to memory-mapped file
+ pos_type streamLength; // Size of memory-mapped file in bytes
+
+public:
+ FileDataMemory()
+ : streamData(nullptr)
+ , streamLength(0) { }
+ FileDataMemory(mpt::const_byte_span data)
+ : streamData(data.data())
+ , streamLength(data.size()) { }
+
+public:
+ bool IsValid() const override {
+ return streamData != nullptr;
+ }
+
+ bool HasFastGetLength() const override {
+ return true;
+ }
+
+ bool HasPinnedView() const override {
+ return true;
+ }
+
+ const std::byte * GetRawData() const override {
+ return streamData;
+ }
+
+ pos_type GetLength() const override {
+ return streamLength;
+ }
+
+ mpt::byte_span Read(pos_type pos, mpt::byte_span dst) const override {
+ if (pos >= streamLength) {
+ return dst.first(0);
+ }
+ pos_type avail = std::min(streamLength - pos, dst.size());
+ std::copy(streamData + pos, streamData + pos + avail, dst.data());
+ return dst.first(avail);
+ }
+
+ bool CanRead(pos_type pos, std::size_t length) const override {
+ if ((pos == streamLength) && (length == 0)) {
+ return true;
+ }
+ if (pos >= streamLength) {
+ return false;
+ }
+ return (length <= streamLength - pos);
+ }
+
+ std::size_t GetReadableLength(pos_type pos, std::size_t length) const override {
+ if (pos >= streamLength) {
+ return 0;
+ }
+ return std::min(length, streamLength - pos);
+ }
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILEDATA_MEMORY_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_stdstream.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_stdstream.hpp
new file mode 100644
index 00000000..e7f72b12
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filedata_stdstream.hpp
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILEDATA_STDSTREAM_HPP
+#define MPT_IO_READ_FILEDATA_STDSTREAM_HPP
+
+
+
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/saturate_cast.hpp"
+#include "mpt/io/base.hpp"
+#include "mpt/io/io_stdstream.hpp"
+#include "mpt/io_read/filedata.hpp"
+#include "mpt/io_read/filedata_base_buffered.hpp"
+#include "mpt/io_read/filedata_base_unseekable.hpp"
+
+#include <ios>
+#include <istream>
+#include <ostream>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+class FileDataStdStream {
+
+public:
+ static bool IsSeekable(std::istream & stream) {
+ return mpt::IO::IsReadSeekable(stream);
+ }
+
+ static IFileData::pos_type GetLength(std::istream & stream) {
+ stream.clear();
+ std::streampos oldpos = stream.tellg();
+ stream.seekg(0, std::ios::end);
+ std::streampos length = stream.tellg();
+ stream.seekg(oldpos);
+ return mpt::saturate_cast<IFileData::pos_type>(static_cast<int64>(length));
+ }
+};
+
+
+
+class FileDataStdStreamSeekable : public FileDataSeekableBuffered {
+
+private:
+ std::istream & stream;
+
+public:
+ FileDataStdStreamSeekable(std::istream & s)
+ : FileDataSeekableBuffered(FileDataStdStream::GetLength(s))
+ , stream(s) {
+ return;
+ }
+
+private:
+ mpt::byte_span InternalReadBuffered(pos_type pos, mpt::byte_span dst) const override {
+ stream.clear(); // tellg needs eof and fail bits unset
+ std::streampos currentpos = stream.tellg();
+ if (currentpos == std::streampos(-1) || static_cast<int64>(pos) != currentpos) {
+ // inefficient istream implementations might invalidate their buffer when seeking, even when seeking to the current position
+ stream.seekg(pos);
+ }
+ stream.read(mpt::byte_cast<char *>(dst.data()), dst.size());
+ return dst.first(static_cast<std::size_t>(stream.gcount()));
+ }
+};
+
+
+
+class FileDataStdStreamUnseekable : public FileDataUnseekable {
+
+private:
+ std::istream & stream;
+
+public:
+ FileDataStdStreamUnseekable(std::istream & s)
+ : stream(s) {
+ return;
+ }
+
+private:
+ bool InternalEof() const override {
+ if (stream) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ mpt::byte_span InternalReadUnseekable(mpt::byte_span dst) const override {
+ stream.read(mpt::byte_cast<char *>(dst.data()), dst.size());
+ return dst.first(static_cast<std::size_t>(stream.gcount()));
+ }
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILEDATA_STDSTREAM_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filereader.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filereader.hpp
new file mode 100644
index 00000000..84a39d6a
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_read/filereader.hpp
@@ -0,0 +1,594 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_READ_FILEREADER_HPP
+#define MPT_IO_READ_FILEREADER_HPP
+
+
+
+#include "mpt/base/alloc.hpp"
+#include "mpt/base/bit.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/span.hpp"
+#include "mpt/base/utility.hpp"
+#include "mpt/io/base.hpp"
+#include "mpt/endian/floatingpoint.hpp"
+#include "mpt/endian/integer.hpp"
+#include "mpt/string/utility.hpp"
+
+#include <algorithm>
+#include <array>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+namespace FileReader {
+
+// Read a "T" object from the stream.
+// If not enough bytes can be read, false is returned.
+// If successful, the file cursor is advanced by the size of "T".
+template <typename T, typename TFileCursor>
+bool Read(TFileCursor & f, T & target) {
+ // cppcheck false-positive
+ // cppcheck-suppress uninitvar
+ mpt::byte_span dst = mpt::as_raw_memory(target);
+ if (dst.size() != f.GetRaw(dst).size()) {
+ return false;
+ }
+ f.Skip(dst.size());
+ return true;
+}
+
+// Read an array of binary-safe T values.
+// If successful, the file cursor is advanced by the size of the array.
+// Otherwise, the target is zeroed.
+template <typename T, std::size_t destSize, typename TFileCursor>
+bool ReadArray(TFileCursor & f, T (&destArray)[destSize]) {
+ static_assert(mpt::is_binary_safe<T>::value);
+ if (!f.CanRead(sizeof(destArray))) {
+ mpt::reset(destArray);
+ return false;
+ }
+ f.ReadRaw(mpt::as_raw_memory(destArray));
+ return true;
+}
+
+// Read an array of binary-safe T values.
+// If successful, the file cursor is advanced by the size of the array.
+// Otherwise, the target is zeroed.
+template <typename T, std::size_t destSize, typename TFileCursor>
+bool ReadArray(TFileCursor & f, std::array<T, destSize> & destArray) {
+ static_assert(mpt::is_binary_safe<T>::value);
+ if (!f.CanRead(sizeof(destArray))) {
+ destArray.fill(T{});
+ return false;
+ }
+ f.ReadRaw(mpt::as_raw_memory(destArray));
+ return true;
+}
+
+// Read destSize elements of binary-safe type T into a vector.
+// If successful, the file cursor is advanced by the size of the vector.
+// Otherwise, the vector is resized to destSize, but possibly existing contents are not cleared.
+template <typename T, typename TFileCursor>
+bool ReadVector(TFileCursor & f, std::vector<T> & destVector, size_t destSize) {
+ static_assert(mpt::is_binary_safe<T>::value);
+ destVector.resize(destSize);
+ if (!f.CanRead(sizeof(T) * destSize)) {
+ return false;
+ }
+ f.ReadRaw(mpt::as_raw_memory(destVector));
+ return true;
+}
+
+template <typename T, std::size_t destSize, typename TFileCursor>
+std::array<T, destSize> ReadArray(TFileCursor & f) {
+ std::array<T, destSize> destArray;
+ ReadArray(f, destArray);
+ return destArray;
+}
+
+// Read some kind of integer in little-endian format.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename T, typename TFileCursor>
+T ReadIntLE(TFileCursor & f) {
+ static_assert(std::numeric_limits<T>::is_integer == true, "Target type is a not an integer");
+ typename mpt::make_le<T>::type target;
+ if (!FileReader::Read(f, target)) {
+ return 0;
+ }
+ return target;
+}
+
+// Read some kind of integer in big-endian format.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename T, typename TFileCursor>
+T ReadIntBE(TFileCursor & f) {
+ static_assert(std::numeric_limits<T>::is_integer == true, "Target type is a not an integer");
+ typename mpt::make_be<T>::type target;
+ if (!FileReader::Read(f, target)) {
+ return 0;
+ }
+ return target;
+}
+
+// Read a integer in little-endian format which has some of its higher bytes not stored in file.
+// If successful, the file cursor is advanced by the given size.
+template <typename T, typename TFileCursor>
+T ReadTruncatedIntLE(TFileCursor & f, typename TFileCursor::pos_type size) {
+ static_assert(std::numeric_limits<T>::is_integer == true, "Target type is a not an integer");
+ assert(sizeof(T) >= size);
+ if (size == 0) {
+ return 0;
+ }
+ if (!f.CanRead(size)) {
+ return 0;
+ }
+ uint8 buf[sizeof(T)];
+ bool negative = false;
+ for (std::size_t i = 0; i < sizeof(T); ++i) {
+ uint8 byte = 0;
+ if (i < size) {
+ FileReader::Read(f, byte);
+ negative = std::numeric_limits<T>::is_signed && ((byte & 0x80) != 0x00);
+ } else {
+ // sign or zero extend
+ byte = negative ? 0xff : 0x00;
+ }
+ buf[i] = byte;
+ }
+ return mpt::bit_cast<typename mpt::make_le<T>::type>(buf);
+}
+
+// Read a supplied-size little endian integer to a fixed size variable.
+// The data is properly sign-extended when fewer bytes are stored.
+// If more bytes are stored, higher order bytes are silently ignored.
+// If successful, the file cursor is advanced by the given size.
+template <typename T, typename TFileCursor>
+T ReadSizedIntLE(TFileCursor & f, typename TFileCursor::pos_type size) {
+ static_assert(std::numeric_limits<T>::is_integer == true, "Target type is a not an integer");
+ if (size == 0) {
+ return 0;
+ }
+ if (!f.CanRead(size)) {
+ return 0;
+ }
+ if (size < sizeof(T)) {
+ return FileReader::ReadTruncatedIntLE<T>(f, size);
+ }
+ T retval = FileReader::ReadIntLE<T>(f);
+ f.Skip(size - sizeof(T));
+ return retval;
+}
+
+// Read unsigned 32-Bit integer in little-endian format.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename TFileCursor>
+uint32 ReadUint32LE(TFileCursor & f) {
+ return FileReader::ReadIntLE<uint32>(f);
+}
+
+// Read unsigned 32-Bit integer in big-endian format.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename TFileCursor>
+uint32 ReadUint32BE(TFileCursor & f) {
+ return FileReader::ReadIntBE<uint32>(f);
+}
+
+// Read signed 32-Bit integer in little-endian format.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename TFileCursor>
+int32 ReadInt32LE(TFileCursor & f) {
+ return FileReader::ReadIntLE<int32>(f);
+}
+
+// Read signed 32-Bit integer in big-endian format.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename TFileCursor>
+int32 ReadInt32BE(TFileCursor & f) {
+ return FileReader::ReadIntBE<int32>(f);
+}
+
+// Read unsigned 24-Bit integer in little-endian format.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename TFileCursor>
+uint32 ReadUint24LE(TFileCursor & f) {
+ const auto arr = FileReader::ReadArray<uint8, 3>(f);
+ return arr[0] | (arr[1] << 8) | (arr[2] << 16);
+}
+
+// Read unsigned 24-Bit integer in big-endian format.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename TFileCursor>
+uint32 ReadUint24BE(TFileCursor & f) {
+ const auto arr = FileReader::ReadArray<uint8, 3>(f);
+ return (arr[0] << 16) | (arr[1] << 8) | arr[2];
+}
+
+// Read unsigned 16-Bit integer in little-endian format.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename TFileCursor>
+uint16 ReadUint16LE(TFileCursor & f) {
+ return FileReader::ReadIntLE<uint16>(f);
+}
+
+// Read unsigned 16-Bit integer in big-endian format.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename TFileCursor>
+uint16 ReadUint16BE(TFileCursor & f) {
+ return FileReader::ReadIntBE<uint16>(f);
+}
+
+// Read signed 16-Bit integer in little-endian format.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename TFileCursor>
+int16 ReadInt16LE(TFileCursor & f) {
+ return FileReader::ReadIntLE<int16>(f);
+}
+
+// Read signed 16-Bit integer in big-endian format.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename TFileCursor>
+int16 ReadInt16BE(TFileCursor & f) {
+ return FileReader::ReadIntBE<int16>(f);
+}
+
+// Read a single 8bit character.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename TFileCursor>
+char ReadChar(TFileCursor & f) {
+ char target;
+ if (!FileReader::Read(f, target)) {
+ return 0;
+ }
+ return target;
+}
+
+// Read unsigned 8-Bit integer.
+// If successful, the file cursor is advanced by the size of the integer.
+template <typename TFileCursor>
+uint8 ReadUint8(TFileCursor & f) {
+ uint8 target;
+ if (!FileReader::Read(f, target)) {
+ return 0;
+ }
+ return target;
+}
+
+// Read signed 8-Bit integer. If successful, the file cursor is advanced by the size of the integer.
+template <typename TFileCursor>
+int8 ReadInt8(TFileCursor & f) {
+ int8 target;
+ if (!FileReader::Read(f, target)) {
+ return 0;
+ }
+ return target;
+}
+
+// Read 32-Bit float in little-endian format.
+// If successful, the file cursor is advanced by the size of the float.
+template <typename TFileCursor>
+float ReadFloatLE(TFileCursor & f) {
+ IEEE754binary32LE target;
+ if (!FileReader::Read(f, target)) {
+ return 0.0f;
+ }
+ return target;
+}
+
+// Read 32-Bit float in big-endian format.
+// If successful, the file cursor is advanced by the size of the float.
+template <typename TFileCursor>
+float ReadFloatBE(TFileCursor & f) {
+ IEEE754binary32BE target;
+ if (!FileReader::Read(f, target)) {
+ return 0.0f;
+ }
+ return target;
+}
+
+// Read 64-Bit float in little-endian format.
+// If successful, the file cursor is advanced by the size of the float.
+template <typename TFileCursor>
+double ReadDoubleLE(TFileCursor & f) {
+ IEEE754binary64LE target;
+ if (!FileReader::Read(f, target)) {
+ return 0.0;
+ }
+ return target;
+}
+
+// Read 64-Bit float in big-endian format.
+// If successful, the file cursor is advanced by the size of the float.
+template <typename TFileCursor>
+double ReadDoubleBE(TFileCursor & f) {
+ IEEE754binary64BE target;
+ if (!FileReader::Read(f, target)) {
+ return 0.0;
+ }
+ return target;
+}
+
+// Read a struct.
+// If successful, the file cursor is advanced by the size of the struct. Otherwise, the target is zeroed.
+template <typename T, typename TFileCursor>
+bool ReadStruct(TFileCursor & f, T & target) {
+ static_assert(mpt::is_binary_safe<T>::value);
+ if (!FileReader::Read(f, target)) {
+ mpt::reset(target);
+ return false;
+ }
+ return true;
+}
+
+// Allow to read a struct partially (if there's less memory available than the struct's size, fill it up with zeros).
+// The file cursor is advanced by "partialSize" bytes.
+template <typename T, typename TFileCursor>
+typename TFileCursor::pos_type ReadStructPartial(TFileCursor & f, T & target, typename TFileCursor::pos_type partialSize = sizeof(T)) {
+ static_assert(mpt::is_binary_safe<T>::value);
+ typename TFileCursor::pos_type copyBytes = std::min(partialSize, sizeof(T));
+ if (!f.CanRead(copyBytes)) {
+ copyBytes = f.BytesLeft();
+ }
+ f.GetRaw(mpt::span(mpt::as_raw_memory(target).data(), copyBytes));
+ std::memset(mpt::as_raw_memory(target).data() + copyBytes, 0, sizeof(target) - copyBytes);
+ f.Skip(partialSize);
+ return copyBytes;
+}
+
+// Read a null-terminated string into a std::string
+template <typename TFileCursor>
+bool ReadNullString(TFileCursor & f, std::string & dest, const typename TFileCursor::pos_type maxLength = std::numeric_limits<typename TFileCursor::pos_type>::max()) {
+ dest.clear();
+ if (!f.CanRead(1)) {
+ return false;
+ }
+ char buffer[mpt::IO::BUFFERSIZE_MINUSCULE];
+ typename TFileCursor::pos_type avail = 0;
+ while ((avail = std::min(f.GetRaw(mpt::as_span(buffer)).size(), maxLength - dest.length())) != 0) {
+ auto end = std::find(buffer, buffer + avail, '\0');
+ dest.insert(dest.end(), buffer, end);
+ f.Skip(end - buffer);
+ if (end < buffer + avail) {
+ // Found null char
+ f.Skip(1);
+ break;
+ }
+ }
+ return dest.length() != 0;
+}
+
+// Read a string up to the next line terminator into a std::string
+template <typename TFileCursor>
+bool ReadLine(TFileCursor & f, std::string & dest, const typename TFileCursor::pos_type maxLength = std::numeric_limits<typename TFileCursor::pos_type>::max()) {
+ dest.clear();
+ if (!f.CanRead(1)) {
+ return false;
+ }
+ char buffer[mpt::IO::BUFFERSIZE_MINUSCULE];
+ char c = '\0';
+ typename TFileCursor::pos_type avail = 0;
+ while ((avail = std::min(f.GetRaw(mpt::as_span(buffer)).size(), maxLength - dest.length())) != 0) {
+ auto end = std::find_if(buffer, buffer + avail, mpt::is_any_line_ending<char>);
+ dest.insert(dest.end(), buffer, end);
+ f.Skip(end - buffer);
+ if (end < buffer + avail) {
+ // Found line ending
+ f.Skip(1);
+ // Handle CRLF line ending
+ if (*end == '\r') {
+ if (FileReader::Read(f, c) && c != '\n') {
+ f.SkipBack(1);
+ }
+ }
+ break;
+ }
+ }
+ return true;
+}
+
+// Compare a magic string with the current stream position.
+// Returns true if they are identical and advances the file cursor by the the length of the "magic" string.
+// Returns false if the string could not be found. The file cursor is not advanced in this case.
+template <size_t N, typename TFileCursor>
+bool ReadMagic(TFileCursor & f, const char (&magic)[N]) {
+ assert(magic[N - 1] == '\0');
+ for (std::size_t i = 0; i < N - 1; ++i) {
+ assert(magic[i] != '\0');
+ }
+ constexpr typename TFileCursor::pos_type magicLength = N - 1;
+ std::byte buffer[magicLength] = {};
+ if (f.GetRaw(mpt::span(buffer, magicLength)).size() != magicLength) {
+ return false;
+ }
+ if (std::memcmp(buffer, magic, magicLength)) {
+ return false;
+ }
+ f.Skip(magicLength);
+ return true;
+}
+
+// Read variable-length unsigned integer (as found in MIDI files).
+// If successful, the file cursor is advanced by the size of the integer and true is returned.
+// False is returned if not enough bytes were left to finish reading of the integer or if an overflow happened (source doesn't fit into target integer).
+// In case of an overflow, the target is also set to the maximum value supported by its data type.
+template <typename T, typename TFileCursor>
+bool ReadVarInt(TFileCursor & f, T & target) {
+ static_assert(std::numeric_limits<T>::is_integer == true && std::numeric_limits<T>::is_signed == false, "Target type is not an unsigned integer");
+
+ if (f.NoBytesLeft()) {
+ target = 0;
+ return false;
+ }
+
+ std::byte bytes[16]; // More than enough for any valid VarInt
+ typename TFileCursor::pos_type avail = f.GetRaw(mpt::as_span(bytes)).size();
+ typename TFileCursor::pos_type readPos = 1;
+
+ uint8 b = mpt::byte_cast<uint8>(bytes[0]);
+ target = (b & 0x7F);
+ std::size_t writtenBits = static_cast<std::size_t>(mpt::bit_width(target)); // Bits used in the most significant byte
+
+ while (readPos < avail && (b & 0x80) != 0) {
+ b = mpt::byte_cast<uint8>(bytes[readPos++]);
+ target <<= 7;
+ target |= (b & 0x7F);
+ writtenBits += 7;
+ if (readPos == avail) {
+ f.Skip(readPos);
+ avail = f.GetRaw(mpt::as_span(bytes)).size();
+ readPos = 0;
+ }
+ }
+ f.Skip(readPos);
+
+ if (writtenBits > sizeof(target) * 8u) {
+ // Overflow
+ target = std::numeric_limits<T>::max();
+ return false;
+ } else if ((b & 0x80) != 0) {
+ // Reached EOF
+ return false;
+ }
+ return true;
+}
+
+template <typename Tid, typename Tsize>
+struct ChunkHeader {
+ using id_type = Tid;
+ using size_type = Tsize;
+ friend constexpr bool declare_binary_safe(const ChunkHeader &) noexcept {
+ return true;
+ }
+ id_type id{};
+ size_type size{};
+ id_type GetID() const {
+ return id;
+ }
+ size_type GetLength() const {
+ return size;
+ }
+};
+
+template <typename TChunkHeader, typename TFileCursor>
+struct Chunk {
+ TChunkHeader header;
+ TFileCursor data;
+ TChunkHeader GetHeader() const {
+ return header;
+ }
+ TFileCursor GetData() const {
+ return data;
+ }
+};
+
+template <typename TChunkHeader, typename TFileCursor>
+struct ChunkList {
+
+ using id_type = decltype(TChunkHeader().GetID());
+ using size_type = decltype(TChunkHeader().GetLength());
+
+ std::vector<Chunk<TChunkHeader, TFileCursor>> chunks;
+
+ // Check if the list contains a given chunk.
+ bool ChunkExists(id_type id) const {
+ return std::find_if(chunks.begin(), chunks.end(), [id](const Chunk<TChunkHeader, TFileCursor> & chunk) { return chunk.GetHeader().GetID() == id; }) != chunks.end();
+ }
+
+ // Retrieve the first chunk with a given ID.
+ TFileCursor GetChunk(id_type id) const {
+ auto chunk = std::find_if(chunks.begin(), chunks.end(), [id](const Chunk<TChunkHeader, TFileCursor> & chunk) { return chunk.GetHeader().GetID() == id; });
+ if (chunk == chunks.end()) {
+ return TFileCursor();
+ }
+ return chunk->GetData();
+ }
+
+ // Retrieve all chunks with a given ID.
+ std::vector<TFileCursor> GetAllChunks(id_type id) const {
+ std::vector<TFileCursor> result;
+ for (const auto & chunk : chunks) {
+ if (chunk.GetHeader().GetID() == id) {
+ result.push_back(chunk.GetData());
+ }
+ }
+ return result;
+ }
+};
+
+// Read a single "TChunkHeader" chunk.
+// T is required to have the methods GetID() and GetLength().
+// GetLength() must return the chunk size in bytes, and GetID() the chunk ID.
+template <typename TChunkHeader, typename TFileCursor>
+Chunk<TChunkHeader, TFileCursor> ReadNextChunk(TFileCursor & f, typename TFileCursor::pos_type alignment) {
+ Chunk<TChunkHeader, TFileCursor> result;
+ if (!FileReader::Read(f, result.header)) {
+ return Chunk<TChunkHeader, TFileCursor>();
+ }
+ typename TFileCursor::pos_type dataSize = result.header.GetLength();
+ result.data = f.ReadChunk(dataSize);
+ if (alignment > 1) {
+ if ((dataSize % alignment) != 0) {
+ f.Skip(alignment - (dataSize % alignment));
+ }
+ }
+ return result;
+}
+
+// Read a series of "TChunkHeader" chunks until the end of file is reached.
+// T is required to have the methods GetID() and GetLength().
+// GetLength() must return the chunk size in bytes, and GetID() the chunk ID.
+template <typename TChunkHeader, typename TFileCursor>
+ChunkList<TChunkHeader, TFileCursor> ReadChunks(TFileCursor & f, typename TFileCursor::pos_type alignment) {
+ ChunkList<TChunkHeader, TFileCursor> result;
+ while (f.CanRead(sizeof(TChunkHeader))) {
+ result.chunks.push_back(FileReader::ReadNextChunk<TChunkHeader, TFileCursor>(f, alignment));
+ }
+ return result;
+}
+
+// Read a series of "TChunkHeader" chunks until a given chunk ID is found.
+// T is required to have the methods GetID() and GetLength().
+// GetLength() must return the chunk size in bytes, and GetID() the chunk ID.
+template <typename TChunkHeader, typename TFileCursor>
+ChunkList<TChunkHeader, TFileCursor> ReadChunksUntil(TFileCursor & f, typename TFileCursor::pos_type alignment, decltype(TChunkHeader().GetID()) lastID) {
+ ChunkList<TChunkHeader, TFileCursor> result;
+ while (f.CanRead(sizeof(TChunkHeader))) {
+ result.chunks.push_back(FileReader::ReadNextChunk<TChunkHeader, TFileCursor>(f, alignment));
+ if (result.chunks.back().GetHeader().GetID() == lastID) {
+ break;
+ }
+ }
+ return result;
+}
+
+} // namespace FileReader
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_READ_FILEREADER_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/io_write/buffer.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/io_write/buffer.hpp
new file mode 100644
index 00000000..646ce0fc
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/io_write/buffer.hpp
@@ -0,0 +1,202 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_IO_WRITE_BUFFER_HPP
+#define MPT_IO_WRITE_BUFFER_HPP
+
+
+
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/io/base.hpp"
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace IO {
+
+
+
+// WriteBuffer class that avoids calling to the underlying file writing
+// functions for every operation, which would involve rather slow un-inlinable
+// virtual calls in the iostream and FILE* cases. It is the users responabiliy
+// to call HasWriteError() to check for writeback errors at this buffering
+// level.
+
+template <typename Tfile>
+class WriteBuffer {
+private:
+ mpt::byte_span buffer;
+ std::size_t size = 0;
+ Tfile & f;
+ bool writeError = false;
+
+public:
+ WriteBuffer(const WriteBuffer &) = delete;
+ WriteBuffer & operator=(const WriteBuffer &) = delete;
+
+public:
+ inline WriteBuffer(Tfile & f_, mpt::byte_span buffer_)
+ : buffer(buffer_)
+ , f(f_) {
+ }
+ inline ~WriteBuffer() noexcept(false) {
+ if (!writeError)
+ {
+ FlushLocal();
+ }
+ }
+
+public:
+ inline Tfile & file() const {
+ if (IsDirty())
+ {
+ FlushLocal();
+ }
+ return f;
+ }
+
+public:
+ inline bool HasWriteError() const {
+ return writeError;
+ }
+ inline void ClearError() {
+ writeError = false;
+ }
+ inline bool IsDirty() const {
+ return size > 0;
+ }
+ inline bool IsClean() const {
+ return size == 0;
+ }
+ inline bool IsFull() const {
+ return size == buffer.size();
+ }
+ inline std::size_t GetCurrentSize() const {
+ return size;
+ }
+ inline bool Write(mpt::const_byte_span data) {
+ bool result = true;
+ for (std::size_t i = 0; i < data.size(); ++i)
+ {
+ buffer[size] = data[i];
+ size++;
+ if (IsFull())
+ {
+ FlushLocal();
+ }
+ }
+ return result;
+ }
+ inline void FlushLocal() {
+ if (IsClean())
+ {
+ return;
+ }
+ try
+ {
+ if (!mpt::IO::WriteRaw(f, mpt::as_span(buffer.data(), size)))
+ {
+ writeError = true;
+ }
+ } catch (const std::exception &)
+ {
+ writeError = true;
+ throw;
+ }
+ size = 0;
+ }
+};
+
+template <typename Tfile>
+struct FileOperations<WriteBuffer<Tfile>> {
+
+private:
+ WriteBuffer<Tfile> & f;
+
+public:
+ FileOperations(WriteBuffer<Tfile> & f_)
+ : f(f_) {
+ return;
+ }
+
+public:
+ inline bool IsValid() {
+ return IsValid(f.file());
+ }
+
+ inline bool IsReadSeekable() {
+ return IsReadSeekable(f.file());
+ }
+
+ inline bool IsWriteSeekable() {
+ return IsWriteSeekable(f.file());
+ }
+
+ inline IO::Offset TellRead() {
+ f.FlushLocal();
+ return TellRead(f.file());
+ }
+
+ inline IO::Offset TellWrite() {
+ return TellWrite(f.file()) + f.GetCurrentSize();
+ }
+
+ inline bool SeekBegin() {
+ f.FlushLocal();
+ return SeekBegin(f.file());
+ }
+
+ inline bool SeekEnd() {
+ f.FlushLocal();
+ return SeekEnd(f.file());
+ }
+
+ inline bool SeekAbsolute(IO::Offset pos) {
+ f.FlushLocal();
+ return SeekAbsolute(f.file(), pos);
+ }
+
+ inline bool SeekRelative(IO::Offset off) {
+ f.FlushLocal();
+ return SeekRelative(f.file(), off);
+ }
+
+ inline mpt::byte_span ReadRawImpl(mpt::byte_span data) {
+ f.FlushLocal();
+ return ReadRawImpl(f.file(), data);
+ }
+
+ inline bool WriteRawImpl(mpt::const_byte_span data) {
+ return f.Write(data);
+ }
+
+ inline bool IsEof() {
+ f.FlushLocal();
+ return IsEof(f.file());
+ }
+
+ inline bool Flush() {
+ f.FlushLocal();
+ return Flush(f.file());
+ }
+};
+
+
+
+} // namespace IO
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_IO_WRITE_BUFFER_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/json/json.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/json/json.hpp
new file mode 100644
index 00000000..f1fc42d8
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/json/json.hpp
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_JSON_JSON_HPP
+#define MPT_JSON_JSON_HPP
+
+#include "mpt/base/detect.hpp"
+#include "mpt/detect/nlohmann_json.hpp"
+
+#if MPT_DETECTED_NLOHMANN_JSON
+#include "mpt/string/types.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+#endif // MPT_DETECTED_NLOHMANN_JSON
+
+#if MPT_DETECTED_NLOHMANN_JSON
+#include <optional>
+#endif // MPT_DETECTED_NLOHMANN_JSON
+
+#if MPT_DETECTED_NLOHMANN_JSON
+#include <nlohmann/json.hpp>
+#endif // MPT_DETECTED_NLOHMANN_JSON
+
+
+
+namespace nlohmann {
+template <>
+struct adl_serializer<mpt::ustring> {
+ static void to_json(json & j, const mpt::ustring & val) {
+ j = mpt::transcode<std::string>(mpt::common_encoding::utf8, val);
+ }
+ static void from_json(const json & j, mpt::ustring & val) {
+ val = mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, j.get<std::string>());
+ }
+};
+template <typename Tvalue>
+struct adl_serializer<std::map<mpt::ustring, Tvalue>> {
+ static void to_json(json & j, const std::map<mpt::ustring, Tvalue> & val) {
+ std::map<std::string, Tvalue> utf8map;
+ for (const auto & value : val)
+ {
+ utf8map[mpt::transcode<std::string>(mpt::common_encoding::utf8, value.first)] = value.second;
+ }
+ j = std::move(utf8map);
+ }
+ static void from_json(const json & j, std::map<mpt::ustring, Tvalue> & val) {
+ std::map<std::string, Tvalue> utf8map = j.get<std::map<std::string, Tvalue>>();
+ std::map<mpt::ustring, Tvalue> result;
+ for (const auto & value : utf8map)
+ {
+ result[mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, value.first)] = value.second;
+ }
+ val = std::move(result);
+ }
+};
+template <typename Tvalue>
+struct adl_serializer<std::optional<Tvalue>> {
+ static void to_json(json & j, const std::optional<Tvalue> & val) {
+ j = (val ? json{*val} : json{nullptr});
+ }
+ static void from_json(const json & j, std::optional<Tvalue> & val) {
+ if (!j.is_null())
+ {
+ val = j.get<Tvalue>();
+ } else
+ {
+ val = std::nullopt;
+ }
+ }
+};
+} // namespace nlohmann
+
+
+
+#endif // MPT_JSON_JSON_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/library/library.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/library/library.hpp
new file mode 100644
index 00000000..1b20277a
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/library/library.hpp
@@ -0,0 +1,465 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_LIBRARY_LIBRARY_HPP
+#define MPT_LIBRARY_LIBRARY_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/saturate_cast.hpp"
+#include "mpt/detect/dl.hpp"
+#include "mpt/detect/ltdl.hpp"
+#include "mpt/path/path.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+
+#include <filesystem>
+#include <optional>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#if MPT_OS_WINDOWS
+#include <windows.h>
+#elif MPT_OS_ANDROID
+#elif defined(MPT_WITH_DL)
+#include <dlfcn.h>
+#elif defined(MPT_WITH_LTDL)
+#include <ltdl.h>
+#endif
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+class library {
+
+public:
+ typedef void * (*func_ptr)(); // pointer to function returning void *
+ static_assert(sizeof(func_ptr) == sizeof(void *));
+
+ enum class path_search {
+ invalid,
+ unsafe,
+ default_,
+ system,
+ application,
+ none,
+ };
+
+ enum class path_prefix {
+ none,
+ default_,
+ };
+
+ enum class path_suffix {
+ none,
+ default_,
+ };
+
+ struct path {
+
+ public:
+ path_search search = path_search::invalid;
+ path_prefix prefix = path_prefix::default_;
+ mpt::path filename{};
+ path_suffix suffix = path_suffix::default_;
+
+ static mpt::path default_prefix() {
+#if MPT_OS_WINDOWS
+ return MPT_PATH("");
+#else
+ return MPT_PATH("lib");
+#endif
+ }
+
+ static mpt::path default_suffix() {
+#if MPT_OS_WINDOWS
+ return MPT_PATH(".dll");
+#elif MPT_OS_ANDROID || defined(MPT_WITH_DL)
+ return MPT_PATH(".so");
+#else
+ return MPT_PATH("");
+#endif
+ }
+
+ std::optional<mpt::path> get_effective_filename() const {
+ switch (search) {
+ case library::path_search::unsafe:
+ break;
+ case library::path_search::default_:
+ break;
+ case library::path_search::system:
+ if (filename.is_absolute()) {
+ return std::nullopt;
+ }
+ break;
+ case library::path_search::application:
+ if (filename.is_absolute()) {
+ return std::nullopt;
+ }
+ break;
+ case library::path_search::none:
+ if (filename.is_relative()) {
+ return std::nullopt;
+ }
+ break;
+ case library::path_search::invalid:
+ return std::nullopt;
+ break;
+ }
+ mpt::path result{};
+ result += filename.parent_path();
+ result += ((prefix == path_prefix::default_) ? default_prefix() : mpt::path{});
+ result += filename.filename();
+ result += ((suffix == path_suffix::default_) ? default_suffix() : mpt::path{});
+ return result;
+ }
+ };
+
+#if MPT_OS_WINDOWS
+
+private:
+ HMODULE m_hModule = NULL;
+
+ library(HMODULE hModule)
+ : m_hModule(hModule) {
+ return;
+ }
+
+public:
+ library(const library &) = delete;
+ library & operator=(const library &) = delete;
+
+ library(library && other) noexcept
+ : m_hModule(other.m_hModule) {
+ other.m_hModule = NULL;
+ }
+
+ library & operator=(library && other) noexcept {
+ FreeLibrary(m_hModule);
+ m_hModule = other.m_hModule;
+ other.m_hModule = NULL;
+ return *this;
+ }
+
+private:
+#if !MPT_OS_WINDOWS_WINRT
+#if (_WIN32_WINNT < 0x0602)
+
+ static mpt::path get_application_path() {
+ std::vector<TCHAR> path(MAX_PATH);
+ while (GetModuleFileName(0, path.data(), mpt::saturate_cast<DWORD>(path.size())) >= path.size()) {
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ return mpt::path{};
+ }
+ path.resize(mpt::exponential_grow(path.size()));
+ }
+ return mpt::path::from_stdpath(std::filesystem::absolute(mpt::transcode<mpt::path>(mpt::winstring(path.data())).stdpath()));
+ }
+
+ static mpt::path get_system_path() {
+ DWORD size = GetSystemDirectory(nullptr, 0);
+ std::vector<TCHAR> path(size + 1);
+ if (!GetSystemDirectory(path.data(), size + 1)) {
+ return mpt::path{};
+ }
+ return mpt::transcode<mpt::path>(mpt::winstring(path.data()));
+ }
+
+#endif
+#endif // !MPT_OS_WINDOWS_WINRT
+
+public:
+ static std::optional<library> load(mpt::library::path path) {
+
+ HMODULE hModule = NULL;
+
+ std::optional<mpt::path> optionalfilename = path.get_effective_filename();
+ if (!optionalfilename) {
+ return std::nullopt;
+ }
+ mpt::path & filename = optionalfilename.value();
+ if (filename.empty()) {
+ return std::nullopt;
+ }
+
+#if MPT_OS_WINDOWS_WINRT
+
+#if (_WIN32_WINNT < 0x0602)
+ MPT_UNUSED(path);
+ return std::nullopt;
+#else // Windows 8
+ switch (path.search) {
+ case library::path_search::unsafe:
+ hModule = LoadPackagedLibrary(filename.ospath().c_str(), 0);
+ break;
+ case library::path_search::default_:
+ hModule = LoadPackagedLibrary(filename.ospath().c_str(), 0);
+ break;
+ case library::path_search::system:
+ hModule = NULL; // Only application packaged libraries can be loaded dynamically in WinRT
+ break;
+ case library::path_search::application:
+ hModule = LoadPackagedLibrary(filename.ospath().c_str(), 0);
+ break;
+ case library::path_search::none:
+ hModule = NULL; // Absolute path is not supported in WinRT
+ break;
+ case library::path_search::invalid:
+ hModule = NULL;
+ break;
+ }
+#endif // Windows Version
+
+#else // !MPT_OS_WINDOWS_WINRT
+
+#if (_WIN32_WINNT >= 0x0602) // Windows 8
+
+ switch (path.search) {
+ case library::path_search::unsafe:
+ hModule = LoadLibrary(filename.ospath().c_str());
+ break;
+ case library::path_search::default_:
+ hModule = LoadLibraryEx(filename.ospath().c_str(), NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
+ break;
+ case library::path_search::system:
+ hModule = LoadLibraryEx(filename.ospath().c_str(), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ break;
+ case library::path_search::application:
+ hModule = LoadLibraryEx(filename.ospath().c_str(), NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
+ break;
+ case library::path_search::none:
+ hModule = LoadLibraryEx(filename.ospath().c_str(), NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
+ break;
+ case library::path_search::invalid:
+ hModule = NULL;
+ break;
+ }
+
+#else // < Windows 8
+
+ switch (path.search) {
+ case library::path_search::unsafe:
+ hModule = LoadLibrary(filename.ospath().c_str());
+ break;
+ case library::path_search::default_:
+ hModule = LoadLibrary(filename.ospath().c_str());
+ break;
+ case library::path_search::system:
+ {
+ mpt::path system_path = get_system_path();
+ if (system_path.empty()) {
+ hModule = NULL;
+ } else {
+ hModule = LoadLibrary((system_path / filename).ospath().c_str());
+ }
+ }
+ break;
+ case library::path_search::application:
+ {
+ mpt::path application_path = get_application_path();
+ if (application_path.empty()) {
+ hModule = NULL;
+ } else {
+ hModule = LoadLibrary((application_path / filename).ospath().c_str());
+ }
+ }
+ break;
+ case library::path_search::none:
+ hModule = LoadLibrary(filename.ospath().c_str());
+ break;
+ case library::path_search::invalid:
+ hModule = NULL;
+ break;
+ }
+
+#endif // MPT_OS_WINDOWS_WINRT
+
+#endif
+
+ return library{hModule};
+ }
+
+ func_ptr get_address(const std::string & symbol) const {
+ return reinterpret_cast<func_ptr>(::GetProcAddress(m_hModule, symbol.c_str()));
+ }
+
+ ~library() {
+ FreeLibrary(m_hModule);
+ }
+
+#elif defined(MPT_WITH_DL)
+
+private:
+ void * handle = nullptr;
+
+ library(void * handle_)
+ : handle(handle_) {
+ return;
+ }
+
+public:
+ library(const library &) = delete;
+ library & operator=(const library &) = delete;
+
+ library(library && other) noexcept
+ : handle(other.handle) {
+ other.handle = NULL;
+ }
+
+ library & operator=(library && other) noexcept {
+ dlclose(handle);
+ m_hModule = other.handle;
+ other.handle = NULL;
+ return *this;
+ }
+
+public:
+ static std::optional<library> load(mpt::library::path path) {
+ std::optional<mpt::path> optionalfilename = path.get_effective_filename();
+ if (!optionalfilename) {
+ return std::nullopt;
+ }
+ mpt::path & filename = optionalfilename.value();
+ if (filename.empty()) {
+ return std::nullopt;
+ }
+ void * handle = dlopen(filename.ospath().c_str(), RTLD_NOW);
+ if (!handle) {
+ return std::nullopt;
+ }
+ return library{handle};
+ }
+
+ func_ptr get_address(const std::string & symbol) const {
+ return reinterpret_cast<func_ptr>(dlsym(handle, symbol.c_str()));
+ }
+
+ ~library() {
+ dlclose(handle);
+ }
+
+#elif defined(MPT_WITH_LTDL)
+
+private:
+ lt_dlhandle handle{};
+
+ library(lt_dlhandle handle_)
+ : handle(handle_) {
+ return;
+ }
+
+public:
+ library(const library &) = delete;
+ library & operator=(const library &) = delete;
+
+ library(library && other) noexcept
+ : handle(other.handle) {
+ other.handle = NULL;
+ }
+
+ library & operator=(library && other) noexcept {
+ dlclose(handle);
+ m_hModule = other.handle;
+ other.handle = NULL;
+ return *this;
+ }
+
+public:
+ static std::optional<library> load(mpt::library::path path) {
+ if (lt_dlinit() != 0) {
+ return std::nullopt;
+ }
+ std::optional<mpt::path> optionalfilename = path.get_effective_filename();
+ if (!optionalfilename) {
+ return std::nullopt;
+ }
+ mpt::path & filename = optionalfilename.value();
+ if (filename.empty()) {
+ return std::nullopt;
+ }
+ lt_dlhandle handle = lt_dlopenext(filename.ospath().c_str());
+ if (!handle) {
+ return std::nullopt;
+ }
+ return library{handle};
+ }
+
+ func_ptr get_address(const std::string & symbol) const {
+ return reinterpret_cast<func_ptr>(dlsym(handle, symbol.c_str()));
+ }
+
+ ~library() {
+ lt_dlclose(handle);
+ lt_dlexit();
+ }
+
+#else
+
+private:
+ void * handle = nullptr;
+
+ library(void * handle_)
+ : handle(handle_) {
+ return;
+ }
+
+public:
+ library(const library &) = delete;
+ library & operator=(const library &) = delete;
+
+ library(library && other) noexcept
+ : handle(other.handle) {
+ other.handle = nullptr;
+ }
+
+ library & operator=(library && other) noexcept {
+ handle = other.handle;
+ other.handle = nullptr;
+ return *this;
+ }
+
+public:
+ static std::optional<library> load(mpt::library::path path) {
+ MPT_UNUSED(path);
+ return std::nullopt;
+ }
+
+ func_ptr get_address(const std::string & symbol) const {
+ MPT_UNUSED(symbol);
+ return nullptr;
+ }
+
+ ~library() {
+ return;
+ }
+
+#endif
+
+ template <typename Tfunc>
+ bool bind(Tfunc *& f, const std::string & symbol) const {
+#if !(MPT_OS_WINDOWS && MPT_COMPILER_GCC)
+ // MinGW64 std::is_function is always false for non __cdecl functions.
+ // Issue is similar to <https://connect.microsoft.com/VisualStudio/feedback/details/774720/stl-is-function-bug>.
+ static_assert(std::is_function<Tfunc>::value);
+#endif
+ const func_ptr addr = get_address(symbol);
+ f = reinterpret_cast<Tfunc *>(addr);
+ return (addr != nullptr);
+ }
+};
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_LIBRARY_LIBRARY_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/mutex/mutex.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/mutex/mutex.hpp
new file mode 100644
index 00000000..2a38eacd
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/mutex/mutex.hpp
@@ -0,0 +1,211 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_MUTEX_MUTEX_HPP
+#define MPT_MUTEX_MUTEX_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+
+
+#if !MPT_PLATFORM_MULTITHREADED
+#define MPT_MUTEX_NONE 1
+#elif MPT_COMPILER_GENERIC
+#define MPT_MUTEX_STD 1
+#elif MPT_OS_WINDOWS && MPT_LIBCXX_GNU && !defined(_GLIBCXX_HAS_GTHREADS) && defined(MPT_WITH_MINGWSTDTHREADS)
+#define MPT_MUTEX_STD 1
+#elif MPT_OS_WINDOWS && MPT_LIBCXX_GNU && !defined(_GLIBCXX_HAS_GTHREADS)
+#define MPT_MUTEX_WIN32 1
+#else
+#define MPT_MUTEX_STD 1
+#endif
+
+#ifndef MPT_MUTEX_STD
+#define MPT_MUTEX_STD 0
+#endif
+#ifndef MPT_MUTEX_WIN32
+#define MPT_MUTEX_WIN32 0
+#endif
+#ifndef MPT_MUTEX_NONE
+#define MPT_MUTEX_NONE 0
+#endif
+
+#if MPT_MUTEX_STD
+#if !MPT_COMPILER_GENERIC && (defined(__MINGW32__) || defined(__MINGW64__)) && !defined(_GLIBCXX_HAS_GTHREADS) && defined(MPT_WITH_MINGWSTDTHREADS)
+#include <mingw.mutex.h>
+#else
+#include <mutex>
+#ifdef MPT_COMPILER_QUIRK_COMPLEX_STD_MUTEX
+#include <shared_mutex>
+#include <type_traits>
+#endif
+#endif
+#elif MPT_MUTEX_WIN32
+#include <windows.h>
+#endif // MPT_MUTEX
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+#if MPT_MUTEX_STD
+
+#ifdef MPT_COMPILER_QUIRK_COMPLEX_STD_MUTEX
+using mutex = std::conditional<sizeof(std::shared_mutex) < sizeof(std::mutex), std::shared_mutex, std::mutex>::type;
+#else
+using mutex = std::mutex;
+#endif
+using recursive_mutex = std::recursive_mutex;
+
+#elif MPT_MUTEX_WIN32
+
+#if (_WIN32_WINNT >= 0x0600) // _WIN32_WINNT_VISTA
+
+// compatible with c++11 std::mutex, can eventually be replaced without touching any usage site
+class mutex {
+private:
+ SRWLOCK impl = SRWLOCK_INIT;
+
+public:
+ mutex() = default;
+ ~mutex() = default;
+ void lock() {
+ AcquireSRWLockExclusive(&impl);
+ }
+ bool try_lock() {
+ return TryAcquireSRWLockExclusive(&impl) ? true : false;
+ }
+ void unlock() {
+ ReleaseSRWLockExclusive(&impl);
+ }
+};
+
+#else // !_WIN32_WINNT_VISTA
+
+// compatible with c++11 std::mutex, can eventually be replaced without touching any usage site
+class mutex {
+private:
+ CRITICAL_SECTION impl;
+
+public:
+ mutex() {
+ InitializeCriticalSection(&impl);
+ }
+ ~mutex() {
+ DeleteCriticalSection(&impl);
+ }
+ void lock() {
+ EnterCriticalSection(&impl);
+ }
+ bool try_lock() {
+ return TryEnterCriticalSection(&impl) ? true : false;
+ }
+ void unlock() {
+ LeaveCriticalSection(&impl);
+ }
+};
+
+#endif // _WIN32_WINNT_VISTA
+
+// compatible with c++11 std::recursive_mutex, can eventually be replaced without touching any usage site
+class recursive_mutex {
+private:
+ CRITICAL_SECTION impl;
+
+public:
+ recursive_mutex() {
+ InitializeCriticalSection(&impl);
+ }
+ ~recursive_mutex() {
+ DeleteCriticalSection(&impl);
+ }
+ void lock() {
+ EnterCriticalSection(&impl);
+ }
+ bool try_lock() {
+ return TryEnterCriticalSection(&impl) ? true : false;
+ }
+ void unlock() {
+ LeaveCriticalSection(&impl);
+ }
+};
+
+#else // MPT_MUTEX_NONE
+
+class mutex {
+public:
+ mutex() {
+ return;
+ }
+ ~mutex() {
+ return;
+ }
+ void lock() {
+ return;
+ }
+ bool try_lock() {
+ return true;
+ }
+ void unlock() {
+ return;
+ }
+};
+
+class recursive_mutex {
+public:
+ recursive_mutex() {
+ return;
+ }
+ ~recursive_mutex() {
+ return;
+ }
+ void lock() {
+ return;
+ }
+ bool try_lock() {
+ return true;
+ }
+ void unlock() {
+ return;
+ }
+};
+
+#endif // MPT_MUTEX
+
+#if MPT_MUTEX_STD
+
+template <typename T>
+using lock_guard = std::lock_guard<T>;
+
+#else // !MPT_MUTEX_STD
+
+// compatible with c++11 std::lock_guard, can eventually be replaced without touching any usage site
+template <typename mutex_type>
+class lock_guard {
+private:
+ mutex_type & mutex;
+
+public:
+ lock_guard(mutex_type & m)
+ : mutex(m) {
+ mutex.lock();
+ }
+ ~lock_guard() {
+ mutex.unlock();
+ }
+};
+
+#endif // MPT_MUTEX_STD
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_MUTEX_MUTEX_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/osinfo/class.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/osinfo/class.hpp
new file mode 100644
index 00000000..2c997966
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/osinfo/class.hpp
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_OSINFO_CLASS_HPP
+#define MPT_OSINFO_CLASS_HPP
+
+
+
+#include "mpt/base/detect_os.hpp"
+#include "mpt/base/namespace.hpp"
+#if !MPT_OS_WINDOWS
+#include "mpt/string/buffer.hpp"
+#endif // !MPT_OS_WINDOWS
+
+#include <string>
+
+#if !MPT_OS_WINDOWS
+#include <sys/utsname.h>
+#endif // !MPT_OS_WINDOWS
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace osinfo {
+
+enum class osclass {
+ Unknown,
+ Windows,
+ Linux,
+ Darwin,
+ BSD,
+ Haiku,
+ DOS,
+};
+
+inline mpt::osinfo::osclass get_class_from_sysname(const std::string & sysname) {
+ mpt::osinfo::osclass result = mpt::osinfo::osclass::Unknown;
+ if (sysname == "") {
+ result = mpt::osinfo::osclass::Unknown;
+ } else if (sysname == "Windows" || sysname == "WindowsNT" || sysname == "Windows_NT") {
+ result = mpt::osinfo::osclass::Windows;
+ } else if (sysname == "Linux") {
+ result = mpt::osinfo::osclass::Linux;
+ } else if (sysname == "Darwin") {
+ result = mpt::osinfo::osclass::Darwin;
+ } else if (sysname == "FreeBSD" || sysname == "DragonFly" || sysname == "NetBSD" || sysname == "OpenBSD" || sysname == "MidnightBSD") {
+ result = mpt::osinfo::osclass::BSD;
+ } else if (sysname == "Haiku") {
+ result = mpt::osinfo::osclass::Haiku;
+ } else if (sysname == "MS-DOS") {
+ result = mpt::osinfo::osclass::DOS;
+ }
+ return result;
+}
+
+inline mpt::osinfo::osclass get_class() {
+#if MPT_OS_WINDOWS
+ return mpt::osinfo::osclass::Windows;
+#else // !MPT_OS_WINDOWS
+ utsname uname_result;
+ if (uname(&uname_result) != 0) {
+ return mpt::osinfo::osclass::Unknown;
+ }
+ return mpt::osinfo::get_class_from_sysname(mpt::ReadAutoBuf(uname_result.sysname));
+#endif // MPT_OS_WINDOWS
+}
+
+} // namespace osinfo
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_OSINFO_CLASS_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/osinfo/windows_version.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/osinfo/windows_version.hpp
new file mode 100644
index 00000000..33c046eb
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/osinfo/windows_version.hpp
@@ -0,0 +1,452 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_OSINFO_WINDOWS_VERSION_HPP
+#define MPT_OSINFO_WINDOWS_VERSION_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+
+#if MPT_OS_WINDOWS
+#include <windows.h>
+#endif // MPT_OS_WINDOWS
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace osinfo {
+
+namespace windows {
+
+
+
+class Version {
+
+public:
+ enum Number : uint64 {
+ WinNT4 = 0x0000000400000000ull,
+ Win2000 = 0x0000000500000000ull,
+ WinXP = 0x0000000500000001ull,
+ WinXP64 = 0x0000000500000002ull,
+ WinVista = 0x0000000600000000ull,
+ Win7 = 0x0000000600000001ull,
+ Win8 = 0x0000000600000002ull,
+ Win81 = 0x0000000600000003ull,
+ Win10 = 0x0000000a00000000ull,
+ WinNewer = Win10 + 1ull
+ };
+
+ struct System {
+ uint32 Major = 0;
+ uint32 Minor = 0;
+ constexpr System() noexcept
+ : Major(0)
+ , Minor(0) {
+ }
+ constexpr System(Number number) noexcept
+ : Major(static_cast<uint32>((static_cast<uint64>(number) >> 32) & 0xffffffffu))
+ , Minor(static_cast<uint32>((static_cast<uint64>(number) >> 0) & 0xffffffffu)) {
+ }
+ explicit constexpr System(uint64 number) noexcept
+ : Major(static_cast<uint32>((number >> 32) & 0xffffffffu))
+ , Minor(static_cast<uint32>((number >> 0) & 0xffffffffu)) {
+ }
+ explicit constexpr System(uint32 major, uint32 minor) noexcept
+ : Major(major)
+ , Minor(minor) {
+ }
+ constexpr operator uint64() const noexcept {
+ return (static_cast<uint64>(Major) << 32) | (static_cast<uint64>(Minor) << 0);
+ }
+ };
+
+ struct ServicePack {
+ uint16 Major = 0;
+ uint16 Minor = 0;
+ constexpr ServicePack() noexcept
+ : Major(0)
+ , Minor(0) {
+ }
+ explicit constexpr ServicePack(uint16 major, uint16 minor) noexcept
+ : Major(major)
+ , Minor(minor) {
+ }
+ constexpr bool HasServicePack() const noexcept {
+ return Major != 0 || Minor != 0;
+ }
+ constexpr operator uint32() const noexcept {
+ return (static_cast<uint32>(Major) << 16) | (static_cast<uint32>(Minor) << 0);
+ }
+ };
+
+ typedef uint32 Build;
+
+ typedef uint32 TypeId;
+
+private:
+ bool m_SystemIsWindows;
+
+ System m_System;
+ ServicePack m_ServicePack;
+ Build m_Build;
+ TypeId m_Type;
+
+private:
+ constexpr Version() noexcept
+ : m_SystemIsWindows(false)
+ , m_System()
+ , m_ServicePack()
+ , m_Build()
+ , m_Type() {
+ }
+
+public:
+ static constexpr Version NoWindows() noexcept {
+ return Version();
+ }
+
+ constexpr Version(mpt::osinfo::windows::Version::System system, mpt::osinfo::windows::Version::ServicePack servicePack, mpt::osinfo::windows::Version::Build build, mpt::osinfo::windows::Version::TypeId type) noexcept
+ : m_SystemIsWindows(true)
+ , m_System(system)
+ , m_ServicePack(servicePack)
+ , m_Build(build)
+ , m_Type(type) {
+ }
+
+public:
+#if MPT_OS_WINDOWS
+
+ static mpt::osinfo::windows::Version FromSDK() noexcept {
+ // Initialize to used SDK version
+#if defined(NTDDI_VERSION)
+#if NTDDI_VERSION >= 0x0A00000B // NTDDI_WIN10_CO Win11
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 22000, 0);
+#elif NTDDI_VERSION >= 0x0A00000A // NTDDI_WIN10_FE 21H2
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19044, 0);
+//#elif // NTDDI_WIN10_FE 21H1
+// return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19043, 0);
+//#elif // NTDDI_WIN10_FE 20H2
+// return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19042, 0);
+#elif NTDDI_VERSION >= 0x0A000009 // NTDDI_WIN10_MN 2004/20H1
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 19041, 0);
+#elif NTDDI_VERSION >= 0x0A000008 // NTDDI_WIN10_VB 1909/19H2
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 18363, 0);
+#elif NTDDI_VERSION >= 0x0A000007 // NTDDI_WIN10_19H1 1903/19H1
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 18362, 0);
+#elif NTDDI_VERSION >= 0x0A000006 // NTDDI_WIN10_RS5 1809
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 17763, 0);
+#elif NTDDI_VERSION >= 0x0A000005 // NTDDI_WIN10_RS4 1803
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 17134, 0);
+#elif NTDDI_VERSION >= 0x0A000004 // NTDDI_WIN10_RS3 1709
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 16299, 0);
+#elif NTDDI_VERSION >= 0x0A000003 // NTDDI_WIN10_RS2 1703
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 15063, 0);
+#elif NTDDI_VERSION >= 0x0A000002 // NTDDI_WIN10_RS1 1607
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 14393, 0);
+#elif NTDDI_VERSION >= 0x0A000001 // NTDDI_WIN10_TH2 1511
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 10586, 0);
+#elif NTDDI_VERSION >= 0x0A000000 // NTDDI_WIN10 1507
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack(0, 0), 10240, 0);
+#elif NTDDI_VERSION >= 0x06030000 // NTDDI_WINBLUE
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win81, mpt::osinfo::windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0, 0);
+#elif NTDDI_VERSION >= 0x06020000 // NTDDI_WIN8
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win8, mpt::osinfo::windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0, 0);
+#elif NTDDI_VERSION >= 0x06010000 // NTDDI_WIN7
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win7, mpt::osinfo::windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0, 0);
+#elif NTDDI_VERSION >= 0x06000000 // NTDDI_VISTA
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::WinVista, mpt::osinfo::windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0, 0);
+#elif NTDDI_VERSION >= 0x05020000 // NTDDI_WS03
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::WinXP64, mpt::osinfo::windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0, 0);
+#elif NTDDI_VERSION >= NTDDI_WINXP
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::WinXP, mpt::osinfo::windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0, 0);
+#elif NTDDI_VERSION >= NTDDI_WIN2K
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::Win2000, mpt::osinfo::windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0, 0);
+#else
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::WinNT4, mpt::osinfo::windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0, 0);
+#endif
+#elif defined(_WIN32_WINNT)
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::System((static_cast<uint64>(_WIN32_WINNT) & 0xff00u) >> 8, (static_cast<uint64>(_WIN32_WINNT) & 0x00ffu) >> 0), mpt::osinfo::windows::Version::ServicePack(0, 0), 0, 0);
+#else
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::System(0, 0), mpt::osinfo::windows::Version::ServicePack(0, 0), 0, 0);
+#endif
+ }
+
+ static mpt::osinfo::windows::Version GatherWindowsVersion() noexcept {
+#if MPT_OS_WINDOWS_WINRT
+ return mpt::osinfo::windows::Version::FromSDK();
+#else // !MPT_OS_WINDOWS_WINRT
+ OSVERSIONINFOEXW versioninfoex{};
+ versioninfoex.dwOSVersionInfoSize = sizeof(versioninfoex);
+#if MPT_COMPILER_MSVC
+#pragma warning(push)
+#pragma warning(disable : 4996) // 'GetVersionExW': was declared deprecated
+#pragma warning(disable : 28159) // Consider using 'IsWindows*' instead of 'GetVersionExW'. Reason: Deprecated. Use VerifyVersionInfo* or IsWindows* macros from VersionHelpers.
+#endif // MPT_COMPILER_MSVC
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif // MPT_COMPILER_CLANG
+ if (GetVersionExW((LPOSVERSIONINFOW)&versioninfoex) == FALSE) {
+ return mpt::osinfo::windows::Version::FromSDK();
+ }
+#if MPT_COMPILER_MSVC
+#pragma warning(pop)
+#endif // MPT_COMPILER_MSVC
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif // MPT_COMPILER_CLANG
+ if (versioninfoex.dwPlatformId != VER_PLATFORM_WIN32_NT) {
+ return mpt::osinfo::windows::Version::FromSDK();
+ }
+ DWORD dwProductType = 0;
+#if (_WIN32_WINNT >= 0x0600) // _WIN32_WINNT_VISTA
+ dwProductType = PRODUCT_UNDEFINED;
+ if (GetProductInfo(versioninfoex.dwMajorVersion, versioninfoex.dwMinorVersion, versioninfoex.wServicePackMajor, versioninfoex.wServicePackMinor, &dwProductType) == FALSE) {
+ dwProductType = PRODUCT_UNDEFINED;
+ }
+#endif
+ return mpt::osinfo::windows::Version(
+ mpt::osinfo::windows::Version::System(versioninfoex.dwMajorVersion, versioninfoex.dwMinorVersion),
+ mpt::osinfo::windows::Version::ServicePack(versioninfoex.wServicePackMajor, versioninfoex.wServicePackMinor),
+ versioninfoex.dwBuildNumber,
+ dwProductType);
+#endif // MPT_OS_WINDOWS_WINRT
+ }
+
+#endif // MPT_OS_WINDOWS
+
+public:
+ static mpt::osinfo::windows::Version Current() noexcept {
+#if MPT_OS_WINDOWS
+ return GatherWindowsVersion();
+#else // !MPT_OS_WINDOWS
+ return mpt::osinfo::windows::Version::NoWindows();
+#endif // MPT_OS_WINDOWS
+ }
+
+public:
+ bool IsWindows() const noexcept {
+ return m_SystemIsWindows;
+ }
+
+ bool IsBefore(mpt::osinfo::windows::Version::System version) const noexcept {
+ if (!m_SystemIsWindows) {
+ return false;
+ }
+ return m_System < version;
+ }
+
+ bool IsBefore(mpt::osinfo::windows::Version::System version, mpt::osinfo::windows::Version::ServicePack servicePack) const noexcept {
+ if (!m_SystemIsWindows) {
+ return false;
+ }
+ if (m_System > version) {
+ return false;
+ }
+ if (m_System < version) {
+ return true;
+ }
+ return m_ServicePack < servicePack;
+ }
+
+ bool IsBefore(mpt::osinfo::windows::Version::System version, mpt::osinfo::windows::Version::Build build) const noexcept {
+ if (!m_SystemIsWindows) {
+ return false;
+ }
+ if (m_System > version) {
+ return false;
+ }
+ if (m_System < version) {
+ return true;
+ }
+ return m_Build < build;
+ }
+
+ bool IsBefore(mpt::osinfo::windows::Version::System version, mpt::osinfo::windows::Version::ServicePack servicePack, mpt::osinfo::windows::Version::Build build) const noexcept {
+ if (!m_SystemIsWindows) {
+ return false;
+ }
+ if (m_System > version) {
+ return false;
+ }
+ if (m_System < version) {
+ return true;
+ }
+ if (m_ServicePack > servicePack) {
+ return false;
+ }
+ if (m_ServicePack < servicePack) {
+ return true;
+ }
+ return m_Build < build;
+ }
+
+ bool IsBefore(mpt::osinfo::windows::Version version) const noexcept {
+ return IsBefore(version.GetSystem(), version.GetServicePack(), version.GetBuild());
+ }
+
+ bool IsAtLeast(mpt::osinfo::windows::Version::System version) const noexcept {
+ if (!m_SystemIsWindows) {
+ return false;
+ }
+ return m_System >= version;
+ }
+
+ bool IsAtLeast(mpt::osinfo::windows::Version::System version, mpt::osinfo::windows::Version::ServicePack servicePack) const noexcept {
+ if (!m_SystemIsWindows) {
+ return false;
+ }
+ if (m_System < version) {
+ return false;
+ }
+ if (m_System > version) {
+ return true;
+ }
+ return m_ServicePack >= servicePack;
+ }
+
+ bool IsAtLeast(mpt::osinfo::windows::Version::System version, mpt::osinfo::windows::Version::Build build) const noexcept {
+ if (!m_SystemIsWindows) {
+ return false;
+ }
+ if (m_System < version) {
+ return false;
+ }
+ if (m_System > version) {
+ return true;
+ }
+ return m_Build >= build;
+ }
+
+ bool IsAtLeast(mpt::osinfo::windows::Version::System version, mpt::osinfo::windows::Version::ServicePack servicePack, mpt::osinfo::windows::Version::Build build) const noexcept {
+ if (!m_SystemIsWindows) {
+ return false;
+ }
+ if (m_System < version) {
+ return false;
+ }
+ if (m_System > version) {
+ return true;
+ }
+ if (m_ServicePack < servicePack) {
+ return false;
+ }
+ if (m_ServicePack > servicePack) {
+ return true;
+ }
+ return m_Build >= build;
+ }
+
+ bool IsAtLeast(mpt::osinfo::windows::Version version) const noexcept {
+ return IsAtLeast(version.GetSystem(), version.GetServicePack(), version.GetBuild());
+ }
+
+ mpt::osinfo::windows::Version::System GetSystem() const noexcept {
+ return m_System;
+ }
+
+ mpt::osinfo::windows::Version::ServicePack GetServicePack() const noexcept {
+ return m_ServicePack;
+ }
+
+ mpt::osinfo::windows::Version::Build GetBuild() const noexcept {
+ return m_Build;
+ }
+
+ mpt::osinfo::windows::Version::TypeId GetTypeId() const noexcept {
+ return m_Type;
+ }
+
+}; // class Version
+
+
+
+namespace wine {
+
+class version {
+protected:
+ bool valid = false;
+ uint8 vmajor = 0;
+ uint8 vminor = 0;
+ uint8 vupdate = 0;
+
+public:
+ version() {
+ return;
+ }
+ version(uint8 vmajor_, uint8 vminor_, uint8 vupdate_)
+ : valid(true)
+ , vmajor(vmajor_)
+ , vminor(vminor_)
+ , vupdate(vupdate_) {
+ return;
+ }
+
+public:
+ bool IsValid() const {
+ return valid;
+ }
+
+private:
+ static mpt::osinfo::windows::wine::version FromInteger(uint32 version) {
+ mpt::osinfo::windows::wine::version result;
+ result.valid = (version <= 0xffffff);
+ result.vmajor = static_cast<uint8>(version >> 16);
+ result.vminor = static_cast<uint8>(version >> 8);
+ result.vupdate = static_cast<uint8>(version >> 0);
+ return result;
+ }
+ uint32 AsInteger() const {
+ uint32 version = 0;
+ version |= static_cast<uint32>(vmajor) << 16;
+ version |= static_cast<uint32>(vminor) << 8;
+ version |= static_cast<uint32>(vupdate) << 0;
+ return version;
+ }
+
+public:
+ bool IsBefore(mpt::osinfo::windows::wine::version other) const {
+ if (!IsValid()) {
+ return false;
+ }
+ return (AsInteger() < other.AsInteger());
+ }
+ bool IsAtLeast(mpt::osinfo::windows::wine::version other) const {
+ if (!IsValid()) {
+ return false;
+ }
+ return (AsInteger() >= other.AsInteger());
+ }
+ uint8 GetMajor() const {
+ return vmajor;
+ }
+ uint8 GetMinor() const {
+ return vminor;
+ }
+ uint8 GetUpdate() const {
+ return vupdate;
+ }
+};
+
+} // namespace wine
+
+
+
+} // namespace windows
+
+} // namespace osinfo
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_OSINFO_WINDOWS_VERSION_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/out_of_memory/out_of_memory.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/out_of_memory/out_of_memory.hpp
new file mode 100644
index 00000000..d8bec8ac
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/out_of_memory/out_of_memory.hpp
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_OUT_OF_MEMORY_OUT_OF_MEMORY_HPP
+#define MPT_OUT_OF_MEMORY_OUT_OF_MEMORY_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/detect/mfc.hpp"
+
+#include <exception>
+#if !MPT_DETECTED_MFC
+#include <new>
+#endif // !MPT_DETECTED_MFC
+
+#if MPT_DETECTED_MFC
+// cppcheck-suppress missingInclude
+#include <afx.h>
+#endif // MPT_DETECTED_MFC
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+// Exception handling helpers, because MFC requires explicit deletion of the exception object,
+// Thus, always call exactly one of mpt::rethrow_out_of_memory(e) or mpt::delete_out_of_memory(e).
+
+#if MPT_DETECTED_MFC
+
+using out_of_memory = CMemoryException *;
+
+[[noreturn]] inline void throw_out_of_memory() {
+ AfxThrowMemoryException();
+}
+
+[[noreturn]] inline void rethrow_out_of_memory(out_of_memory e) {
+ MPT_UNUSED(e);
+ // cppcheck false-positive
+ // cppcheck-suppress rethrowNoCurrentException
+ throw;
+}
+
+inline void delete_out_of_memory(out_of_memory & e) {
+ if (e) {
+ e->Delete();
+ e = nullptr;
+ }
+}
+
+#else // !MPT_DETECTED_MFC
+
+using out_of_memory = const std::bad_alloc &;
+
+[[noreturn]] inline void throw_out_of_memory() {
+ throw std::bad_alloc();
+}
+
+[[noreturn]] inline void rethrow_out_of_memory(out_of_memory e) {
+ MPT_UNUSED(e);
+ // cppcheck false-positive
+ // cppcheck-suppress rethrowNoCurrentException
+ throw;
+}
+
+inline void delete_out_of_memory(out_of_memory e) {
+ MPT_UNUSED(e);
+}
+
+#endif // MPT_DETECTED_MFC
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_OUT_OF_MEMORY_OUT_OF_MEMORY_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/parse/parse.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/parse/parse.hpp
new file mode 100644
index 00000000..30dc22a0
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/parse/parse.hpp
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_PARSE_PARSE_HPP
+#define MPT_PARSE_PARSE_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+
+#include <ios>
+#include <locale>
+#include <sstream>
+#include <string>
+#include <type_traits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+inline std::string parse_as_internal_string_type(const std::string & s) {
+ return s;
+}
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+inline std::wstring parse_as_internal_string_type(const std::wstring & s) {
+ return s;
+}
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
+#if MPT_USTRING_MODE_WIDE
+template <typename Tstring>
+inline std::wstring parse_as_internal_string_type(const Tstring & s) {
+ return mpt::transcode<std::wstring>(s);
+}
+#else // !MPT_USTRING_MODE_WIDE
+template <typename Tstring>
+inline std::string parse_as_internal_string_type(const Tstring & s) {
+ return mpt::transcode<std::string>(mpt::common_encoding::utf8, s);
+}
+#endif // MPT_USTRING_MODE_WIDE
+
+
+template <typename T, typename Tstring>
+inline T ConvertStringTo(const Tstring & str) {
+ std::basic_istringstream<typename decltype(mpt::parse_as_internal_string_type(mpt::as_string(str)))::value_type> stream(mpt::parse_as_internal_string_type(mpt::as_string(str)));
+ stream.imbue(std::locale::classic());
+ T value;
+ if constexpr (std::is_same<T, signed char>::value) {
+ signed int tmp;
+ if (!(stream >> tmp)) {
+ return T{};
+ }
+ value = static_cast<T>(tmp);
+ } else if constexpr (std::is_same<T, unsigned char>::value) {
+ unsigned int tmp;
+ if (!(stream >> tmp)) {
+ return T{};
+ }
+ value = static_cast<T>(tmp);
+ } else {
+ if (!(stream >> value)) {
+ return T{};
+ }
+ }
+ return value;
+}
+
+
+template <typename T, typename Tstring>
+inline T ConvertHexStringTo(const Tstring & str) {
+ std::basic_istringstream<typename decltype(mpt::parse_as_internal_string_type(mpt::as_string(str)))::value_type> stream(mpt::parse_as_internal_string_type(mpt::as_string(str)));
+ stream.imbue(std::locale::classic());
+ T value;
+ if constexpr (std::is_same<T, signed char>::value) {
+ signed int tmp;
+ if (!(stream >> std::hex >> tmp)) {
+ return T{};
+ }
+ value = static_cast<T>(tmp);
+ } else if constexpr (std::is_same<T, unsigned char>::value) {
+ unsigned int tmp;
+ if (!(stream >> std::hex >> tmp)) {
+ return T{};
+ }
+ value = static_cast<T>(tmp);
+ } else {
+ if (!(stream >> std::hex >> value)) {
+ return T{};
+ }
+ }
+ return value;
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_PARSE_PARSE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/parse/split.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/parse/split.hpp
new file mode 100644
index 00000000..9166ad7e
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/parse/split.hpp
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_PARSE_SPLIT_HPP
+#define MPT_PARSE_SPLIT_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+#include "mpt/parse/parse.hpp"
+#include "mpt/string/utility.hpp"
+
+#include <vector>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+template <typename T, typename Tstring>
+std::vector<T> split_parse(const Tstring & str, const Tstring & sep = Tstring(1, char_constants<typename Tstring::value_type>::comma)) {
+ std::vector<T> vals;
+ std::size_t pos = 0;
+ while (str.find(sep, pos) != std::string::npos) {
+ vals.push_back(mpt::ConvertStringTo<T>(str.substr(pos, str.find(sep, pos) - pos)));
+ pos = str.find(sep, pos) + sep.length();
+ }
+ if (!vals.empty() || (str.substr(pos).length() > 0)) {
+ vals.push_back(mpt::ConvertStringTo<T>(str.substr(pos)));
+ }
+ return vals;
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_PARSE_SPLIT_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/parse/tests/tests_parse.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/parse/tests/tests_parse.hpp
new file mode 100644
index 00000000..6ee44e15
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/parse/tests/tests_parse.hpp
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_PARSE_TESTS_PARSE_HPP
+#define MPT_PARSE_TESTS_PARSE_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/format/simple.hpp"
+#include "mpt/parse/parse.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <limits>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace parse {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/parse")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<uint32>("586"), 586u);
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<uint32>("2147483647"), (uint32)std::numeric_limits<int32>::max());
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<uint32>("4294967295"), std::numeric_limits<uint32>::max());
+
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<int64>("-9223372036854775808"), std::numeric_limits<int64>::min());
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<int64>("-159"), -159);
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<int64>("9223372036854775807"), std::numeric_limits<int64>::max());
+
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<uint64>("85059"), 85059u);
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<uint64>("9223372036854775807"), (uint64)std::numeric_limits<int64>::max());
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<uint64>("18446744073709551615"), std::numeric_limits<uint64>::max());
+
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<float>("-87.0"), -87.0f);
+#if !MPT_OS_DJGPP
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<double>("-0.5e-6"), -0.5e-6);
+#endif
+#if !MPT_OS_DJGPP
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<double>("58.65403492763"), 58.65403492763);
+#else
+ MPT_TEST_EXPECT_EQUAL(std::abs(mpt::ConvertStringTo<double>("58.65403492763") - 58.65403492763) <= 0.0001, true);
+#endif
+
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<float>(mpt::format<std::string>::val(-87.0)), -87.0f);
+#if !MPT_OS_DJGPP
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertStringTo<double>(mpt::format<std::string>::val(-0.5e-6)), -0.5e-6);
+#endif
+
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertHexStringTo<unsigned char>("fe"), 254);
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertHexStringTo<unsigned char>(L"fe"), 254);
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+ MPT_TEST_EXPECT_EQUAL(mpt::ConvertHexStringTo<unsigned int>(MPT_USTRING("ffff")), 65535u);
+}
+
+} // namespace parse
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_PARSE_TESTS_PARSE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/path/path.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/path/path.hpp
new file mode 100644
index 00000000..96f88f85
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/path/path.hpp
@@ -0,0 +1,506 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_PATH_PATH_HPP
+#define MPT_PATH_PATH_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+
+#include <filesystem>
+#include <type_traits>
+#include <utility>
+
+#if MPT_OS_WINDOWS
+#include <windows.h>
+#endif // MPT_OS_WINDOWS
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+// mpt::os_path is an alias to a string type that represents native operating system path encoding.
+// Note that this differs from std::filesystem::path::string_type on both Windows and Posix.
+// On Windows, we actually honor UNICODE and thus allow os_path.c_str() to be usable with WinAPI functions.
+// On Posix, we use a type-safe string type in locale encoding, in contrast to the encoding-confused supposedly UTF8 std::string in std::filesystem::path.
+
+#if MPT_OS_WINDOWS
+using os_path = mpt::winstring;
+#else // !MPT_OS_WINDOWS
+using os_path = mpt::lstring;
+#endif // MPT_OS_WINDOWS
+
+
+
+// mpt::os_path literals that do not involve runtime conversion.
+
+#if MPT_OS_WINDOWS
+#define MPT_OSPATH_CHAR(x) TEXT(x)
+#define MPT_OSPATH_LITERAL(x) TEXT(x)
+#define MPT_OSPATH(x) \
+ mpt::winstring { \
+ TEXT(x) \
+ }
+#else // !MPT_OS_WINDOWS
+#define MPT_OSPATH_CHAR(x) x
+#define MPT_OSPATH_LITERAL(x) x
+#define MPT_OSPATH(x) \
+ mpt::lstring { \
+ x \
+ }
+#endif // MPT_OS_WINDOWS
+
+
+
+template <>
+struct make_string_type<std::filesystem::path> {
+ using type = std::filesystem::path;
+};
+
+
+template <>
+struct is_string_type<std::filesystem::path> : public std::true_type { };
+
+
+
+template <>
+struct string_transcoder<std::filesystem::path> {
+ using string_type = std::filesystem::path;
+ static inline mpt::widestring decode(const string_type & src) {
+ if constexpr (std::is_same<std::filesystem::path::value_type, char>::value) {
+ // In contrast to standard recommendation and cppreference,
+ // native encoding on unix-like systems with libstdc++ or libc++ is actually *NOT* UTF8,
+ // but instead the conventional std::locale::locale("") encoding (which happens to be UTF8 on all modern systems, but this is not guaranteed).
+ // Note: libstdc++ and libc++ however assume that their internal representation is UTF8,
+ // which implies that wstring/u32string/u16string/u8string conversions are actually broken and MUST NOT be used, ever.
+ return mpt::transcode<mpt::widestring>(mpt::logical_encoding::locale, src.string());
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+ } else if constexpr (std::is_same<std::filesystem::path::value_type, wchar_t>::value) {
+ return mpt::transcode<mpt::widestring>(src.wstring());
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+ } else if constexpr (std::is_same<std::filesystem::path::value_type, char32_t>::value) {
+ return mpt::transcode<mpt::widestring>(src.u32string());
+ } else if constexpr (std::is_same<std::filesystem::path::value_type, char16_t>::value) {
+ return mpt::transcode<mpt::widestring>(src.u16string());
+#if MPT_CXX_AT_LEAST(20)
+ } else if constexpr (std::is_same<std::filesystem::path::value_type, char8_t>::value) {
+ return mpt::transcode<mpt::widestring>(src.u8string());
+#endif
+ } else {
+#if MPT_OS_WINDOWS && !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+ return mpt::transcode<mpt::widestring>(src.wstring());
+#elif MPT_OS_WINDOWS
+ return mpt::transcode<mpt::widestring>(mpt::logical_encoding::locale, src.string());
+#else
+ // completely unknown implementation, assume it can sanely convert to/from UTF16/UTF32
+ if constexpr (sizeof(mpt::widechar) == sizeof(char32_t)) {
+ return mpt::transcode<mpt::widestring>(src.u32string());
+ } else {
+ return mpt::transcode<mpt::widestring>(src.u16string());
+ }
+#endif
+ }
+ }
+ static inline string_type encode(const mpt::widestring & src, std::filesystem::path::format fmt) {
+ if constexpr (std::is_same<std::filesystem::path::value_type, char>::value) {
+ // In contrast to standard recommendation and cppreference,
+ // native encoding on unix-like systems with libstdc++ or libc++ is actually *NOT* UTF8,
+ // but instead the conventional std::locale::locale("") encoding (which happens to be UTF8 on all modern systems, but this is not guaranteed).
+ // Note: libstdc++ and libc++ however assume that their internal representation is UTF8,
+ // which implies that wstring/u32string/u16string/u8string conversions are actually broken and MUST NOT be used, ever.
+ return std::filesystem::path{mpt::transcode<std::string>(mpt::logical_encoding::locale, src), fmt};
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+ } else if constexpr (std::is_same<std::filesystem::path::value_type, wchar_t>::value) {
+ return std::filesystem::path{mpt::transcode<std::wstring>(src), fmt};
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+ } else if constexpr (std::is_same<std::filesystem::path::value_type, char32_t>::value) {
+ return std::filesystem::path{mpt::transcode<std::u32string>(src), fmt};
+ } else if constexpr (std::is_same<std::filesystem::path::value_type, char16_t>::value) {
+ return std::filesystem::path{mpt::transcode<std::u16string>(src), fmt};
+#if MPT_CXX_AT_LEAST(20)
+ } else if constexpr (std::is_same<std::filesystem::path::value_type, char8_t>::value) {
+ return std::filesystem::path{mpt::transcode<std::u8string>(src), fmt};
+#endif
+ } else {
+#if MPT_OS_WINDOWS && !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+ return std::filesystem::path{mpt::transcode<std::wstring>(src), fmt};
+#elif MPT_OS_WINDOWS
+ return std::filesystem::path{mpt::transcode<std::string>(mpt::logical_encoding::locale, src), fmt};
+#else
+ // completely unknown implementation, assume it can sanely convert to/from UTF16/UTF32
+ if constexpr (sizeof(mpt::widechar) == sizeof(char32_t)) {
+ return std::filesystem::path{mpt::transcode<std::u32string>(src), fmt};
+ } else {
+ return std::filesystem::path{mpt::transcode<std::u16string>(src), fmt};
+ }
+#endif
+ }
+ }
+ static inline string_type encode(const mpt::widestring & src) {
+ return encode(src, std::filesystem::path::auto_format);
+ }
+};
+
+
+
+// Best heuristics we can come up with to define std::filesystem::path literals that do not involve (or at least only non-lossy) runtime conversion.
+
+#if MPT_OS_WINDOWS && !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+#define MPT_STDPATH_CHAR(x) L##x
+#define MPT_STDPATH_LITERAL(x) L##x
+#define MPT_STDPATH(x) \
+ std::filesystem::path { \
+ L##x \
+ }
+#elif MPT_OS_WINDOWS
+#define MPT_STDPATH_CHAR(x) x
+#define MPT_STDPATH_LITERAL(x) x
+#define MPT_STDPATH(x) \
+ std::filesystem::path { \
+ x \
+ }
+#elif MPT_CXX_AT_LEAST(20)
+#define MPT_STDPATH_CHAR(x) u8##x
+#define MPT_STDPATH_LITERAL(x) u8##x
+#define MPT_STDPATH(x) \
+ std::filesystem::path { \
+ u8##x \
+ }
+#else
+#define MPT_STDPATH_CHAR(x) U##x
+#define MPT_STDPATH_LITERAL(x) U##x
+#define MPT_STDPATH(x) \
+ std::filesystem::path { \
+ U##x \
+ }
+#endif
+
+
+
+// std::filesystem::path offers implicit conversions to/from types of which it is confused about their encodings.
+// The only way to work around this problem is to implement our own mpt::path that does not do such broken nonsense.
+// We offer no implicit conversions and only explicit conversions from std::filesystem::path and mpt::os_path.
+
+class path {
+public:
+ using format = std::filesystem::path::format;
+ using std_value_type = std::filesystem::path::value_type;
+ using std_string_type = std::filesystem::path;
+ static constexpr inline std_value_type std_preferred_separator = std::filesystem::path::preferred_separator;
+ using os_value_type = os_path::value_type;
+ using os_string_type = os_path;
+ static constexpr inline os_value_type os_preferred_separator = static_cast<os_value_type>(std::filesystem::path::preferred_separator);
+
+private:
+ std::filesystem::path m_path;
+
+private:
+ template <typename path_type, std::enable_if_t<std::is_same<path_type, std::filesystem::path>::value, bool> = true>
+ explicit path(const path_type & p)
+ : m_path(p) {
+ return;
+ }
+ template <typename path_type, std::enable_if_t<std::is_same<path_type, std::filesystem::path>::value, bool> = true>
+ explicit path(path_type && p)
+ : m_path(std::forward<path_type>(p)) {
+ return;
+ }
+
+public:
+ struct internal {
+ static inline path make_path(std::filesystem::path && p) {
+ return path{std::move(p)};
+ }
+ };
+
+public:
+ template <typename path_type, std::enable_if_t<std::is_same<path_type, std::filesystem::path>::value, bool> = true>
+ static path from_stdpath(const path_type & p) {
+ return path{p};
+ }
+ static std::filesystem::path to_stdpath(const path & p) {
+ return p.m_path;
+ }
+ static os_path to_ospath(const path & p) {
+ return mpt::transcode<os_path>(p.m_path);
+ }
+ static std::filesystem::path from_ospath(const os_path & s, std::filesystem::path::format fmt = std::filesystem::path::auto_format) {
+ return string_transcoder<std::filesystem::path>{}.encode(mpt::transcode<mpt::widestring>(s), fmt);
+ }
+
+public:
+ path() noexcept = default;
+ path(const path & p)
+ : m_path(p.m_path) {
+ return;
+ }
+ path(path && p)
+ : m_path(std::move(p.m_path)) {
+ return;
+ }
+ explicit path(const os_path & s, std::filesystem::path::format fmt = std::filesystem::path::auto_format)
+ : m_path(from_ospath(s, fmt)) {
+ return;
+ }
+ path & operator=(const path & p) {
+ m_path = p.m_path;
+ return *this;
+ }
+ path & operator=(path && p) {
+ m_path = std::move(p.m_path);
+ return *this;
+ }
+ path & assign(const path & p) {
+ m_path = p.m_path;
+ return *this;
+ }
+ path & assign(path && p) {
+ m_path = std::move(p.m_path);
+ return *this;
+ }
+ path & operator/=(const path & p) {
+ m_path /= p.m_path;
+ return *this;
+ }
+ path & operator/=(path && p) {
+ m_path /= std::move(p.m_path);
+ return *this;
+ }
+ // concatenation
+ path & append(const path & p) {
+ m_path /= p.m_path;
+ return *this;
+ }
+ path & append(path && p) {
+ m_path /= std::move(p.m_path);
+ return *this;
+ }
+ path & operator+=(const path & p) {
+ m_path += p.m_path;
+ return *this;
+ }
+ path & operator+=(path && p) {
+ m_path += std::move(p.m_path);
+ return *this;
+ }
+ path & concat(const path & p) {
+ m_path += p.m_path;
+ return *this;
+ }
+ path & concat(path && p) {
+ m_path += std::move(p.m_path);
+ return *this;
+ }
+ // modifiers
+ void clear() noexcept {
+ m_path.clear();
+ }
+ path & make_preferred() {
+ m_path.make_preferred();
+ return *this;
+ }
+ path & remove_filename() {
+ m_path.remove_filename();
+ return *this;
+ }
+ path & replace_filename(const path & replacement) {
+ m_path.replace_filename(replacement.m_path);
+ return *this;
+ }
+ path & replace_extension(const path & replacement = path()) {
+ m_path.replace_extension(replacement.m_path);
+ return *this;
+ }
+ void swap(path & other) {
+ m_path.swap(other.m_path);
+ }
+ // format observers
+ std::filesystem::path stdpath() const {
+ return m_path;
+ }
+ os_path ospath() const {
+ return to_ospath(*this);
+ }
+ // compare
+ int compare(const path & p) const noexcept {
+ return m_path.compare(p.m_path);
+ }
+ // generation
+ path lexically_normal() const {
+ return path{m_path.lexically_normal()};
+ }
+ path lexically_relative(const path & base) const {
+ return path{m_path.lexically_relative(base.m_path)};
+ }
+ path lexically_proximate(const path & base) const {
+ return path{m_path.lexically_proximate(base.m_path)};
+ }
+ // decomposition
+ path root_name() const {
+ return path{m_path.root_name()};
+ }
+ path root_directory() const {
+ return path{m_path.root_directory()};
+ }
+ path root_path() const {
+ return path{m_path.root_path()};
+ }
+ path relative_path() const {
+ return path{m_path.relative_path()};
+ }
+ path parent_path() const {
+ return path{m_path.parent_path()};
+ }
+ path filename() const {
+ return path{m_path.filename()};
+ }
+ path stem() const {
+ return path{m_path.stem()};
+ }
+ path extension() const {
+ return path{m_path.extension()};
+ }
+ // queries
+ [[nodiscard]] bool empty() const noexcept {
+ return m_path.empty();
+ }
+ bool has_root_path() const {
+ return m_path.has_root_path();
+ }
+ bool has_root_name() const {
+ return m_path.has_root_name();
+ }
+ bool has_root_directory() const {
+ return m_path.has_root_directory();
+ }
+ bool has_relative_path() const {
+ return m_path.has_relative_path();
+ }
+ bool has_parent_path() const {
+ return m_path.has_parent_path();
+ }
+ bool has_filename() const {
+ return m_path.has_filename();
+ }
+ bool has_stem() const {
+ return m_path.has_stem();
+ }
+ bool has_extension() const {
+ return m_path.has_extension();
+ }
+ bool is_absolute() const {
+ return m_path.is_absolute();
+ }
+ bool is_relative() const {
+ return m_path.is_relative();
+ }
+ // comparison operators
+ friend bool operator==(const path & lhs, const path & rhs) noexcept {
+ return lhs.m_path == rhs.m_path;
+ }
+ friend bool operator!=(const path & lhs, const path & rhs) noexcept {
+ return lhs.m_path != rhs.m_path;
+ }
+ friend bool operator<(const path & lhs, const path & rhs) noexcept {
+ return lhs.m_path < rhs.m_path;
+ }
+ friend bool operator<=(const path & lhs, const path & rhs) noexcept {
+ return lhs.m_path <= rhs.m_path;
+ }
+ friend bool operator>(const path & lhs, const path & rhs) noexcept {
+ return lhs.m_path > rhs.m_path;
+ }
+ friend bool operator>=(const path & lhs, const path & rhs) noexcept {
+ return lhs.m_path >= rhs.m_path;
+ }
+ // copncatenation operator
+ friend path operator/(const path & lhs, const path & rhs) {
+ return path{lhs.m_path / rhs.m_path};
+ }
+};
+
+
+
+// Best heuristics we can come up with to define mpt::path literals that do not involve (or at least only non-lossy) runtime conversion.
+
+#if MPT_OS_WINDOWS && !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+#define MPT_PATH_CHAR(x) L##x
+#define MPT_PATH_LITERAL(x) L##x
+#define MPT_PATH(x) mpt::path::internal::make_path(L##x)
+#elif MPT_OS_WINDOWS
+#define MPT_PATH_CHAR(x) x
+#define MPT_PATH_LITERAL(x) x
+#define MPT_PATH(x) mpt::path::internal::make_path(x)
+#elif MPT_CXX_AT_LEAST(20)
+#define MPT_PATH_CHAR(x) u8##x
+#define MPT_PATH_LITERAL(x) u8##x
+#define MPT_PATH(x) mpt::path::internal::make_path(u8##x)
+#else
+#define MPT_PATH_CHAR(x) U##x
+#define MPT_PATH_LITERAL(x) U##x
+#define MPT_PATH(x) mpt::path::internal::make_path(U##x)
+#endif
+
+
+
+template <>
+struct make_string_type<mpt::path> {
+ using type = mpt::path;
+};
+
+
+template <>
+struct is_string_type<mpt::path> : public std::true_type { };
+
+
+
+template <>
+struct string_transcoder<mpt::path> {
+ using string_type = mpt::path;
+ static inline mpt::widestring decode(const string_type & src) {
+ return mpt::transcode<mpt::widestring>(src.ospath());
+ }
+ static inline string_type encode(const mpt::widestring & src) {
+ return mpt::path{mpt::transcode<mpt::os_path>(src)};
+ }
+};
+
+
+
+inline mpt::os_path support_long_path(const mpt::os_path & path) {
+#if MPT_OS_WINDOWS
+ if (path.length() < MAX_PATH) {
+ // path is short enough
+ return path;
+ }
+ if (path.substr(0, 4) == MPT_OSPATH_LITERAL("\\\\?\\")) {
+ // path is already in prefixed form
+ return path;
+ }
+ const mpt::os_path absolute_path = mpt::transcode<mpt::os_path>(std::filesystem::absolute(mpt::transcode<std::filesystem::path>(path)));
+ if (absolute_path.substr(0, 2) == MPT_OSPATH_LITERAL("\\\\")) {
+ // Path is a network share: \\server\foo.bar -> \\?\UNC\server\foo.bar
+ return MPT_OSPATH_LITERAL("\\\\?\\UNC") + absolute_path.substr(1);
+ } else {
+ // Regular file: C:\foo.bar -> \\?\C:\foo.bar
+ return MPT_OSPATH_LITERAL("\\\\?\\") + absolute_path;
+ }
+#else
+ return path;
+#endif
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_PATH_PATH_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/random/crand.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/random/crand.hpp
new file mode 100644
index 00000000..610b84f7
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/random/crand.hpp
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_RANDOM_CRAND_HPP
+#define MPT_RANDOM_CRAND_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/numeric.hpp"
+#include "mpt/random/random.hpp"
+
+#include <cstdlib>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+class crand {
+public:
+ using state_type = void;
+ using result_type = int;
+
+private:
+ static void reseed(uint32 seed) {
+ std::srand(seed);
+ }
+
+public:
+ template <typename Trd>
+ static void reseed(Trd & rd) {
+ reseed(mpt::random<uint32>(rd));
+ }
+
+public:
+ crand() = default;
+ explicit crand(const std::string &) {
+ return;
+ }
+
+public:
+ static MPT_CONSTEXPRINLINE result_type min() {
+ return 0;
+ }
+ static MPT_CONSTEXPRINLINE result_type max() {
+ return RAND_MAX;
+ }
+ static MPT_CONSTEXPRINLINE int result_bits() {
+ return mpt::lower_bound_entropy_bits(RAND_MAX);
+ }
+ result_type operator()() {
+ return std::rand();
+ }
+};
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_RANDOM_CRAND_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/random/default_engines.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/random/default_engines.hpp
new file mode 100644
index 00000000..74ae81f2
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/random/default_engines.hpp
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_RANDOM_DEFAULT_ENGINES_HPP
+#define MPT_RANDOM_DEFAULT_ENGINES_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/random/engine.hpp"
+#include "mpt/random/engine_lcg.hpp"
+
+#include <random>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+using deterministic_fast_engine = mpt::lcg_msvc;
+using deterministic_good_engine = mpt::lcg_musl;
+
+// We cannot use std::minstd_rand here because it has not a power-of-2 sized
+// output domain which we rely upon.
+using fast_engine = mpt::lcg_msvc; // about 3 ALU operations, ~32bit of state, suited for inner loops
+using good_engine = std::ranlux48;
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_RANDOM_DEFAULT_ENGINES_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/random/device.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/random/device.hpp
new file mode 100644
index 00000000..7afd4898
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/random/device.hpp
@@ -0,0 +1,304 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_RANDOM_DEVICE_HPP
+#define MPT_RANDOM_DEVICE_HPP
+
+
+
+#include "mpt/base/bit.hpp"
+#include "mpt/base/detect.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/math.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/crc/crc.hpp"
+#include "mpt/endian/integer.hpp"
+#include "mpt/mutex/mutex.hpp"
+#include "mpt/out_of_memory/out_of_memory.hpp"
+#include "mpt/random/engine.hpp"
+#include "mpt/random/engine_lcg.hpp"
+#include "mpt/random/random.hpp"
+
+#include <chrono>
+#include <limits>
+#include <memory>
+#include <random>
+#include <string>
+
+#include <cmath>
+#include <cstring>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+inline constexpr uint32 DETERMINISTIC_RNG_SEED = 3141592653u; // pi
+
+
+
+template <typename T>
+struct default_radom_seed_hash {
+};
+
+template <>
+struct default_radom_seed_hash<uint8> {
+ using type = mpt::crc16;
+};
+
+template <>
+struct default_radom_seed_hash<uint16> {
+ using type = mpt::crc16;
+};
+
+template <>
+struct default_radom_seed_hash<uint32> {
+ using type = mpt::crc32c;
+};
+
+template <>
+struct default_radom_seed_hash<uint64> {
+ using type = mpt::crc64_jones;
+};
+
+
+class prng_random_device_time_seeder {
+
+public:
+ template <typename T>
+ inline T generate_seed() {
+ // Note: CRC is actually not that good a choice here, but it is simple and we
+ // already have an implementaion available. Better choices for mixing entropy
+ // would be a hash function with proper avalanche characteristics or a block
+ // or stream cipher with any pre-choosen random key and IV. The only aspect we
+ // really need here is whitening of the bits.
+ typename mpt::default_radom_seed_hash<T>::type hash;
+
+ {
+ uint64be time;
+ time = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock().now().time_since_epoch()).count();
+ std::byte bytes[sizeof(time)];
+ std::memcpy(bytes, &time, sizeof(time));
+ hash(std::begin(bytes), std::end(bytes));
+ }
+#if !defined(MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK)
+ // Avoid std::chrono::high_resolution_clock on Emscripten because availability is problematic in AudioWorklet context.
+ {
+ uint64be time;
+ time = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock().now().time_since_epoch()).count();
+ std::byte bytes[sizeof(time)];
+ std::memcpy(bytes, &time, sizeof(time));
+ hash(std::begin(bytes), std::end(bytes));
+ }
+#endif // !MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK
+
+ return static_cast<T>(hash.result());
+ }
+
+public:
+ prng_random_device_time_seeder() = default;
+};
+
+
+// C++11 std::random_device may be implemented as a deterministic PRNG.
+// There is no way to seed this PRNG and it is allowed to be seeded with the
+// same value on each program invocation. This makes std::random_device
+// completely useless even as a non-cryptographic entropy pool.
+// We fallback to time-seeded std::mt19937 if std::random_device::entropy() is
+// 0 or less.
+class sane_random_device {
+private:
+ mpt::mutex m;
+ std::string token;
+#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
+ std::unique_ptr<std::random_device> prd;
+ bool rd_reliable{false};
+#endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
+ std::unique_ptr<std::mt19937> rd_fallback;
+
+public:
+ using result_type = unsigned int;
+
+private:
+ void init_fallback() {
+ if (!rd_fallback) {
+ if (token.length() > 0) {
+ uint64 seed_val = mpt::prng_random_device_time_seeder().generate_seed<uint64>();
+ std::vector<unsigned int> seeds;
+ seeds.push_back(static_cast<uint32>(seed_val >> 32));
+ seeds.push_back(static_cast<uint32>(seed_val >> 0));
+ for (std::size_t i = 0; i < token.length(); ++i) {
+ seeds.push_back(static_cast<unsigned int>(static_cast<unsigned char>(token[i])));
+ }
+ std::seed_seq seed(seeds.begin(), seeds.end());
+ rd_fallback = std::make_unique<std::mt19937>(seed);
+ } else {
+ uint64 seed_val = mpt::prng_random_device_time_seeder().generate_seed<uint64>();
+ unsigned int seeds[2];
+ seeds[0] = static_cast<uint32>(seed_val >> 32);
+ seeds[1] = static_cast<uint32>(seed_val >> 0);
+ std::seed_seq seed(seeds + 0, seeds + 2);
+ rd_fallback = std::make_unique<std::mt19937>(seed);
+ }
+ }
+ }
+
+public:
+ sane_random_device() {
+#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
+ try {
+ prd = std::make_unique<std::random_device>();
+ rd_reliable = ((*prd).entropy() > 0.0);
+ } catch (mpt::out_of_memory e) {
+ mpt::rethrow_out_of_memory(e);
+ } catch (const std::exception &) {
+ rd_reliable = false;
+ }
+ if (!rd_reliable) {
+ init_fallback();
+ }
+#else // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
+ init_fallback();
+#endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
+ }
+ sane_random_device(const std::string & token_)
+ : token(token_) {
+#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
+ try {
+ prd = std::make_unique<std::random_device>(token);
+ rd_reliable = ((*prd).entropy() > 0.0);
+ } catch (mpt::out_of_memory e) {
+ mpt::rethrow_out_of_memory(e);
+ } catch (const std::exception &) {
+ rd_reliable = false;
+ }
+ if (!rd_reliable) {
+ init_fallback();
+ }
+#else // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
+ init_fallback();
+#endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
+ }
+ static MPT_CONSTEXPRINLINE result_type min() {
+ return std::numeric_limits<result_type>::min();
+ }
+ static MPT_CONSTEXPRINLINE result_type max() {
+ return std::numeric_limits<result_type>::max();
+ }
+ static MPT_CONSTEXPRINLINE int result_bits() {
+ return sizeof(result_type) * 8;
+ }
+ result_type operator()() {
+ mpt::lock_guard<mpt::mutex> l(m);
+ result_type result = 0;
+#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
+ if (prd) {
+ try {
+ if constexpr (std::random_device::min() != 0 || !mpt::is_mask(std::random_device::max())) {
+ // insane std::random_device
+ // This implementation is not exactly uniformly distributed but good enough
+ // for OpenMPT.
+ constexpr double rd_min = static_cast<double>(std::random_device::min());
+ constexpr double rd_max = static_cast<double>(std::random_device::max());
+ constexpr double rd_range = rd_max - rd_min;
+ constexpr double rd_size = rd_range + 1.0;
+ const double rd_entropy = mpt::log2(rd_size);
+ const int iterations = static_cast<int>(std::ceil(result_bits() / rd_entropy));
+ double tmp = 0.0;
+ for (int i = 0; i < iterations; ++i) {
+ tmp = (tmp * rd_size) + (static_cast<double>((*prd)()) - rd_min);
+ }
+ double result_01 = std::floor(tmp / std::pow(rd_size, iterations));
+ result = static_cast<result_type>(std::floor(result_01 * (static_cast<double>(max() - min()) + 1.0))) + min();
+ } else {
+ // sane std::random_device
+ result = 0;
+ std::size_t rd_bits = mpt::lower_bound_entropy_bits(std::random_device::max());
+ for (std::size_t entropy = 0; entropy < (sizeof(result_type) * 8); entropy += rd_bits) {
+ if (rd_bits < (sizeof(result_type) * 8)) {
+ result = (result << rd_bits) | static_cast<result_type>((*prd)());
+ } else {
+ result = result | static_cast<result_type>((*prd)());
+ }
+ }
+ }
+ } catch (const std::exception &) {
+ rd_reliable = false;
+ init_fallback();
+ }
+ } else {
+ rd_reliable = false;
+ }
+ if (!rd_reliable) {
+ // std::random_device is unreliable
+ // XOR the generated random number with more entropy from the time-seeded
+ // PRNG.
+ // Note: This is safe even if the std::random_device itself is implemented
+ // as a std::mt19937 PRNG because we are very likely using a different
+ // seed.
+ result ^= mpt::random<result_type>(*rd_fallback);
+ }
+#else // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
+ result ^= mpt::random<result_type>(*rd_fallback);
+#endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
+ return result;
+ }
+};
+
+
+class prng_random_device_deterministic_seeder {
+protected:
+ template <typename T>
+ constexpr T generate_seed() noexcept {
+ return static_cast<T>(mpt::DETERMINISTIC_RNG_SEED);
+ }
+
+protected:
+ prng_random_device_deterministic_seeder() = default;
+};
+
+template <typename Trng = mpt::lcg_musl, typename seeder = mpt::prng_random_device_time_seeder>
+class prng_random_device
+ : private seeder {
+public:
+ using result_type = unsigned int;
+
+private:
+ mpt::mutex m;
+ Trng rng;
+
+public:
+ prng_random_device()
+ : rng(seeder::template generate_seed<typename Trng::state_type>()) {
+ return;
+ }
+ prng_random_device(const std::string &)
+ : rng(seeder::template generate_seed<typename Trng::state_type>()) {
+ return;
+ }
+ static MPT_CONSTEXPRINLINE result_type min() {
+ return std::numeric_limits<unsigned int>::min();
+ }
+ static MPT_CONSTEXPRINLINE result_type max() {
+ return std::numeric_limits<unsigned int>::max();
+ }
+ static MPT_CONSTEXPRINLINE int result_bits() {
+ return sizeof(unsigned int) * 8;
+ }
+ result_type operator()() {
+ mpt::lock_guard<mpt::mutex> l(m);
+ return mpt::random<unsigned int>(rng);
+ }
+};
+
+
+using deterministc_random_device = mpt::prng_random_device<mpt::lcg_musl, mpt::prng_random_device_deterministic_seeder>;
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_RANDOM_DEVICE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/random/engine.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/random/engine.hpp
new file mode 100644
index 00000000..1fc36e15
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/random/engine.hpp
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_RANDOM_ENGINE_HPP
+#define MPT_RANDOM_ENGINE_HPP
+
+
+
+#include "mpt/base/macros.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/random/seed.hpp"
+
+#include <memory>
+#include <random>
+
+#include <cstddef>
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+template <typename Trng>
+struct engine_traits {
+ typedef typename Trng::result_type result_type;
+ static MPT_CONSTEXPRINLINE int result_bits() {
+ return Trng::result_bits();
+ }
+ template <typename Trd>
+ static inline Trng make(Trd & rd) {
+ return Trng(rd);
+ }
+};
+
+// C++11 random does not provide any sane way to determine the amount of entropy
+// required to seed a particular engine. VERY STUPID.
+// List the ones we are likely to use.
+
+template <>
+struct engine_traits<std::mt19937> {
+ enum : std::size_t {
+ seed_bits = sizeof(std::mt19937::result_type) * 8 * std::mt19937::state_size
+ };
+ typedef std::mt19937 rng_type;
+ typedef rng_type::result_type result_type;
+ static MPT_CONSTEXPRINLINE int result_bits() {
+ return rng_type::word_size;
+ }
+ template <typename Trd>
+ static inline rng_type make(Trd & rd) {
+ std::unique_ptr<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>> values = std::make_unique<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>>(rd);
+ std::seed_seq seed(values->begin(), values->end());
+ return rng_type(seed);
+ }
+};
+
+template <>
+struct engine_traits<std::mt19937_64> {
+ enum : std::size_t {
+ seed_bits = sizeof(std::mt19937_64::result_type) * 8 * std::mt19937_64::state_size
+ };
+ typedef std::mt19937_64 rng_type;
+ typedef rng_type::result_type result_type;
+ static MPT_CONSTEXPRINLINE int result_bits() {
+ return rng_type::word_size;
+ }
+ template <typename Trd>
+ static inline rng_type make(Trd & rd) {
+ std::unique_ptr<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>> values = std::make_unique<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>>(rd);
+ std::seed_seq seed(values->begin(), values->end());
+ return rng_type(seed);
+ }
+};
+
+template <>
+struct engine_traits<std::ranlux24_base> {
+ enum : std::size_t {
+ seed_bits = std::ranlux24_base::word_size
+ };
+ typedef std::ranlux24_base rng_type;
+ typedef rng_type::result_type result_type;
+ static MPT_CONSTEXPRINLINE int result_bits() {
+ return rng_type::word_size;
+ }
+ template <typename Trd>
+ static inline rng_type make(Trd & rd) {
+ mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
+ std::seed_seq seed(values.begin(), values.end());
+ return rng_type(seed);
+ }
+};
+
+template <>
+struct engine_traits<std::ranlux48_base> {
+ enum : std::size_t {
+ seed_bits = std::ranlux48_base::word_size
+ };
+ typedef std::ranlux48_base rng_type;
+ typedef rng_type::result_type result_type;
+ static MPT_CONSTEXPRINLINE int result_bits() {
+ return rng_type::word_size;
+ }
+ template <typename Trd>
+ static inline rng_type make(Trd & rd) {
+ mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
+ std::seed_seq seed(values.begin(), values.end());
+ return rng_type(seed);
+ }
+};
+
+template <>
+struct engine_traits<std::ranlux24> {
+ enum : std::size_t {
+ seed_bits = std::ranlux24_base::word_size
+ };
+ typedef std::ranlux24 rng_type;
+ typedef rng_type::result_type result_type;
+ static MPT_CONSTEXPRINLINE int result_bits() {
+ return std::ranlux24_base::word_size;
+ }
+ template <typename Trd>
+ static inline rng_type make(Trd & rd) {
+ mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
+ std::seed_seq seed(values.begin(), values.end());
+ return rng_type(seed);
+ }
+};
+
+template <>
+struct engine_traits<std::ranlux48> {
+ enum : std::size_t {
+ seed_bits = std::ranlux48_base::word_size
+ };
+ typedef std::ranlux48 rng_type;
+ typedef rng_type::result_type result_type;
+ static MPT_CONSTEXPRINLINE int result_bits() {
+ return std::ranlux48_base::word_size;
+ }
+ template <typename Trd>
+ static inline rng_type make(Trd & rd) {
+ mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
+ std::seed_seq seed(values.begin(), values.end());
+ return rng_type(seed);
+ }
+};
+
+
+
+template <typename Trng, typename Trd>
+inline Trng make_prng(Trd & rd) {
+ return mpt::engine_traits<Trng>::make(rd);
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_RANDOM_ENGINE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/random/engine_lcg.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/random/engine_lcg.hpp
new file mode 100644
index 00000000..68bc68a7
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/random/engine_lcg.hpp
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_RANDOM_ENGINE_LCG_HPP
+#define MPT_RANDOM_ENGINE_LCG_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/numeric.hpp"
+#include "mpt/random/random.hpp"
+
+#include <random>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+#if MPT_COMPILER_MSVC
+#pragma warning(push)
+#pragma warning(disable : 4724) // potential mod by 0
+#endif // MPT_COMPILER_MSVC
+
+template <typename Tstate, typename Tvalue, Tstate m, Tstate a, Tstate c, Tstate result_mask, int result_shift, int result_bits_>
+class lcg_engine {
+public:
+ typedef Tstate state_type;
+ typedef Tvalue result_type;
+
+private:
+ state_type state;
+
+public:
+ template <typename Trng>
+ explicit inline lcg_engine(Trng & rd)
+ : state(mpt::random<state_type>(rd)) {
+ operator()(); // we return results from the current state and update state after returning. results in better pipelining.
+ }
+ explicit inline lcg_engine(state_type seed)
+ : state(seed) {
+ operator()(); // we return results from the current state and update state after returning. results in better pipelining.
+ }
+
+public:
+ static MPT_CONSTEXPRINLINE result_type min() {
+ return static_cast<result_type>(0);
+ }
+ static MPT_CONSTEXPRINLINE result_type max() {
+ static_assert(((result_mask >> result_shift) << result_shift) == result_mask);
+ return static_cast<result_type>(result_mask >> result_shift);
+ }
+ static MPT_CONSTEXPRINLINE int result_bits() {
+ static_assert(((static_cast<Tstate>(1) << result_bits_) - 1) == (result_mask >> result_shift));
+ return result_bits_;
+ }
+ inline result_type operator()() {
+ // we return results from the current state and update state after returning. results in better pipelining.
+ state_type s = state;
+ result_type result = static_cast<result_type>((s & result_mask) >> result_shift);
+ s = mpt::modulo_if_not_zero<state_type, m>((a * s) + c);
+ state = s;
+ return result;
+ }
+};
+
+#if MPT_COMPILER_MSVC
+#pragma warning(pop)
+#endif // MPT_COMPILER_MSVC
+
+typedef lcg_engine<uint32, uint16, 0u, 214013u, 2531011u, 0x7fff0000u, 16, 15> lcg_msvc;
+typedef lcg_engine<uint32, uint16, 0x80000000u, 1103515245u, 12345u, 0x7fff0000u, 16, 15> lcg_c99;
+typedef lcg_engine<uint64, uint32, 0ull, 6364136223846793005ull, 1ull, 0xffffffff00000000ull, 32, 32> lcg_musl;
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_RANDOM_ENGINE_LCG_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/random/random.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/random/random.hpp
new file mode 100644
index 00000000..2f4a30c4
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/random/random.hpp
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_RANDOM_RANDOM_HPP
+#define MPT_RANDOM_RANDOM_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+#include "mpt/random/engine.hpp"
+
+#include <type_traits>
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+template <typename T, typename Trng>
+inline T random(Trng & rng) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ typedef typename std::make_unsigned<T>::type unsigned_T;
+ const unsigned int rng_bits = mpt::engine_traits<Trng>::result_bits();
+ unsigned_T result = 0;
+ for (std::size_t entropy = 0; entropy < (sizeof(T) * 8); entropy += rng_bits) {
+ if constexpr (rng_bits < (sizeof(T) * 8)) {
+ constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however)
+ result = (result << shift_bits) ^ static_cast<unsigned_T>(rng());
+ } else {
+ result = static_cast<unsigned_T>(rng());
+ }
+ }
+ return static_cast<T>(result);
+}
+
+template <typename T, std::size_t required_entropy_bits, typename Trng>
+inline T random(Trng & rng) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ typedef typename std::make_unsigned<T>::type unsigned_T;
+ const unsigned int rng_bits = mpt::engine_traits<Trng>::result_bits();
+ unsigned_T result = 0;
+ for (std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) {
+ if constexpr (rng_bits < (sizeof(T) * 8)) {
+ constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however)
+ result = (result << shift_bits) ^ static_cast<unsigned_T>(rng());
+ } else {
+ result = static_cast<unsigned_T>(rng());
+ }
+ }
+ if constexpr (required_entropy_bits >= (sizeof(T) * 8)) {
+ return static_cast<T>(result);
+ } else {
+ return static_cast<T>(result & ((static_cast<unsigned_T>(1) << required_entropy_bits) - static_cast<unsigned_T>(1)));
+ }
+}
+
+template <typename T, typename Trng>
+inline T random(Trng & rng, std::size_t required_entropy_bits) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ typedef typename std::make_unsigned<T>::type unsigned_T;
+ const unsigned int rng_bits = mpt::engine_traits<Trng>::result_bits();
+ unsigned_T result = 0;
+ for (std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits) {
+ if constexpr (rng_bits < (sizeof(T) * 8)) {
+ constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however)
+ result = (result << shift_bits) ^ static_cast<unsigned_T>(rng());
+ } else {
+ result = static_cast<unsigned_T>(rng());
+ }
+ }
+ if (required_entropy_bits >= (sizeof(T) * 8)) {
+ return static_cast<T>(result);
+ } else {
+ return static_cast<T>(result & ((static_cast<unsigned_T>(1) << required_entropy_bits) - static_cast<unsigned_T>(1)));
+ }
+}
+
+template <typename T>
+struct uniform_real_distribution {
+private:
+ T a;
+ T b;
+
+public:
+ inline uniform_real_distribution(T a_, T b_)
+ : a(a_)
+ , b(b_) {
+ return;
+ }
+ template <typename Trng>
+ inline T operator()(Trng & rng) const {
+ const int mantissa_bits = std::numeric_limits<T>::digits;
+ return ((b - a) * static_cast<T>(mpt::random<uint64, mantissa_bits>(rng)) / static_cast<T>((static_cast<uint64>(1u) << mantissa_bits))) + a;
+ }
+};
+
+
+template <typename T, typename Trng>
+inline T random(Trng & rng, T min, T max) {
+ static_assert(!std::numeric_limits<T>::is_integer);
+ typedef mpt::uniform_real_distribution<T> dis_type;
+ dis_type dis(min, max);
+ return static_cast<T>(dis(rng));
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_RANDOM_RANDOM_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/random/seed.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/random/seed.hpp
new file mode 100644
index 00000000..e6ca4bd5
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/random/seed.hpp
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_RANDOM_SEED_HPP
+#define MPT_RANDOM_SEED_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+
+#include <array>
+#include <random>
+
+#include <cstddef>
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+template <std::size_t N>
+class seed_seq_values {
+private:
+ std::array<unsigned int, N> seeds;
+
+public:
+ template <typename Trd>
+ explicit seed_seq_values(Trd & rd) {
+ std::uniform_int_distribution<unsigned int> random_int{};
+ for (std::size_t i = 0; i < N; ++i) {
+ seeds[i] = random_int(rd);
+ }
+ }
+ const unsigned int * begin() const {
+ return seeds.data();
+ }
+ const unsigned int * end() const {
+ return seeds.data() + N;
+ }
+};
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_RANDOM_SEED_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/random/tests/tests_random.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/random/tests/tests_random.hpp
new file mode 100644
index 00000000..1076b583
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/random/tests/tests_random.hpp
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_TESTS_RANDOM_HPP
+#define MPT_BASE_TESTS_RANDOM_HPP
+
+
+
+#include "mpt/base/algorithm.hpp"
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/random/default_engines.hpp"
+#include "mpt/random/device.hpp"
+#include "mpt/random/random.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace random {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/random")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ mpt::sane_random_device rd;
+ mpt::good_engine prng = mpt::make_prng<mpt::good_engine>(rd);
+
+ bool failed = false;
+
+ for (std::size_t i = 0; i < 10000; ++i) {
+
+ failed = failed || !mpt::is_in_range(mpt::random<uint16, 7>(prng), 0u, 127u);
+ failed = failed || !mpt::is_in_range(mpt::random<uint16, 8>(prng), 0u, 255u);
+ failed = failed || !mpt::is_in_range(mpt::random<uint16, 9>(prng), 0u, 511u);
+ failed = failed || !mpt::is_in_range(mpt::random<uint64, 1>(prng), 0u, 1u);
+ failed = failed || !mpt::is_in_range(mpt::random<uint16>(prng, 7), 0u, 127u);
+ failed = failed || !mpt::is_in_range(mpt::random<uint16>(prng, 8), 0u, 255u);
+ failed = failed || !mpt::is_in_range(mpt::random<uint16>(prng, 9), 0u, 511u);
+ failed = failed || !mpt::is_in_range(mpt::random<uint64>(prng, 1), 0u, 1u);
+
+ failed = failed || !mpt::is_in_range(mpt::random<int16, 7>(prng), 0, 127);
+ failed = failed || !mpt::is_in_range(mpt::random<int16, 8>(prng), 0, 255);
+ failed = failed || !mpt::is_in_range(mpt::random<int16, 9>(prng), 0, 511);
+ failed = failed || !mpt::is_in_range(mpt::random<int64, 1>(prng), 0, 1);
+ failed = failed || !mpt::is_in_range(mpt::random<int16>(prng, 7), 0, 127);
+ failed = failed || !mpt::is_in_range(mpt::random<int16>(prng, 8), 0, 255);
+ failed = failed || !mpt::is_in_range(mpt::random<int16>(prng, 9), 0, 511);
+ failed = failed || !mpt::is_in_range(mpt::random<int64>(prng, 1), 0, 1);
+
+ failed = failed || !mpt::is_in_range(mpt::random<float>(prng, 0.0f, 1.0f), 0.0f, 1.0f);
+ failed = failed || !mpt::is_in_range(mpt::random<double>(prng, 0.0, 1.0), 0.0, 1.0);
+ failed = failed || !mpt::is_in_range(mpt::random<double>(prng, -1.0, 1.0), -1.0, 1.0);
+ failed = failed || !mpt::is_in_range(mpt::random<double>(prng, -1.0, 0.0), -1.0, 0.0);
+ failed = failed || !mpt::is_in_range(mpt::random<double>(prng, 1.0, 2.0), 1.0, 2.0);
+ failed = failed || !mpt::is_in_range(mpt::random<double>(prng, 1.0, 3.0), 1.0, 3.0);
+ }
+
+ MPT_TEST_EXPECT_EXPR(!failed);
+}
+
+} // namespace random
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_TESTS_RANDOM_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/string/buffer.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/string/buffer.hpp
new file mode 100644
index 00000000..1f032bae
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/string/buffer.hpp
@@ -0,0 +1,344 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_STRING_BUFFER_HPP
+#define MPT_STRING_BUFFER_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/detect/mfc.hpp"
+#include "mpt/string/types.hpp"
+
+#include <algorithm>
+#include <array>
+#include <string>
+#include <string_view>
+#include <type_traits>
+
+#include <cassert>
+#include <cstddef>
+
+#if MPT_DETECTED_MFC
+// cppcheck-suppress missingInclude
+#include <afx.h>
+#endif // MPT_DETECTED_MFC
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+
+template <typename Tstring, typename Tchar>
+class StringBufRefImpl {
+private:
+ Tchar * buf;
+ std::size_t size;
+
+public:
+ // cppcheck false-positive
+ // cppcheck-suppress uninitMemberVar
+ explicit StringBufRefImpl(Tchar * buf_, std::size_t size_)
+ : buf(buf_)
+ , size(size_) {
+ static_assert(sizeof(Tchar) == sizeof(typename Tstring::value_type));
+ assert(size > 0);
+ }
+ StringBufRefImpl(const StringBufRefImpl &) = delete;
+ StringBufRefImpl(StringBufRefImpl &&) = default;
+ StringBufRefImpl & operator=(const StringBufRefImpl &) = delete;
+ StringBufRefImpl & operator=(StringBufRefImpl &&) = delete;
+ operator Tstring() const {
+ std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0
+ return Tstring(buf, buf + len);
+ }
+ explicit operator std::basic_string_view<Tchar>() const {
+ std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0
+ return std::basic_string_view<Tchar>(buf, buf + len);
+ }
+ bool empty() const {
+ return buf[0] == Tchar('\0');
+ }
+ StringBufRefImpl & operator=(const Tstring & str) {
+ std::copy(str.data(), str.data() + std::min(str.length(), size - 1), buf);
+ std::fill(buf + std::min(str.length(), size - 1), buf + size, Tchar('\0'));
+ return *this;
+ }
+};
+
+template <typename Tstring, typename Tchar>
+class StringBufRefImpl<Tstring, const Tchar> {
+private:
+ const Tchar * buf;
+ std::size_t size;
+
+public:
+ // cppcheck false-positive
+ // cppcheck-suppress uninitMemberVar
+ explicit StringBufRefImpl(const Tchar * buf_, std::size_t size_)
+ : buf(buf_)
+ , size(size_) {
+ static_assert(sizeof(Tchar) == sizeof(typename Tstring::value_type));
+ assert(size > 0);
+ }
+ StringBufRefImpl(const StringBufRefImpl &) = delete;
+ StringBufRefImpl(StringBufRefImpl &&) = default;
+ StringBufRefImpl & operator=(const StringBufRefImpl &) = delete;
+ StringBufRefImpl & operator=(StringBufRefImpl &&) = delete;
+ operator Tstring() const {
+ std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0
+ return Tstring(buf, buf + len);
+ }
+ explicit operator std::basic_string_view<Tchar>() const {
+ std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0
+ return std::basic_string_view<Tchar>(buf, len);
+ }
+ bool empty() const {
+ return buf[0] == Tchar('\0');
+ }
+};
+
+
+
+template <typename Tstring, typename Tchar, std::size_t size>
+inline StringBufRefImpl<Tstring, typename std::add_const<Tchar>::type> ReadTypedBuf(const std::array<Tchar, size> & buf) {
+ return StringBufRefImpl<Tstring, typename std::add_const<Tchar>::type>(buf.data(), size);
+}
+template <typename Tstring, typename Tchar, std::size_t size>
+inline StringBufRefImpl<Tstring, typename std::add_const<Tchar>::type> ReadTypedBuf(const Tchar (&buf)[size]) {
+ return StringBufRefImpl<Tstring, typename std::add_const<Tchar>::type>(buf, size);
+}
+template <typename Tstring, typename Tchar>
+inline StringBufRefImpl<Tstring, typename std::add_const<Tchar>::type> ReadTypedBuf(const Tchar * buf, std::size_t size) {
+ return StringBufRefImpl<Tstring, typename std::add_const<Tchar>::type>(buf, size);
+}
+template <typename Tstring, typename Tchar, std::size_t size>
+inline StringBufRefImpl<Tstring, Tchar> WriteTypedBuf(std::array<Tchar, size> & buf) {
+ return StringBufRefImpl<Tstring, Tchar>(buf.data(), size);
+}
+template <typename Tstring, typename Tchar, std::size_t size>
+inline StringBufRefImpl<Tstring, Tchar> WriteTypedBuf(Tchar (&buf)[size]) {
+ return StringBufRefImpl<Tstring, Tchar>(buf, size);
+}
+template <typename Tstring, typename Tchar>
+inline StringBufRefImpl<Tstring, Tchar> WriteTypedBuf(Tchar * buf, std::size_t size) {
+ return StringBufRefImpl<Tstring, Tchar>(buf, size);
+}
+
+
+
+template <typename Tchar, std::size_t size>
+inline StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, typename std::add_const<Tchar>::type> ReadAutoBuf(const std::array<Tchar, size> & buf) {
+ return StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, typename std::add_const<Tchar>::type>(buf.data(), size);
+}
+template <typename Tchar, std::size_t size>
+inline StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, typename std::add_const<Tchar>::type> ReadAutoBuf(const Tchar (&buf)[size]) {
+ return StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, typename std::add_const<Tchar>::type>(buf, size);
+}
+template <typename Tchar>
+inline StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, typename std::add_const<Tchar>::type> ReadAutoBuf(const Tchar * buf, std::size_t size) {
+ return StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, typename std::add_const<Tchar>::type>(buf, size);
+}
+template <typename Tchar, std::size_t size>
+inline StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, Tchar> WriteAutoBuf(std::array<Tchar, size> & buf) {
+ return StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, Tchar>(buf.data(), size);
+}
+template <typename Tchar, std::size_t size>
+inline StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, Tchar> WriteAutoBuf(Tchar (&buf)[size]) {
+ return StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, Tchar>(buf, size);
+}
+template <typename Tchar>
+inline StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, Tchar> WriteAutoBuf(Tchar * buf, std::size_t size) {
+ return StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, Tchar>(buf, size);
+}
+
+
+
+#if MPT_OS_WINDOWS
+
+template <typename Tchar, std::size_t size>
+inline StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, typename std::add_const<Tchar>::type> ReadWinBuf(const std::array<Tchar, size> & buf) {
+ return StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, typename std::add_const<Tchar>::type>(buf.data(), size);
+}
+template <typename Tchar, std::size_t size>
+inline StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, typename std::add_const<Tchar>::type> ReadWinBuf(const Tchar (&buf)[size]) {
+ return StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, typename std::add_const<Tchar>::type>(buf, size);
+}
+template <typename Tchar>
+inline StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, typename std::add_const<Tchar>::type> ReadWinBuf(const Tchar * buf, std::size_t size) {
+ return StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, typename std::add_const<Tchar>::type>(buf, size);
+}
+template <typename Tchar, std::size_t size>
+inline StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, Tchar> WriteWinBuf(std::array<Tchar, size> & buf) {
+ return StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, Tchar>(buf.data(), size);
+}
+template <typename Tchar, std::size_t size>
+inline StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, Tchar> WriteWinBuf(Tchar (&buf)[size]) {
+ return StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, Tchar>(buf, size);
+}
+template <typename Tchar>
+inline StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, Tchar> WriteWinBuf(Tchar * buf, std::size_t size) {
+ return StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, Tchar>(buf, size);
+}
+
+#endif // MPT_OS_WINDOWS
+
+
+
+#if MPT_DETECTED_MFC
+
+template <typename Tchar>
+class CStringBufRefImpl {
+private:
+ Tchar * buf;
+ std::size_t size;
+
+public:
+ // cppcheck false-positive
+ // cppcheck-suppress uninitMemberVar
+ explicit CStringBufRefImpl(Tchar * buf_, std::size_t size_)
+ : buf(buf_)
+ , size(size_) {
+ assert(size > 0);
+ }
+ CStringBufRefImpl(const CStringBufRefImpl &) = delete;
+ CStringBufRefImpl(CStringBufRefImpl &&) = default;
+ CStringBufRefImpl & operator=(const CStringBufRefImpl &) = delete;
+ CStringBufRefImpl & operator=(CStringBufRefImpl &&) = delete;
+ operator CString() const {
+ std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0
+ return CString(buf, mpt::saturate_cast<int>(len));
+ }
+ CStringBufRefImpl & operator=(const CString & str) {
+ std::copy(str.GetString(), str.GetString() + std::min(static_cast<std::size_t>(str.GetLength()), size - 1), buf);
+ std::fill(buf + std::min(static_cast<std::size_t>(str.GetLength()), size - 1), buf + size, Tchar('\0'));
+ return *this;
+ }
+};
+
+template <typename Tchar>
+class CStringBufRefImpl<const Tchar> {
+private:
+ const Tchar * buf;
+ std::size_t size;
+
+public:
+ // cppcheck false-positive
+ // cppcheck-suppress uninitMemberVar
+ explicit CStringBufRefImpl(const Tchar * buf_, std::size_t size_)
+ : buf(buf_)
+ , size(size_) {
+ assert(size > 0);
+ }
+ CStringBufRefImpl(const CStringBufRefImpl &) = delete;
+ CStringBufRefImpl(CStringBufRefImpl &&) = default;
+ CStringBufRefImpl & operator=(const CStringBufRefImpl &) = delete;
+ CStringBufRefImpl & operator=(CStringBufRefImpl &&) = delete;
+ operator CString() const {
+ std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0
+ return CString(buf, mpt::saturate_cast<int>(len));
+ }
+};
+
+template <typename Tchar, std::size_t size>
+inline CStringBufRefImpl<typename std::add_const<Tchar>::type> ReadCStringBuf(const std::array<Tchar, size> & buf) {
+ return CStringBufRefImpl<typename std::add_const<Tchar>::type>(buf.data(), size);
+}
+template <typename Tchar, std::size_t size>
+inline CStringBufRefImpl<typename std::add_const<Tchar>::type> ReadCStringBuf(const Tchar (&buf)[size]) {
+ return CStringBufRefImpl<typename std::add_const<Tchar>::type>(buf, size);
+}
+template <typename Tchar>
+inline CStringBufRefImpl<typename std::add_const<Tchar>::type> ReadCStringBuf(const Tchar * buf, std::size_t size) {
+ return CStringBufRefImpl<typename std::add_const<Tchar>::type>(buf, size);
+}
+template <typename Tchar, std::size_t size>
+inline CStringBufRefImpl<Tchar> WriteCStringBuf(std::array<Tchar, size> & buf) {
+ return CStringBufRefImpl<Tchar>(buf.data(), size);
+}
+template <typename Tchar, std::size_t size>
+inline CStringBufRefImpl<Tchar> WriteCStringBuf(Tchar (&buf)[size]) {
+ return CStringBufRefImpl<Tchar>(buf, size);
+}
+template <typename Tchar>
+inline CStringBufRefImpl<Tchar> WriteCStringBuf(Tchar * buf, std::size_t size) {
+ return CStringBufRefImpl<Tchar>(buf, size);
+}
+
+#endif // MPT_DETECTED_MFC
+
+
+
+template <std::size_t len>
+struct charbuf {
+public:
+ using Tchar = char;
+ using char_type = Tchar;
+ using string_type = std::basic_string<Tchar>;
+ using string_view_type = std::basic_string_view<Tchar>;
+ constexpr std::size_t static_length() const {
+ return len;
+ }
+
+public:
+ Tchar buf[len];
+
+public:
+ charbuf() {
+ std::fill(std::begin(buf), std::end(buf), Tchar('\0'));
+ }
+ charbuf(const charbuf &) = default;
+ charbuf(charbuf &&) = default;
+ charbuf & operator=(const charbuf &) = default;
+ charbuf & operator=(charbuf &&) = default;
+ const Tchar & operator[](std::size_t i) const {
+ return buf[i];
+ }
+ std::string str() const {
+ return static_cast<std::string>(*this);
+ }
+ operator string_type() const {
+ return mpt::ReadAutoBuf(buf);
+ }
+ explicit operator string_view_type() const {
+ return static_cast<string_view_type>(mpt::ReadAutoBuf(buf));
+ }
+ bool empty() const {
+ return mpt::ReadAutoBuf(buf).empty();
+ }
+ charbuf & operator=(const string_type & str) {
+ mpt::WriteAutoBuf(buf) = str;
+ return *this;
+ }
+
+public:
+ friend bool operator!=(const charbuf & a, const charbuf & b) {
+ return static_cast<string_view_type>(a) != static_cast<string_view_type>(b);
+ }
+ friend bool operator!=(const std::string & a, const charbuf & b) {
+ return a != static_cast<string_view_type>(b);
+ }
+ friend bool operator!=(const charbuf & a, const std::string & b) {
+ return static_cast<string_view_type>(a) != b;
+ }
+ friend bool operator==(const charbuf & a, const charbuf & b) {
+ return static_cast<string_view_type>(a) == static_cast<string_view_type>(b);
+ }
+ friend bool operator==(const std::string & a, const charbuf & b) {
+ return a == static_cast<string_view_type>(b);
+ }
+ friend bool operator==(const charbuf & a, const std::string & b) {
+ return static_cast<string_view_type>(a) == b;
+ }
+};
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_STRING_BUFFER_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/string/tests/tests_string_buffer.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/string/tests/tests_string_buffer.hpp
new file mode 100644
index 00000000..2219c1e2
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/string/tests/tests_string_buffer.hpp
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_STRING_TESTS_STRING_BUFFER_HPP
+#define MPT_STRING_TESTS_STRING_BUFFER_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/string/buffer.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <string>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace string {
+namespace buffer {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/string/buffer")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ {
+ char buf[4] = {'x', 'x', 'x', 'x'};
+ mpt::WriteAutoBuf(buf) = std::string("foobar");
+ MPT_TEST_EXPECT_EQUAL(buf[0], 'f');
+ MPT_TEST_EXPECT_EQUAL(buf[1], 'o');
+ MPT_TEST_EXPECT_EQUAL(buf[2], 'o');
+ MPT_TEST_EXPECT_EQUAL(buf[3], '\0');
+ }
+ {
+ char buf[4] = {'x', 'x', 'x', 'x'};
+ char foobar[] = {'f', 'o', 'o', 'b', 'a', 'r', '\0'};
+ mpt::WriteTypedBuf<std::string>(buf) = (char *)foobar;
+ MPT_TEST_EXPECT_EQUAL(buf[0], 'f');
+ MPT_TEST_EXPECT_EQUAL(buf[1], 'o');
+ MPT_TEST_EXPECT_EQUAL(buf[2], 'o');
+ MPT_TEST_EXPECT_EQUAL(buf[3], '\0');
+ }
+ {
+ char buf[4] = {'x', 'x', 'x', 'x'};
+ mpt::WriteTypedBuf<std::string>(buf) = (const char *)"foobar";
+ MPT_TEST_EXPECT_EQUAL(buf[0], 'f');
+ MPT_TEST_EXPECT_EQUAL(buf[1], 'o');
+ MPT_TEST_EXPECT_EQUAL(buf[2], 'o');
+ MPT_TEST_EXPECT_EQUAL(buf[3], '\0');
+ }
+ {
+ char buf[4] = {'x', 'x', 'x', 'x'};
+ mpt::WriteTypedBuf<std::string>(buf) = "foobar";
+ MPT_TEST_EXPECT_EQUAL(buf[0], 'f');
+ MPT_TEST_EXPECT_EQUAL(buf[1], 'o');
+ MPT_TEST_EXPECT_EQUAL(buf[2], 'o');
+ MPT_TEST_EXPECT_EQUAL(buf[3], '\0');
+ }
+ {
+ const char buf[4] = {'f', 'o', 'o', 'b'};
+ std::string foo = mpt::ReadAutoBuf(buf);
+ MPT_TEST_EXPECT_EQUAL(foo, std::string("foob"));
+ }
+}
+
+} // namespace buffer
+} // namespace string
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_STRING_TESTS_STRING_BUFFER_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/string/tests/tests_string_utility.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/string/tests/tests_string_utility.hpp
new file mode 100644
index 00000000..5f24f214
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/string/tests/tests_string_utility.hpp
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_STRING_TESTS_STRING_UTILITY_HPP
+#define MPT_STRING_TESTS_STRING_UTILITY_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/string/utility.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+#include <string>
+
+#include <cstddef>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace string {
+namespace utility {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/string/utility")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ MPT_TEST_EXPECT_EQUAL(mpt::trim_left(std::string(" ")), "");
+ MPT_TEST_EXPECT_EQUAL(mpt::trim_right(std::string(" ")), "");
+ MPT_TEST_EXPECT_EQUAL(mpt::trim(std::string(" ")), "");
+
+ // weird things with std::string containing \0 in the middle and trimming \0
+ MPT_TEST_EXPECT_EQUAL(std::string("\0\ta\0b ", 6).length(), (std::size_t)6);
+ MPT_TEST_EXPECT_EQUAL(mpt::trim_right(std::string("\0\ta\0b ", 6)), std::string("\0\ta\0b", 5));
+ MPT_TEST_EXPECT_EQUAL(mpt::trim(std::string("\0\ta\0b\0", 6), std::string("\0", 1)), std::string("\ta\0b", 4));
+}
+
+} // namespace utility
+} // namespace string
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_STRING_TESTS_STRING_UTILITY_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/string/types.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/string/types.hpp
new file mode 100644
index 00000000..64df0c20
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/string/types.hpp
@@ -0,0 +1,396 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_STRING_TYPES_HPP
+#define MPT_STRING_TYPES_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/detect/mfc.hpp"
+
+#include <string>
+#include <type_traits>
+
+#include <cstddef>
+
+#if MPT_OS_WINDOWS
+#include <windows.h>
+#endif // MPT_OS_WINDOWS
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+enum class common_encoding {
+ utf8,
+ ascii, // strictly 7-bit ASCII
+ iso8859_1,
+ iso8859_15,
+ cp850,
+ cp437,
+ windows1252,
+ amiga,
+ riscos,
+ iso8859_1_no_c1,
+ iso8859_15_no_c1,
+ amiga_no_c1,
+};
+
+
+enum class logical_encoding {
+ locale, // CP_ACP on windows, system configured C locale otherwise
+ active_locale, // active C/C++ global locale
+};
+
+// source code / preprocessor (i.e. # token)
+inline constexpr auto source_encoding = common_encoding::ascii;
+
+// debug log files
+inline constexpr auto logfile_encoding = common_encoding::utf8;
+
+// std::clog / std::cout / std::cerr
+inline constexpr auto stdio_encoding = logical_encoding::locale;
+
+// getenv
+inline constexpr auto environment_encoding = logical_encoding::locale;
+
+// std::exception::what()
+inline constexpr auto exception_encoding = logical_encoding::active_locale;
+
+
+
+
+
+template <typename T>
+struct is_character : public std::false_type { };
+
+template <>
+struct is_character<char> : public std::true_type { };
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+template <>
+struct is_character<wchar_t> : public std::true_type { };
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+#if MPT_CXX_AT_LEAST(20)
+template <>
+struct is_character<char8_t> : public std::true_type { };
+#endif // C++20
+template <>
+struct is_character<char16_t> : public std::true_type { };
+template <>
+struct is_character<char32_t> : public std::true_type { };
+
+
+
+
+
+template <typename T, typename std::enable_if<mpt::is_character<T>::value, bool>::type = true>
+MPT_CONSTEXPRINLINE typename std::make_unsigned<T>::type char_value(T x) noexcept {
+ return static_cast<typename std::make_unsigned<T>::type>(x);
+}
+
+
+
+
+
+template <typename T>
+struct unsafe_char_converter { };
+
+template <>
+struct unsafe_char_converter<char> {
+ static constexpr char32_t decode(char c) noexcept {
+ return static_cast<char32_t>(static_cast<uint32>(static_cast<unsigned char>(c)));
+ }
+ static constexpr char encode(char32_t c) noexcept {
+ return static_cast<char>(static_cast<unsigned char>(static_cast<uint32>(c)));
+ }
+};
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+template <>
+struct unsafe_char_converter<wchar_t> {
+ static constexpr char32_t decode(wchar_t c) noexcept {
+ return static_cast<char32_t>(static_cast<uint32>(static_cast<std::make_unsigned<wchar_t>::type>(c)));
+ }
+ static constexpr wchar_t encode(char32_t c) noexcept {
+ return static_cast<wchar_t>(static_cast<std::make_unsigned<wchar_t>::type>(static_cast<uint32>(c)));
+ }
+};
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
+#if MPT_CXX_AT_LEAST(20)
+template <>
+struct unsafe_char_converter<char8_t> {
+ static constexpr char32_t decode(char8_t c) noexcept {
+ return static_cast<char32_t>(static_cast<uint32>(static_cast<uint8>(c)));
+ }
+ static constexpr char8_t encode(char32_t c) noexcept {
+ return static_cast<char8_t>(static_cast<uint8>(static_cast<uint32>(c)));
+ }
+};
+#endif // C++20
+
+template <>
+struct unsafe_char_converter<char16_t> {
+ static constexpr char32_t decode(char16_t c) noexcept {
+ return static_cast<char32_t>(static_cast<uint32>(static_cast<uint16>(c)));
+ }
+ static constexpr char16_t encode(char32_t c) noexcept {
+ return static_cast<char16_t>(static_cast<uint16>(static_cast<uint32>(c)));
+ }
+};
+
+template <>
+struct unsafe_char_converter<char32_t> {
+ static constexpr char32_t decode(char32_t c) noexcept {
+ return c;
+ }
+ static constexpr char32_t encode(char32_t c) noexcept {
+ return c;
+ }
+};
+
+template <typename Tdstchar, typename Tsrcchar>
+constexpr Tdstchar unsafe_char_convert(Tsrcchar src) noexcept {
+ return mpt::unsafe_char_converter<Tdstchar>::encode(mpt::unsafe_char_converter<Tsrcchar>::decode(src));
+}
+
+
+
+
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+using widestring = std::wstring;
+using widechar = wchar_t;
+#define MPT_WIDECHAR(x) L##x
+#define MPT_WIDELITERAL(x) L##x
+#define MPT_WIDESTRING(x) std::wstring(L##x)
+#else // MPT_COMPILER_QUIRK_NO_WCHAR
+using widestring = std::u32string;
+using widechar = char32_t;
+#define MPT_WIDECHAR(x) U##x
+#define MPT_WIDELITERAL(x) U##x
+#define MPT_WIDESTRING(x) std::u32string(U##x)
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
+
+
+template <common_encoding common_encoding_tag>
+struct common_encoding_char_traits : std::char_traits<char> {
+ static constexpr auto encoding() noexcept {
+ return common_encoding_tag;
+ }
+};
+
+template <logical_encoding logical_encoding_tag>
+struct logical_encoding_char_traits : std::char_traits<char> {
+ static constexpr auto encoding() noexcept {
+ return logical_encoding_tag;
+ }
+};
+
+
+
+using lstring = std::basic_string<char, mpt::logical_encoding_char_traits<logical_encoding::locale>>;
+
+using source_string = std::basic_string<char, mpt::common_encoding_char_traits<source_encoding>>;
+using exception_string = std::basic_string<char, mpt::logical_encoding_char_traits<exception_encoding>>;
+
+#if MPT_OS_WINDOWS
+
+template <typename Tchar>
+struct windows_char_traits { };
+template <>
+struct windows_char_traits<CHAR> { using string_type = mpt::lstring; };
+template <>
+struct windows_char_traits<WCHAR> { using string_type = std::wstring; };
+
+using tstring = windows_char_traits<TCHAR>::string_type;
+
+using winstring = mpt::tstring;
+
+#endif // MPT_OS_WINDOWS
+
+
+
+#if MPT_CXX_AT_LEAST(20)
+
+using u8string = std::u8string;
+using u8char = char8_t;
+#define MPT_U8CHAR(x) u8##x
+#define MPT_U8LITERAL(x) u8##x
+#define MPT_U8STRING(x) std::u8string(u8##x)
+
+#else // !C++20
+
+using u8string = std::basic_string<char, mpt::common_encoding_char_traits<common_encoding::utf8>>;
+using u8char = char;
+#define MPT_U8CHAR(x) x
+#define MPT_U8LITERAL(x) x
+#define MPT_U8STRING(x) mpt::u8string(x)
+
+// mpt::u8string is a moderately type-safe string that is meant to contain
+// UTF-8 encoded char bytes.
+//
+// mpt::u8string is not implicitely convertible to/from std::string, but
+// it is convertible to/from C strings the same way as std::string is.
+//
+// The implementation of mpt::u8string is a compromise of compatibilty
+// with implementation-defined STL details, efficiency, source code size,
+// executable bloat, type-safety and simplicity.
+//
+// mpt::u8string is not meant to be used directly though.
+// mpt::u8string is meant as an alternative implementaion to std::wstring
+// for implementing the unicode string type mpt::ustring.
+
+#endif // C++20
+
+
+
+#if !defined(MPT_USTRING_MODE_UTF8_FORCE) && (MPT_COMPILER_MSVC || (MPT_DETECTED_MFC && defined(UNICODE)))
+// Use wide strings for MSVC because this is the native encoding on
+// microsoft platforms.
+#define MPT_USTRING_MODE_WIDE 1
+#define MPT_USTRING_MODE_UTF8 0
+#else
+#define MPT_USTRING_MODE_WIDE 0
+#define MPT_USTRING_MODE_UTF8 1
+#endif
+
+// mpt::ustring
+//
+// mpt::ustring is a string type that can hold unicode strings.
+// It is implemented as a std::basic_string either based on wchar_t (i.e. the
+// same as std::wstring) or a custom-defined char_traits class that is derived
+// from std::char_traits<char>.
+// The selection of the underlying implementation is done at compile-time.
+// MPT_UCHAR, MPT_ULITERAL and MPT_USTRING are macros that ease construction
+// of ustring char literals, ustring char array literals and ustring objects
+// from ustring char literals that work consistently in both modes.
+// Note that these are not supported for non-ASCII characters appearing in
+// the macro argument.
+// Also note that, as both UTF8 and UTF16 (it is less of an issue for UTF32)
+// are variable-length encodings and mpt::ustring is implemented as a
+// std::basic_string, all member functions that require individual character
+// access will not work consistently or even at all in a meaningful way.
+// This in particular affects operator[], find() and substr().
+// The code makes no effort in preventing these or generating warnings when
+// these are used on mpt::ustring objects. However, compiling in the
+// respectively other mpt::ustring mode will catch most of these anyway.
+
+#if MPT_USTRING_MODE_WIDE
+#if MPT_USTRING_MODE_UTF8
+#error "MPT_USTRING_MODE_WIDE and MPT_USTRING_MODE_UTF8 are mutually exclusive."
+#endif
+
+using ustring = std::wstring;
+using uchar = wchar_t;
+#define MPT_UCHAR(x) L##x
+#define MPT_ULITERAL(x) L##x
+#define MPT_USTRING(x) std::wstring(L##x)
+
+#endif // MPT_USTRING_MODE_WIDE
+
+#if MPT_USTRING_MODE_UTF8
+#if MPT_USTRING_MODE_WIDE
+#error "MPT_USTRING_MODE_WIDE and MPT_USTRING_MODE_UTF8 are mutually exclusive."
+#endif
+
+using ustring = mpt::u8string;
+using uchar = mpt::u8char;
+#define MPT_UCHAR(x) MPT_U8CHAR(x)
+#define MPT_ULITERAL(x) MPT_U8LITERAL(x)
+#define MPT_USTRING(x) MPT_U8STRING(x)
+
+#endif // MPT_USTRING_MODE_UTF8
+
+
+
+template <typename T>
+struct make_string_type { };
+
+template <typename T, typename Ttraits>
+struct make_string_type<std::basic_string<T, Ttraits>> {
+ using type = std::basic_string<T, Ttraits>;
+};
+
+template <typename T>
+struct make_string_type<const T *> {
+ using type = std::basic_string<T>;
+};
+
+template <typename T>
+struct make_string_type<T *> {
+ using type = std::basic_string<T>;
+};
+
+template <typename T, std::size_t N>
+struct make_string_type<T[N]> {
+ using type = typename make_string_type<T *>::type;
+};
+
+#if MPT_DETECTED_MFC
+
+template <>
+struct make_string_type<CStringW> {
+ using type = CStringW;
+};
+
+template <>
+struct make_string_type<CStringA> {
+ using type = CStringA;
+};
+
+#endif // MPT_DETECTED_MFC
+
+
+
+template <typename T>
+struct is_string_type : public std::false_type { };
+template <>
+struct is_string_type<std::string> : public std::true_type { };
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+template <>
+struct is_string_type<std::wstring> : public std::true_type { };
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+#if MPT_CXX_AT_LEAST(20)
+template <>
+struct is_string_type<std::u8string> : public std::true_type { };
+#endif // C++20
+template <>
+struct is_string_type<std::u16string> : public std::true_type { };
+template <>
+struct is_string_type<std::u32string> : public std::true_type { };
+#if MPT_DETECTED_MFC
+template <>
+struct is_string_type<CStringW> : public std::true_type { };
+template <>
+struct is_string_type<CStringA> : public std::true_type { };
+#endif // MPT_DETECTED_MFC
+template <typename T, typename Ttraits>
+struct is_string_type<std::basic_string<T, Ttraits>> : public std::true_type { };
+
+
+
+template <typename T>
+inline typename mpt::make_string_type<T>::type as_string(const T & str) {
+ if constexpr (std::is_pointer<typename std::remove_cv<T>::type>::value) {
+ return str ? typename mpt::make_string_type<T>::type{str} : typename mpt::make_string_type<T>::type{};
+ } else {
+ return str;
+ }
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_STRING_TYPES_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/string/utility.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/string/utility.hpp
new file mode 100644
index 00000000..e2238a09
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/string/utility.hpp
@@ -0,0 +1,359 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_STRING_UTILITY_HPP
+#define MPT_STRING_UTILITY_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/detect/mfc.hpp"
+
+#include <string>
+#include <vector>
+
+#include <cstddef>
+
+#if MPT_DETECTED_MFC
+// cppcheck-suppress missingInclude
+#include <afx.h>
+#endif // MPT_DETECTED_MFC
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+// string_traits abstract the API of underlying string classes, in particular they allow adopting to CString without having to specialize for CString explicitly
+
+template <typename Tstring>
+struct string_traits {
+
+ using string_type = Tstring;
+ using size_type = typename string_type::size_type;
+ using char_type = typename string_type::value_type;
+
+ static inline std::size_t length(const string_type & str) {
+ return str.length();
+ }
+
+ static inline void reserve(string_type & str, std::size_t size) {
+ str.reserve(size);
+ }
+
+ static inline string_type & append(string_type & str, const string_type & a) {
+ return str.append(a);
+ }
+ static inline string_type & append(string_type & str, string_type && a) {
+ return str.append(std::move(a));
+ }
+ static inline string_type & append(string_type & str, std::size_t count, char_type c) {
+ return str.append(count, c);
+ }
+
+ static inline string_type pad(string_type str, std::size_t left, std::size_t right) {
+ str.insert(str.begin(), left, char_type(' '));
+ str.insert(str.end(), right, char_type(' '));
+ return str;
+ }
+};
+
+#if MPT_DETECTED_MFC
+
+template <>
+struct string_traits<CStringA> {
+
+ using string_type = CStringA;
+ using size_type = int;
+ using char_type = typename CStringA::XCHAR;
+
+ static inline size_type length(const string_type & str) {
+ return str.GetLength();
+ }
+
+ static inline void reserve(string_type & str, size_type size) {
+ str.Preallocate(size);
+ }
+
+ static inline string_type & append(string_type & str, const string_type & a) {
+ str += a;
+ return str;
+ }
+ static inline string_type & append(string_type & str, size_type count, char_type c) {
+ while (count--) {
+ str.AppendChar(c);
+ }
+ return str;
+ }
+
+ static inline string_type pad(const string_type & str, size_type left, size_type right) {
+ string_type tmp;
+ while (left--) {
+ tmp.AppendChar(char_type(' '));
+ }
+ tmp += str;
+ while (right--) {
+ tmp.AppendChar(char_type(' '));
+ }
+ return tmp;
+ }
+};
+
+template <>
+struct string_traits<CStringW> {
+
+ using string_type = CStringW;
+ using size_type = int;
+ using char_type = typename CStringW::XCHAR;
+
+ static inline size_type length(const string_type & str) {
+ return str.GetLength();
+ }
+
+ static inline void reserve(string_type & str, size_type size) {
+ str.Preallocate(size);
+ }
+
+ static inline string_type & append(string_type & str, const string_type & a) {
+ str += a;
+ return str;
+ }
+ static inline string_type & append(string_type & str, size_type count, char_type c) {
+ while (count--) {
+ str.AppendChar(c);
+ }
+ return str;
+ }
+
+ static inline string_type pad(const string_type & str, size_type left, size_type right) {
+ string_type tmp;
+ while (left--) {
+ tmp.AppendChar(char_type(' '));
+ }
+ tmp += str;
+ while (right--) {
+ tmp.AppendChar(char_type(' '));
+ }
+ return tmp;
+ }
+};
+
+#endif // MPT_DETECTED_MFC
+
+
+template <typename Tchar>
+struct char_constants {
+ static inline constexpr Tchar space = ' ';
+ static inline constexpr Tchar a = 'a';
+ static inline constexpr Tchar z = 'z';
+ static inline constexpr Tchar A = 'A';
+ static inline constexpr Tchar Z = 'Z';
+ static inline constexpr Tchar lf = '\n';
+ static inline constexpr Tchar cr = '\r';
+ static inline constexpr Tchar tab = '\t';
+ static inline constexpr Tchar comma = ',';
+};
+
+template <>
+struct char_constants<char> {
+ static inline constexpr char space = ' ';
+ static inline constexpr char a = 'a';
+ static inline constexpr char z = 'z';
+ static inline constexpr char A = 'A';
+ static inline constexpr char Z = 'Z';
+ static inline constexpr char lf = '\n';
+ static inline constexpr char cr = '\r';
+ static inline constexpr char tab = '\t';
+ static inline constexpr char comma = ',';
+};
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+template <>
+struct char_constants<wchar_t> {
+ static inline constexpr wchar_t space = L' ';
+ static inline constexpr wchar_t a = L'a';
+ static inline constexpr wchar_t z = L'z';
+ static inline constexpr wchar_t A = L'A';
+ static inline constexpr wchar_t Z = L'Z';
+ static inline constexpr wchar_t lf = L'\n';
+ static inline constexpr wchar_t cr = L'\r';
+ static inline constexpr wchar_t tab = L'\t';
+ static inline constexpr wchar_t comma = L',';
+};
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
+#if MPT_CXX_AT_LEAST(20)
+template <>
+struct char_constants<char8_t> {
+ static inline constexpr char8_t space = u8' ';
+ static inline constexpr char8_t a = u8'a';
+ static inline constexpr char8_t z = u8'z';
+ static inline constexpr char8_t A = u8'A';
+ static inline constexpr char8_t Z = u8'Z';
+ static inline constexpr char8_t lf = u8'\n';
+ static inline constexpr char8_t cr = u8'\r';
+ static inline constexpr char8_t tab = u8'\t';
+ static inline constexpr char8_t comma = u8',';
+};
+#endif
+
+template <>
+struct char_constants<char16_t> {
+ static inline constexpr char16_t space = u' ';
+ static inline constexpr char16_t a = u'a';
+ static inline constexpr char16_t z = u'z';
+ static inline constexpr char16_t A = u'A';
+ static inline constexpr char16_t Z = u'Z';
+ static inline constexpr char16_t lf = u'\n';
+ static inline constexpr char16_t cr = u'\r';
+ static inline constexpr char16_t tab = u'\t';
+ static inline constexpr char16_t comma = u',';
+};
+
+template <>
+struct char_constants<char32_t> {
+ static inline constexpr char32_t space = U' ';
+ static inline constexpr char32_t a = U'a';
+ static inline constexpr char32_t z = U'z';
+ static inline constexpr char32_t A = U'A';
+ static inline constexpr char32_t Z = U'Z';
+ static inline constexpr char32_t lf = U'\n';
+ static inline constexpr char32_t cr = U'\r';
+ static inline constexpr char32_t tab = U'\t';
+ static inline constexpr char32_t comma = U',';
+};
+
+
+template <typename Tchar>
+constexpr bool is_any_line_ending(Tchar c) noexcept {
+ return (c == char_constants<Tchar>::cr) || (c == char_constants<Tchar>::lf);
+}
+
+
+template <typename Tstring>
+inline Tstring default_whitespace() {
+ Tstring result;
+ result.reserve(4);
+ result.push_back(char_constants<typename Tstring::value_type>::space);
+ result.push_back(char_constants<typename Tstring::value_type>::lf);
+ result.push_back(char_constants<typename Tstring::value_type>::cr);
+ result.push_back(char_constants<typename Tstring::value_type>::tab);
+ return result;
+}
+
+
+// Remove whitespace at start of string
+template <typename Tstring>
+inline Tstring trim_left(Tstring str, const Tstring & whitespace = default_whitespace<Tstring>()) {
+ typename Tstring::size_type pos = str.find_first_not_of(whitespace);
+ if (pos != Tstring::npos) {
+ str.erase(str.begin(), str.begin() + pos);
+ } else if (pos == Tstring::npos && str.length() > 0 && str.find_last_of(whitespace) == str.length() - 1) {
+ return Tstring();
+ }
+ return str;
+}
+
+// Remove whitespace at end of string
+template <typename Tstring>
+inline Tstring trim_right(Tstring str, const Tstring & whitespace = default_whitespace<Tstring>()) {
+ typename Tstring::size_type pos = str.find_last_not_of(whitespace);
+ if (pos != Tstring::npos) {
+ str.erase(str.begin() + pos + 1, str.end());
+ } else if (pos == Tstring::npos && str.length() > 0 && str.find_first_of(whitespace) == 0) {
+ return Tstring();
+ }
+ return str;
+}
+
+// Remove whitespace at start and end of string
+template <typename Tstring>
+inline Tstring trim(Tstring str, const Tstring & whitespace = default_whitespace<Tstring>()) {
+ return trim_right(trim_left(str, whitespace), whitespace);
+}
+
+
+template <typename Tstring, typename Tmatch>
+inline bool starts_with(const Tstring & str, const Tmatch & match) {
+ return (str.find(typename mpt::make_string_type<Tmatch>::type{match}) == 0);
+}
+
+template <typename Tstring, typename Tmatch>
+inline bool ends_with(const Tstring & str, const Tmatch & match) {
+ return (str.rfind(typename mpt::make_string_type<Tmatch>::type{match}) == (str.length() - typename mpt::make_string_type<Tmatch>::type{match}.length()));
+}
+
+
+template <typename Tstring, typename Treplace>
+inline Tstring replace(Tstring str, const Treplace & old_str, const Treplace & new_str) {
+ std::size_t pos = 0;
+ while ((pos = str.find(typename mpt::make_string_type<Treplace>::type{old_str}, pos)) != Tstring::npos) {
+ str.replace(pos, typename mpt::make_string_type<Treplace>::type{old_str}.length(), typename mpt::make_string_type<Treplace>::type{new_str});
+ pos += typename mpt::make_string_type<Treplace>::type{new_str}.length();
+ }
+ return str;
+}
+
+
+template <typename Tstring>
+inline Tstring truncate(Tstring str, std::size_t max_len) {
+ if (str.length() > max_len) {
+ str.resize(max_len);
+ }
+ return str;
+}
+
+
+template <typename Tchar>
+inline constexpr Tchar to_lower_ascii(Tchar c) noexcept {
+ if (char_constants<Tchar>::A <= c && c <= char_constants<Tchar>::Z) {
+ c += char_constants<Tchar>::a - char_constants<Tchar>::A;
+ }
+ return c;
+}
+
+template <typename Tchar>
+inline constexpr Tchar to_upper_ascii(Tchar c) noexcept {
+ if (char_constants<Tchar>::a <= c && c <= char_constants<Tchar>::z) {
+ c -= char_constants<Tchar>::a - char_constants<Tchar>::A;
+ }
+ return c;
+}
+
+
+template <typename Tstring>
+inline std::vector<Tstring> split(const Tstring & str, const Tstring & sep = Tstring(1, char_constants<typename Tstring::value_type>::comma)) {
+ std::vector<Tstring> vals;
+ std::size_t pos = 0;
+ while (str.find(sep, pos) != std::string::npos) {
+ vals.push_back(str.substr(pos, str.find(sep, pos) - pos));
+ pos = str.find(sep, pos) + sep.length();
+ }
+ if (!vals.empty() || (str.substr(pos).length() > 0)) {
+ vals.push_back(str.substr(pos));
+ }
+ return vals;
+}
+
+
+template <typename Tstring>
+inline Tstring join(const std::vector<Tstring> & vals, const Tstring & sep = Tstring(1, char_constants<typename Tstring::value_type>::comma)) {
+ Tstring str;
+ for (std::size_t i = 0; i < vals.size(); ++i) {
+ if (i > 0) {
+ str += sep;
+ }
+ str += vals[i];
+ }
+ return str;
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_STRING_UTILITY_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/string_transcode/macros.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/string_transcode/macros.hpp
new file mode 100644
index 00000000..60630c2f
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/string_transcode/macros.hpp
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_STRING_TRANSCODE_MACROS_HPP
+#define MPT_STRING_TRANSCODE_MACROS_HPP
+
+
+
+#include "mpt/string/types.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+
+#include <string>
+
+
+
+// The MPT_UTF8_STRING allows specifying UTF8 char arrays.
+// The resulting type is mpt::ustring and the construction might require runtime translation,
+// i.e. it is NOT generally available at compile time.
+// Use explicit UTF8 encoding,
+// i.e. U+00FC (LATIN SMALL LETTER U WITH DIAERESIS) would be written as "\xC3\xBC".
+#define MPT_UTF8_STRING(x) mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, std::string{x})
+
+
+
+#endif // MPT_STRING_TRANSCODE_MACROS_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/string_transcode/tests/tests_string_transcode.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/string_transcode/tests/tests_string_transcode.hpp
new file mode 100644
index 00000000..311f833b
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/string_transcode/tests/tests_string_transcode.hpp
@@ -0,0 +1,246 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_STRING_TRANSCODE_TESTS_STRING_TRANSCODE_HPP
+#define MPT_STRING_TRANSCODE_TESTS_STRING_TRANSCODE_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/string_transcode/macros.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace string_transcode {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/string_transcode")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ // MPT_UTF8_STRING version
+
+ // Charset conversions (basic sanity checks)
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::utf8, MPT_USTRING("a")), "a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::iso8859_1, MPT_USTRING("a")), "a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::ascii, MPT_USTRING("a")), "a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, "a"), MPT_USTRING("a"));
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<mpt::ustring>(mpt::common_encoding::iso8859_1, "a"), MPT_USTRING("a"));
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<mpt::ustring>(mpt::common_encoding::ascii, "a"), MPT_USTRING("a"));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::logical_encoding::locale, MPT_USTRING("a")), "a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<mpt::ustring>(mpt::logical_encoding::locale, "a"), MPT_USTRING("a"));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::utf8, MPT_UTF8_STRING("a")), "a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::iso8859_1, MPT_UTF8_STRING("a")), "a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::ascii, MPT_UTF8_STRING("a")), "a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, "a"), MPT_UTF8_STRING("a"));
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<mpt::ustring>(mpt::common_encoding::iso8859_1, "a"), MPT_UTF8_STRING("a"));
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<mpt::ustring>(mpt::common_encoding::ascii, "a"), MPT_UTF8_STRING("a"));
+
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::logical_encoding::locale, MPT_UTF8_STRING("a")), "a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<mpt::ustring>(mpt::logical_encoding::locale, "a"), MPT_UTF8_STRING("a"));
+
+#if MPT_OS_EMSCRIPTEN
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::logical_encoding::locale, MPT_UTF8_STRING("\xe2\x8c\x82")), "\xe2\x8c\x82");
+#endif // MPT_OS_EMSCRIPTEN
+
+ // Check that some character replacement is done (and not just empty strings or truncated strings are returned)
+ // We test german umlaut-a (U+00E4) (\xC3\xA4) and CJK U+5BB6 (\xE5\xAE\xB6)
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::ascii, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::iso8859_1, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::cp437, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::utf8, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::ascii, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::iso8859_1, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::cp437, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::utf8, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc"));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::logical_encoding::locale, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::logical_encoding::locale, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc"));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::ascii, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::iso8859_1, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::cp437, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::utf8, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::ascii, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::iso8859_1, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::cp437, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::utf8, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "abc"));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::logical_encoding::locale, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::logical_encoding::locale, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "abc"));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::ascii, "abc\xC3\xA4xyz"), MPT_USTRING("xyz")));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::iso8859_1, "abc\xC3\xA4xyz"), MPT_USTRING("xyz")));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::cp437, "abc\xC3\xA4xyz"), MPT_USTRING("xyz")));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, "abc\xC3\xA4xyz"), MPT_USTRING("xyz")));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::ascii, "abc\xC3\xA4xyz"), MPT_USTRING("abc")));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::iso8859_1, "abc\xC3\xA4xyz"), MPT_USTRING("abc")));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::cp437, "abc\xC3\xA4xyz"), MPT_USTRING("abc")));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, "abc\xC3\xA4xyz"), MPT_USTRING("abc")));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<mpt::ustring>(mpt::logical_encoding::locale, "abc\xC3\xA4xyz"), MPT_USTRING("xyz")));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<mpt::ustring>(mpt::logical_encoding::locale, "abc\xC3\xA4xyz"), MPT_USTRING("abc")));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::ascii, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("xyz")));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::iso8859_1, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("xyz")));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::cp437, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("xyz")));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("xyz")));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::ascii, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("abc")));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::iso8859_1, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("abc")));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::cp437, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("abc")));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("abc")));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<mpt::ustring>(mpt::logical_encoding::locale, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("xyz")));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<mpt::ustring>(mpt::logical_encoding::locale, "abc\xE5\xAE\xB6xyz"), MPT_USTRING("abc")));
+
+ // Check that characters are correctly converted
+ // We test german umlaut-a (U+00E4) and CJK U+5BB6
+
+ // cp437
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::cp437, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc\x84xyz");
+ MPT_TEST_EXPECT_EQUAL(MPT_UTF8_STRING("abc\xC3\xA4xyz"), mpt::transcode<mpt::ustring>(mpt::common_encoding::cp437, "abc\x84xyz"));
+
+ // iso8859
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::iso8859_1, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc\xE4xyz");
+ MPT_TEST_EXPECT_EQUAL(MPT_UTF8_STRING("abc\xC3\xA4xyz"), mpt::transcode<mpt::ustring>(mpt::common_encoding::iso8859_1, "abc\xE4xyz"));
+
+ // utf8
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::utf8, MPT_UTF8_STRING("abc\xC3\xA4xyz")), "abc\xC3\xA4xyz");
+ MPT_TEST_EXPECT_EQUAL(MPT_UTF8_STRING("abc\xC3\xA4xyz"), mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, "abc\xC3\xA4xyz"));
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::utf8, MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz")), "abc\xE5\xAE\xB6xyz");
+ MPT_TEST_EXPECT_EQUAL(MPT_UTF8_STRING("abc\xE5\xAE\xB6xyz"), mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, "abc\xE5\xAE\xB6xyz"));
+
+ // utf16
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::utf8, std::u16string(1, char16_t{0xe4})), "\xC3\xA4");
+ MPT_TEST_EXPECT_EQUAL(std::u16string(1, char16_t{0xe4}), mpt::transcode<std::u16string>(mpt::common_encoding::utf8, "\xC3\xA4"));
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::utf8, std::u16string(1, char16_t{0x5BB6})), "\xE5\xAE\xB6");
+ MPT_TEST_EXPECT_EQUAL(std::u16string(1, char16_t{0x5BB6}), mpt::transcode<std::u16string>(mpt::common_encoding::utf8, "\xE5\xAE\xB6"));
+
+ // utf32
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::utf8, std::u32string(1, char32_t{0xe4})), "\xC3\xA4");
+ MPT_TEST_EXPECT_EQUAL(std::u32string(1, char32_t{0xe4}), mpt::transcode<std::u32string>(mpt::common_encoding::utf8, "\xC3\xA4"));
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::utf8, std::u32string(1, char32_t{0x5BB6})), "\xE5\xAE\xB6");
+ MPT_TEST_EXPECT_EQUAL(std::u32string(1, char32_t{0x5BB6}), mpt::transcode<std::u32string>(mpt::common_encoding::utf8, "\xE5\xAE\xB6"));
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+
+ // wide L"" version
+
+ // Charset conversions (basic sanity checks)
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::utf8, L"a"), "a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::iso8859_1, L"a"), "a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::ascii, L"a"), "a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::wstring>(mpt::common_encoding::utf8, "a"), L"a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::wstring>(mpt::common_encoding::iso8859_1, "a"), L"a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::wstring>(mpt::common_encoding::ascii, "a"), L"a");
+
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::logical_encoding::locale, L"a"), "a");
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::wstring>(mpt::logical_encoding::locale, "a"), L"a");
+
+ // Check that some character replacement is done (and not just empty strings or truncated strings are returned)
+ // We test german umlaut-a (U+00E4) and CJK U+5BB6
+
+#if MPT_COMPILER_MSVC
+#pragma warning(push)
+#pragma warning(disable : 4428) // universal-character-name encountered in source
+#endif
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::ascii, L"abc\u00E4xyz"), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::iso8859_1, L"abc\u00E4xyz"), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::cp437, L"abc\u00E4xyz"), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::utf8, L"abc\u00E4xyz"), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::ascii, L"abc\u00E4xyz"), "abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::iso8859_1, L"abc\u00E4xyz"), "abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::cp437, L"abc\u00E4xyz"), "abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::utf8, L"abc\u00E4xyz"), "abc"));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::logical_encoding::locale, L"abc\u00E4xyz"), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::logical_encoding::locale, L"abc\u00E4xyz"), "abc"));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::ascii, L"abc\u5BB6xyz"), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::iso8859_1, L"abc\u5BB6xyz"), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::cp437, L"abc\u5BB6xyz"), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::common_encoding::utf8, L"abc\u5BB6xyz"), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::ascii, L"abc\u5BB6xyz"), "abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::iso8859_1, L"abc\u5BB6xyz"), "abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::cp437, L"abc\u5BB6xyz"), "abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::common_encoding::utf8, L"abc\u5BB6xyz"), "abc"));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::string>(mpt::logical_encoding::locale, L"abc\u5BB6xyz"), "xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::string>(mpt::logical_encoding::locale, L"abc\u5BB6xyz"), "abc"));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::wstring>(mpt::common_encoding::ascii, "abc\xC3\xA4xyz"), L"xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::wstring>(mpt::common_encoding::iso8859_1, "abc\xC3\xA4xyz"), L"xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::wstring>(mpt::common_encoding::cp437, "abc\xC3\xA4xyz"), L"xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::wstring>(mpt::common_encoding::utf8, "abc\xC3\xA4xyz"), L"xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::wstring>(mpt::common_encoding::ascii, "abc\xC3\xA4xyz"), L"abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::wstring>(mpt::common_encoding::iso8859_1, "abc\xC3\xA4xyz"), L"abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::wstring>(mpt::common_encoding::cp437, "abc\xC3\xA4xyz"), L"abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::wstring>(mpt::common_encoding::utf8, "abc\xC3\xA4xyz"), L"abc"));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::wstring>(mpt::logical_encoding::locale, "abc\xC3\xA4xyz"), L"xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::wstring>(mpt::logical_encoding::locale, "abc\xC3\xA4xyz"), L"abc"));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::wstring>(mpt::common_encoding::ascii, "abc\xE5\xAE\xB6xyz"), L"xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::wstring>(mpt::common_encoding::iso8859_1, "abc\xE5\xAE\xB6xyz"), L"xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::wstring>(mpt::common_encoding::cp437, "abc\xE5\xAE\xB6xyz"), L"xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::wstring>(mpt::common_encoding::utf8, "abc\xE5\xAE\xB6xyz"), L"xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::wstring>(mpt::common_encoding::ascii, "abc\xE5\xAE\xB6xyz"), L"abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::wstring>(mpt::common_encoding::iso8859_1, "abc\xE5\xAE\xB6xyz"), L"abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::wstring>(mpt::common_encoding::cp437, "abc\xE5\xAE\xB6xyz"), L"abc"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::wstring>(mpt::common_encoding::utf8, "abc\xE5\xAE\xB6xyz"), L"abc"));
+
+ MPT_TEST_EXPECT_EXPR(mpt::ends_with(mpt::transcode<std::wstring>(mpt::logical_encoding::locale, "abc\xE5\xAE\xB6xyz"), L"xyz"));
+ MPT_TEST_EXPECT_EXPR(mpt::starts_with(mpt::transcode<std::wstring>(mpt::logical_encoding::locale, "abc\xE5\xAE\xB6xyz"), L"abc"));
+
+ // Check that characters are correctly converted
+ // We test german umlaut-a (U+00E4) and CJK U+5BB6
+
+ // cp437
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::cp437, L"abc\u00E4xyz"), "abc\x84xyz");
+ MPT_TEST_EXPECT_EQUAL(L"abc\u00E4xyz", mpt::transcode<std::wstring>(mpt::common_encoding::cp437, "abc\x84xyz"));
+
+ // iso8859
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::iso8859_1, L"abc\u00E4xyz"), "abc\xE4xyz");
+ MPT_TEST_EXPECT_EQUAL(L"abc\u00E4xyz", mpt::transcode<std::wstring>(mpt::common_encoding::iso8859_1, "abc\xE4xyz"));
+
+ // utf8
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::utf8, L"abc\u00E4xyz"), "abc\xC3\xA4xyz");
+ MPT_TEST_EXPECT_EQUAL(L"abc\u00E4xyz", mpt::transcode<std::wstring>(mpt::common_encoding::utf8, "abc\xC3\xA4xyz"));
+ MPT_TEST_EXPECT_EQUAL(mpt::transcode<std::string>(mpt::common_encoding::utf8, L"abc\u5BB6xyz"), "abc\xE5\xAE\xB6xyz");
+ MPT_TEST_EXPECT_EQUAL(L"abc\u5BB6xyz", mpt::transcode<std::wstring>(mpt::common_encoding::utf8, "abc\xE5\xAE\xB6xyz"));
+
+#if MPT_COMPILER_MSVC
+#pragma warning(pop)
+#endif
+
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+}
+
+} // namespace string_transcode
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_STRING_TRANSCODE_TESTS_STRING_TRANSCODE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/string_transcode/transcode.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/string_transcode/transcode.hpp
new file mode 100644
index 00000000..cdb53b61
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/string_transcode/transcode.hpp
@@ -0,0 +1,1282 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_STRING_TRANSCODE_TRANSCODE_HPP
+#define MPT_STRING_TRANSCODE_TRANSCODE_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/saturate_cast.hpp"
+#include "mpt/detect/mfc.hpp"
+#include "mpt/string/types.hpp"
+
+#include <array>
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+#include <locale>
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+#include <stdexcept>
+#include <string>
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+#include <type_traits>
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+#include <vector>
+
+#if MPT_OS_DJGPP
+#include <cstring>
+#endif // MPT_OS_DJGPP
+
+#if MPT_OS_WINDOWS
+#include <windows.h>
+#endif // MPT_OS_WINDOWS
+
+#if MPT_OS_DJGPP
+#include <dpmi.h>
+#endif // MPT_OS_DJGPP
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+
+// default 1:1 mapping
+inline constexpr char32_t CharsetTableISO8859_1[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff};
+
+inline constexpr char32_t CharsetTableISO8859_15[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x20ac, 0x00a5, 0x0160, 0x00a7, 0x0161, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x017d, 0x00b5, 0x00b6, 0x00b7, 0x017e, 0x00b9, 0x00ba, 0x00bb, 0x0152, 0x0153, 0x0178, 0x00bf,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff};
+
+inline constexpr char32_t CharsetTableWindows1252[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
+ 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
+ 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178,
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff};
+
+inline constexpr char32_t CharsetTableCP850[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
+ 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
+ 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00F8, 0x00a3, 0x00D8, 0x00D7, 0x0192,
+ 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x00AE, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255d, 0x00A2, 0x00A5, 0x2510,
+ 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x00E3, 0x00C3, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00A4,
+ 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250c, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+ 0x00D3, 0x00df, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00b5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
+ 0x00AD, 0x00b1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00f7, 0x00B8, 0x00b0, 0x00A8, 0x00b7, 0x00B9, 0x00B3, 0x00b2, 0x25a0, 0x00a0};
+
+inline constexpr char32_t CharsetTableCP437[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
+ 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
+ 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
+ 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
+ 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
+ 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
+ 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0};
+
+// <https://de.wikipedia.org/wiki/Commodore_Amiga_(Zeichensatz)>
+inline constexpr char32_t CharsetTableAmiga[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2592,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x2013, 0x00ae, 0x00af,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff};
+
+// Based on RISCOSI.TXT from <https://www.unicode.org/L2/L2019/19025-terminals-prop.pdf>,
+// with gaps filled in from standard set at <https://en.wikipedia.org/wiki/RISC_OS_character_set>.
+inline constexpr char32_t CharsetTableRISC_OS[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
+ 0x20AC, 0x0174, 0x0175, 0x25f0, 0x1fbc0, 0x0176, 0x0177, 0xfffd, 0x21e6, 0x21e8, 0x21e9, 0x21e7, 0x2026, 0x2122, 0x2030, 0x2022,
+ 0x2018, 0x2019, 0x2039, 0x203A, 0x201C, 0x201D, 0x201E, 0x2013, 0x2014, 0x2212, 0x0152, 0x0153, 0x2020, 0x2021, 0xFB01, 0xFB02,
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff};
+
+template <typename Tsrcstring>
+inline mpt::widestring decode_8bit(const Tsrcstring & str, const char32_t (&table)[256], mpt::widechar replacement = MPT_WIDECHAR('\uFFFD')) {
+ mpt::widestring res;
+ res.reserve(str.length());
+ for (std::size_t i = 0; i < str.length(); ++i) {
+ std::size_t c = static_cast<std::size_t>(mpt::char_value(str[i]));
+ if (c < std::size(table)) {
+ res.push_back(static_cast<mpt::widechar>(table[c]));
+ } else {
+ res.push_back(replacement);
+ }
+ }
+ return res;
+}
+
+template <typename Tdststring>
+inline Tdststring encode_8bit(const mpt::widestring & str, const char32_t (&table)[256], char replacement = '?') {
+ Tdststring res;
+ res.reserve(str.length());
+ for (std::size_t i = 0; i < str.length(); ++i) {
+ char32_t c = static_cast<char32_t>(str[i]);
+ bool found = false;
+ // Try non-control characters first.
+ // In cases where there are actual characters mirrored in this range (like in AMS/AMS2 character sets),
+ // characters in the common range are preferred this way.
+ for (std::size_t x = 0x20; x < std::size(table); ++x) {
+ if (c == table[x]) {
+ res.push_back(static_cast<typename Tdststring::value_type>(static_cast<uint8>(x)));
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ // try control characters
+ for (std::size_t x = 0x00; x < std::size(table) && x < 0x20; ++x) {
+ if (c == table[x]) {
+ res.push_back(static_cast<typename Tdststring::value_type>(static_cast<uint8>(x)));
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ res.push_back(replacement);
+ }
+ }
+ return res;
+}
+
+template <typename Tsrcstring>
+inline mpt::widestring decode_8bit_no_c1(const Tsrcstring & str, const char32_t (&table)[256], mpt::widechar replacement = MPT_WIDECHAR('\uFFFD')) {
+ mpt::widestring res;
+ res.reserve(str.length());
+ for (std::size_t i = 0; i < str.length(); ++i) {
+ std::size_t c = static_cast<std::size_t>(mpt::char_value(str[i]));
+ if ((0x80 <= c) && (c <= 0x9f)) {
+ res.push_back(replacement);
+ } else if (c < std::size(table)) {
+ res.push_back(static_cast<mpt::widechar>(table[c]));
+ } else {
+ res.push_back(replacement);
+ }
+ }
+ return res;
+}
+
+template <typename Tdststring>
+inline Tdststring encode_8bit_no_c1(const mpt::widestring & str, const char32_t (&table)[256], char replacement = '?') {
+ Tdststring res;
+ res.reserve(str.length());
+ for (std::size_t i = 0; i < str.length(); ++i) {
+ char32_t c = static_cast<char32_t>(str[i]);
+ bool found = false;
+ // Try non-control characters first.
+ // In cases where there are actual characters mirrored in this range (like in AMS/AMS2 character sets),
+ // characters in the common range are preferred this way.
+ for (std::size_t x = 0x20; x < std::size(table); ++x) {
+ if ((0x80 <= c) && (c <= 0x9f)) {
+ continue;
+ }
+ if (c == table[x]) {
+ res.push_back(static_cast<typename Tdststring::value_type>(static_cast<uint8>(x)));
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ // try control characters
+ for (std::size_t x = 0x00; x < std::size(table) && x < 0x20; ++x) {
+ if (c == table[x]) {
+ res.push_back(static_cast<typename Tdststring::value_type>(static_cast<uint8>(x)));
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ res.push_back(replacement);
+ }
+ }
+ return res;
+}
+
+template <typename Tsrcstring>
+inline mpt::widestring decode_ascii(const Tsrcstring & str, mpt::widechar replacement = MPT_WIDECHAR('\uFFFD')) {
+ mpt::widestring res;
+ res.reserve(str.length());
+ for (std::size_t i = 0; i < str.length(); ++i) {
+ uint8 c = mpt::char_value(str[i]);
+ if (c <= 0x7f) {
+ res.push_back(static_cast<mpt::widechar>(static_cast<uint32>(c)));
+ } else {
+ res.push_back(replacement);
+ }
+ }
+ return res;
+}
+
+template <typename Tdststring>
+inline Tdststring encode_ascii(const mpt::widestring & str, char replacement = '?') {
+ Tdststring res;
+ res.reserve(str.length());
+ for (std::size_t i = 0; i < str.length(); ++i) {
+ char32_t c = static_cast<char32_t>(str[i]);
+ if (c <= 0x7f) {
+ res.push_back(static_cast<typename Tdststring::value_type>(static_cast<uint8>(c)));
+ } else {
+ res.push_back(replacement);
+ }
+ }
+ return res;
+}
+
+template <typename Tsrcstring>
+inline mpt::widestring decode_iso8859_1(const Tsrcstring & str, mpt::widechar replacement = MPT_WIDECHAR('\uFFFD')) {
+ MPT_UNUSED(replacement);
+ mpt::widestring res;
+ res.reserve(str.length());
+ for (std::size_t i = 0; i < str.length(); ++i) {
+ uint8 c = mpt::char_value(str[i]);
+ res.push_back(static_cast<mpt::widechar>(static_cast<uint32>(c)));
+ }
+ return res;
+}
+
+template <typename Tdststring>
+inline Tdststring encode_iso8859_1(const mpt::widestring & str, char replacement = '?') {
+ Tdststring res;
+ res.reserve(str.length());
+ for (std::size_t i = 0; i < str.length(); ++i) {
+ char32_t c = static_cast<char32_t>(str[i]);
+ if (c <= 0xff) {
+ res.push_back(static_cast<typename Tdststring::value_type>(static_cast<uint8>(c)));
+ } else {
+ res.push_back(replacement);
+ }
+ }
+ return res;
+}
+
+
+
+template <typename Tsrcstring>
+inline mpt::widestring decode_utf8(const Tsrcstring & str, mpt::widechar replacement = MPT_WIDECHAR('\uFFFD')) {
+ const Tsrcstring & in = str;
+ mpt::widestring out;
+ // state:
+ std::size_t charsleft = 0;
+ char32_t ucs4 = 0;
+ for (uint8 c : in) {
+ if (charsleft == 0) {
+ if ((c & 0x80) == 0x00) {
+ out.push_back(static_cast<mpt::widechar>(c));
+ } else if ((c & 0xE0) == 0xC0) {
+ ucs4 = c & 0x1F;
+ charsleft = 1;
+ } else if ((c & 0xF0) == 0xE0) {
+ ucs4 = c & 0x0F;
+ charsleft = 2;
+ } else if ((c & 0xF8) == 0xF0) {
+ ucs4 = c & 0x07;
+ charsleft = 3;
+ } else {
+ out.push_back(replacement);
+ ucs4 = 0;
+ charsleft = 0;
+ }
+ } else {
+ if ((c & 0xC0) != 0x80) {
+ out.push_back(replacement);
+ ucs4 = 0;
+ charsleft = 0;
+ }
+ ucs4 <<= 6;
+ ucs4 |= c & 0x3F;
+ charsleft--;
+ if (charsleft == 0) {
+ if constexpr (sizeof(mpt::widechar) == 2) {
+ if (ucs4 > 0x1fffff) {
+ out.push_back(replacement);
+ ucs4 = 0;
+ charsleft = 0;
+ }
+ if (ucs4 <= 0xffff) {
+ out.push_back(static_cast<mpt::widechar>(ucs4));
+ } else {
+ uint32 surrogate = static_cast<uint32>(ucs4) - 0x10000;
+ uint16 hi_sur = static_cast<uint16>((0x36 << 10) | ((surrogate >> 10) & ((1 << 10) - 1)));
+ uint16 lo_sur = static_cast<uint16>((0x37 << 10) | ((surrogate >> 0) & ((1 << 10) - 1)));
+ out.push_back(hi_sur);
+ out.push_back(lo_sur);
+ }
+ } else {
+ out.push_back(static_cast<mpt::widechar>(ucs4));
+ }
+ ucs4 = 0;
+ }
+ }
+ }
+ if (charsleft != 0) {
+ out.push_back(replacement);
+ ucs4 = 0;
+ charsleft = 0;
+ }
+ return out;
+}
+
+template <typename Tdststring>
+inline Tdststring encode_utf8(const mpt::widestring & str, typename Tdststring::value_type replacement = static_cast<typename Tdststring::value_type>(mpt::char_value('?'))) {
+ const mpt::widestring & in = str;
+ Tdststring out;
+ for (std::size_t i = 0; i < in.length(); i++) {
+ mpt::widechar wc = in[i];
+ char32_t ucs4 = 0;
+ if constexpr (sizeof(mpt::widechar) == 2) {
+ uint16 c = static_cast<uint16>(wc);
+ if (i + 1 < in.length()) {
+ // check for surrogate pair
+ uint16 hi_sur = in[i + 0];
+ uint16 lo_sur = in[i + 1];
+ if (hi_sur >> 10 == 0x36 && lo_sur >> 10 == 0x37) {
+ // surrogate pair
+ ++i;
+ hi_sur &= (1 << 10) - 1;
+ lo_sur &= (1 << 10) - 1;
+ ucs4 = (static_cast<uint32>(hi_sur) << 10) | (static_cast<uint32>(lo_sur) << 0);
+ } else {
+ // no surrogate pair
+ ucs4 = static_cast<char32_t>(c);
+ }
+ } else {
+ // no surrogate possible
+ ucs4 = static_cast<char32_t>(c);
+ }
+ } else {
+ ucs4 = static_cast<char32_t>(wc);
+ }
+ if (ucs4 > 0x1fffff) {
+ out.push_back(replacement);
+ continue;
+ }
+ uint8 utf8[6];
+ std::size_t numchars = 0;
+ for (numchars = 0; numchars < 6; numchars++) {
+ utf8[numchars] = ucs4 & 0x3F;
+ ucs4 >>= 6;
+ if (ucs4 == 0) {
+ break;
+ }
+ }
+ numchars++;
+ if (numchars == 1) {
+ out.push_back(utf8[0]);
+ continue;
+ }
+ if (numchars == 2 && utf8[numchars - 1] == 0x01) {
+ // generate shortest form
+ out.push_back(utf8[0] | 0x40);
+ continue;
+ }
+ std::size_t charsleft = numchars;
+ while (charsleft > 0) {
+ if (charsleft == numchars) {
+ out.push_back(utf8[charsleft - 1] | (((1 << numchars) - 1) << (8 - numchars)));
+ } else {
+ // cppcheck false-positive
+ // cppcheck-suppress arrayIndexOutOfBounds
+ out.push_back(utf8[charsleft - 1] | 0x80);
+ }
+ charsleft--;
+ }
+ }
+ return out;
+}
+
+
+
+template <typename Tdststring, typename Tsrcstring>
+inline Tdststring utf32_from_utf16(const Tsrcstring & in, mpt::widechar replacement = MPT_WIDECHAR('\uFFFD')) {
+ static_assert(sizeof(typename Tsrcstring::value_type) == 2);
+ static_assert(sizeof(typename Tdststring::value_type) == 4);
+ MPT_UNUSED(replacement);
+ Tdststring out;
+ out.reserve(in.length());
+ for (std::size_t i = 0; i < in.length(); i++) {
+ char16_t wc = static_cast<char16_t>(static_cast<uint16>(in[i]));
+ char32_t ucs4 = 0;
+ uint16 c = static_cast<uint16>(wc);
+ if (i + 1 < in.length()) {
+ // check for surrogate pair
+ uint16 hi_sur = in[i + 0];
+ uint16 lo_sur = in[i + 1];
+ if (hi_sur >> 10 == 0x36 && lo_sur >> 10 == 0x37) {
+ // surrogate pair
+ ++i;
+ hi_sur &= (1 << 10) - 1;
+ lo_sur &= (1 << 10) - 1;
+ ucs4 = (static_cast<uint32>(hi_sur) << 10) | (static_cast<uint32>(lo_sur) << 0);
+ } else {
+ // no surrogate pair
+ ucs4 = static_cast<char32_t>(c);
+ }
+ } else {
+ // no surrogate possible
+ ucs4 = static_cast<char32_t>(c);
+ }
+ out.push_back(static_cast<typename Tdststring::value_type>(static_cast<uint32>(ucs4)));
+ }
+ return out;
+}
+
+template <typename Tdststring, typename Tsrcstring>
+inline Tdststring utf16_from_utf32(const Tsrcstring & in, mpt::widechar replacement = MPT_WIDECHAR('\uFFFD')) {
+ static_assert(sizeof(typename Tsrcstring::value_type) == 4);
+ static_assert(sizeof(typename Tdststring::value_type) == 2);
+ Tdststring out;
+ out.reserve(in.length());
+ for (std::size_t i = 0; i < in.length(); i++) {
+ char32_t ucs4 = static_cast<char32_t>(static_cast<uint32>(in[i]));
+ if (ucs4 > 0x1fffff) {
+ out.push_back(static_cast<typename Tdststring::value_type>(static_cast<uint16>(replacement)));
+ ucs4 = 0;
+ }
+ if (ucs4 <= 0xffff) {
+ out.push_back(static_cast<typename Tdststring::value_type>(static_cast<uint16>(ucs4)));
+ } else {
+ uint32 surrogate = static_cast<uint32>(ucs4) - 0x10000;
+ uint16 hi_sur = static_cast<uint16>((0x36 << 10) | ((surrogate >> 10) & ((1 << 10) - 1)));
+ uint16 lo_sur = static_cast<uint16>((0x37 << 10) | ((surrogate >> 0) & ((1 << 10) - 1)));
+ out.push_back(static_cast<typename Tdststring::value_type>(hi_sur));
+ out.push_back(static_cast<typename Tdststring::value_type>(lo_sur));
+ }
+ }
+ return out;
+}
+
+
+
+#if MPT_OS_WINDOWS
+
+inline bool has_codepage(UINT cp) {
+ return IsValidCodePage(cp) ? true : false;
+}
+
+inline bool windows_has_encoding(common_encoding encoding) {
+ bool result = false;
+ switch (encoding) {
+ case common_encoding::utf8:
+ result = has_codepage(CP_UTF8);
+ break;
+ case common_encoding::ascii:
+ result = has_codepage(20127);
+ break;
+ case common_encoding::iso8859_1:
+ result = has_codepage(28591);
+ break;
+ case common_encoding::iso8859_15:
+ result = has_codepage(28605);
+ break;
+ case common_encoding::cp850:
+ result = has_codepage(850);
+ break;
+ case common_encoding::cp437:
+ result = has_codepage(437);
+ break;
+ case common_encoding::windows1252:
+ result = has_codepage(1252);
+ break;
+ case common_encoding::amiga:
+ result = false;
+ break;
+ case common_encoding::riscos:
+ result = false;
+ break;
+ case common_encoding::iso8859_1_no_c1:
+ result = false;
+ break;
+ case common_encoding::iso8859_15_no_c1:
+ result = false;
+ break;
+ case common_encoding::amiga_no_c1:
+ result = false;
+ break;
+ }
+ return result;
+}
+
+inline bool windows_has_encoding(logical_encoding encoding) {
+ bool result = false;
+ switch (encoding) {
+ case logical_encoding::locale:
+ result = true;
+ break;
+ case logical_encoding::active_locale:
+ result = false;
+ break;
+ }
+ return result;
+}
+
+inline UINT codepage_from_encoding(logical_encoding encoding) {
+ UINT result = 0;
+ switch (encoding) {
+ case logical_encoding::locale:
+ result = CP_ACP;
+ break;
+ case logical_encoding::active_locale:
+ throw std::domain_error("unsupported encoding");
+ break;
+ }
+ return result;
+}
+
+inline UINT codepage_from_encoding(common_encoding encoding) {
+ UINT result = 0;
+ switch (encoding) {
+ case common_encoding::utf8:
+ result = CP_UTF8;
+ break;
+ case common_encoding::ascii:
+ result = 20127;
+ break;
+ case common_encoding::iso8859_1:
+ result = 28591;
+ break;
+ case common_encoding::iso8859_15:
+ result = 28605;
+ break;
+ case common_encoding::cp850:
+ result = 850;
+ break;
+ case common_encoding::cp437:
+ result = 437;
+ break;
+ case common_encoding::windows1252:
+ result = 1252;
+ break;
+ case common_encoding::amiga:
+ throw std::domain_error("unsupported encoding");
+ break;
+ case common_encoding::riscos:
+ throw std::domain_error("unsupported encoding");
+ break;
+ case common_encoding::iso8859_1_no_c1:
+ throw std::domain_error("unsupported encoding");
+ break;
+ case common_encoding::iso8859_15_no_c1:
+ throw std::domain_error("unsupported encoding");
+ break;
+ case common_encoding::amiga_no_c1:
+ throw std::domain_error("unsupported encoding");
+ break;
+ }
+ return result;
+}
+
+template <typename Tdststring>
+inline Tdststring encode_codepage(UINT codepage, const mpt::widestring & src) {
+ static_assert(sizeof(typename Tdststring::value_type) == sizeof(char));
+ static_assert(mpt::is_character<typename Tdststring::value_type>::value);
+ Tdststring encoded_string;
+ int required_size = WideCharToMultiByte(codepage, 0, src.data(), mpt::saturate_cast<int>(src.size()), nullptr, 0, nullptr, nullptr);
+ if (required_size > 0) {
+ encoded_string.resize(required_size);
+ WideCharToMultiByte(codepage, 0, src.data(), mpt::saturate_cast<int>(src.size()), reinterpret_cast<CHAR *>(encoded_string.data()), required_size, nullptr, nullptr);
+ }
+ return encoded_string;
+}
+
+template <typename Tsrcstring>
+inline mpt::widestring decode_codepage(UINT codepage, const Tsrcstring & src) {
+ static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char));
+ static_assert(mpt::is_character<typename Tsrcstring::value_type>::value);
+ mpt::widestring decoded_string;
+ int required_size = MultiByteToWideChar(codepage, 0, reinterpret_cast<const CHAR *>(src.data()), mpt::saturate_cast<int>(src.size()), nullptr, 0);
+ if (required_size > 0) {
+ decoded_string.resize(required_size);
+ MultiByteToWideChar(codepage, 0, reinterpret_cast<const CHAR *>(src.data()), mpt::saturate_cast<int>(src.size()), decoded_string.data(), required_size);
+ }
+ return decoded_string;
+}
+
+#endif // MPT_OS_WINDOWS
+
+
+
+#if MPT_OS_DJGPP
+
+inline common_encoding djgpp_get_locale_encoding() {
+ uint16 active_codepage = 437;
+ uint16 system_codepage = 437;
+ __dpmi_regs regs;
+ std::memset(&regs, 0, sizeof(__dpmi_regs));
+ regs.x.ax = 0x6601;
+ if (__dpmi_int(0x21, &regs) == 0) {
+ int cf = (regs.x.flags >> 0) & 1;
+ if (cf == 0) {
+ active_codepage = regs.x.bx;
+ system_codepage = regs.x.dx;
+ }
+ }
+ common_encoding result = common_encoding::cp437;
+ if (active_codepage == 0) {
+ result = common_encoding::cp437;
+ } else if (active_codepage == 437) {
+ result = common_encoding::cp437;
+ } else if (active_codepage == 850) {
+ result = common_encoding::cp850;
+ } else if (system_codepage == 437) {
+ result = common_encoding::cp437;
+ } else if (system_codepage == 850) {
+ result = common_encoding::cp850;
+ } else {
+ result = common_encoding::cp437;
+ }
+ return result;
+}
+
+#endif // MPT_OS_DJGPP
+
+
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+
+// Note:
+//
+// std::codecvt::out in LLVM libc++ does not advance in and out pointers when
+// running into a non-convertible character. This can happen when no locale is
+// set on FreeBSD or MacOSX. This behaviour violates the C++ standard.
+//
+// We apply the following (albeit costly, even on other platforms) work-around:
+// If the conversion errors out and does not advance the pointers at all, we
+// retry the conversion with a space character prepended to the string. If it
+// still does error out, we retry the whole conversion character by character.
+// This is costly even on other platforms in one single case: The first
+// character is an invalid Unicode code point or otherwise not convertible. Any
+// following non-convertible characters are not a problem.
+
+inline std::wstring decode_locale_impl(const std::string & str, const std::locale & locale, wchar_t replacement = L'\uFFFD', int retry = 0, bool * progress = nullptr) {
+ if (str.empty()) {
+ return std::wstring();
+ }
+ std::vector<wchar_t> out;
+ using codecvt_type = std::codecvt<wchar_t, char, std::mbstate_t>;
+ std::mbstate_t state = std::mbstate_t();
+ const codecvt_type & facet = std::use_facet<codecvt_type>(locale);
+ codecvt_type::result result = codecvt_type::partial;
+ const char * in_begin = str.data();
+ const char * in_end = in_begin + str.size();
+ out.resize((in_end - in_begin) * (mpt::saturate_cast<std::size_t>(facet.max_length()) + 1));
+ wchar_t * out_begin = out.data();
+ wchar_t * out_end = out.data() + out.size();
+ const char * in_next = nullptr;
+ wchar_t * out_next = nullptr;
+ do {
+ if (retry == 2) {
+ for (;;) {
+ in_next = nullptr;
+ out_next = nullptr;
+ result = facet.in(state, in_begin, in_begin + 1, in_next, out_begin, out_end, out_next);
+ if (result == codecvt_type::partial && in_next == in_begin + 1) {
+ in_begin = in_next;
+ out_begin = out_next;
+ continue;
+ } else {
+ break;
+ }
+ }
+ } else {
+ in_next = nullptr;
+ out_next = nullptr;
+ result = facet.in(state, in_begin, in_end, in_next, out_begin, out_end, out_next);
+ }
+ if (result == codecvt_type::partial || (result == codecvt_type::error && out_next == out_end)) {
+ out.resize(out.size() * 2);
+ in_begin = in_next;
+ out_begin = out.data() + (out_next - out_begin);
+ out_end = out.data() + out.size();
+ continue;
+ }
+ if (retry == 0) {
+ if (result == codecvt_type::error && in_next == in_begin && out_next == out_begin) {
+ bool made_progress = true;
+ decode_locale_impl(std::string(" ") + str, locale, replacement, 1, &made_progress);
+ if (!made_progress) {
+ return decode_locale_impl(str, locale, replacement, 2);
+ }
+ }
+ } else if (retry == 1) {
+ if (result == codecvt_type::error && in_next == in_begin && out_next == out_begin) {
+ *progress = false;
+ } else {
+ *progress = true;
+ }
+ return std::wstring();
+ }
+ if (result == codecvt_type::error) {
+ ++in_next;
+ *out_next = replacement;
+ ++out_next;
+ }
+ in_begin = in_next;
+ out_begin = out_next;
+ } while ((result == codecvt_type::error && in_next < in_end && out_next < out_end) || (retry == 2 && in_next < in_end));
+ return std::wstring(out.data(), out_next);
+}
+
+template <typename Tsrcstring>
+inline mpt::widestring decode_locale(const std::locale & locale, const Tsrcstring & src) {
+ if constexpr (std::is_same<Tsrcstring, std::string>::value) {
+ return decode_locale_impl(src, locale);
+ } else {
+ return decode_locale_impl(std::string(src.begin(), src.end()), locale);
+ }
+}
+
+inline std::string encode_locale_impl(const std::wstring & str, const std::locale & locale, char replacement = '?', int retry = 0, bool * progress = nullptr) {
+ if (str.empty()) {
+ return std::string();
+ }
+ std::vector<char> out;
+ using codecvt_type = std::codecvt<wchar_t, char, std::mbstate_t>;
+ std::mbstate_t state = std::mbstate_t();
+ const codecvt_type & facet = std::use_facet<codecvt_type>(locale);
+ codecvt_type::result result = codecvt_type::partial;
+ const wchar_t * in_begin = str.data();
+ const wchar_t * in_end = in_begin + str.size();
+ out.resize((in_end - in_begin) * (mpt::saturate_cast<std::size_t>(facet.max_length()) + 1));
+ char * out_begin = out.data();
+ char * out_end = out.data() + out.size();
+ const wchar_t * in_next = nullptr;
+ char * out_next = nullptr;
+ do {
+ if (retry == 2) {
+ for (;;) {
+ in_next = nullptr;
+ out_next = nullptr;
+ result = facet.out(state, in_begin, in_begin + 1, in_next, out_begin, out_end, out_next);
+ if (result == codecvt_type::partial && in_next == in_begin + 1) {
+ in_begin = in_next;
+ out_begin = out_next;
+ continue;
+ } else {
+ break;
+ }
+ }
+ } else {
+ in_next = nullptr;
+ out_next = nullptr;
+ result = facet.out(state, in_begin, in_end, in_next, out_begin, out_end, out_next);
+ }
+ if (result == codecvt_type::partial || (result == codecvt_type::error && out_next == out_end)) {
+ out.resize(out.size() * 2);
+ in_begin = in_next;
+ out_begin = out.data() + (out_next - out_begin);
+ out_end = out.data() + out.size();
+ continue;
+ }
+ if (retry == 0) {
+ if (result == codecvt_type::error && in_next == in_begin && out_next == out_begin) {
+ bool made_progress = true;
+ encode_locale_impl(std::wstring(L" ") + str, locale, replacement, 1, &made_progress);
+ if (!made_progress) {
+ return encode_locale_impl(str, locale, replacement, 2);
+ }
+ }
+ } else if (retry == 1) {
+ if (result == codecvt_type::error && in_next == in_begin && out_next == out_begin) {
+ *progress = false;
+ } else {
+ *progress = true;
+ }
+ return std::string();
+ }
+ if (result == codecvt_type::error) {
+ ++in_next;
+ *out_next = replacement;
+ ++out_next;
+ }
+ in_begin = in_next;
+ out_begin = out_next;
+ } while ((result == codecvt_type::error && in_next < in_end && out_next < out_end) || (retry == 2 && in_next < in_end));
+ return std::string(out.data(), out_next);
+}
+
+template <typename Tdststring>
+inline Tdststring encode_locale(const std::locale & locale, const mpt::widestring & src) {
+ if constexpr (std::is_same<Tdststring, std::string>::value) {
+ return encode_locale_impl(src, locale);
+ } else {
+ const std::string tmp = encode_locale_impl(src, locale);
+ return Tdststring(tmp.begin(), tmp.end());
+ }
+}
+
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
+
+
+#if MPT_OS_WINDOWS
+template <typename Tdststring>
+inline Tdststring encode(UINT codepage, const mpt::widestring & src) {
+ static_assert(sizeof(typename Tdststring::value_type) == sizeof(char));
+ static_assert(mpt::is_character<typename Tdststring::value_type>::value);
+ return encode_codepage<Tdststring>(codepage, src);
+}
+#endif // MPT_OS_WINDOWS
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+template <typename Tdststring>
+inline Tdststring encode(const std::locale & locale, const mpt::widestring & src) {
+ static_assert(sizeof(typename Tdststring::value_type) == sizeof(char));
+ static_assert(mpt::is_character<typename Tdststring::value_type>::value);
+ return encode_locale<Tdststring>(src, locale);
+}
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
+template <typename Tdststring>
+inline Tdststring encode(const char32_t (&table)[256], const mpt::widestring & src) {
+ static_assert(sizeof(typename Tdststring::value_type) == sizeof(char));
+ static_assert(mpt::is_character<typename Tdststring::value_type>::value);
+ return encode_8bit<Tdststring>(src, table);
+}
+
+template <typename Tdststring>
+inline Tdststring encode(common_encoding encoding, const mpt::widestring & src) {
+ static_assert(sizeof(typename Tdststring::value_type) == sizeof(char));
+ static_assert(mpt::is_character<typename Tdststring::value_type>::value);
+#if MPT_OS_WINDOWS
+ if (windows_has_encoding(encoding)) {
+ return encode_codepage<Tdststring>(codepage_from_encoding(encoding), src);
+ }
+#endif
+ switch (encoding) {
+ case common_encoding::utf8:
+ return encode_utf8<Tdststring>(src);
+ break;
+ case common_encoding::ascii:
+ return encode_ascii<Tdststring>(src);
+ break;
+ case common_encoding::iso8859_1:
+ return encode_iso8859_1<Tdststring>(src);
+ break;
+ case common_encoding::iso8859_15:
+ return encode_8bit<Tdststring>(src, CharsetTableISO8859_15);
+ break;
+ case common_encoding::cp437:
+ return encode_8bit<Tdststring>(src, CharsetTableCP437);
+ break;
+ case common_encoding::cp850:
+ return encode_8bit<Tdststring>(src, CharsetTableCP850);
+ break;
+ case common_encoding::windows1252:
+ return encode_8bit<Tdststring>(src, CharsetTableWindows1252);
+ break;
+ case common_encoding::amiga:
+ return encode_8bit<Tdststring>(src, CharsetTableAmiga);
+ break;
+ case common_encoding::riscos:
+ return encode_8bit<Tdststring>(src, CharsetTableRISC_OS);
+ break;
+ case common_encoding::iso8859_1_no_c1:
+ return encode_8bit_no_c1<Tdststring>(src, CharsetTableISO8859_1);
+ break;
+ case common_encoding::iso8859_15_no_c1:
+ return encode_8bit_no_c1<Tdststring>(src, CharsetTableISO8859_15);
+ break;
+ case common_encoding::amiga_no_c1:
+ return encode_8bit_no_c1<Tdststring>(src, CharsetTableAmiga);
+ break;
+ }
+ throw std::domain_error("unsupported encoding");
+}
+
+template <typename Tdststring>
+inline Tdststring encode(logical_encoding encoding, const mpt::widestring & src) {
+ static_assert(sizeof(typename Tdststring::value_type) == sizeof(char));
+ static_assert(mpt::is_character<typename Tdststring::value_type>::value);
+#if MPT_OS_WINDOWS
+ if (windows_has_encoding(encoding)) {
+ return encode_codepage<Tdststring>(codepage_from_encoding(encoding), src);
+ }
+#endif
+#if MPT_OS_DJGPP
+ switch (encoding) {
+ case logical_encoding::locale:
+ return encode<Tdststring>(djgpp_get_locale_encoding(), src);
+ break;
+ case logical_encoding::active_locale:
+ return encode<Tdststring>(djgpp_get_locale_encoding(), src);
+ break;
+ }
+ throw std::domain_error("unsupported encoding");
+#elif !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+ switch (encoding) {
+ case logical_encoding::locale:
+ return encode_locale<Tdststring>(std::locale(""), src);
+ break;
+ case logical_encoding::active_locale:
+ return encode_locale<Tdststring>(std::locale(), src);
+ break;
+ }
+ throw std::domain_error("unsupported encoding");
+#else
+ throw std::domain_error("unsupported encoding");
+#endif
+}
+
+#if MPT_OS_WINDOWS
+template <typename Tsrcstring>
+inline mpt::widestring decode(UINT codepage, const Tsrcstring & src) {
+ static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char));
+ static_assert(mpt::is_character<typename Tsrcstring::value_type>::value);
+ return decode_codepage(codepage, src);
+}
+#endif // MPT_OS_WINDOWS
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+template <typename Tsrcstring>
+inline mpt::widestring decode(const std::locale & locale, const Tsrcstring & src) {
+ static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char));
+ static_assert(mpt::is_character<typename Tsrcstring::value_type>::value);
+ return decode_locale(src, locale);
+}
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
+template <typename Tsrcstring>
+inline mpt::widestring decode(const char32_t (&table)[256], const Tsrcstring & src) {
+ static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char));
+ static_assert(mpt::is_character<typename Tsrcstring::value_type>::value);
+ return decode_8bit(src, table);
+}
+
+template <typename Tsrcstring>
+inline mpt::widestring decode(common_encoding encoding, const Tsrcstring & src) {
+ static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char));
+ static_assert(mpt::is_character<typename Tsrcstring::value_type>::value);
+#if MPT_OS_WINDOWS
+ if (windows_has_encoding(encoding)) {
+ return decode_codepage(codepage_from_encoding(encoding), src);
+ }
+#endif
+ switch (encoding) {
+ case common_encoding::utf8:
+ return decode_utf8(src);
+ break;
+ case common_encoding::ascii:
+ return decode_ascii(src);
+ break;
+ case common_encoding::iso8859_1:
+ return decode_iso8859_1(src);
+ break;
+ case common_encoding::iso8859_15:
+ return decode_8bit(src, CharsetTableISO8859_15);
+ break;
+ case common_encoding::cp437:
+ return decode_8bit(src, CharsetTableCP437);
+ break;
+ case common_encoding::cp850:
+ return decode_8bit(src, CharsetTableCP850);
+ break;
+ case common_encoding::windows1252:
+ return decode_8bit(src, CharsetTableWindows1252);
+ break;
+ case common_encoding::amiga:
+ return decode_8bit(src, CharsetTableAmiga);
+ break;
+ case common_encoding::riscos:
+ return decode_8bit(src, CharsetTableRISC_OS);
+ break;
+ case common_encoding::iso8859_1_no_c1:
+ return decode_8bit_no_c1(src, CharsetTableISO8859_1);
+ break;
+ case common_encoding::iso8859_15_no_c1:
+ return decode_8bit_no_c1(src, CharsetTableISO8859_15);
+ break;
+ case common_encoding::amiga_no_c1:
+ return decode_8bit_no_c1(src, CharsetTableAmiga);
+ break;
+ }
+ throw std::domain_error("unsupported encoding");
+}
+
+template <typename Tsrcstring>
+inline mpt::widestring decode(logical_encoding encoding, const Tsrcstring & src) {
+ static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char));
+ static_assert(mpt::is_character<typename Tsrcstring::value_type>::value);
+#if MPT_OS_WINDOWS
+ if (windows_has_encoding(encoding)) {
+ return decode_codepage(codepage_from_encoding(encoding), src);
+ }
+#endif
+#if MPT_OS_DJGPP
+ switch (encoding) {
+ case logical_encoding::locale:
+ return decode(djgpp_get_locale_encoding(), src);
+ break;
+ case logical_encoding::active_locale:
+ return decode(djgpp_get_locale_encoding(), src);
+ break;
+ }
+ throw std::domain_error("unsupported encoding");
+#elif !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+ switch (encoding) {
+ case logical_encoding::locale:
+ return decode_locale(std::locale(""), src);
+ break;
+ case logical_encoding::active_locale:
+ return decode_locale(std::locale(), src);
+ break;
+ }
+ throw std::domain_error("unsupported encoding");
+#else
+ throw std::domain_error("unsupported encoding");
+#endif
+}
+
+
+
+inline bool is_utf8(const std::string & str) {
+ return (str == encode<std::string>(common_encoding::utf8, decode<std::string>(common_encoding::utf8, str)));
+}
+
+
+
+template <typename Tstring>
+struct string_transcoder {
+};
+
+template <logical_encoding encoding>
+struct string_transcoder<std::basic_string<char, logical_encoding_char_traits<encoding>>> {
+ using string_type = std::basic_string<char, logical_encoding_char_traits<encoding>>;
+ static inline mpt::widestring decode(const string_type & src) {
+ return mpt::decode<string_type>(encoding, src);
+ }
+ static inline string_type encode(const mpt::widestring & src) {
+ return mpt::encode<string_type>(encoding, src);
+ }
+};
+
+template <common_encoding encoding>
+struct string_transcoder<std::basic_string<char, common_encoding_char_traits<encoding>>> {
+ using string_type = std::basic_string<char, common_encoding_char_traits<encoding>>;
+ static inline mpt::widestring decode(const string_type & src) {
+ return mpt::decode<string_type>(encoding, src);
+ }
+ static inline string_type encode(const mpt::widestring & src) {
+ return mpt::encode<string_type>(encoding, src);
+ }
+};
+
+#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+template <>
+struct string_transcoder<std::wstring> {
+ using string_type = std::wstring;
+ static inline mpt::widestring decode(const string_type & src) {
+ return src;
+ }
+ static inline string_type encode(const mpt::widestring & src) {
+ return src;
+ }
+};
+#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
+
+#if MPT_CXX_AT_LEAST(20)
+template <>
+struct string_transcoder<std::u8string> {
+ using string_type = std::u8string;
+ static inline mpt::widestring decode(const string_type & src) {
+ return mpt::decode_utf8<string_type>(src);
+ }
+ static inline string_type encode(const mpt::widestring & src) {
+ return mpt::encode_utf8<string_type>(src);
+ }
+};
+#endif // C++10
+
+template <>
+struct string_transcoder<std::u16string> {
+ using string_type = std::u16string;
+ static inline mpt::widestring decode(const string_type & src) {
+ if constexpr (sizeof(mpt::widechar) == sizeof(char16_t)) {
+ return mpt::widestring(src.begin(), src.end());
+ } else {
+ return utf32_from_utf16<mpt::widestring, std::u16string>(src);
+ }
+ }
+ static inline string_type encode(const mpt::widestring & src) {
+ if constexpr (sizeof(mpt::widechar) == sizeof(char16_t)) {
+ return string_type(src.begin(), src.end());
+ } else {
+ return utf16_from_utf32<std::u16string, mpt::widestring>(src);
+ }
+ }
+};
+
+#if defined(MPT_COMPILER_QUIRK_NO_WCHAR)
+template <>
+struct string_transcoder<std::u32string> {
+ using string_type = std::u32string;
+ static inline mpt::widestring decode(const string_type & src) {
+ return src;
+ }
+ static inline string_type encode(const mpt::widestring & src) {
+ return src;
+ }
+};
+#else // !MPT_COMPILER_QUIRK_NO_WCHAR
+template <>
+struct string_transcoder<std::u32string> {
+ using string_type = std::u32string;
+ static inline mpt::widestring decode(const string_type & src) {
+ if constexpr (sizeof(mpt::widechar) == sizeof(char32_t)) {
+ return mpt::widestring(src.begin(), src.end());
+ } else {
+ return utf16_from_utf32<mpt::widestring, std::u32string>(src);
+ }
+ }
+ static inline string_type encode(const mpt::widestring & src) {
+ if constexpr (sizeof(mpt::widechar) == sizeof(char32_t)) {
+ return string_type(src.begin(), src.end());
+ } else {
+ return utf32_from_utf16<std::u32string, mpt::widestring>(src);
+ }
+ }
+};
+#endif // MPT_COMPILER_QUIRK_NO_WCHAR
+
+#if MPT_DETECTED_MFC
+
+template <>
+struct string_transcoder<CStringW> {
+ using string_type = CStringW;
+ static inline mpt::widestring decode(const string_type & src) {
+ return mpt::widestring(src.GetString());
+ }
+ static inline string_type encode(const mpt::widestring & src) {
+ return src.c_str();
+ }
+};
+
+template <>
+struct string_transcoder<CStringA> {
+ using string_type = CStringA;
+ static inline mpt::widestring decode(const string_type & src) {
+ return mpt::decode<std::string>(mpt::logical_encoding::locale, std::string(src.GetString()));
+ }
+ static inline string_type encode(const mpt::widestring & src) {
+ return mpt::encode<std::string>(mpt::logical_encoding::locale, src).c_str();
+ }
+};
+
+#endif // MPT_DETECTED_MFC
+
+template <typename Tdststring, typename Tsrcstring, std::enable_if_t<mpt::is_string_type<typename mpt::make_string_type<Tsrcstring>::type>::value, bool> = true>
+inline Tdststring transcode(const Tsrcstring & src) {
+ if constexpr (std::is_same<Tdststring, typename mpt::make_string_type<Tsrcstring>::type>::value) {
+ return mpt::as_string(src);
+ } else {
+ return string_transcoder<Tdststring>::encode(string_transcoder<decltype(mpt::as_string(src))>::decode(mpt::as_string(src)));
+ }
+}
+
+template <typename Tdststring, typename Tsrcstring, typename Tencoding, std::enable_if_t<std::is_same<Tdststring, std::string>::value, bool> = true, std::enable_if_t<mpt::is_string_type<typename mpt::make_string_type<Tsrcstring>::type>::value, bool> = true>
+inline Tdststring transcode(Tencoding to, const Tsrcstring & src) {
+ return mpt::encode<Tdststring>(to, string_transcoder<decltype(mpt::as_string(src))>::decode(mpt::as_string(src)));
+}
+
+template <typename Tdststring, typename Tsrcstring, typename Tencoding, std::enable_if_t<std::is_same<typename mpt::make_string_type<Tsrcstring>::type, std::string>::value, bool> = true, std::enable_if_t<mpt::is_string_type<typename mpt::make_string_type<Tsrcstring>::type>::value, bool> = true>
+inline Tdststring transcode(Tencoding from, const Tsrcstring & src) {
+ return string_transcoder<Tdststring>::encode(mpt::decode<decltype(mpt::as_string(src))>(from, mpt::as_string(src)));
+}
+
+template <typename Tdststring, typename Tsrcstring, typename Tto, typename Tfrom, std::enable_if_t<mpt::is_string_type<typename mpt::make_string_type<Tsrcstring>::type>::value, bool> = true>
+inline Tdststring transcode(Tto to, Tfrom from, const Tsrcstring & src) {
+ return mpt::encode<Tdststring>(to, mpt::decode<decltype(mpt::as_string(src))>(from, mpt::as_string(src)));
+}
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_STRING_TRANSCODE_TRANSCODE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/system_error/system_error.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/system_error/system_error.hpp
new file mode 100644
index 00000000..c15b5bd6
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/system_error/system_error.hpp
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_SYSTEM_ERROR_SYSTEM_ERROR_HPP
+#define MPT_SYSTEM_ERROR_SYSTEM_ERROR_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/format/message.hpp"
+#include "mpt/format/message_macros.hpp"
+#include "mpt/format/simple.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+#include "mpt/out_of_memory/out_of_memory.hpp"
+
+#if MPT_OS_WINDOWS
+#include <stdexcept>
+#if MPT_OS_WINDOWS_WINRT
+#include <vector>
+#endif // MPT_OS_WINDOWS_WINRT
+#endif // MPT_OS_WINDOWS
+
+#if MPT_OS_WINDOWS
+#include <windows.h>
+#endif // MPT_OS_WINDOWS
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+#if MPT_OS_WINDOWS
+
+
+
+namespace windows {
+
+
+
+inline mpt::ustring GetErrorMessage(DWORD errorCode, HANDLE hModule = NULL) {
+#if MPT_OS_WINDOWS_WINRT
+ std::vector<TCHAR> msgbuf(65536);
+ if (FormatMessage(
+ (hModule ? FORMAT_MESSAGE_FROM_HMODULE : 0) | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ hModule,
+ errorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ msgbuf.data(),
+ mpt::saturate_cast<DWORD>(msgbuf.size()),
+ NULL)
+ == 0)
+ {
+ DWORD e = GetLastError();
+ if ((e == ERROR_NOT_ENOUGH_MEMORY) || (e == ERROR_OUTOFMEMORY)) {
+ mpt::throw_out_of_memory();
+ }
+ return {};
+ }
+ return mpt::transcode<mpt::ustring>(mpt::winstring{msgbuf.data()});
+#else
+ mpt::ustring message;
+ void * lpMsgBuf = nullptr;
+ if (FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | (hModule ? FORMAT_MESSAGE_FROM_HMODULE : 0) | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ hModule,
+ errorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&lpMsgBuf,
+ 0,
+ NULL)
+ == 0)
+ {
+ DWORD e = GetLastError();
+ if (lpMsgBuf) {
+ LocalFree(lpMsgBuf);
+ }
+ if ((e == ERROR_NOT_ENOUGH_MEMORY) || (e == ERROR_OUTOFMEMORY)) {
+ mpt::throw_out_of_memory();
+ }
+ return {};
+ }
+ if (!lpMsgBuf) {
+ return {};
+ }
+ try {
+ message = mpt::transcode<mpt::ustring>(mpt::winstring{static_cast<LPTSTR>(lpMsgBuf)});
+ } catch (mpt::out_of_memory e) {
+ LocalFree(lpMsgBuf);
+ mpt::rethrow_out_of_memory(e);
+ }
+ LocalFree(lpMsgBuf);
+ return message;
+#endif
+}
+
+
+class error
+ : public std::runtime_error {
+public:
+ error(DWORD errorCode, HANDLE hModule = NULL)
+ : std::runtime_error(mpt::transcode<std::string>(mpt::exception_encoding, MPT_UFORMAT_MESSAGE("Windows Error: 0x{}: {}")(mpt::format<mpt::ustring>::hex0<8>(errorCode), GetErrorMessage(errorCode, hModule)))) {
+ return;
+ }
+};
+
+
+inline HANDLE CheckFileHANDLE(HANDLE handle) {
+ if (handle == INVALID_HANDLE_VALUE) {
+ DWORD err = ::GetLastError();
+ if ((err == ERROR_NOT_ENOUGH_MEMORY) || (err == ERROR_OUTOFMEMORY)) {
+ mpt::throw_out_of_memory();
+ }
+ throw windows::error(err);
+ }
+ return handle;
+}
+
+
+inline HANDLE CheckHANDLE(HANDLE handle) {
+ if (handle == NULL) {
+ DWORD err = ::GetLastError();
+ if ((err == ERROR_NOT_ENOUGH_MEMORY) || (err == ERROR_OUTOFMEMORY)) {
+ mpt::throw_out_of_memory();
+ }
+ throw windows::error(err);
+ }
+ return handle;
+}
+
+
+inline void CheckBOOL(BOOL result) {
+ if (result == FALSE) {
+ DWORD err = ::GetLastError();
+ if ((err == ERROR_NOT_ENOUGH_MEMORY) || (err == ERROR_OUTOFMEMORY)) {
+ mpt::throw_out_of_memory();
+ }
+ throw windows::error(err);
+ }
+}
+
+
+inline void ExpectError(DWORD expected) {
+ DWORD err = ::GetLastError();
+ if (err != expected) {
+ if ((err == ERROR_NOT_ENOUGH_MEMORY) || (err == ERROR_OUTOFMEMORY)) {
+ mpt::throw_out_of_memory();
+ }
+ throw windows::error(err);
+ }
+}
+
+
+
+} // namespace windows
+
+
+
+#endif // MPT_OS_WINDOWS
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_SYSTEM_ERROR_SYSTEM_ERROR_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/test/test.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/test/test.hpp
new file mode 100644
index 00000000..babb5214
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/test/test.hpp
@@ -0,0 +1,564 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_TEST_TEST_HPP
+#define MPT_TEST_TEST_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/source_location.hpp"
+
+#include <functional>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <stdexcept>
+#include <type_traits>
+#include <typeinfo>
+#include <utility>
+#include <variant>
+
+#include <cstddef>
+#include <cstdlib>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace test {
+
+
+
+template <typename S, typename T, typename = void>
+struct is_to_stream_writable : std::false_type { };
+
+template <typename S, typename T>
+struct is_to_stream_writable<S, T, std::void_t<decltype(std::declval<S &>() << std::declval<T>())>> : std::true_type { };
+
+template <typename T>
+inline auto format(const T & x) -> typename std::enable_if<mpt::test::is_to_stream_writable<std::ostringstream, T>::value, std::string>::type {
+ std::ostringstream s;
+ s << x;
+ return s.str();
+}
+
+template <typename T>
+inline auto format(const T & x) -> typename std::enable_if<!mpt::test::is_to_stream_writable<std::ostringstream, T>::value, std::string>::type {
+ return typeid(x).name();
+}
+
+inline std::string get_exception_text() {
+ std::string result;
+ try {
+ // cppcheck false-positive
+ // cppcheck-suppress rethrowNoCurrentException
+ throw;
+ } catch (const std::exception & e) {
+ result = e.what();
+ } catch (...) {
+ result = "unknown exception";
+ }
+ return result;
+}
+
+struct result_success {
+};
+struct result_failure {
+ std::string text{};
+};
+struct result_unexpected_exception {
+ std::string text{};
+};
+
+struct result {
+ std::variant<std::monostate, result_success, result_failure, result_unexpected_exception> info{std::monostate{}};
+};
+
+struct statistics_counters {
+ std::size_t total{0};
+ std::size_t run{0};
+ std::size_t successes{0};
+ std::size_t failures{0};
+ std::size_t unexpected_exceptions{0};
+ std::size_t completed{0};
+ constexpr statistics_counters & operator+=(const statistics_counters & other) noexcept {
+ total += other.total;
+ run += other.run;
+ successes += other.successes;
+ failures += other.failures;
+ unexpected_exceptions += other.unexpected_exceptions;
+ completed += other.completed;
+ return *this;
+ }
+};
+
+struct group_statistics {
+ statistics_counters tests{};
+ statistics_counters cases{};
+ statistics_counters local_cases{};
+};
+
+struct global_statistics {
+ statistics_counters groups{};
+ statistics_counters tests{};
+ statistics_counters cases{};
+ std::map<std::string, group_statistics> individual_group_statistics{};
+ explicit constexpr operator bool() noexcept {
+ return succeeded();
+ }
+ constexpr bool operator!() noexcept {
+ return failed();
+ }
+ constexpr bool succeeded() noexcept {
+ return groups.successes == groups.run;
+ }
+ constexpr bool failed() noexcept {
+ return groups.failures > 0 || groups.unexpected_exceptions > 0;
+ }
+};
+
+class reporter_interface {
+protected:
+ virtual ~reporter_interface() = default;
+
+public:
+ virtual void run_begin(const mpt::source_location & loc) = 0;
+ virtual void group_begin(const mpt::source_location & loc, const char * name) = 0;
+ virtual void test_begin(const mpt::source_location & loc, const char * name) = 0;
+ virtual void case_run(const mpt::source_location & loc) = 0;
+ virtual void case_run(const mpt::source_location & loc, const char * text_e) = 0;
+ virtual void case_run(const mpt::source_location & loc, const char * text_ex, const char * text_e) = 0;
+ virtual void case_run(const mpt::source_location & loc, const char * text_a, const char * text_cmp, const char * text_b) = 0;
+ virtual void case_result(const mpt::source_location & loc, const mpt::test::result & result) = 0;
+ virtual void test_end(const mpt::source_location & loc, const char * name, const statistics_counters & counters) = 0;
+ virtual void group_end(const mpt::source_location & loc, const char * name, const group_statistics & statistics) = 0;
+ virtual void run_end(const mpt::source_location & loc, const global_statistics & statistics) = 0;
+ virtual void immediate_breakpoint() = 0;
+};
+
+class silent_reporter
+ : public reporter_interface {
+public:
+ silent_reporter() = default;
+ ~silent_reporter() override = default;
+
+public:
+ virtual void run_begin(const mpt::source_location &) override {
+ }
+ virtual void group_begin(const mpt::source_location &, const char *) override {
+ }
+ virtual void test_begin(const mpt::source_location &, const char *) override {
+ }
+ virtual void case_run(const mpt::source_location &) override {
+ }
+ virtual void case_run(const mpt::source_location &, const char *) override {
+ }
+ virtual void case_run(const mpt::source_location &, const char *, const char *) override {
+ }
+ virtual void case_run(const mpt::source_location &, const char *, const char *, const char *) override {
+ }
+ virtual void case_result(const mpt::source_location &, const mpt::test::result &) override {
+ }
+ virtual void test_end(const mpt::source_location &, const char *, const statistics_counters &) override {
+ }
+ virtual void group_end(const mpt::source_location &, const char *, const group_statistics &) override {
+ }
+ virtual void run_end(const mpt::source_location &, const global_statistics &) override {
+ }
+ virtual void immediate_breakpoint() override {
+ }
+};
+
+class simple_reporter : public reporter_interface {
+private:
+ std::ostream & s;
+
+public:
+ simple_reporter(std::ostream & s_)
+ : s(s_) {
+ s.flush();
+ }
+ ~simple_reporter() override {
+ s.flush();
+ }
+
+public:
+ void run_begin(const mpt::source_location & loc) override {
+ static_cast<void>(loc);
+ s << "Running test suite ..." << std::endl;
+ }
+ void group_begin(const mpt::source_location & loc, const char * name) override {
+ static_cast<void>(loc);
+ s << "Running group '" << name << "' ..." << std::endl;
+ }
+ void test_begin(const mpt::source_location & loc, const char * name) override {
+ static_cast<void>(loc);
+ s << " Running test '" << name << "' ..." << std::endl;
+ }
+ void case_run(const mpt::source_location & loc) override {
+ static_cast<void>(loc);
+ s << " Checking ..." << std::endl;
+ }
+ void case_run(const mpt::source_location & loc, const char * text_e) override {
+ static_cast<void>(loc);
+ s << " Checking '" << text_e << "' ..." << std::endl;
+ }
+ void case_run(const mpt::source_location & loc, const char * text_ex, const char * text_e) override {
+ static_cast<void>(loc);
+ if (text_ex) {
+ s << " Checking '" << text_e << " throws " << text_ex << "' ..." << std::endl;
+ } else {
+ s << " Checking '" << text_e << " throws' ..." << std::endl;
+ }
+ }
+ void case_run(const mpt::source_location & loc, const char * text_a, const char * text_cmp, const char * text_b) override {
+ static_cast<void>(loc);
+ s << " Checking '" << text_a << " " << text_cmp << " " << text_b << "' ..." << std::endl;
+ }
+ void case_result(const mpt::source_location & loc, const mpt::test::result & result) override {
+ static_cast<void>(loc);
+ s << " Checking done: ";
+ if (std::holds_alternative<result_success>(result.info)) {
+ s << "Success.";
+ } else if (std::holds_alternative<result_failure>(result.info)) {
+ s << "FAILURE: " << std::get<result_failure>(result.info).text;
+ } else if (std::holds_alternative<result_unexpected_exception>(result.info)) {
+ s << "UNEXPECTED EXCEPTION: " << std::get<result_unexpected_exception>(result.info).text;
+ }
+ s << std::endl;
+ }
+ void test_end(const mpt::source_location & loc, const char * name, const statistics_counters & counters) override {
+ static_cast<void>(loc);
+ static_cast<void>(counters);
+ s << " Running test '" << name << "' done." << std::endl;
+ }
+ void group_end(const mpt::source_location & loc, const char * name, const group_statistics & statistics) override {
+ static_cast<void>(loc);
+ static_cast<void>(statistics);
+ s << "Running group '" << name << "' done." << std::endl;
+ }
+ void run_end(const mpt::source_location & loc, const global_statistics & statistics) override {
+ static_cast<void>(loc);
+ s << "Running test suite done." << std::endl;
+ s << "groups: " << statistics.groups.total << " | " << statistics.groups.successes << " passed";
+ if (statistics.groups.failures || statistics.groups.unexpected_exceptions) {
+ s << " | " << statistics.groups.failures << " FAILED";
+ if (statistics.groups.unexpected_exceptions) {
+ s << " | " << statistics.groups.unexpected_exceptions << " UNEXPECTED EXCEPTIONS";
+ }
+ }
+ s << std::endl;
+ s << "tests: " << statistics.tests.total << " | " << statistics.tests.successes << " passed";
+ if (statistics.tests.failures || statistics.tests.unexpected_exceptions) {
+ s << " | " << statistics.tests.failures << " FAILED";
+ if (statistics.tests.unexpected_exceptions) {
+ s << " | " << statistics.tests.unexpected_exceptions << " UNEXPECTED EXCEPTIONS";
+ }
+ }
+ s << std::endl;
+ s << "checks: " << statistics.cases.total << " | " << statistics.cases.successes << " passed";
+ if (statistics.cases.failures || statistics.cases.unexpected_exceptions) {
+ s << " | " << statistics.cases.failures << " FAILED";
+ if (statistics.cases.unexpected_exceptions) {
+ s << " | " << statistics.cases.unexpected_exceptions << " UNEXPECTED EXCEPTIONS";
+ }
+ }
+ s << std::endl;
+ }
+ void immediate_breakpoint() override {
+ return;
+ }
+};
+
+struct group;
+
+struct context {
+ mpt::test::group & group;
+ mpt::test::reporter_interface & reporter;
+ mpt::test::group_statistics statistics{};
+};
+
+using void_context_function = void (*)(mpt::test::context &);
+
+struct group {
+ group * next{nullptr};
+ const char * name{""};
+ void_context_function func{nullptr};
+ inline group(const char * name_, void_context_function f)
+ : name(name_)
+ , func(f) {
+ next = group_list();
+ group_list() = this;
+ }
+ group_statistics run(mpt::test::reporter_interface & reporter, const mpt::source_location & loc = mpt::source_location::current()) {
+ mpt::test::context context{*this, reporter};
+ context.reporter.group_begin(loc, name);
+ if (func) {
+ func(context);
+ }
+ context.reporter.group_end(loc, name, context.statistics);
+ return context.statistics;
+ }
+
+public:
+ [[nodiscard]] static inline group *& group_list() noexcept {
+ static group * group_list = nullptr;
+ return group_list;
+ }
+};
+
+inline global_statistics run_all(mpt::test::reporter_interface & reporter, const mpt::source_location & loc = mpt::source_location::current()) {
+ global_statistics statistics{};
+ reporter.run_begin(loc);
+ for (group * g = group::group_list(); g; g = g->next) {
+ statistics.groups.total++;
+ statistics.groups.run++;
+ group_statistics s = g->run(reporter, loc);
+ if (s.tests.unexpected_exceptions) {
+ statistics.groups.unexpected_exceptions++;
+ } else if (s.tests.failures) {
+ statistics.groups.failures++;
+ } else {
+ statistics.groups.successes++;
+ }
+ statistics.tests += s.tests;
+ statistics.cases += s.cases;
+ statistics.groups.completed++;
+ statistics.individual_group_statistics[g->name] = s;
+ }
+ reporter.run_end(loc, statistics);
+ return statistics;
+}
+
+struct test {
+
+ mpt::test::context & context;
+ const char * name{""};
+ mpt::source_location source_location{mpt::source_location::current()};
+ void (*breakpoint)(void){nullptr};
+
+ test(const test &) = delete;
+ test & operator=(const test &) = delete;
+
+ inline test(mpt::test::context & context_, void (*breakpoint_)(void) = nullptr, const mpt::source_location & source_location_ = mpt::source_location::current())
+ : context(context_)
+ , source_location(source_location_)
+ , breakpoint(breakpoint_) {
+ report_test_begin();
+ }
+ inline test(mpt::test::context & context_, const char * name_, void (*breakpoint_)(void) = nullptr, const mpt::source_location & source_location_ = mpt::source_location::current())
+ : context(context_)
+ , name(name_)
+ , source_location(source_location_)
+ , breakpoint(breakpoint_) {
+ report_test_begin();
+ }
+
+ inline ~test() {
+ report_test_end();
+ }
+
+ inline void immediate_breakpoint() {
+ if (breakpoint) {
+ breakpoint();
+ } else {
+ context.reporter.immediate_breakpoint();
+ }
+ }
+
+ void report_test_begin() {
+ context.statistics.tests.total++;
+ context.statistics.tests.run++;
+ context.statistics.local_cases = statistics_counters{};
+ context.reporter.test_begin(source_location, name);
+ }
+
+ void report_run() {
+ context.statistics.local_cases.total++;
+ context.statistics.local_cases.run++;
+ context.reporter.case_run(source_location);
+ }
+ void report_run(const char * text_e) {
+ context.statistics.local_cases.total++;
+ context.statistics.local_cases.run++;
+ context.reporter.case_run(source_location, text_e);
+ }
+ void report_run(const char * text_ex, const char * text_e) {
+ context.statistics.local_cases.total++;
+ context.statistics.local_cases.run++;
+ context.reporter.case_run(source_location, text_ex, text_e);
+ }
+ void report_run(const char * text_a, const char * text_cmp, const char * text_b) {
+ context.statistics.local_cases.total++;
+ context.statistics.local_cases.run++;
+ context.reporter.case_run(source_location, text_a, text_cmp, text_b);
+ }
+
+ void report_result(mpt::test::result result) {
+ if (std::holds_alternative<result_success>(result.info)) {
+ context.statistics.local_cases.successes++;
+ } else if (std::holds_alternative<result_failure>(result.info)) {
+ context.statistics.local_cases.failures++;
+ } else if (std::holds_alternative<result_unexpected_exception>(result.info)) {
+ context.statistics.local_cases.unexpected_exceptions++;
+ }
+ context.statistics.local_cases.completed++;
+ context.reporter.case_result(source_location, result);
+ }
+
+ void report_test_end() {
+ context.statistics.cases += context.statistics.local_cases;
+ if (context.statistics.local_cases.unexpected_exceptions) {
+ context.statistics.tests.unexpected_exceptions++;
+ } else if (context.statistics.local_cases.failures) {
+ context.statistics.tests.failures++;
+ } else {
+ context.statistics.tests.successes++;
+ }
+ context.statistics.tests.completed++;
+ context.reporter.test_end(source_location, name, context.statistics.local_cases);
+ }
+
+ template <typename Texception, typename Tcallable, typename std::enable_if<std::is_invocable<Tcallable>::value, bool>::type = true>
+ inline test & expect_throws(Tcallable c, const char * text_ex = nullptr, const char * text_e = nullptr) {
+ const std::type_info & tiexception = typeid(Texception);
+ const std::type_info & tic = typeid(decltype(c()));
+ report_run(text_ex ? text_ex : tiexception.name(), text_e ? text_e : tic.name());
+ mpt::test::result result;
+ try {
+ c();
+ immediate_breakpoint();
+ result.info = mpt::test::result_failure{};
+ } catch (const Texception &) {
+ result.info = mpt::test::result_success{};
+ } catch (...) {
+ immediate_breakpoint();
+ result.info = mpt::test::result_unexpected_exception{mpt::test::get_exception_text()};
+ }
+ report_result(result);
+ return *this;
+ }
+
+ template <typename Tcallable, typename std::enable_if<std::is_invocable<Tcallable>::value, bool>::type = true>
+ inline test & expect_throws_any(Tcallable c, const char * text_e = nullptr) {
+ const std::type_info & tic = typeid(decltype(c()));
+ report_run(nullptr, text_e ? text_e : tic.name());
+ mpt::test::result result;
+ try {
+ c();
+ immediate_breakpoint();
+ result.info = mpt::test::result_failure{};
+ } catch (...) {
+ result.info = mpt::test::result_success{};
+ }
+ report_result(result);
+ return *this;
+ }
+
+ template <typename Texpr, typename std::enable_if<std::is_invocable<Texpr>::value, bool>::type = true>
+ inline test & expect(Texpr e, const char * text_e = nullptr) {
+ const std::type_info & tie = typeid(decltype(std::invoke(e)));
+ report_run(text_e ? text_e : tie.name());
+ mpt::test::result result;
+ try {
+ const auto ve = std::invoke(e);
+ if (!ve) {
+ immediate_breakpoint();
+ result.info = mpt::test::result_failure{/*mpt::test::format(ve)*/};
+ } else {
+ result.info = mpt::test::result_success{};
+ }
+ } catch (...) {
+ immediate_breakpoint();
+ result.info = mpt::test::result_unexpected_exception{mpt::test::get_exception_text()};
+ }
+ report_result(result);
+ return *this;
+ }
+
+ template <typename Ta, typename Tcmp, typename Tb, typename std::enable_if<std::is_invocable<Ta>::value, bool>::type = true, typename std::enable_if<std::is_invocable<Tb>::value, bool>::type = true>
+ inline test & expect(Ta && a, Tcmp cmp, Tb && b, const char * text_a = nullptr, const char * text_cmp = nullptr, const char * text_b = nullptr) {
+ const std::type_info & tia = typeid(decltype(std::invoke(a)));
+ const std::type_info & ticmp = typeid(decltype(cmp));
+ const std::type_info & tib = typeid(decltype(std::invoke(b)));
+ report_run(text_a ? text_a : tia.name(), text_cmp ? text_cmp : ticmp.name(), text_b ? text_b : tib.name());
+ mpt::test::result result;
+ try {
+ const auto va = std::invoke(a);
+ const auto vb = std::invoke(b);
+ if (!cmp(va, vb)) {
+ immediate_breakpoint();
+ result.info = mpt::test::result_failure{mpt::test::format(va) + " " + mpt::test::format(cmp) + " " + mpt::test::format(vb)};
+ } else {
+ result.info = mpt::test::result_success{};
+ }
+ } catch (...) {
+ immediate_breakpoint();
+ result.info = mpt::test::result_unexpected_exception{mpt::test::get_exception_text()};
+ }
+ report_result(result);
+ return *this;
+ }
+
+ template <typename Texpr, typename std::enable_if<!std::is_invocable<Texpr>::value, bool>::type = true>
+ inline test & expect(Texpr && e, const char * text_e = nullptr) {
+ const std::type_info & tie = typeid(decltype(std::forward<Texpr>(e)));
+ report_run(text_e ? text_e : tie.name());
+ mpt::test::result result;
+ try {
+ const auto ve = std::forward<Texpr>(e);
+ if (!ve) {
+ immediate_breakpoint();
+ result.info = mpt::test::result_failure{/*mpt::test::format(ve)*/};
+ } else {
+ result.info = mpt::test::result_success{};
+ }
+ } catch (...) {
+ immediate_breakpoint();
+ result.info = mpt::test::result_unexpected_exception{mpt::test::get_exception_text()};
+ }
+ report_result(result);
+ return *this;
+ }
+
+ template <typename Ta, typename Tcmp, typename Tb, typename std::enable_if<!std::is_invocable<Ta>::value, bool>::type = true, typename std::enable_if<!std::is_invocable<Tb>::value, bool>::type = true>
+ inline test & expect(Ta && a, Tcmp cmp, Tb && b, const char * text_a = nullptr, const char * text_cmp = nullptr, const char * text_b = nullptr) {
+ const std::type_info & tia = typeid(decltype(std::forward<Ta>(a)));
+ const std::type_info & ticmp = typeid(decltype(cmp));
+ const std::type_info & tib = typeid(decltype(std::forward<Tb>(b)));
+ report_run(text_a ? text_a : tia.name(), text_cmp ? text_cmp : ticmp.name(), text_b ? text_b : tib.name());
+ mpt::test::result result;
+ try {
+ const auto va = std::forward<Ta>(a);
+ const auto vb = std::forward<Tb>(b);
+ if (!cmp(va, vb)) {
+ immediate_breakpoint();
+ result.info = mpt::test::result_failure{mpt::test::format(va) + " " + mpt::test::format(cmp) + " " + mpt::test::format(vb)};
+ } else {
+ result.info = mpt::test::result_success{};
+ }
+ } catch (...) {
+ immediate_breakpoint();
+ result.info = mpt::test::result_unexpected_exception{mpt::test::get_exception_text()};
+ }
+ report_result(result);
+ return *this;
+ }
+};
+
+
+
+} // namespace test
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_TEST_TEST_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/test/test_macros.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/test/test_macros.hpp
new file mode 100644
index 00000000..23d296f5
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/test/test_macros.hpp
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_TEST_TEST_MACROS_HPP
+#define MPT_TEST_TEST_MACROS_HPP
+
+
+
+#include "mpt/base/namespace.hpp"
+#include "mpt/base/preprocessor.hpp"
+#include "mpt/test/test.hpp"
+
+#include <functional>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace test {
+
+
+
+#define MPT_TEST_GROUP_BEGIN(name) \
+ inline mpt::test::group MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_name) { \
+ name, [](mpt::test::context & mpt_test_context) {
+#define MPT_TEST_GROUP_END() \
+ } \
+ } \
+ ;
+
+#define MPT_TEST_GROUP_INLINE_IDENTIFIER(identifier, name) \
+ inline void MPT_PP_JOIN(mpt_test_group_func_, identifier)(mpt::test::context & mpt_test_context); \
+ inline mpt::test::group MPT_PP_JOIN(mpt_test_group_name_, identifier){ \
+ name, [](mpt::test::context & mpt_test_context) { \
+ MPT_PP_JOIN(mpt_test_group_func_, identifier) \
+ (mpt_test_context); \
+ }}; \
+ inline void MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func)(mpt::test::context & mpt_test_context)
+
+#define MPT_TEST_GROUP_INLINE(name) \
+ inline void MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func)(mpt::test::context & mpt_test_context); \
+ inline mpt::test::group MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_name){ \
+ name, [](mpt::test::context & mpt_test_context) { \
+ MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func) \
+ (mpt_test_context); \
+ }}; \
+ inline void MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func)(mpt::test::context & mpt_test_context)
+
+#define MPT_TEST_GROUP_STATIC(name) \
+ static void MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func)(mpt::test::context & mpt_test_context); \
+ static mpt::test::group MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_name){ \
+ name, [](mpt::test::context & mpt_test_context) { \
+ MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func) \
+ (mpt_test_context); \
+ }}; \
+ static void MPT_PP_UNIQUE_IDENTIFIER(mpt_test_group_func)(mpt::test::context & mpt_test_context)
+
+#define MPT_TEST_DELAYED(x) [&] { \
+ return x; \
+}
+
+#define MPT_TEST_EXPECT mpt::test::test{context}.expect
+
+#define MPT_TEST_EXPECT_EXPR(e) mpt::test::test{mpt_test_context}.expect([&] { return e; }, #e)
+#define MPT_TEST_EXPECT_CMP(a, cmp, b) mpt::test::test{mpt_test_context}.expect([&] { return a; }, [](const auto & a_, const auto & b_) { return a_ cmp b_; }, [&] { return b; }, #a, #cmp, #b)
+#define MPT_TEST_EXPECT_THROWS_ANY(expr) mpt::test::test{mpt_test_context}.expect_throws_any([&] { expr; }, #expr)
+#define MPT_TEST_EXPECT_THROWS(exception, expr) mpt::test::test{mpt_test_context}.expect_throws<exception>([&] { expr; }, #exception, #expr)
+
+#define MPT_TEST_EXPECT_EQUAL(a, b) mpt::test::test{mpt_test_context}.expect([&] { return a; }, std::equal_to<>{}, [&] { return b; }, #a, "==", #b)
+
+
+
+} // namespace test
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_TEST_TEST_MACROS_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/uuid/guid.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/uuid/guid.hpp
new file mode 100644
index 00000000..b941b753
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/uuid/guid.hpp
@@ -0,0 +1,286 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_UUID_GUID_HPP
+#define MPT_UUID_GUID_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/out_of_memory/out_of_memory.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+#include "mpt/uuid/uuid.hpp"
+
+#include <stdexcept>
+#include <vector>
+
+#if MPT_OS_WINDOWS
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600)
+#include <guiddef.h>
+#endif // _WIN32_WINNT
+#include <objbase.h>
+#include <rpc.h>
+#endif // MPT_OS_WINDOWS
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+#if MPT_OS_WINDOWS
+
+
+
+// COM CLSID<->string conversion
+// A CLSID string is not necessarily a standard UUID string,
+// it might also be a symbolic name for the interface.
+// (see CLSIDFromString ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms680589%28v=vs.85%29.aspx ))
+
+inline mpt::winstring CLSIDToString(CLSID clsid) {
+ std::wstring str;
+ LPOLESTR tmp = nullptr;
+ switch (::StringFromCLSID(clsid, &tmp)) {
+ case S_OK:
+ break;
+ case E_OUTOFMEMORY:
+ if (tmp) {
+ ::CoTaskMemFree(tmp);
+ tmp = nullptr;
+ }
+ mpt::throw_out_of_memory();
+ break;
+ default:
+ if (tmp) {
+ ::CoTaskMemFree(tmp);
+ tmp = nullptr;
+ }
+ throw std::logic_error("StringFromCLSID() failed.");
+ break;
+ }
+ if (!tmp) {
+ throw std::logic_error("StringFromCLSID() failed.");
+ }
+ try {
+ str = tmp;
+ } catch (mpt::out_of_memory e) {
+ ::CoTaskMemFree(tmp);
+ tmp = nullptr;
+ mpt::rethrow_out_of_memory(e);
+ }
+ ::CoTaskMemFree(tmp);
+ tmp = nullptr;
+ return mpt::transcode<mpt::winstring>(str);
+}
+
+inline CLSID StringToCLSID(const mpt::winstring & str_) {
+ const std::wstring str = mpt::transcode<std::wstring>(str_);
+ CLSID clsid = CLSID();
+ std::vector<OLECHAR> tmp(str.c_str(), str.c_str() + str.length() + 1);
+ switch (::CLSIDFromString(tmp.data(), &clsid)) {
+ case NOERROR:
+ // nothing
+ break;
+ case E_INVALIDARG:
+ clsid = CLSID();
+ break;
+ case CO_E_CLASSSTRING:
+ clsid = CLSID();
+ break;
+ case REGDB_E_CLASSNOTREG:
+ clsid = CLSID();
+ break;
+ case REGDB_E_READREGDB:
+ clsid = CLSID();
+ throw std::runtime_error("CLSIDFromString() failed: REGDB_E_READREGDB.");
+ break;
+ default:
+ clsid = CLSID();
+ throw std::logic_error("CLSIDFromString() failed.");
+ break;
+ }
+ return clsid;
+}
+
+inline bool VerifyStringToCLSID(const mpt::winstring & str_, CLSID & clsid) {
+ const std::wstring str = mpt::transcode<std::wstring>(str_);
+ bool result = false;
+ clsid = CLSID();
+ std::vector<OLECHAR> tmp(str.c_str(), str.c_str() + str.length() + 1);
+ switch (::CLSIDFromString(tmp.data(), &clsid)) {
+ case NOERROR:
+ result = true;
+ break;
+ case E_INVALIDARG:
+ result = false;
+ break;
+ case CO_E_CLASSSTRING:
+ result = false;
+ break;
+ case REGDB_E_CLASSNOTREG:
+ result = false;
+ break;
+ case REGDB_E_READREGDB:
+ throw std::runtime_error("CLSIDFromString() failed: REGDB_E_READREGDB.");
+ break;
+ default:
+ throw std::logic_error("CLSIDFromString() failed.");
+ break;
+ }
+ return result;
+}
+
+inline bool IsCLSID(const mpt::winstring & str_) {
+ const std::wstring str = mpt::transcode<std::wstring>(str_);
+ bool result = false;
+ CLSID clsid = CLSID();
+ std::vector<OLECHAR> tmp(str.c_str(), str.c_str() + str.length() + 1);
+ switch (::CLSIDFromString(tmp.data(), &clsid)) {
+ case NOERROR:
+ result = true;
+ break;
+ case E_INVALIDARG:
+ result = false;
+ break;
+ case CO_E_CLASSSTRING:
+ result = false;
+ break;
+ case REGDB_E_CLASSNOTREG:
+ result = false;
+ break;
+ case REGDB_E_READREGDB:
+ result = false;
+ throw std::runtime_error("CLSIDFromString() failed: REGDB_E_READREGDB.");
+ break;
+ default:
+ result = false;
+ throw std::logic_error("CLSIDFromString() failed.");
+ break;
+ }
+ return result;
+}
+
+
+// COM IID<->string conversion
+
+inline IID StringToIID(const mpt::winstring & str_) {
+ const std::wstring str = mpt::transcode<std::wstring>(str_);
+ IID iid = IID();
+ std::vector<OLECHAR> tmp(str.c_str(), str.c_str() + str.length() + 1);
+ switch (::IIDFromString(tmp.data(), &iid)) {
+ case S_OK:
+ // nothing
+ break;
+ case E_OUTOFMEMORY:
+ iid = IID();
+ mpt::throw_out_of_memory();
+ break;
+ case E_INVALIDARG:
+ iid = IID();
+ break;
+ default:
+ iid = IID();
+ throw std::logic_error("IIDFromString() failed.");
+ break;
+ }
+ return iid;
+}
+
+inline mpt::winstring IIDToString(IID iid) {
+ std::wstring str;
+ LPOLESTR tmp = nullptr;
+ switch (::StringFromIID(iid, &tmp)) {
+ case S_OK:
+ break;
+ case E_OUTOFMEMORY:
+ if (tmp) {
+ ::CoTaskMemFree(tmp);
+ tmp = nullptr;
+ }
+ mpt::throw_out_of_memory();
+ break;
+ default:
+ if (tmp) {
+ ::CoTaskMemFree(tmp);
+ tmp = nullptr;
+ }
+ throw std::logic_error("StringFromIID() failed.");
+ break;
+ }
+ if (!tmp) {
+ throw std::logic_error("StringFromIID() failed.");
+ }
+ try {
+ str = tmp;
+ } catch (mpt::out_of_memory e) {
+ ::CoTaskMemFree(tmp);
+ tmp = nullptr;
+ mpt::rethrow_out_of_memory(e);
+ }
+ return mpt::transcode<mpt::winstring>(str);
+}
+
+
+// General GUID<->string conversion.
+// The string must/will be in standard GUID format: {4F9A455D-E7EF-4367-B2F0-0C83A38A5C72}
+
+inline GUID StringToGUID(const mpt::winstring & str) {
+ return StringToIID(str);
+}
+
+inline mpt::winstring GUIDToString(GUID guid) {
+ std::vector<OLECHAR> tmp(256);
+ if (::StringFromGUID2(guid, tmp.data(), static_cast<int>(tmp.size())) <= 0) {
+ throw std::logic_error("StringFromGUID2() failed.");
+ }
+ return mpt::transcode<mpt::winstring>(tmp.data());
+}
+
+
+// Create a COM GUID
+
+inline GUID CreateGUID() {
+ GUID guid = GUID();
+ switch (::CoCreateGuid(&guid)) {
+ case S_OK:
+ // nothing
+ break;
+ default:
+ guid = GUID();
+ throw std::runtime_error("CoCreateGuid() failed.");
+ }
+ return guid;
+}
+
+
+// Checks the UUID against the NULL UUID. Returns false if it is NULL, true otherwise.
+
+inline bool IsValid(::UUID uuid) {
+ return false
+ || uuid.Data1 != 0
+ || uuid.Data2 != 0
+ || uuid.Data3 != 0
+ || uuid.Data4[0] != 0
+ || uuid.Data4[1] != 0
+ || uuid.Data4[2] != 0
+ || uuid.Data4[3] != 0
+ || uuid.Data4[4] != 0
+ || uuid.Data4[5] != 0
+ || uuid.Data4[6] != 0
+ || uuid.Data4[7] != 0;
+}
+
+
+
+#endif // MPT_OS_WINDOWS
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_UUID_GUID_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/uuid/tests/tests_uuid.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/uuid/tests/tests_uuid.hpp
new file mode 100644
index 00000000..7d1f0007
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/uuid/tests/tests_uuid.hpp
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_TESTS_UUID_HPP
+#define MPT_BASE_TESTS_UUID_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/random/default_engines.hpp"
+#include "mpt/random/device.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+#include "mpt/uuid/guid.hpp"
+#include "mpt/uuid/uuid.hpp"
+
+#include <cstddef>
+#include <cstring>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace uuid {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/uuid")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ using namespace mpt::uuid_literals;
+
+ MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull).ToUString(), MPT_USTRING("2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32"));
+#if MPT_OS_WINDOWS
+ constexpr mpt::UUID uuid_tmp = "2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32"_uuid;
+ MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), uuid_tmp);
+ MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), mpt::UUID(mpt::StringToGUID(TEXT("{2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32}"))));
+ MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), mpt::UUID(mpt::StringToCLSID(TEXT("{2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32}"))));
+ MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0x8899AABBCCDDEEFFull), mpt::UUID(mpt::StringToGUID(TEXT("{00112233-4455-6677-8899-AABBCCDDEEFF}"))));
+ MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0xC899AABBCCDDEEFFull), mpt::UUID(mpt::StringToGUID(TEXT("{00112233-4455-6677-C899-AABBCCDDEEFF}"))));
+ MPT_TEST_EXPECT_EQUAL(mpt::GUIDToString(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0x8899AABBCCDDEEFFull)), TEXT("{00112233-4455-6677-8899-AABBCCDDEEFF}"));
+ MPT_TEST_EXPECT_EQUAL(mpt::GUIDToString(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0xC899AABBCCDDEEFFull)), TEXT("{00112233-4455-6677-C899-AABBCCDDEEFF}"));
+#endif // MPT_OS_WINDOWS
+
+ mpt::sane_random_device rd;
+ mpt::good_engine prng = mpt::make_prng<mpt::good_engine>(rd);
+
+#if MPT_OS_WINDOWS
+ MPT_TEST_EXPECT_EQUAL(mpt::IsValid(mpt::CreateGUID()), true);
+ {
+ mpt::UUID uuid = mpt::UUID::Generate(prng);
+ MPT_TEST_EXPECT_EQUAL(uuid, mpt::UUID::FromString(mpt::UUID(uuid).ToUString()));
+ MPT_TEST_EXPECT_EQUAL(uuid, mpt::UUID(mpt::StringToGUID(mpt::GUIDToString(uuid))));
+ MPT_TEST_EXPECT_EQUAL(uuid, mpt::UUID(mpt::StringToIID(mpt::IIDToString(uuid))));
+ MPT_TEST_EXPECT_EQUAL(uuid, mpt::UUID(mpt::StringToCLSID(mpt::CLSIDToString(uuid))));
+ }
+ {
+ GUID guid = mpt::UUID::Generate(prng);
+ MPT_TEST_EXPECT_EQUAL(IsEqualGUID(guid, static_cast<GUID>(mpt::UUID::FromString(mpt::UUID(guid).ToUString()))), TRUE);
+ MPT_TEST_EXPECT_EQUAL(IsEqualGUID(guid, mpt::StringToGUID(mpt::GUIDToString(guid))), TRUE);
+ MPT_TEST_EXPECT_EQUAL(IsEqualGUID(guid, mpt::StringToIID(mpt::IIDToString(guid))), TRUE);
+ MPT_TEST_EXPECT_EQUAL(IsEqualGUID(guid, mpt::StringToCLSID(mpt::CLSIDToString(guid))), TRUE);
+ }
+#endif // MPT_OS_WINDOWS
+ MPT_TEST_EXPECT_EQUAL(mpt::UUID::Generate(prng).IsValid(), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::UUID::GenerateLocalUseOnly(prng).IsValid(), true);
+ MPT_TEST_EXPECT_EQUAL(mpt::UUID::Generate(prng) != mpt::UUID::Generate(prng), true);
+ mpt::UUID a = mpt::UUID::Generate(prng);
+ MPT_TEST_EXPECT_EQUAL(a, mpt::UUID::FromString(a.ToUString()));
+ std::byte uuiddata[16]{};
+ for (std::size_t i = 0; i < 16; ++i) {
+ uuiddata[i] = mpt::byte_cast<std::byte>(static_cast<uint8>(i));
+ }
+ static_assert(sizeof(mpt::UUID) == 16);
+ mpt::UUIDbin uuid2;
+ std::memcpy(&uuid2, uuiddata, 16);
+ MPT_TEST_EXPECT_EQUAL(mpt::UUID(uuid2).ToUString(), MPT_USTRING("00010203-0405-0607-0809-0a0b0c0d0e0f"));
+
+ constexpr mpt::UUID uuid3 = "2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32"_uuid;
+ MPT_TEST_EXPECT_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), uuid3);
+}
+
+} // namespace uuid
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_TESTS_UUID_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/uuid/uuid.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/uuid/uuid.hpp
new file mode 100644
index 00000000..3e56c06d
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/uuid/uuid.hpp
@@ -0,0 +1,419 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_UUID_UUID_HPP
+#define MPT_UUID_UUID_HPP
+
+
+
+#include "mpt/base/constexpr_throw.hpp"
+#include "mpt/base/macros.hpp"
+#include "mpt/base/integer.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/endian/integer.hpp"
+#include "mpt/format/default_formatter.hpp"
+#include "mpt/format/simple.hpp"
+#include "mpt/parse/parse.hpp"
+#include "mpt/random/random.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/string/utility.hpp"
+
+#if MPT_OS_WINDOWS
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600)
+#include <guiddef.h>
+#endif // _WIN32_WINNT
+#include <objbase.h>
+#include <rpc.h>
+#endif // MPT_OS_WINDOWS
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+// Microsoft on-disk layout
+struct GUIDms {
+ uint32le Data1;
+ uint16le Data2;
+ uint16le Data3;
+ uint64be Data4; // yes, big endian here
+};
+constexpr bool declare_binary_safe(const GUIDms &) {
+ return true;
+}
+static_assert(mpt::check_binary_size<GUIDms>(16));
+
+// RFC binary format
+struct UUIDbin {
+ uint32be Data1;
+ uint16be Data2;
+ uint16be Data3;
+ uint64be Data4;
+};
+constexpr bool declare_binary_safe(const UUIDbin &) {
+ return true;
+}
+static_assert(mpt::check_binary_size<UUIDbin>(16));
+
+
+
+struct UUID {
+private:
+ uint32 Data1;
+ uint16 Data2;
+ uint16 Data3;
+ uint64 Data4;
+
+public:
+ MPT_CONSTEXPRINLINE uint32 GetData1() const noexcept {
+ return Data1;
+ }
+ MPT_CONSTEXPRINLINE uint16 GetData2() const noexcept {
+ return Data2;
+ }
+ MPT_CONSTEXPRINLINE uint16 GetData3() const noexcept {
+ return Data3;
+ }
+ MPT_CONSTEXPRINLINE uint64 GetData4() const noexcept {
+ return Data4;
+ }
+
+public:
+ MPT_CONSTEXPRINLINE uint64 GetData64_1() const noexcept {
+ return (static_cast<uint64>(Data1) << 32) | (static_cast<uint64>(Data2) << 16) | (static_cast<uint64>(Data3) << 0);
+ }
+ MPT_CONSTEXPRINLINE uint64 GetData64_2() const noexcept {
+ return Data4;
+ }
+
+public:
+ // xxxxxxxx-xxxx-Mmxx-Nnxx-xxxxxxxxxxxx
+ // <--32-->-<16>-<16>-<-------64------>
+ MPT_CONSTEXPRINLINE bool IsNil() const noexcept {
+ return (Data1 == 0) && (Data2 == 0) && (Data3 == 0) && (Data4 == 0);
+ }
+ MPT_CONSTEXPRINLINE bool IsValid() const noexcept {
+ return (Data1 != 0) || (Data2 != 0) || (Data3 != 0) || (Data4 != 0);
+ }
+ MPT_CONSTEXPRINLINE uint8 Variant() const noexcept {
+ return Nn() >> 4u;
+ }
+ MPT_CONSTEXPRINLINE uint8 Version() const noexcept {
+ return Mm() >> 4u;
+ }
+ MPT_CONSTEXPRINLINE bool IsRFC4122() const noexcept {
+ return (Variant() & 0xcu) == 0x8u;
+ }
+
+private:
+ MPT_CONSTEXPRINLINE uint8 Mm() const noexcept {
+ return static_cast<uint8>((Data3 >> 8) & 0xffu);
+ }
+ MPT_CONSTEXPRINLINE uint8 Nn() const noexcept {
+ return static_cast<uint8>((Data4 >> 56) & 0xffu);
+ }
+ void MakeRFC4122(uint8 version) noexcept {
+ // variant
+ uint8 Nn = static_cast<uint8>((Data4 >> 56) & 0xffu);
+ Data4 &= 0x00ffffffffffffffull;
+ Nn &= ~(0xc0u);
+ Nn |= 0x80u;
+ Data4 |= static_cast<uint64>(Nn) << 56;
+ // version
+ version &= 0x0fu;
+ uint8 Mm = static_cast<uint8>((Data3 >> 8) & 0xffu);
+ Data3 &= 0x00ffu;
+ Mm &= ~(0xf0u);
+ Mm |= (version << 4u);
+ Data3 |= static_cast<uint16>(Mm) << 8;
+ }
+#if MPT_OS_WINDOWS
+private:
+ static mpt::UUID UUIDFromWin32(::UUID uuid) {
+ return mpt::UUID(uuid.Data1, uuid.Data2, uuid.Data3, (static_cast<uint64>(0) | (static_cast<uint64>(uuid.Data4[0]) << 56) | (static_cast<uint64>(uuid.Data4[1]) << 48) | (static_cast<uint64>(uuid.Data4[2]) << 40) | (static_cast<uint64>(uuid.Data4[3]) << 32) | (static_cast<uint64>(uuid.Data4[4]) << 24) | (static_cast<uint64>(uuid.Data4[5]) << 16) | (static_cast<uint64>(uuid.Data4[6]) << 8) | (static_cast<uint64>(uuid.Data4[7]) << 0)));
+ }
+ static ::UUID UUIDToWin32(mpt::UUID uuid) {
+ ::UUID result = ::UUID();
+ result.Data1 = uuid.GetData1();
+ result.Data2 = uuid.GetData2();
+ result.Data3 = uuid.GetData3();
+ result.Data4[0] = static_cast<uint8>(uuid.GetData4() >> 56);
+ result.Data4[1] = static_cast<uint8>(uuid.GetData4() >> 48);
+ result.Data4[2] = static_cast<uint8>(uuid.GetData4() >> 40);
+ result.Data4[3] = static_cast<uint8>(uuid.GetData4() >> 32);
+ result.Data4[4] = static_cast<uint8>(uuid.GetData4() >> 24);
+ result.Data4[5] = static_cast<uint8>(uuid.GetData4() >> 16);
+ result.Data4[6] = static_cast<uint8>(uuid.GetData4() >> 8);
+ result.Data4[7] = static_cast<uint8>(uuid.GetData4() >> 0);
+ return result;
+ }
+
+public:
+ explicit UUID(::UUID uuid) {
+ *this = UUIDFromWin32(uuid);
+ }
+ operator ::UUID() const {
+ return UUIDToWin32(*this);
+ }
+#endif // MPT_OS_WINDOWS
+private:
+ static MPT_CONSTEXPRINLINE uint8 NibbleFromChar(char x) {
+ return ('0' <= x && x <= '9') ? static_cast<uint8>(x - '0' + 0) : ('a' <= x && x <= 'z') ? static_cast<uint8>(x - 'a' + 10)
+ : ('A' <= x && x <= 'Z') ? static_cast<uint8>(x - 'A' + 10)
+ : mpt::constexpr_throw<uint8>(std::domain_error(""));
+ }
+ static MPT_CONSTEXPRINLINE uint8 ByteFromHex(char x, char y) {
+ return static_cast<uint8>(uint8(0) | (NibbleFromChar(x) << 4) | (NibbleFromChar(y) << 0));
+ }
+ static MPT_CONSTEXPRINLINE uint16 ParseHex16(const char * str) {
+ return static_cast<uint16>(uint16(0) | (static_cast<uint16>(ByteFromHex(str[0], str[1])) << 8) | (static_cast<uint16>(ByteFromHex(str[2], str[3])) << 0));
+ }
+ static MPT_CONSTEXPRINLINE uint32 ParseHex32(const char * str) {
+ return static_cast<uint32>(uint32(0) | (static_cast<uint32>(ByteFromHex(str[0], str[1])) << 24) | (static_cast<uint32>(ByteFromHex(str[2], str[3])) << 16) | (static_cast<uint32>(ByteFromHex(str[4], str[5])) << 8) | (static_cast<uint32>(ByteFromHex(str[6], str[7])) << 0));
+ }
+
+public:
+ static MPT_CONSTEXPRINLINE UUID ParseLiteral(const char * str, std::size_t len) {
+ return (len == 36 && str[8] == '-' && str[13] == '-' && str[18] == '-' && str[23] == '-') ? mpt::UUID(
+ ParseHex32(str + 0),
+ ParseHex16(str + 9),
+ ParseHex16(str + 14),
+ uint64(0)
+ | (static_cast<uint64>(ParseHex16(str + 19)) << 48)
+ | (static_cast<uint64>(ParseHex16(str + 24)) << 32)
+ | (static_cast<uint64>(ParseHex32(str + 28)) << 0))
+ : mpt::constexpr_throw<mpt::UUID>(std::domain_error(""));
+ }
+
+public:
+ MPT_CONSTEXPRINLINE UUID() noexcept
+ : Data1(0)
+ , Data2(0)
+ , Data3(0)
+ , Data4(0) {
+ return;
+ }
+ MPT_CONSTEXPRINLINE explicit UUID(uint32 Data1, uint16 Data2, uint16 Data3, uint64 Data4) noexcept
+ : Data1(Data1)
+ , Data2(Data2)
+ , Data3(Data3)
+ , Data4(Data4) {
+ return;
+ }
+ explicit UUID(UUIDbin uuid) {
+ Data1 = uuid.Data1.get();
+ Data2 = uuid.Data2.get();
+ Data3 = uuid.Data3.get();
+ Data4 = uuid.Data4.get();
+ }
+ explicit UUID(GUIDms guid) {
+ Data1 = guid.Data1.get();
+ Data2 = guid.Data2.get();
+ Data3 = guid.Data3.get();
+ Data4 = guid.Data4.get();
+ }
+ operator UUIDbin() const {
+ UUIDbin result{};
+ result.Data1 = GetData1();
+ result.Data2 = GetData2();
+ result.Data3 = GetData3();
+ result.Data4 = GetData4();
+ return result;
+ }
+ operator GUIDms() const {
+ GUIDms result{};
+ result.Data1 = GetData1();
+ result.Data2 = GetData2();
+ result.Data3 = GetData3();
+ result.Data4 = GetData4();
+ return result;
+ }
+
+public:
+ // Create a UUID
+ template <typename Trng>
+ static UUID Generate(Trng & rng) {
+#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
+#if (_WIN32_WINNT >= 0x0602)
+ ::GUID guid = ::GUID();
+ HRESULT result = CoCreateGuid(&guid);
+ if (result != S_OK) {
+ return mpt::UUID::RFC4122Random(rng);
+ }
+ return mpt::UUID::UUIDFromWin32(guid);
+#else
+ return mpt::UUID::RFC4122Random(rng);
+#endif
+#elif MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT
+ ::UUID uuid = ::UUID();
+ RPC_STATUS status = ::UuidCreate(&uuid);
+ if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) {
+ return mpt::UUID::RFC4122Random(rng);
+ }
+ status = RPC_S_OK;
+ if (UuidIsNil(&uuid, &status) != FALSE) {
+ return mpt::UUID::RFC4122Random(rng);
+ }
+ if (status != RPC_S_OK) {
+ return mpt::UUID::RFC4122Random(rng);
+ }
+ return mpt::UUID::UUIDFromWin32(uuid);
+#else
+ return RFC4122Random(rng);
+#endif
+ }
+ // Create a UUID that contains local, traceable information.
+ // Safe for local use. May be faster.
+ template <typename Trng>
+ static UUID GenerateLocalUseOnly(Trng & rng) {
+#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
+#if (_WIN32_WINNT >= 0x0602)
+ ::GUID guid = ::GUID();
+ HRESULT result = CoCreateGuid(&guid);
+ if (result != S_OK) {
+ return mpt::UUID::RFC4122Random(rng);
+ }
+ return mpt::UUID::UUIDFromWin32(guid);
+#else
+ return mpt::UUID::RFC4122Random(rng);
+#endif
+#elif MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT
+#if _WIN32_WINNT >= 0x0501
+ // Available since Win2000, but we check for WinXP in order to not use this
+ // function in Win32old builds. It is not available on some non-fully
+ // patched Win98SE installs in the wild.
+ ::UUID uuid = ::UUID();
+ RPC_STATUS status = ::UuidCreateSequential(&uuid);
+ if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) {
+ return Generate(rng);
+ }
+ status = RPC_S_OK;
+ if (UuidIsNil(&uuid, &status) != FALSE) {
+ return mpt::UUID::RFC4122Random(rng);
+ }
+ if (status != RPC_S_OK) {
+ return mpt::UUID::RFC4122Random(rng);
+ }
+ return mpt::UUID::UUIDFromWin32(uuid);
+#else
+ // Fallback to ::UuidCreate is safe as ::UuidCreateSequential is only a
+ // tiny performance optimization.
+ return Generate(rng);
+#endif
+#else
+ return RFC4122Random(rng);
+#endif
+ }
+ // Create a RFC4122 Random UUID.
+ template <typename Trng>
+ static UUID RFC4122Random(Trng & prng) {
+ UUID result;
+ result.Data1 = mpt::random<uint32>(prng);
+ result.Data2 = mpt::random<uint16>(prng);
+ result.Data3 = mpt::random<uint16>(prng);
+ result.Data4 = mpt::random<uint64>(prng);
+ result.MakeRFC4122(4);
+ return result;
+ }
+ friend UUID UUIDRFC4122NamespaceV3(const UUID & ns, const mpt::ustring & name);
+ friend UUID UUIDRFC4122NamespaceV5(const UUID & ns, const mpt::ustring & name);
+
+public:
+ // General UUID<->string conversion.
+ // The string must/will be in standard UUID format: 4f9a455d-e7ef-4367-b2f0-0c83a38a5c72
+ static UUID FromString(const mpt::ustring & str) {
+ std::vector<mpt::ustring> segments = mpt::split<mpt::ustring>(str, MPT_ULITERAL("-"));
+ if (segments.size() != 5) {
+ return UUID();
+ }
+ if (segments[0].length() != 8) {
+ return UUID();
+ }
+ if (segments[1].length() != 4) {
+ return UUID();
+ }
+ if (segments[2].length() != 4) {
+ return UUID();
+ }
+ if (segments[3].length() != 4) {
+ return UUID();
+ }
+ if (segments[4].length() != 12) {
+ return UUID();
+ }
+ UUID result;
+ result.Data1 = mpt::ConvertHexStringTo<uint32>(segments[0]);
+ result.Data2 = mpt::ConvertHexStringTo<uint16>(segments[1]);
+ result.Data3 = mpt::ConvertHexStringTo<uint16>(segments[2]);
+ result.Data4 = mpt::ConvertHexStringTo<uint64>(segments[3] + segments[4]);
+ return result;
+ }
+ std::string ToAString() const {
+ return std::string()
+ + mpt::format<std::string>::hex0<8>(GetData1())
+ + std::string("-")
+ + mpt::format<std::string>::hex0<4>(GetData2())
+ + std::string("-")
+ + mpt::format<std::string>::hex0<4>(GetData3())
+ + std::string("-")
+ + mpt::format<std::string>::hex0<4>(static_cast<uint16>(GetData4() >> 48))
+ + std::string("-")
+ + mpt::format<std::string>::hex0<4>(static_cast<uint16>(GetData4() >> 32))
+ + mpt::format<std::string>::hex0<8>(static_cast<uint32>(GetData4() >> 0));
+ }
+ mpt::ustring ToUString() const {
+ return mpt::ustring()
+ + mpt::format<mpt::ustring>::hex0<8>(GetData1())
+ + MPT_USTRING("-")
+ + mpt::format<mpt::ustring>::hex0<4>(GetData2())
+ + MPT_USTRING("-")
+ + mpt::format<mpt::ustring>::hex0<4>(GetData3())
+ + MPT_USTRING("-")
+ + mpt::format<mpt::ustring>::hex0<4>(static_cast<uint16>(GetData4() >> 48))
+ + MPT_USTRING("-")
+ + mpt::format<mpt::ustring>::hex0<4>(static_cast<uint16>(GetData4() >> 32))
+ + mpt::format<mpt::ustring>::hex0<8>(static_cast<uint32>(GetData4() >> 0));
+ }
+};
+
+MPT_CONSTEXPRINLINE bool operator==(const mpt::UUID & a, const mpt::UUID & b) noexcept {
+ return (a.GetData1() == b.GetData1()) && (a.GetData2() == b.GetData2()) && (a.GetData3() == b.GetData3()) && (a.GetData4() == b.GetData4());
+}
+
+MPT_CONSTEXPRINLINE bool operator!=(const mpt::UUID & a, const mpt::UUID & b) noexcept {
+ return (a.GetData1() != b.GetData1()) || (a.GetData2() != b.GetData2()) || (a.GetData3() != b.GetData3()) || (a.GetData4() != b.GetData4());
+}
+
+
+namespace uuid_literals {
+
+MPT_CONSTEXPRINLINE mpt::UUID operator"" _uuid(const char * str, std::size_t len) {
+ return mpt::UUID::ParseLiteral(str, len);
+}
+
+} // namespace uuid_literals
+
+
+template <typename Tstring>
+inline Tstring uuid_to_string(mpt::UUID uuid) {
+ return mpt::transcode<Tstring>(uuid.ToUString());
+}
+
+template <>
+inline std::string uuid_to_string<std::string>(mpt::UUID uuid) {
+ return uuid.ToAString();
+}
+
+template <typename Tstring, typename T, std::enable_if_t<std::is_same<T, mpt::UUID>::value, bool> = true>
+inline Tstring format_value_default(const T & x) {
+ return mpt::transcode<Tstring>(mpt::uuid_to_string<typename mpt::select_format_string_type<Tstring>::type>(x));
+}
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_UUID_UUID_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/uuid_namespace/tests/tests_uuid_namespace.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/uuid_namespace/tests/tests_uuid_namespace.hpp
new file mode 100644
index 00000000..9c1812ff
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/uuid_namespace/tests/tests_uuid_namespace.hpp
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_BASE_TESTS_UUID_NAMESPACE_HPP
+#define MPT_BASE_TESTS_UUID_NAMESPACE_HPP
+
+
+
+#include "mpt/base/detect_compiler.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/test/test.hpp"
+#include "mpt/test/test_macros.hpp"
+#include "mpt/uuid/uuid.hpp"
+#include "mpt/uuid_namespace/uuid_namespace.hpp"
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+namespace tests {
+namespace uuid_namespace {
+
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+MPT_TEST_GROUP_INLINE("mpt/uuid_namespace")
+#if MPT_COMPILER_CLANG
+#pragma clang diagnostic pop
+#endif
+{
+ using namespace mpt::uuid_literals;
+ constexpr mpt::UUID uuid_ns_dns = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"_uuid;
+ constexpr mpt::UUID expected = "74738ff5-5367-5958-9aee-98fffdcd1876"_uuid;
+ mpt::UUID gotten = mpt::UUIDRFC4122NamespaceV5(uuid_ns_dns, MPT_USTRING("www.example.org"));
+ MPT_TEST_EXPECT_EQUAL(gotten, expected);
+}
+
+} // namespace uuid_namespace
+} // namespace tests
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_BASE_TESTS_UUID_NAMESPACE_HPP
diff --git a/Src/external_dependencies/openmpt-trunk/src/mpt/uuid_namespace/uuid_namespace.hpp b/Src/external_dependencies/openmpt-trunk/src/mpt/uuid_namespace/uuid_namespace.hpp
new file mode 100644
index 00000000..9114e9f9
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/src/mpt/uuid_namespace/uuid_namespace.hpp
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
+
+#ifndef MPT_UUID_NAMESPACE_UUID_NAMESPACE_HPP
+#define MPT_UUID_NAMESPACE_UUID_NAMESPACE_HPP
+
+
+
+#include "mpt/base/detect.hpp"
+#include "mpt/base/memory.hpp"
+#include "mpt/base/namespace.hpp"
+#include "mpt/crypto/hash.hpp"
+#include "mpt/string/types.hpp"
+#include "mpt/string_transcode/transcode.hpp"
+#include "mpt/uuid/uuid.hpp"
+
+#include <algorithm>
+#include <array>
+#include <string>
+#include <vector>
+
+
+
+namespace mpt {
+inline namespace MPT_INLINE_NS {
+
+
+
+#if MPT_OS_WINDOWS
+
+// Create a RFC4122 Version 3 namespace UUID
+inline mpt::UUID UUIDRFC4122NamespaceV3(const mpt::UUID & ns, const mpt::ustring & name) {
+ mpt::UUIDbin binns = ns;
+ std::vector<std::byte> buf;
+ buf.resize(sizeof(mpt::UUIDbin));
+ std::copy(mpt::as_raw_memory(binns).data(), mpt::as_raw_memory(binns).data() + sizeof(mpt::UUIDbin), buf.data());
+ std::string utf8name = mpt::transcode<std::string>(mpt::common_encoding::utf8, name);
+ buf.resize(buf.size() + utf8name.length());
+ std::transform(utf8name.begin(), utf8name.end(), buf.data() + sizeof(mpt::UUIDbin), [](char c) { return mpt::byte_cast<std::byte>(c); });
+ std::array<std::byte, 16> hash = mpt::crypto::hash::MD5().process(mpt::as_span(buf)).result();
+ mpt::UUIDbin uuidbin;
+ std::copy(hash.begin(), hash.begin() + 16, mpt::as_raw_memory(uuidbin).data());
+ mpt::UUID uuid{uuidbin};
+ uuid.MakeRFC4122(3);
+ return uuid;
+}
+
+// Create a RFC4122 Version 5 namespace UUID
+inline mpt::UUID UUIDRFC4122NamespaceV5(const mpt::UUID & ns, const mpt::ustring & name) {
+ mpt::UUIDbin binns = ns;
+ std::vector<std::byte> buf;
+ buf.resize(sizeof(mpt::UUIDbin));
+ std::copy(mpt::as_raw_memory(binns).data(), mpt::as_raw_memory(binns).data() + sizeof(mpt::UUIDbin), buf.data());
+ std::string utf8name = mpt::transcode<std::string>(mpt::common_encoding::utf8, name);
+ buf.resize(buf.size() + utf8name.length());
+ std::transform(utf8name.begin(), utf8name.end(), buf.data() + sizeof(mpt::UUIDbin), [](char c) { return mpt::byte_cast<std::byte>(c); });
+ std::array<std::byte, 20> hash = mpt::crypto::hash::SHA1().process(mpt::as_span(buf)).result();
+ UUIDbin uuidbin;
+ std::copy(hash.begin(), hash.begin() + 16, mpt::as_raw_memory(uuidbin).data());
+ mpt::UUID uuid{uuidbin};
+ uuid.MakeRFC4122(5);
+ return uuid;
+}
+
+#endif // MPT_OS_WINDOWS
+
+
+
+} // namespace MPT_INLINE_NS
+} // namespace mpt
+
+
+
+#endif // MPT_UUID_NAMESPACE_UUID_NAMESPACE_HPP