diff --git a/.clang-format b/.clang-format index 964be0bd0..7e805ed6e 100644 --- a/.clang-format +++ b/.clang-format @@ -1,79 +1,213 @@ ---- -BasedOnStyle: Microsoft -AccessModifierOffset: '-2' +--- +Language: Cpp +AccessModifierOffset: -2 AlignAfterOpenBracket: Align -AlignConsecutiveMacros: 'true' -AlignConsecutiveAssignments: 'true' -AlignEscapedNewlines: 'Left' -AlignTrailingComments: 'true' -AllowAllArgumentsOnNextLine: 'true' -AllowAllConstructorInitializersOnNextLine: 'false' -AllowShortLambdasOnASingleLine: 'All' -AlwaysBreakTemplateDeclarations: 'Yes' -BinPackArguments: 'true' -BinPackParameters: 'true' +AlignArrayOfStructures: Right +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true BraceWrapping: - AfterCaseLabel: 'true' - AfterClass: 'true' - AfterControlStatement: 'true' - AfterEnum: 'true' - AfterFunction: 'true' - AfterNamespace: 'true' - AfterObjCDeclaration: 'true' - AfterStruct: 'true' - AfterUnion: 'true' - AfterExternBlock: 'true' - BeforeCatch: 'true' - BeforeElse: 'true' - IndentBraces: 'false' - SplitEmptyFunction: 'false' - SplitEmptyRecord: 'true' - SplitEmptyNamespace: 'false' -BreakBeforeBraces: 'Custom' -BreakBeforeTernaryOperators: 'true' -IncludeBlocks: 'Preserve' + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: AfterColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: "^ IWYU pragma:" +QualifierAlignment: Leave +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +PackConstructorInitializers: CurrentLine +BasedOnStyle: "" +ConstructorInitializerAllOnOneLineOrOnePerLine: false +AllowAllConstructorInitializersOnNextLine: true +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - SortPriority: 0 - - Regex: '^(<|"(gtest|gmock|isl|json)/)' - Priority: 3 - SortPriority: 0 - - Regex: '.*' - Priority: 1 - SortPriority: 0 -IncludeIsMainRegex: '(Test)?$' -IncludeIsMainSourceRegex: '' -BreakConstructorInitializers: 'AfterColon' -BreakInheritanceList: 'BeforeColon' -ColumnLimit: '100' -CommentPragmas: '^ IWYU pragma:' -CompactNamespaces: 'false' -ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' -ContinuationIndentWidth: '4' -Cpp11BracedListStyle: 'false' -DerivePointerAlignment: 'false' -IndentCaseLabels: 'true' -IndentPPDirectives: 'BeforeHash' -IndentWidth: '4' -Language: 'Cpp' -NamespaceIndentation: 'All' -PointerAlignment: 'Left' -SpaceAfterCStyleCast: 'false' -SpaceAfterLogicalNot: 'false' -SpaceAfterTemplateKeyword: 'false' -SpaceBeforeAssignmentOperators: 'true' -SpaceBeforeCpp11BracedList: 'true' -SpaceBeforeCtorInitializerColon: 'true' -SpaceBeforeInheritanceColon: 'true' + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: ".*" + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: "(Test)?$" +IncludeIsMainSourceRegex: "" +IndentAccessModifiers: false +IndentCaseLabels: true +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: BeforeHash +IndentExternBlock: AfterExternBlock +IndentRequiresClause: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 1000 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +PPIndentWidth: -1 +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RequiresClausePosition: OwnLine +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements -SpaceBeforeRangeBasedForLoopColon: 'true' -SpaceInEmptyParentheses: 'false' -SpacesInAngles: 'false' -SpacesInCStyleCastParentheses: 'false' -SpacesInParentheses: 'false' -SpacesInSquareBrackets: 'false' +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both Standard: Auto -TabWidth: '4' -UseTab: 'Never' -... +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME diff --git a/.gitattributes b/.gitattributes index c6069925e..9bc210162 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,3 +2,4 @@ *.jpg binary *.png binary *.dksh binary +*.gsh binary diff --git a/.github/workflows/Nintendo 3DS.yml b/.github/workflows/Nintendo 3DS.yml new file mode 100644 index 000000000..9c65e42ab --- /dev/null +++ b/.github/workflows/Nintendo 3DS.yml @@ -0,0 +1,62 @@ +name: Nintendo 3DS + +on: + push: + branches: + - dev/3.0 + pull_request: + +jobs: + N3DS-Release: + runs-on: ubuntu-latest + + container: + image: docker://devkitpro/devkitarm + options: --cpus 2 + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Configure + run: /opt/devkitpro/portlibs/3ds/bin/arm-none-eabi-cmake -S . -B build + + - name: Build + run: make -C build + + - id: commit + uses: pr-mpt/actions-commit-hash@v1 + + - uses: actions/upload-artifact@v2 + with: + name: Nintendo 3DS-${{ steps.commit.outputs.short }} + path: | + build/*.elf + build/*.3dsx + + N3DS-Debug: + runs-on: ubuntu-latest + + container: + image: docker://devkitpro/devkitarm + options: --cpus 2 + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Configure + run: /opt/devkitpro/portlibs/3ds/bin/arm-none-eabi-cmake -DCMAKE_BUILD_TYPE=Debug -S . -B build + + - name: Build + run: make -C build + + - id: commit + uses: pr-mpt/actions-commit-hash@v1 + + - uses: actions/upload-artifact@v2 + with: + name: Nintendo 3DS (Debug)-${{ steps.commit.outputs.short }} + path: | + build/*.elf + build/*.3dsx diff --git a/.github/workflows/Nintendo Switch.yml b/.github/workflows/Nintendo Switch.yml new file mode 100644 index 000000000..4e0f3cbbf --- /dev/null +++ b/.github/workflows/Nintendo Switch.yml @@ -0,0 +1,62 @@ +name: Nintendo Switch + +on: + push: + branches: + - dev/3.0 + pull_request: + +jobs: + Switch-Release: + runs-on: ubuntu-latest + + container: + image: docker://devkitpro/devkita64 + options: --cpus 2 + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Configure + run: /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake -S . -B build + + - name: Build + run: make -C build + + - id: commit + uses: pr-mpt/actions-commit-hash@v1 + + - uses: actions/upload-artifact@v2 + with: + name: Nintendo Switch-${{ steps.commit.outputs.short }} + path: | + build/*.elf + build/*.nro + + Switch-Debug: + runs-on: ubuntu-latest + + container: + image: docker://devkitpro/devkita64 + options: --cpus 2 + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Configure + run: /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake -DCMAKE_BUILD_TYPE=Debug -S . -B build + + - name: Build + run: make -C build + + - id: commit + uses: pr-mpt/actions-commit-hash@v1 + + - uses: actions/upload-artifact@v2 + with: + name: Nintendo Switch (Debug)-${{ steps.commit.outputs.short }} + path: | + build/*.elf + build/*.nro diff --git a/.github/workflows/Nintendo Wii U.yml b/.github/workflows/Nintendo Wii U.yml new file mode 100644 index 000000000..eb628b571 --- /dev/null +++ b/.github/workflows/Nintendo Wii U.yml @@ -0,0 +1,62 @@ +name: Nintendo Wii U + +on: + push: + branches: + - dev/3.0 + pull_request: + +jobs: + WiiU-Release: + runs-on: ubuntu-latest + + container: + image: docker://devkitpro/devkitppc + options: --cpus 2 + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Configure + run: /opt/devkitpro/portlibs/wiiu/bin/powerpc-eabi-cmake -S . -B build + + - name: Build + run: make -C build + + - id: commit + uses: pr-mpt/actions-commit-hash@v1 + + - uses: actions/upload-artifact@v2 + with: + name: Nintendo Wii U-${{ steps.commit.outputs.short }} + path: | + build/*.elf + build/*.wuhb + + WiiU-Debug: + runs-on: ubuntu-latest + + container: + image: docker://devkitpro/devkitppc + options: --cpus 2 + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Configure + run: /opt/devkitpro/portlibs/wiiu/bin/powerpc-eabi-cmake -DCMAKE_BUILD_TYPE=Debug -S . -B build + + - name: Build + run: make -C build + + - id: commit + uses: pr-mpt/actions-commit-hash@v1 + + - uses: actions/upload-artifact@v2 + with: + name: Nintendo Wii U (Debug)-${{ steps.commit.outputs.short }} + path: | + build/*.elf + build/*.wuhb diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 4663b450c..000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: LÖVE Potion - -on: - push: - branches: - - main - pull_request: - -jobs: - first: - name: 3DS - runs-on: ubuntu-latest - - container: - image: docker://devkitpro/devkitarm - options: --cpus 2 - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Build - run: make ctr - - - id: commit - uses: pr-mpt/actions-commit-hash@v1 - - - uses: actions/upload-artifact@v2 - with: - name: Nintendo 3DS-${{ steps.commit.outputs.short }} - path: | - platform/3ds/*.elf - platform/3ds/*.3dsx - - second: - name: Switch - runs-on: ubuntu-latest - - container: - image: docker://devkitpro/devkita64 - options: --cpus 2 - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Build - run: make hac - - - id: commit - uses: pr-mpt/actions-commit-hash@v1 - - - uses: actions/upload-artifact@v2 - with: - name: Nintendo Switch-${{ steps.commit.outputs.short }} - path: | - platform/switch/*.elf - platform/switch/*.nro diff --git a/.gitignore b/.gitignore index f6d23984b..2f9835837 100644 --- a/.gitignore +++ b/.gitignore @@ -2,15 +2,27 @@ build distribute .vscodecounter logs +settings.json distribute +launch.json + +todo.md *.log +*.love +test +*.code-workspace +*.txt +*.py + +*.exe -*.3dsx *.elf -*.t3x +*.3dsx *.smdh +*.t3x +*.shbin *.nro *.nso @@ -18,4 +30,8 @@ distribute *.pfs0 *.dksh +*.rpx +*.wuhb + *.zip +debug.py \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 4db4a4a28..35f1e56ff 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,89 +1,150 @@ { - "configurations": [ - { - "name": "3DS", - "includePath": [ - "${workspaceFolder}/include/**", - "${workspaceFolder}/platform/3ds/include/**", - "${workspaceFolder}/libraries/**", - "${env:DEVKITPRO}/devkitARM/arm-none-eabi/include/**", - "${env:DEVKITPRO}/devkitARM/lib/gcc/arm-none-eabi/**", - "${env:DEVKITPRO}/libctru/include/**", - "${env:DEVKITPRO}/portlibs/3ds/include/**" - ], - "defines": ["__3DS__", "__CONSOLE__=\"3DS\""], - "compilerPath": "${env:DEVKITPRO}/devkitARM/bin/arm-none-eabi-gcc", - "cStandard": "c11", - "cppStandard": "c++20", - "intelliSenseMode": "linux-gcc-arm", - "compilerArgs": [], - "customConfigurationVariables": { "console": "ctr" } + "env": { + "defaultIncludePath": [ + "${workspaceFolder}/include/**", + "${workspaceFolder}/libraries/**" + ], + "defaultCStandard": "c11", + "defaultCppStandard": "c++20", + "defaultDefines": [ + "__APP_VERSION__=\"3.0.0\"", + "__LOVE_VERSION__=\"12.0\"", + "_USE_MATH_DEFINES" + ], + "ctrIncludePaths": [ + "${workspaceFolder}/platform/ctr/include/**", + "${workspaceFolder}/platform/ctr/libraries/**", + "C:/msys64/opt/devkitpro/libctru/include/**", + "C:/msys64/opt/devkitpro/portlibs/3ds/include/**" + ], + "ctrDefines": [ + "__3DS__", + "__OS__=\"Horizon\"", + "__CONSOLE__=\"3DS\"" + ], + "ctrCompilerPath": "C:/msys64/opt/devkitpro/devkitARM/bin/arm-none-eabi-g++", + "hacIncludePaths": [ + "${workspaceFolder}/platform/hac/include/**", + "C:/msys64/opt/devkitpro/libnx/include/**", + "C:/msys64/opt/devkitpro/portlibs/switch/include/**" + ], + "hacDefines": [ + "__SWITCH__", + "__BSD_VISIBLE", + "__CONSOLE__=\"Switch\"", + "__OS__=\"Horizon\"" + ], + "hacCompilerPath": "C:/msys64/opt/devkitpro/devkitA64/bin/aarch64-none-elf-g++", + "cafeIncludePaths": [ + "${workspaceFolder}/platform/cafe/include/**", + "C:/msys64/opt/devkitpro/wut/include/**", + "C:/msys64/opt/devkitpro/portlibs/wiiu/include/**", + "C:/msys64/opt/devkitpro/portlibs/ppc/include/**" + ], + "cafeDefines": [ + "__WIIU__", + "__OS__=\"Cafe\"", + "__CONSOLE__=\"Wii U\"" + ], + "cafeCompilerPath": "C:/msys64/opt/devkitpro/devkitPPC/bin/powerpc-eabi-g++" }, - - { - "name": "3DS {Debug}", - "includePath": [ - "${workspaceFolder}/include/**", - "${workspaceFolder}/platform/3ds/include/**", - "${workspaceFolder}/libraries/**", - "${env:DEVKITPRO}/devkitARM/arm-none-eabi/include/**", - "${env:DEVKITPRO}/devkitARM/lib/gcc/arm-none-eabi/**", - "${env:DEVKITPRO}/libctru/include/**", - "${env:DEVKITPRO}/portlibs/3ds/include/**" - ], - "defines": ["__3DS__", "__DEBUG__", "__CONSOLE__=\"3DS\""], - "compilerPath": "${env:DEVKITPRO}/devkitARM/bin/arm-none-eabi-gcc", - "cStandard": "c11", - "cppStandard": "c++20", - "intelliSenseMode": "linux-gcc-arm", - "compilerArgs": [], - "customConfigurationVariables": { "console": "ctr-debug" } - }, - - { - "name": "Switch", - "includePath": [ - "${workspaceFolder}/include/**", - "${workspaceFolder}/platform/switch/include/**", - "${workspaceFolder}/libraries/**", - "${env:DEVKITPRO}/devkitA64/aarch64-none-elf/include/**", - "${env:DEVKITPRO}/devkitA64/lib/gcc/aarch64-none-elf/**", - "${env:DEVKITPRO}/libnx/include/**", - "${env:DEVKITPRO}/portlibs/switch/include/**" - ], - "defines": ["__SWITCH__", "__BSD_VISIBLE", "__CONSOLE__=\"Switch\""], - "compilerPath": "${env:DEVKITPRO}/devkitA64/bin/aarch64-none-elf-g++", - "cStandard": "c11", - "cppStandard": "c++20", - "intelliSenseMode": "linux-gcc-arm64", - "compilerArgs": [], - "customConfigurationVariables": { "console": "hac" } - }, - - { - "name": "Switch {Debug}", - "includePath": [ - "${workspaceFolder}/include/**", - "${workspaceFolder}/platform/switch/include/**", - "${workspaceFolder}/libraries/**", - "${env:DEVKITPRO}/devkitA64/aarch64-none-elf/include/**", - "${env:DEVKITPRO}/devkitA64/lib/gcc/aarch64-none-elf/**", - "${env:DEVKITPRO}/libnx/include/**", - "${env:DEVKITPRO}/portlibs/switch/include/**" - ], - "defines": [ - "__SWITCH__", - "__BSD_VISIBLE", - "__DEBUG__", - "__CONSOLE__=\"Switch\"" - ], - "compilerPath": "${env:DEVKITPRO}/devkitA64/bin/aarch64-none-elf-gcc", - "cStandard": "c11", - "cppStandard": "c++20", - "intelliSenseMode": "linux-gcc-arm64", - "compilerArgs": [], - "customConfigurationVariables": { "console": "hac-debug" } - } - ], - "version": 4 -} + "configurations": [ + { + "name": "3DS", + "includePath": [ + "${env:defaultIncludePath}", + "${env:ctrIncludePaths}" + ], + "defines": [ + "${env:defaultDefines}", + "${env:ctrDefines}", + "__DEBUG__=0" + ], + "compilerPath": "${env:ctrCompilerPath}", + "cStandard": "c11", + "cppStandard": "${env:defaultCppStandard}", + "intelliSenseMode": "linux-gcc-arm" + }, + { + "name": "3DS {Debug}", + "includePath": [ + "${env:defaultIncludePath}", + "${env:ctrIncludePaths}" + ], + "defines": [ + "${env:defaultDefines}", + "${env:ctrDefines}", + "__DEBUG__=1" + ], + "compilerPath": "${env:ctrCompilerPath}", + "cStandard": "c11", + "cppStandard": "${env:defaultCppStandard}", + "intelliSenseMode": "linux-gcc-arm" + }, + { + "name": "Switch", + "includePath": [ + "${env:defaultIncludePath}", + "${env:hacIncludePaths}" + ], + "defines": [ + "${env:defaultDefines}", + "${env:hacDefines}", + "__DEBUG__=0" + ], + "compilerPath": "${env:hacCompilerPath}", + "cStandard": "c11", + "cppStandard": "${env:defaultCppStandard}", + "intelliSenseMode": "linux-gcc-arm64" + }, + { + "name": "Switch {Debug}", + "includePath": [ + "${env:defaultIncludePath}", + "${env:hacIncludePaths}" + ], + "defines": [ + "${env:defaultDefines}", + "${env:hacDefines}", + "__DEBUG__=1" + ], + "compilerPath": "${env:hacCompilerPath}", + "cStandard": "c11", + "cppStandard": "${env:defaultCppStandard}", + "intelliSenseMode": "linux-gcc-arm64" + }, + { + "name": "Wii U", + "includePath": [ + "${env:defaultIncludePath}", + "${env:cafeIncludePaths}" + ], + "defines": [ + "${env:defaultDefines}", + "${env:cafeDefines}", + "__DEBUG__=0" + ], + "compilerPath": "${env:cafeCompilerPath}", + "cStandard": "c11", + "cppStandard": "${env:defaultCppStandard}", + "intelliSenseMode": "linux-gcc-arm" + }, + { + "name": "Wii U {Debug}", + "includePath": [ + "${env:defaultIncludePath}", + "${env:cafeIncludePaths}" + ], + "defines": [ + "${env:defaultDefines}", + "${env:cafeDefines}", + "__DEBUG__=1" + ], + "compilerPath": "${env:cafeCompilerPath}", + "cStandard": "c11", + "cppStandard": "${env:defaultCppStandard}", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c3b16b971..490976a8f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,64 +1,81 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": - [ - { - "label": "Build Console", - "type": "shell", - "command": "make ${input:args} -j6", - - "presentation": - { - "echo": true, - "reveal": "always", - "focus": true, - "panel":"shared", - "showReuseMessage": false, - "clear": true - }, - - "group": "build", - "problemMatcher": [] - }, - - { - "label": "Clean All", - "type": "shell", - "command": "make clean", - - "presentation": { - "echo": true, - "reveal": "always", - "focus": true, - "panel": "shared", - "showReuseMessage": false, - "clear": true - }, - - "group": "build", - "problemMatcher": [] - }, - - { - "label": "Format All Code", - "type": "shell", - "command": "bash", - "args": [ - "-c", - "clang-format -style=file -i $(find ./include ./platform ./source -name \\*.h -o -name \\*.cpp)" - ] - } - ], - - "inputs": - [ - { - "id": "args", - "type": "command", - "command": "cpptools.activeConfigCustomVariable", - "args": "console" - } - ] -} + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "inputs": [ + { + "id": "mode", + "description": "CMake release mode", + "type": "pickString", + "options": [ + "Release", + "Debug" + ], + "default": "Release" + }, + { + "id": "emulation", + "description": "Enable Emulation", + "type": "pickString", + "options": [ + "0", + "1" + ], + "default": "0" + } + ], + "tasks": [ + { + "label": "Configure 3DS", + "type": "shell", + "command": "${env:DEVKITPRO}/portlibs/3ds/bin/arm-none-eabi-cmake", + "args": [ + "-DCMAKE_BUILD_TYPE=${input:mode}", + "-D__EMULATION__=${input:emulation}", + "-S", + ".", + "-B", + "build" + ], + "group": "build", + "problemMatcher": [] + }, + { + "label": "Configure Switch", + "type": "shell", + "command": "${env:DEVKITPRO}/portlibs/switch/bin/aarch64-none-elf-cmake", + "args": [ + "-DCMAKE_BUILD_TYPE=${input:mode}", + "-D__EMULATION__=${input:emulation}", + "-S", + ".", + "-B", + "build" + ], + "group": "build", + "problemMatcher": [] + }, + { + "label": "Configure Wii U", + "type": "shell", + "command": "${env:DEVKITPRO}/portlibs/wiiu/bin/powerpc-eabi-cmake", + "args": [ + "-DCMAKE_BUILD_TYPE=${input:mode}", + "-D__EMULATION__=${input:emulation}", + "-S", + ".", + "-B", + "build" + ], + "group": "build", + "problemMatcher": [] + }, + { + "label": "Build", + "type": "shell", + "command": "make -C build", + "group": "build", + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..c539145d0 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,527 @@ +cmake_minimum_required(VERSION 3.13) + +# Declare project containing a single eponymous executable +project(lovepotion LANGUAGES C CXX ASM) +add_executable(${PROJECT_NAME}) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20) +message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") + +# Generate symbol list and map information (useful for debugging) +dkp_target_generate_symbol_list(${PROJECT_NAME}) + +find_package(Git QUIET) +if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE COMMIT_HASH + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +endif() + +include(FetchContent) + +set(LOVE_VERSION "12.0") +set(APP_VERSION "3.0.0") + +set(APP_TITLE "LÖVE Potion") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(APP_TITLE "LÖVE Potion (${COMMIT_HASH})") + target_compile_definitions(${PROJECT_NAME} PRIVATE __DEBUG__=1) +else() + target_compile_definitions(${PROJECT_NAME} PRIVATE __DEBUG__=0) +endif() + +set(APP_AUTHOR "LÖVEBrew Team") + +file(COPY libraries/luasocket DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + +# arm-none-eabi-cmake +if(NINTENDO_3DS) + target_compile_definitions(${PROJECT_NAME} PRIVATE + __CONSOLE__="3DS" __OS__="Horizon" + ) + + set(APP_ICON platform/ctr/icon.png) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(APP_ICON platform/ctr/icon-dev.png) + endif() + + # Generate a SMDH file for the executable + ctr_generate_smdh(${PROJECT_NAME}.smdh + NAME "${APP_TITLE}" + + # Other options available: + DESCRIPTION "LÖVE for 3DS • ${APP_VERSION}" + AUTHOR "${APP_AUTHOR}" + ICON ${APP_ICON} + ) + + add_subdirectory(platform/ctr) + + # Specify that the executable is a 3DSX file + ctr_create_3dsx(${PROJECT_NAME} + SMDH ${PROJECT_NAME}.smdh + ROMFS ${PROJECT_NAME}_ctr_romfs + ) + + set(APP_LIBS citro3d) + + add_custom_target(dist COMMAND + ${CMAKE_COMMAND} -E tar "cfv" "${PROJECT_NAME}-3ds-${COMMIT_HASH}.zip" --format=zip + "${CMAKE_CURRENT_BINARY_DIR}/lovepotion.3dsx" + "${CMAKE_CURRENT_BINARY_DIR}/lovepotion.elf" + ) + + add_custom_target(test + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/lovepotion.3dsx" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/test" + COMMAND ${CMAKE_COMMAND} -E tar "cfv" "game.love" --format=zip . + COMMAND ${CMAKE_COMMAND} -E cat "${CMAKE_CURRENT_BINARY_DIR}/lovepotion.3dsx" "game.love" > "${CMAKE_CURRENT_BINARY_DIR}/lovepotion_test.3dsx" + ) + + execute_process(COMMAND patch -d ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket -N -i ${PROJECT_SOURCE_DIR}/platform/ctr/libraries/luasocket.patch) +endif() + +# aarch64-none-elf-cmake +if(NINTENDO_SWITCH) + target_compile_definitions(${PROJECT_NAME} PRIVATE + __CONSOLE__="Switch" __OS__="Horizon" + ) + + nx_generate_nacp(${PROJECT_NAME}.nacp + NAME "${APP_TITLE}" + AUTHOR ${APP_AUTHOR} + VERSION ${APP_VERSION} + ) + + add_subdirectory(platform/hac) + + set(APP_ICON platform/hac/icon.jpg) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(APP_ICON platform/hac/icon-dev.jpg) + endif() + + nx_create_nro(${PROJECT_NAME} + ICON ${APP_ICON} + NACP ${PROJECT_NAME}.nacp + ROMFS ${PROJECT_NAME}_hac_romfs + ) + + find_package(Freetype REQUIRED) + + set(APP_LIBS deko3d freetype bz2) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(APP_LIBS deko3dd freetype bz2) + endif() + + target_include_directories(${PROJECT_NAME} PRIVATE ${FREETYPE_INCLUDE_DIRS}) + + add_custom_target(dist COMMAND + ${CMAKE_COMMAND} -E tar "cfv" "${PROJECT_NAME}-switch-${COMMIT_HASH}.zip" --format=zip + "${CMAKE_CURRENT_BINARY_DIR}/lovepotion.nro" + "${CMAKE_CURRENT_BINARY_DIR}/lovepotion.elf" + ) + + add_custom_target(test + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/lovepotion.nro" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/test" + COMMAND ${CMAKE_COMMAND} -E tar "cfv" "game.love" --format=zip . + COMMAND ${CMAKE_COMMAND} -E cat "${CMAKE_CURRENT_BINARY_DIR}/lovepotion.nro" "game.love" > "${CMAKE_CURRENT_BINARY_DIR}/lovepotion_test.nro" + ) + + execute_process(COMMAND patch -d ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket -N -i ${PROJECT_SOURCE_DIR}/platform/hac/libraries/luasocket.patch) +endif() + +# powerpc-eabi-cmake +if (NINTENDO_WIIU) + target_compile_definitions(${PROJECT_NAME} PRIVATE + __CONSOLE__="Wii U" __OS__="Cafe" + ) + + set(APP_ICON platform/cafe/icon.png) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(APP_ICON platform/cafe/icon-dev.png) + endif() + + add_subdirectory(platform/cafe) + + wut_create_rpx(${PROJECT_NAME}) + + wut_create_wuhb(${PROJECT_NAME} + NAME "${APP_TITLE}" + + # Bundle a content folder (optional) + CONTENT ${PROJECT_NAME}_cafe_content + + # Other options available: + SHORTNAME "${APP_TITLE}" + AUTHOR ${APP_AUTHOR} + ICON ${APP_ICON} + #TVSPLASH myTvSplash.png + #DRCSPLASH myDrcSplash.png + ) + + add_custom_target(dist COMMAND + ${CMAKE_COMMAND} -E tar "cfv" "${PROJECT_NAME}-wiiu-${COMMIT_HASH}.zip" --format=zip + "${CMAKE_CURRENT_BINARY_DIR}/lovepotion.rpx" + "${CMAKE_CURRENT_BINARY_DIR}/lovepotion.wuhb" + "${CMAKE_CURRENT_BINARY_DIR}/lovepotion.elf" + ) + + add_custom_target(test + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/lovepotion.wuhb" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/test" + COMMAND ${CMAKE_COMMAND} -E tar "cfv" "game.love" --format=zip . + COMMAND ${CMAKE_COMMAND} -E cat "${CMAKE_CURRENT_BINARY_DIR}/lovepotion.wuhb" "game.love" > "${CMAKE_CURRENT_BINARY_DIR}/lovepotion_test.wuhb" + ) + + execute_process(COMMAND patch -d ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket -N -i ${PROJECT_SOURCE_DIR}/platform/cafe/libraries/luasocket.patch) + + find_package(Freetype REQUIRED) + set(APP_LIBS freetype bz2) + + find_package(SDL2 REQUIRED) + target_include_directories(${PROJECT_NAME} PRIVATE ${DEVKITPRO}/portlibs/wiiu/include) + target_include_directories(${PROJECT_NAME} PRIVATE ${FREETYPE_INCLUDE_DIRS}) + + target_link_libraries(${PROJECT_NAME} PRIVATE SDL2_mixer mpg123 SDL2) +endif() + +# Options for code generation +target_compile_options(${PROJECT_NAME} PRIVATE + # Common C/C++ options + -Wall -Wno-psabi + + # C++ specific options + $<$:-fexceptions -fno-rtti> +) + +# external libraries # + +# find PkgConfig for liblz4, libmpg123 +find_package(PkgConfig REQUIRED) + +# lua5.1 +pkg_check_modules(lua51 REQUIRED IMPORTED_TARGET lua51) +target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::lua51) + +# ddsparse +add_library(ddsparse + libraries/ddsparse/ddsinfo.h + libraries/ddsparse/ddsparse.cpp + libraries/ddsparse/ddsparse.h +) + +# lua51 +add_library(luabit + libraries/luabit/bit.c +) +target_link_libraries(luabit PRIVATE PkgConfig::lua51) + +# lua53-compat +add_library(lua53 + libraries/lua53/l53strlib.c + libraries/lua53/l53strlib.h + libraries/lua53/lprefix.h + libraries/lua53/lutf8lib.c + libraries/lua53/lutf8lib.h +) +target_link_libraries(lua53 PRIVATE PkgConfig::lua51) + +add_library(luahttps + libraries/luahttps/common/Connection.h + libraries/luahttps/common/ConnectionClient.h + libraries/luahttps/common/HTTPRequest.cpp + libraries/luahttps/common/HTTPRequest.h + libraries/luahttps/common/HTTPSClient.cpp + libraries/luahttps/common/HTTPSClient.h + libraries/luahttps/common/HTTPSCommon.cpp + libraries/luahttps/common/HTTPSCommon.h + libraries/luahttps/common/PlaintextConnection.cpp + libraries/luahttps/common/PlaintextConnection.h + libraries/luahttps/generic/CurlClient.cpp + libraries/luahttps/generic/CurlClient.h + libraries/luahttps/https.cpp +) +target_link_libraries(luahttps PRIVATE PkgConfig::lua51) + +# luasocket +add_library(luasocket + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/luasocket.hpp + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/luasocket.cpp + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/auxiliar.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/auxiliar.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/buffer.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/buffer.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/compat.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/compat.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/except.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/except.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/inet.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/inet.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/io.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/io.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/ltn12.lua + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/luasocket.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/luasocket.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/mime.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/mime.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/options.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/options.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/pierror.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/select.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/select.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/smtp.lua + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/socket.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/tcp.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/tcp.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/timeout.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/timeout.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/udp.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/udp.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/usocket.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/usocket.h +) +target_link_libraries(luasocket PRIVATE PkgConfig::lua51) +target_include_directories(luasocket PRIVATE + libraries/lua53 +) + +# find PkgConfig for liblz4, libmpg123 +find_package(PkgConfig REQUIRED) + +# noise1234 +add_library(noise1234 + libraries/noise1234/noise1234.cpp + libraries/noise1234/noise1234.h + libraries/noise1234/simplexnoise1234.cpp + libraries/noise1234/simplexnoise1234.h +) + +# libwuff +add_library(wuff + libraries/wuff/wuff.c + libraries/wuff/wuff.h + libraries/wuff/wuff_config.h + libraries/wuff/wuff_convert.c + libraries/wuff/wuff_convert.h + libraries/wuff/wuff_internal.c + libraries/wuff/wuff_internal.h + libraries/wuff/wuff_memory.c +) + + +target_compile_definitions(${PROJECT_NAME} PRIVATE + __APP_VERSION__=\"${VERSION}\" __LOVE_VERSION__=\"${LOVE_VERSION}\" + __EMULATION__ +) + +# link lz4 +pkg_check_modules(liblz4 REQUIRED IMPORTED_TARGET liblz4) +target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::liblz4) + +# link lua5.1 +target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::lua51) + +# link curl +pkg_check_modules(libcurl REQUIRED IMPORTED_TARGET libcurl) +target_link_libraries(luahttps PRIVATE PkgConfig::libcurl) + +# link everything else +target_link_libraries(${PROJECT_NAME} PRIVATE + ${APP_LIBS} physfs box2d z turbojpeg png vorbisidec ogg + modplug luabit lua53 wuff ddsparse luahttps luasocket noise1234 +) + +# Include directories +target_include_directories(${PROJECT_NAME} PRIVATE + include + libraries/ddsparse + libraries/dr + libraries/lua53 + libraries/luahttps + libraries/luasocket + libraries/noise1234 + libraries/utf8 + libraries/wuff +) + +# Source code files +# find source -type f -name \*.cpp -a -not -name truetyperasterizer.cpp | clip +target_sources(${PROJECT_NAME} PRIVATE + source/common/console.cpp + source/common/data.cpp + source/common/exception.cpp + source/common/luax.cpp + source/common/module.cpp + source/common/object.cpp + source/common/pixelformat.cpp + source/common/reference.cpp + source/common/type.cpp + source/common/variant.cpp + source/common/vector.cpp + source/main.cpp + source/modules/audio/audio.cpp + source/modules/audio/wrap_audio.cpp + source/modules/data/data.cpp + source/modules/data/wrap_data.cpp + source/modules/event/event.cpp + source/modules/event/wrap_event.cpp + source/modules/filesystem/filesystem.cpp + source/modules/filesystem/physfs/filesystem.cpp + source/modules/filesystem/wrap_filesystem.cpp + source/modules/font/fontmodule.cpp + source/modules/font/wrap_fontmodule.cpp + source/modules/graphics/wrap_graphics.cpp + source/modules/image/imagemodule.cpp + source/modules/image/wrap_imagemodule.cpp + source/modules/joystick/wrap_joystickmodule.cpp + source/modules/keyboard/wrap_keyboard.cpp + source/modules/love/love.cpp + source/modules/math/math.cpp + source/modules/math/wrap_math.cpp + source/modules/physics/physics.cpp + source/modules/physics/wrap_physics.cpp + source/modules/sensor/sensor.cpp + source/modules/sensor/wrap_sensor.cpp + source/modules/sound/sound.cpp + source/modules/sound/wrap_sound.cpp + source/modules/system/wrap_system.cpp + source/modules/thread/threadmodule.cpp + source/modules/thread/wrap_threadmodule.cpp + source/modules/timer/wrap_timer.cpp + source/modules/touch/touch.cpp + source/modules/touch/wrap_touch.cpp + source/modules/window/wrap_window.cpp + source/objects/beziercurve/beziercurve.cpp + source/objects/beziercurve/wrap_beziercurve.cpp + source/objects/bmfontrasterizer/bmfontrasterizer.cpp + source/objects/body/body.cpp + source/objects/body/wrap_body.cpp + source/objects/channel/channel.cpp + source/objects/channel/wrap_channel.cpp + source/objects/compressedimagedata/compressedimagedata.cpp + source/objects/compressedimagedata/compressedslice.cpp + source/objects/compressedimagedata/wrap_compressedimagedata.cpp + source/objects/contact/contact.cpp + source/objects/contact/wrap_contact.cpp + source/objects/data/bytedata/bytedata.cpp + source/objects/data/bytedata/wrap_bytedata.cpp + source/objects/data/compresseddata/compresseddata.cpp + source/objects/data/compresseddata/wrap_compresseddata.cpp + source/objects/data/dataview/dataview.cpp + source/objects/data/dataview/wrap_dataview.cpp + source/objects/data/filedata/filedata.cpp + source/objects/data/filedata/wrap_filedata.cpp + source/objects/data/sounddata/sounddata.cpp + source/objects/data/sounddata/wrap_sounddata.cpp + source/objects/data/wrap_data.cpp + source/objects/decoder/wrap_decoder.cpp + source/objects/file/file.cpp + source/objects/file/physfs/file.cpp + source/objects/file/wrap_file.cpp + source/objects/font/font.cpp + source/objects/font/wrap_font.cpp + source/objects/glyphdata/glyphdata.cpp + source/objects/glyphdata/wrap_glyphdata.cpp + source/objects/imagedata/imagedata.cpp + source/objects/imagedata/imagedatabase.cpp + source/objects/imagedata/wrap_imagedata.cpp + source/objects/imagerasterizer/imagerasterizer.cpp + source/objects/joint/joint.cpp + source/objects/joint/types/distancejoint/distancejoint.cpp + source/objects/joint/types/distancejoint/wrap_distancejoint.cpp + source/objects/joint/types/frictionjoint/frictionjoint.cpp + source/objects/joint/types/frictionjoint/wrap_frictionjoint.cpp + source/objects/joint/types/gearjoint/gearjoint.cpp + source/objects/joint/types/gearjoint/wrap_gearjoint.cpp + source/objects/joint/types/motorjoint/motorjoint.cpp + source/objects/joint/types/motorjoint/wrap_motorjoint.cpp + source/objects/joint/types/mousejoint/mousejoint.cpp + source/objects/joint/types/mousejoint/wrap_mousejoint.cpp + source/objects/joint/types/prismaticjoint/prismaticjoint.cpp + source/objects/joint/types/prismaticjoint/wrap_prismaticjoint.cpp + source/objects/joint/types/pulleyjoint/pulleyjoint.cpp + source/objects/joint/types/pulleyjoint/wrap_pulleyjoint.cpp + source/objects/joint/types/revolutejoint/revolutejoint.cpp + source/objects/joint/types/revolutejoint/wrap_revolutejoint.cpp + source/objects/joint/types/ropejoint/ropejoint.cpp + source/objects/joint/types/ropejoint/wrap_ropejoint.cpp + source/objects/joint/types/weldjoint/weldjoint.cpp + source/objects/joint/types/weldjoint/wrap_weldjoint.cpp + source/objects/joint/types/wheeljoint/wheeljoint.cpp + source/objects/joint/types/wheeljoint/wrap_wheeljoint.cpp + source/objects/joint/wrap_joint.cpp + source/objects/joystick/wrap_joystick.cpp + source/objects/mesh/mesh.cpp + source/objects/mesh/wrap_mesh.cpp + source/objects/quad/quad.cpp + source/objects/quad/wrap_quad.cpp + source/objects/randomgenerator/randomgenerator.cpp + source/objects/randomgenerator/wrap_randomgenerator.cpp + source/objects/rasterizer/rasterizer.cpp + source/objects/rasterizer/wrap_rasterizer.cpp + source/objects/shape/shape.cpp + source/objects/shape/types/chainshape/chainshape.cpp + source/objects/shape/types/chainshape/wrap_chainshape.cpp + source/objects/shape/types/circleshape/circleshape.cpp + source/objects/shape/types/circleshape/wrap_circleshape.cpp + source/objects/shape/types/edgeshape/edgeshape.cpp + source/objects/shape/types/edgeshape/wrap_edgeshape.cpp + source/objects/shape/types/polygonshape/polygonshape.cpp + source/objects/shape/types/polygonshape/wrap_polygonshape.cpp + source/objects/shape/wrap_shape.cpp + source/objects/source/wrap_source.cpp + source/objects/spritebatch/spritebatch.cpp + source/objects/spritebatch/wrap_spritebatch.cpp + source/objects/textbatch/textbatch.cpp + source/objects/textbatch/wrap_textbatch.cpp + source/objects/texture/wrap_texture.cpp + source/objects/thread/luathread.cpp + source/objects/thread/wrap_luathread.cpp + source/objects/transform/transform.cpp + source/objects/transform/wrap_transform.cpp + source/objects/world/world.cpp + source/objects/world/wrap_world.cpp + source/utilities/base64.cpp + source/utilities/bytes.cpp + source/utilities/compressor/compressor.cpp + source/utilities/compressor/types/lz4compressor.cpp + source/utilities/compressor/types/zlibcompressor.cpp + source/utilities/decoder/decoder.cpp + source/utilities/decoder/types/flacdecoder.cpp + source/utilities/decoder/types/modplugdecoder.cpp + source/utilities/decoder/types/mp3decoder.cpp + source/utilities/decoder/types/vorbisdecoder.cpp + source/utilities/decoder/types/wavedecoder.cpp + source/utilities/driver/renderer/polyline/polyline.cpp + source/utilities/driver/renderer/polyline/types/beveljoin.cpp + source/utilities/driver/renderer/polyline/types/miterjoin.cpp + source/utilities/driver/renderer/polyline/types/nonejoin.cpp + source/utilities/driver/renderer/renderstate.cpp + source/utilities/driver/renderer/samplerstate.cpp + source/utilities/formathandler/formathandler.cpp + source/utilities/formathandler/types/astchandler.cpp + source/utilities/formathandler/types/ddshandler.cpp + source/utilities/formathandler/types/jpghandler.cpp + source/utilities/formathandler/types/ktxhandler.cpp + source/utilities/formathandler/types/pkmhandler.cpp + source/utilities/formathandler/types/pnghandler.cpp + source/utilities/guid.cpp + source/utilities/hashfunction/hashfunction.cpp + source/utilities/hashfunction/types/md5.cpp + source/utilities/hashfunction/types/sha1.cpp + source/utilities/hashfunction/types/sha256.cpp + source/utilities/hashfunction/types/sha512.cpp + source/utilities/pool/poolthread.cpp + source/utilities/pool/sources.cpp + source/utilities/pool/vibrations.cpp + source/utilities/result.cpp + source/utilities/shaper/genericshaper.cpp + source/utilities/shaper/textshaper.cpp + source/utilities/stream/stream.cpp + source/utilities/stream/types/datastream.cpp + source/utilities/threads/thread.cpp + source/utilities/threads/threadable.cpp +) diff --git a/LICENSE.md b/LICENSE.md index 681ba787e..48ee26641 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -2,8 +2,8 @@ * Website: https://github.com/lovebrew/LovePotion * License: MIT * Copyrights: - + © 2018 - 2022 Jeremy S. Postelnek & Logan Hickok-Dickson - + © 2015 (Original Software) Ruairidh Carmichael + + © 2018 - Present: Serena S. Postelnek & Logan Hickok-Dickson + + © 2015 (Original Software): Ruairidh Carmichael - https://github.com/videah/LovePotion **Note: this software is not officially supported by the LÖVE Development Team (see below). However, much of this source uses the LÖVE codebase with various modifications. It has been built from the ground up to suit the Nintendo homebrew console scene to ensure it works as intended.** @@ -11,7 +11,7 @@ # LÖVE * Website: https://love2d.org/ * License: zlib -* Copyright © 2006-2021 LOVE Development Team +* Copyright © 2006-2023 LOVE Development Team ## Kepler Project's lua-compat-5.3 @@ -46,40 +46,33 @@ * License: Unknown, MIT/Expat-like (listed as UTF8-CPP) * Copyright 2006 Nemanja Trifunovic -## libmpg123 - -* Website: http://www.mpg123.de/ -* Source download: http://sourceforge.net/projects/mpg123/files/latest/download -* License: LGPL 2.1 -* Copyright (c) 1995-2013 by Michael Hipp and others, free software under the terms of the LGPL v2.1 -* Detailed information from the debian project: - - Copyright 1995-2016 by the mpg123 project - - Copyright 2009-2011 by Malcolm Boczek - - Copyright 2008 Christian Weisgerber - - Copyright 2006-2007 by Zuxy Meng - - Copyright 2000-2002 David Olofson - - Copyright 1998 Fabrice Bellard - - Copyright 1997 Mikko Tommila # libctru * Website: https://github.com/smealum/ctrulib * License: zlib -* Copyright © 2014-2020 libctru Authors +* Copyright © 2014-2023 libctru Authors ## citro2d * Website: https://github.com/devkitPro/citro2d * License: zlib * Copyright © 2017-2018 fincs + # libnx * Website: https://github.com/switchbrew/libnx * License: ISC -* Copyright 2017-2018 libnx Authors +* Copyright © 2017-2023 libnx Authors ## deko3d * Website: https://github.com/devkitPro/deko3d * License: zlib -* Copyright (C) 2018-2020 fincs +* Copyright © 2018-2020 fincs + +# wut +* Website: https://github.com/devkitPro/wut +* License: zlib +* Copyright © 2015 - Present devkitPro, wut Authors + # **License Texts** ## MIT license @@ -180,513 +173,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ``` -## GNU LGPL 2.1 - -``` - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - -Copyright (C) 1991, 1999 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts -as the successor of the GNU Library Public License, version 2, hence -the version number 2.1.] - - Preamble - -The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - -This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - -When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - -To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - -For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - -We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - -To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - -Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - -Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - -When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - -We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - -For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - -In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - -Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - -The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -00. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - -A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - -The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - -"Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - -01. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - -You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - -02. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - -a) The modified work must itself be a software library. - -b) You must cause the files modified to carry prominent notices -stating that you changed the files and the date of any change. - -c) You must cause the whole of the work to be licensed at no -charge to all third parties under the terms of this License. - -d) If a facility in the modified Library refers to a function or a -table of data to be supplied by an application program that uses -the facility, other than as an argument passed when the facility -is invoked, then you must make a good faith effort to ensure that, -in the event an application does not supply such function or -table, the facility still operates, and performs whatever part of -its purpose remains meaningful. - -(For example, a function in a library to compute square roots has -a purpose that is entirely well-defined independent of the -application. Therefore, Subsection 2d requires that any -application-supplied function or table used by this function must -be optional: if the application does not supply it, the square -root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - -03. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - -Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - -This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - -04. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - -If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - -05. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - -However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - -When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - -If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - -Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - -06. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - -You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - -a) Accompany the work with the complete corresponding -machine-readable source code for the Library including whatever -changes were used in the work (which must be distributed under -Sections 1 and 2 above); and, if the work is an executable linked -with the Library, with the complete machine-readable "work that -uses the Library", as object code and/or source code, so that the -user can modify the Library and then relink to produce a modified -executable containing the modified Library. (It is understood -that the user who changes the contents of definitions files in the -Library will not necessarily be able to recompile the application -to use the modified definitions.) - -b) Use a suitable shared library mechanism for linking with the -Library. A suitable mechanism is one that (1) uses at run time a -copy of the library already present on the user's computer system, -rather than copying library functions into the executable, and (2) -will operate properly with a modified version of the library, if -the user installs one, as long as the modified version is -interface-compatible with the version that the work was made with. - -c) Accompany the work with a written offer, valid for at -least three years, to give the same user the materials -specified in Subsection 6a, above, for a charge no more -than the cost of performing this distribution. - -d) If distribution of the work is made by offering access to copy -from a designated place, offer equivalent access to copy the above -specified materials from the same place. - -e) Verify that the user has already received a copy of these -materials or that you have already sent this user a copy. - -For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - -It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - -07. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - -a) Accompany the combined library with a copy of the same work -based on the Library, uncombined with any other library -facilities. This must be distributed under the terms of the -Sections above. - -b) Give prominent notice with the combined library of the fact -that part of it is a work based on the Library, and explaining -where to find the accompanying uncombined form of the same work. - -08. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - -09. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - -10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - -11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - -12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - -13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - -14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - -15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - -If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - -To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - -Copyright (C) - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - -Yoyodyne, Inc., hereby disclaims all copyright interest in the -library `Frob' (a library for tweaking knobs) written by James Random Hacker. - -, 1 April 1990 -Ty Coon, President of Vice - -That's all there is to it! -``` - ## ISC ``` diff --git a/Makefile b/Makefile deleted file mode 100644 index cddf5c0b3..000000000 --- a/Makefile +++ /dev/null @@ -1,92 +0,0 @@ -#----------------------------------- -# Recursive file lookup -# Credit to SciresM -#----------------------------------- -export DIR_WILDCARD = $(foreach d, $(wildcard $(1:=/*)), $(if $(wildcard $d/.), $(call DIR_WILDCARD,$d) $d,)) - -#----------------------------------- -# Common code for the consoles -#----------------------------------- -ROOT_INCLUDES ?= include $(foreach d, $(wildcard include/*), $(if $(wildcard $d/.), $(call DIR_WILDCARD, $d) $d,)) -ROOT_SOURCES ?= source $(foreach d, $(wildcard source/*), $(if $(wildcard $d/.), $(call DIR_WILDCARD, $d) $d,)) -ROOT_LIBRARIES ?= libraries $(foreach d, $(wildcard libraries/*), $(if $(wildcard $d/.), $(call DIR_WILDCARD, $d) $d,)) - -export LOVE_SOURCES = $(foreach dir, $(ROOT_SOURCES), ../../$(wildcard $(dir))) -export LOVE_INCLUDES = $(foreach dir, $(ROOT_INCLUDES), ../../$(wildcard $(dir))) -export LOVE_LIBRARIES = $(foreach dir, $(ROOT_LIBRARIES), ../../$(wildcard $(dir))) - -export LOVE_MAIN_DATA_FILES = ../../source/modules/love/scripts -export LOVE_DATA_FILES = ../../source/scripts - -export LOVE_VERSION = 11.4.0 -#----------------------------------- -# Common portlibs for the consoles -#----------------------------------- -export LOVE_PORTLIBS = -lmodplug -lvorbisidec -lFLAC -lvorbisidec -logg -LOVE_PORTLIBS += -lphysfs -llz4 -lz -lbox2d -ljpeg -lpng `curl-config --libs` -LOVE_PORTLIBS += -ltheora - -#------------------------------------ -# Common configuration for consoles -#------------------------------------ -export APP_TITLE := LÖVE Potion -export APP_AUTHOR := lövebrew team -export APP_VERSION := 2.4.0 -export APP_TITLEID := 1043 - -export DEFINES := -D__DEBUG__=$(DEBUG) -D__APP_VERSION__=\"$(APP_VERSION)\" \ - -D__LOVE_VERSION__=\"$(LOVE_VERSION)\" -#----------------------------------- -# Build -#----------------------------------- -all: ctr hac - -ctr: DEFINES += -D__CONSOLE__=\"3DS\" -ctr: - @$(MAKE) -C platform/3ds - -hac: DEFINES += -D__CONSOLE__=\"Switch\" -hac: - @$(MAKE) -C platform/switch - -#----------------------------------- -# Build & Distribute (Release) -#----------------------------------- -DIST := distribute -COMMIT_HASH := $(shell git rev-parse --short HEAD) - -dist: $(DIST) dist-ctr dist-hac - -$(DIST): - @mkdir -p $@ - -dist-ctr: ctr $(DIST) - @echo Built for 3DS.. - @zip -ujqr ./$(DIST)/LOVEPotion-3DS-$(COMMIT_HASH).zip platform/3ds -i '*.3dsx' '*.elf' - -dist-hac: hac $(DIST) - @echo Built for Switch.. - @zip -ujqr ./$(DIST)/LOVEPotion-Switch-$(COMMIT_HASH).zip platform/switch -i '*.nro' '*.elf' - -#----------------------------------- -# Debug/Development -#----------------------------------- -debug: ctr-debug hac-debug - -ctr-debug: DEBUG=1 -ctr-debug: ctr - -hac-debug: DEBUG=1 -hac-debug: hac - -#----------------------------------- -# Clean -#----------------------------------- -clean: clean-ctr clean-hac - @rm -fr $(DIST) - -clean-ctr: - @$(MAKE) -C platform/3ds clean - -clean-hac: - @$(MAKE) -C platform/switch clean diff --git a/README.md b/README.md index d90482090..d633a6164 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,36 @@ # LÖVE Potion -LÖVE for Nintendo Homebrew -Get started on the [LÖVE Potion wiki!](https://lovebrew.org) +[LÖVE](https://love2d.org) for Nintendo Homebrew. -[![LÖVE Potion](https://github.com/lovebrew/LovePotion/actions/workflows/build.yml/badge.svg)](https://github.com/lovebrew/LovePotion/actions/workflows/build.yml) +You can find the API reference and how to start making your own games [on the Wiki!](https://lovebrew.org) -
- -
+## Build Statuses + +[![Nintendo 3DS](https://github.com/lovebrew/lovepotion/actions/workflows/Nintendo%203DS.yml/badge.svg?branch=dev%2F3.0)](https://github.com/lovebrew/lovepotion/actions/workflows/Nintendo%203DS.yml) [![Nintendo Switch](https://github.com/lovebrew/lovepotion/actions/workflows/Nintendo%20Switch.yml/badge.svg?branch=dev%2F3.0)](https://github.com/lovebrew/lovepotion/actions/workflows/Nintendo%20Switch.yml) [![Nintendo Wii U](https://github.com/lovebrew/lovepotion/actions/workflows/Nintendo%20Wii%20U.yml/badge.svg?branch=dev%2F3.0)](https://github.com/lovebrew/lovepotion/actions/workflows/Nintendo%20Wii%20U.yml) + +## Help and Support + +[![Discord Shield](https://discordapp.com/api/guilds/215551912823619584/widget.png?style=shield)](https://discord.gg/ggbKkhc) + +## Acknowledgements + +- [devkitPro](https://github.com/devkitPro) + - devkitARM and libctru + - devkitPPC and wut + - devkitA64 and libnx + - Various portlibs for the consoles + +- [piepie](https://github.com/piepie62) + - Various C++ components + +- [fincs](https://github.com/fincs) + - citro2d, citro3d, and deko3d + +- [mtheall](https://github.com/mtheall) + - Initial debugging with Switch font rendering + +- [LÖVE's Developers](https://github.com/love2d/love) + - Code is referenced and used in this project + +- [videah](https://github.com/videah) + - Original author of LÖVE Potion diff --git a/include/common/base64.h b/include/common/base64.h deleted file mode 100644 index c978f21ad..000000000 --- a/include/common/base64.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#if defined(__3DS__) - #include <3ds/types.h> -#elif defined(__SWITCH__) - #include -#endif - -namespace love -{ - char* b64_encode(const char* src, size_t srcLength, size_t lineLength, size_t& dstLength); - - char* b64_decode(const char* src, size_t srcLength, size_t& dstLength); -} // namespace love diff --git a/include/common/bidirectionalmap.h b/include/common/bidirectionalmap.h deleted file mode 100644 index d0f913e0b..000000000 --- a/include/common/bidirectionalmap.h +++ /dev/null @@ -1,310 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -// Note: both Key and Value must have a default constructor -// Note: KeyComparator and ValueComparator must act similarly to std::equal_to -template, - typename VC = std::equal_to<>> -class BidirectionalMap -{ - static_assert(Size > 0); - static_assert(!std::is_same_v); - static_assert(!std::is_same_v); - - private: - template - constexpr void populate(const std::pair* data) - { - static_assert(ArraySize <= Size && ArraySize > 0); - - for (size_t i = 0; i < ArraySize; i++) - this->entries[i] = data[i]; - - if constexpr (ArraySize < Size) - { - for (size_t i = ArraySize; i < Size; i++) - this->entries[i] = { Key(), Value() }; - } - } - - public: - using Key = std::remove_cvref_t; - using Value = std::remove_cvref_t; - using KeyComparator = std::remove_cvref_t; - using ValueComparator = std::remove_cvref_t; - - using Entry = std::pair; - - BidirectionalMap() = delete; - - template - constexpr BidirectionalMap(const std::pair (&inEntries)[ArraySize], KC kc = KC {}, - VC vc = VC {}) : - entries(), - populated(ArraySize), - kc(kc), - vc(vc) - { - populate(inEntries); - } - - template - constexpr BidirectionalMap(const std::array, ArraySize>(&inEntries), - KC kc = KC {}, VC vc = VC {}) : - entries(), - populated(ArraySize), - kc(kc), - vc(vc) - { - populate(inEntries.data()); - } - - constexpr BidirectionalMap(const std::pair (&inEntries)[Size], KC kc = KC {}, - VC vc = VC {}) : - entries(), - populated(Size), - kc(kc), - vc(vc) - { - populate(inEntries); - } - - constexpr BidirectionalMap(const std::array, Size>(&inEntries), KC kc = KC {}, - VC vc = VC {}) : - entries(), - populated(Size), - kc(kc), - vc(vc) - { - populate(inEntries.data()); - } - - constexpr bool Find(const Key& search, Value& out) const - { - for (size_t i = 0; i < this->populated; ++i) - { - if (kc(this->entries[i].first, search)) - { - out = this->entries[i].second; - return true; - } - } - - return false; - } - - constexpr bool ReverseFind(const Value& search, Key& out) const - { - for (size_t i = 0; i < this->populated; ++i) - { - if (vc(this->entries[i].second, search)) - { - out = this->entries[i].first; - return true; - } - } - - return false; - } - - /* Can only be used on String-mapped Keys */ - constexpr std::vector GetNames() const - { - std::vector strings; - strings.reserve(this->populated); - - for (size_t i = 0; i < this->populated; i++) - { - if (this->entries[i].first != nullptr) - strings.emplace_back(this->entries[i].first); - } - - return strings; - } - - constexpr std::pair GetEntries() const - { - return { entries.data(), this->populated }; - } - - private: - std::array entries; - const size_t populated; - - [[no_unique_address]] KeyComparator kc; - [[no_unique_address]] ValueComparator vc; -}; - -template<> -class BidirectionalMap<> -{ - public: - // Used to compare C strings; operator== doesn't work properly for those - struct cstringcomp - { - constexpr bool operator()(const char* a, const char* b) const - { - size_t idx = 0; - while (a[idx] != '\0' && b[idx] != '\0') - { - if (a[idx] != b[idx]) - { - return false; - } - idx++; - } - - if ((a[idx] == '\0') != (b[idx] == '\0')) - { - return false; - } - - return true; - } - - constexpr bool operator()(const char* a, std::string_view b) const - { - return a == b; - } - - constexpr bool operator()(std::string_view b, const char* a) const - { - return a == b; - } - }; - - private: - template - struct CheckArgs - { - private: - using CheckArgs2 = CheckArgs; - - static constexpr bool KeyConvertible = - std::is_convertible_v, - std::remove_cvref_t> || - std::is_convertible_v, - std::remove_cvref_t>; - static constexpr bool ValueConvertible = - std::is_convertible_v, - std::remove_cvref_t> || - std::is_convertible_v, - std::remove_cvref_t>; - - public: - static constexpr bool value = KeyConvertible && ValueConvertible && CheckArgs2::value; - - using AType = std::conditional_t< - sizeof...(Args) == 0, A, - std::conditional_t< - std::is_convertible_v, - std::remove_cvref_t>, - std::remove_cvref_t, std::remove_cvref_t>>; - using BType = std::conditional_t< - sizeof...(Args) == 0, B, - std::conditional_t< - std::is_convertible_v, - std::remove_cvref_t>, - std::remove_cvref_t, std::remove_cvref_t>>; - }; - - template - struct CheckArgs - { - static constexpr bool value = true; - using AType = std::remove_cvref_t; - using BType = std::remove_cvref_t; - }; - - template - struct DefaultComparatorForType - { - private: - using testtype = std::remove_cvref_t; - - public: - using value = - std::conditional_t, cstringcomp, std::equal_to<>>; - }; - - template - using defaultcomp_v = typename DefaultComparatorForType::value; - - public: - // clang-format off - - // Note: long name, but shouldn't often be used - template - requires - (sizeof...(Args) % 2 == 0) && - (sizeof...(Args) > 0) && - (CheckArgs::value) - static constexpr auto CreateWithComparators(KeyComparator kc, ValueComparator vc, Args... args) - { - using check = CheckArgs; - - auto setArgs = []( - std::array, Size>& addTo, Args... args) { - auto setArgsRef = []( - auto& me, std::array, Size>& addTo, A key, B val, InnerArgs... args) - { - size_t index = Size - (sizeof...(InnerArgs) + 2) / 2; - addTo[index].first = key; - addTo[index].second = val; - - if constexpr (sizeof...(InnerArgs) != 0) - { - me(me, addTo, std::forward(args)...); - } - }; - setArgsRef(setArgsRef, addTo, args...); - }; - - std::array, sizeof...(Args) / 2> - entries {}; - - setArgs(entries, std::forward(args)...); - - return BidirectionalMap { entries, kc, vc }; - } - - // Note: long name, but shouldn't often be used - template, typename... Args> - requires - (sizeof...(Args) % 2 == 0) && - (sizeof...(Args) > 0) && - (CheckArgs::value) - static constexpr auto CreateWithKeyComparator(KeyComparator kc = KeyComparator(), Args... args) - { - return CreateWithComparators(std::move(kc), defaultcomp_v::BType>(), args...); - } - - // Note: long name, but shouldn't often be used - template, typename... Args> - requires - (sizeof...(Args) % 2 == 0) && - (sizeof...(Args) > 0) && - (CheckArgs::value) - static constexpr auto CreateWithValueComparator(ValueComparator vc = ValueComparator(), Args... args) - { - return CreateWithComparators(defaultcomp_v::AType>(), std::move(vc), args...); - } - - template - requires - (sizeof...(Args) % 2 == 0) && - (sizeof...(Args) > 0) && - (CheckArgs::value) - static constexpr auto Create(Args... args) - { - return CreateWithComparators(defaultcomp_v::AType>(), defaultcomp_v::BType>(), args...); - } - - // clang-format on -}; diff --git a/include/common/color.hpp b/include/common/color.hpp new file mode 100644 index 000000000..538ab4ec9 --- /dev/null +++ b/include/common/color.hpp @@ -0,0 +1,178 @@ +#pragma once + +#include "exception.hpp" +#include "vector.hpp" + +#include +#include + +#include +#include + +struct Color +{ + public: + static constexpr float WHITE[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + static constexpr float TRANSPARENT[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + static constexpr float CTR_TRANSPARENCY[4] = { 0, 0, 0, 1.0f / 255.0f }; + + Color() : r(0), g(0), b(0), a(0) + {} + + Color(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) + {} + + Color(const float (&rgba)[4]) + { + this->r = rgba[0]; + this->g = rgba[1]; + this->b = rgba[2]; + this->a = rgba[3]; + } + + /* only used for 3DS image data */ + Color(uint32_t abgr) + { + this->r = ((abgr & 0xFF000000) >> 0x18) / 255.0f; + this->g = ((abgr & 0x00FF0000) >> 0x10) / 255.0f; + this->b = ((abgr & 0x0000FF00) >> 0x08) / 255.0f; + this->a = ((abgr & 0x000000FF) >> 0x00) / 255.0f; + } + + constexpr std::strong_ordering operator<=>(const Color&) const noexcept = default; + + Color operator+=(const Color& other) + { + this->r += other.r; + this->g += other.g; + this->b += other.b; + this->a += other.a; + + return *this; + } + + Color operator*=(const Color& other) + { + this->r *= other.r; + this->g *= other.g; + this->b *= other.b; + this->a *= other.a; + + return *this; + } + + Color operator*=(float mul) + { + this->r *= mul; + this->g *= mul; + this->b *= mul; + this->a *= mul; + + return *this; + } + + Color operator/=(float div) + { + this->r /= div; + this->g /= div; + this->b /= div; + this->a /= div; + + return *this; + } + + /* https://github.com/devkitPro/citro2d/blob/master/include/c2d/base.h#L110 */ + uint32_t rgba() const + { + uint8_t red = Color::to_uint8_t(this->r); + uint8_t green = Color::to_uint8_t(this->g); + uint8_t blue = Color::to_uint8_t(this->b); + uint8_t alpha = Color::to_uint8_t(this->a); + + return red | (green << (uint32_t)8) | (blue << (uint32_t)16) | (alpha << (uint32_t)24); + } + + uint32_t abgr() const + { + uint8_t red = Color::to_uint8_t(this->r); + uint8_t green = Color::to_uint8_t(this->g); + uint8_t blue = Color::to_uint8_t(this->b); + uint8_t alpha = Color::to_uint8_t(this->a); + + return alpha | (blue << (uint32_t)8) | (green << (uint32_t)16) | (red << (uint32_t)24); + } + + std::array array() const + { + return { this->r, this->g, this->b, this->a }; + } + + /* + ** For tex3ds-based textures + ** @param data: data from the tex3ds texture + ** @param width: power-of-two width of the data + ** @param position: Vector2 coordinate inside the image ([0-width-1], [0-height-1]) + */ + template + static T* FromTile(const void* data, const unsigned width, love::Vector2 position) + { + return ((T*)data) + indexOfTile(width, position.x, position.y); + } + + /* + ** For tex3ds-based textures + ** @param texture: C3D_Tex* holding texture data + ** @param position: Vector2 coordinate inside the image ([0-width-1], [0-height-1]) + */ + template + static V* FromTile(const T* texture, love::Vector2 position) + { + return Color::FromTile(texture->data, texture->width, position); + } + + float r; + float g; + float b; + float a; + + private: + static unsigned indexOfTile(const unsigned width, const unsigned x, const unsigned y) + { + const love::Vector2 tile(x / 8, y / 8); + const love::Vector2 sub(x % 8, y % 8); + + if ((sub.y * 8 + sub.x) > coordsTable.size()) + throw love::Exception("Out of bounds tile position: %dx%d", sub.x, sub.y); + + return ((width / 8) * tile.y + tile.x) * 64 + coordsTable[sub.y * 8 + sub.x]; + } + + static const inline std::array coordsTable = { + 0, 1, 4, 5, 16, 17, 20, 21, 2, 3, 6, 7, 18, 19, 22, 23, 8, 9, 12, 13, 24, 25, + 28, 29, 10, 11, 14, 15, 26, 27, 30, 31, 32, 33, 36, 37, 48, 49, 52, 53, 34, 35, 38, 39, + 50, 51, 54, 55, 40, 41, 44, 45, 56, 57, 60, 61, 42, 43, 46, 47, 58, 59, 62, 63, + }; + + /* https://github.com/devkitPro/citro2d/blob/master/include/c2d/base.h#L86*/ + static uint8_t to_uint8_t(const float& in) + { + return (uint8_t)(255.0f * std::clamp(in, 0.0f, 1.0f) + 0.5f); + } +}; + +struct Color32 +{ + public: + Color32() : r(0), g(0), b(0), a(0) + {} + + Color32(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) + {} + + constexpr std::strong_ordering operator<=>(const Color32&) const noexcept = default; + + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +}; \ No newline at end of file diff --git a/include/common/colors.h b/include/common/colors.h deleted file mode 100644 index e4bf4a58c..000000000 --- a/include/common/colors.h +++ /dev/null @@ -1,178 +0,0 @@ -#pragma once - -#include - -template -struct ColorT -{ - T r; - T g; - T b; - T a; - - ColorT() : r(0), g(0), b(0), a(0) - {} - - ColorT(T r_, T g_, T b_, T a_) : r(r_), g(g_), b(b_), a(a_) - {} - - void Set(T r_, T g_, T b_, T a_) - { - r = r_; - g = g_; - b = b_; - a = a_; - } - - void CopyTo(T color[4]) - { - color[0] = r; - color[1] = g; - color[2] = b; - color[3] = a; - } - - bool operator==(const ColorT& other) const; - bool operator!=(const ColorT& other) const; - - ColorT operator+=(const ColorT& other); - ColorT operator*=(const ColorT& other); - ColorT operator*=(T s); - ColorT operator/=(T s); -}; - -template -bool ColorT::operator==(const ColorT& other) const -{ - return r == other.r && g == other.g && b == other.b && a == other.a; -} - -template -bool ColorT::operator!=(const ColorT& other) const -{ - return !(operator==(other)); -} - -template -ColorT ColorT::operator+=(const ColorT& other) -{ - r += other.r; - g += other.g; - b += other.b; - a += other.a; - - return *this; -} - -template -ColorT ColorT::operator*=(const ColorT& other) -{ - r *= other.r; - g *= other.g; - b *= other.b; - a *= other.a; - - return *this; -} - -template -ColorT ColorT::operator*=(T s) -{ - r *= s; - g *= s; - b *= s; - a *= s; - - return *this; -} - -template -ColorT ColorT::operator/=(T s) -{ - r /= s; - g /= s; - b /= s; - a /= s; - - return *this; -} - -template -ColorT operator+(const ColorT& a, const ColorT& b) -{ - ColorT tmp(a); - - return tmp += b; -} - -template -ColorT operator*(const ColorT& a, const ColorT& b) -{ - ColorT res; - res.r = a.r * b.r; - res.g = a.g * b.g; - res.b = a.b * b.b; - res.a = a.a * b.a; - - return res; -} - -template -ColorT operator*(const ColorT& a, T s) -{ - ColorT tmp(a); - - return tmp *= s; -} - -template -ColorT operator/(const ColorT& a, T s) -{ - ColorT tmp(a); - - return tmp /= s; -} - -typedef ColorT Color32; -typedef ColorT Colorf; - -inline Color32 toColor32(Colorf cf) -{ - return Color32((unsigned char)(cf.r * 255.0f), (unsigned char)(cf.g * 255.0f), - (unsigned char)(cf.b * 255.0f), (unsigned char)(cf.a * 255.0f)); -} - -inline Colorf toColorf(Color32 c) -{ - return Colorf(c.r / 255.0f, c.g / 255.0f, c.b / 255.0f, c.a / 255.0f); -} - -#if defined(__3DS__) -// clang-format off -/// \brief Convert 3DS texture coordinates to pixel index -/// \param width_ Texture width (must be multiple of 8) -/// \param x_ X coordinate -/// \param y_ Y coordinate -inline unsigned coordToIndex(unsigned const width_, unsigned const x_, unsigned const y_) -{ - static unsigned char const table[] = - { - 0, 1, 4, 5, 16, 17, 20, 21, - 2, 3, 6, 7, 18, 19, 22, 23, - 8, 9, 12, 13, 24, 25, 28, 29, - 10, 11, 14, 15, 26, 27, 30, 31, - 32, 33, 36, 37, 48, 49, 52, 53, - 34, 35, 38, 39, 50, 51, 54, 55, - 40, 41, 44, 45, 56, 57, 60, 61, - 42, 43, 46, 47, 58, 59, 62, 63, - }; - - unsigned const tileX = x_ / 8; - unsigned const tileY = y_ / 8; - unsigned const subX = x_ % 8; - unsigned const subY = y_ % 8; - - return ((width_ / 8) * tileY + tileX) * 64 + table[subY * 8 + subX]; -} -// clang-format on -#endif diff --git a/include/common/console.hpp b/include/common/console.hpp new file mode 100644 index 000000000..6a1ce21bf --- /dev/null +++ b/include/common/console.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include + +#include + +namespace love +{ + class Console + { + public: + enum Platform + { + CTR, + HAC, + CAFE, + ALL + }; + + static constexpr std::string_view Name = __CONSOLE__; + + static constexpr Platform Which = (Name == "3DS") ? CTR : (Name == "Switch") ? HAC : CAFE; + + static constexpr bool Is(Console::Platform platform) + { + return Which == platform; + } + + static constexpr bool IsBigEndian() + { + return Which == CAFE; + } + + static void SetMainCore(uint32_t id) + { + if (Console::coreIdSet) + return; + + Console::coreId = id; + Console::coreIdSet = true; + } + + static uint32_t GetMainCoreId() + { + return Console::coreId; + } + + static uint32_t coreId; + static bool coreIdSet; + + Console() = delete; + }; +} // namespace love diff --git a/include/common/data.h b/include/common/data.h deleted file mode 100644 index c5d59c11f..000000000 --- a/include/common/data.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "objects/object.h" - -#include - -namespace love -{ - class Data : public Object - { - public: - static love::Type type; - - virtual ~Data() - {} - - virtual Data* Clone() const = 0; - - virtual void* GetData() const = 0; - - virtual size_t GetSize() const = 0; - }; -} // namespace love diff --git a/include/common/data.hpp b/include/common/data.hpp new file mode 100644 index 000000000..c15744199 --- /dev/null +++ b/include/common/data.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include + +namespace love +{ + class Data : public Object + { + public: + static Type type; + + virtual ~Data() + {} + + virtual Data* Clone() const = 0; + + virtual void* GetData() const = 0; + + virtual size_t GetSize() const = 0; + }; +} // namespace love diff --git a/include/common/debug/logger.h b/include/common/debug/logger.h deleted file mode 100644 index 03304f5b2..000000000 --- a/include/common/debug/logger.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -** logger.h -** @brief : Logs shit when enabled -*/ - -#pragma once - -#include "modules/thread/types/lock.h" - -#include -#include - -class Logger -{ - public: - static Logger& Instance() - { - static Logger instance; - return instance; - } - - void LogOutput(const char* func, size_t line, const char* format, ...) const; - - ~Logger(); - - private: - Logger(); - - love::thread::MutexRef mutex; - - FILE* file; - - static constexpr const char* LOG_FORMAT = "%s:%zu:\n%s\n\n"; -}; - -#if defined(__DEBUG__) - #define LOG(format, ...) \ - Logger::Instance().LogOutput(__PRETTY_FUNCTION__, __LINE__, format, ##__VA_ARGS__) -#endif \ No newline at end of file diff --git a/include/common/delay.h b/include/common/delay.h deleted file mode 100644 index 74f5b568b..000000000 --- a/include/common/delay.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -namespace love -{ - void Sleep(float ms); -} diff --git a/include/common/drawable.hpp b/include/common/drawable.hpp new file mode 100644 index 000000000..963217ec1 --- /dev/null +++ b/include/common/drawable.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "matrix.tcc" +#include "object.hpp" + +namespace love +{ + template + class Graphics; + + class Drawable : public Object + { + public: + static inline Type type = Type("Drawable", &Object::type); + + virtual ~Drawable() + {} + + virtual void Draw(Graphics& graphics, + const Matrix4& matrix) = 0; + }; +} // namespace love diff --git a/include/common/driver/audiodrvc.h b/include/common/driver/audiodrvc.h deleted file mode 100644 index 8dfcb4127..000000000 --- a/include/common/driver/audiodrvc.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "modules/thread/types/lock.h" - -namespace love::common::driver -{ - class Audrv - { - public: - Audrv(); - - bool IsInitialized() const - { - return this->initialized; - }; - - protected: - bool initialized; - }; -} // namespace love::common::driver diff --git a/include/common/driver/hidrvc.h b/include/common/driver/hidrvc.h deleted file mode 100644 index 4b2c1d978..000000000 --- a/include/common/driver/hidrvc.h +++ /dev/null @@ -1,121 +0,0 @@ -#pragma once - -#if defined(__3DS__) - #include <3ds.h> -#else - #include -#endif - -#include -#include - -#include "modules/thread/types/lock.h" -#include "modules/thread/types/mutex.h" - -namespace love::common::driver -{ - class Hidrv - { - public: - struct GamePadButton - { - size_t which; //< Gamepad ID - - const char* name; //< Button Name - int button; //< Button ID (0-index) - }; - - struct GamePadAxis - { - size_t which; //< Gamepad ID - - size_t number; - const char* axis; //< Axis name - float value; //< Axis value (0-1) - }; - - struct GamePadStatus - { - size_t which; //< Gamepad ID - - bool connected; - }; - - struct Finger - { - int64_t id; //< Touch ID - - double x; - double y; - double dx; - double dy; - double pressure; - }; - - struct Resize - { - int width; - int height; - }; - - struct LOVE_Event - { - uint8_t type; - uint8_t subType; - - GamePadStatus padStatus; - GamePadButton button; - GamePadAxis axis; - - Finger touch; - Resize size; - }; - - enum EventType - { - TYPE_GAMEPADAXIS, - TYPE_GAMEPADDOWN, - TYPE_GAMEPADUP, - - TYPE_GAMEPADADDED, - TYPE_GAMEPADREMOVED, - - TYPE_TOUCHPRESS, - TYPE_TOUCHRELEASE, - TYPE_TOUCHMOVED, - - TYPE_LOWMEMORY, - - TYPE_WINDOWEVENT, - - TYPE_FOCUS_GAINED, - TYPE_FOCUS_LOST, - - TYPE_RESIZE, - - TYPE_QUIT - }; - - Hidrv(); - - uint64_t GetButtonPressed(); - - uint64_t GetButtonReleased(); - - uint64_t GetButtonHeld(); - - virtual bool Poll(LOVE_Event* event) = 0; - - void SendFocus(bool focus); - - void SendQuit(); - - void SendLowMemory(); - - void SendResize(int width, int height); - - protected: - bool hysteresis; - std::list events; - }; -} // namespace love::common::driver diff --git a/include/common/exception.h b/include/common/exception.h deleted file mode 100644 index 38e3a6fb7..000000000 --- a/include/common/exception.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include // vararg -#include // vsnprintf -#include // strncpy -#include -#include - -namespace love -{ - class Exception : public std::exception - { - public: - Exception(const char* format, ...); - virtual ~Exception() throw(); - - inline virtual const char* what() const throw() - { - return message.c_str(); - } - - private: - std::string message; - }; -} // namespace love diff --git a/include/common/exception.hpp b/include/common/exception.hpp new file mode 100644 index 000000000..27cc2273f --- /dev/null +++ b/include/common/exception.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +namespace love +{ + class Exception : public std::exception + { + public: + template + Exception(const char* format, FormatArgs&&... args) + { + const auto size = snprintf(nullptr, 0, format, args...); + std::unique_ptr buffer = std::make_unique(size + 1); + + snprintf(buffer.get(), size + 1, format, args...); + this->message = std::string(buffer.get()); + } + + virtual ~Exception() throw(); + + inline virtual const char* what() const throw() + { + return this->message.c_str(); + } + + private: + std::string message; + }; +} // namespace love diff --git a/include/common/lmath.h b/include/common/lmath.h deleted file mode 100644 index 688a2109d..000000000 --- a/include/common/lmath.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#define _USE_MATH_DEFINES -#include -#include // for rand() and RAND_MAX -#include - -/* PI Constants */ -#define LOVE_M_PI 3.14159265358979323846 -#define LOVE_M_PI_2 1.57079632679489661923 -#define LOVE_M_PI_4 0.785398163397448309616 -#define LOVE_M_1_PI 0.318309886183790671538 -#define LOVE_M_2_PI 0.636619772367581343076 -#define LOVE_M_2_SQRTPI 1.12837916709551257390 - -/* Converting Degrees */ -#define LOVE_M_TORAD (float)(LOVE_M_PI / 180.0) -#define LOVE_M_TODEG (float)(180.0 / LOVE_M_PI) -#define LOVE_TORAD(x) (float)(x * LOVE_M_TORAD) -#define LOVE_TODEG(x) (float)(x * LOVE_M_TODEG) - -/* Misc. Math Constants */ -#define LOVE_M_E 2.71828182845904523536 -#define LOVE_M_LOG2E 1.44269504088896340736 -#define LOVE_M_LOG10E 0.434294481903251827651 -#define LOVE_M_LN2 0.693147180559945309417 -#define LOVE_M_LN10 2.30258509299404568402 - -namespace love -{ - struct Rect - { - int x, y; - int w, h; - - bool operator==(const Rect& rhs) const - { - return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h; - } - }; - - constexpr size_t LOVE_MIN_TEX = 8U; - constexpr size_t LOVE_MAX_TEX = 1024U; - - /* - ** Clamps 3DS textures between min - ** and max texture size to prevent - ** the GPU from locking up - */ - inline int NextPO2(size_t in) - { - in--; - in |= in >> 1; - in |= in >> 2; - in |= in >> 4; - in |= in >> 8; - in |= in >> 16; - in++; - - return std::clamp(in, LOVE_MIN_TEX, LOVE_MAX_TEX); - } -} // namespace love diff --git a/include/common/luax.h b/include/common/luax.h deleted file mode 100644 index 7b424774d..000000000 --- a/include/common/luax.h +++ /dev/null @@ -1,369 +0,0 @@ -/* -** common/aux.h -** @brief : Auxillary Lua functions -*/ - -#pragma once - -#define MAX_LUAOBJ_KEY 0x20000000000000ULL - -#include "common/type.h" - -extern "C" -{ -#include "lua/lauxlib.h" -#include "lua/lua.h" -#include "lua/lualib.h" - -#include "lua53/l53strlib.h" -#include "lua53/lutf8lib.h" -} - -#include -#include -#include -#include -#include - -#include "common/reference.h" - -namespace love -{ - class Object; - class Module; - - template - class StrongReference; - - enum Registry - { - REGISTRY_OBJECTS, - REGISTRY_MODULES - }; - - struct Proxy - { - love::Type* type; //< Holds type information (see types.h). - Object* object; //< Pointer to the actual object. - }; - - struct WrappedModule - { - const char* name; - love::Type* type; - - const luaL_Reg* functions; - const lua_CFunction* types; - - love::Module* instance; - }; -} // namespace love - -namespace Luax -{ - int Preload(lua_State* L, lua_CFunction func, const char* name); - - int TableInsert(lua_State* L, int index, int vindex, int position); - - inline bool OptBoolean(lua_State* L, int index, bool boolean) - { - if (lua_isboolean(L, index) == 1) - return (lua_toboolean(L, index) == 1) ? true : false; - - return boolean; - } - - inline std::string ToString(lua_State* L, int index) - { - size_t length; - const char* string = lua_tolstring(L, index, &length); - - return std::string(string, length); - } - - inline std::string CheckString(lua_State* L, int index) - { - size_t length; - const char* string = luaL_checklstring(L, index, &length); - - return std::string(string, length); - } - - inline void PushString(lua_State* L, const std::string& str) - { - lua_pushlstring(L, str.data(), str.size()); - } - - int RegisterSearcher(lua_State* L, lua_CFunction function, int position); - - love::Reference* RefIf(lua_State* L, int type); - - int Resume(lua_State* L, int nargs); - - int DoBuffer(lua_State* L, const char* buffer, size_t size, const char* name); - - int InsistGlobal(lua_State* L, const char* field); - - int Insist(lua_State* L, int index, const char* key); - - int InsistLove(lua_State* L, const char* key); - - int GetLove(lua_State* L, const char* key); - - lua_State* InsistPinnedThread(lua_State* L); - - lua_State* GetPinnedThread(lua_State* L); - - /* REGISTRY */ - - int GetRegistry(lua_State* L, love::Registry registry); - - int InsistRegistry(lua_State* L, love::Registry registry); - - int Require(lua_State* L, const char* name); - - int ConvertObject(lua_State* L, int idx, const char* mod, const char* fn); - - int ConvertObject(lua_State* L, const int idxs[], int n, const char* mod, const char* fn); - - int ConvertObject(lua_State* L, const std::vector& idxs, const char* moduleName, - const char* function); - - int AssertNilError(lua_State* L, int idx); - - int GetLOVEFunction(lua_State* L, const char* mod, const char* fn); - - void SetFunctions(lua_State* L, const luaL_Reg* l); - - int RegisterModule(lua_State* L, const love::WrappedModule& moduleName); - - int RegisterType(lua_State* L, love::Type* object, ...); - - int GarbageCollect(lua_State* L); - - int ToString(lua_State* L); - - int Equal(lua_State* L); - - love::Type* Type(lua_State* L, int idx); - - inline lua_Number CheckNumberClamped01(lua_State* L, int index) - { - return std::min(std::max(luaL_checknumber(L, index), 0.0), 1.0); - } - - inline lua_Number OptNumberClamped01(lua_State* L, int index, double def) - { - return std::min(std::max(luaL_optnumber(L, index, def), 0.0), 1.0); - } - - void GetTypeMetaTable(lua_State* L, const love::Type& type); - - void RunWrapper(lua_State* L, const char* filedata, size_t length, const char* filename, - const love::Type& type); - - int Type(lua_State* L); - - int TypeOf(lua_State* L); - - int Release(lua_State* L); - - bool IsType(lua_State* L, int index, love::Type& type); - - void RawNewType(lua_State* L, love::Type& type, love::Object* object); - - lua_Number ComputeObjectKey(lua_State* L, love::Object* object); - - int IOError(lua_State* L, const char* format, ...); - - int EnumError(lua_State* L, const char* enumName, const char* value); - - int EnumError(lua_State* L, const char* enumName, const std::vector& values, - const char* value); - - void PushType(lua_State* L, love::Type& type, love::Object* object); - - /* flags for tables */ - - bool BoolFlag(lua_State* L, int table_index, const char* key, bool defaultValue); - - int IntFlag(lua_State* L, int table_index, const char* key, int defaultValue); - - double NumberFlag(lua_State* L, int table_index, const char* key, double defaultValue); - - /* end flags for tables */ - - bool ToBoolean(lua_State* L, int index); - - bool CheckBoolean(lua_State* L, int index); - - void PushBoolean(lua_State* L, bool boolean); - - template - void PushType(lua_State* L, T* object) - { - PushType(L, T::type, object); - } - - template - void PushType(lua_State* L, love::StrongReference& object) - { - PushType(L, T::type, object); - } - - int TypeErrror(lua_State* L, int narg, const char* name); - - template - int AssertArgc(lua_State* L) - { - int argc = lua_gettop(L); - - if (argc < min) - return luaL_error(L, "Incorrect number of arguments. Expected at least %d", min); - - if (max != -1 && argc > max) - return luaL_error(L, "Incorrect number of arguments. Got [%d], expected [%d-%d]", argc, - min, max); - return 0; - } - - inline bool IsCTR() - { - if (strncmp(__CONSOLE__, "3DS", 3)) - return true; - - return false; - } - - template - T* ToType(lua_State* L, int index, const love::Type& /*type*/) - { - T* object = (T*)(((love::Proxy*)lua_touserdata(L, index))->object); - - if (object == nullptr) - luaL_error(L, "Cannot use object after it has been released."); - - return object; - } - - template - T* ToType(lua_State* L, int index) - { - return ToType(L, index, T::type); - } - - template - T* CheckType(lua_State* L, int index, const love::Type& type) - { - // If not userdata, error out - if (lua_type(L, index) != LUA_TUSERDATA) - { - const char* name = type.GetName(); - Luax::TypeErrror(L, index, name); - } - - love::Proxy* proxy = (love::Proxy*)lua_touserdata(L, index); - - // Check that it has a type and matches input - if (proxy->type == nullptr || !proxy->type->IsA(type)) - { - const char* name = type.GetName(); - Luax::TypeErrror(L, index, name); - } - - if (proxy->object == nullptr) - luaL_error(L, "Cannot use object after it has been released."); - - return (T*)proxy->object; - } - - template - bool ArgcIsNil(lua_State* L) - { - for (size_t i = 1; i <= count; i++) - { - if (!lua_isnil(L, i)) - return false; - } - - return true; - } - - template - T* CheckType(lua_State* L, int index) - { - return CheckType(L, index, T::type); - } - - int Traceback(lua_State* L); - - template - int CatchException(lua_State* L, const T& func) - { - bool should_error = false; - - try - { - func(); - } - catch (const std::exception& e) - { - should_error = true; - lua_pushstring(L, e.what()); - } - - if (should_error) - return luaL_error(L, "%s", lua_tostring(L, -1)); - - return 0; - } - - template - void CheckTableFields(lua_State* L, int idx, const char* enumName, - bool (*getConstant)(const char*, T&)) - { - luaL_checktype(L, idx, LUA_TTABLE); - - /* - ** We want to error for invalid / misspelled - ** fields in the table. - */ - lua_pushnil(L); - while (lua_next(L, idx)) - { - if (lua_type(L, -2) != LUA_TSTRING) - Luax::TypeErrror(L, -2, "string"); - - const char* key = luaL_checkstring(L, -2); - T constantvalue; - - if (!getConstant(key, constantvalue)) - Luax::EnumError(L, enumName, key); - - lua_pop(L, 1); - } - } - - template - int CatchException(lua_State* L, const T& func, const F& finallyfunc) - { - bool should_error = false; - - try - { - func(); - } - catch (const std::exception& e) - { - should_error = true; - lua_pushstring(L, e.what()); - } - - finallyfunc(should_error); - - if (should_error) - return luaL_error(L, "%s", lua_tostring(L, -1)); - - return 0; - } -}; // namespace Luax diff --git a/include/common/luax.hpp b/include/common/luax.hpp new file mode 100644 index 000000000..f376269fa --- /dev/null +++ b/include/common/luax.hpp @@ -0,0 +1,453 @@ +#pragma once + +#include + +#include +#include + +extern "C" +{ +#include +#include +#include + +#include +#include + + extern int luaopen_bit(lua_State*); +} + +#include +#include +#include +#include +#include + +namespace love +{ + class Object; + class Module; + class Reference; + class Variant; + + template + class StrongReference; + + enum Registry + { + REGISTRY_OBJECTS, + REGISTRY_MODULES + }; + + struct Proxy + { + Type* type; + Object* object; + }; + + struct WrappedModule + { + const char* name; + Type* type; + + std::span functions; + std::span extendedFunctions; + const lua_CFunction* types; + + Module* instance; + }; +} // namespace love + +typedef uint64_t objectkey_t; + +namespace luax +{ + /* ----- main stuff ----- */ + + int Preload(lua_State* L, lua_CFunction function, const char* name); + + int Require(lua_State* L, const char* name); + + int InsistGlobal(lua_State* L, const char* field); + + lua_State* InsistPinnedThread(lua_State* L); + + lua_State* GetPinnedThread(lua_State* L); + + int Insist(lua_State* L, int index, const char* key); + + int InsistLOVE(lua_State* L, const char* key); + + int GetLOVE(lua_State* L, const char* key); + + int RegisterModule(lua_State* L, const love::WrappedModule& moduleName); + + int RegisterSearcher(lua_State* L, lua_CFunction function, int position); + + // int RegisterType(lua_State* L, love::Type* object, ...); + + void RegisterTypeInit(lua_State* L, love::Type* type); + + void RegisterTypeInner(lua_State* L, std::span values); + + template + inline int RegisterType(lua_State* L, love::Type* type, T&&... values) + { + RegisterTypeInit(L, type); + (RegisterTypeInner(L, std::forward(values)), ...); + + lua_pop(L, 1); + + return 0; + } + + int TableInsert(lua_State* L, int index, int vindex, int position); + + int GetLOVEFunction(lua_State* L, const char* mod, const char* fn); + + void SetFunctions(lua_State* L, const luaL_Reg* l); + + void RawNewType(lua_State* L, love::Type& type, love::Object* object); + + objectkey_t ComputeObjectKey(lua_State* L, love::Object* object); + + void PushObjectKey(lua_State* L, objectkey_t key); + + love::Type* Type(lua_State* L, int idx); + + int Resume(lua_State* L, int numArgs); + + /* ----- registry ----- */ + + int InsistRegistry(lua_State* L, love::Registry registry); + + int GetRegistry(lua_State* L, love::Registry registry); + + /* ---- object functionality ----- */ + + int Equal(lua_State* L); + + bool IsType(lua_State* L, int index, love::Type& type); + + int GarbageCollect(lua_State* L); + + int Release(lua_State* L); + + int ToString(lua_State* L); + + int Type(lua_State* L); + + int TypeOf(lua_State* L); + + template + T* ToType(lua_State* L, int index, const love::Type& /*type*/) + { + T* object = (T*)(((love::Proxy*)lua_touserdata(L, index))->object); + + if (object == nullptr) + luaL_error(L, "Cannot use object after it has been released."); + + return object; + } + + template + T* ToType(lua_State* L, int index) + { + return ToType(L, index, T::type); + } + + int TypeError(lua_State* L, int narg, const char* name); + + template + T* CheckType(lua_State* L, int index, const love::Type& type) + { + // If not userdata, error out + if (lua_type(L, index) != LUA_TUSERDATA) + { + const char* name = type.GetName(); + luax::TypeError(L, index, name); + } + + love::Proxy* proxy = (love::Proxy*)lua_touserdata(L, index); + + // Check that it has a type and matches input + if (proxy->type == nullptr || !proxy->type->IsA(type)) + { + const char* name = type.GetName(); + luax::TypeError(L, index, name); + } + + if (proxy->object == nullptr) + luaL_error(L, "Cannot use object after it has been released."); + + return (T*)proxy->object; + } + + template + T* CheckType(lua_State* L, int index) + { + return CheckType(L, index, T::type); + } + + void PushType(lua_State* L, love::Type& type, love::Object* object); + + template + void PushType(lua_State* L, T* object) + { + PushType(L, T::type, object); + } + + template + void PushType(lua_State* L, love::StrongReference& object) + { + PushType(L, T::type, object); + } + + int ConvertObject(lua_State* L, int idx, const char* mod, const char* fn); + + int ConvertObject(lua_State* L, const int idxs[], int n, const char* mod, const char* fn); + + int ConvertObject(lua_State* L, const std::vector& idxs, const char* moduleName, + const char* function); + + /* helper functions */ + + inline void PushString(lua_State* L, std::string_view str) + { + lua_pushlstring(L, str.data(), str.size()); + } + + inline bool OptBoolean(lua_State* L, int index, bool boolean) + { + if (lua_isboolean(L, index) == 1) + return (lua_toboolean(L, index) == 1) ? true : false; + + return boolean; + } + + inline std::string_view OptString(lua_State* L, int index, std::string_view string) + { + if (lua_isstring(L, index) == 1) + { + size_t length = 0; + return lua_tolstring(L, index, &length); + } + + return string; + } + + inline lua_Number CheckNumberClamped(lua_State* L, int index, double min, double max) + { + return std::clamp(luaL_checknumber(L, index), min, max); + } + + inline lua_Number OptNumberClamped(lua_State* L, int index, double min, double max, + double defaultValue) + { + return std::clamp(luaL_optnumber(L, index, defaultValue), min, max); + } + + inline lua_Number CheckNumberClamped01(lua_State* L, int index) + { + return CheckNumberClamped(L, index, 0.0, 1.0); + } + + inline lua_Number OptNumberClamped01(lua_State* L, int index, double defaultValue) + { + return OptNumberClamped(L, index, 0.0, 1.0, defaultValue); + } + + inline bool ToBoolean(lua_State* L, int index) + { + return (lua_toboolean(L, index) != 0); + } + + inline bool CheckBoolean(lua_State* L, int index) + { + luaL_checktype(L, index, LUA_TBOOLEAN); + return luax::ToBoolean(L, index); + } + + inline float CheckFloat(lua_State* L, int index) + { + return static_cast(luaL_checknumber(L, index)); + } + + inline std::string CheckString(lua_State* L, int index) + { + size_t length = 0; + const char* string = lua_tolstring(L, index, &length); + + return std::string(string, length); + } + + inline std::string ToString(lua_State* L, int index) + { + size_t length; + const char* string = lua_tolstring(L, index, &length); + + return std::string(string, length); + } + + void GetTypeMetaTable(lua_State* L, const love::Type& type); + + void WrapObject(lua_State* L, const char* filedata, size_t length, const char* filename, + const love::Type& type); + + inline void PushBoolean(lua_State* L, bool boolean) + { + lua_pushboolean(L, boolean ? 1 : 0); + } + + void PushVariant(lua_State* L, const love::Variant& variant); + + love::Variant CheckVariant(lua_State* L, int index, bool allowUserdata = true, + std::set* tableSet = nullptr); + + size_t ObjectLength(lua_State* L, int index); + + /* ----- errors ----- */ + + int IOError(lua_State* L, const char* format, ...); + + int EnumError(lua_State* L, const char* enumName, const char* value); + + template + void CheckTableFields(lua_State* L, int idx, const char* enumName, + bool (*getConstant)(const char*)) + { + luaL_checktype(L, idx, LUA_TTABLE); + + /* we want to error for invalid / misspelled */ + /* fields in the table. */ + + lua_pushnil(L); + + while (lua_next(L, idx)) + { + if (lua_type(L, -2) != LUA_TSTRING) + luax::TypeError(L, -2, "string"); + + const char* key = luaL_checkstring(L, -2); + + bool value; + if (!(value = getConstant(key))) + luax::EnumError(L, enumName, key); + + lua_pop(L, 1); + } + } + + bool BoolFlag(lua_State* L, int tableIndex, const char* key, bool defaultValue); + + int IntFlag(lua_State* L, int tableIndex, const char* key, int defaultValue); + + double NumberFlag(lua_State* L, int tableIndex, const char* key, double defaultValue); + + int CheckIntFlag(lua_State* L, int tableIndex, const char* key); + + inline std::string concat(std::string&& first, const std::string_view& second) + { + return first.empty() ? std::string(second) : first + ", " + std::string(second); + } + + template + requires(std::is_convertible_v, std::string_view>) + int EnumError(lua_State* L, std::string_view type, const Range& values, std::string_view value) + { + std::string enums = + std::accumulate(std::begin(values), std::end(values), std::string {}, concat); + + const char* enumValue = std::string(value).c_str(); + const char* enumType = std::string(type).c_str(); + + return luaL_error(L, "Invalid %s '%s', expected one of: %s", enumType, enumValue, + enums.c_str()); + } + + // clang-format off + template KC, std::equivalence_relation VC> + int EnumError(lua_State* L, std::string_view enumName, const BidirectionalMap& map, std::string_view value) + { + return EnumError(L, enumName, map.GetNames(), value); + } + // clang-format on + + int Traceback(lua_State* L); + + love::Reference* RefIfType(lua_State* L, int type); + + template + int CatchException(lua_State* L, const T& func) + { + bool shouldError = false; + + try + { + func(); + } + catch (const std::exception& e) + { + shouldError = true; + lua_pushstring(L, e.what()); + } + + if (shouldError) + return luaL_error(L, "%s", lua_tostring(L, -1)); + + return 0; + } + + template + int CatchException(lua_State* L, const T& func, const F& finallyfunc) + { + bool should_error = false; + + try + { + func(); + } + catch (const std::exception& e) + { + should_error = true; + lua_pushstring(L, e.what()); + } + + finallyfunc(should_error); + + if (should_error) + return luaL_error(L, "%s", lua_tostring(L, -1)); + + return 0; + } + + template + int AreArgsNil(lua_State* L, int start) + { + for (size_t index = 0; index < count; index++) + { + if (!lua_isnil(L, start + index)) + return false; + } + + return true; + } + + template + int AssertArgCount(lua_State* L) + { + const auto count = lua_gettop(L); + + if (count < min) + return luaL_error(L, "Expected at least %d argument(s), got %d.", min, count); + + if (count > max && max != -1) + return luaL_error(L, "Expected at most %d argument(s), got %d.", max, count); + + return 0; + } + + int AssertIsFunction(lua_State* L, int index); + + int AssertNilError(lua_State* L, int idx); +} // namespace luax diff --git a/include/common/math.hpp b/include/common/math.hpp new file mode 100644 index 000000000..64b1b91e3 --- /dev/null +++ b/include/common/math.hpp @@ -0,0 +1,109 @@ +#pragma once + +#define _USE_MATH_DEFINES +#include +#include +#include + +/* + * Definitions of useful mathematical constants + * M_E - e + * M_LOG2E - log2(e) + * M_LOG10E - log10(e) + * M_LN2 - ln(2) + * M_LN10 - ln(10) + * M_PI - pi + * M_PI_2 - pi/2 + * M_PI_4 - pi/4 + * M_1_PI - 1/pi + * M_2_PI - 2/pi + * M_2_SQRTPI - 2/sqrt(pi) + * M_SQRT2 - sqrt(2) + * M_SQRT1_2 - 1/sqrt(2) + */ + +/* PI Constants */ +#define LOVE_M_PI 3.14159265358979323846 +#define LOVE_M_PI_2 1.57079632679489661923 +#define LOVE_M_PI_4 0.785398163397448309616 +#define LOVE_M_1_PI 0.318309886183790671538 +#define LOVE_M_2_PI 0.636619772367581343076 +#define LOVE_M_2_SQRTPI 1.12837916709551257390 +#define LOVE_M_TAU (LOVE_M_PI * 2) + +/* Converting Degrees */ +#define LOVE_M_TORAD (float)(LOVE_M_PI / 180.0) +#define LOVE_M_TODEG (float)(180.0 / LOVE_M_PI) +#define LOVE_TORAD(x) (float)(x * LOVE_M_TORAD) +#define LOVE_TODEG(x) (float)(x * LOVE_M_TODEG) + +/* Misc. Math Constants */ +#define LOVE_M_E 2.71828182845904523536 +#define LOVE_M_LOG2E 1.44269504088896340736 +#define LOVE_M_LOG10E 0.434294481903251827651 +#define LOVE_M_LN2 0.693147180559945309417 +#define LOVE_M_LN10 2.30258509299404568402 +#define LOVE_M_SQRT2 1.41421356237309504880 +#define LOVE_M_SQRT1_2 0.707106781186547524401 + +/* 3DS Texture Limits */ +#define LOVE_TEX3DS_MIN 0x08 +#define LOVE_TEX3DS_MAX 0x400 + +#define LOVE_INT8_MAX 0x7F +#define LOVE_UINT8_MAX 0xFF +#define LOVE_INT16_MAX 0x7FFF +#define LOVE_UINT16_MAX 0xFFFF +#define LOVE_INT32_MAX 0x7FFFFFFF +#define LOVE_UINT32_MAX 0xFFFFFFFF +#define LOVE_INT64_MAX 0x7FFFFFFFFFFFFFFF +#define LOVE_UINT64_MAX 0xFFFFFFFFFFFFFFFF + +namespace love +{ + struct Rect + { + static constexpr int EMPTY[4] = { -1, -1, -1, -1 }; + + int x, y; + int w, h; + + Rect() : x(0), y(0), w(0), h(0) + {} + + Rect(const int (&rect)[4]) + { + this->x = rect[0]; + this->y = rect[1]; + this->w = rect[2]; + this->h = rect[3]; + } + + Rect(int x, int y, int width, int height) : x(x), y(y), w(width), h(height) + {} + + Rect(const Rect& other) : x(other.x), y(other.y), w(other.w), h(other.h) + {} + + bool operator==(const Rect& other) const + { + return x == other.x && y == other.y && w == other.w && h == other.h; + } + }; + + /* + ** Clamps 3DS textures between min + ** and max texture size to prevent + ** the GPU from locking up + ** ---- + ** credit: https://github.com/oreo639/3ds-theoraplayer/blob/master/source/frame.c#L10 + */ + static inline unsigned NextPo2(unsigned x) + { + if (x <= 2) + return x; + + unsigned result = 1u << (32 - __builtin_clz(x - 1)); + return std::clamp(result, LOVE_TEX3DS_MIN, LOVE_TEX3DS_MAX); + } +} // namespace love diff --git a/include/common/matrix.h b/include/common/matrix.h deleted file mode 100644 index f76853242..000000000 --- a/include/common/matrix.h +++ /dev/null @@ -1,146 +0,0 @@ -#pragma once - -#if defined(__3DS__) - #include -typedef C3D_Mtx Elements; -#elif defined(__SWITCH__) -typedef float Elements[16]; -#endif - -#include "common/lmath.h" - -namespace love -{ - class Matrix4 - { - public: - Matrix4(); - - Matrix4(const Elements& matrix); - - Matrix4(const Matrix4& a, const Matrix4& b); - - Matrix4(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, - float ky); - - Matrix4(float t00, float t10, float t01, float t11, float x, float y); - - const Elements& GetElements() const; - - void SetIdentity(); - - void SetTranslation(float x, float y); - - void Translate(float x, float y); - - void Rotate(float r); - - void Scale(float sx, float sy); - - void Shear(float kx, float ky); - - bool IsAffine2DTransform() const; - - Matrix4 Inverse() const; - - void SetRawTransformation(float t00, float t10, float t01, float t11, float x, float y); - - void SetTransformation(float x, float y, float angle, float sx, float sy, float ox, - float oy, float kx, float ky); - - void Translation(float x, float y); - - void SetRotation(float r); - - void SetScale(float x, float y); - - void SetShear(float kx, float ky); - - void GetApproximateScale(float& sx, float& sy) const; - - Matrix4 operator*(const Matrix4& m) const; - - void operator*=(const Matrix4& m); - - static Matrix4 Ortho(float left, float right, float bottom, float top, float near, - float far); - - static void Multiply(const Matrix4& a, const Matrix4& b, Matrix4& result); - - void TransformXY(const Elements& elements); - - void TransformXY(); - - /** - * Transforms an array of 2-component vertices by this Matrix. The source - * and destination arrays may be the same. - **/ - template - void TransformXY(Vdst* dst, const Vsrc* src, int size) const; - - /** - * Transforms an array of 2-component vertices by this Matrix, and stores - * them in an array of 3-component vertices. - **/ - template - void TransformXY0(Vdst* dst, const Vsrc* src, int size) const; - - Elements matrix; - - private: - static void Multiply(const Matrix4& a, const Matrix4& b, Elements& c); - }; - -#if defined(__SWITCH__) - template - void Matrix4::TransformXY(Vdst* dst, const Vsrc* src, int size) const - { - for (int i = 0; i < size; i++) - { - // Store in temp variables in case src = dst - float x = (this->matrix[0] * src[i].x) + (this->matrix[4] * src[i].y) + (0) + - (this->matrix[12]); - float y = (this->matrix[1] * src[i].x) + (this->matrix[5] * src[i].y) + (0) + - (this->matrix[13]); - - dst[i].x = x; - dst[i].y = y; - } - } - - template - void Matrix4::TransformXY0(Vdst* dst, const Vsrc* src, int size) const - { - for (int i = 0; i < size; i++) - { - // Store in temp variables in case src = dst - float x = (this->matrix[0] * src[i].x) + (this->matrix[4] * src[i].y) + (0) + - (this->matrix[12]); - float y = (this->matrix[1] * src[i].x) + (this->matrix[5] * src[i].y) + (0) + - (this->matrix[13]); - float z = (this->matrix[2] * src[i].x) + (this->matrix[6] * src[i].y) + (0) + - (this->matrix[14]); - - dst[i].x = x; - dst[i].y = y; - dst[i].z = z; - } - } -#elif defined(__3DS__) - template - void Matrix4::TransformXY(Vdst* dst, const Vsrc* src, int size) const - { - for (int i = 0; i < size; i++) - { - // Store in temp variables in case src = dst - float x = (this->matrix.r[0].x * src[i].x) + (this->matrix.r[0].y * src[i].y) + (0) + - (this->matrix.r[0].w); - float y = (this->matrix.r[1].x * src[i].x) + (this->matrix.r[1].y * src[i].y) + (0) + - (this->matrix.r[1].w); - - dst[i].x = x; - dst[i].y = y; - } - } -#endif -} // namespace love diff --git a/include/common/matrix.tcc b/include/common/matrix.tcc new file mode 100644 index 000000000..c6d5ed717 --- /dev/null +++ b/include/common/matrix.tcc @@ -0,0 +1,17 @@ +#pragma once + +#include "console.hpp" +#include "math.hpp" + +#include + +namespace love +{ + template + class Matrix4 + { + public: + void Transpose() + {} + }; +} // namespace love diff --git a/include/common/message.h b/include/common/message.h deleted file mode 100644 index 0281a4018..000000000 --- a/include/common/message.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "common/variant.h" - -#include - -namespace love -{ - class Message : public Object - { - public: - Message(const std::string& name, const std::vector& args = {}); - - virtual ~Message(); - - static Message* FromLua(lua_State* L, int index); - - int ToLua(lua_State* L); - - private: - const std::string name; - const std::vector args; - }; -} // namespace love diff --git a/include/common/message.hpp b/include/common/message.hpp new file mode 100644 index 000000000..91a5a008c --- /dev/null +++ b/include/common/message.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include +#include + +namespace love +{ + class Message : public Object + { + public: + Message(const std::string& name, const std::vector& args = {}) : + name(name), + args(args) + {} + + ~Message() + {} + + const std::string name; + const std::vector args; + }; +} // namespace love diff --git a/include/common/module.h b/include/common/module.h deleted file mode 100644 index 2e5cb0a41..000000000 --- a/include/common/module.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include "common/exception.h" -#include "objects/object.h" - -#include - -namespace love -{ - class Module : public Object - { - public: - static love::Type type; - - enum ModuleType - { - M_AUDIO, - M_DATA, - M_EVENT, - M_FILESYSTEM, - M_FONT, - M_GRAPHICS, - M_IMAGE, - M_JOYSTICK, - M_KEYBOARD, - M_MATH, - M_PHYSICS, - M_SYSTEM, - M_SOUND, - M_THREAD, - M_TIMER, - M_TOUCH, - M_WINDOW, - M_VIDEO, - M_MAX_ENUM - }; - - virtual ~Module(); - - virtual ModuleType GetModuleType() const = 0; - virtual const char* GetName() const = 0; - - static void RegisterInstance(Module* instance); - - Module* GetInstance(const std::string& name); - - template - static T* GetInstance(ModuleType type) - { - return (T*)instances[type]; - } - - private: - static Module* instances[M_MAX_ENUM]; - }; -} // namespace love diff --git a/include/common/module.hpp b/include/common/module.hpp new file mode 100644 index 000000000..9e8d91740 --- /dev/null +++ b/include/common/module.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include +#include + +#include + +namespace love +{ + class Module : public Object + { + public: + static love::Type type; + + enum ModuleType + { + M_AUDIO, + M_DATA, + M_EVENT, + M_FILESYSTEM, + M_FONT, + M_GRAPHICS, + M_IMAGE, + M_JOYSTICK, + M_KEYBOARD, + M_MATH, + M_PHYSICS, + M_SYSTEM, + M_SENSOR, + M_SOUND, + M_THREAD, + M_TIMER, + M_TOUCH, + M_WINDOW, + M_VIDEO, + M_MAX_ENUM + }; + + virtual ~Module(); + + virtual ModuleType GetModuleType() const = 0; + + virtual const char* GetName() const = 0; + + static void RegisterInstance(Module* instance); + + Module* GetInstance(const std::string& name); + + template + static T* GetInstance(ModuleType type) + { + return (T*)instances[type]; + } + + private: + static Module* instances[M_MAX_ENUM]; + }; +} // namespace love diff --git a/include/common/object.hpp b/include/common/object.hpp new file mode 100644 index 000000000..b61d9d281 --- /dev/null +++ b/include/common/object.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include + +namespace love +{ + class Object + { + public: + static Type type; + + Object(); + + Object(const Object& other); + + virtual ~Object() = 0; + + void Retain(); + + int GetReferenceCount() const; + + void Release(); + + private: + std::atomic count; + }; +} // namespace love diff --git a/include/common/pixelformat.h b/include/common/pixelformat.h deleted file mode 100644 index ada3f9839..000000000 --- a/include/common/pixelformat.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -namespace love -{ - enum PixelFormat - { - PIXELFORMAT_UNKNOWN, - - PIXELFORMAT_NORMAL, - - PIXELFORMAT_TEX3DS_RGBA8, - - // "regular" formats - PIXELFORMAT_RGB8, - - PIXELFORMAT_RGBA8, - PIXELFORMAT_RGBA16, - - PIXELFORMAT_R8, - - PIXELFORMAT_RGBA4, - PIXELFORMAT_RGB565, - - PIXELFORMAT_LA8, - - // depth/stencil - PIXELFORMAT_STENCIL8, - PIXELFORMAT_DEPTH16, - PIXELFORMAT_DEPTH24, - PIXELFORMAT_DEPTH32F, - PIXELFORMAT_DEPTH24_STENCIL8, - PIXELFORMAT_DEPTH2432F_STENCIL8, - - // compressed formats - PIXELFORMAT_DXT1, - PIXELFORMAT_DXT3, - PIXELFORMAT_DXT5, - PIXELFORMAT_BC4, - PIXELFORMAT_BC4s, - PIXELFORMAT_BC5, - PIXELFORMAT_BC5s, - PIXELFORMAT_BC6H, - PIXELFORMAT_BC6Hs, - PIXELFORMAT_BC7, - PIXELFORMAT_PVR1_RGB2, - PIXELFORMAT_PVR1_RGB4, - PIXELFORMAT_PVR1_RGBA2, - PIXELFORMAT_PVR1_RGBA4, - PIXELFORMAT_ETC1, - PIXELFORMAT_ETC2_RGB, - PIXELFORMAT_ETC2_RGBA, - PIXELFORMAT_ETC2_RGBA1, - PIXELFORMAT_EAC_R, - PIXELFORMAT_EAC_Rs, - PIXELFORMAT_EAC_RG, - PIXELFORMAT_EAC_RGs, - PIXELFORMAT_ASTC_4x4, - PIXELFORMAT_ASTC_5x4, - PIXELFORMAT_ASTC_5x5, - PIXELFORMAT_ASTC_6x5, - PIXELFORMAT_ASTC_6x6, - PIXELFORMAT_ASTC_8x5, - PIXELFORMAT_ASTC_8x6, - PIXELFORMAT_ASTC_8x8, - PIXELFORMAT_ASTC_10x5, - PIXELFORMAT_ASTC_10x6, - PIXELFORMAT_ASTC_10x8, - PIXELFORMAT_ASTC_10x10, - PIXELFORMAT_ASTC_12x10, - PIXELFORMAT_ASTC_12x12, - - PIXELFORMAT_MAX_ENUM - }; - - int GetPixelFormatColorComponents(PixelFormat format); - - unsigned GetPixelFormatSize(PixelFormat format); - - bool IsPixelFormatCompressed(PixelFormat format); -} // namespace love diff --git a/include/common/pixelformat.hpp b/include/common/pixelformat.hpp new file mode 100644 index 000000000..90f24cbca --- /dev/null +++ b/include/common/pixelformat.hpp @@ -0,0 +1,271 @@ +#pragma once + +#include + +#include "utilities/bidirectionalmap/bidirectionalmap.hpp" + +namespace love +{ + enum PixelFormat + { + /* standard */ + PIXELFORMAT_UNKNOWN, + + PIXELFORMAT_NORMAL, + PIXELFORMAT_HDR, + + /* single channel */ + PIXELFORMAT_R8_UNORM, + PIXELFORMAT_R8_INT, + PIXELFORMAT_R8_UINT, + PIXELFORMAT_R16_UNORM, + PIXELFORMAT_R16_FLOAT, + PIXELFORMAT_R16_INT, + PIXELFORMAT_R16_UINT, + PIXELFORMAT_R32_FLOAT, + PIXELFORMAT_R32_INT, + PIXELFORMAT_R32_UINT, + + /* dual channel */ + PIXELFORMAT_RG8_UNORM, + PIXELFORMAT_RG8_INT, + PIXELFORMAT_RG8_UINT, + PIXELFORMAT_LA8_UNORM, + PIXELFORMAT_RG16_UNORM, + PIXELFORMAT_RG16_FLOAT, + PIXELFORMAT_RG16_INT, + PIXELFORMAT_RG16_UINT, + PIXELFORMAT_RG32_FLOAT, + PIXELFORMAT_RG32_INT, + PIXELFORMAT_RG32_UINT, + + /* triple channel */ + PIXELFORMAT_RGB8, + + /* quad channel */ + PIXELFORMAT_RGBA8_UNORM, + PIXELFORMAT_RGBA8_UNORM_SRGB, + PIXELFROMAT_BGRA8_UNORM, + PIXELFORMAT_BGRA8_UNORM_SRGB, + PIXELFORMAT_RGBA8_INT, + PIXELFORMAT_RGBA8_UINT, + PIXELFORMAT_RGBA16_UNORM, + PIXELFORMAT_RGBA16_FLOAT, + PIXELFORMAT_RGBA16_INT, + PIXELFORMAT_RGBA16_UINT, + PIXELFORMAT_RGBA32_FLOAT, + PIXELFORMAT_RGBA32_INT, + PIXELFORMAT_RGBA32_UINT, + + /* packed formats */ + PIXELFORMAT_RGBA4_UNORM, + PIXELFORMAT_RGB5A1_UNORM, + PIXELFORMAT_RGB565_UNORM, + PIXELFORMAT_RGB10A2_UNORM, + PIXELFORMAT_RG11B10_FLOAT, + + /* depth/stencil */ + PIXELFORMAT_STENCIL8, + PIXELFORMAT_DEPTH16_UNORM, + PIXELFORMAT_DEPTH24_UNORM, + PIXELFORMAT_DEPTH32_FLOAT, + PIXELFORMAT_DEPTH24_UNORM_STENCIL8, + PIXELFORMAT_DEPTH32_FLOAT_STENCIL8, + + /* compressed dxt */ + PIXELFORMAT_DXT1_UNORM, + PIXELFORMAT_DXT3_UNORM, + PIXELFORMAT_DXT5_UNORM, + + /* compressed bc4 */ + PIXELFORMAT_BC4_UNORM, + PIXELFORMAT_BC4_SNORM, + PIXELFORMAT_BC5_UNORM, + PIXELFORMAT_BC5_SNORM, + PIXELFORMAT_BC6H_UFLOAT, + PIXELFORMAT_BC6H_SFLOAT, + PIXELFORMAT_BC7_UNORM, + PIXELFORMAT_BC7_UNORM_SRGB, + + /* compressed pvr */ + PIXELFORMAT_PVR1_RGB2_UNORM, + PIXELFORMAT_PVR1_RGB4_UNORM, + PIXELFORMAT_PVR1_RGBA2_UNORM, + PIXELFORMAT_PVR1_RGBA4_UNORM, + + /* compressed etc */ + PIXELFORMAT_ETC1_UNORM, + PIXELFORMAT_ETC2_RGB_UNORM, + PIXELFORMAT_ETC2_RGBA_UNORM, + PIXELFORMAT_ETC2_RGBA1_UNORM, + + /* compressed eac */ + PIXELFORMAT_EAC_R_UNORM, + PIXELFORMAT_EAC_R_SNORM, + PIXELFORMAT_EAC_RG_UNORM, + PIXELFORMAT_EAC_RG_SNORM, + + /* compressed astc */ + PIXELFORMAT_ASTC_4x4, + PIXELFORMAT_ASTC_5x4, + PIXELFORMAT_ASTC_5x5, + PIXELFORMAT_ASTC_6x5, + PIXELFORMAT_ASTC_6x6, + PIXELFORMAT_ASTC_8x5, + PIXELFORMAT_ASTC_8x6, + PIXELFORMAT_ASTC_8x8, + PIXELFORMAT_ASTC_10x5, + PIXELFORMAT_ASTC_10x6, + PIXELFORMAT_ASTC_10x8, + PIXELFORMAT_ASTC_10x10, + PIXELFORMAT_ASTC_12x10, + PIXELFORMAT_ASTC_12x12, + + /* ------------ */ + PIXELFORMAT_MAX_ENUM + }; + + enum PixelFormatUsage + { + PIXELFORMAT_USAGE_SAMPLE, // Any sampling in shaders. + PIXELFORMAT_USAGE_LINEAR, // Linear filtering. + PIXELFORMAT_USAGE_RENDERTARGET, // Usable as a render target. + PIXELFORMAT_USAGE_BLEND, // Blend support when used as a render target. + PIXELFORMAT_USAGE_MSAA, // MSAA support when used as a render target. + PIXELFORMAT_USAGE_COMPUTEWRITE, // Writable in compute shaders via imageStore. + PIXELFORMAT_USAGE_MAX_ENUM + }; + + enum PixelFormatUsageFlags + { + PIXELFORMAT_USAGE_FLAGS_NONE = 0, + PIXELFORMAT_USAGE_FLAGS_SAMPLE = (1 << PIXELFORMAT_USAGE_SAMPLE), + PIXELFORMAT_USAGE_FLAGS_LINEAR = (1 << PIXELFORMAT_USAGE_LINEAR), + PIXELFORMAT_USAGE_FLAGS_RENDERTARGET = (1 << PIXELFORMAT_USAGE_RENDERTARGET), + PIXELFORMAT_USAGE_FLAGS_BLEND = (1 << PIXELFORMAT_USAGE_BLEND), + PIXELFORMAT_USAGE_FLAGS_MSAA = (1 << PIXELFORMAT_USAGE_MSAA), + PIXELFORMAT_USAGE_FLAGS_COMPUTEWRITE = (1 << PIXELFORMAT_USAGE_COMPUTEWRITE), + }; + + enum PixelFormatType + { + PIXELFORMAT_TYPE_UNORM, + PIXELFORMAT_TYPE_SNORM, + PIXELFORMAT_TYPE_UFLOAT, + PIXELFORMAT_TYPE_SFLOAT, + PIXELFORMAT_TYPE_UINT, + PIXELFORMAT_TYPE_SINT + }; + + struct PixelFormatInfo + { + int components; + size_t blockWidth; + size_t blockHeight; + size_t blockSize; + bool color; + bool depth; + bool stencil; + bool compressed; + bool sRGB; + PixelFormatType dataType; + }; + + bool GetPixelFormatConstant(const char* in, PixelFormat& out); + + bool GetPixelFormatConstant(PixelFormat in, const char*& out); + + const char* GetPixelFormatName(PixelFormat format); + + const PixelFormatInfo& GetPixelFormatInfo(PixelFormat format); + + bool IsPixelFormatCompressed(PixelFormat format); + + bool IsPixelFormatColor(PixelFormat format); + + bool IsPixelFormatDepthStencil(PixelFormat format); + + bool IsPixelFormatDepth(PixelFormat format); + + bool IsPixelFormatStencil(PixelFormat format); + + bool IsPixelFormatSRGB(PixelFormat format); + + bool IsPixelFormatInteger(PixelFormat format); + + PixelFormat GetSRGBPixelFormat(PixelFormat format); + + PixelFormat GetLinearPixelFormat(PixelFormat format); + + size_t GetPixelFormatBlockSize(PixelFormat format); + + size_t GetPixelFormatUncompressedRowSize(PixelFormat format, int width); + + size_t GetPixelFormatCompressedBlockRowSize(PixelFormat format, int width); + + size_t GetPixelFormatCompressedBlockRowCount(PixelFormat format, int height); + + size_t GetPixelFormatSliceSize(PixelFormat format, int width, int height, + bool needPowerOfTwo = true); + + size_t GetPixelFormatColorComponents(PixelFormat format); + + // clang-format off + static constexpr BidirectionalMap pixelFormats = { + "unknown", PIXELFORMAT_UNKNOWN, + + "normal", PIXELFORMAT_NORMAL, + "r8", PIXELFORMAT_R8_UNORM, + "rgb565", PIXELFORMAT_RGB565_UNORM, + "rgba8", PIXELFORMAT_RGBA8_UNORM, + "rgba16", PIXELFORMAT_RGBA16_UNORM, + + "stencil8", PIXELFORMAT_STENCIL8, + "depth16", PIXELFORMAT_DEPTH16_UNORM, + "depth24", PIXELFORMAT_DEPTH24_UNORM, + "depth32f", PIXELFORMAT_DEPTH32_FLOAT, + "depth24stencil8", PIXELFORMAT_DEPTH24_UNORM_STENCIL8, + + "DXT1", PIXELFORMAT_DXT1_UNORM, + "DXT3", PIXELFORMAT_DXT3_UNORM, + "DXT5", PIXELFORMAT_DXT5_UNORM, + + "BC4", PIXELFORMAT_BC4_UNORM, + "BC4s", PIXELFORMAT_BC4_SNORM, + "BC5", PIXELFORMAT_BC5_UNORM, + "BC5s", PIXELFORMAT_BC5_SNORM, + "BC6h", PIXELFORMAT_BC6H_UFLOAT, + "BC6hs", PIXELFORMAT_BC6H_SFLOAT, + "BC7", PIXELFORMAT_BC7_UNORM, + + "PVR1rgb2", PIXELFORMAT_PVR1_RGB2_UNORM, + "PVR1rgb4", PIXELFORMAT_PVR1_RGB4_UNORM, + "PVR1rgba2", PIXELFORMAT_PVR1_RGBA2_UNORM, + "PVR1rgba4", PIXELFORMAT_PVR1_RGBA4_UNORM, + + "ETC1", PIXELFORMAT_ETC1_UNORM, + "ETC2rgb", PIXELFORMAT_ETC2_RGB_UNORM, + "ETC2rgba", PIXELFORMAT_ETC2_RGBA_UNORM, + "ETC2rgba1", PIXELFORMAT_ETC2_RGBA1_UNORM, + "EACr", PIXELFORMAT_EAC_R_UNORM, + "EACrs", PIXELFORMAT_EAC_R_SNORM, + "EACrg", PIXELFORMAT_EAC_RG_UNORM, + "EACrgs", PIXELFORMAT_EAC_RG_SNORM, + + "ASTC4x4", PIXELFORMAT_ASTC_4x4, + "ASTC5x4", PIXELFORMAT_ASTC_5x4, + "ASTC5x5", PIXELFORMAT_ASTC_5x5, + "ASTC6x5", PIXELFORMAT_ASTC_6x5, + "ASTC6x6", PIXELFORMAT_ASTC_6x6, + "ASTC8x5", PIXELFORMAT_ASTC_8x5, + "ASTC8x6", PIXELFORMAT_ASTC_8x6, + "ASTC8x8", PIXELFORMAT_ASTC_8x8, + "ASTC10x5", PIXELFORMAT_ASTC_10x5, + "ASTC10x6", PIXELFORMAT_ASTC_10x6, + "ASTC10x8", PIXELFORMAT_ASTC_10x8, + "ASTC10x10", PIXELFORMAT_ASTC_10x10, + "ASTC12x10", PIXELFORMAT_ASTC_12x10, + "ASTC12x12", PIXELFORMAT_ASTC_12x12 + }; + // clang-format on +} // namespace love diff --git a/include/common/range.hpp b/include/common/range.hpp new file mode 100644 index 000000000..4a2b27a0d --- /dev/null +++ b/include/common/range.hpp @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2006-2023 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#pragma once + +#include +#include +#include + +namespace love +{ + +struct Range +{ + size_t first; + size_t last; + + Range() + : first(std::numeric_limits::max()) + , last(0) + {} + + Range(size_t offset, size_t size) + : first(offset) + , last(offset + size - 1) + {} + + bool isValid() const { return first <= last; } + + void invalidate() + { + first = std::numeric_limits::max(); + last = 0; + } + + size_t getMin() const { return first; } + size_t getMax() const { return last; } + + size_t getOffset() const { return first; } + size_t getSize() const { return (last - first) + 1; } + + bool contains(const Range &other) const + { + return first <= other.first && last >= other.last; + } + + bool intersects(const Range &other) + { + return !(first > other.last || last < other.first); + } + + void intersect(const Range &other) + { + first = std::max(first, other.first); + last = std::min(last, other.last); + } + + void encapsulate(size_t index) + { + first = std::min(first, index); + last = std::max(last, index); + } + + void encapsulate(size_t offset, size_t size) + { + first = std::min(first, offset); + last = std::max(last, offset + size - 1); + } + + void encapsulate(const Range &other) + { + first = std::min(first, other.first); + last = std::max(last, other.last); + } +}; + +} // love diff --git a/include/common/reference.h b/include/common/reference.h deleted file mode 100644 index 502673b2a..000000000 --- a/include/common/reference.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -struct lua_State; - -namespace love -{ - class Reference - { - public: - Reference(); - - Reference(lua_State* L); - - virtual ~Reference(); - - void Ref(lua_State* L); - - void UnRef(); - - void Push(lua_State* L); - - private: - lua_State* pinnedState; - int index; - }; -} // namespace love diff --git a/include/common/reference.hpp b/include/common/reference.hpp new file mode 100644 index 000000000..a80bdaee8 --- /dev/null +++ b/include/common/reference.hpp @@ -0,0 +1,26 @@ +#pragma once + +struct lua_State; + +namespace love +{ + class Reference + { + public: + Reference(); + + Reference(lua_State* L); + + ~Reference(); + + void Create(lua_State* L); + + void UnReference(); + + void Push(lua_State* L) const; + + private: + lua_State* pinnedState; + int index; + }; +} // namespace love \ No newline at end of file diff --git a/include/common/results.h b/include/common/results.h deleted file mode 100644 index 28538b7be..000000000 --- a/include/common/results.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#if defined(__SWITCH__) - #include - #define __CONSOLE_ABORT(res_expr) diagAbortWithResult(res_expr) - #define MAX_GAMEPADS 4 -#elif defined(__3DS__) - #include <3ds.h> - #define __CONSOLE_ABORT(res_expr) svcBreak(USERBREAK_PANIC) -#endif - -#include - -static std::string LOVE_STRING_EMPTY; - -/* -** Evaluates an expression that returns a result and -** returns the result if it would fail. -*/ -#define R_TRY(res_expr) \ - ({ \ - const auto _tmp_r_try_rc = (res_expr); \ - if (R_FAILED(_tmp_r_try_rc)) \ - return _tmp_r_try_rc; \ - }) - -/* -** Evaluates an expression that returns a result and -** returns the throw_result if it would fail. -*/ -#define R_UNLESS(res_expr, throw_result) \ - ({ \ - const auto _tmp_r_try_rc = (res_expr); \ - if (R_FAILED(_tmp_r_try_rc)) \ - return (throw_result); \ - }) - -/* -** Evaluates an expression that returns a result and -** calls the apropriate abort method if it fails -** Only used during userAppInit() -*/ -#define R_ABORT_UNLESS(res_expr) \ - ({ \ - const auto _tmp_r_try_rc = (res_expr); \ - if (R_FAILED(_tmp_r_try_rc)) \ - __CONSOLE_ABORT(_tmp_r_try_rc); \ - }) - -#define R_ABORT_LAMBDA_UNLESS(res_expr, lambda) \ - ({ \ - const auto _tmp_r_try_rc = (res_expr); \ - const auto _tmp_lambda = (lambda); \ - if (R_FAILED(_tmp_r_try_rc)) \ - { \ - _tmp_lambda(); \ - __CONSOLE_ABORT(_tmp_r_try_rc); \ - } \ - }) diff --git a/include/common/screen.hpp b/include/common/screen.hpp new file mode 100644 index 000000000..810fc9d9e --- /dev/null +++ b/include/common/screen.hpp @@ -0,0 +1,113 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace love +{ + struct ScreenInfo + { + int8_t id; + std::string_view name; + int width; + int height; + }; + + enum Screen : int8_t; + + inline Screen currentScreen = (Screen)0; + static constexpr Screen SCREEN_INVALID = (Screen)-1; + static constexpr inline Screen DEFAULT_SCREEN = (Screen)0; + + const ScreenInfo& GetScreenInfo(Screen); + std::span GetScreenInfo(); + + inline bool IsActiveScreenValid() + { + return currentScreen != SCREEN_INVALID; + } + + inline bool IsScreenActive(Screen id) + { + return currentScreen == id; + } + + inline Screen GetActiveScreen() + { + return currentScreen; + } + + inline void SetActiveScreen(Screen id) + { + currentScreen = id; + } + + inline int GetScreenWidth(Screen id = currentScreen) + { + const auto& info = GetScreenInfo(id); + + return info.width; + } + + inline int GetScreenHeight(Screen id = currentScreen) + { + const auto& info = GetScreenInfo(id); + + return info.height; + } + + inline std::string_view GetScreenName(Screen id = currentScreen) + { + const auto& info = GetScreenInfo(id); + + return info.name; + } + + inline std::string_view GetDefaultScreen() + { + return GetScreenName((Screen)0); + } + + inline bool CheckScreenName(std::string_view name, Screen& screen) + { + const auto& info = GetScreenInfo(); + + for (auto& value : info) + { + if (value.name == name) + { + screen = (Screen)value.id; + return true; + } + } + + return false; + } + + inline std::vector GetScreens() + { + std::vector result {}; + const auto& info = GetScreenInfo(); + + for (auto& item : info) + result.push_back(item.name); + + return result; + } + + inline std::vector GetScreenEnums() + { + std::vector result {}; + const auto& info = GetScreenInfo(); + + for (auto& item : info) + result.push_back((Screen)item.id); + + return result; + } +} // namespace love diff --git a/include/common/screenc.h b/include/common/screenc.h deleted file mode 100644 index c29d2afab..000000000 --- a/include/common/screenc.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#if defined(__3DS__) - #include <3ds.h> -#elif defined(__SWITCH__) - #include -#endif - -#include - -typedef uint8_t RenderScreen; - -namespace love::common -{ - class Screen - { - protected: - template - static bool FindSetCast(const auto& BiMap, const char* in, RenderScreen& value) - { - T temp = static_cast(0); - bool success = BiMap.Find(in, temp); - - value = static_cast(temp); - return success; - } - - template - static bool ReverseFindSetCast(const auto& BiMap, RenderScreen& in, const char*& value) - { - T temp = static_cast(0); - bool success = BiMap.ReverseFind(temp, value); - - return success; - } - - void SetActiveScreen(RenderScreen screen) - { - this->current = screen; - } - - virtual int GetWidth(RenderScreen screen = 0) = 0; - - virtual int GetHeight() = 0; - - protected: - RenderScreen current; - }; -} // namespace love::common diff --git a/include/common/strongref.h b/include/common/strongreference.hpp similarity index 100% rename from include/common/strongref.h rename to include/common/strongreference.hpp diff --git a/include/common/type.h b/include/common/type.h deleted file mode 100644 index 988781676..000000000 --- a/include/common/type.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include - -#if defined(__3DS__) - #include <3ds.h> -#elif defined(__SWITCH__) - #include -#endif - -namespace love -{ - class Type - { - public: - static const uint32_t MAX_TYPES = 128; - - Type(const char* name, Type* parent); - - Type(const Type&) = delete; - - void Init(); - - const char* GetName() const; - - bool IsA(const love::Type& other); - - bool IsA(const uint32_t& other); - - static Type* ByName(const char* name); - - private: - const char* const name; - Type* const parent; - - uint32_t id; - bool initialized; - - std::bitset m_bits; - - static inline std::unordered_map m_types = {}; - }; -} // namespace love diff --git a/include/common/type.hpp b/include/common/type.hpp new file mode 100644 index 000000000..275236f54 --- /dev/null +++ b/include/common/type.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include + +namespace love +{ + class Type + { + public: + static constexpr uint32_t MAX_TYPES = 0x80; + + Type(const char* name, Type* parent); + + Type(const Type&) = delete; + + void Initialize(); + + const char* GetName() const; + + bool IsA(const Type& other); + + bool IsA(const uint32_t& id); + + static Type* ByName(const char* name); + + private: + const char* const name; + Type* const parent; + + uint32_t id; + bool initialized; + + std::bitset bits; + }; +} // namespace love diff --git a/include/common/variant.h b/include/common/variant.h deleted file mode 100644 index 19b686795..000000000 --- a/include/common/variant.h +++ /dev/null @@ -1,136 +0,0 @@ -#pragma once - -#include "common/luax.h" - -#include "common/exception.h" -#include "objects/object.h" - -#include -#include -#include - -namespace love -{ - class Variant - { - static const int MAX_SMALL_STRING_LENGTH = 15; - - struct Nil - { - }; - - class SharedString : public Object - { - public: - SharedString(const char* string, size_t length) : length(length) - { - this->string = new char[length + 1]; - this->string[length] = '\0'; - memcpy(this->string, string, length); - } - - virtual ~SharedString() - { - delete[] this->string; - } - - char* string; - size_t length; - }; - - struct SmallString - { - char string[MAX_SMALL_STRING_LENGTH]; - uint8_t length; - }; - - class SharedTable : public Object - { - public: - SharedTable(std::vector>* table) : table(table) - {} - - virtual ~SharedTable() - { - delete this->table; - } - - std::vector>* table; - }; - - private: - std::variant - variant; - - public: - enum Type - { - UNKNOWN = 0, - BOOLEAN, - NUMBER, - STRING, - SMALLSTRING, - TABLE, - LUSERDATA, - LOVE_OBJECT, - NIL - }; - - Variant() : variant(Nil()) - {} - - Variant(std::monostate v) : variant(v) - {} - - Variant(bool v) : variant(v) - {} - - Variant(float v) : variant(v) - {} - - Variant(const std::string& v); - - Variant(const char* v, size_t length); - - Variant(std::vector>* table); - - Variant(void* v) : variant(v) - {} - - Variant(love::Type* type, Object* object); - - Variant(Variant::Nil v) : variant(v) - {} - - Variant(const Variant& v); - - Variant& operator=(const Variant& v); - - Variant(Variant&& other); - - ~Variant(); - - static Proxy* TryExtractProxy(lua_State* L, size_t index); - - Type GetType() const; - - template - const std::variant_alternative_t& GetValue() const - { - return std::get(variant); - } - - template - std::variant_alternative_t& GetValue() - { - return std::get(variant); - } - - std::string GetTypeString() const; - - static Variant FromLua(lua_State* L, int n, std::set* tableSet = nullptr); - - void ToLua(lua_State* L) const; - }; -} // namespace love diff --git a/include/common/variant.hpp b/include/common/variant.hpp new file mode 100644 index 000000000..f5336b44b --- /dev/null +++ b/include/common/variant.hpp @@ -0,0 +1,136 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace love +{ + class Variant + { + + public: + using VariantPair = std::pair; + + static constexpr int MAX_SMALL_STRING_LENGTH = 0x0F; + + class SharedString : public Object + { + public: + SharedString(const char* string, size_t length) : length(length) + { + this->string = new char[length + 1]; + this->string[length] = '\0'; + + std::memcpy(this->string, string, length); + } + + virtual ~SharedString() + { + delete[] this->string; + } + + char* string; + size_t length; + }; + + class SharedTable : public Object + { + public: + SharedTable() + {} + + virtual ~SharedTable() + {} + + std::vector pairs; + }; + + enum Type + { + UNKNOWN = 0, + BOOLEAN, + NUMBER, + STRING, + SMALLSTRING, + LUSERDATA, + LOVEOBJECT, + NIL, + TABLE + }; + + union Data + { + bool boolean; + double number; + SharedString* string; + void* userdata; + Proxy objectproxy; + SharedTable* table; + struct + { + char str[MAX_SMALL_STRING_LENGTH]; + uint8_t len; + } smallstring; + }; + + Variant(); + + Variant(bool boolean); + + Variant(double number); + + Variant(const char* str, size_t len); + + Variant(const std::string& str); + + Variant(void* lightuserdata); + + Variant(love::Type* type, love::Object* object); + + Variant(SharedTable* table); + + Variant(const Variant& other); + + Variant(Variant&& other); + + ~Variant(); + + Variant& operator=(const Variant& other); + + Variant::Type GetType() const + { + return this->type; + }; + + const Data& GetData() const + { + return this->data; + } + + static Variant Unknown() + { + return Variant(UNKNOWN); + } + + bool Is(Variant::Type type) + { + return this->type == type; + } + + bool Is(Variant::Type type) const + { + return this->type == type; + } + + private: + Variant::Type type; + Data data; + + Variant(Variant::Type type); + }; +} // namespace love diff --git a/include/common/vector.h b/include/common/vector.h deleted file mode 100644 index c22ce1f20..000000000 --- a/include/common/vector.h +++ /dev/null @@ -1,482 +0,0 @@ -/** - * Copyright (c) 2006-2020 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ - -#ifndef LOVE_VECTOR_H -#define LOVE_VECTOR_H - -// STD -#include - -namespace love -{ - - // All math operators are component-wise. - struct Vector2 - { - float x, y; - - Vector2() : x(0.0f), y(0.0f) - {} - - Vector2(float x, float y) : x(x), y(y) - {} - - Vector2(const Vector2& v) : x(v.x), y(v.y) - {} - - static const char* toString() - { - return "Vector2(%f, %f)"; - } - - float getLength() const; - float getLengthSquare() const; - - /** - * Normalizes the Vector. - * @param length Desired length of the vector. - **/ - void normalize(float length = 1.0f); - - /** - * Gets a vector perpendicular to the Vector. - * To get the true (normalized) normal, use v.getNormal(1.0f / v.getLength()) - **/ - Vector2 getNormal() const; - - /** - * Gets a vector perpendicular to the Vector. - * To get the true (normalized) normal, use v.getNormal(1.0f / v.getLength()) - **/ - Vector2 getNormal(float scale) const; - - static inline float dot(const Vector2& a, const Vector2& b); - static inline float cross(const Vector2& a, const Vector2& b); - - Vector2 operator+(const Vector2& v) const; - Vector2 operator-(const Vector2& v) const; - - Vector2 operator*(float s) const; - Vector2 operator/(float s) const; - - Vector2 operator-() const; - - void operator+=(const Vector2& v); - void operator-=(const Vector2& v); - - void operator*=(float s); - void operator/=(float s); - - bool operator==(const Vector2& v) const; - bool operator!=(const Vector2& v) const; - - }; // Vector2 - - // All math operators are component-wise. - struct Vector3 - { - float x, y, z; - - Vector3() : x(0.0f), y(0.0f), z(0.0f) - {} - - Vector3(float x, float y, float z) : x(x), y(y), z(z) - {} - - Vector3(const Vector2& v, float z = 0.0f) : x(v.x), y(v.y), z(z) - {} - - float getLength() const; - float getLengthSquare() const; - - /** - * Normalizes the Vector. - * @param length Desired length of the vector. - **/ - void normalize(float length = 1.0); - - static inline float dot(const Vector3& a, const Vector3& b); - static inline Vector3 cross(const Vector3& a, const Vector3& b); - - Vector3 operator+(const Vector3& v) const; - Vector3 operator-(const Vector3& v) const; - - Vector3 operator*(float s) const; - Vector3 operator/(float s) const; - - Vector3 operator-() const; - - void operator+=(const Vector3& v); - void operator-=(const Vector3& v); - - void operator*=(float s); - void operator/=(float s); - - bool operator==(const Vector3& v) const; - bool operator!=(const Vector3& v) const; - - }; // Vector3 - - // All math operators are component-wise. - struct Vector4 - { - float x, y, z, w; - - Vector4() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) - {} - - Vector4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) - {} - - Vector4(const Vector2& v, float z = 0.0f, float w = 0.0f) : x(v.x), y(v.y), z(z), w(w) - {} - - Vector4(const Vector3& v, float w = 0.0f) : x(v.x), y(v.y), z(v.z), w(w) - {} - - float getLength() const; - float getLengthSquare() const; - - /** - * Normalizes the Vector. - * @param length Desired length of the vector. - **/ - void normalize(float length = 1.0); - - static inline float dot(const Vector4& a, const Vector4& b); - - Vector4 operator+(const Vector4& v) const; - Vector4 operator-(const Vector4& v) const; - - Vector4 operator*(float s) const; - Vector4 operator/(float s) const; - - Vector4 operator-() const; - - void operator+=(const Vector4& v); - void operator-=(const Vector4& v); - - void operator*=(float s); - void operator/=(float s); - - bool operator==(const Vector4& v) const; - bool operator!=(const Vector4& v) const; - - }; // Vector4 - - inline float Vector2::getLength() const - { - return sqrtf(x * x + y * y); - } - - inline float Vector2::getLengthSquare() const - { - return x * x + y * y; - } - - inline Vector2 Vector2::getNormal() const - { - return Vector2(-y, x); - } - - inline Vector2 Vector2::getNormal(float scale) const - { - return Vector2(-y * scale, x * scale); - } - - inline float Vector2::dot(const Vector2& a, const Vector2& b) - { - return a.x * b.x + a.y * b.y; - } - - inline float Vector2::cross(const Vector2& a, const Vector2& b) - { - return a.x * b.y - a.y * b.x; - } - - inline void Vector2::normalize(float length) - { - float length_current = getLength(); - if (length_current > 0) - { - float m = length / length_current; - x *= m; - y *= m; - } - } - - inline Vector2 Vector2::operator+(const Vector2& v) const - { - return Vector2(x + v.x, y + v.y); - } - - inline Vector2 Vector2::operator-(const Vector2& v) const - { - return Vector2(x - v.x, y - v.y); - } - - inline Vector2 Vector2::operator*(float s) const - { - return Vector2(x * s, y * s); - } - - inline Vector2 Vector2::operator/(float s) const - { - float invs = 1.0f / s; - return Vector2(x * invs, y * invs); - } - - inline Vector2 Vector2::operator-() const - { - return Vector2(-x, -y); - } - - inline void Vector2::operator+=(const Vector2& v) - { - x += v.x; - y += v.y; - } - - inline void Vector2::operator-=(const Vector2& v) - { - x -= v.x; - y -= v.y; - } - - inline void Vector2::operator*=(float s) - { - x *= s; - y *= s; - } - - inline void Vector2::operator/=(float s) - { - float invs = 1.0f / s; - x *= invs; - y *= invs; - } - - inline bool Vector2::operator==(const Vector2& v) const - { - return x == v.x && y == v.y; - } - - inline bool Vector2::operator!=(const Vector2& v) const - { - return x != v.x || y != v.y; - } - - inline float Vector3::getLength() const - { - return sqrtf(x * x + y * y + z * z); - } - - inline float Vector3::getLengthSquare() const - { - return x * x + y * y + z * z; - } - - inline float Vector3::dot(const Vector3& a, const Vector3& b) - { - return a.x * b.x + a.y * b.y + a.z * b.z; - } - - inline Vector3 Vector3::cross(const Vector3& a, const Vector3& b) - { - return Vector3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); - } - - inline void Vector3::normalize(float length) - { - float length_current = getLength(); - if (length_current > 0) - { - float m = length / length_current; - x *= m; - y *= m; - z *= m; - } - } - - inline Vector3 Vector3::operator+(const Vector3& v) const - { - return Vector3(x + v.x, y + v.y, z + v.z); - } - - inline Vector3 Vector3::operator-(const Vector3& v) const - { - return Vector3(x - v.x, y - v.y, z - v.z); - } - - inline Vector3 Vector3::operator*(float s) const - { - return Vector3(x * s, y * s, z * s); - } - - inline Vector3 Vector3::operator/(float s) const - { - float invs = 1.0f / s; - return Vector3(x * invs, y * invs, z * invs); - } - - inline Vector3 Vector3::operator-() const - { - return Vector3(-x, -y, -z); - } - - inline void Vector3::operator+=(const Vector3& v) - { - x += v.x; - y += v.y; - z += v.z; - } - - inline void Vector3::operator-=(const Vector3& v) - { - x -= v.x; - y -= v.y; - z -= v.z; - } - - inline void Vector3::operator*=(float s) - { - x *= s; - y *= s; - z *= s; - } - - inline void Vector3::operator/=(float s) - { - float invs = 1.0f / s; - x *= invs; - y *= invs; - z *= invs; - } - - inline bool Vector3::operator==(const Vector3& v) const - { - return x == v.x && y == v.y && z == v.z; - } - - inline bool Vector3::operator!=(const Vector3& v) const - { - return x != v.x || y != v.y || z != v.z; - } - - inline float Vector4::getLength() const - { - return sqrtf(x * x + y * y + z * z + w * w); - } - - inline float Vector4::getLengthSquare() const - { - return x * x + y * y + z * z + w * w; - } - - inline float Vector4::dot(const Vector4& a, const Vector4& b) - { - return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; - } - - inline void Vector4::normalize(float length) - { - float length_current = getLength(); - if (length_current > 0) - { - float m = length / length_current; - x *= m; - y *= m; - z *= m; - w *= m; - } - } - - inline Vector4 Vector4::operator+(const Vector4& v) const - { - return Vector4(x + v.x, y + v.y, z + v.z, w + v.w); - } - - inline Vector4 Vector4::operator-(const Vector4& v) const - { - return Vector4(x - v.x, y - v.y, z - v.z, w - v.w); - } - - inline Vector4 Vector4::operator*(float s) const - { - return Vector4(x * s, y * s, z * s, w * s); - } - - inline Vector4 Vector4::operator/(float s) const - { - float invs = 1.0f / s; - return Vector4(x * invs, y * invs, z * invs, w * invs); - } - - inline Vector4 Vector4::operator-() const - { - return Vector4(-x, -y, -z, -w); - } - - inline void Vector4::operator+=(const Vector4& v) - { - x += v.x; - y += v.y; - z += v.z; - w += v.w; - } - - inline void Vector4::operator-=(const Vector4& v) - { - x -= v.x; - y -= v.y; - z -= v.z; - w -= v.w; - } - - inline void Vector4::operator*=(float s) - { - x *= s; - y *= s; - z *= s; - w *= s; - } - - inline void Vector4::operator/=(float s) - { - float invs = 1.0f / s; - x *= invs; - y *= invs; - z *= invs; - w *= invs; - } - - inline bool Vector4::operator==(const Vector4& v) const - { - return x == v.x && y == v.y && z == v.z && w == v.w; - } - - inline bool Vector4::operator!=(const Vector4& v) const - { - return x != v.x || y != v.y || z != v.z || w != v.w; - } - -} // namespace love - -#endif // LOVE_VECTOR_H diff --git a/include/common/vector.hpp b/include/common/vector.hpp new file mode 100644 index 000000000..621f9c1de --- /dev/null +++ b/include/common/vector.hpp @@ -0,0 +1,482 @@ +/** + * Copyright (c) 2006-2022 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifndef LOVE_VECTOR_H +#define LOVE_VECTOR_H + +// STD +#include + +namespace love +{ + + // All math operators are component-wise. + struct Vector2 + { + float x, y; + + Vector2() : x(0.0f), y(0.0f) + {} + + Vector2(float x, float y) : x(x), y(y) + {} + + Vector2(const Vector2& v) : x(v.x), y(v.y) + {} + + float getLength() const; + float getLengthSquare() const; + + /** + * Normalizes the Vector. + * @param length Desired length of the vector. + **/ + void normalize(float length = 1.0f); + + /** + * Gets a vector perpendicular to the Vector. + * To get the true (normalized) normal, use v.getNormal(1.0f / v.getLength()) + **/ + Vector2 getNormal() const; + + /** + * Gets a vector perpendicular to the Vector. + * To get the true (normalized) normal, use v.getNormal(1.0f / v.getLength()) + **/ + Vector2 getNormal(float scale) const; + + static inline float dot(const Vector2& a, const Vector2& b); + static inline float cross(const Vector2& a, const Vector2& b); + + Vector2 operator+(const Vector2& v) const; + Vector2 operator-(const Vector2& v) const; + + Vector2 operator*(float s) const; + Vector2 operator/(float s) const; + + Vector2 operator-() const; + + void operator+=(const Vector2& v); + void operator-=(const Vector2& v); + + void operator*=(float s); + void operator/=(float s); + + bool operator==(const Vector2& v) const; + bool operator!=(const Vector2& v) const; + + }; // Vector2 + + // All math operators are component-wise. + struct Vector3 + { + float x, y, z; + + Vector3() : x(0.0f), y(0.0f), z(0.0f) + {} + + Vector3(float x, float y, float z) : x(x), y(y), z(z) + {} + + Vector3(const Vector2& v, float z = 0.0f) : x(v.x), y(v.y), z(z) + {} + + bool isZero() const + { + return this->x == 0.0f && this->y == 0.0f && this->z == 0.0f; + } + + float getLength() const; + float getLengthSquare() const; + + /** + * Normalizes the Vector. + * @param length Desired length of the vector. + **/ + void normalize(float length = 1.0); + + static inline float dot(const Vector3& a, const Vector3& b); + static inline Vector3 cross(const Vector3& a, const Vector3& b); + + Vector3 operator+(const Vector3& v) const; + Vector3 operator-(const Vector3& v) const; + + Vector3 operator*(float s) const; + Vector3 operator/(float s) const; + + Vector3 operator-() const; + + void operator+=(const Vector3& v); + void operator-=(const Vector3& v); + + void operator*=(float s); + void operator/=(float s); + + bool operator==(const Vector3& v) const; + bool operator!=(const Vector3& v) const; + + }; // Vector3 + + // All math operators are component-wise. + struct Vector4 + { + float x, y, z, w; + + Vector4() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) + {} + + Vector4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) + {} + + Vector4(const Vector2& v, float z = 0.0f, float w = 0.0f) : x(v.x), y(v.y), z(z), w(w) + {} + + Vector4(const Vector3& v, float w = 0.0f) : x(v.x), y(v.y), z(v.z), w(w) + {} + + float getLength() const; + float getLengthSquare() const; + + /** + * Normalizes the Vector. + * @param length Desired length of the vector. + **/ + void normalize(float length = 1.0); + + static inline float dot(const Vector4& a, const Vector4& b); + + Vector4 operator+(const Vector4& v) const; + Vector4 operator-(const Vector4& v) const; + + Vector4 operator*(float s) const; + Vector4 operator/(float s) const; + + Vector4 operator-() const; + + void operator+=(const Vector4& v); + void operator-=(const Vector4& v); + + void operator*=(float s); + void operator/=(float s); + + bool operator==(const Vector4& v) const; + bool operator!=(const Vector4& v) const; + + }; // Vector4 + + inline float Vector2::getLength() const + { + return sqrtf(x * x + y * y); + } + + inline float Vector2::getLengthSquare() const + { + return x * x + y * y; + } + + inline Vector2 Vector2::getNormal() const + { + return Vector2(-y, x); + } + + inline Vector2 Vector2::getNormal(float scale) const + { + return Vector2(-y * scale, x * scale); + } + + inline float Vector2::dot(const Vector2& a, const Vector2& b) + { + return a.x * b.x + a.y * b.y; + } + + inline float Vector2::cross(const Vector2& a, const Vector2& b) + { + return a.x * b.y - a.y * b.x; + } + + inline void Vector2::normalize(float length) + { + float length_current = getLength(); + if (length_current > 0) + { + float m = length / length_current; + x *= m; + y *= m; + } + } + + inline Vector2 Vector2::operator+(const Vector2& v) const + { + return Vector2(x + v.x, y + v.y); + } + + inline Vector2 Vector2::operator-(const Vector2& v) const + { + return Vector2(x - v.x, y - v.y); + } + + inline Vector2 Vector2::operator*(float s) const + { + return Vector2(x * s, y * s); + } + + inline Vector2 Vector2::operator/(float s) const + { + float invs = 1.0f / s; + return Vector2(x * invs, y * invs); + } + + inline Vector2 Vector2::operator-() const + { + return Vector2(-x, -y); + } + + inline void Vector2::operator+=(const Vector2& v) + { + x += v.x; + y += v.y; + } + + inline void Vector2::operator-=(const Vector2& v) + { + x -= v.x; + y -= v.y; + } + + inline void Vector2::operator*=(float s) + { + x *= s; + y *= s; + } + + inline void Vector2::operator/=(float s) + { + float invs = 1.0f / s; + x *= invs; + y *= invs; + } + + inline bool Vector2::operator==(const Vector2& v) const + { + return x == v.x && y == v.y; + } + + inline bool Vector2::operator!=(const Vector2& v) const + { + return x != v.x || y != v.y; + } + + inline float Vector3::getLength() const + { + return sqrtf(x * x + y * y + z * z); + } + + inline float Vector3::getLengthSquare() const + { + return x * x + y * y + z * z; + } + + inline float Vector3::dot(const Vector3& a, const Vector3& b) + { + return a.x * b.x + a.y * b.y + a.z * b.z; + } + + inline Vector3 Vector3::cross(const Vector3& a, const Vector3& b) + { + return Vector3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); + } + + inline void Vector3::normalize(float length) + { + float length_current = getLength(); + if (length_current > 0) + { + float m = length / length_current; + x *= m; + y *= m; + z *= m; + } + } + + inline Vector3 Vector3::operator+(const Vector3& v) const + { + return Vector3(x + v.x, y + v.y, z + v.z); + } + + inline Vector3 Vector3::operator-(const Vector3& v) const + { + return Vector3(x - v.x, y - v.y, z - v.z); + } + + inline Vector3 Vector3::operator*(float s) const + { + return Vector3(x * s, y * s, z * s); + } + + inline Vector3 Vector3::operator/(float s) const + { + float invs = 1.0f / s; + return Vector3(x * invs, y * invs, z * invs); + } + + inline Vector3 Vector3::operator-() const + { + return Vector3(-x, -y, -z); + } + + inline void Vector3::operator+=(const Vector3& v) + { + x += v.x; + y += v.y; + z += v.z; + } + + inline void Vector3::operator-=(const Vector3& v) + { + x -= v.x; + y -= v.y; + z -= v.z; + } + + inline void Vector3::operator*=(float s) + { + x *= s; + y *= s; + z *= s; + } + + inline void Vector3::operator/=(float s) + { + float invs = 1.0f / s; + x *= invs; + y *= invs; + z *= invs; + } + + inline bool Vector3::operator==(const Vector3& v) const + { + return x == v.x && y == v.y && z == v.z; + } + + inline bool Vector3::operator!=(const Vector3& v) const + { + return x != v.x || y != v.y || z != v.z; + } + + inline float Vector4::getLength() const + { + return sqrtf(x * x + y * y + z * z + w * w); + } + + inline float Vector4::getLengthSquare() const + { + return x * x + y * y + z * z + w * w; + } + + inline float Vector4::dot(const Vector4& a, const Vector4& b) + { + return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; + } + + inline void Vector4::normalize(float length) + { + float length_current = getLength(); + if (length_current > 0) + { + float m = length / length_current; + x *= m; + y *= m; + z *= m; + w *= m; + } + } + + inline Vector4 Vector4::operator+(const Vector4& v) const + { + return Vector4(x + v.x, y + v.y, z + v.z, w + v.w); + } + + inline Vector4 Vector4::operator-(const Vector4& v) const + { + return Vector4(x - v.x, y - v.y, z - v.z, w - v.w); + } + + inline Vector4 Vector4::operator*(float s) const + { + return Vector4(x * s, y * s, z * s, w * s); + } + + inline Vector4 Vector4::operator/(float s) const + { + float invs = 1.0f / s; + return Vector4(x * invs, y * invs, z * invs, w * invs); + } + + inline Vector4 Vector4::operator-() const + { + return Vector4(-x, -y, -z, -w); + } + + inline void Vector4::operator+=(const Vector4& v) + { + x += v.x; + y += v.y; + z += v.z; + w += v.w; + } + + inline void Vector4::operator-=(const Vector4& v) + { + x -= v.x; + y -= v.y; + z -= v.z; + w -= v.w; + } + + inline void Vector4::operator*=(float s) + { + x *= s; + y *= s; + z *= s; + w *= s; + } + + inline void Vector4::operator/=(float s) + { + float invs = 1.0f / s; + x *= invs; + y *= invs; + z *= invs; + w *= invs; + } + + inline bool Vector4::operator==(const Vector4& v) const + { + return x == v.x && y == v.y && z == v.z && w == v.w; + } + + inline bool Vector4::operator!=(const Vector4& v) const + { + return x != v.x || y != v.y || z != v.z || w != v.w; + } + +} // namespace love + +#endif // LOVE_VECTOR_H diff --git a/include/common/version.h b/include/common/version.h deleted file mode 100644 index 4992ff19e..000000000 --- a/include/common/version.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include -#include - -namespace love -{ - namespace version - { - class Version - { - public: - consteval Version(std::string_view version) : values() - { - int position = 0; - size_t last = 0; - size_t size = 0; - - for (size_t index = 0; index < version.length(); index++) - { - if (version.at(index) == DELIMETER) - { - std::string_view token = version.substr(last, size); - this->values[position] = Version::atoi(token); - - last = index + 1; - position++; - size = 0; - } - else - { - if (position == 2) - { - std::string_view token = version.substr(last, version.length() - last); - this->values[position++] = Version::atoi(token); - } - size++; - } - } - } - - consteval int Major() const - { - return this->values[0]; - } - - consteval int Minor() const - { - return this->values[1]; - } - - consteval int Revision() const - { - return this->values[2]; - } - - private: - static constexpr char DELIMETER = '.'; - - constexpr static int atoi(std::string_view view) - { - int value = 0; - for (const auto& character : view) - { - if ('0' <= character && character <= '9') - { - value = value * 10 + character - '0'; - } - } - return value; - } - - std::array values; - }; - - static constexpr Version LOVE_POTION(__APP_VERSION__); - static constexpr Version LOVE_FRAMEWORK(__LOVE_VERSION__); - - static constexpr const char* CODENAME = "Mysterious Mysteries"; - static constexpr const char* COMPATABILITY[] = { __APP_VERSION__, "2.2.0", 0 }; - } // namespace version -} // namespace love diff --git a/include/common/version.hpp b/include/common/version.hpp new file mode 100644 index 000000000..7cc2236df --- /dev/null +++ b/include/common/version.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include + +namespace love +{ + struct Version + { + public: + constexpr Version() : major(0), minor(0), micro(0) + {} + + constexpr Version(std::string_view v) : major(0), minor(0), micro(0) + { + auto nposzero = [](size_t x) { return x == std::string_view::npos ? 0 : x; }; + + std::string_view major = v.substr(0, nposzero(v.find(DELIMITER))); + size_t minorIdx = std::min(major.size() + 1, v.size()); + std::string_view minor = v.substr(minorIdx, nposzero(v.find(DELIMITER, minorIdx))); + size_t microIdx = std::min(major.size() + 1 + minor.size() + 1, v.size()); + std::string_view micro = + v.substr(microIdx, nposzero(v.find_first_not_of(DIGITS, microIdx))); + + if (major.size() != 0) + this->major = atoi(major); + + if (minor.size() != 0) + this->minor = atoi(minor); + + if (micro.size() != 0) + this->micro = atoi(micro); + + if (major.size() == 0 && minor.size() == 0 && micro.size() == 0) + this->major = atoi(v); + } + + template + requires std::constructible_from || + std::convertible_to + constexpr Version(T&& version) : Version(std::string_view(std::forward(version))) + {} + + Version(uint8_t major, uint8_t minor, uint8_t micro) : + major(major), + minor(minor), + micro(micro) + {} + + constexpr std::strong_ordering operator<=>(const Version&) const noexcept = default; + + uint8_t major; + uint8_t minor; + uint8_t micro; + + private: + static constexpr char DELIMITER = '.'; + static constexpr const char* DIGITS = "123457890"; + + constexpr static uint8_t atoi(std::string_view view) + { + uint8_t value = 0; + + for (const auto& character : view) + { + if ('0' <= character && character <= '9') + value = value * 10 + character - '0'; + } + + return value; + } + }; + + static constexpr Version LOVE_POTION(__APP_VERSION__); + static constexpr Version LOVE_FRAMEWORK(__LOVE_VERSION__); + + static constexpr const char* CODENAME = "Mysterious Mysteries"; + static constexpr std::array COMPATIBILITY = { __APP_VERSION__ }; +} // namespace love diff --git a/include/modules/audio/audio.h b/include/modules/audio/audio.h deleted file mode 100644 index 379df9211..000000000 --- a/include/modules/audio/audio.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include "common/module.h" -#include "objects/source/source.h" - -#include "modules/audio/pool/pool.h" -#include "modules/thread/types/threadable.h" - -#include "driver/audiodrv.h" -#include - -namespace love -{ - class Audio : public Module - { - public: - ModuleType GetModuleType() const - { - return M_AUDIO; - } - - const char* GetName() const override - { - return "love.audio"; - } - - Audio(); - - virtual ~Audio(); - - int GetActiveSourceCount() const; - - int GetMaxSources() const; - - Source* NewSource(SoundData* data); - - Source* NewSource(Decoder* decoder); - - bool Play(Source* source); - - bool Play(const std::vector& sources); - - bool Play(); - - void Stop(Source* source); - - void Stop(const std::vector& sources); - - void Stop(); - - void Pause(const std::vector& sources); - - void Pause(Source* source); - - std::vector Pause(); - - void SetVolume(float volume); - - float GetVolume() const; - - private: - Pool* pool; - - class PoolThread : public Threadable - { - public: - PoolThread(Pool* pool); - - virtual ~PoolThread(); - - void SetFinish(); - - void ThreadFunction(); - - protected: - Pool* pool; - std::atomic finish; - }; - - float volume = 1.0f; - - PoolThread* poolThread; - }; -} // namespace love diff --git a/include/modules/audio/audio.hpp b/include/modules/audio/audio.hpp new file mode 100644 index 000000000..96c6ef876 --- /dev/null +++ b/include/modules/audio/audio.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace love +{ + class Audio : public Module + { + public: + Audio(); + + ~Audio(); + + ModuleType GetModuleType() const override + { + return M_AUDIO; + } + + const char* GetName() const override + { + return "love.audio"; + } + + Source* NewSource(Decoder* decoder) const; + + Source* NewSource(SoundData* soundData) const; + + Source* NewSource(int sampleRate, int bitDepth, int channels, + int buffers) const; + + int GetActiveSourceCount() const; + + int GetMaxSources() const; + + void SetVolume(float volume); + + float GetVolume() const; + + bool Play(Source* source); + + bool Play(const std::vector*>& sources); + + void Stop(Source* source); + + void Stop(const std::vector*>& sources); + + void Stop(); + + void Pause(Source* source); + + void Pause(const std::vector*>& sources); + + std::vector*> Pause(); + + private: + AudioPool* pool; + PoolThread* thread; + }; +} // namespace love diff --git a/include/modules/audio/pool/pool.h b/include/modules/audio/pool/pool.h deleted file mode 100644 index e2615f6f7..000000000 --- a/include/modules/audio/pool/pool.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include "driver/audiodrv.h" -#include "modules/thread/types/lock.h" - -#include -#include -#include - -namespace love -{ - namespace common - { - class Source; - } - - class Pool - { - public: - Pool(); - - ~Pool(); - - int GetActiveSourceCount() const; - - int GetMaxSources() const; - - bool IsRunning(); - - bool IsPlaying(common::Source* source); - - bool AssignSource(common::Source* source, size_t& channel, char& wasPlaying); - - void AddSource(common::Source*, size_t channel); - - bool FindSource(common::Source* source, size_t& channel); - - bool ReleaseSource(common::Source* source, bool stop = true); - - void Finish(); - - void Update(); - - void Sleep(); - - thread::Lock Lock(); - - std::vector GetPlayingSources(); - - private: - friend class common::Source; - - std::atomic running = true; - std::map playing; - thread::MutexRef mutex; - - std::queue available; - const size_t TOTAL_CHANNELS = 24; - }; -} // namespace love diff --git a/include/modules/audio/wrap_audio.h b/include/modules/audio/wrap_audio.h deleted file mode 100644 index 37cedf770..000000000 --- a/include/modules/audio/wrap_audio.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "modules/audio/audio.h" -#include "objects/file/file.h" -#include "objects/filedata/filedata.h" -#include "objects/sounddata/sounddata.h" -#include "objects/source/wrap_source.h" - -#include "common/luax.h" - -namespace Wrap_Audio -{ - int GetActiveSourceCount(lua_State* L); - - int GetVolume(lua_State* L); - - int NewSource(lua_State* L); - - int Pause(lua_State* L); - - int Play(lua_State* L); - - int SetVolume(lua_State* L); - - int Stop(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_Audio diff --git a/include/modules/audio/wrap_audio.hpp b/include/modules/audio/wrap_audio.hpp new file mode 100644 index 000000000..0cfe6828f --- /dev/null +++ b/include/modules/audio/wrap_audio.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace Wrap_Audio +{ + int GetActiveSourceCount(lua_State* L); + + int NewSource(lua_State* L); + + int NewQueueableSource(lua_State* L); + + int Play(lua_State* L); + + int Stop(lua_State* L); + + int Pause(lua_State* L); + + int SetVolume(lua_State* L); + + int GetVolume(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_Audio diff --git a/include/modules/data/compressor/compressor.h b/include/modules/data/compressor/compressor.h deleted file mode 100644 index 9fa3392f9..000000000 --- a/include/modules/data/compressor/compressor.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "common/exception.h" - -#include -#include - -#include - -#include - -namespace love -{ - class Compressor - { - public: - enum Format - { - FORMAT_LZ4, - FORMAT_ZLIB, - FORMAT_GZIP, - FORMAT_DEFLATE, - FORMAT_MAX_ENUM - }; - - static Compressor* GetCompressor(Format format); - - virtual ~Compressor() - {} - - virtual char* Compress(Format format, const char* data, size_t size, int level, - size_t& compressedSize) = 0; - - virtual char* Decompress(Format format, const char* data, size_t size, - size_t& decompressedSize) = 0; - - virtual bool IsSupported(Format format) const = 0; - - static bool GetConstant(const char* in, Format& out); - static bool GetConstant(Format in, const char*& out); - static std::vector GetConstants(Format); - - protected: - Compressor() - {} - }; -} // namespace love diff --git a/include/modules/data/compressor/types/lz4compressor.h b/include/modules/data/compressor/types/lz4compressor.h deleted file mode 100644 index 85306143e..000000000 --- a/include/modules/data/compressor/types/lz4compressor.h +++ /dev/null @@ -1,139 +0,0 @@ -#pragma once - -#include "modules/data/compressor/compressor.h" - -namespace love -{ - class LZ4Compressor : public Compressor - { - public: - char* Compress(Compressor::Format format, const char* data, size_t size, int level, - size_t& compressedSize) override - { - if (format != Compressor::FORMAT_LZ4) - throw love::Exception("Invalid format (expected LZ4)"); - - if (size > LZ4_MAX_INPUT_SIZE) - throw love::Exception("Data is too large for LZ4 compressor"); - - // LÖVE uses a custom header - const size_t headerSize = sizeof(uint32_t); - - int maxDestinationSize = LZ4_compressBound((int)size); - size_t maxSize = headerSize + (size_t)maxDestinationSize; - - char* compressedBytes = nullptr; - - try - { - compressedBytes = new char[maxSize]; - } - catch (std::bad_alloc&) - { - throw love::Exception("Out of memory."); - } - - *(uint32_t*)compressedBytes = (uint32_t)size; - - int compressionSize = 0; - - if (level > 8) - compressionSize = LZ4_compress_HC(data, compressedBytes + headerSize, (int)size, - maxDestinationSize, LZ4HC_CLEVEL_DEFAULT); - else - compressionSize = LZ4_compress_default(data, compressedBytes + headerSize, - (int)size, maxDestinationSize); - - if (compressionSize <= 0) - { - delete[] compressedBytes; - throw love::Exception("Could not LZ4-compress data!"); - } - - /* - ** We allocated space for the maximum possible amount of data, but the - ** actual compressed size might be much smaller, so we should shrink the - ** data buffer if so. - */ - - if ((double)maxSize / (double)(compressionSize + headerSize) > 1.2) - { - char* compressionBytes = new (std::nothrow) char[compressionSize + headerSize]; - - if (compressionBytes) - { - memcpy(compressionBytes, compressedBytes, compressionSize + headerSize); - delete[] compressedBytes; - compressedBytes = compressionBytes; - } - } - - compressedSize = (size_t)compressionSize + headerSize; - - return compressedBytes; - } - - char* Decompress(Compressor::Format format, const char* data, size_t size, - size_t& decompressedSize) override - { - if (format != FORMAT_LZ4) - throw love::Exception("Invalid format (expected LZ4)."); - - // LÖVE uses a custom header - const size_t headerSize = sizeof(uint32_t); - - char* rawBytes = nullptr; - - if (size < headerSize) - throw love::Exception("Invalid LZ4-compressed data size."); - - uint32_t rawSize = *(uint32_t*)data; - - try - { - rawBytes = new char[rawSize]; - } - catch (std::bad_alloc&) - { - throw love::Exception("Out of memory."); - } - - /* - ** If the uncompressed size is passed in as an argument (non-zero) and - ** it matches the header's stored size, then we assume it's 100% accurate - ** and we use a more efficient decompression function. - */ - - if (decompressedSize > 0 && decompressedSize == (size_t)rawSize) - { - // We don't care for the header here, but account for its size - if (LZ4_decompress_fast(data + headerSize, rawBytes, (int)decompressedSize) < 0) - { - delete[] rawBytes; - throw love::Exception("Could not decompress LZ4-compressed data!"); - } - } - else - { - // We do care for the header here, account for its size - int result = LZ4_decompress_safe(data + headerSize, rawBytes, - (int)(size - headerSize), rawSize); - - if (result < 0) - { - delete[] rawBytes; - throw love::Exception("Could not decompress LZ4-compressed data."); - } - - decompressedSize = (size_t)result; - } - - return rawBytes; - } - - bool IsSupported(Compressor::Format format) const - { - return format == Compressor::FORMAT_LZ4; - } - }; -} // namespace love diff --git a/include/modules/data/compressor/types/zlibcompressor.h b/include/modules/data/compressor/types/zlibcompressor.h deleted file mode 100644 index db02078ba..000000000 --- a/include/modules/data/compressor/types/zlibcompressor.h +++ /dev/null @@ -1,209 +0,0 @@ -#pragma once - -#include "modules/data/compressor/compressor.h" - -namespace love -{ - class zlibCompressor : public Compressor - { - public: - char* Compress(Compressor::Format format, const char* data, size_t size, int level, - size_t& compressedSize) override - { - if (!this->IsSupported(format)) - throw love::Exception("Invalid format (expected zlib or gzip)."); - - if (level < 0) - level = Z_DEFAULT_COMPRESSION; - else if (level > 9) - level = 9; - - uLong maxSize = this->zlibCompressBound(format, (uLong)size); - - char* compressedBytes = nullptr; - - try - { - compressedBytes = new char[maxSize]; - } - catch (std::bad_alloc&) - { - throw love::Exception("Out of memory."); - } - - uLongf destinationLength = maxSize; - - int status = this->zlibCompress(format, (Bytef*)compressedBytes, &destinationLength, - (const Bytef*)data, (uLong)size, level); - - if (status != Z_OK) - { - delete[] compressedBytes; - throw love::Exception("Could not zlib/gzip-compress data."); - } - - /* - ** We allocated space for the maximum possible amount of data, but the - ** actual compressed size might be much smaller, so we should shrink the - ** data buffer if so. - */ - - if ((double)maxSize / (double)destinationLength >= 1.3) - { - char* compressionBytes = new (std::nothrow) char[destinationLength]; - - if (compressionBytes) - { - memcpy(compressionBytes, compressedBytes, destinationLength); - delete[] compressedBytes; - compressedBytes = compressionBytes; - } - } - - compressedSize = (size_t)destinationLength; - - return compressedBytes; - } - - char* Decompress(Compressor::Format format, const char* data, size_t size, - size_t& decompressedSize) override - { - if (!this->IsSupported(format)) - throw love::Exception("Invalid format (expected zlib or gzip)."); - - char* rawBytes = nullptr; - - // We might know the output size before decompression. If not, we guess. - size_t rawSize = decompressedSize > 0 ? decompressedSize : size * 2; - - // Repeatedly try to decompress with an increasingly large output buffer. - while (true) - { - try - { - rawBytes = new char[rawSize]; - } - catch (std::bad_alloc&) - { - throw love::Exception("Out of memory."); - } - - uLongf destinationLength = (uLongf)rawSize; - - int status = this->zlibDecompress(format, (Bytef*)rawBytes, &destinationLength, - (const Bytef*)data, (uLong)size); - - if (status == Z_OK) - { - decompressedSize = (size_t)destinationLength; - break; - } - else if (status != Z_BUF_ERROR) - { - // Anything except not enough room, throw an exception - delete[] rawBytes; - throw love::Exception("Could not decompress zlib/gzip-compressed data."); - } - - // Not enough room: try with a larger size - delete[] rawBytes; - rawSize *= 2; - } - - return rawBytes; - } - - bool IsSupported(Compressor::Format format) const - { - return format == Compressor::FORMAT_ZLIB || format == Compressor::FORMAT_GZIP || - format == Compressor::FORMAT_DEFLATE; - } - - private: - uLong zlibCompressBound(Compressor::Format format, uLong sourceLength) - { - uLong size = sourceLength + (sourceLength >> 12) + (sourceLength >> 14) + - (sourceLength >> 25) + 13; - - if (format == FORMAT_GZIP) - size += 18 - 6; - - return size; - } - - int zlibCompress(Compressor::Format format, Bytef* destination, uLongf* destinationLength, - const Bytef* source, uLong sourceLength, int level) - { - z_stream stream = {}; - - stream.next_in = (Bytef*)source; - stream.avail_in = (uInt)sourceLength; - - stream.next_out = destination; - stream.avail_out = (uInt)(*destinationLength); - - int windowBits = 15; - - if (format == FORMAT_GZIP) - windowBits += 16; - else if (format == FORMAT_DEFLATE) - windowBits = -windowBits; - - int error = deflateInit2(&stream, level, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY); - - if (error != Z_OK) - return error; - - error = deflate(&stream, Z_FINISH); - - if (error != Z_STREAM_END) - { - deflateEnd(&stream); - return error == Z_OK ? Z_BUF_ERROR : error; - } - - *destinationLength = stream.total_out; - - return deflateEnd(&stream); - } - - int zlibDecompress(Compressor::Format format, Bytef* destination, uLongf* destinationLength, - const Bytef* source, uLong sourceLength) - { - z_stream stream = {}; - - stream.next_in = (Bytef*)source; - stream.avail_in = (uInt)sourceLength; - - stream.next_out = destination; - stream.avail_out = (uInt)(*destinationLength); - - // 15 is the default, add 32 to auto-detect header type - int windowBits = 15 + 32; - - if (format == FORMAT_DEFLATE) - windowBits = -15; - - int error = inflateInit2(&stream, windowBits); - - if (error != Z_OK) - return error; - - error = inflate(&stream, windowBits); - - if (error != Z_STREAM_END) - { - inflateEnd(&stream); - - if (error == Z_NEED_DICT || (error == Z_BUF_ERROR && stream.avail_in == 0)) - return Z_DATA_ERROR; - - return error; - } - - *destinationLength = stream.total_out; - - return inflateEnd(&stream); - } - }; -} // namespace love diff --git a/include/modules/data/data.hpp b/include/modules/data/data.hpp new file mode 100644 index 000000000..e1d37e001 --- /dev/null +++ b/include/modules/data/data.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include + +#include +#include +#include + +#include +#include + +namespace love +{ + class DataModule : public Module + { + public: + enum EncodeFormat + { + ENCODE_BASE64, + ENCODE_HEX, + ENCODE_MAX_ENUM + }; + + enum ContainerType + { + CONTAINER_DATA, + CONTAINER_STRING, + CONTAINER_MAX_ENUM + }; + + ModuleType GetModuleType() const override + { + return M_DATA; + } + + const char* GetName() const override + { + return "love.data"; + } + + char* Decode(EncodeFormat format, const char* source, size_t sourceLength, + size_t& destinationLength); + + char* Decompress(Compressor::Format format, const char* compressedBytes, + size_t compressedSize, size_t& rawSize); + + char* Decompress(CompressedData* data, size_t& decompressedSize); + + CompressedData* Compress(Compressor::Format format, const char* rawBytes, size_t rawSize, + int level = -1); + + char* Encode(EncodeFormat format, const char* source, size_t sourceLength, + size_t& destinationLength, size_t lineLength = 0); + + void Hash(HashFunction::Function function, Data* input, HashFunction::Value& output); + + void Hash(HashFunction::Function function, const char* input, uint64_t size, + HashFunction::Value& output); + + ByteData* NewByteData(size_t size); + + ByteData* NewByteData(const void* data, size_t size); + + ByteData* NewByteData(void* data, size_t size, bool own); + + DataView* NewDataView(Data* data, size_t offset, size_t size); + + // clang-format off + static constexpr BidirectionalMap containers = { + "data", DataModule::ContainerType::CONTAINER_DATA, + "string", DataModule::ContainerType::CONTAINER_STRING + }; + + static constexpr BidirectionalMap formats = { + "hex", DataModule::EncodeFormat::ENCODE_HEX, + "base64", DataModule::EncodeFormat::ENCODE_BASE64 + }; + // clang-format on + + private: + std::string Hash(HashFunction::Function function, Data* input); + + std::string Hash(HashFunction::Function function, const char* input, uint64_t size); + }; +} // namespace love diff --git a/include/modules/data/datamodule.h b/include/modules/data/datamodule.h deleted file mode 100644 index 93034f4da..000000000 --- a/include/modules/data/datamodule.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include "modules/data/hashfunction/hashfunction.h" -#include "objects/data/byte/bytedata.h" -#include "objects/data/compressed/compresseddata.h" -#include "objects/data/view/dataview.h" - -#include "common/module.h" - -namespace love -{ - namespace data - { - enum EncodeFormat - { - ENCODE_BASE64, - ENCODE_HEX, - ENCODE_MAX_ENUM - }; - - enum ContainerType - { - CONTAINER_DATA, - CONTAINER_STRING, - CONTAINER_MAX_ENUM - }; - - std::string _Hash(HashFunction::Function func, Data* input); - - std::string _Hash(HashFunction::Function func, const char* input, uint64_t size); - - void _Hash(HashFunction::Function func, Data* input, HashFunction::Value& output); - - void _Hash(HashFunction::Function func, const char* input, uint64_t size, - HashFunction::Value& output); - - char* _Encode(EncodeFormat format, const char* src, size_t srcLength, size_t& dstLength, - size_t lineLength = 0); - - char* _Decode(EncodeFormat format, const char* src, size_t srcLength, size_t& dstLength); - - CompressedData* _Compress(Compressor::Format format, const char* rawBytes, size_t rawSize, - int level = -1); - - char* _Decompress(Compressor::Format format, const char* compresssedBytes, - size_t compressedSize, size_t& rawSize); - - char* _Decompress(CompressedData* data, size_t& decompressedSize); - } // namespace data - - class DataModule : public love::Module - { - public: - ByteData* NewByteData(size_t size); - ByteData* NewByteData(const void* data, size_t size); - ByteData* NewByteData(void* data, size_t size, bool own); - - DataView* NewDataView(Data* data, size_t offset, size_t size); - - ModuleType GetModuleType() const - { - return M_DATA; - } - - const char* GetName() const override - { - return "love.data"; - } - - static bool GetConstant(data::ContainerType in, const char*& out); - static bool GetConstant(const char* in, data::ContainerType& out); - static std::vector GetConstants(data::ContainerType); - - static bool GetConstant(data::EncodeFormat in, const char*& out); - static bool GetConstant(const char* in, data::EncodeFormat& out); - static std::vector GetConstants(data::EncodeFormat); - }; -} // namespace love diff --git a/include/modules/data/hashfunction/hashfunction.h b/include/modules/data/hashfunction/hashfunction.h deleted file mode 100644 index 6c6072ec5..000000000 --- a/include/modules/data/hashfunction/hashfunction.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "common/data.h" -#include "common/exception.h" - -#include - -namespace love -{ - class HashFunction - { - public: - enum Function - { - FUNCTION_MD5, - FUNCTION_SHA1, - FUNCTION_SHA224, - FUNCTION_SHA256, - FUNCTION_SHA384, - FUNCTION_SHA512, - FUNCTION_MAX_ENUM - }; - - struct Value - { - char data[64]; - size_t size; - }; - - static HashFunction* GetHashFunction(Function func); - - virtual ~HashFunction() - {} - - virtual void Hash(Function func, const char* input, uint64_t length, - Value& output) const = 0; - - virtual bool IsSupported(Function func) const = 0; - - static bool GetConstant(const char* in, Function& out); - static bool GetConstant(const Function& in, const char*& out); - static std::vector GetConstants(Function); - - protected: - HashFunction() - {} - }; -} // namespace love diff --git a/include/modules/data/wrap_data.hpp b/include/modules/data/wrap_data.hpp new file mode 100644 index 000000000..6ac76c2f2 --- /dev/null +++ b/include/modules/data/wrap_data.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +namespace Wrap_DataModule +{ + love::DataModule::ContainerType CheckContainerType(lua_State* L, int index); + + int Hash(lua_State* L); + + int Compress(lua_State* L); + + int Decompress(lua_State* L); + + int Encode(lua_State* L); + + int Decode(lua_State* L); + + int NewByteData(lua_State* L); + + int NewDataView(lua_State* L); + + int Pack(lua_State* L); + + int Unpack(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_DataModule diff --git a/include/modules/data/wrap_datamodule.h b/include/modules/data/wrap_datamodule.h deleted file mode 100644 index 23a098b15..000000000 --- a/include/modules/data/wrap_datamodule.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "modules/data/datamodule.h" - -#include "objects/data/wrap_data.h" - -#include "objects/data/byte/bytedata.h" -#include "objects/data/byte/wrap_bytedata.h" - -#include "objects/data/compressed/compresseddata.h" -#include "objects/data/compressed/wrap_compresseddata.h" - -#include "objects/data/view/dataview.h" -#include "objects/data/view/wrap_dataview.h" -#include - -namespace Wrap_DataModule -{ - love::data::ContainerType CheckContainerType(lua_State* L, int index); - - int NewByteData(lua_State* L); - - int NewDataView(lua_State* L); - - int Hash(lua_State* L); - - int Compress(lua_State* L); - - int Decompress(lua_State* L); - - int Decode(lua_State* L); - - int Encode(lua_State* L); - - int Pack(lua_State* L); - - int Unpack(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_DataModule diff --git a/include/modules/event/event.hpp b/include/modules/event/event.hpp new file mode 100644 index 000000000..3aa250b78 --- /dev/null +++ b/include/modules/event/event.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include + +namespace love +{ + class Event : public Module + { + public: + ModuleType GetModuleType() const override + { + return M_EVENT; + } + + const char* GetName() const override + { + return "love.event"; + } + + void Push(Message* message); + + bool Poll(Message*& message); + + void Clear(); + + void Pump(); + + Message* Wait(); + + void InternalClear(); + + Message* Convert(const LOVE_Event& event); + + Message* ConvertGeneralEvent(const LOVE_Event& event, std::vector& args); + + Message* ConvertJoystickEvent(const LOVE_Event& event, std::vector& args); + + Message* ConvertTouchEvent(const LOVE_Event& event, std::vector& args); + + Message* ConvertWindowEvent(const LOVE_Event& event, std::vector& args); + + Message* ConvertKeyboardEvent(const LOVE_Event& event, std::vector& args); + + protected: + std::queue queue; + LOVE_Event event; + love::mutex mutex; + }; +} // namespace love diff --git a/include/modules/event/eventc.h b/include/modules/event/eventc.h deleted file mode 100644 index f4a8e5972..000000000 --- a/include/modules/event/eventc.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -** modules/event.h -** @brief : Handles events.. like quitting.. and other things -*/ - -#pragma once - -#include "common/module.h" - -#include "common/message.h" - -#include "modules/joystick/joystick.h" - -#include "modules/thread/types/lock.h" -#include "modules/thread/types/mutex.h" - -#include "driver/hidrv.h" - -#include -#include -#include - -struct LOVE_Event; - -namespace love::driver -{ - class Hidrv; -} - -namespace love::common -{ - class Event : public Module - { - public: - Event(); - - ModuleType GetModuleType() const - { - return M_EVENT; - } - - const char* GetName() const override - { - return "love.event"; - } - - void InternalClear(); - - void Pump(); - - Message* Wait(); - - void Clear(); - - void Push(Message* message); - - bool Poll(Message*& message); - - void ExceptionIfInRenderPass(const char* name); - - std::unique_ptr& GetDriver(); - - protected: - love::thread::MutexRef mutex; - - std::unique_ptr driver; - - private: - std::queue queue; - - Message* Convert(const driver::Hidrv::LOVE_Event& event); - - Message* ConvertJoystickEvent(const driver::Hidrv::LOVE_Event& event) const; - - Message* ConvertWindowEvent(const driver::Hidrv::LOVE_Event& event); - }; -} // namespace love::common diff --git a/include/modules/event/wrap_event.h b/include/modules/event/wrap_event.h deleted file mode 100644 index 800c7ed02..000000000 --- a/include/modules/event/wrap_event.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "modules/event/event.h" -namespace Wrap_Event -{ - int Clear(lua_State* L); - - int Pump(lua_State* L); - - int Push(lua_State* L); - - int Quit(lua_State* L); - - int Wait(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_Event diff --git a/include/modules/event/wrap_event.hpp b/include/modules/event/wrap_event.hpp new file mode 100644 index 000000000..bca6472d1 --- /dev/null +++ b/include/modules/event/wrap_event.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace Wrap_Event +{ + int Clear(lua_State* L); + + int Pump(lua_State* L); + + int Push(lua_State* L); + + int Quit(lua_State* L); + + int Wait(lua_State* L); + + int Restart(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_Event diff --git a/include/modules/filesystem/filesystem.h b/include/modules/filesystem/filesystem.h deleted file mode 100644 index 8a5e5e64a..000000000 --- a/include/modules/filesystem/filesystem.h +++ /dev/null @@ -1,149 +0,0 @@ -/* -** modules/filesystem.h -** @brief : Filesystem operations (read/write files) -*/ - -#pragma once - -#include "common/module.h" - -#include "objects/file/file.h" -#include "objects/filedata/filedata.h" - -#include - -#define MAX_STAMP 0x20000000000000LL - -namespace love -{ - class Filesystem : public Module - { - public: - enum FileType - { - FILETYPE_FILE, - FILETYPE_DIRECTORY, - FILETYPE_SYMLINK, - FILETYPE_OTHER, - FILETYPE_MAX_ENUM - }; - - struct Info - { - int64_t size; - int64_t modtime; - FileType type; - }; - - Filesystem(); - ~Filesystem(); - - static love::Type type; - - /* Module implementors */ - - const char* GetName() const override - { - return "love.filesystem"; - } - - ModuleType GetModuleType() const - { - return M_FILESYSTEM; - } - - /* Löve2D Functions */ - - void Init(const char* arg0); - - void Append(const char* filename, const void* data, int64_t size); - - bool CreateDirectory(const char* name); - - void GetDirectoryItems(const char* directory, std::vector& items); - - const char* GetIdentity(); - - bool GetInfo(const char* filename, Info& info) const; - - std::string GetSaveDirectory(); - - File* NewFile(const char* filename); - - FileData* NewFileData(const void* data, size_t size, const char* filename); - - FileData* Read(const char* filename, int64_t size = File::ALL); - - bool Remove(const char* filename); - - bool SetIdentity(const char* identity, bool appendToPath); - - void Write(const char* filename, const void* data, int64_t size); - - void AllowMountingForPath(const std::string& path); - - bool Mount(const char* archive, const char* mountpoint, bool appendToPath = false); - - bool Mount(Data* data, const char* archive, const char* mountpoint, - bool appendToPath = false); - - bool UnMount(const char* archive); - - bool UnMount(Data* data); - - bool SetSource(const char* source); - - void SetFused(bool fused); - - bool IsFused() const; - - const char* GetSource() const; - - const char* GetWorkingDirectory(); - - std::string GetExecutablePath() const; - - std::string GetSourceBaseDirectory() const; - - bool SetupWriteDirectory(); - - std::vector& GetRequirePath(); - - void SetSymLinksEnabled(bool enable); - - std::string GetUserDirectory(); - - std::string GetRealDirectory(const char* filename) const; - - // std::vector & GetCRequirePath() override; - - /* Helper Functions */ - - static bool GetConstant(const char* in, FileType& out); - static bool GetConstant(FileType in, const char*& out); - - static std::vector GetConstants(FileType); - - private: - std::string identity; - std::string relativeSavePath; - std::string fullSavePath; - - std::string appdata; - std::string gameSource; - std::string cwd; - std::string exePath; - - std::vector allowedMountPaths; - - std::vector requirePath; - std::vector cRequirePath; - - std::map> mountedData; - - bool fused; - bool fusedSet; - - std::string GetAppDataDirectory(); - }; -} // namespace love diff --git a/include/modules/filesystem/filesystem.hpp b/include/modules/filesystem/filesystem.hpp new file mode 100644 index 000000000..1cbaace03 --- /dev/null +++ b/include/modules/filesystem/filesystem.hpp @@ -0,0 +1,178 @@ +#pragma once + +#include +#include + +#include +#include + +#include + +#include +#include + +namespace love +{ + class Filesystem : public Module + { + public: + enum FileType + { + FILETYPE_FILE, + FILETYPE_DIRECTORY, + FILETYPE_SYMLINK, + FILETYPE_OTHER, + FILETYPE_MAX_ENUM + }; + + enum CommonPath + { + APP_SAVEDIR, + APP_DOCUMENTS, + USER_HOME, + USER_APPDATA, + USER_DOCUMENTS, + PATH_MAX_ENUM + }; + + enum MountPermissions + { + MOUNT_READ, + MOUNT_READWRITE, + MOUNT_MAX_ENUM + }; + + struct Info + { + int64_t size; + int64_t modtime; + FileType type; + bool readOnly; + }; + + Filesystem() + {} + + virtual ~Filesystem() + {} + + ModuleType GetModuleType() const override + { + return M_FILESYSTEM; + } + + virtual void Init(const char* arg0) = 0; + + virtual void SetFused(bool fused) = 0; + + virtual bool IsFused() const = 0; + + virtual bool SetIdentity(const char* identity, bool appendToPath) = 0; + + virtual std::string GetIdentity() const = 0; + + virtual bool SetSource(const char* source) = 0; + + virtual std::string GetSource() const = 0; + + virtual bool Mount(const char* archive, const char* mountpoint, + bool appendToPath = false) = 0; + + virtual bool Mount(Data* data, const char* archive, const char* mountpoint, + bool appendToPath = false) = 0; + + virtual bool MountFullPath(const char* archive, const char* mountPoint, + MountPermissions permissions, bool appendToPath = false) = 0; + + virtual bool MountCommonPath(CommonPath path, const char* mountPoint, + MountPermissions permissions, bool appendToPath = false) = 0; + + virtual bool UnMount(const char* archive) = 0; + + virtual bool UnMount(Data* data) = 0; + + virtual bool UnMount(CommonPath path) = 0; + + virtual bool UnMountFullPath(const char* fullPath) = 0; + + virtual File* OpenFile(const char* filename, File::Mode mode) const = 0; + + FileData* NewFileData(const void* data, size_t size, const char* filename) const; + + virtual std::string GetFullCommonPath(CommonPath path) = 0; + + virtual std::string GetWorkingDirectory() = 0; + + virtual std::string GetUserDirectory() = 0; + + virtual std::string GetAppdataDirectory() = 0; + + virtual std::string GetSaveDirectory() = 0; + + virtual std::string GetSourceBaseDirectory() const = 0; + + virtual std::string GetRealDirectory(const char* filename) const = 0; + + virtual bool GetInfo(const char* filename, Info& info) const = 0; + + virtual bool CreateDirectory(const char* name) = 0; + + virtual bool Remove(const char* filename) = 0; + + virtual FileData* Read(const char* filename, int64_t size) const = 0; + + virtual FileData* Read(const char* filename) const = 0; + + virtual void Write(const char* filename, const void* data, int64_t size) const = 0; + + virtual void Append(const char* filename, const void* data, int64_t size) const = 0; + + virtual bool GetDirectoryItems(const char* directory, std::vector& items) = 0; + + virtual void SetSymlinksEnabled(bool enable) = 0; + + virtual bool AreSymlinksEnabled() const = 0; + + virtual std::vector& GetRequirePath() = 0; + + // virtual std::vector& GetCRequirePath() = 0; + + virtual void AllowMountingForPath(const std::string& path) = 0; + + virtual bool SetupWriteDirectory() = 0; + + bool IsRealDirectory(const std::string& path) const; + + bool CreateRealDirectory(const std::string& path); + + std::string GetExecutablePath() const; + + // clang-format off + static constexpr BidirectionalMap fileTypes = { + "file", Filesystem::FileType::FILETYPE_FILE, + "directory", Filesystem::FileType::FILETYPE_DIRECTORY, + "symlink", Filesystem::FileType::FILETYPE_SYMLINK, + "other", Filesystem::FileType::FILETYPE_OTHER + }; + + static constexpr BidirectionalMap commonPaths = { + "appsavedir", Filesystem::APP_SAVEDIR, + "appdocuments", Filesystem::APP_DOCUMENTS, + "userhome", Filesystem::USER_HOME, + "userdocuments", Filesystem::USER_DOCUMENTS + }; + + static constexpr BidirectionalMap mountPermissions = { + "read", Filesystem::MOUNT_READ, + "readwrite", Filesystem::MOUNT_READWRITE + }; + // clang-format on + + protected: + static constexpr int64_t MAX_STAMP = 0x20000000000000; + + std::string executablePath; + + bool GetRealPathType(const std::string& path, FileType& ftype) const; + }; +} // namespace love diff --git a/include/modules/filesystem/physfs/filesystem.hpp b/include/modules/filesystem/physfs/filesystem.hpp new file mode 100644 index 000000000..580ac1646 --- /dev/null +++ b/include/modules/filesystem/physfs/filesystem.hpp @@ -0,0 +1,143 @@ +#pragma once + +#include + +#include + +#include + +#include +#include + +namespace love::physfs +{ + class Filesystem : public love::Filesystem + { + public: + Filesystem(); + + virtual ~Filesystem(); + + const char* GetName() const override + { + return "love.filesystem.physfs"; + } + + void Init(const char* arg0) override; + + void SetFused(bool fused) override; + + bool IsFused() const override; + + bool SetIdentity(const char* identity, bool appendToPath = false) override; + + std::string GetIdentity() const override; + + bool SetSource(const char* source) override; + + std::string GetSource() const override; + + bool Mount(const char* archive, const char* mountPoint, bool appendToPath = false) override; + + bool Mount(Data* data, const char* name, const char* mountPoint, + bool appendToPath = false) override; + + bool MountFullPath(const char* archive, const char* mountPoint, + MountPermissions permissions, bool appendToPath = false) override; + + bool MountCommonPath(CommonPath path, const char* mountPoint, MountPermissions permissions, + bool appendToPath = false) override; + + bool UnMount(const char* archive) override; + + bool UnMount(Data* data) override; + + bool UnMount(CommonPath path) override; + + bool UnMountFullPath(const char* fullPath) override; + + love::File* OpenFile(const char* filename, File::Mode mode) const override; + + std::string GetFullCommonPath(CommonPath path) override; + + std::string GetWorkingDirectory() override; + + std::string GetUserDirectory() override; + + std::string GetAppdataDirectory() override; + + std::string GetSaveDirectory() override; + + std::string GetSourceBaseDirectory() const override; + + std::string GetRealDirectory(const char* filename) const override; + + bool GetInfo(const char* filepath, Info& info) const override; + + bool CreateDirectory(const char* directory) override; + + bool Remove(const char* file) override; + + FileData* Read(const char* filename, int64_t size) const override; + + FileData* Read(const char* filename) const override; + + void Write(const char* filename, const void* data, int64_t size) const override; + + void Append(const char* filename, const void* data, int64_t size) const override; + + bool GetDirectoryItems(const char* directory, std::vector& items) override; + + void SetSymlinksEnabled(bool enable) override; + + bool AreSymlinksEnabled() const override; + + std::vector& GetRequirePath() override; + + // std::vector& GetCRequirePath() override; + + void AllowMountingForPath(const std::string& path) override; + + bool SetupWriteDirectory() override; + + static const char* GetLastError(bool clear = false); + + private: + struct CommonPathMountInfo + { + bool mounted; + std::string mountPoint; + MountPermissions permissions; + }; + + bool MountCommonPathInternal(CommonPath path, const char* mountpoint, + MountPermissions permissions, bool appendToPath, + bool createDirectory); + + static bool IsAppCommonPath(CommonPath path); + + std::string currentDirectory; + std::string saveIdentity; + + bool appendIdentityToPath; + + std::string gameSource; + + bool fused; + bool fusedSet; + + std::vector requirePath; + // std::vector cRequirePath + + std::vector allowedMountPaths; + + std::map> mountedData; + + std::array fullPaths; + std::array commonPathMountInfo; + + bool saveDirectoryNeedsMounting; + + std::array appCommonPaths; + }; +} // namespace love::physfs diff --git a/include/modules/filesystem/wrap_filesystem.h b/include/modules/filesystem/wrap_filesystem.h deleted file mode 100644 index f1737259d..000000000 --- a/include/modules/filesystem/wrap_filesystem.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include "common/data.h" -#include "modules/data/wrap_datamodule.h" -#include "modules/filesystem/filesystem.h" -#include "objects/file/wrap_file.h" -#include "objects/filedata/wrap_filedata.h" - -bool SetupWriteDirectory(); -namespace Wrap_Filesystem -{ - love::Data* GetData(lua_State* L, int index); - - int Init(lua_State* L); - - int Append(lua_State* L); - - int CreateDirectory(lua_State* L); - - int GetDirectoryItems(lua_State* L); - - int GetIdentity(lua_State* L); - - int GetRealDirectory(lua_State* L); - - int SetRequirePath(lua_State* L); - - int GetUserDirectory(lua_State* L); - - int Lines(lua_State* L); - - int Load(lua_State* L); - - int GetInfo(lua_State* L); - - int GetSaveDirectory(lua_State* L); - - int GetExecutablePath(lua_State* L); - - int GetRequirePath(lua_State* L); - - int Loader(lua_State* L); - - int Load(lua_State* L); - - int NewFile(lua_State* L); - - int NewFileData(lua_State* L); - - int SetFused(lua_State* L); - - int IsFused(lua_State* L); - - int SetSource(lua_State* L); - - int GetSource(lua_State* L); - - int GetSourceBaseDirectory(lua_State* L); - - int GetWorkingDirectory(lua_State* L); - - int Mount(lua_State* L); - - int UnMount(lua_State* L); - - int Read(lua_State* L); - - int Register(lua_State* L); - - int Remove(lua_State* L); - - int Remove(lua_State* L); - - int SetIdentity(lua_State* L); - - int WriteOrAppend(lua_State* L, love::File::Mode mode); - - love::File* GetFile(lua_State* L, int index); - - love::FileData* GetFileData(lua_State* L, int index); - - bool CanGetData(lua_State* L, int index); - - int Write(lua_State* L); - - std::string Redirect(const char* path); -} // namespace Wrap_Filesystem diff --git a/include/modules/filesystem/wrap_filesystem.hpp b/include/modules/filesystem/wrap_filesystem.hpp new file mode 100644 index 000000000..9a66f4259 --- /dev/null +++ b/include/modules/filesystem/wrap_filesystem.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include + +#include + +#include +#include + +namespace Wrap_Filesystem +{ + love::Filesystem::MountPermissions CheckPermissionType(lua_State* L, int index); + + love::Filesystem::CommonPath CheckCommonPathType(lua_State* L, int index); + + int Init(lua_State* L); + + int Append(lua_State* L); + + int CreateDirectory(lua_State* L); + + int GetDirectoryItems(lua_State* L); + + int GetIdentity(lua_State* L); + + int GetRealDirectory(lua_State* L); + + int SetRequirePath(lua_State* L); + + int GetUserDirectory(lua_State* L); + + int Lines(lua_State* L); + + int Load(lua_State* L); + + int GetInfo(lua_State* L); + + int GetSaveDirectory(lua_State* L); + + int GetExecutablePath(lua_State* L); + + int GetRequirePath(lua_State* L); + + int Loader(lua_State* L); + + int Load(lua_State* L); + + int OpenFile(lua_State* L); + + int NewFileData(lua_State* L); + + int SetFused(lua_State* L); + + int IsFused(lua_State* L); + + int SetSource(lua_State* L); + + int GetSource(lua_State* L); + + int GetSourceBaseDirectory(lua_State* L); + + int GetWorkingDirectory(lua_State* L); + + int Mount(lua_State* L); + + int MountFullPath(lua_State* L); + + int MountCommonPath(lua_State* L); + + int UnMount(lua_State* L); + + int UnMountFullPath(lua_State* L); + + int UnMountCommonPath(lua_State* L); + + int Read(lua_State* L); + + int Register(lua_State* L); + + int Remove(lua_State* L); + + int Remove(lua_State* L); + + int SetIdentity(lua_State* L); + + int WriteOrAppend(lua_State* L, love::File::Mode mode); + + love::Data* GetData(lua_State* L, int index); + + love::File* GetFile(lua_State* L, int index); + + love::FileData* GetFileData(lua_State* L, int index, bool ioError, int& numResults); + + love::FileData* GetFileData(lua_State* L, int index); + + bool CanGetData(lua_State* L, int index); + + bool CanGetFile(lua_State* L, int index); + + bool CanGetFileData(lua_State* L, int index); + + int Write(lua_State* L); + + std::string Redirect(const char* path); + + bool SetupWriteDirectory(); +} // namespace Wrap_Filesystem diff --git a/include/modules/font/fontmodule.tcc b/include/modules/font/fontmodule.tcc new file mode 100644 index 000000000..e7f5e210c --- /dev/null +++ b/include/modules/font/fontmodule.tcc @@ -0,0 +1,70 @@ +#pragma once + +#include +#include + +#include +#include + +#include + +#include + +#include + +#include + +namespace love +{ + class SystemFont; + + template + class FontModule : public Module + { + public: + FontModule(); + + virtual ~FontModule(); + + virtual ModuleType GetModuleType() const + { + return M_FONT; + } + + virtual const char* GetName() const + { + return "love.font"; + } + + Rasterizer* NewBMFontRasterizer(FileData* data, + const std::vector*>& images, + float dpiScale) const; + + virtual Rasterizer* NewImageRasterizer(ImageData* data, + const std::string& text, int extraSpacing, + float dpiScale) const = 0; + + virtual Rasterizer* NewImageRasterizer(ImageData* data, uint32_t* glyphs, + int glyphCount, int extraSpacing, + float dpiScale) const = 0; + + Rasterizer* NewTrueTypeRasterizer(int size, TrueTypeRasterizer<>::Hinting hinting) const; + + Rasterizer* NewTrueTypeRasterizer(int size, float dpiScale, + TrueTypeRasterizer<>::Hinting hinting) const; + + virtual Rasterizer* NewTrueTypeRasterizer(Data* data, int size, + TrueTypeRasterizer<>::Hinting hinting) const = 0; + + virtual Rasterizer* NewTrueTypeRasterizer(Data* data, int size, float dpiScale, + TrueTypeRasterizer<>::Hinting hinting) const = 0; + + GlyphData* NewGlyphData(Rasterizer* rasterizer, const std::string& text) const; + + GlyphData* NewGlyphData(Rasterizer* rasterizer, uint32_t glyph) const; + + protected: + StrongReference defaultFontData; + FT_Library library; + }; +} // namespace love diff --git a/include/modules/font/fontmodulec.h b/include/modules/font/fontmodulec.h deleted file mode 100644 index 6925e9db5..000000000 --- a/include/modules/font/fontmodulec.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "common/data.h" -#include "utf8/utf8.h" - -#include "objects/filedata/filedata.h" -#include "objects/font/font.h" - -#include "objects/glyphdata/glyphdata.h" -#include "objects/rasterizer/rasterizer.h" - -#include "common/module.h" - -namespace love::common -{ - class FontModule : public Module - { - public: - FontModule(); - - virtual ~FontModule(); - - ModuleType GetModuleType() const - { - return M_FONT; - } - - const char* GetName() const override - { - return "love.font"; - } - - virtual Rasterizer* NewRasterizer(FileData* data) = 0; - - love::GlyphData* NewGlyphData(Rasterizer* rasterizer, const std::string& glyph); - - love::GlyphData* NewGlyphData(Rasterizer* rasterizer, uint32_t glyph); - }; -} // namespace love::common diff --git a/include/modules/font/wrap_fontmodule.h b/include/modules/font/wrap_fontmodule.h deleted file mode 100644 index 0c3459916..000000000 --- a/include/modules/font/wrap_fontmodule.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "modules/font/fontmodule.h" - -#include "modules/filesystem/wrap_filesystem.h" -#include "objects/filedata/wrap_filedata.h" - -#include "objects/glyphdata/wrap_glyphdata.h" -#include "objects/rasterizer/wrap_rasterizer.h" - -namespace Wrap_FontModule -{ - int NewRasterizer(lua_State* L); - - int NewGlyphData(lua_State* L); - - int NewTrueTypeRasterizer(lua_State* L); - - int NewBCFNTRasterizer(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_FontModule diff --git a/include/modules/font/wrap_fontmodule.hpp b/include/modules/font/wrap_fontmodule.hpp new file mode 100644 index 000000000..f9876922a --- /dev/null +++ b/include/modules/font/wrap_fontmodule.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace Wrap_FontModule +{ + int NewRasterizer(lua_State* L); + + int NewGlyphData(lua_State* L); + + int NewTrueTypeRasterizer(lua_State* L); + + int NewBMFontRasterizer(lua_State* L); + + int NewImageRasterizer(lua_State* L); + + int Register(lua_State* L); + + extern std::span extensions; +} // namespace Wrap_FontModule diff --git a/include/modules/graphics/graphics.h b/include/modules/graphics/graphics.h deleted file mode 100644 index ea637b06e..000000000 --- a/include/modules/graphics/graphics.h +++ /dev/null @@ -1,663 +0,0 @@ -/* -** modules/graphics.h -** @brief : handles graphical drawing -*/ - -#pragma once - -#include "common/colors.h" -#include "common/module.h" -#include "common/screen.h" -#include "common/vector.h" - -/* OBJECTS */ - -#include "objects/texture/texture.h" - -#include "objects/image/image.h" -#include "objects/image/wrap_image.h" - -#include "objects/font/font.h" -#include "objects/font/wrap_font.h" - -#include "objects/quad/quad.h" -#include "objects/quad/wrap_quad.h" - -#include "objects/canvas/canvas.h" -#include "objects/canvas/wrap_canvas.h" - -#include "objects/transform/transform.h" -#include "objects/transform/wrap_transform.h" - -#include "objects/text/text.h" -#include "objects/text/wrap_text.h" - -#include "modules/font/fontmodule.h" - -#include "objects/imagedata/imagedata.h" - -#include "objects/video/video.h" -#include "objects/videostream/videostream.h" - -#include "common/lmath.h" -#include -#include - -#if defined(__SWITCH__) - #include "deko3d/shader.h" - #include "deko3d/vertex.h" - - #include "objects/shader/wrap_shader.h" -#endif - -namespace love -{ - class Graphics : public Module - { - public: - static const size_t MAX_USER_STACK_DEPTH = 128; - - enum DrawMode - { - DRAW_LINE, - DRAW_FILL, - DRAW_MAX_ENUM - }; - - enum ArcMode - { - ARC_OPEN, - ARC_CLOSED, - ARC_PIE, - ARC_MAX_ENUM - }; - - enum BlendMode - { - BLEND_ALPHA, - BLEND_ADD, - BLEND_SUBTRACT, - BLEND_MULTIPLY, - BLEND_LIGHTEN, - BLEND_DARKEN, - BLEND_SCREEN, - BLEND_REPLACE, - BLEND_NONE, - BLEND_MAX_ENUM - }; - - enum BlendAlpha - { - BLENDALPHA_MULTIPLY, - BLENDALPHA_PREMULTIPLIED, - BLENDALPHA_MAX_ENUM - }; - - enum LineStyle - { - LINE_ROUGH, - LINE_SMOOTH, - LINE_MAX_ENUM - }; - - enum LineJoin - { - LINE_JOIN_NONE, - LINE_JOIN_MITER, - LINE_JOIN_BEVEL, - LINE_JOIN_MAX_ENUM - }; - - struct RendererInfo - { - std::string name; - std::string version; - std::string vendor; - std::string device; - }; - - enum StackType - { - STACK_ALL, - STACK_TRANSFORM, - STACK_MAX_ENUM - }; - - static constexpr float MIN_DEPTH = 1.0f / 16384.0f; - static inline float CURRENT_DEPTH = 0; - static inline RenderScreen ACTIVE_SCREEN = 0; - - struct ColorMask - { - bool r : 1, g : 1, b : 1, a : 1; - - ColorMask() : r(true), g(true), b(true), a(true) - {} - - ColorMask(bool _r, bool _g, bool _b, bool _a) : r(_r), g(_g), b(_b), a(_a) - {} - - bool operator==(const ColorMask& m) const - { - return r == m.r && g == m.g && b == m.b && a == m.a; - } - - bool operator!=(const ColorMask& m) const - { - return !(operator==(m)); - } - - uint8_t GetColorMask() const - { - return r | (g << 1) | (b << 2) | (a << 3); - } - }; - - std::vector transformStack; - - /* Gamma Correction */ - - static bool gammaCorrectColor; - - static void SetGammaCorrect(bool enable); - - static bool IsGammaCorrect(); - - static void GammaCorrectColor(Colorf& c); - - static void UnGammaCorrectColor(Colorf& c); - - static Colorf GammaCorrectColor(const Colorf& c); - - static Colorf UnGammaCorrectColor(const Colorf& c); - - /* End */ - - Graphics(); - - virtual ~Graphics(); - - ModuleType GetModuleType() const - { - return M_GRAPHICS; - } - - const char* GetName() const override - { - return "love.graphics"; - } - - template - static void CheckStandardTransform(lua_State* L, int idx, const T& func) - { - if (Luax::IsType(L, idx, Transform::type)) - { - Transform* self = Luax::ToType(L, idx); - func(self->GetMatrix()); - } - else - { - float x = (float)luaL_optnumber(L, idx + 0, 0.0); - float y = (float)luaL_optnumber(L, idx + 1, 0.0); - float a = (float)luaL_optnumber(L, idx + 2, 0.0); - float sx = (float)luaL_optnumber(L, idx + 3, 1.0); - float sy = (float)luaL_optnumber(L, idx + 4, sx); - float ox = (float)luaL_optnumber(L, idx + 5, 0.0); - float oy = (float)luaL_optnumber(L, idx + 6, 0.0); - float kx = (float)luaL_optnumber(L, idx + 7, 0.0); - float ky = (float)luaL_optnumber(L, idx + 8, 0.0); - - func(Matrix4(x, y, a, sx, sy, ox, oy, kx, ky)); - } - } - - Matrix4 GetTransform() - { - return this->transformStack.back(); - } - - bool IsActive() const; - - bool IsCreated() const; - - Colorf GetColor() const; - - Colorf GetBackgroundColor() const; - - void SetBackgroundColor(const Colorf& color); - - /* render screen */ - - void SetActiveScreen(RenderScreen screen); - - const RenderScreen GetActiveScreen() const; - - const int GetWidth(RenderScreen screen) const; - - const int GetHeight() const; - - std::vector GetScreens() const; - - /* end screens */ - - bool GetScissor(Rect& scissor) const; - - void IntersectScissor(const Rect& scissor); - - const Texture::Filter& GetDefaultFilter() const; - - void Push(StackType type = STACK_TRANSFORM); - - void ApplyTransform(Transform* transform); - - void ReplaceTransform(Transform* transform); - - void Origin(); - - void Translate(float offsetX, float offsetY); - - void Rotate(float rotation); - - void Scale(float scalarX, float ScalarY); - - void Shear(float kx, float ky); - - void Pop(); - - /* Objects */ - - Image* NewImage(const Image::Slices& data); - - virtual Font* NewFont(Rasterizer* rasterizer, - const Texture::Filter& filter = Texture::defaultFilter) = 0; -#if defined(__SWITCH__) - virtual Font* NewDefaultFont(int size, TrueTypeRasterizer::Hinting hinting, - const Texture::Filter& filter = Texture::defaultFilter) = 0; - -#elif defined(__3DS__) - virtual Font* NewDefaultFont(int size, - const Texture::Filter& filter = Texture::defaultFilter) = 0; -#endif - - Image* NewImage(Texture::TextureType t, PixelFormat format, int width, int height, - int slices); - - Quad* NewQuad(Quad::Viewport v, double sw, double sh); - - Text* NewText(Font* font, const std::vector& text = {}); - - void SetFont(Font* font); - - Font* GetFont(); - - Video* NewVideo(VideoStream* stream, float dpiscale); - - float GetPointSize() const; - - LineStyle GetLineStyle() const; - - LineJoin GetLineJoin() const; - - float GetLineWidth() const; - - void SetLineJoin(LineJoin join); - - void SetLineStyle(LineStyle style); - - Canvas* NewCanvas(const Canvas::Settings& settings); - - Canvas* GetCanvas() const; - - void Draw(Drawable* drawable, const Matrix4& matrix); - void Draw(Texture* texture, Quad* quad, const Matrix4& matrix); - - void Print(const std::vector& strings, const Matrix4& localTransform); - void Print(const std::vector& strings, Font* font, - const Matrix4& localTransform); - - void PrintF(const std::vector& strings, float wrap, - Font::AlignMode align, const Matrix4& localTransform); - void PrintF(const std::vector& strings, Font* font, float wrap, - Font::AlignMode align, const Matrix4& localTransform); - - virtual void SetDefaultFilter(const Texture::Filter& filter); - - /* virtual void stuff -- subclass implements */ - - virtual void SetScissor(const Rect& rect) = 0; - - virtual void SetScissor() = 0; - - virtual void Clear(std::optional color, std::optional stencil, - std::optional depth) = 0; - - virtual void Clear(std::vector>& colors, std::optional stencil, - std::optional depth) = 0; - - Graphics::BlendMode GetBlendMode(BlendAlpha& alphaMode); - - virtual void SetBlendMode(BlendMode mode, BlendAlpha alpha) = 0; - - virtual void SetColorMask(ColorMask mask) = 0; - - virtual void SetColor(Colorf color); - - void SetDefaultMipmapFilter(Texture::FilterMode filter, float sharpness); - -#if defined(__SWITCH__) - virtual void SetMeshCullMode(vertex::CullMode cull) = 0; - - virtual void SetFrontFaceWinding(vertex::Winding winding) = 0; -#endif - - /* Primitives */ - - virtual void Rectangle(DrawMode mode, float x, float y, float width, float height) = 0; - - virtual void Rectangle(DrawMode mode, float x, float y, float width, float height, float rx, - float ry) = 0; - - virtual void Rectangle(DrawMode mode, float x, float y, float width, float height, float rx, - float ry, int points) = 0; - - virtual void Ellipse(DrawMode mode, float x, float y, float a, float b) = 0; - - virtual void Ellipse(DrawMode mode, float x, float y, float a, float b, int points) = 0; - - virtual void Circle(DrawMode mode, float x, float y, float radius) = 0; - - virtual void Circle(DrawMode mode, float x, float y, float radius, int points) = 0; - -#if defined(__SWITCH__) - virtual void Polygon(DrawMode mode, const Vector2* points, size_t count, - bool skipLastFilledVertex = true) = 0; -#else - virtual void Polygon(DrawMode mode, const Vector2* points, size_t count) = 0; -#endif - - virtual void Arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, - float angle1, float angle2) = 0; - - virtual void Arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, - float angle1, float angle2, int points) = 0; - - virtual void Points(const Vector2* points, size_t count, const Colorf* colors, - size_t colorCount) = 0; - - virtual void SetPointSize(float size) = 0; - - virtual void Line(const Vector2* points, int count) = 0; - - virtual void SetLineWidth(float width) = 0; - - virtual RendererInfo GetRendererInfo() const = 0; - - Vector2 TransformPoint(Vector2 point); - - Vector2 InverseTransformPoint(Vector2 point); - - /* Primitives */ - - ColorMask GetColorMask() const; - -#if defined(__SWITCH__) - vertex::CullMode GetMeshCullMode() const; - - vertex::Winding GetFrontFaceWinding() const; - - void SetShader(); - - void SetShader(Shader* shader); - - Shader* GetShader() const; -#endif - - size_t GetStackDepth() const - { - return this->stackTypeStack.size(); - } - - /* RenderTarget Stuff */ - - enum TemporaryRenderTargetFlags - { - TEMPORARY_RT_DEPTH = (1 << 0), - TEMPORARY_RT_STENCIL = (1 << 1), - }; - - struct RenderTargetStrongRef; - - struct RenderTarget - { - Canvas* canvas; - int mipmap; - - RenderTarget(Canvas* canvas, int mipmap = 0) : canvas(canvas), mipmap(mipmap) - {} - - RenderTarget() : canvas(nullptr), mipmap(0) - {} - - bool operator!=(const RenderTarget& other) const - { - return canvas != other.canvas || mipmap != other.mipmap; - } - - bool operator!=(const RenderTargetStrongRef& other) const - { - return canvas != other.canvas.Get() || mipmap != other.mipmap; - } - }; - - struct RenderTargetStrongRef - { - StrongReference canvas; - int mipmap = 0; - - RenderTargetStrongRef(Canvas* canvas, int mipmap = 0) : canvas(canvas), mipmap(mipmap) - {} - - bool operator!=(const RenderTargetStrongRef& other) const - { - return canvas.Get() != other.canvas.Get() || mipmap != other.mipmap; - } - - bool operator!=(const RenderTarget& other) const - { - return canvas.Get() != other.canvas || mipmap != other.mipmap; - } - }; - - struct RenderTargets - { - std::vector colors; - RenderTarget depthStencil; - - uint32_t temporaryRTFlags; - - RenderTargets() : depthStencil(nullptr), temporaryRTFlags(0) - {} - - const RenderTarget& GetFirstTarget() const - { - return colors.empty() ? depthStencil : colors[0]; - } - - bool operator==(const RenderTargets& other) const - { - size_t ncolors = colors.size(); - if (ncolors != other.colors.size()) - return false; - - for (size_t i = 0; i < ncolors; i++) - { - if (colors[i] != other.colors[i]) - return false; - } - - if (depthStencil != other.depthStencil || - temporaryRTFlags != other.temporaryRTFlags) - return false; - - return true; - } - }; - - struct RenderTargetsStrongRef - { - std::vector colors; - RenderTargetStrongRef depthStencil; - - uint32_t temporaryRTFlags; - - RenderTargetsStrongRef() : depthStencil(nullptr), temporaryRTFlags(0) - {} - - const RenderTargetStrongRef& GetFirstTarget() const - { - return colors.empty() ? depthStencil : colors[0]; - } - }; - - class TempTransform - { - public: - TempTransform(Graphics* gfx) : gfx(gfx) - { - gfx->PushTransform(); - } - - TempTransform(Graphics* gfx, const Matrix4& t) : gfx(gfx) - { - gfx->PushTransform(); - gfx->transformStack.back() *= t; - } - - template - void TransformXY(vDst dst, vSrc src, int count) - { - gfx->GetTransform().TransformXY(dst, src, count); - } - - ~TempTransform() - { - gfx->PopTransform(); - } - - private: - Graphics* gfx; - }; - - struct Stats - { - int drawCalls; - int canvasSwitches; - int shaderSwitches; - int canvases; - int images; - int fonts; - }; - - void PushTransform(); - - void PopTransform(); - - void SetCanvas(Canvas* canvas); - - bool IsCanvasActive(Canvas* canvas) const; - - bool IsCanvasActive() const; - - /* States or Something */ - void Reset(); - - virtual void Present() = 0; - - bool SetMode(int width, int height); - - static bool GetConstant(const char* in, DrawMode& out); - static bool GetConstant(DrawMode in, const char*& out); - static std::vector GetConstants(DrawMode); - - static bool GetConstant(const char* in, BlendMode& out); - static bool GetConstant(BlendMode in, const char*& out); - static std::vector GetConstants(BlendMode); - - static bool GetConstant(const char* in, BlendAlpha& out); - static bool GetConstant(BlendAlpha in, const char*& out); - static std::vector GetConstants(BlendAlpha); - - static bool GetConstant(const char* in, ArcMode& out); - static bool GetConstant(ArcMode in, const char*& out); - static std::vector GetConstants(ArcMode); - - static bool GetConstant(const char* in, StackType& out); - static bool GetConstant(StackType in, const char*& out); - static std::vector GetConstants(StackType); - - static bool GetConstant(const char* in, LineStyle& out); - static bool GetConstant(LineStyle in, const char*& out); - static std::vector GetConstants(LineStyle); - - static bool GetConstant(const char* in, LineJoin& out); - static bool GetConstant(LineJoin in, const char*& out); - static std::vector GetConstants(LineJoin); - - protected: - struct DisplayState - { - Colorf foreground = Colorf(1, 1, 1, 1); - Colorf background = Colorf(0, 0, 0, 1); - - float lineWidth = 2.0f; - float pointSize = 1.0f; - - StrongReference font; - StrongReference canvas; - -#if defined(__SWITCH__) - StrongReference shader; -#endif - - Rect scissorRect = Rect(); - bool scissor = false; - - Texture::Filter defaultFilter = Texture::Filter(); - Texture::FilterMode defaultMipmapFilter = Texture::FILTER_LINEAR; - float defaultMipmapSharpness = 0.0f; - - LineStyle lineStyle = LINE_SMOOTH; - LineJoin lineJoin = LINE_JOIN_MITER; - -#if defined(__SWITCH__) - vertex::CullMode meshCullMode = vertex::CULL_NONE; - vertex::Winding winding = vertex::WINDING_CCW; -#endif - - BlendMode blendMode = BLEND_ALPHA; - BlendAlpha blendAlphaMode = BLENDALPHA_MULTIPLY; - - ColorMask colorMask = ColorMask(true, true, true, true); - }; - - std::vector states; - std::vector stackTypeStack; - std::vector pixelScaleStack; - - void RestoreState(const DisplayState& state); - - void RestoreStateChecked(const DisplayState& state); - - int width; - int height; - - private: - void CheckSetDefaultFont(); - - bool active; - bool created; - - StrongReference defaultFont; - RendererInfo rendererInfo; - }; -} // namespace love diff --git a/include/modules/graphics/graphics.tcc b/include/modules/graphics/graphics.tcc new file mode 100644 index 000000000..97e2cc048 --- /dev/null +++ b/include/modules/graphics/graphics.tcc @@ -0,0 +1,1533 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace love +{ + using OptionalColor = std::optional; + using OptionalInt = std::optional; + using OptionalDouble = std::optional; + + template + class Graphics : public Module + { + public: + static constexpr uint8_t MAX_USER_STACK_DEPTH = 0x80; + + enum DrawMode + { + DRAW_LINE, + DRAW_FILL + }; + + enum ArcMode + { + ARC_OPEN, + ARC_CLOSED, + ARC_PIE + }; + + enum StackType + { + STACK_ALL, + STACK_TRANSFORM + }; + + enum TemporaryRenderTargetFlags + { + TEMPORARY_RT_DEPTH = (1 << 0), + TEMPORARY_RT_STENCIL = (1 << 1), + }; + + struct RenderTargetStrongReference; + + struct RenderTarget + { + public: + RenderTarget(Texture* texture, int slice = 0, int mipmap = 0) : + texture(texture), + slice(slice), + mipmap(mipmap) + {} + + RenderTarget() : texture(nullptr), slice(0), mipmap(0) + {} + + bool operator!=(const RenderTarget& other) const + { + return this->texture != other.texture || this->slice != other.slice || + mipmap != other.mipmap; + } + + bool operator!=(const RenderTargetStrongReference& other) const + { + return this->texture != other.texture.Get() || this->slice != other.slice || + this->mipmap != other.mipmap; + } + + Texture* texture; + int slice; + int mipmap; + }; + + struct RenderTargetStrongReference + { + public: + RenderTargetStrongReference(Texture* texture, int slice = 0, + int mipmap = 0) : + texture(texture), + slice(slice), + mipmap(mipmap) + {} + + bool operator!=(const RenderTargetStrongReference& other) const + { + return this->texture.Get() != other.texture.Get() || this->slice != other.slice || + this->mipmap != other.mipmap; + } + + bool operator!=(const RenderTarget& other) const + { + return this->texture.Get() != other.texture || this->slice != other.slice || + this->mipmap != other.mipmap; + } + + StrongReference> texture; + int slice = 0; + int mipmap = 0; + }; + + struct RenderTargets + { + public: + std::vector colors; + RenderTarget depthStencil; + uint32_t temporaryFlags; + + RenderTargets() : depthStencil(nullptr), temporaryFlags(0) + {} + + const RenderTarget& GetFirstTarget() const + { + return colors.empty() ? depthStencil : this->colors[0]; + } + + bool operator==(const RenderTargets& other) const + { + size_t totalColors = colors.size(); + if (totalColors != other.colors.size()) + return false; + + for (size_t index = 0; index < totalColors; index++) + { + if (this->colors[index] != other.colors[index]) + return false; + } + + if (this->depthStencil != other.depthStencil || + this->temporaryRTFlags != other.temporaryRTFlags) + { + return false; + } + + return true; + } + }; + + struct RenderTargetsStrongReference + { + public: + RenderTargetsStrongReference() : depthStencil(nullptr), temporaryFlags(0) + {} + + const RenderTargetStrongReference& GetFirstTarget() const + { + return this->colors.empty() ? this->depthStencil : this->colors[0]; + } + + std::vector colors; + RenderTargetStrongReference depthStencil; + uint_fast32_t temporaryFlags; + }; + + struct Stats + { + int drawCalls; + int drawCallsBatched; + int renderTargetSwitches; + int shaderSwitches; + int textures; + int fonts; + int64_t textureMemory; + + float gpuTime; + float cpuTime; + }; + + struct DisplayState + { + DisplayState() + { + this->defaultSamplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_LINEAR; + } + + Color foreground = { 1.0, 1.0, 1.0, 1.0 }; + Color background = { 0.0, 0.0, 0.0, 1.0 }; + + RenderState::BlendState blendState = RenderState::ComputeBlendState( + RenderState::BLEND_ALPHA, RenderState::BLENDALPHA_MULTIPLY); + + StrongReference font; + StrongReference> shader; + RenderTargetsStrongReference renderTargets; + + struct + { + float width = 1.0f; + RenderState::LineStyle style = RenderState::LINE_SMOOTH; + RenderState::LineJoin join = RenderState::LINE_JOIN_MITER; + } line; + + float pointSize = 1.0f; + + struct + { + bool active = false; + Rect bounds = {}; + } scissor; + + struct + { + RenderState::CompareMode mode = RenderState::COMPARE_ALWAYS; + bool depthWrite = false; + } compare; + + vertex::CullMode cullMode = vertex::CULL_NONE; + vertex::Winding windingMode = vertex::WINDING_CCW; + + RenderState::ColorMask colorMask {}; + SamplerState defaultSamplerState = SamplerState {}; + }; + + static inline bool gammaCorrectColor = false; + + static void SetGammaCorrect(bool enable) + { + gammaCorrectColor = enable; + } + + static bool IsGammaCorrect() + { + return gammaCorrectColor; + } + + static void GammaCorrectColor(Color& color) + { + if (Graphics::IsGammaCorrect()) + { + color.r = Math::GammaToLinear(color.r); + color.g = Math::GammaToLinear(color.g); + color.a = Math::GammaToLinear(color.b); + } + } + + static Color GammaCorrectColor(const Color& color) + { + Color result = color; + Graphics::GammaCorrectColor(result); + + return result; + } + + static void UnGammaCorrectColor(Color& color) + { + if (Graphics::IsGammaCorrect()) + { + color.r = Math::LinearToGamma(color.r); + color.g = Math::LinearToGamma(color.g); + color.a = Math::LinearToGamma(color.b); + } + } + + static Color UnGammaCorrectColor(const Color& color) + { + Color result = color; + Graphics::UnGammaCorrectColor(result); + + return result; + } + + Graphics() : + width(0), + height(0), + pixelWidth(0), + pixelHeight(0), + created(false), + active(true), + renderTargetSwitchCount(0) + { + this->transformStack.reserve(16); + this->transformStack.push_back(Matrix4()); + + this->pixelScaleStack.reserve(16); + this->pixelScaleStack.push_back(1.0); + + this->states.reserve(10); + this->states.push_back(DisplayState()); + } + + virtual ~Graphics() + { + this->states.clear(); + this->defaultFont.Set(nullptr); + } + + virtual ModuleType GetModuleType() const + { + return M_GRAPHICS; + } + + virtual const char* GetName() const + { + return "love.graphics"; + } + + void Clear(OptionalColor color, OptionalInt stencil, OptionalDouble depth) + { + Renderer::Instance().BindFramebuffer(); + + if (color.has_value()) + { + // GammaCorrectColor(color); + Renderer::Instance().Clear(color.value()); + } + + if (stencil.has_value() && depth.has_value()) + Renderer::Instance().ClearDepthStencil(stencil.value(), 0xFF, + depth.value()); + } + + void Clear(std::vector& colors, OptionalInt stencil, OptionalDouble depth) + { + int colorCount = colors.size(); + + if (colorCount == 0 || !stencil.has_value() || !depth.has_value()) + this->Clear(colorCount > 0 ? colors[0] : Color {}, stencil, depth); + } + + void Present() + { + Renderer::Instance().Present(); + + Renderer::drawCalls = 0; + Renderer::drawCallsBatched = 0; + Shader::shaderSwitches = 0; + } + + /* graphics state */ + + void Reset() + { + DisplayState state {}; + this->RestoreState(state); + this->Origin(); + } + + void Pop() + { + if (this->stackTypeStack.size() < 1) + throw love::Exception("Minimum stack depth reached (more pops than pushes?)"); + + this->PopTransform(); + this->pixelScaleStack.pop_back(); + + if (this->stackTypeStack.back() == STACK_ALL) + { + DisplayState& newState = this->states[this->states.size() - 2]; + this->RestoreStateChecked(newState); + this->states.pop_back(); + } + + this->stackTypeStack.pop_back(); + } + + /* objects */ + + Font* NewFont(Rasterizer* data) const + { + return new Font(data, this->states.back().defaultSamplerState); + } + + void SetFont(Font* font) + { + this->states.back().font = font; + } + + Font* GetFont() + { + return this->states.back().font; + } + + SpriteBatch* NewSpriteBatch(Texture* texture, int size) const + { + return new SpriteBatch(texture, size); + } + + TextBatch* NewTextBatch(Font* font, const ColoredStrings& text = {}) const + { + return new TextBatch(font, text); + } + + Mesh* NewMesh(int vertexCount, vertex::PrimitiveType mode) const + { + return new Mesh(vertexCount, mode); + } + + Mesh* NewMesh(const void* data, size_t size, vertex::PrimitiveType mode) const + { + return new Mesh(data, size, mode); + } + + void SetRenderTargetsInternal(const RenderTargets& targets, int width, int height, + bool hasSRGBTexture) + { + // const DisplayState& state = this->states.back(); + + bool isWindow = targets.GetFirstTarget().texture == nullptr; + + if (isWindow) + Renderer::Instance().BindFramebuffer(); + else + { + Renderer::Instance().BindFramebuffer( + targets.GetFirstTarget().texture); + } + } + + void SetRenderTarget() + { + DisplayState& state = this->states.back(); + + if (state.renderTargets.colors.empty() && + state.renderTargets.depthStencil.texture == nullptr) + { + return; + } + + Renderer::FlushVertices(); + this->SetRenderTargetsInternal(RenderTargets {}, this->width, this->height, + this->IsGammaCorrect()); + + state.renderTargets = RenderTargetsStrongReference(); + renderTargetSwitchCount++; + } + + void SetRenderTarget(RenderTarget target, uint32_t flags) + { + if (target.texture == nullptr) + return SetRenderTarget(); + + RenderTargets targets {}; + targets.colors.push_back(target); + targets.temporaryFlags = flags; + + this->SetRenderTargets(targets); + } + + void SetRenderTargets(const RenderTargetsStrongReference& strongTargets) + { + RenderTargets targets {}; + targets.colors.reserve(strongTargets.colors.size()); + + for (const auto& target : strongTargets.colors) + targets.colors.emplace_back(target.texture.Get(), target.slice, target.mipmap); + + targets.depthStencil = + RenderTarget(strongTargets.depthStencil.texture, strongTargets.depthStencil.slice, + strongTargets.depthStencil.mipmap); + + targets.temporaryFlags = strongTargets.temporaryFlags; + return this->SetRenderTargets(targets); + } + + void SetRenderTargets(const RenderTargets& targets) + { + DisplayState& state = this->states.back(); + int count = (int)targets.colors.size(); + + RenderTarget firstTarget = targets.GetFirstTarget(); + Texture* firstTexture = firstTarget.texture; + + if (firstTexture == nullptr) + return this->SetRenderTarget(); + + const auto& previousTargets = state.renderTargets; + + /* validate if the rendertargets changed */ + if (count == (int)previousTargets.colors.size()) + { + bool modified = false; + + for (int index = 0; index < count; index++) + { + if (targets.colors[index] != previousTargets.colors[index]) + { + modified = true; + break; + } + } + + if (!modified && targets.depthStencil != previousTargets.depthStencil) + modified = true; + + if (targets.temporaryFlags != previousTargets.temporaryFlags) + modified = true; + + if (!modified) + return; + } + + if (count > 0x01) + { + throw love::Exception("This system can't simultaneously render to %d textures", + count); + } + + PixelFormat firstColorFormat = PIXELFORMAT_UNKNOWN; + if (!targets.colors.empty()) + firstColorFormat = targets.colors[0].texture->GetPixelFormat(); + + if (!firstTexture->IsRenderTarget()) + { + throw love::Exception( + "Texture must be created as a render target to be used in setRenderTargets."); + } + + if (love::IsPixelFormatDepthStencil(firstColorFormat)) + { + throw love::Exception( + "Depth/stencil format textures must be used with the 'depthstencil' field of " + "the table passed into setRenderTargets."); + } + + if (firstTarget.mipmap < 0 || firstTarget.mipmap >= firstTexture->GetMipmapCount()) + throw love::Exception("Invalid mipmap level %d.", firstTarget.mipmap + 1); + + if (!firstTexture->IsValidSlice(firstTarget.slice, firstTarget.mipmap)) + throw love::Exception("Invalid slice index: %d.", firstTarget.slice + 1); + + bool hasSRGBtexture = love::IsPixelFormatSRGB(firstColorFormat); + int pixelWidth = firstTexture->GetPixelWidth(firstTarget.mipmap); + int pixelHeight = firstTexture->GetPixelHeight(firstTarget.mipmap); + int reqMSAA = firstTexture->GetRequestedMSAA(); + + bool multiformatsupported = false; + + /* do a ton of validation - NOTE: only ONE rendertarget allowed MAX */ + for (int i = 1; i < count; i++) + { + Texture* canvas = targets.colors[i].texture; + PixelFormat format = canvas->GetPixelFormat(); + int mip = targets.colors[i].mipmap; + int slice = targets.colors[i].slice; + + if (!canvas->IsRenderTarget()) + throw love::Exception("Texture must be created as a render target to be used " + "in setRenderTargets."); + + if (mip < 0 || mip >= canvas->GetMipmapCount()) + throw love::Exception("Invalid mipmap level %d.", mip + 1); + + if (!canvas->IsValidSlice(slice, mip)) + throw love::Exception("Invalid slice index: %d.", slice + 1); + + if (canvas->GetPixelWidth(mip) != pixelWidth || + canvas->GetPixelHeight(mip) != pixelHeight) + { + throw love::Exception("All textures must have the same pixel dimensions."); + } + + if (!multiformatsupported && format != firstColorFormat) + { + throw love::Exception("This system doesn't support multi-render-target " + "rendering with different texture formats."); + } + + if (canvas->GetRequestedMSAA() != reqMSAA) + throw love::Exception("All textures must have the same MSAA value."); + + if (love::IsPixelFormatDepthStencil(format)) + { + throw love::Exception( + "Depth/stencil format textures must be used with the 'depthstencil' field " + "of the table passed into setRenderTargets."); + } + + if (love::IsPixelFormatSRGB(format)) + hasSRGBtexture = true; + } + + /* validate depth stencil */ + + this->SetRenderTargetsInternal(targets, pixelWidth, pixelHeight, hasSRGBtexture); + + RenderTargetsStrongReference references {}; + references.colors.reserve(targets.colors.size()); + + for (auto target : targets.colors) + references.colors.emplace_back(target.texture, target.slice, target.mipmap); + + references.depthStencil = RenderTargetStrongReference(targets.depthStencil.texture, + targets.depthStencil.slice); + references.temporaryFlags = targets.temporaryFlags; + + std::swap(state.renderTargets, references); + + renderTargetSwitchCount++; + } + + RenderTargets GetRenderTargets() const + { + const auto& targets = this->states.back().renderTargets; + + RenderTargets newTargets {}; + newTargets.colors.reserve(targets.colors.size()); + + for (const auto& target : targets.colors) + newTargets.colors.emplace_back(target.texture.Get(), target.slice, target.mipmap); + + newTargets.depthStencil = + RenderTarget(targets.depthStencil.texture, targets.depthStencil.slice, + targets.depthStencil.mipmap); + newTargets.temporaryFlags = targets.temporaryFlags; + + return newTargets; + } + + bool IsRenderTargetActive() const + { + const auto& targets = this->states.back().renderTargets; + return !targets.colors.empty() || targets.depthStencil.texture != nullptr; + } + + bool IsRenderTargetActive(Texture* texture) const + { + const auto& targets = this->states.back().renderTargets; + + for (const auto& target : targets.colors) + { + if (target.texture.Get() == texture) + return true; + } + + if (targets.depthStencil.texture.Get() == texture) + return true; + + return false; + } + + bool IsRenderTargetActive(Texture* texture, int slice) const + { + const auto& targets = this->states.back().renderTargets; + + for (const auto& target : targets.colors) + { + if (target.texture.Get() == texture && target.slice == slice) + return true; + } + + if (targets.depthStencil.texture.Get() == texture && + targets.depthStencil.slice == slice) + { + return true; + } + + return false; + } + + void SetShader() + { + this->states.back().shader.Set(nullptr); + } + + void SetShader(Shader* shader) + { + if (shader == nullptr) + return this->SetShader(); + } + + Font* NewDefaultFont(int size, TrueTypeRasterizer<>::Hinting hinting) const + { + auto module = Module::GetInstance>(M_FONT); + + if (!module) + throw love::Exception("Font module has not been loaded."); + + StrongRasterizer rasterizer(module->NewTrueTypeRasterizer(size, hinting)); + return this->NewFont(rasterizer.Get()); + } + + void CheckSetDefaultFont() + { + if (this->states.back().font.Get() != nullptr) + return; + + if (!this->defaultFont.Get()) + { + this->defaultFont.Set( + this->NewDefaultFont(13, TrueTypeRasterizer<>::HINTING_NORMAL)); + } + + this->states.back().font.Set(this->defaultFont.Get()); + } + + void Print(const ColoredStrings& strings, const Matrix4& matrix) + { + this->CheckSetDefaultFont(); + + if (this->states.back().font.Get() != nullptr) + this->Print(strings, this->states.back().font.Get(), matrix); + } + + void Print(const ColoredStrings& strings, Font* font, const Matrix4& matrix) + { + font->Print(*this, strings, matrix, this->states.back().foreground); + } + + void Printf(const ColoredStrings& strings, float wrap, Font::AlignMode align, + const Matrix4& matrix) + { + this->CheckSetDefaultFont(); + + if (this->states.back().font.Get() != nullptr) + this->Printf(strings, this->states.back().font.Get(), wrap, align, matrix); + } + + void Printf(const ColoredStrings& strings, Font* font, float wrap, Font::AlignMode align, + const Matrix4& matrix) + { + font->Printf(*this, strings, wrap, align, matrix, this->states.back().foreground); + } + + Shader* GetShader() const + { + return this->states.back().shader.Get(); + } + + Stats GetStats() const + { + Stats stats {}; + + stats.drawCalls = Renderer<>::drawCalls; + stats.textures = Texture<>::textureCount; + stats.fonts = Font::fontCount; + stats.shaderSwitches = Shader<>::shaderSwitches; + stats.textureMemory = Texture<>::totalGraphicsMemory; + stats.drawCallsBatched = Renderer<>::drawCallsBatched; + stats.renderTargetSwitches = renderTargetSwitchCount; + + stats.cpuTime = Renderer<>::cpuTime; + stats.gpuTime = Renderer<>::gpuTime; + + return stats; + } + + bool IsCreated() const + { + return this->created; + } + + bool IsActive() const + { + return this->active; + } + + void SetActive(bool active) + { + this->active = active; + } + + void Push(StackType type) + { + if (this->stackTypeStack.size() == MAX_USER_STACK_DEPTH) + throw love::Exception("Maximum stack depth reached (more pushes than pops?)"); + + this->PushTransform(); + this->pixelScaleStack.push_back(this->pixelScaleStack.back()); + + if (type == STACK_ALL) + this->states.push_back(this->states.back()); + + this->stackTypeStack.push_back(type); + } + + void PushTransform() + { + this->transformStack.push_back(this->transformStack.back()); + } + + void PushIdentityTransform() + { + this->transformStack.push_back(Matrix4()); + } + + void PopTransform() + { + this->transformStack.pop_back(); + } + + const Matrix4& GetTransform() const + { + return this->transformStack.back(); + } + + void Rotate(float r) + { + this->transformStack.back().Rotate(r); + } + + void Scale(float x, float y) + { + this->transformStack.back().Scale(x, y); + this->pixelScaleStack.back() *= (fabs(x) + fabs(y)) / 2.0; + } + + void ApplyTransform(const Matrix4& matrix) + { + auto& current = this->transformStack.back(); + current *= matrix; + + float scaleX, scaleY; + current.GetApproximateScale(scaleX, scaleY); + this->pixelScaleStack.back() = (scaleX + scaleY) / 2.0; + } + + void ReplaceTransform(const Matrix4& matrix) + { + this->transformStack.back() = matrix; + + float scaleX, scaleY; + matrix.GetApproximateScale(scaleX, scaleY); + this->pixelScaleStack.back() = (scaleX + scaleY) / 2.0; + } + + Vector2 TransformPoint(Vector2 point) + { + Vector2 result {}; + this->transformStack.back().TransformXY(&result, &point, 1); + + return result; + } + + Vector2 InverseTransformPoint(Vector2 point) + { + Vector2 result {}; + this->transformStack.back().Inverse().TransformXY(&result, &point, 1); + + return result; + } + + void Translate(float x, float y) + { + this->transformStack.back().Translate(x, y); + } + + void Shear(float kx, float ky) + { + this->transformStack.back().Shear(kx, ky); + } + + void Origin() + { + auto& transform = this->transformStack.back(); + transform.SetIdentity(); + + this->pixelScaleStack.back() = 1; + } + + void RestoreState(const DisplayState& state) + { + this->SetColor(state.foreground); + this->SetBackgroundColor(state.background); + + /* todo: set blend state */ + + this->SetLineWidth(state.line.width); + this->SetLineStyle(state.line.style); + this->SetLineJoin(state.line.join); + + this->SetPointSize(state.pointSize); + + this->SetMeshCullMode(state.cullMode); + this->SetFrontFaceWinding(state.windingMode); + + this->SetFont(state.font.Get()); + this->SetShader(state.shader.Get()); + this->SetRenderTargets(state.renderTargets); + + this->SetColorMask(state.colorMask); + this->SetDefaultSamplerState(state.defaultSamplerState); + } + + bool IsPixelFormatSupported(PixelFormat format, uint32_t usage, bool isSRGB = false) + { + if (isSRGB) + format = love::GetSRGBPixelFormat(format); + + bool rendertarget = (usage & PIXELFORMAT_USAGE_FLAGS_RENDERTARGET) != 0; + bool readable = (usage & PIXELFORMAT_USAGE_FLAGS_SAMPLE) != 0; + + format = this->GetSizedFormat(format, rendertarget, readable); + + /* todo: calculate pixel format usages*/ + return false; + } + + void RestoreStateChecked(const DisplayState& state) + { + const DisplayState& current = this->states.back(); + + if (state.foreground != current.foreground) + this->SetColor(state.foreground); + + this->SetBackgroundColor(state.background); + + /* todo set blend state */ + + this->SetLineWidth(state.line.width); + this->SetLineStyle(state.line.style); + this->SetLineJoin(state.line.join); + + if (state.pointSize != current.pointSize) + this->SetPointSize(state.pointSize); + + this->SetMeshCullMode(state.cullMode); + + if (state.windingMode != current.windingMode) + this->SetFrontFaceWinding(state.windingMode); + + this->SetFont(state.font.Get()); + this->SetShader(state.shader.Get()); + + const auto& targets = state.renderTargets; + const auto& currentTargets = current.renderTargets; + + bool changed = targets.colors.size() != currentTargets.colors.size(); + + if (!changed) + { + for (size_t index = 0; + index < targets.colors.size() && index < currentTargets.colors.size(); index++) + { + if (targets.colors[index] != currentTargets.colors[index]) + { + changed = true; + break; + } + } + + if (!changed && targets.depthStencil != currentTargets.depthStencil) + changed = true; + + if (targets.temporaryFlags != currentTargets.temporaryFlags) + changed = true; + } + + if (changed) + this->SetRenderTargets(state.renderTargets); + + if (state.colorMask != current.colorMask) + this->SetColorMask(state.colorMask); + + this->SetDefaultSamplerState(state.defaultSamplerState); + } + + void SetColor(const Color& color) + { + this->states.back().foreground = color; + } + + const Color GetColor() const + { + return this->states.back().foreground; + } + + void SetBackgroundColor(const Color& color) + { + this->states.back().background = color; + } + + const Color GetBackgroundColor() const + { + return this->states.back().background; + } + + void SetLineWidth(float width) + { + this->states.back().line.width = width; + Renderer::Instance().SetLineWidth(width); + } + + const float GetLineWidth() const + { + return this->states.back().line.width; + } + + void SetLineStyle(RenderState::LineStyle style) + { + this->states.back().line.style = style; + Renderer::Instance().SetLineStyle(style); + } + + const RenderState::LineStyle GetLineStyle() const + { + return this->states.back().line.style; + } + + void SetLineJoin(RenderState::LineJoin join) + { + this->states.back().line.join = join; + } + + const RenderState::LineJoin GetLineJoin() const + { + return this->states.back().line.join; + } + + void SetPointSize(float size) + { + this->states.back().pointSize = size; + Renderer::Instance().SetPointSize(size); + } + + const float GetPointSize() const + { + return this->states.back().pointSize; + } + + void SetScissor(const Rect& scissor) + { + this->states.back().scissor.bounds = scissor; + this->states.back().scissor.active = true; + Renderer::Instance().SetScissor(scissor, this->IsRenderTargetActive()); + } + + void SetScissor() + { + this->states.back().scissor.active = false; + Renderer::Instance().SetScissor(Rect::EMPTY, false); + } + + bool GetScissor(Rect& rectangle) const + { + const auto& state = this->states.back().scissor; + rectangle = state.bounds; + + return state.active; + } + + void SetMeshCullMode(vertex::CullMode mode) + { + this->states.back().cullMode = mode; + Renderer::Instance().SetMeshCullMode(mode); + } + + const vertex::CullMode GetMeshCullMode() const + { + return this->states.back().cullMode; + } + + void SetFrontFaceWinding(vertex::Winding winding) + { + this->states.back().windingMode = winding; + Renderer::Instance().SetVertexWinding(winding); + } + + const vertex::Winding GetFrontFaceWinding() const + { + return this->states.back().windingMode; + } + + void SetColorMask(const RenderState::ColorMask& mask) + { + this->states.back().colorMask = mask; + Renderer::Instance().SetColorMask(mask); + } + + const RenderState::ColorMask GetColorMask() const + { + return this->states.back().colorMask; + } + + void SetDefaultSamplerState(const SamplerState& state) + { + this->states.back().defaultSamplerState = state; + } + + const SamplerState& GetDefaultSamplerState() const + { + return this->states.back().defaultSamplerState; + } + + /* PRIMITIVES */ + + void Polyline(const std::span points) + { + float halfWidth = this->GetLineWidth() * 0.5f; + RenderState::LineJoin lineJoin = this->GetLineJoin(); + RenderState::LineStyle lineStyle = this->GetLineStyle(); + + float pixelSize = 1.0f / std::max((float)this->pixelScaleStack.back(), 0.000001f); + bool shouldSmooth = lineStyle == RenderState::LINE_SMOOTH; + + if (lineJoin == RenderState::LINE_JOIN_NONE) + { + NoneJoinPolyline line; + line.render(points.data(), points.size(), halfWidth, pixelSize, shouldSmooth); + + line.draw(this); + } + else if (lineJoin == RenderState::LINE_JOIN_BEVEL) + { + BevelJoinPolyline line; + line.render(points.data(), points.size(), halfWidth, pixelSize, shouldSmooth); + + line.draw(this); + } + else if (lineJoin == RenderState::LINE_JOIN_MITER) + { + MiterJoinPolyline line; + line.render(points.data(), points.size(), halfWidth, pixelSize, shouldSmooth); + + line.draw(this); + } + } + + void Polygon(DrawMode mode, std::span points, bool skipLastVertex = true) + { + if (mode == DRAW_LINE) + this->Polyline(points); + else + { + const auto transform = this->GetTransform(); + bool is2D = transform.IsAffine2DTransform(); + + const int count = points.size() - (skipLastVertex ? 1 : 0); + DrawCommand command(count, vertex::PRIMITIVE_TRIANGLE_FAN); + + if (is2D) + transform.TransformXY(command.Positions().get(), points.data(), command.count); + + command.FillVertices(this->GetColor()); + + Renderer::Instance().Render(command); + } + } + + int CalculateEllipsePoints(float rx, float ry) const + { + auto pixelScale = (float)this->pixelScaleStack.back(); + auto points = sqrtf(((rx + ry) / 2.0f) * 20.0f * pixelScale); + + return std::max(points, 8.0f); + } + + void Rectangle(DrawMode mode, float x, float y, float width, float height) + { + std::array points = { Vector2(x, y), Vector2(x, y + height), + Vector2(x + width, y + height), + Vector2(x + width, y), Vector2(x, y) }; + + this->Polygon(mode, points); + } + + void Rectangle(DrawMode mode, float x, float y, float width, float height, float rx, + float ry, int points) + { + if (rx <= 0 || ry <= 0) + { + this->Rectangle(mode, x, y, width, height); + return; + } + + if (width >= 0.02f) + rx = std::min(rx, width / 2.0f - 0.01f); + + if (height >= 0.02f) + ry = std::min(ry, height / 2.0f - 0.01f); + + points = std::max(points / 4, 1); + + const float halfPi = static_cast(LOVE_M_PI / 2); + float angleShift = halfPi / ((float)points + 1.0f); + + int pointCount = (points + 2) * 4; + + Vector2 coords[pointCount + 1] {}; + float phi = 0.0f; + + for (int index = 0; index <= points + 2; ++index, phi += angleShift) + { + coords[index].x = x + rx * (1 - cosf(phi)); + coords[index].y = y + ry * (1 - sinf(phi)); + } + + phi = halfPi; + + for (int index = points + 2; index <= 2 * (points + 2); ++index, phi += angleShift) + { + coords[index].x = x + width - rx * (1 + cosf(phi)); + coords[index].y = y + ry * (1 - sinf(phi)); + } + + phi = 2 * halfPi; + + for (int index = 2 * (points + 2); index <= 3 * (points + 2); + ++index, phi += angleShift) + { + coords[index].x = x + width - rx * (1 + cosf(phi)); + coords[index].y = y + height - ry * (1 + sinf(phi)); + } + + phi = 3 * halfPi; + + for (int index = 3 * (points + 2); index <= 4 * (points + 2); + ++index, phi += angleShift) + { + coords[index].x = x + rx * (1 - cosf(phi)); + coords[index].y = y + height - ry * (1 + sinf(phi)); + } + + coords[pointCount] = coords[0]; + this->Polygon(mode, std::span(coords, pointCount + 1)); + } + + void Rectangle(DrawMode mode, float x, float y, float width, float height, float rx, + float ry) + { + const float pointsRx = std::min(rx, std::abs(width / 2)); + const float pointsRy = std::min(ry, std::abs(height / 2)); + + int points = this->CalculateEllipsePoints(pointsRx, pointsRy); + this->Rectangle(mode, x, y, width, height, rx, ry, points); + } + + void Ellipse(DrawMode mode, float x, float y, float a, float b, int points) + { + float twoPi = (float)(LOVE_M_TAU); + + if (points <= 0) + points = 1; + + const float angleShift = (twoPi / points); + float phi = 0.0f; + + int extraPoints = 1 + (mode == DRAW_FILL ? 1 : 0); + Vector2 polygonCoords[points + extraPoints] {}; + Vector2* coords = polygonCoords; + + if (mode == DRAW_FILL) + { + coords[0].x = x; + coords[0].y = y; + coords++; + } + + for (int index = 0; index < points; ++index, phi += angleShift) + { + coords[index].x = x + a * cosf(phi); + coords[index].y = y + b * sinf(phi); + } + + coords[points] = coords[0]; + + this->Polygon(mode, std::span(polygonCoords, points + extraPoints), false); + } + + void Ellipse(DrawMode mode, float x, float y, float a, float b) + { + this->Ellipse(mode, x, y, a, b, this->CalculateEllipsePoints(a, b)); + } + + void Circle(DrawMode mode, float x, float y, float radius, int points) + { + this->Ellipse(mode, x, y, radius, radius, points); + } + + void Circle(DrawMode mode, float x, float y, float radius) + { + this->Ellipse(mode, x, y, radius, radius); + } + + void Arc(DrawMode mode, ArcMode arcMode, float x, float y, float radius, float angle1, + float angle2, int points) + { + if (points <= 0 || angle1 == angle2) + return; + + if (fabs(angle1 - angle2) >= LOVE_M_TAU) + { + this->Circle(mode, x, y, radius, points); + return; + } + + const float angleShift = (angle2 - angle1) / points; + + if (angleShift == 0.0f) + return; + + const auto sharpAngle = fabsf(angle1 - angle2) < LOVE_TORAD(4); + if (mode == DRAW_LINE && arcMode == ARC_CLOSED && sharpAngle) + arcMode = ARC_OPEN; + + if (mode == DRAW_FILL && arcMode == ARC_OPEN) + arcMode = ARC_CLOSED; + + float phi = angle1; + + Vector2* coords = nullptr; + int numCoords = 0; + + // clang-format off + const auto createPoints = [&](Vector2* coordinates) + { + for (int index = 0; index <= points; ++index, phi += angleShift) + { + coordinates[index].x = x + radius * cosf(phi); + coordinates[index].y = y + radius * sinf(phi); + } + }; + // clang-format on + + if (arcMode == ARC_PIE) + { + numCoords = points + 3; + coords = new Vector2[numCoords]; + + coords[0] = coords[numCoords - 1] = Vector2(x, y); + createPoints(coords + 1); + } + else if (arcMode == ARC_OPEN) + { + numCoords = points + 1; + coords = new Vector2[numCoords]; + + createPoints(coords); + } + else + { + numCoords = points + 2; + coords = new Vector2[numCoords]; + + createPoints(coords); + coords[numCoords - 1] = coords[0]; + } + + this->Polygon(mode, std::span(coords, numCoords)); + delete[] coords; + } + + void Arc(DrawMode mode, ArcMode arcMode, float x, float y, float radius, float angle1, + float angle2) + { + float points = this->CalculateEllipsePoints(radius, radius); + float angle = fabsf(angle1 - angle2); + + if (angle < (float)LOVE_M_TAU) + points *= angle / (float)LOVE_M_TAU; + + this->Arc(mode, arcMode, x, y, radius, angle1, angle2, (int)(points + 0.5f)); + } + + void Points(std::span points, std::span colors) + { + if (Console::Is(Console::CTR)) + { + for (const auto& point : points) + this->Circle(DRAW_FILL, point.x, point.y, this->states.back().pointSize); + + return; + } + + const auto& transform = this->GetTransform(); + bool is2D = transform.IsAffine2DTransform(); + + DrawCommand command(points.size(), vertex::PRIMITIVE_POINTS); + + if (is2D) + transform.TransformXY(command.Positions().get(), points.data(), points.size()); + + if (colors.size() > 1) + command.FillVertices(colors); + else + command.FillVertices(colors[0]); + + Renderer::Instance().Render(command); + } + + void Line(std::span points) + { + this->Polyline(points); + } + + /* PRIMITIVES */ + + void SetBlendState(const RenderState::BlendState& state) + { + Renderer::Instance().SetBlendMode(state); + this->states.back().blendState = state; + } + + void SetBlendMode(RenderState::BlendMode mode, RenderState::BlendAlpha alphaMode) + { + if (alphaMode == RenderState::BLENDALPHA_MULTIPLY && + !RenderState::IsAlphaMultiplyBlendSupported(mode)) + { + std::optional modeOpt; + if (!(modeOpt = RenderState::blendModes.ReverseFind(mode))) + modeOpt = "unknown"; + + throw love::Exception("The '%s' blend mode must be used with premultiplied alpha.", + *modeOpt); + } + + const auto state = RenderState::ComputeBlendState(mode, alphaMode); + Renderer::Instance().SetBlendMode(state); + } + + const RenderState::BlendState& GetBlendState() const + { + return this->states.back().blendState; + } + + const RenderState::BlendMode GetBlendMode(RenderState::BlendAlpha& alphaMode) + { + return RenderState::ComputeBlendMode(this->states.back().blendState, alphaMode); + } + + PixelFormat GetSizedFormat(PixelFormat format, bool rendertarget, bool readable) const + { + uint32_t requiredFlags = 0; + + if (rendertarget) + requiredFlags |= PIXELFORMAT_USAGE_FLAGS_RENDERTARGET; + + if (readable) + requiredFlags |= PIXELFORMAT_USAGE_FLAGS_SAMPLE; + + switch (format) + { + case PIXELFORMAT_NORMAL: + return PIXELFORMAT_RGBA8_UNORM; + case PIXELFORMAT_HDR: + return PIXELFORMAT_RGBA16_FLOAT; + default: + return format; + } + } + + void InternalScale(const Matrix4& transform) + { + this->transformStack.back() *= transform; + } + + void IntersectScissor(const Rect& rectangle) + { + Rect currect = states.back().scissor.bounds; + + if (!states.back().scissor.active) + { + currect.x = 0; + currect.y = 0; + currect.w = std::numeric_limits::max(); + currect.h = std::numeric_limits::max(); + } + + int x1 = std::max(currect.x, rectangle.x); + int y1 = std::max(currect.y, rectangle.y); + + int x2 = std::min(currect.x + currect.w, rectangle.x + rectangle.w); + int y2 = std::min(currect.y + currect.h, rectangle.y + rectangle.h); + + Rect newrect = { x1, y1, std::max(0, x2 - x1), std::max(0, y2 - y1) }; + SetScissor(newrect); + } + + Quad* NewQuad(const Quad::Viewport& viewport, double sourceWidth, double sourceHeight) const + { + return new Quad(viewport, sourceWidth, sourceHeight); + } + + // clang-format off + static constexpr BidirectionalMap drawModes = { + "fill", DRAW_FILL, + "line", DRAW_LINE + }; + + static constexpr BidirectionalMap stackTypes = { + "all", STACK_ALL, + "transform", STACK_TRANSFORM + }; + + static constexpr BidirectionalMap arcModes = { + "open", ARC_OPEN, + "closed", ARC_CLOSED, + "pie", ARC_PIE + }; + // clang-format on + + protected: + std::vector pixelScaleStack; + std::vector> transformStack; + + std::vector states; + std::vector stackTypeStack; + + int width; + int height; + + int pixelWidth; + int pixelHeight; + + bool created; + bool active; + + int renderTargetSwitchCount; + + StrongReference defaultFont; + }; +} // namespace love diff --git a/include/modules/graphics/wrap_graphics.h b/include/modules/graphics/wrap_graphics.h deleted file mode 100644 index dafb05015..000000000 --- a/include/modules/graphics/wrap_graphics.h +++ /dev/null @@ -1,173 +0,0 @@ -#pragma once - -#include "modules/graphics/graphics.h" - -#if defined(__SWITCH__) - #include "deko3d/graphics.h" -#elif defined(__3DS__) - #include "citro2d/graphics.h" -#endif - -#include "modules/window/window.h" - -#include "objects/font/font.h" - -#include "common/luax.h" -#include "objects/drawable/wrap_drawable.h" - -namespace Wrap_Graphics -{ - int IsActive(lua_State* L); - - int IsCreated(lua_State* L); - - int Arc(lua_State* L); - - int Circle(lua_State* L); - - int Clear(lua_State* L); - - int Discard(lua_State* L); - - int Draw(lua_State* L); - - int Ellipse(lua_State* L); - - int Line(lua_State* L); - - int GetPointSize(lua_State* L); - - int SetPointSize(lua_State* L); - - int GetCanvas(lua_State* L); - - int Points(lua_State* L); - - int Polygon(lua_State* L); - - int Present(lua_State* L); - - int Push(lua_State* L); - - int Origin(lua_State* L); - - int Translate(lua_State* L); - - int Scale(lua_State* L); - - int Rotate(lua_State* L); - - int Shear(lua_State* L); - - int Pop(lua_State* L); - - int Print(lua_State* L); - - int PrintF(lua_State* L); - - int Rectangle(lua_State* L); - - int SetScissor(lua_State* L); - - int IntersectScissor(lua_State* L); - - int ApplyTransform(lua_State* L); - - int ReplaceTransform(lua_State* L); - - int TransformPoint(lua_State* L); - - int InverseTransformPoint(lua_State* L); - - int GetScissor(lua_State* L); - - int GetBlendMode(lua_State* L); - - int SetBlendMode(lua_State* L); - - int SetColorMask(lua_State* L); - - int GetColorMask(lua_State* L); - - int NewImage(lua_State* L); - - int NewFont(lua_State* L); - - int NewQuad(lua_State* L); - - int NewText(lua_State* L); - - int NewCanvas(lua_State* L); - - int NewVideo(lua_State* L); - - int SetDefaultFilter(lua_State* L); - - int SetLineWidth(lua_State* L); - - int SetLineJoin(lua_State* L); - - int SetLineStyle(lua_State* L); - - int SetNewFont(lua_State* L); - - int SetFont(lua_State* L); - - int Stencil(lua_State* L); - - int GetRendererInfo(lua_State* L); - - int GetBackgroundColor(lua_State* L); - - int GetCanvas(lua_State* L); - - int GetColor(lua_State* L); - - int Reset(lua_State* L); - - int GetDefaultFilter(lua_State* L); - - int GetFont(lua_State* L); - - int GetLineWidth(lua_State* L); - - int GetLineJoin(lua_State* L); - - int GetLineStyle(lua_State* L); - - int GetScissor(lua_State* L); - - int GetWidth(lua_State* L); - - int GetHeight(lua_State* L); - - int SetActiveScreen(lua_State* L); - - int GetActiveScreen(lua_State* L); - - int GetScreens(lua_State* L); - - int GetDimensions(lua_State* L); - - int SetBackgroundColor(lua_State* L); - - int SetColor(lua_State* L); - - int SetCanvas(lua_State* L); - - /* Nintendo 3DS */ - - int Get3DDepth(lua_State* L); - - int Get3D(lua_State* L); - - int Set3D(lua_State* L); - - int GetWide(lua_State* L); - - int SetWide(lua_State* L); - - /* End Nintendo 3DS */ - - int Register(lua_State* L); -} // namespace Wrap_Graphics diff --git a/include/modules/graphics/wrap_graphics.hpp b/include/modules/graphics/wrap_graphics.hpp new file mode 100644 index 000000000..ced9315fb --- /dev/null +++ b/include/modules/graphics/wrap_graphics.hpp @@ -0,0 +1,212 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_Graphics +{ + template + static void CheckStandardTransform(lua_State* L, int index, const T& func) + { + if (luax::IsType(L, index, love::Transform::type)) + { + love::Transform* transform = luax::ToType(L, index); + func(transform->GetMatrix()); + } + else + { + float x = luaL_optnumber(L, index + 0, 0.0); + float y = luaL_optnumber(L, index + 1, 0.0); + float a = luaL_optnumber(L, index + 2, 0.0); + float sx = luaL_optnumber(L, index + 3, 1.0); + float sy = luaL_optnumber(L, index + 4, sx); + float ox = luaL_optnumber(L, index + 5, 0.0); + float oy = luaL_optnumber(L, index + 6, 0.0); + float kx = luaL_optnumber(L, index + 7, 0.0); + float ky = luaL_optnumber(L, index + 8, 0.0); + func(love::Matrix4(x, y, a, sx, sy, ox, oy, kx, ky)); + } + } + + int Reset(lua_State* L); + + int Clear(lua_State* L); + + int Present(lua_State* L); + + int IsCreated(lua_State* L); + + int IsActive(lua_State* L); + + int Origin(lua_State* L); + + int IsGammaCorrect(lua_State* L); + + int GetWidth(lua_State* L); + + int GetHeight(lua_State* L); + + int GetDimensions(lua_State* L); + + int GetPixelWidth(lua_State* L); + + int GetPixelHeight(lua_State* L); + + int GetPixelDimensions(lua_State* L); + + int GetDPIScale(lua_State* L); + + /* todo: SetCanvas */ + + /* todo: GetCanvas */ + + int SetScissor(lua_State* L); + + int IntersectScissor(lua_State* L); + + int GetScissor(lua_State* L); + + int SetColor(lua_State* L); + + int GetColor(lua_State* L); + + int SetBackgroundColor(lua_State* L); + + int GetBackgroundColor(lua_State* L); + + int SetColorMask(lua_State* L); + + int GetColorMask(lua_State* L); + + int SetBlendMode(lua_State* L); + + int GetBlendMode(lua_State* L); + + int SetBlendState(lua_State* L); + + int GetBlendState(lua_State* L); + + int SetMeshCullMode(lua_State* L); + + int GetMeshCullMode(lua_State* L); + + int SetFrontFaceWinding(lua_State* L); + + int GetFrontFaceWinding(lua_State* L); + + int SetDefaultFilter(lua_State* L); + + int GetDefaultFilter(lua_State* L); + + int SetDefaultMipmapFilter(lua_State* L); + + int GetDefaultMipmapFilter(lua_State* L); + + int SetLineWidth(lua_State* L); + + int GetLineWidth(lua_State* L); + + int SetLineStyle(lua_State* L); + + int GetLineStyle(lua_State* L); + + int SetLineJoin(lua_State* L); + + int GetLineJoin(lua_State* L); + + int SetPointSize(lua_State* L); + + int GetPointSize(lua_State* L); + + int SetActiveScreen(lua_State* L); + + int GetScreens(lua_State* L); + + /* NINTENDO 3DS */ + + int Get3D(lua_State* L); + + int Set3D(lua_State* L); + + int GetDepth(lua_State* L); + + /* OBJECTS */ + + int NewFont(lua_State* L); + + int SetFont(lua_State* L); + + int GetFont(lua_State* L); + + int Draw(lua_State* L); + + int NewTexture(lua_State* L); + + int NewImage(lua_State* L); + + int NewCanvas(lua_State* L); + + int SetCanvas(lua_State* L); + + int GetCanvas(lua_State* L); + + int NewQuad(lua_State* L); + + int NewTextBatch(lua_State* L); + + int NewSpriteBatch(lua_State* L); + + int NewMesh(lua_State* L); + + int Print(lua_State* L); + + int Printf(lua_State* L); + + /* PRIMITIVES */ + + int Rectangle(lua_State* L); + + int Circle(lua_State* L); + + int Line(lua_State* L); + + int Arc(lua_State* L); + + int Points(lua_State* L); + + int Ellipse(lua_State* L); + + int Polygon(lua_State* L); + + /* OTHER STUFF */ + + int GetRendererInfo(lua_State* L); + + int GetStats(lua_State* L); + + int Push(lua_State* L); + + int Translate(lua_State* L); + + int Scale(lua_State* L); + + int Shear(lua_State* L); + + int Rotate(lua_State* L); + + int ApplyTransform(lua_State* L); + + int InverseTransformPoint(lua_State* L); + + int TransformPoint(lua_State* L); + + int ReplaceTransform(lua_State* L); + + int Pop(lua_State* L); + + int Register(lua_State* L); + + extern std::span extensions; +} // namespace Wrap_Graphics diff --git a/include/modules/image/imagemodule.h b/include/modules/image/imagemodule.h deleted file mode 100644 index 0882c866d..000000000 --- a/include/modules/image/imagemodule.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include "common/module.h" -#include "objects/file/file.h" - -#include "modules/data/wrap_datamodule.h" -#include "modules/filesystem/wrap_filesystem.h" - -#include "objects/compressedimagedata/compressedimagedata.h" -#include "objects/imagedata/imagedata.h" - -#include "objects/imagedata/types/formathandler.h" -#include "objects/imagedata/wrap_imagedata.h" - -#include - -namespace love -{ - class ImageModule : public Module - { - public: - ImageModule(); - - virtual ~ImageModule(); - - ModuleType GetModuleType() const override - { - return M_IMAGE; - } - - const char* GetName() const override - { - return "love.image"; - } - - ImageData* NewImageData(Data* data); - -#if defined(__SWITCH__) - ImageData* NewImageData(int width, int height, PixelFormat format = PIXELFORMAT_RGBA8); -#elif defined(__3DS__) - ImageData* NewImageData(int width, int height, - PixelFormat format = PIXELFORMAT_TEX3DS_RGBA8); -#endif - ImageData* NewImageData(int width, int height, PixelFormat format, void* data, - bool own = false); - - CompressedImageData* NewCompressedData(Data* data); - - bool IsCompressed(Data* data); - - const std::list& GetFormatHandlers() const; - - static bool GetConstant(PixelFormat in, const char*& out); - - static bool GetConstant(const char* in, PixelFormat& out); - - private: - std::list formatHandlers; - }; -} // namespace love diff --git a/include/modules/image/imagemodule.hpp b/include/modules/image/imagemodule.hpp new file mode 100644 index 000000000..7e6359a62 --- /dev/null +++ b/include/modules/image/imagemodule.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include +#include + +#include + +namespace love +{ + class ImageModule : public Module + { + public: + ImageModule(); + + virtual ~ImageModule(); + + ModuleType GetModuleType() const override + { + return M_IMAGE; + } + + const char* GetName() const override + { + return "love.image"; + } + + ImageData* NewImageData(Data* data) const; + + ImageData* NewImageData(int width, int height, + PixelFormat format = PIXELFORMAT_RGBA8_UNORM) const; + + ImageData* NewImageData(int width, int height, PixelFormat format, + void* data, bool own = false) const; + + CompressedImageData* NewCompressedImageData(Data* data) const; + + bool IsCompressed(Data* data) const; + + const std::list& GetFormatHandlers() const; + + private: + std::list formatHandlers; + }; +} // namespace love diff --git a/include/modules/image/wrap_imagemodule.h b/include/modules/image/wrap_imagemodule.h deleted file mode 100644 index 51cabc14e..000000000 --- a/include/modules/image/wrap_imagemodule.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "common/luax.h" - -#include "modules/image/imagemodule.h" - -namespace Wrap_ImageModule -{ - int NewImageData(lua_State* L); - - int NewCompressedData(lua_State* L); - - int IsCompressed(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_ImageModule diff --git a/include/modules/image/wrap_imagemodule.hpp b/include/modules/image/wrap_imagemodule.hpp new file mode 100644 index 000000000..26035dded --- /dev/null +++ b/include/modules/image/wrap_imagemodule.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace Wrap_ImageModule +{ + int NewImageData(lua_State* L); + + int NewCompressedData(lua_State* L); + + int IsCompressed(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_ImageModule diff --git a/include/modules/joystick/joystickc.h b/include/modules/joystick/joystickc.h deleted file mode 100644 index 99804aad9..000000000 --- a/include/modules/joystick/joystickc.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "common/module.h" - -#include "objects/gamepad/gamepad.h" - -#include -#include - -namespace love::common -{ - class Joystick : public Module - { - public: - Joystick(); - - ~Joystick(); - - ModuleType GetModuleType() const - { - return M_JOYSTICK; - } - - const char* GetName() const override - { - return "love.joystick"; - } - - love::Gamepad* GetJoystickFromID(size_t index); - - size_t GetJoystickCount() const; - - love::Gamepad* AddGamepad(size_t index); - - void RemoveGamepad(love::Gamepad* gamepad); - - int GetIndex(const love::Gamepad* gamepad); - - int CheckGamepadAdded(); - - int CheckGamepadRemoved(); - - protected: - virtual size_t GetActiveControllerCount() - { - return 1; - }; - - private: - std::vector active; - std::list gamepads; - - size_t activeCount; - }; -} // namespace love::common diff --git a/include/modules/joystick/joystickmodule.tcc b/include/modules/joystick/joystickmodule.tcc new file mode 100644 index 000000000..95e83389a --- /dev/null +++ b/include/modules/joystick/joystickmodule.tcc @@ -0,0 +1,95 @@ +#pragma once + +#include +#include + +#include + +#include +#include +#include + +namespace love +{ + template + class JoystickModule : public Module + { + public: + JoystickModule() + {} + + virtual ~JoystickModule() + { + for (auto joystick : this->active) + { + joystick->Close(); + joystick->Release(); + } + } + + ModuleType GetModuleType() const override + { + return M_JOYSTICK; + } + + const char* GetName() const override + { + return "love.joystick"; + } + + void RemoveJoystick(Joystick* joystick) + { + if (!joystick) + return; + + auto iterator = std::find(this->active.begin(), this->active.end(), joystick); + + if (iterator != this->active.end()) + { + (*iterator)->Close(); + this->active.erase(iterator); + } + } + + Joystick* GetJoystickFromId(int instanceId) + { + for (auto joystick : this->active) + { + if (joystick->GetInstanceID() == instanceId) + return joystick; + } + + return nullptr; + } + + Joystick* GetJoystick(int index) + { + if (index < 0 || (size_t)index >= this->active.size()) + return nullptr; + + return this->active[index]; + } + + int GetIndex(const Joystick* joystick) + { + for (size_t index = 0; index < this->active.size(); index++) + { + if (this->active[index] == joystick) + return index; + } + + return -1; + } + + int GetJoystickCount() const + { + return (int)this->active.size(); + } + + protected: + std::vector*> active; + std::list*> joysticks; + + std::map recentGUIDs; + }; +} // namespace love diff --git a/include/modules/joystick/wrap_joystick.h b/include/modules/joystick/wrap_joystick.h deleted file mode 100644 index 5e662ee1c..000000000 --- a/include/modules/joystick/wrap_joystick.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "modules/joystick/joystick.h" -#include "objects/gamepad/gamepad.h" - -#include "common/luax.h" - -namespace Wrap_Joystick -{ - int GetJoystickCount(lua_State* L); - - int GetJoysticks(lua_State* L); - - int Split(lua_State* L); - - int Merge(lua_State* L); - - love::Gamepad* CheckGamepad(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Joystick diff --git a/include/modules/joystick/wrap_joystickmodule.hpp b/include/modules/joystick/wrap_joystickmodule.hpp new file mode 100644 index 000000000..1023e813e --- /dev/null +++ b/include/modules/joystick/wrap_joystickmodule.hpp @@ -0,0 +1,19 @@ +#pragma once + +#pragma once + +#include +#include + +#include + +namespace Wrap_JoystickModule +{ + int GetJoysticks(lua_State* L); + + int GetIndex(lua_State* L); + + int GetJoystickCount(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_JoystickModule diff --git a/include/modules/keyboard/keyboard.tcc b/include/modules/keyboard/keyboard.tcc new file mode 100644 index 000000000..b5daad8fe --- /dev/null +++ b/include/modules/keyboard/keyboard.tcc @@ -0,0 +1,88 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace love +{ + template + class Keyboard : public Module + { + public: + enum KeyboardType + { + TYPE_NORMAL, + TYPE_QWERTY, + TYPE_NUMPAD + }; + + struct KeyboardOptions + { + uint8_t type; + bool isPassword; + std::string_view hint; + uint32_t maxLength; + }; + + enum KeyboardOption + { + OPTION_TYPE, + OPTION_PASSCODE, + OPTION_HINT, + OPTION_MAX_LENGTH, + OPTION_MAX_ENUM + }; + + static constexpr uint32_t DEFAULT_INPUT_LENGTH = 0x14; + static constexpr uint32_t MINIMUM_INPUT_LENGTH = 0x01; + + Keyboard(uint32_t maxTextLength) + { + try + { + this->text = std::make_unique(maxTextLength + 1); + } + catch (const std::bad_alloc&) + { + throw love::Exception("Out of memory."); + } + } + + ModuleType GetModuleType() const override + { + return M_KEYBOARD; + } + + const char* GetName() const override + { + return "love.keyboard"; + } + + const bool HasScreenKeyboard() const + { + return true; + } + + std::string_view GetText() const + { + return this->text.get(); + } + + // clang-format off + static constexpr BidirectionalMap keyboardOptions = { + "type", KeyboardOption::OPTION_TYPE, + "password", KeyboardOption::OPTION_PASSCODE, + "hint", KeyboardOption::OPTION_HINT, + "length", KeyboardOption::OPTION_MAX_LENGTH + }; + // clang-format on + + protected: + std::unique_ptr text; + }; +} // namespace love diff --git a/include/modules/keyboard/keyboardc.h b/include/modules/keyboard/keyboardc.h deleted file mode 100644 index c1a16c6a8..000000000 --- a/include/modules/keyboard/keyboardc.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include "common/module.h" - -#include -#include - -namespace love::common -{ - class Keyboard : public Module - { - public: - enum class KeyboardType : uint8_t; - - struct SwkbdOpt - { - KeyboardType type; - bool isPassword; - std::string hint; - - u32 maxLength; - }; - - enum KeyboardOption - { - OPTION_TYPE, - OPTION_PASSCODE, - OPTION_HINT, - OPTION_MAX_LEN, - OPTION_MAX_ENUM - }; - - static constexpr uint32_t DEFAULT_INPUT_LENGTH = 0x14; - static constexpr uint32_t MINIMUM_INPUT_LENGTH = 0x01; - - Keyboard(uint32_t swkbdMaxLength); - - ~Keyboard(); - - ModuleType GetModuleType() const - { - return M_KEYBOARD; - } - - const char* GetName() const override - { - return "love.keyboard"; - } - - /* LOVE2D Functions */ - - virtual std::string SetTextInput(const SwkbdOpt& options) = 0; - - /* End LÖVE Functions */ - - constexpr virtual uint32_t ENCODING_MULTIPLIER() = 0; - - const uint32_t CalculateEncodingMaxLength(const uint32_t in); - - static bool GetConstant(const char* in, KeyboardOption& out); - static bool GetConstant(KeyboardOption in, const char*& out); - static std::vector GetConstants(KeyboardOption); - - static const char* GetConstant(KeyboardOption in); - static constexpr uint8_t MAX_TYPES = 3; - - protected: - char* text; - }; -} // namespace love::common diff --git a/include/modules/keyboard/wrap_keyboard.h b/include/modules/keyboard/wrap_keyboard.h deleted file mode 100644 index 7130e5e60..000000000 --- a/include/modules/keyboard/wrap_keyboard.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "modules/keyboard/keyboard.h" - -namespace Wrap_Keyboard -{ - int SetTextInput(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_Keyboard diff --git a/include/modules/keyboard/wrap_keyboard.hpp b/include/modules/keyboard/wrap_keyboard.hpp new file mode 100644 index 000000000..6ecc81db8 --- /dev/null +++ b/include/modules/keyboard/wrap_keyboard.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace Wrap_Keyboard +{ + int SetTextInput(lua_State* L); + + int HasTextInput(lua_State* L); + + int HasScreenKeyboard(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_Keyboard diff --git a/include/modules/love.h b/include/modules/love.h deleted file mode 100644 index a625c969a..000000000 --- a/include/modules/love.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -namespace love -{ - int Initialize(lua_State* L); - - int NoGame(lua_State* L); - - int LoadArgs(lua_State* L); - - int LoadCallbacks(lua_State* L); - - int Boot(lua_State* L); - - int GetVersion(lua_State* L); - - int EnableAccelerometerAsJoystick(lua_State* L); - - int IsVersionCompatible(lua_State* L); - - /* Debugging Utility */ - - int OpenConsole(lua_State* L); - - /* ----------------- */ - - int Preload(lua_State* L, lua_CFunction func, const char* name); -}; // namespace love diff --git a/include/modules/love/love.hpp b/include/modules/love/love.hpp new file mode 100644 index 000000000..ab89679d4 --- /dev/null +++ b/include/modules/love/love.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +namespace love +{ + enum DoneAction + { + DONE_QUIT, + DONE_RESTART + }; + + template + void PreInit(); + + int Initialize(lua_State* L); + + template + void OnExit(); + + int LoadArgs(lua_State* L); + + int LoadCallbacks(lua_State* L); + + int Boot(lua_State* L); + + int NoGame(lua_State* L); + + int LoadLogFile(lua_State* L); + + int OpenNestlink(lua_State* L); + + template + bool MainLoop(lua_State* L, int numArgs); + + int GetVersion(lua_State* L); + + int IsVersionCompatible(lua_State* L); +} // namespace love diff --git a/include/modules/math/math.hpp b/include/modules/math/math.hpp new file mode 100644 index 000000000..e9e1a6e92 --- /dev/null +++ b/include/modules/math/math.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +namespace love +{ + class Transform; + + class Math : public Module + { + public: + struct Triangle + { + Triangle(const Vector2& x, const Vector2& y, const Vector2& z) : a(x), b(y), c(z) + {} + + Vector2 a, b, c; + }; + + static std::vector Triangulate(const std::vector& polygon); + + static bool IsConvex(const std::vector& polygon); + + static float GammaToLinear(float color); + + static float LinearToGamma(float color); + + static inline double SimplexNoise1(double x) + { + return SimplexNoise1234::noise(x) * 0.5 + 0.5; + } + + static inline double SimplexNoise2(double x, double y) + { + return SimplexNoise1234::noise(x, y) * 0.5 + 0.5; + } + + static inline double SimplexNoise3(double x, double y, double z) + { + return SimplexNoise1234::noise(x, y, z) * 0.5 + 0.5; + } + + static inline double SimplexNoise4(double x, double y, double z, double w) + { + return SimplexNoise1234::noise(x, y, z, w) * 0.5 + 0.5; + } + + static inline double PerlinNoise1(double x) + { + return Noise1234::noise(x) * 0.5 + 0.5; + } + + static inline double PerlinNoise2(double x, double y) + { + return Noise1234::noise(x, y) * 0.5 + 0.5; + } + + static inline double PerlinNoise3(double x, double y, double z) + { + return Noise1234::noise(x, y, z) * 0.5 + 0.5; + } + + static inline double PerlinNoise4(double x, double y, double z, double w) + { + return Noise1234::noise(x, y, z, w) * 0.5 + 0.5; + } + + Math(); + + virtual ~Math() + {} + + RandomGenerator* GetRandomGenerator() + { + return &this->randomGenerator; + } + + RandomGenerator* NewRandomGenerator() const; + + BezierCurve* NewBezierCurve(const std::vector& points) const; + + Transform* NewTransform() const; + + Transform* NewTransform(float x, float y, float a, float sx, float sy, float ox, float oy, + float kx, float ky) const; + + virtual ModuleType GetModuleType() const + { + return M_MATH; + } + + virtual const char* GetName() const + { + return "love.math"; + } + + private: + RandomGenerator randomGenerator; + }; +} // namespace love diff --git a/include/modules/math/mathmodule.h b/include/modules/math/mathmodule.h deleted file mode 100644 index 3728cf5d5..000000000 --- a/include/modules/math/mathmodule.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once - -#include "objects/random/randomgenerator.h" - -#include "common/lmath.h" -#include "common/module.h" -#include "common/vector.h" - -#include "noise1234/noise1234.h" -#include "noise1234/simplexnoise1234.h" - -#include -#include - -namespace love -{ - float GammaToLinear(float c); - - float LinearToGamma(float c); - - class Transform; - - class Math : public Module - { - public: - struct Triangle - { - Triangle(const Vector2& x, const Vector2& y, const Vector2& z) : a(x), b(y), c(z) - {} - - Vector2 a, b, c; - }; - - Math(); - - virtual ~Math() {}; - - ModuleType GetModuleType() const - { - return M_MATH; - } - - const char* GetName() const override - { - return "love.math"; - } - - /* RandomGenerator */ - - RandomGenerator* GetRandomGenerator() - { - return &this->rng; - } - - RandomGenerator* NewRandomGenerator(); - - Transform* NewTransform(); - - Transform* NewTransform(float x, float y, float a, float sx, float sy, float ox, float oy, - float kx, float ky); - - /* LÖVE Functions */ - - static float GammaToLinear(float color); - - static float LinearToGamma(float color); - - std::vector Triangulate(const std::vector& polygon); - - bool IsConvex(const std::vector& polygon); - - private: - RandomGenerator rng; - - /* Helper Functions */ - - inline bool IsCounterClockwise(const Vector2& a, const Vector2& b, const Vector2& c) - { - return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) >= 0; - } - - inline bool OnSameSide(const Vector2& a, const Vector2& b, const Vector2& c, - const Vector2& d) - { - float px = d.x - c.x, py = d.y - c.y; - - // return det(p, a-c) * det(p, b-c) >= 0 - float l = px * (a.y - c.y) - py * (a.x - c.x); - float m = px * (b.y - c.y) - py * (b.x - c.x); - - return l * m >= 0; - } - - inline bool PointInTriangle(const Vector2& p, const Vector2& a, const Vector2& b, - const Vector2& c) - { - return OnSameSide(p, a, b, c) && OnSameSide(p, b, a, c) && OnSameSide(p, c, a, b); - } - - inline bool AnyPointInTriangle(const std::list& vertices, const Vector2& a, - const Vector2& b, const Vector2& c) - { - for (const Vector2* p : vertices) - { - if ((p != &a) && (p != &b) && (p != &c) && - PointInTriangle(*p, a, b, c)) // oh god... - return true; - } - - return false; - } - - inline bool IsEar(const Vector2& a, const Vector2& b, const Vector2& c, - const std::list& vertices) - { - return IsCounterClockwise(a, b, c) && !AnyPointInTriangle(vertices, a, b, c); - } - }; -} // namespace love diff --git a/include/modules/math/wrap_math.hpp b/include/modules/math/wrap_math.hpp new file mode 100644 index 000000000..9a3d1e492 --- /dev/null +++ b/include/modules/math/wrap_math.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace Wrap_Math +{ + int GetRandomGenerator(lua_State* L); + + int NewRandomGenerator(lua_State* L); + + int NewBezierCurve(lua_State* L); + + int NewTransform(lua_State* L); + + int Triangulate(lua_State* L); + + int IsConvex(lua_State* L); + + int GammaToLinear(lua_State* L); + + int LinearToGamma(lua_State* L); + + int PerlinNoise(lua_State* L); + + int SimplexNoise(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_Math diff --git a/include/modules/math/wrap_mathmodule.h b/include/modules/math/wrap_mathmodule.h deleted file mode 100644 index efb5734c8..000000000 --- a/include/modules/math/wrap_mathmodule.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "common/luax.h" - -#include "objects/random/randomgenerator.h" -#include "objects/random/wrap_randomgenerator.h" - -#include "objects/transform/transform.h" -#include "objects/transform/wrap_transform.h" - -#include "modules/math/mathmodule.h" - -namespace Wrap_Math -{ - int GetRandomGenerator(lua_State* L); - - int GammaToLinear(lua_State* L); - - int IsConvex(lua_State* L); - - int LinearToGamma(lua_State* L); - - int NewRandomGenerator(lua_State* L); - - int NewTransform(lua_State* L); - - int Noise(lua_State* L); - - int Triangulate(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_Math diff --git a/include/modules/physics/physics.h b/include/modules/physics/physics.h deleted file mode 100644 index 6347983ab..000000000 --- a/include/modules/physics/physics.h +++ /dev/null @@ -1,149 +0,0 @@ -#pragma once - -#include "common/exception.h" -#include "common/module.h" - -#include "body.h" -#include "wrap_body.h" - -#include "wrap_fixture.h" - -#include "chainshape/chainshape.h" -#include "circleshape/circleshape.h" -#include "distancejoint/distancejoint.h" -#include "edgeshape/edgeshape.h" -#include "frictionjoint/frictionjoint.h" -#include "gearjoint/gearjoint.h" -#include "motorjoint/motorjoint.h" -#include "mousejoint/mousejoint.h" -#include "polygonshape/polygonshape.h" -#include "prismaticjoint/prismaticjoint.h" -#include "pulleyjoint/pulleyjoint.h" -#include "revolutejoint/revolutejoint.h" -#include "ropejoint/ropejoint.h" -#include "weldjoint/weldjoint.h" -#include "wheeljoint/wheeljoint.h" - -namespace love -{ - class Physics : public Module - { - public: - /* 30px = 1m by default */ - static constexpr int DEFAULT_METER = 30; - - Physics(); - - virtual ~Physics(); - - ModuleType GetModuleType() const - { - return M_PHYSICS; - } - - const char* GetName() const override - { - return "love.physics"; - } - - static void SetMeter(float scale); - - static float GetMeter(); - - static float ScaleDown(float scale); - - static float ScaleUp(float scale); - - static void ScaleDown(float& x, float& y); - - static void ScaleUp(float& x, float& y); - - static b2Vec2 ScaleDown(const b2Vec2& vector); - - static b2Vec2 ScaleUp(const b2Vec2& vector); - - static b2AABB ScaleDown(const b2AABB& aabb); - - static b2AABB ScaleUp(const b2AABB& aabb); - - static void b2LinearFrequency(float& frequency, float& ratio, float stiffness, - float damping, b2Body* bodyA, b2Body* bodyB); - - static void b2AngularFrequency(float& frequency, float& ratio, float stiffness, - float damping, b2Body* bodyA, b2Body* bodyB); - - /* lua stuff */ - - int GetDistance(lua_State* L); - - World* NewWorld(float gx, float gy, bool sleep); - - Body* NewBody(World* world, float x, float y, Body::Type type); - - Body* NewBody(World* world, Body::Type type); - - Fixture* NewFixture(Body* body, Shape* shape, float density); - - CircleShape* NewCircleShape(float radius); - - CircleShape* NewCircleShape(float x, float y, float radius); - - PolygonShape* NewRectangleShape(float width, float height); - - PolygonShape* NewRectangleShape(float x, float y, float width, float height); - - PolygonShape* NewRectangleShape(float x, float y, float width, float height, float angle); - - EdgeShape* NewEdgeShape(float x1, float y1, float x2, float y2, bool oneSided); - - int NewPolygonShape(lua_State* L); - - int NewChainShape(lua_State* L); - - DistanceJoint* NewDistanceJoint(Body* a, Body* b, float x1, float y1, float x2, float y2, - bool collideConnected); - - MouseJoint* NewMouseJoint(Body* body, float x, float y); - - RevoluteJoint* NewRevoluteJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, - bool collideConnected); - - RevoluteJoint* NewRevoluteJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, - bool collideConnected, float referenceAngle); - - PrismaticJoint* NewPrismaticJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, - float xAnchor, float yAnchor, bool collideConnected); - - PrismaticJoint* NewPrismaticJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, - float xAnchor, float yAnchor, bool collideConnected, - float referenceAngle); - - PulleyJoint* NewPulleyJoint(Body* a, Body* b, b2Vec2 groundAnchorA, b2Vec2 groundAnchorB, - b2Vec2 anchorA, b2Vec2 anchorB, float ratio, - bool collideConnected); - - GearJoint* NewGearJoint(Joint* a, Joint* b, float ratio, bool collideConnected); - - FrictionJoint* NewFrictionJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, - bool collideConnected); - - WeldJoint* NewWeldJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, - bool collideConnected); - - WeldJoint* NewWeldJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, - bool collideConnected, float referenceAngle); - - WheelJoint* NewWheelJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, - float ax, float ay, bool collideConnected); - - RopeJoint* NewRopeJoint(Body* a, Body* b, float x1, float y1, float x2, float y2, - float maxLength, bool collideConnected); - - MotorJoint* NewMotorJoint(Body* a, Body* b); - - MotorJoint* NewMotorJoint(Body* a, Body* b, float correctionFactor, bool collideConnected); - - private: - static float meter; - }; -} // namespace love diff --git a/include/modules/physics/physics.hpp b/include/modules/physics/physics.hpp new file mode 100644 index 000000000..347c3427a --- /dev/null +++ b/include/modules/physics/physics.hpp @@ -0,0 +1,174 @@ +#pragma once + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace love +{ + class Physics : public Module + { + public: + static constexpr int DEFAULT_METER = 30; + + Physics(); + + virtual ~Physics(); + + const char* GetName() const override + { + return "love.physics"; + } + + virtual ModuleType GetModuleType() const + { + return M_PHYSICS; + } + + World* NewWorld(float gravityX, float gravityY, bool sleep) const; + + Body* NewBody(World* world, float x, float y, Body::Type type) const; + + Body* NewBody(World* world, Body::Type type) const; + + Body* NewCircleBody(World* world, Body::Type type, float x, float y, float radius) const; + + Body* NewRectangleBody(World* world, Body::Type type, float x, float y, float width, + float height, float angle) const; + + Body* NewPolygonBody(World* world, Body::Type type, const std::span& points) const; + + Body* NewEdgeBody(World* world, Body::Type type, float x1, float y1, float x2, float y2, + bool oneSided) const; + + Body* NewChainBody(World* world, Body::Type type, bool loop, + const std::span& points) const; + + CircleShape* NewCircleShape(Body* body, float x, float y, float radius) const; + + PolygonShape* NewRectangleShape(Body* body, float x, float y, float width, float height, + float angle) const; + + EdgeShape* NewEdgeShape(Body* body, float x1, float y1, float x2, float y2, + bool oneSided) const; + + PolygonShape* NewPolygonShape(Body* body, const std::span& points) const; + + ChainShape* NewChainShape(Body* body, bool loop, const std::span& points) const; + + DistanceJoint* NewDistanceJoint(Body* bodyA, Body* bodyB, float x1, float y1, float x2, + float y2, bool collideConnected) const; + + FrictionJoint* NewFrictionJoint(Body* bodyA, Body* bodyB, float x1, float y1, float x2, + float y2, bool collideConnected) const; + + GearJoint* NewGearJoint(Joint* jointA, Joint* jointB, float ratio, + bool collideConnected) const; + + MotorJoint* NewMotorJoint(Body* bodyA, Body* bodyB); + + MotorJoint* NewMotorJoint(Body* bodyA, Body* bodyB, float corrrectionFactor, + bool collideConnected) const; + + MouseJoint* NewMouseJoint(Body* body, float x, float y) const; + + PrismaticJoint* NewPrismaticJoint(Body* bodyA, Body* bodyB, float x1, float y1, float x2, + float y2, float axisX, float axisY, + bool collideConnected) const; + + PrismaticJoint* NewPrismaticJoint(Body* bodyA, Body* bodyB, float x1, float y1, float x2, + float y2, float axisX, float axisY, bool collideConnected, + float referenceAngle) const; + + PulleyJoint* NewPulleyJoint(Body* bodyA, Body* bodyB, b2Vec2 groundAnchorA, + b2Vec2 groundAnchorB, b2Vec2 anchorA, b2Vec2 anchorB, + float ratio, bool collideConnected) const; + + RevoluteJoint* NewRevoluteJoint(Body* bodyA, Body* bodyB, float xA, float yA, float xB, + float yB, bool collideConnected) const; + + RevoluteJoint* NewRevoluteJoint(Body* bodyA, Body* bodyB, float xA, float yA, float xB, + float yB, bool collideConnected, + float referenceAngle) const; + + RopeJoint* NewRopeJoint(Body* bodyA, Body* bodyB, float x1, float y1, float x2, float y2, + float maxLength, bool collideConnected) const; + + WeldJoint* NewWeldJoint(Body* bodyA, Body* bodyB, float x1, float y1, float x2, float y2, + bool collideConnected) const; + + WeldJoint* NewWeldJoint(Body* bodyA, Body* bodyB, float x1, float y1, float x2, float y2, + bool collideConnected, float referenceAngle) const; + + WheelJoint* NewWheelJoint(Body* bodyA, Body* bodyB, float x1, float y1, float x2, float y2, + float axisX, float axisY, bool collideConnected) const; + + int GetDistance(lua_State* L); + + static void SetMeter(float scale); + + static float GetMeter(); + + static float ScaleDown(float value); + + static float ScaleUp(float value); + + static void ScaleDown(float& x, float& y); + + static void ScaleUp(float& x, float& y); + + static b2Vec2 ScaleDown(const b2Vec2& vector); + + static b2Vec2 ScaleUp(const b2Vec2& vector); + + static b2AABB ScaleDown(const b2AABB& aabb); + + static b2AABB ScaleUp(const b2AABB& aabb); + + static void ComputeLinearStiffness(float& stiffness, float& damping, float frequency, + float ratio, const b2Body* bodyA, const b2Body* bodyB); + + static void ComputeLinearFrequency(float& frequency, float& ratio, float stiffness, + float damping, b2Body* bodyA, b2Body* bodyB); + + static void ComputeAngularStiffness(float& frequency, float& ratio, float stiffness, + float damping, const b2Body* bodyA, + const b2Body* bodyB); + + static void ComputeAngularFrequency(float& frequency, float& ratio, float stiffness, + float damping, const b2Body* bodyA, + const b2Body* bodyB); + + b2BlockAllocator* GetAllocator() + { + return &this->allocator; + } + + private: + static float meter; + b2BlockAllocator allocator; + }; +} // namespace love diff --git a/include/modules/physics/wrap_physics.h b/include/modules/physics/wrap_physics.h deleted file mode 100644 index 4d0b255d9..000000000 --- a/include/modules/physics/wrap_physics.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "common/luax.h" - -#include "modules/physics/physics.h" -#include "objects/box2d/common.h" - -namespace Wrap_Physics -{ - int NewWorld(lua_State* L); - - int NewBody(lua_State* L); - - int NewFixture(lua_State* L); - - int NewCircleShape(lua_State* L); - - int NewRectangleShape(lua_State* L); - - int NewEdgeShape(lua_State* L); - - int NewPolygonShape(lua_State* L); - - int NewChainShape(lua_State* L); - - int NewDistanceJoint(lua_State* L); - - int NewMouseJoint(lua_State* L); - - int NewRevoluteJoint(lua_State* L); - - int NewPrismaticJoint(lua_State* L); - - int NewPulleyJoint(lua_State* L); - - int NewGearJoint(lua_State* L); - - int NewFrictionJoint(lua_State* L); - - int NewWeldJoint(lua_State* L); - - int NewWheelJoint(lua_State* L); - - int NewRopeJoint(lua_State* L); - - int NewMotorJoint(lua_State* L); - - int GetDistance(lua_State* L); - - int SetMeter(lua_State* L); - - int GetMeter(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_Physics diff --git a/include/modules/physics/wrap_physics.hpp b/include/modules/physics/wrap_physics.hpp new file mode 100644 index 000000000..ba3a0dfe3 --- /dev/null +++ b/include/modules/physics/wrap_physics.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +namespace Wrap_Physics +{ + int NewWorld(lua_State* L); + + int NewBody(lua_State* L); + + int NewCircleBody(lua_State* L); + + int NewRectangleBody(lua_State* L); + + int NewPolygonBody(lua_State* L); + + int NewEdgeBody(lua_State* L); + + int NewChainBody(lua_State* L); + + int NewCircleShape(lua_State* L); + + int NewRectangleShape(lua_State* L); + + int NewEdgeShape(lua_State* L); + + int NewPolygonShape(lua_State* L); + + int NewChainShape(lua_State* L); + + int NewDistanceJoint(lua_State* L); + + int NewFrictionJoint(lua_State* L); + + int NewGearJoint(lua_State* L); + + int NewMotorJoint(lua_State* L); + + int NewMouseJoint(lua_State* L); + + int NewPrismaticJoint(lua_State* L); + + int NewPulleyJoint(lua_State* L); + + int NewRevoluteJoint(lua_State* L); + + int NewRopeJoint(lua_State* L); + + int NewWeldJoint(lua_State* L); + + int NewWheelJoint(lua_State* L); + + int GetDistance(lua_State* L); + + int SetMeter(lua_State* L); + + int GetMeter(lua_State* L); + + int ComputeLinearStiffness(lua_State* L); + + int ComputeLinearFrequency(lua_State* L); + + int ComputeAngularStiffness(lua_State* L); + + int ComputeAngularFrequency(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_Physics diff --git a/include/modules/sensor/sensor.hpp b/include/modules/sensor/sensor.hpp new file mode 100644 index 000000000..8d857c4e1 --- /dev/null +++ b/include/modules/sensor/sensor.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +#include + +#include + +#include +#include + +namespace love +{ + class Sensor : public Module + { + public: + enum SensorType + { + SENSOR_ACCELEROMETER, + SENSOR_GYROSCOPE, + SENSOR_MAX_ENUM + }; + + Sensor(); + + virtual ~Sensor() + {} + + const char* GetName() const override + { + return "love.sensor"; + } + + ModuleType GetModuleType() const override + { + return M_SENSOR; + } + + bool HasSensor(SensorType type); + + bool IsEnabled(SensorType type); + + const char* GetSensorName(SensorType type); + + void SetEnabled(SensorType type, bool enable); + + std::array GetData(SensorType type); + + // clang-format off + static constexpr BidirectionalMap sensorTypes = { + "accelerometer", SENSOR_ACCELEROMETER, + "gyroscope", SENSOR_GYROSCOPE + }; + // clang-format on + + protected: + std::map sensors; + }; +} // namespace love diff --git a/include/modules/sensor/wrap_sensor.hpp b/include/modules/sensor/wrap_sensor.hpp new file mode 100644 index 000000000..3bfc7a906 --- /dev/null +++ b/include/modules/sensor/wrap_sensor.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace Wrap_Sensor +{ + love::Sensor::SensorType CheckSensorType(lua_State* L, int index); + + int HasSensor(lua_State* L); + + int IsEnabled(lua_State* L); + + int SetEnabled(lua_State* L); + + int GetData(lua_State* L); + + int GetName(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_Sensor diff --git a/include/modules/sound/sound.h b/include/modules/sound/sound.h deleted file mode 100644 index 7fb2310b4..000000000 --- a/include/modules/sound/sound.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "objects/decoder/decoder.h" -#include "objects/filedata/filedata.h" -#include "objects/sounddata/sounddata.h" - -#include "flacdecoder.h" -#include "modplugdecoder.h" -#include "mp3decoder.h" -#include "vorbisdecoder.h" -#include "wavedecoder.h" - -#include "common/module.h" - -namespace love -{ - class Sound : public Module - { - public: - static love::Type type; - - ~Sound(); - - ModuleType GetModuleType() const - { - return M_SOUND; - } - - const char* GetName() const override - { - return "love.sound"; - } - - Decoder* NewDecoder(love::FileData* data, int bufferSize); - - SoundData* NewSoundData(Decoder* decoder); - - SoundData* NewSoundData(int samples, int sampleRate, int bitDepth, int channels); - - SoundData* NewSoundData(void* data, int samples, int sampleRate, int bitDepth, - int channels); - }; -} // namespace love diff --git a/include/modules/sound/sound.hpp b/include/modules/sound/sound.hpp new file mode 100644 index 000000000..f60fe4eaa --- /dev/null +++ b/include/modules/sound/sound.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include +#include + +#include + +namespace love +{ + class Sound : public Module + { + public: + virtual ~Sound(); + + ModuleType GetModuleType() const override + { + return M_SOUND; + } + + const char* GetName() const override + { + return "love.sound"; + } + + Decoder* NewDecoder(Stream* stream, int bufferSize); + + SoundData* NewSoundData(Decoder* decoder); + + SoundData* NewSoundData(int samples, int sampleRate, int bitDepth, int channels); + + SoundData* NewSoundData(void* data, int samples, int sampleRate, int bitDepth, + int channels); + }; +} // namespace love diff --git a/include/modules/sound/wrap_sound.h b/include/modules/sound/wrap_sound.h deleted file mode 100644 index f54844129..000000000 --- a/include/modules/sound/wrap_sound.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "common/luax.h" - -#include "objects/decoder/decoder.h" -#include "objects/decoder/wrap_decoder.h" - -#include "objects/sounddata/wrap_sounddata.h" - -#include "modules/filesystem/wrap_filesystem.h" -#include "modules/sound/sound.h" - -namespace Wrap_Sound -{ - int NewDecoder(lua_State* L); - - int NewSoundData(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_Sound diff --git a/include/modules/sound/wrap_sound.hpp b/include/modules/sound/wrap_sound.hpp new file mode 100644 index 000000000..df01789ec --- /dev/null +++ b/include/modules/sound/wrap_sound.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace Wrap_Sound +{ + int NewDecoder(lua_State* L); + + int NewSoundData(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_Sound diff --git a/include/modules/system/system.tcc b/include/modules/system/system.tcc new file mode 100644 index 000000000..3f89575b9 --- /dev/null +++ b/include/modules/system/system.tcc @@ -0,0 +1,85 @@ +#pragma once + +#include +#include + +#include + +namespace love +{ + template + class System : public Module + { + public: + enum PowerState + { + POWER_UNKNOWN, + POWER_BATTERY, + POWER_CHARGING, + POWER_CHARGED, + POWER_MAX_ENUM + }; + + enum NetworkState + { + NETWORK_UNKNOWN, + NETWORK_DISCONNECTED, + NETWORK_CONNECTED, + NETWORK_MAX_ENUM + }; + + enum SystemTheme + { + THEME_LIGHT, + THEME_DARK, + THEME_MAX_ENUM + }; + + System() : info {} + {} + + ModuleType GetModuleType() const override + { + return M_SYSTEM; + } + + const char* GetName() const override + { + return "love.system"; + } + + static const char* GetOS() + { + return __OS__; + } + + // clang-format off + static constexpr BidirectionalMap powerStates = { + "unknown", System::PowerState::POWER_UNKNOWN, + "battery", System::PowerState::POWER_BATTERY, + "charged", System::PowerState::POWER_CHARGED, + "charging", System::PowerState::POWER_CHARGING + }; + + static constexpr BidirectionalMap networkStates = { + "unknown", System::NetworkState::NETWORK_UNKNOWN, + "connected", System::NetworkState::NETWORK_CONNECTED, + "disconnected", System::NetworkState::NETWORK_DISCONNECTED + }; + // clang-format on + + protected: + struct + { + size_t processors; + std::string model; + std::string locale; + std::string version; + std::string username; + std::string friendCode; + std::string colorTheme; + } info; + + private: + }; +} // namespace love diff --git a/include/modules/system/systemc.h b/include/modules/system/systemc.h deleted file mode 100644 index 43016d3e8..000000000 --- a/include/modules/system/systemc.h +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -#include "common/module.h" - -using namespace std::literals::string_literals; - -#define OS_NAME "Horizon" - -#if defined(__3DS__) - #define LANGUAGE_COUNT 12 -#elif defined(__SWITCH__) - #define LANGUAGE_COUNT 17 -#endif - -namespace love::common -{ - class System : public Module - { - public: - enum PowerState - { - POWER_UNKNOWN, - POWER_BATTERY, - POWER_CHARGING, - POWER_CHARGED, - POWER_MAX_ENUM - }; - - enum NetworkState - { - NETWORK_UNKNOWN, - NETWORK_DISCONNECTED, - NETWORK_CONNECTED, - NETWORK_MAX_ENUM - }; - - enum SystemTheme - { - THEME_LIGHT, - THEME_DARK, - THEME_MAX_ENUM - }; - - System(); - - ModuleType GetModuleType() const - { - return M_SYSTEM; - } - - const char* GetName() const override - { - return "love.system"; - } - - /* LÖVE2D Functions */ - - std::string GetOS() const; - - virtual PowerState GetPowerInfo(uint8_t& percent) const = 0; - - virtual NetworkState GetNetworkInfo(uint8_t& signal) const = 0; - - /* pure virtual subclass stuff */ - - virtual int GetProcessorCount() = 0; - - virtual const std::string& GetUsername() = 0; - - virtual const std::string& GetSystemTheme() = 0; - - virtual const std::string& GetPreferredLocales() = 0; - - virtual const std::string& GetVersion() = 0; - - virtual const std::string& GetRegion() = 0; - - virtual const std::string& GetModel() = 0; - - virtual const std::string& GetFriendCode() = 0; - - /* end pure virtual methods */ - - static bool GetConstant(const char* in, PowerState& out); - static bool GetConstant(PowerState in, const char*& out); - - static bool GetConstant(const char* in, NetworkState& out); - static bool GetConstant(NetworkState in, const char*& out); - - protected: - struct - { - int processors; - std::string model; - std::string region; - std::string version; - std::string username; - std::string language; - std::string friendCode; - std::string colorTheme; - } systemInfo; - }; -} // namespace love::common diff --git a/include/modules/system/wrap_system.h b/include/modules/system/wrap_system.h deleted file mode 100644 index e88f54873..000000000 --- a/include/modules/system/wrap_system.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "common/luax.h" -namespace Wrap_System -{ - int GetOS(lua_State* L); - - int GetProcessorCount(lua_State* L); - - int GetPowerInfo(lua_State* L); - - int GetNetworkInfo(lua_State* L); - - int GetPreferredLocales(lua_State* L); - - int GetRegion(lua_State* L); - - int GetModel(lua_State* L); - - int GetUsername(lua_State* L); - - int GetVersion(lua_State* L); - - int GetFriendCode(lua_State* L); - - int GetSystemTheme(lua_State* L); - - int GetPlayCoins(lua_State* L); - - int SetPlayCoins(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_System diff --git a/include/modules/system/wrap_system.hpp b/include/modules/system/wrap_system.hpp new file mode 100644 index 000000000..2dae7d551 --- /dev/null +++ b/include/modules/system/wrap_system.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace Wrap_System +{ + int GetOS(lua_State* L); + + int GetProcessorCount(lua_State* L); + + int GetPowerInfo(lua_State* L); + + int GetNetworkInfo(lua_State* L); + + int GetPreferredLocales(lua_State* L); + + int GetModel(lua_State* L); + + int GetUsername(lua_State* L); + + int GetVersion(lua_State* L); + + int GetFriendInfo(lua_State* L); + + int GetSystemTheme(lua_State* L); + + int GetPlayCoins(lua_State* L); + + int SetPlayCoins(lua_State* L); + + int Register(lua_State* L); + + extern std::span extensions; +} // namespace Wrap_System diff --git a/include/modules/thread/threadc.h b/include/modules/thread/threadc.h deleted file mode 100644 index 9b691d82f..000000000 --- a/include/modules/thread/threadc.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "modules/thread/types/mutex.h" -#include "modules/thread/types/threadable.h" - -namespace love -{ - namespace common - { - class Thread - { - public: - Thread(Threadable* t); - - /* Detach the thread */ - virtual ~Thread(); - - virtual bool Start() = 0; - - /* - ** In SDL, SDL_WaitThread will wait - ** for the thread to be finished and then - ** SDL frees the pointer to the thread - */ - virtual void Wait() = 0; - - bool IsRunning(); - - static constexpr size_t STACK_SIZE = 0x2000; - - protected: - Threadable* t; - bool running; - - bool hasThread; - - ::Thread thread; - thread::Mutex mutex; - - static void Runner(void* data); - - static s32 GetCurrentThreadPriority(); - }; - } // namespace common -} // namespace love diff --git a/include/modules/thread/threadmodule.h b/include/modules/thread/threadmodule.h deleted file mode 100644 index 52f622a7c..000000000 --- a/include/modules/thread/threadmodule.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "modules/thread/types/mutex.h" -#include "objects/channel/channel.h" -#include "objects/thread/luathread.h" - -#include "common/data.h" -#include "common/module.h" -namespace love -{ - class ThreadModule : public Module - { - public: - /* Module implementors */ - ~ThreadModule() {}; - - const char* GetName() const override - { - return "love.thread"; - } - - ModuleType GetModuleType() const - { - return M_THREAD; - } - - /* Löve2D Functions */ - - Channel* NewChannel(); - - Channel* GetChannel(const std::string& name); - - LuaThread* NewThread(const std::string& name, love::Data* data); - - private: - std::map> namedChannels; - thread::MutexRef namedChannelMutex; - }; -} // namespace love \ No newline at end of file diff --git a/include/modules/thread/threadmodule.hpp b/include/modules/thread/threadmodule.hpp new file mode 100644 index 000000000..acdf1f444 --- /dev/null +++ b/include/modules/thread/threadmodule.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include + +namespace love +{ + class ThreadModule : public Module + { + public: + virtual ~ThreadModule() + {} + + virtual const char* GetName() const override + { + return "love.thread"; + } + + virtual ModuleType GetModuleType() const override + { + return M_THREAD; + } + + LuaThread* NewThread(const std::string& name, Data* data) const; + + Channel* NewChannel() const; + + Channel* GetChannel(const std::string& name); + + protected: + std::map> namedChannels; + love::mutex mutex; + }; +} // namespace love diff --git a/include/modules/thread/types/conditional.h b/include/modules/thread/types/conditional.h deleted file mode 100644 index 57b6f620f..000000000 --- a/include/modules/thread/types/conditional.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "modules/thread/types/mutex.h" - -namespace love::thread -{ - class Conditional - { - public: - Conditional(); - ~Conditional(); - - void Signal(); - void Broadcast(); - bool Wait(Mutex* mutex, s64 timeout = -1); - - private: - CondVar condVar; - }; - - class ConditionalRef - { - public: - ConditionalRef(); - ~ConditionalRef(); - - operator Conditional*() const; - Conditional* operator->() const; - - private: - Conditional* conditional; - }; - - inline Conditional* NewConditional() - { - return new Conditional(); - } -} // namespace love::thread diff --git a/include/modules/thread/types/lock.h b/include/modules/thread/types/lock.h deleted file mode 100644 index e7248bb05..000000000 --- a/include/modules/thread/types/lock.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "modules/thread/types/mutex.h" - -namespace love::thread -{ - class Lock - { - public: - Lock(Mutex& mutex); - Lock(Mutex* mutex); - - Lock(Lock&& other); - - ~Lock(); - - private: - Mutex* mutex; - }; -} // namespace love::thread diff --git a/include/modules/thread/types/mutex.h b/include/modules/thread/types/mutex.h deleted file mode 100644 index 33e20d6d3..000000000 --- a/include/modules/thread/types/mutex.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#if defined(__3DS__) - #include <3ds.h> - -typedef LightLock LOVE_Mutex; - - #define LOVE_mutexInit LightLock_Init - #define LOVE_mutexLock LightLock_Lock - #define LOVE_mutexUnlock LightLock_Unlock -#elif defined(__SWITCH__) - #include - -typedef Mutex LOVE_Mutex; - - #define LOVE_mutexInit mutexInit - #define LOVE_mutexLock mutexLock - #define LOVE_mutexUnlock mutexUnlock -#endif - -namespace love::thread -{ - class Conditional; - - class Mutex - { - public: - Mutex(); - - ~Mutex(); - - Mutex(const Mutex&) = delete; - Mutex(Mutex&&) = delete; - Mutex& operator=(const Mutex&) = delete; - Mutex& operator=(Mutex&&) = delete; - - void Lock(); - - bool IsLocked(); - - void Unlock(); - - private: - LOVE_Mutex mutex; - - bool locked; - - friend class Conditional; - }; - - class MutexRef - { - public: - MutexRef(); - - ~MutexRef(); - - operator Mutex*() const; - Mutex* operator->() const; - - private: - Mutex* mutex; - }; - - inline Mutex* NewMutex() - { - return new Mutex(); - } -} // namespace love::thread diff --git a/include/modules/thread/types/threadable.h b/include/modules/thread/types/threadable.h deleted file mode 100644 index 01709e377..000000000 --- a/include/modules/thread/types/threadable.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "objects/object.h" -#include - -namespace love -{ - class Thread; - - class Threadable : public love::Object - { - public: - static love::Type type; - - Threadable(); - virtual ~Threadable(); - - virtual void ThreadFunction() = 0; - - bool Start(); - - void Wait(); - - bool IsRunning() const; - - const char* GetThreadName() const; - - private: - friend class love::Thread; - - protected: - love::Thread* owner; - std::string threadName; - }; -} // namespace love diff --git a/include/modules/thread/wrap_threadmodule.h b/include/modules/thread/wrap_threadmodule.h deleted file mode 100644 index 2edab04d2..000000000 --- a/include/modules/thread/wrap_threadmodule.h +++ /dev/null @@ -1,12 +0,0 @@ -#include "common/luax.h" - -namespace Wrap_ThreadModule -{ - int NewThread(lua_State* L); - - int NewChannel(lua_State* L); - - int GetChannel(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_ThreadModule diff --git a/include/modules/thread/wrap_threadmodule.hpp b/include/modules/thread/wrap_threadmodule.hpp new file mode 100644 index 000000000..0641c910f --- /dev/null +++ b/include/modules/thread/wrap_threadmodule.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace Wrap_ThreadModule +{ + int NewThread(lua_State* L); + + int NewChannel(lua_State* L); + + int GetChannel(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_ThreadModule diff --git a/include/modules/timer/timer.tcc b/include/modules/timer/timer.tcc new file mode 100644 index 000000000..99c262b2c --- /dev/null +++ b/include/modules/timer/timer.tcc @@ -0,0 +1,66 @@ +#pragma once + +#include +#include + +#include +using namespace std::chrono_literals; + +namespace love +{ + template + class Timer : public Module + { + public: + Timer() : + currentTime(0), + previousFpsUpdate(0), + fps(0), + averageDelta(0), + fpsUpdateFrequency(1), + frames(0), + delta(0) + {} + + virtual ~Timer() + {} + + const char* GetName() const override + { + return "love.timer"; + } + + ModuleType GetModuleType() const override + { + return M_TIMER; + } + + double GetDelta() const + { + return this->delta; + } + + double GetAverageDelta() const + { + return this->averageDelta; + } + + int GetFPS() const + { + return this->fps; + } + + protected: + double currentTime; + double previousFpsUpdate; + + double previousTime; + + int fps; + double averageDelta; + double fpsUpdateFrequency; + + int frames; + double delta; + }; +} // namespace love diff --git a/include/modules/timer/timerc.h b/include/modules/timer/timerc.h deleted file mode 100644 index 0380006bb..000000000 --- a/include/modules/timer/timerc.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -** modules/timer.h -** @brief : Used for time keeping -*/ - -#pragma once - -#include "common/module.h" - -namespace love::common -{ - class Timer : public Module - { - public: - static constexpr auto SLEEP_DURATION = 1000000ULL; - - Timer(); - - ModuleType GetModuleType() const - { - return M_TIMER; - } - - const char* GetName() const override - { - return "love.timer"; - } - - // Löve2D Functions - - static double GetTime(); - - double GetAverageDelta(); - - double GetDelta(); - - int GetFPS(); - - void Sleep(float seconds); - - double Step(); - - // End Löve2D Functions - - protected: - double currentTime; - double lastTime; - double prevFPSUpdate; - - int fps; - double averageDelta; - - double fpsUpdateFrequency; - int frames; - - double dt; - - static uint64_t reference; - }; -} // namespace love::common diff --git a/include/modules/timer/wrap_timer.h b/include/modules/timer/wrap_timer.h deleted file mode 100644 index 775c99322..000000000 --- a/include/modules/timer/wrap_timer.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "modules/timer/timer.h" -namespace Wrap_Timer -{ - int GetAverageDelta(lua_State* L); - - int GetDelta(lua_State* L); - - int GetFPS(lua_State* L); - - int GetTime(lua_State* L); - - int Sleep(lua_State* L); - - int Step(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_Timer diff --git a/include/modules/timer/wrap_timer.hpp b/include/modules/timer/wrap_timer.hpp new file mode 100644 index 000000000..3e21fe7eb --- /dev/null +++ b/include/modules/timer/wrap_timer.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace Wrap_Timer +{ + int GetAverageDelta(lua_State* L); + + int GetDelta(lua_State* L); + + int GetFPS(lua_State* L); + + int GetTime(lua_State* L); + + int Sleep(lua_State* L); + + int Step(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_Timer diff --git a/include/modules/touch/touch.h b/include/modules/touch/touch.h deleted file mode 100644 index a6b6f6219..000000000 --- a/include/modules/touch/touch.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "common/module.h" - -#include - -namespace love -{ - class Touch : public Module - { - public: - struct TouchInfo - { - int64_t id; - - double x; - double y; - - double dx; - double dy; - - double pressure; - }; - - ModuleType GetModuleType() const - { - return M_TOUCH; - } - - const char* GetName() const override - { - return "love.touch"; - } - - const std::vector& GetTouches() const; - - const TouchInfo& GetTouch(int64_t id) const; - - void OnEvent(int type, const TouchInfo& info); - - private: - std::vector touches; - }; -} // namespace love diff --git a/include/modules/touch/touch.hpp b/include/modules/touch/touch.hpp new file mode 100644 index 000000000..b6c239aa2 --- /dev/null +++ b/include/modules/touch/touch.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include + +#include + +namespace love +{ + class Touch : public Module + { + public: + virtual ~Touch() + {} + + ModuleType GetModuleType() const override + { + return M_TOUCH; + }; + + const char* GetName() const override + { + return "love.touch"; + } + + const std::vector& GetTouches() const; + + const Finger& GetTouch(int64_t id) const; + + void OnEvent(SubEventType type, const Finger& info); + + private: + std::vector touches; + }; +} // namespace love diff --git a/include/modules/touch/wrap_touch.h b/include/modules/touch/wrap_touch.h deleted file mode 100644 index 3d9f07e47..000000000 --- a/include/modules/touch/wrap_touch.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "modules/touch/touch.h" - -namespace Wrap_Touch -{ - int GetTouches(lua_State* L); - - int GetPosition(lua_State* L); - - int GetPressure(lua_State* L); - - int64_t CheckTouchID(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Touch diff --git a/include/modules/touch/wrap_touch.hpp b/include/modules/touch/wrap_touch.hpp new file mode 100644 index 000000000..5ba0e6b4d --- /dev/null +++ b/include/modules/touch/wrap_touch.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace Wrap_Touch +{ + int GetTouches(lua_State* L); + + int GetPosition(lua_State* L); + + int GetPressure(lua_State* L); + + int64_t CheckTouchID(lua_State* L, int index); + + int Register(lua_State* L); +} // namespace Wrap_Touch diff --git a/include/modules/video/videomodule.h b/include/modules/video/videomodule.h deleted file mode 100644 index df597e9a5..000000000 --- a/include/modules/video/videomodule.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "common/module.h" -#include "objects/file/file.h" -#include "objects/videostream/videostream.h" - -namespace love -{ - class Worker; - - class VideoModule : public Module - { - public: - VideoModule(); - - virtual ~VideoModule(); - - virtual const char* GetName() const - { - return "love.video"; - } - - virtual ModuleType GetModuleType() const - { - return M_VIDEO; - } - - VideoStream* NewVideoStream(File* file); - - private: - Worker* workerThread; - }; -} // namespace love diff --git a/include/modules/video/worker.h b/include/modules/video/worker.h deleted file mode 100644 index 2b2701398..000000000 --- a/include/modules/video/worker.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "modules/thread/types/conditional.h" -#include "modules/thread/types/threadable.h" - -#include "objects/videostream/theorastream.h" -#include "objects/videostream/utility/stream.h" - -#include - -namespace love -{ - class Worker : public Threadable - { - public: - Worker(); - - virtual ~Worker(); - - void ThreadFunction(); - - void AddStream(TheoraStream* stream); - - void Stop(); - - private: - std::vector> streams; - - thread::MutexRef mutex; - thread::ConditionalRef condition; - - bool stopping; - }; -} // namespace love diff --git a/include/modules/video/wrap_videomodule.h b/include/modules/video/wrap_videomodule.h deleted file mode 100644 index d1e001bdb..000000000 --- a/include/modules/video/wrap_videomodule.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "modules/video/videomodule.h" - -namespace Wrap_VideoModule -{ - int NewVideoStream(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_VideoModule diff --git a/include/modules/window/window.tcc b/include/modules/window/window.tcc new file mode 100644 index 000000000..81a6e9a75 --- /dev/null +++ b/include/modules/window/window.tcc @@ -0,0 +1,182 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace love +{ + template + class Graphics; + + template + class Window : public Module + { + public: + struct WindowSize + { + int width; + int height; + + bool operator==(const WindowSize& other) const + { + return other.width == this->width && other.height == this->height; + } + }; + + enum FullscreenType + { + FULLSCREEN_EXCLUSIVE, + FULLSCREEN_DESKTOP, + FULLSCREEN_MAX_ENUM + }; + + enum Setting + { + SETTING_FULLSCREEN, + SETTING_FULLSCREEN_TYPE, + SETTING_VSYNC, + SETTING_MSAA, + SETTING_STENCIL, + SETTING_DEPTH, + SETTING_RESIZABLE, + SETTING_MIN_WIDTH, + SETTING_MIN_HEIGHT, + SETTING_BORDERLESS, + SETTING_CENTERED, + SETTING_DISPLAYINDEX, + SETTING_USE_DPISCALE, + SETTING_REFRESHRATE, + SETTING_X, + SETTING_Y, + SETTING_MAX_ENUM + }; + + struct WindowSettings + { + bool fullscreen = false; + FullscreenType fullScreenType = Window::FULLSCREEN_DESKTOP; + int vsync = 1; + int msaa = 0; + bool stencil = true; + int depth = 0; + bool resizable = false; + int minwidth = 1; + int minheight = 1; + bool borderless = false; + bool centered = true; + int displayindex = 0; + bool usedpiscale = true; + double refreshrate = 0.0; + bool useposition = false; + int x = 0; + int y = 0; + }; + + Window() : open(false), width(800), height(600), pixelWidth(800), pixelHeight(600) + {} + + virtual ~Window() + {} + + virtual ModuleType GetModuleType() const + { + return M_WINDOW; + } + + virtual const char* GetName() const + { + return "love.window"; + } + + bool IsOpen() const + { + return this->open; + } + + int GetWidth() const + { + return this->width; + } + + int GetHeight() const + { + return this->height; + } + + int GetPixelWidth() const + { + return this->pixelWidth; + } + + int GetPixelHeight() const + { + return this->pixelHeight; + } + + float GetDPIScale() const + { + return 1.0f; + } + + void WindowToPixelCoords(double* x, double* y) const + { + if (x != nullptr) + *x = (*x) * ((double)this->pixelWidth / (double)this->width); + + if (y != nullptr) + *y = (*y) * ((double)this->pixelHeight / (double)this->height); + } + + void PixelToWindowCoords(double* x, double* y) const + { + if (x != nullptr) + *x = (*x) * ((double)this->width / (double)this->pixelWidth); + + if (y != nullptr) + *y = (*y) * ((double)this->height / (double)this->pixelHeight); + } + + // clang-format off + static constexpr BidirectionalMap windowSettings = { + "fullscreen", SETTING_FULLSCREEN, + "fullscreentype", SETTING_FULLSCREEN_TYPE, + "vsync", SETTING_VSYNC, + "msaa", SETTING_MSAA, + "stencil", SETTING_STENCIL, + "depth", SETTING_DEPTH, + "resizable", SETTING_RESIZABLE, + "minwidth", SETTING_MIN_WIDTH, + "minheight", SETTING_MIN_HEIGHT, + "borderless", SETTING_BORDERLESS, + "centered", SETTING_CENTERED, + "displayindex", SETTING_DISPLAYINDEX, + "usedpiscale", SETTING_USE_DPISCALE, + "refreshrate", SETTING_REFRESHRATE, + "x", SETTING_X, + "y", SETTING_Y + }; + + static constexpr BidirectionalMap fullScreenTypes = { + "exclusive", FULLSCREEN_EXCLUSIVE, + "desktop", FULLSCREEN_DESKTOP + }; + // clang-format on + + protected: + bool open; + bool sleepAllowed; + + WindowSettings settings; + + int width; + int height; + + int pixelWidth; + int pixelHeight; + }; +} // namespace love diff --git a/include/modules/window/windowc.h b/include/modules/window/windowc.h deleted file mode 100644 index 3c14b11de..000000000 --- a/include/modules/window/windowc.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "modules/graphics/graphics.h" - -namespace love::common -{ - class Window : public Module - { - public: - typedef std::pair DisplaySize; - - Window(); - - virtual ~Window(); - - ModuleType GetModuleType() const - { - return M_WINDOW; - } - - const char* GetName() const override - { - return "love.window"; - } - - void Close(); - - void Close(bool allowExceptions); - - // Löve2D Functions - - virtual int GetDisplayCount() = 0; - - const std::vector& GetFullscreenModes(); - - virtual DisplaySize GetDesktopSize() - { - DisplaySize size { 0, 0 }; - return size; - } - - virtual void OnSizeChanged(int width, int height) - {} - - virtual void GetWindow(int& width, int& height) - {} - - virtual bool CreateWindowAndContext() - { - return true; - } - - bool IsOpen() const; - - bool SetMode(); - - // End Löve2D Functions - - void SetGraphics(Graphics* g); - - protected: - StrongReference graphics; - bool open; - - std::vector fullscreenModes; - }; -} // namespace love::common diff --git a/include/modules/window/wrap_window.h b/include/modules/window/wrap_window.h deleted file mode 100644 index be9836ee6..000000000 --- a/include/modules/window/wrap_window.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "modules/window/window.h" -namespace Wrap_Window -{ - int Close(lua_State* L); - - int GetDisplayCount(lua_State* L); - - int GetFullscreenModes(lua_State* L); - - int IsOpen(lua_State* L); - - int SetMode(lua_State* L); - - int LoadButtons(lua_State* L); - - int LoadMessageBox(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_Window diff --git a/include/modules/window/wrap_window.hpp b/include/modules/window/wrap_window.hpp new file mode 100644 index 000000000..9b8109a8a --- /dev/null +++ b/include/modules/window/wrap_window.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace Wrap_Window +{ + int SetMode(lua_State* L); + + int SetIcon(lua_State* L); + + int SetTitle(lua_State* L); + + int IsOpen(lua_State*L); + + int Register(lua_State* L); +} // namespace Wrap_Window diff --git a/include/objects/beziercurve/beziercurve.hpp b/include/objects/beziercurve/beziercurve.hpp new file mode 100644 index 000000000..c0e34a425 --- /dev/null +++ b/include/objects/beziercurve/beziercurve.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +#include + +namespace love +{ + class BezierCurve : public Object + { + public: + static Type type; + + BezierCurve(const std::vector& controlPoints); + + size_t GetDegree() const + { + return this->controlPoints.size() - 1; + } + + BezierCurve GetDerivative() const; + + const Vector2& GetControlPoint(int index) const; + + void SetControlPoint(int index, const Vector2& point); + + void InsertControlPoint(const Vector2& point, int position = -1); + + void RemoveControlPoint(int index); + + size_t GetControlPointCount() const + { + return this->controlPoints.size(); + } + + void Translate(const Vector2& translation); + + void Rotate(double phi, const Vector2& center); + + void Scale(double scale, const Vector2& center); + + Vector2 Evaluate(double time) const; + + BezierCurve* GetSegment(double start, double end) const; + + std::vector Render(int accuracy = 4) const; + + std::vector RenderSegment(double start, double end, int accuracy = 4) const; + + private: + std::vector controlPoints; + }; +} // namespace love diff --git a/include/objects/beziercurve/wrap_beziercurve.hpp b/include/objects/beziercurve/wrap_beziercurve.hpp new file mode 100644 index 000000000..652ea9795 --- /dev/null +++ b/include/objects/beziercurve/wrap_beziercurve.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +namespace Wrap_BezierCurve +{ + love::BezierCurve* CheckBezierCurve(lua_State* L, int index); + + int GetDegree(lua_State* L); + + int GetDerivative(lua_State* L); + + int GetControlPoint(lua_State* L); + + int SetControlPoint(lua_State* L); + + int InsertControlPoint(lua_State* L); + + int RemoveControlPoint(lua_State* L); + + int GetControlPointCount(lua_State* L); + + int Translate(lua_State* L); + + int Rotate(lua_State* L); + + int Scale(lua_State* L); + + int Evaluate(lua_State* L); + + int GetSegment(lua_State* L); + + int Render(lua_State* L); + + int RenderSegment(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_BezierCurve diff --git a/include/objects/bmfontrasterizer/bmfontrasterizer.hpp b/include/objects/bmfontrasterizer/bmfontrasterizer.hpp new file mode 100644 index 000000000..128c08291 --- /dev/null +++ b/include/objects/bmfontrasterizer/bmfontrasterizer.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include + +#include + +#include +#include + +namespace love +{ + class BMFontRasterizer : public Rasterizer + { + public: + BMFontRasterizer(FileData* fontDefinition, + const std::vector*>& imageList, float dpiScale); + + virtual ~BMFontRasterizer() + {} + + int GetLineHeight() const override; + + int GetGlyphSpacing(uint32_t glyph) const override; + + int GetGlyphIndex(uint32_t glyph) const override; + + GlyphData* GetGlyphDataForIndex(int index) const override; + + int GetGlyphCount() const override; + + bool HasGlyph(uint32_t glyph) const override; + + float GetKerning(uint32_t left, uint32_t right) const override; + + DataType GetDataType() const override; + + TextShaper* NewTextShaper() override; + + static bool Accepts(FileData* fontDefinition); + + private: + struct BMFontCharacter + { + int x; + int y; + int page; + GlyphData::GlyphMetrics metrics; + uint32_t glyph; + }; + + void ParseConfig(const std::string& config); + + std::string fontFolder; + std::unordered_map>> images; + + std::vector characters; + + std::unordered_map characterIndices; + std::unordered_map kernings; + + int fontSize; + bool unicode; + int lineHeight; + }; +} // namespace love \ No newline at end of file diff --git a/include/objects/body/body.hpp b/include/objects/body/body.hpp new file mode 100644 index 000000000..d340150c4 --- /dev/null +++ b/include/objects/body/body.hpp @@ -0,0 +1,193 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include + +namespace love +{ + class World; + class Shape; + + class Body : public Object + { + public: + static love::Type type; + + friend class Joint; + friend class DistanceJoint; + friend class MouseJoint; + friend class CircleShape; + friend class PolygonShape; + friend class Shape; + + enum Type + { + BODY_INVALID, + BODY_STATIC, + BODY_DYNAMIC, + BODY_KINEMATIC, + BODY_MAX_ENUM + }; + + Body(World* world, b2Vec2 position, Body::Type type); + + virtual ~Body(); + + float GetX(); + + float GetY(); + + float GetAngle(); + + void GetPosition(float& x, float& y); + + void GetLinearVelocity(float& x, float& y); + + void GetWorldCenter(float& x, float& y); + + void GetLocalCenter(float& x, float& y); + + float GetAngularVelocity(); + + void GetKinematicState(b2Vec2& position, float& angle, b2Vec2& velocity, + float& angularVelocity) const; + + float GetMass() const; + + float GetInertia() const; + + int GetMassData(lua_State* L); + + bool HasCustomMassData() const + { + return this->hasCustomMass; + } + + float GetAngularDamping() const; + + float GetLinearDamping() const; + + float GetGravityScale() const; + + Body::Type GetType() const; + + void ApplyLinearImpulse(float x, float y, bool wake); + + void ApplyLinearImpulse(float x, float y, float offsetX, float offsetY, bool wake); + + void ApplyAngularImpulse(float impulse, bool wake); + + void ApplyTorque(float torque, bool wake); + + void ApplyForce(float x, float y, bool wake); + + void ApplyForce(float x, float y, float offsetX, float offsetY, bool wake); + + void SetX(float x); + + void SetY(float y); + + void SetLinearVelocity(float x, float y); + + void SetAngle(float angle); + + void SetAngularVelocity(float rotation); + + void SetKinematicState(b2Vec2 position, float angle, b2Vec2 velocity, + float angularVelocity); + + void SetPosition(float x, float y); + + void ResetMassData(); + + void SetMassData(float x, float y, float mass, float inertia); + + void SetMass(float mass); + + void SetInertia(float inertia); + + void SetAngularDamping(float damping); + + void SetLinearDamping(float damping); + + void SetGravityScale(float scale); + + void SetType(Body::Type type); + + void GetWorldPoint(float x, float y, float& outX, float& outY) const; + + void GetWorldVector(float x, float y, float& outX, float& outY) const; + + int GetWorldPoints(lua_State* L); + + void GetLocalPoint(float x, float y, float& outX, float& outY) const; + + void GetLocalVector(float x, float y, float& outX, float& outY) const; + + int GetLocalPoints(lua_State* L); + + void GetLinearVelocityFromWorldPoint(float x, float y, float& outX, float& outY) const; + + void GetLinearVelocityFromLocalPoint(float x, float y, float& outX, float& outY) const; + + bool IsBullet() const; + + void SetBullet(bool bullet); + + bool IsEnabled() const; + + bool IsAwake() const; + + void SetSleepingAllowed(bool allowed); + + bool IsSleepingAllowed() const; + + void SetEnabled(bool enabled); + + void SetAwake(bool awake); + + void SetFixedRotation(bool fixed); + + bool IsFixedRotation() const; + + bool IsTouching(Body* other) const; + + World* GetWorld() const; + + Shape* GetShape() const; + + int GetShapes(lua_State* L) const; + + int GetJoints(lua_State* L) const; + + int GetContacts(lua_State* L) const; + + void Destroy(); + + int SetUserdata(lua_State* L); + + int GetUserdata(lua_State* L); + + b2Body* body; + + // clang-format off + static constexpr BidirectionalMap bodyTypes = + { + "static", BODY_STATIC, + "dynamic", BODY_DYNAMIC, + "kinematic", BODY_KINEMATIC + }; + // clang-format on + + private: + World* world; + bool hasCustomMass; + Reference* reference = nullptr; + }; +} // namespace love diff --git a/include/objects/body/wrap_body.hpp b/include/objects/body/wrap_body.hpp new file mode 100644 index 000000000..dfa33513e --- /dev/null +++ b/include/objects/body/wrap_body.hpp @@ -0,0 +1,145 @@ +#pragma once + +#include +#include + +namespace Wrap_Body +{ + love::Body* CheckBody(lua_State* L, int index); + + int GetX(lua_State* L); + + int GetY(lua_State* L); + + int GetAngle(lua_State* L); + + int GetPosition(lua_State* L); + + int GetTransform(lua_State* L); + + int GetLinearVelocity(lua_State* L); + + int GetWorldCenter(lua_State* L); + + int GetLocalCenter(lua_State* L); + + int GetAngularVelocity(lua_State* L); + + int GetKinematicState(lua_State* L); + + int GetMass(lua_State* L); + + int GetInertia(lua_State* L); + + int GetMassData(lua_State* L); + + int HasCustomMassData(lua_State* L); + + int GetAngularDamping(lua_State* L); + + int GetLinearDamping(lua_State* L); + + int GetGravityScale(lua_State* L); + + int GetType(lua_State* L); + + int ApplyLinearImpulse(lua_State* L); + + int ApplyAngularImpulse(lua_State* L); + + int ApplyTorque(lua_State* L); + + int ApplyForce(lua_State* L); + + int SetX(lua_State* L); + + int SetY(lua_State* L); + + int SetTransform(lua_State* L); + + int SetLinearVelocity(lua_State* L); + + int SetAngle(lua_State* L); + + int SetAngularVelocity(lua_State* L); + + int SetPosition(lua_State* L); + + int SetKinematicState(lua_State* L); + + int ResetMassData(lua_State* L); + + int SetMassData(lua_State* L); + + int SetMass(lua_State* L); + + int SetInertia(lua_State* L); + + int SetAngularDamping(lua_State* L); + + int SetLinearDamping(lua_State* L); + + int SetGravityScale(lua_State* L); + + int SetType(lua_State* L); + + int GetWorldPoint(lua_State* L); + + int GetWorldVector(lua_State* L); + + int GetWorldPoints(lua_State* L); + + int GetLocalPoint(lua_State* L); + + int GetLocalVector(lua_State* L); + + int GetLocalPoints(lua_State* L); + + int GetLinearVelocityFromWorldPoint(lua_State* L); + + int GetLinearVelocityFromLocalPoint(lua_State* L); + + int IsBullet(lua_State* L); + + int SetBullet(lua_State* L); + + int IsActive(lua_State* L); + + int IsAwake(lua_State* L); + + int SetSleepingAllowed(lua_State* L); + + int IsSleepingAllowed(lua_State* L); + + int SetActive(lua_State* L); + + int SetAwake(lua_State* L); + + int SetFixedRotation(lua_State* L); + + int IsFixedRotation(lua_State* L); + + int IsTouching(lua_State* L); + + int GetWorld(lua_State* L); + + int GetShape(lua_State* L); + + int GetShapes(lua_State* L); + + int GetJoints(lua_State* L); + + int GetContacts(lua_State* L); + + int Destroy(lua_State* L); + + int IsDestroyed(lua_State* L); + + int SetUserdata(lua_State* L); + + int GetUserdata(lua_State* L); + + extern const luaL_Reg bodyFunctions[0x42]; + + int Register(lua_State* L); +} // namespace Wrap_Body diff --git a/include/objects/box2d/body/body.h b/include/objects/box2d/body/body.h deleted file mode 100644 index ae1d3e26e..000000000 --- a/include/objects/box2d/body/body.h +++ /dev/null @@ -1,197 +0,0 @@ -#pragma once - -#include "objects/object.h" - -#include "common/reference.h" - -#include - -#include - -namespace love -{ - class World; - class Shape; - class Fixture; - - class Body : public Object - { - public: - static love::Type type; - - enum Type - { - BODY_INVALID, - BODY_STATIC, - BODY_DYNAMIC, - BODY_KINEMATIC, - BODY_MAX_ENUM - }; - - struct BodyUserdata - { - Reference* ref = nullptr; - }; - - friend class Joint; - friend class DistanceJoint; - friend class MouseJoint; - friend class CircleShape; - friend class PolygonShape; - friend class Shape; - friend class Fixture; - - /* box2d relevant methods */ - - Body(World* world, b2Vec2 position, Body::Type type); - - virtual ~Body(); - - float GetX(); - - float GetY(); - - float GetAngle(); - - void GetPosition(float& xPosition, float& yPosition); - - void GetLinearVelocity(float& xVelocity, float& yVelocity); - - void GetWorldCenter(float& xMassCenter, float& yMassCenter); - - void GetLocalCenter(float& xMassCenter, float& yMassCenter); - - float GetAngularVelocity() const; - - bool IsEnabled() const; - - void SetEnabled(bool enabled); - - int SetUserData(lua_State* L); - - int GetUserData(lua_State* L); - - void GetKinematicState(b2Vec2& outPosition, float& outAngle, b2Vec2& outVelocity, - float& outAngularVelocity) const; - - void SetKinematicState(b2Vec2 pos, float a, b2Vec2 vel, float da); - - float GetMass() const; - - float GetInertia() const; - - int GetMassData(lua_State* L); - - float GetAngularDamping() const; - - float GetLinearDamping() const; - - float GetGravityScale() const; - - Body::Type GetType() const; - - void ApplyLinearImpulse(float xImpulse, float yImpulse, bool wake); - - void ApplyLinearImpulse(float xImpulse, float yImpulse, float xOffset, float yOffset, - bool wake); - - void ApplyAngularImpulse(float impulse, bool wake); - - void ApplyTorque(float torque, bool wake); - - void ApplyForce(float xForce, float yForce, bool wake); - - void ApplyForce(float xForce, float yForce, float xOffset, float yOffset, bool wake); - - void SetX(float x); - - void SetY(float y); - - void SetLinearVelocity(float x, float y); - - void SetAngle(float angle); - - void SetAngularVelocity(float spin); - - void SetPosition(float x, float y); - - void ResetMassData(); - - void SetMassData(float x, float y, float mass, float inertia); - - void SetMass(float mass); - - void SetInertia(float inertia); - - void SetAngularDamping(float damping); - - void SetLinearDamping(float damping); - - void SetGravityScale(float scale); - - void SetType(Body::Type type); - - void GetWorldPoint(float x, float y, float& xWorld, float& yWorld); - - void GetWorldVector(float x, float y, float& xWorld, float& yWorld); - - int GetWorldPoints(lua_State* L); - - void GetLocalPoint(float x, float y, float& xLocal, float& yLocal); - - void GetLocalVector(float x, float y, float& xLocal, float& yLocal); - - int GetLocalPoints(lua_State* L); - - void GetLinearVelocityFromWorldPoint(float x, float y, float& xVelocity, float& yVelocity); - - void GetLinearVelocityFromLocalPoint(float x, float y, float& xVelocity, float& yVelocity); - - bool IsBullet() const; - - void SetBullet(bool bullet); - - bool IsActive() const; - - bool IsAwake() const; - - void SetSleepingAllowed(bool allowed); - - bool IsSleepingAllowed() const; - - void SetActive(bool active); - - void SetAwake(bool awake); - - void SetFixedRotation(bool fixed); - - bool IsFixedRotation() const; - - bool IsTouching(Body* other) const; - - World* GetWorld() const; - - int GetFixtures(lua_State* L) const; - - int GetJoints(lua_State* L) const; - - int GetContacts(lua_State* L) const; - - void Destroy(); - - int SetUserdata(lua_State* L); - - int GetUserdata(lua_State* L); - - /* public because joints, etc request it */ - b2Body* body; - - static bool GetConstant(const char* in, Body::Type& out); - static bool GetConstant(Body::Type in, const char*& out); - static std::vector GetConstants(Body::Type); - - private: - World* world; - BodyUserdata* userdata; - }; -} // namespace love diff --git a/include/objects/box2d/body/wrap_body.h b/include/objects/box2d/body/wrap_body.h deleted file mode 100644 index 1237ecc86..000000000 --- a/include/objects/box2d/body/wrap_body.h +++ /dev/null @@ -1,140 +0,0 @@ -#pragma once - -#include "common/luax.h" - -#include "body/body.h" - -namespace Wrap_Body -{ - int GetX(lua_State* L); - - int GetY(lua_State* L); - - int GetAngle(lua_State* L); - - int GetPosition(lua_State* L); - - int GetTransform(lua_State* L); - - int GetLinearVelocity(lua_State* L); - - int GetWorldCenter(lua_State* L); - - int GetLocalCenter(lua_State* L); - - int GetAngularVelocity(lua_State* L); - - int GetKinematicState(lua_State* L); - - int GetMass(lua_State* L); - - int GetInertia(lua_State* L); - - int GetMassData(lua_State* L); - - int GetAngularDamping(lua_State* L); - - int GetLinearDamping(lua_State* L); - - int GetGravityScale(lua_State* L); - - int GetType(lua_State* L); - - int ApplyLinearImpulse(lua_State* L); - - int ApplyAngularImpulse(lua_State* L); - - int ApplyTorque(lua_State* L); - - int ApplyForce(lua_State* L); - - int SetX(lua_State* L); - - int SetY(lua_State* L); - - int SetTransform(lua_State* L); - - int SetLinearVelocity(lua_State* L); - - int SetAngle(lua_State* L); - - int SetAngularVelocity(lua_State* L); - - int SetPosition(lua_State* L); - - int SetKinematicState(lua_State* L); - - int ResetMassData(lua_State* L); - - int SetMassData(lua_State* L); - - int SetMass(lua_State* L); - - int SetInertia(lua_State* L); - - int SetAngularDamping(lua_State* L); - - int SetLinearDamping(lua_State* L); - - int SetGravityScale(lua_State* L); - - int SetType(lua_State* L); - - int GetWorldPoint(lua_State* L); - - int GetWorldVector(lua_State* L); - - int GetWorldPoints(lua_State* L); - - int GetLocalPoint(lua_State* L); - - int GetLocalVector(lua_State* L); - - int GetLocalPoints(lua_State* L); - - int GetLinearVelocityFromWorldPoint(lua_State* L); - - int GetLinearVelocityFromLocalPoint(lua_State* L); - - int IsBullet(lua_State* L); - - int SetBullet(lua_State* L); - - int IsActive(lua_State* L); - - int IsAwake(lua_State* L); - - int SetSleepingAllowed(lua_State* L); - - int IsSleepingAllowed(lua_State* L); - - int SetActive(lua_State* L); - - int SetAwake(lua_State* L); - - int SetFixedRotation(lua_State* L); - - int IsFixedRotation(lua_State* L); - - int IsTouching(lua_State* L); - - int GetWorld(lua_State* L); - - int GetFixtures(lua_State* L); - - int GetJoints(lua_State* L); - - int GetContacts(lua_State* L); - - int Destroy(lua_State* L); - - int IsDestroyed(lua_State* L); - - int SetUserdata(lua_State* L); - - int GetUserdata(lua_State* L); - - love::Body* CheckBody(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Body diff --git a/include/objects/box2d/callbacks/contactcallback.h b/include/objects/box2d/callbacks/contactcallback.h deleted file mode 100644 index de574fa26..000000000 --- a/include/objects/box2d/callbacks/contactcallback.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "common/reference.h" - -#include - -namespace love -{ - class World; - - class ContactCallback - { - public: - Reference* ref; - lua_State* L; - World* world; - - ContactCallback(World* world); - - ~ContactCallback(); - - void Process(b2Contact* contact, const b2ContactImpulse* impulse = NULL); - }; -} // namespace love diff --git a/include/objects/box2d/callbacks/contactfilter.h b/include/objects/box2d/callbacks/contactfilter.h deleted file mode 100644 index 92292f72b..000000000 --- a/include/objects/box2d/callbacks/contactfilter.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "common/reference.h" - -namespace love -{ - class Fixture; - - class ContactFilter - { - public: - Reference* ref; - lua_State* L; - - ContactFilter(); - - ~ContactFilter(); - - bool Process(Fixture* a, Fixture* b); - }; -} // namespace love diff --git a/include/objects/box2d/callbacks/querycallback.h b/include/objects/box2d/callbacks/querycallback.h deleted file mode 100644 index a8bef3ab6..000000000 --- a/include/objects/box2d/callbacks/querycallback.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "common/reference.h" - -#include - -namespace love -{ - class World; - - class QueryCallback : public b2QueryCallback - { - public: - QueryCallback(World* world, lua_State* L, int index); - - ~QueryCallback(); - - virtual bool ReportFixture(b2Fixture* fixture); - - private: - World* world; - lua_State* L; - int functionIndex; - }; -} // namespace love diff --git a/include/objects/box2d/callbacks/raycastcallback.h b/include/objects/box2d/callbacks/raycastcallback.h deleted file mode 100644 index 2d7691d31..000000000 --- a/include/objects/box2d/callbacks/raycastcallback.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "common/reference.h" - -#include - -namespace love -{ - class World; - - class RayCastCallback : public b2RayCastCallback - { - public: - RayCastCallback(World* world, lua_State* L, int index); - - ~RayCastCallback(); - - virtual float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, - float fraction); - - private: - World* world; - lua_State* L; - int functionIndex; - }; -} // namespace love diff --git a/include/objects/box2d/chainshape/chainshape.h b/include/objects/box2d/chainshape/chainshape.h deleted file mode 100644 index b2da70095..000000000 --- a/include/objects/box2d/chainshape/chainshape.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "edgeshape/edgeshape.h" -#include "shape/shape.h" - -namespace love -{ - class ChainShape : public Shape - { - public: - static love::Type type; - - ChainShape(b2ChainShape* chainShape, bool own = true); - - virtual ~ChainShape(); - - void SetNextVertex(float x, float y); - - b2Vec2 GetNextVertex() const; - - void SetPreviousVertex(float x, float y); - - b2Vec2 GetPreviousVertex() const; - - EdgeShape* GetChildEdge(int index) const; - - int GetVertexCount() const; - - b2Vec2 GetPoint(int index) const; - - const b2Vec2* GetPoints() const; - }; -} // namespace love diff --git a/include/objects/box2d/chainshape/wrap_chainshape.h b/include/objects/box2d/chainshape/wrap_chainshape.h deleted file mode 100644 index 4a0a147a3..000000000 --- a/include/objects/box2d/chainshape/wrap_chainshape.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "common/luax.h" - -#include "chainshape/chainshape.h" - -namespace Wrap_ChainShape -{ - int SetNextVertex(lua_State* L); - - int SetPreviousVertex(lua_State* L); - - int GetChildEdge(lua_State* L); - - int GetVertexCount(lua_State* L); - - int GetPoint(lua_State* L); - - int GetNextVertex(lua_State* L); - - int GetPreviousVertex(lua_State* L); - - int GetPoints(lua_State* L); - - love::ChainShape* CheckChainShape(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_ChainShape diff --git a/include/objects/box2d/circleshape/circleshape.h b/include/objects/box2d/circleshape/circleshape.h deleted file mode 100644 index 82eee2f9d..000000000 --- a/include/objects/box2d/circleshape/circleshape.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "shape/shape.h" -#include - -namespace love -{ - class CircleShape : public Shape - { - public: - static love::Type type; - - CircleShape(b2CircleShape* circleShape, bool own = true); - - virtual ~CircleShape(); - - float GetRadius() const; - - void SetRadius(float radius); - - void GetPoint(float& xOut, float& yOut) const; - - void SetPoint(float x, float y); - }; -} // namespace love diff --git a/include/objects/box2d/circleshape/wrap_circleshape.h b/include/objects/box2d/circleshape/wrap_circleshape.h deleted file mode 100644 index bb2404a11..000000000 --- a/include/objects/box2d/circleshape/wrap_circleshape.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "circleshape/circleshape.h" -#include "common/luax.h" - -namespace Wrap_CircleShape -{ - int GetRadius(lua_State* L); - - int GetPoint(lua_State* L); - - int SetPoint(lua_State* L); - - int SetRadius(lua_State* L); - - love::CircleShape* CheckCircleShape(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_CircleShape diff --git a/include/objects/box2d/common.h b/include/objects/box2d/common.h deleted file mode 100644 index f1af8e556..000000000 --- a/include/objects/box2d/common.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "modules/physics/wrap_physics.h" - -#include "body/wrap_body.h" -#include "contact/wrap_contact.h" -#include "fixture/wrap_fixture.h" -#include "joint/wrap_joint.h" -#include "shape/wrap_shape.h" -#include "world/wrap_world.h" - -#include "chainshape/wrap_chainshape.h" -#include "circleshape/wrap_circleshape.h" -#include "edgeshape/wrap_edgeshape.h" -#include "polygonshape/wrap_polygonshape.h" - -#include "distancejoint/wrap_distancejoint.h" -#include "frictionjoint/wrap_frictionjoint.h" -#include "gearjoint/wrap_gearjoint.h" -#include "motorjoint/wrap_motorjoint.h" -#include "mousejoint/wrap_mousejoint.h" -#include "prismaticjoint/wrap_prismaticjoint.h" -#include "pulleyjoint/wrap_pulleyjoint.h" -#include "revolutejoint/wrap_revolutejoint.h" -#include "ropejoint/wrap_ropejoint.h" -#include "weldjoint/wrap_weldjoint.h" -#include "wheeljoint/wrap_wheeljoint.h" diff --git a/include/objects/box2d/contact/contact.h b/include/objects/box2d/contact/contact.h deleted file mode 100644 index 2956ba418..000000000 --- a/include/objects/box2d/contact/contact.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include "objects/object.h" -#include "world.h" - -#include "modules/physics/physics.h" - -#include - -namespace love -{ - class World; - - class Contact : public Object - { - public: - friend class World; - friend class ContactCallback; - - static love::Type type; - - Contact(World* world, b2Contact* contact); - - virtual ~Contact(); - - void Invalidate(); - - bool IsValid(); - - int GetPositions(lua_State* L); - - int GetNormal(lua_State* L); - - float GetFriction() const; - - float GetRestitution() const; - - bool IsEnabled() const; - - bool IsTouching() const; - - void SetFriction(float friction); - - void SetRestitution(float restitution); - - void SetEnabled(bool enabled); - - void ResetFriction(); - - void ResetRestitution(); - - void SetTangentSpeed(float speed); - - float GetTangentSpeed() const; - - void GetChildren(int& childA, int& childB); - - void GetFixtures(Fixture*& fixtureA, Fixture*& fixtureB); - - private: - b2Contact* contact; - World* world; - }; -} // namespace love diff --git a/include/objects/box2d/contact/wrap_contact.h b/include/objects/box2d/contact/wrap_contact.h deleted file mode 100644 index 6a9c11ab6..000000000 --- a/include/objects/box2d/contact/wrap_contact.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "common/luax.h" - -#include "contact/contact.h" - -namespace Wrap_Contact -{ - int GetPositions(lua_State* L); - - int GetNormal(lua_State* L); - - int GetFriction(lua_State* L); - - int GetRestitution(lua_State* L); - - int IsEnabled(lua_State* L); - - int IsTouching(lua_State* L); - - int SetFriction(lua_State* L); - - int SetRestitution(lua_State* L); - - int SetEnabled(lua_State* L); - - int ResetFriction(lua_State* L); - - int ResetRestitution(lua_State* L); - - int SetTangentSpeed(lua_State* L); - - int GetTangentSpeed(lua_State* L); - - int GetChildren(lua_State* L); - - int GetFixtures(lua_State* L); - - int IsDestroyed(lua_State* L); - - love::Contact* CheckContact(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Contact diff --git a/include/objects/box2d/distancejoint/distancejoint.h b/include/objects/box2d/distancejoint/distancejoint.h deleted file mode 100644 index 653099775..000000000 --- a/include/objects/box2d/distancejoint/distancejoint.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "joint/joint.h" - -namespace love -{ - class DistanceJoint : public Joint - { - public: - DistanceJoint(Body* a, Body* b, float x1, float y1, float x2, float y2, - bool collideConnected); - - virtual ~DistanceJoint(); - - void SetLength(float length); - - float GetLength() const; - - void SetFrequency(float hz); - - float GetFrequency() const; - - void SetDampingRatio(float ratio); - - float GetDampingRatio() const; - - void SetStiffness(float stiffness); - - float GetStiffness() const; - - void SetDamping(float ratio); - - float GetDamping() const; - - private: - b2DistanceJoint* joint; - }; -} // namespace love diff --git a/include/objects/box2d/distancejoint/wrap_distancejoint.h b/include/objects/box2d/distancejoint/wrap_distancejoint.h deleted file mode 100644 index 2583a36cf..000000000 --- a/include/objects/box2d/distancejoint/wrap_distancejoint.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "distancejoint/distancejoint.h" - -namespace Wrap_DistanceJoint -{ - int SetLength(lua_State* L); - - int GetLength(lua_State* L); - - int SetFrequency(lua_State* L); - - int GetFrequency(lua_State* L); - - int SetDampingRatio(lua_State* L); - - int GetDampingRatio(lua_State* L); - - int SetStiffness(lua_State* L); - - int GetStiffness(lua_State* L); - - int SetDamping(lua_State* L); - - int GetDamping(lua_State* L); - - love::DistanceJoint* CheckDistanceJoint(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_DistanceJoint diff --git a/include/objects/box2d/edgeshape/edgeshape.h b/include/objects/box2d/edgeshape/edgeshape.h deleted file mode 100644 index c092adcfc..000000000 --- a/include/objects/box2d/edgeshape/edgeshape.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "shape/shape.h" - -namespace love -{ - class EdgeShape : public Shape - { - public: - static love::Type type; - - EdgeShape(b2EdgeShape* shape, bool own = true); - - virtual ~EdgeShape(); - - void SetNextVertex(float x, float y); - - b2Vec2 GetNextVertex() const; - - void SetPreviousVertex(float x, float y); - - b2Vec2 GetPreviousVertex() const; - - int GetPoints(lua_State* L); - }; -} // namespace love diff --git a/include/objects/box2d/edgeshape/wrap_edgeshape.h b/include/objects/box2d/edgeshape/wrap_edgeshape.h deleted file mode 100644 index 1aa7a38f5..000000000 --- a/include/objects/box2d/edgeshape/wrap_edgeshape.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "edgeshape/edgeshape.h" - -namespace Wrap_EdgeShape -{ - int SetNextVertex(lua_State* L); - - int SetPreviousVertex(lua_State* L); - - int GetNextVertex(lua_State* L); - - int GetPreviousVertex(lua_State* L); - - int GetPoints(lua_State* L); - - love::EdgeShape* CheckEdgeShape(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_EdgeShape diff --git a/include/objects/box2d/fixture/fixture.h b/include/objects/box2d/fixture/fixture.h deleted file mode 100644 index 827c111ae..000000000 --- a/include/objects/box2d/fixture/fixture.h +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once - -#include "common/reference.h" -#include "objects/object.h" - -#include - -#include "shape.h" - -namespace love -{ - class World; - class Body; - - class Fixture : public Object - { - public: - friend class Physics; - friend class Shape; - - struct FixtureUserdata - { - Reference* ref = nullptr; - }; - - static love::Type type; - - Fixture(Body* body, Shape* shape, float density); - - virtual ~Fixture(); - - Shape::Type GetType(); - - Shape* GetShape(); - - bool IsValid() const; - - bool IsSensor() const; - - void SetSensor(bool sensor); - - Body* GetBody() const; - - void SetFilterData(int* filter); - - void GetFilterData(int* filter); - - int SetUserdata(lua_State* L); - - int GetUserdata(lua_State* L); - - void SetFriction(float friction); - - void SetRestitution(float restitution); - - void SetDensity(float density); - - float GetFriction() const; - - float GetRestitution() const; - - float GetDensity() const; - - bool TestPoint(float x, float y) const; - - int RayCast(lua_State* L) const; - - void SetGroupIndex(int index); - - int GetGroupIndex() const; - - int SetCategory(lua_State* L); - - int SetMask(lua_State* L); - - int GetCategory(lua_State* L); - - int GetMask(lua_State* L); - - uint16_t GetBits(lua_State* L); - - int PushBits(lua_State* L, uint16_t bits); - - int GetBoundingBox(lua_State* L) const; - - int GetMassData(lua_State* L) const; - - void Destroy(bool implicit = false); - - protected: - void CheckCreateShape(); - - Body* body; - FixtureUserdata* userdata; - b2Fixture* fixture; - - StrongReference shape; - }; -} // namespace love diff --git a/include/objects/box2d/fixture/wrap_fixture.h b/include/objects/box2d/fixture/wrap_fixture.h deleted file mode 100644 index 40072fad0..000000000 --- a/include/objects/box2d/fixture/wrap_fixture.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include "common/luax.h" - -#include "fixture.h" - -namespace Wrap_Fixture -{ - int GetType(lua_State* L); - - int SetFriction(lua_State* L); - - int SetRestitution(lua_State* L); - - int SetDensity(lua_State* L); - - int SetSensor(lua_State* L); - - int GetFriction(lua_State* L); - - int GetRestitution(lua_State* L); - - int GetDensity(lua_State* L); - - int IsSensor(lua_State* L); - - int GetBody(lua_State* L); - - int GetShape(lua_State* L); - - int TestPoint(lua_State* L); - - int RayCast(lua_State* L); - - int SetFilterData(lua_State* L); - - int GetFilterData(lua_State* L); - - int SetCategory(lua_State* L); - - int GetCategory(lua_State* L); - - int SetMask(lua_State* L); - - int GetMask(lua_State* L); - - int SetUserdata(lua_State* L); - - int GetUserdata(lua_State* L); - - int GetBoundingBox(lua_State* L); - - int GetMassData(lua_State* L); - - int GetGroupIndex(lua_State* L); - - int SetGroupIndex(lua_State* L); - - int Destroy(lua_State* L); - - int IsDestroyed(lua_State* L); - - love::Fixture* CheckFixture(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Fixture diff --git a/include/objects/box2d/frictionjoint/frictionjoint.h b/include/objects/box2d/frictionjoint/frictionjoint.h deleted file mode 100644 index 389ce9df3..000000000 --- a/include/objects/box2d/frictionjoint/frictionjoint.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "joint/joint.h" - -namespace love -{ - class FrictionJoint : public Joint - { - public: - static love::Type type; - - FrictionJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, - bool collideConnected); - - virtual ~FrictionJoint(); - - void SetMaxForce(float force); - - float GetMaxForce() const; - - void SetMaxTorque(float torque); - - float GetMaxTorque() const; - - private: - b2FrictionJoint* joint; - }; -} // namespace love diff --git a/include/objects/box2d/frictionjoint/wrap_frictionjoint.h b/include/objects/box2d/frictionjoint/wrap_frictionjoint.h deleted file mode 100644 index a07b0096b..000000000 --- a/include/objects/box2d/frictionjoint/wrap_frictionjoint.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "frictionjoint/frictionjoint.h" - -namespace Wrap_FrictionJoint -{ - int SetMaxForce(lua_State* L); - - int GetMaxForce(lua_State* L); - - int SetMaxTorque(lua_State* L); - - int GetMaxTorque(lua_State* L); - - love::FrictionJoint* CheckFrictionJoint(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_FrictionJoint diff --git a/include/objects/box2d/gearjoint/gearjoint.h b/include/objects/box2d/gearjoint/gearjoint.h deleted file mode 100644 index cf34fa69c..000000000 --- a/include/objects/box2d/gearjoint/gearjoint.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "joint/joint.h" - -namespace love -{ - class GearJoint : public Joint - { - public: - static love::Type type; - - GearJoint(Joint* a, Joint* b, float ratio, bool collideConnected); - - virtual ~GearJoint(); - - void SetRatio(float ratio); - - float GetRatio() const; - - Joint* GetJointA() const; - - Joint* GetJointB() const; - - private: - b2GearJoint* joint; - }; -} // namespace love diff --git a/include/objects/box2d/gearjoint/wrap_gearjoint.h b/include/objects/box2d/gearjoint/wrap_gearjoint.h deleted file mode 100644 index edcd08bfe..000000000 --- a/include/objects/box2d/gearjoint/wrap_gearjoint.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "gearjoint/gearjoint.h" - -namespace Wrap_GearJoint -{ - int SetRatio(lua_State* L); - - int GetRatio(lua_State* L); - - int GetJoints(lua_State* L); - - love::GearJoint* CheckGearJoint(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_GearJoint diff --git a/include/objects/box2d/joint/joint.h b/include/objects/box2d/joint/joint.h deleted file mode 100644 index 89373a0c5..000000000 --- a/include/objects/box2d/joint/joint.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include "common/luax.h" - -#include "common/reference.h" -#include "objects/object.h" - -#include - -#include - -namespace love -{ - class Body; - class World; - - class Joint : public Object - { - public: - static love::Type type; - - enum Type - { - JOINT_INVALID, - JOINT_DISTANCE, - JOINT_REVOLUTE, - JOINT_PRISMATIC, - JOINT_MOUSE, - JOINT_PULLEY, - JOINT_GEAR, - JOINT_FRICTION, - JOINT_WELD, - JOINT_WHEEL, - JOINT_ROPE, - JOINT_MOTOR, - JOINT_MAX_ENUM - }; - - struct JointUserdata - { - Reference* ref = nullptr; - }; - - friend class GearJoint; - - Joint(Body* body); - - Joint(Body* a, Body* b); - - virtual ~Joint(); - - bool IsValid() const; - - Joint::Type GetType() const; - - virtual Body* GetBodyA() const; - - virtual Body* GetBodyB() const; - - int GetAnchors(lua_State* L); - - int GetReactionForce(lua_State* L); - - float GetReactionTorque(float delta); - - bool IsEnabled() const; - - bool GetCollideConnected() const; - - int SetUserdata(lua_State* L); - - int GetUserdata(lua_State* L); - - void DestroyJoint(bool implicit = false); - - static bool GetConstant(const char* in, Joint::Type& out); - static bool GetConstant(Joint::Type in, const char*& out); - std::vector GetConstants(Joint::Type); - - protected: - b2Joint* CreateJoint(b2JointDef* jointDefinition); - World* world; - JointUserdata* userdata; - - private: - Body* bodyA; - Body* bodyB; - - b2Joint* joint; - }; -} // namespace love diff --git a/include/objects/box2d/joint/wrap_joint.h b/include/objects/box2d/joint/wrap_joint.h deleted file mode 100644 index 914481839..000000000 --- a/include/objects/box2d/joint/wrap_joint.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "joint/joint.h" - -namespace Wrap_Joint // ( ͡° ͜ʖ ͡°) -{ - void PushJoint(lua_State* L, love::Joint* joint); - - love::Joint* CheckJoint(lua_State* L, int index); - - int GetType(lua_State* L); - - int GetBodies(lua_State* L); - - int GetAnchors(lua_State* L); - - int GetReactionForce(lua_State* L); - - int GetReactionTorque(lua_State* L); - - int GetCollideConnected(lua_State* L); - - int SetUserdata(lua_State* L); - - int GetUserdata(lua_State* L); - - int Destroy(lua_State* L); - - int IsDestroyed(lua_State* L); - - int Register(lua_State* L); - - extern const luaL_Reg functions[11]; -} // namespace Wrap_Joint diff --git a/include/objects/box2d/motorjoint/motorjoint.h b/include/objects/box2d/motorjoint/motorjoint.h deleted file mode 100644 index a3e18df61..000000000 --- a/include/objects/box2d/motorjoint/motorjoint.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "joint/joint.h" - -namespace love -{ - class MotorJoint : public Joint - { - public: - static love::Type type; - - MotorJoint(Body* a, Body* b); - - MotorJoint(Body* a, Body* b, float correctionFactor, bool collideConnected); - - virtual ~MotorJoint(); - - void SetLinearOffset(float x, float y); - - int GetLinearOffset(lua_State* L) const; - - void SetAngularOffset(float offset); - - float GetAngularOffset() const; - - void SetMaxForce(float force); - - float GetMaxForce() const; - - void SetMaxTorque(float torque); - - float GetMaxTorque() const; - - void SetCorrectionFactor(float factor); - - float GetCorrectionFactor() const; - - private: - b2MotorJoint* joint; - }; -} // namespace love diff --git a/include/objects/box2d/motorjoint/wrap_motorjoint.h b/include/objects/box2d/motorjoint/wrap_motorjoint.h deleted file mode 100644 index 1a00a02e6..000000000 --- a/include/objects/box2d/motorjoint/wrap_motorjoint.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "motorjoint/motorjoint.h" - -namespace Wrap_MotorJoint -{ - love::MotorJoint* CheckMotorJoint(lua_State* L, int index); - - int SetLinearOffset(lua_State* L); - - int GetLinearOffset(lua_State* L); - - int SetAngularOffset(lua_State* L); - - int GetAngularOffset(lua_State* L); - - int SetMaxForce(lua_State* L); - - int GetMaxForce(lua_State* L); - - int SetMaxTorque(lua_State* L); - - int GetMaxTorque(lua_State* L); - - int SetCorrectionFactor(lua_State* L); - - int GetCorrectionFactor(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_MotorJoint diff --git a/include/objects/box2d/mousejoint/mousejoint.h b/include/objects/box2d/mousejoint/mousejoint.h deleted file mode 100644 index 60068c32b..000000000 --- a/include/objects/box2d/mousejoint/mousejoint.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "joint/joint.h" - -namespace love -{ - class MouseJoint : public Joint - { - public: - static love::Type type; - - MouseJoint(Body* a, float x, float y); - - virtual ~MouseJoint(); - - void SetTarget(float x, float y); - - int GetTarget(lua_State* L); - - void SetMaxForce(float force); - - float GetMaxForce() const; - - void SetFrequency(float hz); - - float GetFrequency() const; - - void SetDampingRatio(float ratio); - - float GetDampingRatio() const; - - void SetStiffness(float stiffness); - - float GetStiffness() const; - - void SetDamping(float ratio); - - float GetDamping() const; - - virtual Body* GetBodyA() const; - - virtual Body* GetBodyB() const; - - private: - b2MouseJoint* joint; - }; -} // namespace love diff --git a/include/objects/box2d/mousejoint/wrap_mousejoint.h b/include/objects/box2d/mousejoint/wrap_mousejoint.h deleted file mode 100644 index 01740c00c..000000000 --- a/include/objects/box2d/mousejoint/wrap_mousejoint.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "mousejoint/mousejoint.h" - -namespace Wrap_MouseJoint -{ - int SetTarget(lua_State* L); - - int GetTarget(lua_State* L); - - int SetMaxForce(lua_State* L); - - int GetMaxForce(lua_State* L); - - int SetFrequency(lua_State* L); - - int GetFrequency(lua_State* L); - - int SetDampingRatio(lua_State* L); - - int GetDampingRatio(lua_State* L); - - int SetStiffness(lua_State* L); - - int GetStiffness(lua_State* L); - - int SetDamping(lua_State* L); - - int GetDamping(lua_State* L); - - love::MouseJoint* CheckMouseJoint(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_MouseJoint diff --git a/include/objects/box2d/polygonshape/polygonshape.h b/include/objects/box2d/polygonshape/polygonshape.h deleted file mode 100644 index c579dc3d5..000000000 --- a/include/objects/box2d/polygonshape/polygonshape.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "shape/shape.h" - -namespace love -{ - class PolygonShape : public Shape - { - public: - static love::Type type; - - PolygonShape(b2PolygonShape* polygonShape, bool own = true); - - virtual ~PolygonShape(); - - int GetPoints(lua_State* L); - - bool Validate() const; - }; -} // namespace love diff --git a/include/objects/box2d/polygonshape/wrap_polygonshape.h b/include/objects/box2d/polygonshape/wrap_polygonshape.h deleted file mode 100644 index 99f0936c5..000000000 --- a/include/objects/box2d/polygonshape/wrap_polygonshape.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "polygonshape/polygonshape.h" - -namespace Wrap_PolygonShape -{ - love::PolygonShape* CheckPolygonShape(lua_State* L, int index); - - int GetPoints(lua_State* L); - - int Validate(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_PolygonShape diff --git a/include/objects/box2d/prismaticjoint/prismaticjoint.h b/include/objects/box2d/prismaticjoint/prismaticjoint.h deleted file mode 100644 index b7281e15d..000000000 --- a/include/objects/box2d/prismaticjoint/prismaticjoint.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include "joint/joint.h" - -namespace love -{ - class PrismaticJoint : public Joint - { - public: - static love::Type type; - - PrismaticJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, float xAnchor, - float yAnchor, bool collideConnected); - - PrismaticJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, float xAnchor, - float yAnchor, bool collideConnected, float referenceAngle); - - virtual ~PrismaticJoint(); - - float GetJointTranslation() const; - - float GetJointSpeed() const; - - void SetMotorEnabled(bool enable); - - bool IsMotorEnabled() const; - - void SetMaxMotorForce(float force); - - float GetMaxMotorForce() const; - - float GetMotorForce(float invDt) const; - - void SetMotorSpeed(float speed); - - float GetMotorSpeed() const; - - void SetLimitsEnabled(bool enable); - - bool AreLimitsEnabled() const; - - void SetUpperLimit(float limit); - - float GetUpperLimit() const; - - void SetLowerLimit(float limit); - - float GetLowerLimit() const; - - void SetLimits(float lower, float upeper); - - int GetLimits(lua_State* L); - - int GetAxis(lua_State* L); - - float GetReferenceAngle() const; - - private: - b2PrismaticJoint* joint; - - void Initialize(b2PrismaticJointDef& def, Body* body1, Body* body2, float xA, float yA, - float xB, float yB, float ax, float ay, bool collideConnected); - }; -} // namespace love diff --git a/include/objects/box2d/prismaticjoint/wrap_prismaticjoint.h b/include/objects/box2d/prismaticjoint/wrap_prismaticjoint.h deleted file mode 100644 index 3509c1a38..000000000 --- a/include/objects/box2d/prismaticjoint/wrap_prismaticjoint.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "prismaticjoint/prismaticjoint.h" - -namespace Wrap_PrismaticJoint -{ - int GetJointTranslation(lua_State* L); - - int GetJointSpeed(lua_State* L); - - int SetMotorEnabled(lua_State* L); - - int IsMotorEnabled(lua_State* L); - - int SetMaxMotorForce(lua_State* L); - - int SetMotorSpeed(lua_State* L); - - int GetMotorSpeed(lua_State* L); - - int GetMotorForce(lua_State* L); - - int GetMaxMotorForce(lua_State* L); - - int SetLimitsEnabled(lua_State* L); - - int AreLimitsEnabled(lua_State* L); - - int SetUpperLimit(lua_State* L); - - int SetLowerLimit(lua_State* L); - - int SetLimits(lua_State* L); - - int GetLowerLimit(lua_State* L); - - int GetUpperLimit(lua_State* L); - - int GetLimits(lua_State* L); - - int GetAxis(lua_State* L); - - int GetReferenceAngle(lua_State* L); - - love::PrismaticJoint* CheckPrismaticJoint(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_PrismaticJoint diff --git a/include/objects/box2d/pulleyjoint/pulleyjoint.h b/include/objects/box2d/pulleyjoint/pulleyjoint.h deleted file mode 100644 index f52ee6e0f..000000000 --- a/include/objects/box2d/pulleyjoint/pulleyjoint.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "joint/joint.h" - -namespace love -{ - class PulleyJoint : public Joint - { - public: - static love::Type type; - - PulleyJoint(Body* a, Body* b, b2Vec2 groundAnchorA, b2Vec2 groundAnchorB, b2Vec2 anchorA, - b2Vec2 anchorB, float ratio, bool collideConnected); - - virtual ~PulleyJoint(); - - int GetGroundAnchors(lua_State* L); - - float GetLengthA() const; - - float GetLengthB() const; - - float GetRatio() const; - - private: - b2PulleyJoint* joint; - }; -} // namespace love diff --git a/include/objects/box2d/pulleyjoint/wrap_pulleyjoint.h b/include/objects/box2d/pulleyjoint/wrap_pulleyjoint.h deleted file mode 100644 index 8e3b37282..000000000 --- a/include/objects/box2d/pulleyjoint/wrap_pulleyjoint.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "pulleyjoint/pulleyjoint.h" - -namespace Wrap_PulleyJoint -{ - int GetGroundAnchors(lua_State* L); - - int GetLengthA(lua_State* L); - - int GetLengthB(lua_State* L); - - int GetRatio(lua_State* L); - - love::PulleyJoint* CheckPulleyJoint(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_PulleyJoint diff --git a/include/objects/box2d/revolutejoint/revolutejoint.h b/include/objects/box2d/revolutejoint/revolutejoint.h deleted file mode 100644 index e9bfe33a8..000000000 --- a/include/objects/box2d/revolutejoint/revolutejoint.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include "joint/joint.h" - -namespace love -{ - class RevoluteJoint : public Joint - { - public: - static love::Type type; - - RevoluteJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, - bool collideConnected); - - RevoluteJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, - bool collideConnected, float referenceAngle); - - virtual ~RevoluteJoint(); - - float GetJointAngle() const; - - float GetJointSpeed() const; - - void SetMotorEnabled(bool enable); - - bool IsMotorEnabled() const; - - void SetMaxMotorTorque(float torque); - - void SetMotorSpeed(float speed); - - float GetMotorSpeed() const; - - float GetMotorTorque(float invDt) const; - - float GetMaxMotorTorque() const; - - void SetLimitsEnabled(bool enable); - - bool AreLimitsEnabled() const; - - void SetUpperLimit(float limit); - - float GetUpperLimit() const; - - void SetLowerLimit(float limit); - - float GetLowerLimit() const; - - void SetLimits(float lower, float upeper); - - int GetLimits(lua_State* L); - - float GetReferenceAngle() const; - - private: - b2RevoluteJoint* joint; - - void Initialize(b2RevoluteJointDef& definitio, Body* a, Body* b, float xA, float yA, - float xB, float yB, bool collideConnected); - }; -} // namespace love diff --git a/include/objects/box2d/revolutejoint/wrap_revolutejoint.h b/include/objects/box2d/revolutejoint/wrap_revolutejoint.h deleted file mode 100644 index 01e7ee22b..000000000 --- a/include/objects/box2d/revolutejoint/wrap_revolutejoint.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "revolutejoint/revolutejoint.h" - -namespace Wrap_RevoluteJoint -{ - int GetJointAngle(lua_State* L); - - int GetJointSpeed(lua_State* L); - - int SetMotorEnabled(lua_State* L); - - int IsMotorEnabled(lua_State* L); - - int SetMaxMotorTorque(lua_State* L); - - int SetMotorSpeed(lua_State* L); - - int GetMotorSpeed(lua_State* L); - - int GetMotorTorque(lua_State* L); - - int GetMaxMotorTorque(lua_State* L); - - int SetLimitsEnabled(lua_State* L); - - int AreLimitsEnabled(lua_State* L); - - int SetUpperLimit(lua_State* L); - - int SetLowerLimit(lua_State* L); - - int SetLimits(lua_State* L); - - int GetLowerLimit(lua_State* L); - - int GetUpperLimit(lua_State* L); - - int GetLimits(lua_State* L); - - int GetReferenceAngle(lua_State* L); - - love::RevoluteJoint* CheckRevoluteJoint(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_RevoluteJoint diff --git a/include/objects/box2d/ropejoint/ropejoint.h b/include/objects/box2d/ropejoint/ropejoint.h deleted file mode 100644 index 1cc810e02..000000000 --- a/include/objects/box2d/ropejoint/ropejoint.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "joint/joint.h" - -namespace love -{ - class RopeJoint : public Joint - { - public: - static love::Type type; - - RopeJoint(Body* a, Body* b, float x1, float y1, float x2, float y2, float maxLength, - bool collideConnected); - - virtual ~RopeJoint(); - - float GetMaxLength() const; - - void SetMaxLength(float length); - - private: - b2DistanceJoint* joint; - }; -} // namespace love diff --git a/include/objects/box2d/ropejoint/wrap_ropejoint.h b/include/objects/box2d/ropejoint/wrap_ropejoint.h deleted file mode 100644 index 4c8f12092..000000000 --- a/include/objects/box2d/ropejoint/wrap_ropejoint.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "ropejoint/ropejoint.h" - -namespace Wrap_RopeJoint -{ - int GetMaxLength(lua_State* L); - - int SetMaxLength(lua_State* L); - - love::RopeJoint* CheckRopeJoint(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_RopeJoint diff --git a/include/objects/box2d/shape/shape.h b/include/objects/box2d/shape/shape.h deleted file mode 100644 index 2e1a02992..000000000 --- a/include/objects/box2d/shape/shape.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include "common/luax.h" - -#include "objects/object.h" - -#include - -#include - -namespace love -{ - class Shape : public Object - { - public: - static love::Type type; - - enum Type - { - SHAPE_INVALID, - SHAPE_CIRCLE, - SHAPE_POLYGON, - SHAPE_EDGE, - SHAPE_CHAIN, - SHAPE_MAX_ENUM - }; - - friend class Fixture; - - Shape(); - - Shape(b2Shape* shape, bool own = true); - - virtual ~Shape(); - - Shape::Type GetType() const; - - float GetRadius() const; - - int GetChildCount() const; - - bool TestPoint(float x, float y, float radius, float px, float py) const; - - int RayCast(lua_State* L) const; - - int ComputeAABB(lua_State* L) const; - - int ComputeMass(lua_State* L) const; - - static bool GetConstant(const char* in, Shape::Type& out); - static bool GetConstant(Shape::Type in, const char*& out); - static std::vector GetConstants(Shape::Type); - - protected: - b2Shape* shape; - bool own; - }; -} // namespace love diff --git a/include/objects/box2d/shape/wrap_shape.h b/include/objects/box2d/shape/wrap_shape.h deleted file mode 100644 index eaf8bae38..000000000 --- a/include/objects/box2d/shape/wrap_shape.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "shape.h" - -namespace Wrap_Shape -{ - int GetType(lua_State* L); - - int GetRadius(lua_State* L); - - int GetChildCount(lua_State* L); - - int TestPoint(lua_State* L); - - int RayCast(lua_State* L); - - int ComputeAABB(lua_State* L); - - int ComputeMass(lua_State* L); - - love::Shape* CheckShape(lua_State* L, int index); - - int Register(lua_State* L); - - extern const luaL_Reg functions[8]; -} // namespace Wrap_Shape diff --git a/include/objects/box2d/weldjoint/weldjoint.h b/include/objects/box2d/weldjoint/weldjoint.h deleted file mode 100644 index 2470a4c24..000000000 --- a/include/objects/box2d/weldjoint/weldjoint.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "joint/joint.h" - -namespace love -{ - class WeldJoint : public Joint - { - public: - static love::Type type; - - WeldJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, bool collideConnected); - - WeldJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, bool collideConnected, - float referenceAngle); - - virtual ~WeldJoint(); - - void SetFrequency(float hz); - - float GetFrequency() const; - - void SetDampingRatio(float ratio); - - float GetDampingRatio() const; - - void SetStiffness(float stiffness); - - float GetStiffness() const; - - void SetDamping(float damping); - - float GetDamping() const; - - float GetReferenceAngle() const; - - private: - b2WeldJoint* joint; - - void Initialize(b2WeldJointDef& definition, Body* a, Body* b, float xA, float yA, float xB, - float yB, bool collideConnected); - }; -} // namespace love diff --git a/include/objects/box2d/weldjoint/wrap_weldjoint.h b/include/objects/box2d/weldjoint/wrap_weldjoint.h deleted file mode 100644 index f3580d3c4..000000000 --- a/include/objects/box2d/weldjoint/wrap_weldjoint.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "weldjoint/weldjoint.h" - -namespace Wrap_WeldJoint -{ - int SetFrequency(lua_State* L); - - int GetFrequency(lua_State* L); - - int SetDampingRatio(lua_State* L); - - int GetDampingRatio(lua_State* L); - - int SetStiffness(lua_State* L); - - int GetStiffness(lua_State* L); - - int SetDamping(lua_State* L); - - int GetDamping(lua_State* L); - - int GetReferenceAngle(lua_State* L); - - love::WeldJoint* CheckWeldJoint(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_WeldJoint diff --git a/include/objects/box2d/wheeljoint/wheeljoint.h b/include/objects/box2d/wheeljoint/wheeljoint.h deleted file mode 100644 index 44828d695..000000000 --- a/include/objects/box2d/wheeljoint/wheeljoint.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include "joint/joint.h" - -namespace love -{ - class WheelJoint : public Joint - { - public: - static love::Type type; - - WheelJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, float ax, float ay, - bool collideConnected); - - virtual ~WheelJoint(); - - float GetJointTranslation() const; - - float GetJointSpeed() const; - - void SetMotorEnabled(bool enable); - - bool IsMotorEnabled() const; - - void SetMotorSpeed(float speed); - - float GetMotorSpeed() const; - - void SetMaxMotorTorque(float torque); - - float GetMaxMotorTorque() const; - - float GetMotorTorque(float invDt) const; - - void SetFrequency(float hz); - - float GetFrequency() const; - - void SetDampingRatio(float ratio); - - float GetDampingRatio() const; - - void SetStiffness(float stiffness); - - float GetStiffness() const; - - void SetDamping(float damping); - - float GetDamping() const; - - int GetAxis(lua_State* L); - - private: - b2WheelJoint* joint; - }; -} // namespace love diff --git a/include/objects/box2d/wheeljoint/wrap_wheeljoint.h b/include/objects/box2d/wheeljoint/wrap_wheeljoint.h deleted file mode 100644 index 8028c20e4..000000000 --- a/include/objects/box2d/wheeljoint/wrap_wheeljoint.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "wheeljoint/wheeljoint.h" - -namespace Wrap_WheelJoint -{ - int GetJointTranslation(lua_State* L); - - int GetJointSpeed(lua_State* L); - - int SetMotorEnabled(lua_State* L); - - int IsMotorEnabled(lua_State* L); - - int SetMotorSpeed(lua_State* L); - - int GetMotorSpeed(lua_State* L); - - int SetMaxMotorTorque(lua_State* L); - - int GetMaxMotorTorque(lua_State* L); - - int GetMotorTorque(lua_State* L); - - int SetFrequency(lua_State* L); - - int GetFrequency(lua_State* L); - - int SetDampingRatio(lua_State* L); - - int GetDampingRatio(lua_State* L); - - int SetStiffness(lua_State* L); - - int GetStiffness(lua_State* L); - - int SetDamping(lua_State* L); - - int GetDamping(lua_State* L); - - int GetAxis(lua_State* L); - - love::WheelJoint* CheckWheelJoint(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_WheelJoint diff --git a/include/objects/box2d/world/world.h b/include/objects/box2d/world/world.h deleted file mode 100644 index 23a2913ab..000000000 --- a/include/objects/box2d/world/world.h +++ /dev/null @@ -1,127 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "common/reference.h" -#include "objects/object.h" - -#include -#include - -#include "contactcallback.h" -#include "contactfilter.h" -#include "querycallback.h" -#include "raycastcallback.h" - -#include - -namespace love -{ - class Contact; - class Body; - class Fixture; - class Joint; - - class World : public Object, - public b2ContactListener, - public b2ContactFilter, - public b2DestructionListener - { - public: - friend class Joint; - friend class DistanceJoint; - friend class MouseJoint; - friend class Body; - friend class Fixture; - - static love::Type type; - - World(); - - World(b2Vec2 gravity, bool sleep); - - virtual ~World(); - - void Update(float dt); - - void Update(float dt, int velocityIterations, int positionIterations); - - void BeginContact(b2Contact* contact); - - void EndContact(b2Contact* contact); - - void PreSolve(b2Contact* contact, const b2Manifold* oldManifold); - - void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse); - - bool ShouldCollide(b2Fixture* a, b2Fixture* b); - - void SayGoodbye(b2Fixture* fixture); - - void SayGoodbye(b2Joint* joint); - - bool IsValid() const; - - int SetCallbacks(lua_State* L); - - int GetCallbacks(lua_State* L); - - void SetLuaCallbacks(lua_State* L); - - int SetContactFilter(lua_State* L); - - int GetContactFilter(lua_State* L); - - void SetGravity(float x, float y); - - int GetGravity(lua_State* L); - - void TranslateOrigin(float x, float y); - - void SetSleepingAllowed(bool allow); - - bool IsSleepingAllowed() const; - - bool IsLocked() const; - - int GetBodyCount() const; - - int GetJointCount() const; - - int GetContactCount() const; - - int GetBodies(lua_State* L) const; - - int GetJoints(lua_State* L) const; - - int GetContacts(lua_State* L); - - b2Body* GetGroundBody() const; - - int QueryBoundingBox(lua_State* L); - - int RayCast(lua_State* L); - - void Destroy(); - - void RegisterObject(void* b2Object, love::Object* object); - - void UnRegisterObject(void* b2Object); - - love::Object* FindObject(void* b2Object) const; - - private: - b2World* world; - b2Body* groundBody; - - std::vector destructBodies; - std::vector destructFixtures; - std::vector destructJoints; - - bool destructWorld; - - ContactCallback begin, end, presolve, postsolve; - ContactFilter filter; - - std::unordered_map box2dObjectMap; - }; -} // namespace love diff --git a/include/objects/box2d/world/wrap_world.h b/include/objects/box2d/world/wrap_world.h deleted file mode 100644 index a04100772..000000000 --- a/include/objects/box2d/world/wrap_world.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "world/world.h" - -namespace Wrap_World -{ - int Update(lua_State* L); - - int SetCallbacks(lua_State* L); - - int GetCallbacks(lua_State* L); - - int SetContactFilter(lua_State* L); - - int GetContactFilter(lua_State* L); - - int SetGravity(lua_State* L); - - int GetGravity(lua_State* L); - - int TranslateOrigin(lua_State* L); - - int SetSleepingAllowed(lua_State* L); - - int IsSleepingAllowed(lua_State* L); - - int IsLocked(lua_State* L); - - int GetBodyCount(lua_State* L); - - int GetJointCount(lua_State* L); - - int GetContactCount(lua_State* L); - - int GetBodies(lua_State* L); - - int GetJoints(lua_State* L); - - int GetContacts(lua_State* L); - - int QueryBoundingBox(lua_State* L); - - int RayCast(lua_State* L); - - int Destroy(lua_State* L); - - int IsDestroyed(lua_State* L); - - love::World* CheckWorld(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_World diff --git a/include/objects/canvas/canvasc.h b/include/objects/canvas/canvasc.h deleted file mode 100644 index 5d2faf1f2..000000000 --- a/include/objects/canvas/canvasc.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#if defined(__3DS__) - #include -#elif defined(__SWITCH__) - #include -#endif - -#include "objects/texture/texture.h" - -namespace love -{ - class Graphics; - - namespace common - { - class Canvas : public love::Texture - { - public: - static love::Type type; - - struct Settings - { - int width = 1; - int height = 1; - }; - - Canvas(const Settings& settings); - - virtual void Draw(Graphics* gfx, love::Quad* quad, const Matrix4& localTransform) = 0; - - bool HasFirstClear() - { - return this->cleared; - } - - protected: - Settings settings; - bool cleared; - }; - } // namespace common -} // namespace love diff --git a/include/objects/canvas/wrap_canvas.h b/include/objects/canvas/wrap_canvas.h deleted file mode 100644 index 2b003759d..000000000 --- a/include/objects/canvas/wrap_canvas.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "objects/canvas/canvas.h" -#include "objects/texture/wrap_texture.h" - -#include "common/luax.h" - -namespace Wrap_Canvas -{ - int RenderTo(lua_State* L); - - love::Canvas* CheckCanvas(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Canvas diff --git a/include/objects/channel/channel.h b/include/objects/channel/channel.h deleted file mode 100644 index 730e526b8..000000000 --- a/include/objects/channel/channel.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "common/variant.h" -#include "objects/object.h" - -#include "modules/thread/types/conditional.h" -#include "modules/thread/types/lock.h" -#include "modules/thread/types/mutex.h" - -#include - -namespace love -{ - class Channel : public Object - { - // for the Wrapper - friend int Wrap_Channel_PerformAtomic(lua_State*); - - public: - static love::Type type; - - Channel(); - - virtual ~Channel(); - - uint64_t Push(const Variant& variant); - - // Blocking push - bool Supply(const Variant& variant); - - bool Supply(const Variant& variant, double timeout); - - bool Pop(Variant* variant); - - // Blocking pop - bool Demand(Variant* variant); - - bool Demand(Variant* variant, double timeout); - - bool Peek(Variant* variant); - - int GetCount() const; - - bool HasRead(uint64_t id) const; - - void Clear(); - - private: - void LockMutex(); - void UnlockMutex(); - bool _Pop(Variant* variant); - uint64_t _Push(const Variant& variant); - - thread::MutexRef mutex; - thread::ConditionalRef condition; - std::queue queue; - - uint64_t sent; - uint64_t received; - }; - - int Wrap_Channel_PerformAtomic(lua_State*); -} // namespace love diff --git a/include/objects/channel/channel.hpp b/include/objects/channel/channel.hpp new file mode 100644 index 000000000..bf6b0bd2b --- /dev/null +++ b/include/objects/channel/channel.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace love +{ + + class Channel : public Object + { + public: + static Type type; + + Channel(); + + virtual ~Channel() + {} + + uint64_t Push(const Variant& variant); + + bool Supply(const Variant& variant); + + bool Supply(const Variant& variant, double timeout); + + bool Pop(Variant* variant); + + bool Demand(Variant* variant); + + bool Demand(Variant* variant, double timeout); + + bool Peek(Variant* variant); + + bool HasRead(uint64_t id); + + int GetCount(); + + void Clear(); + + void LockMutex(); + + void UnlockMutex(); + + private: + uint64_t _Push(const Variant& variant); + + bool _Pop(Variant* variant); + + uint64_t sent; + uint64_t received; + + love::mutex mutex; + love::conditional condition; + + std::queue queue; + }; +} // namespace love diff --git a/include/objects/channel/wrap_channel.h b/include/objects/channel/wrap_channel.h deleted file mode 100644 index b0d02dab0..000000000 --- a/include/objects/channel/wrap_channel.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/channel/channel.h" - -namespace Wrap_Channel -{ - int Push(lua_State* L); - - int Supply(lua_State* L); - - int Pop(lua_State* L); - - int Demand(lua_State* L); - - int Peek(lua_State* L); - - int GetCount(lua_State* L); - - int HasRead(lua_State* L); - - int Clear(lua_State* L); - - love::Channel* CheckChannel(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Channel diff --git a/include/objects/channel/wrap_channel.hpp b/include/objects/channel/wrap_channel.hpp new file mode 100644 index 000000000..e17afa829 --- /dev/null +++ b/include/objects/channel/wrap_channel.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +namespace Wrap_Channel +{ + int Push(lua_State* L); + + int Supply(lua_State* L); + + int Pop(lua_State* L); + + int Demand(lua_State* L); + + int Peek(lua_State* L); + + int GetCount(lua_State* L); + + int HasRead(lua_State* L); + + int GetCount(lua_State* L); + + int PerformAtomic(lua_State* L); + + int Clear(lua_State* L); + + love::Channel* CheckChannel(lua_State* L, int index); + + int Register(lua_State* L); +} // namespace Wrap_Channel diff --git a/include/objects/compressedimagedata/compressedimagedata.h b/include/objects/compressedimagedata/compressedimagedata.h deleted file mode 100644 index 8d03a7961..000000000 --- a/include/objects/compressedimagedata/compressedimagedata.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include "common/data.h" -#include "common/pixelformat.h" - -#include "objects/compressedimagedata/types/compressedslice.h" -#include "objects/imagedata/types/formathandler.h" - -#include -#include - -namespace love -{ - class CompressedImageData : public Data - { - public: - static love::Type type; - - CompressedImageData(const std::list& formats, Data* fileData); - - CompressedImageData(const CompressedImageData& other); - - virtual ~CompressedImageData(); - - CompressedImageData* Clone() const override; - - void* GetData() const override; - - size_t GetSize() const override; - - int GetMipmapCount() const; - - int GetSliceCount(int mip = 0) const; - - size_t GetSize(int mipLevel) const; - - void* GetData(int mipLevel) const; - - int GetWidth(int mipLevel = 0) const; - - int GetHeight(int mipLevel = 0) const; - - PixelFormat GetFormat() const; - - bool IsSRGB() const; - - CompressedSlice* GetSlice(int slice, int mipLevel) const; - - protected: - PixelFormat format; - bool sRGB; - - StrongReference memory; - std::vector> images; - - void CheckSliceExists(int slice, int mipLevel) const; - }; -} // namespace love diff --git a/include/objects/compressedimagedata/compressedimagedata.hpp b/include/objects/compressedimagedata/compressedimagedata.hpp new file mode 100644 index 000000000..4a49ba5c2 --- /dev/null +++ b/include/objects/compressedimagedata/compressedimagedata.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +#include +#include + +#include +#include + +namespace love +{ + class CompressedImageData : public Data + { + public: + static Type type; + + CompressedImageData(const std::list& formats, Data* fileData); + + CompressedImageData(const CompressedImageData& data); + + virtual ~CompressedImageData() + {} + + CompressedImageData* Clone() const override; + + void* GetData() const override; + + size_t GetSize() const override; + + int GetMipmapCount() const; + + int GetSliceCount(int mipmap = 0) const; + + size_t GetSize(int mipmap) const; + + void* GetData(int mipmap) const; + + int GetWidth(int mipmap = 0) const; + + int GetHeight(int mipmap = 0) const; + + PixelFormat GetFormat() const; + + bool IsSRGB() const; + + CompressedSlice* GetSlice(int slice, int mipmap) const; + + protected: + PixelFormat format; + bool sRGB; + + StrongReference memory; + std::vector> images; + + void CheckSliceExists(int slice, int mipmap) const; + }; +} // namespace love diff --git a/include/objects/compressedimagedata/compressedslice.hpp b/include/objects/compressedimagedata/compressedslice.hpp new file mode 100644 index 000000000..ff17f6072 --- /dev/null +++ b/include/objects/compressedimagedata/compressedslice.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace love +{ + class CompressedSlice : public ImageDataBase + { + public: + CompressedSlice(PixelFormat format, int width, int height, ByteData* memory, size_t offset, + size_t size); + + CompressedSlice(const CompressedSlice& slice); + + virtual ~CompressedSlice() + {} + + CompressedSlice* Clone() const override; + + void* GetData() const override + { + return (uint8_t*)this->memory->GetData() + this->offset; + } + + size_t GetSize() const override + { + return this->dataSize; + } + + bool IsSRGB() const override + { + return this->sRGB; + } + + size_t GetOffset() const + { + return this->offset; + } + + private: + StrongReference memory; + + size_t offset; + size_t dataSize; + bool sRGB; + }; +} // namespace love diff --git a/include/objects/compressedimagedata/handlers/astchandler.h b/include/objects/compressedimagedata/handlers/astchandler.h deleted file mode 100644 index 7ea939b14..000000000 --- a/include/objects/compressedimagedata/handlers/astchandler.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "objects/imagedata/types/formathandler.h" - -namespace love -{ - class ASTCHandler : public FormatHandler - { - public: - virtual ~ASTCHandler() - {} - - bool CanParseCompressed(Data* data) override; - - StrongReference ParseCompressed( - Data* fileData, std::vector>& images, - PixelFormat& format, bool& sRGB) override; - - const char* GetName() override - { - return "ASTCHandler"; - } - - private: - static constexpr uint32_t ASTC_IDENTIFIER = 0x5CA1AB13; - - struct ASTCHeader - { - uint8_t identifier[4]; - uint8_t blockdimX; - uint8_t blockdimY; - uint8_t blockdimZ; - uint8_t sizeX[3]; - uint8_t sizeY[3]; - uint8_t sizeZ[3]; - }; - }; -} // namespace love diff --git a/include/objects/compressedimagedata/handlers/ddshandler.h b/include/objects/compressedimagedata/handlers/ddshandler.h deleted file mode 100644 index c28bbfdd1..000000000 --- a/include/objects/compressedimagedata/handlers/ddshandler.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "objects/imagedata/types/formathandler.h" - -namespace love -{ - class DDSHandler : public FormatHandler - { - public: - virtual ~DDSHandler() - {} - - bool CanDecode(Data* data) override; - - DecodedImage Decode(Data* data) override; - - bool CanParseCompressed(Data* data) override; - - StrongReference ParseCompressed( - Data* fileData, std::vector>& images, - PixelFormat& format, bool& isSRGB) override; - - const char* GetName() override - { - return "DDSHandler"; - } - }; -} // namespace love diff --git a/include/objects/compressedimagedata/handlers/pkmhandler.h b/include/objects/compressedimagedata/handlers/pkmhandler.h deleted file mode 100644 index 730126a4b..000000000 --- a/include/objects/compressedimagedata/handlers/pkmhandler.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "objects/imagedata/types/formathandler.h" - -namespace love -{ - class PKMHandler : public FormatHandler - { - public: - enum PKMTextureFormat - { - ETC1_RGB_NO_MIPMAPS, - ETC2PACKAGE_RGB_NO_MIPMAPS, - ETC2PACKAGE_RGBA_NO_MIPMAPS_OLD, - ETC2PACKAGE_RGBA_NO_MIPMAPS, - ETC2PACKAGE_RGBA1_NO_MIPMAPS, - ETC2PACKAGE_R_NO_MIPMAPS, - ETC2PACKAGE_RG_NO_MIPMAPS, - ETC2PACKAGE_R_SIGNED_NO_MIPMAPS, - ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS - }; - - virtual ~PKMHandler() - {} - - bool CanParseCompressed(Data* data) override; - - StrongReference ParseCompressed( - Data* fileData, std::vector>& images, - PixelFormat& format, bool& isSRGB) override; - - const char* GetName() override - { - return "PKMHandler"; - } - - private: - static constexpr uint8_t PKMIDENTIFIER[] = { 'P', 'K', 'M', ' ' }; - - struct PKMHeader - { - uint8_t identifier[4]; - uint8_t version[2]; - - uint16_t textureFormatBig; - uint16_t extendedWidthBig; - uint16_t extendedHeightBig; - - uint16_t widthBig; - uint16_t heightBig; - }; - }; -}; // namespace love diff --git a/include/objects/compressedimagedata/types/compressedmemory.h b/include/objects/compressedimagedata/types/compressedmemory.h deleted file mode 100644 index 563374caa..000000000 --- a/include/objects/compressedimagedata/types/compressedmemory.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "objects/object.h" - -namespace love -{ - class CompressedMemory : public Object - { - public: - CompressedMemory(size_t size); - - virtual ~CompressedMemory(); - - uint8_t* data; - size_t size; - }; -} // namespace love diff --git a/include/objects/compressedimagedata/types/compressedslice.h b/include/objects/compressedimagedata/types/compressedslice.h deleted file mode 100644 index 63cee13ed..000000000 --- a/include/objects/compressedimagedata/types/compressedslice.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "objects/compressedimagedata/types/compressedmemory.h" -#include "objects/imagedata/imagedatabase.h" - -namespace love -{ - class CompressedSlice : public ImageDataBase - { - public: - CompressedSlice(PixelFormat format, int width, int height, CompressedMemory* memory, - size_t offset, size_t size); - - CompressedSlice(const CompressedSlice& slice); - - virtual ~CompressedSlice(); - - CompressedSlice* Clone() const override; - - void* GetData() const override - { - return this->memory->data + this->offset; - } - - size_t GetSize() const override - { - return this->dataSize; - } - - bool IsSRGB() const override - { - return this->sRGB; - } - - size_t GetOffset() const - { - return this->offset; - } - - private: - StrongReference memory; - - size_t offset; - size_t dataSize; - - bool sRGB; - }; -} // namespace love diff --git a/include/objects/compressedimagedata/wrap_compressedimagedata.h b/include/objects/compressedimagedata/wrap_compressedimagedata.h deleted file mode 100644 index 568d8b8eb..000000000 --- a/include/objects/compressedimagedata/wrap_compressedimagedata.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/compressedimagedata/compressedimagedata.h" - -namespace Wrap_CompressedImageData -{ - int Clone(lua_State* L); - - int GetWidth(lua_State* L); - - int GetHeight(lua_State* L); - - int GetDimensions(lua_State* L); - - int GetMipmapCount(lua_State* L); - - int GetFormat(lua_State* L); - - love::CompressedImageData* CheckCompressedImageData(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_CompressedImageData diff --git a/include/objects/compressedimagedata/wrap_compressedimagedata.hpp b/include/objects/compressedimagedata/wrap_compressedimagedata.hpp new file mode 100644 index 000000000..0df465c92 --- /dev/null +++ b/include/objects/compressedimagedata/wrap_compressedimagedata.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace Wrap_CompressedImageData +{ + love::CompressedImageData* CheckCompressedImageData(lua_State* L, int index); + + int Clone(lua_State* L); + + int GetWidth(lua_State* L); + + int GetHeight(lua_State* L); + + int GetDimensions(lua_State* L); + + int GetMipmapCount(lua_State* L); + + int GetFormat(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_CompressedImageData \ No newline at end of file diff --git a/include/objects/contact/contact.hpp b/include/objects/contact/contact.hpp new file mode 100644 index 000000000..8ee385415 --- /dev/null +++ b/include/objects/contact/contact.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace love +{ + class World; + + class Contact : public Object + { + public: + friend class World; + friend class World::ContactCallback; + + static Type type; + + Contact(World* world, b2Contact* contact); + + virtual ~Contact(); + + void Invalidate(); + + bool IsValid(); + + int GetPositions(lua_State* L); + + int GetNormal(lua_State* L); + + float GetFriction() const; + + float GetRestitution() const; + + bool IsEnabled(); + + bool IsTouching() const; + + void SetFriction(float friction); + + void SetRestitution(float restitution); + + void SetEnabled(bool enabled); + + void ResetFriction(); + + void ResetRestitution(); + + void SetTangentSpeed(float speed); + + float GetTangentSpeed() const; + + void GetChildren(int& childA, int& childB); + + void GetShapes(Shape*& shapeA, Shape*& shapeB); + + private: + b2Contact* contact; + World* world; + }; +} // namespace love diff --git a/include/objects/contact/wrap_contact.hpp b/include/objects/contact/wrap_contact.hpp new file mode 100644 index 000000000..30f1470d3 --- /dev/null +++ b/include/objects/contact/wrap_contact.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +namespace Wrap_Contact +{ + love::Contact* CheckContact(lua_State* L, int index); + + int GetPositions(lua_State* L); + + int GetNormal(lua_State* L); + + int GetFriction(lua_State* L); + + int GetRestitution(lua_State* L); + + int IsEnabled(lua_State* L); + + int IsTouching(lua_State* L); + + int SetFriction(lua_State* L); + + int SetRestitution(lua_State* L); + + int SetEnabled(lua_State* L); + + int ResetFriction(lua_State* L); + + int ResetRestitution(lua_State* L); + + int SetTangentSpeed(lua_State* L); + + int GetTangentSpeed(lua_State* L); + + int GetChildren(lua_State* L); + + int GetShapes(lua_State* L); + + int GetFixtures(lua_State* L); + + int IsDestroyed(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_Contact diff --git a/include/objects/data/byte/bytedata.h b/include/objects/data/byte/bytedata.h deleted file mode 100644 index ae44f008b..000000000 --- a/include/objects/data/byte/bytedata.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "common/data.h" -#include "common/exception.h" -namespace love -{ - class ByteData : public Data - { - public: - static love::Type type; - - ByteData(size_t size); - ByteData(const void* data, size_t size); - ByteData(void* data, size_t size, bool own); - ByteData(const ByteData& other); - - virtual ~ByteData(); - - ByteData* Clone() const override; - void* GetData() const override; - size_t GetSize() const override; - - private: - void Create(); - - char* data; - size_t size; - }; -} // namespace love diff --git a/include/objects/data/byte/wrap_bytedata.h b/include/objects/data/byte/wrap_bytedata.h deleted file mode 100644 index 4c2c7cb57..000000000 --- a/include/objects/data/byte/wrap_bytedata.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "objects/data/byte/bytedata.h" -#include "objects/data/wrap_data.h" - -#include "common/luax.h" - -namespace Wrap_ByteData -{ - int Clone(lua_State* L); - - love::ByteData* CheckByteData(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_ByteData diff --git a/include/objects/data/bytedata/bytedata.hpp b/include/objects/data/bytedata/bytedata.hpp new file mode 100644 index 000000000..024cf7200 --- /dev/null +++ b/include/objects/data/bytedata/bytedata.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "common/data.hpp" + +#include + +namespace love +{ + class ByteData : public Data + { + public: + static Type type; + + ByteData(size_t size, bool clear = true); + + ByteData(const void* data, size_t size); + + ByteData(void* data, size_t size, bool own); + + ByteData(const ByteData& other); + + ByteData* Clone() const override; + + void* GetData() const override; + + size_t GetSize() const override; + + private: + void Create(); + + std::unique_ptr data; + size_t size; + }; +} // namespace love diff --git a/include/objects/data/bytedata/wrap_bytedata.hpp b/include/objects/data/bytedata/wrap_bytedata.hpp new file mode 100644 index 000000000..83a705d1c --- /dev/null +++ b/include/objects/data/bytedata/wrap_bytedata.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_ByteData +{ + love::ByteData* CheckByteData(lua_State* L, int index); + + int Clone(lua_State* L); + + int SetString(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_ByteData diff --git a/include/objects/data/compressed/compresseddata.h b/include/objects/data/compressed/compresseddata.h deleted file mode 100644 index 229e854f3..000000000 --- a/include/objects/data/compressed/compresseddata.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "common/data.h" -#include "modules/data/compressor/compressor.h" - -namespace love -{ - class CompressedData : public Data - { - public: - static love::Type type; - - CompressedData(Compressor::Format format, char* data, size_t compressedSize, size_t rawSize, - bool own = true); - CompressedData(const CompressedData& other); - - virtual ~CompressedData(); - - Compressor::Format GetFormat() const; - - size_t GetDecompressedSize() const; - - CompressedData* Clone() const override; - - void* GetData() const override; - - size_t GetSize() const override; - - private: - Compressor::Format format; - - char* data; - size_t dataSize; - - size_t originalSize; - }; -} // namespace love diff --git a/include/objects/data/compressed/wrap_compresseddata.h b/include/objects/data/compressed/wrap_compresseddata.h deleted file mode 100644 index c365f0dc5..000000000 --- a/include/objects/data/compressed/wrap_compresseddata.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "objects/data/compressed/compresseddata.h" -#include "objects/data/wrap_data.h" - -#include "common/luax.h" - -namespace Wrap_CompressedData -{ - love::CompressedData* CheckCompressedData(lua_State* L, int index); - - int Clone(lua_State* L); - - int GetFormat(lua_State* L); - - int Register(lua_State* L); -} // namespace Wrap_CompressedData diff --git a/include/objects/data/compresseddata/compresseddata.hpp b/include/objects/data/compresseddata/compresseddata.hpp new file mode 100644 index 000000000..fd806e33b --- /dev/null +++ b/include/objects/data/compresseddata/compresseddata.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include + +namespace love +{ + class CompressedData : public Data + { + public: + static Type type; + + CompressedData(Compressor::Format format, char* data, size_t compressedSize, size_t rawSize, + bool own = true); + + CompressedData(const CompressedData& other); + + Compressor::Format GetFormat() const; + + size_t GetDecompressedSize() const; + + CompressedData* Clone() const override; + + void* GetData() const override; + + size_t GetSize() const override; + + private: + Compressor::Format format; + + std::unique_ptr data; + size_t dataSize; + + size_t originalSize; + }; +} // namespace love diff --git a/include/objects/data/compresseddata/wrap_compresseddata.hpp b/include/objects/data/compresseddata/wrap_compresseddata.hpp new file mode 100644 index 000000000..4d27624a7 --- /dev/null +++ b/include/objects/data/compresseddata/wrap_compresseddata.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_CompressedData +{ + love::CompressedData* CheckCompressedData(lua_State* L, int index); + + int Clone(lua_State* L); + + int GetFormat(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_CompressedData diff --git a/include/objects/data/dataview/dataview.hpp b/include/objects/data/dataview/dataview.hpp new file mode 100644 index 000000000..0515f89da --- /dev/null +++ b/include/objects/data/dataview/dataview.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +namespace love +{ + class DataView : public Data + { + public: + static Type type; + + DataView(Data* data, size_t offset, size_t size); + + DataView(const DataView& other); + + DataView* Clone() const override; + + void* GetData() const override; + + size_t GetSize() const override; + + private: + StrongReference data; + + size_t offset; + size_t size; + }; +} // namespace love diff --git a/include/objects/data/dataview/wrap_dataview.hpp b/include/objects/data/dataview/wrap_dataview.hpp new file mode 100644 index 000000000..d226c3bdc --- /dev/null +++ b/include/objects/data/dataview/wrap_dataview.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_DataView +{ + love::DataView* CheckDataView(lua_State* L, int index); + + int Clone(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_DataView diff --git a/include/objects/data/filedata/filedata.hpp b/include/objects/data/filedata/filedata.hpp new file mode 100644 index 000000000..304fef515 --- /dev/null +++ b/include/objects/data/filedata/filedata.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include + +namespace love +{ + class FileData : public Data + { + public: + static Type type; + + FileData(uint64_t size, const std::string& filename); + + FileData(const FileData& content); + + FileData* Clone() const; + + void* GetData() const; + + size_t GetSize() const; + + const std::string& GetFilename() const; + + const std::string& GetExtension() const; + + const std::string& GetName() const; + + private: + std::unique_ptr data; + uint64_t size; + + std::string filename; + std::string extension; + std::string name; + }; +} // namespace love diff --git a/include/objects/data/filedata/wrap_filedata.hpp b/include/objects/data/filedata/wrap_filedata.hpp new file mode 100644 index 000000000..26f153f2a --- /dev/null +++ b/include/objects/data/filedata/wrap_filedata.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_FileData +{ + int Clone(lua_State* L); + + int GetFilename(lua_State* L); + + int GetExtension(lua_State* L); + + love::FileData* CheckFileData(lua_State* L, int index); + + love::FileData* GetFileData(lua_State* L, int index); + + int Register(lua_State* L); +} // namespace Wrap_FileData diff --git a/include/objects/data/sounddata/sounddata.hpp b/include/objects/data/sounddata/sounddata.hpp new file mode 100644 index 000000000..af9964d5a --- /dev/null +++ b/include/objects/data/sounddata/sounddata.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +namespace love +{ + class SoundData : public Data + { + public: + static Type type; + + SoundData(Decoder* decoder); + + SoundData(int samples, int sampleRate, int bitDepth, int channels); + + SoundData(void* data, int samples, int sampleRate, int bitDepth, int channels); + + SoundData(const SoundData& other); + + virtual ~SoundData() + {} + + SoundData* Clone() const; + + void* GetData() const; + + size_t GetSize() const; + + virtual int GetChannelCount() const; + + virtual int GetBitDepth() const; + + virtual int GetSampleRate() const; + + virtual int GetSampleCount() const; + + virtual float GetDuration() const; + + void SetSample(int index, float sample); + + void SetSample(int index, int channel, float sample); + + float GetSample(int index) const; + + float GetSample(int index, int channel) const; + + private: + static constexpr int BUFFER_SIZE = 0x80000; + + void Load(int samples, int sampleRate, int bitDepth, int channels, void* newData = nullptr); + + std::vector buffer; + size_t size; + + int sampleRate; + int bitDepth; + int channels; + }; +} // namespace love diff --git a/include/objects/data/sounddata/wrap_sounddata.hpp b/include/objects/data/sounddata/wrap_sounddata.hpp new file mode 100644 index 000000000..5fa931224 --- /dev/null +++ b/include/objects/data/sounddata/wrap_sounddata.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace Wrap_SoundData +{ + int Clone(lua_State* L); + + int GetBitDepth(lua_State* L); + + int GetChannelCount(lua_State* L); + + int GetDuration(lua_State* L); + + int GetSampleCount(lua_State* L); + + int GetSampleRate(lua_State* L); + + int GetSample(lua_State* L); + + int SetSample(lua_State* L); + + love::SoundData* CheckSoundData(lua_State* L, int index); + + int Register(lua_State* L); +} // namespace Wrap_SoundData diff --git a/include/objects/data/view/dataview.h b/include/objects/data/view/dataview.h deleted file mode 100644 index 5fcf90289..000000000 --- a/include/objects/data/view/dataview.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "common/data.h" - -namespace love -{ - class DataView : public Data - { - public: - static love::Type type; - - DataView(Data* data, size_t offset, size_t size); - DataView(const DataView& other); - - virtual ~DataView(); - - DataView* Clone() const override; - void* GetData() const override; - size_t GetSize() const override; - - private: - StrongReference data; - - size_t offset; - size_t size; - }; -} // namespace love diff --git a/include/objects/data/view/wrap_dataview.h b/include/objects/data/view/wrap_dataview.h deleted file mode 100644 index bb14a2d68..000000000 --- a/include/objects/data/view/wrap_dataview.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "objects/data/view/dataview.h" -#include "objects/data/wrap_data.h" - -namespace Wrap_DataView -{ - love::DataView* CheckDataView(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_DataView diff --git a/include/objects/data/wrap_data.h b/include/objects/data/wrap_data.h deleted file mode 100644 index aabe70f8e..000000000 --- a/include/objects/data/wrap_data.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "common/data.h" -#include "common/luax.h" - -namespace Wrap_Data -{ - int GetPointer(lua_State* L); - - int GetSize(lua_State* L); - - int GetString(lua_State* L); - - love::Data* CheckData(lua_State* L, int index); - - extern const luaL_Reg functions[4]; - - int Register(lua_State* L); -} // namespace Wrap_Data diff --git a/include/objects/data/wrap_data.hpp b/include/objects/data/wrap_data.hpp new file mode 100644 index 000000000..614746212 --- /dev/null +++ b/include/objects/data/wrap_data.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +namespace Wrap_Data +{ + love::Data* CheckData(lua_State* L, int index); + + int GetPointer(lua_State* L); + + int GetSize(lua_State* L); + + int GetString(lua_State* L); + + extern const luaL_Reg functions[0x0D]; + + int Register(lua_State* L); +} // namespace Wrap_Data diff --git a/include/objects/decoder/decoder.h b/include/objects/decoder/decoder.h deleted file mode 100644 index e71a307a3..000000000 --- a/include/objects/decoder/decoder.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include "common/data.h" -#include "common/exception.h" - -#include "common/strongref.h" - -namespace love -{ - class Decoder : public Object - { - public: - static love::Type type; - - Decoder(Data* data, int bufferSize); - - virtual ~Decoder(); - - static const int DEFAULT_BUFFER_SIZE = 0x4000; - static const int DEFAULT_SAMPLE_RATE = 44100; - - static const int DEFAULT_CHANNELS = 2; - static const int DEFAULT_BIT_DEPTH = 16; - - virtual Decoder* Clone() = 0; - - virtual int Decode() = 0; - - virtual int Decode(s16* buffer) = 0; - - virtual int GetSize() const; - - virtual void* GetBuffer() const; - - virtual bool Seek(double position) = 0; - - virtual bool Rewind() = 0; - - virtual bool IsSeekable() = 0; - - virtual bool IsFinished(); - - virtual int GetChannelCount() const = 0; - - virtual int GetBitDepth() const = 0; - - virtual int GetSampleRate() const; - - virtual double GetDuration() = 0; - - protected: - StrongReference data; - - int bufferSize; - int sampleRate; - void* buffer; - bool eof; - }; -} // namespace love diff --git a/include/objects/decoder/types/flacdecoder.h b/include/objects/decoder/types/flacdecoder.h deleted file mode 100644 index 2473df74c..000000000 --- a/include/objects/decoder/types/flacdecoder.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include "objects/decoder/decoder.h" -#include - -namespace love -{ - class FLACDecoder : public Decoder - { - public: - FLACDecoder(Data* data, int bufferSize); - ~FLACDecoder(); - - struct FLACFile - { - size_t bitsPerSample; - size_t sampleRate; - size_t channels; - - uint32_t totalSamples; - - const char* data; - size_t size; - size_t read; - - int32_t* outputBuffer; - int32_t* writeBuffer; - - size_t bufferUsed; - }; - - static bool Accepts(const std::string& ext); - - Decoder* Clone(); - - int Decode(); - - int Decode(s16* buffer); - - bool Seek(double position); - - bool Rewind(); - - bool IsSeekable(); - - int GetChannelCount() const; - - int GetBitDepth() const; - - int GetSampleRate() const; - - double GetDuration(); - - private: - FLACFile file; - size_t decodeBufferRead; - - FLAC__StreamDecoder* decoder; - FLAC__StreamDecoderInitStatus status; - }; -} // namespace love diff --git a/include/objects/decoder/types/modplugdecoder.h b/include/objects/decoder/types/modplugdecoder.h deleted file mode 100644 index 5251341cf..000000000 --- a/include/objects/decoder/types/modplugdecoder.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "objects/decoder/decoder.h" -#include - -namespace love -{ - class ModPlugDecoder : public Decoder - { - public: - ModPlugDecoder(Data* data, int bufferSize); - - virtual ~ModPlugDecoder(); - - static bool Accepts(const std::string& ext); - - Decoder* Clone(); - - int Decode(); - - int Decode(s16* buffer); - - bool Seek(double position); - - bool Rewind(); - - bool IsSeekable(); - - int GetChannelCount() const; - - int GetBitDepth() const; - - double GetDuration(); - - private: - ModPlugFile* plug; - ModPlug_Settings settings; - - double duration; - }; -} // namespace love diff --git a/include/objects/decoder/types/mp3decoder.h b/include/objects/decoder/types/mp3decoder.h deleted file mode 100644 index 3dcbe50b0..000000000 --- a/include/objects/decoder/types/mp3decoder.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include - -#include "objects/decoder/decoder.h" - -namespace love -{ - class MP3Decoder : public Decoder - { - public: - struct MP3File - { - unsigned char* data; - size_t size; - size_t offset; - - MP3File(Data* data) : - data((unsigned char*)data->GetData()), - size(data->GetSize()), - offset(0) - {} - }; - - MP3Decoder(Data* data, int bufferSize); - ~MP3Decoder(); - - static bool Accepts(const std::string& ext); - - static void Quit(); - - Decoder* Clone(); - - int Decode(); - - int Decode(s16* buffer); - - bool Seek(double position); - - bool Rewind(); - - bool IsSeekable(); - - int GetChannelCount() const; - - int GetBitDepth() const; - - double GetDuration(); - - private: - MP3File file; - - mpg123_handle* handle; - static bool inited; - int channels; - double duration; - }; -} // namespace love diff --git a/include/objects/decoder/types/vorbisdecoder.h b/include/objects/decoder/types/vorbisdecoder.h deleted file mode 100644 index dad787874..000000000 --- a/include/objects/decoder/types/vorbisdecoder.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#define OV_EXCLUDE_STATIC_CALLBACKS - -#include -#include - -#include "objects/decoder/decoder.h" - -namespace love -{ - class VorbisDecoder : public Decoder - { - public: - struct OggFile - { - const char* data; - int64_t size; - int64_t read; - }; - - VorbisDecoder(Data* data, int bufferSize); - ~VorbisDecoder(); - - static bool Accepts(const std::string& extension); - - Decoder* Clone(); - - int Decode(s16* buffer); - - int Decode(); - - bool Seek(double s); - - bool Rewind(); - - bool IsSeekable(); - - int GetChannelCount() const; - - int GetBitDepth() const; - - int GetSampleRate() const; - - double GetDuration(); - - private: - OggFile file; - - ov_callbacks callbacks; - OggVorbis_File handle; - vorbis_info* info; - vorbis_comment* comment; - - double duration; - }; -} // namespace love diff --git a/include/objects/decoder/types/wavedecoder.h b/include/objects/decoder/types/wavedecoder.h deleted file mode 100644 index ff68bab06..000000000 --- a/include/objects/decoder/types/wavedecoder.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "wuff.h" - -#include "objects/decoder/decoder.h" - -namespace love -{ - class WaveDecoder : public Decoder - { - public: - struct WaveFile - { - char* data; - size_t size; - size_t offset; - }; - - WaveDecoder(Data* decoder, int bufferCode); - ~WaveDecoder(); - - static bool Accepts(const std::string& ext); - - Decoder* Clone(); - - int Decode(); - - int Decode(s16* buffer); - - bool Seek(double position); - - bool Rewind(); - - bool IsSeekable(); - - int GetChannelCount() const; - - int GetBitDepth() const; - - int GetSampleRate() const; - - double GetDuration(); - - private: - WaveFile file; - - wuff_handle* handle; - wuff_info info; - }; -} // namespace love diff --git a/include/objects/decoder/wrap_decoder.h b/include/objects/decoder/wrap_decoder.h deleted file mode 100644 index e879145a9..000000000 --- a/include/objects/decoder/wrap_decoder.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/decoder/decoder.h" - -namespace Wrap_Decoder -{ - int Clone(lua_State* L); - - int GetBitDepth(lua_State* L); - - int GetChannelCount(lua_State* L); - - int GetDuration(lua_State* L); - - int GetSampleRate(lua_State* L); - - int Decode(lua_State* L); - - int Seek(lua_State* L); - - love::Decoder* CheckDecoder(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Decoder diff --git a/include/objects/decoder/wrap_decoder.hpp b/include/objects/decoder/wrap_decoder.hpp new file mode 100644 index 000000000..da250da09 --- /dev/null +++ b/include/objects/decoder/wrap_decoder.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace Wrap_Decoder +{ + int Clone(lua_State* L); + + int GetBitDepth(lua_State* L); + + int GetChannelCount(lua_State* L); + + int GetDuration(lua_State* L); + + int GetSampleRate(lua_State* L); + + int Decode(lua_State* L); + + int Seek(lua_State* L); + + love::Decoder* CheckDecoder(lua_State* L, int index); + + int Register(lua_State* L); +} // namespace Wrap_Decoder diff --git a/include/objects/drawable/drawable.h b/include/objects/drawable/drawable.h deleted file mode 100644 index 5f5f05deb..000000000 --- a/include/objects/drawable/drawable.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/* -** drawable.h -** Superclass for Textures -*/ - -#include "common/matrix.h" -#include "objects/object.h" - -namespace love -{ - class Graphics; - - class Drawable : public Object - { - public: - static love::Type type; - - virtual ~Drawable() {}; - - virtual void Draw(Graphics* gfx, const Matrix4& localTransform) = 0; - }; -} // namespace love diff --git a/include/objects/drawable/wrap_drawable.h b/include/objects/drawable/wrap_drawable.h deleted file mode 100644 index f17232d13..000000000 --- a/include/objects/drawable/wrap_drawable.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/drawable/drawable.h" - -namespace Wrap_Drawable -{ - int Register(lua_State* L); -} diff --git a/include/objects/file/file.h b/include/objects/file/file.h deleted file mode 100644 index 17d8fca64..000000000 --- a/include/objects/file/file.h +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include "objects/object.h" - -#include "common/exception.h" -#include "objects/filedata/filedata.h" - -#include - -#include - -namespace love -{ - class File : public Object - { - public: - static love::Type type; - - enum Mode - { - MODE_CLOSED, - MODE_READ, - MODE_WRITE, - MODE_APPEND, - MODE_MAX_ENUM - }; - - enum BufferMode - { - BUFFER_NONE, - BUFFER_LINE, - BUFFER_FULL, - BUFFER_MAX_ENUM - }; - - static const int64_t ALL = -1; - - File(const std::string& filename); - - virtual ~File(); - - bool Close(); - - bool Flush(); - - BufferMode GetBuffer(int64_t& size) const; - - const std::string& GetFilename() const; - - Mode GetMode(); - - int64_t GetSize(); - - bool IsEOF(); - - bool IsOpen(); - - bool Open(File::Mode mode); - - int64_t Read(void* destination, int64_t size); - FileData* Read(int64_t size = ALL); - - bool Seek(uint64_t position); - - bool SetBuffer(BufferMode mode, int64_t size); - - int64_t Tell(); - - bool Write(const void* data, int64_t size); - bool Write(Data* data, int64_t size); - - /* OPEN MODES */ - - static bool GetConstant(const char* in, Mode& out); - static bool GetConstant(Mode in, const char*& out); - - static std::vector GetConstants(Mode mode); - - /* BUFFER MODES */ - - static bool GetConstant(const char* in, BufferMode& out); - static bool GetConstant(BufferMode in, const char*& out); - - static std::vector GetConstants(BufferMode mode); - - private: - std::string filename; - - PHYSFS_file* file; - Mode mode; - - BufferMode bufferMode; - int64_t bufferSize; - }; -} // namespace love diff --git a/include/objects/file/file.hpp b/include/objects/file/file.hpp new file mode 100644 index 000000000..70a8ef936 --- /dev/null +++ b/include/objects/file/file.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace love +{ + class File : public Stream + { + public: + enum Mode + { + MODE_CLOSED, + MODE_READ, + MODE_WRITE, + MODE_APPEND, + MODE_MAX_ENUM + }; + + enum BufferMode + { + BUFFER_NONE, + BUFFER_LINE, + BUFFER_FULL, + BUFFER_MAX_ENUM + }; + + static Type type; + + virtual ~File() + {} + + bool IsReadable() const override + { + return this->GetMode() == MODE_READ; + } + + bool IsWritable() const override + { + return this->GetMode() == MODE_WRITE || this->GetMode() == MODE_APPEND; + } + + bool IsSeekable() const override + { + return this->IsOpen(); + } + + using Stream::Read; + + using Stream::Write; + + virtual bool Open(Mode mode) = 0; + + virtual bool Close() = 0; + + virtual bool IsOpen() const = 0; + + FileData* Read(int64_t size) override; + + FileData* Read(); + + virtual bool IsEOF() = 0; + + virtual bool SetBuffer(BufferMode mode, int64_t size) = 0; + + virtual BufferMode GetBuffer(int64_t& size) const = 0; + + virtual Mode GetMode() const = 0; + + virtual const std::string& GetFilename() const = 0; + + virtual std::string GetExtension() const; + + // clang-format off + static constexpr BidirectionalMap modes = { + "r", File::Mode::MODE_READ, + "w", File::Mode::MODE_WRITE, + "a", File::Mode::MODE_APPEND, + "c", File::Mode::MODE_CLOSED + }; + + static constexpr BidirectionalMap bufferModes = { + "none", File::BufferMode::BUFFER_NONE, + "line", File::BufferMode::BUFFER_LINE, + "full", File::BufferMode::BUFFER_FULL + }; + // clang-format on + }; +} // namespace love diff --git a/include/objects/file/physfs/file.hpp b/include/objects/file/physfs/file.hpp new file mode 100644 index 000000000..cfcbe109e --- /dev/null +++ b/include/objects/file/physfs/file.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include + +#include + +#include + +namespace love::physfs +{ + class File : public love::File + { + public: + File(const std::string& filename, Mode mode); + + virtual ~File(); + + File* Clone() override; + + int64_t Read(void* destination, int64_t size) override; + + bool Write(const void* data, int64_t size) override; + + bool Flush() override; + + int64_t GetSize() override; + + bool Seek(int64_t position, SeekOrigin origin) override; + + int64_t Tell() override; + + using love::File::Read; + + using love::File::Write; + + bool Open(Mode mode) override; + + bool Close() override; + + bool IsOpen() const override; + + bool IsEOF() override; + + bool SetBuffer(BufferMode mode, int64_t size) override; + + BufferMode GetBuffer(int64_t& size) const override; + + Mode GetMode() const override; + + const std::string& GetFilename() const override; + + private: + File(const File& other); + + std::string filename; + PHYSFS_File* file; + + Mode fileMode; + + BufferMode bufferMode; + int64_t bufferSize; + }; +} // namespace love::physfs diff --git a/include/objects/file/wrap_file.h b/include/objects/file/wrap_file.h deleted file mode 100644 index 14a66459f..000000000 --- a/include/objects/file/wrap_file.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "modules/data/wrap_datamodule.h" -#include "objects/file/file.h" - -namespace Wrap_File -{ - int Close(lua_State* L); - - int Flush(lua_State* L); - - int GetBuffer(lua_State* L); - - int GetFilename(lua_State* L); - - int GetMode(lua_State* L); - - int GetSize(lua_State* L); - - int IsEOF(lua_State* L); - - int IsOpen(lua_State* L); - - int Lines_I(lua_State* L); - - int Lines(lua_State* L); - - int Open(lua_State* L); - - int Read(lua_State* L); - - int Seek(lua_State* L); - - int SetBuffer(lua_State* L); - - int Tell(lua_State* L); - - int Write(lua_State* L); - - love::File* CheckFile(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_File diff --git a/include/objects/file/wrap_file.hpp b/include/objects/file/wrap_file.hpp new file mode 100644 index 000000000..db0a77cdd --- /dev/null +++ b/include/objects/file/wrap_file.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +namespace Wrap_File +{ + int Close(lua_State* L); + + int Flush(lua_State* L); + + int GetBuffer(lua_State* L); + + int GetFilename(lua_State* L); + + int GetMode(lua_State* L); + + int GetSize(lua_State* L); + + int IsEOF(lua_State* L); + + int IsOpen(lua_State* L); + + int Lines_I(lua_State* L); + + int Lines(lua_State* L); + + int Open(lua_State* L); + + int Read(lua_State* L); + + int Seek(lua_State* L); + + int SetBuffer(lua_State* L); + + int Tell(lua_State* L); + + int Write(lua_State* L); + + love::File* CheckFile(lua_State* L, int index); + + int Register(lua_State* L); +} // namespace Wrap_File diff --git a/include/objects/filedata/filedata.h b/include/objects/filedata/filedata.h deleted file mode 100644 index 87d127f98..000000000 --- a/include/objects/filedata/filedata.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "common/data.h" -#include "common/exception.h" - -#include - -namespace love -{ - class FileData : public Data - { - public: - static love::Type type; - - FileData(uint64_t size, const std::string& filename); - FileData(const FileData& content); - - virtual ~FileData(); - - FileData* Clone() const; - - void* GetData() const; - - size_t GetSize() const; - - const std::string& GetFilename() const; - - const std::string& GetExtension() const; - - const std::string& GetName() const; - - private: - char* data; - uint64_t size; - - std::string filename; - std::string extension; - std::string name; - }; -} // namespace love diff --git a/include/objects/filedata/wrap_filedata.h b/include/objects/filedata/wrap_filedata.h deleted file mode 100644 index 3cf3a5fc9..000000000 --- a/include/objects/filedata/wrap_filedata.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "objects/data/wrap_data.h" -#include "objects/filedata/filedata.h" - -namespace Wrap_FileData -{ - int Clone(lua_State* L); - - int GetFilename(lua_State* L); - - int GetExtension(lua_State* L); - - love::FileData* CheckFileData(lua_State* L, int index); - - love::FileData* GetFileData(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_FileData diff --git a/include/objects/font/font.hpp b/include/objects/font/font.hpp new file mode 100644 index 000000000..4cfa88196 --- /dev/null +++ b/include/objects/font/font.hpp @@ -0,0 +1,217 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#if defined(__3DS__) + #include +using TextureHandle = C3D_Tex; +#else +using TextureHandle = love::Texture; +#endif + +namespace love +{ + using StrongRasterizer = StrongReference; + + class Font : public Object + { + public: + static inline Type type = Type("Font", &Object::type); + + using Codepoints = std::vector; + + enum AlignMode + { + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT, + ALIGN_JUSTIFY, + ALIGN_MAX_ENUM + }; + + struct Glyph + { + TextureHandle* texture; + int spacing; + std::array vertices; + + int sheet; + }; + + struct DrawCommand + { + TextureHandle* texture; + int start; + int count; + + int sheet; + }; + + static inline int fontCount = 0; + + Font(Rasterizer* rasterizer, const SamplerState& state); + + virtual ~Font() + { + Font::fontCount--; + } + + int GetWidth(const std::string& text); + + int GetWidth(uint32_t glyph); + + bool HasGlyphs(const std::string& text) const; + + bool HasGlyph(uint32_t glyph) const; + + float GetKerning(uint32_t left, uint32_t right); + + float GetKerning(const std::string& left, const std::string& right); + + void GetWrap(const ColoredStrings& text, float w32raplimit, std::vector& lines, + std::vector* line_widths = nullptr); + + void GetWrap(const ColoredCodepoints& codepoints, float wraplimit, + std::vector& ranges, std::vector* linewidths = nullptr); + + void SetFallbacks(const std::vector& fallbacks) + {} + + void Print(Graphics& graphics, const ColoredStrings& text, + const Matrix4& localTransform, const Color& color); + + void Printf(Graphics& graphics, const ColoredStrings& text, float wrap, + AlignMode alignment, const Matrix4& localTransform, + const Color& color); + + int GetAscent() const; + + int GetDescent() const; + + float GetHeight() const; + + uint32_t GetTextureCacheID() const + { + return this->textureCacheID; + } + + float GetBaseline() const; + + std::vector GenerateVertices(const ColoredCodepoints& codepoints, Range range, + const Color& color, + std::vector& vertices, + float extraSpacing = 0.0f, Vector2 offset = {}, + TextShaper::TextInfo* info = nullptr); + + std::vector GenerateVerticesFormatted(const ColoredCodepoints& codepoints, + const Color& color, float wrap, + AlignMode align, + std::vector& vertices, + TextShaper::TextInfo* info = nullptr); + + void SetLineHeight(float height); + + float GetLineHeight() const; + + void SetSamplerState(const SamplerState& state); + + const SamplerState& GetSamplerState() const + { + return this->samplerState; + } + + float GetDPIScale() const + { + return this->dpiScale; + } + + // clang-format off + static constexpr BidirectionalMap alignModes = { + "left", ALIGN_LEFT, + "center", ALIGN_CENTER, + "right", ALIGN_RIGHT, + "justify", ALIGN_JUSTIFY + }; + // clang-format on + + private: + struct TextureSize + { + int width; + int height; + }; + + static constexpr int MAX_TEXTURE_SIZE = 2048; + + bool LoadVolatile(); + + void UnloadVolatile(); + + void CreateTexture(); + + TextureSize GetNextTextureSize() const; + + GlyphData* GetRasterizerGlyphData(TextShaper::GlyphIndex glyphIndex, float& dpiScale); + + const Glyph& AddGlyph(TextShaper::GlyphIndex glyphIndex); + + const Glyph& FindGlyph(TextShaper::GlyphIndex glyphIndex); + + void Printv(Graphics& graphics, const Matrix4& transform, + const std::vector& drawCommands, + const std::vector& vertices); + + int textureX; + int textureY; + int textureWidth; + int textureHeight; + + int rowHeight; + + uint32_t textureCacheID; + +#if defined(__3DS__) + std::vector textures; +#else + std::vector > > textures; +#endif + + StrongReference shaper; + + std::unordered_map glyphs; + std::unordered_map kernings; + + static constexpr auto SPACES_PER_TAB = 0x04; + static constexpr uint32_t TAB_GLYPH = 9; + static constexpr uint32_t SPACE_GLYPH = 32; + + static constexpr uint32_t NEWLINE_GLYPH = 10; + static constexpr uint32_t CARRIAGE_GLYPH = 13; + + static constexpr int TEXTURE_PADDING = 2; + + SamplerState samplerState; + float dpiScale; + bool useSpacesAsTab; + + PixelFormat format; + }; +} // namespace love diff --git a/include/objects/font/fontc.h b/include/objects/font/fontc.h deleted file mode 100644 index 61ff81420..000000000 --- a/include/objects/font/fontc.h +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once - -#include "common/exception.h" -#include "common/lmath.h" - -#include "common/matrix.h" -#include "common/vector.h" - -#include "common/colors.h" -#include "objects/texture/texture.h" - -#include "objects/rasterizer/rasterizer.h" - -#include - -namespace love -{ - class Graphics; - - class Font; -} // namespace love - -namespace love::common -{ - class Font : public love::Object - { - public: - struct ColoredString - { - std::string string; - Colorf color; - }; - - enum AlignMode - { - ALIGN_LEFT, - ALIGN_CENTER, - ALIGN_RIGHT, - ALIGN_JUSTIFY, - ALIGN_MAX_ENUM - }; - -#if defined(__3DS__) - static constexpr int MAX_SYSFONTS = 5; -#elif defined(__SWITCH__) - static constexpr int MAX_SYSFONTS = 7; -#endif - - enum class SystemFontType : uint8_t; - - static love::Type type; - - Font(const Texture::Filter& filter); - - virtual ~Font(); - - virtual void Print(Graphics* gfx, const std::vector& text, - const Matrix4& localTransform, const Colorf& color) = 0; - - virtual void Printf(Graphics* gfx, const std::vector& text, float wrap, - AlignMode align, const Matrix4& localTransform, - const Colorf& color) = 0; - - int GetWidth(const std::string& string); - - virtual int GetWidth(uint32_t prevGlyph, uint32_t codepoint) = 0; - - virtual float GetHeight() const = 0; - - float GetLineHeight() const; - - void SetLineHeight(float lineHeight); - - Texture::Filter GetFilter(); - - virtual void SetFilter(const Texture::Filter& filter) = 0; - - virtual float GetKerning(uint32_t leftGlyph, uint32_t rightGlyph) = 0; - - virtual float GetKerning(const std::string& leftChar, const std::string& rightChar) = 0; - - virtual float GetDPIScale() const = 0; - - virtual float GetAscent() const = 0; - - virtual float GetBaseline() const = 0; - - virtual float GetDescent() const = 0; - - virtual void SetFallbacks(const std::vector& fallbacks) = 0; - - bool HasGlyphs(const std::string& text) const; - - virtual bool HasGlyph(uint32_t glyph) const = 0; - - static bool GetConstant(const char* in, AlignMode& out); - static bool GetConstant(AlignMode in, const char*& out); - static std::vector GetConstants(AlignMode); - - static bool GetConstant(const char* in, SystemFontType& out); - static bool GetConstant(SystemFontType in, const char*& out); - static std::vector GetConstants(SystemFontType); - - static int fontCount; - - protected: - float lineHeight; - Texture::Filter filter; - - int height; - float dpiScale; - }; -} // namespace love::common diff --git a/include/objects/font/wrap_font.h b/include/objects/font/wrap_font.h deleted file mode 100644 index 28bc597c1..000000000 --- a/include/objects/font/wrap_font.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/font/font.h" - -namespace Wrap_Font -{ - int GetWidth(lua_State* L); - - int GetHeight(lua_State* L); - - int GetWrap(lua_State* L); - - int SetLineHeight(lua_State* L); - - int GetLineHeight(lua_State* L); - - int SetFilter(lua_State* L); - - int GetFilter(lua_State* L); - - int GetAscent(lua_State* L); - - int GetDescent(lua_State* L); - - int GetBaseline(lua_State* L); - - int HasGlyphs(lua_State* L); - - int GetKerning(lua_State* L); - - int SetFallbacks(lua_State* L); - - int GetDPIScale(lua_State* L); - - love::Font* CheckFont(lua_State* L, int index); - - void CheckColoredString(lua_State* L, int index, - std::vector& strings); - - int Register(lua_State* L); -} // namespace Wrap_Font diff --git a/include/objects/font/wrap_font.hpp b/include/objects/font/wrap_font.hpp new file mode 100644 index 000000000..7c713b712 --- /dev/null +++ b/include/objects/font/wrap_font.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +namespace Wrap_Font +{ + love::Font* CheckFont(lua_State* L, int index); + + void CheckColoredString(lua_State* L, int index, love::ColoredStrings& strings); + + int GetWidth(lua_State* L); + + int GetHeight(lua_State* L); + + int GetWrap(lua_State* L); + + int SetLineHeight(lua_State* L); + + int GetLineHeight(lua_State* L); + + int SetFilter(lua_State* L); + + int GetFilter(lua_State* L); + + int GetAscent(lua_State* L); + + int GetDescent(lua_State* L); + + int GetBaseline(lua_State* L); + + int HasGlyphs(lua_State* L); + + int GetKerning(lua_State* L); + + int SetFallbacks(lua_State* L); + + int GetDPIScale(lua_State* L); + + int Register(lua_State* L); + + extern std::function wrap_extension; + + extern std::span extensions; +} // namespace Wrap_Font diff --git a/include/objects/gamepad/gamepadc.h b/include/objects/gamepad/gamepadc.h deleted file mode 100644 index cb60eceb1..000000000 --- a/include/objects/gamepad/gamepadc.h +++ /dev/null @@ -1,140 +0,0 @@ -#pragma once - -#include "objects/object.h" - -#include -#include -#include - -namespace love::common -{ - class Gamepad : public Object - { - public: - enum InputType - { - INPUT_TYPE_AXIS, - INPUT_TYPE_BUTTON, - INPUT_TYPE_MAX_ENUM - }; - - enum class GamepadAxis : uint64_t; - - enum class GamepadButton : uint64_t; - - struct GamepadInput - { - InputType type; - - union - { - GamepadAxis axis; - GamepadButton button; - }; - }; - - struct JoystickInput - { - InputType type; - - union - { - int axis; - int button; - }; - }; - - typedef std::pair ButtonMapping; - - struct Vibration - { - float left = 0.0f; - float right = 0.0f; - - float endTime = 0.0f; - - int id = -1; - - static constexpr uint32_t max = std::numeric_limits::max(); - }; - - static love::Type type; - - Gamepad(size_t id); - - Gamepad(size_t id, size_t index); - - virtual ~Gamepad() - {} - - virtual bool Open(size_t index) = 0; - - virtual void Close() = 0; - - virtual bool IsConnected() const = 0; - - virtual const char* GetName() const = 0; - - virtual size_t GetAxisCount() const = 0; - - virtual size_t GetButtonCount() const = 0; - - virtual float GetAxis(size_t axis) const = 0; - - virtual std::vector GetAxes() const = 0; - - virtual bool IsDown(const std::vector& buttons) const = 0; - - bool IsGamepad() const; - - virtual float GetGamepadAxis(GamepadAxis axis) const = 0; - - virtual bool IsGamepadDown(const std::vector& buttons) const = 0; - - std::string GetGUID() const; - - size_t GetInstanceID() const; - - size_t GetID() const; - - virtual bool IsVibrationSupported() = 0; - - virtual bool SetVibration(float left, float right, float duration = -1.0f) = 0; - - virtual bool SetVibration() = 0; - - virtual void GetVibration(float& left, float& right) = 0; - - /* helpers */ - - virtual bool IsDown(size_t index, ButtonMapping& button) = 0; - - virtual bool IsHeld(size_t index, ButtonMapping& button) const = 0; - - virtual bool IsUp(size_t index, ButtonMapping& button) = 0; - - static bool GetConstant(const char* in, InputType& out); - static bool GetConstant(InputType in, const char*& out); - - static bool GetConstant(const char* in, GamepadAxis& out); - static bool GetConstant(GamepadAxis in, const char*& out); - - static bool GetConstant(const char* in, GamepadButton& out); - static bool GetConstant(GamepadButton in, const char*& out); - - static float ClampValue(float x); - - protected: - size_t id; - int8_t instanceID; - - Vibration vibration; - - std::string name; - std::string guid; - - private: - Gamepad() - {} - }; -} // namespace love::common diff --git a/include/objects/gamepad/wrap_gamepad.h b/include/objects/gamepad/wrap_gamepad.h deleted file mode 100644 index 46f7cd209..000000000 --- a/include/objects/gamepad/wrap_gamepad.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/gamepad/gamepad.h" - -namespace Wrap_Gamepad -{ - int GetAxes(lua_State* L); - - int GetAxis(lua_State* L); - - int GetButtonCount(lua_State* L); - - int GetGamepadAxis(lua_State* L); - - int GetID(lua_State* L); - - int GetName(lua_State* L); - - int GetVibration(lua_State* L); - - int IsConnected(lua_State* L); - - int IsDown(lua_State* L); - - int IsGamepad(lua_State* L); - - int IsGamepadDown(lua_State* L); - - int IsVibrationSupported(lua_State* L); - - int SetVibration(lua_State* L); - - love::Gamepad* CheckJoystick(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Gamepad diff --git a/include/objects/glyphdata/glyphdata.hpp b/include/objects/glyphdata/glyphdata.hpp new file mode 100644 index 000000000..aead1a0ff --- /dev/null +++ b/include/objects/glyphdata/glyphdata.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include + +namespace love +{ + class GlyphData : public Data + { + public: + static Type type; + + struct GlyphMetrics + { + int width; + int height; + int advance; + int bearingX; + int bearingY; + }; + + struct GlyphSheetInfo + { + int index; + float left; + float top; + float right; + float bottom; + }; + + GlyphData(uint32_t glyph, GlyphMetrics metrics, PixelFormat format); + + GlyphData(uint32_t glyph, GlyphMetrics metrics, GlyphSheetInfo info, PixelFormat format); + + GlyphData(const GlyphData& other); + + ~GlyphData() + {} + + virtual GlyphData* Clone() const override; + + virtual void* GetData() const override; + + virtual size_t GetSize() const override; + + PixelFormat GetFormat() const; + + size_t GetPixelSize() const; + + int GetHeight() const; + + int GetWidth() const; + + uint32_t GetGlyph() const; + + std::string GetGlyphString() const; + + int GetAdvance() const; + + int GetBearingX() const; + + int GetBearingY() const; + + int GetMinX() const; + + int GetMinY() const; + + int GetMaxX() const; + + int GetMaxY() const; + + const GlyphSheetInfo& GetGlyphSheetInfo() const + { + return this->sheetInfo; + } + + protected: + uint32_t glyph; + GlyphMetrics metrics; + std::unique_ptr data; + PixelFormat format; + + GlyphSheetInfo sheetInfo; + }; +} // namespace love diff --git a/include/objects/glyphdata/glyphdatac.h b/include/objects/glyphdata/glyphdatac.h deleted file mode 100644 index fa2609fdf..000000000 --- a/include/objects/glyphdata/glyphdatac.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include "common/data.h" -#include "common/exception.h" - -#include - -namespace love::common -{ - class GlyphData : public Data - { - public: - static love::Type type; - - struct GlyphMetrics - { - int height; - int width; - int advance; - int bearingX; - int bearingY; - }; - - GlyphData(uint32_t glyph, GlyphMetrics metrics); - - GlyphData(const GlyphData& glyphData); - - virtual ~GlyphData(); - - virtual GlyphData* Clone() const = 0; - - virtual size_t GetPixelSize() const - { - return 0; - }; - - virtual size_t GetSize() const - { - return 0; - }; - - virtual void* GetData() const override - { - return nullptr; - } - - virtual void* GetData(int x, int y) const - { - return nullptr; - } - - int GetWidth() const; - - int GetHeight() const; - - uint32_t GetGlyph() const; - - std::string GetGlyphString() const; - - int GetAdvance() const; - - int GetBearingX() const; - - int GetBearingY() const; - - int GetMinX() const; - - int GetMinY() const; - - int GetMaxX() const; - - int GetMaxY() const; - - protected: - uint32_t glyph; - GlyphMetrics metrics; - }; -} // namespace love::common diff --git a/include/objects/glyphdata/wrap_glyphdata.h b/include/objects/glyphdata/wrap_glyphdata.h deleted file mode 100644 index 31e494de5..000000000 --- a/include/objects/glyphdata/wrap_glyphdata.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "objects/data/wrap_data.h" -#include "objects/glyphdata/glyphdata.h" - -#include "common/luax.h" - -namespace Wrap_GlyphData -{ - int Clone(lua_State* L); - - int GetWidth(lua_State* L); - - int GetHeight(lua_State* L); - - int GetDimensions(lua_State* L); - - int GetGlyph(lua_State* L); - - int GetGlyphString(lua_State* L); - - int GetAdvance(lua_State* L); - - int GetBearing(lua_State* L); - - int GetBoundingBox(lua_State* L); - - love::GlyphData* CheckGlyphData(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_GlyphData diff --git a/include/objects/glyphdata/wrap_glyphdata.hpp b/include/objects/glyphdata/wrap_glyphdata.hpp new file mode 100644 index 000000000..c9d721a4b --- /dev/null +++ b/include/objects/glyphdata/wrap_glyphdata.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include + +namespace Wrap_GlyphData +{ + int Clone(lua_State* L); + + int GetWidth(lua_State* L); + + int GetHeight(lua_State* L); + + int GetDimensions(lua_State* L); + + int GetGlyph(lua_State* L); + + int GetGlyphString(lua_State* L); + + int GetAdvance(lua_State* L); + + int GetBearing(lua_State* L); + + int GetBoundingBox(lua_State* L); + + int GetFormat(lua_State* L); + + love::GlyphData* CheckGlyphData(lua_State* L, int index); + + int Register(lua_State* L); +} // namespace Wrap_GlyphData diff --git a/include/objects/image/image.h b/include/objects/image/image.h deleted file mode 100644 index 1a0bb4eff..000000000 --- a/include/objects/image/image.h +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include "common/lmath.h" -#include "modules/filesystem/wrap_filesystem.h" -#include "objects/texture/texture.h" - -#include "objects/compressedimagedata/compressedimagedata.h" -#include "objects/imagedata/imagedata.h" - -namespace love -{ - class Image : public Texture - { - public: - static love::Type type; - - enum MipmapsType - { - MIPMAPS_NONE, - MIPMAPS_DATA, - MIPMAPS_GENERATED, - }; - - struct Slices - { - public: - Slices(TextureType type); - - void Clear(); - - void Set(int slice, int mipmap, ImageDataBase* data); - - ImageDataBase* Get(int slice, int mipmap) const; - - void Add(CompressedImageData* data, int start, int startmip, bool addAllSlices, - bool addAllMips); - - int GetSliceCount(int mip = 0) const; - - int GetMipmapCount(int slice = 0) const; - - MipmapsType Validate() const; - - TextureType GetTextureType() const - { - return this->textureType; - } - - private: - TextureType textureType; - std::vector>> data; - }; - - void ReplacePixels(const void* data, size_t size, const Rect& rect); - - ~Image(); - - Image(TextureType type, PixelFormat format, int width, int height, int slices); - - void Init(ImageDataBase* data); - - void Init(PixelFormat format, int width, int height); - - Image(const Slices& data); - - protected: - PixelFormat format; - Slices data; - MipmapsType mipmapsType; - bool sRGB; - - private: - Image(const Slices& data, bool validate); - - TextureType textureType; - }; -} // namespace love diff --git a/include/objects/image/wrap_image.h b/include/objects/image/wrap_image.h deleted file mode 100644 index 10bf58804..000000000 --- a/include/objects/image/wrap_image.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "common/luax.h" - -#include "objects/image/image.h" -#include "objects/texture/wrap_texture.h" - -namespace Wrap_Image -{ - int GetDimensions(lua_State* L); - - int GetFilter(lua_State* L); - - int GetHeight(lua_State* L); - - int GetWidth(lua_State* L); - - int GetWrap(lua_State* L); - - int SetFilter(lua_State* L); - - int SetWrap(lua_State* L); - - love::Image* CheckImage(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Image diff --git a/include/objects/imagedata/handlers/jpghandler.h b/include/objects/imagedata/handlers/jpghandler.h deleted file mode 100644 index edbcceb0e..000000000 --- a/include/objects/imagedata/handlers/jpghandler.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "objects/imagedata/types/formathandler.h" - -namespace love -{ - class JPGHandler : public FormatHandler - { - public: - virtual bool CanDecode(Data* data); - - virtual DecodedImage Decode(Data* data); - - virtual void FreeRawPixels(unsigned char* memory); - - virtual const char* GetName() - { - return "JPGHandler"; - } - }; -} // namespace love diff --git a/include/objects/imagedata/handlers/pnghandler.h b/include/objects/imagedata/handlers/pnghandler.h deleted file mode 100644 index 8ac53fe73..000000000 --- a/include/objects/imagedata/handlers/pnghandler.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "objects/imagedata/types/formathandler.h" - -namespace love -{ - class PNGHandler : public FormatHandler - { - public: - virtual bool CanDecode(Data* data); - - virtual bool CanEncode(PixelFormat rawFormat, EncodedFormat encodedFormat); - - virtual DecodedImage Decode(Data* data); - - virtual EncodedImage Encode(const DecodedImage& image, EncodedFormat format); - - virtual void FreeRawPixels(unsigned char* memory); - - virtual const char* GetName() - { - return "PNGHandler"; - } - }; -} // namespace love diff --git a/include/objects/imagedata/handlers/t3xhandler.h b/include/objects/imagedata/handlers/t3xhandler.h deleted file mode 100644 index fe8299b19..000000000 --- a/include/objects/imagedata/handlers/t3xhandler.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include "objects/imagedata/types/formathandler.h" - -namespace love -{ - class T3XHandler : public FormatHandler - { - public: -#if not defined(__3DS__) - using FormatHandler::CanDecode; - using FormatHandler::Decode; -#else - virtual bool CanDecode(Data* data); - - virtual DecodedImage Decode(Data* data); -#endif - virtual void FreeRawPixels(unsigned char* memory); - - virtual const char* GetName() - { - return "T3XHandler"; - } - - private: - struct __attribute__((packed)) Tex3DSHeader - { - uint16_t numSubTextures; //< 1 - - uint8_t width_log2 : 3; //< log2(texWidth) - 3 (powTwoWidth) - uint8_t height_log2 : 3; //< log2(texHeight) - 3 (powTwoHeight) - - uint8_t type : 1; //< 0 = GPU_TEX_2D - uint8_t format; //< 0 = GPU_RGBA8 - uint8_t mipmapLevels; //< 0 - - uint16_t width; //< subtexWidth (sourceWidth) - uint16_t height; //< subtexHeight (sourceHeight) - - uint16_t left; //< left = border - uint16_t top; //< top = texHeight - border - uint16_t right; //< right = subtexWidth + border - uint16_t bottom; //< bottom = texHeight - border - subtexHeight - }; - }; -} // namespace love diff --git a/include/objects/imagedata/imagedata.h b/include/objects/imagedata/imagedata.h deleted file mode 100644 index 0b6d6e128..000000000 --- a/include/objects/imagedata/imagedata.h +++ /dev/null @@ -1,121 +0,0 @@ -#pragma once - -#include "common/colors.h" -#include "common/data.h" -#include "common/pixelformat.h" - -#include "objects/filedata/filedata.h" -#include "objects/imagedata/imagedatabase.h" - -#include "objects/imagedata/types/formathandler.h" - -#include "thread/types/mutex.h" - -#include - -namespace love -{ - class ImageData : public ImageDataBase - { - public: - union Pixel - { - uint8_t rgba8[4]; - uint16_t rgba16[4]; - // float16_t rgba16f[4]; - float rgba32f[4]; - uint16_t packed16; - uint32_t packed32; - }; - - typedef void (*PixelSetFunction)(const Colorf& c, Pixel* p); - typedef void (*PixelGetFunction)(const Pixel* p, Colorf& c); - - static love::Type type; - - ImageData(Data* data); - -#if defined(__SWITCH__) - ImageData(int width, int height, PixelFormat format = PIXELFORMAT_RGBA8); -#elif defined(__3DS__) - ImageData(int width, int height, PixelFormat format = PIXELFORMAT_TEX3DS_RGBA8); -#endif - ImageData(int width, int height, PixelFormat format, void* data, bool own); - - ImageData(const ImageData& other); - - virtual ~ImageData(); - - void Paste(ImageData* source, int dx, int dy, int sx, int sy, int sw, int sh); - - bool Inside(int x, int y) const; - - /* TODO: SetPixel and GetPixel differ between 3DS and Switch! */ - void SetPixel(int x, int y, const Colorf& color); - - void GetPixel(int x, int y, Colorf& color) const; - - Colorf GetPixel(int x, int y) const; - - PixelSetFunction getPixelSetFunction() const - { - return pixelSetFunction; - } - - PixelGetFunction getPixelGetFunction() const - { - return pixelGetFunction; - } - - FileData* Encode(FormatHandler::EncodedFormat encodedFormat, const char* filename, - bool writefile) const; - - thread::Mutex* GetMutex() const; - - ImageData* Clone() const override; - - void* GetData() const override; - - size_t GetSize() const override; - - bool IsSRGB() const override; - - size_t GetPixelSize() const; - - static bool ValidatePixelFormat(PixelFormat format); - - static bool CanPaste(PixelFormat source, PixelFormat destination); - - static bool GetConstant(const char* in, FormatHandler::EncodedFormat& out); - - static bool GetConstant(FormatHandler::EncodedFormat in, const char*& out); - - static std::vector GetConstants(FormatHandler::EncodedFormat); - - static PixelSetFunction GetPixelSetFunction(PixelFormat format); - - static PixelGetFunction GetPixelGetFunction(PixelFormat format); - - private: - union Row - { - uint8_t* u8; - uint16_t* u16; - }; - - void Create(int width, int height, PixelFormat format, void* data = nullptr); - - void Decode(Data* data); - - uint8_t* data = nullptr; - - thread::MutexRef mutex; - - StrongReference decodeHandler; - - PixelSetFunction pixelSetFunction; - PixelGetFunction pixelGetFunction; - - bool initialized; - }; -} // namespace love diff --git a/include/objects/imagedata/imagedata.tcc b/include/objects/imagedata/imagedata.tcc new file mode 100644 index 000000000..dce31cfcf --- /dev/null +++ b/include/objects/imagedata/imagedata.tcc @@ -0,0 +1,500 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#if defined(__3DS__) + #include +#else + #include + #include + #include + #include + + #include + #include +#endif + +#include +#include + +namespace love +{ + template + class ImageData : public ImageDataBase + { + public: + union Pixel + { + uint8_t rgba8[4]; + uint16_t rgba16[4]; + float rgba32f[4]; + uint16_t packed16; + uint32_t packed32; + }; + + union Row + { + uint8_t* u8; + uint16_t* u16; + // float16_t* f16; + float_t* f32; + }; + + static inline Type type = Type("ImageData", &Object::type); + + typedef void (*PixelSetFunction)(const Color&, Pixel*); + typedef void (*PixelGetFunction)(const Pixel*, Color&); + + static void pasteRGBA8toRGBA16(Row src, Row dst, int w) + { + for (int i = 0; i < w * 4; i++) + dst.u16[i] = (uint16_t)src.u8[i] << 8u; + } + + static void pasteRGBA16toRGBA8(Row src, Row dst, int w) + { + for (int i = 0; i < w * 4; i++) + dst.u8[i] = src.u16[i] >> 8u; + } + + static void setPixelRGBA8(const Color& color, Pixel* pixel) + { + if (Console::Is(Console::CTR)) + { + pixel->packed32 = color.abgr(); + return; + } + + pixel->rgba8[0] = static_cast(std::clamp(color.r, 0, 1) * 0xFF); + pixel->rgba8[1] = static_cast(std::clamp(color.g, 0, 1) * 0xFF); + pixel->rgba8[2] = static_cast(std::clamp(color.b, 0, 1) * 0xFF); + pixel->rgba8[3] = static_cast(std::clamp(color.a, 0, 1) * 0xFF); + } + + static void getPixelRGBA8(const Pixel* pixel, Color& color) + { + if (Console::Is(Console::CTR)) + { + color = Color(pixel->packed32); + return; + } + + color.r = pixel->rgba8[0] / 0xFF; + color.g = pixel->rgba8[1] / 0xFF; + color.b = pixel->rgba8[2] / 0xFF; + color.a = pixel->rgba8[3] / 0xFF; + } + + static void setPixelRGBA16(const Color& color, Pixel* pixel) + { + pixel->rgba16[0] = + static_cast(std::clamp(color.r, 0, 1) * 0xFFFF + 0.5f); + pixel->rgba16[1] = + static_cast(std::clamp(color.b, 0, 1) * 0xFFFF + 0.5f); + pixel->rgba16[2] = + static_cast(std::clamp(color.g, 0, 1) * 0xFFFF + 0.5f); + pixel->rgba16[3] = + static_cast(std::clamp(color.a, 0, 1) * 0xFFFF + 0.5f); + } + + static void getPixelRGBA16(const Pixel* pixel, Color& color) + { + color.r = pixel->rgba16[0] / 0xFFFF; + color.g = pixel->rgba16[1] / 0xFFFF; + color.b = pixel->rgba16[2] / 0xFFFF; + color.a = pixel->rgba16[3] / 0xFFFF; + } + + ImageData(Data* data) : ImageDataBase(PIXELFORMAT_UNKNOWN, 0, 0) + { + this->Decode(data); + } + + ImageData(int width, int height, PixelFormat format = PIXELFORMAT_RGBA8_UNORM) : + ImageDataBase(format, width, height) + { + const char* formatName = love::GetPixelFormatName(format); + if (!ValidPixelFormat(format)) + throw love::Exception("ImageData does not support the %s pixel format", formatName); + + this->Create(width, height, format); + std::memset(this->data.get(), 0, this->GetSize()); + } + + ImageData(int width, int height, PixelFormat format, void* data, bool own) : + ImageDataBase(format, width, height) + { + const char* formatName = love::GetPixelFormatName(format); + if (!ValidPixelFormat(format)) + throw love::Exception("ImageData does not support the %s pixel format", formatName); + + if (own) + this->data.reset((uint8_t*)data); + else + this->Create(width, height, format, data); + } + + ImageData(const ImageData& other) : ImageDataBase(other.format, other.width, other.height) + { + this->Create(width, height, format, other.GetData()); + } + + virtual ~ImageData() + {} + + void CopyBytes(const void* buffer, const size_t size) + { + if (buffer != nullptr) + std::memcpy(this->data.get(), buffer, size); + } + + template + void CopyBytesTiled(const void* buffer, const int width, const int height) + { + if (width % 8 != 0 && height % 8 != 0) + throw love::Exception("Cannot create ImageData that is not a multiple of 8."); + + const auto powTwoWidth = NextPo2(width); + + V* destination = (V*)this->data.get(); + V* source = (V*)buffer; + + for (int j = 0; j < height; j += 8) + { + std::memcpy(destination, source, width * 8 * sizeof(V)); + + source += width * 8; + destination += powTwoWidth * 8; + } + } + + static bool Outside(int destX, int destY, int destWidth, int destHeight, const Rect& source) + { + return source.x >= source.w || source.x + source.w < 0 || source.y >= source.h || + source.y + source.h < 0 || destX >= destWidth || destX + destWidth < 0 || + destY >= destHeight || destY + destHeight < 0; + } + + void AdjustPaste(const ImageData* source, int& x, int& y, const int width, const int height, + Rect& sourceRect) + { + if (Outside(x, y, width, height, sourceRect)) + return; + + // Normalize values to the inside of both images. + if (x < 0) + { + sourceRect.w += x; + sourceRect.x -= x; + x = 0; + } + + if (y < 0) + { + sourceRect.h += y; + sourceRect.y -= y; + y = 0; + } + + if (sourceRect.x < 0) + { + sourceRect.w += sourceRect.x; + x -= sourceRect.x; + sourceRect.x = 0; + } + + if (sourceRect.y < 0) + { + sourceRect.h += sourceRect.y; + y -= sourceRect.y; + sourceRect.y = 0; + } + + sourceRect.w = std::min(sourceRect.w, width - x); + sourceRect.h = std::min(sourceRect.h, height - y); + + sourceRect.w = std::min(sourceRect.w, source->GetWidth() - sourceRect.x); + sourceRect.h = std::min(sourceRect.h, source->GetHeight() - sourceRect.y); + } + + void Paste(ImageData* source, int x, int y, Rect& sourceRect) + { + PixelFormat destFormat = this->GetFormat(); + PixelFormat srcFormat = source->GetFormat(); + + const auto srcWidth = source->GetWidth(); + const auto srcHeight = source->GetHeight(); + + const auto destWidth = this->GetWidth(); + const auto destHeight = this->GetHeight(); + + size_t srcPixelSize = source->GetPixelSize(); + size_t dstPixelSize = this->GetPixelSize(); + + this->AdjustPaste(source, x, y, destWidth, destHeight, sourceRect); + + std::unique_lock lock(source->mutex); + std::unique_lock other(this->mutex); + + uint8_t* srcData = (uint8_t*)source->GetData(); + uint8_t* dstData = (uint8_t*)this->GetData(); + + auto getFunction = source->pixelGetFunction; + auto setFunction = this->pixelSetFunction; + + if (srcFormat == destFormat && (sourceRect.w == destWidth && destWidth == srcWidth && + sourceRect.h == destHeight && destHeight == srcHeight)) + { + std::memcpy(dstData, srcData, source->GetSize()); + } + else if (sourceRect.w > 0) + { + // Otherwise, copy each row individually. + for (int i = 0; i < sourceRect.h; i++) + { + Row rowsrc = { srcData + + (sourceRect.x + (i + sourceRect.y) * srcWidth) * srcPixelSize }; + Row rowdst = { dstData + (x + (i + y) * destWidth) * dstPixelSize }; + + if (srcFormat == destFormat) + std::memcpy(rowdst.u8, rowsrc.u8, srcPixelSize * sourceRect.w); + else + { + // Slow path: convert src -> Colorf -> dst. + Color color {}; + for (int x = 0; x < sourceRect.w; x++) + { + auto srcPixel = (const Pixel*)(rowsrc.u8 + x * srcPixelSize); + auto dstPixel = (Pixel*)(rowdst.u8 + x * dstPixelSize); + + getFunction(srcPixel, color); + setFunction(color, dstPixel); + } + } + } + } + } + + void SetPixel(int x, int y, const Color& color) + { + if (!this->Inside(x, y)) + throw love::Exception("Attempt to set out-of-range pixel!"); + + size_t size = this->GetPixelSize(); + Pixel* pixel = nullptr; + + if (Console::Is(Console::CTR)) + { + const auto _width = NextPo2(this->width); + pixel = (Pixel*)Color::FromTile(this->data.get(), _width, { (float)x, (float)y }); + } + else + pixel = (Pixel*)(this->data.get() + (y * this->width + x) * size); + + if (this->pixelSetFunction == nullptr) + { + const char* formatName = love::GetPixelFormatName(this->format); + throw love::Exception("ImageData:setPixel does not support the %s pixel format", + formatName); + } + + std::unique_lock lock(this->mutex); + this->pixelSetFunction(color, pixel); + } + + virtual void GetPixel(int x, int y, Color& color) const + { + if (!this->Inside(x, y)) + throw love::Exception("Attempt to set out-of-range pixel!"); + + size_t size = this->GetPixelSize(); + Pixel* pixel = nullptr; + + if (Console::Is(Console::CTR)) + { + const auto _width = NextPo2(this->width); + pixel = (Pixel*)Color::FromTile(this->data.get(), _width, { (float)x, (float)y }); + } + else + pixel = (Pixel*)(this->data.get() + (y * this->width + x) * size); + + if (this->pixelGetFunction == nullptr) + { + const char* formatName = love::GetPixelFormatName(this->format); + throw love::Exception("ImageData:getPixel does not support the %s pixel format", + formatName); + } + + std::unique_lock lock(this->mutex); + this->pixelGetFunction(pixel, color); + } + + virtual Color GetPixel(int x, int y) const + { + Color color {}; + this->GetPixel(x, y, color); + + return color; + } + + FileData* Encode(FormatHandler::EncodedFormat format, const char* filename, + bool writeFile) const; + + bool IsSRGB() const override + { + return false; + } + + PixelFormat GetFormat() const + { + return this->format; + } + + int GetWidth() const + { + return this->width; + } + + int GetHeight() const + { + return this->height; + } + + size_t GetSize() const override + { + return this->width * this->height * this->GetPixelSize(); + } + + void* GetData() const override + { + return (void*)this->data.get(); + } + + bool Inside(int x, int y) const + { + return x >= 0 && x < this->GetWidth() && y >= 0 && y < this->GetHeight(); + } + + size_t GetPixelSize() const + { + return GetPixelFormatBlockSize(this->format); + } + + static bool ValidPixelFormat(PixelFormat format) + { + bool isColorFormat = IsPixelFormatColor(format); + bool isCompressedFormat = IsPixelFormatCompressed(format); + + return isColorFormat && !isCompressedFormat; + } + + PixelSetFunction GetPixelSetFunction() + { + return this->pixelSetFunction; + } + + PixelGetFunction GetPixelGetFunction() + { + return this->pixelGetFunction; + } + + PixelGetFunction GetPixelGetFunction(PixelFormat format) + { + switch (format) + { + case PIXELFORMAT_RGBA8_UNORM: + return getPixelRGBA8; + case PIXELFORMAT_RGBA16_UNORM: + return getPixelRGBA16; + default: + return nullptr; + } + } + + PixelSetFunction GetPixelSetFunction(PixelFormat format) + { + switch (format) + { + case PIXELFORMAT_RGBA8_UNORM: + return setPixelRGBA8; + case PIXELFORMAT_RGBA16_UNORM: + return setPixelRGBA16; + default: + return nullptr; + } + } + + love::mutex& GetMutex() const + { + return this->mutex; + } + + // clang-format off + static constexpr BidirectionalMap encodedFormats = { + "png", FormatHandler::ENCODED_PNG, + "exr", FormatHandler::ENCODED_EXR + }; + // clang-format on + + protected: + void Create(int width, int height, PixelFormat format, void* data = nullptr) + { + const auto size = GetPixelFormatSliceSize(format, width, height); + + try + { + this->data = std::make_unique(size); + std::memset(this->data.get(), 0, size); + } + catch (std::bad_alloc&) + { + throw love::Exception("Out of memory."); + } + + if (data) + { + if (Console::Is(Console::CTR)) + { + if (width % 8 != 0 && height % 8 != 0) + throw love::Exception( + "Cannot create ImageData that is not a multiple of 8."); + + if (format == PIXELFORMAT_RGB565_UNORM) + this->CopyBytesTiled(data, width, height); + else + this->CopyBytesTiled(data, width, height); + } + else + std::copy_n((uint8_t*)data, size, this->data.get()); + } + + this->decoder = nullptr; + this->format = format; + + this->pixelSetFunction = GetPixelSetFunction(format); + this->pixelGetFunction = GetPixelGetFunction(format); + } + + void Decode(Data* data); + + std::unique_ptr data; + mutable love::mutex mutex; + StrongReference decoder; + + PixelSetFunction pixelSetFunction; + PixelGetFunction pixelGetFunction; + }; +} // namespace love diff --git a/include/objects/imagedata/imagedatabase.h b/include/objects/imagedata/imagedatabase.h deleted file mode 100644 index ecb368c0b..000000000 --- a/include/objects/imagedata/imagedatabase.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "common/data.h" -#include "common/pixelformat.h" - -namespace love -{ - class ImageDataBase : public Data - { - public: - virtual ~ImageDataBase() {}; - - PixelFormat GetFormat() const; - - int GetWidth() const; - - int GetHeight() const; - - virtual bool IsSRGB() const = 0; - - protected: - ImageDataBase(PixelFormat format, int width, int height); - - PixelFormat format; - int width; - int height; - }; -} // namespace love diff --git a/include/objects/imagedata/imagedatabase.hpp b/include/objects/imagedata/imagedatabase.hpp new file mode 100644 index 000000000..c002157ac --- /dev/null +++ b/include/objects/imagedata/imagedatabase.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +namespace love +{ + class ImageDataBase : public Data + { + public: + virtual ~ImageDataBase() + {} + + PixelFormat GetFormat() const; + + int GetWidth() const; + + int GetHeight() const; + + virtual bool IsSRGB() const = 0; + + protected: + ImageDataBase(PixelFormat format, int width, int height); + + PixelFormat format; + int width; + int height; + }; +} // namespace love \ No newline at end of file diff --git a/include/objects/imagedata/types/formathandler.h b/include/objects/imagedata/types/formathandler.h deleted file mode 100644 index 367a4ffbb..000000000 --- a/include/objects/imagedata/types/formathandler.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include "common/data.h" -#include "common/pixelformat.h" - -#include "objects/compressedimagedata/types/compressedmemory.h" -#include "objects/compressedimagedata/types/compressedslice.h" - -#include "objects/object.h" - -#include - -namespace love -{ - class FormatHandler : public Object - { - public: - enum EncodedFormat - { - ENCODED_TGA, - ENCODED_PNG, - ENCODED_MAX_ENUM - }; - - struct DecodedImage - { - PixelFormat format = PIXELFORMAT_RGBA8; - - int width = 0; - int height = 0; - size_t size = 0; - -#if defined(__3DS__) - int subWidth = 0; - int subHeight = 0; -#endif - - unsigned char* data = nullptr; - }; - - struct EncodedImage - { - size_t size = 0; - unsigned char* data = nullptr; - }; - - FormatHandler(); - - virtual ~FormatHandler(); - - virtual bool CanDecode(Data* data); - - virtual bool CanEncode(PixelFormat rawFormat, EncodedFormat encodedFormat); - - virtual DecodedImage Decode(Data* data); - - virtual EncodedImage Encode(const DecodedImage& image, EncodedFormat format); - - virtual const char* GetName() - { - return "FormatHandler"; - } - - virtual bool CanParseCompressed(Data* data); - - virtual StrongReference ParseCompressed( - Data* filedata, std::vector>& images, - PixelFormat& format, bool& sRGB); - - virtual void FreeRawPixels(unsigned char* memory); - }; -} // namespace love diff --git a/include/objects/imagedata/wrap_imagedata.h b/include/objects/imagedata/wrap_imagedata.h deleted file mode 100644 index 5679c8a88..000000000 --- a/include/objects/imagedata/wrap_imagedata.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/imagedata/imagedata.h" - -namespace Wrap_ImageData -{ - int Clone(lua_State* L); - - int GetFormat(lua_State* L); - - int GetWidth(lua_State* L); - - int GetHeight(lua_State* L); - - int GetDimensions(lua_State* L); - - int GetPixel(lua_State* L); - - int SetPixel(lua_State* L); - - int _MapPixelUnsafe(lua_State* L); - - int Paste(lua_State* L); - - int Encode(lua_State* L); - - int _PerformAtomic(lua_State* L); - - love::ImageData* CheckImageData(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_ImageData diff --git a/include/objects/imagedata/wrap_imagedata.hpp b/include/objects/imagedata/wrap_imagedata.hpp new file mode 100644 index 000000000..a7f7f7f60 --- /dev/null +++ b/include/objects/imagedata/wrap_imagedata.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +namespace Wrap_ImageData +{ + love::ImageData* CheckImageData(lua_State* L, int index); + + int Clone(lua_State* L); + + int GetFormat(lua_State* L); + + int GetWidth(lua_State* L); + + int GetHeight(lua_State* L); + + int GetDimensions(lua_State* L); + + int GetPixel(lua_State* L); + + int SetPixel(lua_State* L); + + int __MapPixelUnsafe(lua_State* L); + + int Paste(lua_State* L); + + int Encode(lua_State* L); + + int __PerformAtomic(lua_State* L); + + int Register(lua_State* L); + + extern std::span extensions; +} // namespace Wrap_ImageData diff --git a/include/objects/imagerasterizer/imagerasterizer.hpp b/include/objects/imagerasterizer/imagerasterizer.hpp new file mode 100644 index 000000000..a6abf3386 --- /dev/null +++ b/include/objects/imagerasterizer/imagerasterizer.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include +#include + +#include + +namespace love +{ + class ImageRasterizer : public Rasterizer + { + public: + ImageRasterizer(ImageData* data, const uint32_t* glyphs, int glyphCount, + int extraSpacing, float dpiScale); + + virtual ~ImageRasterizer() + {} + + int GetLineHeight() const override; + + int GetGlyphSpacing(uint32_t glyph) const override; + + int GetGlyphIndex(uint32_t glyph) const override; + + GlyphData* GetGlyphDataForIndex(int index) const override; + + int GetGlyphCount() const override; + + bool HasGlyph(uint32_t glyph) const override; + + DataType GetDataType() const override; + + TextShaper* NewTextShaper() override; + + private: + struct ImageGlyphData + { + int x; + int width; + uint32_t glyph; + }; + + void Load(const uint32_t* glyphs, int glyphCount); + + StrongReference> imageData; + + int glyphCount; + int extraSpacing; + + std::vector imageGlyphs; + std::map glyphIndicies; + + Color32 spacer; + }; +} // namespace love \ No newline at end of file diff --git a/include/objects/joint/joint.hpp b/include/objects/joint/joint.hpp new file mode 100644 index 000000000..3091d91d7 --- /dev/null +++ b/include/objects/joint/joint.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace love +{ + class Body; + class World; + + class Joint : public Object + { + public: + static love::Type type; + + friend class GearJoint; + + enum Type + { + JOINT_INVALID, + JOINT_DISTANCE, + JOINT_REVOLUTE, + JOINT_PRISMATIC, + JOINT_MOUSE, + JOINT_PULLEY, + JOINT_GEAR, + JOINT_FRICTION, + JOINT_WELD, + JOINT_WHEEL, + JOINT_ROPE, + JOINT_MOTOR, + JOINT_MAX_ENUM + }; + + Joint(Body* body); + + Joint(Body* a, Body* b); + + virtual ~Joint(); + + bool IsValid() const; + + Joint::Type GetType() const; + + virtual Body* GetBodyA() const; + + virtual Body* GetBodyB() const; + + int GetAnchors(lua_State* L); + + int GetReactionForce(lua_State* L); + + float GetReactionTorque(float delta); + + bool IsEnabled() const; + + bool GetCollideConnected() const; + + int SetUserdata(lua_State* L); + + int GetUserdata(lua_State* L); + + void DestroyJoint(bool implicit = false); + + // clang-format off + static constexpr BidirectionalMap jointTypes = + { + "distance", JOINT_DISTANCE, + "revolute", JOINT_REVOLUTE, + "prismatic", JOINT_PRISMATIC, + "mouse", JOINT_MOUSE, + "pulley", JOINT_PULLEY, + "gear", JOINT_GEAR, + "friction", JOINT_FRICTION, + "weld", JOINT_WELD, + "wheel", JOINT_WHEEL, + "rope", JOINT_ROPE, + "motor", JOINT_MOTOR + }; + // clang-format on + + protected: + b2Joint* CreateJoint(b2JointDef* def); + World* world; + Reference* reference = nullptr; + + private: + Body* bodyA; + Body* bodyB; + + b2Joint* joint; + }; +} // namespace love diff --git a/include/objects/joint/types/distancejoint/distancejoint.hpp b/include/objects/joint/types/distancejoint/distancejoint.hpp new file mode 100644 index 000000000..570bfa0d2 --- /dev/null +++ b/include/objects/joint/types/distancejoint/distancejoint.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace love +{ + class DistanceJoint : public Joint + { + public: + static love::Type type; + + DistanceJoint(Body* a, Body* b, float x1, float y1, float x2, float y2, + bool collideConnected); + + virtual ~DistanceJoint(); + + void SetLength(float length); + + float GetLength() const; + + void SetStiffness(float k); + + float GetStiffness() const; + + void SetDamping(float ratio); + + float GetDamping() const; + + private: + b2DistanceJoint* joint; + }; +} // namespace love diff --git a/include/objects/joint/types/distancejoint/wrap_distancejoint.hpp b/include/objects/joint/types/distancejoint/wrap_distancejoint.hpp new file mode 100644 index 000000000..7deb2b099 --- /dev/null +++ b/include/objects/joint/types/distancejoint/wrap_distancejoint.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_DistanceJoint +{ + love::DistanceJoint* CheckDistanceJoint(lua_State* L, int index); + + int SetLength(lua_State* L); + + int GetLength(lua_State* L); + + int SetStiffness(lua_State* L); + + int GetStiffness(lua_State* L); + + int SetDamping(lua_State* L); + + int GetDamping(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_DistanceJoint diff --git a/include/objects/joint/types/frictionjoint/frictionjoint.hpp b/include/objects/joint/types/frictionjoint/frictionjoint.hpp new file mode 100644 index 000000000..42bc7140a --- /dev/null +++ b/include/objects/joint/types/frictionjoint/frictionjoint.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace love +{ + class FrictionJoint : public Joint + { + public: + static love::Type type; + + FrictionJoint(Body* a, Body* b, float x1, float y1, float x2, float y2, + bool collideConnected); + + virtual ~FrictionJoint(); + + void SetMaxForce(float force); + + float GetMaxForce() const; + + void SetMaxTorque(float torque); + + float GetMaxTorque() const; + + private: + b2FrictionJoint* joint; + }; +} // namespace love diff --git a/include/objects/joint/types/frictionjoint/wrap_frictionjoint.hpp b/include/objects/joint/types/frictionjoint/wrap_frictionjoint.hpp new file mode 100644 index 000000000..2756e8b30 --- /dev/null +++ b/include/objects/joint/types/frictionjoint/wrap_frictionjoint.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_FrictionJoint +{ + love::FrictionJoint* CheckFrictionJoint(lua_State* L, int index); + + int SetMaxForce(lua_State* L); + + int GetMaxForce(lua_State* L); + + int SetMaxTorque(lua_State* L); + + int GetMaxTorque(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_FrictionJoint diff --git a/include/objects/joint/types/gearjoint/gearjoint.hpp b/include/objects/joint/types/gearjoint/gearjoint.hpp new file mode 100644 index 000000000..38715a12f --- /dev/null +++ b/include/objects/joint/types/gearjoint/gearjoint.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace love +{ + class GearJoint : public Joint + { + public: + static love::Type type; + + GearJoint(Joint* joint1, Joint* joint2, float ratio, bool collideConnected); + + virtual ~GearJoint(); + + void SetRatio(float ratio); + + float GetRatio() const; + + Joint* GetJointA() const; + + Joint* GetJointB() const; + + private: + b2GearJoint* joint; + }; +} // namespace love diff --git a/include/objects/joint/types/gearjoint/wrap_gearjoint.hpp b/include/objects/joint/types/gearjoint/wrap_gearjoint.hpp new file mode 100644 index 000000000..27a2eb467 --- /dev/null +++ b/include/objects/joint/types/gearjoint/wrap_gearjoint.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_GearJoint +{ + love::GearJoint* CheckGearJoint(lua_State* L, int index); + + int SetRatio(lua_State* L); + + int GetRatio(lua_State* L); + + int GetJoints(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_GearJoint diff --git a/include/objects/joint/types/motorjoint/motorjoint.hpp b/include/objects/joint/types/motorjoint/motorjoint.hpp new file mode 100644 index 000000000..980cd2552 --- /dev/null +++ b/include/objects/joint/types/motorjoint/motorjoint.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include + +namespace love +{ + class MotorJoint : public Joint + { + public: + static love::Type type; + + MotorJoint(Body* a, Body* b); + + MotorJoint(Body* a, Body* b, float correctionFactor, bool collideConnected); + + virtual ~MotorJoint(); + + void SetLinearOffset(float x, float y); + + int GetLinearOffset(lua_State* L) const; + + void SetAngularOffset(float angularOffset); + + float GetAngularOffset() const; + + void SetMaxForce(float force); + + float GetMaxForce() const; + + void SetMaxTorque(float torque); + + float GetMaxTorque() const; + + void SetCorrectionFactor(float factor); + + float GetCorrectionFactor() const; + + private: + b2MotorJoint* joint; + }; +} // namespace love diff --git a/include/objects/joint/types/motorjoint/wrap_motorjoint.hpp b/include/objects/joint/types/motorjoint/wrap_motorjoint.hpp new file mode 100644 index 000000000..7ae5ca5c2 --- /dev/null +++ b/include/objects/joint/types/motorjoint/wrap_motorjoint.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_MotorJoint +{ + love::MotorJoint* CheckMotorJoint(lua_State* L, int index); + + int SetLinearOffset(lua_State* L); + + int GetLinearOffset(lua_State* L); + + int SetAngularOffset(lua_State* L); + + int GetAngularOffset(lua_State* L); + + int SetMaxForce(lua_State* L); + + int GetMaxForce(lua_State* L); + + int SetMaxTorque(lua_State* L); + + int GetMaxTorque(lua_State* L); + + int SetCorrectionFactor(lua_State* L); + + int GetCorrectionFactor(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_MotorJoint diff --git a/include/objects/joint/types/mousejoint/mousejoint.hpp b/include/objects/joint/types/mousejoint/mousejoint.hpp new file mode 100644 index 000000000..3c1d6b43d --- /dev/null +++ b/include/objects/joint/types/mousejoint/mousejoint.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include + +namespace love +{ + class MouseJoint : public Joint + { + public: + static love::Type type; + + MouseJoint(Body* body, float x, float y); + + virtual ~MouseJoint(); + + void SetTarget(float x, float y); + + int GetTarget(lua_State* L); + + void SetMaxForce(float force); + + float GetMaxForce() const; + + void SetFrequency(float hz); + + float GetFrequency() const; + + void SetDampingRatio(float ratio); + + float GetDampingRatio() const; + + void SetStiffness(float stiffness); + + float GetStiffness() const; + + void SetDamping(float damping); + + float GetDamping() const; + + virtual Body* GetBodyA() const; + + virtual Body* GetBodyB() const; + + private: + b2MouseJoint* joint; + }; +} // namespace love diff --git a/include/objects/joint/types/mousejoint/wrap_mousejoint.hpp b/include/objects/joint/types/mousejoint/wrap_mousejoint.hpp new file mode 100644 index 000000000..6712678ca --- /dev/null +++ b/include/objects/joint/types/mousejoint/wrap_mousejoint.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_MouseJoint +{ + love::MouseJoint* CheckMouseJoint(lua_State* L, int index); + + int SetTarget(lua_State* L); + + int GetTarget(lua_State* L); + + int SetMaxForce(lua_State* L); + + int GetMaxForce(lua_State* L); + + int SetStiffness(lua_State* L); + + int GetStiffness(lua_State* L); + + int SetDamping(lua_State* L); + + int GetDamping(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_MouseJoint diff --git a/include/objects/joint/types/prismaticjoint/prismaticjoint.hpp b/include/objects/joint/types/prismaticjoint/prismaticjoint.hpp new file mode 100644 index 000000000..0ac2e8015 --- /dev/null +++ b/include/objects/joint/types/prismaticjoint/prismaticjoint.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include + +namespace love +{ + class PrismaticJoint : public Joint + { + public: + static love::Type type; + + PrismaticJoint(Body* bodyA, Body* bodyB, float xA, float yA, float xB, float yB, float aX, + float aY, bool collideConnected); + + PrismaticJoint(Body* bodyA, Body* bodyB, float xA, float yA, float xB, float yB, float aX, + float aY, bool collideConnected, float referenceAngle); + + virtual ~PrismaticJoint(); + + float GetJointTranslation() const; + + float GetJointSpeed() const; + + void SetMotorEnabled(bool enable); + + bool IsMotorEnabled() const; + + void SetMaxMotorForce(float force); + + void SetMotorSpeed(float speed); + + float GetMotorSpeed() const; + + float GetMotorForce(float inverseDelta) const; + + float GetMaxMotorForce() const; + + void SetLimitsEnabled(bool enable); + + bool AreLimitsEnabled() const; + + void SetUpperLimit(float limit); + + void SetLowerLimit(float limit); + + void SetLimits(float lower, float upper); + + float GetUpperLimit() const; + + float GetLowerLimit() const; + + int GetLimits(lua_State* L); + + int GetAxis(lua_State* L); + + float GetReferenceAngle() const; + + private: + b2PrismaticJoint* joint; + + void Init(b2PrismaticJointDef& definition, Body* bodyA, Body* bodyB, float xA, float yA, + float xB, float yB, float aX, float aY, bool collideConnected); + }; +} // namespace love diff --git a/include/objects/joint/types/prismaticjoint/wrap_prismaticjoint.hpp b/include/objects/joint/types/prismaticjoint/wrap_prismaticjoint.hpp new file mode 100644 index 000000000..5391fdd7c --- /dev/null +++ b/include/objects/joint/types/prismaticjoint/wrap_prismaticjoint.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_PrismaticJoint +{ + love::PrismaticJoint* CheckPrismaticJoint(lua_State* L, int index); + + int GetJointTranslation(lua_State* L); + + int GetJointSpeed(lua_State* L); + + int SetMotorEnabled(lua_State* L); + + int IsMotorEnabled(lua_State* L); + + int SetMaxMotorForce(lua_State* L); + + int SetMotorSpeed(lua_State* L); + + int GetMotorSpeed(lua_State* L); + + int GetMotorForce(lua_State* L); + + int GetMaxMotorForce(lua_State* L); + + int SetLimitsEnabled(lua_State* L); + + int AreLimitsEnabled(lua_State* L); + + int SetUpperLimit(lua_State* L); + + int SetLowerLimit(lua_State* L); + + int SetLimits(lua_State* L); + + int GetUpperLimit(lua_State* L); + + int GetLowerLimit(lua_State* L); + + int GetLimits(lua_State* L); + + int GetAxis(lua_State* L); + + int GetReferenceAngle(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_PrismaticJoint diff --git a/include/objects/joint/types/pulleyjoint/pulleyjoint.hpp b/include/objects/joint/types/pulleyjoint/pulleyjoint.hpp new file mode 100644 index 000000000..30eabe2cb --- /dev/null +++ b/include/objects/joint/types/pulleyjoint/pulleyjoint.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace love +{ + class PulleyJoint : public Joint + { + public: + static love::Type type; + + PulleyJoint(Body* bodyA, Body* bodyB, b2Vec2 groundAnchorA, b2Vec2 groundAnchorB, + b2Vec2 anchorA, b2Vec2 anchorB, float ratio, bool collideConnected); + + virtual ~PulleyJoint(); + + int GetGroundAnchors(lua_State* L); + + float GetLengthA() const; + + float GetLengthB() const; + + float GetRatio() const; + + private: + b2PulleyJoint* joint; + }; +} // namespace love diff --git a/include/objects/joint/types/pulleyjoint/wrap_pulleyjoint.hpp b/include/objects/joint/types/pulleyjoint/wrap_pulleyjoint.hpp new file mode 100644 index 000000000..54f6a081b --- /dev/null +++ b/include/objects/joint/types/pulleyjoint/wrap_pulleyjoint.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_PulleyJoint +{ + love::PulleyJoint* CheckPulleyJoint(lua_State* L, int index); + + int GetGroundAnchors(lua_State* L); + + int GetLengthA(lua_State* L); + + int GetLengthB(lua_State* L); + + int GetRatio(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_PulleyJoint diff --git a/include/objects/joint/types/revolutejoint/revolutejoint.hpp b/include/objects/joint/types/revolutejoint/revolutejoint.hpp new file mode 100644 index 000000000..33d113d21 --- /dev/null +++ b/include/objects/joint/types/revolutejoint/revolutejoint.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include + +namespace love +{ + class RevoluteJoint : public Joint + { + public: + static love::Type type; + + RevoluteJoint(Body* bodyA, Body* bodyB, float xA, float yA, float xB, float yB, + bool collideConnected); + + RevoluteJoint(Body* bodyA, Body* bodyB, float xA, float yA, float xB, float yB, + bool collideConnected, float referenceAngle); + + virtual ~RevoluteJoint(); + + float GetJointAngle() const; + + float GetJointSpeed() const; + + void SetMotorEnabled(bool enable); + + bool IsMotorEnabled() const; + + void SetMaxMotorTorque(float torque); + + void SetMotorSpeed(float speed); + + float GetMotorSpeed() const; + + float GetMotorTorque(float delta) const; + + float GetMaxMotorTorque() const; + + void SetLimitsEnabled(bool enable); + + bool AreLimitsEnabled() const; + + void SetUpperLimit(float limit); + + void SetLowerLimit(float limit); + + void SetLimits(float lower, float upper); + + float GetUpperLimit() const; + + float GetLowerLimit() const; + + int GetLimits(lua_State* L); + + float GetReferenceAngle() const; + + private: + b2RevoluteJoint* joint; + + void Init(b2RevoluteJointDef& definition, Body* bodyA, Body* bodyB, float xA, float yA, + float xB, float yB, bool collideConnected); + }; +} // namespace love diff --git a/include/objects/joint/types/revolutejoint/wrap_revolutejoint.hpp b/include/objects/joint/types/revolutejoint/wrap_revolutejoint.hpp new file mode 100644 index 000000000..3f80bc767 --- /dev/null +++ b/include/objects/joint/types/revolutejoint/wrap_revolutejoint.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_RevoluteJoint +{ + love::RevoluteJoint* CheckRevoluteJoint(lua_State* L, int index); + + int GetJointAngle(lua_State* L); + + int GetJointSpeed(lua_State* L); + + int SetMotorEnabled(lua_State* L); + + int IsMotorEnabled(lua_State* L); + + int SetMaxMotorTorque(lua_State* L); + + int SetMotorSpeed(lua_State* L); + + int GetMotorSpeed(lua_State* L); + + int GetMotorTorque(lua_State* L); + + int GetMaxMotorTorque(lua_State* L); + + int SetLimitsEnabled(lua_State* L); + + int AreLimitsEnabled(lua_State* L); + + int SetUpperLimit(lua_State* L); + + int SetLowerLimit(lua_State* L); + + int SetLimits(lua_State* L); + + int GetUpperLimit(lua_State* L); + + int GetLowerLimit(lua_State* L); + + int GetLimits(lua_State* L); + + int GetReferenceAngle(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_RevoluteJoint diff --git a/include/objects/joint/types/ropejoint/ropejoint.hpp b/include/objects/joint/types/ropejoint/ropejoint.hpp new file mode 100644 index 000000000..398f9aff7 --- /dev/null +++ b/include/objects/joint/types/ropejoint/ropejoint.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace love +{ + class RopeJoint : public Joint + { + public: + static love::Type type; + + RopeJoint(Body* bodyA, Body* bodyB, float x1, float y1, float x2, float y2, float maxLength, + bool collideConnected); + + virtual ~RopeJoint(); + + float GetMaxLength() const; + + void SetMaxLength(float maxLength); + + private: + b2DistanceJoint* joint; + }; +} // namespace love diff --git a/include/objects/joint/types/ropejoint/wrap_ropejoint.hpp b/include/objects/joint/types/ropejoint/wrap_ropejoint.hpp new file mode 100644 index 000000000..8811861cc --- /dev/null +++ b/include/objects/joint/types/ropejoint/wrap_ropejoint.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_RopeJoint +{ + love::RopeJoint* CheckRopeJoint(lua_State* L, int index); + + int GetMaxLength(lua_State* L); + + int SetMaxLength(lua_State* L); + + int Register(lua_State* L); +} diff --git a/include/objects/joint/types/weldjoint/weldjoint.hpp b/include/objects/joint/types/weldjoint/weldjoint.hpp new file mode 100644 index 000000000..87611c217 --- /dev/null +++ b/include/objects/joint/types/weldjoint/weldjoint.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace love +{ + class WeldJoint : public Joint + { + public: + static love::Type type; + + WeldJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, bool collideConnected); + + WeldJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, bool collideConnected, + float referenceAngle); + + virtual ~WeldJoint(); + + void SetStiffness(float k); + + float GetStiffness() const; + + void SetDamping(float ratio); + + float GetDamping() const; + + float GetReferenceAngle() const; + + private: + b2WeldJoint* joint; + + void Init(b2WeldJointDef& definition, Body* a, Body* b, float xA, float yA, float xB, + float yB, bool collideConnected); + }; +} // namespace love diff --git a/include/objects/joint/types/weldjoint/wrap_weldjoint.hpp b/include/objects/joint/types/weldjoint/wrap_weldjoint.hpp new file mode 100644 index 000000000..6336b3e7d --- /dev/null +++ b/include/objects/joint/types/weldjoint/wrap_weldjoint.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_WeldJoint +{ + love::WeldJoint* CheckWeldJoint(lua_State* L, int index); + + int SetStiffness(lua_State* L); + + int GetStiffness(lua_State* L); + + int SetDamping(lua_State* L); + + int GetDamping(lua_State* L); + + int GetReferenceAngle(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_WeldJoint diff --git a/include/objects/joint/types/wheeljoint/wheeljoint.hpp b/include/objects/joint/types/wheeljoint/wheeljoint.hpp new file mode 100644 index 000000000..85e63fed7 --- /dev/null +++ b/include/objects/joint/types/wheeljoint/wheeljoint.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include + +namespace love +{ + class WheelJoint : public Joint + { + public: + static love::Type type; + + WheelJoint(Body* a, Body* b, float xA, float yA, float xB, float yB, float aX, float aY, + bool collideConnected); + + virtual ~WheelJoint(); + + float GetJointTranslation() const; + + float GetJointSpeed() const; + + void SetMotorEnabled(bool enable); + + bool IsMotorEnabled() const; + + void SetMotorSpeed(float speed); + + float GetMotorSpeed() const; + + void SetMaxMotorTorque(float torque); + + float GetMaxMotorTorque() const; + + float GetMotorTorque(float dt) const; + + void SetStiffness(float k); + + float GetStiffness() const; + + void SetDamping(float damping); + + float GetDamping() const; + + int GetAxis(lua_State* L); + + private: + b2WheelJoint* joint; + }; +} // namespace love diff --git a/include/objects/joint/types/wheeljoint/wrap_wheeljoint.hpp b/include/objects/joint/types/wheeljoint/wrap_wheeljoint.hpp new file mode 100644 index 000000000..3f07e1de7 --- /dev/null +++ b/include/objects/joint/types/wheeljoint/wrap_wheeljoint.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_WheelJoint +{ + love::WheelJoint* CheckWheelJoint(lua_State* L, int index); + + int GetJointTranslation(lua_State* L); + + int GetJointSpeed(lua_State* L); + + int SetMotorEnabled(lua_State* L); + + int IsMotorEnabled(lua_State* L); + + int SetMotorSpeed(lua_State* L); + + int GetMotorSpeed(lua_State* L); + + int SetMaxMotorTorque(lua_State* L); + + int GetMaxMotorTorque(lua_State* L); + + int GetMotorTorque(lua_State* L); + + int SetStiffness(lua_State* L); + + int GetStiffness(lua_State* L); + + int SetDamping(lua_State* L); + + int GetDamping(lua_State* L); + + int GetAxis(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_WheelJoint diff --git a/include/objects/joint/wrap_joint.hpp b/include/objects/joint/wrap_joint.hpp new file mode 100644 index 000000000..e14aeb900 --- /dev/null +++ b/include/objects/joint/wrap_joint.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +namespace Wrap_Joint +{ + void PushJoint(lua_State* L, love::Joint* joint); + + love::Joint* CheckJoint(lua_State* L, int index); + + int GetType(lua_State* L); + + int GetBodies(lua_State* L); + + int GetAnchors(lua_State* L); + + int GetReactionForce(lua_State* L); + + int GetReactionTorque(lua_State* L); + + int GetCollideConnected(lua_State* L); + + int SetUserdata(lua_State* L); + + int GetUserdata(lua_State* L); + + int Destroy(lua_State* L); + + int IsDestroyed(lua_State* L); + + extern const luaL_Reg jointFunctions[0x0A]; + + int Register(lua_State* L); +} // namespace Wrap_Joint diff --git a/include/objects/joystick/joystick.tcc b/include/objects/joystick/joystick.tcc new file mode 100644 index 000000000..cf69a8894 --- /dev/null +++ b/include/objects/joystick/joystick.tcc @@ -0,0 +1,151 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +namespace love +{ + template + class Joystick : public Object + { + public: + static inline Type type = Type("Joystick", &Object::type); + + enum GamepadAxis + { + GAMEPAD_AXIS_LEFTY, + GAMEPAD_AXIS_LEFTX, + GAMEPAD_AXIS_RIGHTY, + GAMEPAD_AXIS_RIGHTX, + GAMEPAD_AXIS_TRIGGERLEFT, + GAMEPAD_AXIS_TRIGGERRIGHT, + GAMEPAD_AXIS_MAX_ENUM + }; + + enum GamepadButton + { + GAMEPAD_BUTTON_INVALID, + GAMEPAD_BUTTON_A, + GAMEPAD_BUTTON_B, + GAMEPAD_BUTTON_X, + GAMEPAD_BUTTON_Y, + GAMEPAD_BUTTON_BACK, + GAMEPAD_BUTTON_GUIDE, + GAMEPAD_BUTTON_START, + GAMEPAD_BUTTON_LEFTSTICK, + GAMEPAD_BUTTON_RIGHTSTICK, + GAMEPAD_BUTTON_LEFTSHOULDER, + GAMEPAD_BUTTON_RIGHTSHOULDER, + GAMEPAD_BUTTON_DPAD_UP, + GAMEPAD_BUTTON_DPAD_DOWN, + GAMEPAD_BUTTON_DPAD_LEFT, + GAMEPAD_BUTTON_DPAD_RIGHT, + }; + + enum InputType + { + INPUT_TYPE_AXIS, + INPUT_TYPE_BUTTON, + INPUT_TYPE_MAX_ENUM + }; + + struct JoystickInput + { + InputType type; + + GamepadAxis axis; + int axisNumber; + + GamepadButton button; + int buttonNumber; + }; + + Joystick() : sensors() + {} + + virtual ~Joystick() + {} + + std::string GetName() const + { + return this->name; + } + + int GetInstanceID() + { + return this->instanceId; + } + + int GetID() const + { + return this->id; + } + + std::string GetGUID() const + { + return this->guid; + } + + std::unique_ptr::pointer GetHandle() const + { + return this->handle.get(); + } + + // clang-format off + static constexpr BidirectionalMap inputTypes = { + "axis", INPUT_TYPE_AXIS, + "button", INPUT_TYPE_BUTTON + }; + + static constexpr BidirectionalMap buttonTypes = { + "a", GAMEPAD_BUTTON_A, + "b", GAMEPAD_BUTTON_B, + "x", GAMEPAD_BUTTON_X, + "y", GAMEPAD_BUTTON_Y, + "back", GAMEPAD_BUTTON_BACK, + "guide", GAMEPAD_BUTTON_GUIDE, + "start", GAMEPAD_BUTTON_START, + "leftstick", GAMEPAD_BUTTON_LEFTSTICK, + "rightstick", GAMEPAD_BUTTON_RIGHTSTICK, + "leftshoulder", GAMEPAD_BUTTON_LEFTSHOULDER, + "rightshoulder", GAMEPAD_BUTTON_RIGHTSHOULDER, + "dpup", GAMEPAD_BUTTON_DPAD_UP, + "dpdown", GAMEPAD_BUTTON_DPAD_DOWN, + "dpleft", GAMEPAD_BUTTON_DPAD_LEFT, + "dpright", GAMEPAD_BUTTON_DPAD_RIGHT + }; + + static constexpr BidirectionalMap axisTypes = { + "leftx", GAMEPAD_AXIS_LEFTX, + "lefty", GAMEPAD_AXIS_LEFTY, + "rightx", GAMEPAD_AXIS_RIGHTX, + "righty", GAMEPAD_AXIS_RIGHTY, + "triggerleft", GAMEPAD_AXIS_TRIGGERLEFT, + "triggerright", GAMEPAD_AXIS_TRIGGERRIGHT + }; + + static constexpr float JoystickMax = 150.0f; + + protected: + std::string name; + + int instanceId; + int id; + + std::string guid; + std::unique_ptr handle; + + std::map sensors; + }; +} // namespace love diff --git a/include/objects/joystick/wrap_joystick.hpp b/include/objects/joystick/wrap_joystick.hpp new file mode 100644 index 000000000..690e24fc4 --- /dev/null +++ b/include/objects/joystick/wrap_joystick.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include +#include + +#include + +namespace Wrap_Joystick +{ + love::Joystick* CheckJoystick(lua_State* L, int index); + + int IsConnected(lua_State* L); + + int GetName(lua_State* L); + + int GetID(lua_State* L); + + int GetIndex(lua_State* L); + + int GetGUID(lua_State* L); + + int GetDeviceInfo(lua_State* L); + + int GetAxisCount(lua_State* L); + + int GetButtonCount(lua_State* L); + + int GetHatCount(lua_State* L); + + int GetAxis(lua_State* L); + + int GetAxes(lua_State* L); + + int GetHat(lua_State* L); + + int IsDown(lua_State* L); + + int SetPlayerIndex(lua_State* L); + + int GetPlayerIndex(lua_State* L); + + int IsGamepad(lua_State* L); + + int GetGamepadType(lua_State* L); + + int GetGamepadAxis(lua_State* L); + + int IsGamepadDown(lua_State* L); + + int IsVibrationSupported(lua_State* L); + + int SetVibration(lua_State* L); + + int GetVibration(lua_State* L); + + int HasSensor(lua_State*L); + + int IsSensorEnabled(lua_State*L); + + int SetSensorEnabled(lua_State* L); + + int GetSensorData(lua_State* L); + + int Split(lua_State* L); + + int Join(lua_State* L); + + int Register(lua_State* L); + + extern std::span extension; +} // namespace Wrap_Joystick diff --git a/include/objects/mesh/mesh.hpp b/include/objects/mesh/mesh.hpp new file mode 100644 index 000000000..08554879d --- /dev/null +++ b/include/objects/mesh/mesh.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +#include +#include + +namespace love +{ + class Mesh : public Drawable + { + public: + static Type type; + + Mesh(const void* data, size_t size, vertex::PrimitiveType mode); + + Mesh(int vertexCount, vertex::PrimitiveType mode); + + ~Mesh(); + + void* CheckVertexDataOffset(size_t index, size_t* byteOffset); + + size_t GetVertexCount() const; + + size_t GetVertexStride() const; + + std::vector GetVertexBuffer() const; + + void* GetVertexData() const; + + void SetVertexDataModified(size_t offset, size_t dataSize); + + void Flush(); + + void SetVertexMap(const std::vector& map); + + void SetVertexMap(const void* data, size_t dataSize); + + void SetVertexMap(); + + bool GetVertexMap(std::vector& map) const; + + void SetIndexBuffer(std::vector& buffer); + + std::vector GetIndexBuffer() const; + + size_t GetIndexCount() const; + + void SetTexture(Texture* texture); + + void SetTexture(); + + Texture* GetTexture() const; + + void SetDrawMode(vertex::PrimitiveType mode); + + vertex::PrimitiveType GetDrawMode() const; + + void SetDrawRange(int start, int count); + + void SetDrawRange(); + + bool GetDrawRange(int& start, int& count) const; + + void Draw(Graphics& graphics, + const Matrix4& matrix) override; + + private: + void DrawInternal(Graphics& graphics, const Matrix4& matrix, + int instanceCount, std::vector* indirectArgs, + int argsIndex); + + std::vector buffer; + std::vector indexBuffer; + + size_t vertexCount; + size_t vertexStride; + + size_t indexCount; + vertex::PrimitiveType mode; + StrongReference> texture; + + Range drawRange; + bool useIndexBuffer; + }; +} // namespace love \ No newline at end of file diff --git a/include/objects/mesh/wrap_mesh.hpp b/include/objects/mesh/wrap_mesh.hpp new file mode 100644 index 000000000..61b6da359 --- /dev/null +++ b/include/objects/mesh/wrap_mesh.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +namespace Wrap_Mesh +{ + love::Mesh* CheckMesh(lua_State* L, int index); + + int SetVertices(lua_State* L); + + int SetVertex(lua_State* L); + + int GetVertex(lua_State* L); + + int GetVertexCount(lua_State* L); + + int GetVertexFormat(lua_State* L); + + int GetVertexBuffer(lua_State*L); + + int Flush(lua_State* L); + + int SetVertexMap(lua_State* L); + + int GetVertexMap(lua_State* L); + + int SetIndexBuffer(lua_State* L); + + int GetIndexBuffer(lua_State* L); + + int SetTexture(lua_State* L); + + int GetTexture(lua_State* L); + + int SetDrawMode(lua_State*L); + + int GetDrawMode(lua_State*L); + + int SetDrawRange(lua_State*L); + + int GetDrawRange(lua_State*L); + + int Register(lua_State* L); +} // namespace Wrap_Mesh \ No newline at end of file diff --git a/include/objects/object.h b/include/objects/object.h deleted file mode 100644 index 8ec7bc41b..000000000 --- a/include/objects/object.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "common/strongref.h" -#include "common/type.h" - -#include - -namespace love -{ - class Object - { - public: - static love::Type type; - - Object(); - - Object(const Object& other); - - virtual ~Object() = 0; - - void Retain(); - - int GetReferenceCount() const; - - void Release(); - - private: - std::atomic count; - }; -} // namespace love diff --git a/include/objects/quad/quad.hpp b/include/objects/quad/quad.hpp new file mode 100644 index 000000000..9a15f3d40 --- /dev/null +++ b/include/objects/quad/quad.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include + +namespace love +{ + class Quad : public Object + { + public: + static Type type; + + struct Viewport + { + double x, y; + double w, h; + }; + + Quad(const Viewport& viewport, double sourceWidth, double sourceHeight); + + virtual ~Quad(); + + void Refresh(const Viewport& viewport, double sourceWidth, double sourceHeight); + + void SetViewport(const Viewport& viewport); + + Viewport GetViewport() const; + + double GetTextureWidth() const; + + double GetTextureHeight() const; + + const Vector2* GetVertexPositions() const; + + const Vector2* GetVertexTextureCoords() const; + + void SetVertexTextureCoord(int index, const Vector2& coord) + { + this->vertexTextureCoords[index] = coord; + } + + void SetLayer(int layer); + + int GetLayer() const; + + private: + Vector2 vertexPositions[4]; + Vector2 vertexTextureCoords[4]; + + int arrayLayer; + Viewport viewport; + + double sourceWidth; + double sourceHeight; + }; +} // namespace love diff --git a/include/objects/quad/quadc.h b/include/objects/quad/quadc.h deleted file mode 100644 index 3ac3dc846..000000000 --- a/include/objects/quad/quadc.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "common/lmath.h" -#include "common/vector.h" - -#include "objects/object.h" - -namespace love::common -{ - class Quad : public Object - { - public: - struct Viewport - { - double x, y; - double w, h; - }; - - static love::Type type; - - Quad(double sw, double sh); - - virtual ~Quad(); - - /* LOVE FUNCTIONS */ - - double GetTextureWidth() const; - - double GetTextureHeight() const; - - void SetViewport(const Viewport& viewport); - - const Viewport& GetViewport() const; - - /* END LOVE FUNCTIONS */ - - void RefreshViewport(const Viewport& viewport, double sw, double sh); - - virtual void Refresh(const Viewport& viewport, double sw, double sh) = 0; - - protected: - Viewport viewport; - - double sw; - double sh; - }; -} // namespace love::common diff --git a/include/objects/quad/wrap_quad.h b/include/objects/quad/wrap_quad.h deleted file mode 100644 index e23c8cf07..000000000 --- a/include/objects/quad/wrap_quad.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/quad/quad.h" - -namespace Wrap_Quad -{ - int GetTextureDimensions(lua_State* L); - - int GetViewport(lua_State* L); - - int SetViewport(lua_State* L); - - love::Quad* CheckQuad(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Quad diff --git a/include/objects/quad/wrap_quad.hpp b/include/objects/quad/wrap_quad.hpp new file mode 100644 index 000000000..850b879c3 --- /dev/null +++ b/include/objects/quad/wrap_quad.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace Wrap_Quad +{ + love::Quad* CheckQuad(lua_State* L, int index); + + int SetViewport(lua_State* L); + + int GetViewport(lua_State* L); + + int GetTextureDimensions(lua_State* L); + + int SetLayer(lua_State* L); + + int GetLayer(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_Quad diff --git a/include/objects/random/randomgenerator.h b/include/objects/random/randomgenerator.h deleted file mode 100644 index 22bd3da7d..000000000 --- a/include/objects/random/randomgenerator.h +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#include "common/exception.h" -#include "common/lmath.h" -#include "objects/object.h" - -namespace love -{ - class RandomGenerator : public Object - { - public: - static love::Type type; - - union Seed - { - u64 b64; - - struct - { - u32 low; - u32 high; - } b32; - }; - - RandomGenerator(); - - virtual ~RandomGenerator() {}; - - u64 UniformRandom(); - - inline double Random() - { - u64 random = UniformRandom(); - union - { - u64 i; - double d; - } u; - - u.i = ((0x3FFULL) << 52) | (random >> 12); - - return u.d - 1.0; - } - - inline double Random(double max) - { - return Random() * max; - } - - inline double Random(double min, double max) - { - return Random() * (max - min) + min; - } - - double RandomNormal(double stddev); - - void SetSeed(Seed seed); - - Seed GetSeed() const; - - void SetState(const std::string& state); - - std::string GetState() const; - - private: - Seed seed; - Seed state; - - double lastRandomNormal; - }; -} // namespace love diff --git a/include/objects/random/wrap_randomgenerator.h b/include/objects/random/wrap_randomgenerator.h deleted file mode 100644 index d836979a8..000000000 --- a/include/objects/random/wrap_randomgenerator.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/random/randomgenerator.h" - -namespace Wrap_RandomGenerator -{ - int GetSeed(lua_State* L); - - int GetState(lua_State* L); - - int _Random(lua_State* L); - - int RandomNormal(lua_State* L); - - int SetSeed(lua_State* L); - - int SetState(lua_State* L); - - love::RandomGenerator::Seed CheckRandomSeed(lua_State* L, int index); - - love::RandomGenerator* CheckRandomGenerator(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_RandomGenerator diff --git a/include/objects/randomgenerator/randomgenerator.hpp b/include/objects/randomgenerator/randomgenerator.hpp new file mode 100644 index 000000000..c719ce98f --- /dev/null +++ b/include/objects/randomgenerator/randomgenerator.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace love +{ + class RandomGenerator : public Object + { + public: + static Type type; + + union Seed + { + uint64_t b64; + +#if defined(__WIIU__) + struct + { + uint32_t high; + uint32_t low; + } b32; +#else + struct + { + uint32_t low; + uint32_t high; + } b32; +#endif + }; + + RandomGenerator(); + + virtual ~RandomGenerator() + {} + + uint64_t Rand(); + + inline double Random() + { + uint64_t random = this->Rand(); + + union + { + uint64_t i; + double d; + } u; + + u.i = ((0x3FFULL) << 52) | (random >> 12); + + return u.d - 1.0; + } + + inline double Random(double max) + { + return this->Random() * max; + } + + inline double Random(double min, double max) + { + return this->Random() * (max - min) + min; + } + + double RandomNormal(double stdDev); + + void SetSeed(Seed seed); + + Seed GetSeed() const; + + void SetState(const std::string& stateString); + + std::string_view GetState() const; + + private: + static constexpr auto RANDOM_STATE_MUL = 2685821657736338717ULL; + + Seed seed; + Seed randomState; + double lastRandomNormal; + }; +} // namespace love diff --git a/include/objects/randomgenerator/wrap_randomgenerator.hpp b/include/objects/randomgenerator/wrap_randomgenerator.hpp new file mode 100644 index 000000000..a1dd62e55 --- /dev/null +++ b/include/objects/randomgenerator/wrap_randomgenerator.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace Wrap_RandomGenerator +{ + love::RandomGenerator* CheckRandomGenerator(lua_State* L, int index); + + love::RandomGenerator::Seed CheckSeed(lua_State* L, int index); + + int __Random(lua_State* L); + + int RandomNormal(lua_State* L); + + int SetSeed(lua_State* L); + + int GetSeed(lua_State* L); + + int SetState(lua_State* L); + + int GetState(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_RandomGenerator diff --git a/include/objects/rasterizer/rasterizer.h b/include/objects/rasterizer/rasterizer.h deleted file mode 100644 index b499aa280..000000000 --- a/include/objects/rasterizer/rasterizer.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include "objects/glyphdata/glyphdata.h" -namespace love -{ - class Rasterizer : public Object - { - public: - struct FontMetrics - { - int advance; - int ascent; - int descent; - int height; - }; - - enum DataType - { - DATA_TRUETYPE, - DATA_IMAGE, - DATA_BCFNT - }; - - static love::Type type; - - virtual ~Rasterizer(); - - virtual int GetHeight() const; - - virtual int GetAdvance() const; - - virtual int GetAscent() const; - - virtual int GetDescent() const; - - virtual int GetLineHeight() const = 0; - - virtual GlyphData* GetGlyphData(uint32_t glyph) const = 0; - - virtual GlyphData* GetGlyphData(const std::string& text) const; - - virtual int GetGlyphCount() const = 0; - - virtual bool HasGlyph(uint32_t glyph) const = 0; - - virtual bool HasGlyphs(const std::string& text) const; - - virtual float GetKerning(uint32_t left, uint32_t right) const; - - virtual DataType GetDataType() const = 0; - - float GetDPIScale() const; - - protected: - FontMetrics metrics; - float dpiScale = 1.0f; - }; -} // namespace love diff --git a/include/objects/rasterizer/rasterizer.hpp b/include/objects/rasterizer/rasterizer.hpp new file mode 100644 index 000000000..9aaef090d --- /dev/null +++ b/include/objects/rasterizer/rasterizer.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include + +#include + +namespace love +{ + class TextShaper; + + class Rasterizer : public Object + { + public: + struct FontMetrics + { + int advance; + int ascent; + int descent; + int height; + }; + + enum DataType + { + DATA_TRUETYPE, + DATA_IMAGE, + DATA_BCFNT + }; + + static Type type; + + virtual ~Rasterizer() + {} + + int GetAdvance() const + { + return this->metrics.advance; + } + + int GetAscent() const + { + return this->metrics.ascent; + } + + int GetDescent() const + { + return this->metrics.descent; + } + + int GetHeight() const + { + return this->metrics.height; + } + + virtual int GetLineHeight() const = 0; + + virtual int GetGlyphSpacing(uint32_t glyph) const = 0; + + virtual int GetGlyphIndex(uint32_t glyph) const = 0; + + GlyphData* GetGlyphData(uint32_t glyph) const; + + GlyphData* GetGlyphData(const std::string& text) const; + + virtual GlyphData* GetGlyphDataForIndex(int index) const = 0; + + virtual int GetGlyphCount() const = 0; + + virtual bool HasGlyph(uint32_t glyph) const = 0; + + virtual bool HasGlyphs(const std::string& text) const; + + virtual float GetKerning(uint32_t left, uint32_t right) const; + + float GetDPIScale() const + { + return this->dpiScale; + } + + virtual DataType GetDataType() const = 0; + + virtual void* GetHandle() const + { + return nullptr; + } + + virtual TextShaper* NewTextShaper() = 0; + + protected: + FontMetrics metrics; + float dpiScale; + }; +} // namespace love diff --git a/include/objects/rasterizer/wrap_rasterizer.h b/include/objects/rasterizer/wrap_rasterizer.h deleted file mode 100644 index 7efeb5201..000000000 --- a/include/objects/rasterizer/wrap_rasterizer.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "common/luax.h" - -#include "objects/glyphdata/glyphdata.h" -#include "objects/rasterizer/rasterizer.h" - -namespace Wrap_Rasterizer -{ - int GetHeight(lua_State* L); - - int GetAdvance(lua_State* L); - - int GetAscent(lua_State* L); - - int GetDescent(lua_State* L); - - int GetLineHeight(lua_State* L); - - int GetGlyphData(lua_State* L); - - int GetGlyphCount(lua_State* L); - - int HasGlyphs(lua_State* L); - - love::Rasterizer* CheckRasterizer(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Rasterizer diff --git a/include/objects/rasterizer/wrap_rasterizer.hpp b/include/objects/rasterizer/wrap_rasterizer.hpp new file mode 100644 index 000000000..1a4281f1a --- /dev/null +++ b/include/objects/rasterizer/wrap_rasterizer.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace Wrap_Rasterizer +{ + int GetHeight(lua_State* L); + + int GetAdvance(lua_State* L); + + int GetAscent(lua_State* L); + + int GetDescent(lua_State* L); + + int GetLineHeight(lua_State* L); + + int GetGlyphData(lua_State* L); + + int GetGlyphCount(lua_State* L); + + int HasGlyphs(lua_State* L); + + love::Rasterizer* CheckRasterizer(lua_State* L, int index); + + int Register(lua_State* L); +} // namespace Wrap_Rasterizer diff --git a/include/objects/shader/shader.tcc b/include/objects/shader/shader.tcc new file mode 100644 index 000000000..429b4dcd0 --- /dev/null +++ b/include/objects/shader/shader.tcc @@ -0,0 +1,76 @@ +#pragma once + +#include +#include +#include + +#include + +namespace love +{ + template + class Shader : public Object + { + public: + static inline int shaderSwitches = 0; + + enum StandardShader + { + STANDARD_DEFAULT, //< primitives + STANDARD_TEXTURE, //< textures + STANDARD_VIDEO, //< ogg theora videos + STANDARD_MAX_ENUM + }; + + enum ShaderStageType + { + STAGE_VERTEX, + STAGE_PIXEL + }; + + static inline Type type = Type("Shader", &Object::type); + + static inline Shader* current = nullptr; + + static inline Shader* defaults[StandardShader::STANDARD_MAX_ENUM] { + nullptr + }; + + struct Program; + + static bool IsDefaultActive() + { + for (int index = 0; index < StandardShader::STANDARD_MAX_ENUM; index++) + { + if (current == defaults[index]) + return true; + } + + return false; + } + + static bool IsDefaultActive(StandardShader type) + { + if (current == defaults[type] && defaults[type]) + return true; + + return false; + } + + // clang-format off + static constexpr BidirectionalMap standardShaders = { + "color", STANDARD_DEFAULT, + "texture", STANDARD_TEXTURE, + "video", STANDARD_VIDEO + }; + + static constexpr BidirectionalMap shaderStages = { + "vertex", STAGE_VERTEX, + "pixel", STAGE_PIXEL + }; + // clang-format on + + protected: + StandardShader shaderType; + }; +} // namespace love diff --git a/include/objects/shader/wrap_shader.hpp b/include/objects/shader/wrap_shader.hpp new file mode 100644 index 000000000..57acf4ad0 --- /dev/null +++ b/include/objects/shader/wrap_shader.hpp @@ -0,0 +1,4 @@ +#pragma once + +namespace Wrap_Shader +{} // namespace Wrap_Shader diff --git a/include/objects/shape/shape.hpp b/include/objects/shape/shape.hpp new file mode 100644 index 000000000..97ef3159c --- /dev/null +++ b/include/objects/shape/shape.hpp @@ -0,0 +1,133 @@ +#pragma once + +#include +#include + +#include + +#include + +#include + +namespace love +{ + class Shape : public Object + { + public: + static love::Type type; + + friend class Physics; + + enum Type + { + SHAPE_INVALID, + SHAPE_CIRCLE, + SHAPE_POLYGON, + SHAPE_EDGE, + SHAPE_CHAIN, + SHAPE_MAX_ENUM + }; + + Shape(Body* body, const b2Shape& shape); + + virtual ~Shape(); + + void Destroy(bool implicit = false); + + bool IsValid() const + { + return this->fixture != nullptr; + } + + bool IsShapeValid() const + { + return this->shape != nullptr; + } + + bool IsSensor() const; + + void SetSensor(bool sensor); + + Body* GetBody() const; + + void SetFilterData(int* data); + + void GetFilterData(int* data); + + int SetUserdata(lua_State* L); + + int GetUserdata(lua_State* L); + + void SetFriction(float friction); + + void SetRestitution(float restitution); + + void SetDensity(float density); + + float GetFriction() const; + + float GetRestitution() const; + + float GetDensity() const; + + bool TestPoint(float x, float y) const; + + bool TestPoint(float x, float y, float angle, float cx, float cy) const; + + Shape::Type GetType() const; + + float GetRadius() const; + + int GetChildCount() const; + + int RayCast(lua_State* L) const; + + int ComputeAABB(lua_State* L) const; + + int ComputeMass(lua_State* L) const; + + void SetGroupIndex(int index); + + int GetGroupIndex() const; + + int SetCategory(lua_State* L); + + int SetMask(lua_State* L); + + int GetCategory(lua_State* L); + + int GetMask(lua_State* L); + + uint16_t GetCategoryBits(lua_State* L) const; + + int PushCategoryBits(lua_State* L, uint16_t bits); + + int GetBoundingBox(lua_State* L) const; + + int GetMassData(lua_State* L) const; + + void ThrowIfFixtureNotValid() const; + + void ThrowIfShapeNotValid() const; + + // clang-format off + static constexpr BidirectionalMap shapeTypes = + { + "circle", SHAPE_CIRCLE, + "polygon", SHAPE_POLYGON, + "edge", SHAPE_EDGE, + "chain", SHAPE_CHAIN + }; + // clang-format on + + protected: + b2Shape* shape; + bool own; + + Shape::Type shapeType; + Body* body; + + b2Fixture* fixture; + Reference* reference = nullptr; + }; +} // namespace love diff --git a/include/objects/shape/types/chainshape/chainshape.hpp b/include/objects/shape/types/chainshape/chainshape.hpp new file mode 100644 index 000000000..a5eb71ff2 --- /dev/null +++ b/include/objects/shape/types/chainshape/chainshape.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +namespace love +{ + class ChainShape : public Shape + { + public: + static love::Type type; + + ChainShape(Body* body, const b2ChainShape& c); + + virtual ~ChainShape(); + + void SetNextVertex(float x, float y); + + void SetPreviousVertex(float x, float y); + + b2Vec2 GetNextVertex() const; + + b2Vec2 GetPreviousVertex() const; + + EdgeShape* GetChildEdge(int index) const; + + int GetVertexCount() const; + + b2Vec2 GetPoint(int index) const; + + const b2Vec2* GetPoints() const; + }; +} // namespace love diff --git a/include/objects/shape/types/chainshape/wrap_chainshape.hpp b/include/objects/shape/types/chainshape/wrap_chainshape.hpp new file mode 100644 index 000000000..3c63e957a --- /dev/null +++ b/include/objects/shape/types/chainshape/wrap_chainshape.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_ChainShape +{ + love::ChainShape* CheckChainShape(lua_State* L, int index); + + int SetNextVertex(lua_State* L); + + int SetPreviousVertex(lua_State* L); + + int GetVertexCount(lua_State* L); + + int GetPoint(lua_State* L); + + int GetNextVertex(lua_State* L); + + int GetPreviousVertex(lua_State* L); + + int GetPoints(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_ChainShape diff --git a/include/objects/shape/types/circleshape/circleshape.hpp b/include/objects/shape/types/circleshape/circleshape.hpp new file mode 100644 index 000000000..654548ed2 --- /dev/null +++ b/include/objects/shape/types/circleshape/circleshape.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include + +namespace love +{ + class CircleShape : public Shape + { + public: + static love::Type type; + + CircleShape(Body* body, const b2CircleShape& circleShape); + + virtual ~CircleShape(); + + float GetRadius() const; + + void SetRadius(float radius); + + void GetPoint(float& x, float& y) const; + + void SetPoint(float x, float y); + }; +} // namespace love diff --git a/include/objects/shape/types/circleshape/wrap_circleshape.hpp b/include/objects/shape/types/circleshape/wrap_circleshape.hpp new file mode 100644 index 000000000..de04f88fe --- /dev/null +++ b/include/objects/shape/types/circleshape/wrap_circleshape.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_CircleShape +{ + love::CircleShape* CheckCircleShape(lua_State* L, int index); + + int GetRadius(lua_State* L); + + int SetRadius(lua_State* L); + + int GetPoint(lua_State* L); + + int SetPoint(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_CircleShape diff --git a/include/objects/shape/types/edgeshape/edgeshape.hpp b/include/objects/shape/types/edgeshape/edgeshape.hpp new file mode 100644 index 000000000..dbfe861ef --- /dev/null +++ b/include/objects/shape/types/edgeshape/edgeshape.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace love +{ + class EdgeShape : public Shape + { + public: + static love::Type type; + + EdgeShape(Body* body, const b2EdgeShape& e); + + virtual ~EdgeShape(); + + void SetNextVertex(float x, float y); + + b2Vec2 GetNextVertex() const; + + void SetPreviousVertex(float x, float y); + + b2Vec2 GetPreviousVertex() const; + + int GetPoints(lua_State* L); + }; +} // namespace love diff --git a/include/objects/shape/types/edgeshape/wrap_edgeshape.hpp b/include/objects/shape/types/edgeshape/wrap_edgeshape.hpp new file mode 100644 index 000000000..9195c594a --- /dev/null +++ b/include/objects/shape/types/edgeshape/wrap_edgeshape.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include +#include + +namespace Wrap_EdgeShape +{ + love::EdgeShape* CheckEdgeShape(lua_State* L, int index); + + int SetNextVertex(lua_State* L); + + int SetPreviousVertex(lua_State* L); + + int GetNextVertex(lua_State* L); + + int GetPreviousVertex(lua_State* L); + + int GetPoints(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_EdgeShape diff --git a/include/objects/shape/types/polygonshape/polygonshape.hpp b/include/objects/shape/types/polygonshape/polygonshape.hpp new file mode 100644 index 000000000..6d7138dec --- /dev/null +++ b/include/objects/shape/types/polygonshape/polygonshape.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace love +{ + class PolygonShape : public Shape + { + public: + static love::Type type; + + PolygonShape(Body* body, const b2PolygonShape& polygonShape); + + virtual ~PolygonShape(); + + int GetPoints(lua_State* L); + + bool Validate() const; + }; +} // namespace love diff --git a/include/objects/shape/types/polygonshape/wrap_polygonshape.hpp b/include/objects/shape/types/polygonshape/wrap_polygonshape.hpp new file mode 100644 index 000000000..b59d1476a --- /dev/null +++ b/include/objects/shape/types/polygonshape/wrap_polygonshape.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace Wrap_PolygonShape +{ + love::PolygonShape* CheckPolygonShape(lua_State* L, int index); + + int GetPoints(lua_State* L); + + int Validate(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_PolygonShape diff --git a/include/objects/shape/wrap_shape.hpp b/include/objects/shape/wrap_shape.hpp new file mode 100644 index 000000000..112498c1a --- /dev/null +++ b/include/objects/shape/wrap_shape.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +#include + +namespace Wrap_Shape +{ + void PushShape(lua_State* L, love::Shape* shape); + + love::Shape* CheckShape(lua_State* L, int index); + + int GetType(lua_State* L); + + int GetRadius(lua_State* L); + + int GetChildCount(lua_State* L); + + int SetFriction(lua_State* L); + + int SetRestitution(lua_State* L); + + int SetDensity(lua_State* L); + + int SetSensor(lua_State* L); + + int GetFriction(lua_State* L); + + int GetRestitution(lua_State* L); + + int GetDensity(lua_State* L); + + int IsSensor(lua_State* L); + + int GetBody(lua_State* L); + + int TestPoint(lua_State* L); + + int RayCast(lua_State* L); + + int ComputeAABB(lua_State* L); + + int ComputeMass(lua_State* L); + + int SetFilterData(lua_State* L); + + int GetFilterData(lua_State* L); + + int SetCategory(lua_State* L); + + int GetCategory(lua_State* L); + + int SetMask(lua_State* L); + + int GetMask(lua_State* L); + + int SetUserdata(lua_State* L); + + int GetUserdata(lua_State* L); + + int GetBoundingBox(lua_State* L); + + int GetMassData(lua_State* L); + + int GetGroupIndex(lua_State* L); + + int SetGroupIndex(lua_State* L); + + int Destroy(lua_State* L); + + int IsDestroyed(lua_State* L); + + extern const luaL_Reg shapeFunctions[0x1E]; + + int Register(lua_State* L); +} // namespace Wrap_Shape diff --git a/include/objects/sounddata/sounddata.h b/include/objects/sounddata/sounddata.h deleted file mode 100644 index fae6facd8..000000000 --- a/include/objects/sounddata/sounddata.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "objects/decoder/decoder.h" -#include - -namespace love -{ - class SoundData : public Data - { - public: - static love::Type type; - - SoundData(Decoder* decoder); - SoundData(int samples, int sampleRate, int bitDepth, int channels); - SoundData(void* data, int samples, int sampleRate, int bitDepth, int channels); - - SoundData(const SoundData& other); - - ~SoundData(); - - SoundData* Clone() const; - - void* GetData() const; - - size_t GetSize() const; - - int GetChannelCount() const; - - int GetBitDepth() const; - - int GetSampleRate() const; - - int GetSampleCount() const; - - float GetDuration() const; - - void SetSample(int i, float sample); - - void SetSample(int i, int channel, float sample); - - float GetSample(int i) const; - - float GetSample(int i, int channel) const; - - private: - uint8_t* data; - size_t size; - - int sampleRate; - int bitDepth; - int channels; - - void Load(int samples, int sampleRate, int bitDepth, int channels, void* newData = 0); - }; -} // namespace love diff --git a/include/objects/sounddata/wrap_sounddata.h b/include/objects/sounddata/wrap_sounddata.h deleted file mode 100644 index 42638e56f..000000000 --- a/include/objects/sounddata/wrap_sounddata.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/sounddata/sounddata.h" - -namespace Wrap_SoundData -{ - int Clone(lua_State* L); - - int GetBitDepth(lua_State* L); - - int GetChannelCount(lua_State* L); - - int GetDuration(lua_State* L); - - int GetSampleCount(lua_State* L); - - int GetSampleRate(lua_State* L); - - int GetSample(lua_State* L); - - int SetSample(lua_State* L); - - love::SoundData* CheckSoundData(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_SoundData diff --git a/include/objects/source/source.tcc b/include/objects/source/source.tcc new file mode 100644 index 000000000..d06864b33 --- /dev/null +++ b/include/objects/source/source.tcc @@ -0,0 +1,183 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include + +#include + +namespace love +{ + class InvalidFormatException : public Exception + { + public: + InvalidFormatException(int channels, int bitDepth) : + Exception("%d-channel Sources with %d bits per sample are not supported.", channels, + bitDepth) + {} + }; + + class QueueFormatMismatchException : public Exception + { + public: + QueueFormatMismatchException() : + Exception("Queued sound data must have same format as sound Source.") + {} + }; + + class QueueTypeMismatchException : public love::Exception + { + public: + QueueTypeMismatchException() : + Exception("Only queueable Sources can be queued with sound data.") + {} + }; + + class QueueMalformedLengthException : public love::Exception + { + public: + QueueMalformedLengthException(int bytes) : + Exception("Data length must be a multiple of sample size (%d bytes).", bytes) + {} + }; + + class QueueLoopingException : public love::Exception + { + public: + QueueLoopingException() : Exception("Queueable Sources can not be looped.") + {} + }; + + template + class Source : public Object + { + public: + class DataBuffer + { + public: + DataBuffer() + {} + + DataBuffer(const void* data, size_t size); + + ~DataBuffer(); + + int16_t* GetBuffer() + { + return this->buffer; + } + + size_t GetSize() + { + return this->size; + } + + private: + int16_t* buffer; + + size_t size; + size_t alignSize; + }; + + enum SourceType + { + TYPE_STATIC, + TYPE_STREAM, + TYPE_QUEUE, + TYPE_MAX_ENUM + }; + + enum Unit + { + UNIT_SECONDS, + UNIT_SAMPLES, + UNIT_MAX_ENUM + }; + + static inline Type type = Type("Source", &Object::type); + + Source(SourceType type) : + sourceType(type), + looping(false), + minVolume(1.0f), + maxVolume(1.0f), + volume(1.0f), + valid(false), + current(false), + samplesOffset(0.0), + channel(0) + {} + + SourceType GetType() const + { + return this->sourceType; + } + + float GetMaxVolume() const + { + return this->maxVolume; + } + + void SetMaxVolume(float volume) + { + this->maxVolume = volume; + } + + float GetMinVolume() const + { + return this->minVolume; + } + + void SetMinVolume(float volume) + { + this->minVolume = volume; + } + + bool IsLooping() const + { + return this->looping; + } + + // clang-format off + static constexpr BidirectionalMap sourceTypes = { + "static", TYPE_STATIC, + "stream", TYPE_STREAM, + "queue", TYPE_QUEUE + }; + + static constexpr BidirectionalMap unitTypes = { + "seconds", UNIT_SECONDS, + "samples", UNIT_SAMPLES + }; + // clang-format on + + protected: + SourceType sourceType; + bool looping; + + float minVolume; + float maxVolume; + float volume; + + bool valid; + bool current; + + StrongReference decoder; + + int sampleRate; + int channels; + int bitDepth; + + int bufferCount; + double samplesOffset; + + size_t channel; + + std::shared_ptr staticBuffer; + }; +} // namespace love diff --git a/include/objects/source/sourcec.h b/include/objects/source/sourcec.h deleted file mode 100644 index 77870ebb2..000000000 --- a/include/objects/source/sourcec.h +++ /dev/null @@ -1,182 +0,0 @@ -#pragma once - -#include "objects/decoder/decoder.h" -#include "objects/sounddata/sounddata.h" - -#include "modules/audio/pool/pool.h" -#include "objects/object.h" - -#include - -namespace love -{ - class StaticDataBuffer : public Object - { - public: - StaticDataBuffer(void* data, size_t size); - - virtual ~StaticDataBuffer(); - - inline s16* GetBuffer() const - { - return this->buffer.first; - } - - inline size_t GetSize() const - { - return this->buffer.second; - } - - private: - std::pair buffer; - }; - - namespace common - { - class Source : public Object - { - public: - static love::Type type; - - enum Type - { - TYPE_STATIC, - TYPE_STREAM, - TYPE_QUEUE, - TYPE_MAX_ENUM - }; - - enum Unit - { - UNIT_SECONDS, - UNIT_SAMPLES, - UNIT_MAX_ENUM - }; - - Source(Pool* pool, SoundData* sound); - - Source(Pool* pool, Decoder* decoder); - - Source(const Source& other); - - virtual ~Source() {}; - - virtual Source* Clone() = 0; - - bool Play(); - - void Stop(); - - void Pause(); - - virtual bool IsPlaying() const = 0; - - virtual bool IsFinished() const = 0; - - Type GetType() const; - - int GetChannelCount() const; - - double GetDuration(Unit unit); - - int GetFreeBufferCount() const; - - float GetMinVolume() const; - - float GetMaxVolume() const; - - float GetVolume() const; - - virtual void SetVolume(float volume) = 0; - - void Seek(double offset, Unit unit); - - virtual void SetLooping(bool should) = 0; - - void SetVolumeLimits(float min, float max); - - void SetMinVolume(float volume); - - void SetMaxVolume(float volume); - - bool IsLooping() const; - - double Tell(Source::Unit unit); - - static bool Play(const std::vector& sources); - - static void Stop(const std::vector& sources); - - static void Pause(const std::vector& sources); - - static std::vector Pause(Pool* pool); - - static void Stop(Pool* pool); - - static bool GetConstant(const char* in, Type& out); - static bool GetConstant(Type in, const char*& out); - static std::vector GetConstants(Type); - - static bool GetConstant(const char* in, Unit& out); - static bool GetConstant(Unit in, const char*& out); - static std::vector GetConstants(Unit); - - virtual bool Update() = 0; - - virtual void StopAtomic() = 0; - - protected: - Type sourceType; - - constexpr static int DEFAULT_BUFFERS = 2; - constexpr static int MAX_BUFFERS = 2; - - void* sourceBuffer; - size_t souceBufferSize; - - bool valid = false; - - float volume = 1.0f; - - bool looping = false; - - float minVolume = 0.0f; - float maxVolume = 1.0f; - - std::atomic offsetSamples = 0; - - int sampleRate = 0; - int channels = 0; - int bitDepth = 0; - - Pool* pool = nullptr; - - size_t channel = 0; - - virtual void InitializeStreamBuffers(Decoder* decoder) = 0; - - StrongReference decoder; - StrongReference staticBuffer; - - virtual void ClearChannel() = 0; - - virtual void FreeBuffer() = 0; - - virtual void Reset() = 0; - - virtual void PrepareAtomic() = 0; - - virtual int StreamAtomic(size_t which) = 0; - - virtual bool PlayAtomic() = 0; - - virtual void PauseAtomic() = 0; - - virtual void ResumeAtomic() = 0; - - virtual double GetSampleOffset() = 0; - - void TeardownAtomic(); - }; - } // namespace common -} // namespace love diff --git a/include/objects/source/wrap_source.h b/include/objects/source/wrap_source.h deleted file mode 100644 index 45fe6da82..000000000 --- a/include/objects/source/wrap_source.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/source/source.h" - -namespace Wrap_Source -{ - int Clone(lua_State* L); - - int GetChannelCount(lua_State* L); - - int GetDuration(lua_State* L); - - int GetFreeBufferCount(lua_State* L); - - int GetType(lua_State* L); - - int GetVolume(lua_State* L); - - int GetVolumeLimits(lua_State* L); - - int IsLooping(lua_State* L); - - int IsPlaying(lua_State* L); - - int Pause(lua_State* L); - - int Play(lua_State* L); - - int Seek(lua_State* L); - - int SetLooping(lua_State* L); - - int SetVolume(lua_State* L); - - int SetVolumeLimits(lua_State* L); - - int Stop(lua_State* L); - - int Tell(lua_State* L); - - love::Source* CheckSource(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Source diff --git a/include/objects/source/wrap_source.hpp b/include/objects/source/wrap_source.hpp new file mode 100644 index 000000000..8e3a5b5b8 --- /dev/null +++ b/include/objects/source/wrap_source.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +namespace Wrap_Source +{ + love::Source* CheckSource(lua_State* L, int index); + + int Clone(lua_State* L); + + int Play(lua_State* L); + + int Stop(lua_State* L); + + int Pause(lua_State* L); + + int SetVolume(lua_State* L); + + int GetVolume(lua_State* L); + + int SetPitch(lua_State* L); + + int GetPitch(lua_State* L); + + int Seek(lua_State* L); + + int Tell(lua_State* L); + + int GetDuration(lua_State* L); + + int SetLooping(lua_State* L); + + int IsLooping(lua_State* L); + + int IsPlaying(lua_State* L); + + int SetVolumeLimits(lua_State* L); + + int GetVolumeLimits(lua_State* L); + + int GetChannelCount(lua_State* L); + + int GetFreeBufferCount(lua_State* L); + + int Queue(lua_State* L); + + int GetType(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_Source diff --git a/include/objects/spritebatch/spritebatch.hpp b/include/objects/spritebatch/spritebatch.hpp new file mode 100644 index 000000000..3450a2acb --- /dev/null +++ b/include/objects/spritebatch/spritebatch.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include +#include +#include + +#include + +#include + +#include + +namespace love +{ + class SpriteBatch : public Drawable + { + public: + static Type type; + + SpriteBatch(Texture* texture, int size); + + virtual ~SpriteBatch(); + + int Add(const Matrix4& matrix, int index = -1); + + int Add(Quad* quad, const Matrix4& matrix, int index = -1); + + int AddLayer(int layer, const Matrix4& matrix, int index = -1); + + int AddLayer(int layer, Quad* quad, const Matrix4& matrix, int index = -1); + + void Clear(); + + void Flush(); + + void SetTexture(Texture* texture); + + Texture* GetTexture() const; + + void SetColor(const Color& color); + + void SetColor(); + + Color GetColor() const; + + int GetCount() const; + + int GetBufferSize() const; + + void AttachAttribute(const std::string& name); + + void SetDrawRange(int start, int count); + + void SetDrawRange(); + + bool GetDrawRange(int& start, int& count) const; + + void Draw(Graphics& graphics, + const Matrix4& matrix) override; + + private: + void SetBufferSize(int size); + + StrongReference> texture; + + int size; + int next; + + Color color; + + std::vector buffer; + + vertex::CommonFormat format; + size_t vertexStride; + + int rangeStart; + int rangeCount; + }; +} // namespace love \ No newline at end of file diff --git a/include/objects/spritebatch/wrap_spritebatch.hpp b/include/objects/spritebatch/wrap_spritebatch.hpp new file mode 100644 index 000000000..6ce49d5ed --- /dev/null +++ b/include/objects/spritebatch/wrap_spritebatch.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +namespace Wrap_SpriteBatch +{ + love::SpriteBatch* CheckSpriteBatch(lua_State* L, int index); + + int Add(lua_State* L); + + int Set(lua_State* L); + + int Clear(lua_State* L); + + int Flush(lua_State* L); + + int SetTexture(lua_State* L); + + int GetTexture(lua_State* L); + + int SetColor(lua_State* L); + + int GetColor(lua_State* L); + + int GetCount(lua_State* L); + + int GetBufferSize(lua_State* L); + + int SetDrawRange(lua_State* L); + + int GetDrawRange(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_SpriteBatch \ No newline at end of file diff --git a/include/objects/text/textc.h b/include/objects/text/textc.h deleted file mode 100644 index ce340eaab..000000000 --- a/include/objects/text/textc.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "objects/drawable/drawable.h" -#include "objects/font/font.h" - -#include "common/exception.h" - -namespace love -{ - class Graphics; - - namespace common - { - class Text : public Drawable - { - public: - static love::Type type; - - Text(love::Font* font, const std::vector& text = {}); - - virtual void Set(const std::vector& text) = 0; - - virtual void Set(const std::vector& text, float wrap, - Font::AlignMode align) = 0; - - virtual int Add(const std::vector& text, - const Matrix4& localTransform) = 0; - - virtual int Addf(const std::vector& text, float wrap, - Font::AlignMode align, const Matrix4& localTransform) = 0; - - virtual void Clear() = 0; - - virtual void SetFont(love::Font* font) = 0; - - love::Font* GetFont() const; - - virtual int GetWidth(int index = 0) const = 0; - - virtual int GetHeight(int index = 0) const = 0; - - virtual void Draw(Graphics* gfx, const Matrix4& m) = 0; - - protected: - StrongReference font; - }; - } // namespace common -} // namespace love \ No newline at end of file diff --git a/include/objects/text/wrap_text.h b/include/objects/text/wrap_text.h deleted file mode 100644 index c2bf86603..000000000 --- a/include/objects/text/wrap_text.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "objects/font/wrap_font.h" -#include "objects/text/text.h" - -namespace Wrap_Text -{ - int Set(lua_State* L); - - int Setf(lua_State* L); - - int Add(lua_State* L); - - int Addf(lua_State* L); - - int Clear(lua_State* L); - - int SetFont(lua_State* L); - - int GetFont(lua_State* L); - - int GetWidth(lua_State* L); - - int GetHeight(lua_State* L); - - int GetDimensions(lua_State* L); - - love::Text* CheckText(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Text \ No newline at end of file diff --git a/include/objects/textbatch/textbatch.hpp b/include/objects/textbatch/textbatch.hpp new file mode 100644 index 000000000..14bad15f4 --- /dev/null +++ b/include/objects/textbatch/textbatch.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace love +{ + class TextBatch : public Drawable + { + public: + static inline Type type = Type("TextBatch", &Drawable::type); + + TextBatch(Font* font, const ColoredStrings& text = {}); + + virtual ~TextBatch(); + + void Set(const ColoredStrings& text); + + void Set(const ColoredStrings& text, float wrap, Font::AlignMode align); + + int Add(const ColoredStrings& text, const Matrix4& matrix); + + int Addf(const ColoredStrings& text, float wrap, Font::AlignMode align, + const Matrix4& matrix); + + void Clear(); + + void SetFont(Font* font); + + Font* GetFont() const + { + return this->font.Get(); + } + + int GetWidth(int index = 0) const; + + int GetHeight(int index = 0) const; + + void Draw(Graphics& graphics, + const Matrix4& matrix) override; + + private: + struct TextData + { + ColoredCodepoints codepoints; + float wrap; + Font::AlignMode align; + TextShaper::TextInfo textInfo; + bool useMatrix; + bool appendVertices; + Matrix4 matrix; + }; + + void UploadVertices(const std::vector& vertices, size_t offset); + + void RegenerateVertices(); + + void AddTextData(const TextData& data); + + StrongReference font; + std::vector buffer; + Range modifiedVertices; + + std::vector drawCommands; + std::vector textData; + size_t vertexOffset; + + uint32_t textureCacheId; + }; +} // namespace love diff --git a/include/objects/textbatch/wrap_textbatch.hpp b/include/objects/textbatch/wrap_textbatch.hpp new file mode 100644 index 000000000..4b60f7226 --- /dev/null +++ b/include/objects/textbatch/wrap_textbatch.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +namespace Wrap_TextBatch +{ + love::TextBatch* CheckTextBatch(lua_State* L, int index); + + int Set(lua_State* L); + + int Setf(lua_State* L); + + int Add(lua_State* L); + + int Addf(lua_State* L); + + int Clear(lua_State* L); + + int SetFont(lua_State* L); + + int GetFont(lua_State* L); + + int GetWidth(lua_State* L); + + int GetHeight(lua_State* L); + + int GetDimensions(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_TextBatch diff --git a/include/objects/texture/texture.tcc b/include/objects/texture/texture.tcc new file mode 100644 index 000000000..ef1dceb78 --- /dev/null +++ b/include/objects/texture/texture.tcc @@ -0,0 +1,594 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +namespace love +{ + using VectorMipmapLayers = std::vector>>; + + template + class Texture : public Drawable + { + public: + enum TextureType + { + TEXTURE_2D, + TEXTURE_VOLUME, + TEXTURE_2D_ARRAY, + TEXTURE_CUBE + }; + + static inline Type type = Type("Texture", &Drawable::type); + + enum MipmapsMode + { + MIPMAPS_NONE, + MIPMAPS_MANUAL, + MIPMAPS_AUTO + }; + + enum SettingType + { + SETTING_WIDTH, + SETTING_HEIGHT, + SETTING_LAYERS, + SETTING_MIPMAPS, + SETTING_MIPMAP_COUNT, + SETTING_FORMAT, + SETTING_LINEAR, + SETTING_TYPE, + SETTING_DPI_SCALE, + SETTING_MSAA, + SETTING_RENDER_TARGET, + SETTING_COMPUTE_WRITE, + SETTING_READABLE, + SETTING_MAX_ENUM + }; + + // Size and format will be overridden by ImageData when supplied. + struct Settings + { + int width = 1; + int height = 1; + int layers = 1; // depth for 3D textures + TextureType type = TEXTURE_2D; + MipmapsMode mipmaps = MIPMAPS_NONE; + PixelFormat format = PIXELFORMAT_NORMAL; + bool linear = false; + float dpiScale = 1.0f; + int msaa = 1; + bool renderTarget = false; + bool computeWrite = false; + int mipmapCount = 0; + std::optional readable; + }; + + struct Slices + { + public: + Slices(TextureType type) : textureType(type) + {} + + void Clear() + { + this->data.clear(); + } + + void Set(int slice, int mipmap, ImageDataBase* data) + { + if (this->textureType == TEXTURE_VOLUME) + { + if (mipmap >= (int)this->data.size()) + this->data.resize(mipmap + 1); + + if (slice >= (int)this->data[mipmap].size()) + this->data[mipmap].resize(slice + 1); + + this->data[mipmap][slice].Set(data); + } + else + { + if (slice >= (int)this->data.size()) + this->data.resize(slice + 1); + + if (mipmap >= (int)this->data[slice].size()) + this->data[slice].resize(mipmap + 1); + + this->data[slice][mipmap].Set(data); + } + } + + ImageDataBase* Get(int slice, int mipmap) const + { + if (slice < 0 || slice >= this->GetSliceCount(mipmap)) + return nullptr; + + if (mipmap < 0 || mipmap >= this->GetMipmapCount(slice)) + return nullptr; + + if (this->textureType == TEXTURE_VOLUME) + return this->data[mipmap][slice].Get(); + + return this->data[slice][mipmap].Get(); + } + + TextureType GetTextureType() const + { + return this->textureType; + } + + void Add(CompressedImageData* data, int startSlice, int startMipmap, bool addAllSlices, + bool addAllMipmaps) + { + int sliceCount = addAllSlices ? data->GetSliceCount() : 1; + int mipmapCount = addAllMipmaps ? data->GetMipmapCount() : 1; + + for (int mipmap = 0; mipmap < mipmapCount; mipmap++) + { + for (int slice = 0; slice < sliceCount; slice++) + { + this->Set(startSlice + slice, startMipmap + mipmap, + data->GetSlice(slice, mipmap)); + } + } + } + + int GetSliceCount(int mipmap = 0) const + { + if (this->textureType == TEXTURE_VOLUME) + { + if (mipmap < 0 || mipmap >= (int)this->data.size()) + return 0; + + return this->data[mipmap].size(); + } + + return this->data.size(); + } + + int GetMipmapCount(int slice = 0) const + { + if (this->textureType == TEXTURE_VOLUME) + return this->data.size(); + else + { + if (slice < 0 || slice >= (int)data.size()) + return 0; + + return this->data[slice].size(); + } + } + + bool Validate() const + { + int sliceCount = this->GetSliceCount(); + int mipmapCount = this->GetMipmapCount(0); + + if (sliceCount == 0 || mipmapCount == 0) + { + throw love::Exception( + "At least one ImageData or CompressedImageData is required."); + } + + if (this->textureType == TEXTURE_CUBE && sliceCount != 6) + throw love::Exception("Cube textures must have exactly 6 sides."); + + auto* first = this->Get(0, 0); + + int width = first->GetWidth(); + int height = first->GetHeight(); + + int depth = (this->textureType == TEXTURE_VOLUME) ? sliceCount : 1; + PixelFormat format = first->GetFormat(); + + int expectedMipmaps = Texture<>::GetTotalMipmapCount(width, height, depth); + + if (mipmapCount != expectedMipmaps && mipmapCount != 1) + { + throw love::Exception( + "Texture does not have all required mipmap levels (expected %d, got %d)", + expectedMipmaps, mipmapCount); + } + + if (this->textureType == TEXTURE_CUBE && width != height) + { + throw love::Exception( + "Cube textures must have equal widths and heights for each cube face."); + } + + int mipWidth = width; + int mipHeight = height; + int mipSlices = sliceCount; + + for (int mipmap = 0; mipmap < mipmapCount; mipmap++) + { + if (this->textureType == TEXTURE_VOLUME) + { + sliceCount = this->GetSliceCount(mipmap); + + if (sliceCount != mipSlices) + { + throw love::Exception("Invalid number of image data layers in mipmap " + "level %d (expected %d, got %d)", + mipmap + 1, mipSlices, sliceCount); + } + } + + for (int slice = 0; slice < sliceCount; slice++) + { + auto* sliceData = this->Get(slice, mipmap); + + if (!sliceData) + { + throw love::Exception("Missing image data (slice %d, mipmap level %d)", + slice + 1, mipmap + 1); + } + + int realWidth = sliceData->GetWidth(); + int realHeight = sliceData->GetHeight(); + + if (this->GetMipmapCount(slice) != mipmapCount) + { + throw love::Exception( + "All texture layers must have the same mipmap count."); + } + + if (mipWidth != realWidth) + { + throw love::Exception("Width of image data (slice %d, mipmap level %d) " + "is incorrect (expected %d, got %d)", + slice + 1, mipmap + 1, mipWidth, realWidth); + } + + if (mipHeight != realHeight) + { + throw love::Exception("Height of image data (slice %d, mipmap level " + "%d) is incorrect (expected %d, got %d)", + slice + 1, mipmap + 1, mipHeight, realHeight); + } + + if (format != sliceData->GetFormat()) + { + throw love::Exception( + "All texture slices and mipmaps must have the same pixel format."); + } + } + + mipWidth = std::max(mipWidth / 2, 1); + mipHeight = std::max(mipHeight / 2, 1); + + if (this->textureType == TEXTURE_VOLUME) + mipSlices = std::max(mipSlices / 2, 1); + } + + return true; + } + + private: + TextureType textureType; + VectorMipmapLayers data; + }; + + static inline int64_t totalGraphicsMemory = 0; + static inline int textureCount = 0; + + Texture(const Settings& settings, const Slices* data) : + textureType(settings.type), + format(settings.format), + mipmapMode(settings.mipmaps), + renderTarget(settings.renderTarget), + readable(true), + sRGB(false), + width(settings.width), + height(settings.height), + depth(settings.type == TEXTURE_VOLUME ? settings.layers : 1), + layers(settings.type == TEXTURE_2D_ARRAY ? settings.layers : 1), + mipmapCount(1), + pixelWidth(0), + pixelHeight(0), + requestedMSAA(settings.msaa > 1 ? settings.msaa : 0), + actualSamples(1), + state {}, + graphicsMemorySize(0), + slices(settings.type) + { + if (data != nullptr && data->GetMipmapCount() > 0 && data->GetSliceCount() > 0) + { + this->textureType = data->GetTextureType(); + + if (requestedMSAA > 1) + throw love::Exception("MSAA textures cannot be created from ImageData."); + + int dataMipmaps = 1; + if (data->Validate() && data->GetMipmapCount() > 1) + dataMipmaps = data->GetMipmapCount(); + + const auto* slice = data->Get(0, 0); + this->format = slice->GetFormat(); + + if (sRGB) + this->format = love::GetSRGBPixelFormat(this->format); + + this->pixelWidth = slice->GetWidth(); + this->pixelHeight = slice->GetHeight(); + + if (this->textureType == TEXTURE_2D_ARRAY) + this->layers = data->GetSliceCount(); + else if (this->textureType == TEXTURE_VOLUME) + this->depth = data->GetSliceCount(); + + this->width = (int)(this->pixelWidth / settings.dpiScale + 0.5); + this->height = (int)(this->pixelHeight / settings.dpiScale + 0.5); + + if (this->IsCompressed() && dataMipmaps <= 1) + this->mipmapMode = MIPMAPS_NONE; + } + else + { + if (this->IsCompressed()) + throw love::Exception("Compressed textures must be created with initial data."); + + this->pixelWidth = (int)((this->width / settings.dpiScale) + 0.5); + this->pixelHeight = (int)((this->height / settings.dpiScale) + 0.5); + } + + if (settings.readable.has_value()) + this->readable = settings.readable.value(); + else + { + bool isDepthStencilFormat = love::IsPixelFormatDepthStencil(this->format); + this->readable = (!this->renderTarget || !isDepthStencilFormat); + } + } + + ~Texture() + { + --textureCount; + this->SetGraphicsMemorySize(0); + } + + MipmapsMode GetMipmapsMode() const + { + return this->mipmapMode; + } + + TextureType GetTextureType() const + { + return this->textureType; + } + + PixelFormat GetPixelFormat() const + { + return this->format; + } + + bool IsCompressed() const + { + return love::IsPixelFormatCompressed(this->format); + } + + int GetWidth(int mipmap = 0) const + { + return std::max(this->width >> mipmap, 1); + } + + bool IsFormatLinear() const + { + return !this->sRGB && !love::IsPixelFormatSRGB(this->format); + } + + int GetHeight(int mipmap = 0) const + { + return std::max(this->height >> mipmap, 1); + } + + int GetDepth(int mipmap = 0) const + { + return std::max(this->depth >> mipmap, 1); + } + + int GetLayerCount() const + { + return this->layers; + } + + int GetMipmapCount() const + { + return this->mipmapCount; + } + + int GetPixelWidth(int mipmap = 0) const + { + return std::max(this->pixelWidth >> mipmap, 1); + } + + int GetPixelHeight(int mipmap = 0) const + { + return std::max(this->pixelHeight >> mipmap, 1); + } + + int GetRequestedMSAA() const + { + return this->requestedMSAA; + } + + int GetDPIScale() const + { + return (float)this->pixelHeight / (float)this->height; + } + + int GetSliceCount(int mipmap) const + { + switch (this->textureType) + { + case TEXTURE_2D: + return 1; + case TEXTURE_CUBE: + return 6; + case TEXTURE_2D_ARRAY: + return this->layers; + case TEXTURE_VOLUME: + return this->GetDepth(mipmap); + default: + break; + }; + + return 1; + } + + bool IsReadable() const + { + return this->readable; + } + + bool IsRenderTarget() const + { + return this->renderTarget; + } + + bool IsValidSlice(int slice, int mipmap) const + { + return slice >= 0 && slice < this->GetSliceCount(mipmap); + } + + int GetMSAA() const + { + return this->actualSamples; + } + + bool IsComputeWritable() const + { + return false; + } + + void SetSamplerState(const SamplerState& state) + { + if (!this->readable) + return; + + this->state = state; + + if (this->state.mipmapFilter != SamplerState::MIPMAP_FILTER_NONE && + this->GetMipmapCount() == 1) + { + this->state.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE; + } + } + + static int GetTotalMipmapCount(int width, int height) + { + return std::log2(std::max(width, height)) + 1; + } + + static int GetTotalMipmapCount(int width, int height, int depth) + { + return std::log2(std::max(std::max(width, height), depth)) + 1; + } + + const SamplerState& GetSamplerState() const + { + return this->state; + } + + Quad* GetQuad() const + { + return this->quad; + } + + virtual void Draw(Graphics& graphics, + const Matrix4& transform) = 0; + + virtual void Draw(Graphics& graphics, Quad* quad, + const Matrix4& transform) = 0; + + // clang-format off + static constexpr BidirectionalMap textureTypes = { + "2d", TEXTURE_2D, + "volume", TEXTURE_VOLUME, + "array", TEXTURE_2D_ARRAY, + "cube", TEXTURE_CUBE + }; + + static constexpr BidirectionalMap mipmapModes = { + "none", MIPMAPS_NONE, + "manual", MIPMAPS_MANUAL, + "auto", MIPMAPS_AUTO + }; + + static constexpr BidirectionalMap settingsTypes = { + "width", SETTING_WIDTH, + "height", SETTING_HEIGHT, + "layers", SETTING_LAYERS, + "mipmaps", SETTING_MIPMAPS, + "mipmapcount", SETTING_MIPMAP_COUNT, + "format", SETTING_FORMAT, + "linear", SETTING_LINEAR, + "type", SETTING_TYPE, + "dpiscale", SETTING_DPI_SCALE, + "msaa", SETTING_MSAA, + "canvas", SETTING_RENDER_TARGET, + "computewrite", SETTING_COMPUTE_WRITE, + "readable", SETTING_READABLE + }; + // clang-format on + + protected: + void CreateTexture() + { + this->SetSamplerState(this->state); + } + + void SetGraphicsMemorySize(int64_t bytes) + { + totalGraphicsMemory = + std::max(totalGraphicsMemory - this->graphicsMemorySize, 0); + + bytes = std::max(bytes, 0); + this->graphicsMemorySize = bytes; + + totalGraphicsMemory += bytes; + } + + TextureType textureType; + PixelFormat format; + MipmapsMode mipmapMode; + + bool renderTarget; + bool readable; + bool sRGB; + + int width; + int height; + + int depth; + int layers; + int mipmapCount; + + int pixelWidth; + int pixelHeight; + + int requestedMSAA; + int actualSamples; + + SamplerState state; + + StrongReference quad; + int64_t graphicsMemorySize; + Slices slices; + }; +} // namespace love diff --git a/include/objects/texture/texturec.h b/include/objects/texture/texturec.h deleted file mode 100644 index a163b5499..000000000 --- a/include/objects/texture/texturec.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include "common/exception.h" -#include "common/lmath.h" -#include "common/strongref.h" -#include "objects/quad/quad.h" - -#include "objects/drawable/drawable.h" - -#if defined(__SWITCH__) - #include "deko3d/common.h" -#endif - -#include - -namespace love::common -{ - class Texture : public Drawable - { - public: - enum TextureType - { - TEXTURE_2D, - TEXTURE_MAX_ENUM - }; - - enum WrapMode - { - WRAP_CLAMP, - WRAP_CLAMP_ZERO, - WRAP_REPEAT, - WRAP_MIRRORED_REPEAT, - WRAP_MAX_ENUM - }; - - enum FilterMode - { - FILTER_NEAREST, - FILTER_LINEAR, - FILTER_NONE, - FILTER_MAX_ENUM - }; - - struct Filter - { - FilterMode min = FILTER_LINEAR; - FilterMode mag = FILTER_LINEAR; - - FilterMode mipmap = FILTER_NONE; - float anisotropy = 1.0f; - }; - - struct Wrap - { - WrapMode s = WRAP_CLAMP_ZERO; - WrapMode t = WRAP_CLAMP_ZERO; - WrapMode r = WRAP_CLAMP_ZERO; - }; - - static love::Type type; - - static Filter defaultFilter; - static FilterMode defaultMipmapFilter; - static float defaultMipmapSharpness; - - Texture(TextureType texType); - virtual ~Texture(); - - TextureType GetTextureType() const; - - int GetWidth(int mip = 0) const; - int GetHeight(int mip = 0) const; - - virtual void SetFilter(const Filter& f) = 0; - virtual const Filter& GetFilter() const; - - virtual bool SetWrap(const Wrap& w) = 0; - virtual const Wrap& GetWrap() const; - - virtual void Draw(Graphics* gfx, const Matrix4& localTransform) = 0; - virtual void Draw(Graphics* gfx, love::Quad* quad, const Matrix4& localTransform) = 0; - - love::Quad* GetQuad() const; - - int GetMipmapCount() const; - - static int GetTotalMipmapCount(int width, int height); - - static int GetTotalMipmapCount(int width, int height, int depth); - - static int getTotalMipmapCount(int w, int h); - static int getTotalMipmapCount(int w, int h, int d); - - static bool GetConstant(const char* in, TextureType& out); - static bool GetConstant(TextureType in, const char*& out); - static std::vector GetConstants(TextureType); - - static bool GetConstant(const char* in, FilterMode& out); - static bool GetConstant(FilterMode in, const char*& out); - static std::vector GetConstants(FilterMode); - - static bool GetConstant(const char* in, WrapMode& out); - static bool GetConstant(WrapMode in, const char*& out); - static std::vector GetConstants(WrapMode); - - protected: - TextureType texType; - - int width; - int height; - - Filter filter; - int mipmapCount; - Wrap wrap; - - StrongReference quad; - - void InitQuad(); - }; -} // namespace love::common diff --git a/include/objects/texture/wrap_texture.h b/include/objects/texture/wrap_texture.h deleted file mode 100644 index af05e8d3d..000000000 --- a/include/objects/texture/wrap_texture.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/texture/texture.h" -namespace Wrap_Texture -{ - int GetTextureType(lua_State* L); - - int GetWidth(lua_State* L); - - int GetHeight(lua_State* L); - - int GetDimensions(lua_State* L); - - int SetFilter(lua_State* L); - - int GetFilter(lua_State* L); - - int SetWrap(lua_State* L); - - int GetWrap(lua_State* L); - - extern const luaL_Reg functions[9]; - - love::Texture* CheckTexture(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Texture diff --git a/include/objects/texture/wrap_texture.hpp b/include/objects/texture/wrap_texture.hpp new file mode 100644 index 000000000..7e1fe1319 --- /dev/null +++ b/include/objects/texture/wrap_texture.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include +#include + +namespace Wrap_Texture +{ + love::Texture* CheckTexture(lua_State* L, int index); + + int GetTextureType(lua_State* L); + + int GetWidth(lua_State* L); + + int GetHeight(lua_State* L); + + int GetDimensions(lua_State* L); + + int GetDepth(lua_State* L); + + int GetLayerCount(lua_State* L); + + int GetMipmapCount(lua_State* L); + + int GetPixelWidth(lua_State* L); + + int GetPixelHeight(lua_State* L); + + int GetPixelDimensions(lua_State* L); + + int GetDPIScale(lua_State* L); + + int IsFormatLinear(lua_State* L); + + int IsCompressed(lua_State* L); + + int GetMSAA(lua_State* L); + + int SetFilter(lua_State* L); + + int GetFilter(lua_State* L); + + int SetMipmapFilter(lua_State* L); + + int GetMipmapFilter(lua_State* L); + + int SetWrap(lua_State* L); + + int GetWrap(lua_State* L); + + int GetFormat(lua_State* L); + + int IsRenderTarget(lua_State* L); + + int IsComputeWritable(lua_State* L); + + int IsReadable(lua_State* L); + + int SetDepthSampleMode(lua_State* L); + + int GetDepthSampleMode(lua_State* L); + + int GetMipmapMode(lua_State* L); + + int GenerateMipmaps(lua_State* L); + + int ReplacePixels(lua_State* L); + + int RenderTo(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_Texture diff --git a/include/objects/thread/luathread.h b/include/objects/thread/luathread.h deleted file mode 100644 index 40ec2b2bb..000000000 --- a/include/objects/thread/luathread.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "modules/thread/types/threadable.h" - -#include "common/data.h" -#include - -#include - -namespace love -{ - class LuaThread : public Threadable - { - public: - static love::Type type; - - LuaThread(const std::string& name, love::Data* code); - ~LuaThread(); - - void ThreadFunction(); - - const std::string& GetError() const; - - bool Start(const std::vector& args); - - private: - void OnError(); - bool hasError; - - StrongReference code; - - std::string name; - std::string error; - - std::vector args; - }; -} // namespace love diff --git a/include/objects/thread/luathread.hpp b/include/objects/thread/luathread.hpp new file mode 100644 index 000000000..028401433 --- /dev/null +++ b/include/objects/thread/luathread.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace love +{ + class LuaThread : public Threadable + { + public: + static Type type; + + LuaThread(const std::string& name, Data* code); + + virtual ~LuaThread() + {} + + void ThreadFunction(); + + const std::string& GetError() const; + + bool HasError() const + { + return this->hasError; + } + + bool Start(const std::vector& args); + + private: + void OnError(); + + StrongReference code; + std::string name; + std::string error; + bool hasError; + + std::vector args; + }; +} // namespace love diff --git a/include/objects/thread/wrap_luathread.h b/include/objects/thread/wrap_luathread.h deleted file mode 100644 index b4bea38c7..000000000 --- a/include/objects/thread/wrap_luathread.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/thread/luathread.h" - -namespace Wrap_LuaThread -{ - int Start(lua_State* L); - - int Wait(lua_State* L); - - int GetError(lua_State* L); - - int IsRunning(lua_State* L); - - love::LuaThread* CheckThread(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_LuaThread diff --git a/include/objects/thread/wrap_luathread.hpp b/include/objects/thread/wrap_luathread.hpp new file mode 100644 index 000000000..f8acbcc98 --- /dev/null +++ b/include/objects/thread/wrap_luathread.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +namespace Wrap_LuaThread +{ + int Start(lua_State* L); + + int Wait(lua_State* L); + + int GetError(lua_State* L); + + int IsRunning(lua_State* L); + + love::LuaThread* CheckThread(lua_State* L, int index); + + int Register(lua_State* L); +} // namespace Wrap_LuaThread diff --git a/include/objects/transform/transform.h b/include/objects/transform/transform.h deleted file mode 100644 index 6f30e14e7..000000000 --- a/include/objects/transform/transform.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include "common/matrix.h" -#include "common/vector.h" - -#include "objects/object.h" - -#include - -namespace love -{ - class Transform : public Object - { - public: - enum MatrixLayout - { - MATRIX_ROW_MAJOR, - MATRIX_COLUMN_MAJOR, - MATRIX_MAX_ENUM - }; - - static love::Type type; - - Transform(); - - Transform(const Matrix4& matrix); - - Transform(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, - float ky); - - virtual ~Transform() {}; - - Transform* Clone(); - - Transform* Inverse(); - - void Apply(Transform* other); - - void Translate(float x, float y); - - void Rotate(float radians); - - void Scale(float sx, float sy); - - void Shear(float kx, float ky); - - void Reset(); - - void SetTransformation(float x, float y, float a, float sx, float sy, float ox, float oy, - float kx, float ky); - - Vector2 TransformPoint(Vector2 point) const; - - Vector2 InverseTransformPoint(Vector2 point); - - const Matrix4& GetMatrix() const; - - void SetMatrix(const Matrix4& matrix); - - static bool GetConstant(const char* in, MatrixLayout& out); - static bool GetConstant(MatrixLayout in, const char*& out); - static std::vector GetConstants(MatrixLayout); - - private: - Matrix4 matrix; - bool inverseDirty; - Matrix4 inverseMatrix; - - inline const Matrix4& GetInverseMatrix() - { - if (this->inverseDirty) - { - this->inverseDirty = false; - this->inverseMatrix = this->matrix.Inverse(); - } - - return this->inverseMatrix; - } - }; -} // namespace love diff --git a/include/objects/transform/transform.hpp b/include/objects/transform/transform.hpp new file mode 100644 index 000000000..454a8071b --- /dev/null +++ b/include/objects/transform/transform.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include + +#include + +namespace love +{ + class Transform : public Object + { + public: + enum MatrixLayout + { + MATRIX_ROW_MAJOR, + MATRIX_COLUMN_MAJOR + }; + + static Type type; + + Transform(); + + Transform(const Matrix4& matrix); + + Transform(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, + float ky); + + ~Transform() + {} + + Transform* Clone(); + + Transform* Inverse(); + + void Apply(Transform* other); + + void Translate(float x, float y); + + void Rotate(float angle); + + void Scale(float x, float y); + + void Shear(float x, float y); + + void Reset(); + + void SetTransformation(float x, float y, float a, float sx, float sy, float ox, float oy, + float kx, float ky); + + Vector2 TransformPoint(Vector2 point) const; + + Vector2 InverseTransformPoint(Vector2 point); + + Matrix4& GetMatrix(); + + void SetMatrix(const Matrix4& matrix); + + // clang-format off + static constexpr BidirectionalMap matrixLayouts = { + "row", MATRIX_ROW_MAJOR, + "column", MATRIX_COLUMN_MAJOR + }; + // clang-format on + + private: + inline const Matrix4& GetInverseMatrix() + { + if (this->inverseDirty) + { + this->inverseDirty = false; + this->inverseMatrix = this->matrix.Inverse(); + } + + return this->inverseMatrix; + } + + Matrix4 matrix; + bool inverseDirty; + Matrix4 inverseMatrix; + }; +} // namespace love diff --git a/include/objects/transform/wrap_transform.h b/include/objects/transform/wrap_transform.h deleted file mode 100644 index a51d2525d..000000000 --- a/include/objects/transform/wrap_transform.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/transform/transform.h" - -namespace Wrap_Transform -{ - int Clone(lua_State* L); - - int Inverse(lua_State* L); - - int Apply(lua_State* L); - - int IsAffine2DTransform(lua_State* L); - - int Translate(lua_State* L); - - int Rotate(lua_State* L); - - int Scale(lua_State* L); - - int Shear(lua_State* L); - - int Reset(lua_State* L); - - int SetTransformation(lua_State* L); - - int SetMatrix(lua_State* L); - - int GetMatrix(lua_State* L); - - int TransformPoint(lua_State* L); - - int InverseTransformPoint(lua_State* L); - - int _Mul(lua_State* L); - - love::Transform* CheckTransform(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Transform diff --git a/include/objects/transform/wrap_transform.hpp b/include/objects/transform/wrap_transform.hpp new file mode 100644 index 000000000..d564f9042 --- /dev/null +++ b/include/objects/transform/wrap_transform.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +namespace Wrap_Transform +{ + love::Transform* CheckTransform(lua_State* L, int index); + + int Clone(lua_State* L); + + int Inverse(lua_State* L); + + int Apply(lua_State* L); + + int IsAffine2DTransform(lua_State* L); + + int Translate(lua_State* L); + + int Rotate(lua_State* L); + + int Scale(lua_State* L); + + int Shear(lua_State* L); + + int Reset(lua_State* L); + + int SetTransformation(lua_State* L); + + int SetMatrix(lua_State* L); + + int GetMatrix(lua_State* L); + + int TransformPoint(lua_State* L); + + int InverseTransformPoint(lua_State* L); + + int __Mul(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_Transform diff --git a/include/objects/truetyperasterizer/truetyperasterizer.tcc b/include/objects/truetyperasterizer/truetyperasterizer.tcc new file mode 100644 index 000000000..78d2aa6ae --- /dev/null +++ b/include/objects/truetyperasterizer/truetyperasterizer.tcc @@ -0,0 +1,92 @@ +#pragma once + +#include +#include + +#include + +#include + +#include + +#if defined(__3DS__) +struct FT_Library +{ +}; + + #include <3ds.h> +using FT_Face = CFNT_s*; +#else + #include + #include FT_FREETYPE_H + #include FT_GLYPH_H +#endif + +namespace love +{ + template + class TrueTypeRasterizer : public Rasterizer + { + public: + enum Hinting + { + HINTING_NORMAL, + HINTING_LIGHT, + HINTING_MONO, + HINTING_NONE, + HINTING_MAX_ENUM + }; + + TrueTypeRasterizer() + {} + + TrueTypeRasterizer(FT_Library library, Data* data, int size, float dpiSacale, + Hinting hinting); + + virtual ~TrueTypeRasterizer(); + + int GetLineHeight() const override; + + int GetGlyphSpacing(uint32_t glyph) const override; + + int GetGlyphIndex(uint32_t glyph) const override; + + GlyphData* GetGlyphDataForIndex(int index) const override; + + int GetGlyphCount() const override; + + bool HasGlyph(uint32_t glyph) const override; + + float GetKerning(uint32_t left, uint32_t right) const override; + + DataType GetDataType() const override; + + TextShaper* NewTextShaper() override; + + void* GetHandle() const override + { + return (void*)this->face; + } + + static bool Accepts(FT_Library library, Data* data); + + // clang-format off + static constexpr BidirectionalMap hintings = + { + "normal", HINTING_NORMAL, + "light", HINTING_LIGHT, + "mono", HINTING_MONO, + "none", HINTING_NONE + }; + // clang-format on + + protected: + mutable FT_Face face; + StrongReference data; + Hinting hinting; + + mutable std::unordered_map glyphMap; + mutable int glyphCount; + float scale; + }; +} // namespace love diff --git a/include/objects/video/videoc.h b/include/objects/video/videoc.h deleted file mode 100644 index 8a96bfd49..000000000 --- a/include/objects/video/videoc.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include "objects/drawable/drawable.h" - -#include "objects/source/source.h" -#include "objects/videostream/videostream.h" - -#include "objects/image/image.h" -#include "objects/texture/texture.h" - -namespace love -{ - class Graphics; - - namespace common - { - class Video : public Drawable - { - public: - static love::Type type; - - Video(Graphics* graphics, VideoStream* stream, float dpiScale = 1.0f); - - virtual ~Video(); - - virtual void Draw(Graphics* graphics, const Matrix4& matrix) = 0; - - VideoStream* GetStream(); - - love::Source* GetSource(); - - void SetSource(love::Source* source); - - int GetWidth() const; - - int GetHeight() const; - - int GetPixelWidth() const; - - int GetPixelHeight() const; - - void SetFilter(const Texture::Filter& filter); - - const Texture::Filter& GetFilter() const; - - protected: - virtual void Update() = 0; - - StrongReference stream; - - int width; - int height; - - Texture::Filter filter; - - StrongReference source; - StrongReference images[3]; - }; - } // namespace common -} // namespace love diff --git a/include/objects/video/wrap_video.h b/include/objects/video/wrap_video.h deleted file mode 100644 index c17eecec4..000000000 --- a/include/objects/video/wrap_video.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/video/video.h" - -namespace Wrap_Video -{ - int GetStream(lua_State* L); - - int GetSource(lua_State* L); - - int SetSource(lua_State* L); - - int GetWidth(lua_State* L); - - int GetHeight(lua_State* L); - - int GetDimensions(lua_State* L); - - int GetPixelWidth(lua_State* L); - - int GetPixelHeight(lua_State* L); - - int GetPixelDimensions(lua_State* L); - - int SetFilter(lua_State* L); - - int GetFilter(lua_State* L); - - love::Video* CheckVideo(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Video diff --git a/include/objects/videostream/sync/deltasync.h b/include/objects/videostream/sync/deltasync.h deleted file mode 100644 index 50cb74cd5..000000000 --- a/include/objects/videostream/sync/deltasync.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "modules/thread/types/mutex.h" -#include "objects/videostream/sync/framesync.h" - -namespace love -{ - class DeltaSync : public FrameSync - { - public: - DeltaSync(); - - ~DeltaSync(); - - virtual double GetPosition() const override; - - virtual void Update(double dt) override; - - virtual void Play() override; - - virtual void Pause() override; - - virtual void Seek(double time) override; - - virtual bool IsPlaying() const override; - - private: - bool playing; - double position; - double speed; - - thread::MutexRef mutex; - }; -} // namespace love diff --git a/include/objects/videostream/sync/framesync.h b/include/objects/videostream/sync/framesync.h deleted file mode 100644 index 4ff27a16e..000000000 --- a/include/objects/videostream/sync/framesync.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "objects/object.h" - -namespace love -{ - class FrameSync : public Object - { - public: - virtual ~FrameSync() - {} - - virtual double GetPosition() const = 0; - - virtual void Update(double) - {} - - void CopyState(const FrameSync* other); - - /* playback api */ - - virtual void Play() = 0; - - virtual void Pause() = 0; - - virtual void Seek(double offset) = 0; - - virtual double Tell() const; - - virtual bool IsPlaying() const = 0; - }; -} // namespace love diff --git a/include/objects/videostream/sync/sourcesync.h b/include/objects/videostream/sync/sourcesync.h deleted file mode 100644 index e74554e89..000000000 --- a/include/objects/videostream/sync/sourcesync.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "common/strongref.h" - -#include "objects/source/source.h" -#include "objects/videostream/sync/framesync.h" - -namespace love -{ - class SourceSync : public FrameSync - { - public: - SourceSync(Source* source); - - virtual double GetPosition() const override; - - virtual void Play() override; - - virtual void Pause() override; - - virtual void Seek(double time) override; - - virtual bool IsPlaying() const override; - - private: - StrongReference source; - }; -} // namespace love diff --git a/include/objects/videostream/theora/oggdemuxer.h b/include/objects/videostream/theora/oggdemuxer.h deleted file mode 100644 index 70dcbdf13..000000000 --- a/include/objects/videostream/theora/oggdemuxer.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include - -#include "objects/file/file.h" - -#include - -namespace love -{ - class OggDemuxer - { - public: - OggDemuxer(File* file); - - ~OggDemuxer(); - - enum StreamType - { - TYPE_THEORA, - TYPE_UNKNOWN - }; - - static constexpr int SYNC_VALUE = 0x2000; - static constexpr int THEORA_BYTES_MIN = 0x07; - static constexpr int THEORA_HEADER_TYPE = 0x80; - static constexpr double REWIND_THRESHOLD = 0.01; - - StreamType FindStream(); - - bool ReadPacket(ogg_packet& packet, bool mustSucceed = false); - - void ReSync(); - - bool IsEOS() const; - - const std::string& GetFilename() const; - - bool Seek(ogg_packet& packet, double target, std::function getTime); - - private: - StrongReference file; - - ogg_sync_state sync; - ogg_stream_state stream; - ogg_page page; - - bool intiialized; - int serial; - bool endOfStream; - - bool ReadPage(bool errorEOF = false); - - StreamType DetermineType(); - }; -} // namespace love diff --git a/include/objects/videostream/theora/theorastreamc.h b/include/objects/videostream/theora/theorastreamc.h deleted file mode 100644 index e30f8971d..000000000 --- a/include/objects/videostream/theora/theorastreamc.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "modules/thread/types/mutex.h" - -#include "objects/file/file.h" -#include "objects/videostream/theora/oggdemuxer.h" -#include "objects/videostream/videostream.h" - -namespace love::common -{ - class TheoraStream : public VideoStream - { - public: - TheoraStream(File* file); - - ~TheoraStream(); - - const void* GetFrontBuffer() const; - - virtual size_t GetSize() const = 0; - - void FillBackBuffer(); - - bool SwapBuffers(); - - int GetWidth() const; - - int GetHeight() const; - - const std::string& GetFilename() const; - - void SetSync(FrameSync* other); - - bool IsPlaying() const; - - void ThreadedFillBackBuffer(double dt); - - virtual void SetupBuffers() = 0; - - virtual void FillBufferData(th_ycbcr_buffer bufferInfo) = 0; - - protected: - IFrame* frontBuffer; - IFrame* backBuffer; - - OggDemuxer demuxer; - bool headerParsed; - - ogg_packet packet; - - th_info info; - th_dec_ctx* decoder; - - thread::MutexRef bufferMutex; - bool frameReady; - - double lastFrame; - double nextFrame; - - struct PostProcess - { - int current; - int maximum; - int offset; - } quality; - - void ParseHeader(); - - void SeekDecoder(double target); - }; -} // namespace love::common diff --git a/include/objects/videostream/utility/stream.h b/include/objects/videostream/utility/stream.h deleted file mode 100644 index d151ff1ed..000000000 --- a/include/objects/videostream/utility/stream.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "objects/object.h" - -namespace love -{ - class Stream : public Object - { - public: - static love::Type type; - - virtual ~Stream() - {} - - virtual void FillBackBuffer() - {} - - virtual const void* GetFrontBuffer() const = 0; - - virtual size_t GetSize() const = 0; - - virtual bool SwapBuffers() = 0; - }; -} // namespace love diff --git a/include/objects/videostream/videostream.h b/include/objects/videostream/videostream.h deleted file mode 100644 index 2b5845511..000000000 --- a/include/objects/videostream/videostream.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include "common/strongref.h" - -#include "objects/videostream/sync/framesync.h" -#include "objects/videostream/utility/stream.h" - -namespace love -{ - class VideoStream : public Stream - { - public: - static love::Type type; - - struct IFrame - { - IFrame() - {} - - ~IFrame() - {} - }; - - virtual ~VideoStream() - {} - - virtual int GetWidth() const = 0; - - virtual int GetHeight() const = 0; - - virtual const std::string& GetFilename() const = 0; - - /* playback api */ - - virtual void Play(); - - virtual void Pause(); - - virtual void Seek(double offset); - - virtual double Tell() const; - - virtual bool IsPlaying() const; - - /* sync stuff */ - - virtual void SetSync(FrameSync* sync); - - virtual FrameSync* GetSync() const; - - protected: - StrongReference frameSync; - }; -} // namespace love diff --git a/include/objects/videostream/wrap_videostream.h b/include/objects/videostream/wrap_videostream.h deleted file mode 100644 index 47c72c5c1..000000000 --- a/include/objects/videostream/wrap_videostream.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "objects/videostream/videostream.h" - -namespace Wrap_VideoStream -{ - int SetSync(lua_State* L); - - int GetFilename(lua_State* L); - - int Play(lua_State* L); - - int Pause(lua_State* L); - - int Seek(lua_State* L); - - int Rewind(lua_State* L); - - int Tell(lua_State* L); - - int IsPlaying(lua_State* L); - - love::VideoStream* CheckVideoStream(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_VideoStream diff --git a/include/objects/world/world.hpp b/include/objects/world/world.hpp new file mode 100644 index 000000000..66dc74ac7 --- /dev/null +++ b/include/objects/world/world.hpp @@ -0,0 +1,225 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include + +namespace love +{ + class Contact; + class Body; + class Shape; + class Joint; + + class World : public Object, + public b2ContactListener, + public b2ContactFilter, + public b2DestructionListener + { + public: + friend class Joint; + friend class DistanceJoint; + friend class MouseJoint; + friend class Body; + friend class Shape; + + static love::Type type; + + class ContactCallback + { + public: + Reference* reference; + lua_State* state; + World* world; + + ContactCallback(World* world); + + void Process(b2Contact* contact, const b2ContactImpulse* impulse = nullptr); + + ~ContactCallback(); + }; + + class ContactFilter + { + public: + Reference* reference; + lua_State* state; + + ContactFilter(); + + ~ContactFilter(); + + bool Process(Shape* a, Shape* b); + }; + + class QueryCallback : public b2QueryCallback + { + public: + QueryCallback(World* world, lua_State* L, int index); + + virtual ~QueryCallback(); + + bool ReportFixture(b2Fixture* fixture) override; + + private: + World* world; + lua_State* state; + int funcIndex; + int userArgs; + }; + + class CollectCallback : public b2QueryCallback + { + public: + CollectCallback(World* world, uint16_t mask, lua_State* L); + + virtual ~CollectCallback(); + + bool ReportFixture(b2Fixture* fixture) override; + + private: + World* world; + uint16_t categoryMask; + lua_State* state; + int index = 1; + }; + + class RayCastCallback : public b2RayCastCallback + { + public: + RayCastCallback(World* world, lua_State* L, int index); + + virtual ~RayCastCallback(); + + float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, + float fraction) override; + + private: + World* world; + lua_State* state; + int funcIndex; + int userArgs; + }; + + class RayCastOneCallback : public b2RayCastCallback + { + public: + RayCastOneCallback(uint16_t mask, bool any); + + virtual ~RayCastOneCallback() + {} + + float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, + float fraction) override; + + b2Fixture* hitFixture; + b2Vec2 hitPoint; + b2Vec2 hitNormal; + float hitFraction; + + private: + uint16_t categoryMask; + bool any; + }; + + World(); + + World(b2Vec2 gravity, bool sleep); + + virtual ~World(); + + void Update(float delta); + + void Update(float delta, int velocityIterations, int positionIterations); + + void BeginContact(b2Contact* contact); + + void EndContact(b2Contact* contact); + + void PreSolve(b2Contact* contact, const b2Manifold* oldManifold); + + void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse); + + bool ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB); + + void SayGoodbye(b2Fixture* fixture); + + void SayGoodbye(b2Joint* joint); + + bool IsValid() const; + + int SetCallbacks(lua_State* L); + + int GetCallbacks(lua_State* L); + + void SetCallbacksL(lua_State* L); + + int SetContactFilter(lua_State* L); + + int GetContactFilter(lua_State* L); + + void SetGravity(float x, float y); + + int GetGravity(lua_State* L); + + void TranslateOrigin(float x, float y); + + void SetSleepingAllowed(bool allowed); + + bool IsSleepingAllowed() const; + + bool IsLocked() const; + + int GetBodyCount() const; + + int GetJointCount() const; + + int GetContactCount() const; + + int GetBodies(lua_State* L) const; + + int GetJoints(lua_State* L) const; + + int GetContacts(lua_State* L); + + b2Body* GetGroundBody() const; + + int QueryShapesInArea(lua_State* L); + + int GetShapesInArea(lua_State* L); + + int RayCast(lua_State* L); + + int RayCastAny(lua_State* L); + + int RayCastClosest(lua_State* L); + + void Destroy(); + + void RegisterObject(void* b2Object, Object* object); + + void UnregisterObject(void* b2Object); + + Object* FindObject(void* b2Object) const; + + private: + b2World* world; + b2Body* groundBody; + + std::vector destructBodies; + std::vector destructShapes; + std::vector destructJoints; + + bool destructWorld; + + ContactCallback begin, end, preSolve, postSolve; + ContactFilter filter; + + std::unordered_map box2dObjectMap; + }; +} // namespace love diff --git a/include/objects/world/wrap_world.hpp b/include/objects/world/wrap_world.hpp new file mode 100644 index 000000000..857246b5c --- /dev/null +++ b/include/objects/world/wrap_world.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +namespace Wrap_World +{ + love::World* CheckWorld(lua_State* L, int index); + + int Update(lua_State* L); + + int SetCallbacks(lua_State* L); + + int GetCallbacks(lua_State* L); + + int SetContactFilter(lua_State* L); + + int GetContactFilter(lua_State* L); + + int SetGravity(lua_State* L); + + int GetGravity(lua_State* L); + + int TranslateOrigin(lua_State* L); + + int SetSleepingAllowed(lua_State* L); + + int IsSleepingAllowed(lua_State* L); + + int IsLocked(lua_State* L); + + int GetBodyCount(lua_State* L); + + int GetJointCount(lua_State* L); + + int GetContactCount(lua_State* L); + + int GetBodies(lua_State* L); + + int GetJoints(lua_State* L); + + int GetContacts(lua_State* L); + + int QueryShapesInArea(lua_State* L); + + int GetShapesInArea(lua_State* L); + + int RayCast(lua_State* L); + + int RayCastAny(lua_State* L); + + int RayCastClosest(lua_State* L); + + int Destroy(lua_State* L); + + int IsDestroyed(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_World diff --git a/include/scripts/nogame.lua b/include/scripts/nogame.lua new file mode 100644 index 000000000..c388f51c9 --- /dev/null +++ b/include/scripts/nogame.lua @@ -0,0 +1,318 @@ +R"luastring"--( +-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string. +-- There is a matching delimiter at the bottom of the file. +---@class love +---@class love +local love = require("love") + +---The nogame ( ͡° ͜ʖ ͡°) +function love.nogame() + ---For testing on PC, use nest + if love.filesystem.getInfo("nest/init.lua", "file") then + require("nest").init({ console = "switch" }) + end + + ---Temporary locals for the strings to set + local no_game_path, cartridge_path = nil, nil + + local operating_system = love._os:lower() + local running_console = love._console and love._console:lower() + + local path, mount_path = "assets/", nil + if operating_system == "windows" then + if running_console == "3ds" then + path = path .. "ctr/" + end + no_game_path = path .. "nogame.png" + cartridge_path = path .. "cartridge.png" + else + mount_path = "romfs" + if operating_system ~= "horizon" then + path = "/vol/content/" + else + path = "romfs:/" + end + + local success = love.filesystem.mountFullPath(path, mount_path, "read", true) + + if not success then + error("Failed to mount nogame romfs") + end + + no_game_path = mount_path .. "/nogame/nogame.png" + cartridge_path = mount_path .. "/nogame/cartridge.png" + end + + local wave_thread_code = + [[ + local heights, phases, speeds, dimensions = ... + + love.timer = require("love.timer") + love.math = require("love.math") + + local step = math.ceil(dimensions.x / 100) + + if dimensions.x == 400 then + step = 100 + elseif dimensions.x == 1280 then + step = 128 + elseif dimensions.x == 1920 then + step = 192 + end + + local channels = + { + love.thread.getChannel("wave_1"), + love.thread.getChannel("wave_2") + } + + local function main_loop() + for index = 1, #channels do + channels[index]:clear() + + -- points to render BezierCurve with + local points = {} + local height = dimensions.y - (dimensions.y * heights[index]) + + for x = 0, dimensions.x, step do + local sine = math.sin(x * speeds[index] / dimensions.x + love.timer.getTime() + phases[index]) + local y = sine * 10 + height + + table.insert(points, x) + table.insert(points, y) + end + + local curve_points = love.math.newBezierCurve(points):render(2) + + table.insert(curve_points, dimensions.x) + table.insert(curve_points, dimensions.y) + + table.insert(curve_points, 0) + table.insert(curve_points, dimensions.y) + + channels[index]:supply(curve_points) + end + end + + while true do + main_loop() + end + ]] + + local Wave = {} + Wave.__index = Wave + + function Wave.new(id, color) + local self = setmetatable({}, Wave) + + self.channel = love.thread.getChannel(("wave_%d"):format(id)) + self.color = color + + return self + end + + function Wave:draw() + local points = self.channel:demand() + + love.graphics.setColor(self.color) + love.graphics.polygon("fill", points) + end + + local NoGame = {} + NoGame.__index = NoGame + + function NoGame.new() + local self = setmetatable({}, NoGame) + self.texture = love.graphics.newImage(no_game_path) + + local screen = self.getPosition() + self.x = (love.graphics.getWidth(screen) - self.texture:getWidth()) * 0.5 + + if screen == "default" then + self.y = (love.graphics.getHeight() * 0.95) - self.texture:getHeight() + return self + end + + self.y = (love.graphics.getHeight() - self.texture:getHeight()) * 0.5 + return self + end + + function NoGame.getPosition() + if running_console == "wii u" then + return "gamepad" + elseif running_console == "switch" then + return "default" + end + return "bottom" + end + + function NoGame:draw() + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(self.texture, self.x, self.y) + end + + local Cartridge = {} + Cartridge.__index = Cartridge + + function Cartridge.new() + local self = setmetatable({}, Cartridge) + + local width, height = 0, 0 + self.texture = love.graphics.newImage(cartridge_path) + + width, height, self.quads = Cartridge.createQuads(self.texture) + + self.offset = { + x = width * 0.5, + y = height * 0.5 + } + + self.x = (love.graphics.getWidth() - width) * 0.5 + self.y = (love.graphics.getHeight() - height) * 0.5 + + self.x = self.x + self.offset.x + self.y = self.y + self.offset.y + + self.angle = 0 + self.isRight = true + + self.maxAngle = math.pi / 8 + + return self + end + + ---Create the quads for the cartridge + ---@param texture love.Image + ---@return number, number, love.Quad + function Cartridge.createQuads(texture) + local result = {} + + local width, height = 150, 150 + if running_console ~= "3ds" then + width, height = 600, 600 + end + + for index = 1, 2 do + result[index] = love.graphics.newQuad((index - 1) * width, 0, width, height, texture) + end + + return width, height, result + end + + function Cartridge:update(dt) + local angle_add = -math.pi / 4 + if self.isRight then + angle_add = -angle_add + end + self.angle = self.angle + angle_add * dt + + if self.angle > self.maxAngle then + self.isRight = false + return + end + + if self.angle < -self.maxAngle then + self.isRight = true + end + end + + function Cartridge:draw(depth) + love.graphics.setColor(1, 1, 1, 1) + + local x_depth = self.x - (depth * 3) + + for index = 1, #self.quads do + local quad = self.quads[index] + love.graphics.draw(self.texture, quad, x_depth, self.y, self.angle, 1, 1, self.offset.x, self.offset.y) + end + end + + local waves = {} + + local no_game = nil + local cartridge = nil + local wave_thread = nil + + function love.load() + love.graphics.setBackgroundColor(0.93, 0.93, 0.93) + + table.insert(waves, Wave.new(1, { 0.81, 0.31, 0.56 })) + table.insert(waves, Wave.new(2, { 0.28, 0.65, 0.93 })) + + wave_thread = love.thread.newThread(wave_thread_code) + + cartridge = Cartridge.new() + no_game = NoGame.new() + + local width, height = love.graphics.getDimensions() + wave_thread:start({ 0.35, 0.25 }, { 0, 2 }, { 3.0, 3.5 }, { x = width, y = height }) + end + + function love.update(dt) + cartridge:update(dt) + end + + local function draw_top(depth) + for _, value in ipairs(waves) do + value:draw() + end + + cartridge:draw(depth) + + if running_console == "switch" then + no_game:draw() + end + end + + local function draw_bottom(screen) + if running_console ~= "switch" then + love.graphics.setColor(waves[2].color) + love.graphics.rectangle("fill", 0, 0, love.graphics.getDimensions(screen)) + end + + no_game:draw() + end + + local screens = { "left", "right", "tv", "default" } + + local function contains(t, value) + for key, needle in ipairs(t) do + if needle == value then + return true + end + end + return false + end + + ---Gets the 3D Depth on the 3DS + ---@return number + local function get_depth() + if love.graphics.getDepth then + return love.graphics.getDepth() + end + return 0 + end + + function love.draw(screen) + local depth = -get_depth() + if screen == "right" then + depth = -depth + end + + if contains(screens, screen) then + draw_top(depth) + else + draw_bottom(screen) + end + end + + function love.gamepadpressed(_, button) + if button == "start" then + love.event.quit() + end + end +end + +return love.nogame +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/include/scripts/wrap_event.lua b/include/scripts/wrap_event.lua new file mode 100644 index 000000000..c481318fe --- /dev/null +++ b/include/scripts/wrap_event.lua @@ -0,0 +1,6 @@ +R"luastring"--( +function love.event.poll() + return love.event.poll_i +end +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/source/scripts/wrap_graphics.lua b/include/scripts/wrap_graphics.lua similarity index 82% rename from source/scripts/wrap_graphics.lua rename to include/scripts/wrap_graphics.lua index 0933b1e4c..16f65d2a3 100644 --- a/source/scripts/wrap_graphics.lua +++ b/include/scripts/wrap_graphics.lua @@ -1,3 +1,4 @@ +R"luastring" --( function love.graphics.newVideo(file, settings) settings = settings == nil and {} or settings @@ -9,7 +10,7 @@ function love.graphics.newVideo(file, settings) local source, success if settings.audio ~= false and love.audio then - success, source = pcall(love.audio.newSource, video:getStream():getFilename(), "stream") + success, source = pcall(love.audio.newSource, video:getStream():getFilename(), "stream", "file") end if success then @@ -26,3 +27,5 @@ function love.graphics.newVideo(file, settings) return video end +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/source/scripts/wrap_imagedata.lua b/include/scripts/wrap_imagedata.lua similarity index 92% rename from source/scripts/wrap_imagedata.lua rename to include/scripts/wrap_imagedata.lua index 6e037e870..aad6cb5ce 100644 --- a/source/scripts/wrap_imagedata.lua +++ b/include/scripts/wrap_imagedata.lua @@ -1,3 +1,4 @@ +R"luastring"--( local ImageData_mt = ... local ImageData = ImageData_mt.__index @@ -35,3 +36,5 @@ function ImageData:mapPixel(func, ix, iy, iw, ih) -- performAtomic and mapPixelUnsafe have Lua-C API and FFI versions. self:_performAtomic(self._mapPixelUnsafe, self, func, ix, iy, iw, ih) end +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/source/scripts/wrap_math.lua b/include/scripts/wrap_math.lua similarity index 92% rename from source/scripts/wrap_math.lua rename to include/scripts/wrap_math.lua index 940427beb..25a371d63 100644 --- a/source/scripts/wrap_math.lua +++ b/include/scripts/wrap_math.lua @@ -1,3 +1,4 @@ +R"luastring"--( local love_math = ... local floor = math.floor @@ -62,3 +63,5 @@ function love_math.colorFromBytes(r, g, b, a) return r, g, b, a end +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/source/scripts/wrap_randomgenerator.lua b/include/scripts/wrap_randomgenerator.lua similarity index 88% rename from source/scripts/wrap_randomgenerator.lua rename to include/scripts/wrap_randomgenerator.lua index f7fabd98f..92e311855 100644 --- a/source/scripts/wrap_randomgenerator.lua +++ b/include/scripts/wrap_randomgenerator.lua @@ -1,3 +1,4 @@ +R"luastring"--( local rng_mt = ... local RandomGenerator = rng_mt.__index @@ -32,3 +33,5 @@ function RandomGenerator:random(l, u) local r = _random(self) return getrandom(r, l, u) end +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/source/scripts/wrap_video.lua b/include/scripts/wrap_video.lua similarity index 83% rename from source/scripts/wrap_video.lua rename to include/scripts/wrap_video.lua index 3f608e617..7b6a49dad 100644 --- a/source/scripts/wrap_video.lua +++ b/include/scripts/wrap_video.lua @@ -1,3 +1,4 @@ +R"luastring"--( local Video_mt = ... local Video = Video_mt.__index @@ -29,3 +30,5 @@ end function Video:isPlaying() return self:getStream():isPlaying() end +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/include/scripts/wrap_window.lua b/include/scripts/wrap_window.lua new file mode 100644 index 000000000..9429f2015 --- /dev/null +++ b/include/scripts/wrap_window.lua @@ -0,0 +1,59 @@ +R"luastring"--( +local function main(...) + local screens = love.graphics.getScreens() + local delta_time = 0 + + local messagebox = require("window_messagebox") + local message_box = messagebox.new(...) + + while true do + if love.event and love.event.pump then + love.event.pump() + + for name, a, b, c, d, e, f in love.event.poll() do + if name == "quit" then + return 0 + else + local result = message_box:poll(name, a, b, c, d, e, f) + if result then + g_windowShown = false + return result + end + end + end + end + + if love.timer then + delta_time = love.timer.step() + end + + message_box:update(delta_time) + + if love.graphics then + for _, screen in ipairs(screens) do + love.graphics.origin() + + love.graphics.setActiveScreen(screen) + love.graphics.clear(love.graphics.getBackgroundColor()) + + if screen == "bottom" then + message_box:draw() + end + end + + love.graphics.present() + end + + if love.timer then + love.timer.sleep(0.001) + end + end +end + +function love.window.showMessageBox(title, text, buttons) + g_windowShown = true + + return main(title, text, buttons) +end +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/include/utilities/base64.hpp b/include/utilities/base64.hpp new file mode 100644 index 000000000..7155e8315 --- /dev/null +++ b/include/utilities/base64.hpp @@ -0,0 +1,9 @@ +#include + +namespace love +{ + char* Base64Encode(const char* src, size_t srcLength, size_t lineLength, + size_t& dstLength); + + char* Base64Decode(const char* src, size_t srcLength, size_t& dstLength); +} // namespace love diff --git a/include/utilities/bidirectionalmap/bidirectionalmap.hpp b/include/utilities/bidirectionalmap/bidirectionalmap.hpp new file mode 100644 index 000000000..0429b5b82 --- /dev/null +++ b/include/utilities/bidirectionalmap/bidirectionalmap.hpp @@ -0,0 +1,425 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace BidirectionalMapInternals +{ + // Used to compare C strings; operator== doesn't work properly for those + struct cstringcomp + { + constexpr bool operator()(const char* a, const char* b) const + { + if (a == nullptr && b == nullptr) + { + return true; + } + else if (a == nullptr || b == nullptr) + { + return false; + } + return std::string_view(a) == std::string_view(b); + } + + constexpr bool operator()(const char* a, std::string_view b) const { return a == b; } + + constexpr bool operator()(std::string_view b, const char* a) const { return a == b; } + }; + + template + struct DefaultComparatorForType + { + private: + using testtype = std::remove_cvref_t; + + public: + using value = + std::conditional_t, cstringcomp, std::equal_to<>>; + }; + + template + using defaultcomp_v = typename DefaultComparatorForType::value; + + template + struct FoldArgs + { + public: + static constexpr bool value = true; + + using AType = std::remove_cvref_t; + using BType = std::remove_cvref_t; + + using KCType = defaultcomp_v; + using VCType = defaultcomp_v; + }; + + template + struct FoldArgs + { + private: + using SubFoldArgs = FoldArgs; + using FoldArgs2 = + FoldArgs; + + public: + static constexpr bool value = SubFoldArgs::value && FoldArgs2::value; + + using AType = typename FoldArgs2::AType; + using BType = typename FoldArgs2::BType; + + using KCType = defaultcomp_v; + using VCType = defaultcomp_v; + }; + + template + struct FoldArgs + { + private: + using UnCV_A = std::remove_cvref_t; + using UnCV_B = std::remove_cvref_t; + + using UnCV_APrime = std::remove_cvref_t; + using UnCV_BPrime = std::remove_cvref_t; + + using Decay_A = std::decay_t; + using Decay_B = std::decay_t; + + using Decay_APrime = std::decay_t; + using Decay_BPrime = std::decay_t; + + static constexpr bool KeyConvertible = + std::is_convertible_v || std::is_convertible_v || + std::is_convertible_v || std::is_convertible_v; + static constexpr bool ValueConvertible = + std::is_convertible_v || std::is_convertible_v || + std::is_convertible_v || std::is_convertible_v; + + public: + static constexpr bool value = KeyConvertible && ValueConvertible; + + // clang-format off + using AType = std::conditional_t< + std::is_convertible_v, + UnCV_A, + std::conditional_t< + std::is_convertible_v, + UnCV_APrime, + std::conditional_t, + Decay_A, + Decay_APrime + > + > + >; + using BType = std::conditional_t< + std::is_convertible_v, + UnCV_B, + std::conditional_t< + std::is_convertible_v, + UnCV_BPrime, + std::conditional_t, + Decay_B, + Decay_BPrime + > + > + >; + // clang-format on + + using KCType = defaultcomp_v; + using VCType = defaultcomp_v; + }; + + template + struct ConvertibleArgs : public std::false_type + { + }; + + template ArgKey, std::convertible_to ArgVal> + struct ConvertibleArgs : public std::true_type + { + }; + + // clang-format off + template ArgKey, std::convertible_to ArgVal, + typename... Args> + requires (sizeof...(Args) % 2 == 0) && (sizeof...(Args) > 0) && ConvertibleArgs::value + struct ConvertibleArgs : public std::true_type + // clang-format on + { + }; +}; // namespace BidirectionalMapInternals + +// clang-format off +template KeyComparator = std::equal_to<>, std::equivalence_relation ValueComparator = std::equal_to<>> + requires (Size > 0) && + std::same_as> && + std::same_as> && + std::same_as> && + std::same_as> +class BidirectionalMap +// clang-format on +{ +public: + using key_type = Key; + using value_type = Value; + + using Entry = std::pair; + +private: + static consteval void buildVectorHelper( + std::vector>& fillMe, auto&& a, auto&& b, auto&&... args) + { + if constexpr (sizeof...(args) == 0) + { + fillMe.emplace_back(std::forward(a), std::forward(b)); + } + else + { + fillMe.emplace_back(std::forward(a), std::forward(b)); + return buildVectorHelper(fillMe, std::forward(args)...); + } + } + static consteval std::vector> buildArgVector(auto&&... args) + { + std::vector> ret; + buildVectorHelper(ret, std::forward(args)...); + return ret; + } + template + static consteval auto buildArgArray(Args&&... args) + { + auto buildArrayHelper = + [](auto&& v, std::index_sequence) + ->std::array, Size> + { + return {{std::move(v[Indices])...}}; + }; + return buildArrayHelper( + buildArgVector(std::forward(args)...), std::make_index_sequence{}); + } + + template KC, + std::convertible_to VC, std::size_t... PairSeq> + consteval BidirectionalMap(std::ranges::owning_view&& pairs, KC&& kc, VC&& vc, + std::index_sequence) + : entries{{std::forward>( + *(pairs.begin() + PairSeq))...}}, + populated{std::min(std::ranges::size(pairs), Size)}, + kc{std::forward(kc)}, + vc{std::forward(vc)} + { + } + + template KC, + std::convertible_to VC, std::size_t RangeSize> + consteval BidirectionalMap(std::ranges::owning_view&& pairs, KC&& kc, VC&& vc, + std::integral_constant) + : BidirectionalMap(std::forward(pairs), std::forward(kc), + std::forward(vc), std::make_index_sequence{}) + { + } + +public: + BidirectionalMap() = delete; + + // clang-format off + template CurrentKey = Key, + std::convertible_to CurrentVal = Value, + std::size_t ArraySize = Size, + std::convertible_to KC = KeyComparator, + std::convertible_to VC = ValueComparator> + requires (ArraySize <= Size) + consteval BidirectionalMap(std::pair (&&inEntries)[ArraySize], KC&& kc = KC {}, VC&& vc = VC {}) : + BidirectionalMap(std::ranges::owning_view(std::forward(inEntries)), + std::forward(kc), std::forward(vc), std::integral_constant{}) + {} + + template CurrentKey = Key, + std::convertible_to CurrentVal = Value, + std::size_t ArraySize = Size, + std::convertible_to KC = KeyComparator, + std::convertible_to VC = ValueComparator> + requires (ArraySize <= Size) + consteval BidirectionalMap(std::array, ArraySize>&& inEntries, KC&& kc = KC {}, VC&& vc = VC {}) : + BidirectionalMap(std::ranges::owning_view(std::forward(inEntries)), + std::forward(kc), std::forward(vc), std::integral_constant{}) + {} + + template KC, std::convertible_to VC> + requires BidirectionalMapInternals::ConvertibleArgs::value && (sizeof...(Args) / 2 <= Size) + consteval BidirectionalMap(KC&& kc, VC&& vc, Args&&... args) : + BidirectionalMap(buildArgArray(std::forward(args)...), std::forward(kc), std::forward(vc)) + {} + + template + requires BidirectionalMapInternals::ConvertibleArgs::value && (sizeof...(Args) / 2 <= Size) + consteval BidirectionalMap(Args&&... args) : + BidirectionalMap(BidirectionalMapInternals::defaultcomp_v(), BidirectionalMapInternals::defaultcomp_v(), std::forward(args)...) + {} + // clang-format on + + /* + ** When mapped as T, V -- find value V from T + */ + // clang-format off + template + requires std::equivalence_relation || + std::equivalence_relation + constexpr std::optional> Find(const K& search) const + // clang-format on + { + for (std::size_t i = 0; i < this->populated; ++i) + { + if (compareKey(this->entries[i].first, search)) + { + return std::ref(this->entries[i].second); + } + } + + return std::nullopt; + } + + /* + ** When mapped as T, V -- find value T from V + */ + // clang-format off + template + requires std::equivalence_relation || + std::equivalence_relation + constexpr std::optional> ReverseFind(const V& search) const + // clang-format on + { + for (std::size_t i = 0; i < this->populated; ++i) + { + if (compareValue(this->entries[i].second, search)) + { + return std::ref(this->entries[i].first); + } + } + + return std::nullopt; + } + + constexpr auto GetKeys() const + { + return std::ranges::ref_view(this->entries) | std::views::keys | + std::views::take(this->populated); + } + + /* Can only be used on String-mapped Keys */ + // clang-format off + constexpr auto GetNames() const + requires std::is_same_v || std::is_same_v || + std::is_same_v + // clang-format on + { + return GetKeys(); + } + + constexpr auto GetValues() const + { + return std::ranges::ref_view(this->entries) | std::views::values | + std::views::take(this->populated); + } + + constexpr std::span GetEntries() const + { + return {entries.begin(), entries.begin() + populated}; + } + +private: + std::array entries; + std::size_t populated; + + [[no_unique_address]] KeyComparator kc; + [[no_unique_address]] ValueComparator vc; + + template + requires std::equivalence_relation + constexpr auto compareValue(const Value& v1, V&& v2) const + { + return std::invoke(vc, v1, std::forward(v2)); + } + + // clang-format off + template + requires std::equivalence_relation && + (!std::equivalence_relation) + constexpr auto compareValue(const Value& v1, V&& v2) const + // clang-format on + { + return std::invoke(vc, std::forward(v2), v1); + } + + // clang-format off + template + requires std::equivalence_relation + constexpr auto compareKey(const Key& v1, K&& v2) const + // clang-format on + { + return std::invoke(kc, v1, std::forward(v2)); + } + + // clang-format off + template + constexpr auto compareKey(const Key& v1, K&& v2) const + requires std::equivalence_relation && + (!std::equivalence_relation) + // clang-format on + { + return std::invoke(kc, std::forward(v2), v1); + } +}; + +// clang-format off +template> +requires Folded::value +BidirectionalMap(KC&&, VC&&, Args&&...) -> BidirectionalMap, + std::remove_cvref_t>; + +template> +requires Folded::value +BidirectionalMap(Args&&...) -> BidirectionalMap; + +template +BidirectionalMap(std::pair (&&)[ArraySize], KC&&, VC&&) -> BidirectionalMap, + std::remove_cvref_t, + ArraySize, + std::remove_cvref_t, + std::remove_cvref_t>; + +template +BidirectionalMap(std::pair (&&)[ArraySize]) -> BidirectionalMap, + std::remove_cvref_t, + ArraySize, + BidirectionalMapInternals::defaultcomp_v>, + BidirectionalMapInternals::defaultcomp_v>>; + +template +BidirectionalMap(std::array, ArraySize>&&, KC&&, VC&&) -> BidirectionalMap, + std::remove_cvref_t, + ArraySize, + std::remove_cvref_t, + std::remove_cvref_t>; + +template +BidirectionalMap(std::array, ArraySize>&&) -> BidirectionalMap, + std::remove_cvref_t, + ArraySize, + BidirectionalMapInternals::defaultcomp_v>, + BidirectionalMapInternals::defaultcomp_v>>; +// clang-format on diff --git a/include/utilities/bidirectionalmap/bidirectionalmultimap.hpp b/include/utilities/bidirectionalmap/bidirectionalmultimap.hpp new file mode 100644 index 000000000..f734ec034 --- /dev/null +++ b/include/utilities/bidirectionalmap/bidirectionalmultimap.hpp @@ -0,0 +1,500 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace BidirectionalMultiMapInternals +{ + // Used to compare C strings; operator== doesn't work properly for those + struct cstringcomp + { + constexpr bool operator()(const char* a, const char* b) const + { + if (a == nullptr && b == nullptr) + { + return true; + } + else if (a == nullptr || b == nullptr) + { + return false; + } + return std::string_view(a) == std::string_view(b); + } + + constexpr bool operator()(const char* a, std::string_view b) const { return a == b; } + + constexpr bool operator()(std::string_view b, const char* a) const { return a == b; } + }; + + template + struct DefaultComparatorForType + { + private: + using testtype = std::remove_cvref_t; + + public: + using value = + std::conditional_t, cstringcomp, std::equal_to<>>; + }; + + template + using defaultcomp_v = typename DefaultComparatorForType::value; + + template + struct FoldArgs + { + public: + static constexpr bool value = true; + + using AType = std::remove_cvref_t; + using BType = std::remove_cvref_t; + + using KCType = defaultcomp_v; + using VCType = defaultcomp_v; + }; + + template + struct FoldArgs + { + private: + using SubFoldArgs = FoldArgs; + using FoldArgs2 = + FoldArgs; + + public: + static constexpr bool value = SubFoldArgs::value && FoldArgs2::value; + + using AType = typename FoldArgs2::AType; + using BType = typename FoldArgs2::BType; + + using KCType = defaultcomp_v; + using VCType = defaultcomp_v; + }; + + template + struct FoldArgs + { + private: + using UnCV_A = std::remove_cvref_t; + using UnCV_B = std::remove_cvref_t; + + using UnCV_APrime = std::remove_cvref_t; + using UnCV_BPrime = std::remove_cvref_t; + + using Decay_A = std::decay_t; + using Decay_B = std::decay_t; + + using Decay_APrime = std::decay_t; + using Decay_BPrime = std::decay_t; + + static constexpr bool KeyConvertible = + std::is_convertible_v || std::is_convertible_v || + std::is_convertible_v || std::is_convertible_v; + static constexpr bool ValueConvertible = + std::is_convertible_v || std::is_convertible_v || + std::is_convertible_v || std::is_convertible_v; + + public: + static constexpr bool value = KeyConvertible && ValueConvertible; + + // clang-format off + using AType = std::conditional_t< + std::is_convertible_v, + UnCV_A, + std::conditional_t< + std::is_convertible_v, + UnCV_APrime, + std::conditional_t, + Decay_A, + Decay_APrime + > + > + >; + using BType = std::conditional_t< + std::is_convertible_v, + UnCV_B, + std::conditional_t< + std::is_convertible_v, + UnCV_BPrime, + std::conditional_t, + Decay_B, + Decay_BPrime + > + > + >; + // clang-format on + + using KCType = defaultcomp_v; + using VCType = defaultcomp_v; + }; + + template + struct ConvertibleArgs : public std::false_type + { + }; + + template ArgKey, std::convertible_to ArgVal> + struct ConvertibleArgs : public std::true_type + { + }; + + // clang-format off + template ArgKey, + std::convertible_to ArgVal, typename... Args> + requires (sizeof...(Args) % 2 == 0) && (sizeof...(Args) > 0) && ConvertibleArgs::value + struct ConvertibleArgs : public std::true_type + // clang-format on + { + }; +}; // namespace BidirectionalMultiMapInternals + +// clang-format off +template KeyComparator = std::equal_to<>, std::equivalence_relation ValueComparator = std::equal_to<>> + requires (Size > 0) && + std::same_as> && + std::same_as> && + std::same_as> && + std::same_as> +class BidirectionalMultiMap +// clang-format on +{ +public: + using key_type = Key; + using value_type = Value; + + using Entry = std::pair; + +private: + static consteval void buildVectorHelper( + std::vector>& fillMe, auto&& a, auto&& b, auto&&... args) + { + if constexpr (sizeof...(args) == 0) + { + fillMe.emplace_back(std::forward(a), std::forward(b)); + } + else + { + fillMe.emplace_back(std::forward(a), std::forward(b)); + return buildVectorHelper(fillMe, std::forward(args)...); + } + } + static consteval std::vector> buildArgVector(auto&&... args) + { + std::vector> ret; + buildVectorHelper(ret, std::forward(args)...); + return ret; + } + template + static consteval auto buildArgArray(Args&&... args) + { + auto buildArrayHelper = + [](auto&& v, std::index_sequence) + ->std::array, Size> + { + return {{std::move(v[Indices])...}}; + }; + return buildArrayHelper( + buildArgVector(std::forward(args)...), std::make_index_sequence{}); + } + + template + static consteval auto buildRefArray(const std::array& sortMe, + std::size_t populated, auto&& comparator, auto&& accessor) + { + std::size_t newIndex = 0; + + std::array sorted{}; + std::array alreadyAdded{}; + + for (std::size_t i = 0; i < populated; i++) + { + if (!alreadyAdded[i]) + { + sorted[newIndex++] = i; + alreadyAdded[i] = true; + for (std::size_t j = i + 1; j < populated; j++) + { + if (!alreadyAdded[j] && + std::invoke(comparator, std::invoke(accessor, sortMe[j]), + std::invoke(accessor, sortMe[i]))) + { + alreadyAdded[j] = true; + sorted[newIndex++] = j; + } + } + } + } + + for (std::size_t i = populated; i < ArraySize; i++) + { + sorted[i] = i; + } + + return sorted; + } + + template KC, + std::convertible_to VC, std::size_t... PairSeq> + consteval BidirectionalMultiMap(std::ranges::owning_view&& pairs, KC&& kc, VC&& vc, + std::index_sequence) + : entries{{std::forward>( + *(pairs.begin() + PairSeq))...}}, + populated{std::min(std::ranges::size(pairs), Size)}, + kc{std::forward(kc)}, + vc{std::forward(vc)}, + forwardEntries{buildRefArray(entries, populated, this->kc, &Entry::first)}, + backwardEntries{buildRefArray(entries, populated, this->vc, &Entry::second)} + { + } + + template KC, + std::convertible_to VC, std::size_t RangeSize> + consteval BidirectionalMultiMap(std::ranges::owning_view&& pairs, KC&& kc, VC&& vc, + std::integral_constant) + : BidirectionalMultiMap(std::forward(pairs), std::forward(kc), + std::forward(vc), std::make_index_sequence{}) + { + } + +public: + BidirectionalMultiMap() = delete; + + // clang-format off + template CurrentKey = Key, + std::convertible_to CurrentVal = Value, + std::size_t ArraySize = Size, + std::convertible_to KC = KeyComparator, + std::convertible_to VC = ValueComparator> + requires (ArraySize <= Size) + consteval BidirectionalMultiMap(std::pair (&&inEntries)[ArraySize], KC&& kc = KC {}, VC&& vc = VC {}) : + BidirectionalMultiMap(std::ranges::owning_view(std::forward(inEntries)), + std::forward(kc), std::forward(vc), std::integral_constant{}) + {} + + template CurrentKey = Key, + std::convertible_to CurrentVal = Value, + std::size_t ArraySize = Size, + std::convertible_to KC = KeyComparator, + std::convertible_to VC = ValueComparator> + requires (ArraySize <= Size) + consteval BidirectionalMultiMap(std::array, ArraySize>&& inEntries, KC&& kc = KC {}, VC&& vc = VC {}) : + BidirectionalMultiMap(std::ranges::owning_view(std::forward(inEntries)), + std::forward(kc), std::forward(vc), std::integral_constant{}) + {} + + template KC, std::convertible_to VC> + requires BidirectionalMultiMapInternals::ConvertibleArgs::value && (sizeof...(Args) / 2 <= Size) + consteval BidirectionalMultiMap(KC&& kc, VC&& vc, Args&&... args) : + BidirectionalMultiMap(buildArgArray(std::forward(args)...), std::forward(kc), std::forward(vc)) + {} + + template + requires BidirectionalMultiMapInternals::ConvertibleArgs::value && (sizeof...(Args) / 2 <= Size) + consteval BidirectionalMultiMap(Args&&... args) : + BidirectionalMultiMap(BidirectionalMultiMapInternals::defaultcomp_v(), BidirectionalMultiMapInternals::defaultcomp_v(), std::forward(args)...) + {} + // clang-format on + + /* + ** When mapped as T, V -- find values V from T + */ + template + requires std::equivalence_relation || + std::equivalence_relation constexpr auto Find(const K& search) const + { + auto range = frange(); + auto first = std::find_if(std::ranges::begin(range), std::ranges::end(range), + [&search, this](const auto& e) { return compareKey(e.first, search); }); + auto last = + std::find_if(std::ranges::rbegin(range), std::ranges::rend(range), [&](const auto& e) { + return compareKey(e.first, search); + }).base(); + + return frange() | std::views::values | std::views::drop(first - std::ranges::begin(range)) | + std::views::take(last - first); + } + + template + requires std::equivalence_relation || + std::equivalence_relation constexpr std::optional> + FindFirst(const K& search) const + { + auto found = Find(search); + if (std::ranges::begin(found) != std::ranges::end(found)) + return {std::ref(*std::ranges::begin(found))}; + + return std::nullopt; + } + + /* + ** When mapped as T, V -- find first VectorSize values T from V + */ + template + requires std::equivalence_relation || + std::equivalence_relation constexpr auto ReverseFind( + const V& search) const + { + auto range = brange(); + auto first = std::find_if(std::ranges::begin(range), std::ranges::end(range), + [&search, this](const auto& e) { return compareValue(e.second, search); }); + auto last = + std::find_if(std::ranges::rbegin(range), std::ranges::rend(range), [&](const auto& e) { + return compareValue(e.second, search); + }).base(); + + return brange() | std::views::keys | std::views::drop(first - std::ranges::begin(range)) | + std::views::take(last - first); + } + + template + requires std::equivalence_relation || + std::equivalence_relation constexpr std::optional> + ReverseFindFirst(const V& search) const + { + auto found = ReverseFind(search); + if (std::ranges::begin(found) != std::ranges::end(found)) + return {std::ref(*std::ranges::begin(found))}; + + return std::nullopt; + } + + constexpr auto GetKeys() const + { + return std::ranges::ref_view(this->entries) | std::views::keys | + std::views::take(this->populated); + } + + /* Can only be used on String-mapped Keys */ + // clang-format off + constexpr auto GetNames() const + requires std::is_same_v || std::is_same_v || + std::is_same_v + // clang-format on + { + return GetKeys(); + } + + constexpr auto GetValues() const + { + return std::ranges::ref_view(this->entries) | std::views::values | + std::views::take(this->populated); + } + + constexpr std::span GetEntries() const + { + return {entries.begin(), entries.begin() + populated}; + } + +private: + std::array entries; + std::size_t populated; + + [[no_unique_address]] KeyComparator kc; + [[no_unique_address]] ValueComparator vc; + + std::array forwardEntries; + std::array backwardEntries; + + constexpr auto frange() const + { + return forwardEntries | + std::views::transform([this](std::size_t i) -> const Entry& { return entries[i]; }) | + std::views::take(populated); + } + + constexpr auto brange() const + { + return backwardEntries | + std::views::transform([this](std::size_t i) -> const Entry& { return entries[i]; }) | + std::views::take(populated); + } + + template + requires std::equivalence_relation constexpr auto compareValue( + const Value& v1, V&& v2) const + { + return std::invoke(vc, v1, std::forward(v2)); + } + + // clang-format off + template + requires std::equivalence_relation && + (!std::equivalence_relation) + constexpr auto compareValue(const Value& v1, V&& v2) const + // clang-format on + { + return std::invoke(vc, std::forward(v2), v1); + } + + template + requires std::equivalence_relation constexpr auto compareKey( + const Key& v1, K&& v2) const + { + return std::invoke(kc, v1, std::forward(v2)); + } + + // clang-format off + template + constexpr auto compareKey(const Key& v1, K&& v2) const + requires std::equivalence_relation && + (!std::equivalence_relation) + // clang-format on + { + return std::invoke(kc, std::forward(v2), v1); + } +}; + +// clang-format off +template> +requires Folded::value +BidirectionalMultiMap(KC&&, VC&&, Args&&...) -> BidirectionalMultiMap, + std::remove_cvref_t>; + +template> +requires Folded::value +BidirectionalMultiMap(Args&&...) -> BidirectionalMultiMap; + +template +BidirectionalMultiMap(std::pair (&&)[ArraySize], KC&&, VC&&) -> BidirectionalMultiMap, + std::remove_cvref_t, + ArraySize, + std::remove_cvref_t, + std::remove_cvref_t>; + +template +BidirectionalMultiMap(std::pair (&&)[ArraySize]) -> BidirectionalMultiMap, + std::remove_cvref_t, + ArraySize, + BidirectionalMultiMapInternals::defaultcomp_v>, + BidirectionalMultiMapInternals::defaultcomp_v>>; + +template +BidirectionalMultiMap(std::array, ArraySize>&&, KC&&, VC&&) -> BidirectionalMultiMap, + std::remove_cvref_t, + ArraySize, + std::remove_cvref_t, + std::remove_cvref_t>; + +template +BidirectionalMultiMap(std::array, ArraySize>&&) -> BidirectionalMultiMap, + std::remove_cvref_t, + ArraySize, + BidirectionalMultiMapInternals::defaultcomp_v>, + BidirectionalMultiMapInternals::defaultcomp_v>>; +// clang-format on diff --git a/include/utilities/bidirectionalmap/smallvector.hpp b/include/utilities/bidirectionalmap/smallvector.hpp new file mode 100644 index 000000000..779ebc440 --- /dev/null +++ b/include/utilities/bidirectionalmap/smallvector.hpp @@ -0,0 +1,407 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace SmallVectorInternals +{ + template + union STVData + { + T a; + unsigned char b; + constexpr STVData() : b() + {} + constexpr ~STVData() + {} + }; + + template + struct STVIterator + { + private: + using data_type = + std::conditional_t, const STVData>, STVData>; + data_type* data; + + public: + constexpr STVIterator() = default; + + constexpr explicit STVIterator(data_type* d) : data(d) + {} + + using difference_type = decltype(data - data); + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = value_type&; + using const_reference = const value_type&; + using iterator_category = std::random_access_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr reference operator*() noexcept + { + return data->a; + } + constexpr const_reference operator*() const noexcept + { + return data->a; + } + constexpr pointer operator->() noexcept + { + return &data->a; + } + constexpr const_pointer operator->() const noexcept + { + return &data->a; + } + constexpr reference operator[](difference_type i) noexcept + { + return (data + i)->a; + } + constexpr const_reference operator[](difference_type i) const noexcept + { + return (data + i)->a; + } + + constexpr bool operator==(const STVIterator&) const noexcept = default; + constexpr auto operator<=>(const STVIterator&) const noexcept = default; + + constexpr STVIterator& operator++() noexcept + { + data++; + return *this; + } + constexpr STVIterator operator++(int) noexcept + { + STVIterator ret; + data++; + return ret; + } + constexpr STVIterator& operator--() noexcept + { + data--; + return *this; + } + constexpr STVIterator operator--(int) noexcept + { + STVIterator ret; + data--; + return ret; + } + + constexpr difference_type operator-(const STVIterator& o) const noexcept + { + return data - o.data; + } + + constexpr STVIterator& operator+=(difference_type d) noexcept + { + data += d; + return *this; + } + + constexpr STVIterator& operator-=(difference_type d) noexcept + { + data -= d; + return *this; + } + }; +} // namespace SmallVectorInternals + +template +constexpr SmallVectorInternals::STVIterator operator+( + const SmallVectorInternals::STVIterator& it, + typename SmallVectorInternals::STVIterator::difference_type i) +{ + auto ret = it; + ret += i; + return ret; +} + +template +constexpr SmallVectorInternals::STVIterator operator+( + typename SmallVectorInternals::STVIterator::difference_type i, + const typename SmallVectorInternals::STVIterator& it) +{ + auto ret = it; + ret += i; + return ret; +} + +template +constexpr SmallVectorInternals::STVIterator operator-( + const SmallVectorInternals::STVIterator& it, + typename SmallVectorInternals::STVIterator::difference_type i) +{ + auto ret = it; + ret -= i; + return ret; +} + +template +requires std::is_destructible_v +class SmallVector +{ + private: + static_assert(sizeof(SmallVectorInternals::STVData) == sizeof(T), + "A union of a char and a T was larger than T. Why is your compiler bad?"); + + std::array, Size> alldata; + std::size_t populated { 0 }; + + constexpr SmallVector& self() + { + return *this; + } + constexpr const SmallVector& self() const + { + return *this; + } + + public: + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = SmallVectorInternals::STVIterator; + using const_iterator = SmallVectorInternals::STVIterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + constexpr SmallVector() = default; + + constexpr ~SmallVector() noexcept(std::is_nothrow_destructible_v) + { + for (std::size_t i = 0; i < size(); i++) + { + std::destroy_at(&self()[i]); + } + } + + constexpr SmallVector(const SmallVector& o) noexcept(noexcept(push_back(o[0]))) + requires std::is_copy_constructible_v + { + for (std::size_t i = 0; i < o.size(); i++) + { + push_back(o[i]); + } + } + + constexpr SmallVector(SmallVector&& o) noexcept(noexcept(emplace_back(std::move(o[0]))) && + std::is_nothrow_destructible_v) + requires std::is_move_constructible_v + { + for (std::size_t i = 0; i < o.size(); i++) + { + emplace_back(std::move(o[i])); + std::destroy_at(&o[i]); + } + o.populated = 0; + } + + constexpr SmallVector& operator=(const SmallVector& o) noexcept( + std::is_nothrow_destructible_v&& std::is_nothrow_copy_constructible_v&& + std::is_nothrow_copy_assignable_v) + requires std::is_copy_constructible_v && std::is_copy_assignable_v + { + std::size_t minsize = std::min(size(), o.size()); + std::size_t targetsize = o.size(); + std::size_t currentsize = size(); + + for (std::size_t i = 0; i < minsize; i++) + { + self()[i] = o[i]; + } + for (std::size_t i = minsize; i < targetsize; i++) + { + std::construct_at(&self()[i], o[i]); + } + for (std::size_t i = targetsize; i < currentsize; i++) + { + std::destroy_at(&self()[i]); + } + + populated = o.populated; + + return *this; + } + + constexpr SmallVector& operator=(SmallVector&& o) noexcept( + std::is_nothrow_destructible_v&& std::is_nothrow_move_constructible_v&& + std::is_nothrow_move_assignable_v) + requires std::is_move_constructible_v || std::is_move_assignable_v + { + std::size_t minsize = std::min(size(), o.size()); + std::size_t targetsize = o.size(); + std::size_t currentsize = size(); + + for (std::size_t i = 0; i < minsize; i++) + { + self()[i] = std::move(o[i]); + std::destroy_at(&o[i]); + } + for (std::size_t i = minsize; i < targetsize; i++) + { + std::construct_at(&self()[i], std::move(o[i])); + std::destroy_at(&o[i]); + } + for (std::size_t i = targetsize; i < currentsize; i++) + { + std::destroy_at(self()[i]); + } + + populated = o.populated; + o.populated = 0; + + return *this; + } + + template + requires(std::convertible_to && ...) && (sizeof...(Args) <= Size) + constexpr SmallVector(Args&&... args) noexcept( + (noexcept(emplace_back(std::forward(args))) && ...)) + { + (emplace_back(std::forward(args)) && ...); + } + + template + requires std::convertible_to && (SpanSize <= Size) && + (SpanSize != std::dynamic_extent) + constexpr SmallVector(const std::span& in) noexcept( + noexcept(emplace_back(in[0]))) + { + for (std::size_t i = 0; i < in.size(); i++) + { + emplace_back(in[i]); + } + } + + // Returns true if the value fit, false if it didn't + constexpr bool push_back(const T& value) noexcept(std::is_nothrow_copy_constructible_v) + requires std::is_copy_constructible_v + { + if (size() < capacity()) + { + std::construct_at(&self()[populated++], std::forward(value)); + return true; + } + + return false; + } + + template + constexpr bool emplace_back(Args&&... args) noexcept( + std::is_nothrow_constructible_v) + requires std::is_constructible_v + { + if (size() < capacity()) + { + std::construct_at(&self()[populated++], std::forward(args)...); + return true; + } + + return false; + } + + constexpr void pop_back() noexcept(std::is_nothrow_destructible_v) + { + if (size() > 0) + { + std::destroy_at(&self()[--populated]); + } + } + + constexpr const_reference operator[](size_type i) const noexcept + { + return alldata[i].a; + } + + constexpr reference operator[](size_type i) noexcept + { + return alldata[i].a; + } + + constexpr size_type size() const noexcept + { + return populated; + } + + constexpr size_type capacity() const noexcept + { + return Size; + } + + constexpr iterator begin() noexcept + { + return iterator { alldata.data() }; + } + + constexpr iterator end() noexcept + { + return iterator { alldata.data() } + size(); + } + + constexpr reverse_iterator rbegin() noexcept + { + return std::make_reverse_iterator(end()); + } + + constexpr reverse_iterator rend() noexcept + { + return std::make_reverse_iterator(begin()); + } + + constexpr const_iterator begin() const noexcept + { + return const_iterator { alldata.data() }; + } + + constexpr const_iterator end() const noexcept + { + return const_iterator { alldata.data() } + size(); + } + + constexpr const_reverse_iterator rbegin() const noexcept + { + return std::make_reverse_iterator(end()); + } + + constexpr const_reverse_iterator rend() const noexcept + { + return std::make_reverse_iterator(begin()); + } + + constexpr const_iterator cbegin() const noexcept + { + return const_iterator { alldata.data() }; + } + + constexpr const_iterator cend() const noexcept + { + return const_iterator { alldata.data() } + size(); + } + + constexpr const_reverse_iterator crbegin() const noexcept + { + return std::make_reverse_iterator(cend()); + } + + constexpr const_reverse_iterator crend() const noexcept + { + return std::make_reverse_iterator(cbegin()); + } +}; + +template +SmallVector(Args...) -> SmallVector, sizeof...(Args)>; +template +SmallVector(std::span) -> SmallVector; diff --git a/include/utilities/bytes.hpp b/include/utilities/bytes.hpp new file mode 100644 index 000000000..8076948f9 --- /dev/null +++ b/include/utilities/bytes.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +namespace love +{ + static constexpr const char hexChars[] = "0123456789abcdef"; + + uint8_t Nibble(char c); + + char* BytesToHex(const uint8_t* source, size_t sourceLength, + size_t& destinationLength); + + uint8_t* HexToBytes(const char* source, size_t sourceLength, + size_t& destinationLength); +} // namespace love diff --git a/include/utilities/compressor/compressor.hpp b/include/utilities/compressor/compressor.hpp new file mode 100644 index 000000000..a9ba66bef --- /dev/null +++ b/include/utilities/compressor/compressor.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include + +#include +#include + +#include + +#include + +namespace love +{ + class Compressor + { + public: + enum Format + { + FORMAT_LZ4, + FORMAT_ZLIB, + FORMAT_GZIP, + FORMAT_DEFLATE, + FORMAT_MAX_ENUM + }; + + static Compressor* GetCompressor(Format format); + + virtual ~Compressor() + {} + + virtual char* Compress(Format format, const char* data, size_t size, int level, + size_t& compressedSize) = 0; + + virtual char* Decompress(Format format, const char* data, size_t size, + size_t& decompressedSize) = 0; + + virtual bool IsSupported(Format format) const = 0; + + // clang-format off + static constexpr BidirectionalMap formats = { + "lz4", Compressor::Format::FORMAT_LZ4, + "zlib", Compressor::Format::FORMAT_ZLIB, + "gzip", Compressor::Format::FORMAT_GZIP, + "deflate", Compressor::Format::FORMAT_DEFLATE + }; + // clang-format on + + protected: + Compressor() + {} + }; +} // namespace love diff --git a/include/utilities/compressor/types/lz4compressor.hpp b/include/utilities/compressor/types/lz4compressor.hpp new file mode 100644 index 000000000..a56498c64 --- /dev/null +++ b/include/utilities/compressor/types/lz4compressor.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace love +{ + class LZ4Compressor : public Compressor + { + public: + char* Compress(Compressor::Format format, const char* data, size_t size, + int level, size_t& compressedSize) override; + + char* Decompress(Compressor::Format format, const char* data, size_t size, + size_t& decompressedSize) override; + + bool IsSupported(Compressor::Format format) const + { + return format == Compressor::FORMAT_LZ4; + } + }; +} // namespace love diff --git a/include/utilities/compressor/types/zlibcompressor.hpp b/include/utilities/compressor/types/zlibcompressor.hpp new file mode 100644 index 000000000..b10e56330 --- /dev/null +++ b/include/utilities/compressor/types/zlibcompressor.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace love +{ + class ZlibCompressor : public Compressor + { + public: + char* Compress(Compressor::Format format, const char* data, size_t size, + int level, size_t& compressedSize) override; + + char* Decompress(Compressor::Format format, const char* data, size_t size, + size_t& decompressedSize) override; + + bool IsSupported(Compressor::Format format) const + { + return format == Compressor::FORMAT_ZLIB || + format == Compressor::FORMAT_GZIP || + format == Compressor::FORMAT_DEFLATE; + } + + private: + uLong zlibCompressBound(Compressor::Format format, uLong sourceLength); + + int zlibCompress(Compressor::Format format, Bytef* destination, + uLongf* destinationLength, const Bytef* source, + uLong sourceLength, int level); + + int zlibDecompress(Compressor::Format format, Bytef* destination, + uLongf* destinationLength, const Bytef* source, + uLong sourceLength); + }; +} // namespace love diff --git a/include/utilities/decoder/decoder.hpp b/include/utilities/decoder/decoder.hpp new file mode 100644 index 000000000..70e29dac6 --- /dev/null +++ b/include/utilities/decoder/decoder.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include +#include + +#include +#include + +#include +#include + +namespace love +{ + class Decoder : public Object + { + public: + enum StreamSource + { + STREAM_FILE, + STREAM_MEMORY, + STREAM_MAX_ENUM + }; + + static constexpr int DEFAULT_BUFFER_SIZE = 0x4000; + static constexpr int DEFAULT_SAMPLE_RATE = 0xAC44; + + static constexpr int DEFAULT_CHANNELS = 0x02; + static constexpr int DEFAULT_BIT_DEPTH = 0x10; + + static Type type; + + Decoder(Stream* stream, int bufferSize); + + virtual Decoder* Clone() = 0; + + virtual int Decode() = 0; + + virtual void* GetBuffer() const; + + virtual int GetBitDepth() const = 0; + + virtual int GetChannelCount() const = 0; + + virtual double GetDuration() = 0; + + virtual int GetSampleRate() const; + + virtual int GetSize() const; + + virtual bool IsFinished(); + + virtual bool IsSeekable() = 0; + + virtual bool Rewind() = 0; + + virtual bool Seek(double position) = 0; + + // clang-format off + static constexpr BidirectionalMap streamSources = { + "memory", Decoder::STREAM_MEMORY, + "file", Decoder::STREAM_FILE + }; + // clang-format on + + protected: + StrongReference stream; + + int bufferSize; + int sampleRate; + + std::unique_ptr buffer; + bool eof; + }; +} // namespace love diff --git a/include/utilities/decoder/types/flacdecoder.hpp b/include/utilities/decoder/types/flacdecoder.hpp new file mode 100644 index 000000000..086a66483 --- /dev/null +++ b/include/utilities/decoder/types/flacdecoder.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include + +namespace love +{ + class FLACDecoder : public Decoder + { + public: + FLACDecoder(Stream* stream, int bufferSize); + + ~FLACDecoder(); + + Decoder* Clone() override; + + int Decode() override; + + bool Seek(double position) override; + + bool Rewind() override; + + bool IsSeekable() override; + + int GetChannelCount() const override; + + int GetBitDepth() const override; + + int GetSampleRate() const override; + + double GetDuration() override; + + private: + drflac* handle; + }; +} // namespace love diff --git a/include/utilities/decoder/types/modplugdecoder.hpp b/include/utilities/decoder/types/modplugdecoder.hpp new file mode 100644 index 000000000..bda1d1138 --- /dev/null +++ b/include/utilities/decoder/types/modplugdecoder.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include + +namespace love +{ + class ModPlugDecoder : public Decoder + { + public: + ModPlugDecoder(Stream* stream, int bufferSize); + + ~ModPlugDecoder(); + + Decoder* Clone() override; + + int Decode() override; + + bool Seek(double position) override; + + bool Rewind() override; + + bool IsSeekable() override; + + int GetChannelCount() const override; + + int GetBitDepth() const override; + + double GetDuration() override; + + private: + StrongReference data; + + ModPlugFile* plug; + ModPlug_Settings settings; + + double duration; + }; +} // namespace love diff --git a/include/utilities/decoder/types/mp3decoder.hpp b/include/utilities/decoder/types/mp3decoder.hpp new file mode 100644 index 000000000..6ea730bc5 --- /dev/null +++ b/include/utilities/decoder/types/mp3decoder.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include + +namespace love +{ + class MP3Decoder : public Decoder + { + public: + MP3Decoder(Stream* data, int bufferSize); + + ~MP3Decoder(); + + Decoder* Clone() override; + + int Decode() override; + + bool Seek(double position) override; + + bool Rewind() override; + + bool IsSeekable() override; + + int GetChannelCount() const override; + + int GetBitDepth() const override; + + double GetDuration() override; + + private: + static size_t OnRead(void* source, void* data, size_t bytesToRead); + static drmp3_bool32 OnSeek(void* source, int offset, drmp3_seek_origin origin); + + static constexpr int EXPECTED_BITRATE = 0x0F; + static constexpr int EXPECTED_SAMPLERATE = 0x03; + + drmp3 handle; + std::vector seekTable; + + int64_t offset; + double duration; + }; +} // namespace love diff --git a/include/utilities/decoder/types/vorbisdecoder.hpp b/include/utilities/decoder/types/vorbisdecoder.hpp new file mode 100644 index 000000000..3c1b2fdd5 --- /dev/null +++ b/include/utilities/decoder/types/vorbisdecoder.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include + +#define OV_EXCLUDE_STATIC_CALLBACKS +#include +#include + +namespace love +{ + class VorbisDecoder : public Decoder + { + public: + VorbisDecoder(Stream* stream, int bufferSize); + + virtual ~VorbisDecoder(); + + Decoder* Clone() override; + + int Decode() override; + + bool Seek(double position) override; + + bool Rewind() override; + + bool IsSeekable() override; + + int GetChannelCount() const override; + + int GetBitDepth() const override; + + int GetSampleRate() const override; + + double GetDuration() override; + + private: + OggVorbis_File handle; + vorbis_info* info; + + double duration; + }; +} // namespace love diff --git a/include/utilities/decoder/types/wavedecoder.hpp b/include/utilities/decoder/types/wavedecoder.hpp new file mode 100644 index 000000000..9d2b48a47 --- /dev/null +++ b/include/utilities/decoder/types/wavedecoder.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include + +namespace love +{ + class WaveDecoder : public Decoder + { + public: + WaveDecoder(Stream* stream, int bufferSize); + + ~WaveDecoder(); + + Decoder* Clone() override; + + int Decode() override; + + bool Seek(double position) override; + + bool Rewind() override; + + bool IsSeekable() override; + + int GetChannelCount() const override; + + int GetBitDepth() const override; + + int GetSampleRate() const override; + + double GetDuration() override; + + private: + wuff_handle* handle; + wuff_info info; + }; +} // namespace love diff --git a/include/utilities/driver/dsp.tcc b/include/utilities/driver/dsp.tcc new file mode 100644 index 000000000..8e3dd0b93 --- /dev/null +++ b/include/utilities/driver/dsp.tcc @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +#include +#include + +namespace love +{ + template + class DSP + { + public: + enum AudioFormat + { + FORMAT_MONO, + FORMAT_STEREO, + FORMAT_MAX_ENUM + }; + + bool IsInitialized() const + { + return this->initialized; + } + + void Sleep() + {} + + protected: + bool initialized; + }; +} // namespace love diff --git a/include/utilities/driver/events.hpp b/include/utilities/driver/events.hpp new file mode 100644 index 000000000..4c247e0ea --- /dev/null +++ b/include/utilities/driver/events.hpp @@ -0,0 +1,117 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace love +{ + struct GamepadButton + { + size_t id; //< Gamepad ID + + const char* name; //< Button name + int button; //< Button number + }; + + struct GamepadAxis + { + size_t id; //< Gamepad ID + + size_t axis; //< Axis number + const char* name; //< Axis name + float value; //< Axis value + }; + + struct GamepadStatus + { + size_t id; //< Gamepad ID + }; + + struct GamepadSensor + { + size_t id; + + Sensor::SensorType type; + std::vector data; + }; + + struct Finger + { + int64_t id; //< Touch ID + + double x; //< Touch x-coordinate + double y; //< Touch y-coordinate + + double dx; //< Touch movement (x-axis) + double dy; //< Touch movement (y-axis) + + double pressure; //< Touch pressue (always 0 or 1) + }; + + struct Resize + { + int width; //< Screen width + int height; //< Screen height + }; + + struct KeyboardInput + { + std::string text; + }; + + enum EventType + { + TYPE_WINDOW, + TYPE_TOUCH, + TYPE_GAMEPAD, + TYPE_GENERAL, + TYPE_KEYBOARD + }; + + enum SubEventType + { + SUBTYPE_GAMEPADAXIS, + SUBTYPE_GAMEPADDOWN, + SUBTYPE_GAMEPADUP, + + SUBTYPE_GAMEPADADDED, + SUBTYPE_GAMEPADREMOVED, + + SUBTYPE_GAMEPADUPDATED, + SUBTYPE_GAMEPADSENSORUPDATED, + + SUBTYPE_TOUCHPRESS, + SUBTYPE_TOUCHRELEASE, + SUBTYPE_TOUCHMOVED, + + SUBTYPE_TEXTINPUT, + + SUBTYPE_LOWMEMORY, + + SUBTYPE_FOCUS_GAINED, + SUBTYPE_FOCUS_LOST, + + SUBTYPE_RESIZE, + + SUBTYPE_QUIT + }; + + struct LOVE_Event + { + EventType type; + SubEventType subType; + + GamepadStatus padStatus; + GamepadButton padButton; + GamepadAxis padAxis; + GamepadSensor padSensor; + + Finger touchFinger; + Resize size; + KeyboardInput keyboard; + }; +} // namespace love diff --git a/include/utilities/driver/hid.tcc b/include/utilities/driver/hid.tcc new file mode 100644 index 000000000..c9a4d7b49 --- /dev/null +++ b/include/utilities/driver/hid.tcc @@ -0,0 +1,189 @@ +#pragma once + +#include + +#include + +#include +#include + +namespace love +{ + template + class HID + { + public: + static HID& Instance() + { + static HID instance; + return instance; + } + + HID() : hysteresis(false), touchHeld(false), focused(false), events() + {} + + bool Poll(LOVE_Event* event) + { + if (!this->events.empty()) + { + *event = this->events.front(); + this->events.pop_front(); + + return true; + } + + if (this->hysteresis) + return this->hysteresis = false; + + this->_Poll(); + + /* return our events */ + + if (this->events.empty()) + return false; + + *event = this->events.front(); + this->events.pop_front(); + + return this->hysteresis = true; + } + + void SendFocus(bool focus) + { + if (this->focused == focus) + return; + + auto& event = this->events.emplace_back(); + + event.type = TYPE_WINDOW; + event.subType = (focus) ? SUBTYPE_FOCUS_GAINED : SUBTYPE_FOCUS_LOST; + this->focused = focus; + } + + void SendQuit() + { + auto& event = this->events.emplace_back(); + + event.type = TYPE_GENERAL; + event.subType = SUBTYPE_QUIT; + } + + void SendTextInput(std::string_view string) + { + auto& event = this->events.emplace_back(); + + event.type = TYPE_KEYBOARD; + event.subType = SUBTYPE_TEXTINPUT; + + event.keyboard.text = string; + } + + protected: + void SendLowMemory() + { + auto& event = this->events.emplace_back(); + + event.type = TYPE_GENERAL; + event.subType = SUBTYPE_LOWMEMORY; + } + + void SendResize(int width, int height) + { + auto& event = this->events.emplace_back(); + + event.type = TYPE_WINDOW; + event.subType = SUBTYPE_RESIZE; + + event.size.width = width; + event.size.height = height; + } + + void SendJoystickStatus(size_t id, bool connected) + { + auto& event = this->events.emplace_back(); + + event.type = TYPE_GAMEPAD; + event.subType = (connected) ? SUBTYPE_GAMEPADADDED : SUBTYPE_GAMEPADREMOVED; + + event.padStatus.id = id; + } + + void SendJoystickSensorUpdated(size_t id, Sensor::SensorType type, std::vector data) + { + auto& event = this->events.emplace_back(); + + event.type = TYPE_GAMEPAD; + event.subType = SUBTYPE_GAMEPADSENSORUPDATED; + + event.padSensor.id = id; + event.padSensor.type = type; + event.padSensor.data = data; + } + + void SendJoystickUpdated(size_t id) + { + auto& event = this->events.emplace_back(); + + event.type = TYPE_GAMEPAD; + event.subType = SUBTYPE_GAMEPADUPDATED; + + event.padStatus.id = id; + } + + void SendTouchEvent(SubEventType type, size_t id, float x, float y, float dx, float dy, + float pressure) + { + auto& newEvent = this->events.emplace_back(); + + newEvent.type = TYPE_TOUCH; + newEvent.subType = type; + + newEvent.touchFinger.id = id; + newEvent.touchFinger.x = x; + newEvent.touchFinger.y = y; + newEvent.touchFinger.dx = dx; + newEvent.touchFinger.dy = dy; + newEvent.touchFinger.pressure = pressure; + } + + void SendGamepadPress(bool pressed, size_t id, Joystick<>::GamepadButton button, + int buttonIndex) + { + auto& newEvent = this->events.emplace_back(); + + newEvent.type = TYPE_GAMEPAD; + newEvent.subType = pressed ? SUBTYPE_GAMEPADDOWN : SUBTYPE_GAMEPADUP; + + newEvent.padButton.name = *Joystick<>::buttonTypes.ReverseFind(button); + newEvent.padButton.id = id; + newEvent.padButton.button = buttonIndex; + } + + void SendGamepadAxis(size_t id, Joystick<>::GamepadAxis axis, int axisIndex, float value) + { + auto& newEvent = this->events.emplace_back(); + + newEvent.type = TYPE_GAMEPAD; + newEvent.subType = SUBTYPE_GAMEPADAXIS; + + newEvent.padAxis.id = id; + + const char* axisName = *Joystick<>::axisTypes.ReverseFind(axis); + newEvent.padAxis.name = axisName; + + newEvent.padAxis.axis = (int)axis; + newEvent.padAxis.value = value; + } + + private: + bool hysteresis; + + protected: + bool touchHeld; + bool focused; + + std::list events; + + virtual void _Poll() = 0; + }; +} // namespace love diff --git a/include/utilities/driver/renderer/drawcommand.tcc b/include/utilities/driver/renderer/drawcommand.tcc new file mode 100644 index 000000000..9437b81c7 --- /dev/null +++ b/include/utilities/driver/renderer/drawcommand.tcc @@ -0,0 +1,169 @@ +#pragma once + +#include +#include + +#include +#include + +#include + +#include + +namespace love +{ + using namespace vertex; + +#if defined(__3DS__) + using Handle = C3D_Tex; +#else + using Handle = Texture; +#endif + + template + struct DrawCommand + { + public: + DrawCommand() + {} + + DrawCommand(size_t count, PrimitiveType type = PRIMITIVE_TRIANGLES, + Shader<>::StandardShader shader = Shader<>::STANDARD_DEFAULT) : + positions {}, + count(count), + size(count * VERTEX_SIZE), + format(CommonFormat::PRIMITIVE), + type(type), + shader(shader) + { + if (count == 0) + throw love::Exception("Vertex count cannot be zero."); + + try + { + this->positions = std::make_unique(count); + this->vertices = std::make_unique(count); + } + catch (std::bad_alloc&) + { + throw love::Exception("Out of memory."); + } + } + + DrawCommand Clone() + { + /* init count, size, shader, and type */ + DrawCommand clone(this->count, this->type, this->shader); + clone.format = this->format; + clone.handles = this->handles; + + std::copy_n(this->Positions().get(), this->count, clone.Positions().get()); + std::copy_n(this->Vertices().get(), this->count, clone.Vertices().get()); + + return clone; + } + + const std::unique_ptr& Positions() const + { + return this->positions; + } + + const std::unique_ptr& Vertices() const + { + return this->vertices; + } + + /* primitive */ + void FillVertices(const Color& color) + { + for (size_t index = 0; index < this->count; index++) + { + // clang-format off + this->vertices[index] = + { + .position = { this->positions[index].x, this->positions[index].y, 0 }, + .color = color.array(), + .texcoord = { 0.0f, 0.0f } + }; + // clang-format on + } + } + + /* primitive */ + void FillVertices(const Color* colors) + { + for (size_t index = 0; index < this->count; index++) + { + // clang-format off + this->vertices[index] = + { + .position = { this->positions[index].x, this->positions[index].y, 0 }, + .color = colors[index].array(), + .texcoord = { 0, 0 } + }; + // clang-format on + } + } + + /* primitive */ + void FillVertices(std::span colors) + { + for (size_t index = 0; index < this->count; index++) + { + // clang-format off + this->vertices[index] = + { + .position = { this->positions[index].x, this->positions[index].y, 0 }, + .color = colors[index].array(), + .texcoord = { 0, 0 } + }; + // clang-format on + } + } + + /* texture */ + void FillVertices(const Color& color, const Vector2* textureCoords) + { + for (size_t index = 0; index < this->count; index++) + { + // clang-format off + this->vertices[index] = + { + .position = { this->positions[index].x, this->positions[index].y, 0 }, + .color = color.array(), + .texcoord = { textureCoords[index].x, textureCoords[index].y } + }; + // clang-format on + } + } + + /* font */ + void FillVertices(const Vertex* source) + { + for (size_t index = 0; index < this->count; index++) + { + // clang-format off + this->vertices[index] = + { + .position = { this->positions[index].x, this->positions[index].y, 0 }, + .color = source[index].color, + .texcoord = source[index].texcoord + }; + // clang-format on + } + } + + public: + std::unique_ptr positions; + std::unique_ptr vertices; + + size_t count; + size_t size; + + CommonFormat format; + PrimitiveType type; + Shader<>::StandardShader shader; + std::vector handles; + CullMode cullMode; + }; // namespace love +} // namespace love \ No newline at end of file diff --git a/include/utilities/driver/renderer/framebuffer.tcc b/include/utilities/driver/renderer/framebuffer.tcc new file mode 100644 index 000000000..0de2df034 --- /dev/null +++ b/include/utilities/driver/renderer/framebuffer.tcc @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#include + +namespace love +{ + template + class Framebuffer + { + public: + Framebuffer() : width(0), height(0), scissor {}, viewport {} + {} + + int GetWidth() const + { + return this->width; + } + + int GetHeight() const + { + return this->height; + } + + Rect GetViewport() + { + return this->viewport; + } + + Rect GetScissor() + { + return this->scissor; + } + + protected: + static constexpr float Z_NEAR = -10.0f; + static constexpr float Z_FAR = 10.0f; + + Screen id; + + int width; + int height; + + Rect scissor; + Rect viewport; + }; +} // namespace love \ No newline at end of file diff --git a/include/utilities/driver/renderer/polyline/polyline.hpp b/include/utilities/driver/renderer/polyline/polyline.hpp new file mode 100644 index 000000000..3c1e54106 --- /dev/null +++ b/include/utilities/driver/renderer/polyline/polyline.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include +#include + +#include + +// C++ +#include +#include + +namespace love +{ + template + class Graphics; + + /** + * Abstract base class for a chain of segments. + * @author Matthias Richter + **/ + class Polyline + { + // treat adjacent segments with angles between their directions <5 degree as straight + + public: + static constexpr float LINES_PARALLEL_EPS = 0.05f; + + Polyline(vertex::TriangleIndexMode mode = vertex::TRIANGLE_STRIP) : + vertices(nullptr), + overdraw(nullptr), + vertex_count(0), + overdraw_vertex_count(0), + triangle_mode(mode), + overdraw_vertex_start(0) + {} + + virtual ~Polyline(); + + /** + * @param vertices Vertices defining the core line segments + * @param count Number of vertices + * @param size_hint Expected number of vertices of the rendering sleeve around the + * core line. + * @param halfwidth linewidth / 2. + * @param pixel_size Dimension of one pixel on the screen in world coordinates. + * @param draw_overdraw Fake antialias the line. + */ + void render(const Vector2* vertices, size_t count, size_t size_hint, float halfwidth, + float pixel_size, bool draw_overdraw); + + /** Draws the line on the screen + */ + void draw(Graphics* gfx); + + protected: + virtual void calc_overdraw_vertex_count(bool is_looping); + virtual void render_overdraw(const std::vector& normals, float pixel_size, + bool is_looping); + virtual void fill_color_array(Color constant_color, Color* colors, int count); + + /** Calculate line boundary points. + * + * @param[out] anchors Anchor points defining the core line. + * @param[out] normals Normals defining the edge of the sleeve. + * @param[in,out] segment Direction of segment pq (updated to the segment qr). + * @param[in,out] segmentLength Length of segment pq (updated to the segment qr). + * @param[in,out] segmentNormal Normal on the segment pq (updated to the segment qr). + * @param[in] pointA Current point on the line (q). + * @param[in] pointB Next point on the line (r). + * @param[in] halfWidth Half line width (see Polyline.render()). + */ + virtual void renderEdge(std::vector& anchors, std::vector& normals, + Vector2& segment, float& segmentLength, Vector2& segmentNormal, + const Vector2& pointA, const Vector2& pointB, float halfWidth) = 0; + + Vector2* vertices; + Vector2* overdraw; + size_t vertex_count; + size_t overdraw_vertex_count; + vertex::TriangleIndexMode triangle_mode; + size_t overdraw_vertex_start; + + }; // Polyline +} // namespace love diff --git a/include/utilities/driver/renderer/polyline/types/beveljoin.hpp b/include/utilities/driver/renderer/polyline/types/beveljoin.hpp new file mode 100644 index 000000000..04d2e7359 --- /dev/null +++ b/include/utilities/driver/renderer/polyline/types/beveljoin.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace love +{ + /** + * A Polyline whose segments are connected by a flat edge. + * @author Matthias Richter + */ + class BevelJoinPolyline : public Polyline + { + public: + void render(const Vector2* vertices, size_t count, float halfwidth, float pixel_size, + bool draw_overdraw) + { + Polyline::render(vertices, count, 4 * count - 4, halfwidth, pixel_size, draw_overdraw); + } + + protected: + void renderEdge(std::vector& anchors, std::vector& normals, Vector2& s, + float& len_s, Vector2& ns, const Vector2& q, const Vector2& r, + float hw) override; + + }; // BevelJoinPolyline +} // namespace love diff --git a/include/utilities/driver/renderer/polyline/types/miterjoin.hpp b/include/utilities/driver/renderer/polyline/types/miterjoin.hpp new file mode 100644 index 000000000..ac8ce0f6c --- /dev/null +++ b/include/utilities/driver/renderer/polyline/types/miterjoin.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace love +{ + /** + * A Polyline whose segments are connected by a sharp edge. + * @author Matthias Richter + */ + class MiterJoinPolyline : public Polyline + { + public: + void render(const Vector2* vertices, size_t count, float halfwidth, float pixel_size, + bool draw_overdraw) + { + Polyline::render(vertices, count, 2 * count, halfwidth, pixel_size, draw_overdraw); + } + + protected: + void renderEdge(std::vector& anchors, std::vector& normals, Vector2& s, + float& len_s, Vector2& ns, const Vector2& q, const Vector2& r, + float hw) override; + + }; // MiterJoinPolyline +} // namespace love diff --git a/include/utilities/driver/renderer/polyline/types/nonejoin.hpp b/include/utilities/driver/renderer/polyline/types/nonejoin.hpp new file mode 100644 index 000000000..51894f398 --- /dev/null +++ b/include/utilities/driver/renderer/polyline/types/nonejoin.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include + +namespace love +{ + /** + * A Polyline whose segments are not connected. + * @author Matthias Richter + */ + class NoneJoinPolyline : public Polyline + { + public: + NoneJoinPolyline() : Polyline(vertex::TRIANGLE_QUADS) + {} + + void render(const Vector2* vertices, size_t count, float halfwidth, float pixel_size, + bool draw_overdraw) + { + Polyline::render(vertices, count, 4 * count - 4, halfwidth, pixel_size, draw_overdraw); + + // discard the first and last two vertices. (these are redundant) + for (size_t i = 0; i < vertex_count - 4; ++i) + this->vertices[i] = this->vertices[i + 2]; + + // The last quad is now garbage, so zero it out to make sure it doesn't + // get rasterized. These vertices are in between the core line vertices + // and the overdraw vertices in the combined vertex array, so they still + // get "rendered" since we draw everything with one draw call. + std::fill_n(this->vertices + (vertex_count - 4), 4, Vector2 {}); + + vertex_count -= 4; + } + + protected: + void calc_overdraw_vertex_count(bool is_looping) override; + void render_overdraw(const std::vector& normals, float pixel_size, + bool is_looping) override; + void fill_color_array(Color constant_color, Color* colors, int count) override; + void renderEdge(std::vector& anchors, std::vector& normals, Vector2& s, + float& len_s, Vector2& ns, const Vector2& q, const Vector2& r, + float hw) override; + + }; // NoneJoinPolyline +} // namespace love diff --git a/include/utilities/driver/renderer/renderer.tcc b/include/utilities/driver/renderer/renderer.tcc new file mode 100644 index 000000000..ab9f3aee4 --- /dev/null +++ b/include/utilities/driver/renderer/renderer.tcc @@ -0,0 +1,62 @@ +#pragma once + +#include +#include + +#include +#include + +#define DK_HPP_SUPPORT_VECTOR + +namespace love +{ + template + class Renderer + { + public: + static inline int shaderSwitches = 0; + static inline int drawCalls = 0; + static inline int drawCallsBatched = 0; + + static inline float gpuTime = 0.0f; + static inline float cpuTime = 0.0f; + + struct Info + { + std::string_view name; + std::string_view version; + std::string_view vendor; + std::string_view device; + + bool filled = false; + }; + + static constexpr float Z_NEAR = -10.0f; + static constexpr float Z_FAR = 10.0f; + + Renderer() : info {}, inFrame(false), viewport {} + {} + + Rect GetViewport() const + { + return this->viewport; + } + + void SetLineWidth(float width) + {} + + void SetLineStyle(RenderState::LineStyle style) + {} + + void SetPointSize(float size) + {} + + protected: + Info info; + + static inline size_t m_vertexOffset = 0; + + bool inFrame; + Rect viewport; + }; +} // namespace love diff --git a/include/utilities/driver/renderer/renderstate.hpp b/include/utilities/driver/renderer/renderstate.hpp new file mode 100644 index 000000000..77e345819 --- /dev/null +++ b/include/utilities/driver/renderer/renderstate.hpp @@ -0,0 +1,265 @@ +#pragma once + +#include + +#include + +#include +#include + +#include + +namespace love +{ + namespace RenderState + { + enum BlendMode + { + BLEND_ALPHA, + BLEND_ADD, + BLEND_SUBTRACT, + BLEND_MULTIPLY, + BLEND_LIGHTEN, + BLEND_DARKEN, + BLEND_SCREEN, + BLEND_REPLACE, + BLEND_NONE, + BLEND_CUSTOM, + BLEND_MAX_ENUM + }; + + enum BlendAlpha + { + BLENDALPHA_MULTIPLY, + BLENDALPHA_PREMULTIPLIED, + BLENDALPHA_MAX_ENUM + }; + + enum BlendFactor + { + BLENDFACTOR_ZERO, + BLENDFACTOR_ONE, + BLENDFACTOR_SRC_COLOR, + BLENDFACTOR_ONE_MINUS_SRC_COLOR, + BLENDFACTOR_SRC_ALPHA, + BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + BLENDFACTOR_DST_COLOR, + BLENDFACTOR_ONE_MINUS_DST_COLOR, + BLENDFACTOR_DST_ALPHA, + BLENDFACTOR_ONE_MINUS_DST_ALPHA, + BLENDFACTOR_SRC_ALPHA_SATURATED, + BLENDFACTOR_MAX_ENUM + }; + + enum BlendOperation + { + BLENDOP_ADD, + BLENDOP_SUBTRACT, + BLENDOP_REVERSE_SUBTRACT, + BLENDOP_MIN, + BLENDOP_MAX, + BLENDOP_MAX_ENUM + }; + + enum StencilAction + { + STENCIL_KEEP, + STENCIL_ZERO, + STENCIL_REPLACE, + STENCIL_INCREMENT, + STENCIL_DECREMENT, + STENCIL_INCREMENT_WRAP, + STENCIL_DECREMENT_WRAP, + STENCIL_INVERT, + STENCIL_MAX_ENUM + }; + + enum CompareMode + { + COMPARE_LESS, + COMPARE_LEQUAL, + COMPARE_EQUAL, + COMPARE_GEQUAL, + COMPARE_GREATER, + COMPARE_NOTEQUAL, + COMPARE_ALWAYS, + COMPARE_NEVER, + COMPARE_MAX_ENUM + }; + + enum LineStyle + { + LINE_ROUGH, + LINE_SMOOTH + }; + + enum LineJoin + { + LINE_JOIN_NONE, + LINE_JOIN_MITER, + LINE_JOIN_BEVEL + }; + + struct BlendState + { + BlendOperation operationRGB = BLENDOP_ADD; + BlendOperation operationA = BLENDOP_ADD; + + BlendFactor srcFactorRGB = BLENDFACTOR_ONE; + BlendFactor srcFactorA = BLENDFACTOR_ONE; + + BlendFactor dstFactorRGB = BLENDFACTOR_ZERO; + BlendFactor dstFactorA = BLENDFACTOR_ZERO; + + bool enabled = false; + + BlendState() + {} + + BlendState(BlendOperation rgb, BlendOperation alpha, BlendFactor srcRGB, + BlendFactor srcAlpha, BlendFactor dstRGB, BlendFactor dstAlpha) : + operationRGB(rgb), + operationA(alpha), + srcFactorRGB(srcRGB), + srcFactorA(srcAlpha), + dstFactorRGB(dstRGB), + dstFactorA(dstAlpha) + {} + + bool operator==(const BlendState& other) const + { + return this->enabled == other.enabled && + (this->operationRGB == other.operationRGB) && + (this->operationA == other.operationA) && + (this->srcFactorRGB == other.srcFactorRGB) && + (this->srcFactorA == other.srcFactorA) && + (this->dstFactorRGB == other.dstFactorRGB) && + (this->dstFactorA == other.dstFactorA); + } + }; + + struct DepthState + { + CompareMode mode = COMPARE_ALWAYS; + bool write = false; + + bool operator==(const DepthState& other) const + { + return this->write == other.write && this->mode == other.mode; + } + }; + + struct ColorMask + { + bool r : 1, g : 1, b : 1, a : 1; + + ColorMask() : r(true), g(true), b(true), a(true) + {} + + ColorMask(bool _r, bool _g, bool _b, bool _a) : r(_r), g(_g), b(_b), a(_a) + {} + + bool operator==(const ColorMask& m) const + { + return r == m.r && g == m.g && b == m.b && a == m.a; + } + + bool operator!=(const ColorMask& m) const + { + return !(operator==(m)); + } + + uint8_t GetColorMask() const + { + return r | (g << 1) | (b << 2) | (a << 3); + } + }; + + struct ScissorState + { + Rect rectangle = { 0, 0, 0, 0 }; + bool enabled = false; + }; + + BlendState ComputeBlendState(BlendMode mode, BlendAlpha alphaMode); + + BlendMode ComputeBlendMode(BlendState state, BlendAlpha& alpha); + + bool IsAlphaMultiplyBlendSupported(BlendMode mode); + + // clang-format off + static constexpr BidirectionalMap compareModes = { + "less", COMPARE_LESS, + "lequal", COMPARE_LEQUAL, + "equal", COMPARE_EQUAL, + "gequal", COMPARE_GEQUAL, + "greater", COMPARE_GREATER, + "notequal", COMPARE_NOTEQUAL, + "always", COMPARE_ALWAYS, + "never", COMPARE_NEVER + }; + + static constexpr BidirectionalMap stencilActions = { + "keep", STENCIL_KEEP, + "zero", STENCIL_ZERO, + "replace", STENCIL_REPLACE, + "increment", STENCIL_INCREMENT, + "decrement", STENCIL_DECREMENT, + "incrementwrap", STENCIL_INCREMENT_WRAP, + "decrementwrap", STENCIL_DECREMENT_WRAP, + "invert", STENCIL_INVERT + }; + + static constexpr BidirectionalMap blendModes = { + "alpha", BLEND_ALPHA, + "add", BLEND_ADD, + "subtract", BLEND_SUBTRACT, + "multiply", BLEND_MULTIPLY, + "lighten", BLEND_LIGHTEN, + "darken", BLEND_DARKEN, + "screen", BLEND_SCREEN, + "replace", BLEND_REPLACE, + "none", BLEND_NONE, + "custom", BLEND_CUSTOM + }; + + static constexpr BidirectionalMap blendAlphaModes = { + "alphamultiply", BLENDALPHA_MULTIPLY, + "premultiplied", BLENDALPHA_PREMULTIPLIED + }; + + static constexpr BidirectionalMap blendFactors = { + "zero", BLENDFACTOR_ZERO, + "one", BLENDFACTOR_ONE, + "srccolor", BLENDFACTOR_SRC_COLOR, + "oneminussrccolor", BLENDFACTOR_ONE_MINUS_SRC_COLOR, + "srcalpha", BLENDFACTOR_SRC_ALPHA, + "oneminussrcalpha", BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + "dstcolor", BLENDFACTOR_DST_COLOR, + "oneminusdstcolor", BLENDFACTOR_ONE_MINUS_DST_COLOR, + "dstalpha", BLENDFACTOR_DST_ALPHA, + "oneminusdstalpha", BLENDFACTOR_ONE_MINUS_DST_ALPHA, + "srcalphasaturated", BLENDFACTOR_SRC_ALPHA_SATURATED + }; + + static constexpr BidirectionalMap blendOperations = { + "add", BLENDOP_ADD, + "subtract", BLENDOP_SUBTRACT, + "reversesubtract", BLENDOP_REVERSE_SUBTRACT, + "min", BLENDOP_MIN, + "max", BLENDOP_MAX + }; + + static constexpr BidirectionalMap lineStyles = { + "rough", LINE_ROUGH, + "smooth", LINE_SMOOTH, + }; + + static constexpr BidirectionalMap lineJoins = { + "none", LINE_JOIN_NONE, + "miter", LINE_JOIN_MITER, + "bevel", LINE_JOIN_BEVEL + }; + // clang-format on + } // namespace RenderState +} // namespace love diff --git a/include/utilities/driver/renderer/samplerstate.hpp b/include/utilities/driver/renderer/samplerstate.hpp new file mode 100644 index 000000000..eed30f4cd --- /dev/null +++ b/include/utilities/driver/renderer/samplerstate.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include "renderstate.hpp" + +#include +#include +#include + +#include + +namespace love +{ + struct SamplerState + { + enum WrapMode + { + WRAP_CLAMP, + WRAP_CLAMP_ZERO, + WRAP_CLAMP_ONE, + WRAP_REPEAT, + WRAP_MIRRORED_REPEAT, + WRAP_MAX_ENUM + }; + + enum FilterMode + { + FILTER_NEAREST, + FILTER_LINEAR, + FILTER_MAX_ENUM + }; + + enum MipmapFilterMode + { + MIPMAP_FILTER_NONE, + MIPMAP_FILTER_LINEAR, + MIPMAP_FILTER_NEAREST, + MIPMAP_FILTER_MAX_ENUM + }; + + FilterMode minFilter = FILTER_LINEAR; + FilterMode magFilter = FILTER_LINEAR; + + MipmapFilterMode mipmapFilter = MIPMAP_FILTER_NONE; + + WrapMode wrapU = WRAP_CLAMP; + WrapMode wrapV = WRAP_CLAMP; + WrapMode wrapW = WRAP_CLAMP; + + float lodBias = 0.0f; + + uint8_t maxAnisotropy = 1; + std::optional depthSampleMode; + + uint8_t minLod = 0; + uint8_t maxLod = LOVE_UINT8_MAX; + + uint64_t ToKey() const; + + static SamplerState FromKey(uint64_t); + + static bool IsClampZeroOrOne(WrapMode wrap); + + // clang-format off + static constexpr BidirectionalMap filterModes = { + "nearest", SamplerState::FILTER_NEAREST, + "linear", SamplerState::FILTER_LINEAR + }; + + static constexpr BidirectionalMap wrapModes = { + "clamp", SamplerState::WRAP_CLAMP, + "repeat", SamplerState::WRAP_REPEAT, + "clampzero", SamplerState::WRAP_CLAMP_ZERO, + "clampone", SamplerState::WRAP_CLAMP_ONE, + "mirroredrepeat", SamplerState::WRAP_MIRRORED_REPEAT + }; + + static constexpr BidirectionalMap mipMapFilterModes = { + "none", SamplerState::MIPMAP_FILTER_NONE, + "linear", SamplerState::MIPMAP_FILTER_LINEAR, + "nearest", SamplerState::MIPMAP_FILTER_NEAREST + }; + // clang-format on + }; +} // namespace love diff --git a/include/utilities/driver/renderer/vertex.hpp b/include/utilities/driver/renderer/vertex.hpp new file mode 100644 index 000000000..0a17a1a87 --- /dev/null +++ b/include/utilities/driver/renderer/vertex.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include + +#include + +namespace love +{ + namespace vertex + { + enum CullMode + { + CULL_NONE, + CULL_BACK, + CULL_FRONT, + CULL_MAX_ENUM + }; + + enum Winding + { + WINDING_CW, + WINDING_CCW, + WINDING_MAX_ENUM + }; + + enum TriangleIndexMode + { + TRIANGLE_NONE, + TRIANGLE_STRIP, + TRIANGLE_FAN, + TRIANGLE_QUADS + }; + + enum PrimitiveType + { + PRIMITIVE_TRIANGLES, + PRIMITIVE_TRIANGLE_STRIP, + PRIMITIVE_TRIANGLE_FAN, + PRIMITIVE_QUADS, + PRIMITIVE_POINTS, + PRIMITIVE_MAX_ENUM + }; + + /* + ** Position, Color + ** Position, TexCoords, Color + */ + enum class CommonFormat + { + NONE, + PRIMITIVE, + TEXTURE, + FONT + }; + + enum AttributeStep + { + STEP_PER_VERTEX, + STEP_PER_INSTANCE, + STEP_MAX_ENUM + }; + + enum BuiltinVertexAttribute + { + ATTRIB_POS = 0, + ATTRIB_TEXCOORD, + ATTRIB_COLOR, + ATTRIB_MAX_ENUM + }; + + enum BuiltinVertexAttributeFlags + { + ATTRIBFLAG_POS = 1 << ATTRIB_POS, + ATTRIBFLAG_TEXCOORD = 1 << ATTRIB_TEXCOORD, + ATTRIBFLAG_COLOR = 1 << ATTRIB_COLOR, + }; + + struct VertexAttributes + { + static constexpr uint32_t MAX = 32; + }; + + struct Vertex + { + std::array position; + std::array color; + std::array texcoord; + }; + + static constexpr size_t VERTEX_SIZE = sizeof(Vertex); + + // clang-format off + static constexpr BidirectionalMap cullModes = { + "none", CULL_NONE, + "back", CULL_BACK, + "front", CULL_FRONT + }; + + static constexpr BidirectionalMap windingModes = { + "cw", WINDING_CW, + "ccw", WINDING_CCW + }; + + static constexpr BidirectionalMap triangleModes = { + "none", TRIANGLE_NONE, + "strip", TRIANGLE_STRIP, + "fan", TRIANGLE_FAN, + "quads", TRIANGLE_QUADS + }; + + static constexpr BidirectionalMap primitiveTypes = { + "triangles", PRIMITIVE_TRIANGLES, + "strip", PRIMITIVE_TRIANGLE_STRIP, + "fan", PRIMITIVE_TRIANGLE_FAN, + "points", PRIMITIVE_POINTS + }; + // clang-format on + } // namespace vertex +} // namespace love diff --git a/include/utilities/formathandler/formathandler.hpp b/include/utilities/formathandler/formathandler.hpp new file mode 100644 index 000000000..c807a8388 --- /dev/null +++ b/include/utilities/formathandler/formathandler.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace love +{ + class CompressedSlice; + + class FormatHandler : public Object + { + public: + enum EncodedFormat + { + ENCODED_TGA, + ENCODED_PNG, + ENCODED_EXR + }; + + struct DecodedImage + { + PixelFormat format = PIXELFORMAT_RGBA8_UNORM; + int width = 0; + int height = 0; + size_t size = 0; + std::unique_ptr data; + }; + + struct EncodedImage + { + size_t size = 0; + std::unique_ptr data = nullptr; + }; + + virtual ~FormatHandler() + {} + + virtual bool CanDecode(Data* data); + + virtual bool CanEncode(PixelFormat format, EncodedFormat encodedFormat); + + virtual DecodedImage Decode(Data* data); + + virtual EncodedImage Encode(const DecodedImage& image, EncodedFormat format); + + virtual bool CanParseCompressed(Data* data); + + virtual StrongReference ParseCompressed( + Data* filedata, std::vector>& images, + PixelFormat& format, bool& sRGB); + + void FreeRawPixels(uint8_t* memory) + { + delete[] memory; + } + + void FreeEncodedImage(uint8_t* memory) + { + delete[] memory; + } + }; +} // namespace love diff --git a/include/utilities/formathandler/types/astchandler.hpp b/include/utilities/formathandler/types/astchandler.hpp new file mode 100644 index 000000000..0c78892ee --- /dev/null +++ b/include/utilities/formathandler/types/astchandler.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include +#include + +namespace love +{ + class ASTCHandler : public FormatHandler + { + public: + struct BlockDim + { + uint32_t x; + uint32_t y; + + bool operator==(const BlockDim&) const = default; + }; + + virtual ~ASTCHandler() + {} + + bool CanParseCompressed(Data* data) override; + + StrongReference ParseCompressed( + Data* filedata, std::vector>& images, + PixelFormat& format, bool& sRGB) override; + + // clang-format off + static constexpr BidirectionalMap blockDims = + { + BlockDim {4, 4}, PIXELFORMAT_ASTC_4x4, + BlockDim {5, 4}, PIXELFORMAT_ASTC_5x4, + BlockDim {5, 5}, PIXELFORMAT_ASTC_5x5, + BlockDim {6, 5}, PIXELFORMAT_ASTC_6x5, + BlockDim {6, 6}, PIXELFORMAT_ASTC_6x6, + BlockDim {8, 5}, PIXELFORMAT_ASTC_8x5, + BlockDim {8, 6}, PIXELFORMAT_ASTC_8x6, + BlockDim {8, 8}, PIXELFORMAT_ASTC_8x8, + BlockDim {10, 5}, PIXELFORMAT_ASTC_10x5, + BlockDim {10, 6}, PIXELFORMAT_ASTC_10x6, + BlockDim {10, 8}, PIXELFORMAT_ASTC_10x8, + BlockDim {10, 10}, PIXELFORMAT_ASTC_10x10, + BlockDim {12, 10}, PIXELFORMAT_ASTC_12x10, + BlockDim {12, 12}, PIXELFORMAT_ASTC_12x12 + }; + // clang-format on + + private: + static constexpr uint32_t ASTC_IDENTIFIER = 0x5CA1AB13; + + struct ASTCHeader + { + uint8_t identifier[4]; + uint8_t blockdimX; + uint8_t blockdimY; + uint8_t blockdimZ; + uint8_t sizeX[3]; + uint8_t sizeY[3]; + uint8_t sizeZ[3]; + }; + }; +} // namespace love \ No newline at end of file diff --git a/include/utilities/formathandler/types/ddshandler.hpp b/include/utilities/formathandler/types/ddshandler.hpp new file mode 100644 index 000000000..caf940244 --- /dev/null +++ b/include/utilities/formathandler/types/ddshandler.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace love +{ + class DDSHandler : public FormatHandler + { + public: + virtual ~DDSHandler() + {} + + bool CanDecode(Data* data) override; + + DecodedImage Decode(Data* data) override; + + bool CanParseCompressed(Data* data) override; + + StrongReference ParseCompressed( + Data* filedata, std::vector>& images, + PixelFormat& format, bool& sRGB) override; + }; +} // namespace love \ No newline at end of file diff --git a/include/utilities/formathandler/types/jpghandler.hpp b/include/utilities/formathandler/types/jpghandler.hpp new file mode 100644 index 000000000..1bbc739c3 --- /dev/null +++ b/include/utilities/formathandler/types/jpghandler.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace love +{ + class JPGHandler : public FormatHandler + { + public: + virtual bool CanDecode(Data* data); + + virtual DecodedImage Decode(Data* data); + }; +} // namespace love diff --git a/include/utilities/formathandler/types/ktxhandler.hpp b/include/utilities/formathandler/types/ktxhandler.hpp new file mode 100644 index 000000000..08bc4083f --- /dev/null +++ b/include/utilities/formathandler/types/ktxhandler.hpp @@ -0,0 +1,157 @@ +#pragma once + +#pragma once + +#include +#include + +namespace love +{ + class KTXHandler : public FormatHandler + { + public: + enum KTXGLInternalFormat : uint32_t + { + /* ETC2 */ + KTX_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276, + KTX_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277, + KTX_GL_COMPRESSED_RGBA8_ETC2_EAC = 0x9278, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279, + + /* DXT */ + KTX_GL_COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0, + KTX_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2, + KTX_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E, + KTX_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3, + KTX_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F, + + /* BC4 and BC5 */ + KTX_GL_COMPRESSED_RED_RGTC1 = 0x8DBB, + KTX_GL_COMPRESSED_RG_RGTC2 = 0x8DBD, + + /* BC6 and BC7 */ + KTX_GL_COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C, + KTX_GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D, + + /* ASTC */ + KTX_GL_COMPRESSED_RGBA_ASTC_4x4_KHR = 0x93B0, + KTX_GL_COMPRESSED_RGBA_ASTC_5x4_KHR = 0x93B1, + KTX_GL_COMPRESSED_RGBA_ASTC_5x5_KHR = 0x93B2, + KTX_GL_COMPRESSED_RGBA_ASTC_6x5_KHR = 0x93B3, + KTX_GL_COMPRESSED_RGBA_ASTC_6x6_KHR = 0x93B4, + KTX_GL_COMPRESSED_RGBA_ASTC_8x5_KHR = 0x93B5, + KTX_GL_COMPRESSED_RGBA_ASTC_8x6_KHR = 0x93B6, + KTX_GL_COMPRESSED_RGBA_ASTC_8x8_KHR = 0x93B7, + KTX_GL_COMPRESSED_RGBA_ASTC_10x5_KHR = 0x93B8, + KTX_GL_COMPRESSED_RGBA_ASTC_10x6_KHR = 0x93B9, + KTX_GL_COMPRESSED_RGBA_ASTC_10x8_KHR = 0x93BA, + KTX_GL_COMPRESSED_RGBA_ASTC_10x10_KHR = 0x93BB, + KTX_GL_COMPRESSED_RGBA_ASTC_12x10_KHR = 0x93BC, + KTX_GL_COMPRESSED_RGBA_ASTC_12x12_KHR = 0x93BD, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR = 0x93D0, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR = 0x93D1, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR = 0x93D2, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR = 0x93D3, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR = 0x93D4, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR = 0x93D5, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR = 0x93D6, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR = 0x93D7, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR = 0x93D8, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR = 0x93D9, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR = 0x93DA, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR = 0x93DB, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR = 0x93DC, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR = 0x93DD + }; + + virtual ~KTXHandler() + {} + + bool CanParseCompressed(Data* data) override; + + StrongReference ParseCompressed( + Data* filedata, std::vector>& images, + PixelFormat& format, bool& sRGB) override; + + struct ConvertedFormat + { + PixelFormat format; + bool sRGB; + + bool operator==(const ConvertedFormat&) const = default; + }; + + // clang-format off + static constexpr BidirectionalMap ktxFormats = + { + KTX_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, ConvertedFormat { PIXELFORMAT_ETC2_RGBA1_UNORM, false }, + KTX_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, ConvertedFormat { PIXELFORMAT_ETC2_RGBA1_UNORM, true }, + KTX_GL_COMPRESSED_RGBA8_ETC2_EAC, ConvertedFormat { PIXELFORMAT_ETC2_RGBA_UNORM, false }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, ConvertedFormat { PIXELFORMAT_ETC2_RGBA_UNORM, true }, + KTX_GL_COMPRESSED_RGB_S3TC_DXT1_EXT, ConvertedFormat { PIXELFORMAT_DXT1_UNORM, false }, + KTX_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, ConvertedFormat { PIXELFORMAT_DXT3_UNORM, false }, + KTX_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, ConvertedFormat { PIXELFORMAT_DXT3_UNORM, true }, + KTX_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, ConvertedFormat { PIXELFORMAT_DXT5_UNORM, false }, + KTX_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, ConvertedFormat { PIXELFORMAT_DXT5_UNORM, true }, + KTX_GL_COMPRESSED_RED_RGTC1, ConvertedFormat { PIXELFORMAT_BC4_UNORM, false }, + KTX_GL_COMPRESSED_RG_RGTC2, ConvertedFormat { PIXELFORMAT_BC5_UNORM, false }, + KTX_GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, ConvertedFormat { PIXELFORMAT_BC7_UNORM, true }, + KTX_GL_COMPRESSED_RGBA_BPTC_UNORM, ConvertedFormat { PIXELFORMAT_BC7_UNORM, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_4x4_KHR, ConvertedFormat { PIXELFORMAT_ASTC_4x4, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_5x4_KHR, ConvertedFormat { PIXELFORMAT_ASTC_5x4, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_5x5_KHR, ConvertedFormat { PIXELFORMAT_ASTC_5x5, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_6x5_KHR, ConvertedFormat { PIXELFORMAT_ASTC_6x5, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_6x6_KHR, ConvertedFormat { PIXELFORMAT_ASTC_6x6, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_8x5_KHR, ConvertedFormat { PIXELFORMAT_ASTC_8x5, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_8x6_KHR, ConvertedFormat { PIXELFORMAT_ASTC_8x6, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_8x8_KHR, ConvertedFormat { PIXELFORMAT_ASTC_8x8, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_10x5_KHR, ConvertedFormat { PIXELFORMAT_ASTC_10x5, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_10x6_KHR, ConvertedFormat { PIXELFORMAT_ASTC_10x6, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_10x8_KHR, ConvertedFormat { PIXELFORMAT_ASTC_10x8, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_10x10_KHR, ConvertedFormat { PIXELFORMAT_ASTC_10x10, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_12x10_KHR, ConvertedFormat { PIXELFORMAT_ASTC_12x10, false }, + KTX_GL_COMPRESSED_RGBA_ASTC_12x12_KHR, ConvertedFormat { PIXELFORMAT_ASTC_12x12, false }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, ConvertedFormat { PIXELFORMAT_ASTC_4x4, true }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, ConvertedFormat { PIXELFORMAT_ASTC_5x4, true }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, ConvertedFormat { PIXELFORMAT_ASTC_5x5, true }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, ConvertedFormat { PIXELFORMAT_ASTC_6x5, true }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, ConvertedFormat { PIXELFORMAT_ASTC_6x6, true }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, ConvertedFormat { PIXELFORMAT_ASTC_8x5, true }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, ConvertedFormat { PIXELFORMAT_ASTC_8x6, true }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, ConvertedFormat { PIXELFORMAT_ASTC_8x8, true }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, ConvertedFormat { PIXELFORMAT_ASTC_10x5, true }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, ConvertedFormat { PIXELFORMAT_ASTC_10x6, true }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, ConvertedFormat { PIXELFORMAT_ASTC_10x8, true }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, ConvertedFormat { PIXELFORMAT_ASTC_10x10, true }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, ConvertedFormat { PIXELFORMAT_ASTC_12x10, true }, + KTX_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, ConvertedFormat { PIXELFORMAT_ASTC_12x12, true } + }; + // clang-format on + + private: + struct KTXHeader + { + uint8_t identifier[12]; + uint32_t endianness; + uint32_t glType; + uint32_t glTypeSize; + uint32_t glFormat; + uint32_t glInternalFormat; + uint32_t glBaseInternalFormat; + uint32_t pixelWidth; + uint32_t pixelHeight; + uint32_t pixelDepth; + uint32_t numberOfArrayElements; + uint32_t numberOfFaces; + uint32_t numberOfMipmapLevels; + uint32_t bytesOfKeyValueData; + }; + + static constexpr uint8_t IDENTIFIER_REF[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, + 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; + + static constexpr uint32_t KTX_ENDIAN_REF = 0x04030201; + static constexpr uint32_t KTX_ENDIAN_REF_REV = 0x01020304; + static constexpr uint32_t HEADER_SIZE = 0x40; + }; +} // namespace love \ No newline at end of file diff --git a/include/utilities/formathandler/types/pkmhandler.hpp b/include/utilities/formathandler/types/pkmhandler.hpp new file mode 100644 index 000000000..308909764 --- /dev/null +++ b/include/utilities/formathandler/types/pkmhandler.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +namespace love +{ + class PKMHandler : public FormatHandler + { + public: + static constexpr uint8_t identifier[] = { 'P', 'K', 'M', ' ' }; + + struct PKMHeader + { + uint8_t identifier[4]; + uint8_t version[2]; + uint16_t textureFormatBig; + uint16_t extendedWidthBig; + uint16_t extendedHeightBig; + uint16_t widthBig; + uint16_t heightBig; + }; + + enum PKMTextureFormat + { + ETC1_RGB_NO_MIPMAPS, + ETC2PACKAGE_RGB_NO_MIPMAPS, + ETC2PACKAGE_RGBA_NO_MIPMAPS_OLD, + ETC2PACKAGE_RGBA_NO_MIPMAPS, + ETC2PACKAGE_RGBA1_NO_MIPMAPS, + ETC2PACKAGE_R_NO_MIPMAPS, + ETC2PACKAGE_RG_NO_MIPMAPS, + ETC2PACKAGE_R_SIGNED_NO_MIPMAPS, + ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS + }; + + virtual ~PKMHandler() + {} + + // Implements FormatHandler. + bool CanParseCompressed(Data* data) override; + + StrongReference ParseCompressed( + Data* filedata, std::vector>& images, + PixelFormat& format, bool& sRGB) override; + }; +} // namespace love \ No newline at end of file diff --git a/include/utilities/formathandler/types/pnghandler.hpp b/include/utilities/formathandler/types/pnghandler.hpp new file mode 100644 index 000000000..527082255 --- /dev/null +++ b/include/utilities/formathandler/types/pnghandler.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace love +{ + class PNGHandler : public FormatHandler + { + public: + virtual bool CanDecode(Data* data); + + virtual DecodedImage Decode(Data* data); + + virtual bool CanEncode(PixelFormat rawFormat, EncodedFormat encodedFormat); + + virtual EncodedImage Encode(const DecodedImage& image, EncodedFormat format); + }; +} // namespace love diff --git a/include/utilities/functions.hpp b/include/utilities/functions.hpp new file mode 100644 index 000000000..357b598bd --- /dev/null +++ b/include/utilities/functions.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include + +#include +#include + +namespace love +{ + template + requires(std::is_pointer_v>) + inline bool is_nullptr_empty(const Range& range) + { + if (range.empty()) + return true; + + const auto lambda = [](const auto& v) { return v == nullptr; }; + return std::ranges::all_of(range.begin(), range.end(), lambda); + } + + inline bool has_file_extension(const std::filesystem::path& path) + { + return path.has_extension(); + } + + inline size_t get_line(const std::string_view& string, int start) + { + if (string.empty()) + return std::string::npos; + + size_t position = 0; + + std::string newline = ""; + if (string.find("\n") == std::string::npos) + newline = "\n"; + + if ((position = (std::string(string) + newline).find("\n", start)) != std::string::npos) + return position; + + return std::string::npos; + } + + inline void translatePath(std::filesystem::path& filepath) + { + if (!Console::Is(Console::CTR)) + return; + + static constexpr std::array textures = { ".png", ".jpg", ".jpeg" }; + static constexpr std::array fonts = { ".ttf", ".otf" }; + + for (auto extension : textures) + { + if (extension == filepath.extension()) + filepath.replace_extension(".t3x"); + } + + for (auto extension : fonts) + { + if (extension == filepath.extension()) + filepath.replace_extension(".bcfnt"); + } + } +} // namespace love diff --git a/include/utilities/guid.hpp b/include/utilities/guid.hpp new file mode 100644 index 000000000..4caa54ebe --- /dev/null +++ b/include/utilities/guid.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include + +#include + +namespace love::guid +{ + enum GamepadType + { + GAMEPAD_TYPE_UNKNOWN, + GAMEPAD_TYPE_NINTENDO_3DS, + GAMEPAD_TYPE_NEW_NINTENDO_3DS, + GAMEPAD_TYPE_NINTENDO_SWITCH_HANDHELD, + GAMEPAD_TYPE_NINTENDO_SWITCH_PRO, + GAMEPAD_TYPE_JOYCON_LEFT, + GAMEPAD_TYPE_JOYCON_RIGHT, + GAMEPAD_TYPE_JOYCON_PAIR, + GAMEPAD_TYPE_WII_U_GAMEPAD, + GAMEPAD_TYPE_WII_REMOTE, + GAMEPAD_TYPE_WII_REMOTE_NUNCHUCK, + GAMEPAD_TYPE_WII_CLASSIC, + GAMEPAD_TYPE_WII_PRO, + GAMEPAD_TYPE_MAX_ENUM + }; + + struct GamepadInfo + { + int buttonCount; + int axisCount; + int hatCount; + const char* name; + const char* guid; + bool hasZL; + bool hasZR; + }; + + struct DeviceInfo + { + int vendorId; + int productId; + int productVersion; + }; + + int GetGamepadButtonCount(GamepadType type); + + int GetGamepadAxisCount(GamepadType type); + + int GetGamepadHatCount(GamepadType type); + + const char* GetGamepadGUID(GamepadType type); + + const char* GetGamepadName(GamepadType type); + + bool GetGamepadHasZL(GamepadType type); + + bool GetGamepadHasZR(GamepadType type); + + bool GetDeviceInfo(GamepadType type, DeviceInfo& info); + + // clang-format off + static constexpr BidirectionalMap gamepadTypes = { + "unknown", love::guid::GAMEPAD_TYPE_UNKNOWN, + "nintendo3ds", love::guid::GAMEPAD_TYPE_NINTENDO_3DS, + "newnintendo3ds", love::guid::GAMEPAD_TYPE_NEW_NINTENDO_3DS, + "switchpro", love::guid::GAMEPAD_TYPE_NINTENDO_SWITCH_PRO, + "switchhandheld", love::guid::GAMEPAD_TYPE_NINTENDO_SWITCH_HANDHELD, + "joyconleft", love::guid::GAMEPAD_TYPE_JOYCON_LEFT, + "joyconright", love::guid::GAMEPAD_TYPE_JOYCON_RIGHT, + "joyconpair", love::guid::GAMEPAD_TYPE_JOYCON_PAIR, + "wiiugamepad", love::guid::GAMEPAD_TYPE_WII_U_GAMEPAD, + "wiiremote", love::guid::GAMEPAD_TYPE_WII_REMOTE, + "wiiremotenunchuck", love::guid::GAMEPAD_TYPE_WII_REMOTE_NUNCHUCK, + "wiiclassic", love::guid::GAMEPAD_TYPE_WII_CLASSIC, + "wiipro", love::guid::GAMEPAD_TYPE_WII_PRO + }; + // clang-format on +} // namespace love::guid diff --git a/include/utilities/haptics/vibration.tcc b/include/utilities/haptics/vibration.tcc new file mode 100644 index 000000000..5dd584985 --- /dev/null +++ b/include/utilities/haptics/vibration.tcc @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace love +{ + template + class Vibration + { + public: + static constexpr uint32_t MAX = std::numeric_limits::max(); + static constexpr float HAPTYIC_INFINITY = -1.0f; + + static Type type; + + struct VibrationInfo + { + float left; + float right; + + float endTime; + }; + + Vibration() : vibrationInfo {} + {} + + void SetDuration(float duration) + { + this->vibrationInfo.endTime = duration; + } + + float GetDuration() + { + return this->vibrationInfo.endTime; + } + + void GetValues(float& left, float& right) + { + left = this->vibrationInfo.left; + right = this->vibrationInfo.right; + } + + void SendValues(float left, float right) + {} + + bool Stop() + { + return true; + } + + protected: + VibrationInfo vibrationInfo; + int handleCount; + }; +} // namespace love diff --git a/include/utilities/hashfunction/hashfunction.hpp b/include/utilities/hashfunction/hashfunction.hpp new file mode 100644 index 000000000..dfd38eff6 --- /dev/null +++ b/include/utilities/hashfunction/hashfunction.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace love +{ + class HashFunction + { + public: + static inline uint32_t leftrot(uint32_t x, uint8_t amount) + { + return (x << amount) | (x >> (32 - amount)); + } + + static inline uint32_t rightrot(uint32_t x, uint8_t amount) + { + return (x >> amount) | (x << (32 - amount)); + } + + static inline uint64_t rightrot(uint64_t x, uint8_t amount) + { + return (x >> amount) | (x << (64 - amount)); + } + + enum Function + { + FUNCTION_MD5, + FUNCTION_SHA1, + FUNCTION_SHA224, + FUNCTION_SHA256, + FUNCTION_SHA384, + FUNCTION_SHA512, + FUNCTION_MAX_ENUM + }; + + struct Value + { + char data[64]; + size_t size; + }; + + static HashFunction* GetHashFunction(Function func); + + virtual ~HashFunction() + {} + + virtual void Hash(Function func, const char* input, uint64_t length, + Value& output) const = 0; + + virtual bool IsSupported(Function func) const = 0; + + // clang-format off + static constexpr BidirectionalMap functions = { + "md5", HashFunction::Function::FUNCTION_MD5, + "sha1", HashFunction::Function::FUNCTION_SHA1, + "sha224", HashFunction::Function::FUNCTION_SHA224, + "sha256", HashFunction::Function::FUNCTION_SHA256, + "sha384", HashFunction::Function::FUNCTION_SHA384, + "sha512", HashFunction::Function::FUNCTION_SHA512 + }; + // clang-format on + + protected: + HashFunction() + {} + }; +} // namespace love diff --git a/include/utilities/hashfunction/types/md5.hpp b/include/utilities/hashfunction/types/md5.hpp new file mode 100644 index 000000000..322845856 --- /dev/null +++ b/include/utilities/hashfunction/types/md5.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include + +namespace love +{ + class MD5 : public HashFunction + { + public: + bool IsSupported(Function function) const override + { + return function == FUNCTION_MD5; + }; + + void Hash(Function function, const char* input, uint64_t length, + Value& output) const override; + + private: + static constexpr uint8_t shifts[0x40] = { + 0x07, 0x0C, 0x11, 0x16, 0x07, 0x0C, 0x11, 0x16, 0x07, 0x0C, 0x11, 0x16, 0x07, + 0x0C, 0x11, 0x16, 0x05, 0x09, 0x0E, 0x14, 0x05, 0x09, 0x0E, 0x14, 0x05, 0x09, + 0x0E, 0x14, 0x05, 0x09, 0x0E, 0x14, 0x04, 0x0B, 0x10, 0x17, 0x04, 0x0B, 0x10, + 0x17, 0x04, 0x0B, 0x10, 0x17, 0x04, 0x0B, 0x10, 0x17, 0x06, 0x0A, 0x0F, 0x15, + 0x06, 0x0A, 0x0F, 0x15, 0x06, 0x0A, 0x0F, 0x15, 0x06, 0x0A, 0x0F, 0x15 + }; + + static constexpr uint32_t constants[0x40] = { + 0XD76AA478, 0XE8C7B756, 0X242070DB, 0XC1BDCEEE, 0XF57C0FAF, 0X4787C62A, + 0XA8304613, 0XFD469501, 0X698098D8, 0X8B44F7AF, 0XFFFF5BB1, 0X895CD7BE, + 0X6B901122, 0XFD987193, 0XA679438E, 0X49B40821, 0XF61E2562, 0XC040B340, + 0X265E5A51, 0XE9B6C7AA, 0XD62F105D, 0X02441453, 0XD8A1E681, 0XE7D3FBC8, + 0X21E1CDE6, 0XC33707D6, 0XF4D50D87, 0X455A14ED, 0XA9E3E905, 0XFCEFA3F8, + 0X676F02D9, 0X8D2A4C8A, 0XFFFA3942, 0X8771F681, 0X6D9D6122, 0XFDE5380C, + 0XA4BEEA44, 0X4BDECFA9, 0XF6BB4B60, 0XBEBFBC70, 0X289B7EC6, 0XEAA127FA, + 0XD4EF3085, 0X04881D05, 0XD9D4D039, 0XE6DB99E5, 0X1FA27CF8, 0XC4AC5665, + 0XF4292244, 0X432AFF97, 0XAB9423A7, 0XFC93A039, 0X655B59C3, 0X8F0CCC92, + 0XFFEFF47D, 0X85845DD1, 0X6FA87E4F, 0XFE2CE6E0, 0XA3014314, 0X4E0811A1, + 0XF7537E82, 0XBD3AF235, 0X2AD7D2BB, 0XEB86D391 + }; + }; +} // namespace love diff --git a/include/utilities/hashfunction/types/sha1.hpp b/include/utilities/hashfunction/types/sha1.hpp new file mode 100644 index 000000000..be2dc3ec9 --- /dev/null +++ b/include/utilities/hashfunction/types/sha1.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace love +{ + class SHA1 : public HashFunction + { + public: + bool IsSupported(Function function) const override + { + return function == FUNCTION_SHA1; + }; + + void Hash(Function function, const char* input, uint64_t length, + Value& output) const override; + }; +} // namespace love diff --git a/include/utilities/hashfunction/types/sha256.hpp b/include/utilities/hashfunction/types/sha256.hpp new file mode 100644 index 000000000..6398d49d5 --- /dev/null +++ b/include/utilities/hashfunction/types/sha256.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +namespace love +{ + class SHA256 : public HashFunction + { + public: + bool IsSupported(Function function) const override + { + return function == FUNCTION_SHA224 || function == FUNCTION_SHA256; + }; + + void Hash(Function function, const char* input, uint64_t length, + Value& output) const override; + + private: + static constexpr uint32_t initial224[0x08] = { + 0XC1059ED8, 0X367CD507, 0X3070DD17, 0XF70E5939, + 0XFFC00B31, 0X68581511, 0X64F98FA7, 0XBEFA4FA4, + }; + + static constexpr uint32_t initial256[0x08] = { + 0X6A09E667, 0XBB67AE85, 0X3C6EF372, 0XA54FF53A, + 0X510E527F, 0X9B05688C, 0X1F83D9AB, 0X5BE0CD19, + }; + + static constexpr uint32_t constants[0x40] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + }; +} // namespace love diff --git a/include/utilities/hashfunction/types/sha512.hpp b/include/utilities/hashfunction/types/sha512.hpp new file mode 100644 index 000000000..5eaa048d3 --- /dev/null +++ b/include/utilities/hashfunction/types/sha512.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include + +namespace love +{ + class SHA512 : public HashFunction + { + public: + bool IsSupported(Function function) const override + { + return function == FUNCTION_SHA384 || function == FUNCTION_SHA512; + }; + + void Hash(Function function, const char* input, uint64_t length, + Value& output) const override; + + private: + static constexpr uint64_t initial384[0x08] = { + 0XCBBB9D5DC1059ED8, 0X629A292A367CD507, 0X9159015A3070DD17, + 0X152FECD8F70E5939, 0X67332667FFC00B31, 0X8EB44A8768581511, + 0XDB0C2E0D64F98FA7, 0X47B5481DBEFA4FA4 + }; + + static constexpr uint64_t initial512[0x08] = { + 0X6A09E667F3BCC908, 0XBB67AE8584CAA73B, 0X3C6EF372FE94F82B, + 0XA54FF53A5F1D36F1, 0X510E527FADE682D1, 0X9B05688C2B3E6C1F, + 0X1F83D9ABFB41BD6B, 0X5BE0CD19137E2179 + }; + + static constexpr uint64_t constants[0x50] = { + 0X428A2F98D728AE22, 0X7137449123EF65CD, 0XB5C0FBCFEC4D3B2F, + 0XE9B5DBA58189DBBC, 0X3956C25BF348B538, 0X59F111F1B605D019, + 0X923F82A4AF194F9B, 0XAB1C5ED5DA6D8118, 0XD807AA98A3030242, + 0X12835B0145706FBE, 0X243185BE4EE4B28C, 0X550C7DC3D5FFB4E2, + 0X72BE5D74F27B896F, 0X80DEB1FE3B1696B1, 0X9BDC06A725C71235, + 0XC19BF174CF692694, 0XE49B69C19EF14AD2, 0XEFBE4786384F25E3, + 0X0FC19DC68B8CD5B5, 0X240CA1CC77AC9C65, 0X2DE92C6F592B0275, + 0X4A7484AA6EA6E483, 0X5CB0A9DCBD41FBD4, 0X76F988DA831153B5, + 0X983E5152EE66DFAB, 0XA831C66D2DB43210, 0XB00327C898FB213F, + 0XBF597FC7BEEF0EE4, 0XC6E00BF33DA88FC2, 0XD5A79147930AA725, + 0X06CA6351E003826F, 0X142929670A0E6E70, 0X27B70A8546D22FFC, + 0X2E1B21385C26C926, 0X4D2C6DFC5AC42AED, 0X53380D139D95B3DF, + 0X650A73548BAF63DE, 0X766A0ABB3C77B2A8, 0X81C2C92E47EDAEE6, + 0X92722C851482353B, 0XA2BFE8A14CF10364, 0XA81A664BBC423001, + 0XC24B8B70D0F89791, 0XC76C51A30654BE30, 0XD192E819D6EF5218, + 0XD69906245565A910, 0XF40E35855771202A, 0X106AA07032BBD1B8, + 0X19A4C116B8D2D0C8, 0X1E376C085141AB53, 0X2748774CDF8EEB99, + 0X34B0BCB5E19B48A8, 0X391C0CB3C5C95A63, 0X4ED8AA4AE3418ACB, + 0X5B9CCA4F7763E373, 0X682E6FF3D6B2B8A3, 0X748F82EE5DEFB2FC, + 0X78A5636F43172F60, 0X84C87814A1F0AB72, 0X8CC702081A6439EC, + 0X90BEFFFA23631E28, 0XA4506CEBDE82BDE9, 0XBEF9A3F7B2C67915, + 0XC67178F2E372532B, 0XCA273ECEEA26619C, 0XD186B8C721C0C207, + 0XEADA7DD6CDE0EB1E, 0XF57D4F7FEE6ED178, 0X06F067AA72176FBA, + 0X0A637DC5A2C898A6, 0X113F9804BEF90DAE, 0X1B710B35131C471B, + 0X28DB77F523047D84, 0X32CAAB7B40C72493, 0X3C9EBE0A15C9BEBC, + 0X431D67C49C100D4C, 0X4CC5D4BECB3E42B6, 0X597F299CFC657E2A, + 0X5FCB6FAB3AD6FAEC, 0X6C44198C4A475817 + }; + }; +} // namespace love diff --git a/include/utilities/log/logfile.hpp b/include/utilities/log/logfile.hpp new file mode 100644 index 000000000..e8639a405 --- /dev/null +++ b/include/utilities/log/logfile.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include + +#include +#include + +#include +#include +#include + +#include + +class Log +{ + public: + static Log& Instance() + { + static Log instance; + return instance; + } + + ~Log() + { + fclose(this->file); + } + + template + void Write(std::source_location location, const char* format, FormatArgs&&... args) + { + if (!m_enabled) + return; + + const auto size = snprintf(nullptr, 0, format, args...); + std::unique_ptr buffer = std::make_unique(size + 1); + + snprintf(buffer.get(), size + 1, format, args...); + + const auto path = std::string(location.file_name()); + const auto filename = path.substr(path.find_last_of('/') + 1); + + const auto line = (uint32_t)location.line(); + const auto column = (uint32_t)location.column(); + const auto funcname = location.function_name(); + + fprintf(this->file, BUFFER_FORMAT, filename.c_str(), line, column, funcname, buffer.get()); + fflush(this->file); + } + + private: + static inline const char* FILENAME = "debug.log"; + static constexpr const char* BUFFER_FORMAT = "%s(%zu:%zu): `%s`:\n%s\n\n"; + + Log() : file(nullptr) + { + if (m_enabled) + this->file = fopen(FILENAME, "w"); + } + + FILE* file; + static constexpr bool m_enabled = (__DEBUG__); +}; + +#if __DEBUG__ == 0 + #define LOG(...) +#else + #define LOG(format, ...) \ + Log::Instance().Write(std::source_location::current(), format, ##__VA_ARGS__); +#endif diff --git a/include/utilities/pool/poolthread.hpp b/include/utilities/pool/poolthread.hpp new file mode 100644 index 000000000..b62664d30 --- /dev/null +++ b/include/utilities/pool/poolthread.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include + +namespace love +{ + class PoolThread : public Threadable + { + public: + PoolThread(VibrationPool* pool); + + PoolThread(AudioPool* pool); + + virtual ~PoolThread(); + + void SetFinish(); + + void ThreadFunction(); + + private: + VibrationPool* vibrations; + AudioPool* sources; + + std::atomic finish; + }; +} // namespace love diff --git a/include/utilities/pool/sources.hpp b/include/utilities/pool/sources.hpp new file mode 100644 index 000000000..0ddf91990 --- /dev/null +++ b/include/utilities/pool/sources.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace love +{ + class AudioPool + { + public: + AudioPool(); + + ~AudioPool(); + + bool IsAvailable(); + + bool IsPlaying(Source* source); + + void Update(); + + int GetActiveSourceCount() const; + + int GetMaxSources() const; + + private: + friend class Source; + static constexpr size_t MAX_SOURCES = 24; + + int totalSources; + std::queue available; + std::map*, size_t> playing; + love::mutex mutex; + + std::unique_lock Lock(); + + std::vector*> GetPlayingSources(); + + bool ReleaseSource(Source* source, bool stop = true); + + void AddSource(Source* source, size_t channel); + + bool AssignSource(Source* source, size_t& channel, uint8_t& wasPlaying); + + bool FindSource(Source* source, size_t& channel); + }; +} // namespace love diff --git a/include/utilities/pool/vibrations.hpp b/include/utilities/pool/vibrations.hpp new file mode 100644 index 000000000..c19159a21 --- /dev/null +++ b/include/utilities/pool/vibrations.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +#include +#include +#include + +using Vibration = love::Vibration; + +namespace love +{ + class VibrationPool + { + public: + VibrationPool(); + + ~VibrationPool(); + + void Update(); + + bool AddVibration(::Vibration* vibration); + + private: + std::map<::Vibration*, size_t> vibrating; + std::queue available; + love::mutex mutex; + + bool ReleaseVibration(::Vibration* vibration); + + bool FindVibration(::Vibration* vibration, size_t& id); + }; +} // namespace love diff --git a/include/utilities/result.hpp b/include/utilities/result.hpp new file mode 100644 index 000000000..4cbac16e3 --- /dev/null +++ b/include/utilities/result.hpp @@ -0,0 +1,64 @@ +#include +#include + +#include + +namespace love +{ + struct FreeDeleter + { + void operator()(void* ptr) + { + if (ptr) + free(ptr); + } + }; + + inline bool g_EarlyExit = false; + + // clang-format off + template + concept StructV = requires(T a) + { + { a.value } -> std::same_as; + }; + // clang-format on + + class ResultCode + { + public: + ResultCode() + { + this->result = 0; + } + + ResultCode(int32_t result); + + // clang-format off + template + // clang-format on + ResultCode(T result) + { + this->result = result.mValue; + } + + bool Success() const; + + bool Failed() const; + + operator int32_t() + { + return result; + } + + private: + int32_t result; + }; + +#define R_UNLESS(res_expr, throw_result) \ + ({ \ + const auto _tmp_r_try_rc = love::ResultCode((res_expr)); \ + if (_tmp_r_try_rc.Failed()) \ + return (throw_result); \ + }) +} // namespace love diff --git a/include/utilities/sensor/sensorbase.hpp b/include/utilities/sensor/sensorbase.hpp new file mode 100644 index 000000000..943e2a76b --- /dev/null +++ b/include/utilities/sensor/sensorbase.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace love +{ + class SensorBase + { + public: + SensorBase() : enabled(false) + {} + + virtual std::vector GetData() = 0; + + virtual void SetEnabled(bool enable) + { + this->enabled = enable; + } + + bool IsEnabled() const + { + return this->enabled; + } + + protected: + bool enabled; + }; +} // namespace love diff --git a/include/utilities/shaper/genericshaper.hpp b/include/utilities/shaper/genericshaper.hpp new file mode 100644 index 000000000..c7aabfd6c --- /dev/null +++ b/include/utilities/shaper/genericshaper.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace love +{ + class GenericShaper : public TextShaper + { + public: + GenericShaper(Rasterizer* rasterizer); + + virtual ~GenericShaper() + {} + + void ComputeGlyphPositions(const ColoredCodepoints& codepoints, Range range, Vector2 offset, + float extraspacing, std::vector* positions, + std::vector* colors, TextInfo* info) override; + + int ComputeWordWrapIndex(const ColoredCodepoints& codepoints, Range range, float wraplimit, + float* width) override; + }; +} // namespace love \ No newline at end of file diff --git a/include/utilities/shaper/textshaper.hpp b/include/utilities/shaper/textshaper.hpp new file mode 100644 index 000000000..5e9270b36 --- /dev/null +++ b/include/utilities/shaper/textshaper.hpp @@ -0,0 +1,159 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace love +{ + class Rasterizer; + + struct ColoredString + { + std::string str; + Color color; + }; + + using ColoredStrings = std::vector; + + struct IndexedColor + { + Color color; + int index; + }; + + struct ColoredCodepoints + { + std::vector cps; + std::vector colors; + }; + + void GetCodepointsFromString(const std::string& string, std::vector& codepoints); + + void GetCodepointsFromString(const std::vector& strings, + ColoredCodepoints& codepoints); + + class TextShaper : public Object + { + public: + struct GlyphIndex + { + int index; + int rasterizerIndex; + }; + + struct GlyphPosition + { + Vector2 position; + GlyphIndex glyphIndex; + }; + + struct TextInfo + { + float width; + float height; + }; + + static constexpr int SPACES_PER_TAB = 4; + + static love::Type type; + + virtual ~TextShaper() + {} + + const std::vector>& GetRasterizers() const + { + return this->rasterizers; + } + + bool IsUsingSpacesForTab() const + { + return this->useSpacesForTab; + } + + float GetHeight() const + { + return this->height; + } + + void SetLineHeight(float lineHeight) + { + this->lineHeight = lineHeight; + } + + float GetLineHeight() const + { + return this->lineHeight; + } + + int GetAscent() const; + + int GetDescent() const; + + float GetBaseline() const; + + bool HasGlyph(uint32_t glyph) const; + + bool HasGlyphs(const std::string& text) const; + + float GetKerning(uint32_t leftGlyph, uint32_t rightGlyph); + + float GetKerning(const std::string& left, const std::string& right); + + int GetGlyphAdvance(uint32_t glyph, GlyphIndex* index = nullptr); + + int GetWidth(const std::string& string); + + void GetWrap(const std::vector& text, float wrapLimit, + std::vector& lines, std::vector* lineWidths = nullptr); + + void GetWrap(const ColoredCodepoints& codepoints, float wrapLimit, + std::vector& lineRanges, std::vector* lineWidths = nullptr); + + virtual void SetFallbacks(const std::vector& fallbacks); + + virtual void ComputeGlyphPositions(const ColoredCodepoints& codepoints, Range range, + Vector2 offset, float extraSpacing, + std::vector* positions, + std::vector* colors, TextInfo* info) = 0; + + virtual int ComputeWordWrapIndex(const ColoredCodepoints& codepoints, Range range, + float wrapLimit, float* width) = 0; + + static uint64_t PackGlyphIndex(GlyphIndex index) + { + return (static_cast(index.rasterizerIndex) << 32) | (uint64_t)index.index; + } + + static GlyphIndex UnpackGlyphIndex(uint64_t packed) + { + return { static_cast(packed & 0xFFFFFFFF), static_cast(packed >> 32) }; + } + + protected: + TextShaper(Rasterizer* rasterizer); + + static inline bool IsWhitespace(uint32_t codepoint) + { + return codepoint == ' ' || codepoint == '\t'; + } + + std::vector> rasterizers; + std::vector dpiScales; + + private: + int height; + float lineHeight; + bool useSpacesForTab; + + std::unordered_map> glyphAdvances; + std::unordered_map kernings; + }; +} // namespace love \ No newline at end of file diff --git a/include/utilities/stream/stream.hpp b/include/utilities/stream/stream.hpp new file mode 100644 index 000000000..92a92214e --- /dev/null +++ b/include/utilities/stream/stream.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include + +#include + +#include + +namespace love +{ + class Data; + + class Stream : public Object + { + public: + enum SeekOrigin + { + ORIGIN_BEGIN, + ORIGIN_CURRENT, + ORIGIN_END, + ORIGIN_MAX_ENUM + }; + + static love::Type type; + + virtual ~Stream() + {} + + virtual Stream* Clone() = 0; + + virtual bool IsReadable() const = 0; + + virtual bool IsWritable() const = 0; + + virtual bool IsSeekable() const = 0; + + virtual int64_t Read(void* destination, int64_t size) = 0; + + virtual Data* Read(int64_t size); + + virtual bool Write(const void* source, int64_t size) = 0; + + virtual bool Write(Data* source, int64_t offset, int64_t size); + + bool Write(Data* source); + + virtual bool Flush() = 0; + + virtual int64_t GetSize() = 0; + + virtual bool Seek(int64_t position, SeekOrigin = ORIGIN_BEGIN) = 0; + + virtual int64_t Tell() = 0; + + // clang-format off + static constexpr BidirectionalMap seekOrigins = { + SEEK_SET, Stream::ORIGIN_BEGIN, + SEEK_CUR, Stream::ORIGIN_CURRENT, + SEEK_END, Stream::ORIGIN_END + }; + // clang-format on + }; +} // namespace love diff --git a/include/utilities/stream/types/datastream.hpp b/include/utilities/stream/types/datastream.hpp new file mode 100644 index 000000000..5ba386841 --- /dev/null +++ b/include/utilities/stream/types/datastream.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include + +#include + +namespace love +{ + class DataStream : public Stream + { + public: + static Type type; + + DataStream(Data* data); + + DataStream* Clone() override; + + bool IsReadable() const override; + + bool IsWritable() const override; + + bool IsSeekable() const override; + + int64_t Read(void* data, int64_t size) override; + + bool Write(const void* data, int64_t size) override; + + bool Flush() override; + + int64_t GetSize() override; + + bool Seek(int64_t position, SeekOrigin origin = SeekOrigin::ORIGIN_BEGIN) override; + + int64_t Tell() override; + + private: + DataStream(const DataStream& other); + + StrongReference data; + const uint8_t* memory; + uint8_t* writeable; + + size_t offset; + size_t size; + }; +} // namespace love diff --git a/include/utilities/temptransform.hpp b/include/utilities/temptransform.hpp new file mode 100644 index 000000000..b36e4bbf5 --- /dev/null +++ b/include/utilities/temptransform.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +namespace love +{ + class TempTransform + { + public: + TempTransform(Graphics& graphics) : graphics(&graphics) + { + this->graphics->PushTransform(); + } + + TempTransform(Graphics& graphics, + const Matrix4& transform) : + TempTransform(graphics) + { + this->graphics->PushTransform(); + this->graphics->InternalScale(transform); + } + + template + void TransformXY(vDst dst, vSrc src, int count) + { + this->graphics->GetTransform().TransformXYVert(dst, src, count); + } + + template + void TransformXYPure(vDst dst, vSrc src, int count) + { + this->graphics->GetTransform().TransformXYVertPure(dst, src, count); + } + + ~TempTransform() + { + this->graphics->PopTransform(); + } + + private: + Graphics* graphics; + }; +} // namespace love diff --git a/include/utilities/threads/thread.hpp b/include/utilities/threads/thread.hpp new file mode 100644 index 000000000..c55577367 --- /dev/null +++ b/include/utilities/threads/thread.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#include + +namespace love +{ + class Threadable; + + class Thread + { + public: + Thread(Threadable* threadable); + + virtual ~Thread(); + + bool IsRunning() const + { + return this->running; + } + + void Wait(); + + bool Start(); + + private: + static int Runner(void* data); + + Threadable* threadable; + std::atomic running; + + love::thread thread; + love::mutex mutex; + }; +} // namespace love diff --git a/include/utilities/threads/threadable.hpp b/include/utilities/threads/threadable.hpp new file mode 100644 index 000000000..b099111c1 --- /dev/null +++ b/include/utilities/threads/threadable.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include + +namespace love +{ + class Threadable : public Object + { + public: + static Type type; + + Threadable(); + + virtual ~Threadable(); + + virtual void ThreadFunction() = 0; + + bool Start(); + + void Wait(); + + bool IsRunning() const; + + const char* GetThreadName() const; + + protected: + love::Thread* owner; + std::string name; + }; +} // namespace love diff --git a/libraries/dr/dr_flac.h b/libraries/dr/dr_flac.h new file mode 100644 index 000000000..0c43eed7f --- /dev/null +++ b/libraries/dr/dr_flac.h @@ -0,0 +1,12507 @@ +/* +FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. +dr_flac - v0.12.39 - 2022-09-17 + +David Reid - mackron@gmail.com + +GitHub: https://github.com/mackron/dr_libs +*/ + +/* +RELEASE NOTES - v0.12.0 +======================= +Version 0.12.0 has breaking API changes including changes to the existing API and the removal of deprecated APIs. + + +Improved Client-Defined Memory Allocation +----------------------------------------- +The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The +existing system of DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE are still in place and will be used by default when no custom +allocation callbacks are specified. + +To use the new system, you pass in a pointer to a drflac_allocation_callbacks object to drflac_open() and family, like this: + + void* my_malloc(size_t sz, void* pUserData) + { + return malloc(sz); + } + void* my_realloc(void* p, size_t sz, void* pUserData) + { + return realloc(p, sz); + } + void my_free(void* p, void* pUserData) + { + free(p); + } + + ... + + drflac_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = &myData; + allocationCallbacks.onMalloc = my_malloc; + allocationCallbacks.onRealloc = my_realloc; + allocationCallbacks.onFree = my_free; + drflac* pFlac = drflac_open_file("my_file.flac", &allocationCallbacks); + +The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines. + +Passing in null for the allocation callbacks object will cause dr_flac to use defaults which is the same as DRFLAC_MALLOC, +DRFLAC_REALLOC and DRFLAC_FREE and the equivalent of how it worked in previous versions. + +Every API that opens a drflac object now takes this extra parameter. These include the following: + + drflac_open() + drflac_open_relaxed() + drflac_open_with_metadata() + drflac_open_with_metadata_relaxed() + drflac_open_file() + drflac_open_file_with_metadata() + drflac_open_memory() + drflac_open_memory_with_metadata() + drflac_open_and_read_pcm_frames_s32() + drflac_open_and_read_pcm_frames_s16() + drflac_open_and_read_pcm_frames_f32() + drflac_open_file_and_read_pcm_frames_s32() + drflac_open_file_and_read_pcm_frames_s16() + drflac_open_file_and_read_pcm_frames_f32() + drflac_open_memory_and_read_pcm_frames_s32() + drflac_open_memory_and_read_pcm_frames_s16() + drflac_open_memory_and_read_pcm_frames_f32() + + + +Optimizations +------------- +Seeking performance has been greatly improved. A new binary search based seeking algorithm has been introduced which significantly +improves performance over the brute force method which was used when no seek table was present. Seek table based seeking also takes +advantage of the new binary search seeking system to further improve performance there as well. Note that this depends on CRC which +means it will be disabled when DR_FLAC_NO_CRC is used. + +The SSE4.1 pipeline has been cleaned up and optimized. You should see some improvements with decoding speed of 24-bit files in +particular. 16-bit streams should also see some improvement. + +drflac_read_pcm_frames_s16() has been optimized. Previously this sat on top of drflac_read_pcm_frames_s32() and performed it's s32 +to s16 conversion in a second pass. This is now all done in a single pass. This includes SSE2 and ARM NEON optimized paths. + +A minor optimization has been implemented for drflac_read_pcm_frames_s32(). This will now use an SSE2 optimized pipeline for stereo +channel reconstruction which is the last part of the decoding process. + +The ARM build has seen a few improvements. The CLZ (count leading zeroes) and REV (byte swap) instructions are now used when +compiling with GCC and Clang which is achieved using inline assembly. The CLZ instruction requires ARM architecture version 5 at +compile time and the REV instruction requires ARM architecture version 6. + +An ARM NEON optimized pipeline has been implemented. To enable this you'll need to add -mfpu=neon to the command line when compiling. + + +Removed APIs +------------ +The following APIs were deprecated in version 0.11.0 and have been completely removed in version 0.12.0: + + drflac_read_s32() -> drflac_read_pcm_frames_s32() + drflac_read_s16() -> drflac_read_pcm_frames_s16() + drflac_read_f32() -> drflac_read_pcm_frames_f32() + drflac_seek_to_sample() -> drflac_seek_to_pcm_frame() + drflac_open_and_decode_s32() -> drflac_open_and_read_pcm_frames_s32() + drflac_open_and_decode_s16() -> drflac_open_and_read_pcm_frames_s16() + drflac_open_and_decode_f32() -> drflac_open_and_read_pcm_frames_f32() + drflac_open_and_decode_file_s32() -> drflac_open_file_and_read_pcm_frames_s32() + drflac_open_and_decode_file_s16() -> drflac_open_file_and_read_pcm_frames_s16() + drflac_open_and_decode_file_f32() -> drflac_open_file_and_read_pcm_frames_f32() + drflac_open_and_decode_memory_s32() -> drflac_open_memory_and_read_pcm_frames_s32() + drflac_open_and_decode_memory_s16() -> drflac_open_memory_and_read_pcm_frames_s16() + drflac_open_and_decode_memory_f32() -> drflac_open_memroy_and_read_pcm_frames_f32() + +Prior versions of dr_flac operated on a per-sample basis whereas now it operates on PCM frames. The removed APIs all relate +to the old per-sample APIs. You now need to use the "pcm_frame" versions. +*/ + + +/* +Introduction +============ +dr_flac is a single file library. To use it, do something like the following in one .c file. + + ```c + #define DR_FLAC_IMPLEMENTATION + #include "dr_flac.h" + ``` + +You can then #include this file in other parts of the program as you would with any other header file. To decode audio data, do something like the following: + + ```c + drflac* pFlac = drflac_open_file("MySong.flac", NULL); + if (pFlac == NULL) { + // Failed to open FLAC file + } + + drflac_int32* pSamples = malloc(pFlac->totalPCMFrameCount * pFlac->channels * sizeof(drflac_int32)); + drflac_uint64 numberOfInterleavedSamplesActuallyRead = drflac_read_pcm_frames_s32(pFlac, pFlac->totalPCMFrameCount, pSamples); + ``` + +The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of channels and the bits per sample, +should be directly accessible - just make sure you don't change their values. Samples are always output as interleaved signed 32-bit PCM. In the example above +a native FLAC stream was opened, however dr_flac has seamless support for Ogg encapsulated FLAC streams as well. + +You do not need to decode the entire stream in one go - you just specify how many samples you'd like at any given time and the decoder will give you as many +samples as it can, up to the amount requested. Later on when you need the next batch of samples, just call it again. Example: + + ```c + while (drflac_read_pcm_frames_s32(pFlac, chunkSizeInPCMFrames, pChunkSamples) > 0) { + do_something(); + } + ``` + +You can seek to a specific PCM frame with `drflac_seek_to_pcm_frame()`. + +If you just want to quickly decode an entire FLAC file in one go you can do something like this: + + ```c + unsigned int channels; + unsigned int sampleRate; + drflac_uint64 totalPCMFrameCount; + drflac_int32* pSampleData = drflac_open_file_and_read_pcm_frames_s32("MySong.flac", &channels, &sampleRate, &totalPCMFrameCount, NULL); + if (pSampleData == NULL) { + // Failed to open and decode FLAC file. + } + + ... + + drflac_free(pSampleData, NULL); + ``` + +You can read samples as signed 16-bit integer and 32-bit floating-point PCM with the *_s16() and *_f32() family of APIs respectively, but note that these +should be considered lossy. + + +If you need access to metadata (album art, etc.), use `drflac_open_with_metadata()`, `drflac_open_file_with_metdata()` or `drflac_open_memory_with_metadata()`. +The rationale for keeping these APIs separate is that they're slightly slower than the normal versions and also just a little bit harder to use. dr_flac +reports metadata to the application through the use of a callback, and every metadata block is reported before `drflac_open_with_metdata()` returns. + +The main opening APIs (`drflac_open()`, etc.) will fail if the header is not present. The presents a problem in certain scenarios such as broadcast style +streams or internet radio where the header may not be present because the user has started playback mid-stream. To handle this, use the relaxed APIs: + + `drflac_open_relaxed()` + `drflac_open_with_metadata_relaxed()` + +It is not recommended to use these APIs for file based streams because a missing header would usually indicate a corrupt or perverse file. In addition, these +APIs can take a long time to initialize because they may need to spend a lot of time finding the first frame. + + + +Build Options +============= +#define these options before including this file. + +#define DR_FLAC_NO_STDIO + Disable `drflac_open_file()` and family. + +#define DR_FLAC_NO_OGG + Disables support for Ogg/FLAC streams. + +#define DR_FLAC_BUFFER_SIZE + Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls back to the client for more data. + Larger values means more memory, but better performance. My tests show diminishing returns after about 4KB (which is the default). Consider reducing this if + you have a very efficient implementation of onRead(), or increase it if it's very inefficient. Must be a multiple of 8. + +#define DR_FLAC_NO_CRC + Disables CRC checks. This will offer a performance boost when CRC is unnecessary. This will disable binary search seeking. When seeking, the seek table will + be used if available. Otherwise the seek will be performed using brute force. + +#define DR_FLAC_NO_SIMD + Disables SIMD optimizations (SSE on x86/x64 architectures, NEON on ARM architectures). Use this if you are having compatibility issues with your compiler. + +#define DR_FLAC_NO_WCHAR + Disables all functions ending with `_w`. Use this if your compiler does not provide wchar.h. Not required if DR_FLAC_NO_STDIO is also defined. + + + +Notes +===== +- dr_flac does not support changing the sample rate nor channel count mid stream. +- dr_flac is not thread-safe, but its APIs can be called from any thread so long as you do your own synchronization. +- When using Ogg encapsulation, a corrupted metadata block will result in `drflac_open_with_metadata()` and `drflac_open()` returning inconsistent samples due + to differences in corrupted stream recorvery logic between the two APIs. +*/ + +#ifndef dr_flac_h +#define dr_flac_h + +#ifdef __cplusplus +extern "C" { +#endif + +#define DRFLAC_STRINGIFY(x) #x +#define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) + +#define DRFLAC_VERSION_MAJOR 0 +#define DRFLAC_VERSION_MINOR 12 +#define DRFLAC_VERSION_REVISION 39 +#define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) + +#include /* For size_t. */ + +/* Sized types. */ +typedef signed char drflac_int8; +typedef unsigned char drflac_uint8; +typedef signed short drflac_int16; +typedef unsigned short drflac_uint16; +typedef signed int drflac_int32; +typedef unsigned int drflac_uint32; +#if defined(_MSC_VER) && !defined(__clang__) + typedef signed __int64 drflac_int64; + typedef unsigned __int64 drflac_uint64; +#else + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + #endif + typedef signed long long drflac_int64; + typedef unsigned long long drflac_uint64; + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop + #endif +#endif +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) + typedef drflac_uint64 drflac_uintptr; +#else + typedef drflac_uint32 drflac_uintptr; +#endif +typedef drflac_uint8 drflac_bool8; +typedef drflac_uint32 drflac_bool32; +#define DRFLAC_TRUE 1 +#define DRFLAC_FALSE 0 + +#if !defined(DRFLAC_API) + #if defined(DRFLAC_DLL) + #if defined(_WIN32) + #define DRFLAC_DLL_IMPORT __declspec(dllimport) + #define DRFLAC_DLL_EXPORT __declspec(dllexport) + #define DRFLAC_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define DRFLAC_DLL_IMPORT __attribute__((visibility("default"))) + #define DRFLAC_DLL_EXPORT __attribute__((visibility("default"))) + #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define DRFLAC_DLL_IMPORT + #define DRFLAC_DLL_EXPORT + #define DRFLAC_DLL_PRIVATE static + #endif + #endif + + #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) + #define DRFLAC_API DRFLAC_DLL_EXPORT + #else + #define DRFLAC_API DRFLAC_DLL_IMPORT + #endif + #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE + #else + #define DRFLAC_API extern + #define DRFLAC_PRIVATE static + #endif +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1700 /* Visual Studio 2012 */ + #define DRFLAC_DEPRECATED __declspec(deprecated) +#elif (defined(__GNUC__) && __GNUC__ >= 4) /* GCC 4 */ + #define DRFLAC_DEPRECATED __attribute__((deprecated)) +#elif defined(__has_feature) /* Clang */ + #if __has_feature(attribute_deprecated) + #define DRFLAC_DEPRECATED __attribute__((deprecated)) + #else + #define DRFLAC_DEPRECATED + #endif +#else + #define DRFLAC_DEPRECATED +#endif + +DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision); +DRFLAC_API const char* drflac_version_string(void); + +/* +As data is read from the client it is placed into an internal buffer for fast access. This controls the size of that buffer. Larger values means more speed, +but also more memory. In my testing there is diminishing returns after about 4KB, but you can fiddle with this to suit your own needs. Must be a multiple of 8. +*/ +#ifndef DR_FLAC_BUFFER_SIZE +#define DR_FLAC_BUFFER_SIZE 4096 +#endif + +/* Check if we can enable 64-bit optimizations. */ +#if defined(_WIN64) || defined(_LP64) || defined(__LP64__) +#define DRFLAC_64BIT +#endif + +#ifdef DRFLAC_64BIT +typedef drflac_uint64 drflac_cache_t; +#else +typedef drflac_uint32 drflac_cache_t; +#endif + +/* The various metadata block types. */ +#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 +#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1 +#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2 +#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 +#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 +#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5 +#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6 +#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127 + +/* The various picture types specified in the PICTURE block. */ +#define DRFLAC_PICTURE_TYPE_OTHER 0 +#define DRFLAC_PICTURE_TYPE_FILE_ICON 1 +#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 +#define DRFLAC_PICTURE_TYPE_COVER_FRONT 3 +#define DRFLAC_PICTURE_TYPE_COVER_BACK 4 +#define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5 +#define DRFLAC_PICTURE_TYPE_MEDIA 6 +#define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7 +#define DRFLAC_PICTURE_TYPE_ARTIST 8 +#define DRFLAC_PICTURE_TYPE_CONDUCTOR 9 +#define DRFLAC_PICTURE_TYPE_BAND 10 +#define DRFLAC_PICTURE_TYPE_COMPOSER 11 +#define DRFLAC_PICTURE_TYPE_LYRICIST 12 +#define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13 +#define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14 +#define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 +#define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 +#define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 +#define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18 +#define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 +#define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 + +typedef enum +{ + drflac_container_native, + drflac_container_ogg, + drflac_container_unknown +} drflac_container; + +typedef enum +{ + drflac_seek_origin_start, + drflac_seek_origin_current +} drflac_seek_origin; + +/* The order of members in this structure is important because we map this directly to the raw data within the SEEKTABLE metadata block. */ +typedef struct +{ + drflac_uint64 firstPCMFrame; + drflac_uint64 flacFrameOffset; /* The offset from the first byte of the header of the first frame. */ + drflac_uint16 pcmFrameCount; +} drflac_seekpoint; + +typedef struct +{ + drflac_uint16 minBlockSizeInPCMFrames; + drflac_uint16 maxBlockSizeInPCMFrames; + drflac_uint32 minFrameSizeInPCMFrames; + drflac_uint32 maxFrameSizeInPCMFrames; + drflac_uint32 sampleRate; + drflac_uint8 channels; + drflac_uint8 bitsPerSample; + drflac_uint64 totalPCMFrameCount; + drflac_uint8 md5[16]; +} drflac_streaminfo; + +typedef struct +{ + /* + The metadata type. Use this to know how to interpret the data below. Will be set to one of the + DRFLAC_METADATA_BLOCK_TYPE_* tokens. + */ + drflac_uint32 type; + + /* + A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to + not modify the contents of this buffer. Use the structures below for more meaningful and structured + information about the metadata. It's possible for this to be null. + */ + const void* pRawData; + + /* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */ + drflac_uint32 rawDataSize; + + union + { + drflac_streaminfo streaminfo; + + struct + { + int unused; + } padding; + + struct + { + drflac_uint32 id; + const void* pData; + drflac_uint32 dataSize; + } application; + + struct + { + drflac_uint32 seekpointCount; + const drflac_seekpoint* pSeekpoints; + } seektable; + + struct + { + drflac_uint32 vendorLength; + const char* vendor; + drflac_uint32 commentCount; + const void* pComments; + } vorbis_comment; + + struct + { + char catalog[128]; + drflac_uint64 leadInSampleCount; + drflac_bool32 isCD; + drflac_uint8 trackCount; + const void* pTrackData; + } cuesheet; + + struct + { + drflac_uint32 type; + drflac_uint32 mimeLength; + const char* mime; + drflac_uint32 descriptionLength; + const char* description; + drflac_uint32 width; + drflac_uint32 height; + drflac_uint32 colorDepth; + drflac_uint32 indexColorCount; + drflac_uint32 pictureDataSize; + const drflac_uint8* pPictureData; + } picture; + } data; +} drflac_metadata; + + +/* +Callback for when data needs to be read from the client. + + +Parameters +---------- +pUserData (in) + The user data that was passed to drflac_open() and family. + +pBufferOut (out) + The output buffer. + +bytesToRead (in) + The number of bytes to read. + + +Return Value +------------ +The number of bytes actually read. + + +Remarks +------- +A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until either the entire bytesToRead is filled or +you have reached the end of the stream. +*/ +typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); + +/* +Callback for when data needs to be seeked. + + +Parameters +---------- +pUserData (in) + The user data that was passed to drflac_open() and family. + +offset (in) + The number of bytes to move, relative to the origin. Will never be negative. + +origin (in) + The origin of the seek - the current position or the start of the stream. + + +Return Value +------------ +Whether or not the seek was successful. + + +Remarks +------- +The offset will never be negative. Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be +either drflac_seek_origin_start or drflac_seek_origin_current. + +When seeking to a PCM frame using drflac_seek_to_pcm_frame(), dr_flac may call this with an offset beyond the end of the FLAC stream. This needs to be detected +and handled by returning DRFLAC_FALSE. +*/ +typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); + +/* +Callback for when a metadata block is read. + + +Parameters +---------- +pUserData (in) + The user data that was passed to drflac_open() and family. + +pMetadata (in) + A pointer to a structure containing the data of the metadata block. + + +Remarks +------- +Use pMetadata->type to determine which metadata block is being handled and how to read the data. This +will be set to one of the DRFLAC_METADATA_BLOCK_TYPE_* tokens. +*/ +typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); + + +typedef struct +{ + void* pUserData; + void* (* onMalloc)(size_t sz, void* pUserData); + void* (* onRealloc)(void* p, size_t sz, void* pUserData); + void (* onFree)(void* p, void* pUserData); +} drflac_allocation_callbacks; + +/* Structure for internal use. Only used for decoders opened with drflac_open_memory. */ +typedef struct +{ + const drflac_uint8* data; + size_t dataSize; + size_t currentReadPos; +} drflac__memory_stream; + +/* Structure for internal use. Used for bit streaming. */ +typedef struct +{ + /* The function to call when more data needs to be read. */ + drflac_read_proc onRead; + + /* The function to call when the current read position needs to be moved. */ + drflac_seek_proc onSeek; + + /* The user data to pass around to onRead and onSeek. */ + void* pUserData; + + + /* + The number of unaligned bytes in the L2 cache. This will always be 0 until the end of the stream is hit. At the end of the + stream there will be a number of bytes that don't cleanly fit in an L1 cache line, so we use this variable to know whether + or not the bistreamer needs to run on a slower path to read those last bytes. This will never be more than sizeof(drflac_cache_t). + */ + size_t unalignedByteCount; + + /* The content of the unaligned bytes. */ + drflac_cache_t unalignedCache; + + /* The index of the next valid cache line in the "L2" cache. */ + drflac_uint32 nextL2Line; + + /* The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining. */ + drflac_uint32 consumedBits; + + /* + The cached data which was most recently read from the client. There are two levels of cache. Data flows as such: + Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions. + */ + drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]; + drflac_cache_t cache; + + /* + CRC-16. This is updated whenever bits are read from the bit stream. Manually set this to 0 to reset the CRC. For FLAC, this + is reset to 0 at the beginning of each frame. + */ + drflac_uint16 crc16; + drflac_cache_t crc16Cache; /* A cache for optimizing CRC calculations. This is filled when when the L1 cache is reloaded. */ + drflac_uint32 crc16CacheIgnoredBytes; /* The number of bytes to ignore when updating the CRC-16 from the CRC-16 cache. */ +} drflac_bs; + +typedef struct +{ + /* The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC. */ + drflac_uint8 subframeType; + + /* The number of wasted bits per sample as specified by the sub-frame header. */ + drflac_uint8 wastedBitsPerSample; + + /* The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC. */ + drflac_uint8 lpcOrder; + + /* A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. */ + drflac_int32* pSamplesS32; +} drflac_subframe; + +typedef struct +{ + /* + If the stream uses variable block sizes, this will be set to the index of the first PCM frame. If fixed block sizes are used, this will + always be set to 0. This is 64-bit because the decoded PCM frame number will be 36 bits. + */ + drflac_uint64 pcmFrameNumber; + + /* + If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0. This + is 32-bit because in fixed block sizes, the maximum frame number will be 31 bits. + */ + drflac_uint32 flacFrameNumber; + + /* The sample rate of this frame. */ + drflac_uint32 sampleRate; + + /* The number of PCM frames in each sub-frame within this frame. */ + drflac_uint16 blockSizeInPCMFrames; + + /* + The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this + will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE. + */ + drflac_uint8 channelAssignment; + + /* The number of bits per sample within this frame. */ + drflac_uint8 bitsPerSample; + + /* The frame's CRC. */ + drflac_uint8 crc8; +} drflac_frame_header; + +typedef struct +{ + /* The header. */ + drflac_frame_header header; + + /* + The number of PCM frames left to be read in this FLAC frame. This is initially set to the block size. As PCM frames are read, + this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame. + */ + drflac_uint32 pcmFramesRemaining; + + /* The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels. */ + drflac_subframe subframes[8]; +} drflac_frame; + +typedef struct +{ + /* The function to call when a metadata block is read. */ + drflac_meta_proc onMeta; + + /* The user data posted to the metadata callback function. */ + void* pUserDataMD; + + /* Memory allocation callbacks. */ + drflac_allocation_callbacks allocationCallbacks; + + + /* The sample rate. Will be set to something like 44100. */ + drflac_uint32 sampleRate; + + /* + The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the + value specified in the STREAMINFO block. + */ + drflac_uint8 channels; + + /* The bits per sample. Will be set to something like 16, 24, etc. */ + drflac_uint8 bitsPerSample; + + /* The maximum block size, in samples. This number represents the number of samples in each channel (not combined). */ + drflac_uint16 maxBlockSizeInPCMFrames; + + /* + The total number of PCM Frames making up the stream. Can be 0 in which case it's still a valid stream, but just means + the total PCM frame count is unknown. Likely the case with streams like internet radio. + */ + drflac_uint64 totalPCMFrameCount; + + + /* The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream. */ + drflac_container container; + + /* The number of seekpoints in the seektable. */ + drflac_uint32 seekpointCount; + + + /* Information about the frame the decoder is currently sitting on. */ + drflac_frame currentFLACFrame; + + + /* The index of the PCM frame the decoder is currently sitting on. This is only used for seeking. */ + drflac_uint64 currentPCMFrame; + + /* The position of the first FLAC frame in the stream. This is only ever used for seeking. */ + drflac_uint64 firstFLACFramePosInBytes; + + + /* A hack to avoid a malloc() when opening a decoder with drflac_open_memory(). */ + drflac__memory_stream memoryStream; + + + /* A pointer to the decoded sample data. This is an offset of pExtraData. */ + drflac_int32* pDecodedSamples; + + /* A pointer to the seek table. This is an offset of pExtraData, or NULL if there is no seek table. */ + drflac_seekpoint* pSeekpoints; + + /* Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData. */ + void* _oggbs; + + /* Internal use only. Used for profiling and testing different seeking modes. */ + drflac_bool32 _noSeekTableSeek : 1; + drflac_bool32 _noBinarySearchSeek : 1; + drflac_bool32 _noBruteForceSeek : 1; + + /* The bit streamer. The raw FLAC data is fed through this object. */ + drflac_bs bs; + + /* Variable length extra data. We attach this to the end of the object so we can avoid unnecessary mallocs. */ + drflac_uint8 pExtraData[1]; +} drflac; + + +/* +Opens a FLAC decoder. + + +Parameters +---------- +onRead (in) + The function to call when data needs to be read from the client. + +onSeek (in) + The function to call when the read position of the client data needs to move. + +pUserData (in, optional) + A pointer to application defined data that will be passed to onRead and onSeek. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ +Returns a pointer to an object representing the decoder. + + +Remarks +------- +Close the decoder with `drflac_close()`. + +`pAllocationCallbacks` can be NULL in which case it will use `DRFLAC_MALLOC`, `DRFLAC_REALLOC` and `DRFLAC_FREE`. + +This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated FLAC, both of which should work seamlessly +without any manual intervention. Ogg encapsulation also works with multiplexed streams which basically means it can play FLAC encoded audio tracks in videos. + +This is the lowest level function for opening a FLAC stream. You can also use `drflac_open_file()` and `drflac_open_memory()` to open the stream from a file or +from a block of memory respectively. + +The STREAMINFO block must be present for this to succeed. Use `drflac_open_relaxed()` to open a FLAC stream where the header may not be present. + +Use `drflac_open_with_metadata()` if you need access to metadata. + + +Seek Also +--------- +drflac_open_file() +drflac_open_memory() +drflac_open_with_metadata() +drflac_close() +*/ +DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +Opens a FLAC stream with relaxed validation of the header block. + + +Parameters +---------- +onRead (in) + The function to call when data needs to be read from the client. + +onSeek (in) + The function to call when the read position of the client data needs to move. + +container (in) + Whether or not the FLAC stream is encapsulated using standard FLAC encapsulation or Ogg encapsulation. + +pUserData (in, optional) + A pointer to application defined data that will be passed to onRead and onSeek. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ +A pointer to an object representing the decoder. + + +Remarks +------- +The same as drflac_open(), except attempts to open the stream even when a header block is not present. + +Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do not set this to `drflac_container_unknown` +as that is for internal use only. + +Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never found it will continue forever. To abort, +force your `onRead` callback to return 0, which dr_flac will use as an indicator that the end of the stream was found. + +Use `drflac_open_with_metadata_relaxed()` if you need access to metadata. +*/ +DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.). + + +Parameters +---------- +onRead (in) + The function to call when data needs to be read from the client. + +onSeek (in) + The function to call when the read position of the client data needs to move. + +onMeta (in) + The function to call for every metadata block. + +pUserData (in, optional) + A pointer to application defined data that will be passed to onRead, onSeek and onMeta. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ +A pointer to an object representing the decoder. + + +Remarks +------- +Close the decoder with `drflac_close()`. + +`pAllocationCallbacks` can be NULL in which case it will use `DRFLAC_MALLOC`, `DRFLAC_REALLOC` and `DRFLAC_FREE`. + +This is slower than `drflac_open()`, so avoid this one if you don't need metadata. Internally, this will allocate and free memory on the heap for every +metadata block except for STREAMINFO and PADDING blocks. + +The caller is notified of the metadata via the `onMeta` callback. All metadata blocks will be handled before the function returns. This callback takes a +pointer to a `drflac_metadata` object which is a union containing the data of all relevant metadata blocks. Use the `type` member to discriminate against +the different metadata types. + +The STREAMINFO block must be present for this to succeed. Use `drflac_open_with_metadata_relaxed()` to open a FLAC stream where the header may not be present. + +Note that this will behave inconsistently with `drflac_open()` if the stream is an Ogg encapsulated stream and a metadata block is corrupted. This is due to +the way the Ogg stream recovers from corrupted pages. When `drflac_open_with_metadata()` is being used, the open routine will try to read the contents of the +metadata block, whereas `drflac_open()` will simply seek past it (for the sake of efficiency). This inconsistency can result in different samples being +returned depending on whether or not the stream is being opened with metadata. + + +Seek Also +--------- +drflac_open_file_with_metadata() +drflac_open_memory_with_metadata() +drflac_open() +drflac_close() +*/ +DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +The same as drflac_open_with_metadata(), except attempts to open the stream even when a header block is not present. + +See Also +-------- +drflac_open_with_metadata() +drflac_open_relaxed() +*/ +DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +Closes the given FLAC decoder. + + +Parameters +---------- +pFlac (in) + The decoder to close. + + +Remarks +------- +This will destroy the decoder object. + + +See Also +-------- +drflac_open() +drflac_open_with_metadata() +drflac_open_file() +drflac_open_file_w() +drflac_open_file_with_metadata() +drflac_open_file_with_metadata_w() +drflac_open_memory() +drflac_open_memory_with_metadata() +*/ +DRFLAC_API void drflac_close(drflac* pFlac); + + +/* +Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM. + + +Parameters +---------- +pFlac (in) + The decoder. + +framesToRead (in) + The number of PCM frames to read. + +pBufferOut (out, optional) + A pointer to the buffer that will receive the decoded samples. + + +Return Value +------------ +Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. + + +Remarks +------- +pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. +*/ +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut); + + +/* +Reads sample data from the given FLAC decoder, output as interleaved signed 16-bit PCM. + + +Parameters +---------- +pFlac (in) + The decoder. + +framesToRead (in) + The number of PCM frames to read. + +pBufferOut (out, optional) + A pointer to the buffer that will receive the decoded samples. + + +Return Value +------------ +Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. + + +Remarks +------- +pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. + +Note that this is lossy for streams where the bits per sample is larger than 16. +*/ +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut); + +/* +Reads sample data from the given FLAC decoder, output as interleaved 32-bit floating point PCM. + + +Parameters +---------- +pFlac (in) + The decoder. + +framesToRead (in) + The number of PCM frames to read. + +pBufferOut (out, optional) + A pointer to the buffer that will receive the decoded samples. + + +Return Value +------------ +Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. + + +Remarks +------- +pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. + +Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly represent every possible number. +*/ +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut); + +/* +Seeks to the PCM frame at the given index. + + +Parameters +---------- +pFlac (in) + The decoder. + +pcmFrameIndex (in) + The index of the PCM frame to seek to. See notes below. + + +Return Value +------------- +`DRFLAC_TRUE` if successful; `DRFLAC_FALSE` otherwise. +*/ +DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex); + + + +#ifndef DR_FLAC_NO_STDIO +/* +Opens a FLAC decoder from the file at the given path. + + +Parameters +---------- +pFileName (in) + The path of the file to open, either absolute or relative to the current directory. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ +A pointer to an object representing the decoder. + + +Remarks +------- +Close the decoder with drflac_close(). + + +Remarks +------- +This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the number of files a process can have open +at any given time, so keep this mind if you have many decoders open at the same time. + + +See Also +-------- +drflac_open_file_with_metadata() +drflac_open() +drflac_close() +*/ +DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.) + + +Parameters +---------- +pFileName (in) + The path of the file to open, either absolute or relative to the current directory. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + +onMeta (in) + The callback to fire for each metadata block. + +pUserData (in) + A pointer to the user data to pass to the metadata callback. + +pAllocationCallbacks (in) + A pointer to application defined callbacks for managing memory allocations. + + +Remarks +------- +Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. + + +See Also +-------- +drflac_open_with_metadata() +drflac_open() +drflac_close() +*/ +DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +#endif + +/* +Opens a FLAC decoder from a pre-allocated block of memory + + +Parameters +---------- +pData (in) + A pointer to the raw encoded FLAC data. + +dataSize (in) + The size in bytes of `data`. + +pAllocationCallbacks (in) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ +A pointer to an object representing the decoder. + + +Remarks +------- +This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for the lifetime of the decoder. + + +See Also +-------- +drflac_open() +drflac_close() +*/ +DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.) + + +Parameters +---------- +pData (in) + A pointer to the raw encoded FLAC data. + +dataSize (in) + The size in bytes of `data`. + +onMeta (in) + The callback to fire for each metadata block. + +pUserData (in) + A pointer to the user data to pass to the metadata callback. + +pAllocationCallbacks (in) + A pointer to application defined callbacks for managing memory allocations. + + +Remarks +------- +Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. + + +See Also +------- +drflac_open_with_metadata() +drflac_open() +drflac_close() +*/ +DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); + + + +/* High Level APIs */ + +/* +Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a +pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with drflac_free(). + +You can pass in custom memory allocation callbacks via the pAllocationCallbacks parameter. This can be NULL in which +case it will use DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE. + +Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously +read samples into a dynamically sized buffer on the heap until no samples are left. + +Do not call this function on a broadcast type of stream (like internet radio streams and whatnot). +*/ +DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* Same as drflac_open_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ +DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* Same as drflac_open_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ +DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +#ifndef DR_FLAC_NO_STDIO +/* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a file. */ +DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* Same as drflac_open_file_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ +DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* Same as drflac_open_file_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ +DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +#endif + +/* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a block of memory. */ +DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ +DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ +DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +Frees memory that was allocated internally by dr_flac. + +Set pAllocationCallbacks to the same object that was passed to drflac_open_*_and_read_pcm_frames_*(). If you originally passed in NULL, pass in NULL for this. +*/ +DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks); + + +/* Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block. */ +typedef struct +{ + drflac_uint32 countRemaining; + const char* pRunningData; +} drflac_vorbis_comment_iterator; + +/* +Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT +metadata block. +*/ +DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments); + +/* +Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The +returned string is NOT null terminated. +*/ +DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); + + +/* Structure representing an iterator for cuesheet tracks in a CUESHEET metadata block. */ +typedef struct +{ + drflac_uint32 countRemaining; + const char* pRunningData; +} drflac_cuesheet_track_iterator; + +/* The order of members here is important because we map this directly to the raw data within the CUESHEET metadata block. */ +typedef struct +{ + drflac_uint64 offset; + drflac_uint8 index; + drflac_uint8 reserved[3]; +} drflac_cuesheet_track_index; + +typedef struct +{ + drflac_uint64 offset; + drflac_uint8 trackNumber; + char ISRC[12]; + drflac_bool8 isAudio; + drflac_bool8 preEmphasis; + drflac_uint8 indexCount; + const drflac_cuesheet_track_index* pIndexPoints; +} drflac_cuesheet_track; + +/* +Initializes a cuesheet track iterator. This can be used for iterating over the cuesheet tracks in a CUESHEET metadata +block. +*/ +DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData); + +/* Goes to the next cuesheet track in the given iterator. If DRFLAC_FALSE is returned it means there are no more comments. */ +DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack); + + +#ifdef __cplusplus +} +#endif +#endif /* dr_flac_h */ + + +/************************************************************************************************************************************************************ + ************************************************************************************************************************************************************ + + IMPLEMENTATION + + ************************************************************************************************************************************************************ + ************************************************************************************************************************************************************/ +#if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) +#ifndef dr_flac_c +#define dr_flac_c + +/* Disable some annoying warnings. */ +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #if __GNUC__ >= 7 + #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + #endif +#endif + +#ifdef __linux__ + #ifndef _BSD_SOURCE + #define _BSD_SOURCE + #endif + #ifndef _DEFAULT_SOURCE + #define _DEFAULT_SOURCE + #endif + #ifndef __USE_BSD + #define __USE_BSD + #endif + #include +#endif + +#include +#include + +#ifdef _MSC_VER + #define DRFLAC_INLINE __forceinline +#elif defined(__GNUC__) + /* + I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when + the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some + case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the + command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue + I am using "__inline__" only when we're compiling in strict ANSI mode. + */ + #if defined(__STRICT_ANSI__) + #define DRFLAC_GNUC_INLINE_HINT __inline__ + #else + #define DRFLAC_GNUC_INLINE_HINT inline + #endif + + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT + #endif +#elif defined(__WATCOMC__) + #define DRFLAC_INLINE __inline +#else + #define DRFLAC_INLINE +#endif + +/* CPU architecture. */ +#if defined(__x86_64__) || defined(_M_X64) + #define DRFLAC_X64 +#elif defined(__i386) || defined(_M_IX86) + #define DRFLAC_X86 +#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) + #define DRFLAC_ARM +#endif + +/* +Intrinsics Support + +There's a bug in GCC 4.2.x which results in an incorrect compilation error when using _mm_slli_epi32() where it complains with + + "error: shift must be an immediate" + +Unfortuantely dr_flac depends on this for a few things so we're just going to disable SSE on GCC 4.2 and below. +*/ +#if !defined(DR_FLAC_NO_SIMD) + #if defined(DRFLAC_X64) || defined(DRFLAC_X86) + #if defined(_MSC_VER) && !defined(__clang__) + /* MSVC. */ + #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2) /* 2005 */ + #define DRFLAC_SUPPORT_SSE2 + #endif + #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41) /* 2010 */ + #define DRFLAC_SUPPORT_SSE41 + #endif + #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) + /* Assume GNUC-style. */ + #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2) + #define DRFLAC_SUPPORT_SSE2 + #endif + #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41) + #define DRFLAC_SUPPORT_SSE41 + #endif + #endif + + /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ + #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) + #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include() + #define DRFLAC_SUPPORT_SSE2 + #endif + #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include() + #define DRFLAC_SUPPORT_SSE41 + #endif + #endif + + #if defined(DRFLAC_SUPPORT_SSE41) + #include + #elif defined(DRFLAC_SUPPORT_SSE2) + #include + #endif + #endif + + #if defined(DRFLAC_ARM) + #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + #define DRFLAC_SUPPORT_NEON + #include + #endif + #endif +#endif + +/* Compile-time CPU feature support. */ +#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) + #if defined(_MSC_VER) && !defined(__clang__) + #if _MSC_VER >= 1400 + #include + static void drflac__cpuid(int info[4], int fid) + { + __cpuid(info, fid); + } + #else + #define DRFLAC_NO_CPUID + #endif + #else + #if defined(__GNUC__) || defined(__clang__) + static void drflac__cpuid(int info[4], int fid) + { + /* + It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the + specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for + supporting different assembly dialects. + + What's basically happening is that we're saving and restoring the ebx register manually. + */ + #if defined(DRFLAC_X86) && defined(__PIC__) + __asm__ __volatile__ ( + "xchg{l} {%%}ebx, %k1;" + "cpuid;" + "xchg{l} {%%}ebx, %k1;" + : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) + ); + #else + __asm__ __volatile__ ( + "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) + ); + #endif + } + #else + #define DRFLAC_NO_CPUID + #endif + #endif +#else + #define DRFLAC_NO_CPUID +#endif + +static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2) + #if defined(DRFLAC_X64) + return DRFLAC_TRUE; /* 64-bit targets always support SSE2. */ + #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) + return DRFLAC_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */ + #else + #if defined(DRFLAC_NO_CPUID) + return DRFLAC_FALSE; + #else + int info[4]; + drflac__cpuid(info, 1); + return (info[3] & (1 << 26)) != 0; + #endif + #endif + #else + return DRFLAC_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */ + #endif +#else + return DRFLAC_FALSE; /* No compiler support. */ +#endif +} + +static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) +{ +#if defined(DRFLAC_SUPPORT_SSE41) + #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41) + #if defined(__SSE4_1__) || defined(__AVX__) + return DRFLAC_TRUE; /* If the compiler is allowed to freely generate SSE41 code we can assume support. */ + #else + #if defined(DRFLAC_NO_CPUID) + return DRFLAC_FALSE; + #else + int info[4]; + drflac__cpuid(info, 1); + return (info[2] & (1 << 19)) != 0; + #endif + #endif + #else + return DRFLAC_FALSE; /* SSE41 is only supported on x86 and x64 architectures. */ + #endif +#else + return DRFLAC_FALSE; /* No compiler support. */ +#endif +} + + +#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__) + #define DRFLAC_HAS_LZCNT_INTRINSIC +#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) + #define DRFLAC_HAS_LZCNT_INTRINSIC +#elif defined(__clang__) + #if defined(__has_builtin) + #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) + #define DRFLAC_HAS_LZCNT_INTRINSIC + #endif + #endif +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) + #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #define DRFLAC_HAS_BYTESWAP32_INTRINSIC + #define DRFLAC_HAS_BYTESWAP64_INTRINSIC +#elif defined(__clang__) + #if defined(__has_builtin) + #if __has_builtin(__builtin_bswap16) + #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap32) + #define DRFLAC_HAS_BYTESWAP32_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap64) + #define DRFLAC_HAS_BYTESWAP64_INTRINSIC + #endif + #endif +#elif defined(__GNUC__) + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + #define DRFLAC_HAS_BYTESWAP32_INTRINSIC + #define DRFLAC_HAS_BYTESWAP64_INTRINSIC + #endif + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #endif +#elif defined(__WATCOMC__) && defined(__386__) + #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #define DRFLAC_HAS_BYTESWAP32_INTRINSIC + #define DRFLAC_HAS_BYTESWAP64_INTRINSIC + extern __inline drflac_uint16 _watcom_bswap16(drflac_uint16); + extern __inline drflac_uint32 _watcom_bswap32(drflac_uint32); + extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64); +#pragma aux _watcom_bswap16 = \ + "xchg al, ah" \ + parm [ax] \ + value [ax] \ + modify nomemory; +#pragma aux _watcom_bswap32 = \ + "bswap eax" \ + parm [eax] \ + value [eax] \ + modify nomemory; +#pragma aux _watcom_bswap64 = \ + "bswap eax" \ + "bswap edx" \ + "xchg eax,edx" \ + parm [eax edx] \ + value [eax edx] \ + modify nomemory; +#endif + + +/* Standard library stuff. */ +#ifndef DRFLAC_ASSERT +#include +#define DRFLAC_ASSERT(expression) assert(expression) +#endif +#ifndef DRFLAC_MALLOC +#define DRFLAC_MALLOC(sz) malloc((sz)) +#endif +#ifndef DRFLAC_REALLOC +#define DRFLAC_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef DRFLAC_FREE +#define DRFLAC_FREE(p) free((p)) +#endif +#ifndef DRFLAC_COPY_MEMORY +#define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef DRFLAC_ZERO_MEMORY +#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#ifndef DRFLAC_ZERO_OBJECT +#define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p))) +#endif + +#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */ + +typedef drflac_int32 drflac_result; +#define DRFLAC_SUCCESS 0 +#define DRFLAC_ERROR -1 /* A generic error. */ +#define DRFLAC_INVALID_ARGS -2 +#define DRFLAC_INVALID_OPERATION -3 +#define DRFLAC_OUT_OF_MEMORY -4 +#define DRFLAC_OUT_OF_RANGE -5 +#define DRFLAC_ACCESS_DENIED -6 +#define DRFLAC_DOES_NOT_EXIST -7 +#define DRFLAC_ALREADY_EXISTS -8 +#define DRFLAC_TOO_MANY_OPEN_FILES -9 +#define DRFLAC_INVALID_FILE -10 +#define DRFLAC_TOO_BIG -11 +#define DRFLAC_PATH_TOO_LONG -12 +#define DRFLAC_NAME_TOO_LONG -13 +#define DRFLAC_NOT_DIRECTORY -14 +#define DRFLAC_IS_DIRECTORY -15 +#define DRFLAC_DIRECTORY_NOT_EMPTY -16 +#define DRFLAC_END_OF_FILE -17 +#define DRFLAC_NO_SPACE -18 +#define DRFLAC_BUSY -19 +#define DRFLAC_IO_ERROR -20 +#define DRFLAC_INTERRUPT -21 +#define DRFLAC_UNAVAILABLE -22 +#define DRFLAC_ALREADY_IN_USE -23 +#define DRFLAC_BAD_ADDRESS -24 +#define DRFLAC_BAD_SEEK -25 +#define DRFLAC_BAD_PIPE -26 +#define DRFLAC_DEADLOCK -27 +#define DRFLAC_TOO_MANY_LINKS -28 +#define DRFLAC_NOT_IMPLEMENTED -29 +#define DRFLAC_NO_MESSAGE -30 +#define DRFLAC_BAD_MESSAGE -31 +#define DRFLAC_NO_DATA_AVAILABLE -32 +#define DRFLAC_INVALID_DATA -33 +#define DRFLAC_TIMEOUT -34 +#define DRFLAC_NO_NETWORK -35 +#define DRFLAC_NOT_UNIQUE -36 +#define DRFLAC_NOT_SOCKET -37 +#define DRFLAC_NO_ADDRESS -38 +#define DRFLAC_BAD_PROTOCOL -39 +#define DRFLAC_PROTOCOL_UNAVAILABLE -40 +#define DRFLAC_PROTOCOL_NOT_SUPPORTED -41 +#define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42 +#define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43 +#define DRFLAC_SOCKET_NOT_SUPPORTED -44 +#define DRFLAC_CONNECTION_RESET -45 +#define DRFLAC_ALREADY_CONNECTED -46 +#define DRFLAC_NOT_CONNECTED -47 +#define DRFLAC_CONNECTION_REFUSED -48 +#define DRFLAC_NO_HOST -49 +#define DRFLAC_IN_PROGRESS -50 +#define DRFLAC_CANCELLED -51 +#define DRFLAC_MEMORY_ALREADY_MAPPED -52 +#define DRFLAC_AT_END -53 +#define DRFLAC_CRC_MISMATCH -128 + +#define DRFLAC_SUBFRAME_CONSTANT 0 +#define DRFLAC_SUBFRAME_VERBATIM 1 +#define DRFLAC_SUBFRAME_FIXED 8 +#define DRFLAC_SUBFRAME_LPC 32 +#define DRFLAC_SUBFRAME_RESERVED 255 + +#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 +#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 + +#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 +#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 +#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 +#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 + +#define DRFLAC_SEEKPOINT_SIZE_IN_BYTES 18 +#define DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 +#define DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 + +#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) + + +DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision) +{ + if (pMajor) { + *pMajor = DRFLAC_VERSION_MAJOR; + } + + if (pMinor) { + *pMinor = DRFLAC_VERSION_MINOR; + } + + if (pRevision) { + *pRevision = DRFLAC_VERSION_REVISION; + } +} + +DRFLAC_API const char* drflac_version_string(void) +{ + return DRFLAC_VERSION_STRING; +} + + +/* CPU caps. */ +#if defined(__has_feature) + #if __has_feature(thread_sanitizer) + #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) + #else + #define DRFLAC_NO_THREAD_SANITIZE + #endif +#else + #define DRFLAC_NO_THREAD_SANITIZE +#endif + +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) +static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; +#endif + +#ifndef DRFLAC_NO_CPUID +static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE; +static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE; + +/* +I've had a bug report that Clang's ThreadSanitizer presents a warning in this function. Having reviewed this, this does +actually make sense. However, since CPU caps should never differ for a running process, I don't think the trade off of +complicating internal API's by passing around CPU caps versus just disabling the warnings is worthwhile. I'm therefore +just going to disable these warnings. This is disabled via the DRFLAC_NO_THREAD_SANITIZE attribute. +*/ +DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) +{ + static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE; + + if (!isCPUCapsInitialized) { + /* LZCNT */ +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) + int info[4] = {0}; + drflac__cpuid(info, 0x80000001); + drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; +#endif + + /* SSE2 */ + drflac__gIsSSE2Supported = drflac_has_sse2(); + + /* SSE4.1 */ + drflac__gIsSSE41Supported = drflac_has_sse41(); + + /* Initialized. */ + isCPUCapsInitialized = DRFLAC_TRUE; + } +} +#else +static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE; + +static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void) +{ +#if defined(DRFLAC_SUPPORT_NEON) + #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON) + #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + return DRFLAC_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */ + #else + /* TODO: Runtime check. */ + return DRFLAC_FALSE; + #endif + #else + return DRFLAC_FALSE; /* NEON is only supported on ARM architectures. */ + #endif +#else + return DRFLAC_FALSE; /* No compiler support. */ +#endif +} + +DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) +{ + drflac__gIsNEONSupported = drflac__has_neon(); + +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + drflac__gIsLZCNTSupported = DRFLAC_TRUE; +#endif +} +#endif + + +/* Endian Management */ +static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void) +{ +#if defined(DRFLAC_X86) || defined(DRFLAC_X64) + return DRFLAC_TRUE; +#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN + return DRFLAC_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) +{ +#ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC + #if defined(_MSC_VER) && !defined(__clang__) + return _byteswap_ushort(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap16(n); + #elif defined(__WATCOMC__) && defined(__386__) + return _watcom_bswap16(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF00) >> 8) | + ((n & 0x00FF) << 8); +#endif +} + +static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) +{ +#ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC + #if defined(_MSC_VER) && !defined(__clang__) + return _byteswap_ulong(n); + #elif defined(__GNUC__) || defined(__clang__) + #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRFLAC_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ + /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ + drflac_uint32 r; + __asm__ __volatile__ ( + #if defined(DRFLAC_64BIT) + "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ + #else + "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) + #endif + ); + return r; + #else + return __builtin_bswap32(n); + #endif + #elif defined(__WATCOMC__) && defined(__386__) + return _watcom_bswap32(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF000000) >> 24) | + ((n & 0x00FF0000) >> 8) | + ((n & 0x0000FF00) << 8) | + ((n & 0x000000FF) << 24); +#endif +} + +static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) +{ +#ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC + #if defined(_MSC_VER) && !defined(__clang__) + return _byteswap_uint64(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap64(n); + #elif defined(__WATCOMC__) && defined(__386__) + return _watcom_bswap64(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */ + return ((n & ((drflac_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((drflac_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((drflac_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((drflac_uint64)0x000000FF << 32)) >> 8) | + ((n & ((drflac_uint64)0xFF000000 )) << 8) | + ((n & ((drflac_uint64)0x00FF0000 )) << 24) | + ((n & ((drflac_uint64)0x0000FF00 )) << 40) | + ((n & ((drflac_uint64)0x000000FF )) << 56); +#endif +} + + +static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n) +{ + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint16(n); + } + + return n; +} + +static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) +{ + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint32(n); + } + + return n; +} + +static DRFLAC_INLINE drflac_uint32 drflac__be2host_32_ptr_unaligned(const void* pData) +{ + const drflac_uint8* pNum = (drflac_uint8*)pData; + return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); +} + +static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) +{ + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint64(n); + } + + return n; +} + + +static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) +{ + if (!drflac__is_little_endian()) { + return drflac__swap_endian_uint32(n); + } + + return n; +} + +static DRFLAC_INLINE drflac_uint32 drflac__le2host_32_ptr_unaligned(const void* pData) +{ + const drflac_uint8* pNum = (drflac_uint8*)pData; + return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; +} + + +static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) +{ + drflac_uint32 result = 0; + result |= (n & 0x7F000000) >> 3; + result |= (n & 0x007F0000) >> 2; + result |= (n & 0x00007F00) >> 1; + result |= (n & 0x0000007F) >> 0; + + return result; +} + + + +/* The CRC code below is based on this document: http://zlib.net/crc_v3.txt */ +static drflac_uint8 drflac__crc8_table[] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +static drflac_uint16 drflac__crc16_table[] = { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, + 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, + 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, + 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, + 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, + 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, + 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, + 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, + 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 +}; + +static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data) +{ + return drflac__crc8_table[crc ^ data]; +} + +static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count) +{ +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + /* REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc8(crc, 0, 8);") */ + drflac_uint8 p = 0x07; + for (int i = count-1; i >= 0; --i) { + drflac_uint8 bit = (data & (1 << i)) >> i; + if (crc & 0x80) { + crc = ((crc << 1) | bit) ^ p; + } else { + crc = ((crc << 1) | bit); + } + } + return crc; +#else + drflac_uint32 wholeBytes; + drflac_uint32 leftoverBits; + drflac_uint64 leftoverDataMask; + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + + DRFLAC_ASSERT(count <= 32); + + wholeBytes = count >> 3; + leftoverBits = count - (wholeBytes*8); + leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); + } + return crc; +#endif +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data) +{ + return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data]; +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16_cache(drflac_uint16 crc, drflac_cache_t data) +{ +#ifdef DRFLAC_64BIT + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); +#endif + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); + + return crc; +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount) +{ + switch (byteCount) + { +#ifdef DRFLAC_64BIT + case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); + case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); + case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); + case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); +#endif + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); + } + + return crc; +} + +#if 0 +static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count) +{ +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + /* REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc16(crc, 0, 16);") */ + drflac_uint16 p = 0x8005; + for (int i = count-1; i >= 0; --i) { + drflac_uint16 bit = (data & (1ULL << i)) >> i; + if (r & 0x8000) { + r = ((r << 1) | bit) ^ p; + } else { + r = ((r << 1) | bit); + } + } + + return crc; +#else + drflac_uint32 wholeBytes; + drflac_uint32 leftoverBits; + drflac_uint64 leftoverDataMask; + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + + DRFLAC_ASSERT(count <= 64); + + wholeBytes = count >> 3; + leftoverBits = count & 7; + leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + default: + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count) +{ +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else + drflac_uint32 wholeBytes; + drflac_uint32 leftoverBits; + drflac_uint64 leftoverDataMask; + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + + DRFLAC_ASSERT(count <= 64); + + wholeBytes = count >> 3; + leftoverBits = count & 7; + leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + default: + case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */ + case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); + case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); + case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +} + + +static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count) +{ +#ifdef DRFLAC_64BIT + return drflac_crc16__64bit(crc, data, count); +#else + return drflac_crc16__32bit(crc, data, count); +#endif +} +#endif + + +#ifdef DRFLAC_64BIT +#define drflac__be2host__cache_line drflac__be2host_64 +#else +#define drflac__be2host__cache_line drflac__be2host_32 +#endif + +/* +BIT READING ATTEMPT #2 + +This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting +on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache +is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an +array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data +from onRead() is read into. +*/ +#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) +#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) +#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) +#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount))) +#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) +#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount)) +#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) +#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1))) +#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) +#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) +#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) + + +#ifndef DR_FLAC_NO_CRC +static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs) +{ + bs->crc16 = 0; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +} + +static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs) +{ + if (bs->crc16CacheIgnoredBytes == 0) { + bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache); + } else { + bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); + bs->crc16CacheIgnoredBytes = 0; + } +} + +static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) +{ + /* We should never be flushing in a situation where we are not aligned on a byte boundary. */ + DRFLAC_ASSERT((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); + + /* + The bits that were read from the L1 cache need to be accumulated. The number of bytes needing to be accumulated is determined + by the number of bits that have been consumed. + */ + if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { + drflac__update_crc16(bs); + } else { + /* We only accumulate the consumed bits. */ + bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); + + /* + The bits that we just accumulated should never be accumulated again. We need to keep track of how many bytes were accumulated + so we can handle that later. + */ + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; + } + + return bs->crc16; +} +#endif + +static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) +{ + size_t bytesRead; + size_t alignedL1LineCount; + + /* Fast path. Try loading straight from L2. */ + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } + + /* + If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's + any left. + */ + if (bs->unalignedByteCount > 0) { + return DRFLAC_FALSE; /* If we have any unaligned bytes it means there's no more aligned bytes left in the client. */ + } + + bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); + + bs->nextL2Line = 0; + if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } + + + /* + If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably + means we've just reached the end of the file. We need to move the valid data down to the end of the buffer + and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to + the size of the L1 so we'll need to seek backwards by any misaligned bytes. + */ + alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); + + /* We need to keep track of any unaligned bytes for later use. */ + bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + if (bs->unalignedByteCount > 0) { + bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; + } + + if (alignedL1LineCount > 0) { + size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; + size_t i; + for (i = alignedL1LineCount; i > 0; --i) { + bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; + } + + bs->nextL2Line = (drflac_uint32)offset; + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } else { + /* If we get into this branch it means we weren't able to load any L1-aligned data. */ + bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); + return DRFLAC_FALSE; + } +} + +static drflac_bool32 drflac__reload_cache(drflac_bs* bs) +{ + size_t bytesRead; + +#ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); +#endif + + /* Fast path. Try just moving the next value in the L2 cache to the L1 cache. */ + if (drflac__reload_l1_cache_from_l2(bs)) { + bs->cache = drflac__be2host__cache_line(bs->cache); + bs->consumedBits = 0; +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; +#endif + return DRFLAC_TRUE; + } + + /* Slow path. */ + + /* + If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last + few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the + data from the unaligned cache. + */ + bytesRead = bs->unalignedByteCount; + if (bytesRead == 0) { + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); /* <-- The stream has been exhausted, so marked the bits as consumed. */ + return DRFLAC_FALSE; + } + + DRFLAC_ASSERT(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; + + bs->cache = drflac__be2host__cache_line(bs->unalignedCache); + bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs)); /* <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property. */ + bs->unalignedByteCount = 0; /* <-- At this point the unaligned bytes have been moved into the cache and we thus have no more unaligned bytes. */ + +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache >> bs->consumedBits; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +#endif + return DRFLAC_TRUE; +} + +static void drflac__reset_cache(drflac_bs* bs) +{ + bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); /* <-- This clears the L2 cache. */ + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); /* <-- This clears the L1 cache. */ + bs->cache = 0; + bs->unalignedByteCount = 0; /* <-- This clears the trailing unaligned bytes. */ + bs->unalignedCache = 0; + +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = 0; + bs->crc16CacheIgnoredBytes = 0; +#endif +} + + +static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut) +{ + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResultOut != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 32); + + if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* + If we want to load all 32-bits from a 32-bit cache we need to do it slightly differently because we can't do + a 32-bit shift on a 32-bit integer. This will never be the case on 64-bit caches, so we can have a slightly + more optimal solution for this. + */ +#ifdef DRFLAC_64BIT + *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + bs->consumedBits += bitCount; + bs->cache <<= bitCount; +#else + if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + bs->consumedBits += bitCount; + bs->cache <<= bitCount; + } else { + /* Cannot shift by 32-bits, so need to do it differently. */ + *pResultOut = (drflac_uint32)bs->cache; + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); + bs->cache = 0; + } +#endif + + return DRFLAC_TRUE; + } else { + /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ + drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); + drflac_uint32 bitCountLo = bitCount - bitCountHi; + drflac_uint32 resultHi; + + DRFLAC_ASSERT(bitCountHi > 0); + DRFLAC_ASSERT(bitCountHi < 32); + resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } + + *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + return DRFLAC_TRUE; + } +} + +static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) +{ + drflac_uint32 result; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 32); + + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + /* Do not attempt to shift by 32 as it's undefined. */ + if (bitCount < 32) { + drflac_uint32 signbit; + signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + } + + *pResult = (drflac_int32)result; + return DRFLAC_TRUE; +} + +#ifdef DRFLAC_64BIT +static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) +{ + drflac_uint32 resultHi; + drflac_uint32 resultLo; + + DRFLAC_ASSERT(bitCount <= 64); + DRFLAC_ASSERT(bitCount > 32); + + if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_uint32(bs, 32, &resultLo)) { + return DRFLAC_FALSE; + } + + *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); + return DRFLAC_TRUE; +} +#endif + +/* Function below is unused, but leaving it here in case I need to quickly add it again. */ +#if 0 +static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut) +{ + drflac_uint64 result; + drflac_uint64 signbit; + + DRFLAC_ASSERT(bitCount <= 64); + + if (!drflac__read_uint64(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + + *pResultOut = (drflac_int64)result; + return DRFLAC_TRUE; +} +#endif + +static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult) +{ + drflac_uint32 result; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 16); + + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_uint16)result; + return DRFLAC_TRUE; +} + +#if 0 +static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult) +{ + drflac_int32 result; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 16); + + if (!drflac__read_int32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_int16)result; + return DRFLAC_TRUE; +} +#endif + +static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult) +{ + drflac_uint32 result; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 8); + + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_uint8)result; + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult) +{ + drflac_int32 result; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 8); + + if (!drflac__read_int32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_int8)result; + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) +{ + if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + bs->consumedBits += (drflac_uint32)bitsToSeek; + bs->cache <<= bitsToSeek; + return DRFLAC_TRUE; + } else { + /* It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here. */ + bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bs->cache = 0; + + /* Simple case. Seek in groups of the same number as bits that fit within a cache line. */ +#ifdef DRFLAC_64BIT + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + drflac_uint64 bin; + if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + } +#else + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + drflac_uint32 bin; + if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + } +#endif + + /* Whole leftover bytes. */ + while (bitsToSeek >= 8) { + drflac_uint8 bin; + if (!drflac__read_uint8(bs, 8, &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= 8; + } + + /* Leftover bits. */ + if (bitsToSeek > 0) { + drflac_uint8 bin; + if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek = 0; /* <-- Necessary for the assert below. */ + } + + DRFLAC_ASSERT(bitsToSeek == 0); + return DRFLAC_TRUE; + } +} + + +/* This function moves the bit streamer to the first bit after the sync code (bit 15 of the of the frame header). It will also update the CRC-16. */ +static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) +{ + DRFLAC_ASSERT(bs != NULL); + + /* + The sync code is always aligned to 8 bits. This is convenient for us because it means we can do byte-aligned movements. The first + thing to do is align to the next byte. + */ + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return DRFLAC_FALSE; + } + + for (;;) { + drflac_uint8 hi; + +#ifndef DR_FLAC_NO_CRC + drflac__reset_crc16(bs); +#endif + + if (!drflac__read_uint8(bs, 8, &hi)) { + return DRFLAC_FALSE; + } + + if (hi == 0xFF) { + drflac_uint8 lo; + if (!drflac__read_uint8(bs, 6, &lo)) { + return DRFLAC_FALSE; + } + + if (lo == 0x3E) { + return DRFLAC_TRUE; + } else { + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return DRFLAC_FALSE; + } + } + } + } + + /* Should never get here. */ + /*return DRFLAC_FALSE;*/ +} + + +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) +#define DRFLAC_IMPLEMENT_CLZ_LZCNT +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__) +#define DRFLAC_IMPLEMENT_CLZ_MSVC +#endif +#if defined(__WATCOMC__) && defined(__386__) +#define DRFLAC_IMPLEMENT_CLZ_WATCOM +#endif +#ifdef __MRC__ +#include +#define DRFLAC_IMPLEMENT_CLZ_MRC +#endif + +static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) +{ + drflac_uint32 n; + static drflac_uint32 clz_table_4[] = { + 0, + 4, + 3, 3, + 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1 + }; + + if (x == 0) { + return sizeof(x)*8; + } + + n = clz_table_4[x >> (sizeof(x)*8 - 4)]; + if (n == 0) { +#ifdef DRFLAC_64BIT + if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } + if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } + if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } + if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } +#else + if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } + if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } + if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } +#endif + n += clz_table_4[x >> (sizeof(x)*8 - 4)]; + } + + return n - 1; +} + +#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT +static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) +{ + /* Fast compile time check for ARM. */ +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + return DRFLAC_TRUE; +#elif defined(__MRC__) + return DRFLAC_TRUE; +#else + /* If the compiler itself does not support the intrinsic then we'll need to return false. */ + #ifdef DRFLAC_HAS_LZCNT_INTRINSIC + return drflac__gIsLZCNTSupported; + #else + return DRFLAC_FALSE; + #endif +#endif +} + +static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) +{ + /* + It's critical for competitive decoding performance that this function be highly optimal. With MSVC we can use the __lzcnt64() and __lzcnt() intrinsics + to achieve good performance, however on GCC and Clang it's a little bit more annoying. The __builtin_clzl() and __builtin_clzll() intrinsics leave + it undefined as to the return value when `x` is 0. We need this to be well defined as returning 32 or 64, depending on whether or not it's a 32- or + 64-bit build. To work around this we would need to add a conditional to check for the x = 0 case, but this creates unnecessary inefficiency. To work + around this problem I have written some inline assembly to emit the LZCNT (x86) or CLZ (ARM) instruction directly which removes the need to include + the conditional. This has worked well in the past, but for some reason Clang's MSVC compatible driver, clang-cl, does not seem to be handling this + in the same way as the normal Clang driver. It seems that `clang-cl` is just outputting the wrong results sometimes, maybe due to some register + getting clobbered? + + I'm not sure if this is a bug with dr_flac's inlined assembly (most likely), a bug in `clang-cl` or just a misunderstanding on my part with inline + assembly rules for `clang-cl`. If somebody can identify an error in dr_flac's inlined assembly I'm happy to get that fixed. + + Fortunately there is an easy workaround for this. Clang implements MSVC-specific intrinsics for compatibility. It also defines _MSC_VER for extra + compatibility. We can therefore just check for _MSC_VER and use the MSVC intrinsic which, fortunately for us, Clang supports. It would still be nice + to know how to fix the inlined assembly for correctness sake, however. + */ + +#if defined(_MSC_VER) /*&& !defined(__clang__)*/ /* <-- Intentionally wanting Clang to use the MSVC __lzcnt64/__lzcnt intrinsics due to above ^. */ + #ifdef DRFLAC_64BIT + return (drflac_uint32)__lzcnt64(x); + #else + return (drflac_uint32)__lzcnt(x); + #endif +#else + #if defined(__GNUC__) || defined(__clang__) + #if defined(DRFLAC_X64) + { + drflac_uint64 r; + __asm__ __volatile__ ( + "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" + ); + + return (drflac_uint32)r; + } + #elif defined(DRFLAC_X86) + { + drflac_uint32 r; + __asm__ __volatile__ ( + "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" + ); + + return r; + } + #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(DRFLAC_64BIT) /* <-- I haven't tested 64-bit inline assembly, so only enabling this for the 32-bit build for now. */ + { + unsigned int r; + __asm__ __volatile__ ( + #if defined(DRFLAC_64BIT) + "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ + #else + "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) + #endif + ); + + return r; + } + #else + if (x == 0) { + return sizeof(x)*8; + } + #ifdef DRFLAC_64BIT + return (drflac_uint32)__builtin_clzll((drflac_uint64)x); + #else + return (drflac_uint32)__builtin_clzl((drflac_uint32)x); + #endif + #endif + #else + /* Unsupported compiler. */ + #error "This compiler does not support the lzcnt intrinsic." + #endif +#endif +} +#endif + +#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC +#include /* For BitScanReverse(). */ + +static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) +{ + drflac_uint32 n; + + if (x == 0) { + return sizeof(x)*8; + } + +#ifdef DRFLAC_64BIT + _BitScanReverse64((unsigned long*)&n, x); +#else + _BitScanReverse((unsigned long*)&n, x); +#endif + return sizeof(x)*8 - n - 1; +} +#endif + +#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM +static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); +#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT +/* Use the LZCNT instruction (only available on some processors since the 2010s). */ +#pragma aux drflac__clz_watcom_lzcnt = \ + "db 0F3h, 0Fh, 0BDh, 0C0h" /* lzcnt eax, eax */ \ + parm [eax] \ + value [eax] \ + modify nomemory; +#else +/* Use the 386+-compatible implementation. */ +#pragma aux drflac__clz_watcom = \ + "bsr eax, eax" \ + "xor eax, 31" \ + parm [eax] nomemory \ + value [eax] \ + modify exact [eax] nomemory; +#endif +#endif + +static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) +{ +#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT + if (drflac__is_lzcnt_supported()) { + return drflac__clz_lzcnt(x); + } else +#endif + { +#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC + return drflac__clz_msvc(x); +#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) + return drflac__clz_watcom_lzcnt(x); +#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM) + return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x); +#elif defined(__MRC__) + return __cntlzw(x); +#else + return drflac__clz_software(x); +#endif + } +} + + +static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) +{ + drflac_uint32 zeroCounter = 0; + drflac_uint32 setBitOffsetPlus1; + + while (bs->cache == 0) { + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + if (bs->cache == 1) { + /* Not catching this would lead to undefined behaviour: a shift of a 32-bit number by 32 or more is undefined */ + *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1; + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; + } + + setBitOffsetPlus1 = drflac__clz(bs->cache); + setBitOffsetPlus1 += 1; + + if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } + + bs->consumedBits += setBitOffsetPlus1; + bs->cache <<= setBitOffsetPlus1; + + *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; + return DRFLAC_TRUE; +} + + + +static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart) +{ + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(offsetFromStart > 0); + + /* + Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which + is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit. + To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder. + */ + if (offsetFromStart > 0x7FFFFFFF) { + drflac_uint64 bytesRemaining = offsetFromStart; + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + + while (bytesRemaining > 0x7FFFFFFF) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + } + + if (bytesRemaining > 0) { + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + } else { + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + } + + /* The cache should be reset to force a reload of fresh data from the client. */ + drflac__reset_cache(bs); + return DRFLAC_TRUE; +} + + +static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) +{ + drflac_uint8 crc; + drflac_uint64 result; + drflac_uint8 utf8[7] = {0}; + int byteCount; + int i; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pNumberOut != NULL); + DRFLAC_ASSERT(pCRCOut != NULL); + + crc = *pCRCOut; + + if (!drflac__read_uint8(bs, 8, utf8)) { + *pNumberOut = 0; + return DRFLAC_AT_END; + } + crc = drflac_crc8(crc, utf8[0], 8); + + if ((utf8[0] & 0x80) == 0) { + *pNumberOut = utf8[0]; + *pCRCOut = crc; + return DRFLAC_SUCCESS; + } + + /*byteCount = 1;*/ + if ((utf8[0] & 0xE0) == 0xC0) { + byteCount = 2; + } else if ((utf8[0] & 0xF0) == 0xE0) { + byteCount = 3; + } else if ((utf8[0] & 0xF8) == 0xF0) { + byteCount = 4; + } else if ((utf8[0] & 0xFC) == 0xF8) { + byteCount = 5; + } else if ((utf8[0] & 0xFE) == 0xFC) { + byteCount = 6; + } else if ((utf8[0] & 0xFF) == 0xFE) { + byteCount = 7; + } else { + *pNumberOut = 0; + return DRFLAC_CRC_MISMATCH; /* Bad UTF-8 encoding. */ + } + + /* Read extra bytes. */ + DRFLAC_ASSERT(byteCount > 1); + + result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); + for (i = 1; i < byteCount; ++i) { + if (!drflac__read_uint8(bs, 8, utf8 + i)) { + *pNumberOut = 0; + return DRFLAC_AT_END; + } + crc = drflac_crc8(crc, utf8[i], 8); + + result = (result << 6) | (utf8[i] & 0x3F); + } + + *pNumberOut = result; + *pCRCOut = crc; + return DRFLAC_SUCCESS; +} + + +static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x) +{ +#if 1 /* Needs optimizing. */ + drflac_uint32 result = 0; + while (x > 0) { + result += 1; + x >>= 1; + } + + return result; +#endif +} + +static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision) +{ + /* https://web.archive.org/web/20220205005724/https://github.com/ietf-wg-cellar/flac-specification/blob/37a49aa48ba4ba12e8757badfc59c0df35435fec/rfc_backmatter.md */ + return bitsPerSample + precision + drflac__ilog2_u32(order) > 32; +} + + +/* +The next two functions are responsible for calculating the prediction. + +When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's +safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16. +*/ +#if defined(__clang__) +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +{ + drflac_int32 prediction = 0; + + DRFLAC_ASSERT(order <= 32); + + /* 32-bit version. */ + + /* VC++ optimizes this to a single jmp. I've not yet verified this for other compilers. */ + switch (order) + { + case 32: prediction += coefficients[31] * pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; + } + + return (drflac_int32)(prediction >> shift); +} + +static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +{ + drflac_int64 prediction; + + DRFLAC_ASSERT(order <= 32); + + /* 64-bit version. */ + + /* This method is faster on the 32-bit build when compiling with VC++. See note below. */ +#ifndef DRFLAC_64BIT + if (order == 8) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + } + else if (order == 7) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + } + else if (order == 3) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + } + else if (order == 6) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + } + else if (order == 5) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + } + else if (order == 4) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + } + else if (order == 12) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; + } + else if (order == 2) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + } + else if (order == 1) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + } + else if (order == 10) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + } + else if (order == 9) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + } + else if (order == 11) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + } + else + { + int j; + + prediction = 0; + for (j = 0; j < (int)order; ++j) { + prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; + } + } +#endif + + /* + VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some + reason. The ugly version above is faster so we'll just switch between the two depending on the target platform. + */ +#ifdef DRFLAC_64BIT + prediction = 0; + switch (order) + { + case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1]; + } +#endif + + return (drflac_int32)(prediction >> shift); +} + + +#if 0 +/* +Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the +sake of readability and should only be used as a reference. +*/ +static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_uint32 i; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); + + for (i = 0; i < count; ++i) { + drflac_uint32 zeroCounter = 0; + for (;;) { + drflac_uint8 bit; + if (!drflac__read_uint8(bs, 1, &bit)) { + return DRFLAC_FALSE; + } + + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + + drflac_uint32 decodedRice; + if (riceParam > 0) { + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + return DRFLAC_FALSE; + } + } else { + decodedRice = 0; + } + + decodedRice |= (zeroCounter << riceParam); + if ((decodedRice & 0x01)) { + decodedRice = ~(decodedRice >> 1); + } else { + decodedRice = (decodedRice >> 1); + } + + + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } + } + + return DRFLAC_TRUE; +} +#endif + +#if 0 +static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +{ + drflac_uint32 zeroCounter = 0; + drflac_uint32 decodedRice; + + for (;;) { + drflac_uint8 bit; + if (!drflac__read_uint8(bs, 1, &bit)) { + return DRFLAC_FALSE; + } + + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + + if (riceParam > 0) { + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + return DRFLAC_FALSE; + } + } else { + decodedRice = 0; + } + + *pZeroCounterOut = zeroCounter; + *pRiceParamPartOut = decodedRice; + return DRFLAC_TRUE; +} +#endif + +#if 0 +static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +{ + drflac_cache_t riceParamMask; + drflac_uint32 zeroCounter; + drflac_uint32 setBitOffsetPlus1; + drflac_uint32 riceParamPart; + drflac_uint32 riceLength; + + DRFLAC_ASSERT(riceParam > 0); /* <-- riceParam should never be 0. drflac__read_rice_parts__param_equals_zero() should be used instead for this case. */ + + riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); + + zeroCounter = 0; + while (bs->cache == 0) { + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + setBitOffsetPlus1 = drflac__clz(bs->cache); + zeroCounter += setBitOffsetPlus1; + setBitOffsetPlus1 += 1; + + riceLength = setBitOffsetPlus1 + riceParam; + if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); + + bs->consumedBits += riceLength; + bs->cache <<= riceLength; + } else { + drflac_uint32 bitCountLo; + drflac_cache_t resultHi; + + bs->consumedBits += riceLength; + bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1); /* <-- Equivalent to "if (setBitOffsetPlus1 < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { bs->cache <<= setBitOffsetPlus1; }" */ + + /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ + bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); + resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); /* <-- Use DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE() if ever this function allows riceParam=0. */ + + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { +#ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); +#endif + bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs->consumedBits = 0; +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; +#endif + } else { + /* Slow path. We need to fetch more data from the client. */ + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } + } + + riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); + + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + } + + pZeroCounterOut[0] = zeroCounter; + pRiceParamPartOut[0] = riceParamPart; + + return DRFLAC_TRUE; +} +#endif + +static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +{ + drflac_uint32 riceParamPlus1 = riceParam + 1; + /*drflac_cache_t riceParamPlus1Mask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParamPlus1);*/ + drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); + drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + + /* + The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have + no idea how this will work in practice... + */ + drflac_cache_t bs_cache = bs->cache; + drflac_uint32 bs_consumedBits = bs->consumedBits; + + /* The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line. */ + drflac_uint32 lzcount = drflac__clz(bs_cache); + if (lzcount < sizeof(bs_cache)*8) { + pZeroCounterOut[0] = lzcount; + + /* + It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting + this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled + outside of this function at a higher level. + */ + extract_rice_param_part: + bs_cache <<= lzcount; + bs_consumedBits += lzcount; + + if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { + /* Getting here means the rice parameter part is wholly contained within the current cache line. */ + pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); + bs_cache <<= riceParamPlus1; + bs_consumedBits += riceParamPlus1; + } else { + drflac_uint32 riceParamPartHi; + drflac_uint32 riceParamPartLo; + drflac_uint32 riceParamPartLoBitCount; + + /* + Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache + line, reload the cache, and then combine it with the head of the next cache line. + */ + + /* Grab the high part of the rice parameter part. */ + riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); + + /* Before reloading the cache we need to grab the size in bits of the low part. */ + riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; + DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + + /* Now reload the cache. */ + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); + #endif + bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = riceParamPartLoBitCount; + #ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + /* Slow path. We need to fetch more data from the client. */ + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } + + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; + } + + /* We should now have enough information to construct the rice parameter part. */ + riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); + pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; + + bs_cache <<= riceParamPartLoBitCount; + } + } else { + /* + Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call + to drflac__clz() and we need to reload the cache. + */ + drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); + for (;;) { + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); + #endif + bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = 0; + #ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + /* Slow path. We need to fetch more data from the client. */ + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits; + } + + lzcount = drflac__clz(bs_cache); + zeroCounter += lzcount; + + if (lzcount < sizeof(bs_cache)*8) { + break; + } + } + + pZeroCounterOut[0] = zeroCounter; + goto extract_rice_param_part; + } + + /* Make sure the cache is restored at the end of it all. */ + bs->cache = bs_cache; + bs->consumedBits = bs_consumedBits; + + return DRFLAC_TRUE; +} + +static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam) +{ + drflac_uint32 riceParamPlus1 = riceParam + 1; + drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + + /* + The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have + no idea how this will work in practice... + */ + drflac_cache_t bs_cache = bs->cache; + drflac_uint32 bs_consumedBits = bs->consumedBits; + + /* The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line. */ + drflac_uint32 lzcount = drflac__clz(bs_cache); + if (lzcount < sizeof(bs_cache)*8) { + /* + It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting + this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled + outside of this function at a higher level. + */ + extract_rice_param_part: + bs_cache <<= lzcount; + bs_consumedBits += lzcount; + + if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { + /* Getting here means the rice parameter part is wholly contained within the current cache line. */ + bs_cache <<= riceParamPlus1; + bs_consumedBits += riceParamPlus1; + } else { + /* + Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache + line, reload the cache, and then combine it with the head of the next cache line. + */ + + /* Before reloading the cache we need to grab the size in bits of the low part. */ + drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; + DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + + /* Now reload the cache. */ + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); + #endif + bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = riceParamPartLoBitCount; + #ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + /* Slow path. We need to fetch more data from the client. */ + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + + if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } + + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; + } + + bs_cache <<= riceParamPartLoBitCount; + } + } else { + /* + Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call + to drflac__clz() and we need to reload the cache. + */ + for (;;) { + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); + #endif + bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = 0; + #ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + /* Slow path. We need to fetch more data from the client. */ + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits; + } + + lzcount = drflac__clz(bs_cache); + if (lzcount < sizeof(bs_cache)*8) { + break; + } + } + + goto extract_rice_param_part; + } + + /* Make sure the cache is restored at the end of it all. */ + bs->cache = bs_cache; + bs->consumedBits = bs_consumedBits; + + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorder(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + drflac_uint32 zeroCountPart0; + drflac_uint32 riceParamPart0; + drflac_uint32 riceParamMask; + drflac_uint32 i; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); + + (void)bitsPerSample; + (void)order; + (void)shift; + (void)coefficients; + + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + + i = 0; + while (i < count) { + /* Rice extraction. */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return DRFLAC_FALSE; + } + + /* Rice reconstruction. */ + riceParamPart0 &= riceParamMask; + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + + pSamplesOut[i] = riceParamPart0; + + i += 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + drflac_uint32 zeroCountPart0 = 0; + drflac_uint32 zeroCountPart1 = 0; + drflac_uint32 zeroCountPart2 = 0; + drflac_uint32 zeroCountPart3 = 0; + drflac_uint32 riceParamPart0 = 0; + drflac_uint32 riceParamPart1 = 0; + drflac_uint32 riceParamPart2 = 0; + drflac_uint32 riceParamPart3 = 0; + drflac_uint32 riceParamMask; + const drflac_int32* pSamplesOutEnd; + drflac_uint32 i; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); + + if (lpcOrder == 0) { + return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } + + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + pSamplesOutEnd = pSamplesOut + (count & ~3); + + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + while (pSamplesOut < pSamplesOutEnd) { + /* + Rice extraction. It's faster to do this one at a time against local variables than it is to use the x4 version + against an array. Not sure why, but perhaps it's making more efficient use of registers? + */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return DRFLAC_FALSE; + } + + riceParamPart0 &= riceParamMask; + riceParamPart1 &= riceParamMask; + riceParamPart2 &= riceParamMask; + riceParamPart3 &= riceParamMask; + + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart1 |= (zeroCountPart1 << riceParam); + riceParamPart2 |= (zeroCountPart2 << riceParam); + riceParamPart3 |= (zeroCountPart3 << riceParam); + + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; + riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; + riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; + + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + + pSamplesOut += 4; + } + } else { + while (pSamplesOut < pSamplesOutEnd) { + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return DRFLAC_FALSE; + } + + riceParamPart0 &= riceParamMask; + riceParamPart1 &= riceParamMask; + riceParamPart2 &= riceParamMask; + riceParamPart3 &= riceParamMask; + + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart1 |= (zeroCountPart1 << riceParam); + riceParamPart2 |= (zeroCountPart2 << riceParam); + riceParamPart3 |= (zeroCountPart3 << riceParam); + + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; + riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; + riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; + + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + + pSamplesOut += 4; + } + } + + i = (count & ~3); + while (i < count) { + /* Rice extraction. */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return DRFLAC_FALSE; + } + + /* Rice reconstruction. */ + riceParamPart0 &= riceParamMask; + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + /*riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1);*/ + + /* Sample reconstruction. */ + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + } else { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + } + + i += 1; + pSamplesOut += 1; + } + + return DRFLAC_TRUE; +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b) +{ + __m128i r; + + /* Pack. */ + r = _mm_packs_epi32(a, b); + + /* a3a2 a1a0 b3b2 b1b0 -> a3a2 b3b2 a1a0 b1b0 */ + r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0)); + + /* a3a2 b3b2 a1a0 b1b0 -> a3b3 a2b2 a1b1 a0b0 */ + r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); + r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); + + return r; +} +#endif + +#if defined(DRFLAC_SUPPORT_SSE41) +static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a) +{ + return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); +} + +static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x) +{ + __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); + __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); + return _mm_add_epi32(x64, x32); +} + +static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x) +{ + return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); +} + +static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count) +{ + /* + To simplify this we are assuming count < 32. This restriction allows us to work on a low side and a high side. The low side + is shifted with zero bits, whereas the right side is shifted with sign bits. + */ + __m128i lo = _mm_srli_epi64(x, count); + __m128i hi = _mm_srai_epi32(x, count); + + hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); /* The high part needs to have the low part cleared. */ + + return _mm_or_si128(lo, hi); +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + int i; + drflac_uint32 riceParamMask; + drflac_int32* pDecodedSamples = pSamplesOut; + drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + drflac_uint32 zeroCountParts0 = 0; + drflac_uint32 zeroCountParts1 = 0; + drflac_uint32 zeroCountParts2 = 0; + drflac_uint32 zeroCountParts3 = 0; + drflac_uint32 riceParamParts0 = 0; + drflac_uint32 riceParamParts1 = 0; + drflac_uint32 riceParamParts2 = 0; + drflac_uint32 riceParamParts3 = 0; + __m128i coefficients128_0; + __m128i coefficients128_4; + __m128i coefficients128_8; + __m128i samples128_0; + __m128i samples128_4; + __m128i samples128_8; + __m128i riceParamMask128; + + const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + riceParamMask128 = _mm_set1_epi32(riceParamMask); + + /* Pre-load. */ + coefficients128_0 = _mm_setzero_si128(); + coefficients128_4 = _mm_setzero_si128(); + coefficients128_8 = _mm_setzero_si128(); + + samples128_0 = _mm_setzero_si128(); + samples128_4 = _mm_setzero_si128(); + samples128_8 = _mm_setzero_si128(); + + /* + Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than + what's available in the input buffers. It would be convenient to use a fall-through switch to do this, but this results + in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted + so I think there's opportunity for this to be simplified. + */ +#if 1 + { + int runningOrder = order; + + /* 0 - 3. */ + if (runningOrder >= 4) { + coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); + samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; + case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; + case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; + } + runningOrder = 0; + } + + /* 4 - 7 */ + if (runningOrder >= 4) { + coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); + samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; + case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; + case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; + } + runningOrder = 0; + } + + /* 8 - 11 */ + if (runningOrder == 4) { + coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); + samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; + case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; + case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; + } + runningOrder = 0; + } + + /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ + coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); + } +#else + /* This causes strict-aliasing warnings with GCC. */ + switch (order) + { + case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + } +#endif + + /* For this version we are doing one sample at a time. */ + while (pDecodedSamples < pDecodedSamplesEnd) { + __m128i prediction128; + __m128i zeroCountPart128; + __m128i riceParamPart128; + + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return DRFLAC_FALSE; + } + + zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); + riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); + + riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); + riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); /* <-- SSE2 compatible */ + /*riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_mullo_epi32(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01)), _mm_set1_epi32(0xFFFFFFFF)));*/ /* <-- Only supported from SSE4.1 and is slower in my testing... */ + + if (order <= 4) { + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); + + /* Horizontal add and shift. */ + prediction128 = drflac__mm_hadd_epi32(prediction128); + prediction128 = _mm_srai_epi32(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + } else if (order <= 8) { + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); + prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); + + /* Horizontal add and shift. */ + prediction128 = drflac__mm_hadd_epi32(prediction128); + prediction128 = _mm_srai_epi32(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + + samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + } else { + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); + prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); + prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); + + /* Horizontal add and shift. */ + prediction128 = drflac__mm_hadd_epi32(prediction128); + prediction128 = _mm_srai_epi32(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + + samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); + samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + } + + /* We store samples in groups of 4. */ + _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + + /* Make sure we process the last few samples. */ + i = (count & ~3); + while (i < (int)count) { + /* Rice extraction. */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return DRFLAC_FALSE; + } + + /* Rice reconstruction. */ + riceParamParts0 &= riceParamMask; + riceParamParts0 |= (zeroCountParts0 << riceParam); + riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; + + /* Sample reconstruction. */ + pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + + i += 1; + pDecodedSamples += 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + int i; + drflac_uint32 riceParamMask; + drflac_int32* pDecodedSamples = pSamplesOut; + drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + drflac_uint32 zeroCountParts0 = 0; + drflac_uint32 zeroCountParts1 = 0; + drflac_uint32 zeroCountParts2 = 0; + drflac_uint32 zeroCountParts3 = 0; + drflac_uint32 riceParamParts0 = 0; + drflac_uint32 riceParamParts1 = 0; + drflac_uint32 riceParamParts2 = 0; + drflac_uint32 riceParamParts3 = 0; + __m128i coefficients128_0; + __m128i coefficients128_4; + __m128i coefficients128_8; + __m128i samples128_0; + __m128i samples128_4; + __m128i samples128_8; + __m128i prediction128; + __m128i riceParamMask128; + + const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + + DRFLAC_ASSERT(order <= 12); + + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + riceParamMask128 = _mm_set1_epi32(riceParamMask); + + prediction128 = _mm_setzero_si128(); + + /* Pre-load. */ + coefficients128_0 = _mm_setzero_si128(); + coefficients128_4 = _mm_setzero_si128(); + coefficients128_8 = _mm_setzero_si128(); + + samples128_0 = _mm_setzero_si128(); + samples128_4 = _mm_setzero_si128(); + samples128_8 = _mm_setzero_si128(); + +#if 1 + { + int runningOrder = order; + + /* 0 - 3. */ + if (runningOrder >= 4) { + coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); + samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; + case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; + case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; + } + runningOrder = 0; + } + + /* 4 - 7 */ + if (runningOrder >= 4) { + coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); + samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; + case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; + case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; + } + runningOrder = 0; + } + + /* 8 - 11 */ + if (runningOrder == 4) { + coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); + samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; + case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; + case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; + } + runningOrder = 0; + } + + /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ + coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); + } +#else + switch (order) + { + case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + } +#endif + + /* For this version we are doing one sample at a time. */ + while (pDecodedSamples < pDecodedSamplesEnd) { + __m128i zeroCountPart128; + __m128i riceParamPart128; + + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return DRFLAC_FALSE; + } + + zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); + riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); + + riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); + riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); + + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_xor_si128(prediction128, prediction128); /* Reset to 0. */ + + switch (order) + { + case 12: + case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0)))); + case 10: + case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2)))); + case 8: + case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0)))); + case 6: + case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2)))); + case 4: + case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0)))); + case 2: + case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); + } + + /* Horizontal add and shift. */ + prediction128 = drflac__mm_hadd_epi64(prediction128); + prediction128 = drflac__mm_srai_epi64(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + + /* Our value should be sitting in prediction128[0]. We need to combine this with our SSE samples. */ + samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); + samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + + /* Slide our rice parameter down so that the value in position 0 contains the next one to process. */ + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + + /* We store samples in groups of 4. */ + _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + + /* Make sure we process the last few samples. */ + i = (count & ~3); + while (i < (int)count) { + /* Rice extraction. */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return DRFLAC_FALSE; + } + + /* Rice reconstruction. */ + riceParamParts0 &= riceParamMask; + riceParamParts0 |= (zeroCountParts0 << riceParam); + riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; + + /* Sample reconstruction. */ + pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + + i += 1; + pDecodedSamples += 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); + + /* In my testing the order is rarely > 12, so in this case I'm going to simplify the SSE implementation by only handling order <= 12. */ + if (lpcOrder > 0 && lpcOrder <= 12) { + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } else { + return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } + } else { + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x) +{ + vst1q_s32(p+0, x.val[0]); + vst1q_s32(p+4, x.val[1]); +} + +static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x) +{ + vst1q_u32(p+0, x.val[0]); + vst1q_u32(p+4, x.val[1]); +} + +static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x) +{ + vst1q_f32(p+0, x.val[0]); + vst1q_f32(p+4, x.val[1]); +} + +static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x) +{ + vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); +} + +static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x) +{ + vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); +} + +static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0) +{ + drflac_int32 x[4]; + x[3] = x3; + x[2] = x2; + x[1] = x1; + x[0] = x0; + return vld1q_s32(x); +} + +static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b) +{ + /* Equivalent to SSE's _mm_alignr_epi8(a, b, 4) */ + + /* Reference */ + /*return drflac__vdupq_n_s32x4( + vgetq_lane_s32(a, 0), + vgetq_lane_s32(b, 3), + vgetq_lane_s32(b, 2), + vgetq_lane_s32(b, 1) + );*/ + + return vextq_s32(b, a, 1); +} + +static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) +{ + /* Equivalent to SSE's _mm_alignr_epi8(a, b, 4) */ + + /* Reference */ + /*return drflac__vdupq_n_s32x4( + vgetq_lane_s32(a, 0), + vgetq_lane_s32(b, 3), + vgetq_lane_s32(b, 2), + vgetq_lane_s32(b, 1) + );*/ + + return vextq_u32(b, a, 1); +} + +static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x) +{ + /* The sum must end up in position 0. */ + + /* Reference */ + /*return vdupq_n_s32( + vgetq_lane_s32(x, 3) + + vgetq_lane_s32(x, 2) + + vgetq_lane_s32(x, 1) + + vgetq_lane_s32(x, 0) + );*/ + + int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); + return vpadd_s32(r, r); +} + +static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x) +{ + return vadd_s64(vget_high_s64(x), vget_low_s64(x)); +} + +static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x) +{ + /* Reference */ + /*return drflac__vdupq_n_s32x4( + vgetq_lane_s32(x, 0), + vgetq_lane_s32(x, 1), + vgetq_lane_s32(x, 2), + vgetq_lane_s32(x, 3) + );*/ + + return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); +} + +static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x) +{ + return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); +} + +static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x) +{ + return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + int i; + drflac_uint32 riceParamMask; + drflac_int32* pDecodedSamples = pSamplesOut; + drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + drflac_uint32 zeroCountParts[4]; + drflac_uint32 riceParamParts[4]; + int32x4_t coefficients128_0; + int32x4_t coefficients128_4; + int32x4_t coefficients128_8; + int32x4_t samples128_0; + int32x4_t samples128_4; + int32x4_t samples128_8; + uint32x4_t riceParamMask128; + int32x4_t riceParam128; + int32x2_t shift64; + uint32x4_t one128; + + const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + riceParamMask128 = vdupq_n_u32(riceParamMask); + + riceParam128 = vdupq_n_s32(riceParam); + shift64 = vdup_n_s32(-shift); /* Negate the shift because we'll be doing a variable shift using vshlq_s32(). */ + one128 = vdupq_n_u32(1); + + /* + Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than + what's available in the input buffers. It would be conenient to use a fall-through switch to do this, but this results + in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted + so I think there's opportunity for this to be simplified. + */ + { + int runningOrder = order; + drflac_int32 tempC[4] = {0, 0, 0, 0}; + drflac_int32 tempS[4] = {0, 0, 0, 0}; + + /* 0 - 3. */ + if (runningOrder >= 4) { + coefficients128_0 = vld1q_s32(coefficients + 0); + samples128_0 = vld1q_s32(pSamplesOut - 4); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; /* fallthrough */ + case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; /* fallthrough */ + case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; /* fallthrough */ + } + + coefficients128_0 = vld1q_s32(tempC); + samples128_0 = vld1q_s32(tempS); + runningOrder = 0; + } + + /* 4 - 7 */ + if (runningOrder >= 4) { + coefficients128_4 = vld1q_s32(coefficients + 4); + samples128_4 = vld1q_s32(pSamplesOut - 8); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; /* fallthrough */ + case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; /* fallthrough */ + case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; /* fallthrough */ + } + + coefficients128_4 = vld1q_s32(tempC); + samples128_4 = vld1q_s32(tempS); + runningOrder = 0; + } + + /* 8 - 11 */ + if (runningOrder == 4) { + coefficients128_8 = vld1q_s32(coefficients + 8); + samples128_8 = vld1q_s32(pSamplesOut - 12); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; /* fallthrough */ + case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; /* fallthrough */ + case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; /* fallthrough */ + } + + coefficients128_8 = vld1q_s32(tempC); + samples128_8 = vld1q_s32(tempS); + runningOrder = 0; + } + + /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ + coefficients128_0 = drflac__vrevq_s32(coefficients128_0); + coefficients128_4 = drflac__vrevq_s32(coefficients128_4); + coefficients128_8 = drflac__vrevq_s32(coefficients128_8); + } + + /* For this version we are doing one sample at a time. */ + while (pDecodedSamples < pDecodedSamplesEnd) { + int32x4_t prediction128; + int32x2_t prediction64; + uint32x4_t zeroCountPart128; + uint32x4_t riceParamPart128; + + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return DRFLAC_FALSE; + } + + zeroCountPart128 = vld1q_u32(zeroCountParts); + riceParamPart128 = vld1q_u32(riceParamParts); + + riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); + riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + + if (order <= 4) { + for (i = 0; i < 4; i += 1) { + prediction128 = vmulq_s32(coefficients128_0, samples128_0); + + /* Horizontal add and shift. */ + prediction64 = drflac__vhaddq_s32(prediction128); + prediction64 = vshl_s32(prediction64, shift64); + prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); + + samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + } else if (order <= 8) { + for (i = 0; i < 4; i += 1) { + prediction128 = vmulq_s32(coefficients128_4, samples128_4); + prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); + + /* Horizontal add and shift. */ + prediction64 = drflac__vhaddq_s32(prediction128); + prediction64 = vshl_s32(prediction64, shift64); + prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); + + samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + } else { + for (i = 0; i < 4; i += 1) { + prediction128 = vmulq_s32(coefficients128_8, samples128_8); + prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); + prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); + + /* Horizontal add and shift. */ + prediction64 = drflac__vhaddq_s32(prediction128); + prediction64 = vshl_s32(prediction64, shift64); + prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); + + samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + } + + /* We store samples in groups of 4. */ + vst1q_s32(pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + + /* Make sure we process the last few samples. */ + i = (count & ~3); + while (i < (int)count) { + /* Rice extraction. */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return DRFLAC_FALSE; + } + + /* Rice reconstruction. */ + riceParamParts[0] &= riceParamMask; + riceParamParts[0] |= (zeroCountParts[0] << riceParam); + riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; + + /* Sample reconstruction. */ + pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + + i += 1; + pDecodedSamples += 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + int i; + drflac_uint32 riceParamMask; + drflac_int32* pDecodedSamples = pSamplesOut; + drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + drflac_uint32 zeroCountParts[4]; + drflac_uint32 riceParamParts[4]; + int32x4_t coefficients128_0; + int32x4_t coefficients128_4; + int32x4_t coefficients128_8; + int32x4_t samples128_0; + int32x4_t samples128_4; + int32x4_t samples128_8; + uint32x4_t riceParamMask128; + int32x4_t riceParam128; + int64x1_t shift64; + uint32x4_t one128; + int64x2_t prediction128 = { 0 }; + uint32x4_t zeroCountPart128; + uint32x4_t riceParamPart128; + + const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + riceParamMask128 = vdupq_n_u32(riceParamMask); + + riceParam128 = vdupq_n_s32(riceParam); + shift64 = vdup_n_s64(-shift); /* Negate the shift because we'll be doing a variable shift using vshlq_s32(). */ + one128 = vdupq_n_u32(1); + + /* + Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than + what's available in the input buffers. It would be convenient to use a fall-through switch to do this, but this results + in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted + so I think there's opportunity for this to be simplified. + */ + { + int runningOrder = order; + drflac_int32 tempC[4] = {0, 0, 0, 0}; + drflac_int32 tempS[4] = {0, 0, 0, 0}; + + /* 0 - 3. */ + if (runningOrder >= 4) { + coefficients128_0 = vld1q_s32(coefficients + 0); + samples128_0 = vld1q_s32(pSamplesOut - 4); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; /* fallthrough */ + case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; /* fallthrough */ + case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; /* fallthrough */ + } + + coefficients128_0 = vld1q_s32(tempC); + samples128_0 = vld1q_s32(tempS); + runningOrder = 0; + } + + /* 4 - 7 */ + if (runningOrder >= 4) { + coefficients128_4 = vld1q_s32(coefficients + 4); + samples128_4 = vld1q_s32(pSamplesOut - 8); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; /* fallthrough */ + case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; /* fallthrough */ + case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; /* fallthrough */ + } + + coefficients128_4 = vld1q_s32(tempC); + samples128_4 = vld1q_s32(tempS); + runningOrder = 0; + } + + /* 8 - 11 */ + if (runningOrder == 4) { + coefficients128_8 = vld1q_s32(coefficients + 8); + samples128_8 = vld1q_s32(pSamplesOut - 12); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; /* fallthrough */ + case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; /* fallthrough */ + case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; /* fallthrough */ + } + + coefficients128_8 = vld1q_s32(tempC); + samples128_8 = vld1q_s32(tempS); + runningOrder = 0; + } + + /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ + coefficients128_0 = drflac__vrevq_s32(coefficients128_0); + coefficients128_4 = drflac__vrevq_s32(coefficients128_4); + coefficients128_8 = drflac__vrevq_s32(coefficients128_8); + } + + /* For this version we are doing one sample at a time. */ + while (pDecodedSamples < pDecodedSamplesEnd) { + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return DRFLAC_FALSE; + } + + zeroCountPart128 = vld1q_u32(zeroCountParts); + riceParamPart128 = vld1q_u32(riceParamParts); + + riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); + riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + + for (i = 0; i < 4; i += 1) { + int64x1_t prediction64; + + prediction128 = veorq_s64(prediction128, prediction128); /* Reset to 0. */ + switch (order) + { + case 12: + case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8))); + case 10: + case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8))); + case 8: + case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4))); + case 6: + case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4))); + case 4: + case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0))); + case 2: + case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); + } + + /* Horizontal add and shift. */ + prediction64 = drflac__vhaddq_s64(prediction128); + prediction64 = vshl_s64(prediction64, shift64); + prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); + + /* Our value should be sitting in prediction64[0]. We need to combine this with our SSE samples. */ + samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); + + /* Slide our rice parameter down so that the value in position 0 contains the next one to process. */ + riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + + /* We store samples in groups of 4. */ + vst1q_s32(pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + + /* Make sure we process the last few samples. */ + i = (count & ~3); + while (i < (int)count) { + /* Rice extraction. */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return DRFLAC_FALSE; + } + + /* Rice reconstruction. */ + riceParamParts[0] &= riceParamMask; + riceParamParts[0] |= (zeroCountParts[0] << riceParam); + riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; + + /* Sample reconstruction. */ + pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + + i += 1; + pDecodedSamples += 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); + + /* In my testing the order is rarely > 12, so in this case I'm going to simplify the NEON implementation by only handling order <= 12. */ + if (lpcOrder > 0 && lpcOrder <= 12) { + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } else { + return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } + } else { + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } +} +#endif + +static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ +#if defined(DRFLAC_SUPPORT_SSE41) + if (drflac__gIsSSE41Supported) { + return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported) { + return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } else +#endif + { + /* Scalar fallback. */ + #if 0 + return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + #else + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + #endif + } +} + +/* Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes. */ +static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam) +{ + drflac_uint32 i; + + DRFLAC_ASSERT(bs != NULL); + + for (i = 0; i < count; ++i) { + if (!drflac__seek_rice_parts(bs, riceParam)) { + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; +} + +#if defined(__clang__) +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_uint32 i; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(unencodedBitsPerSample <= 31); /* <-- unencodedBitsPerSample is a 5 bit number, so cannot exceed 31. */ + DRFLAC_ASSERT(pSamplesOut != NULL); + + for (i = 0; i < count; ++i) { + if (unencodedBitsPerSample > 0) { + if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { + return DRFLAC_FALSE; + } + } else { + pSamplesOut[i] = 0; + } + + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } + } + + return DRFLAC_TRUE; +} + + +/* +Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called +when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be ignored. The + and parameters are used to determine how many residual values need to be decoded. +*/ +static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +{ + drflac_uint8 residualMethod; + drflac_uint8 partitionOrder; + drflac_uint32 samplesInPartition; + drflac_uint32 partitionsRemaining; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(blockSize != 0); + DRFLAC_ASSERT(pDecodedSamples != NULL); /* <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode? */ + + if (!drflac__read_uint8(bs, 2, &residualMethod)) { + return DRFLAC_FALSE; + } + + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return DRFLAC_FALSE; /* Unknown or unsupported residual coding method. */ + } + + /* Ignore the first values. */ + pDecodedSamples += lpcOrder; + + if (!drflac__read_uint8(bs, 4, &partitionOrder)) { + return DRFLAC_FALSE; + } + + /* + From the FLAC spec: + The Rice partition order in a Rice-coded residual section must be less than or equal to 8. + */ + if (partitionOrder > 8) { + return DRFLAC_FALSE; + } + + /* Validation check. */ + if ((blockSize / (1 << partitionOrder)) < lpcOrder) { + return DRFLAC_FALSE; + } + + samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; + partitionsRemaining = (1 << partitionOrder); + for (;;) { + drflac_uint8 riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!drflac__read_uint8(bs, 4, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 15) { + riceParam = 0xFF; + } + } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!drflac__read_uint8(bs, 5, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 31) { + riceParam = 0xFF; + } + } + + if (riceParam != 0xFF) { + if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; + } + } else { + drflac_uint8 unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return DRFLAC_FALSE; + } + + if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; + } + } + + pDecodedSamples += samplesInPartition; + + if (partitionsRemaining == 1) { + break; + } + + partitionsRemaining -= 1; + + if (partitionOrder != 0) { + samplesInPartition = blockSize / (1 << partitionOrder); + } + } + + return DRFLAC_TRUE; +} + +/* +Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called +when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be set to 0. The + and parameters are used to determine how many residual values need to be decoded. +*/ +static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) +{ + drflac_uint8 residualMethod; + drflac_uint8 partitionOrder; + drflac_uint32 samplesInPartition; + drflac_uint32 partitionsRemaining; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(blockSize != 0); + + if (!drflac__read_uint8(bs, 2, &residualMethod)) { + return DRFLAC_FALSE; + } + + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return DRFLAC_FALSE; /* Unknown or unsupported residual coding method. */ + } + + if (!drflac__read_uint8(bs, 4, &partitionOrder)) { + return DRFLAC_FALSE; + } + + /* + From the FLAC spec: + The Rice partition order in a Rice-coded residual section must be less than or equal to 8. + */ + if (partitionOrder > 8) { + return DRFLAC_FALSE; + } + + /* Validation check. */ + if ((blockSize / (1 << partitionOrder)) <= order) { + return DRFLAC_FALSE; + } + + samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + partitionsRemaining = (1 << partitionOrder); + for (;;) + { + drflac_uint8 riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!drflac__read_uint8(bs, 4, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 15) { + riceParam = 0xFF; + } + } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!drflac__read_uint8(bs, 5, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 31) { + riceParam = 0xFF; + } + } + + if (riceParam != 0xFF) { + if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { + return DRFLAC_FALSE; + } + } else { + drflac_uint8 unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return DRFLAC_FALSE; + } + + if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { + return DRFLAC_FALSE; + } + } + + + if (partitionsRemaining == 1) { + break; + } + + partitionsRemaining -= 1; + samplesInPartition = blockSize / (1 << partitionOrder); + } + + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) +{ + drflac_uint32 i; + + /* Only a single sample needs to be decoded here. */ + drflac_int32 sample; + if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + /* + We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely) + we'll want to look at a more efficient way. + */ + for (i = 0; i < blockSize; ++i) { + pDecodedSamples[i] = sample; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) +{ + drflac_uint32 i; + + for (i = 0; i < blockSize; ++i) { + drflac_int32 sample; + if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + pDecodedSamples[i] = sample; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) +{ + drflac_uint32 i; + + static drflac_int32 lpcCoefficientsTable[5][4] = { + {0, 0, 0, 0}, + {1, 0, 0, 0}, + {2, -1, 0, 0}, + {3, -3, 1, 0}, + {4, -6, 4, -1} + }; + + /* Warm up samples and coefficients. */ + for (i = 0; i < lpcOrder; ++i) { + drflac_int32 sample; + if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + pDecodedSamples[i] = sample; + } + + if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { + return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) +{ + drflac_uint8 i; + drflac_uint8 lpcPrecision; + drflac_int8 lpcShift; + drflac_int32 coefficients[32]; + + /* Warm up samples. */ + for (i = 0; i < lpcOrder; ++i) { + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + pDecodedSamples[i] = sample; + } + + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + return DRFLAC_FALSE; + } + if (lpcPrecision == 15) { + return DRFLAC_FALSE; /* Invalid. */ + } + lpcPrecision += 1; + + if (!drflac__read_int8(bs, 5, &lpcShift)) { + return DRFLAC_FALSE; + } + + /* + From the FLAC specification: + + Quantized linear predictor coefficient shift needed in bits (NOTE: this number is signed two's-complement) + + Emphasis on the "signed two's-complement". In practice there does not seem to be any encoders nor decoders supporting negative shifts. For now dr_flac is + not going to support negative shifts as I don't have any reference files. However, when a reference file comes through I will consider adding support. + */ + if (lpcShift < 0) { + return DRFLAC_FALSE; + } + + DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); + for (i = 0; i < lpcOrder; ++i) { + if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) { + return DRFLAC_FALSE; + } + } + + if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header) +{ + const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; + const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; /* -1 = reserved. */ + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(header != NULL); + + /* Keep looping until we find a valid sync code. */ + for (;;) { + drflac_uint8 crc8 = 0xCE; /* 0xCE = drflac_crc8(0, 0x3FFE, 14); */ + drflac_uint8 reserved = 0; + drflac_uint8 blockingStrategy = 0; + drflac_uint8 blockSize = 0; + drflac_uint8 sampleRate = 0; + drflac_uint8 channelAssignment = 0; + drflac_uint8 bitsPerSample = 0; + drflac_bool32 isVariableBlockSize; + + if (!drflac__find_and_seek_to_next_sync_code(bs)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DRFLAC_FALSE; + } + if (reserved == 1) { + continue; + } + crc8 = drflac_crc8(crc8, reserved, 1); + + if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, blockingStrategy, 1); + + if (!drflac__read_uint8(bs, 4, &blockSize)) { + return DRFLAC_FALSE; + } + if (blockSize == 0) { + continue; + } + crc8 = drflac_crc8(crc8, blockSize, 4); + + if (!drflac__read_uint8(bs, 4, &sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, sampleRate, 4); + + if (!drflac__read_uint8(bs, 4, &channelAssignment)) { + return DRFLAC_FALSE; + } + if (channelAssignment > 10) { + continue; + } + crc8 = drflac_crc8(crc8, channelAssignment, 4); + + if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { + return DRFLAC_FALSE; + } + if (bitsPerSample == 3 || bitsPerSample == 7) { + continue; + } + crc8 = drflac_crc8(crc8, bitsPerSample, 3); + + + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DRFLAC_FALSE; + } + if (reserved == 1) { + continue; + } + crc8 = drflac_crc8(crc8, reserved, 1); + + + isVariableBlockSize = blockingStrategy == 1; + if (isVariableBlockSize) { + drflac_uint64 pcmFrameNumber; + drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_AT_END) { + return DRFLAC_FALSE; + } else { + continue; + } + } + header->flacFrameNumber = 0; + header->pcmFrameNumber = pcmFrameNumber; + } else { + drflac_uint64 flacFrameNumber = 0; + drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_AT_END) { + return DRFLAC_FALSE; + } else { + continue; + } + } + header->flacFrameNumber = (drflac_uint32)flacFrameNumber; /* <-- Safe cast. */ + header->pcmFrameNumber = 0; + } + + + DRFLAC_ASSERT(blockSize > 0); + if (blockSize == 1) { + header->blockSizeInPCMFrames = 192; + } else if (blockSize <= 5) { + DRFLAC_ASSERT(blockSize >= 2); + header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); + } else if (blockSize == 6) { + if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8); + header->blockSizeInPCMFrames += 1; + } else if (blockSize == 7) { + if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16); + if (header->blockSizeInPCMFrames == 0xFFFF) { + return DRFLAC_FALSE; /* Frame is too big. This is the size of the frame minus 1. The STREAMINFO block defines the max block size which is 16-bits. Adding one will make it 17 bits and therefore too big. */ + } + header->blockSizeInPCMFrames += 1; + } else { + DRFLAC_ASSERT(blockSize >= 8); + header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); + } + + + if (sampleRate <= 11) { + header->sampleRate = sampleRateTable[sampleRate]; + } else if (sampleRate == 12) { + if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 8); + header->sampleRate *= 1000; + } else if (sampleRate == 13) { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 16); + } else if (sampleRate == 14) { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 16); + header->sampleRate *= 10; + } else { + continue; /* Invalid. Assume an invalid block. */ + } + + + header->channelAssignment = channelAssignment; + + header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; + if (header->bitsPerSample == 0) { + header->bitsPerSample = streaminfoBitsPerSample; + } + + if (header->bitsPerSample != streaminfoBitsPerSample) { + /* If this subframe has a different bitsPerSample then streaminfo or the first frame, reject it */ + return DRFLAC_FALSE; + } + + if (!drflac__read_uint8(bs, 8, &header->crc8)) { + return DRFLAC_FALSE; + } + +#ifndef DR_FLAC_NO_CRC + if (header->crc8 != crc8) { + continue; /* CRC mismatch. Loop back to the top and find the next sync code. */ + } +#endif + return DRFLAC_TRUE; + } +} + +static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) +{ + drflac_uint8 header; + int type; + + if (!drflac__read_uint8(bs, 8, &header)) { + return DRFLAC_FALSE; + } + + /* First bit should always be 0. */ + if ((header & 0x80) != 0) { + return DRFLAC_FALSE; + } + + type = (header & 0x7E) >> 1; + if (type == 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; + } else if (type == 1) { + pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; + } else { + if ((type & 0x20) != 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; + pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1; + } else if ((type & 0x08) != 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; + pSubframe->lpcOrder = (drflac_uint8)(type & 0x07); + if (pSubframe->lpcOrder > 4) { + pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + pSubframe->lpcOrder = 0; + } + } else { + pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + } + } + + if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { + return DRFLAC_FALSE; + } + + /* Wasted bits per sample. */ + pSubframe->wastedBitsPerSample = 0; + if ((header & 0x01) == 1) { + unsigned int wastedBitsPerSample; + if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { + return DRFLAC_FALSE; + } + pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut) +{ + drflac_subframe* pSubframe; + drflac_uint32 subframeBitsPerSample; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(frame != NULL); + + pSubframe = frame->subframes + subframeIndex; + if (!drflac__read_subframe_header(bs, pSubframe)) { + return DRFLAC_FALSE; + } + + /* Side channels require an extra bit per sample. Took a while to figure that one out... */ + subframeBitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + subframeBitsPerSample += 1; + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + subframeBitsPerSample += 1; + } + + if (subframeBitsPerSample > 32) { + /* libFLAC and ffmpeg reject 33-bit subframes as well */ + return DRFLAC_FALSE; + } + + /* Need to handle wasted bits per sample. */ + if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { + return DRFLAC_FALSE; + } + subframeBitsPerSample -= pSubframe->wastedBitsPerSample; + + pSubframe->pSamplesS32 = pDecodedSamplesOut; + + switch (pSubframe->subframeType) + { + case DRFLAC_SUBFRAME_CONSTANT: + { + drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + } break; + + case DRFLAC_SUBFRAME_VERBATIM: + { + drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + } break; + + case DRFLAC_SUBFRAME_FIXED: + { + drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + } break; + + case DRFLAC_SUBFRAME_LPC: + { + drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + } break; + + default: return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) +{ + drflac_subframe* pSubframe; + drflac_uint32 subframeBitsPerSample; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(frame != NULL); + + pSubframe = frame->subframes + subframeIndex; + if (!drflac__read_subframe_header(bs, pSubframe)) { + return DRFLAC_FALSE; + } + + /* Side channels require an extra bit per sample. Took a while to figure that one out... */ + subframeBitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + subframeBitsPerSample += 1; + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + subframeBitsPerSample += 1; + } + + /* Need to handle wasted bits per sample. */ + if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { + return DRFLAC_FALSE; + } + subframeBitsPerSample -= pSubframe->wastedBitsPerSample; + + pSubframe->pSamplesS32 = NULL; + + switch (pSubframe->subframeType) + { + case DRFLAC_SUBFRAME_CONSTANT: + { + if (!drflac__seek_bits(bs, subframeBitsPerSample)) { + return DRFLAC_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_VERBATIM: + { + unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_FIXED: + { + unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return DRFLAC_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_LPC: + { + drflac_uint8 lpcPrecision; + + unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + return DRFLAC_FALSE; + } + if (lpcPrecision == 15) { + return DRFLAC_FALSE; /* Invalid. */ + } + lpcPrecision += 1; + + + bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; /* +5 for shift. */ + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return DRFLAC_FALSE; + } + } break; + + default: return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + + +static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment) +{ + drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; + + DRFLAC_ASSERT(channelAssignment <= 10); + return lookup[channelAssignment]; +} + +static drflac_result drflac__decode_flac_frame(drflac* pFlac) +{ + int channelCount; + int i; + drflac_uint8 paddingSizeInBits; + drflac_uint16 desiredCRC16; +#ifndef DR_FLAC_NO_CRC + drflac_uint16 actualCRC16; +#endif + + /* This function should be called while the stream is sitting on the first byte after the frame header. */ + DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); + + /* The frame block size must never be larger than the maximum block size defined by the FLAC stream. */ + if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { + return DRFLAC_ERROR; + } + + /* The number of channels in the frame must match the channel count from the STREAMINFO block. */ + channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + if (channelCount != (int)pFlac->channels) { + return DRFLAC_ERROR; + } + + for (i = 0; i < channelCount; ++i) { + if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { + return DRFLAC_ERROR; + } + } + + paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); + if (paddingSizeInBits > 0) { + drflac_uint8 padding = 0; + if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { + return DRFLAC_AT_END; + } + } + +#ifndef DR_FLAC_NO_CRC + actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#endif + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return DRFLAC_AT_END; + } + +#ifndef DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */ + } +#endif + + pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + + return DRFLAC_SUCCESS; +} + +static drflac_result drflac__seek_flac_frame(drflac* pFlac) +{ + int channelCount; + int i; + drflac_uint16 desiredCRC16; +#ifndef DR_FLAC_NO_CRC + drflac_uint16 actualCRC16; +#endif + + channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + for (i = 0; i < channelCount; ++i) { + if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { + return DRFLAC_ERROR; + } + } + + /* Padding. */ + if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { + return DRFLAC_ERROR; + } + + /* CRC. */ +#ifndef DR_FLAC_NO_CRC + actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#endif + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return DRFLAC_AT_END; + } + +#ifndef DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */ + } +#endif + + return DRFLAC_SUCCESS; +} + +static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac) +{ + DRFLAC_ASSERT(pFlac != NULL); + + for (;;) { + drflac_result result; + + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + + result = drflac__decode_flac_frame(pFlac); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_CRC_MISMATCH) { + continue; /* CRC mismatch. Skip to the next frame. */ + } else { + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; + } +} + +static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame) +{ + drflac_uint64 firstPCMFrame; + drflac_uint64 lastPCMFrame; + + DRFLAC_ASSERT(pFlac != NULL); + + firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; + if (firstPCMFrame == 0) { + firstPCMFrame = ((drflac_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; + } + + lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + if (lastPCMFrame > 0) { + lastPCMFrame -= 1; /* Needs to be zero based. */ + } + + if (pFirstPCMFrame) { + *pFirstPCMFrame = firstPCMFrame; + } + if (pLastPCMFrame) { + *pLastPCMFrame = lastPCMFrame; + } +} + +static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) +{ + drflac_bool32 result; + + DRFLAC_ASSERT(pFlac != NULL); + + result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); + + DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); + pFlac->currentPCMFrame = 0; + + return result; +} + +static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac) +{ + /* This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section. */ + DRFLAC_ASSERT(pFlac != NULL); + return drflac__seek_flac_frame(pFlac); +} + + +static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek) +{ + drflac_uint64 pcmFramesRead = 0; + while (pcmFramesToSeek > 0) { + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + break; /* Couldn't read the next frame, so just break from the loop and return. */ + } + } else { + if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { + pcmFramesRead += pcmFramesToSeek; + pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek; /* <-- Safe cast. Will always be < currentFrame.pcmFramesRemaining < 65536. */ + pcmFramesToSeek = 0; + } else { + pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; + pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + } + } + } + + pFlac->currentPCMFrame += pcmFramesRead; + return pcmFramesRead; +} + + +static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex) +{ + drflac_bool32 isMidFrame = DRFLAC_FALSE; + drflac_uint64 runningPCMFrameCount; + + DRFLAC_ASSERT(pFlac != NULL); + + /* If we are seeking forward we start from the current position. Otherwise we need to start all the way from the start of the file. */ + if (pcmFrameIndex >= pFlac->currentPCMFrame) { + /* Seeking forward. Need to seek from the current position. */ + runningPCMFrameCount = pFlac->currentPCMFrame; + + /* The frame header for the first frame may not yet have been read. We need to do that if necessary. */ + if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + } else { + isMidFrame = DRFLAC_TRUE; + } + } else { + /* Seeking backwards. Need to seek from the start of the file. */ + runningPCMFrameCount = 0; + + /* Move back to the start. */ + if (!drflac__seek_to_first_frame(pFlac)) { + return DRFLAC_FALSE; + } + + /* Decode the first frame in preparation for sample-exact seeking below. */ + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + } + + /* + We need to as quickly as possible find the frame that contains the target sample. To do this, we iterate over each frame and inspect its + header. If based on the header we can determine that the frame contains the sample, we do a full decode of that frame. + */ + for (;;) { + drflac_uint64 pcmFrameCountInThisFLACFrame; + drflac_uint64 firstPCMFrameInFLACFrame = 0; + drflac_uint64 lastPCMFrameInFLACFrame = 0; + + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + + pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; + if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { + /* + The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + it never existed and keep iterating. + */ + drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + + if (!isMidFrame) { + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */ + } else { + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ + } else { + return DRFLAC_FALSE; + } + } + } else { + /* We started seeking mid-frame which means we need to skip the frame decoding part. */ + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } + } else { + /* + It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + frame never existed and leave the running sample count untouched. + */ + if (!isMidFrame) { + drflac_result result = drflac__seek_to_next_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningPCMFrameCount += pcmFrameCountInThisFLACFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ + } else { + return DRFLAC_FALSE; + } + } + } else { + /* + We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with + drflac__seek_to_next_flac_frame() which only works if the decoder is sitting on the byte just after the frame header. + */ + runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + isMidFrame = DRFLAC_FALSE; + } + + /* If we are seeking to the end of the file and we've just hit it, we're done. */ + if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { + return DRFLAC_TRUE; + } + } + + next_iteration: + /* Grab the next frame in preparation for the next iteration. */ + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + } +} + + +#if !defined(DR_FLAC_NO_CRC) +/* +We use an average compression ratio to determine our approximate start location. FLAC files are generally about 50%-70% the size of their +uncompressed counterparts so we'll use this as a basis. I'm going to split the middle and use a factor of 0.6 to determine the starting +location. +*/ +#define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f + +static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset) +{ + DRFLAC_ASSERT(pFlac != NULL); + DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); + DRFLAC_ASSERT(targetByte >= rangeLo); + DRFLAC_ASSERT(targetByte <= rangeHi); + + *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; + + for (;;) { + /* After rangeLo == rangeHi == targetByte fails, we need to break out. */ + drflac_uint64 lastTargetByte = targetByte; + + /* When seeking to a byte, failure probably means we've attempted to seek beyond the end of the stream. To counter this we just halve it each attempt. */ + if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) { + /* If we couldn't even seek to the first byte in the stream we have a problem. Just abandon the whole thing. */ + if (targetByte == 0) { + drflac__seek_to_first_frame(pFlac); /* Try to recover. */ + return DRFLAC_FALSE; + } + + /* Halve the byte location and continue. */ + targetByte = rangeLo + ((rangeHi - rangeLo)/2); + rangeHi = targetByte; + } else { + /* Getting here should mean that we have seeked to an appropriate byte. */ + + /* Clear the details of the FLAC frame so we don't misreport data. */ + DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); + + /* + Now seek to the next FLAC frame. We need to decode the entire frame (not just the header) because it's possible for the header to incorrectly pass the + CRC check and return bad data. We need to decode the entire frame to be more certain. Although this seems unlikely, this has happened to me in testing + so it needs to stay this way for now. + */ +#if 1 + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + /* Halve the byte location and continue. */ + targetByte = rangeLo + ((rangeHi - rangeLo)/2); + rangeHi = targetByte; + } else { + break; + } +#else + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + /* Halve the byte location and continue. */ + targetByte = rangeLo + ((rangeHi - rangeLo)/2); + rangeHi = targetByte; + } else { + break; + } +#endif + } + + /* We already tried this byte and there are no more to try, break out. */ + if(targetByte == lastTargetByte) { + return DRFLAC_FALSE; + } + } + + /* The current PCM frame needs to be updated based on the frame we just seeked to. */ + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + + DRFLAC_ASSERT(targetByte <= rangeHi); + + *pLastSuccessfulSeekOffset = targetByte; + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset) +{ + /* This section of code would be used if we were only decoding the FLAC frame header when calling drflac__seek_to_approximate_flac_frame_to_byte(). */ +#if 0 + if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) { + /* We failed to decode this frame which may be due to it being corrupt. We'll just use the next valid FLAC frame. */ + if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) { + return DRFLAC_FALSE; + } + } +#endif + + return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset; +} + + +static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi) +{ + /* This assumes pFlac->currentPCMFrame is sitting on byteRangeLo upon entry. */ + + drflac_uint64 targetByte; + drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; + drflac_uint64 pcmRangeHi = 0; + drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1; + drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; + drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + + targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); + if (targetByte > byteRangeHi) { + targetByte = byteRangeHi; + } + + for (;;) { + if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { + /* We found a FLAC frame. We need to check if it contains the sample we're looking for. */ + drflac_uint64 newPCMRangeLo; + drflac_uint64 newPCMRangeHi; + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); + + /* If we selected the same frame, it means we should be pretty close. Just decode the rest. */ + if (pcmRangeLo == newPCMRangeLo) { + if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { + break; /* Failed to seek to closest frame. */ + } + + if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return DRFLAC_TRUE; + } else { + break; /* Failed to seek forward. */ + } + } + + pcmRangeLo = newPCMRangeLo; + pcmRangeHi = newPCMRangeHi; + + if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { + /* The target PCM frame is in this FLAC frame. */ + if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { + return DRFLAC_TRUE; + } else { + break; /* Failed to seek to FLAC frame. */ + } + } else { + const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); + + if (pcmRangeLo > pcmFrameIndex) { + /* We seeked too far forward. We need to move our target byte backward and try again. */ + byteRangeHi = lastSuccessfulSeekOffset; + if (byteRangeLo > byteRangeHi) { + byteRangeLo = byteRangeHi; + } + + targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2); + if (targetByte < byteRangeLo) { + targetByte = byteRangeLo; + } + } else /*if (pcmRangeHi < pcmFrameIndex)*/ { + /* We didn't seek far enough. We need to move our target byte forward and try again. */ + + /* If we're close enough we can just seek forward. */ + if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { + if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return DRFLAC_TRUE; + } else { + break; /* Failed to seek to FLAC frame. */ + } + } else { + byteRangeLo = lastSuccessfulSeekOffset; + if (byteRangeHi < byteRangeLo) { + byteRangeHi = byteRangeLo; + } + + targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); + if (targetByte > byteRangeHi) { + targetByte = byteRangeHi; + } + + if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) { + closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset; + } + } + } + } + } else { + /* Getting here is really bad. We just recover as best we can, but moving to the first frame in the stream, and then abort. */ + break; + } + } + + drflac__seek_to_first_frame(pFlac); /* <-- Try to recover. */ + return DRFLAC_FALSE; +} + +static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex) +{ + drflac_uint64 byteRangeLo; + drflac_uint64 byteRangeHi; + drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + + /* Our algorithm currently assumes the FLAC stream is currently sitting at the start. */ + if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) { + return DRFLAC_FALSE; + } + + /* If we're close enough to the start, just move to the start and seek forward. */ + if (pcmFrameIndex < seekForwardThreshold) { + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; + } + + /* + Our starting byte range is the byte position of the first FLAC frame and the approximate end of the file as if it were completely uncompressed. This ensures + the entire file is included, even though most of the time it'll exceed the end of the actual stream. This is OK as the frame searching logic will handle it. + */ + byteRangeLo = pFlac->firstFLACFramePosInBytes; + byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + + return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); +} +#endif /* !DR_FLAC_NO_CRC */ + +static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex) +{ + drflac_uint32 iClosestSeekpoint = 0; + drflac_bool32 isMidFrame = DRFLAC_FALSE; + drflac_uint64 runningPCMFrameCount; + drflac_uint32 iSeekpoint; + + + DRFLAC_ASSERT(pFlac != NULL); + + if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { + return DRFLAC_FALSE; + } + + /* Do not use the seektable if pcmFramIndex is not coverd by it. */ + if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { + return DRFLAC_FALSE; + } + + for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { + if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { + break; + } + + iClosestSeekpoint = iSeekpoint; + } + + /* There's been cases where the seek table contains only zeros. We need to do some basic validation on the closest seekpoint. */ + if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { + return DRFLAC_FALSE; + } + if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { + return DRFLAC_FALSE; + } + +#if !defined(DR_FLAC_NO_CRC) + /* At this point we should know the closest seek point. We can use a binary search for this. We need to know the total sample count for this. */ + if (pFlac->totalPCMFrameCount > 0) { + drflac_uint64 byteRangeLo; + drflac_uint64 byteRangeHi; + + byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; + + /* + If our closest seek point is not the last one, we only need to search between it and the next one. The section below calculates an appropriate starting + value for byteRangeHi which will clamp it appropriately. + + Note that the next seekpoint must have an offset greater than the closest seekpoint because otherwise our binary search algorithm will break down. There + have been cases where a seektable consists of seek points where every byte offset is set to 0 which causes problems. If this happens we need to abort. + */ + if (iClosestSeekpoint < pFlac->seekpointCount-1) { + drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1; + + /* Basic validation on the seekpoints to ensure they're usable. */ + if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { + return DRFLAC_FALSE; /* The next seekpoint doesn't look right. The seek table cannot be trusted from here. Abort. */ + } + + if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { /* Make sure it's not a placeholder seekpoint. */ + byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; /* byteRangeHi must be zero based. */ + } + } + + if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + + if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { + return DRFLAC_TRUE; + } + } + } + } +#endif /* !DR_FLAC_NO_CRC */ + + /* Getting here means we need to use a slower algorithm because the binary search method failed or cannot be used. */ + + /* + If we are seeking forward and the closest seekpoint is _before_ the current sample, we just seek forward from where we are. Otherwise we start seeking + from the seekpoint's first sample. + */ + if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { + /* Optimized case. Just seek forward from where we are. */ + runningPCMFrameCount = pFlac->currentPCMFrame; + + /* The frame header for the first frame may not yet have been read. We need to do that if necessary. */ + if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + } else { + isMidFrame = DRFLAC_TRUE; + } + } else { + /* Slower case. Seek to the start of the seekpoint and then seek forward from there. */ + runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; + + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + return DRFLAC_FALSE; + } + + /* Grab the frame the seekpoint is sitting on in preparation for the sample-exact seeking below. */ + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + } + + for (;;) { + drflac_uint64 pcmFrameCountInThisFLACFrame; + drflac_uint64 firstPCMFrameInFLACFrame = 0; + drflac_uint64 lastPCMFrameInFLACFrame = 0; + + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + + pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; + if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { + /* + The sample should be in this frame. We need to fully decode it, but if it's an invalid frame (a CRC mismatch) we need to pretend + it never existed and keep iterating. + */ + drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + + if (!isMidFrame) { + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */ + } else { + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ + } else { + return DRFLAC_FALSE; + } + } + } else { + /* We started seeking mid-frame which means we need to skip the frame decoding part. */ + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } + } else { + /* + It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + frame never existed and leave the running sample count untouched. + */ + if (!isMidFrame) { + drflac_result result = drflac__seek_to_next_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningPCMFrameCount += pcmFrameCountInThisFLACFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ + } else { + return DRFLAC_FALSE; + } + } + } else { + /* + We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with + drflac__seek_to_next_flac_frame() which only works if the decoder is sitting on the byte just after the frame header. + */ + runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + isMidFrame = DRFLAC_FALSE; + } + + /* If we are seeking to the end of the file and we've just hit it, we're done. */ + if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { + return DRFLAC_TRUE; + } + } + + next_iteration: + /* Grab the next frame in preparation for the next iteration. */ + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + } +} + + +#ifndef DR_FLAC_NO_OGG +typedef struct +{ + drflac_uint8 capturePattern[4]; /* Should be "OggS" */ + drflac_uint8 structureVersion; /* Always 0. */ + drflac_uint8 headerType; + drflac_uint64 granulePosition; + drflac_uint32 serialNumber; + drflac_uint32 sequenceNumber; + drflac_uint32 checksum; + drflac_uint8 segmentCount; + drflac_uint8 segmentTable[255]; +} drflac_ogg_page_header; +#endif + +typedef struct +{ + drflac_read_proc onRead; + drflac_seek_proc onSeek; + drflac_meta_proc onMeta; + drflac_container container; + void* pUserData; + void* pUserDataMD; + drflac_uint32 sampleRate; + drflac_uint8 channels; + drflac_uint8 bitsPerSample; + drflac_uint64 totalPCMFrameCount; + drflac_uint16 maxBlockSizeInPCMFrames; + drflac_uint64 runningFilePos; + drflac_bool32 hasStreamInfoBlock; + drflac_bool32 hasMetadataBlocks; + drflac_bs bs; /* <-- A bit streamer is required for loading data during initialization. */ + drflac_frame_header firstFrameHeader; /* <-- The header of the first frame that was read during relaxed initalization. Only set if there is no STREAMINFO block. */ + +#ifndef DR_FLAC_NO_OGG + drflac_uint32 oggSerial; + drflac_uint64 oggFirstBytePos; + drflac_ogg_page_header oggBosHeader; +#endif +} drflac_init_info; + +static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) +{ + blockHeader = drflac__be2host_32(blockHeader); + *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31); + *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24); + *blockSize = (blockHeader & 0x00FFFFFFUL); +} + +static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) +{ + drflac_uint32 blockHeader; + + *blockSize = 0; + if (onRead(pUserData, &blockHeader, 4) != 4) { + return DRFLAC_FALSE; + } + + drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) +{ + drflac_uint32 blockSizes; + drflac_uint64 frameSizes = 0; + drflac_uint64 importantProps; + drflac_uint8 md5[16]; + + /* min/max block size. */ + if (onRead(pUserData, &blockSizes, 4) != 4) { + return DRFLAC_FALSE; + } + + /* min/max frame size. */ + if (onRead(pUserData, &frameSizes, 6) != 6) { + return DRFLAC_FALSE; + } + + /* Sample rate, channels, bits per sample and total sample count. */ + if (onRead(pUserData, &importantProps, 8) != 8) { + return DRFLAC_FALSE; + } + + /* MD5 */ + if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { + return DRFLAC_FALSE; + } + + blockSizes = drflac__be2host_32(blockSizes); + frameSizes = drflac__be2host_64(frameSizes); + importantProps = drflac__be2host_64(importantProps); + + pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16); + pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF); + pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40); + pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16); + pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44); + pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1; + pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1; + pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); + DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); + + return DRFLAC_TRUE; +} + + +static void* drflac__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return DRFLAC_MALLOC(sz); +} + +static void* drflac__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return DRFLAC_REALLOC(p, sz); +} + +static void drflac__free_default(void* p, void* pUserData) +{ + (void)pUserData; + DRFLAC_FREE(p); +} + + +static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } + + /* Try using realloc(). */ + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); + } + + return NULL; +} + +static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); + } + + /* Try emulating realloc() in terms of malloc()/free(). */ + if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { + void* p2; + + p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); + if (p2 == NULL) { + return NULL; + } + + if (p != NULL) { + DRFLAC_COPY_MEMORY(p2, p, szOld); + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } + + return p2; + } + + return NULL; +} + +static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL || pAllocationCallbacks == NULL) { + return; + } + + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } +} + + +static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks) +{ + /* + We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that + we'll be sitting on byte 42. + */ + drflac_uint64 runningFilePos = 42; + drflac_uint64 seektablePos = 0; + drflac_uint32 seektableSize = 0; + + for (;;) { + drflac_metadata metadata; + drflac_uint8 isLastBlock = 0; + drflac_uint8 blockType; + drflac_uint32 blockSize; + if (drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == DRFLAC_FALSE) { + return DRFLAC_FALSE; + } + runningFilePos += 4; + + metadata.type = blockType; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + + switch (blockType) + { + case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: + { + if (blockSize < 4) { + return DRFLAC_FALSE; + } + + if (onMeta) { + void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); + metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); + metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); + onMeta(pUserDataMD, &metadata); + + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: + { + seektablePos = runningFilePos; + seektableSize = blockSize; + + if (onMeta) { + drflac_uint32 seekpointCount; + drflac_uint32 iSeekpoint; + void* pRawData; + + seekpointCount = blockSize/DRFLAC_SEEKPOINT_SIZE_IN_BYTES; + + pRawData = drflac__malloc_from_callbacks(seekpointCount * sizeof(drflac_seekpoint), pAllocationCallbacks); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + /* We need to read seekpoint by seekpoint and do some processing. */ + for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { + drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; + + if (onRead(pUserData, pSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) != DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + /* Endian swap. */ + pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame); + pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset); + pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount); + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.seektable.seekpointCount = seekpointCount; + metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; + + onMeta(pUserDataMD, &metadata); + + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: + { + if (blockSize < 8) { + return DRFLAC_FALSE; + } + + if (onMeta) { + void* pRawData; + const char* pRunningData; + const char* pRunningDataEnd; + drflac_uint32 i; + + pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + pRunningData = (const char*)pRawData; + pRunningDataEnd = (const char*)pRawData + blockSize; + + metadata.data.vorbis_comment.vendorLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + + /* Need space for the rest of the block */ + if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; + metadata.data.vorbis_comment.commentCount = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + + /* Need space for 'commentCount' comments after the block, which at minimum is a drflac_uint32 per comment */ + if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { /* <-- Note the order of operations to avoid overflow to a valid value */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + metadata.data.vorbis_comment.pComments = pRunningData; + + /* Check that the comments section is valid before passing it to the callback */ + for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { + drflac_uint32 commentLength; + + if (pRunningDataEnd - pRunningData < 4) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + pRunningData += commentLength; + } + + onMeta(pUserDataMD, &metadata); + + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: + { + if (blockSize < 396) { + return DRFLAC_FALSE; + } + + if (onMeta) { + void* pRawData; + const char* pRunningData; + const char* pRunningDataEnd; + size_t bufferSize; + drflac_uint8 iTrack; + drflac_uint8 iIndex; + void* pTrackData; + + /* + This needs to be loaded in two passes. The first pass is used to calculate the size of the memory allocation + we need for storing the necessary data. The second pass will fill that buffer with usable data. + */ + pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + pRunningData = (const char*)pRawData; + pRunningDataEnd = (const char*)pRawData + blockSize; + + DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; + metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8; + metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; + metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; + metadata.data.cuesheet.pTrackData = NULL; /* Will be filled later. */ + + /* Pass 1: Calculate the size of the buffer for the track data. */ + { + const char* pRunningDataSaved = pRunningData; /* Will be restored at the end in preparation for the second pass. */ + + bufferSize = metadata.data.cuesheet.trackCount * DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES; + + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + drflac_uint8 indexCount; + drflac_uint32 indexPointSize; + + if (pRunningDataEnd - pRunningData < DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + /* Skip to the index point count */ + pRunningData += 35; + + indexCount = pRunningData[0]; + pRunningData += 1; + + bufferSize += indexCount * sizeof(drflac_cuesheet_track_index); + + /* Quick validation check. */ + indexPointSize = indexCount * DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + pRunningData += indexPointSize; + } + + pRunningData = pRunningDataSaved; + } + + /* Pass 2: Allocate a buffer and fill the data. Validation was done in the step above so can be skipped. */ + { + char* pRunningTrackData; + + pTrackData = drflac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); + if (pTrackData == NULL) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + pRunningTrackData = (char*)pTrackData; + + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + drflac_uint8 indexCount; + + DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES); + pRunningData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; /* Skip forward, but not beyond the last byte in the CUESHEET_TRACK block which is the index count. */ + pRunningTrackData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + + /* Grab the index count for the next part. */ + indexCount = pRunningData[0]; + pRunningData += 1; + pRunningTrackData += 1; + + /* Extract each track index. */ + for (iIndex = 0; iIndex < indexCount; ++iIndex) { + drflac_cuesheet_track_index* pTrackIndex = (drflac_cuesheet_track_index*)pRunningTrackData; + + DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); + pRunningData += DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + pRunningTrackData += sizeof(drflac_cuesheet_track_index); + + pTrackIndex->offset = drflac__be2host_64(pTrackIndex->offset); + } + } + + metadata.data.cuesheet.pTrackData = pTrackData; + } + + /* The original data is no longer needed. */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + pRawData = NULL; + + onMeta(pUserDataMD, &metadata); + + drflac__free_from_callbacks(pTrackData, pAllocationCallbacks); + pTrackData = NULL; + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: + { + if (blockSize < 32) { + return DRFLAC_FALSE; + } + + if (onMeta) { + void* pRawData; + const char* pRunningData; + const char* pRunningDataEnd; + + pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + pRunningData = (const char*)pRawData; + pRunningDataEnd = (const char*)pRawData + blockSize; + + metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + + /* Need space for the rest of the block */ + if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; + metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + + /* Need space for the rest of the block */ + if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; + metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; + + /* Need space for the picture after the block */ + if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { /* <-- Note the order of operations to avoid overflow to a valid value */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + onMeta(pUserDataMD, &metadata); + + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_PADDING: + { + if (onMeta) { + metadata.data.padding.unused = 0; + + /* Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. */ + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */ + } else { + onMeta(pUserDataMD, &metadata); + } + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_INVALID: + { + /* Invalid chunk. Just skip over this one. */ + if (onMeta) { + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */ + } + } + } break; + + default: + { + /* + It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we + can at the very least report the chunk to the application and let it look at the raw data. + */ + if (onMeta) { + void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + onMeta(pUserDataMD, &metadata); + + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + } + + /* If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. */ + if (onMeta == NULL && blockSize > 0) { + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; + } + } + + runningFilePos += blockSize; + if (isLastBlock) { + break; + } + } + + *pSeektablePos = seektablePos; + *pSeekpointCount = seektableSize / DRFLAC_SEEKPOINT_SIZE_IN_BYTES; + *pFirstFramePos = runningFilePos; + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +{ + /* Pre Condition: The bit stream should be sitting just past the 4-byte id header. */ + + drflac_uint8 isLastBlock; + drflac_uint8 blockType; + drflac_uint32 blockSize; + + (void)onSeek; + + pInit->container = drflac_container_native; + + /* The first metadata block should be the STREAMINFO block. */ + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return DRFLAC_FALSE; + } + + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + if (!relaxed) { + /* We're opening in strict mode and the first block is not the STREAMINFO block. Error. */ + return DRFLAC_FALSE; + } else { + /* + Relaxed mode. To open from here we need to just find the first frame and set the sample rate, etc. to whatever is defined + for that frame. + */ + pInit->hasStreamInfoBlock = DRFLAC_FALSE; + pInit->hasMetadataBlocks = DRFLAC_FALSE; + + if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { + return DRFLAC_FALSE; /* Couldn't find a frame. */ + } + + if (pInit->firstFrameHeader.bitsPerSample == 0) { + return DRFLAC_FALSE; /* Failed to initialize because the first frame depends on the STREAMINFO block, which does not exist. */ + } + + pInit->sampleRate = pInit->firstFrameHeader.sampleRate; + pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); + pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; + pInit->maxBlockSizeInPCMFrames = 65535; /* <-- See notes here: https://xiph.org/flac/format.html#metadata_block_streaminfo */ + return DRFLAC_TRUE; + } + } else { + drflac_streaminfo streaminfo; + if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + return DRFLAC_FALSE; + } + + pInit->hasStreamInfoBlock = DRFLAC_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; + pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; /* Don't care about the min block size - only the max (used for determining the size of the memory allocation). */ + pInit->hasMetadataBlocks = !isLastBlock; + + if (onMeta) { + drflac_metadata metadata; + metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + metadata.data.streaminfo = streaminfo; + onMeta(pUserDataMD, &metadata); + } + + return DRFLAC_TRUE; + } +} + +#ifndef DR_FLAC_NO_OGG +#define DRFLAC_OGG_MAX_PAGE_SIZE 65307 +#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 /* CRC-32 of "OggS". */ + +typedef enum +{ + drflac_ogg_recover_on_crc_mismatch, + drflac_ogg_fail_on_crc_mismatch +} drflac_ogg_crc_mismatch_recovery; + +#ifndef DR_FLAC_NO_CRC +static drflac_uint32 drflac__crc32_table[] = { + 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, + 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, + 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, + 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, + 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, + 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, + 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, + 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, + 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, + 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, + 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, + 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, + 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, + 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, + 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, + 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, + 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, + 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, + 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, + 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, + 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, + 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, + 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, + 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, + 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, + 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, + 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, + 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, + 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, + 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, + 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, + 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, + 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, + 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, + 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, + 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, + 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, + 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, + 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, + 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, + 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, + 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, + 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, + 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, + 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, + 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, + 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, + 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, + 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, + 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, + 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, + 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, + 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, + 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, + 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, + 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, + 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, + 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, + 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, + 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, + 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, + 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, + 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, + 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L +}; +#endif + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data) +{ +#ifndef DR_FLAC_NO_CRC + return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data]; +#else + (void)data; + return crc32; +#endif +} + +#if 0 +static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data) +{ + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF)); + return crc32; +} + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data) +{ + crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF)); + crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF)); + return crc32; +} +#endif + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize) +{ + /* This can be optimized. */ + drflac_uint32 i; + for (i = 0; i < dataSize; ++i) { + crc32 = drflac_crc32_byte(crc32, pData[i]); + } + return crc32; +} + + +static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4]) +{ + return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; +} + +static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) +{ + return 27 + pHeader->segmentCount; +} + +static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) +{ + drflac_uint32 pageBodySize = 0; + int i; + + for (i = 0; i < pHeader->segmentCount; ++i) { + pageBodySize += pHeader->segmentTable[i]; + } + + return pageBodySize; +} + +static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +{ + drflac_uint8 data[23]; + drflac_uint32 i; + + DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); + + if (onRead(pUserData, data, 23) != 23) { + return DRFLAC_AT_END; + } + *pBytesRead += 23; + + /* + It's not actually used, but set the capture pattern to 'OggS' for completeness. Not doing this will cause static analysers to complain about + us trying to access uninitialized data. We could alternatively just comment out this member of the drflac_ogg_page_header structure, but I + like to have it map to the structure of the underlying data. + */ + pHeader->capturePattern[0] = 'O'; + pHeader->capturePattern[1] = 'g'; + pHeader->capturePattern[2] = 'g'; + pHeader->capturePattern[3] = 'S'; + + pHeader->structureVersion = data[0]; + pHeader->headerType = data[1]; + DRFLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); + DRFLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); + DRFLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); + DRFLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); + pHeader->segmentCount = data[22]; + + /* Calculate the CRC. Note that for the calculation the checksum part of the page needs to be set to 0. */ + data[18] = 0; + data[19] = 0; + data[20] = 0; + data[21] = 0; + + for (i = 0; i < 23; ++i) { + *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]); + } + + + if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { + return DRFLAC_AT_END; + } + *pBytesRead += pHeader->segmentCount; + + for (i = 0; i < pHeader->segmentCount; ++i) { + *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); + } + + return DRFLAC_SUCCESS; +} + +static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +{ + drflac_uint8 id[4]; + + *pBytesRead = 0; + + if (onRead(pUserData, id, 4) != 4) { + return DRFLAC_AT_END; + } + *pBytesRead += 4; + + /* We need to read byte-by-byte until we find the OggS capture pattern. */ + for (;;) { + if (drflac_ogg__is_capture_pattern(id)) { + drflac_result result; + + *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + + result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); + if (result == DRFLAC_SUCCESS) { + return DRFLAC_SUCCESS; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; + } else { + return result; + } + } + } else { + /* The first 4 bytes did not equal the capture pattern. Read the next byte and try again. */ + id[0] = id[1]; + id[1] = id[2]; + id[2] = id[3]; + if (onRead(pUserData, &id[3], 1) != 1) { + return DRFLAC_AT_END; + } + *pBytesRead += 1; + } + } +} + + +/* +The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works +in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is designed +in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type +dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from +the physical Ogg bitstream are converted and delivered in native FLAC format. +*/ +typedef struct +{ + drflac_read_proc onRead; /* The original onRead callback from drflac_open() and family. */ + drflac_seek_proc onSeek; /* The original onSeek callback from drflac_open() and family. */ + void* pUserData; /* The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family. */ + drflac_uint64 currentBytePos; /* The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. */ + drflac_uint64 firstBytePos; /* The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. */ + drflac_uint32 serialNumber; /* The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization. */ + drflac_ogg_page_header bosPageHeader; /* Used for seeking. */ + drflac_ogg_page_header currentPageHeader; + drflac_uint32 bytesRemainingInPage; + drflac_uint32 pageDataSize; + drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE]; +} drflac_oggbs; /* oggbs = Ogg Bitstream */ + +static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) +{ + size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); + oggbs->currentBytePos += bytesActuallyRead; + + return bytesActuallyRead; +} + +static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) +{ + if (origin == drflac_seek_origin_start) { + if (offset <= 0x7FFFFFFF) { + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos = offset; + + return DRFLAC_TRUE; + } else { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos = offset; + + return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); + } + } else { + while (offset > 0x7FFFFFFF) { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos += 0x7FFFFFFF; + offset -= 0x7FFFFFFF; + } + + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { /* <-- Safe cast thanks to the loop above. */ + return DRFLAC_FALSE; + } + oggbs->currentBytePos += offset; + + return DRFLAC_TRUE; + } +} + +static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod) +{ + drflac_ogg_page_header header; + for (;;) { + drflac_uint32 crc32 = 0; + drflac_uint32 bytesRead; + drflac_uint32 pageBodySize; +#ifndef DR_FLAC_NO_CRC + drflac_uint32 actualCRC32; +#endif + + if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos += bytesRead; + + pageBodySize = drflac_ogg__get_page_body_size(&header); + if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) { + continue; /* Invalid page size. Assume it's corrupted and just move to the next page. */ + } + + if (header.serialNumber != oggbs->serialNumber) { + /* It's not a FLAC page. Skip it. */ + if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + continue; + } + + + /* We need to read the entire page and then do a CRC check on it. If there's a CRC mismatch we need to skip this page. */ + if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { + return DRFLAC_FALSE; + } + oggbs->pageDataSize = pageBodySize; + +#ifndef DR_FLAC_NO_CRC + actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); + if (actualCRC32 != header.checksum) { + if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) { + continue; /* CRC mismatch. Skip this page. */ + } else { + /* + Even though we are failing on a CRC mismatch, we still want our stream to be in a good state. Therefore we + go to the next valid page to ensure we're in a good state, but return false to let the caller know that the + seek did not fully complete. + */ + drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); + return DRFLAC_FALSE; + } + } +#else + (void)recoveryMethod; /* <-- Silence a warning. */ +#endif + + oggbs->currentPageHeader = header; + oggbs->bytesRemainingInPage = pageBodySize; + return DRFLAC_TRUE; + } +} + +/* Function below is unused at the moment, but I might be re-adding it later. */ +#if 0 +static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg) +{ + drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; + drflac_uint8 iSeg = 0; + drflac_uint32 iByte = 0; + while (iByte < bytesConsumedInPage) { + drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (iByte + segmentSize > bytesConsumedInPage) { + break; + } else { + iSeg += 1; + iByte += segmentSize; + } + } + + *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte); + return iSeg; +} + +static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) +{ + /* The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page. */ + for (;;) { + drflac_bool32 atEndOfPage = DRFLAC_FALSE; + + drflac_uint8 bytesRemainingInSeg; + drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); + + drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; + for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { + drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (segmentSize < 255) { + if (iSeg == oggbs->currentPageHeader.segmentCount-1) { + atEndOfPage = DRFLAC_TRUE; + } + + break; + } + + bytesToEndOfPacketOrPage += segmentSize; + } + + /* + At this point we will have found either the packet or the end of the page. If were at the end of the page we'll + want to load the next page and keep searching for the end of the packet. + */ + drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); + oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; + + if (atEndOfPage) { + /* + We're potentially at the next packet, but we need to check the next page first to be sure because the packet may + straddle pages. + */ + if (!drflac_oggbs__goto_next_page(oggbs)) { + return DRFLAC_FALSE; + } + + /* If it's a fresh packet it most likely means we're at the next packet. */ + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { + return DRFLAC_TRUE; + } + } else { + /* We're at the next packet. */ + return DRFLAC_TRUE; + } + } +} + +static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) +{ + /* The bitstream should be sitting on the first byte just after the header of the frame. */ + + /* What we're actually doing here is seeking to the start of the next packet. */ + return drflac_oggbs__seek_to_next_packet(oggbs); +} +#endif + +static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut; + size_t bytesRead = 0; + + DRFLAC_ASSERT(oggbs != NULL); + DRFLAC_ASSERT(pRunningBufferOut != NULL); + + /* Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one. */ + while (bytesRead < bytesToRead) { + size_t bytesRemainingToRead = bytesToRead - bytesRead; + + if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { + DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); + bytesRead += bytesRemainingToRead; + oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; + break; + } + + /* If we get here it means some of the requested data is contained in the next pages. */ + if (oggbs->bytesRemainingInPage > 0) { + DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); + bytesRead += oggbs->bytesRemainingInPage; + pRunningBufferOut += oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + + DRFLAC_ASSERT(bytesRemainingToRead > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + break; /* Failed to go to the next page. Might have simply hit the end of the stream. */ + } + } + + return bytesRead; +} + +static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + int bytesSeeked = 0; + + DRFLAC_ASSERT(oggbs != NULL); + DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ + + /* Seeking is always forward which makes things a lot simpler. */ + if (origin == drflac_seek_origin_start) { + if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { + return DRFLAC_FALSE; + } + + return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); + } + + DRFLAC_ASSERT(origin == drflac_seek_origin_current); + + while (bytesSeeked < offset) { + int bytesRemainingToSeek = offset - bytesSeeked; + DRFLAC_ASSERT(bytesRemainingToSeek >= 0); + + if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { + bytesSeeked += bytesRemainingToSeek; + (void)bytesSeeked; /* <-- Silence a dead store warning emitted by Clang Static Analyzer. */ + oggbs->bytesRemainingInPage -= bytesRemainingToSeek; + break; + } + + /* If we get here it means some of the requested data is contained in the next pages. */ + if (oggbs->bytesRemainingInPage > 0) { + bytesSeeked += (int)oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + + DRFLAC_ASSERT(bytesRemainingToSeek > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { + /* Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. */ + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + drflac_uint64 originalBytePos; + drflac_uint64 runningGranulePosition; + drflac_uint64 runningFrameBytePos; + drflac_uint64 runningPCMFrameCount; + + DRFLAC_ASSERT(oggbs != NULL); + + originalBytePos = oggbs->currentBytePos; /* For recovery. Points to the OggS identifier. */ + + /* First seek to the first frame. */ + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { + return DRFLAC_FALSE; + } + oggbs->bytesRemainingInPage = 0; + + runningGranulePosition = 0; + for (;;) { + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); + return DRFLAC_FALSE; /* Never did find that sample... */ + } + + runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; + if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { + break; /* The sample is somewhere in the previous page. */ + } + + /* + At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we + disregard any pages that do not begin a fresh packet. + */ + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { /* <-- Is it a fresh page? */ + if (oggbs->currentPageHeader.segmentTable[0] >= 2) { + drflac_uint8 firstBytesInPage[2]; + firstBytesInPage[0] = oggbs->pageData[0]; + firstBytesInPage[1] = oggbs->pageData[1]; + + if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { /* <-- Does the page begin with a frame's sync code? */ + runningGranulePosition = oggbs->currentPageHeader.granulePosition; + } + + continue; + } + } + } + + /* + We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the + start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of + a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until + we find the one containing the target sample. + */ + if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + return DRFLAC_FALSE; + } + + /* + At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep + looping over these frames until we find the one containing the sample we're after. + */ + runningPCMFrameCount = runningGranulePosition; + for (;;) { + /* + There are two ways to find the sample and seek past irrelevant frames: + 1) Use the native FLAC decoder. + 2) Use Ogg's framing system. + + Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to + do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code + duplication for the decoding of frame headers. + + Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg + bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the + standard drflac__*() APIs because that will read in extra data for its own internal caching which in turn breaks + the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read + using the native FLAC decoding APIs, such as drflac__read_next_flac_frame_header(), need to be re-implemented so as to + avoid the use of the drflac_bs object. + + Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons: + 1) Seeking is already partially accelerated using Ogg's paging system in the code block above. + 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon. + 3) Simplicity. + */ + drflac_uint64 firstPCMFrameInFLACFrame = 0; + drflac_uint64 lastPCMFrameInFLACFrame = 0; + drflac_uint64 pcmFrameCountInThisFrame; + + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + + pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; + + /* If we are seeking to the end of the file and we've just hit it, we're done. */ + if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + pFlac->currentPCMFrame = pcmFrameIndex; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + return DRFLAC_TRUE; + } else { + return DRFLAC_FALSE; + } + } + + if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { + /* + The sample should be in this FLAC frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + it never existed and keep iterating. + */ + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ + drflac_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); /* <-- Safe cast because the maximum number of samples in a frame is 65535. */ + if (pcmFramesToDecode == 0) { + return DRFLAC_TRUE; + } + + pFlac->currentPCMFrame = runningPCMFrameCount; + + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */ + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; /* CRC mismatch. Pretend this frame never existed. */ + } else { + return DRFLAC_FALSE; + } + } + } else { + /* + It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + frame never existed and leave the running sample count untouched. + */ + drflac_result result = drflac__seek_to_next_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningPCMFrameCount += pcmFrameCountInThisFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; /* CRC mismatch. Pretend this frame never existed. */ + } else { + return DRFLAC_FALSE; + } + } + } + } +} + + + +static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +{ + drflac_ogg_page_header header; + drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + drflac_uint32 bytesRead = 0; + + /* Pre Condition: The bit stream should be sitting just past the 4-byte OggS capture pattern. */ + (void)relaxed; + + pInit->container = drflac_container_ogg; + pInit->oggFirstBytePos = 0; + + /* + We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the + stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if + any match the FLAC specification. Important to keep in mind that the stream may be multiplexed. + */ + if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; + } + pInit->runningFilePos += bytesRead; + + for (;;) { + int pageBodySize; + + /* Break if we're past the beginning of stream page. */ + if ((header.headerType & 0x02) == 0) { + return DRFLAC_FALSE; + } + + /* Check if it's a FLAC header. */ + pageBodySize = drflac_ogg__get_page_body_size(&header); + if (pageBodySize == 51) { /* 51 = the lacing value of the FLAC header packet. */ + /* It could be a FLAC page... */ + drflac_uint32 bytesRemainingInPage = pageBodySize; + drflac_uint8 packetType; + + if (onRead(pUserData, &packetType, 1) != 1) { + return DRFLAC_FALSE; + } + + bytesRemainingInPage -= 1; + if (packetType == 0x7F) { + /* Increasingly more likely to be a FLAC page... */ + drflac_uint8 sig[4]; + if (onRead(pUserData, sig, 4) != 4) { + return DRFLAC_FALSE; + } + + bytesRemainingInPage -= 4; + if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { + /* Almost certainly a FLAC page... */ + drflac_uint8 mappingVersion[2]; + if (onRead(pUserData, mappingVersion, 2) != 2) { + return DRFLAC_FALSE; + } + + if (mappingVersion[0] != 1) { + return DRFLAC_FALSE; /* Only supporting version 1.x of the Ogg mapping. */ + } + + /* + The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to + be handling it in a generic way based on the serial number and packet types. + */ + if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + + /* Expecting the native FLAC signature "fLaC". */ + if (onRead(pUserData, sig, 4) != 4) { + return DRFLAC_FALSE; + } + + if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { + /* The remaining data in the page should be the STREAMINFO block. */ + drflac_streaminfo streaminfo; + drflac_uint8 isLastBlock; + drflac_uint8 blockType; + drflac_uint32 blockSize; + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return DRFLAC_FALSE; + } + + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + return DRFLAC_FALSE; /* Invalid block type. First block must be the STREAMINFO block. */ + } + + if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + /* Success! */ + pInit->hasStreamInfoBlock = DRFLAC_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; + pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; + pInit->hasMetadataBlocks = !isLastBlock; + + if (onMeta) { + drflac_metadata metadata; + metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + metadata.data.streaminfo = streaminfo; + onMeta(pUserDataMD, &metadata); + } + + pInit->runningFilePos += pageBodySize; + pInit->oggFirstBytePos = pInit->runningFilePos - 79; /* Subtracting 79 will place us right on top of the "OggS" identifier of the FLAC bos page. */ + pInit->oggSerial = header.serialNumber; + pInit->oggBosHeader = header; + break; + } else { + /* Failed to read STREAMINFO block. Aww, so close... */ + return DRFLAC_FALSE; + } + } else { + /* Invalid file. */ + return DRFLAC_FALSE; + } + } else { + /* Not a FLAC header. Skip it. */ + if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + } else { + /* Not a FLAC header. Seek past the entire page and move on to the next. */ + if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + } else { + if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + + pInit->runningFilePos += pageBodySize; + + + /* Read the header of the next page. */ + if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; + } + pInit->runningFilePos += bytesRead; + } + + /* + If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next + packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialization phase for Ogg is to create the + Ogg bistream object. + */ + pInit->hasMetadataBlocks = DRFLAC_TRUE; /* <-- Always have at least VORBIS_COMMENT metadata block. */ + return DRFLAC_TRUE; +} +#endif + +static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) +{ + drflac_bool32 relaxed; + drflac_uint8 id[4]; + + if (pInit == NULL || onRead == NULL || onSeek == NULL) { + return DRFLAC_FALSE; + } + + DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); + pInit->onRead = onRead; + pInit->onSeek = onSeek; + pInit->onMeta = onMeta; + pInit->container = container; + pInit->pUserData = pUserData; + pInit->pUserDataMD = pUserDataMD; + + pInit->bs.onRead = onRead; + pInit->bs.onSeek = onSeek; + pInit->bs.pUserData = pUserData; + drflac__reset_cache(&pInit->bs); + + + /* If the container is explicitly defined then we can try opening in relaxed mode. */ + relaxed = container != drflac_container_unknown; + + /* Skip over any ID3 tags. */ + for (;;) { + if (onRead(pUserData, id, 4) != 4) { + return DRFLAC_FALSE; /* Ran out of data. */ + } + pInit->runningFilePos += 4; + + if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { + drflac_uint8 header[6]; + drflac_uint8 flags; + drflac_uint32 headerSize; + + if (onRead(pUserData, header, 6) != 6) { + return DRFLAC_FALSE; /* Ran out of data. */ + } + pInit->runningFilePos += 6; + + flags = header[1]; + + DRFLAC_COPY_MEMORY(&headerSize, header+2, 4); + headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); + if (flags & 0x10) { + headerSize += 10; + } + + if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; /* Failed to seek past the tag. */ + } + pInit->runningFilePos += headerSize; + } else { + break; + } + } + + if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#ifndef DR_FLAC_NO_OGG + if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { + return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#endif + + /* If we get here it means we likely don't have a header. Try opening in relaxed mode, if applicable. */ + if (relaxed) { + if (container == drflac_container_native) { + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#ifndef DR_FLAC_NO_OGG + if (container == drflac_container_ogg) { + return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#endif + } + + /* Unsupported container. */ + return DRFLAC_FALSE; +} + +static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) +{ + DRFLAC_ASSERT(pFlac != NULL); + DRFLAC_ASSERT(pInit != NULL); + + DRFLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); + pFlac->bs = pInit->bs; + pFlac->onMeta = pInit->onMeta; + pFlac->pUserDataMD = pInit->pUserDataMD; + pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; + pFlac->sampleRate = pInit->sampleRate; + pFlac->channels = (drflac_uint8)pInit->channels; + pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample; + pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; + pFlac->container = pInit->container; +} + + +static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac_init_info init; + drflac_uint32 allocationSize; + drflac_uint32 wholeSIMDVectorCountPerChannel; + drflac_uint32 decodedSamplesAllocationSize; +#ifndef DR_FLAC_NO_OGG + drflac_oggbs* pOggbs = NULL; +#endif + drflac_uint64 firstFramePos; + drflac_uint64 seektablePos; + drflac_uint32 seekpointCount; + drflac_allocation_callbacks allocationCallbacks; + drflac* pFlac; + + /* CPU support first. */ + drflac__init_cpu_caps(); + + if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { + return NULL; + } + + if (pAllocationCallbacks != NULL) { + allocationCallbacks = *pAllocationCallbacks; + if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) { + return NULL; /* Invalid allocation callbacks. */ + } + } else { + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = drflac__malloc_default; + allocationCallbacks.onRealloc = drflac__realloc_default; + allocationCallbacks.onFree = drflac__free_default; + } + + + /* + The size of the allocation for the drflac object needs to be large enough to fit the following: + 1) The main members of the drflac structure + 2) A block of memory large enough to store the decoded samples of the largest frame in the stream + 3) If the container is Ogg, a drflac_oggbs object + + The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration + the different SIMD instruction sets. + */ + allocationSize = sizeof(drflac); + + /* + The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector + we are supporting. + */ + if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); + } else { + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1; + } + + decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; + + allocationSize += decodedSamplesAllocationSize; + allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; /* Allocate extra bytes to ensure we have enough for alignment. */ + +#ifndef DR_FLAC_NO_OGG + /* There's additional data required for Ogg streams. */ + if (init.container == drflac_container_ogg) { + allocationSize += sizeof(drflac_oggbs); + + pOggbs = (drflac_oggbs*)drflac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); + if (pOggbs == NULL) { + return NULL; /*DRFLAC_OUT_OF_MEMORY;*/ + } + + DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); + pOggbs->onRead = onRead; + pOggbs->onSeek = onSeek; + pOggbs->pUserData = pUserData; + pOggbs->currentBytePos = init.oggFirstBytePos; + pOggbs->firstBytePos = init.oggFirstBytePos; + pOggbs->serialNumber = init.oggSerial; + pOggbs->bosPageHeader = init.oggBosHeader; + pOggbs->bytesRemainingInPage = 0; + } +#endif + + /* + This part is a bit awkward. We need to load the seektable so that it can be referenced in-memory, but I want the drflac object to + consist of only a single heap allocation. To this, the size of the seek table needs to be known, which we determine when reading + and decoding the metadata. + */ + firstFramePos = 42; /* <-- We know we are at byte 42 at this point. */ + seektablePos = 0; + seekpointCount = 0; + if (init.hasMetadataBlocks) { + drflac_read_proc onReadOverride = onRead; + drflac_seek_proc onSeekOverride = onSeek; + void* pUserDataOverride = pUserData; + +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { + onReadOverride = drflac__on_read_ogg; + onSeekOverride = drflac__on_seek_ogg; + pUserDataOverride = (void*)pOggbs; + } +#endif + + if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { + #ifndef DR_FLAC_NO_OGG + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif + return NULL; + } + + allocationSize += seekpointCount * sizeof(drflac_seekpoint); + } + + + pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks); + if (pFlac == NULL) { + #ifndef DR_FLAC_NO_OGG + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif + return NULL; + } + + drflac__init_from_info(pFlac, &init); + pFlac->allocationCallbacks = allocationCallbacks; + pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); + +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { + drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(drflac_seekpoint))); + DRFLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); + + /* At this point the pOggbs object has been handed over to pInternalOggbs and can be freed. */ + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + pOggbs = NULL; + + /* The Ogg bistream needs to be layered on top of the original bitstream. */ + pFlac->bs.onRead = drflac__on_read_ogg; + pFlac->bs.onSeek = drflac__on_seek_ogg; + pFlac->bs.pUserData = (void*)pInternalOggbs; + pFlac->_oggbs = (void*)pInternalOggbs; + } +#endif + + pFlac->firstFLACFramePosInBytes = firstFramePos; + + /* NOTE: Seektables are not currently compatible with Ogg encapsulation (Ogg has its own accelerated seeking system). I may change this later, so I'm leaving this here for now. */ +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) + { + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + else +#endif + { + /* If we have a seektable we need to load it now, making sure we move back to where we were previously. */ + if (seektablePos != 0) { + pFlac->seekpointCount = seekpointCount; + pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); + + DRFLAC_ASSERT(pFlac->bs.onSeek != NULL); + DRFLAC_ASSERT(pFlac->bs.onRead != NULL); + + /* Seek to the seektable, then just read directly into our seektable buffer. */ + if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { + drflac_uint32 iSeekpoint; + + for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { + if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) == DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { + /* Endian swap. */ + pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); + pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); + pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); + } else { + /* Failed to read the seektable. Pretend we don't have one. */ + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + break; + } + } + + /* We need to seek back to where we were. If this fails it's a critical error. */ + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) { + drflac__free_from_callbacks(pFlac, &allocationCallbacks); + return NULL; + } + } else { + /* Failed to seek to the seektable. Ominous sign, but for now we can just pretend we don't have one. */ + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + } + } + + + /* + If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode + the first frame. + */ + if (!init.hasStreamInfoBlock) { + pFlac->currentFLACFrame.header = init.firstFrameHeader; + for (;;) { + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + break; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + drflac__free_from_callbacks(pFlac, &allocationCallbacks); + return NULL; + } + continue; + } else { + drflac__free_from_callbacks(pFlac, &allocationCallbacks); + return NULL; + } + } + } + } + + return pFlac; +} + + + +#ifndef DR_FLAC_NO_STDIO +#include +#ifndef DR_FLAC_NO_WCHAR +#include /* For wcslen(), wcsrtombs() */ +#endif + +/* drflac_result_from_errno() is only used for fopen() and wfopen() so putting it inside DR_WAV_NO_STDIO for now. If something else needs this later we can move it out. */ +#include +static drflac_result drflac_result_from_errno(int e) +{ + switch (e) + { + case 0: return DRFLAC_SUCCESS; + #ifdef EPERM + case EPERM: return DRFLAC_INVALID_OPERATION; + #endif + #ifdef ENOENT + case ENOENT: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef ESRCH + case ESRCH: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef EINTR + case EINTR: return DRFLAC_INTERRUPT; + #endif + #ifdef EIO + case EIO: return DRFLAC_IO_ERROR; + #endif + #ifdef ENXIO + case ENXIO: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef E2BIG + case E2BIG: return DRFLAC_INVALID_ARGS; + #endif + #ifdef ENOEXEC + case ENOEXEC: return DRFLAC_INVALID_FILE; + #endif + #ifdef EBADF + case EBADF: return DRFLAC_INVALID_FILE; + #endif + #ifdef ECHILD + case ECHILD: return DRFLAC_ERROR; + #endif + #ifdef EAGAIN + case EAGAIN: return DRFLAC_UNAVAILABLE; + #endif + #ifdef ENOMEM + case ENOMEM: return DRFLAC_OUT_OF_MEMORY; + #endif + #ifdef EACCES + case EACCES: return DRFLAC_ACCESS_DENIED; + #endif + #ifdef EFAULT + case EFAULT: return DRFLAC_BAD_ADDRESS; + #endif + #ifdef ENOTBLK + case ENOTBLK: return DRFLAC_ERROR; + #endif + #ifdef EBUSY + case EBUSY: return DRFLAC_BUSY; + #endif + #ifdef EEXIST + case EEXIST: return DRFLAC_ALREADY_EXISTS; + #endif + #ifdef EXDEV + case EXDEV: return DRFLAC_ERROR; + #endif + #ifdef ENODEV + case ENODEV: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef ENOTDIR + case ENOTDIR: return DRFLAC_NOT_DIRECTORY; + #endif + #ifdef EISDIR + case EISDIR: return DRFLAC_IS_DIRECTORY; + #endif + #ifdef EINVAL + case EINVAL: return DRFLAC_INVALID_ARGS; + #endif + #ifdef ENFILE + case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES; + #endif + #ifdef EMFILE + case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES; + #endif + #ifdef ENOTTY + case ENOTTY: return DRFLAC_INVALID_OPERATION; + #endif + #ifdef ETXTBSY + case ETXTBSY: return DRFLAC_BUSY; + #endif + #ifdef EFBIG + case EFBIG: return DRFLAC_TOO_BIG; + #endif + #ifdef ENOSPC + case ENOSPC: return DRFLAC_NO_SPACE; + #endif + #ifdef ESPIPE + case ESPIPE: return DRFLAC_BAD_SEEK; + #endif + #ifdef EROFS + case EROFS: return DRFLAC_ACCESS_DENIED; + #endif + #ifdef EMLINK + case EMLINK: return DRFLAC_TOO_MANY_LINKS; + #endif + #ifdef EPIPE + case EPIPE: return DRFLAC_BAD_PIPE; + #endif + #ifdef EDOM + case EDOM: return DRFLAC_OUT_OF_RANGE; + #endif + #ifdef ERANGE + case ERANGE: return DRFLAC_OUT_OF_RANGE; + #endif + #ifdef EDEADLK + case EDEADLK: return DRFLAC_DEADLOCK; + #endif + #ifdef ENAMETOOLONG + case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG; + #endif + #ifdef ENOLCK + case ENOLCK: return DRFLAC_ERROR; + #endif + #ifdef ENOSYS + case ENOSYS: return DRFLAC_NOT_IMPLEMENTED; + #endif + #ifdef ENOTEMPTY + case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY; + #endif + #ifdef ELOOP + case ELOOP: return DRFLAC_TOO_MANY_LINKS; + #endif + #ifdef ENOMSG + case ENOMSG: return DRFLAC_NO_MESSAGE; + #endif + #ifdef EIDRM + case EIDRM: return DRFLAC_ERROR; + #endif + #ifdef ECHRNG + case ECHRNG: return DRFLAC_ERROR; + #endif + #ifdef EL2NSYNC + case EL2NSYNC: return DRFLAC_ERROR; + #endif + #ifdef EL3HLT + case EL3HLT: return DRFLAC_ERROR; + #endif + #ifdef EL3RST + case EL3RST: return DRFLAC_ERROR; + #endif + #ifdef ELNRNG + case ELNRNG: return DRFLAC_OUT_OF_RANGE; + #endif + #ifdef EUNATCH + case EUNATCH: return DRFLAC_ERROR; + #endif + #ifdef ENOCSI + case ENOCSI: return DRFLAC_ERROR; + #endif + #ifdef EL2HLT + case EL2HLT: return DRFLAC_ERROR; + #endif + #ifdef EBADE + case EBADE: return DRFLAC_ERROR; + #endif + #ifdef EBADR + case EBADR: return DRFLAC_ERROR; + #endif + #ifdef EXFULL + case EXFULL: return DRFLAC_ERROR; + #endif + #ifdef ENOANO + case ENOANO: return DRFLAC_ERROR; + #endif + #ifdef EBADRQC + case EBADRQC: return DRFLAC_ERROR; + #endif + #ifdef EBADSLT + case EBADSLT: return DRFLAC_ERROR; + #endif + #ifdef EBFONT + case EBFONT: return DRFLAC_INVALID_FILE; + #endif + #ifdef ENOSTR + case ENOSTR: return DRFLAC_ERROR; + #endif + #ifdef ENODATA + case ENODATA: return DRFLAC_NO_DATA_AVAILABLE; + #endif + #ifdef ETIME + case ETIME: return DRFLAC_TIMEOUT; + #endif + #ifdef ENOSR + case ENOSR: return DRFLAC_NO_DATA_AVAILABLE; + #endif + #ifdef ENONET + case ENONET: return DRFLAC_NO_NETWORK; + #endif + #ifdef ENOPKG + case ENOPKG: return DRFLAC_ERROR; + #endif + #ifdef EREMOTE + case EREMOTE: return DRFLAC_ERROR; + #endif + #ifdef ENOLINK + case ENOLINK: return DRFLAC_ERROR; + #endif + #ifdef EADV + case EADV: return DRFLAC_ERROR; + #endif + #ifdef ESRMNT + case ESRMNT: return DRFLAC_ERROR; + #endif + #ifdef ECOMM + case ECOMM: return DRFLAC_ERROR; + #endif + #ifdef EPROTO + case EPROTO: return DRFLAC_ERROR; + #endif + #ifdef EMULTIHOP + case EMULTIHOP: return DRFLAC_ERROR; + #endif + #ifdef EDOTDOT + case EDOTDOT: return DRFLAC_ERROR; + #endif + #ifdef EBADMSG + case EBADMSG: return DRFLAC_BAD_MESSAGE; + #endif + #ifdef EOVERFLOW + case EOVERFLOW: return DRFLAC_TOO_BIG; + #endif + #ifdef ENOTUNIQ + case ENOTUNIQ: return DRFLAC_NOT_UNIQUE; + #endif + #ifdef EBADFD + case EBADFD: return DRFLAC_ERROR; + #endif + #ifdef EREMCHG + case EREMCHG: return DRFLAC_ERROR; + #endif + #ifdef ELIBACC + case ELIBACC: return DRFLAC_ACCESS_DENIED; + #endif + #ifdef ELIBBAD + case ELIBBAD: return DRFLAC_INVALID_FILE; + #endif + #ifdef ELIBSCN + case ELIBSCN: return DRFLAC_INVALID_FILE; + #endif + #ifdef ELIBMAX + case ELIBMAX: return DRFLAC_ERROR; + #endif + #ifdef ELIBEXEC + case ELIBEXEC: return DRFLAC_ERROR; + #endif + #ifdef EILSEQ + case EILSEQ: return DRFLAC_INVALID_DATA; + #endif + #ifdef ERESTART + case ERESTART: return DRFLAC_ERROR; + #endif + #ifdef ESTRPIPE + case ESTRPIPE: return DRFLAC_ERROR; + #endif + #ifdef EUSERS + case EUSERS: return DRFLAC_ERROR; + #endif + #ifdef ENOTSOCK + case ENOTSOCK: return DRFLAC_NOT_SOCKET; + #endif + #ifdef EDESTADDRREQ + case EDESTADDRREQ: return DRFLAC_NO_ADDRESS; + #endif + #ifdef EMSGSIZE + case EMSGSIZE: return DRFLAC_TOO_BIG; + #endif + #ifdef EPROTOTYPE + case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL; + #endif + #ifdef ENOPROTOOPT + case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE; + #endif + #ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED; + #endif + #ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED; + #endif + #ifdef EOPNOTSUPP + case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION; + #endif + #ifdef EPFNOSUPPORT + case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EAFNOSUPPORT + case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EADDRINUSE + case EADDRINUSE: return DRFLAC_ALREADY_IN_USE; + #endif + #ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: return DRFLAC_ERROR; + #endif + #ifdef ENETDOWN + case ENETDOWN: return DRFLAC_NO_NETWORK; + #endif + #ifdef ENETUNREACH + case ENETUNREACH: return DRFLAC_NO_NETWORK; + #endif + #ifdef ENETRESET + case ENETRESET: return DRFLAC_NO_NETWORK; + #endif + #ifdef ECONNABORTED + case ECONNABORTED: return DRFLAC_NO_NETWORK; + #endif + #ifdef ECONNRESET + case ECONNRESET: return DRFLAC_CONNECTION_RESET; + #endif + #ifdef ENOBUFS + case ENOBUFS: return DRFLAC_NO_SPACE; + #endif + #ifdef EISCONN + case EISCONN: return DRFLAC_ALREADY_CONNECTED; + #endif + #ifdef ENOTCONN + case ENOTCONN: return DRFLAC_NOT_CONNECTED; + #endif + #ifdef ESHUTDOWN + case ESHUTDOWN: return DRFLAC_ERROR; + #endif + #ifdef ETOOMANYREFS + case ETOOMANYREFS: return DRFLAC_ERROR; + #endif + #ifdef ETIMEDOUT + case ETIMEDOUT: return DRFLAC_TIMEOUT; + #endif + #ifdef ECONNREFUSED + case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED; + #endif + #ifdef EHOSTDOWN + case EHOSTDOWN: return DRFLAC_NO_HOST; + #endif + #ifdef EHOSTUNREACH + case EHOSTUNREACH: return DRFLAC_NO_HOST; + #endif + #ifdef EALREADY + case EALREADY: return DRFLAC_IN_PROGRESS; + #endif + #ifdef EINPROGRESS + case EINPROGRESS: return DRFLAC_IN_PROGRESS; + #endif + #ifdef ESTALE + case ESTALE: return DRFLAC_INVALID_FILE; + #endif + #ifdef EUCLEAN + case EUCLEAN: return DRFLAC_ERROR; + #endif + #ifdef ENOTNAM + case ENOTNAM: return DRFLAC_ERROR; + #endif + #ifdef ENAVAIL + case ENAVAIL: return DRFLAC_ERROR; + #endif + #ifdef EISNAM + case EISNAM: return DRFLAC_ERROR; + #endif + #ifdef EREMOTEIO + case EREMOTEIO: return DRFLAC_IO_ERROR; + #endif + #ifdef EDQUOT + case EDQUOT: return DRFLAC_NO_SPACE; + #endif + #ifdef ENOMEDIUM + case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef EMEDIUMTYPE + case EMEDIUMTYPE: return DRFLAC_ERROR; + #endif + #ifdef ECANCELED + case ECANCELED: return DRFLAC_CANCELLED; + #endif + #ifdef ENOKEY + case ENOKEY: return DRFLAC_ERROR; + #endif + #ifdef EKEYEXPIRED + case EKEYEXPIRED: return DRFLAC_ERROR; + #endif + #ifdef EKEYREVOKED + case EKEYREVOKED: return DRFLAC_ERROR; + #endif + #ifdef EKEYREJECTED + case EKEYREJECTED: return DRFLAC_ERROR; + #endif + #ifdef EOWNERDEAD + case EOWNERDEAD: return DRFLAC_ERROR; + #endif + #ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: return DRFLAC_ERROR; + #endif + #ifdef ERFKILL + case ERFKILL: return DRFLAC_ERROR; + #endif + #ifdef EHWPOISON + case EHWPOISON: return DRFLAC_ERROR; + #endif + default: return DRFLAC_ERROR; + } +} + +static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err; +#endif + + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRFLAC_INVALID_ARGS; + } + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + err = fopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drflac_result_from_errno(err); + } +#else +#if defined(_WIN32) || defined(__APPLE__) + *ppFile = fopen(pFilePath, pOpenMode); +#else + #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) + *ppFile = fopen64(pFilePath, pOpenMode); + #else + *ppFile = fopen(pFilePath, pOpenMode); + #endif +#endif + if (*ppFile == NULL) { + drflac_result result = drflac_result_from_errno(errno); + if (result == DRFLAC_SUCCESS) { + result = DRFLAC_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ + } + + return result; + } +#endif + + return DRFLAC_SUCCESS; +} + +/* +_wfopen() isn't always available in all compilation environments. + + * Windows only. + * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). + * MinGW-64 (both 32- and 64-bit) seems to support it. + * MinGW wraps it in !defined(__STRICT_ANSI__). + * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). + +This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() +fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. +*/ +#if defined(_WIN32) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #define DRFLAC_HAS_WFOPEN + #endif +#endif + +#ifndef DR_FLAC_NO_WCHAR +static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRFLAC_INVALID_ARGS; + } + +#if defined(DRFLAC_HAS_WFOPEN) + { + /* Use _wfopen() on Windows. */ + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drflac_result_from_errno(err); + } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return drflac_result_from_errno(errno); + } + #endif + (void)pAllocationCallbacks; + } +#else + /* + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because + fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note + that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler + error I'll look into improving compatibility. + */ + + /* + Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just + need to abort with an error. If you encounter a compiler lacking such support, add it to this list + and submit a bug report and it'll be added to the library upstream. + */ + #if defined(__DJGPP__) + { + /* Nothing to do here. This will fall through to the error check below. */ + } + #else + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + char pOpenModeMB[32] = {0}; + + /* Get the length first. */ + DRFLAC_ZERO_OBJECT(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) { + return drflac_result_from_errno(errno); + } + + pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) { + return DRFLAC_OUT_OF_MEMORY; + } + + pFilePathTemp = pFilePath; + DRFLAC_ZERO_OBJECT(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); + + /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ + { + size_t i = 0; + for (;;) { + if (pOpenMode[i] == 0) { + pOpenModeMB[i] = '\0'; + break; + } + + pOpenModeMB[i] = (char)pOpenMode[i]; + i += 1; + } + } + + *ppFile = fopen(pFilePathMB, pOpenModeMB); + + drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); + } + #endif + + if (*ppFile == NULL) { + return DRFLAC_ERROR; + } +#endif + + return DRFLAC_SUCCESS; +} +#endif + +static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); +} + +static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +{ + DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ + + return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} + + +DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + FILE* pFile; + + if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { + return NULL; + } + + pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return NULL; + } + + return pFlac; +} + +#ifndef DR_FLAC_NO_WCHAR +DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + FILE* pFile; + + if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { + return NULL; + } + + pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return NULL; + } + + return pFlac; +} +#endif + +DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + FILE* pFile; + + if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { + return NULL; + } + + pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return pFlac; + } + + return pFlac; +} + +#ifndef DR_FLAC_NO_WCHAR +DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + FILE* pFile; + + if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { + return NULL; + } + + pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return pFlac; + } + + return pFlac; +} +#endif +#endif /* DR_FLAC_NO_STDIO */ + +static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + size_t bytesRemaining; + + DRFLAC_ASSERT(memoryStream != NULL); + DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); + + bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesToRead > 0) { + DRFLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); + memoryStream->currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + + DRFLAC_ASSERT(memoryStream != NULL); + DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ + + if (offset > (drflac_int64)memoryStream->dataSize) { + return DRFLAC_FALSE; + } + + if (origin == drflac_seek_origin_current) { + if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { + memoryStream->currentReadPos += offset; + } else { + return DRFLAC_FALSE; /* Trying to seek too far forward. */ + } + } else { + if ((drflac_uint32)offset <= memoryStream->dataSize) { + memoryStream->currentReadPos = offset; + } else { + return DRFLAC_FALSE; /* Trying to seek too far forward. */ + } + } + + return DRFLAC_TRUE; +} + +DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac__memory_stream memoryStream; + drflac* pFlac; + + memoryStream.data = (const drflac_uint8*)pData; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + pFlac->memoryStream = memoryStream; + + /* This is an awful hack... */ +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + + return pFlac; +} + +DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac__memory_stream memoryStream; + drflac* pFlac; + + memoryStream.data = (const drflac_uint8*)pData; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + pFlac->memoryStream = memoryStream; + + /* This is an awful hack... */ +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + + return pFlac; +} + + + +DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); +} +DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); +} + +DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); +} +DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); +} + +DRFLAC_API void drflac_close(drflac* pFlac) +{ + if (pFlac == NULL) { + return; + } + +#ifndef DR_FLAC_NO_STDIO + /* + If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file() + was used by looking at the callbacks. + */ + if (pFlac->bs.onRead == drflac__on_read_stdio) { + fclose((FILE*)pFlac->bs.pUserData); + } + +#ifndef DR_FLAC_NO_OGG + /* Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained. */ + if (pFlac->container == drflac_container_ogg) { + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg); + + if (oggbs->onRead == drflac__on_read_stdio) { + fclose((FILE*)oggbs->pUserData); + } + } +#endif +#endif + + drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + for (i = 0; i < frameCount; ++i) { + drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 right0 = left0 - side0; + drflac_uint32 right1 = left1 - side1; + drflac_uint32 right2 = left2 - side2; + drflac_uint32 right3 = left3 - side3; + + pOutputSamples[i*8+0] = (drflac_int32)left0; + pOutputSamples[i*8+1] = (drflac_int32)right0; + pOutputSamples[i*8+2] = (drflac_int32)left1; + pOutputSamples[i*8+3] = (drflac_int32)right1; + pOutputSamples[i*8+4] = (drflac_int32)left2; + pOutputSamples[i*8+5] = (drflac_int32)right2; + pOutputSamples[i*8+6] = (drflac_int32)left3; + pOutputSamples[i*8+7] = (drflac_int32)right3; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i right = _mm_sub_epi32(left, side); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; + + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); + + drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + for (i = 0; i < frameCount; ++i) { + drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 left0 = right0 + side0; + drflac_uint32 left1 = right1 + side1; + drflac_uint32 left2 = right2 + side2; + drflac_uint32 left3 = right3 + side3; + + pOutputSamples[i*8+0] = (drflac_int32)left0; + pOutputSamples[i*8+1] = (drflac_int32)right0; + pOutputSamples[i*8+2] = (drflac_int32)left1; + pOutputSamples[i*8+3] = (drflac_int32)right1; + pOutputSamples[i*8+4] = (drflac_int32)left2; + pOutputSamples[i*8+5] = (drflac_int32)right2; + pOutputSamples[i*8+6] = (drflac_int32)left3; + pOutputSamples[i*8+7] = (drflac_int32)right3; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + for (i = 0; i < frameCount4; ++i) { + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i left = _mm_add_epi32(right, side); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; + + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); + + drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + for (drflac_uint64 i = 0; i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_int32 shift = unusedBitsPerSample; + + if (shift > 0) { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + + pOutputSamples[i*8+0] = (drflac_int32)temp0L; + pOutputSamples[i*8+1] = (drflac_int32)temp0R; + pOutputSamples[i*8+2] = (drflac_int32)temp1L; + pOutputSamples[i*8+3] = (drflac_int32)temp1R; + pOutputSamples[i*8+4] = (drflac_int32)temp2L; + pOutputSamples[i*8+5] = (drflac_int32)temp2R; + pOutputSamples[i*8+6] = (drflac_int32)temp3L; + pOutputSamples[i*8+7] = (drflac_int32)temp3R; + } + } else { + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); + temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); + temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); + temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); + + temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); + temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); + temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); + temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); + + pOutputSamples[i*8+0] = (drflac_int32)temp0L; + pOutputSamples[i*8+1] = (drflac_int32)temp0R; + pOutputSamples[i*8+2] = (drflac_int32)temp1L; + pOutputSamples[i*8+3] = (drflac_int32)temp1R; + pOutputSamples[i*8+4] = (drflac_int32)temp2L; + pOutputSamples[i*8+5] = (drflac_int32)temp2R; + pOutputSamples[i*8+6] = (drflac_int32)temp3L; + pOutputSamples[i*8+7] = (drflac_int32)temp3R; + } + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_int32 shift = unusedBitsPerSample; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + + left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); + right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; + } + } else { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + + left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); + right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); + } + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_int32 shift = unusedBitsPerSample; + int32x4_t wbpsShift0_4; /* wbps = Wasted Bits Per Sample */ + int32x4_t wbpsShift1_4; /* wbps = Wasted Bits Per Sample */ + uint32x4_t one4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + one4 = vdupq_n_u32(1); + + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); + + left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); + + drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; + } + } else { + int32x4_t shift4; + + shift -= 1; + shift4 = vdupq_n_s32(shift); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); + + left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); + + drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); + } + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + for (drflac_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + + pOutputSamples[i*8+0] = (drflac_int32)tempL0; + pOutputSamples[i*8+1] = (drflac_int32)tempR0; + pOutputSamples[i*8+2] = (drflac_int32)tempL1; + pOutputSamples[i*8+3] = (drflac_int32)tempR1; + pOutputSamples[i*8+4] = (drflac_int32)tempL2; + pOutputSamples[i*8+5] = (drflac_int32)tempR2; + pOutputSamples[i*8+6] = (drflac_int32)tempL3; + pOutputSamples[i*8+7] = (drflac_int32)tempR3; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + int32x4_t shift4_0 = vdupq_n_s32(shift0); + int32x4_t shift4_1 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + int32x4_t left; + int32x4_t right; + + left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); + right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); + + drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut) +{ + drflac_uint64 framesRead; + drflac_uint32 unusedBitsPerSample; + + if (pFlac == NULL || framesToRead == 0) { + return 0; + } + + if (pBufferOut == NULL) { + return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); + } + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); + unusedBitsPerSample = 32 - pFlac->bitsPerSample; + + framesRead = 0; + while (framesToRead > 0) { + /* If we've run out of samples in this frame, go to the next. */ + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + break; /* Couldn't read the next frame, so just break from the loop and return. */ + } + } else { + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + drflac_uint64 frameCountThisIteration = framesToRead; + + if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { + frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; + } + + if (channelCount == 2) { + const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + + switch (pFlac->currentFLACFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + } + } else { + /* Generic interleaving. */ + drflac_uint64 i; + for (i = 0; i < frameCountThisIteration; ++i) { + unsigned int j; + for (j = 0; j < channelCount; ++j) { + pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + } + } + } + + framesRead += frameCountThisIteration; + pBufferOut += frameCountThisIteration * channelCount; + framesToRead -= frameCountThisIteration; + pFlac->currentPCMFrame += frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; + } + } + + return framesRead; +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + for (i = 0; i < frameCount; ++i) { + drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 right = left - side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 right0 = left0 - side0; + drflac_uint32 right1 = left1 - side1; + drflac_uint32 right2 = left2 - side2; + drflac_uint32 right3 = left3 - side3; + + left0 >>= 16; + left1 >>= 16; + left2 >>= 16; + left3 >>= 16; + + right0 >>= 16; + right1 >>= 16; + right2 >>= 16; + right3 >>= 16; + + pOutputSamples[i*8+0] = (drflac_int16)left0; + pOutputSamples[i*8+1] = (drflac_int16)right0; + pOutputSamples[i*8+2] = (drflac_int16)left1; + pOutputSamples[i*8+3] = (drflac_int16)right1; + pOutputSamples[i*8+4] = (drflac_int16)left2; + pOutputSamples[i*8+5] = (drflac_int16)right2; + pOutputSamples[i*8+6] = (drflac_int16)left3; + pOutputSamples[i*8+7] = (drflac_int16)right3; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i right = _mm_sub_epi32(left, side); + + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; + + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); + + left = vshrq_n_u32(left, 16); + right = vshrq_n_u32(right, 16); + + drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + for (i = 0; i < frameCount; ++i) { + drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 left = right + side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 left0 = right0 + side0; + drflac_uint32 left1 = right1 + side1; + drflac_uint32 left2 = right2 + side2; + drflac_uint32 left3 = right3 + side3; + + left0 >>= 16; + left1 >>= 16; + left2 >>= 16; + left3 >>= 16; + + right0 >>= 16; + right1 >>= 16; + right2 >>= 16; + right3 >>= 16; + + pOutputSamples[i*8+0] = (drflac_int16)left0; + pOutputSamples[i*8+1] = (drflac_int16)right0; + pOutputSamples[i*8+2] = (drflac_int16)left1; + pOutputSamples[i*8+3] = (drflac_int16)right1; + pOutputSamples[i*8+4] = (drflac_int16)left2; + pOutputSamples[i*8+5] = (drflac_int16)right2; + pOutputSamples[i*8+6] = (drflac_int16)left3; + pOutputSamples[i*8+7] = (drflac_int16)right3; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + for (i = 0; i < frameCount4; ++i) { + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i left = _mm_add_epi32(right, side); + + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; + + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); + + left = vshrq_n_u32(left, 16); + right = vshrq_n_u32(right, 16); + + drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + for (drflac_uint64 i = 0; i < frameCount; ++i) { + drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; + + if (shift > 0) { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + + temp0L >>= 16; + temp1L >>= 16; + temp2L >>= 16; + temp3L >>= 16; + + temp0R >>= 16; + temp1R >>= 16; + temp2R >>= 16; + temp3R >>= 16; + + pOutputSamples[i*8+0] = (drflac_int16)temp0L; + pOutputSamples[i*8+1] = (drflac_int16)temp0R; + pOutputSamples[i*8+2] = (drflac_int16)temp1L; + pOutputSamples[i*8+3] = (drflac_int16)temp1R; + pOutputSamples[i*8+4] = (drflac_int16)temp2L; + pOutputSamples[i*8+5] = (drflac_int16)temp2R; + pOutputSamples[i*8+6] = (drflac_int16)temp3L; + pOutputSamples[i*8+7] = (drflac_int16)temp3R; + } + } else { + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = ((drflac_int32)(mid0 + side0) >> 1); + temp1L = ((drflac_int32)(mid1 + side1) >> 1); + temp2L = ((drflac_int32)(mid2 + side2) >> 1); + temp3L = ((drflac_int32)(mid3 + side3) >> 1); + + temp0R = ((drflac_int32)(mid0 - side0) >> 1); + temp1R = ((drflac_int32)(mid1 - side1) >> 1); + temp2R = ((drflac_int32)(mid2 - side2) >> 1); + temp3R = ((drflac_int32)(mid3 - side3) >> 1); + + temp0L >>= 16; + temp1L >>= 16; + temp2L >>= 16; + temp3L >>= 16; + + temp0R >>= 16; + temp1R >>= 16; + temp2R >>= 16; + temp3R >>= 16; + + pOutputSamples[i*8+0] = (drflac_int16)temp0L; + pOutputSamples[i*8+1] = (drflac_int16)temp0R; + pOutputSamples[i*8+2] = (drflac_int16)temp1L; + pOutputSamples[i*8+3] = (drflac_int16)temp1R; + pOutputSamples[i*8+4] = (drflac_int16)temp2L; + pOutputSamples[i*8+5] = (drflac_int16)temp2R; + pOutputSamples[i*8+6] = (drflac_int16)temp3L; + pOutputSamples[i*8+7] = (drflac_int16)temp3R; + } + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + + left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); + right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); + + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); + } + } else { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + + left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); + right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); + + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); + } + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; + int32x4_t wbpsShift0_4; /* wbps = Wasted Bits Per Sample */ + int32x4_t wbpsShift1_4; /* wbps = Wasted Bits Per Sample */ + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + + left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); + + left = vshrq_n_s32(left, 16); + right = vshrq_n_s32(right, 16); + + drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); + } + } else { + int32x4_t shift4; + + shift -= 1; + shift4 = vdupq_n_s32(shift); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + + left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); + + left = vshrq_n_s32(left, 16); + right = vshrq_n_s32(right, 16); + + drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); + } + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + for (drflac_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + + tempL0 >>= 16; + tempL1 >>= 16; + tempL2 >>= 16; + tempL3 >>= 16; + + tempR0 >>= 16; + tempR1 >>= 16; + tempR2 >>= 16; + tempR3 >>= 16; + + pOutputSamples[i*8+0] = (drflac_int16)tempL0; + pOutputSamples[i*8+1] = (drflac_int16)tempR0; + pOutputSamples[i*8+2] = (drflac_int16)tempL1; + pOutputSamples[i*8+3] = (drflac_int16)tempR1; + pOutputSamples[i*8+4] = (drflac_int16)tempL2; + pOutputSamples[i*8+5] = (drflac_int16)tempR2; + pOutputSamples[i*8+6] = (drflac_int16)tempL3; + pOutputSamples[i*8+7] = (drflac_int16)tempR3; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + + /* At this point we have results. We can now pack and interleave these into a single __m128i object and then store the in the output buffer. */ + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + int32x4_t shift0_4 = vdupq_n_s32(shift0); + int32x4_t shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + int32x4_t left; + int32x4_t right; + + left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); + right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); + + left = vshrq_n_s32(left, 16); + right = vshrq_n_s32(right, 16); + + drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut) +{ + drflac_uint64 framesRead; + drflac_uint32 unusedBitsPerSample; + + if (pFlac == NULL || framesToRead == 0) { + return 0; + } + + if (pBufferOut == NULL) { + return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); + } + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); + unusedBitsPerSample = 32 - pFlac->bitsPerSample; + + framesRead = 0; + while (framesToRead > 0) { + /* If we've run out of samples in this frame, go to the next. */ + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + break; /* Couldn't read the next frame, so just break from the loop and return. */ + } + } else { + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + drflac_uint64 frameCountThisIteration = framesToRead; + + if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { + frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; + } + + if (channelCount == 2) { + const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + + switch (pFlac->currentFLACFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + } + } else { + /* Generic interleaving. */ + drflac_uint64 i; + for (i = 0; i < frameCountThisIteration; ++i) { + unsigned int j; + for (j = 0; j < channelCount; ++j) { + drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16); + } + } + } + + framesRead += frameCountThisIteration; + pBufferOut += frameCountThisIteration * channelCount; + framesToRead -= frameCountThisIteration; + pFlac->currentPCMFrame += frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; + } + } + + return framesRead; +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + for (i = 0; i < frameCount; ++i) { + drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + float factor = 1 / 2147483648.0; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 right0 = left0 - side0; + drflac_uint32 right1 = left1 - side1; + drflac_uint32 right2 = left2 - side2; + drflac_uint32 right3 = left3 - side3; + + pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; + pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; + pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; + pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; + pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; + pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; + pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; + pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left * factor; + pOutputSamples[i*2+1] = (drflac_int32)right * factor; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + __m128 factor; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + factor = _mm_set1_ps(1.0f / 8388608.0f); + + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i right = _mm_sub_epi32(left, side); + __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); + __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); + + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + float32x4_t factor4; + int32x4_t shift0_4; + int32x4_t shift1_4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + factor4 = vdupq_n_f32(1.0f / 8388608.0f); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; + float32x4_t leftf; + float32x4_t rightf; + + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); + leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); + + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + for (i = 0; i < frameCount; ++i) { + drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + float factor = 1 / 2147483648.0; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 left0 = right0 + side0; + drflac_uint32 left1 = right1 + side1; + drflac_uint32 left2 = right2 + side2; + drflac_uint32 left3 = right3 + side3; + + pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; + pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; + pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; + pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; + pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; + pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; + pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; + pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left * factor; + pOutputSamples[i*2+1] = (drflac_int32)right * factor; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + __m128 factor; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + factor = _mm_set1_ps(1.0f / 8388608.0f); + + for (i = 0; i < frameCount4; ++i) { + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i left = _mm_add_epi32(right, side); + __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); + __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); + + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + float32x4_t factor4; + int32x4_t shift0_4; + int32x4_t shift1_4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + factor4 = vdupq_n_f32(1.0f / 8388608.0f); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; + float32x4_t leftf; + float32x4_t rightf; + + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); + leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); + + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + for (drflac_uint64 i = 0; i < frameCount; ++i) { + drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; + float factor = 1 / 2147483648.0; + + if (shift > 0) { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + + pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; + pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; + pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; + pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; + pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; + pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; + pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; + pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; + } + } else { + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); + temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); + temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); + temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); + + temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); + temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); + temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); + temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); + + pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; + pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; + pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; + pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; + pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; + pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; + pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; + pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; + } + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample - 8; + float factor; + __m128 factor128; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + factor = 1.0f / 8388608.0f; + factor128 = _mm_set1_ps(factor); + + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i tempL; + __m128i tempR; + __m128 leftf; + __m128 rightf; + + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + + tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); + tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); + + leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); + rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); + + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; + } + } else { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i tempL; + __m128i tempR; + __m128 leftf; + __m128 rightf; + + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + + tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); + tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); + + leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); + rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); + + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; + } + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample - 8; + float factor; + float32x4_t factor4; + int32x4_t shift4; + int32x4_t wbps0_4; /* Wasted Bits Per Sample */ + int32x4_t wbps1_4; /* Wasted Bits Per Sample */ + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + factor = 1.0f / 8388608.0f; + factor4 = vdupq_n_f32(factor); + wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + int32x4_t lefti; + int32x4_t righti; + float32x4_t leftf; + float32x4_t rightf; + + uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); + uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); + + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + + lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); + + leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); + + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; + } + } else { + shift -= 1; + shift4 = vdupq_n_s32(shift); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t lefti; + int32x4_t righti; + float32x4_t leftf; + float32x4_t rightf; + + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); + + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + + lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); + + leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); + + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; + } + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + for (drflac_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + float factor = 1 / 2147483648.0; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + + pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor; + pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor; + pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor; + pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor; + pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor; + pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor; + pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor; + pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + + float factor = 1.0f / 8388608.0f; + __m128 factor128 = _mm_set1_ps(factor); + + for (i = 0; i < frameCount4; ++i) { + __m128i lefti; + __m128i righti; + __m128 leftf; + __m128 rightf; + + lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + + leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128); + rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128); + + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + + float factor = 1.0f / 8388608.0f; + float32x4_t factor4 = vdupq_n_f32(factor); + int32x4_t shift0_4 = vdupq_n_s32(shift0); + int32x4_t shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + int32x4_t lefti; + int32x4_t righti; + float32x4_t leftf; + float32x4_t rightf; + + lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); + righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); + + leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); + + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut) +{ + drflac_uint64 framesRead; + drflac_uint32 unusedBitsPerSample; + + if (pFlac == NULL || framesToRead == 0) { + return 0; + } + + if (pBufferOut == NULL) { + return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); + } + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); + unusedBitsPerSample = 32 - pFlac->bitsPerSample; + + framesRead = 0; + while (framesToRead > 0) { + /* If we've run out of samples in this frame, go to the next. */ + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + break; /* Couldn't read the next frame, so just break from the loop and return. */ + } + } else { + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + drflac_uint64 frameCountThisIteration = framesToRead; + + if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { + frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; + } + + if (channelCount == 2) { + const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + + switch (pFlac->currentFLACFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + } + } else { + /* Generic interleaving. */ + drflac_uint64 i; + for (i = 0; i < frameCountThisIteration; ++i) { + unsigned int j; + for (j = 0; j < channelCount; ++j) { + drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); + } + } + } + + framesRead += frameCountThisIteration; + pBufferOut += frameCountThisIteration * channelCount; + framesToRead -= frameCountThisIteration; + pFlac->currentPCMFrame += frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration; + } + } + + return framesRead; +} + + +DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) +{ + if (pFlac == NULL) { + return DRFLAC_FALSE; + } + + /* Don't do anything if we're already on the seek point. */ + if (pFlac->currentPCMFrame == pcmFrameIndex) { + return DRFLAC_TRUE; + } + + /* + If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present + when the decoder was opened. + */ + if (pFlac->firstFLACFramePosInBytes == 0) { + return DRFLAC_FALSE; + } + + if (pcmFrameIndex == 0) { + pFlac->currentPCMFrame = 0; + return drflac__seek_to_first_frame(pFlac); + } else { + drflac_bool32 wasSuccessful = DRFLAC_FALSE; + drflac_uint64 originalPCMFrame = pFlac->currentPCMFrame; + + /* Clamp the sample to the end. */ + if (pcmFrameIndex > pFlac->totalPCMFrameCount) { + pcmFrameIndex = pFlac->totalPCMFrameCount; + } + + /* If the target sample and the current sample are in the same frame we just move the position forward. */ + if (pcmFrameIndex > pFlac->currentPCMFrame) { + /* Forward. */ + drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); + if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { + pFlac->currentFLACFrame.pcmFramesRemaining -= offset; + pFlac->currentPCMFrame = pcmFrameIndex; + return DRFLAC_TRUE; + } + } else { + /* Backward. */ + drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); + drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; + if (currentFLACFramePCMFramesConsumed > offsetAbs) { + pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; + pFlac->currentPCMFrame = pcmFrameIndex; + return DRFLAC_TRUE; + } + } + + /* + Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so + we'll instead use Ogg's natural seeking facility. + */ +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + wasSuccessful = drflac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); + } + else +#endif + { + /* First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. */ + if (/*!wasSuccessful && */!pFlac->_noSeekTableSeek) { + wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); + } + +#if !defined(DR_FLAC_NO_CRC) + /* Fall back to binary search if seek table seeking fails. This requires the length of the stream to be known. */ + if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { + wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); + } +#endif + + /* Fall back to brute force if all else fails. */ + if (!wasSuccessful && !pFlac->_noBruteForceSeek) { + wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); + } + } + + if (wasSuccessful) { + pFlac->currentPCMFrame = pcmFrameIndex; + } else { + /* Seek failed. Try putting the decoder back to it's original state. */ + if (drflac_seek_to_pcm_frame(pFlac, originalPCMFrame) == DRFLAC_FALSE) { + /* Failed to seek back to the original PCM frame. Fall back to 0. */ + drflac_seek_to_pcm_frame(pFlac, 0); + } + } + + return wasSuccessful; + } +} + + + +/* High Level APIs */ + +#if defined(SIZE_MAX) + #define DRFLAC_SIZE_MAX SIZE_MAX +#else + #if defined(DRFLAC_64BIT) + #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF) + #else + #define DRFLAC_SIZE_MAX 0xFFFFFFFF + #endif +#endif + + +/* Using a macro as the definition of the drflac__full_decode_and_close_*() API family. Sue me. */ +#define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ +static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\ +{ \ + type* pSampleData = NULL; \ + drflac_uint64 totalPCMFrameCount; \ + \ + DRFLAC_ASSERT(pFlac != NULL); \ + \ + totalPCMFrameCount = pFlac->totalPCMFrameCount; \ + \ + if (totalPCMFrameCount == 0) { \ + type buffer[4096]; \ + drflac_uint64 pcmFramesRead; \ + size_t sampleDataBufferSize = sizeof(buffer); \ + \ + pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ + if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ + type* pNewSampleData; \ + size_t newSampleDataBufferSize; \ + \ + newSampleDataBufferSize = sampleDataBufferSize * 2; \ + pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ + if (pNewSampleData == NULL) { \ + drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ + goto on_error; \ + } \ + \ + sampleDataBufferSize = newSampleDataBufferSize; \ + pSampleData = pNewSampleData; \ + } \ + \ + DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ + totalPCMFrameCount += pcmFramesRead; \ + } \ + \ + /* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \ + protect those ears from random noise! */ \ + DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ + } else { \ + drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ + if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \ + goto on_error; /* The decoded data is too big. */ \ + } \ + \ + pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); /* <-- Safe cast as per the check above. */ \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ + } \ + \ + if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ + if (channelsOut) *channelsOut = pFlac->channels; \ + if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ + \ + drflac_close(pFlac); \ + return pSampleData; \ + \ +on_error: \ + drflac_close(pFlac); \ + return NULL; \ +} + +DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32) +DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16) +DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) + +DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalPCMFrameCountOut) { + *totalPCMFrameCountOut = 0; + } + + pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); +} + +DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalPCMFrameCountOut) { + *totalPCMFrameCountOut = 0; + } + + pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); +} + +DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalPCMFrameCountOut) { + *totalPCMFrameCountOut = 0; + } + + pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); +} + +#ifndef DR_FLAC_NO_STDIO +DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + + pFlac = drflac_open_file(filename, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); +} + +DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + + pFlac = drflac_open_file(filename, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); +} + +DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + + pFlac = drflac_open_file(filename, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); +} +#endif + +DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + + pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); +} + +DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + + pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); +} + +DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + + pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); +} + + +DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + drflac__free_from_callbacks(p, pAllocationCallbacks); + } else { + drflac__free_default(p, NULL); + } +} + + + + +DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments) +{ + if (pIter == NULL) { + return; + } + + pIter->countRemaining = commentCount; + pIter->pRunningData = (const char*)pComments; +} + +DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) +{ + drflac_int32 length; + const char* pComment; + + /* Safety. */ + if (pCommentLengthOut) { + *pCommentLengthOut = 0; + } + + if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { + return NULL; + } + + length = drflac__le2host_32_ptr_unaligned(pIter->pRunningData); + pIter->pRunningData += 4; + + pComment = pIter->pRunningData; + pIter->pRunningData += length; + pIter->countRemaining -= 1; + + if (pCommentLengthOut) { + *pCommentLengthOut = length; + } + + return pComment; +} + + + + +DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData) +{ + if (pIter == NULL) { + return; + } + + pIter->countRemaining = trackCount; + pIter->pRunningData = (const char*)pTrackData; +} + +DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack) +{ + drflac_cuesheet_track cuesheetTrack; + const char* pRunningData; + drflac_uint64 offsetHi; + drflac_uint64 offsetLo; + + if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { + return DRFLAC_FALSE; + } + + pRunningData = pIter->pRunningData; + + offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + cuesheetTrack.offset = offsetLo | (offsetHi << 32); + cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; + DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; + cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; + cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; + cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; + cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index); + + pIter->pRunningData = pRunningData; + pIter->countRemaining -= 1; + + if (pCuesheetTrack) { + *pCuesheetTrack = cuesheetTrack; + } + + return DRFLAC_TRUE; +} + +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop +#endif +#endif /* dr_flac_c */ +#endif /* DR_FLAC_IMPLEMENTATION */ + + +/* +REVISION HISTORY +================ +v0.12.39 - 2022-09-17 + - Fix compilation with DJGPP. + - Fix compilation error with Visual Studio 2019 and the ARM build. + - Fix an error with SSE 4.1 detection. + - Add support for disabling wchar_t with DR_WAV_NO_WCHAR. + - Improve compatibility with compilers which lack support for explicit struct packing. + - Improve compatibility with low-end and embedded hardware by reducing the amount of stack + allocation when loading an Ogg encapsulated file. + +v0.12.38 - 2022-04-10 + - Fix compilation error on older versions of GCC. + +v0.12.37 - 2022-02-12 + - Improve ARM detection. + +v0.12.36 - 2022-02-07 + - Fix a compilation error with the ARM build. + +v0.12.35 - 2022-02-06 + - Fix a bug due to underestimating the amount of precision required for the prediction stage. + - Fix some bugs found from fuzz testing. + +v0.12.34 - 2022-01-07 + - Fix some misalignment bugs when reading metadata. + +v0.12.33 - 2021-12-22 + - Fix a bug with seeking when the seek table does not start at PCM frame 0. + +v0.12.32 - 2021-12-11 + - Fix a warning with Clang. + +v0.12.31 - 2021-08-16 + - Silence some warnings. + +v0.12.30 - 2021-07-31 + - Fix platform detection for ARM64. + +v0.12.29 - 2021-04-02 + - Fix a bug where the running PCM frame index is set to an invalid value when over-seeking. + - Fix a decoding error due to an incorrect validation check. + +v0.12.28 - 2021-02-21 + - Fix a warning due to referencing _MSC_VER when it is undefined. + +v0.12.27 - 2021-01-31 + - Fix a static analysis warning. + +v0.12.26 - 2021-01-17 + - Fix a compilation warning due to _BSD_SOURCE being deprecated. + +v0.12.25 - 2020-12-26 + - Update documentation. + +v0.12.24 - 2020-11-29 + - Fix ARM64/NEON detection when compiling with MSVC. + +v0.12.23 - 2020-11-21 + - Fix compilation with OpenWatcom. + +v0.12.22 - 2020-11-01 + - Fix an error with the previous release. + +v0.12.21 - 2020-11-01 + - Fix a possible deadlock when seeking. + - Improve compiler support for older versions of GCC. + +v0.12.20 - 2020-09-08 + - Fix a compilation error on older compilers. + +v0.12.19 - 2020-08-30 + - Fix a bug due to an undefined 32-bit shift. + +v0.12.18 - 2020-08-14 + - Fix a crash when compiling with clang-cl. + +v0.12.17 - 2020-08-02 + - Simplify sized types. + +v0.12.16 - 2020-07-25 + - Fix a compilation warning. + +v0.12.15 - 2020-07-06 + - Check for negative LPC shifts and return an error. + +v0.12.14 - 2020-06-23 + - Add include guard for the implementation section. + +v0.12.13 - 2020-05-16 + - Add compile-time and run-time version querying. + - DRFLAC_VERSION_MINOR + - DRFLAC_VERSION_MAJOR + - DRFLAC_VERSION_REVISION + - DRFLAC_VERSION_STRING + - drflac_version() + - drflac_version_string() + +v0.12.12 - 2020-04-30 + - Fix compilation errors with VC6. + +v0.12.11 - 2020-04-19 + - Fix some pedantic warnings. + - Fix some undefined behaviour warnings. + +v0.12.10 - 2020-04-10 + - Fix some bugs when trying to seek with an invalid seek table. + +v0.12.9 - 2020-04-05 + - Fix warnings. + +v0.12.8 - 2020-04-04 + - Add drflac_open_file_w() and drflac_open_file_with_metadata_w(). + - Fix some static analysis warnings. + - Minor documentation updates. + +v0.12.7 - 2020-03-14 + - Fix compilation errors with VC6. + +v0.12.6 - 2020-03-07 + - Fix compilation error with Visual Studio .NET 2003. + +v0.12.5 - 2020-01-30 + - Silence some static analysis warnings. + +v0.12.4 - 2020-01-29 + - Silence some static analysis warnings. + +v0.12.3 - 2019-12-02 + - Fix some warnings when compiling with GCC and the -Og flag. + - Fix a crash in out-of-memory situations. + - Fix potential integer overflow bug. + - Fix some static analysis warnings. + - Fix a possible crash when using custom memory allocators without a custom realloc() implementation. + - Fix a bug with binary search seeking where the bits per sample is not a multiple of 8. + +v0.12.2 - 2019-10-07 + - Internal code clean up. + +v0.12.1 - 2019-09-29 + - Fix some Clang Static Analyzer warnings. + - Fix an unused variable warning. + +v0.12.0 - 2019-09-23 + - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation + routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs: + - drflac_open() + - drflac_open_relaxed() + - drflac_open_with_metadata() + - drflac_open_with_metadata_relaxed() + - drflac_open_file() + - drflac_open_file_with_metadata() + - drflac_open_memory() + - drflac_open_memory_with_metadata() + - drflac_open_and_read_pcm_frames_s32() + - drflac_open_and_read_pcm_frames_s16() + - drflac_open_and_read_pcm_frames_f32() + - drflac_open_file_and_read_pcm_frames_s32() + - drflac_open_file_and_read_pcm_frames_s16() + - drflac_open_file_and_read_pcm_frames_f32() + - drflac_open_memory_and_read_pcm_frames_s32() + - drflac_open_memory_and_read_pcm_frames_s16() + - drflac_open_memory_and_read_pcm_frames_f32() + Set this extra parameter to NULL to use defaults which is the same as the previous behaviour. Setting this NULL will use + DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE. + - Remove deprecated APIs: + - drflac_read_s32() + - drflac_read_s16() + - drflac_read_f32() + - drflac_seek_to_sample() + - drflac_open_and_decode_s32() + - drflac_open_and_decode_s16() + - drflac_open_and_decode_f32() + - drflac_open_and_decode_file_s32() + - drflac_open_and_decode_file_s16() + - drflac_open_and_decode_file_f32() + - drflac_open_and_decode_memory_s32() + - drflac_open_and_decode_memory_s16() + - drflac_open_and_decode_memory_f32() + - Remove drflac.totalSampleCount which is now replaced with drflac.totalPCMFrameCount. You can emulate drflac.totalSampleCount + by doing pFlac->totalPCMFrameCount*pFlac->channels. + - Rename drflac.currentFrame to drflac.currentFLACFrame to remove ambiguity with PCM frames. + - Fix errors when seeking to the end of a stream. + - Optimizations to seeking. + - SSE improvements and optimizations. + - ARM NEON optimizations. + - Optimizations to drflac_read_pcm_frames_s16(). + - Optimizations to drflac_read_pcm_frames_s32(). + +v0.11.10 - 2019-06-26 + - Fix a compiler error. + +v0.11.9 - 2019-06-16 + - Silence some ThreadSanitizer warnings. + +v0.11.8 - 2019-05-21 + - Fix warnings. + +v0.11.7 - 2019-05-06 + - C89 fixes. + +v0.11.6 - 2019-05-05 + - Add support for C89. + - Fix a compiler warning when CRC is disabled. + - Change license to choice of public domain or MIT-0. + +v0.11.5 - 2019-04-19 + - Fix a compiler error with GCC. + +v0.11.4 - 2019-04-17 + - Fix some warnings with GCC when compiling with -std=c99. + +v0.11.3 - 2019-04-07 + - Silence warnings with GCC. + +v0.11.2 - 2019-03-10 + - Fix a warning. + +v0.11.1 - 2019-02-17 + - Fix a potential bug with seeking. + +v0.11.0 - 2018-12-16 + - API CHANGE: Deprecated drflac_read_s32(), drflac_read_s16() and drflac_read_f32() and replaced them with + drflac_read_pcm_frames_s32(), drflac_read_pcm_frames_s16() and drflac_read_pcm_frames_f32(). The new APIs take + and return PCM frame counts instead of sample counts. To upgrade you will need to change the input count by + dividing it by the channel count, and then do the same with the return value. + - API_CHANGE: Deprecated drflac_seek_to_sample() and replaced with drflac_seek_to_pcm_frame(). Same rules as + the changes to drflac_read_*() apply. + - API CHANGE: Deprecated drflac_open_and_decode_*() and replaced with drflac_open_*_and_read_*(). Same rules as + the changes to drflac_read_*() apply. + - Optimizations. + +v0.10.0 - 2018-09-11 + - Remove the DR_FLAC_NO_WIN32_IO option and the Win32 file IO functionality. If you need to use Win32 file IO you + need to do it yourself via the callback API. + - Fix the clang build. + - Fix undefined behavior. + - Fix errors with CUESHEET metdata blocks. + - Add an API for iterating over each cuesheet track in the CUESHEET metadata block. This works the same way as the + Vorbis comment API. + - Other miscellaneous bug fixes, mostly relating to invalid FLAC streams. + - Minor optimizations. + +v0.9.11 - 2018-08-29 + - Fix a bug with sample reconstruction. + +v0.9.10 - 2018-08-07 + - Improve 64-bit detection. + +v0.9.9 - 2018-08-05 + - Fix C++ build on older versions of GCC. + +v0.9.8 - 2018-07-24 + - Fix compilation errors. + +v0.9.7 - 2018-07-05 + - Fix a warning. + +v0.9.6 - 2018-06-29 + - Fix some typos. + +v0.9.5 - 2018-06-23 + - Fix some warnings. + +v0.9.4 - 2018-06-14 + - Optimizations to seeking. + - Clean up. + +v0.9.3 - 2018-05-22 + - Bug fix. + +v0.9.2 - 2018-05-12 + - Fix a compilation error due to a missing break statement. + +v0.9.1 - 2018-04-29 + - Fix compilation error with Clang. + +v0.9 - 2018-04-24 + - Fix Clang build. + - Start using major.minor.revision versioning. + +v0.8g - 2018-04-19 + - Fix build on non-x86/x64 architectures. + +v0.8f - 2018-02-02 + - Stop pretending to support changing rate/channels mid stream. + +v0.8e - 2018-02-01 + - Fix a crash when the block size of a frame is larger than the maximum block size defined by the FLAC stream. + - Fix a crash the the Rice partition order is invalid. + +v0.8d - 2017-09-22 + - Add support for decoding streams with ID3 tags. ID3 tags are just skipped. + +v0.8c - 2017-09-07 + - Fix warning on non-x86/x64 architectures. + +v0.8b - 2017-08-19 + - Fix build on non-x86/x64 architectures. + +v0.8a - 2017-08-13 + - A small optimization for the Clang build. + +v0.8 - 2017-08-12 + - API CHANGE: Rename dr_* types to drflac_*. + - Optimizations. This brings dr_flac back to about the same class of efficiency as the reference implementation. + - Add support for custom implementations of malloc(), realloc(), etc. + - Add CRC checking to Ogg encapsulated streams. + - Fix VC++ 6 build. This is only for the C++ compiler. The C compiler is not currently supported. + - Bug fixes. + +v0.7 - 2017-07-23 + - Add support for opening a stream without a header block. To do this, use drflac_open_relaxed() / drflac_open_with_metadata_relaxed(). + +v0.6 - 2017-07-22 + - Add support for recovering from invalid frames. With this change, dr_flac will simply skip over invalid frames as if they + never existed. Frames are checked against their sync code, the CRC-8 of the frame header and the CRC-16 of the whole frame. + +v0.5 - 2017-07-16 + - Fix typos. + - Change drflac_bool* types to unsigned. + - Add CRC checking. This makes dr_flac slower, but can be disabled with #define DR_FLAC_NO_CRC. + +v0.4f - 2017-03-10 + - Fix a couple of bugs with the bitstreaming code. + +v0.4e - 2017-02-17 + - Fix some warnings. + +v0.4d - 2016-12-26 + - Add support for 32-bit floating-point PCM decoding. + - Use drflac_int* and drflac_uint* sized types to improve compiler support. + - Minor improvements to documentation. + +v0.4c - 2016-12-26 + - Add support for signed 16-bit integer PCM decoding. + +v0.4b - 2016-10-23 + - A minor change to drflac_bool8 and drflac_bool32 types. + +v0.4a - 2016-10-11 + - Rename drBool32 to drflac_bool32 for styling consistency. + +v0.4 - 2016-09-29 + - API/ABI CHANGE: Use fixed size 32-bit booleans instead of the built-in bool type. + - API CHANGE: Rename drflac_open_and_decode*() to drflac_open_and_decode*_s32(). + - API CHANGE: Swap the order of "channels" and "sampleRate" parameters in drflac_open_and_decode*(). Rationale for this is to + keep it consistent with drflac_audio. + +v0.3f - 2016-09-21 + - Fix a warning with GCC. + +v0.3e - 2016-09-18 + - Fixed a bug where GCC 4.3+ was not getting properly identified. + - Fixed a few typos. + - Changed date formats to ISO 8601 (YYYY-MM-DD). + +v0.3d - 2016-06-11 + - Minor clean up. + +v0.3c - 2016-05-28 + - Fixed compilation error. + +v0.3b - 2016-05-16 + - Fixed Linux/GCC build. + - Updated documentation. + +v0.3a - 2016-05-15 + - Minor fixes to documentation. + +v0.3 - 2016-05-11 + - Optimizations. Now at about parity with the reference implementation on 32-bit builds. + - Lots of clean up. + +v0.2b - 2016-05-10 + - Bug fixes. + +v0.2a - 2016-05-10 + - Made drflac_open_and_decode() more robust. + - Removed an unused debugging variable + +v0.2 - 2016-05-09 + - Added support for Ogg encapsulation. + - API CHANGE. Have the onSeek callback take a third argument which specifies whether or not the seek + should be relative to the start or the current position. Also changes the seeking rules such that + seeking offsets will never be negative. + - Have drflac_open_and_decode() fail gracefully if the stream has an unknown total sample count. + +v0.1b - 2016-05-07 + - Properly close the file handle in drflac_open_file() and family when the decoder fails to initialize. + - Removed a stale comment. + +v0.1a - 2016-05-05 + - Minor formatting changes. + - Fixed a warning on the GCC build. + +v0.1 - 2016-05-03 + - Initial versioned release. +*/ + +/* +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright 2020 David Reid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ diff --git a/libraries/dr/dr_mp3.h b/libraries/dr/dr_mp3.h new file mode 100644 index 000000000..35d3b1a52 --- /dev/null +++ b/libraries/dr/dr_mp3.h @@ -0,0 +1,5705 @@ +/* +MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. +dr_mp3 - v0.6.34 - 2022-09-17 + +David Reid - mackron@gmail.com + +GitHub: https://github.com/mackron/dr_libs + +Based on minimp3 (https://github.com/lieff/minimp3) which is where the real work was done. See the +bottom of this file for differences between minimp3 and dr_mp3. +*/ + +/* +RELEASE NOTES - VERSION 0.6 +=========================== +Version 0.6 includes breaking changes with the configuration of decoders. The ability to customize +the number of output channels and the sample rate has been removed. You must now use the channel +count and sample rate reported by the MP3 stream itself, and all channel and sample rate conversion +must be done yourself. + + +Changes to Initialization +------------------------- +Previously, `drmp3_init()`, etc. took a pointer to a `drmp3_config` object that allowed you to +customize the output channels and sample rate. This has been removed. If you need the old behaviour +you will need to convert the data yourself or just not upgrade. The following APIs have changed. + + `drmp3_init()` + `drmp3_init_memory()` + `drmp3_init_file()` + + +Miscellaneous Changes +--------------------- +Support for loading a file from a `wchar_t` string has been added via the `drmp3_init_file_w()` API. +*/ + +/* +Introducation +============= +dr_mp3 is a single file library. To use it, do something like the following in one .c file. + + ```c + #define DR_MP3_IMPLEMENTATION + #include "dr_mp3.h" + ``` + +You can then #include this file in other parts of the program as you would with any other header +file. To decode audio data, do something like the following: + + ```c + drmp3 mp3; + if (!drmp3_init_file(&mp3, "MySong.mp3", NULL)) { + // Failed to open file + } + + ... + + drmp3_uint64 framesRead = drmp3_read_pcm_frames_f32(pMP3, framesToRead, pFrames); + ``` + +The drmp3 object is transparent so you can get access to the channel count and sample rate like so: + + ``` + drmp3_uint32 channels = mp3.channels; + drmp3_uint32 sampleRate = mp3.sampleRate; + ``` + +The example above initializes a decoder from a file, but you can also initialize it from a block of +memory and read and seek callbacks with `drmp3_init_memory()` and `drmp3_init()` respectively. + +You do not need to do any annoying memory management when reading PCM frames - this is all managed +internally. You can request any number of PCM frames in each call to `drmp3_read_pcm_frames_f32()` +and it will return as many PCM frames as it can, up to the requested amount. + +You can also decode an entire file in one go with `drmp3_open_and_read_pcm_frames_f32()`, +`drmp3_open_memory_and_read_pcm_frames_f32()` and `drmp3_open_file_and_read_pcm_frames_f32()`. + + +Build Options +============= +#define these options before including this file. + +#define DR_MP3_NO_STDIO + Disable drmp3_init_file(), etc. + +#define DR_MP3_NO_SIMD + Disable SIMD optimizations. +*/ + +#ifndef dr_mp3_h + #define dr_mp3_h + + #ifdef __cplusplus +extern "C" +{ + #endif + + #define DRMP3_STRINGIFY(x) #x + #define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) + + #define DRMP3_VERSION_MAJOR 0 + #define DRMP3_VERSION_MINOR 6 + #define DRMP3_VERSION_REVISION 34 + #define DRMP3_VERSION_STRING \ + DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) \ + "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) + + #include /* For size_t. */ + + /* Sized types. */ + typedef signed char drmp3_int8; + typedef unsigned char drmp3_uint8; + typedef signed short drmp3_int16; + typedef unsigned short drmp3_uint16; + typedef signed int drmp3_int32; + typedef unsigned int drmp3_uint32; + #if defined(_MSC_VER) && !defined(__clang__) + typedef signed __int64 drmp3_int64; + typedef unsigned __int64 drmp3_uint64; + #else + #if defined(__clang__) || \ + (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + #endif +typedef signed long long drmp3_int64; +typedef unsigned long long drmp3_uint64; + #if defined(__clang__) || \ + (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop + #endif + #endif + #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || \ + defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || \ + defined(_M_ARM64) || defined(__powerpc64__) + typedef drmp3_uint64 drmp3_uintptr; + #else +typedef drmp3_uint32 drmp3_uintptr; + #endif + typedef drmp3_uint8 drmp3_bool8; + typedef drmp3_uint32 drmp3_bool32; + #define DRMP3_TRUE 1 + #define DRMP3_FALSE 0 + + #if !defined(DRMP3_API) + #if defined(DRMP3_DLL) + #if defined(_WIN32) + #define DRMP3_DLL_IMPORT __declspec(dllimport) + #define DRMP3_DLL_EXPORT __declspec(dllexport) + #define DRMP3_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define DRMP3_DLL_IMPORT __attribute__((visibility("default"))) + #define DRMP3_DLL_EXPORT __attribute__((visibility("default"))) + #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define DRMP3_DLL_IMPORT + #define DRMP3_DLL_EXPORT + #define DRMP3_DLL_PRIVATE static + #endif + #endif + + #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) + #define DRMP3_API DRMP3_DLL_EXPORT + #else + #define DRMP3_API DRMP3_DLL_IMPORT + #endif + #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE + #else + #define DRMP3_API extern + #define DRMP3_PRIVATE static + #endif + #endif + + typedef drmp3_int32 drmp3_result; + #define DRMP3_SUCCESS 0 + #define DRMP3_ERROR -1 /* A generic error. */ + #define DRMP3_INVALID_ARGS -2 + #define DRMP3_INVALID_OPERATION -3 + #define DRMP3_OUT_OF_MEMORY -4 + #define DRMP3_OUT_OF_RANGE -5 + #define DRMP3_ACCESS_DENIED -6 + #define DRMP3_DOES_NOT_EXIST -7 + #define DRMP3_ALREADY_EXISTS -8 + #define DRMP3_TOO_MANY_OPEN_FILES -9 + #define DRMP3_INVALID_FILE -10 + #define DRMP3_TOO_BIG -11 + #define DRMP3_PATH_TOO_LONG -12 + #define DRMP3_NAME_TOO_LONG -13 + #define DRMP3_NOT_DIRECTORY -14 + #define DRMP3_IS_DIRECTORY -15 + #define DRMP3_DIRECTORY_NOT_EMPTY -16 + #define DRMP3_END_OF_FILE -17 + #define DRMP3_NO_SPACE -18 + #define DRMP3_BUSY -19 + #define DRMP3_IO_ERROR -20 + #define DRMP3_INTERRUPT -21 + #define DRMP3_UNAVAILABLE -22 + #define DRMP3_ALREADY_IN_USE -23 + #define DRMP3_BAD_ADDRESS -24 + #define DRMP3_BAD_SEEK -25 + #define DRMP3_BAD_PIPE -26 + #define DRMP3_DEADLOCK -27 + #define DRMP3_TOO_MANY_LINKS -28 + #define DRMP3_NOT_IMPLEMENTED -29 + #define DRMP3_NO_MESSAGE -30 + #define DRMP3_BAD_MESSAGE -31 + #define DRMP3_NO_DATA_AVAILABLE -32 + #define DRMP3_INVALID_DATA -33 + #define DRMP3_TIMEOUT -34 + #define DRMP3_NO_NETWORK -35 + #define DRMP3_NOT_UNIQUE -36 + #define DRMP3_NOT_SOCKET -37 + #define DRMP3_NO_ADDRESS -38 + #define DRMP3_BAD_PROTOCOL -39 + #define DRMP3_PROTOCOL_UNAVAILABLE -40 + #define DRMP3_PROTOCOL_NOT_SUPPORTED -41 + #define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42 + #define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43 + #define DRMP3_SOCKET_NOT_SUPPORTED -44 + #define DRMP3_CONNECTION_RESET -45 + #define DRMP3_ALREADY_CONNECTED -46 + #define DRMP3_NOT_CONNECTED -47 + #define DRMP3_CONNECTION_REFUSED -48 + #define DRMP3_NO_HOST -49 + #define DRMP3_IN_PROGRESS -50 + #define DRMP3_CANCELLED -51 + #define DRMP3_MEMORY_ALREADY_MAPPED -52 + #define DRMP3_AT_END -53 + + #define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 + #define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME * 2) + + #ifdef _MSC_VER + #define DRMP3_INLINE __forceinline + #elif defined(__GNUC__) + /* + I've had a bug report where GCC is emitting warnings about functions possibly not being + inlineable. This warning happens when the __attribute__((always_inline)) attribute is + defined without an "inline" statement. I think therefore there must be some case where + "__inline__" is not always defined, thus the compiler emitting these warnings. When using + -std=c89 or -ansi on the command line, we cannot use the "inline" keyword and instead need + to use "__inline__". In an attempt to work around this issue I am using "__inline__" only + when we're compiling in strict ANSI mode. + */ + #if defined(__STRICT_ANSI__) + #define DRMP3_GNUC_INLINE_HINT __inline__ + #else + #define DRMP3_GNUC_INLINE_HINT inline + #endif + + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT + #endif + #elif defined(__WATCOMC__) + #define DRMP3_INLINE __inline + #else + #define DRMP3_INLINE + #endif + + DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, + drmp3_uint32* pRevision); + DRMP3_API const char* drmp3_version_string(void); + + /* + Low Level Push API + ================== + */ + typedef struct + { + int frame_bytes, channels, hz, layer, bitrate_kbps; + } drmp3dec_frame_info; + + typedef struct + { + float mdct_overlap[2][9 * 32], qmf_state[15 * 2 * 32]; + int reserv, free_format_bytes; + drmp3_uint8 header[4], reserv_buf[511]; + } drmp3dec; + + /* Initializes a low level decoder. */ + DRMP3_API void drmp3dec_init(drmp3dec* dec); + + /* Reads a frame from a low level decoder. */ + DRMP3_API int drmp3dec_decode_frame(drmp3dec* dec, const drmp3_uint8* mp3, int mp3_bytes, + void* pcm, drmp3dec_frame_info* info); + + /* Helper for converting between f32 and s16. */ + DRMP3_API void drmp3dec_f32_to_s16(const float* in, drmp3_int16* out, size_t num_samples); + + /* + Main API (Pull API) + =================== + */ + typedef enum + { + drmp3_seek_origin_start, + drmp3_seek_origin_current + } drmp3_seek_origin; + + typedef struct + { + drmp3_uint64 seekPosInBytes; /* Points to the first byte of an MP3 frame. */ + drmp3_uint64 pcmFrameIndex; /* The index of the PCM frame this seek point targets. */ + drmp3_uint16 mp3FramesToDiscard; /* The number of whole MP3 frames to be discarded before + pcmFramesToDiscard. */ + drmp3_uint16 pcmFramesToDiscard; /* The number of leading samples to read and discard. These + are discarded after mp3FramesToDiscard. */ + } drmp3_seek_point; + + /* + Callback for when data is read. Return value is the number of bytes actually read. + + pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family. + pBufferOut [out] The output buffer. + bytesToRead [in] The number of bytes to read. + + Returns the number of bytes actually read. + + A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from + this callback until either the entire bytesToRead is filled or you have reached the end of the + stream. + */ + typedef size_t (*drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); + + /* + Callback for when data needs to be seeked. + + pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family. + offset [in] The number of bytes to move, relative to the origin. Will never be negative. + origin [in] The origin of the seek - the current position or the start of the stream. + + Returns whether or not the seek was successful. + + Whether or not it is relative to the beginning or current position is determined by the "origin" + parameter which will be either drmp3_seek_origin_start or drmp3_seek_origin_current. + */ + typedef drmp3_bool32 (*drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin); + + typedef struct + { + void* pUserData; + void* (*onMalloc)(size_t sz, void* pUserData); + void* (*onRealloc)(void* p, size_t sz, void* pUserData); + void (*onFree)(void* p, void* pUserData); + } drmp3_allocation_callbacks; + + typedef struct + { + drmp3_uint32 channels; + drmp3_uint32 sampleRate; + } drmp3_config; + + typedef struct + { + drmp3dec decoder; + drmp3_uint32 channels; + drmp3_uint32 sampleRate; + drmp3_read_proc onRead; + drmp3_seek_proc onSeek; + void* pUserData; + drmp3_allocation_callbacks allocationCallbacks; + drmp3_uint32 mp3FrameChannels; /* The number of channels in the currently loaded MP3 frame. + Internal use only. */ + drmp3_uint32 mp3FrameSampleRate; /* The sample rate of the currently loaded MP3 frame. + Internal use only. */ + drmp3_uint32 pcmFramesConsumedInMP3Frame; + drmp3_uint32 pcmFramesRemainingInMP3Frame; + drmp3_uint8 pcmFrames[sizeof(float) * + DRMP3_MAX_SAMPLES_PER_FRAME]; /* <-- Multipled by sizeof(float) to + ensure there's enough room for + DR_MP3_FLOAT_OUTPUT. */ + drmp3_uint64 currentPCMFrame; /* The current PCM frame, globally, based on the output sample + rate. Mainly used for seeking. */ + drmp3_uint64 + streamCursor; /* The current byte the decoder is sitting on in the raw stream. */ + drmp3_seek_point* + pSeekPoints; /* NULL by default. Set with drmp3_bind_seek_table(). Memory is owned by + the client. dr_mp3 will never attempt to free this pointer. */ + drmp3_uint32 seekPointCount; /* The number of items in pSeekPoints. When set to 0 assumes to + no seek table. Defaults to zero. */ + size_t dataSize; + size_t dataCapacity; + size_t dataConsumed; + drmp3_uint8* pData; + drmp3_bool32 atEnd : 1; + struct + { + const drmp3_uint8* pData; + size_t dataSize; + size_t currentReadPos; + } memory; /* Only used for decoders that were opened against a block of memory. */ + } drmp3; + + /* + Initializes an MP3 decoder. + + onRead [in] The function to call when data needs to be read from the client. + onSeek [in] The function to call when the read position of the client data needs to + move. pUserData [in, optional] A pointer to application defined data that will be passed to + onRead and onSeek. + + Returns true if successful; false otherwise. + + Close the loader with drmp3_uninit(). + + See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit() + */ + DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, + void* pUserData, + const drmp3_allocation_callbacks* pAllocationCallbacks); + + /* + Initializes an MP3 decoder from a block of memory. + + This does not create a copy of the data. It is up to the application to ensure the buffer + remains valid for the lifetime of the drmp3 object. + + The buffer should contain the contents of the entire MP3 file. + */ + DRMP3_API drmp3_bool32 + drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, + const drmp3_allocation_callbacks* pAllocationCallbacks); + + #ifndef DR_MP3_NO_STDIO + /* + Initializes an MP3 decoder from a file. + + This holds the internal FILE object until drmp3_uninit() is called. Keep this in mind if you're + caching drmp3 objects because the operating system may restrict the number of file handles an + application can have open at any given time. + */ + DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, + const drmp3_allocation_callbacks* pAllocationCallbacks); + DRMP3_API drmp3_bool32 + drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, + const drmp3_allocation_callbacks* pAllocationCallbacks); + #endif + + /* + Uninitializes an MP3 decoder. + */ + DRMP3_API void drmp3_uninit(drmp3* pMP3); + + /* + Reads PCM frames as interleaved 32-bit IEEE floating point PCM. + + Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 + frames. + */ + DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, + float* pBufferOut); + + /* + Reads PCM frames as interleaved signed 16-bit integer PCM. + + Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 + frames. + */ + DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, + drmp3_int16* pBufferOut); + + /* + Seeks to a specific frame. + + Note that this is _not_ an MP3 frame, but rather a PCM frame. + */ + DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex); + + /* + Calculates the total number of PCM frames in the MP3 stream. Cannot be used for infinite streams + such as internet radio. Runs in linear time. Returns 0 on error. + */ + DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3); + + /* + Calculates the total number of MP3 frames in the MP3 stream. Cannot be used for infinite streams + such as internet radio. Runs in linear time. Returns 0 on error. + */ + DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3); + + /* + Calculates the total number of MP3 and PCM frames in the MP3 stream. Cannot be used for infinite + streams such as internet radio. Runs in linear time. Returns 0 on error. + + This is equivalent to calling drmp3_get_mp3_frame_count() and drmp3_get_pcm_frame_count() except + that it's more efficient. + */ + DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, + drmp3_uint64* pMP3FrameCount, + drmp3_uint64* pPCMFrameCount); + + /* + Calculates the seekpoints based on PCM frames. This is slow. + + pSeekpoint count is a pointer to a uint32 containing the seekpoint count. On input it contains + the desired count. On output it contains the actual count. The reason for this design is that + the client may request too many seekpoints, in which case dr_mp3 will return a corrected count. + + Note that seektable seeking is not quite sample exact when the MP3 stream contains inconsistent + sample rates. + */ + DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, + drmp3_seek_point* pSeekPoints); + + /* + Binds a seek table to the decoder. + + This does _not_ make a copy of pSeekPoints - it only references it. It is up to the application + to ensure this remains valid while it is bound to the decoder. + + Use drmp3_calculate_seek_points() to calculate the seek points. + */ + DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, + drmp3_seek_point* pSeekPoints); + + /* + Opens an decodes an entire MP3 stream as a single operation. + + On output pConfig will receive the channel count and sample rate of the stream. + + Free the returned pointer with drmp3_free(). + */ + DRMP3_API float* drmp3_open_and_read_pcm_frames_f32( + drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, + drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); + DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16( + drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, + drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); + + DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32( + const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, + const drmp3_allocation_callbacks* pAllocationCallbacks); + DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16( + const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, + const drmp3_allocation_callbacks* pAllocationCallbacks); + + #ifndef DR_MP3_NO_STDIO + DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32( + const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, + const drmp3_allocation_callbacks* pAllocationCallbacks); + DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16( + const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, + const drmp3_allocation_callbacks* pAllocationCallbacks); + #endif + + /* + Allocates a block of memory on the heap. + */ + DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks); + + /* + Frees any memory that was allocated by a public drmp3 API. + */ + DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks); + + #ifdef __cplusplus +} + #endif +#endif /* dr_mp3_h */ + +/************************************************************************************************************************************************************ + ************************************************************************************************************************************************************ + + IMPLEMENTATION + + ************************************************************************************************************************************************************ + ************************************************************************************************************************************************************/ +#if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) + #ifndef dr_mp3_c + #define dr_mp3_c + + #include /* For INT_MAX */ + #include + #include + +DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision) +{ + if (pMajor) + { + *pMajor = DRMP3_VERSION_MAJOR; + } + + if (pMinor) + { + *pMinor = DRMP3_VERSION_MINOR; + } + + if (pRevision) + { + *pRevision = DRMP3_VERSION_REVISION; + } +} + +DRMP3_API const char* drmp3_version_string(void) +{ + return DRMP3_VERSION_STRING; +} + + /* Disable SIMD when compiling with TCC for now. */ + #if defined(__TINYC__) + #define DR_MP3_NO_SIMD + #endif + + #define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) + + #define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ + #ifndef DRMP3_MAX_FRAME_SYNC_MATCHES + #define DRMP3_MAX_FRAME_SYNC_MATCHES 10 + #endif + + #define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES \ + DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ + + #define DRMP3_MAX_BITRESERVOIR_BYTES 511 + #define DRMP3_SHORT_BLOCK_TYPE 2 + #define DRMP3_STOP_BLOCK_TYPE 3 + #define DRMP3_MODE_MONO 3 + #define DRMP3_MODE_JOINT_STEREO 1 + #define DRMP3_HDR_SIZE 4 + #define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) + #define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) + #define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) + #define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1)) + #define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) + #define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) + #define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) + #define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) + #define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) + #define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) + #define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) + #define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) + #define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) + #define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) + #define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) \ + (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1)) * 3) + #define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) + #define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) + + #define DRMP3_BITS_DEQUANTIZER_OUT -1 + #define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT * 4 - 210) + #define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3) + + #define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) + #define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) + + #if !defined(DR_MP3_NO_SIMD) + + #if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || \ + defined(__aarch64__) || defined(_M_ARM64)) + /* x64 always have SSE2, arm64 always have neon, no need for generic code */ + #define DR_MP3_ONLY_SIMD + #endif + + #if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || \ + ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || \ + defined(__x86_64__)) && \ + ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) + #if defined(_MSC_VER) + #include + #endif + #include + #define DRMP3_HAVE_SSE 1 + #define DRMP3_HAVE_SIMD 1 + #define DRMP3_VSTORE _mm_storeu_ps + #define DRMP3_VLD _mm_loadu_ps + #define DRMP3_VSET _mm_set1_ps + #define DRMP3_VADD _mm_add_ps + #define DRMP3_VSUB _mm_sub_ps + #define DRMP3_VMUL _mm_mul_ps + #define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) + #define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) + #define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) + #define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) +typedef __m128 drmp3_f4; + #if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD) + #define drmp3_cpuid __cpuid + #else +static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) +{ + #if defined(__PIC__) + __asm__ __volatile__( + #if defined(__x86_64__) + "push %%rbx\n" + "cpuid\n" + "xchgl %%ebx, %1\n" + "pop %%rbx\n" + #else + "xchgl %%ebx, %1\n" + "cpuid\n" + "xchgl %%ebx, %1\n" + #endif + : "=a"(CPUInfo[0]), "=r"(CPUInfo[1]), "=c"(CPUInfo[2]), "=d"(CPUInfo[3]) + : "a"(InfoType)); + #else + __asm__ __volatile__("cpuid" + : "=a"(CPUInfo[0]), "=b"(CPUInfo[1]), "=c"(CPUInfo[2]), "=d"(CPUInfo[3]) + : "a"(InfoType)); + #endif +} + #endif +static int drmp3_have_simd(void) +{ + #ifdef DR_MP3_ONLY_SIMD + return 1; + #else + static int g_have_simd; + int CPUInfo[4]; + #ifdef MINIMP3_TEST + static int g_counter; + if (g_counter++ > 100) + return 0; + #endif + if (g_have_simd) + goto end; + drmp3_cpuid(CPUInfo, 0); + if (CPUInfo[0] > 0) + { + drmp3_cpuid(CPUInfo, 1); + g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; /* SSE2 */ + return g_have_simd - 1; + } + +end: + return g_have_simd - 1; + #endif +} + #elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) + #include + #define DRMP3_HAVE_SSE 0 + #define DRMP3_HAVE_SIMD 1 + #define DRMP3_VSTORE vst1q_f32 + #define DRMP3_VLD vld1q_f32 + #define DRMP3_VSET vmovq_n_f32 + #define DRMP3_VADD vaddq_f32 + #define DRMP3_VSUB vsubq_f32 + #define DRMP3_VMUL vmulq_f32 + #define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y) + #define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y) + #define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) + #define DRMP3_VREV(x) \ + vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) +typedef float32x4_t drmp3_f4; +static int drmp3_have_simd(void) +{ /* TODO: detect neon for !DR_MP3_ONLY_SIMD */ + return 1; +} + #else + #define DRMP3_HAVE_SSE 0 + #define DRMP3_HAVE_SIMD 0 + #ifdef DR_MP3_ONLY_SIMD + #error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled + #endif + #endif + + #else + + #define DRMP3_HAVE_SIMD 0 + + #endif + + #if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) + #define DRMP3_HAVE_ARMV6 1 +static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a) +{ + drmp3_int32 x = 0; + __asm__("ssat %0, #16, %1" : "=r"(x) : "r"(a)); + return x; +} + #else + #define DRMP3_HAVE_ARMV6 0 + #endif + + /* Standard library stuff. */ + #ifndef DRMP3_ASSERT + #include + #define DRMP3_ASSERT(expression) assert(expression) + #endif + #ifndef DRMP3_COPY_MEMORY + #define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) + #endif + #ifndef DRMP3_MOVE_MEMORY + #define DRMP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) + #endif + #ifndef DRMP3_ZERO_MEMORY + #define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) + #endif + #define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p))) + #ifndef DRMP3_MALLOC + #define DRMP3_MALLOC(sz) malloc((sz)) + #endif + #ifndef DRMP3_REALLOC + #define DRMP3_REALLOC(p, sz) realloc((p), (sz)) + #endif + #ifndef DRMP3_FREE + #define DRMP3_FREE(p) free((p)) + #endif + +typedef struct +{ + const drmp3_uint8* buf; + int pos, limit; +} drmp3_bs; + +typedef struct +{ + float scf[3 * 64]; + drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; +} drmp3_L12_scale_info; + +typedef struct +{ + drmp3_uint8 tab_offset, code_tab_width, band_count; +} drmp3_L12_subband_alloc; + +typedef struct +{ + const drmp3_uint8* sfbtab; + drmp3_uint16 part_23_length, big_values, scalefac_compress; + drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; + drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; +} drmp3_L3_gr_info; + +typedef struct +{ + drmp3_bs bs; + drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; + drmp3_L3_gr_info gr_info[4]; + float grbuf[2][576], scf[40], syn[18 + 15][2 * 32]; + drmp3_uint8 ist_pos[2][39]; +} drmp3dec_scratch; + +static void drmp3_bs_init(drmp3_bs* bs, const drmp3_uint8* data, int bytes) +{ + bs->buf = data; + bs->pos = 0; + bs->limit = bytes * 8; +} + +static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs* bs, int n) +{ + drmp3_uint32 next, cache = 0, s = bs->pos & 7; + int shl = n + s; + const drmp3_uint8* p = bs->buf + (bs->pos >> 3); + if ((bs->pos += n) > bs->limit) + return 0; + next = *p++ & (255 >> s); + while ((shl -= 8) > 0) + { + cache |= next << shl; + next = *p++; + } + return cache | (next >> -shl); +} + +static int drmp3_hdr_valid(const drmp3_uint8* h) +{ + return h[0] == 0xff && ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && + (DRMP3_HDR_GET_LAYER(h) != 0) && (DRMP3_HDR_GET_BITRATE(h) != 15) && + (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3); +} + +static int drmp3_hdr_compare(const drmp3_uint8* h1, const drmp3_uint8* h2) +{ + return drmp3_hdr_valid(h2) && ((h1[1] ^ h2[1]) & 0xFE) == 0 && ((h1[2] ^ h2[2]) & 0x0C) == 0 && + !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2)); +} + +static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8* h) +{ + static const drmp3_uint8 halfrate[2][3][15] = { + { { 0, 4, 8, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64, 72, 80 }, + { 0, 4, 8, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64, 72, 80 }, + { 0, 16, 24, 28, 32, 40, 48, 56, 64, 72, 80, 88, 96, 112, 128 } }, + {{ 0, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160 }, + { 0, 16, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192 }, + { 0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224 }}, + }; + return 2 * halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1] + [DRMP3_HDR_GET_BITRATE(h)]; +} + +static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8* h) +{ + static const unsigned g_hz[3] = { 44100, 48000, 32000 }; + return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> + (int)!DRMP3_HDR_TEST_NOT_MPEG25(h); +} + +static unsigned drmp3_hdr_frame_samples(const drmp3_uint8* h) +{ + return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h)); +} + +static int drmp3_hdr_frame_bytes(const drmp3_uint8* h, int free_format_size) +{ + int frame_bytes = + drmp3_hdr_frame_samples(h) * drmp3_hdr_bitrate_kbps(h) * 125 / drmp3_hdr_sample_rate_hz(h); + if (DRMP3_HDR_IS_LAYER_1(h)) + { + frame_bytes &= ~3; /* slot align */ + } + return frame_bytes ? frame_bytes : free_format_size; +} + +static int drmp3_hdr_padding(const drmp3_uint8* h) +{ + return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; +} + + #ifndef DR_MP3_ONLY_MP3 +static const drmp3_L12_subband_alloc* drmp3_L12_subband_alloc_table(const drmp3_uint8* hdr, + drmp3_L12_scale_info* sci) +{ + const drmp3_L12_subband_alloc* alloc; + int mode = DRMP3_HDR_GET_STEREO_MODE(hdr); + int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 + : (mode == DRMP3_MODE_JOINT_STEREO) + ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 + : 32; + + if (DRMP3_HDR_IS_LAYER_1(hdr)) + { + static const drmp3_L12_subband_alloc g_alloc_L1[] = { + {76, 4, 32} + }; + alloc = g_alloc_L1; + nbands = 32; + } + else if (!DRMP3_HDR_TEST_MPEG1(hdr)) + { + static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { + {60, 4, 4}, + {44, 3, 7}, + {44, 2, 19} + }; + alloc = g_alloc_L2M2; + nbands = 30; + } + else + { + static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { + { 0, 4, 3}, + {16, 4, 8}, + {32, 3, 12}, + {40, 2, 7} + }; + int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr); + unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO); + if (!kbps) /* free-format */ + { + kbps = 192; + } + + alloc = g_alloc_L2M1; + nbands = 27; + if (kbps < 56) + { + static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { + {44, 4, 2}, + {44, 3, 10} + }; + alloc = g_alloc_L2M1_lowrate; + nbands = sample_rate_idx == 2 ? 12 : 8; + } + else if (kbps >= 96 && sample_rate_idx != 1) + { + nbands = 30; + } + } + + sci->total_bands = (drmp3_uint8)nbands; + sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands); + + return alloc; +} + +static void drmp3_L12_read_scalefactors(drmp3_bs* bs, drmp3_uint8* pba, drmp3_uint8* scfcod, + int bands, float* scf) +{ + static const float g_deq_L12[18 * 3] = { + #define DRMP3_DQ(x) 9.53674316e-07f / x, 7.56931807e-07f / x, 6.00777173e-07f / x + DRMP3_DQ(3), DRMP3_DQ(7), DRMP3_DQ(15), DRMP3_DQ(31), DRMP3_DQ(63), + DRMP3_DQ(127), DRMP3_DQ(255), DRMP3_DQ(511), DRMP3_DQ(1023), DRMP3_DQ(2047), + DRMP3_DQ(4095), DRMP3_DQ(8191), DRMP3_DQ(16383), DRMP3_DQ(32767), DRMP3_DQ(65535), + DRMP3_DQ(3), DRMP3_DQ(5), DRMP3_DQ(9) + }; + int i, m; + for (i = 0; i < bands; i++) + { + float s = 0; + int ba = *pba++; + int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; + for (m = 4; m; m >>= 1) + { + if (mask & m) + { + int b = drmp3_bs_get_bits(bs, 6); + s = g_deq_L12[ba * 3 - 6 + b % 3] * (int)(1 << 21 >> b / 3); + } + *scf++ = s; + } + } +} + +static void drmp3_L12_read_scale_info(const drmp3_uint8* hdr, drmp3_bs* bs, + drmp3_L12_scale_info* sci) +{ + static const drmp3_uint8 g_bitalloc_code_tab[] = { + 0, 17, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 17, 18, 3, 19, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 16, 0, 17, 18, 3, 19, 4, 5, 16, 0, 17, 18, 16, 0, 17, + 18, 19, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 17, 18, 3, 19, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + }; + const drmp3_L12_subband_alloc* subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci); + + int i, k = 0, ba_bits = 0; + const drmp3_uint8* ba_code_tab = g_bitalloc_code_tab; + + for (i = 0; i < sci->total_bands; i++) + { + drmp3_uint8 ba; + if (i == k) + { + k += subband_alloc->band_count; + ba_bits = subband_alloc->code_tab_width; + ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; + subband_alloc++; + } + ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + sci->bitalloc[2 * i] = ba; + if (i < sci->stereo_bands) + { + ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + } + sci->bitalloc[2 * i + 1] = sci->stereo_bands ? ba : 0; + } + + for (i = 0; i < 2 * sci->total_bands; i++) + { + sci->scfcod[i] = + (drmp3_uint8)(sci->bitalloc[i] + ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) + : 6); + } + + drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands * 2, sci->scf); + + for (i = sci->stereo_bands; i < sci->total_bands; i++) + { + sci->bitalloc[2 * i + 1] = 0; + } +} + +static int drmp3_L12_dequantize_granule(float* grbuf, drmp3_bs* bs, drmp3_L12_scale_info* sci, + int group_size) +{ + int i, j, k, choff = 576; + for (j = 0; j < 4; j++) + { + float* dst = grbuf + group_size * j; + for (i = 0; i < 2 * sci->total_bands; i++) + { + int ba = sci->bitalloc[i]; + if (ba != 0) + { + if (ba < 17) + { + int half = (1 << (ba - 1)) - 1; + for (k = 0; k < group_size; k++) + { + dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half); + } + } + else + { + unsigned mod = (2 << (ba - 17)) + 1; /* 3, 5, 9 */ + unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); /* 5, 7, 10 */ + for (k = 0; k < group_size; k++, code /= mod) + { + dst[k] = (float)((int)(code % mod - mod / 2)); + } + } + } + dst += choff; + choff = 18 - choff; + } + } + return group_size * 4; +} + +static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info* sci, const float* scf, float* dst) +{ + int i, k; + DRMP3_COPY_MEMORY(dst + 576 + sci->stereo_bands * 18, dst + sci->stereo_bands * 18, + (sci->total_bands - sci->stereo_bands) * 18 * sizeof(float)); + for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) + { + for (k = 0; k < 12; k++) + { + dst[k + 0] *= scf[0]; + dst[k + 576] *= scf[3]; + } + } +} + #endif + +static int drmp3_L3_read_side_info(drmp3_bs* bs, drmp3_L3_gr_info* gr, const drmp3_uint8* hdr) +{ + static const drmp3_uint8 g_scf_long[8][23] = { + { 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54, 0}, + {12, 12, 12, 12, 12, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64, 76, 90, 2, 2, 2, 2, 2, 0}, + { 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54, 0}, + { 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 18, 22, 26, 32, 38, 46, 54, 62, 70, 76, 36, 0}, + { 6, 6, 6, 6, 6, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54, 0}, + { 4, 4, 4, 4, 4, 4, 6, 6, 8, 8, 10, 12, 16, 20, 24, 28, 34, 42, 50, 54, 76, 158, 0}, + { 4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 10, 12, 16, 18, 22, 28, 34, 40, 46, 54, 54, 192, 0}, + { 4, 4, 4, 4, 4, 4, 6, 6, 8, 10, 12, 16, 20, 24, 30, 38, 46, 56, 68, 84, 102, 26, 0} + }; + static const drmp3_uint8 g_scf_short[8][40] = { + {4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 10, 12, 12, + 12, 14, 14, 14, 18, 18, 18, 24, 24, 24, 30, 30, 30, 40, 40, 40, 18, 18, 18, 0}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, 16, 16, 16, 20, 20, 20, 24, 24, + 24, 28, 28, 28, 36, 36, 36, 2, 2, 2, 2, 2, 2, 2, 2, 2, 26, 26, 26, 0 }, + {4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 8, 8, 8, 10, 10, + 10, 14, 14, 14, 18, 18, 18, 26, 26, 26, 32, 32, 32, 42, 42, 42, 18, 18, 18, 0}, + {4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 10, 12, 12, + 12, 14, 14, 14, 18, 18, 18, 24, 24, 24, 32, 32, 32, 44, 44, 44, 12, 12, 12, 0}, + {4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 10, 12, 12, + 12, 14, 14, 14, 18, 18, 18, 24, 24, 24, 30, 30, 30, 40, 40, 40, 18, 18, 18, 0}, + {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, + 10, 12, 12, 12, 14, 14, 14, 18, 18, 18, 22, 22, 22, 30, 30, 30, 56, 56, 56, 0}, + {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 10, 10, + 10, 12, 12, 12, 14, 14, 14, 16, 16, 16, 20, 20, 20, 26, 26, 26, 66, 66, 66, 0}, + {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 12, 12, + 12, 16, 16, 16, 20, 20, 20, 26, 26, 26, 34, 34, 34, 42, 42, 42, 12, 12, 12, 0} + }; + static const drmp3_uint8 g_scf_mixed[8][40] = { + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 10, 10, 10, 12, 12, 12, 14, + 14, 14, 18, 18, 18, 24, 24, 24, 30, 30, 30, 40, 40, 40, 18, 18, 18, 0 }, + { 12, 12, 12, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 16, 20, 20, 20, 24, 24, + 24, 28, 28, 28, 36, 36, 36, 2, 2, 2, 2, 2, 2, 2, 2, 2, 26, 26, 26, 0 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 10, 10, 10, 14, + 14, 14, 18, 18, 18, 26, 26, 26, 32, 32, 32, 42, 42, 42, 18, 18, 18, 0 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 10, 10, 10, 12, 12, 12, 14, + 14, 14, 18, 18, 18, 24, 24, 24, 32, 32, 32, 44, 44, 44, 12, 12, 12, 0 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 10, 10, 10, 12, 12, 12, 14, + 14, 14, 18, 18, 18, 24, 24, 24, 30, 30, 30, 40, 40, 40, 18, 18, 18, 0 }, + { 4, 4, 4, 4, 4, 4, 6, 6, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 10, + 12, 12, 12, 14, 14, 14, 18, 18, 18, 22, 22, 22, 30, 30, 30, 56, 56, 56, 0 }, + { 4, 4, 4, 4, 4, 4, 6, 6, 4, 4, 4, 6, 6, 6, 6, 6, 6, 10, 10, 10, + 12, 12, 12, 14, 14, 14, 16, 16, 16, 20, 20, 20, 26, 26, 26, 66, 66, 66, 0 }, + { 4, 4, 4, 4, 4, 4, 6, 6, 4, 4, 4, 6, 6, 6, 8, 8, 8, 12, 12, 12, + 16, 16, 16, 20, 20, 20, 26, 26, 26, 34, 34, 34, 42, 42, 42, 12, 12, 12, 0 } + }; + + unsigned tables, scfsi = 0; + int main_data_begin, part_23_sum = 0; + int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; + int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); + sr_idx -= (sr_idx != 0); + + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + gr_count *= 2; + main_data_begin = drmp3_bs_get_bits(bs, 9); + scfsi = drmp3_bs_get_bits(bs, 7 + gr_count); + } + else + { + main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; + } + + do + { + if (DRMP3_HDR_IS_MONO(hdr)) + { + scfsi <<= 4; + } + gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12); + part_23_sum += gr->part_23_length; + gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9); + if (gr->big_values > 288) + { + return -1; + } + gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8); + gr->scalefac_compress = + (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); + gr->sfbtab = g_scf_long[sr_idx]; + gr->n_long_sfb = 22; + gr->n_short_sfb = 0; + if (drmp3_bs_get_bits(bs, 1)) + { + gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2); + if (!gr->block_type) + { + return -1; + } + gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->region_count[0] = 7; + gr->region_count[1] = 255; + if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE) + { + scfsi &= 0x0F0F; + if (!gr->mixed_block_flag) + { + gr->region_count[0] = 8; + gr->sfbtab = g_scf_short[sr_idx]; + gr->n_long_sfb = 0; + gr->n_short_sfb = 39; + } + else + { + gr->sfbtab = g_scf_mixed[sr_idx]; + gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; + gr->n_short_sfb = 30; + } + } + tables = drmp3_bs_get_bits(bs, 10); + tables <<= 5; + gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + } + else + { + gr->block_type = 0; + gr->mixed_block_flag = 0; + tables = drmp3_bs_get_bits(bs, 15); + gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4); + gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->region_count[2] = 255; + } + gr->table_select[0] = (drmp3_uint8)(tables >> 10); + gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31); + gr->table_select[2] = (drmp3_uint8)((tables)&31); + gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) + : (gr->scalefac_compress >= 500)); + gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15); + scfsi <<= 4; + gr++; + } while (--gr_count); + + if (part_23_sum + bs->pos > bs->limit + main_data_begin * 8) + { + return -1; + } + + return main_data_begin; +} + +static void drmp3_L3_read_scalefactors(drmp3_uint8* scf, drmp3_uint8* ist_pos, + const drmp3_uint8* scf_size, const drmp3_uint8* scf_count, + drmp3_bs* bitbuf, int scfsi) +{ + int i, k; + for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) + { + int cnt = scf_count[i]; + if (scfsi & 8) + { + DRMP3_COPY_MEMORY(scf, ist_pos, cnt); + } + else + { + int bits = scf_size[i]; + if (!bits) + { + DRMP3_ZERO_MEMORY(scf, cnt); + DRMP3_ZERO_MEMORY(ist_pos, cnt); + } + else + { + int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; + for (k = 0; k < cnt; k++) + { + int s = drmp3_bs_get_bits(bitbuf, bits); + ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s); + scf[k] = (drmp3_uint8)s; + } + } + } + ist_pos += cnt; + scf += cnt; + } + scf[0] = scf[1] = scf[2] = 0; +} + +static float drmp3_L3_ldexp_q2(float y, int exp_q2) +{ + static const float g_expfrac[4] = { 9.31322575e-10f, 7.83145814e-10f, 6.58544508e-10f, + 5.53767716e-10f }; + int e; + do + { + e = DRMP3_MIN(30 * 4, exp_q2); + y *= g_expfrac[e & 3] * (1 << 30 >> (e >> 2)); + } while ((exp_q2 -= e) > 0); + return y; +} + +static void drmp3_L3_decode_scalefactors(const drmp3_uint8* hdr, drmp3_uint8* ist_pos, drmp3_bs* bs, + const drmp3_L3_gr_info* gr, float* scf, int ch) +{ + static const drmp3_uint8 g_scf_partitions[3] + [28] = { + {6, 5, 5, 5, 6, 5, 5, 5, 6, 5, 7, 3, 11, 10, + 0, 0, 7, 7, 7, 0, 6, 6, 6, 3, 8, 8, 5, 0}, + {8, 9, 6, 12, 6, 9, 9, 9, 6, 9, 12, 6, 15, 18, + 0, 0, 6, 15, 12, 0, 6, 12, 9, 6, 6, 18, 9, 0 }, + {9, 9, 6, 12, 9, 9, 9, 9, 9, 9, 12, 6, 18, 18, + 0, 0, 12, 12, 12, 0, 12, 9, 9, 6, 15, 12, 9, 0} + }; + const drmp3_uint8* scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; + drmp3_uint8 scf_size[4], iscf[40]; + int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; + float gain; + + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + static const drmp3_uint8 g_scfc_decode[16] = { 0, 1, 2, 3, 12, 5, 6, 7, + 9, 10, 11, 13, 14, 15, 18, 19 }; + int part = g_scfc_decode[gr->scalefac_compress]; + scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2); + scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3); + } + else + { + static const drmp3_uint8 g_mod[6 * 4] = { 5, 5, 4, 4, 5, 5, 4, 1, 4, 3, 1, 1, + 5, 6, 6, 1, 4, 4, 4, 1, 4, 3, 1, 1 }; + int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch; + sfc = gr->scalefac_compress >> ist; + for (k = ist * 3 * 4; sfc >= 0; sfc -= modprod, k += 4) + { + for (modprod = 1, i = 3; i >= 0; i--) + { + scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]); + modprod *= g_mod[k + i]; + } + } + scf_partition += k; + scfsi = -16; + } + drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); + + if (gr->n_short_sfb) + { + int sh = 3 - scf_shift; + for (i = 0; i < gr->n_short_sfb; i += 3) + { + iscf[gr->n_long_sfb + i + 0] = + (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); + iscf[gr->n_long_sfb + i + 1] = + (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); + iscf[gr->n_long_sfb + i + 2] = + (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); + } + } + else if (gr->preflag) + { + static const drmp3_uint8 g_preamp[10] = { 1, 1, 1, 1, 2, 2, 3, 3, 3, 2 }; + for (i = 0; i < 10; i++) + { + iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]); + } + } + + gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT * 4 - 210 - + (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); + gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI / 4), DRMP3_MAX_SCFI - gain_exp); + for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) + { + scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); + } +} + +static const float g_drmp3_pow43[129 + 16] = { + 0, -1, -2.519842f, -4.326749f, -6.349604f, -8.549880f, -10.902724f, + -13.390518f, -16.000000f, -18.720754f, -21.544347f, -24.463781f, -27.473142f, -30.567351f, + -33.741992f, -36.993181f, 0, 1, 2.519842f, 4.326749f, 6.349604f, + 8.549880f, 10.902724f, 13.390518f, 16.000000f, 18.720754f, 21.544347f, 24.463781f, + 27.473142f, 30.567351f, 33.741992f, 36.993181f, 40.317474f, 43.711787f, 47.173345f, + 50.699631f, 54.288352f, 57.937408f, 61.644865f, 65.408941f, 69.227979f, 73.100443f, + 77.024898f, 81.000000f, 85.024491f, 89.097188f, 93.216975f, 97.382800f, 101.593667f, + 105.848633f, 110.146801f, 114.487321f, 118.869381f, 123.292209f, 127.755065f, 132.257246f, + 136.798076f, 141.376907f, 145.993119f, 150.646117f, 155.335327f, 160.060199f, 164.820202f, + 169.614826f, 174.443577f, 179.305980f, 184.201575f, 189.129918f, 194.090580f, 199.083145f, + 204.107210f, 209.162385f, 214.248292f, 219.364564f, 224.510845f, 229.686789f, 234.892058f, + 240.126328f, 245.389280f, 250.680604f, 256.000000f, 261.347174f, 266.721841f, 272.123723f, + 277.552547f, 283.008049f, 288.489971f, 293.998060f, 299.532071f, 305.091761f, 310.676898f, + 316.287249f, 321.922592f, 327.582707f, 333.267377f, 338.976394f, 344.709550f, 350.466646f, + 356.247482f, 362.051866f, 367.879608f, 373.730522f, 379.604427f, 385.501143f, 391.420496f, + 397.362314f, 403.326427f, 409.312672f, 415.320884f, 421.350905f, 427.402579f, 433.475750f, + 439.570269f, 445.685987f, 451.822757f, 457.980436f, 464.158883f, 470.357960f, 476.577530f, + 482.817459f, 489.077615f, 495.357868f, 501.658090f, 507.978156f, 514.317941f, 520.677324f, + 527.056184f, 533.454404f, 539.871867f, 546.308458f, 552.764065f, 559.238575f, 565.731879f, + 572.243870f, 578.774440f, 585.323483f, 591.890898f, 598.476581f, 605.080431f, 611.702349f, + 618.342238f, 625.000000f, 631.675540f, 638.368763f, 645.079578f +}; + +static float drmp3_L3_pow_43(int x) +{ + float frac; + int sign, mult = 256; + + if (x < 129) + { + return g_drmp3_pow43[16 + x]; + } + + if (x < 1024) + { + mult = 16; + x <<= 3; + } + + sign = 2 * x & 64; + frac = (float)((x & 63) - sign) / ((x & ~63) + sign); + return g_drmp3_pow43[16 + ((x + sign) >> 6)] * (1.f + frac * ((4.f / 3) + frac * (2.f / 9))) * + mult; +} + +static void drmp3_L3_huffman(float* dst, drmp3_bs* bs, const drmp3_L3_gr_info* gr_info, + const float* scf, int layer3gr_limit) +{ + static const drmp3_int16 tabs[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 785, 785, 785, 785, 784, 784, 784, + 784, 513, 513, 513, 513, 513, 513, 513, 513, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, -255, + 1313, 1298, 1282, 785, 785, 785, 785, 784, 784, 784, 784, 769, 769, + 769, 769, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 290, 288, -255, 1313, 1298, 1282, 769, 769, + 769, 769, 529, 529, 529, 529, 529, 529, 529, 529, 528, 528, 528, + 528, 528, 528, 528, 528, 512, 512, 512, 512, 512, 512, 512, 512, + 290, 288, -253, -318, -351, -367, 785, 785, 785, 785, 784, 784, 784, + 784, 769, 769, 769, 769, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 819, 818, 547, 547, 275, + 275, 275, 275, 561, 560, 515, 546, 289, 274, 288, 258, -254, -287, + 1329, 1299, 1314, 1312, 1057, 1057, 1042, 1042, 1026, 1026, 784, 784, 784, + 784, 529, 529, 529, 529, 529, 529, 529, 529, 769, 769, 769, 769, + 768, 768, 768, 768, 563, 560, 306, 306, 291, 259, -252, -413, -477, + -542, 1298, -575, 1041, 1041, 784, 784, 784, 784, 769, 769, 769, 769, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, -383, -399, 1107, 1092, 1106, 1061, 849, 849, 789, 789, + 1104, 1091, 773, 773, 1076, 1075, 341, 340, 325, 309, 834, 804, 577, + 577, 532, 532, 516, 516, 832, 818, 803, 816, 561, 561, 531, 531, + 515, 546, 289, 289, 288, 258, -252, -429, -493, -559, 1057, 1057, 1042, + 1042, 529, 529, 529, 529, 529, 529, 529, 529, 784, 784, 784, 784, + 769, 769, 769, 769, 512, 512, 512, 512, 512, 512, 512, 512, -382, + 1077, -415, 1106, 1061, 1104, 849, 849, 789, 789, 1091, 1076, 1029, 1075, + 834, 834, 597, 581, 340, 340, 339, 324, 804, 833, 532, 532, 832, + 772, 818, 803, 817, 787, 816, 771, 290, 290, 290, 290, 288, 258, + -253, -349, -414, -447, -463, 1329, 1299, -479, 1314, 1312, 1057, 1057, 1042, + 1042, 1026, 1026, 785, 785, 785, 785, 784, 784, 784, 784, 769, 769, + 769, 769, 768, 768, 768, 768, -319, 851, 821, -335, 836, 850, 805, + 849, 341, 340, 325, 336, 533, 533, 579, 579, 564, 564, 773, 832, + 578, 548, 563, 516, 321, 276, 306, 291, 304, 259, -251, -572, -733, + -830, -863, -879, 1041, 1041, 784, 784, 784, 784, 769, 769, 769, 769, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, -511, -527, -543, 1396, 1351, 1381, 1366, 1395, 1335, 1380, + -559, 1334, 1138, 1138, 1063, 1063, 1350, 1392, 1031, 1031, 1062, 1062, 1364, + 1363, 1120, 1120, 1333, 1348, 881, 881, 881, 881, 375, 374, 359, 373, + 343, 358, 341, 325, 791, 791, 1123, 1122, -703, 1105, 1045, -719, 865, + 865, 790, 790, 774, 774, 1104, 1029, 338, 293, 323, 308, -799, -815, + 833, 788, 772, 818, 803, 816, 322, 292, 307, 320, 561, 531, 515, + 546, 289, 274, 288, 258, -251, -525, -605, -685, -765, -831, -846, 1298, + 1057, 1057, 1312, 1282, 785, 785, 785, 785, 784, 784, 784, 784, 769, + 769, 769, 769, 512, 512, 512, 512, 512, 512, 512, 512, 1399, 1398, + 1383, 1367, 1382, 1396, 1351, -511, 1381, 1366, 1139, 1139, 1079, 1079, 1124, + 1124, 1364, 1349, 1363, 1333, 882, 882, 882, 882, 807, 807, 807, 807, + 1094, 1094, 1136, 1136, 373, 341, 535, 535, 881, 775, 867, 822, 774, + -591, 324, 338, -671, 849, 550, 550, 866, 864, 609, 609, 293, 336, + 534, 534, 789, 835, 773, -751, 834, 804, 308, 307, 833, 788, 832, + 772, 562, 562, 547, 547, 305, 275, 560, 515, 290, 290, -252, -397, + -477, -557, -622, -653, -719, -735, -750, 1329, 1299, 1314, 1057, 1057, 1042, + 1042, 1312, 1282, 1024, 1024, 785, 785, 785, 785, 784, 784, 784, 784, + 769, 769, 769, 769, -383, 1127, 1141, 1111, 1126, 1140, 1095, 1110, 869, + 869, 883, 883, 1079, 1109, 882, 882, 375, 374, 807, 868, 838, 881, + 791, -463, 867, 822, 368, 263, 852, 837, 836, -543, 610, 610, 550, + 550, 352, 336, 534, 534, 865, 774, 851, 821, 850, 805, 593, 533, + 579, 564, 773, 832, 578, 578, 548, 548, 577, 577, 307, 276, 306, + 291, 516, 560, 259, 259, -250, -2107, -2507, -2764, -2909, -2974, -3007, -3023, + 1041, 1041, 1040, 1040, 769, 769, 769, 769, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, -767, -1052, + -1213, -1277, -1358, -1405, -1469, -1535, -1550, -1582, -1614, -1647, -1662, -1694, -1726, + -1759, -1774, -1807, -1822, -1854, -1886, 1565, -1919, -1935, -1951, -1967, 1731, 1730, + 1580, 1717, -1983, 1729, 1564, -1999, 1548, -2015, -2031, 1715, 1595, -2047, 1714, + -2063, 1610, -2079, 1609, -2095, 1323, 1323, 1457, 1457, 1307, 1307, 1712, 1547, + 1641, 1700, 1699, 1594, 1685, 1625, 1442, 1442, 1322, 1322, -780, -973, -910, + 1279, 1278, 1277, 1262, 1276, 1261, 1275, 1215, 1260, 1229, -959, 974, 974, + 989, 989, -943, 735, 478, 478, 495, 463, 506, 414, -1039, 1003, 958, + 1017, 927, 942, 987, 957, 431, 476, 1272, 1167, 1228, -1183, 1256, -1199, + 895, 895, 941, 941, 1242, 1227, 1212, 1135, 1014, 1014, 490, 489, 503, + 487, 910, 1013, 985, 925, 863, 894, 970, 955, 1012, 847, -1343, 831, + 755, 755, 984, 909, 428, 366, 754, 559, -1391, 752, 486, 457, 924, + 997, 698, 698, 983, 893, 740, 740, 908, 877, 739, 739, 667, 667, + 953, 938, 497, 287, 271, 271, 683, 606, 590, 712, 726, 574, 302, + 302, 738, 736, 481, 286, 526, 725, 605, 711, 636, 724, 696, 651, + 589, 681, 666, 710, 364, 467, 573, 695, 466, 466, 301, 465, 379, + 379, 709, 604, 665, 679, 316, 316, 634, 633, 436, 436, 464, 269, + 424, 394, 452, 332, 438, 363, 347, 408, 393, 448, 331, 422, 362, + 407, 392, 421, 346, 406, 391, 376, 375, 359, 1441, 1306, -2367, 1290, + -2383, 1337, -2399, -2415, 1426, 1321, -2431, 1411, 1336, -2447, -2463, -2479, 1169, + 1169, 1049, 1049, 1424, 1289, 1412, 1352, 1319, -2495, 1154, 1154, 1064, 1064, + 1153, 1153, 416, 390, 360, 404, 403, 389, 344, 374, 373, 343, 358, + 372, 327, 357, 342, 311, 356, 326, 1395, 1394, 1137, 1137, 1047, 1047, + 1365, 1392, 1287, 1379, 1334, 1364, 1349, 1378, 1318, 1363, 792, 792, 792, + 792, 1152, 1152, 1032, 1032, 1121, 1121, 1046, 1046, 1120, 1120, 1030, 1030, + -2895, 1106, 1061, 1104, 849, 849, 789, 789, 1091, 1076, 1029, 1090, 1060, + 1075, 833, 833, 309, 324, 532, 532, 832, 772, 818, 803, 561, 561, + 531, 560, 515, 546, 289, 274, 288, 258, -250, -1179, -1579, -1836, -1996, + -2124, -2253, -2333, -2413, -2477, -2542, -2574, -2607, -2622, -2655, 1314, 1313, 1298, + 1312, 1282, 785, 785, 785, 785, 1040, 1040, 1025, 1025, 768, 768, 768, + 768, -766, -798, -830, -862, -895, -911, -927, -943, -959, -975, -991, -1007, + -1023, -1039, -1055, -1070, 1724, 1647, -1103, -1119, 1631, 1767, 1662, 1738, 1708, + 1723, -1135, 1780, 1615, 1779, 1599, 1677, 1646, 1778, 1583, -1151, 1777, 1567, + 1737, 1692, 1765, 1722, 1707, 1630, 1751, 1661, 1764, 1614, 1736, 1676, 1763, + 1750, 1645, 1598, 1721, 1691, 1762, 1706, 1582, 1761, 1566, -1167, 1749, 1629, + 767, 766, 751, 765, 494, 494, 735, 764, 719, 749, 734, 763, 447, + 447, 748, 718, 477, 506, 431, 491, 446, 476, 461, 505, 415, 430, + 475, 445, 504, 399, 460, 489, 414, 503, 383, 474, 429, 459, 502, + 502, 746, 752, 488, 398, 501, 473, 413, 472, 486, 271, 480, 270, + -1439, -1455, 1357, -1471, -1487, -1503, 1341, 1325, -1519, 1489, 1463, 1403, 1309, + -1535, 1372, 1448, 1418, 1476, 1356, 1462, 1387, -1551, 1475, 1340, 1447, 1402, + 1386, -1567, 1068, 1068, 1474, 1461, 455, 380, 468, 440, 395, 425, 410, + 454, 364, 467, 466, 464, 453, 269, 409, 448, 268, 432, 1371, 1473, + 1432, 1417, 1308, 1460, 1355, 1446, 1459, 1431, 1083, 1083, 1401, 1416, 1458, + 1445, 1067, 1067, 1370, 1457, 1051, 1051, 1291, 1430, 1385, 1444, 1354, 1415, + 1400, 1443, 1082, 1082, 1173, 1113, 1186, 1066, 1185, 1050, -1967, 1158, 1128, + 1172, 1097, 1171, 1081, -1983, 1157, 1112, 416, 266, 375, 400, 1170, 1142, + 1127, 1065, 793, 793, 1169, 1033, 1156, 1096, 1141, 1111, 1155, 1080, 1126, + 1140, 898, 898, 808, 808, 897, 897, 792, 792, 1095, 1152, 1032, 1125, + 1110, 1139, 1079, 1124, 882, 807, 838, 881, 853, 791, -2319, 867, 368, + 263, 822, 852, 837, 866, 806, 865, -2399, 851, 352, 262, 534, 534, + 821, 836, 594, 594, 549, 549, 593, 593, 533, 533, 848, 773, 579, + 579, 564, 578, 548, 563, 276, 276, 577, 576, 306, 291, 516, 560, + 305, 305, 275, 259, -251, -892, -2058, -2620, -2828, -2957, -3023, -3039, 1041, + 1041, 1040, 1040, 769, 769, 769, 769, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, -511, -527, -543, + -559, 1530, -575, -591, 1528, 1527, 1407, 1526, 1391, 1023, 1023, 1023, 1023, + 1525, 1375, 1268, 1268, 1103, 1103, 1087, 1087, 1039, 1039, 1523, -604, 815, + 815, 815, 815, 510, 495, 509, 479, 508, 463, 507, 447, 431, 505, + 415, 399, -734, -782, 1262, -815, 1259, 1244, -831, 1258, 1228, -847, -863, + 1196, -879, 1253, 987, 987, 748, -767, 493, 493, 462, 477, 414, 414, + 686, 669, 478, 446, 461, 445, 474, 429, 487, 458, 412, 471, 1266, + 1264, 1009, 1009, 799, 799, -1019, -1276, -1452, -1581, -1677, -1757, -1821, -1886, + -1933, -1997, 1257, 1257, 1483, 1468, 1512, 1422, 1497, 1406, 1467, 1496, 1421, + 1510, 1134, 1134, 1225, 1225, 1466, 1451, 1374, 1405, 1252, 1252, 1358, 1480, + 1164, 1164, 1251, 1251, 1238, 1238, 1389, 1465, -1407, 1054, 1101, -1423, 1207, + -1439, 830, 830, 1248, 1038, 1237, 1117, 1223, 1148, 1236, 1208, 411, 426, + 395, 410, 379, 269, 1193, 1222, 1132, 1235, 1221, 1116, 976, 976, 1192, + 1162, 1177, 1220, 1131, 1191, 963, 963, -1647, 961, 780, -1663, 558, 558, + 994, 993, 437, 408, 393, 407, 829, 978, 813, 797, 947, -1743, 721, + 721, 377, 392, 844, 950, 828, 890, 706, 706, 812, 859, 796, 960, + 948, 843, 934, 874, 571, 571, -1919, 690, 555, 689, 421, 346, 539, + 539, 944, 779, 918, 873, 932, 842, 903, 888, 570, 570, 931, 917, + 674, 674, -2575, 1562, -2591, 1609, -2607, 1654, 1322, 1322, 1441, 1441, 1696, + 1546, 1683, 1593, 1669, 1624, 1426, 1426, 1321, 1321, 1639, 1680, 1425, 1425, + 1305, 1305, 1545, 1668, 1608, 1623, 1667, 1592, 1638, 1666, 1320, 1320, 1652, + 1607, 1409, 1409, 1304, 1304, 1288, 1288, 1664, 1637, 1395, 1395, 1335, 1335, + 1622, 1636, 1394, 1394, 1319, 1319, 1606, 1621, 1392, 1392, 1137, 1137, 1137, + 1137, 345, 390, 360, 375, 404, 373, 1047, -2751, -2767, -2783, 1062, 1121, + 1046, -2799, 1077, -2815, 1106, 1061, 789, 789, 1105, 1104, 263, 355, 310, + 340, 325, 354, 352, 262, 339, 324, 1091, 1076, 1029, 1090, 1060, 1075, + 833, 833, 788, 788, 1088, 1028, 818, 818, 803, 803, 561, 561, 531, + 531, 816, 771, 546, 546, 289, 274, 288, 258, -253, -317, -381, -446, + -478, -509, 1279, 1279, -811, -1179, -1451, -1756, -1900, -2028, -2189, -2253, -2333, + -2414, -2445, -2511, -2526, 1313, 1298, -2559, 1041, 1041, 1040, 1040, 1025, 1025, + 1024, 1024, 1022, 1007, 1021, 991, 1020, 975, 1019, 959, 687, 687, 1018, + 1017, 671, 671, 655, 655, 1016, 1015, 639, 639, 758, 758, 623, 623, + 757, 607, 756, 591, 755, 575, 754, 559, 543, 543, 1009, 783, -575, + -621, -685, -749, 496, -590, 750, 749, 734, 748, 974, 989, 1003, 958, + 988, 973, 1002, 942, 987, 957, 972, 1001, 926, 986, 941, 971, 956, + 1000, 910, 985, 925, 999, 894, 970, -1071, -1087, -1102, 1390, -1135, 1436, + 1509, 1451, 1374, -1151, 1405, 1358, 1480, 1420, -1167, 1507, 1494, 1389, 1342, + 1465, 1435, 1450, 1326, 1505, 1310, 1493, 1373, 1479, 1404, 1492, 1464, 1419, + 428, 443, 472, 397, 736, 526, 464, 464, 486, 457, 442, 471, 484, + 482, 1357, 1449, 1434, 1478, 1388, 1491, 1341, 1490, 1325, 1489, 1463, 1403, + 1309, 1477, 1372, 1448, 1418, 1433, 1476, 1356, 1462, 1387, -1439, 1475, 1340, + 1447, 1402, 1474, 1324, 1461, 1371, 1473, 269, 448, 1432, 1417, 1308, 1460, + -1711, 1459, -1727, 1441, 1099, 1099, 1446, 1386, 1431, 1401, -1743, 1289, 1083, + 1083, 1160, 1160, 1458, 1445, 1067, 1067, 1370, 1457, 1307, 1430, 1129, 1129, + 1098, 1098, 268, 432, 267, 416, 266, 400, -1887, 1144, 1187, 1082, 1173, + 1113, 1186, 1066, 1050, 1158, 1128, 1143, 1172, 1097, 1171, 1081, 420, 391, + 1157, 1112, 1170, 1142, 1127, 1065, 1169, 1049, 1156, 1096, 1141, 1111, 1155, + 1080, 1126, 1154, 1064, 1153, 1140, 1095, 1048, -2159, 1125, 1110, 1137, -2175, + 823, 823, 1139, 1138, 807, 807, 384, 264, 368, 263, 868, 838, 853, + 791, 867, 822, 852, 837, 866, 806, 865, 790, -2319, 851, 821, 836, + 352, 262, 850, 805, 849, -2399, 533, 533, 835, 820, 336, 261, 578, + 548, 563, 577, 532, 532, 832, 772, 562, 562, 547, 547, 305, 275, + 560, 515, 290, 290, 288, 258 + }; + static const drmp3_uint8 tab32[] = { 130, 162, 193, 209, 44, 28, 76, 140, 9, 9, + 9, 9, 9, 9, 9, 9, 190, 254, 222, 238, + 126, 94, 157, 157, 109, 61, 173, 205 }; + static const drmp3_uint8 tab33[] = { 252, 236, 220, 204, 188, 172, 156, 140, + 124, 108, 92, 76, 60, 44, 28, 12 }; + static const drmp3_int16 tabindex[2 * 16] = { 0, 32, 64, 98, 0, 132, 180, 218, + 292, 364, 426, 538, 648, 746, 0, 1126, + 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, + 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842 }; + static const drmp3_uint8 g_linbits[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 6, 8, 10, 13, 4, 5, 6, 7, 8, 9, 11, 13 }; + + #define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) + #define DRMP3_FLUSH_BITS(n) \ + { \ + bs_cache <<= (n); \ + bs_sh += (n); \ + } + #define DRMP3_CHECK_BITS \ + while (bs_sh >= 0) \ + { \ + bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; \ + bs_sh -= 8; \ + } + #define DRMP3_BSPOS ((bs_next_ptr - bs->buf) * 8 - 24 + bs_sh) + + float one = 0.0f; + int ireg = 0, big_val_cnt = gr_info->big_values; + const drmp3_uint8* sfb = gr_info->sfbtab; + const drmp3_uint8* bs_next_ptr = bs->buf + bs->pos / 8; + drmp3_uint32 bs_cache = + (((bs_next_ptr[0] * 256u + bs_next_ptr[1]) * 256u + bs_next_ptr[2]) * 256u + bs_next_ptr[3]) + << (bs->pos & 7); + int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; + bs_next_ptr += 4; + + while (big_val_cnt > 0) + { + int tab_num = gr_info->table_select[ireg]; + int sfb_cnt = gr_info->region_count[ireg++]; + const drmp3_int16* codebook = tabs + tabindex[tab_num]; + int linbits = g_linbits[tab_num]; + if (linbits) + { + do + { + np = *sfb++ / 2; + pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[DRMP3_PEEK_BITS(w)]; + while (leaf < 0) + { + DRMP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; + } + DRMP3_FLUSH_BITS(leaf >> 8); + + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + if (lsb == 15) + { + lsb += DRMP3_PEEK_BITS(linbits); + DRMP3_FLUSH_BITS(linbits); + DRMP3_CHECK_BITS; + *dst = + one * drmp3_L3_pow_43(lsb) * ((drmp3_int32)bs_cache < 0 ? -1 : 1); + } + else + { + *dst = g_drmp3_pow43[16 + lsb - 16 * (bs_cache >> 31)] * one; + } + DRMP3_FLUSH_BITS(lsb ? 1 : 0); + } + DRMP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } + else + { + do + { + np = *sfb++ / 2; + pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[DRMP3_PEEK_BITS(w)]; + while (leaf < 0) + { + DRMP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; + } + DRMP3_FLUSH_BITS(leaf >> 8); + + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + *dst = g_drmp3_pow43[16 + lsb - 16 * (bs_cache >> 31)] * one; + DRMP3_FLUSH_BITS(lsb ? 1 : 0); + } + DRMP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } + } + + for (np = 1 - big_val_cnt;; dst += 4) + { + const drmp3_uint8* codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; + int leaf = codebook_count1[DRMP3_PEEK_BITS(4)]; + if (!(leaf & 8)) + { + leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; + } + DRMP3_FLUSH_BITS(leaf & 7); + if (DRMP3_BSPOS > layer3gr_limit) + { + break; + } + #define DRMP3_RELOAD_SCALEFACTOR \ + if (!--np) \ + { \ + np = *sfb++ / 2; \ + if (!np) \ + break; \ + one = *scf++; \ + } + #define DRMP3_DEQ_COUNT1(s) \ + if (leaf & (128 >> s)) \ + { \ + dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; \ + DRMP3_FLUSH_BITS(1) \ + } + DRMP3_RELOAD_SCALEFACTOR; + DRMP3_DEQ_COUNT1(0); + DRMP3_DEQ_COUNT1(1); + DRMP3_RELOAD_SCALEFACTOR; + DRMP3_DEQ_COUNT1(2); + DRMP3_DEQ_COUNT1(3); + DRMP3_CHECK_BITS; + } + + bs->pos = layer3gr_limit; +} + +static void drmp3_L3_midside_stereo(float* left, int n) +{ + int i = 0; + float* right = left + 576; + #if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) + { + for (; i < n - 3; i += 4) + { + drmp3_f4 vl = DRMP3_VLD(left + i); + drmp3_f4 vr = DRMP3_VLD(right + i); + DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr)); + DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr)); + } + #ifdef __GNUC__ + /* Workaround for spurious -Waggressive-loop-optimizations warning from gcc. + * For more info see: https://github.com/lieff/minimp3/issues/88 + */ + if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) + return; + #endif + } + #endif + for (; i < n; i++) + { + float a = left[i]; + float b = right[i]; + left[i] = a + b; + right[i] = a - b; + } +} + +static void drmp3_L3_intensity_stereo_band(float* left, int n, float kl, float kr) +{ + int i; + for (i = 0; i < n; i++) + { + left[i + 576] = left[i] * kr; + left[i] = left[i] * kl; + } +} + +static void drmp3_L3_stereo_top_band(const float* right, const drmp3_uint8* sfb, int nbands, + int max_band[3]) +{ + int i, k; + + max_band[0] = max_band[1] = max_band[2] = -1; + + for (i = 0; i < nbands; i++) + { + for (k = 0; k < sfb[i]; k += 2) + { + if (right[k] != 0 || right[k + 1] != 0) + { + max_band[i % 3] = i; + break; + } + } + right += sfb[i]; + } +} + +static void drmp3_L3_stereo_process(float* left, const drmp3_uint8* ist_pos, const drmp3_uint8* sfb, + const drmp3_uint8* hdr, int max_band[3], int mpeg2_sh) +{ + static const float g_pan[7 * 2] = { + 0, 1, 0.21132487f, 0.78867513f, 0.36602540f, 0.63397460f, 0.5f, + 0.5f, 0.63397460f, 0.36602540f, 0.78867513f, 0.21132487f, 1, 0 + }; + unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; + + for (i = 0; sfb[i]; i++) + { + unsigned ipos = ist_pos[i]; + if ((int)i > max_band[i % 3] && ipos < max_pos) + { + float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + kl = g_pan[2 * ipos]; + kr = g_pan[2 * ipos + 1]; + } + else + { + kl = 1; + kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); + if (ipos & 1) + { + kl = kr; + kr = 1; + } + } + drmp3_L3_intensity_stereo_band(left, sfb[i], kl * s, kr * s); + } + else if (DRMP3_HDR_TEST_MS_STEREO(hdr)) + { + drmp3_L3_midside_stereo(left, sfb[i]); + } + left += sfb[i]; + } +} + +static void drmp3_L3_intensity_stereo(float* left, drmp3_uint8* ist_pos, const drmp3_L3_gr_info* gr, + const drmp3_uint8* hdr) +{ + int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; + int i, max_blocks = gr->n_short_sfb ? 3 : 1; + + drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); + if (gr->n_long_sfb) + { + max_band[0] = max_band[1] = max_band[2] = + DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]); + } + for (i = 0; i < max_blocks; i++) + { + int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; + int itop = n_sfb - max_blocks + i; + int prev = itop - max_blocks; + ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); + } + drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); +} + +static void drmp3_L3_reorder(float* grbuf, float* scratch, const drmp3_uint8* sfb) +{ + int i, len; + float *src = grbuf, *dst = scratch; + + for (; 0 != (len = *sfb); sfb += 3, src += 2 * len) + { + for (i = 0; i < len; i++, src++) + { + *dst++ = src[0 * len]; + *dst++ = src[1 * len]; + *dst++ = src[2 * len]; + } + } + DRMP3_COPY_MEMORY(grbuf, scratch, (dst - scratch) * sizeof(float)); +} + +static void drmp3_L3_antialias(float* grbuf, int nbands) +{ + static const float g_aa[2][8] = { + {0.85749293f, 0.88174200f, 0.94962865f, 0.98331459f, 0.99551782f, 0.99916056f, 0.99989920f, + 0.99999316f}, + {0.51449576f, 0.47173197f, 0.31337745f, 0.18191320f, 0.09457419f, 0.04096558f, 0.01419856f, + 0.00369997f} + }; + + for (; nbands > 0; nbands--, grbuf += 18) + { + int i = 0; + #if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) + for (; i < 8; i += 4) + { + drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i); + drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i); + drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i); + drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i); + vd = DRMP3_VREV(vd); + DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1))); + vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0)); + DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd)); + } + #endif + #ifndef DR_MP3_ONLY_SIMD + for (; i < 8; i++) + { + float u = grbuf[18 + i]; + float d = grbuf[17 - i]; + grbuf[18 + i] = u * g_aa[0][i] - d * g_aa[1][i]; + grbuf[17 - i] = u * g_aa[1][i] + d * g_aa[0][i]; + } + #endif + } +} + +static void drmp3_L3_dct3_9(float* y) +{ + float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; + + s0 = y[0]; + s2 = y[2]; + s4 = y[4]; + s6 = y[6]; + s8 = y[8]; + t0 = s0 + s6 * 0.5f; + s0 -= s6; + t4 = (s4 + s2) * 0.93969262f; + t2 = (s8 + s2) * 0.76604444f; + s6 = (s4 - s8) * 0.17364818f; + s4 += s8 - s2; + + s2 = s0 - s4 * 0.5f; + y[4] = s4 + s0; + s8 = t0 - t2 + s6; + s0 = t0 - t4 + t2; + s4 = t0 + t4 - s6; + + s1 = y[1]; + s3 = y[3]; + s5 = y[5]; + s7 = y[7]; + + s3 *= 0.86602540f; + t0 = (s5 + s1) * 0.98480775f; + t4 = (s5 - s7) * 0.34202014f; + t2 = (s1 + s7) * 0.64278761f; + s1 = (s1 - s5 - s7) * 0.86602540f; + + s5 = t0 - s3 - t2; + s7 = t4 - s3 - t0; + s3 = t4 + s3 - t2; + + y[0] = s4 - s7; + y[1] = s2 + s1; + y[2] = s0 - s3; + y[3] = s8 + s5; + y[5] = s8 - s5; + y[6] = s0 + s3; + y[7] = s2 - s1; + y[8] = s4 + s7; +} + +static void drmp3_L3_imdct36(float* grbuf, float* overlap, const float* window, int nbands) +{ + int i, j; + static const float g_twid9[18] = { 0.73727734f, 0.79335334f, 0.84339145f, 0.88701083f, + 0.92387953f, 0.95371695f, 0.97629601f, 0.99144486f, + 0.99904822f, 0.67559021f, 0.60876143f, 0.53729961f, + 0.46174861f, 0.38268343f, 0.30070580f, 0.21643961f, + 0.13052619f, 0.04361938f }; + + for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) + { + float co[9], si[9]; + co[0] = -grbuf[0]; + si[0] = grbuf[17]; + for (i = 0; i < 4; i++) + { + si[8 - 2 * i] = grbuf[4 * i + 1] - grbuf[4 * i + 2]; + co[1 + 2 * i] = grbuf[4 * i + 1] + grbuf[4 * i + 2]; + si[7 - 2 * i] = grbuf[4 * i + 4] - grbuf[4 * i + 3]; + co[2 + 2 * i] = -(grbuf[4 * i + 3] + grbuf[4 * i + 4]); + } + drmp3_L3_dct3_9(co); + drmp3_L3_dct3_9(si); + + si[1] = -si[1]; + si[3] = -si[3]; + si[5] = -si[5]; + si[7] = -si[7]; + + i = 0; + + #if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) + for (; i < 8; i += 4) + { + drmp3_f4 vovl = DRMP3_VLD(overlap + i); + drmp3_f4 vc = DRMP3_VLD(co + i); + drmp3_f4 vs = DRMP3_VLD(si + i); + drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i); + drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i); + drmp3_f4 vw0 = DRMP3_VLD(window + i); + drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i); + drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0)); + DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1))); + DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1))); + vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0)); + DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum)); + } + #endif + for (; i < 9; i++) + { + float ovl = overlap[i]; + float sum = co[i] * g_twid9[9 + i] + si[i] * g_twid9[0 + i]; + overlap[i] = co[i] * g_twid9[0 + i] - si[i] * g_twid9[9 + i]; + grbuf[i] = ovl * window[0 + i] - sum * window[9 + i]; + grbuf[17 - i] = ovl * window[9 + i] + sum * window[0 + i]; + } + } +} + +static void drmp3_L3_idct3(float x0, float x1, float x2, float* dst) +{ + float m1 = x1 * 0.86602540f; + float a1 = x0 - x2 * 0.5f; + dst[1] = x0 + x2; + dst[0] = a1 + m1; + dst[2] = a1 - m1; +} + +static void drmp3_L3_imdct12(float* x, float* dst, float* overlap) +{ + static const float g_twid3[6] = { 0.79335334f, 0.92387953f, 0.99144486f, + 0.60876143f, 0.38268343f, 0.13052619f }; + float co[3], si[3]; + int i; + + drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); + drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); + si[1] = -si[1]; + + for (i = 0; i < 3; i++) + { + float ovl = overlap[i]; + float sum = co[i] * g_twid3[3 + i] + si[i] * g_twid3[0 + i]; + overlap[i] = co[i] * g_twid3[0 + i] - si[i] * g_twid3[3 + i]; + dst[i] = ovl * g_twid3[2 - i] - sum * g_twid3[5 - i]; + dst[5 - i] = ovl * g_twid3[5 - i] + sum * g_twid3[2 - i]; + } +} + +static void drmp3_L3_imdct_short(float* grbuf, float* overlap, int nbands) +{ + for (; nbands > 0; nbands--, overlap += 9, grbuf += 18) + { + float tmp[18]; + DRMP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); + DRMP3_COPY_MEMORY(grbuf, overlap, 6 * sizeof(float)); + drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); + drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); + drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6); + } +} + +static void drmp3_L3_change_sign(float* grbuf) +{ + int b, i; + for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) + for (i = 1; i < 18; i += 2) + grbuf[i] = -grbuf[i]; +} + +static void drmp3_L3_imdct_gr(float* grbuf, float* overlap, unsigned block_type, + unsigned n_long_bands) +{ + static const float g_mdct_window[2][18] = { + {0.99904822f, 0.99144486f, 0.97629601f, 0.95371695f, 0.92387953f, 0.88701083f, 0.84339145f, + 0.79335334f, 0.73727734f, 0.04361938f, 0.13052619f, 0.21643961f, 0.30070580f, 0.38268343f, + 0.46174861f, 0.53729961f,0.60876143f, 0.67559021f }, + { 1, 1, 1, 1, 1, 1, 0.99144486f, 0.92387953f, 0.79335334f, 0, 0, 0, 0, 0, 0, 0.13052619f, + 0.38268343f, 0.60876143f} + }; + if (n_long_bands) + { + drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); + grbuf += 18 * n_long_bands; + overlap += 9 * n_long_bands; + } + if (block_type == DRMP3_SHORT_BLOCK_TYPE) + drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); + else + drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], + 32 - n_long_bands); +} + +static void drmp3_L3_save_reservoir(drmp3dec* h, drmp3dec_scratch* s) +{ + int pos = (s->bs.pos + 7) / 8u; + int remains = s->bs.limit / 8u - pos; + if (remains > DRMP3_MAX_BITRESERVOIR_BYTES) + { + pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES; + remains = DRMP3_MAX_BITRESERVOIR_BYTES; + } + if (remains > 0) + { + DRMP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); + } + h->reserv = remains; +} + +static int drmp3_L3_restore_reservoir(drmp3dec* h, drmp3_bs* bs, drmp3dec_scratch* s, + int main_data_begin) +{ + int frame_bytes = (bs->limit - bs->pos) / 8; + int bytes_have = DRMP3_MIN(h->reserv, main_data_begin); + DRMP3_COPY_MEMORY(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), + DRMP3_MIN(h->reserv, main_data_begin)); + DRMP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos / 8, frame_bytes); + drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); + return h->reserv >= main_data_begin; +} + +static void drmp3_L3_decode(drmp3dec* h, drmp3dec_scratch* s, drmp3_L3_gr_info* gr_info, int nch) +{ + int ch; + + for (ch = 0; ch < nch; ch++) + { + int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; + drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); + drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); + } + + if (DRMP3_HDR_TEST_I_STEREO(h->header)) + { + drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); + } + else if (DRMP3_HDR_IS_MS_STEREO(h->header)) + { + drmp3_L3_midside_stereo(s->grbuf[0], 576); + } + + for (ch = 0; ch < nch; ch++, gr_info++) + { + int aa_bands = 31; + int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) + << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); + + if (gr_info->n_short_sfb) + { + aa_bands = n_long_bands - 1; + drmp3_L3_reorder(s->grbuf[ch] + n_long_bands * 18, s->syn[0], + gr_info->sfbtab + gr_info->n_long_sfb); + } + + drmp3_L3_antialias(s->grbuf[ch], aa_bands); + drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); + drmp3_L3_change_sign(s->grbuf[ch]); + } +} + +static void drmp3d_DCT_II(float* grbuf, int n) +{ + static const float g_sec[24] = { 10.19000816f, 0.50060302f, 0.50241929f, 3.40760851f, + 0.50547093f, 0.52249861f, 2.05778098f, 0.51544732f, + 0.56694406f, 1.48416460f, 0.53104258f, 0.64682180f, + 1.16943991f, 0.55310392f, 0.78815460f, 0.97256821f, + 0.58293498f, 1.06067765f, 0.83934963f, 0.62250412f, + 1.72244716f, 0.74453628f, 0.67480832f, 5.10114861f }; + int i, k = 0; + #if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) + for (; k < n; k += 4) + { + drmp3_f4 t[4][8], *x; + float* y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + drmp3_f4 x0 = DRMP3_VLD(&y[i * 18]); + drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i) * 18]); + drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i) * 18]); + drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i) * 18]); + drmp3_f4 t0 = DRMP3_VADD(x0, x3); + drmp3_f4 t1 = DRMP3_VADD(x1, x2); + drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3 * i + 0]); + drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3 * i + 1]); + x[0] = DRMP3_VADD(t0, t1); + x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3 * i + 2]); + x[16] = DRMP3_VADD(t3, t2); + x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3 * i + 2]); + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + drmp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], + x6 = x[6], x7 = x[7], xt; + xt = DRMP3_VSUB(x0, x7); + x0 = DRMP3_VADD(x0, x7); + x7 = DRMP3_VSUB(x1, x6); + x1 = DRMP3_VADD(x1, x6); + x6 = DRMP3_VSUB(x2, x5); + x2 = DRMP3_VADD(x2, x5); + x5 = DRMP3_VSUB(x3, x4); + x3 = DRMP3_VADD(x3, x4); + x4 = DRMP3_VSUB(x0, x3); + x0 = DRMP3_VADD(x0, x3); + x3 = DRMP3_VSUB(x1, x2); + x1 = DRMP3_VADD(x1, x2); + x[0] = DRMP3_VADD(x0, x1); + x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f); + x5 = DRMP3_VADD(x5, x6); + x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f); + x7 = DRMP3_VADD(x7, xt); + x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f); + x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); /* rotate by PI/8 */ + x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f)); + x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); + x0 = DRMP3_VSUB(xt, x6); + xt = DRMP3_VADD(xt, x6); + x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f); + x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f); + x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f); + x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f); + x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f); + x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f); + } + + if (k > n - 3) + { + #if DRMP3_HAVE_SSE + #define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64*)(void*)&y[i * 18], v) + #else + #define DRMP3_VSAVE2(i, v) vst1_f32((float32_t*)&y[(i)*18], vget_low_f32(v)) + #endif + for (i = 0; i < 7; i++, y += 4 * 18) + { + drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); + DRMP3_VSAVE2(0, t[0][i]); + DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s)); + DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1])); + DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s)); + } + DRMP3_VSAVE2(0, t[0][7]); + DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7])); + DRMP3_VSAVE2(2, t[1][7]); + DRMP3_VSAVE2(3, t[3][7]); + } + else + { + #define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[(i)*18], v) + for (i = 0; i < 7; i++, y += 4 * 18) + { + drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); + DRMP3_VSAVE4(0, t[0][i]); + DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s)); + DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1])); + DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s)); + } + DRMP3_VSAVE4(0, t[0][7]); + DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7])); + DRMP3_VSAVE4(2, t[1][7]); + DRMP3_VSAVE4(3, t[3][7]); + } + } + else + #endif + #ifdef DR_MP3_ONLY_SIMD + {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */ + #else + for (; k < n; k++) + { + float t[4][8], *x, *y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + float x0 = y[i * 18]; + float x1 = y[(15 - i) * 18]; + float x2 = y[(16 + i) * 18]; + float x3 = y[(31 - i) * 18]; + float t0 = x0 + x3; + float t1 = x1 + x2; + float t2 = (x1 - x2) * g_sec[3 * i + 0]; + float t3 = (x0 - x3) * g_sec[3 * i + 1]; + x[0] = t0 + t1; + x[8] = (t0 - t1) * g_sec[3 * i + 2]; + x[16] = t3 + t2; + x[24] = (t3 - t2) * g_sec[3 * i + 2]; + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], + x7 = x[7], xt; + xt = x0 - x7; + x0 += x7; + x7 = x1 - x6; + x1 += x6; + x6 = x2 - x5; + x2 += x5; + x5 = x3 - x4; + x3 += x4; + x4 = x0 - x3; + x0 += x3; + x3 = x1 - x2; + x1 += x2; + x[0] = x0 + x1; + x[4] = (x0 - x1) * 0.70710677f; + x5 = x5 + x6; + x6 = (x6 + x7) * 0.70710677f; + x7 = x7 + xt; + x3 = (x3 + x4) * 0.70710677f; + x5 -= x7 * 0.198912367f; /* rotate by PI/8 */ + x7 += x5 * 0.382683432f; + x5 -= x7 * 0.198912367f; + x0 = xt - x6; + xt += x6; + x[1] = (xt + x7) * 0.50979561f; + x[2] = (x4 + x3) * 0.54119611f; + x[3] = (x0 - x5) * 0.60134488f; + x[5] = (x0 + x5) * 0.89997619f; + x[6] = (x4 - x3) * 1.30656302f; + x[7] = (xt - x7) * 2.56291556f; + } + for (i = 0; i < 7; i++, y += 4 * 18) + { + y[0 * 18] = t[0][i]; + y[1 * 18] = t[2][i] + t[3][i] + t[3][i + 1]; + y[2 * 18] = t[1][i] + t[1][i + 1]; + y[3 * 18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; + } + y[0 * 18] = t[0][7]; + y[1 * 18] = t[2][7] + t[3][7]; + y[2 * 18] = t[1][7]; + y[3 * 18] = t[3][7]; + } + #endif +} + + #ifndef DR_MP3_FLOAT_OUTPUT +typedef drmp3_int16 drmp3d_sample_t; + +static drmp3_int16 drmp3d_scale_pcm(float sample) +{ + drmp3_int16 s; + #if DRMP3_HAVE_ARMV6 + drmp3_int32 s32 = (drmp3_int32)(sample + .5f); + s32 -= (s32 < 0); + s = (drmp3_int16)drmp3_clip_int16_arm(s32); + #else + if (sample >= 32766.5) + return (drmp3_int16)32767; + if (sample <= -32767.5) + return (drmp3_int16)-32768; + s = (drmp3_int16)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + #endif + return s; +} + #else +typedef float drmp3d_sample_t; + +static float drmp3d_scale_pcm(float sample) +{ + return sample * (1.f / 32768.f); +} + #endif + +static void drmp3d_synth_pair(drmp3d_sample_t* pcm, int nch, const float* z) +{ + float a; + a = (z[14 * 64] - z[0]) * 29; + a += (z[1 * 64] + z[13 * 64]) * 213; + a += (z[12 * 64] - z[2 * 64]) * 459; + a += (z[3 * 64] + z[11 * 64]) * 2037; + a += (z[10 * 64] - z[4 * 64]) * 5153; + a += (z[5 * 64] + z[9 * 64]) * 6574; + a += (z[8 * 64] - z[6 * 64]) * 37489; + a += z[7 * 64] * 75038; + pcm[0] = drmp3d_scale_pcm(a); + + z += 2; + a = z[14 * 64] * 104; + a += z[12 * 64] * 1567; + a += z[10 * 64] * 9727; + a += z[8 * 64] * 64019; + a += z[6 * 64] * -9975; + a += z[4 * 64] * -45; + a += z[2 * 64] * 146; + a += z[0 * 64] * -5; + pcm[16 * nch] = drmp3d_scale_pcm(a); +} + +static void drmp3d_synth(float* xl, drmp3d_sample_t* dstl, int nch, float* lins) +{ + int i; + float* xr = xl + 576 * (nch - 1); + drmp3d_sample_t* dstr = dstl + (nch - 1); + + static const float g_win[] = { + -1, 26, -31, 208, 218, 401, -519, 2063, 2000, 4788, -5517, 7134, + 5959, 35640, -39336, 74992, -1, 24, -35, 202, 222, 347, -581, 2080, + 1952, 4425, -5879, 7640, 5288, 33791, -41176, 74856, -1, 21, -38, 196, + 225, 294, -645, 2087, 1893, 4063, -6237, 8092, 4561, 31947, -43006, 74630, + -1, 19, -41, 190, 227, 244, -711, 2085, 1822, 3705, -6589, 8492, + 3776, 30112, -44821, 74313, -1, 17, -45, 183, 228, 197, -779, 2075, + 1739, 3351, -6935, 8840, 2935, 28289, -46617, 73908, -1, 16, -49, 176, + 228, 153, -848, 2057, 1644, 3004, -7271, 9139, 2037, 26482, -48390, 73415, + -2, 14, -53, 169, 227, 111, -919, 2032, 1535, 2663, -7597, 9389, + 1082, 24694, -50137, 72835, -2, 13, -58, 161, 224, 72, -991, 2001, + 1414, 2330, -7910, 9592, 70, 22929, -51853, 72169, -2, 11, -63, 154, + 221, 36, -1064, 1962, 1280, 2006, -8209, 9750, -998, 21189, -53534, 71420, + -2, 10, -68, 147, 215, 2, -1137, 1919, 1131, 1692, -8491, 9863, + -2122, 19478, -55178, 70590, -3, 9, -73, 139, 208, -29, -1210, 1870, + 970, 1388, -8755, 9935, -3300, 17799, -56778, 69679, -3, 8, -79, 132, + 200, -57, -1283, 1817, 794, 1095, -8998, 9966, -4533, 16155, -58333, 68692, + -4, 7, -85, 125, 189, -83, -1356, 1759, 605, 814, -9219, 9959, + -5818, 14548, -59838, 67629, -4, 7, -91, 117, 177, -106, -1428, 1698, + 402, 545, -9416, 9916, -7154, 12980, -61289, 66494, -5, 6, -97, 111, + 163, -127, -1498, 1634, 185, 288, -9585, 9838, -8540, 11455, -62684, 65290 + }; + float* zlin = lins + 15 * 64; + const float* w = g_win; + + zlin[4 * 15] = xl[18 * 16]; + zlin[4 * 15 + 1] = xr[18 * 16]; + zlin[4 * 15 + 2] = xl[0]; + zlin[4 * 15 + 3] = xr[0]; + + zlin[4 * 31] = xl[1 + 18 * 16]; + zlin[4 * 31 + 1] = xr[1 + 18 * 16]; + zlin[4 * 31 + 2] = xl[1]; + zlin[4 * 31 + 3] = xr[1]; + + drmp3d_synth_pair(dstr, nch, lins + 4 * 15 + 1); + drmp3d_synth_pair(dstr + 32 * nch, nch, lins + 4 * 15 + 64 + 1); + drmp3d_synth_pair(dstl, nch, lins + 4 * 15); + drmp3d_synth_pair(dstl + 32 * nch, nch, lins + 4 * 15 + 64); + + #if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) + for (i = 14; i >= 0; i--) + { + #define DRMP3_VLOAD(k) \ + drmp3_f4 w0 = DRMP3_VSET(*w++); \ + drmp3_f4 w1 = DRMP3_VSET(*w++); \ + drmp3_f4 vz = DRMP3_VLD(&zlin[4 * i - 64 * k]); \ + drmp3_f4 vy = DRMP3_VLD(&zlin[4 * i - 64 * (15 - k)]); + #define DRMP3_V0(k) \ + { \ + DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)); \ + a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); \ + } + #define DRMP3_V1(k) \ + { \ + DRMP3_VLOAD(k) \ + b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); \ + a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); \ + } + #define DRMP3_V2(k) \ + { \ + DRMP3_VLOAD(k) \ + b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); \ + a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); \ + } + drmp3_f4 a, b; + zlin[4 * i] = xl[18 * (31 - i)]; + zlin[4 * i + 1] = xr[18 * (31 - i)]; + zlin[4 * i + 2] = xl[1 + 18 * (31 - i)]; + zlin[4 * i + 3] = xr[1 + 18 * (31 - i)]; + zlin[4 * i + 64] = xl[1 + 18 * (1 + i)]; + zlin[4 * i + 64 + 1] = xr[1 + 18 * (1 + i)]; + zlin[4 * i - 64 + 2] = xl[18 * (1 + i)]; + zlin[4 * i - 64 + 3] = xr[18 * (1 + i)]; + + DRMP3_V0(0) + DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7) + + { + #ifndef DR_MP3_FLOAT_OUTPUT + #if DRMP3_HAVE_SSE + static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; + __m128i pcm8 = + _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); + dstr[(15 - i) * nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); + dstr[(17 + i) * nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); + dstl[(15 - i) * nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); + dstl[(17 + i) * nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); + dstr[(47 - i) * nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); + dstr[(49 + i) * nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); + dstl[(47 - i) * nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); + dstl[(49 + i) * nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); + #else + int16x4_t pcma, pcmb; + a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); + b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), + vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), + vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + vst1_lane_s16(dstr + (15 - i) * nch, pcma, 1); + vst1_lane_s16(dstr + (17 + i) * nch, pcmb, 1); + vst1_lane_s16(dstl + (15 - i) * nch, pcma, 0); + vst1_lane_s16(dstl + (17 + i) * nch, pcmb, 0); + vst1_lane_s16(dstr + (47 - i) * nch, pcma, 3); + vst1_lane_s16(dstr + (49 + i) * nch, pcmb, 3); + vst1_lane_s16(dstl + (47 - i) * nch, pcma, 2); + vst1_lane_s16(dstl + (49 + i) * nch, pcmb, 2); + #endif + #else + #if DRMP3_HAVE_SSE + static const drmp3_f4 g_scale = { 1.0f / 32768.0f, 1.0f / 32768.0f, 1.0f / 32768.0f, + 1.0f / 32768.0f }; + #else + const drmp3_f4 g_scale = vdupq_n_f32(1.0f / 32768.0f); + #endif + a = DRMP3_VMUL(a, g_scale); + b = DRMP3_VMUL(b, g_scale); + #if DRMP3_HAVE_SSE + _mm_store_ss(dstr + (15 - i) * nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstr + (17 + i) * nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstl + (15 - i) * nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstl + (17 + i) * nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstr + (47 - i) * nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstr + (49 + i) * nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstl + (47 - i) * nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + _mm_store_ss(dstl + (49 + i) * nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); + #else + vst1q_lane_f32(dstr + (15 - i) * nch, a, 1); + vst1q_lane_f32(dstr + (17 + i) * nch, b, 1); + vst1q_lane_f32(dstl + (15 - i) * nch, a, 0); + vst1q_lane_f32(dstl + (17 + i) * nch, b, 0); + vst1q_lane_f32(dstr + (47 - i) * nch, a, 3); + vst1q_lane_f32(dstr + (49 + i) * nch, b, 3); + vst1q_lane_f32(dstl + (47 - i) * nch, a, 2); + vst1q_lane_f32(dstl + (49 + i) * nch, b, 2); + #endif + #endif /* DR_MP3_FLOAT_OUTPUT */ + } + } + else + #endif + #ifdef DR_MP3_ONLY_SIMD + {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */ + #else + for (i = 14; i >= 0; i--) + { + #define DRMP3_LOAD(k) \ + float w0 = *w++; \ + float w1 = *w++; \ + float* vz = &zlin[4 * i - k * 64]; \ + float* vy = &zlin[4 * i - (15 - k) * 64]; + #define DRMP3_S0(k) \ + { \ + int j; \ + DRMP3_LOAD(k); \ + for (j = 0; j < 4; j++) \ + b[j] = vz[j] * w1 + vy[j] * w0, a[j] = vz[j] * w0 - vy[j] * w1; \ + } + #define DRMP3_S1(k) \ + { \ + int j; \ + DRMP3_LOAD(k); \ + for (j = 0; j < 4; j++) \ + b[j] += vz[j] * w1 + vy[j] * w0, a[j] += vz[j] * w0 - vy[j] * w1; \ + } + #define DRMP3_S2(k) \ + { \ + int j; \ + DRMP3_LOAD(k); \ + for (j = 0; j < 4; j++) \ + b[j] += vz[j] * w1 + vy[j] * w0, a[j] += vy[j] * w1 - vz[j] * w0; \ + } + float a[4], b[4]; + + zlin[4 * i] = xl[18 * (31 - i)]; + zlin[4 * i + 1] = xr[18 * (31 - i)]; + zlin[4 * i + 2] = xl[1 + 18 * (31 - i)]; + zlin[4 * i + 3] = xr[1 + 18 * (31 - i)]; + zlin[4 * (i + 16)] = xl[1 + 18 * (1 + i)]; + zlin[4 * (i + 16) + 1] = xr[1 + 18 * (1 + i)]; + zlin[4 * (i - 16) + 2] = xl[18 * (1 + i)]; + zlin[4 * (i - 16) + 3] = xr[18 * (1 + i)]; + + DRMP3_S0(0) + DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7) + + dstr[(15 - i) * nch] = drmp3d_scale_pcm(a[1]); + dstr[(17 + i) * nch] = drmp3d_scale_pcm(b[1]); + dstl[(15 - i) * nch] = drmp3d_scale_pcm(a[0]); + dstl[(17 + i) * nch] = drmp3d_scale_pcm(b[0]); + dstr[(47 - i) * nch] = drmp3d_scale_pcm(a[3]); + dstr[(49 + i) * nch] = drmp3d_scale_pcm(b[3]); + dstl[(47 - i) * nch] = drmp3d_scale_pcm(a[2]); + dstl[(49 + i) * nch] = drmp3d_scale_pcm(b[2]); + } + #endif +} + +static void drmp3d_synth_granule(float* qmf_state, float* grbuf, int nbands, int nch, + drmp3d_sample_t* pcm, float* lins) +{ + int i; + for (i = 0; i < nch; i++) + { + drmp3d_DCT_II(grbuf + 576 * i, nbands); + } + + DRMP3_COPY_MEMORY(lins, qmf_state, sizeof(float) * 15 * 64); + + for (i = 0; i < nbands; i += 2) + { + drmp3d_synth(grbuf + i, pcm + 32 * nch * i, nch, lins + i * 64); + } + #ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL + if (nch == 1) + { + for (i = 0; i < 15 * 64; i += 2) + { + qmf_state[i] = lins[nbands * 64 + i]; + } + } + else + #endif + { + DRMP3_COPY_MEMORY(qmf_state, lins + nbands * 64, sizeof(float) * 15 * 64); + } +} + +static int drmp3d_match_frame(const drmp3_uint8* hdr, int mp3_bytes, int frame_bytes) +{ + int i, nmatch; + for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++) + { + i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i); + if (i + DRMP3_HDR_SIZE > mp3_bytes) + return nmatch > 0; + if (!drmp3_hdr_compare(hdr, hdr + i)) + return 0; + } + return 1; +} + +static int drmp3d_find_frame(const drmp3_uint8* mp3, int mp3_bytes, int* free_format_bytes, + int* ptr_frame_bytes) +{ + int i, k; + for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++) + { + if (drmp3_hdr_valid(mp3)) + { + int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes); + int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3); + + for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && + i + 2 * k < mp3_bytes - DRMP3_HDR_SIZE; + k++) + { + if (drmp3_hdr_compare(mp3, mp3 + k)) + { + int fb = k - drmp3_hdr_padding(mp3); + int nextfb = fb + drmp3_hdr_padding(mp3 + k); + if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || + !drmp3_hdr_compare(mp3, mp3 + k + nextfb)) + continue; + frame_and_padding = k; + frame_bytes = fb; + *free_format_bytes = fb; + } + } + + if ((frame_bytes && i + frame_and_padding <= mp3_bytes && + drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || + (!i && frame_and_padding == mp3_bytes)) + { + *ptr_frame_bytes = frame_and_padding; + return i; + } + *free_format_bytes = 0; + } + } + *ptr_frame_bytes = 0; + return mp3_bytes; +} + +DRMP3_API void drmp3dec_init(drmp3dec* dec) +{ + dec->header[0] = 0; +} + +DRMP3_API int drmp3dec_decode_frame(drmp3dec* dec, const drmp3_uint8* mp3, int mp3_bytes, void* pcm, + drmp3dec_frame_info* info) +{ + int i = 0, igr, frame_size = 0, success = 1; + const drmp3_uint8* hdr; + drmp3_bs bs_frame[1]; + drmp3dec_scratch scratch; + + if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3)) + { + frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3); + if (frame_size != mp3_bytes && + (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size))) + { + frame_size = 0; + } + } + if (!frame_size) + { + DRMP3_ZERO_MEMORY(dec, sizeof(drmp3dec)); + i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); + if (!frame_size || i + frame_size > mp3_bytes) + { + info->frame_bytes = i; + return 0; + } + } + + hdr = mp3 + i; + DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE); + info->frame_bytes = i + frame_size; + info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; + info->hz = drmp3_hdr_sample_rate_hz(hdr); + info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); + info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); + + drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE); + if (DRMP3_HDR_IS_CRC(hdr)) + { + drmp3_bs_get_bits(bs_frame, 16); + } + + if (info->layer == 3) + { + int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); + if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) + { + drmp3dec_init(dec); + return 0; + } + success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); + if (success && pcm != NULL) + { + for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); + igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t) * 576 * info->channels)) + { + DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576 * 2 * sizeof(float)); + drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr * info->channels, + info->channels); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, + (drmp3d_sample_t*)pcm, scratch.syn[0]); + } + } + drmp3_L3_save_reservoir(dec, &scratch); + } + else + { + #ifdef DR_MP3_ONLY_MP3 + return 0; + #else + drmp3_L12_scale_info sci[1]; + + if (pcm == NULL) + { + return drmp3_hdr_frame_samples(hdr); + } + + drmp3_L12_read_scale_info(hdr, bs_frame, sci); + + DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576 * 2 * sizeof(float)); + for (i = 0, igr = 0; igr < 3; igr++) + { + if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, + info->layer | 1))) + { + i = 0; + drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, + (drmp3d_sample_t*)pcm, scratch.syn[0]); + DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576 * 2 * sizeof(float)); + pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t) * 384 * info->channels); + } + if (bs_frame->pos > bs_frame->limit) + { + drmp3dec_init(dec); + return 0; + } + } + #endif + } + + return success * drmp3_hdr_frame_samples(dec->header); +} + +DRMP3_API void drmp3dec_f32_to_s16(const float* in, drmp3_int16* out, size_t num_samples) +{ + size_t i = 0; + #if DRMP3_HAVE_SIMD + size_t aligned_count = num_samples & ~7; + for (; i < aligned_count; i += 8) + { + drmp3_f4 scale = DRMP3_VSET(32768.0f); + drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i]), scale); + drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i + 4]), scale); + #if DRMP3_HAVE_SSE + drmp3_f4 s16max = DRMP3_VSET(32767.0f); + drmp3_f4 s16min = DRMP3_VSET(-32768.0f); + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); + out[i] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); + out[i + 1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); + out[i + 2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); + out[i + 3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); + out[i + 4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); + out[i + 5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); + out[i + 6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); + out[i + 7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); + #else + int16x4_t pcma, pcmb; + a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); + b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); + pcma = vqmovn_s32( + vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); + pcmb = vqmovn_s32( + vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + vst1_lane_s16(out + i, pcma, 0); + vst1_lane_s16(out + i + 1, pcma, 1); + vst1_lane_s16(out + i + 2, pcma, 2); + vst1_lane_s16(out + i + 3, pcma, 3); + vst1_lane_s16(out + i + 4, pcmb, 0); + vst1_lane_s16(out + i + 5, pcmb, 1); + vst1_lane_s16(out + i + 6, pcmb, 2); + vst1_lane_s16(out + i + 7, pcmb, 3); + #endif + } + #endif + for (; i < num_samples; i++) + { + float sample = in[i] * 32768.0f; + if (sample >= 32766.5) + out[i] = (drmp3_int16)32767; + else if (sample <= -32767.5) + out[i] = (drmp3_int16)-32768; + else + { + short s = (drmp3_int16)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + out[i] = s; + } + } +} + + /************************************************************************************************************************************************************ + + Main Public API + + ************************************************************************************************************************************************************/ + #if defined(SIZE_MAX) + #define DRMP3_SIZE_MAX SIZE_MAX + #else + #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) + #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF) + #else + #define DRMP3_SIZE_MAX 0xFFFFFFFF + #endif + #endif + + /* Options. */ + #ifndef DRMP3_SEEK_LEADING_MP3_FRAMES + #define DRMP3_SEEK_LEADING_MP3_FRAMES 2 + #endif + + #define DRMP3_MIN_DATA_CHUNK_SIZE 16384 + + /* The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends + * at least 16K, but in an attempt to reduce data movement I'm making this slightly larger. + */ + #ifndef DRMP3_DATA_CHUNK_SIZE + #define DRMP3_DATA_CHUNK_SIZE (DRMP3_MIN_DATA_CHUNK_SIZE * 4) + #endif + + #define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) + #define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi))) + + #ifndef DRMP3_PI_D + #define DRMP3_PI_D 3.14159265358979323846264 + #endif + + #define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2 + +static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a) +{ + return x * (1 - a) + y * a; +} +static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a) +{ + float r0 = (y - x); + float r1 = r0 * a; + return x + r1; + /*return x + (y - x)*a;*/ +} + +/* +Greatest common factor using Euclid's algorithm iteratively. +*/ +static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b) +{ + for (;;) + { + if (b == 0) + { + break; + } + else + { + drmp3_uint32 t = a; + a = b; + b = t % a; + } + } + + return a; +} + +static void* drmp3__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return DRMP3_MALLOC(sz); +} + +static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return DRMP3_REALLOC(p, sz); +} + +static void drmp3__free_default(void* p, void* pUserData) +{ + (void)pUserData; + DRMP3_FREE(p); +} + +static void* drmp3__malloc_from_callbacks(size_t sz, + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) + { + return NULL; + } + + if (pAllocationCallbacks->onMalloc != NULL) + { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } + + /* Try using realloc(). */ + if (pAllocationCallbacks->onRealloc != NULL) + { + return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); + } + + return NULL; +} + +static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) + { + return NULL; + } + + if (pAllocationCallbacks->onRealloc != NULL) + { + return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); + } + + /* Try emulating realloc() in terms of malloc()/free(). */ + if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) + { + void* p2; + + p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); + if (p2 == NULL) + { + return NULL; + } + + if (p != NULL) + { + DRMP3_COPY_MEMORY(p2, p, szOld); + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } + + return p2; + } + + return NULL; +} + +static void drmp3__free_from_callbacks(void* p, + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL || pAllocationCallbacks == NULL) + { + return; + } + + if (pAllocationCallbacks->onFree != NULL) + { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } +} + +static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults( + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) + { + /* Copy. */ + return *pAllocationCallbacks; + } + else + { + /* Defaults. */ + drmp3_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = drmp3__malloc_default; + allocationCallbacks.onRealloc = drmp3__realloc_default; + allocationCallbacks.onFree = drmp3__free_default; + return allocationCallbacks; + } +} + +static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead) +{ + size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); + pMP3->streamCursor += bytesRead; + return bytesRead; +} + +static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin) +{ + DRMP3_ASSERT(offset >= 0); + + if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) + { + return DRMP3_FALSE; + } + + if (origin == drmp3_seek_origin_start) + { + pMP3->streamCursor = (drmp3_uint64)offset; + } + else + { + pMP3->streamCursor += offset; + } + + return DRMP3_TRUE; +} + +static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin) +{ + if (offset <= 0x7FFFFFFF) + { + return drmp3__on_seek(pMP3, (int)offset, origin); + } + + /* Getting here "offset" is too large for a 32-bit integer. We just keep seeking forward until + * we hit the offset. */ + if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) + { + return DRMP3_FALSE; + } + + offset -= 0x7FFFFFFF; + while (offset > 0) + { + if (offset <= 0x7FFFFFFF) + { + if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) + { + return DRMP3_FALSE; + } + offset = 0; + } + else + { + if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) + { + return DRMP3_FALSE; + } + offset -= 0x7FFFFFFF; + } + } + + return DRMP3_TRUE; +} + +static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +{ + drmp3_uint32 pcmFramesRead = 0; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onRead != NULL); + + if (pMP3->atEnd) + { + return 0; + } + + for (;;) + { + drmp3dec_frame_info info; + + /* minimp3 recommends doing data submission in chunks of at least 16K. If we don't have at + * least 16K bytes available, get more. */ + if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) + { + size_t bytesRead; + + /* First we need to move the data down. */ + if (pMP3->pData != NULL) + { + DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + } + + pMP3->dataConsumed = 0; + + if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) + { + drmp3_uint8* pNewData; + size_t newDataCap; + + newDataCap = DRMP3_DATA_CHUNK_SIZE; + + pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks( + pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + if (pNewData == NULL) + { + return 0; /* Out of memory. */ + } + + pMP3->pData = pNewData; + pMP3->dataCapacity = newDataCap; + } + + bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, + (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) + { + if (pMP3->dataSize == 0) + { + pMP3->atEnd = DRMP3_TRUE; + return 0; /* No data. */ + } + } + + pMP3->dataSize += bytesRead; + } + + if (pMP3->dataSize > INT_MAX) + { + pMP3->atEnd = DRMP3_TRUE; + return 0; /* File too big. */ + } + + DRMP3_ASSERT(pMP3->pData != NULL); + DRMP3_ASSERT(pMP3->dataCapacity > 0); + + pcmFramesRead = drmp3dec_decode_frame( + &pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, + &info); /* <-- Safe size_t -> int conversion thanks to the check above. */ + + /* Consume the data. */ + if (info.frame_bytes > 0) + { + pMP3->dataConsumed += (size_t)info.frame_bytes; + pMP3->dataSize -= (size_t)info.frame_bytes; + } + + /* pcmFramesRead will be equal to 0 if decoding failed. If it is zero and info.frame_bytes > + * 0 then we have successfully decoded the frame. */ + if (pcmFramesRead > 0) + { + pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; + pMP3->mp3FrameChannels = info.channels; + pMP3->mp3FrameSampleRate = info.hz; + break; + } + else if (info.frame_bytes == 0) + { + /* Need more data. minimp3 recommends doing data submission in 16K chunks. */ + size_t bytesRead; + + /* First we need to move the data down. */ + DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + pMP3->dataConsumed = 0; + + if (pMP3->dataCapacity == pMP3->dataSize) + { + /* No room. Expand. */ + drmp3_uint8* pNewData; + size_t newDataCap; + + newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE; + + pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks( + pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + if (pNewData == NULL) + { + return 0; /* Out of memory. */ + } + + pMP3->pData = pNewData; + pMP3->dataCapacity = newDataCap; + } + + /* Fill in a chunk. */ + bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, + (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) + { + pMP3->atEnd = DRMP3_TRUE; + return 0; /* Error reading more data. */ + } + + pMP3->dataSize += bytesRead; + } + }; + + return pcmFramesRead; +} + +static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +{ + drmp3_uint32 pcmFramesRead = 0; + drmp3dec_frame_info info; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->memory.pData != NULL); + + if (pMP3->atEnd) + { + return 0; + } + + for (;;) + { + pcmFramesRead = drmp3dec_decode_frame( + &pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, + (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); + if (pcmFramesRead > 0) + { + pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; + pMP3->mp3FrameChannels = info.channels; + pMP3->mp3FrameSampleRate = info.hz; + break; + } + else if (info.frame_bytes > 0) + { + /* No frames were read, but it looks like we skipped past one. Read the next MP3 frame. + */ + pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + } + else + { + /* Nothing at all was read. Abort. */ + break; + } + } + + /* Consume the data. */ + pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + + return pcmFramesRead; +} + +static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +{ + if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) + { + return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); + } + else + { + return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); + } +} + +static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3) +{ + DRMP3_ASSERT(pMP3 != NULL); + return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames); +} + + #if 0 +static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) +{ + drmp3_uint32 pcmFrameCount; + + DRMP3_ASSERT(pMP3 != NULL); + + pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFrameCount == 0) { + return 0; + } + + /* We have essentially just skipped past the frame, so just set the remaining samples to 0. */ + pMP3->currentPCMFrame += pcmFrameCount; + pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount; + pMP3->pcmFramesRemainingInMP3Frame = 0; + + return pcmFrameCount; +} + #endif + +static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, + void* pUserData, + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(onRead != NULL); + + /* This function assumes the output object has already been reset to 0. Do not do that here, + * otherwise things will break. */ + drmp3dec_init(&pMP3->decoder); + + pMP3->onRead = onRead; + pMP3->onSeek = onSeek; + pMP3->pUserData = pUserData; + pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + + if (pMP3->allocationCallbacks.onFree == NULL || + (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) + { + return DRMP3_FALSE; /* Invalid allocation callbacks. */ + } + + /* Decode the first frame to confirm that it is indeed a valid MP3 stream. */ + if (drmp3_decode_next_frame(pMP3) == 0) + { + drmp3__free_from_callbacks( + pMP3->pData, + &pMP3->allocationCallbacks); /* The call above may have allocated memory. Need to make + sure it's freed before aborting. */ + return DRMP3_FALSE; /* Not a valid MP3 stream. */ + } + + pMP3->channels = pMP3->mp3FrameChannels; + pMP3->sampleRate = pMP3->mp3FrameSampleRate; + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, + void* pUserData, + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pMP3 == NULL || onRead == NULL) + { + return DRMP3_FALSE; + } + + DRMP3_ZERO_OBJECT(pMP3); + return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); +} + +static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + drmp3* pMP3 = (drmp3*)pUserData; + size_t bytesRemaining; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); + + bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; + if (bytesToRead > bytesRemaining) + { + bytesToRead = bytesRemaining; + } + + if (bytesToRead > 0) + { + DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, + bytesToRead); + pMP3->memory.currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin) +{ + drmp3* pMP3 = (drmp3*)pUserData; + + DRMP3_ASSERT(pMP3 != NULL); + + if (origin == drmp3_seek_origin_current) + { + if (byteOffset > 0) + { + if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) + { + byteOffset = + (int)(pMP3->memory.dataSize - + pMP3->memory.currentReadPos); /* Trying to seek too far forward. */ + } + } + else + { + if (pMP3->memory.currentReadPos < (size_t)-byteOffset) + { + byteOffset = + -(int)pMP3->memory.currentReadPos; /* Trying to seek too far backwards. */ + } + } + + /* This will never underflow thanks to the clamps above. */ + pMP3->memory.currentReadPos += byteOffset; + } + else + { + if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) + { + pMP3->memory.currentReadPos = byteOffset; + } + else + { + pMP3->memory.currentReadPos = + pMP3->memory.dataSize; /* Trying to seek too far forward. */ + } + } + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pMP3 == NULL) + { + return DRMP3_FALSE; + } + + DRMP3_ZERO_OBJECT(pMP3); + + if (pData == NULL || dataSize == 0) + { + return DRMP3_FALSE; + } + + pMP3->memory.pData = (const drmp3_uint8*)pData; + pMP3->memory.dataSize = dataSize; + pMP3->memory.currentReadPos = 0; + + return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, + pAllocationCallbacks); +} + + #ifndef DR_MP3_NO_STDIO + #include + #include /* For wcslen(), wcsrtombs() */ + + /* drmp3_result_from_errno() is only used inside DR_MP3_NO_STDIO for now. Move this out + * if it's ever used elsewhere. */ + #include +static drmp3_result drmp3_result_from_errno(int e) +{ + switch (e) + { + case 0: + return DRMP3_SUCCESS; + #ifdef EPERM + case EPERM: + return DRMP3_INVALID_OPERATION; + #endif + #ifdef ENOENT + case ENOENT: + return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef ESRCH + case ESRCH: + return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef EINTR + case EINTR: + return DRMP3_INTERRUPT; + #endif + #ifdef EIO + case EIO: + return DRMP3_IO_ERROR; + #endif + #ifdef ENXIO + case ENXIO: + return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef E2BIG + case E2BIG: + return DRMP3_INVALID_ARGS; + #endif + #ifdef ENOEXEC + case ENOEXEC: + return DRMP3_INVALID_FILE; + #endif + #ifdef EBADF + case EBADF: + return DRMP3_INVALID_FILE; + #endif + #ifdef ECHILD + case ECHILD: + return DRMP3_ERROR; + #endif + #ifdef EAGAIN + case EAGAIN: + return DRMP3_UNAVAILABLE; + #endif + #ifdef ENOMEM + case ENOMEM: + return DRMP3_OUT_OF_MEMORY; + #endif + #ifdef EACCES + case EACCES: + return DRMP3_ACCESS_DENIED; + #endif + #ifdef EFAULT + case EFAULT: + return DRMP3_BAD_ADDRESS; + #endif + #ifdef ENOTBLK + case ENOTBLK: + return DRMP3_ERROR; + #endif + #ifdef EBUSY + case EBUSY: + return DRMP3_BUSY; + #endif + #ifdef EEXIST + case EEXIST: + return DRMP3_ALREADY_EXISTS; + #endif + #ifdef EXDEV + case EXDEV: + return DRMP3_ERROR; + #endif + #ifdef ENODEV + case ENODEV: + return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef ENOTDIR + case ENOTDIR: + return DRMP3_NOT_DIRECTORY; + #endif + #ifdef EISDIR + case EISDIR: + return DRMP3_IS_DIRECTORY; + #endif + #ifdef EINVAL + case EINVAL: + return DRMP3_INVALID_ARGS; + #endif + #ifdef ENFILE + case ENFILE: + return DRMP3_TOO_MANY_OPEN_FILES; + #endif + #ifdef EMFILE + case EMFILE: + return DRMP3_TOO_MANY_OPEN_FILES; + #endif + #ifdef ENOTTY + case ENOTTY: + return DRMP3_INVALID_OPERATION; + #endif + #ifdef ETXTBSY + case ETXTBSY: + return DRMP3_BUSY; + #endif + #ifdef EFBIG + case EFBIG: + return DRMP3_TOO_BIG; + #endif + #ifdef ENOSPC + case ENOSPC: + return DRMP3_NO_SPACE; + #endif + #ifdef ESPIPE + case ESPIPE: + return DRMP3_BAD_SEEK; + #endif + #ifdef EROFS + case EROFS: + return DRMP3_ACCESS_DENIED; + #endif + #ifdef EMLINK + case EMLINK: + return DRMP3_TOO_MANY_LINKS; + #endif + #ifdef EPIPE + case EPIPE: + return DRMP3_BAD_PIPE; + #endif + #ifdef EDOM + case EDOM: + return DRMP3_OUT_OF_RANGE; + #endif + #ifdef ERANGE + case ERANGE: + return DRMP3_OUT_OF_RANGE; + #endif + #ifdef EDEADLK + case EDEADLK: + return DRMP3_DEADLOCK; + #endif + #ifdef ENAMETOOLONG + case ENAMETOOLONG: + return DRMP3_PATH_TOO_LONG; + #endif + #ifdef ENOLCK + case ENOLCK: + return DRMP3_ERROR; + #endif + #ifdef ENOSYS + case ENOSYS: + return DRMP3_NOT_IMPLEMENTED; + #endif + #ifdef ENOTEMPTY + case ENOTEMPTY: + return DRMP3_DIRECTORY_NOT_EMPTY; + #endif + #ifdef ELOOP + case ELOOP: + return DRMP3_TOO_MANY_LINKS; + #endif + #ifdef ENOMSG + case ENOMSG: + return DRMP3_NO_MESSAGE; + #endif + #ifdef EIDRM + case EIDRM: + return DRMP3_ERROR; + #endif + #ifdef ECHRNG + case ECHRNG: + return DRMP3_ERROR; + #endif + #ifdef EL2NSYNC + case EL2NSYNC: + return DRMP3_ERROR; + #endif + #ifdef EL3HLT + case EL3HLT: + return DRMP3_ERROR; + #endif + #ifdef EL3RST + case EL3RST: + return DRMP3_ERROR; + #endif + #ifdef ELNRNG + case ELNRNG: + return DRMP3_OUT_OF_RANGE; + #endif + #ifdef EUNATCH + case EUNATCH: + return DRMP3_ERROR; + #endif + #ifdef ENOCSI + case ENOCSI: + return DRMP3_ERROR; + #endif + #ifdef EL2HLT + case EL2HLT: + return DRMP3_ERROR; + #endif + #ifdef EBADE + case EBADE: + return DRMP3_ERROR; + #endif + #ifdef EBADR + case EBADR: + return DRMP3_ERROR; + #endif + #ifdef EXFULL + case EXFULL: + return DRMP3_ERROR; + #endif + #ifdef ENOANO + case ENOANO: + return DRMP3_ERROR; + #endif + #ifdef EBADRQC + case EBADRQC: + return DRMP3_ERROR; + #endif + #ifdef EBADSLT + case EBADSLT: + return DRMP3_ERROR; + #endif + #ifdef EBFONT + case EBFONT: + return DRMP3_INVALID_FILE; + #endif + #ifdef ENOSTR + case ENOSTR: + return DRMP3_ERROR; + #endif + #ifdef ENODATA + case ENODATA: + return DRMP3_NO_DATA_AVAILABLE; + #endif + #ifdef ETIME + case ETIME: + return DRMP3_TIMEOUT; + #endif + #ifdef ENOSR + case ENOSR: + return DRMP3_NO_DATA_AVAILABLE; + #endif + #ifdef ENONET + case ENONET: + return DRMP3_NO_NETWORK; + #endif + #ifdef ENOPKG + case ENOPKG: + return DRMP3_ERROR; + #endif + #ifdef EREMOTE + case EREMOTE: + return DRMP3_ERROR; + #endif + #ifdef ENOLINK + case ENOLINK: + return DRMP3_ERROR; + #endif + #ifdef EADV + case EADV: + return DRMP3_ERROR; + #endif + #ifdef ESRMNT + case ESRMNT: + return DRMP3_ERROR; + #endif + #ifdef ECOMM + case ECOMM: + return DRMP3_ERROR; + #endif + #ifdef EPROTO + case EPROTO: + return DRMP3_ERROR; + #endif + #ifdef EMULTIHOP + case EMULTIHOP: + return DRMP3_ERROR; + #endif + #ifdef EDOTDOT + case EDOTDOT: + return DRMP3_ERROR; + #endif + #ifdef EBADMSG + case EBADMSG: + return DRMP3_BAD_MESSAGE; + #endif + #ifdef EOVERFLOW + case EOVERFLOW: + return DRMP3_TOO_BIG; + #endif + #ifdef ENOTUNIQ + case ENOTUNIQ: + return DRMP3_NOT_UNIQUE; + #endif + #ifdef EBADFD + case EBADFD: + return DRMP3_ERROR; + #endif + #ifdef EREMCHG + case EREMCHG: + return DRMP3_ERROR; + #endif + #ifdef ELIBACC + case ELIBACC: + return DRMP3_ACCESS_DENIED; + #endif + #ifdef ELIBBAD + case ELIBBAD: + return DRMP3_INVALID_FILE; + #endif + #ifdef ELIBSCN + case ELIBSCN: + return DRMP3_INVALID_FILE; + #endif + #ifdef ELIBMAX + case ELIBMAX: + return DRMP3_ERROR; + #endif + #ifdef ELIBEXEC + case ELIBEXEC: + return DRMP3_ERROR; + #endif + #ifdef EILSEQ + case EILSEQ: + return DRMP3_INVALID_DATA; + #endif + #ifdef ERESTART + case ERESTART: + return DRMP3_ERROR; + #endif + #ifdef ESTRPIPE + case ESTRPIPE: + return DRMP3_ERROR; + #endif + #ifdef EUSERS + case EUSERS: + return DRMP3_ERROR; + #endif + #ifdef ENOTSOCK + case ENOTSOCK: + return DRMP3_NOT_SOCKET; + #endif + #ifdef EDESTADDRREQ + case EDESTADDRREQ: + return DRMP3_NO_ADDRESS; + #endif + #ifdef EMSGSIZE + case EMSGSIZE: + return DRMP3_TOO_BIG; + #endif + #ifdef EPROTOTYPE + case EPROTOTYPE: + return DRMP3_BAD_PROTOCOL; + #endif + #ifdef ENOPROTOOPT + case ENOPROTOOPT: + return DRMP3_PROTOCOL_UNAVAILABLE; + #endif + #ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: + return DRMP3_PROTOCOL_NOT_SUPPORTED; + #endif + #ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: + return DRMP3_SOCKET_NOT_SUPPORTED; + #endif + #ifdef EOPNOTSUPP + case EOPNOTSUPP: + return DRMP3_INVALID_OPERATION; + #endif + #ifdef EPFNOSUPPORT + case EPFNOSUPPORT: + return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EAFNOSUPPORT + case EAFNOSUPPORT: + return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EADDRINUSE + case EADDRINUSE: + return DRMP3_ALREADY_IN_USE; + #endif + #ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: + return DRMP3_ERROR; + #endif + #ifdef ENETDOWN + case ENETDOWN: + return DRMP3_NO_NETWORK; + #endif + #ifdef ENETUNREACH + case ENETUNREACH: + return DRMP3_NO_NETWORK; + #endif + #ifdef ENETRESET + case ENETRESET: + return DRMP3_NO_NETWORK; + #endif + #ifdef ECONNABORTED + case ECONNABORTED: + return DRMP3_NO_NETWORK; + #endif + #ifdef ECONNRESET + case ECONNRESET: + return DRMP3_CONNECTION_RESET; + #endif + #ifdef ENOBUFS + case ENOBUFS: + return DRMP3_NO_SPACE; + #endif + #ifdef EISCONN + case EISCONN: + return DRMP3_ALREADY_CONNECTED; + #endif + #ifdef ENOTCONN + case ENOTCONN: + return DRMP3_NOT_CONNECTED; + #endif + #ifdef ESHUTDOWN + case ESHUTDOWN: + return DRMP3_ERROR; + #endif + #ifdef ETOOMANYREFS + case ETOOMANYREFS: + return DRMP3_ERROR; + #endif + #ifdef ETIMEDOUT + case ETIMEDOUT: + return DRMP3_TIMEOUT; + #endif + #ifdef ECONNREFUSED + case ECONNREFUSED: + return DRMP3_CONNECTION_REFUSED; + #endif + #ifdef EHOSTDOWN + case EHOSTDOWN: + return DRMP3_NO_HOST; + #endif + #ifdef EHOSTUNREACH + case EHOSTUNREACH: + return DRMP3_NO_HOST; + #endif + #ifdef EALREADY + case EALREADY: + return DRMP3_IN_PROGRESS; + #endif + #ifdef EINPROGRESS + case EINPROGRESS: + return DRMP3_IN_PROGRESS; + #endif + #ifdef ESTALE + case ESTALE: + return DRMP3_INVALID_FILE; + #endif + #ifdef EUCLEAN + case EUCLEAN: + return DRMP3_ERROR; + #endif + #ifdef ENOTNAM + case ENOTNAM: + return DRMP3_ERROR; + #endif + #ifdef ENAVAIL + case ENAVAIL: + return DRMP3_ERROR; + #endif + #ifdef EISNAM + case EISNAM: + return DRMP3_ERROR; + #endif + #ifdef EREMOTEIO + case EREMOTEIO: + return DRMP3_IO_ERROR; + #endif + #ifdef EDQUOT + case EDQUOT: + return DRMP3_NO_SPACE; + #endif + #ifdef ENOMEDIUM + case ENOMEDIUM: + return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef EMEDIUMTYPE + case EMEDIUMTYPE: + return DRMP3_ERROR; + #endif + #ifdef ECANCELED + case ECANCELED: + return DRMP3_CANCELLED; + #endif + #ifdef ENOKEY + case ENOKEY: + return DRMP3_ERROR; + #endif + #ifdef EKEYEXPIRED + case EKEYEXPIRED: + return DRMP3_ERROR; + #endif + #ifdef EKEYREVOKED + case EKEYREVOKED: + return DRMP3_ERROR; + #endif + #ifdef EKEYREJECTED + case EKEYREJECTED: + return DRMP3_ERROR; + #endif + #ifdef EOWNERDEAD + case EOWNERDEAD: + return DRMP3_ERROR; + #endif + #ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: + return DRMP3_ERROR; + #endif + #ifdef ERFKILL + case ERFKILL: + return DRMP3_ERROR; + #endif + #ifdef EHWPOISON + case EHWPOISON: + return DRMP3_ERROR; + #endif + default: + return DRMP3_ERROR; + } +} + +static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err; + #endif + + if (ppFile != NULL) + { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) + { + return DRMP3_INVALID_ARGS; + } + + #if defined(_MSC_VER) && _MSC_VER >= 1400 + err = fopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) + { + return drmp3_result_from_errno(err); + } + #else + #if defined(_WIN32) || defined(__APPLE__) + *ppFile = fopen(pFilePath, pOpenMode); + #else + #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && \ + defined(_LARGEFILE64_SOURCE) + *ppFile = fopen64(pFilePath, pOpenMode); + #else + *ppFile = fopen(pFilePath, pOpenMode); + #endif + #endif + if (*ppFile == NULL) + { + drmp3_result result = drmp3_result_from_errno(errno); + if (result == DRMP3_SUCCESS) + { + result = DRMP3_ERROR; /* Just a safety check to make sure we never ever return success + when pFile == NULL. */ + } + + return result; + } + #endif + + return DRMP3_SUCCESS; +} + + /* + _wfopen() isn't always available in all compilation environments. + + * Windows only. + * MSVC seems to support it universally as far back as VC6 from what I can tell + (haven't checked further back). + * MinGW-64 (both 32- and 64-bit) seems to support it. + * MinGW wraps it in !defined(__STRICT_ANSI__). + * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). + + This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() + and _wfopen() as opposed to the wcsrtombs() fallback, so if you notice your compiler not + detecting this properly I'm happy to look at adding support. + */ + #if defined(_WIN32) + #if defined(_MSC_VER) || defined(__MINGW64__) || \ + (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #define DRMP3_HAS_WFOPEN + #endif + #endif + +static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (ppFile != NULL) + { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) + { + return DRMP3_INVALID_ARGS; + } + + #if defined(DRMP3_HAS_WFOPEN) + { + /* Use _wfopen() on Windows. */ + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) + { + return drmp3_result_from_errno(err); + } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) + { + return drmp3_result_from_errno(errno); + } + #endif + (void)pAllocationCallbacks; + } + #else + /* + Use fopen() on anything other than Windows. Requires a conversion. This is annoying + because fopen() is locale specific. The only real way I can think of to do this is + with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses + a static global mbstate_t object for maintaining state. I've checked this with + -std=c89 and it works, but if somebody get's a compiler error I'll look into + improving compatibility. + */ + + /* + Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this + case we just need to abort with an error. If you encounter a compiler lacking such + support, add it to this list and submit a bug report and it'll be added to the + library upstream. + */ + #if defined(__DJGPP__) + { + /* Nothing to do here. This will fall through to the error check below. */ + } + #else + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + char pOpenModeMB[32] = { 0 }; + + /* Get the length first. */ + DRMP3_ZERO_OBJECT(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) + { + return drmp3_result_from_errno(errno); + } + + pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) + { + return DRMP3_OUT_OF_MEMORY; + } + + pFilePathTemp = pFilePath; + DRMP3_ZERO_OBJECT(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); + + /* The open mode should always consist of ASCII characters so we should be able to do a + * trivial conversion. */ + { + size_t i = 0; + for (;;) + { + if (pOpenMode[i] == 0) + { + pOpenModeMB[i] = '\0'; + break; + } + + pOpenModeMB[i] = (char)pOpenMode[i]; + i += 1; + } + } + + *ppFile = fopen(pFilePathMB, pOpenModeMB); + + drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); + } + #endif + + if (*ppFile == NULL) + { + return DRMP3_ERROR; + } + #endif + + return DRMP3_SUCCESS; +} + +static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); +} + +static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin) +{ + return fseek((FILE*)pUserData, offset, + (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} + +DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3_bool32 result; + FILE* pFile; + + if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) + { + return DRMP3_FALSE; + } + + result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, + pAllocationCallbacks); + if (result != DRMP3_TRUE) + { + fclose(pFile); + return result; + } + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3_bool32 result; + FILE* pFile; + + if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) + { + return DRMP3_FALSE; + } + + result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, + pAllocationCallbacks); + if (result != DRMP3_TRUE) + { + fclose(pFile); + return result; + } + + return DRMP3_TRUE; +} + #endif + +DRMP3_API void drmp3_uninit(drmp3* pMP3) +{ + if (pMP3 == NULL) + { + return; + } + + #ifndef DR_MP3_NO_STDIO + if (pMP3->onRead == drmp3__on_read_stdio) + { + FILE* pFile = (FILE*)pMP3->pUserData; + if (pFile != NULL) + { + fclose(pFile); + pMP3->pUserData = NULL; /* Make sure the file handle is cleared to NULL to we don't + attempt to close it a second time. */ + } + } + #endif + + drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); +} + + #if defined(DR_MP3_FLOAT_OUTPUT) +static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount) +{ + drmp3_uint64 i; + drmp3_uint64 i4; + drmp3_uint64 sampleCount4; + + /* Unrolled. */ + i = 0; + sampleCount4 = sampleCount >> 2; + for (i4 = 0; i4 < sampleCount4; i4 += 1) + { + float x0 = src[i + 0]; + float x1 = src[i + 1]; + float x2 = src[i + 2]; + float x3 = src[i + 3]; + + x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); + x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); + x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); + x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); + + x0 = x0 * 32767.0f; + x1 = x1 * 32767.0f; + x2 = x2 * 32767.0f; + x3 = x3 * 32767.0f; + + dst[i + 0] = (drmp3_int16)x0; + dst[i + 1] = (drmp3_int16)x1; + dst[i + 2] = (drmp3_int16)x2; + dst[i + 3] = (drmp3_int16)x3; + + i += 4; + } + + /* Leftover. */ + for (; i < sampleCount; i += 1) + { + float x = src[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + x = x * 32767.0f; /* -1..1 to -32767..32767 */ + + dst[i] = (drmp3_int16)x; + } +} + #endif + + #if !defined(DR_MP3_FLOAT_OUTPUT) +static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount) +{ + drmp3_uint64 i; + for (i = 0; i < sampleCount; i += 1) + { + float x = (float)src[i]; + x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */ + dst[i] = x; + } +} + #endif + +static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, + void* pBufferOut) +{ + drmp3_uint64 totalFramesRead = 0; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onRead != NULL); + + while (framesToRead > 0) + { + drmp3_uint32 framesToConsume = + (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); + if (pBufferOut != NULL) + { + #if defined(DR_MP3_FLOAT_OUTPUT) + /* f32 */ + float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR( + pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); + float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR( + &pMP3->pcmFrames[0], + sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, + sizeof(float) * framesToConsume * pMP3->channels); + #else + /* s16 */ + drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR( + pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels); + drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR( + &pMP3->pcmFrames[0], + sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, + sizeof(drmp3_int16) * framesToConsume * pMP3->channels); + #endif + } + + pMP3->currentPCMFrame += framesToConsume; + pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; + pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume; + totalFramesRead += framesToConsume; + framesToRead -= framesToConsume; + + if (framesToRead == 0) + { + break; + } + + DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); + + /* + At this point we have exhausted our in-memory buffer so we need to re-fill. Note that the + sample rate may have changed at this point which means we'll also need to update our sample + rate conversion pipeline. + */ + if (drmp3_decode_next_frame(pMP3) == 0) + { + break; + } + } + + return totalFramesRead; +} + +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, + float* pBufferOut) +{ + if (pMP3 == NULL || pMP3->onRead == NULL) + { + return 0; + } + + #if defined(DR_MP3_FLOAT_OUTPUT) + /* Fast path. No conversion required. */ + return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); + #else + /* Slow path. Convert from s16 to f32. */ + { + drmp3_int16 pTempS16[8192]; + drmp3_uint64 totalPCMFramesRead = 0; + + while (totalPCMFramesRead < framesToRead) + { + drmp3_uint64 framesJustRead; + drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels; + if (framesToReadNow > framesRemaining) + { + framesToReadNow = framesRemaining; + } + + framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); + if (framesJustRead == 0) + { + break; + } + + drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR( + pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), + pTempS16, framesJustRead * pMP3->channels); + totalPCMFramesRead += framesJustRead; + } + + return totalPCMFramesRead; + } + #endif +} + +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, + drmp3_int16* pBufferOut) +{ + if (pMP3 == NULL || pMP3->onRead == NULL) + { + return 0; + } + + #if !defined(DR_MP3_FLOAT_OUTPUT) + /* Fast path. No conversion required. */ + return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); + #else + /* Slow path. Convert from f32 to s16. */ + { + float pTempF32[4096]; + drmp3_uint64 totalPCMFramesRead = 0; + + while (totalPCMFramesRead < framesToRead) + { + drmp3_uint64 framesJustRead; + drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels; + if (framesToReadNow > framesRemaining) + { + framesToReadNow = framesRemaining; + } + + framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); + if (framesJustRead == 0) + { + break; + } + + drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * + totalPCMFramesRead * + pMP3->channels), + pTempF32, framesJustRead * pMP3->channels); + totalPCMFramesRead += framesJustRead; + } + + return totalPCMFramesRead; + } + #endif +} + +static void drmp3_reset(drmp3* pMP3) +{ + DRMP3_ASSERT(pMP3 != NULL); + + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = 0; + pMP3->currentPCMFrame = 0; + pMP3->dataSize = 0; + pMP3->atEnd = DRMP3_FALSE; + drmp3dec_init(&pMP3->decoder); +} + +static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) +{ + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onSeek != NULL); + + /* Seek to the start of the stream to begin with. */ + if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) + { + return DRMP3_FALSE; + } + + /* Clear any cached data. */ + drmp3_reset(pMP3); + return DRMP3_TRUE; +} + +static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, + drmp3_uint64 frameOffset) +{ + drmp3_uint64 framesRead; + + /* + Just using a dumb read-and-discard for now. What would be nice is to parse only the header + of the MP3 frame, and then skip over leading frames without spending the time doing a full + decode. I cannot see an easy way to do this in minimp3, however, so it may involve some kind + of manual processing. + */ + #if defined(DR_MP3_FLOAT_OUTPUT) + framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); + #else + framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); + #endif + if (framesRead != frameOffset) + { + return DRMP3_FALSE; + } + + return DRMP3_TRUE; +} + +static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex) +{ + DRMP3_ASSERT(pMP3 != NULL); + + if (frameIndex == pMP3->currentPCMFrame) + { + return DRMP3_TRUE; + } + + /* + If we're moving foward we just read from where we're at. Otherwise we need to move back to the + start of the stream and read from the beginning. + */ + if (frameIndex < pMP3->currentPCMFrame) + { + /* Moving backward. Move to the start of the stream and then move forward. */ + if (!drmp3_seek_to_start_of_stream(pMP3)) + { + return DRMP3_FALSE; + } + } + + DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); + return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, + (frameIndex - pMP3->currentPCMFrame)); +} + +static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, + drmp3_uint32* pSeekPointIndex) +{ + drmp3_uint32 iSeekPoint; + + DRMP3_ASSERT(pSeekPointIndex != NULL); + + *pSeekPointIndex = 0; + + if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) + { + return DRMP3_FALSE; + } + + /* Linear search for simplicity to begin with while I'm getting this thing working. Once it's + * all working change this to a binary search. */ + for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) + { + if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) + { + break; /* Found it. */ + } + + *pSeekPointIndex = iSeekPoint; + } + + return DRMP3_TRUE; +} + +static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex) +{ + drmp3_seek_point seekPoint; + drmp3_uint32 priorSeekPointIndex; + drmp3_uint16 iMP3Frame; + drmp3_uint64 leftoverFrames; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->pSeekPoints != NULL); + DRMP3_ASSERT(pMP3->seekPointCount > 0); + + /* If there is no prior seekpoint it means the target PCM frame comes before the first seek + * point. Just assume a seekpoint at the start of the file in this case. */ + if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) + { + seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; + } + else + { + seekPoint.seekPosInBytes = 0; + seekPoint.pcmFrameIndex = 0; + seekPoint.mp3FramesToDiscard = 0; + seekPoint.pcmFramesToDiscard = 0; + } + + /* First thing to do is seek to the first byte of the relevant MP3 frame. */ + if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) + { + return DRMP3_FALSE; /* Failed to seek. */ + } + + /* Clear any cached data. */ + drmp3_reset(pMP3); + + /* Whole MP3 frames need to be discarded first. */ + for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) + { + drmp3_uint32 pcmFramesRead; + drmp3d_sample_t* pPCMFrames; + + /* Pass in non-null for the last frame because we want to ensure the sample rate converter + * is preloaded correctly. */ + pPCMFrames = NULL; + if (iMP3Frame == seekPoint.mp3FramesToDiscard - 1) + { + pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames; + } + + /* We first need to decode the next frame. */ + pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames); + if (pcmFramesRead == 0) + { + return DRMP3_FALSE; + } + } + + /* We seeked to an MP3 frame in the raw stream so we need to make sure the current PCM frame is + * set correctly. */ + pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; + + /* + Now at this point we can follow the same process as the brute force technique where we just skip + over unnecessary MP3 frames and then read-and-discard at least 2 whole MP3 frames. + */ + leftoverFrames = frameIndex - pMP3->currentPCMFrame; + return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); +} + +DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex) +{ + if (pMP3 == NULL || pMP3->onSeek == NULL) + { + return DRMP3_FALSE; + } + + if (frameIndex == 0) + { + return drmp3_seek_to_start_of_stream(pMP3); + } + + /* Use the seek table if we have one. */ + if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) + { + return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); + } + else + { + return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); + } +} + +DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, + drmp3_uint64* pPCMFrameCount) +{ + drmp3_uint64 currentPCMFrame; + drmp3_uint64 totalPCMFrameCount; + drmp3_uint64 totalMP3FrameCount; + + if (pMP3 == NULL) + { + return DRMP3_FALSE; + } + + /* + The way this works is we move back to the start of the stream, iterate over each MP3 frame and + calculate the frame count based on our output sample rate, the seek back to the PCM frame we + were sitting on before calling this function. + */ + + /* The stream must support seeking for this to work. */ + if (pMP3->onSeek == NULL) + { + return DRMP3_FALSE; + } + + /* We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so + * we can restore later. */ + currentPCMFrame = pMP3->currentPCMFrame; + + if (!drmp3_seek_to_start_of_stream(pMP3)) + { + return DRMP3_FALSE; + } + + totalPCMFrameCount = 0; + totalMP3FrameCount = 0; + + for (;;) + { + drmp3_uint32 pcmFramesInCurrentMP3Frame; + + pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFramesInCurrentMP3Frame == 0) + { + break; + } + + totalPCMFrameCount += pcmFramesInCurrentMP3Frame; + totalMP3FrameCount += 1; + } + + /* Finally, we need to seek back to where we were. */ + if (!drmp3_seek_to_start_of_stream(pMP3)) + { + return DRMP3_FALSE; + } + + if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) + { + return DRMP3_FALSE; + } + + if (pMP3FrameCount != NULL) + { + *pMP3FrameCount = totalMP3FrameCount; + } + if (pPCMFrameCount != NULL) + { + *pPCMFrameCount = totalPCMFrameCount; + } + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3) +{ + drmp3_uint64 totalPCMFrameCount; + if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) + { + return 0; + } + + return totalPCMFrameCount; +} + +DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3) +{ + drmp3_uint64 totalMP3FrameCount; + if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) + { + return 0; + } + + return totalMP3FrameCount; +} + +static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, + drmp3_uint64* pRunningPCMFrameCount, + float* pRunningPCMFrameCountFractionalPart) +{ + float srcRatio; + float pcmFrameCountOutF; + drmp3_uint32 pcmFrameCountOut; + + srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; + DRMP3_ASSERT(srcRatio > 0); + + pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); + pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF; + *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; + *pRunningPCMFrameCount += pcmFrameCountOut; +} + +typedef struct +{ + drmp3_uint64 bytePos; + drmp3_uint64 pcmFrameIndex; /* <-- After sample rate conversion. */ +} drmp3__seeking_mp3_frame_info; + +DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, + drmp3_seek_point* pSeekPoints) +{ + drmp3_uint32 seekPointCount; + drmp3_uint64 currentPCMFrame; + drmp3_uint64 totalMP3FrameCount; + drmp3_uint64 totalPCMFrameCount; + + if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) + { + return DRMP3_FALSE; /* Invalid args. */ + } + + seekPointCount = *pSeekPointCount; + if (seekPointCount == 0) + { + return DRMP3_FALSE; /* The client has requested no seek points. Consider this to be invalid + arguments since the client has probably not intended this. */ + } + + /* We'll need to seek back to the current sample after calculating the seekpoints so we need to + * go ahead and grab the current location at the top. */ + currentPCMFrame = pMP3->currentPCMFrame; + + /* We never do more than the total number of MP3 frames and we limit it to 32-bits. */ + if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) + { + return DRMP3_FALSE; + } + + /* If there's less than DRMP3_SEEK_LEADING_MP3_FRAMES+1 frames we just report 1 seek point which + * will be the very start of the stream. */ + if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES + 1) + { + seekPointCount = 1; + pSeekPoints[0].seekPosInBytes = 0; + pSeekPoints[0].pcmFrameIndex = 0; + pSeekPoints[0].mp3FramesToDiscard = 0; + pSeekPoints[0].pcmFramesToDiscard = 0; + } + else + { + drmp3_uint64 pcmFramesBetweenSeekPoints; + drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES + 1]; + drmp3_uint64 runningPCMFrameCount = 0; + float runningPCMFrameCountFractionalPart = 0; + drmp3_uint64 nextTargetPCMFrame; + drmp3_uint32 iMP3Frame; + drmp3_uint32 iSeekPoint; + + if (seekPointCount > totalMP3FrameCount - 1) + { + seekPointCount = (drmp3_uint32)totalMP3FrameCount - 1; + } + + pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount + 1); + + /* + Here is where we actually calculate the seek points. We need to start by moving the start of + the stream. We then enumerate over each MP3 frame. + */ + if (!drmp3_seek_to_start_of_stream(pMP3)) + { + return DRMP3_FALSE; + } + + /* + We need to cache the byte positions of the previous MP3 frames. As a new MP3 frame is + iterated, we cycle the byte positions in this array. The value in the first item in this + array is the byte position that will be reported in the next seek point. + */ + + /* We need to initialize the array of MP3 byte positions for the leading MP3 frames. */ + for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES + 1; ++iMP3Frame) + { + drmp3_uint32 pcmFramesInCurrentMP3FrameIn; + + /* The byte position of the next frame will be the stream's cursor position, minus + * whatever is sitting in the buffer. */ + DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); + mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; + mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; + + /* We need to get information about this frame so we can know how many samples it + * contained. */ + pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFramesInCurrentMP3FrameIn == 0) + { + return DRMP3_FALSE; /* This should never happen. */ + } + + drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, + &runningPCMFrameCount, + &runningPCMFrameCountFractionalPart); + } + + /* + At this point we will have extracted the byte positions of the leading MP3 frames. We can + now start iterating over each seek point and calculate them. + */ + nextTargetPCMFrame = 0; + for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) + { + nextTargetPCMFrame += pcmFramesBetweenSeekPoints; + + for (;;) + { + if (nextTargetPCMFrame < runningPCMFrameCount) + { + /* The next seek point is in the current MP3 frame. */ + pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; + pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; + pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = + (drmp3_uint16)(nextTargetPCMFrame - + mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES - 1] + .pcmFrameIndex); + break; + } + else + { + size_t i; + drmp3_uint32 pcmFramesInCurrentMP3FrameIn; + + /* + The next seek point is not in the current MP3 frame, so continue on to the next + one. The first thing to do is cycle the cached MP3 frame info. + */ + for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo) - 1; ++i) + { + mp3FrameInfo[i] = mp3FrameInfo[i + 1]; + } + + /* Cache previous MP3 frame info. */ + mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo) - 1].bytePos = + pMP3->streamCursor - pMP3->dataSize; + mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo) - 1].pcmFrameIndex = + runningPCMFrameCount; + + /* + Go to the next MP3 frame. This shouldn't ever fail, but just in case it does we + just set the seek point and break. If it happens, it should only ever do it for + the last seek point. + */ + pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFramesInCurrentMP3FrameIn == 0) + { + pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; + pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; + pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = + (drmp3_uint16)(nextTargetPCMFrame - + mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES - 1] + .pcmFrameIndex); + break; + } + + drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, + &runningPCMFrameCount, + &runningPCMFrameCountFractionalPart); + } + } + } + + /* Finally, we need to seek back to where we were. */ + if (!drmp3_seek_to_start_of_stream(pMP3)) + { + return DRMP3_FALSE; + } + if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) + { + return DRMP3_FALSE; + } + } + + *pSeekPointCount = seekPointCount; + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, + drmp3_seek_point* pSeekPoints) +{ + if (pMP3 == NULL) + { + return DRMP3_FALSE; + } + + if (seekPointCount == 0 || pSeekPoints == NULL) + { + /* Unbinding. */ + pMP3->seekPointCount = 0; + pMP3->pSeekPoints = NULL; + } + else + { + /* Binding. */ + pMP3->seekPointCount = seekPointCount; + pMP3->pSeekPoints = pSeekPoints; + } + + return DRMP3_TRUE; +} + +static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, + drmp3_uint64* pTotalFrameCount) +{ + drmp3_uint64 totalFramesRead = 0; + drmp3_uint64 framesCapacity = 0; + float* pFrames = NULL; + float temp[4096]; + + DRMP3_ASSERT(pMP3 != NULL); + + for (;;) + { + drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; + drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) + { + break; + } + + /* Reallocate the output buffer if there's not enough room. */ + if (framesCapacity < totalFramesRead + framesJustRead) + { + drmp3_uint64 oldFramesBufferSize; + drmp3_uint64 newFramesBufferSize; + drmp3_uint64 newFramesCap; + float* pNewFrames; + + newFramesCap = framesCapacity * 2; + if (newFramesCap < totalFramesRead + framesJustRead) + { + newFramesCap = totalFramesRead + framesJustRead; + } + + oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); + newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); + if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) + { + break; + } + + pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, + (size_t)oldFramesBufferSize, + &pMP3->allocationCallbacks); + if (pNewFrames == NULL) + { + drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + break; + } + + pFrames = pNewFrames; + framesCapacity = newFramesCap; + } + + DRMP3_COPY_MEMORY(pFrames + totalFramesRead * pMP3->channels, temp, + (size_t)(framesJustRead * pMP3->channels * sizeof(float))); + totalFramesRead += framesJustRead; + + /* If the number of frames we asked for is less that what we actually read it means we've + * reached the end. */ + if (framesJustRead != framesToReadRightNow) + { + break; + } + } + + if (pConfig != NULL) + { + pConfig->channels = pMP3->channels; + pConfig->sampleRate = pMP3->sampleRate; + } + + drmp3_uninit(pMP3); + + if (pTotalFrameCount) + { + *pTotalFrameCount = totalFramesRead; + } + + return pFrames; +} + +static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, + drmp3_uint64* pTotalFrameCount) +{ + drmp3_uint64 totalFramesRead = 0; + drmp3_uint64 framesCapacity = 0; + drmp3_int16* pFrames = NULL; + drmp3_int16 temp[4096]; + + DRMP3_ASSERT(pMP3 != NULL); + + for (;;) + { + drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; + drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) + { + break; + } + + /* Reallocate the output buffer if there's not enough room. */ + if (framesCapacity < totalFramesRead + framesJustRead) + { + drmp3_uint64 newFramesBufferSize; + drmp3_uint64 oldFramesBufferSize; + drmp3_uint64 newFramesCap; + drmp3_int16* pNewFrames; + + newFramesCap = framesCapacity * 2; + if (newFramesCap < totalFramesRead + framesJustRead) + { + newFramesCap = totalFramesRead + framesJustRead; + } + + oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16); + newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16); + if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) + { + break; + } + + pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks( + pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, + &pMP3->allocationCallbacks); + if (pNewFrames == NULL) + { + drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + break; + } + + pFrames = pNewFrames; + framesCapacity = newFramesCap; + } + + DRMP3_COPY_MEMORY(pFrames + totalFramesRead * pMP3->channels, temp, + (size_t)(framesJustRead * pMP3->channels * sizeof(drmp3_int16))); + totalFramesRead += framesJustRead; + + /* If the number of frames we asked for is less that what we actually read it means we've + * reached the end. */ + if (framesJustRead != framesToReadRightNow) + { + break; + } + } + + if (pConfig != NULL) + { + pConfig->channels = pMP3->channels; + pConfig->sampleRate = pMP3->sampleRate; + } + + drmp3_uninit(pMP3); + + if (pTotalFrameCount) + { + *pTotalFrameCount = totalFramesRead; + } + + return pFrames; +} + +DRMP3_API float* drmp3_open_and_read_pcm_frames_f32( + drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, + drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) + { + return NULL; + } + + return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} + +DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16( + drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, + drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) + { + return NULL; + } + + return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} + +DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32( + const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) + { + return NULL; + } + + return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} + +DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16( + const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) + { + return NULL; + } + + return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} + + #ifndef DR_MP3_NO_STDIO +DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32( + const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) + { + return NULL; + } + + return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} + +DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16( + const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, + const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) + { + return NULL; + } + + return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} + #endif + +DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) + { + return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks); + } + else + { + return drmp3__malloc_default(sz, NULL); + } +} + +DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) + { + drmp3__free_from_callbacks(p, pAllocationCallbacks); + } + else + { + drmp3__free_default(p, NULL); + } +} + + #endif /* dr_mp3_c */ +#endif /*DR_MP3_IMPLEMENTATION*/ + +/* +DIFFERENCES BETWEEN minimp3 AND dr_mp3 +====================================== +- First, keep in mind that minimp3 (https://github.com/lieff/minimp3) is where all the real work was +done. All of the code relating to the actual decoding remains mostly unmodified, apart from some +namespacing changes. +- dr_mp3 adds a pulling style API which allows you to deliver raw data via callbacks. So, rather +than pushing data to the decoder, the decoder _pulls_ data from your callbacks. +- In addition to callbacks, a decoder can be initialized from a block of memory and a file. +- The dr_mp3 pull API reads PCM frames rather than whole MP3 frames. +- dr_mp3 adds convenience APIs for opening and decoding entire files in one go. +- dr_mp3 is fully namespaced, including the implementation section, which is more suitable when +compiling projects as a single translation unit (aka unity builds). At the time of writing this, a +unity build is not possible when using minimp3 in conjunction with stb_vorbis. dr_mp3 addresses +this. +*/ + +/* +RELEASE NOTES - v0.5.0 +======================= +Version 0.5.0 has breaking API changes. + +Improved Client-Defined Memory Allocation +----------------------------------------- +The main change with this release is the addition of a more flexible way of implementing custom +memory allocation routines. The existing system of DRMP3_MALLOC, DRMP3_REALLOC and DRMP3_FREE are +still in place and will be used by default when no custom allocation callbacks are specified. + +To use the new system, you pass in a pointer to a drmp3_allocation_callbacks object to drmp3_init() +and family, like this: + + void* my_malloc(size_t sz, void* pUserData) + { + return malloc(sz); + } + void* my_realloc(void* p, size_t sz, void* pUserData) + { + return realloc(p, sz); + } + void my_free(void* p, void* pUserData) + { + free(p); + } + + ... + + drmp3_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = &myData; + allocationCallbacks.onMalloc = my_malloc; + allocationCallbacks.onRealloc = my_realloc; + allocationCallbacks.onFree = my_free; + drmp3_init_file(&mp3, "my_file.mp3", NULL, &allocationCallbacks); + +The advantage of this new system is that it allows you to specify user data which will be passed in +to the allocation routines. + +Passing in null for the allocation callbacks object will cause dr_mp3 to use defaults which is the +same as DRMP3_MALLOC, DRMP3_REALLOC and DRMP3_FREE and the equivalent of how it worked in previous +versions. + +Every API that opens a drmp3 object now takes this extra parameter. These include the following: + + drmp3_init() + drmp3_init_file() + drmp3_init_memory() + drmp3_open_and_read_pcm_frames_f32() + drmp3_open_and_read_pcm_frames_s16() + drmp3_open_memory_and_read_pcm_frames_f32() + drmp3_open_memory_and_read_pcm_frames_s16() + drmp3_open_file_and_read_pcm_frames_f32() + drmp3_open_file_and_read_pcm_frames_s16() + +Renamed APIs +------------ +The following APIs have been renamed for consistency with other dr_* libraries and to make it clear +that they return PCM frame counts rather than sample counts. + + drmp3_open_and_read_f32() -> drmp3_open_and_read_pcm_frames_f32() + drmp3_open_and_read_s16() -> drmp3_open_and_read_pcm_frames_s16() + drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32() + drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16() + drmp3_open_file_and_read_f32() -> drmp3_open_file_and_read_pcm_frames_f32() + drmp3_open_file_and_read_s16() -> drmp3_open_file_and_read_pcm_frames_s16() +*/ + +/* +REVISION HISTORY +================ +v0.6.34 - 2022-09-17 + - Fix compilation with DJGPP. + - Fix compilation when compiling with x86 with no SSE2. + - Remove an unnecessary variable from the drmp3 structure. + +v0.6.33 - 2022-04-10 + - Fix compilation error with the MSVC ARM64 build. + - Fix compilation error on older versions of GCC. + - Remove some unused functions. + +v0.6.32 - 2021-12-11 + - Fix a warning with Clang. + +v0.6.31 - 2021-08-22 + - Fix a bug when loading from memory. + +v0.6.30 - 2021-08-16 + - Silence some warnings. + - Replace memory operations with DRMP3_* macros. + +v0.6.29 - 2021-08-08 + - Bring up to date with minimp3. + +v0.6.28 - 2021-07-31 + - Fix platform detection for ARM64. + - Fix a compilation error with C89. + +v0.6.27 - 2021-02-21 + - Fix a warning due to referencing _MSC_VER when it is undefined. + +v0.6.26 - 2021-01-31 + - Bring up to date with minimp3. + +v0.6.25 - 2020-12-26 + - Remove DRMP3_DEFAULT_CHANNELS and DRMP3_DEFAULT_SAMPLE_RATE which are leftovers from some +removed APIs. + +v0.6.24 - 2020-12-07 + - Fix a typo in version date for 0.6.23. + +v0.6.23 - 2020-12-03 + - Fix an error where a file can be closed twice when initialization of the decoder fails. + +v0.6.22 - 2020-12-02 + - Fix an error where it's possible for a file handle to be left open when initialization of the +decoder fails. + +v0.6.21 - 2020-11-28 + - Bring up to date with minimp3. + +v0.6.20 - 2020-11-21 + - Fix compilation with OpenWatcom. + +v0.6.19 - 2020-11-13 + - Minor code clean up. + +v0.6.18 - 2020-11-01 + - Improve compiler support for older versions of GCC. + +v0.6.17 - 2020-09-28 + - Bring up to date with minimp3. + +v0.6.16 - 2020-08-02 + - Simplify sized types. + +v0.6.15 - 2020-07-25 + - Fix a compilation warning. + +v0.6.14 - 2020-07-23 + - Fix undefined behaviour with memmove(). + +v0.6.13 - 2020-07-06 + - Fix a bug when converting from s16 to f32 in drmp3_read_pcm_frames_f32(). + +v0.6.12 - 2020-06-23 + - Add include guard for the implementation section. + +v0.6.11 - 2020-05-26 + - Fix use of uninitialized variable error. + +v0.6.10 - 2020-05-16 + - Add compile-time and run-time version querying. + - DRMP3_VERSION_MINOR + - DRMP3_VERSION_MAJOR + - DRMP3_VERSION_REVISION + - DRMP3_VERSION_STRING + - drmp3_version() + - drmp3_version_string() + +v0.6.9 - 2020-04-30 + - Change the `pcm` parameter of drmp3dec_decode_frame() to a `const drmp3_uint8*` for consistency +with internal APIs. + +v0.6.8 - 2020-04-26 + - Optimizations to decoding when initializing from memory. + +v0.6.7 - 2020-04-25 + - Fix a compilation error with DR_MP3_NO_STDIO + - Optimization to decoding by reducing some data movement. + +v0.6.6 - 2020-04-23 + - Fix a minor bug with the running PCM frame counter. + +v0.6.5 - 2020-04-19 + - Fix compilation error on ARM builds. + +v0.6.4 - 2020-04-19 + - Bring up to date with changes to minimp3. + +v0.6.3 - 2020-04-13 + - Fix some pedantic warnings. + +v0.6.2 - 2020-04-10 + - Fix a crash in drmp3_open_*_and_read_pcm_frames_*() if the output config object is NULL. + +v0.6.1 - 2020-04-05 + - Fix warnings. + +v0.6.0 - 2020-04-04 + - API CHANGE: Remove the pConfig parameter from the following APIs: + - drmp3_init() + - drmp3_init_memory() + - drmp3_init_file() + - Add drmp3_init_file_w() for opening a file from a wchar_t encoded path. + +v0.5.6 - 2020-02-12 + - Bring up to date with minimp3. + +v0.5.5 - 2020-01-29 + - Fix a memory allocation bug in high level s16 decoding APIs. + +v0.5.4 - 2019-12-02 + - Fix a possible null pointer dereference when using custom memory allocators for realloc(). + +v0.5.3 - 2019-11-14 + - Fix typos in documentation. + +v0.5.2 - 2019-11-02 + - Bring up to date with minimp3. + +v0.5.1 - 2019-10-08 + - Fix a warning with GCC. + +v0.5.0 - 2019-10-07 + - API CHANGE: Add support for user defined memory allocation routines. This system allows the +program to specify their own memory allocation routines with a user data pointer for client-specific +contextual data. This adds an extra parameter to the end of the following APIs: + - drmp3_init() + - drmp3_init_file() + - drmp3_init_memory() + - drmp3_open_and_read_pcm_frames_f32() + - drmp3_open_and_read_pcm_frames_s16() + - drmp3_open_memory_and_read_pcm_frames_f32() + - drmp3_open_memory_and_read_pcm_frames_s16() + - drmp3_open_file_and_read_pcm_frames_f32() + - drmp3_open_file_and_read_pcm_frames_s16() + - API CHANGE: Renamed the following APIs: + - drmp3_open_and_read_f32() -> drmp3_open_and_read_pcm_frames_f32() + - drmp3_open_and_read_s16() -> drmp3_open_and_read_pcm_frames_s16() + - drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32() + - drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16() + - drmp3_open_file_and_read_f32() -> drmp3_open_file_and_read_pcm_frames_f32() + - drmp3_open_file_and_read_s16() -> drmp3_open_file_and_read_pcm_frames_s16() + +v0.4.7 - 2019-07-28 + - Fix a compiler error. + +v0.4.6 - 2019-06-14 + - Fix a compiler error. + +v0.4.5 - 2019-06-06 + - Bring up to date with minimp3. + +v0.4.4 - 2019-05-06 + - Fixes to the VC6 build. + +v0.4.3 - 2019-05-05 + - Use the channel count and/or sample rate of the first MP3 frame instead of +DRMP3_DEFAULT_CHANNELS and DRMP3_DEFAULT_SAMPLE_RATE when they are set to 0. To use the old +behaviour, just set the relevant property to DRMP3_DEFAULT_CHANNELS or DRMP3_DEFAULT_SAMPLE_RATE. + - Add s16 reading APIs + - drmp3_read_pcm_frames_s16 + - drmp3_open_memory_and_read_pcm_frames_s16 + - drmp3_open_and_read_pcm_frames_s16 + - drmp3_open_file_and_read_pcm_frames_s16 + - Add drmp3_get_mp3_and_pcm_frame_count() to the public header section. + - Add support for C89. + - Change license to choice of public domain or MIT-0. + +v0.4.2 - 2019-02-21 + - Fix a warning. + +v0.4.1 - 2018-12-30 + - Fix a warning. + +v0.4.0 - 2018-12-16 + - API CHANGE: Rename some APIs: + - drmp3_read_f32 -> to drmp3_read_pcm_frames_f32 + - drmp3_seek_to_frame -> drmp3_seek_to_pcm_frame + - drmp3_open_and_decode_f32 -> drmp3_open_and_read_pcm_frames_f32 + - drmp3_open_and_decode_memory_f32 -> drmp3_open_memory_and_read_pcm_frames_f32 + - drmp3_open_and_decode_file_f32 -> drmp3_open_file_and_read_pcm_frames_f32 + - Add drmp3_get_pcm_frame_count(). + - Add drmp3_get_mp3_frame_count(). + - Improve seeking performance. + +v0.3.2 - 2018-09-11 + - Fix a couple of memory leaks. + - Bring up to date with minimp3. + +v0.3.1 - 2018-08-25 + - Fix C++ build. + +v0.3.0 - 2018-08-25 + - Bring up to date with minimp3. This has a minor API change: the "pcm" parameter of +drmp3dec_decode_frame() has been changed from short* to void* because it can now output both s16 and +f32 samples, depending on whether or not the DR_MP3_FLOAT_OUTPUT option is set. + +v0.2.11 - 2018-08-08 + - Fix a bug where the last part of a file is not read. + +v0.2.10 - 2018-08-07 + - Improve 64-bit detection. + +v0.2.9 - 2018-08-05 + - Fix C++ build on older versions of GCC. + - Bring up to date with minimp3. + +v0.2.8 - 2018-08-02 + - Fix compilation errors with older versions of GCC. + +v0.2.7 - 2018-07-13 + - Bring up to date with minimp3. + +v0.2.6 - 2018-07-12 + - Bring up to date with minimp3. + +v0.2.5 - 2018-06-22 + - Bring up to date with minimp3. + +v0.2.4 - 2018-05-12 + - Bring up to date with minimp3. + +v0.2.3 - 2018-04-29 + - Fix TCC build. + +v0.2.2 - 2018-04-28 + - Fix bug when opening a decoder from memory. + +v0.2.1 - 2018-04-27 + - Efficiency improvements when the decoder reaches the end of the stream. + +v0.2 - 2018-04-21 + - Bring up to date with minimp3. + - Start using major.minor.revision versioning. + +v0.1d - 2018-03-30 + - Bring up to date with minimp3. + +v0.1c - 2018-03-11 + - Fix C++ build error. + +v0.1b - 2018-03-07 + - Bring up to date with minimp3. + +v0.1a - 2018-02-28 + - Fix compilation error on GCC/Clang. + - Fix some warnings. + +v0.1 - 2018-02-xx + - Initial versioned release. +*/ + +/* +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright 2020 David Reid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/* + https://github.com/lieff/minimp3 + To the extent possible under law, the author(s) have dedicated all copyright and related and + neighboring rights to this software to the public domain worldwide. This software is distributed + without any warranty. See . +*/ diff --git a/libraries/https/common/Connection.h b/libraries/https/common/Connection.h deleted file mode 100644 index 1832c5ee7..000000000 --- a/libraries/https/common/Connection.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include - -class Connection -{ - public: - virtual bool connect(const std::string& hostname, uint16_t port) = 0; - virtual size_t read(char* buffer, size_t size) = 0; - virtual size_t write(const char* buffer, size_t size) = 0; - virtual void close() = 0; - virtual ~Connection() {}; -}; diff --git a/libraries/https/common/ConnectionClient.h b/libraries/https/common/ConnectionClient.h deleted file mode 100644 index 150c3e041..000000000 --- a/libraries/https/common/ConnectionClient.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "Connection.h" -#include "HTTPRequest.h" -#include "HTTPSClient.h" - -template -class ConnectionClient : public HTTPSClient -{ - public: - virtual bool valid() const override; - virtual HTTPSClient::Reply request(const HTTPSClient::Request& req) override; - - private: - static Connection* factory(); -}; - -template -bool ConnectionClient::valid() const -{ - return Connection::valid(); -} - -template -Connection* ConnectionClient::factory() -{ - return new Connection(); -} - -template -HTTPSClient::Reply ConnectionClient::request(const HTTPSClient::Request& req) -{ - HTTPRequest request(factory); - return request.request(req); -} diff --git a/libraries/https/common/HTTPRequest.cpp b/libraries/https/common/HTTPRequest.cpp deleted file mode 100644 index 23cb10551..000000000 --- a/libraries/https/common/HTTPRequest.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include -#include -#include -#include -#include - -#include "HTTPRequest.h" -#include "PlaintextConnection.h" - -HTTPRequest::HTTPRequest(ConnectionFactory factory) : factory(factory) -{} - -HTTPSClient::Reply HTTPRequest::request(const HTTPSClient::Request& req) -{ - HTTPSClient::Reply reply; - reply.responseCode = 0; - - auto info = parseUrl(req.url); - if (!info.valid) - return reply; - - std::unique_ptr conn; - if (info.schema == "http") - conn.reset(new PlaintextConnection()); - else if (info.schema == "https") - conn.reset(factory()); - else - throw std::runtime_error("Unknown url schema"); - - if (!conn->connect(info.hostname, info.port)) - return reply; - - // Build the request - { - std::stringstream request; - std::string method = req.method; - - bool hasData = req.postdata.length() > 0; - if (method.length() == 0) - method = hasData ? "POST" : "GET"; - - request << req.method << info.query << " HTTP/1.1\r\n"; - - for (auto& header : req.headers) - request << header.first << ": " << header.second << "\r\n"; - - request << "Connection: Close\r\n"; - - request << "Host: " << info.hostname << "\r\n"; - - if (hasData && req.headers.count("Content-Type") == 0) - request << "Content-Type: application/x-www-form-urlencoded\r\n"; - - if (hasData) - request << "Content-Length: " << req.postdata.size() << "\r\n"; - - request << "\r\n"; - - if (hasData) - request << req.postdata; - - // Send it - std::string requestData = request.str(); - conn->write(requestData.c_str(), requestData.size()); - } - - // Now receive the reply - std::stringstream response; - { - char buffer[8192]; - - while (true) - { - size_t read = conn->read(buffer, sizeof(buffer)); - response.write(buffer, read); - if (read == 0) - break; - } - - conn->close(); - } - - reply.responseCode = 500; - // And parse it - { - std::string protocol; - response >> protocol; - if (protocol != "HTTP/1.1") - return reply; - - response >> reply.responseCode; - response.ignore(std::numeric_limits::max(), '\n'); - - for (std::string line; getline(response, line, '\n') && line != "\r";) - { - auto sep = line.find(':'); - reply.headers[line.substr(0, sep)] = line.substr(sep + 1, line.size() - sep - 1); - } - - auto begin = std::istreambuf_iterator(response); - auto end = std::istreambuf_iterator(); - reply.body = std::string(begin, end); - } - - return reply; -} - -HTTPRequest::DissectedURL HTTPRequest::parseUrl(const std::string& url) -{ - DissectedURL dis; - dis.valid = false; - - // Schema - auto schemaStart = 0; - auto schemaEnd = url.find("://"); - dis.schema = url.substr(schemaStart, schemaEnd - schemaStart); - - // Auth+Hostname+Port - auto connStart = schemaEnd + 3; - auto connEnd = url.find('/', connStart); - if (connEnd == std::string::npos) - connEnd = url.size(); - - // TODO: Auth - if (url.find("@", connStart, connEnd - connStart) != std::string::npos) - return dis; - - // Port - auto portStart = url.find(':', connStart); - auto portEnd = connEnd; - if (portStart == std::string::npos || portStart > portEnd) - { - dis.port = dis.schema == "http" ? 80 : 443; - portStart = portEnd; - } - else - dis.port = std::stoi(url.substr(portStart + 1, portEnd - portStart - 1)); - - // Hostname - auto hostnameStart = connStart; - auto hostnameEnd = portStart; - dis.hostname = url.substr(hostnameStart, hostnameEnd - hostnameStart); - - // And the query - dis.query = url.substr(connEnd); - if (dis.query.size() == 0) - dis.query = "/"; - - dis.valid = true; - - return dis; -} diff --git a/libraries/https/common/HTTPRequest.h b/libraries/https/common/HTTPRequest.h deleted file mode 100644 index 3fead3a2b..000000000 --- a/libraries/https/common/HTTPRequest.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include - -#include "Connection.h" -#include "HTTPSClient.h" - -class HTTPRequest -{ - public: - typedef std::function ConnectionFactory; - HTTPRequest(ConnectionFactory factory); - - HTTPSClient::Reply request(const HTTPSClient::Request& req); - - private: - ConnectionFactory factory; - - struct DissectedURL - { - bool valid; - std::string schema; - std::string hostname; - uint16_t port; - std::string query; - // TODO: Auth? - }; - - DissectedURL parseUrl(const std::string& url); -}; diff --git a/libraries/https/common/HTTPSClient.cpp b/libraries/https/common/HTTPSClient.cpp deleted file mode 100644 index 3729cb939..000000000 --- a/libraries/https/common/HTTPSClient.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include - -#include "HTTPSClient.h" - -// This may not be the order you expect, as shorter strings always compare less, -// but it's sufficient for our map -bool HTTPSClient::ci_string_less::operator()(const std::string& lhs, const std::string& rhs) const -{ - const size_t lhs_size = lhs.size(); - const size_t rhs_size = rhs.size(); - const size_t steps = std::min(lhs_size, rhs_size); - - if (lhs_size < rhs_size) - return true; - else if (lhs_size > rhs_size) - return false; - - for (size_t i = 0; i < steps; ++i) - { - char l = std::tolower(lhs[i]); - char r = std::tolower(rhs[i]); - if (l < r) - return true; - else if (l > r) - return false; - } - - return false; -} - -HTTPSClient::Request::Request(const std::string& url) : url(url), method("") -{} diff --git a/libraries/https/common/HTTPSClient.h b/libraries/https/common/HTTPSClient.h deleted file mode 100644 index 3b84bc74b..000000000 --- a/libraries/https/common/HTTPSClient.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include -#include - -class HTTPSClient -{ - public: - struct ci_string_less - { - bool operator()(const std::string& lhs, const std::string& rhs) const; - }; - - using header_map = std::map; - - struct Request - { - Request(const std::string& url); - - header_map headers; - std::string url; - std::string postdata; - std::string method; - }; - - struct Reply - { - header_map headers; - std::string body; - int responseCode; - }; - - virtual ~HTTPSClient() - {} - - virtual bool valid() const = 0; - virtual Reply request(const Request& req) = 0; -}; diff --git a/libraries/https/common/PlaintextConnection.cpp b/libraries/https/common/PlaintextConnection.cpp deleted file mode 100644 index 9fb8960f8..000000000 --- a/libraries/https/common/PlaintextConnection.cpp +++ /dev/null @@ -1,78 +0,0 @@ - -#include - -#include -#include -#include -#include - -#include "PlaintextConnection.h" - -PlaintextConnection::PlaintextConnection() : fd(-1) -{} - -PlaintextConnection::~PlaintextConnection() -{ - if (fd != -1) - ::close(fd); -} - -bool PlaintextConnection::connect(const std::string& hostname, uint16_t port) -{ - addrinfo hints; - std::memset(&hints, 0, sizeof(hints)); - hints.ai_flags = hints.ai_protocol = 0; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - addrinfo* addrs = nullptr; - std::string portString = std::to_string(port); - getaddrinfo(hostname.c_str(), portString.c_str(), &hints, &addrs); - - // Try all addresses returned - bool connected = false; - for (addrinfo* addr = addrs; !connected && addr; addr = addr->ai_next) - { - fd = socket(addr->ai_family, SOCK_STREAM, 0); - connected = ::connect(fd, addr->ai_addr, addr->ai_addrlen) == 0; - if (!connected) - ::close(fd); - } - - freeaddrinfo(addrs); - - if (!connected) - { - fd = -1; - return false; - } - - return true; -} - -size_t PlaintextConnection::read(char* buffer, size_t size) -{ - auto read = ::recv(fd, buffer, size, 0); - if (read < 0) - read = 0; - return static_cast(read); -} - -size_t PlaintextConnection::write(const char* buffer, size_t size) -{ - auto written = ::send(fd, buffer, size, 0); - if (written < 0) - written = 0; - return static_cast(written); -} - -void PlaintextConnection::close() -{ - ::close(fd); - fd = -1; -} - -int PlaintextConnection::getFd() const -{ - return fd; -} diff --git a/libraries/https/generic/CurlClient.cpp b/libraries/https/generic/CurlClient.cpp deleted file mode 100644 index 2b09ed0c2..000000000 --- a/libraries/https/generic/CurlClient.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include "CurlClient.h" - -#include -#include -#include -#include - -namespace -{ - std::once_flag once; -} - -static size_t stringstreamWriter(char* ptr, size_t size, size_t nmemb, void* userdata) -{ - std::stringstream* ss = (std::stringstream*)userdata; - - size_t count = size * nmemb; - ss->write(ptr, count); - - return count; -} - -static size_t headerWriter(char* ptr, size_t size, size_t nmemb, void* userdata) -{ - std::map& headers = *((std::map*)userdata); - - size_t count = size * nmemb; - std::string line(ptr, count); - - size_t split = line.find(':'); - size_t newline = line.find('\r'); - - if (newline == std::string::npos) - newline = line.size(); - - if (split != std::string::npos) - headers[line.substr(0, split)] = line.substr(split + 1, newline - split - 1); - - return count; -} - -bool CurlClient::valid() const -{ - std::call_once(once, curl_global_init, CURL_GLOBAL_DEFAULT); - - return true; -} - -HTTPSClient::Reply CurlClient::request(const HTTPSClient::Request& req) -{ - std::call_once(once, curl_global_init, CURL_GLOBAL_DEFAULT); - - Reply reply {}; - reply.responseCode = 400; - - CURL* handle = curl_easy_init(); - - if (!handle) - throw std::runtime_error("Could not create curl request"); - - curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(handle, CURLOPT_URL, req.url.c_str()); - curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L); - - if (req.method == "PUT") - curl_easy_setopt(handle, CURLOPT_PUT, 1L); - if (req.method == "POST") - { - curl_easy_setopt(handle, CURLOPT_POST, 1L); - curl_easy_setopt(handle, CURLOPT_POSTFIELDS, req.postdata.c_str()); - curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, req.postdata.size()); - } - else - curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, req.method.c_str()); - - if (req.postdata.size() > 0 && (req.method != "GET" && req.method != "HEAD")) - { - curl_easy_setopt(handle, CURLOPT_POSTFIELDS, req.postdata.c_str()); - curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, req.postdata.size()); - } - - // Curl doesn't copy memory, keep the strings around - std::vector lines; - for (auto& header : req.headers) - { - std::stringstream line; - line << header.first << ": " << header.second; - lines.push_back(line.str()); - } - - curl_slist* sendHeaders = nullptr; - for (auto& line : lines) - sendHeaders = curl_slist_append(sendHeaders, line.c_str()); - - if (sendHeaders) - curl_easy_setopt(handle, CURLOPT_HTTPHEADER, sendHeaders); - - std::stringstream body; - - curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, stringstreamWriter); - curl_easy_setopt(handle, CURLOPT_WRITEDATA, &body); - - curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerWriter); - curl_easy_setopt(handle, CURLOPT_HEADERDATA, &reply.headers); - - curl_easy_perform(handle); - - if (sendHeaders) - curl_slist_free_all(sendHeaders); - - { - long responseCode; - curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode); - reply.responseCode = (int)responseCode; - } - - reply.body = body.str(); - - curl_easy_cleanup(handle); - - return reply; -} diff --git a/libraries/https/generic/CurlClient.h b/libraries/https/generic/CurlClient.h deleted file mode 100644 index 9fc67ac70..000000000 --- a/libraries/https/generic/CurlClient.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -#include "../common/HTTPSClient.h" - -class CurlClient : public HTTPSClient -{ - public: - virtual bool valid() const override; - virtual HTTPSClient::Reply request(const HTTPSClient::Request& req) override; -}; diff --git a/libraries/https/https.cpp b/libraries/https/https.cpp deleted file mode 100644 index 88420d58a..000000000 --- a/libraries/https/https.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include -#include -#include - -extern "C" -{ -#include -#include -} - -#include "common/HTTPSCommon.h" - -static const std::set validMethods = { - "GET", "HEAD", "POST", "PUT", "DELETE", "PATCH" -}; - -static std::string w_checkstring(lua_State* L, int idx) -{ - size_t len; - const char* str = luaL_checklstring(L, idx, &len); - - return std::string(str, len); -} - -static void w_pushstring(lua_State* L, const std::string& str) -{ - lua_pushlstring(L, str.data(), str.size()); -} - -static void w_readheaders(lua_State* L, int idx, HTTPSClient::header_map& headers) -{ - if (idx < 0) - idx += lua_gettop(L) + 1; - - lua_pushnil(L); - while (lua_next(L, idx)) - { - auto header = w_checkstring(L, -2); - headers[header] = w_checkstring(L, -1); - lua_pop(L, 1); - } - lua_pop(L, 1); -} - -static std::string quoted(const std::string& value) -{ - return "\"" + value + "\""; -} - -static std::string w_optmethod(lua_State* L, int idx, const std::string& defaultMethod) -{ - if (lua_isnoneornil(L, idx)) - return defaultMethod; - - auto str = w_checkstring(L, idx); - - std::transform(str.begin(), str.end(), str.begin(), [](uint8_t c) { return std::toupper(c); }); - - if (validMethods.find(str) == validMethods.end()) - { - // clang-format off - std::string values = std::accumulate(validMethods.begin(), validMethods.end(), std::string(), - [](const std::string& a, const std::string& b) { return a.empty() ? quoted(b) : quoted(a) + ", " + quoted(b); }); - std::string message = std::string("Invalid method '" + str + "' Expected one of " + values); - // clang-format on - luaL_argerror(L, idx, message.c_str()); - } - - return str; -} - -static int w_request(lua_State* L) -{ - auto url = w_checkstring(L, 1); - HTTPSClient::Request req(url); - - bool advanced = false; - - if (lua_istable(L, 2)) - { - advanced = true; - - std::string defaultMethod = "GET"; - - lua_getfield(L, 2, "data"); - - if (!lua_isnoneornil(L, -1)) - { - req.postdata = w_checkstring(L, -1); - defaultMethod = "POST"; - } - - lua_pop(L, 1); - - lua_getfield(L, 2, "method"); - req.method = w_optmethod(L, -1, defaultMethod); - lua_pop(L, 1); - - lua_getfield(L, 2, "headers"); - if (!lua_isnoneornil(L, -1)) - w_readheaders(L, -1, req.headers); - lua_pop(L, 1); - } - - HTTPSClient::Reply reply; - - try - { - reply = request(req); - } - catch (const std::exception& e) - { - std::string errorMessage = e.what(); - lua_pushnil(L); - lua_pushstring(L, errorMessage.c_str()); - return 2; - } - - lua_pushinteger(L, reply.responseCode); - w_pushstring(L, reply.body); - - if (advanced) - { - lua_newtable(L); - for (const auto& header : reply.headers) - { - w_pushstring(L, header.first); - w_pushstring(L, header.second); - lua_settable(L, -3); - } - } - - return advanced ? 3 : 2; -} - -extern "C" int luaopen_https(lua_State* L) -{ - lua_newtable(L); - - lua_pushcfunction(L, w_request); - lua_setfield(L, -2, "request"); - - return 1; -} diff --git a/libraries/lua/Makefile b/libraries/lua/Makefile deleted file mode 100644 index 1d46d3423..000000000 --- a/libraries/lua/Makefile +++ /dev/null @@ -1,178 +0,0 @@ -# makefile for building Lua -# see ../INSTALL for installation instructions -# see ../Makefile and luaconf.h for further customization - -# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= - -# Your platform. See PLATS for possible values. -PLAT= none - -CC= gcc -CFLAGS= -O2 -Wall $(MYCFLAGS) -AR= ar rcu -RANLIB= ranlib -RM= rm -f -LIBS= -lm $(MYLIBS) - -MYCFLAGS= -MYLDFLAGS= -MYLIBS= - -# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE ========= - -PLATS= aix ansi bsd generic linux macosx mingw posix solaris - -LUA_A= liblua.a -CORE_O= lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \ - lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o \ - lundump.o lvm.o lzio.o -LIB_O= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \ - lstrlib.o loadlib.o linit.o - -LUA_T= lua -LUA_O= lua.o - -LUAC_T= luac -LUAC_O= luac.o print.o - -ALL_O= $(CORE_O) $(LIB_O) $(LUA_O) $(LUAC_O) -ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T) -ALL_A= $(LUA_A) - -default: $(PLAT) - -all: $(ALL_T) - -o: $(ALL_O) - -a: $(ALL_A) - -$(LUA_A): $(CORE_O) $(LIB_O) - $(AR) $@ $? - $(RANLIB) $@ - -$(LUA_T): $(LUA_O) $(LUA_A) - $(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) $(LIBS) - -$(LUAC_T): $(LUAC_O) $(LUA_A) - $(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS) - -clean: - $(RM) $(ALL_T) $(ALL_O) - -depend: - @$(CC) $(CFLAGS) -MM l*.c print.c - -echo: - @echo "PLAT = $(PLAT)" - @echo "CC = $(CC)" - @echo "CFLAGS = $(CFLAGS)" - @echo "AR = $(AR)" - @echo "RANLIB = $(RANLIB)" - @echo "RM = $(RM)" - @echo "MYCFLAGS = $(MYCFLAGS)" - @echo "MYLDFLAGS = $(MYLDFLAGS)" - @echo "MYLIBS = $(MYLIBS)" - -# convenience targets for popular platforms - -none: - @echo "Please choose a platform: $(PLATS)" - -aix: - $(MAKE) all CC="xlc" CFLAGS="-O2" MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" MYLDFLAGS="-brtl -bexpall" - -ansi: - $(MAKE) all MYCFLAGS=-DLUA_ANSI - -bsd: - $(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-Wl,-E" - -generic: - $(MAKE) all MYCFLAGS= - -linux: - $(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-Wl,-E -ldl -lreadline -lhistory -lncurses" - -macosx: - $(MAKE) all MYCFLAGS=-DLUA_USE_MACOSX -# use this on Mac OS X 10.4 -# $(MAKE) all MYCFLAGS="-DLUA_USE_MACOSX -DLUA_USE_READLINE" MYLIBS="-lreadline" - -mingw: - $(MAKE) "LUA_A=lua51.dll" "LUA_T=lua.exe" \ - "AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \ - "MYCFLAGS=-DLUA_BUILD_AS_DLL" "MYLIBS=" "MYLDFLAGS=-s" lua.exe - -posix: - $(MAKE) all MYCFLAGS=-DLUA_USE_POSIX - -solaris: - $(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" - -# list targets that do not create files (but not all makes understand .PHONY) -.PHONY: all $(PLATS) default o a clean depend echo none - -# DO NOT DELETE - -lapi.o: lapi.c lua.h luaconf.h lapi.h lobject.h llimits.h ldebug.h \ - lstate.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h \ - lundump.h lvm.h -lauxlib.o: lauxlib.c lua.h luaconf.h lauxlib.h -lbaselib.o: lbaselib.c lua.h luaconf.h lauxlib.h lualib.h -lcode.o: lcode.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \ - lzio.h lmem.h lopcodes.h lparser.h ltable.h ldebug.h lstate.h ltm.h \ - ldo.h lgc.h -ldblib.o: ldblib.c lua.h luaconf.h lauxlib.h lualib.h -ldebug.o: ldebug.c lua.h luaconf.h lapi.h lobject.h llimits.h lcode.h \ - llex.h lzio.h lmem.h lopcodes.h lparser.h ltable.h ldebug.h lstate.h \ - ltm.h ldo.h lfunc.h lstring.h lgc.h lvm.h -ldo.o: ldo.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ - lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lparser.h ltable.h \ - lstring.h lundump.h lvm.h -ldump.o: ldump.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h \ - lzio.h lmem.h lundump.h -lfunc.o: lfunc.c lua.h luaconf.h lfunc.h lobject.h llimits.h lgc.h lmem.h \ - lstate.h ltm.h lzio.h -lgc.o: lgc.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ - lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h -linit.o: linit.c lua.h luaconf.h lualib.h lauxlib.h -liolib.o: liolib.c lua.h luaconf.h lauxlib.h lualib.h -llex.o: llex.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h ltm.h \ - lzio.h lmem.h llex.h lparser.h ltable.h lstring.h lgc.h -lmathlib.o: lmathlib.c lua.h luaconf.h lauxlib.h lualib.h -lmem.o: lmem.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ - ltm.h lzio.h lmem.h ldo.h -loadlib.o: loadlib.c lauxlib.h lua.h luaconf.h lobject.h llimits.h \ - lualib.h -lobject.o: lobject.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h \ - ltm.h lzio.h lmem.h lstring.h lgc.h lvm.h -lopcodes.o: lopcodes.c lopcodes.h llimits.h lua.h luaconf.h -loslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h -lparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \ - lzio.h lmem.h lopcodes.h lparser.h ltable.h ldebug.h lstate.h ltm.h \ - ldo.h lfunc.h lstring.h lgc.h -lstate.o: lstate.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ - ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h ltable.h -lstring.o: lstring.c lua.h luaconf.h lmem.h llimits.h lobject.h lstate.h \ - ltm.h lzio.h lstring.h lgc.h -lstrlib.o: lstrlib.c lua.h luaconf.h lauxlib.h lualib.h -ltable.o: ltable.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ - ltm.h lzio.h lmem.h ldo.h lgc.h ltable.h -ltablib.o: ltablib.c lua.h luaconf.h lauxlib.h lualib.h -ltm.o: ltm.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h lzio.h \ - lmem.h lstring.h lgc.h ltable.h -lua.o: lua.c lua.h luaconf.h lauxlib.h lualib.h -luac.o: luac.c lua.h luaconf.h lauxlib.h ldo.h lobject.h llimits.h \ - lstate.h ltm.h lzio.h lmem.h lfunc.h lopcodes.h lstring.h lgc.h \ - lundump.h -lundump.o: lundump.c lua.h luaconf.h ldebug.h lstate.h lobject.h \ - llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h lundump.h -lvm.o: lvm.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ - lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h ltable.h lvm.h -lzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \ - lzio.h -print.o: print.c ldebug.h lstate.h lua.h luaconf.h lobject.h llimits.h \ - ltm.h lzio.h lmem.h lopcodes.h lundump.h - -# (end of Makefile) diff --git a/libraries/lua/lapi.c b/libraries/lua/lapi.c deleted file mode 100644 index 5d5145d2e..000000000 --- a/libraries/lua/lapi.c +++ /dev/null @@ -1,1087 +0,0 @@ -/* -** $Id: lapi.c,v 2.55.1.5 2008/07/04 18:41:18 roberto Exp $ -** Lua API -** See Copyright Notice in lua.h -*/ - - -#include -#include -#include -#include - -#define lapi_c -#define LUA_CORE - -#include "lua.h" - -#include "lapi.h" -#include "ldebug.h" -#include "ldo.h" -#include "lfunc.h" -#include "lgc.h" -#include "lmem.h" -#include "lobject.h" -#include "lstate.h" -#include "lstring.h" -#include "ltable.h" -#include "ltm.h" -#include "lundump.h" -#include "lvm.h" - - - -const char lua_ident[] = - "$Lua: " LUA_RELEASE " " LUA_COPYRIGHT " $\n" - "$Authors: " LUA_AUTHORS " $\n" - "$URL: www.lua.org $\n"; - - - -#define api_checknelems(L, n) api_check(L, (n) <= (L->top - L->base)) - -#define api_checkvalidindex(L, i) api_check(L, (i) != luaO_nilobject) - -#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;} - - - -static TValue *index2adr (lua_State *L, int idx) { - if (idx > 0) { - TValue *o = L->base + (idx - 1); - api_check(L, idx <= L->ci->top - L->base); - if (o >= L->top) return cast(TValue *, luaO_nilobject); - else return o; - } - else if (idx > LUA_REGISTRYINDEX) { - api_check(L, idx != 0 && -idx <= L->top - L->base); - return L->top + idx; - } - else switch (idx) { /* pseudo-indices */ - case LUA_REGISTRYINDEX: return registry(L); - case LUA_ENVIRONINDEX: { - Closure *func = curr_func(L); - sethvalue(L, &L->env, func->c.env); - return &L->env; - } - case LUA_GLOBALSINDEX: return gt(L); - default: { - Closure *func = curr_func(L); - idx = LUA_GLOBALSINDEX - idx; - return (idx <= func->c.nupvalues) - ? &func->c.upvalue[idx-1] - : cast(TValue *, luaO_nilobject); - } - } -} - - -static Table *getcurrenv (lua_State *L) { - if (L->ci == L->base_ci) /* no enclosing function? */ - return hvalue(gt(L)); /* use global table as environment */ - else { - Closure *func = curr_func(L); - return func->c.env; - } -} - - -void luaA_pushobject (lua_State *L, const TValue *o) { - setobj2s(L, L->top, o); - api_incr_top(L); -} - - -LUA_API int lua_checkstack (lua_State *L, int size) { - int res = 1; - lua_lock(L); - if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK) - res = 0; /* stack overflow */ - else if (size > 0) { - luaD_checkstack(L, size); - if (L->ci->top < L->top + size) - L->ci->top = L->top + size; - } - lua_unlock(L); - return res; -} - - -LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { - int i; - if (from == to) return; - lua_lock(to); - api_checknelems(from, n); - api_check(from, G(from) == G(to)); - api_check(from, to->ci->top - to->top >= n); - from->top -= n; - for (i = 0; i < n; i++) { - setobj2s(to, to->top++, from->top + i); - } - lua_unlock(to); -} - - -LUA_API void lua_setlevel (lua_State *from, lua_State *to) { - to->nCcalls = from->nCcalls; -} - - -LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { - lua_CFunction old; - lua_lock(L); - old = G(L)->panic; - G(L)->panic = panicf; - lua_unlock(L); - return old; -} - - -LUA_API lua_State *lua_newthread (lua_State *L) { - lua_State *L1; - lua_lock(L); - luaC_checkGC(L); - L1 = luaE_newthread(L); - setthvalue(L, L->top, L1); - api_incr_top(L); - lua_unlock(L); - luai_userstatethread(L, L1); - return L1; -} - - - -/* -** basic stack manipulation -*/ - - -LUA_API int lua_gettop (lua_State *L) { - return cast_int(L->top - L->base); -} - - -LUA_API void lua_settop (lua_State *L, int idx) { - lua_lock(L); - if (idx >= 0) { - api_check(L, idx <= L->stack_last - L->base); - while (L->top < L->base + idx) - setnilvalue(L->top++); - L->top = L->base + idx; - } - else { - api_check(L, -(idx+1) <= (L->top - L->base)); - L->top += idx+1; /* `subtract' index (index is negative) */ - } - lua_unlock(L); -} - - -LUA_API void lua_remove (lua_State *L, int idx) { - StkId p; - lua_lock(L); - p = index2adr(L, idx); - api_checkvalidindex(L, p); - while (++p < L->top) setobjs2s(L, p-1, p); - L->top--; - lua_unlock(L); -} - - -LUA_API void lua_insert (lua_State *L, int idx) { - StkId p; - StkId q; - lua_lock(L); - p = index2adr(L, idx); - api_checkvalidindex(L, p); - for (q = L->top; q>p; q--) setobjs2s(L, q, q-1); - setobjs2s(L, p, L->top); - lua_unlock(L); -} - - -LUA_API void lua_replace (lua_State *L, int idx) { - StkId o; - lua_lock(L); - /* explicit test for incompatible code */ - if (idx == LUA_ENVIRONINDEX && L->ci == L->base_ci) - luaG_runerror(L, "no calling environment"); - api_checknelems(L, 1); - o = index2adr(L, idx); - api_checkvalidindex(L, o); - if (idx == LUA_ENVIRONINDEX) { - Closure *func = curr_func(L); - api_check(L, ttistable(L->top - 1)); - func->c.env = hvalue(L->top - 1); - luaC_barrier(L, func, L->top - 1); - } - else { - setobj(L, o, L->top - 1); - if (idx < LUA_GLOBALSINDEX) /* function upvalue? */ - luaC_barrier(L, curr_func(L), L->top - 1); - } - L->top--; - lua_unlock(L); -} - - -LUA_API void lua_pushvalue (lua_State *L, int idx) { - lua_lock(L); - setobj2s(L, L->top, index2adr(L, idx)); - api_incr_top(L); - lua_unlock(L); -} - - - -/* -** access functions (stack -> C) -*/ - - -LUA_API int lua_type (lua_State *L, int idx) { - StkId o = index2adr(L, idx); - return (o == luaO_nilobject) ? LUA_TNONE : ttype(o); -} - - -LUA_API const char *lua_typename (lua_State *L, int t) { - UNUSED(L); - return (t == LUA_TNONE) ? "no value" : luaT_typenames[t]; -} - - -LUA_API int lua_iscfunction (lua_State *L, int idx) { - StkId o = index2adr(L, idx); - return iscfunction(o); -} - - -LUA_API int lua_isnumber (lua_State *L, int idx) { - TValue n; - const TValue *o = index2adr(L, idx); - return tonumber(o, &n); -} - - -LUA_API int lua_isstring (lua_State *L, int idx) { - int t = lua_type(L, idx); - return (t == LUA_TSTRING || t == LUA_TNUMBER); -} - - -LUA_API int lua_isuserdata (lua_State *L, int idx) { - const TValue *o = index2adr(L, idx); - return (ttisuserdata(o) || ttislightuserdata(o)); -} - - -LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { - StkId o1 = index2adr(L, index1); - StkId o2 = index2adr(L, index2); - return (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 - : luaO_rawequalObj(o1, o2); -} - - -LUA_API int lua_equal (lua_State *L, int index1, int index2) { - StkId o1, o2; - int i; - lua_lock(L); /* may call tag method */ - o1 = index2adr(L, index1); - o2 = index2adr(L, index2); - i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 : equalobj(L, o1, o2); - lua_unlock(L); - return i; -} - - -LUA_API int lua_lessthan (lua_State *L, int index1, int index2) { - StkId o1, o2; - int i; - lua_lock(L); /* may call tag method */ - o1 = index2adr(L, index1); - o2 = index2adr(L, index2); - i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 - : luaV_lessthan(L, o1, o2); - lua_unlock(L); - return i; -} - - - -LUA_API lua_Number lua_tonumber (lua_State *L, int idx) { - TValue n; - const TValue *o = index2adr(L, idx); - if (tonumber(o, &n)) - return nvalue(o); - else - return 0; -} - - -LUA_API lua_Integer lua_tointeger (lua_State *L, int idx) { - TValue n; - const TValue *o = index2adr(L, idx); - if (tonumber(o, &n)) { - lua_Integer res; - lua_Number num = nvalue(o); - lua_number2integer(res, num); - return res; - } - else - return 0; -} - - -LUA_API int lua_toboolean (lua_State *L, int idx) { - const TValue *o = index2adr(L, idx); - return !l_isfalse(o); -} - - -LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { - StkId o = index2adr(L, idx); - if (!ttisstring(o)) { - lua_lock(L); /* `luaV_tostring' may create a new string */ - if (!luaV_tostring(L, o)) { /* conversion failed? */ - if (len != NULL) *len = 0; - lua_unlock(L); - return NULL; - } - luaC_checkGC(L); - o = index2adr(L, idx); /* previous call may reallocate the stack */ - lua_unlock(L); - } - if (len != NULL) *len = tsvalue(o)->len; - return svalue(o); -} - - -LUA_API size_t lua_objlen (lua_State *L, int idx) { - StkId o = index2adr(L, idx); - switch (ttype(o)) { - case LUA_TSTRING: return tsvalue(o)->len; - case LUA_TUSERDATA: return uvalue(o)->len; - case LUA_TTABLE: return luaH_getn(hvalue(o)); - case LUA_TNUMBER: { - size_t l; - lua_lock(L); /* `luaV_tostring' may create a new string */ - l = (luaV_tostring(L, o) ? tsvalue(o)->len : 0); - lua_unlock(L); - return l; - } - default: return 0; - } -} - - -LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { - StkId o = index2adr(L, idx); - return (!iscfunction(o)) ? NULL : clvalue(o)->c.f; -} - - -LUA_API void *lua_touserdata (lua_State *L, int idx) { - StkId o = index2adr(L, idx); - switch (ttype(o)) { - case LUA_TUSERDATA: return (rawuvalue(o) + 1); - case LUA_TLIGHTUSERDATA: return pvalue(o); - default: return NULL; - } -} - - -LUA_API lua_State *lua_tothread (lua_State *L, int idx) { - StkId o = index2adr(L, idx); - return (!ttisthread(o)) ? NULL : thvalue(o); -} - - -LUA_API const void *lua_topointer (lua_State *L, int idx) { - StkId o = index2adr(L, idx); - switch (ttype(o)) { - case LUA_TTABLE: return hvalue(o); - case LUA_TFUNCTION: return clvalue(o); - case LUA_TTHREAD: return thvalue(o); - case LUA_TUSERDATA: - case LUA_TLIGHTUSERDATA: - return lua_touserdata(L, idx); - default: return NULL; - } -} - - - -/* -** push functions (C -> stack) -*/ - - -LUA_API void lua_pushnil (lua_State *L) { - lua_lock(L); - setnilvalue(L->top); - api_incr_top(L); - lua_unlock(L); -} - - -LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { - lua_lock(L); - setnvalue(L->top, n); - api_incr_top(L); - lua_unlock(L); -} - - -LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { - lua_lock(L); - setnvalue(L->top, cast_num(n)); - api_incr_top(L); - lua_unlock(L); -} - - -LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) { - lua_lock(L); - luaC_checkGC(L); - setsvalue2s(L, L->top, luaS_newlstr(L, s, len)); - api_incr_top(L); - lua_unlock(L); -} - - -LUA_API void lua_pushstring (lua_State *L, const char *s) { - if (s == NULL) - lua_pushnil(L); - else - lua_pushlstring(L, s, strlen(s)); -} - - -LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, - va_list argp) { - const char *ret; - lua_lock(L); - luaC_checkGC(L); - ret = luaO_pushvfstring(L, fmt, argp); - lua_unlock(L); - return ret; -} - - -LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { - const char *ret; - va_list argp; - lua_lock(L); - luaC_checkGC(L); - va_start(argp, fmt); - ret = luaO_pushvfstring(L, fmt, argp); - va_end(argp); - lua_unlock(L); - return ret; -} - - -LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { - Closure *cl; - lua_lock(L); - luaC_checkGC(L); - api_checknelems(L, n); - cl = luaF_newCclosure(L, n, getcurrenv(L)); - cl->c.f = fn; - L->top -= n; - while (n--) - setobj2n(L, &cl->c.upvalue[n], L->top+n); - setclvalue(L, L->top, cl); - lua_assert(iswhite(obj2gco(cl))); - api_incr_top(L); - lua_unlock(L); -} - - -LUA_API void lua_pushboolean (lua_State *L, int b) { - lua_lock(L); - setbvalue(L->top, (b != 0)); /* ensure that true is 1 */ - api_incr_top(L); - lua_unlock(L); -} - - -LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { - lua_lock(L); - setpvalue(L->top, p); - api_incr_top(L); - lua_unlock(L); -} - - -LUA_API int lua_pushthread (lua_State *L) { - lua_lock(L); - setthvalue(L, L->top, L); - api_incr_top(L); - lua_unlock(L); - return (G(L)->mainthread == L); -} - - - -/* -** get functions (Lua -> stack) -*/ - - -LUA_API void lua_gettable (lua_State *L, int idx) { - StkId t; - lua_lock(L); - t = index2adr(L, idx); - api_checkvalidindex(L, t); - luaV_gettable(L, t, L->top - 1, L->top - 1); - lua_unlock(L); -} - - -LUA_API void lua_getfield (lua_State *L, int idx, const char *k) { - StkId t; - TValue key; - lua_lock(L); - t = index2adr(L, idx); - api_checkvalidindex(L, t); - setsvalue(L, &key, luaS_new(L, k)); - luaV_gettable(L, t, &key, L->top); - api_incr_top(L); - lua_unlock(L); -} - - -LUA_API void lua_rawget (lua_State *L, int idx) { - StkId t; - lua_lock(L); - t = index2adr(L, idx); - api_check(L, ttistable(t)); - setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1)); - lua_unlock(L); -} - - -LUA_API void lua_rawgeti (lua_State *L, int idx, int n) { - StkId o; - lua_lock(L); - o = index2adr(L, idx); - api_check(L, ttistable(o)); - setobj2s(L, L->top, luaH_getnum(hvalue(o), n)); - api_incr_top(L); - lua_unlock(L); -} - - -LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { - lua_lock(L); - luaC_checkGC(L); - sethvalue(L, L->top, luaH_new(L, narray, nrec)); - api_incr_top(L); - lua_unlock(L); -} - - -LUA_API int lua_getmetatable (lua_State *L, int objindex) { - const TValue *obj; - Table *mt = NULL; - int res; - lua_lock(L); - obj = index2adr(L, objindex); - switch (ttype(obj)) { - case LUA_TTABLE: - mt = hvalue(obj)->metatable; - break; - case LUA_TUSERDATA: - mt = uvalue(obj)->metatable; - break; - default: - mt = G(L)->mt[ttype(obj)]; - break; - } - if (mt == NULL) - res = 0; - else { - sethvalue(L, L->top, mt); - api_incr_top(L); - res = 1; - } - lua_unlock(L); - return res; -} - - -LUA_API void lua_getfenv (lua_State *L, int idx) { - StkId o; - lua_lock(L); - o = index2adr(L, idx); - api_checkvalidindex(L, o); - switch (ttype(o)) { - case LUA_TFUNCTION: - sethvalue(L, L->top, clvalue(o)->c.env); - break; - case LUA_TUSERDATA: - sethvalue(L, L->top, uvalue(o)->env); - break; - case LUA_TTHREAD: - setobj2s(L, L->top, gt(thvalue(o))); - break; - default: - setnilvalue(L->top); - break; - } - api_incr_top(L); - lua_unlock(L); -} - - -/* -** set functions (stack -> Lua) -*/ - - -LUA_API void lua_settable (lua_State *L, int idx) { - StkId t; - lua_lock(L); - api_checknelems(L, 2); - t = index2adr(L, idx); - api_checkvalidindex(L, t); - luaV_settable(L, t, L->top - 2, L->top - 1); - L->top -= 2; /* pop index and value */ - lua_unlock(L); -} - - -LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { - StkId t; - TValue key; - lua_lock(L); - api_checknelems(L, 1); - t = index2adr(L, idx); - api_checkvalidindex(L, t); - setsvalue(L, &key, luaS_new(L, k)); - luaV_settable(L, t, &key, L->top - 1); - L->top--; /* pop value */ - lua_unlock(L); -} - - -LUA_API void lua_rawset (lua_State *L, int idx) { - StkId t; - lua_lock(L); - api_checknelems(L, 2); - t = index2adr(L, idx); - api_check(L, ttistable(t)); - setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1); - luaC_barriert(L, hvalue(t), L->top-1); - L->top -= 2; - lua_unlock(L); -} - - -LUA_API void lua_rawseti (lua_State *L, int idx, int n) { - StkId o; - lua_lock(L); - api_checknelems(L, 1); - o = index2adr(L, idx); - api_check(L, ttistable(o)); - setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top-1); - luaC_barriert(L, hvalue(o), L->top-1); - L->top--; - lua_unlock(L); -} - - -LUA_API int lua_setmetatable (lua_State *L, int objindex) { - TValue *obj; - Table *mt; - lua_lock(L); - api_checknelems(L, 1); - obj = index2adr(L, objindex); - api_checkvalidindex(L, obj); - if (ttisnil(L->top - 1)) - mt = NULL; - else { - api_check(L, ttistable(L->top - 1)); - mt = hvalue(L->top - 1); - } - switch (ttype(obj)) { - case LUA_TTABLE: { - hvalue(obj)->metatable = mt; - if (mt) - luaC_objbarriert(L, hvalue(obj), mt); - break; - } - case LUA_TUSERDATA: { - uvalue(obj)->metatable = mt; - if (mt) - luaC_objbarrier(L, rawuvalue(obj), mt); - break; - } - default: { - G(L)->mt[ttype(obj)] = mt; - break; - } - } - L->top--; - lua_unlock(L); - return 1; -} - - -LUA_API int lua_setfenv (lua_State *L, int idx) { - StkId o; - int res = 1; - lua_lock(L); - api_checknelems(L, 1); - o = index2adr(L, idx); - api_checkvalidindex(L, o); - api_check(L, ttistable(L->top - 1)); - switch (ttype(o)) { - case LUA_TFUNCTION: - clvalue(o)->c.env = hvalue(L->top - 1); - break; - case LUA_TUSERDATA: - uvalue(o)->env = hvalue(L->top - 1); - break; - case LUA_TTHREAD: - sethvalue(L, gt(thvalue(o)), hvalue(L->top - 1)); - break; - default: - res = 0; - break; - } - if (res) luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1)); - L->top--; - lua_unlock(L); - return res; -} - - -/* -** `load' and `call' functions (run Lua code) -*/ - - -#define adjustresults(L,nres) \ - { if (nres == LUA_MULTRET && L->top >= L->ci->top) L->ci->top = L->top; } - - -#define checkresults(L,na,nr) \ - api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na))) - - -LUA_API void lua_call (lua_State *L, int nargs, int nresults) { - StkId func; - lua_lock(L); - api_checknelems(L, nargs+1); - checkresults(L, nargs, nresults); - func = L->top - (nargs+1); - luaD_call(L, func, nresults); - adjustresults(L, nresults); - lua_unlock(L); -} - - - -/* -** Execute a protected call. -*/ -struct CallS { /* data to `f_call' */ - StkId func; - int nresults; -}; - - -static void f_call (lua_State *L, void *ud) { - struct CallS *c = cast(struct CallS *, ud); - luaD_call(L, c->func, c->nresults); -} - - - -LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) { - struct CallS c; - int status; - ptrdiff_t func; - lua_lock(L); - api_checknelems(L, nargs+1); - checkresults(L, nargs, nresults); - if (errfunc == 0) - func = 0; - else { - StkId o = index2adr(L, errfunc); - api_checkvalidindex(L, o); - func = savestack(L, o); - } - c.func = L->top - (nargs+1); /* function to be called */ - c.nresults = nresults; - status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); - adjustresults(L, nresults); - lua_unlock(L); - return status; -} - - -/* -** Execute a protected C call. -*/ -struct CCallS { /* data to `f_Ccall' */ - lua_CFunction func; - void *ud; -}; - - -static void f_Ccall (lua_State *L, void *ud) { - struct CCallS *c = cast(struct CCallS *, ud); - Closure *cl; - cl = luaF_newCclosure(L, 0, getcurrenv(L)); - cl->c.f = c->func; - setclvalue(L, L->top, cl); /* push function */ - api_incr_top(L); - setpvalue(L->top, c->ud); /* push only argument */ - api_incr_top(L); - luaD_call(L, L->top - 2, 0); -} - - -LUA_API int lua_cpcall (lua_State *L, lua_CFunction func, void *ud) { - struct CCallS c; - int status; - lua_lock(L); - c.func = func; - c.ud = ud; - status = luaD_pcall(L, f_Ccall, &c, savestack(L, L->top), 0); - lua_unlock(L); - return status; -} - - -LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, - const char *chunkname) { - ZIO z; - int status; - lua_lock(L); - if (!chunkname) chunkname = "?"; - luaZ_init(L, &z, reader, data); - status = luaD_protectedparser(L, &z, chunkname); - lua_unlock(L); - return status; -} - - -LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data) { - int status; - TValue *o; - lua_lock(L); - api_checknelems(L, 1); - o = L->top - 1; - if (isLfunction(o)) - status = luaU_dump(L, clvalue(o)->l.p, writer, data, 0); - else - status = 1; - lua_unlock(L); - return status; -} - - -LUA_API int lua_status (lua_State *L) { - return L->status; -} - - -/* -** Garbage-collection function -*/ - -LUA_API int lua_gc (lua_State *L, int what, int data) { - int res = 0; - global_State *g; - lua_lock(L); - g = G(L); - switch (what) { - case LUA_GCSTOP: { - g->GCthreshold = MAX_LUMEM; - break; - } - case LUA_GCRESTART: { - g->GCthreshold = g->totalbytes; - break; - } - case LUA_GCCOLLECT: { - luaC_fullgc(L); - break; - } - case LUA_GCCOUNT: { - /* GC values are expressed in Kbytes: #bytes/2^10 */ - res = cast_int(g->totalbytes >> 10); - break; - } - case LUA_GCCOUNTB: { - res = cast_int(g->totalbytes & 0x3ff); - break; - } - case LUA_GCSTEP: { - lu_mem a = (cast(lu_mem, data) << 10); - if (a <= g->totalbytes) - g->GCthreshold = g->totalbytes - a; - else - g->GCthreshold = 0; - while (g->GCthreshold <= g->totalbytes) { - luaC_step(L); - if (g->gcstate == GCSpause) { /* end of cycle? */ - res = 1; /* signal it */ - break; - } - } - break; - } - case LUA_GCSETPAUSE: { - res = g->gcpause; - g->gcpause = data; - break; - } - case LUA_GCSETSTEPMUL: { - res = g->gcstepmul; - g->gcstepmul = data; - break; - } - default: res = -1; /* invalid option */ - } - lua_unlock(L); - return res; -} - - - -/* -** miscellaneous functions -*/ - - -LUA_API int lua_error (lua_State *L) { - lua_lock(L); - api_checknelems(L, 1); - luaG_errormsg(L); - lua_unlock(L); - return 0; /* to avoid warnings */ -} - - -LUA_API int lua_next (lua_State *L, int idx) { - StkId t; - int more; - lua_lock(L); - t = index2adr(L, idx); - api_check(L, ttistable(t)); - more = luaH_next(L, hvalue(t), L->top - 1); - if (more) { - api_incr_top(L); - } - else /* no more elements */ - L->top -= 1; /* remove key */ - lua_unlock(L); - return more; -} - - -LUA_API void lua_concat (lua_State *L, int n) { - lua_lock(L); - api_checknelems(L, n); - if (n >= 2) { - luaC_checkGC(L); - luaV_concat(L, n, cast_int(L->top - L->base) - 1); - L->top -= (n-1); - } - else if (n == 0) { /* push empty string */ - setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); - api_incr_top(L); - } - /* else n == 1; nothing to do */ - lua_unlock(L); -} - - -LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) { - lua_Alloc f; - lua_lock(L); - if (ud) *ud = G(L)->ud; - f = G(L)->frealloc; - lua_unlock(L); - return f; -} - - -LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { - lua_lock(L); - G(L)->ud = ud; - G(L)->frealloc = f; - lua_unlock(L); -} - - -LUA_API void *lua_newuserdata (lua_State *L, size_t size) { - Udata *u; - lua_lock(L); - luaC_checkGC(L); - u = luaS_newudata(L, size, getcurrenv(L)); - setuvalue(L, L->top, u); - api_incr_top(L); - lua_unlock(L); - return u + 1; -} - - - - -static const char *aux_upvalue (StkId fi, int n, TValue **val) { - Closure *f; - if (!ttisfunction(fi)) return NULL; - f = clvalue(fi); - if (f->c.isC) { - if (!(1 <= n && n <= f->c.nupvalues)) return NULL; - *val = &f->c.upvalue[n-1]; - return ""; - } - else { - Proto *p = f->l.p; - if (!(1 <= n && n <= p->sizeupvalues)) return NULL; - *val = f->l.upvals[n-1]->v; - return getstr(p->upvalues[n-1]); - } -} - - -LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { - const char *name; - TValue *val; - lua_lock(L); - name = aux_upvalue(index2adr(L, funcindex), n, &val); - if (name) { - setobj2s(L, L->top, val); - api_incr_top(L); - } - lua_unlock(L); - return name; -} - - -LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { - const char *name; - TValue *val; - StkId fi; - lua_lock(L); - fi = index2adr(L, funcindex); - api_checknelems(L, 1); - name = aux_upvalue(fi, n, &val); - if (name) { - L->top--; - setobj(L, val, L->top); - luaC_barrier(L, clvalue(fi), L->top); - } - lua_unlock(L); - return name; -} - diff --git a/libraries/lua/lapi.h b/libraries/lua/lapi.h deleted file mode 100644 index 2c3fab244..000000000 --- a/libraries/lua/lapi.h +++ /dev/null @@ -1,16 +0,0 @@ -/* -** $Id: lapi.h,v 2.2.1.1 2007/12/27 13:02:25 roberto Exp $ -** Auxiliary functions from Lua API -** See Copyright Notice in lua.h -*/ - -#ifndef lapi_h -#define lapi_h - - -#include "lobject.h" - - -LUAI_FUNC void luaA_pushobject (lua_State *L, const TValue *o); - -#endif diff --git a/libraries/lua/lauxlib.c b/libraries/lua/lauxlib.c deleted file mode 100644 index 9664072cd..000000000 --- a/libraries/lua/lauxlib.c +++ /dev/null @@ -1,653 +0,0 @@ -/* -** $Id: lauxlib.c,v 1.159.1.3 2008/01/21 13:20:51 roberto Exp $ -** Auxiliary functions for building Lua libraries -** See Copyright Notice in lua.h -*/ - - -#include -#include -#include -#include -#include -#include - - -/* This file uses only the official API of Lua. -** Any function declared here could be written as an application function. -*/ - -#define lauxlib_c -#define LUA_LIB - -#include "lua.h" - -#include "lauxlib.h" - - -#define FREELIST_REF 0 /* free list of references */ - - -/* convert a stack index to positive */ -#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \ - lua_gettop(L) + (i) + 1) - - -/* -** {====================================================== -** Error-report functions -** ======================================================= -*/ - - -LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) { - lua_Debug ar; - if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ - return luaL_error(L, "bad argument #%d (%s)", narg, extramsg); - lua_getinfo(L, "n", &ar); - if (strcmp(ar.namewhat, "method") == 0) { - narg--; /* do not count `self' */ - if (narg == 0) /* error is in the self argument itself? */ - return luaL_error(L, "calling " LUA_QS " on bad self (%s)", - ar.name, extramsg); - } - if (ar.name == NULL) - ar.name = "?"; - return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)", - narg, ar.name, extramsg); -} - - -LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) { - const char *msg = lua_pushfstring(L, "%s expected, got %s", - tname, luaL_typename(L, narg)); - return luaL_argerror(L, narg, msg); -} - - -static void tag_error (lua_State *L, int narg, int tag) { - luaL_typerror(L, narg, lua_typename(L, tag)); -} - - -LUALIB_API void luaL_where (lua_State *L, int level) { - lua_Debug ar; - if (lua_getstack(L, level, &ar)) { /* check function at level */ - lua_getinfo(L, "Sl", &ar); /* get info about it */ - if (ar.currentline > 0) { /* is there info? */ - lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); - return; - } - } - lua_pushliteral(L, ""); /* else, no information available... */ -} - - -LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { - va_list argp; - va_start(argp, fmt); - luaL_where(L, 1); - lua_pushvfstring(L, fmt, argp); - va_end(argp); - lua_concat(L, 2); - return lua_error(L); -} - -/* }====================================================== */ - - -LUALIB_API int luaL_checkoption (lua_State *L, int narg, const char *def, - const char *const lst[]) { - const char *name = (def) ? luaL_optstring(L, narg, def) : - luaL_checkstring(L, narg); - int i; - for (i=0; lst[i]; i++) - if (strcmp(lst[i], name) == 0) - return i; - return luaL_argerror(L, narg, - lua_pushfstring(L, "invalid option " LUA_QS, name)); -} - - -LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { - lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */ - if (!lua_isnil(L, -1)) /* name already in use? */ - return 0; /* leave previous value on top, but return 0 */ - lua_pop(L, 1); - lua_newtable(L); /* create metatable */ - lua_pushvalue(L, -1); - lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ - return 1; -} - - -LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { - void *p = lua_touserdata(L, ud); - if (p != NULL) { /* value is a userdata? */ - if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ - lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */ - if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ - lua_pop(L, 2); /* remove both metatables */ - return p; - } - } - } - luaL_typerror(L, ud, tname); /* else error */ - return NULL; /* to avoid warnings */ -} - - -LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) { - if (!lua_checkstack(L, space)) - luaL_error(L, "stack overflow (%s)", mes); -} - - -LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) { - if (lua_type(L, narg) != t) - tag_error(L, narg, t); -} - - -LUALIB_API void luaL_checkany (lua_State *L, int narg) { - if (lua_type(L, narg) == LUA_TNONE) - luaL_argerror(L, narg, "value expected"); -} - - -LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) { - const char *s = lua_tolstring(L, narg, len); - if (!s) tag_error(L, narg, LUA_TSTRING); - return s; -} - - -LUALIB_API const char *luaL_optlstring (lua_State *L, int narg, - const char *def, size_t *len) { - if (lua_isnoneornil(L, narg)) { - if (len) - *len = (def ? strlen(def) : 0); - return def; - } - else return luaL_checklstring(L, narg, len); -} - - -LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) { - lua_Number d = lua_tonumber(L, narg); - if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ - tag_error(L, narg, LUA_TNUMBER); - return d; -} - - -LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) { - return luaL_opt(L, luaL_checknumber, narg, def); -} - - -LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int narg) { - lua_Integer d = lua_tointeger(L, narg); - if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ - tag_error(L, narg, LUA_TNUMBER); - return d; -} - - -LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int narg, - lua_Integer def) { - return luaL_opt(L, luaL_checkinteger, narg, def); -} - - -LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { - if (!lua_getmetatable(L, obj)) /* no metatable? */ - return 0; - lua_pushstring(L, event); - lua_rawget(L, -2); - if (lua_isnil(L, -1)) { - lua_pop(L, 2); /* remove metatable and metafield */ - return 0; - } - else { - lua_remove(L, -2); /* remove only metatable */ - return 1; - } -} - - -LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { - obj = abs_index(L, obj); - if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ - return 0; - lua_pushvalue(L, obj); - lua_call(L, 1, 1); - return 1; -} - - -LUALIB_API void (luaL_register) (lua_State *L, const char *libname, - const luaL_Reg *l) { - luaI_openlib(L, libname, l, 0); -} - - -static int libsize (const luaL_Reg *l) { - int size = 0; - for (; l->name; l++) size++; - return size; -} - - -LUALIB_API void luaI_openlib (lua_State *L, const char *libname, - const luaL_Reg *l, int nup) { - if (libname) { - int size = libsize(l); - /* check whether lib already exists */ - luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); - lua_getfield(L, -1, libname); /* get _LOADED[libname] */ - if (!lua_istable(L, -1)) { /* not found? */ - lua_pop(L, 1); /* remove previous result */ - /* try global variable (and create one if it does not exist) */ - if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL) - luaL_error(L, "name conflict for module " LUA_QS, libname); - lua_pushvalue(L, -1); - lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */ - } - lua_remove(L, -2); /* remove _LOADED table */ - lua_insert(L, -(nup+1)); /* move library table to below upvalues */ - } - for (; l->name; l++) { - int i; - for (i=0; ifunc, nup); - lua_setfield(L, -(nup+2), l->name); - } - lua_pop(L, nup); /* remove upvalues */ -} - - - -/* -** {====================================================== -** getn-setn: size for arrays -** ======================================================= -*/ - -#if defined(LUA_COMPAT_GETN) - -static int checkint (lua_State *L, int topop) { - int n = (lua_type(L, -1) == LUA_TNUMBER) ? lua_tointeger(L, -1) : -1; - lua_pop(L, topop); - return n; -} - - -static void getsizes (lua_State *L) { - lua_getfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); - if (lua_isnil(L, -1)) { /* no `size' table? */ - lua_pop(L, 1); /* remove nil */ - lua_newtable(L); /* create it */ - lua_pushvalue(L, -1); /* `size' will be its own metatable */ - lua_setmetatable(L, -2); - lua_pushliteral(L, "kv"); - lua_setfield(L, -2, "__mode"); /* metatable(N).__mode = "kv" */ - lua_pushvalue(L, -1); - lua_setfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); /* store in register */ - } -} - - -LUALIB_API void luaL_setn (lua_State *L, int t, int n) { - t = abs_index(L, t); - lua_pushliteral(L, "n"); - lua_rawget(L, t); - if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */ - lua_pushliteral(L, "n"); /* use it */ - lua_pushinteger(L, n); - lua_rawset(L, t); - } - else { /* use `sizes' */ - getsizes(L); - lua_pushvalue(L, t); - lua_pushinteger(L, n); - lua_rawset(L, -3); /* sizes[t] = n */ - lua_pop(L, 1); /* remove `sizes' */ - } -} - - -LUALIB_API int luaL_getn (lua_State *L, int t) { - int n; - t = abs_index(L, t); - lua_pushliteral(L, "n"); /* try t.n */ - lua_rawget(L, t); - if ((n = checkint(L, 1)) >= 0) return n; - getsizes(L); /* else try sizes[t] */ - lua_pushvalue(L, t); - lua_rawget(L, -2); - if ((n = checkint(L, 2)) >= 0) return n; - return (int)lua_objlen(L, t); -} - -#endif - -/* }====================================================== */ - - - -LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p, - const char *r) { - const char *wild; - size_t l = strlen(p); - luaL_Buffer b; - luaL_buffinit(L, &b); - while ((wild = strstr(s, p)) != NULL) { - luaL_addlstring(&b, s, wild - s); /* push prefix */ - luaL_addstring(&b, r); /* push replacement in place of pattern */ - s = wild + l; /* continue after `p' */ - } - luaL_addstring(&b, s); /* push last suffix */ - luaL_pushresult(&b); - return lua_tostring(L, -1); -} - - -LUALIB_API const char *luaL_findtable (lua_State *L, int idx, - const char *fname, int szhint) { - const char *e; - lua_pushvalue(L, idx); - do { - e = strchr(fname, '.'); - if (e == NULL) e = fname + strlen(fname); - lua_pushlstring(L, fname, e - fname); - lua_rawget(L, -2); - if (lua_isnil(L, -1)) { /* no such field? */ - lua_pop(L, 1); /* remove this nil */ - lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */ - lua_pushlstring(L, fname, e - fname); - lua_pushvalue(L, -2); - lua_settable(L, -4); /* set new table into field */ - } - else if (!lua_istable(L, -1)) { /* field has a non-table value? */ - lua_pop(L, 2); /* remove table and value */ - return fname; /* return problematic part of the name */ - } - lua_remove(L, -2); /* remove previous table */ - fname = e + 1; - } while (*e == '.'); - return NULL; -} - - - -/* -** {====================================================== -** Generic Buffer manipulation -** ======================================================= -*/ - - -#define bufflen(B) ((B)->p - (B)->buffer) -#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) - -#define LIMIT (LUA_MINSTACK/2) - - -static int emptybuffer (luaL_Buffer *B) { - size_t l = bufflen(B); - if (l == 0) return 0; /* put nothing on stack */ - else { - lua_pushlstring(B->L, B->buffer, l); - B->p = B->buffer; - B->lvl++; - return 1; - } -} - - -static void adjuststack (luaL_Buffer *B) { - if (B->lvl > 1) { - lua_State *L = B->L; - int toget = 1; /* number of levels to concat */ - size_t toplen = lua_strlen(L, -1); - do { - size_t l = lua_strlen(L, -(toget+1)); - if (B->lvl - toget + 1 >= LIMIT || toplen > l) { - toplen += l; - toget++; - } - else break; - } while (toget < B->lvl); - lua_concat(L, toget); - B->lvl = B->lvl - toget + 1; - } -} - - -LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { - if (emptybuffer(B)) - adjuststack(B); - return B->buffer; -} - - -LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { - while (l--) - luaL_addchar(B, *s++); -} - - -LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { - luaL_addlstring(B, s, strlen(s)); -} - - -LUALIB_API void luaL_pushresult (luaL_Buffer *B) { - emptybuffer(B); - lua_concat(B->L, B->lvl); - B->lvl = 1; -} - - -LUALIB_API void luaL_addvalue (luaL_Buffer *B) { - lua_State *L = B->L; - size_t vl; - const char *s = lua_tolstring(L, -1, &vl); - if (vl <= bufffree(B)) { /* fit into buffer? */ - memcpy(B->p, s, vl); /* put it there */ - B->p += vl; - lua_pop(L, 1); /* remove from stack */ - } - else { - if (emptybuffer(B)) - lua_insert(L, -2); /* put buffer before new value */ - B->lvl++; /* add new value into B stack */ - adjuststack(B); - } -} - - -LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { - B->L = L; - B->p = B->buffer; - B->lvl = 0; -} - -/* }====================================================== */ - - -LUALIB_API int luaL_ref (lua_State *L, int t) { - int ref; - t = abs_index(L, t); - if (lua_isnil(L, -1)) { - lua_pop(L, 1); /* remove from stack */ - return LUA_REFNIL; /* `nil' has a unique fixed reference */ - } - lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ - ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */ - lua_pop(L, 1); /* remove it from stack */ - if (ref != 0) { /* any free element? */ - lua_rawgeti(L, t, ref); /* remove it from list */ - lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ - } - else { /* no free elements */ - ref = (int)lua_objlen(L, t); - ref++; /* create new reference */ - } - lua_rawseti(L, t, ref); - return ref; -} - - -LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { - if (ref >= 0) { - t = abs_index(L, t); - lua_rawgeti(L, t, FREELIST_REF); - lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ - lua_pushinteger(L, ref); - lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ - } -} - - - -/* -** {====================================================== -** Load functions -** ======================================================= -*/ - -typedef struct LoadF { - int extraline; - FILE *f; - char buff[LUAL_BUFFERSIZE]; -} LoadF; - - -static const char *getF (lua_State *L, void *ud, size_t *size) { - LoadF *lf = (LoadF *)ud; - (void)L; - if (lf->extraline) { - lf->extraline = 0; - *size = 1; - return "\n"; - } - if (feof(lf->f)) return NULL; - *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); - return (*size > 0) ? lf->buff : NULL; -} - - -static int errfile (lua_State *L, const char *what, int fnameindex) { - const char *serr = strerror(errno); - const char *filename = lua_tostring(L, fnameindex) + 1; - lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); - lua_remove(L, fnameindex); - return LUA_ERRFILE; -} - - -LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { - LoadF lf; - int status, readstatus; - int c; - int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ - lf.extraline = 0; - if (filename == NULL) { - lua_pushliteral(L, "=stdin"); - lf.f = stdin; - } - else { - lua_pushfstring(L, "@%s", filename); - lf.f = fopen(filename, "r"); - if (lf.f == NULL) return errfile(L, "open", fnameindex); - } - c = getc(lf.f); - if (c == '#') { /* Unix exec. file? */ - lf.extraline = 1; - while ((c = getc(lf.f)) != EOF && c != '\n') ; /* skip first line */ - if (c == '\n') c = getc(lf.f); - } - if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ - lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ - if (lf.f == NULL) return errfile(L, "reopen", fnameindex); - /* skip eventual `#!...' */ - while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) - {} - - lf.extraline = 0; - } - ungetc(c, lf.f); - status = lua_load(L, getF, &lf, lua_tostring(L, -1)); - readstatus = ferror(lf.f); - if (filename) fclose(lf.f); /* close file (even in case of errors) */ - if (readstatus) { - lua_settop(L, fnameindex); /* ignore results from `lua_load' */ - return errfile(L, "read", fnameindex); - } - lua_remove(L, fnameindex); - return status; -} - - -typedef struct LoadS { - const char *s; - size_t size; -} LoadS; - - -static const char *getS (lua_State *L, void *ud, size_t *size) { - LoadS *ls = (LoadS *)ud; - (void)L; - if (ls->size == 0) return NULL; - *size = ls->size; - ls->size = 0; - return ls->s; -} - - -LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size, - const char *name) { - LoadS ls; - ls.s = buff; - ls.size = size; - return lua_load(L, getS, &ls, name); -} - - -LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s) { - return luaL_loadbuffer(L, s, strlen(s), s); -} - - - -/* }====================================================== */ - - -static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { - (void)ud; - (void)osize; - if (nsize == 0) { - free(ptr); - return NULL; - } - else - return realloc(ptr, nsize); -} - - -static int panic (lua_State *L) { - (void)L; /* to avoid warnings */ - fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", - lua_tostring(L, -1)); - return 0; -} - - -LUALIB_API lua_State *luaL_newstate (void) { - lua_State *L = lua_newstate(l_alloc, NULL); - if (L) lua_atpanic(L, &panic); - return L; -} diff --git a/libraries/lua/lauxlib.h b/libraries/lua/lauxlib.h deleted file mode 100644 index 34258235d..000000000 --- a/libraries/lua/lauxlib.h +++ /dev/null @@ -1,174 +0,0 @@ -/* -** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $ -** Auxiliary functions for building Lua libraries -** See Copyright Notice in lua.h -*/ - - -#ifndef lauxlib_h -#define lauxlib_h - - -#include -#include - -#include "lua.h" - - -#if defined(LUA_COMPAT_GETN) -LUALIB_API int (luaL_getn) (lua_State *L, int t); -LUALIB_API void (luaL_setn) (lua_State *L, int t, int n); -#else -#define luaL_getn(L,i) ((int)lua_objlen(L, i)) -#define luaL_setn(L,i,j) ((void)0) /* no op! */ -#endif - -#if defined(LUA_COMPAT_OPENLIB) -#define luaI_openlib luaL_openlib -#endif - - -/* extra error code for `luaL_load' */ -#define LUA_ERRFILE (LUA_ERRERR+1) - - -typedef struct luaL_Reg { - const char *name; - lua_CFunction func; -} luaL_Reg; - - - -LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname, - const luaL_Reg *l, int nup); -LUALIB_API void (luaL_register) (lua_State *L, const char *libname, - const luaL_Reg *l); -LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); -LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); -LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname); -LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); -LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, - size_t *l); -LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, - const char *def, size_t *l); -LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); -LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); - -LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); -LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, - lua_Integer def); - -LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); -LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); -LUALIB_API void (luaL_checkany) (lua_State *L, int narg); - -LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); -LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); - -LUALIB_API void (luaL_where) (lua_State *L, int lvl); -LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); - -LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, - const char *const lst[]); - -LUALIB_API int (luaL_ref) (lua_State *L, int t); -LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); - -LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); -LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, - const char *name); -LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); - -LUALIB_API lua_State *(luaL_newstate) (void); - - -LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, - const char *r); - -LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx, - const char *fname, int szhint); - - - - -/* -** =============================================================== -** some useful macros -** =============================================================== -*/ - -#define luaL_argcheck(L, cond,numarg,extramsg) \ - ((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) -#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) -#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) -#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) -#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) -#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) -#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) - -#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) - -#define luaL_dofile(L, fn) \ - (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) - -#define luaL_dostring(L, s) \ - (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) - -#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) - -#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) - -/* -** {====================================================== -** Generic Buffer manipulation -** ======================================================= -*/ - - - -typedef struct luaL_Buffer { - char *p; /* current position in buffer */ - int lvl; /* number of strings in the stack (level) */ - lua_State *L; - char buffer[LUAL_BUFFERSIZE]; -} luaL_Buffer; - -#define luaL_addchar(B,c) \ - ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \ - (*(B)->p++ = (char)(c))) - -/* compatibility only */ -#define luaL_putchar(B,c) luaL_addchar(B,c) - -#define luaL_addsize(B,n) ((B)->p += (n)) - -LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); -LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B); -LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); -LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); -LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); -LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); - - -/* }====================================================== */ - - -/* compatibility with ref system */ - -/* pre-defined references */ -#define LUA_NOREF (-2) -#define LUA_REFNIL (-1) - -#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \ - (lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0)) - -#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref)) - -#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref)) - - -#define luaL_reg luaL_Reg - -#endif - - diff --git a/libraries/lua/lbaselib.c b/libraries/lua/lbaselib.c deleted file mode 100644 index 2ab550bd4..000000000 --- a/libraries/lua/lbaselib.c +++ /dev/null @@ -1,653 +0,0 @@ -/* -** $Id: lbaselib.c,v 1.191.1.6 2008/02/14 16:46:22 roberto Exp $ -** Basic library -** See Copyright Notice in lua.h -*/ - - - -#include -#include -#include -#include - -#define lbaselib_c -#define LUA_LIB - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - - - -/* -** If your system does not support `stdout', you can just remove this function. -** If you need, you can define your own `print' function, following this -** model but changing `fputs' to put the strings at a proper place -** (a console window or a log file, for instance). -*/ -static int luaB_print (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - int i; - lua_getglobal(L, "tostring"); - for (i=1; i<=n; i++) { - const char *s; - lua_pushvalue(L, -1); /* function to be called */ - lua_pushvalue(L, i); /* value to print */ - lua_call(L, 1, 1); - s = lua_tostring(L, -1); /* get result */ - if (s == NULL) - return luaL_error(L, LUA_QL("tostring") " must return a string to " - LUA_QL("print")); - if (i>1) fputs("\t", stdout); - fputs(s, stdout); - lua_pop(L, 1); /* pop result */ - } - fputs("\n", stdout); - return 0; -} - - -static int luaB_tonumber (lua_State *L) { - int base = luaL_optint(L, 2, 10); - if (base == 10) { /* standard conversion */ - luaL_checkany(L, 1); - if (lua_isnumber(L, 1)) { - lua_pushnumber(L, lua_tonumber(L, 1)); - return 1; - } - } - else { - const char *s1 = luaL_checkstring(L, 1); - char *s2; - unsigned long n; - luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); - n = strtoul(s1, &s2, base); - if (s1 != s2) { /* at least one valid digit? */ - while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */ - if (*s2 == '\0') { /* no invalid trailing characters? */ - lua_pushnumber(L, (lua_Number)n); - return 1; - } - } - } - lua_pushnil(L); /* else not a number */ - return 1; -} - - -static int luaB_error (lua_State *L) { - int level = luaL_optint(L, 2, 1); - lua_settop(L, 1); - if (lua_isstring(L, 1) && level > 0) { /* add extra information? */ - luaL_where(L, level); - lua_pushvalue(L, 1); - lua_concat(L, 2); - } - return lua_error(L); -} - - -static int luaB_getmetatable (lua_State *L) { - luaL_checkany(L, 1); - if (!lua_getmetatable(L, 1)) { - lua_pushnil(L); - return 1; /* no metatable */ - } - luaL_getmetafield(L, 1, "__metatable"); - return 1; /* returns either __metatable field (if present) or metatable */ -} - - -static int luaB_setmetatable (lua_State *L) { - int t = lua_type(L, 2); - luaL_checktype(L, 1, LUA_TTABLE); - luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, - "nil or table expected"); - if (luaL_getmetafield(L, 1, "__metatable")) - luaL_error(L, "cannot change a protected metatable"); - lua_settop(L, 2); - lua_setmetatable(L, 1); - return 1; -} - - -static void getfunc (lua_State *L, int opt) { - if (lua_isfunction(L, 1)) lua_pushvalue(L, 1); - else { - lua_Debug ar; - int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1); - luaL_argcheck(L, level >= 0, 1, "level must be non-negative"); - if (lua_getstack(L, level, &ar) == 0) - luaL_argerror(L, 1, "invalid level"); - lua_getinfo(L, "f", &ar); - if (lua_isnil(L, -1)) - luaL_error(L, "no function environment for tail call at level %d", - level); - } -} - - -static int luaB_getfenv (lua_State *L) { - getfunc(L, 1); - if (lua_iscfunction(L, -1)) /* is a C function? */ - lua_pushvalue(L, LUA_GLOBALSINDEX); /* return the thread's global env. */ - else - lua_getfenv(L, -1); - return 1; -} - - -static int luaB_setfenv (lua_State *L) { - luaL_checktype(L, 2, LUA_TTABLE); - getfunc(L, 0); - lua_pushvalue(L, 2); - if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) { - /* change environment of current thread */ - lua_pushthread(L); - lua_insert(L, -2); - lua_setfenv(L, -2); - return 0; - } - else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0) - luaL_error(L, - LUA_QL("setfenv") " cannot change environment of given object"); - return 1; -} - - -static int luaB_rawequal (lua_State *L) { - luaL_checkany(L, 1); - luaL_checkany(L, 2); - lua_pushboolean(L, lua_rawequal(L, 1, 2)); - return 1; -} - - -static int luaB_rawget (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_checkany(L, 2); - lua_settop(L, 2); - lua_rawget(L, 1); - return 1; -} - -static int luaB_rawset (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_checkany(L, 2); - luaL_checkany(L, 3); - lua_settop(L, 3); - lua_rawset(L, 1); - return 1; -} - - -static int luaB_gcinfo (lua_State *L) { - lua_pushinteger(L, lua_getgccount(L)); - return 1; -} - - -static int luaB_collectgarbage (lua_State *L) { - static const char *const opts[] = {"stop", "restart", "collect", - "count", "step", "setpause", "setstepmul", NULL}; - static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, - LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL}; - int o = luaL_checkoption(L, 1, "collect", opts); - int ex = luaL_optint(L, 2, 0); - int res = lua_gc(L, optsnum[o], ex); - switch (optsnum[o]) { - case LUA_GCCOUNT: { - int b = lua_gc(L, LUA_GCCOUNTB, 0); - lua_pushnumber(L, res + ((lua_Number)b/1024)); - return 1; - } - case LUA_GCSTEP: { - lua_pushboolean(L, res); - return 1; - } - default: { - lua_pushnumber(L, res); - return 1; - } - } -} - - -static int luaB_type (lua_State *L) { - luaL_checkany(L, 1); - lua_pushstring(L, luaL_typename(L, 1)); - return 1; -} - - -static int luaB_next (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - lua_settop(L, 2); /* create a 2nd argument if there isn't one */ - if (lua_next(L, 1)) - return 2; - else { - lua_pushnil(L); - return 1; - } -} - - -static int luaB_pairs (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ - lua_pushvalue(L, 1); /* state, */ - lua_pushnil(L); /* and initial value */ - return 3; -} - - -static int ipairsaux (lua_State *L) { - int i = luaL_checkint(L, 2); - luaL_checktype(L, 1, LUA_TTABLE); - i++; /* next value */ - lua_pushinteger(L, i); - lua_rawgeti(L, 1, i); - return (lua_isnil(L, -1)) ? 0 : 2; -} - - -static int luaB_ipairs (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ - lua_pushvalue(L, 1); /* state, */ - lua_pushinteger(L, 0); /* and initial value */ - return 3; -} - - -static int load_aux (lua_State *L, int status) { - if (status == 0) /* OK? */ - return 1; - else { - lua_pushnil(L); - lua_insert(L, -2); /* put before error message */ - return 2; /* return nil plus error message */ - } -} - - -static int luaB_loadstring (lua_State *L) { - size_t l; - const char *s = luaL_checklstring(L, 1, &l); - const char *chunkname = luaL_optstring(L, 2, s); - return load_aux(L, luaL_loadbuffer(L, s, l, chunkname)); -} - - -static int luaB_loadfile (lua_State *L) { - const char *fname = luaL_optstring(L, 1, NULL); - return load_aux(L, luaL_loadfile(L, fname)); -} - - -/* -** Reader for generic `load' function: `lua_load' uses the -** stack for internal stuff, so the reader cannot change the -** stack top. Instead, it keeps its resulting string in a -** reserved slot inside the stack. -*/ -static const char *generic_reader (lua_State *L, void *ud, size_t *size) { - (void)ud; /* to avoid warnings */ - luaL_checkstack(L, 2, "too many nested functions"); - lua_pushvalue(L, 1); /* get function */ - lua_call(L, 0, 1); /* call it */ - if (lua_isnil(L, -1)) { - *size = 0; - return NULL; - } - else if (lua_isstring(L, -1)) { - lua_replace(L, 3); /* save string in a reserved stack slot */ - return lua_tolstring(L, 3, size); - } - else luaL_error(L, "reader function must return a string"); - return NULL; /* to avoid warnings */ -} - - -static int luaB_load (lua_State *L) { - int status; - const char *cname = luaL_optstring(L, 2, "=(load)"); - luaL_checktype(L, 1, LUA_TFUNCTION); - lua_settop(L, 3); /* function, eventual name, plus one reserved slot */ - status = lua_load(L, generic_reader, NULL, cname); - return load_aux(L, status); -} - - -static int luaB_dofile (lua_State *L) { - const char *fname = luaL_optstring(L, 1, NULL); - int n = lua_gettop(L); - if (luaL_loadfile(L, fname) != 0) lua_error(L); - lua_call(L, 0, LUA_MULTRET); - return lua_gettop(L) - n; -} - - -static int luaB_assert (lua_State *L) { - luaL_checkany(L, 1); - if (!lua_toboolean(L, 1)) - return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); - return lua_gettop(L); -} - - -static int luaB_unpack (lua_State *L) { - int i, e, n; - luaL_checktype(L, 1, LUA_TTABLE); - i = luaL_optint(L, 2, 1); - e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1)); - if (i > e) return 0; /* empty range */ - n = e - i + 1; /* number of elements */ - if (n <= 0 || !lua_checkstack(L, n)) /* n <= 0 means arith. overflow */ - return luaL_error(L, "too many results to unpack"); - lua_rawgeti(L, 1, i); /* push arg[i] (avoiding overflow problems) */ - while (i++ < e) /* push arg[i + 1...e] */ - lua_rawgeti(L, 1, i); - return n; -} - - -static int luaB_select (lua_State *L) { - int n = lua_gettop(L); - if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') { - lua_pushinteger(L, n-1); - return 1; - } - else { - int i = luaL_checkint(L, 1); - if (i < 0) i = n + i; - else if (i > n) i = n; - luaL_argcheck(L, 1 <= i, 1, "index out of range"); - return n - i; - } -} - - -static int luaB_pcall (lua_State *L) { - int status; - luaL_checkany(L, 1); - status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); - lua_pushboolean(L, (status == 0)); - lua_insert(L, 1); - return lua_gettop(L); /* return status + all results */ -} - - -static int luaB_xpcall (lua_State *L) { - int status; - luaL_checkany(L, 2); - lua_settop(L, 2); - lua_insert(L, 1); /* put error function under function to be called */ - status = lua_pcall(L, 0, LUA_MULTRET, 1); - lua_pushboolean(L, (status == 0)); - lua_replace(L, 1); - return lua_gettop(L); /* return status + all results */ -} - - -static int luaB_tostring (lua_State *L) { - luaL_checkany(L, 1); - if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ - return 1; /* use its value */ - switch (lua_type(L, 1)) { - case LUA_TNUMBER: - lua_pushstring(L, lua_tostring(L, 1)); - break; - case LUA_TSTRING: - lua_pushvalue(L, 1); - break; - case LUA_TBOOLEAN: - lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); - break; - case LUA_TNIL: - lua_pushliteral(L, "nil"); - break; - default: - lua_pushfstring(L, "%s: %p", luaL_typename(L, 1), lua_topointer(L, 1)); - break; - } - return 1; -} - - -static int luaB_newproxy (lua_State *L) { - lua_settop(L, 1); - lua_newuserdata(L, 0); /* create proxy */ - if (lua_toboolean(L, 1) == 0) - return 1; /* no metatable */ - else if (lua_isboolean(L, 1)) { - lua_newtable(L); /* create a new metatable `m' ... */ - lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */ - lua_pushboolean(L, 1); - lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */ - } - else { - int validproxy = 0; /* to check if weaktable[metatable(u)] == true */ - if (lua_getmetatable(L, 1)) { - lua_rawget(L, lua_upvalueindex(1)); - validproxy = lua_toboolean(L, -1); - lua_pop(L, 1); /* remove value */ - } - luaL_argcheck(L, validproxy, 1, "boolean or proxy expected"); - lua_getmetatable(L, 1); /* metatable is valid; get it */ - } - lua_setmetatable(L, 2); - return 1; -} - - -static const luaL_Reg base_funcs[] = { - {"assert", luaB_assert}, - {"collectgarbage", luaB_collectgarbage}, - {"dofile", luaB_dofile}, - {"error", luaB_error}, - {"gcinfo", luaB_gcinfo}, - {"getfenv", luaB_getfenv}, - {"getmetatable", luaB_getmetatable}, - {"loadfile", luaB_loadfile}, - {"load", luaB_load}, - {"loadstring", luaB_loadstring}, - {"next", luaB_next}, - {"pcall", luaB_pcall}, - {"print", luaB_print}, - {"rawequal", luaB_rawequal}, - {"rawget", luaB_rawget}, - {"rawset", luaB_rawset}, - {"select", luaB_select}, - {"setfenv", luaB_setfenv}, - {"setmetatable", luaB_setmetatable}, - {"tonumber", luaB_tonumber}, - {"tostring", luaB_tostring}, - {"type", luaB_type}, - {"unpack", luaB_unpack}, - {"xpcall", luaB_xpcall}, - {NULL, NULL} -}; - - -/* -** {====================================================== -** Coroutine library -** ======================================================= -*/ - -#define CO_RUN 0 /* running */ -#define CO_SUS 1 /* suspended */ -#define CO_NOR 2 /* 'normal' (it resumed another coroutine) */ -#define CO_DEAD 3 - -static const char *const statnames[] = - {"running", "suspended", "normal", "dead"}; - -static int costatus (lua_State *L, lua_State *co) { - if (L == co) return CO_RUN; - switch (lua_status(co)) { - case LUA_YIELD: - return CO_SUS; - case 0: { - lua_Debug ar; - if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ - return CO_NOR; /* it is running */ - else if (lua_gettop(co) == 0) - return CO_DEAD; - else - return CO_SUS; /* initial state */ - } - default: /* some error occured */ - return CO_DEAD; - } -} - - -static int luaB_costatus (lua_State *L) { - lua_State *co = lua_tothread(L, 1); - luaL_argcheck(L, co, 1, "coroutine expected"); - lua_pushstring(L, statnames[costatus(L, co)]); - return 1; -} - - -static int auxresume (lua_State *L, lua_State *co, int narg) { - int status = costatus(L, co); - if (!lua_checkstack(co, narg)) - luaL_error(L, "too many arguments to resume"); - if (status != CO_SUS) { - lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]); - return -1; /* error flag */ - } - lua_xmove(L, co, narg); - lua_setlevel(L, co); - status = lua_resume(co, narg); - if (status == 0 || status == LUA_YIELD) { - int nres = lua_gettop(co); - if (!lua_checkstack(L, nres + 1)) - luaL_error(L, "too many results to resume"); - lua_xmove(co, L, nres); /* move yielded values */ - return nres; - } - else { - lua_xmove(co, L, 1); /* move error message */ - return -1; /* error flag */ - } -} - - -static int luaB_coresume (lua_State *L) { - lua_State *co = lua_tothread(L, 1); - int r; - luaL_argcheck(L, co, 1, "coroutine expected"); - r = auxresume(L, co, lua_gettop(L) - 1); - if (r < 0) { - lua_pushboolean(L, 0); - lua_insert(L, -2); - return 2; /* return false + error message */ - } - else { - lua_pushboolean(L, 1); - lua_insert(L, -(r + 1)); - return r + 1; /* return true + `resume' returns */ - } -} - - -static int luaB_auxwrap (lua_State *L) { - lua_State *co = lua_tothread(L, lua_upvalueindex(1)); - int r = auxresume(L, co, lua_gettop(L)); - if (r < 0) { - if (lua_isstring(L, -1)) { /* error object is a string? */ - luaL_where(L, 1); /* add extra info */ - lua_insert(L, -2); - lua_concat(L, 2); - } - lua_error(L); /* propagate error */ - } - return r; -} - - -static int luaB_cocreate (lua_State *L) { - lua_State *NL = lua_newthread(L); - luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, - "Lua function expected"); - lua_pushvalue(L, 1); /* move function to top */ - lua_xmove(L, NL, 1); /* move function from L to NL */ - return 1; -} - - -static int luaB_cowrap (lua_State *L) { - luaB_cocreate(L); - lua_pushcclosure(L, luaB_auxwrap, 1); - return 1; -} - - -static int luaB_yield (lua_State *L) { - return lua_yield(L, lua_gettop(L)); -} - - -static int luaB_corunning (lua_State *L) { - if (lua_pushthread(L)) - lua_pushnil(L); /* main thread is not a coroutine */ - return 1; -} - - -static const luaL_Reg co_funcs[] = { - {"create", luaB_cocreate}, - {"resume", luaB_coresume}, - {"running", luaB_corunning}, - {"status", luaB_costatus}, - {"wrap", luaB_cowrap}, - {"yield", luaB_yield}, - {NULL, NULL} -}; - -/* }====================================================== */ - - -static void auxopen (lua_State *L, const char *name, - lua_CFunction f, lua_CFunction u) { - lua_pushcfunction(L, u); - lua_pushcclosure(L, f, 1); - lua_setfield(L, -2, name); -} - - -static void base_open (lua_State *L) { - /* set global _G */ - lua_pushvalue(L, LUA_GLOBALSINDEX); - lua_setglobal(L, "_G"); - /* open lib into global table */ - luaL_register(L, "_G", base_funcs); - lua_pushliteral(L, LUA_VERSION); - lua_setglobal(L, "_VERSION"); /* set global _VERSION */ - /* `ipairs' and `pairs' need auxiliary functions as upvalues */ - auxopen(L, "ipairs", luaB_ipairs, ipairsaux); - auxopen(L, "pairs", luaB_pairs, luaB_next); - /* `newproxy' needs a weaktable as upvalue */ - lua_createtable(L, 0, 1); /* new table `w' */ - lua_pushvalue(L, -1); /* `w' will be its own metatable */ - lua_setmetatable(L, -2); - lua_pushliteral(L, "kv"); - lua_setfield(L, -2, "__mode"); /* metatable(w).__mode = "kv" */ - lua_pushcclosure(L, luaB_newproxy, 1); - lua_setglobal(L, "newproxy"); /* set global `newproxy' */ -} - - -LUALIB_API int luaopen_base (lua_State *L) { - base_open(L); - luaL_register(L, LUA_COLIBNAME, co_funcs); - return 2; -} - diff --git a/libraries/lua/lcode.c b/libraries/lua/lcode.c deleted file mode 100644 index 679cb9cfd..000000000 --- a/libraries/lua/lcode.c +++ /dev/null @@ -1,831 +0,0 @@ -/* -** $Id: lcode.c,v 2.25.1.5 2011/01/31 14:53:16 roberto Exp $ -** Code generator for Lua -** See Copyright Notice in lua.h -*/ - - -#include - -#define lcode_c -#define LUA_CORE - -#include "lua.h" - -#include "lcode.h" -#include "ldebug.h" -#include "ldo.h" -#include "lgc.h" -#include "llex.h" -#include "lmem.h" -#include "lobject.h" -#include "lopcodes.h" -#include "lparser.h" -#include "ltable.h" - - -#define hasjumps(e) ((e)->t != (e)->f) - - -static int isnumeral(expdesc *e) { - return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP); -} - - -void luaK_nil (FuncState *fs, int from, int n) { - Instruction *previous; - if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ - if (fs->pc == 0) { /* function start? */ - if (from >= fs->nactvar) - return; /* positions are already clean */ - } - else { - previous = &fs->f->code[fs->pc-1]; - if (GET_OPCODE(*previous) == OP_LOADNIL) { - int pfrom = GETARG_A(*previous); - int pto = GETARG_B(*previous); - if (pfrom <= from && from <= pto+1) { /* can connect both? */ - if (from+n-1 > pto) - SETARG_B(*previous, from+n-1); - return; - } - } - } - } - luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0); /* else no optimization */ -} - - -int luaK_jump (FuncState *fs) { - int jpc = fs->jpc; /* save list of jumps to here */ - int j; - fs->jpc = NO_JUMP; - j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP); - luaK_concat(fs, &j, jpc); /* keep them on hold */ - return j; -} - - -void luaK_ret (FuncState *fs, int first, int nret) { - luaK_codeABC(fs, OP_RETURN, first, nret+1, 0); -} - - -static int condjump (FuncState *fs, OpCode op, int A, int B, int C) { - luaK_codeABC(fs, op, A, B, C); - return luaK_jump(fs); -} - - -static void fixjump (FuncState *fs, int pc, int dest) { - Instruction *jmp = &fs->f->code[pc]; - int offset = dest-(pc+1); - lua_assert(dest != NO_JUMP); - if (abs(offset) > MAXARG_sBx) - luaX_syntaxerror(fs->ls, "control structure too long"); - SETARG_sBx(*jmp, offset); -} - - -/* -** returns current `pc' and marks it as a jump target (to avoid wrong -** optimizations with consecutive instructions not in the same basic block). -*/ -int luaK_getlabel (FuncState *fs) { - fs->lasttarget = fs->pc; - return fs->pc; -} - - -static int getjump (FuncState *fs, int pc) { - int offset = GETARG_sBx(fs->f->code[pc]); - if (offset == NO_JUMP) /* point to itself represents end of list */ - return NO_JUMP; /* end of list */ - else - return (pc+1)+offset; /* turn offset into absolute position */ -} - - -static Instruction *getjumpcontrol (FuncState *fs, int pc) { - Instruction *pi = &fs->f->code[pc]; - if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) - return pi-1; - else - return pi; -} - - -/* -** check whether list has any jump that do not produce a value -** (or produce an inverted value) -*/ -static int need_value (FuncState *fs, int list) { - for (; list != NO_JUMP; list = getjump(fs, list)) { - Instruction i = *getjumpcontrol(fs, list); - if (GET_OPCODE(i) != OP_TESTSET) return 1; - } - return 0; /* not found */ -} - - -static int patchtestreg (FuncState *fs, int node, int reg) { - Instruction *i = getjumpcontrol(fs, node); - if (GET_OPCODE(*i) != OP_TESTSET) - return 0; /* cannot patch other instructions */ - if (reg != NO_REG && reg != GETARG_B(*i)) - SETARG_A(*i, reg); - else /* no register to put value or register already has the value */ - *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); - - return 1; -} - - -static void removevalues (FuncState *fs, int list) { - for (; list != NO_JUMP; list = getjump(fs, list)) - patchtestreg(fs, list, NO_REG); -} - - -static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, - int dtarget) { - while (list != NO_JUMP) { - int next = getjump(fs, list); - if (patchtestreg(fs, list, reg)) - fixjump(fs, list, vtarget); - else - fixjump(fs, list, dtarget); /* jump to default target */ - list = next; - } -} - - -static void dischargejpc (FuncState *fs) { - patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc); - fs->jpc = NO_JUMP; -} - - -void luaK_patchlist (FuncState *fs, int list, int target) { - if (target == fs->pc) - luaK_patchtohere(fs, list); - else { - lua_assert(target < fs->pc); - patchlistaux(fs, list, target, NO_REG, target); - } -} - - -void luaK_patchtohere (FuncState *fs, int list) { - luaK_getlabel(fs); - luaK_concat(fs, &fs->jpc, list); -} - - -void luaK_concat (FuncState *fs, int *l1, int l2) { - if (l2 == NO_JUMP) return; - else if (*l1 == NO_JUMP) - *l1 = l2; - else { - int list = *l1; - int next; - while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ - list = next; - fixjump(fs, list, l2); - } -} - - -void luaK_checkstack (FuncState *fs, int n) { - int newstack = fs->freereg + n; - if (newstack > fs->f->maxstacksize) { - if (newstack >= MAXSTACK) - luaX_syntaxerror(fs->ls, "function or expression too complex"); - fs->f->maxstacksize = cast_byte(newstack); - } -} - - -void luaK_reserveregs (FuncState *fs, int n) { - luaK_checkstack(fs, n); - fs->freereg += n; -} - - -static void freereg (FuncState *fs, int reg) { - if (!ISK(reg) && reg >= fs->nactvar) { - fs->freereg--; - lua_assert(reg == fs->freereg); - } -} - - -static void freeexp (FuncState *fs, expdesc *e) { - if (e->k == VNONRELOC) - freereg(fs, e->u.s.info); -} - - -static int addk (FuncState *fs, TValue *k, TValue *v) { - lua_State *L = fs->L; - TValue *idx = luaH_set(L, fs->h, k); - Proto *f = fs->f; - int oldsize = f->sizek; - if (ttisnumber(idx)) { - lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v)); - return cast_int(nvalue(idx)); - } - else { /* constant not found; create a new entry */ - setnvalue(idx, cast_num(fs->nk)); - luaM_growvector(L, f->k, fs->nk, f->sizek, TValue, - MAXARG_Bx, "constant table overflow"); - while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); - setobj(L, &f->k[fs->nk], v); - luaC_barrier(L, f, v); - return fs->nk++; - } -} - - -int luaK_stringK (FuncState *fs, TString *s) { - TValue o; - setsvalue(fs->L, &o, s); - return addk(fs, &o, &o); -} - - -int luaK_numberK (FuncState *fs, lua_Number r) { - TValue o; - setnvalue(&o, r); - return addk(fs, &o, &o); -} - - -static int boolK (FuncState *fs, int b) { - TValue o; - setbvalue(&o, b); - return addk(fs, &o, &o); -} - - -static int nilK (FuncState *fs) { - TValue k, v; - setnilvalue(&v); - /* cannot use nil as key; instead use table itself to represent nil */ - sethvalue(fs->L, &k, fs->h); - return addk(fs, &k, &v); -} - - -void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { - if (e->k == VCALL) { /* expression is an open function call? */ - SETARG_C(getcode(fs, e), nresults+1); - } - else if (e->k == VVARARG) { - SETARG_B(getcode(fs, e), nresults+1); - SETARG_A(getcode(fs, e), fs->freereg); - luaK_reserveregs(fs, 1); - } -} - - -void luaK_setoneret (FuncState *fs, expdesc *e) { - if (e->k == VCALL) { /* expression is an open function call? */ - e->k = VNONRELOC; - e->u.s.info = GETARG_A(getcode(fs, e)); - } - else if (e->k == VVARARG) { - SETARG_B(getcode(fs, e), 2); - e->k = VRELOCABLE; /* can relocate its simple result */ - } -} - - -void luaK_dischargevars (FuncState *fs, expdesc *e) { - switch (e->k) { - case VLOCAL: { - e->k = VNONRELOC; - break; - } - case VUPVAL: { - e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0); - e->k = VRELOCABLE; - break; - } - case VGLOBAL: { - e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info); - e->k = VRELOCABLE; - break; - } - case VINDEXED: { - freereg(fs, e->u.s.aux); - freereg(fs, e->u.s.info); - e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux); - e->k = VRELOCABLE; - break; - } - case VVARARG: - case VCALL: { - luaK_setoneret(fs, e); - break; - } - default: break; /* there is one value available (somewhere) */ - } -} - - -static int code_label (FuncState *fs, int A, int b, int jump) { - luaK_getlabel(fs); /* those instructions may be jump targets */ - return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump); -} - - -static void discharge2reg (FuncState *fs, expdesc *e, int reg) { - luaK_dischargevars(fs, e); - switch (e->k) { - case VNIL: { - luaK_nil(fs, reg, 1); - break; - } - case VFALSE: case VTRUE: { - luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); - break; - } - case VK: { - luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info); - break; - } - case VKNUM: { - luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval)); - break; - } - case VRELOCABLE: { - Instruction *pc = &getcode(fs, e); - SETARG_A(*pc, reg); - break; - } - case VNONRELOC: { - if (reg != e->u.s.info) - luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0); - break; - } - default: { - lua_assert(e->k == VVOID || e->k == VJMP); - return; /* nothing to do... */ - } - } - e->u.s.info = reg; - e->k = VNONRELOC; -} - - -static void discharge2anyreg (FuncState *fs, expdesc *e) { - if (e->k != VNONRELOC) { - luaK_reserveregs(fs, 1); - discharge2reg(fs, e, fs->freereg-1); - } -} - - -static void exp2reg (FuncState *fs, expdesc *e, int reg) { - discharge2reg(fs, e, reg); - if (e->k == VJMP) - luaK_concat(fs, &e->t, e->u.s.info); /* put this jump in `t' list */ - if (hasjumps(e)) { - int final; /* position after whole expression */ - int p_f = NO_JUMP; /* position of an eventual LOAD false */ - int p_t = NO_JUMP; /* position of an eventual LOAD true */ - if (need_value(fs, e->t) || need_value(fs, e->f)) { - int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); - p_f = code_label(fs, reg, 0, 1); - p_t = code_label(fs, reg, 1, 0); - luaK_patchtohere(fs, fj); - } - final = luaK_getlabel(fs); - patchlistaux(fs, e->f, final, reg, p_f); - patchlistaux(fs, e->t, final, reg, p_t); - } - e->f = e->t = NO_JUMP; - e->u.s.info = reg; - e->k = VNONRELOC; -} - - -void luaK_exp2nextreg (FuncState *fs, expdesc *e) { - luaK_dischargevars(fs, e); - freeexp(fs, e); - luaK_reserveregs(fs, 1); - exp2reg(fs, e, fs->freereg - 1); -} - - -int luaK_exp2anyreg (FuncState *fs, expdesc *e) { - luaK_dischargevars(fs, e); - if (e->k == VNONRELOC) { - if (!hasjumps(e)) return e->u.s.info; /* exp is already in a register */ - if (e->u.s.info >= fs->nactvar) { /* reg. is not a local? */ - exp2reg(fs, e, e->u.s.info); /* put value on it */ - return e->u.s.info; - } - } - luaK_exp2nextreg(fs, e); /* default */ - return e->u.s.info; -} - - -void luaK_exp2val (FuncState *fs, expdesc *e) { - if (hasjumps(e)) - luaK_exp2anyreg(fs, e); - else - luaK_dischargevars(fs, e); -} - - -int luaK_exp2RK (FuncState *fs, expdesc *e) { - luaK_exp2val(fs, e); - switch (e->k) { - case VKNUM: - case VTRUE: - case VFALSE: - case VNIL: { - if (fs->nk <= MAXINDEXRK) { /* constant fit in RK operand? */ - e->u.s.info = (e->k == VNIL) ? nilK(fs) : - (e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) : - boolK(fs, (e->k == VTRUE)); - e->k = VK; - return RKASK(e->u.s.info); - } - else break; - } - case VK: { - if (e->u.s.info <= MAXINDEXRK) /* constant fit in argC? */ - return RKASK(e->u.s.info); - else break; - } - default: break; - } - /* not a constant in the right range: put it in a register */ - return luaK_exp2anyreg(fs, e); -} - - -void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { - switch (var->k) { - case VLOCAL: { - freeexp(fs, ex); - exp2reg(fs, ex, var->u.s.info); - return; - } - case VUPVAL: { - int e = luaK_exp2anyreg(fs, ex); - luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0); - break; - } - case VGLOBAL: { - int e = luaK_exp2anyreg(fs, ex); - luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info); - break; - } - case VINDEXED: { - int e = luaK_exp2RK(fs, ex); - luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e); - break; - } - default: { - lua_assert(0); /* invalid var kind to store */ - break; - } - } - freeexp(fs, ex); -} - - -void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { - int func; - luaK_exp2anyreg(fs, e); - freeexp(fs, e); - func = fs->freereg; - luaK_reserveregs(fs, 2); - luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key)); - freeexp(fs, key); - e->u.s.info = func; - e->k = VNONRELOC; -} - - -static void invertjump (FuncState *fs, expdesc *e) { - Instruction *pc = getjumpcontrol(fs, e->u.s.info); - lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && - GET_OPCODE(*pc) != OP_TEST); - SETARG_A(*pc, !(GETARG_A(*pc))); -} - - -static int jumponcond (FuncState *fs, expdesc *e, int cond) { - if (e->k == VRELOCABLE) { - Instruction ie = getcode(fs, e); - if (GET_OPCODE(ie) == OP_NOT) { - fs->pc--; /* remove previous OP_NOT */ - return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond); - } - /* else go through */ - } - discharge2anyreg(fs, e); - freeexp(fs, e); - return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond); -} - - -void luaK_goiftrue (FuncState *fs, expdesc *e) { - int pc; /* pc of last jump */ - luaK_dischargevars(fs, e); - switch (e->k) { - case VK: case VKNUM: case VTRUE: { - pc = NO_JUMP; /* always true; do nothing */ - break; - } - case VJMP: { - invertjump(fs, e); - pc = e->u.s.info; - break; - } - default: { - pc = jumponcond(fs, e, 0); - break; - } - } - luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */ - luaK_patchtohere(fs, e->t); - e->t = NO_JUMP; -} - - -static void luaK_goiffalse (FuncState *fs, expdesc *e) { - int pc; /* pc of last jump */ - luaK_dischargevars(fs, e); - switch (e->k) { - case VNIL: case VFALSE: { - pc = NO_JUMP; /* always false; do nothing */ - break; - } - case VJMP: { - pc = e->u.s.info; - break; - } - default: { - pc = jumponcond(fs, e, 1); - break; - } - } - luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */ - luaK_patchtohere(fs, e->f); - e->f = NO_JUMP; -} - - -static void codenot (FuncState *fs, expdesc *e) { - luaK_dischargevars(fs, e); - switch (e->k) { - case VNIL: case VFALSE: { - e->k = VTRUE; - break; - } - case VK: case VKNUM: case VTRUE: { - e->k = VFALSE; - break; - } - case VJMP: { - invertjump(fs, e); - break; - } - case VRELOCABLE: - case VNONRELOC: { - discharge2anyreg(fs, e); - freeexp(fs, e); - e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0); - e->k = VRELOCABLE; - break; - } - default: { - lua_assert(0); /* cannot happen */ - break; - } - } - /* interchange true and false lists */ - { int temp = e->f; e->f = e->t; e->t = temp; } - removevalues(fs, e->f); - removevalues(fs, e->t); -} - - -void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { - t->u.s.aux = luaK_exp2RK(fs, k); - t->k = VINDEXED; -} - - -static int constfolding (OpCode op, expdesc *e1, expdesc *e2) { - lua_Number v1, v2, r; - if (!isnumeral(e1) || !isnumeral(e2)) return 0; - v1 = e1->u.nval; - v2 = e2->u.nval; - switch (op) { - case OP_ADD: r = luai_numadd(v1, v2); break; - case OP_SUB: r = luai_numsub(v1, v2); break; - case OP_MUL: r = luai_nummul(v1, v2); break; - case OP_DIV: - if (v2 == 0) return 0; /* do not attempt to divide by 0 */ - r = luai_numdiv(v1, v2); break; - case OP_MOD: - if (v2 == 0) return 0; /* do not attempt to divide by 0 */ - r = luai_nummod(v1, v2); break; - case OP_POW: r = luai_numpow(v1, v2); break; - case OP_UNM: r = luai_numunm(v1); break; - case OP_LEN: return 0; /* no constant folding for 'len' */ - default: lua_assert(0); r = 0; break; - } - if (luai_numisnan(r)) return 0; /* do not attempt to produce NaN */ - e1->u.nval = r; - return 1; -} - - -static void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) { - if (constfolding(op, e1, e2)) - return; - else { - int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0; - int o1 = luaK_exp2RK(fs, e1); - if (o1 > o2) { - freeexp(fs, e1); - freeexp(fs, e2); - } - else { - freeexp(fs, e2); - freeexp(fs, e1); - } - e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2); - e1->k = VRELOCABLE; - } -} - - -static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1, - expdesc *e2) { - int o1 = luaK_exp2RK(fs, e1); - int o2 = luaK_exp2RK(fs, e2); - freeexp(fs, e2); - freeexp(fs, e1); - if (cond == 0 && op != OP_EQ) { - int temp; /* exchange args to replace by `<' or `<=' */ - temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */ - cond = 1; - } - e1->u.s.info = condjump(fs, op, cond, o1, o2); - e1->k = VJMP; -} - - -void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) { - expdesc e2; - e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0; - switch (op) { - case OPR_MINUS: { - if (!isnumeral(e)) - luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */ - codearith(fs, OP_UNM, e, &e2); - break; - } - case OPR_NOT: codenot(fs, e); break; - case OPR_LEN: { - luaK_exp2anyreg(fs, e); /* cannot operate on constants */ - codearith(fs, OP_LEN, e, &e2); - break; - } - default: lua_assert(0); - } -} - - -void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { - switch (op) { - case OPR_AND: { - luaK_goiftrue(fs, v); - break; - } - case OPR_OR: { - luaK_goiffalse(fs, v); - break; - } - case OPR_CONCAT: { - luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */ - break; - } - case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: - case OPR_MOD: case OPR_POW: { - if (!isnumeral(v)) luaK_exp2RK(fs, v); - break; - } - default: { - luaK_exp2RK(fs, v); - break; - } - } -} - - -void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) { - switch (op) { - case OPR_AND: { - lua_assert(e1->t == NO_JUMP); /* list must be closed */ - luaK_dischargevars(fs, e2); - luaK_concat(fs, &e2->f, e1->f); - *e1 = *e2; - break; - } - case OPR_OR: { - lua_assert(e1->f == NO_JUMP); /* list must be closed */ - luaK_dischargevars(fs, e2); - luaK_concat(fs, &e2->t, e1->t); - *e1 = *e2; - break; - } - case OPR_CONCAT: { - luaK_exp2val(fs, e2); - if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) { - lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1); - freeexp(fs, e1); - SETARG_B(getcode(fs, e2), e1->u.s.info); - e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info; - } - else { - luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */ - codearith(fs, OP_CONCAT, e1, e2); - } - break; - } - case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break; - case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break; - case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break; - case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break; - case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break; - case OPR_POW: codearith(fs, OP_POW, e1, e2); break; - case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break; - case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break; - case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break; - case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break; - case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break; - case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break; - default: lua_assert(0); - } -} - - -void luaK_fixline (FuncState *fs, int line) { - fs->f->lineinfo[fs->pc - 1] = line; -} - - -static int luaK_code (FuncState *fs, Instruction i, int line) { - Proto *f = fs->f; - dischargejpc(fs); /* `pc' will change */ - /* put new instruction in code array */ - luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction, - MAX_INT, "code size overflow"); - f->code[fs->pc] = i; - /* save corresponding line information */ - luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int, - MAX_INT, "code size overflow"); - f->lineinfo[fs->pc] = line; - return fs->pc++; -} - - -int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) { - lua_assert(getOpMode(o) == iABC); - lua_assert(getBMode(o) != OpArgN || b == 0); - lua_assert(getCMode(o) != OpArgN || c == 0); - return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline); -} - - -int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { - lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx); - lua_assert(getCMode(o) == OpArgN); - return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline); -} - - -void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { - int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1; - int b = (tostore == LUA_MULTRET) ? 0 : tostore; - lua_assert(tostore != 0); - if (c <= MAXARG_C) - luaK_codeABC(fs, OP_SETLIST, base, b, c); - else { - luaK_codeABC(fs, OP_SETLIST, base, b, 0); - luaK_code(fs, cast(Instruction, c), fs->ls->lastline); - } - fs->freereg = base + 1; /* free registers with list values */ -} - diff --git a/libraries/lua/lcode.h b/libraries/lua/lcode.h deleted file mode 100644 index b941c6072..000000000 --- a/libraries/lua/lcode.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -** $Id: lcode.h,v 1.48.1.1 2007/12/27 13:02:25 roberto Exp $ -** Code generator for Lua -** See Copyright Notice in lua.h -*/ - -#ifndef lcode_h -#define lcode_h - -#include "llex.h" -#include "lobject.h" -#include "lopcodes.h" -#include "lparser.h" - - -/* -** Marks the end of a patch list. It is an invalid value both as an absolute -** address, and as a list link (would link an element to itself). -*/ -#define NO_JUMP (-1) - - -/* -** grep "ORDER OPR" if you change these enums -*/ -typedef enum BinOpr { - OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, - OPR_CONCAT, - OPR_NE, OPR_EQ, - OPR_LT, OPR_LE, OPR_GT, OPR_GE, - OPR_AND, OPR_OR, - OPR_NOBINOPR -} BinOpr; - - -typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; - - -#define getcode(fs,e) ((fs)->f->code[(e)->u.s.info]) - -#define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx) - -#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) - -LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); -LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C); -LUAI_FUNC void luaK_fixline (FuncState *fs, int line); -LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); -LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); -LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); -LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s); -LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r); -LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); -LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); -LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); -LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); -LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e); -LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); -LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); -LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); -LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); -LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); -LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); -LUAI_FUNC int luaK_jump (FuncState *fs); -LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); -LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); -LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); -LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); -LUAI_FUNC int luaK_getlabel (FuncState *fs); -LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v); -LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v); -LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2); -LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); - - -#endif diff --git a/libraries/lua/ldblib.c b/libraries/lua/ldblib.c deleted file mode 100644 index 2027eda59..000000000 --- a/libraries/lua/ldblib.c +++ /dev/null @@ -1,398 +0,0 @@ -/* -** $Id: ldblib.c,v 1.104.1.4 2009/08/04 18:50:18 roberto Exp $ -** Interface from Lua to its debug API -** See Copyright Notice in lua.h -*/ - - -#include -#include -#include - -#define ldblib_c -#define LUA_LIB - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - - -static int db_getregistry (lua_State *L) { - lua_pushvalue(L, LUA_REGISTRYINDEX); - return 1; -} - - -static int db_getmetatable (lua_State *L) { - luaL_checkany(L, 1); - if (!lua_getmetatable(L, 1)) { - lua_pushnil(L); /* no metatable */ - } - return 1; -} - - -static int db_setmetatable (lua_State *L) { - int t = lua_type(L, 2); - luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, - "nil or table expected"); - lua_settop(L, 2); - lua_pushboolean(L, lua_setmetatable(L, 1)); - return 1; -} - - -static int db_getfenv (lua_State *L) { - luaL_checkany(L, 1); - lua_getfenv(L, 1); - return 1; -} - - -static int db_setfenv (lua_State *L) { - luaL_checktype(L, 2, LUA_TTABLE); - lua_settop(L, 2); - if (lua_setfenv(L, 1) == 0) - luaL_error(L, LUA_QL("setfenv") - " cannot change environment of given object"); - return 1; -} - - -static void settabss (lua_State *L, const char *i, const char *v) { - lua_pushstring(L, v); - lua_setfield(L, -2, i); -} - - -static void settabsi (lua_State *L, const char *i, int v) { - lua_pushinteger(L, v); - lua_setfield(L, -2, i); -} - - -static lua_State *getthread (lua_State *L, int *arg) { - if (lua_isthread(L, 1)) { - *arg = 1; - return lua_tothread(L, 1); - } - else { - *arg = 0; - return L; - } -} - - -static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) { - if (L == L1) { - lua_pushvalue(L, -2); - lua_remove(L, -3); - } - else - lua_xmove(L1, L, 1); - lua_setfield(L, -2, fname); -} - - -static int db_getinfo (lua_State *L) { - lua_Debug ar; - int arg; - lua_State *L1 = getthread(L, &arg); - const char *options = luaL_optstring(L, arg+2, "flnSu"); - if (lua_isnumber(L, arg+1)) { - if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) { - lua_pushnil(L); /* level out of range */ - return 1; - } - } - else if (lua_isfunction(L, arg+1)) { - lua_pushfstring(L, ">%s", options); - options = lua_tostring(L, -1); - lua_pushvalue(L, arg+1); - lua_xmove(L, L1, 1); - } - else - return luaL_argerror(L, arg+1, "function or level expected"); - if (!lua_getinfo(L1, options, &ar)) - return luaL_argerror(L, arg+2, "invalid option"); - lua_createtable(L, 0, 2); - if (strchr(options, 'S')) { - settabss(L, "source", ar.source); - settabss(L, "short_src", ar.short_src); - settabsi(L, "linedefined", ar.linedefined); - settabsi(L, "lastlinedefined", ar.lastlinedefined); - settabss(L, "what", ar.what); - } - if (strchr(options, 'l')) - settabsi(L, "currentline", ar.currentline); - if (strchr(options, 'u')) - settabsi(L, "nups", ar.nups); - if (strchr(options, 'n')) { - settabss(L, "name", ar.name); - settabss(L, "namewhat", ar.namewhat); - } - if (strchr(options, 'L')) - treatstackoption(L, L1, "activelines"); - if (strchr(options, 'f')) - treatstackoption(L, L1, "func"); - return 1; /* return table */ -} - - -static int db_getlocal (lua_State *L) { - int arg; - lua_State *L1 = getthread(L, &arg); - lua_Debug ar; - const char *name; - if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ - return luaL_argerror(L, arg+1, "level out of range"); - name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2)); - if (name) { - lua_xmove(L1, L, 1); - lua_pushstring(L, name); - lua_pushvalue(L, -2); - return 2; - } - else { - lua_pushnil(L); - return 1; - } -} - - -static int db_setlocal (lua_State *L) { - int arg; - lua_State *L1 = getthread(L, &arg); - lua_Debug ar; - if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ - return luaL_argerror(L, arg+1, "level out of range"); - luaL_checkany(L, arg+3); - lua_settop(L, arg+3); - lua_xmove(L, L1, 1); - lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2))); - return 1; -} - - -static int auxupvalue (lua_State *L, int get) { - const char *name; - int n = luaL_checkint(L, 2); - luaL_checktype(L, 1, LUA_TFUNCTION); - if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */ - name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); - if (name == NULL) return 0; - lua_pushstring(L, name); - lua_insert(L, -(get+1)); - return get + 1; -} - - -static int db_getupvalue (lua_State *L) { - return auxupvalue(L, 1); -} - - -static int db_setupvalue (lua_State *L) { - luaL_checkany(L, 3); - return auxupvalue(L, 0); -} - - - -static const char KEY_HOOK = 'h'; - - -static void hookf (lua_State *L, lua_Debug *ar) { - static const char *const hooknames[] = - {"call", "return", "line", "count", "tail return"}; - lua_pushlightuserdata(L, (void *)&KEY_HOOK); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_pushlightuserdata(L, L); - lua_rawget(L, -2); - if (lua_isfunction(L, -1)) { - lua_pushstring(L, hooknames[(int)ar->event]); - if (ar->currentline >= 0) - lua_pushinteger(L, ar->currentline); - else lua_pushnil(L); - lua_assert(lua_getinfo(L, "lS", ar)); - lua_call(L, 2, 0); - } -} - - -static int makemask (const char *smask, int count) { - int mask = 0; - if (strchr(smask, 'c')) mask |= LUA_MASKCALL; - if (strchr(smask, 'r')) mask |= LUA_MASKRET; - if (strchr(smask, 'l')) mask |= LUA_MASKLINE; - if (count > 0) mask |= LUA_MASKCOUNT; - return mask; -} - - -static char *unmakemask (int mask, char *smask) { - int i = 0; - if (mask & LUA_MASKCALL) smask[i++] = 'c'; - if (mask & LUA_MASKRET) smask[i++] = 'r'; - if (mask & LUA_MASKLINE) smask[i++] = 'l'; - smask[i] = '\0'; - return smask; -} - - -static void gethooktable (lua_State *L) { - lua_pushlightuserdata(L, (void *)&KEY_HOOK); - lua_rawget(L, LUA_REGISTRYINDEX); - if (!lua_istable(L, -1)) { - lua_pop(L, 1); - lua_createtable(L, 0, 1); - lua_pushlightuserdata(L, (void *)&KEY_HOOK); - lua_pushvalue(L, -2); - lua_rawset(L, LUA_REGISTRYINDEX); - } -} - - -static int db_sethook (lua_State *L) { - int arg, mask, count; - lua_Hook func; - lua_State *L1 = getthread(L, &arg); - if (lua_isnoneornil(L, arg+1)) { - lua_settop(L, arg+1); - func = NULL; mask = 0; count = 0; /* turn off hooks */ - } - else { - const char *smask = luaL_checkstring(L, arg+2); - luaL_checktype(L, arg+1, LUA_TFUNCTION); - count = luaL_optint(L, arg+3, 0); - func = hookf; mask = makemask(smask, count); - } - gethooktable(L); - lua_pushlightuserdata(L, L1); - lua_pushvalue(L, arg+1); - lua_rawset(L, -3); /* set new hook */ - lua_pop(L, 1); /* remove hook table */ - lua_sethook(L1, func, mask, count); /* set hooks */ - return 0; -} - - -static int db_gethook (lua_State *L) { - int arg; - lua_State *L1 = getthread(L, &arg); - char buff[5]; - int mask = lua_gethookmask(L1); - lua_Hook hook = lua_gethook(L1); - if (hook != NULL && hook != hookf) /* external hook? */ - lua_pushliteral(L, "external hook"); - else { - gethooktable(L); - lua_pushlightuserdata(L, L1); - lua_rawget(L, -2); /* get hook */ - lua_remove(L, -2); /* remove hook table */ - } - lua_pushstring(L, unmakemask(mask, buff)); - lua_pushinteger(L, lua_gethookcount(L1)); - return 3; -} - - -static int db_debug (lua_State *L) { - for (;;) { - char buffer[250]; - fputs("lua_debug> ", stderr); - if (fgets(buffer, sizeof(buffer), stdin) == 0 || - strcmp(buffer, "cont\n") == 0) - return 0; - if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || - lua_pcall(L, 0, 0, 0)) { - fputs(lua_tostring(L, -1), stderr); - fputs("\n", stderr); - } - lua_settop(L, 0); /* remove eventual returns */ - } -} - - -#define LEVELS1 12 /* size of the first part of the stack */ -#define LEVELS2 10 /* size of the second part of the stack */ - -static int db_errorfb (lua_State *L) { - int level; - int firstpart = 1; /* still before eventual `...' */ - int arg; - lua_State *L1 = getthread(L, &arg); - lua_Debug ar; - if (lua_isnumber(L, arg+2)) { - level = (int)lua_tointeger(L, arg+2); - lua_pop(L, 1); - } - else - level = (L == L1) ? 1 : 0; /* level 0 may be this own function */ - if (lua_gettop(L) == arg) - lua_pushliteral(L, ""); - else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */ - else lua_pushliteral(L, "\n"); - lua_pushliteral(L, "stack traceback:"); - while (lua_getstack(L1, level++, &ar)) { - if (level > LEVELS1 && firstpart) { - /* no more than `LEVELS2' more levels? */ - if (!lua_getstack(L1, level+LEVELS2, &ar)) - level--; /* keep going */ - else { - lua_pushliteral(L, "\n\t..."); /* too many levels */ - while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */ - level++; - } - firstpart = 0; - continue; - } - lua_pushliteral(L, "\n\t"); - lua_getinfo(L1, "Snl", &ar); - lua_pushfstring(L, "%s:", ar.short_src); - if (ar.currentline > 0) - lua_pushfstring(L, "%d:", ar.currentline); - if (*ar.namewhat != '\0') /* is there a name? */ - lua_pushfstring(L, " in function " LUA_QS, ar.name); - else { - if (*ar.what == 'm') /* main? */ - lua_pushfstring(L, " in main chunk"); - else if (*ar.what == 'C' || *ar.what == 't') - lua_pushliteral(L, " ?"); /* C function or tail call */ - else - lua_pushfstring(L, " in function <%s:%d>", - ar.short_src, ar.linedefined); - } - lua_concat(L, lua_gettop(L) - arg); - } - lua_concat(L, lua_gettop(L) - arg); - return 1; -} - - -static const luaL_Reg dblib[] = { - {"debug", db_debug}, - {"getfenv", db_getfenv}, - {"gethook", db_gethook}, - {"getinfo", db_getinfo}, - {"getlocal", db_getlocal}, - {"getregistry", db_getregistry}, - {"getmetatable", db_getmetatable}, - {"getupvalue", db_getupvalue}, - {"setfenv", db_setfenv}, - {"sethook", db_sethook}, - {"setlocal", db_setlocal}, - {"setmetatable", db_setmetatable}, - {"setupvalue", db_setupvalue}, - {"traceback", db_errorfb}, - {NULL, NULL} -}; - - -LUALIB_API int luaopen_debug (lua_State *L) { - luaL_register(L, LUA_DBLIBNAME, dblib); - return 1; -} - diff --git a/libraries/lua/ldebug.c b/libraries/lua/ldebug.c deleted file mode 100644 index 50ad3d380..000000000 --- a/libraries/lua/ldebug.c +++ /dev/null @@ -1,638 +0,0 @@ -/* -** $Id: ldebug.c,v 2.29.1.6 2008/05/08 16:56:26 roberto Exp $ -** Debug Interface -** See Copyright Notice in lua.h -*/ - - -#include -#include -#include - - -#define ldebug_c -#define LUA_CORE - -#include "lua.h" - -#include "lapi.h" -#include "lcode.h" -#include "ldebug.h" -#include "ldo.h" -#include "lfunc.h" -#include "lobject.h" -#include "lopcodes.h" -#include "lstate.h" -#include "lstring.h" -#include "ltable.h" -#include "ltm.h" -#include "lvm.h" - - - -static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name); - - -static int currentpc (lua_State *L, CallInfo *ci) { - if (!isLua(ci)) return -1; /* function is not a Lua function? */ - if (ci == L->ci) - ci->savedpc = L->savedpc; - return pcRel(ci->savedpc, ci_func(ci)->l.p); -} - - -static int currentline (lua_State *L, CallInfo *ci) { - int pc = currentpc(L, ci); - if (pc < 0) - return -1; /* only active lua functions have current-line information */ - else - return getline(ci_func(ci)->l.p, pc); -} - - -/* -** this function can be called asynchronous (e.g. during a signal) -*/ -LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { - if (func == NULL || mask == 0) { /* turn off hooks? */ - mask = 0; - func = NULL; - } - L->hook = func; - L->basehookcount = count; - resethookcount(L); - L->hookmask = cast_byte(mask); - return 1; -} - - -LUA_API lua_Hook lua_gethook (lua_State *L) { - return L->hook; -} - - -LUA_API int lua_gethookmask (lua_State *L) { - return L->hookmask; -} - - -LUA_API int lua_gethookcount (lua_State *L) { - return L->basehookcount; -} - - -LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { - int status; - CallInfo *ci; - lua_lock(L); - for (ci = L->ci; level > 0 && ci > L->base_ci; ci--) { - level--; - if (f_isLua(ci)) /* Lua function? */ - level -= ci->tailcalls; /* skip lost tail calls */ - } - if (level == 0 && ci > L->base_ci) { /* level found? */ - status = 1; - ar->i_ci = cast_int(ci - L->base_ci); - } - else if (level < 0) { /* level is of a lost tail call? */ - status = 1; - ar->i_ci = 0; - } - else status = 0; /* no such level */ - lua_unlock(L); - return status; -} - - -static Proto *getluaproto (CallInfo *ci) { - return (isLua(ci) ? ci_func(ci)->l.p : NULL); -} - - -static const char *findlocal (lua_State *L, CallInfo *ci, int n) { - const char *name; - Proto *fp = getluaproto(ci); - if (fp && (name = luaF_getlocalname(fp, n, currentpc(L, ci))) != NULL) - return name; /* is a local variable in a Lua function */ - else { - StkId limit = (ci == L->ci) ? L->top : (ci+1)->func; - if (limit - ci->base >= n && n > 0) /* is 'n' inside 'ci' stack? */ - return "(*temporary)"; - else - return NULL; - } -} - - -LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { - CallInfo *ci = L->base_ci + ar->i_ci; - const char *name = findlocal(L, ci, n); - lua_lock(L); - if (name) - luaA_pushobject(L, ci->base + (n - 1)); - lua_unlock(L); - return name; -} - - -LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { - CallInfo *ci = L->base_ci + ar->i_ci; - const char *name = findlocal(L, ci, n); - lua_lock(L); - if (name) - setobjs2s(L, ci->base + (n - 1), L->top - 1); - L->top--; /* pop value */ - lua_unlock(L); - return name; -} - - -static void funcinfo (lua_Debug *ar, Closure *cl) { - if (cl->c.isC) { - ar->source = "=[C]"; - ar->linedefined = -1; - ar->lastlinedefined = -1; - ar->what = "C"; - } - else { - ar->source = getstr(cl->l.p->source); - ar->linedefined = cl->l.p->linedefined; - ar->lastlinedefined = cl->l.p->lastlinedefined; - ar->what = (ar->linedefined == 0) ? "main" : "Lua"; - } - luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); -} - - -static void info_tailcall (lua_Debug *ar) { - ar->name = ar->namewhat = ""; - ar->what = "tail"; - ar->lastlinedefined = ar->linedefined = ar->currentline = -1; - ar->source = "=(tail call)"; - luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); - ar->nups = 0; -} - - -static void collectvalidlines (lua_State *L, Closure *f) { - if (f == NULL || f->c.isC) { - setnilvalue(L->top); - } - else { - Table *t = luaH_new(L, 0, 0); - int *lineinfo = f->l.p->lineinfo; - int i; - for (i=0; il.p->sizelineinfo; i++) - setbvalue(luaH_setnum(L, t, lineinfo[i]), 1); - sethvalue(L, L->top, t); - } - incr_top(L); -} - - -static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, - Closure *f, CallInfo *ci) { - int status = 1; - if (f == NULL) { - info_tailcall(ar); - return status; - } - for (; *what; what++) { - switch (*what) { - case 'S': { - funcinfo(ar, f); - break; - } - case 'l': { - ar->currentline = (ci) ? currentline(L, ci) : -1; - break; - } - case 'u': { - ar->nups = f->c.nupvalues; - break; - } - case 'n': { - ar->namewhat = (ci) ? getfuncname(L, ci, &ar->name) : NULL; - if (ar->namewhat == NULL) { - ar->namewhat = ""; /* not found */ - ar->name = NULL; - } - break; - } - case 'L': - case 'f': /* handled by lua_getinfo */ - break; - default: status = 0; /* invalid option */ - } - } - return status; -} - - -LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { - int status; - Closure *f = NULL; - CallInfo *ci = NULL; - lua_lock(L); - if (*what == '>') { - StkId func = L->top - 1; - luai_apicheck(L, ttisfunction(func)); - what++; /* skip the '>' */ - f = clvalue(func); - L->top--; /* pop function */ - } - else if (ar->i_ci != 0) { /* no tail call? */ - ci = L->base_ci + ar->i_ci; - lua_assert(ttisfunction(ci->func)); - f = clvalue(ci->func); - } - status = auxgetinfo(L, what, ar, f, ci); - if (strchr(what, 'f')) { - if (f == NULL) setnilvalue(L->top); - else setclvalue(L, L->top, f); - incr_top(L); - } - if (strchr(what, 'L')) - collectvalidlines(L, f); - lua_unlock(L); - return status; -} - - -/* -** {====================================================== -** Symbolic Execution and code checker -** ======================================================= -*/ - -#define check(x) if (!(x)) return 0; - -#define checkjump(pt,pc) check(0 <= pc && pc < pt->sizecode) - -#define checkreg(pt,reg) check((reg) < (pt)->maxstacksize) - - - -static int precheck (const Proto *pt) { - check(pt->maxstacksize <= MAXSTACK); - check(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize); - check(!(pt->is_vararg & VARARG_NEEDSARG) || - (pt->is_vararg & VARARG_HASARG)); - check(pt->sizeupvalues <= pt->nups); - check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0); - check(pt->sizecode > 0 && GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN); - return 1; -} - - -#define checkopenop(pt,pc) luaG_checkopenop((pt)->code[(pc)+1]) - -int luaG_checkopenop (Instruction i) { - switch (GET_OPCODE(i)) { - case OP_CALL: - case OP_TAILCALL: - case OP_RETURN: - case OP_SETLIST: { - check(GETARG_B(i) == 0); - return 1; - } - default: return 0; /* invalid instruction after an open call */ - } -} - - -static int checkArgMode (const Proto *pt, int r, enum OpArgMask mode) { - switch (mode) { - case OpArgN: check(r == 0); break; - case OpArgU: break; - case OpArgR: checkreg(pt, r); break; - case OpArgK: - check(ISK(r) ? INDEXK(r) < pt->sizek : r < pt->maxstacksize); - break; - } - return 1; -} - - -static Instruction symbexec (const Proto *pt, int lastpc, int reg) { - int pc; - int last; /* stores position of last instruction that changed `reg' */ - last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */ - check(precheck(pt)); - for (pc = 0; pc < lastpc; pc++) { - Instruction i = pt->code[pc]; - OpCode op = GET_OPCODE(i); - int a = GETARG_A(i); - int b = 0; - int c = 0; - check(op < NUM_OPCODES); - checkreg(pt, a); - switch (getOpMode(op)) { - case iABC: { - b = GETARG_B(i); - c = GETARG_C(i); - check(checkArgMode(pt, b, getBMode(op))); - check(checkArgMode(pt, c, getCMode(op))); - break; - } - case iABx: { - b = GETARG_Bx(i); - if (getBMode(op) == OpArgK) check(b < pt->sizek); - break; - } - case iAsBx: { - b = GETARG_sBx(i); - if (getBMode(op) == OpArgR) { - int dest = pc+1+b; - check(0 <= dest && dest < pt->sizecode); - if (dest > 0) { - int j; - /* check that it does not jump to a setlist count; this - is tricky, because the count from a previous setlist may - have the same value of an invalid setlist; so, we must - go all the way back to the first of them (if any) */ - for (j = 0; j < dest; j++) { - Instruction d = pt->code[dest-1-j]; - if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break; - } - /* if 'j' is even, previous value is not a setlist (even if - it looks like one) */ - check((j&1) == 0); - } - } - break; - } - } - if (testAMode(op)) { - if (a == reg) last = pc; /* change register `a' */ - } - if (testTMode(op)) { - check(pc+2 < pt->sizecode); /* check skip */ - check(GET_OPCODE(pt->code[pc+1]) == OP_JMP); - } - switch (op) { - case OP_LOADBOOL: { - if (c == 1) { /* does it jump? */ - check(pc+2 < pt->sizecode); /* check its jump */ - check(GET_OPCODE(pt->code[pc+1]) != OP_SETLIST || - GETARG_C(pt->code[pc+1]) != 0); - } - break; - } - case OP_LOADNIL: { - if (a <= reg && reg <= b) - last = pc; /* set registers from `a' to `b' */ - break; - } - case OP_GETUPVAL: - case OP_SETUPVAL: { - check(b < pt->nups); - break; - } - case OP_GETGLOBAL: - case OP_SETGLOBAL: { - check(ttisstring(&pt->k[b])); - break; - } - case OP_SELF: { - checkreg(pt, a+1); - if (reg == a+1) last = pc; - break; - } - case OP_CONCAT: { - check(b < c); /* at least two operands */ - break; - } - case OP_TFORLOOP: { - check(c >= 1); /* at least one result (control variable) */ - checkreg(pt, a+2+c); /* space for results */ - if (reg >= a+2) last = pc; /* affect all regs above its base */ - break; - } - case OP_FORLOOP: - case OP_FORPREP: - checkreg(pt, a+3); - /* go through */ - case OP_JMP: { - int dest = pc+1+b; - /* not full check and jump is forward and do not skip `lastpc'? */ - if (reg != NO_REG && pc < dest && dest <= lastpc) - pc += b; /* do the jump */ - break; - } - case OP_CALL: - case OP_TAILCALL: { - if (b != 0) { - checkreg(pt, a+b-1); - } - c--; /* c = num. returns */ - if (c == LUA_MULTRET) { - check(checkopenop(pt, pc)); - } - else if (c != 0) - checkreg(pt, a+c-1); - if (reg >= a) last = pc; /* affect all registers above base */ - break; - } - case OP_RETURN: { - b--; /* b = num. returns */ - if (b > 0) checkreg(pt, a+b-1); - break; - } - case OP_SETLIST: { - if (b > 0) checkreg(pt, a + b); - if (c == 0) { - pc++; - check(pc < pt->sizecode - 1); - } - break; - } - case OP_CLOSURE: { - int nup, j; - check(b < pt->sizep); - nup = pt->p[b]->nups; - check(pc + nup < pt->sizecode); - for (j = 1; j <= nup; j++) { - OpCode op1 = GET_OPCODE(pt->code[pc + j]); - check(op1 == OP_GETUPVAL || op1 == OP_MOVE); - } - if (reg != NO_REG) /* tracing? */ - pc += nup; /* do not 'execute' these pseudo-instructions */ - break; - } - case OP_VARARG: { - check((pt->is_vararg & VARARG_ISVARARG) && - !(pt->is_vararg & VARARG_NEEDSARG)); - b--; - if (b == LUA_MULTRET) check(checkopenop(pt, pc)); - checkreg(pt, a+b-1); - break; - } - default: break; - } - } - return pt->code[last]; -} - -#undef check -#undef checkjump -#undef checkreg - -/* }====================================================== */ - - -int luaG_checkcode (const Proto *pt) { - return (symbexec(pt, pt->sizecode, NO_REG) != 0); -} - - -static const char *kname (Proto *p, int c) { - if (ISK(c) && ttisstring(&p->k[INDEXK(c)])) - return svalue(&p->k[INDEXK(c)]); - else - return "?"; -} - - -static const char *getobjname (lua_State *L, CallInfo *ci, int stackpos, - const char **name) { - if (isLua(ci)) { /* a Lua function? */ - Proto *p = ci_func(ci)->l.p; - int pc = currentpc(L, ci); - Instruction i; - *name = luaF_getlocalname(p, stackpos+1, pc); - if (*name) /* is a local? */ - return "local"; - i = symbexec(p, pc, stackpos); /* try symbolic execution */ - lua_assert(pc != -1); - switch (GET_OPCODE(i)) { - case OP_GETGLOBAL: { - int g = GETARG_Bx(i); /* global index */ - lua_assert(ttisstring(&p->k[g])); - *name = svalue(&p->k[g]); - return "global"; - } - case OP_MOVE: { - int a = GETARG_A(i); - int b = GETARG_B(i); /* move from `b' to `a' */ - if (b < a) - return getobjname(L, ci, b, name); /* get name for `b' */ - break; - } - case OP_GETTABLE: { - int k = GETARG_C(i); /* key index */ - *name = kname(p, k); - return "field"; - } - case OP_GETUPVAL: { - int u = GETARG_B(i); /* upvalue index */ - *name = p->upvalues ? getstr(p->upvalues[u]) : "?"; - return "upvalue"; - } - case OP_SELF: { - int k = GETARG_C(i); /* key index */ - *name = kname(p, k); - return "method"; - } - default: break; - } - } - return NULL; /* no useful name found */ -} - - -static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { - Instruction i; - if ((isLua(ci) && ci->tailcalls > 0) || !isLua(ci - 1)) - return NULL; /* calling function is not Lua (or is unknown) */ - ci--; /* calling function */ - i = ci_func(ci)->l.p->code[currentpc(L, ci)]; - if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL || - GET_OPCODE(i) == OP_TFORLOOP) - return getobjname(L, ci, GETARG_A(i), name); - else - return NULL; /* no useful name can be found */ -} - - -/* only ANSI way to check whether a pointer points to an array */ -static int isinstack (CallInfo *ci, const TValue *o) { - StkId p; - for (p = ci->base; p < ci->top; p++) - if (o == p) return 1; - return 0; -} - - -void luaG_typeerror (lua_State *L, const TValue *o, const char *op) { - const char *name = NULL; - const char *t = luaT_typenames[ttype(o)]; - const char *kind = (isinstack(L->ci, o)) ? - getobjname(L, L->ci, cast_int(o - L->base), &name) : - NULL; - if (kind) - luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)", - op, kind, name, t); - else - luaG_runerror(L, "attempt to %s a %s value", op, t); -} - - -void luaG_concaterror (lua_State *L, StkId p1, StkId p2) { - if (ttisstring(p1) || ttisnumber(p1)) p1 = p2; - lua_assert(!ttisstring(p1) && !ttisnumber(p1)); - luaG_typeerror(L, p1, "concatenate"); -} - - -void luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) { - TValue temp; - if (luaV_tonumber(p1, &temp) == NULL) - p2 = p1; /* first operand is wrong */ - luaG_typeerror(L, p2, "perform arithmetic on"); -} - - -int luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { - const char *t1 = luaT_typenames[ttype(p1)]; - const char *t2 = luaT_typenames[ttype(p2)]; - if (t1[2] == t2[2]) - luaG_runerror(L, "attempt to compare two %s values", t1); - else - luaG_runerror(L, "attempt to compare %s with %s", t1, t2); - return 0; -} - - -static void addinfo (lua_State *L, const char *msg) { - CallInfo *ci = L->ci; - if (isLua(ci)) { /* is Lua code? */ - char buff[LUA_IDSIZE]; /* add file:line information */ - int line = currentline(L, ci); - luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE); - luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); - } -} - - -void luaG_errormsg (lua_State *L) { - if (L->errfunc != 0) { /* is there an error handling function? */ - StkId errfunc = restorestack(L, L->errfunc); - if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR); - setobjs2s(L, L->top, L->top - 1); /* move argument */ - setobjs2s(L, L->top - 1, errfunc); /* push function */ - incr_top(L); - luaD_call(L, L->top - 2, 1); /* call it */ - } - luaD_throw(L, LUA_ERRRUN); -} - - -void luaG_runerror (lua_State *L, const char *fmt, ...) { - va_list argp; - va_start(argp, fmt); - addinfo(L, luaO_pushvfstring(L, fmt, argp)); - va_end(argp); - luaG_errormsg(L); -} - diff --git a/libraries/lua/ldebug.h b/libraries/lua/ldebug.h deleted file mode 100644 index ba28a9724..000000000 --- a/libraries/lua/ldebug.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -** $Id: ldebug.h,v 2.3.1.1 2007/12/27 13:02:25 roberto Exp $ -** Auxiliary functions from Debug Interface module -** See Copyright Notice in lua.h -*/ - -#ifndef ldebug_h -#define ldebug_h - - -#include "lstate.h" - - -#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1) - -#define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0) - -#define resethookcount(L) (L->hookcount = L->basehookcount) - - -LUAI_FUNC void luaG_typeerror (lua_State *L, const TValue *o, - const char *opname); -LUAI_FUNC void luaG_concaterror (lua_State *L, StkId p1, StkId p2); -LUAI_FUNC void luaG_aritherror (lua_State *L, const TValue *p1, - const TValue *p2); -LUAI_FUNC int luaG_ordererror (lua_State *L, const TValue *p1, - const TValue *p2); -LUAI_FUNC void luaG_runerror (lua_State *L, const char *fmt, ...); -LUAI_FUNC void luaG_errormsg (lua_State *L); -LUAI_FUNC int luaG_checkcode (const Proto *pt); -LUAI_FUNC int luaG_checkopenop (Instruction i); - -#endif diff --git a/libraries/lua/ldo.c b/libraries/lua/ldo.c deleted file mode 100644 index d1bf786cb..000000000 --- a/libraries/lua/ldo.c +++ /dev/null @@ -1,519 +0,0 @@ -/* -** $Id: ldo.c,v 2.38.1.4 2012/01/18 02:27:10 roberto Exp $ -** Stack and Call structure of Lua -** See Copyright Notice in lua.h -*/ - - -#include -#include -#include - -#define ldo_c -#define LUA_CORE - -#include "lua.h" - -#include "ldebug.h" -#include "ldo.h" -#include "lfunc.h" -#include "lgc.h" -#include "lmem.h" -#include "lobject.h" -#include "lopcodes.h" -#include "lparser.h" -#include "lstate.h" -#include "lstring.h" -#include "ltable.h" -#include "ltm.h" -#include "lundump.h" -#include "lvm.h" -#include "lzio.h" - - - - -/* -** {====================================================== -** Error-recovery functions -** ======================================================= -*/ - - -/* chain list of long jump buffers */ -struct lua_longjmp { - struct lua_longjmp *previous; - luai_jmpbuf b; - volatile int status; /* error code */ -}; - - -void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { - switch (errcode) { - case LUA_ERRMEM: { - setsvalue2s(L, oldtop, luaS_newliteral(L, MEMERRMSG)); - break; - } - case LUA_ERRERR: { - setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); - break; - } - case LUA_ERRSYNTAX: - case LUA_ERRRUN: { - setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ - break; - } - } - L->top = oldtop + 1; -} - - -static void restore_stack_limit (lua_State *L) { - lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); - if (L->size_ci > LUAI_MAXCALLS) { /* there was an overflow? */ - int inuse = cast_int(L->ci - L->base_ci); - if (inuse + 1 < LUAI_MAXCALLS) /* can `undo' overflow? */ - luaD_reallocCI(L, LUAI_MAXCALLS); - } -} - - -static void resetstack (lua_State *L, int status) { - L->ci = L->base_ci; - L->base = L->ci->base; - luaF_close(L, L->base); /* close eventual pending closures */ - luaD_seterrorobj(L, status, L->base); - L->nCcalls = L->baseCcalls; - L->allowhook = 1; - restore_stack_limit(L); - L->errfunc = 0; - L->errorJmp = NULL; -} - - -void luaD_throw (lua_State *L, int errcode) { - if (L->errorJmp) { - L->errorJmp->status = errcode; - LUAI_THROW(L, L->errorJmp); - } - else { - L->status = cast_byte(errcode); - if (G(L)->panic) { - resetstack(L, errcode); - lua_unlock(L); - G(L)->panic(L); - } - exit(EXIT_FAILURE); - } -} - - -int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { - struct lua_longjmp lj; - lj.status = 0; - lj.previous = L->errorJmp; /* chain new error handler */ - L->errorJmp = &lj; - LUAI_TRY(L, &lj, - (*f)(L, ud); - ); - L->errorJmp = lj.previous; /* restore old error handler */ - return lj.status; -} - -/* }====================================================== */ - - -static void correctstack (lua_State *L, TValue *oldstack) { - CallInfo *ci; - GCObject *up; - L->top = (L->top - oldstack) + L->stack; - for (up = L->openupval; up != NULL; up = up->gch.next) - gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; - for (ci = L->base_ci; ci <= L->ci; ci++) { - ci->top = (ci->top - oldstack) + L->stack; - ci->base = (ci->base - oldstack) + L->stack; - ci->func = (ci->func - oldstack) + L->stack; - } - L->base = (L->base - oldstack) + L->stack; -} - - -void luaD_reallocstack (lua_State *L, int newsize) { - TValue *oldstack = L->stack; - int realsize = newsize + 1 + EXTRA_STACK; - lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); - luaM_reallocvector(L, L->stack, L->stacksize, realsize, TValue); - L->stacksize = realsize; - L->stack_last = L->stack+newsize; - correctstack(L, oldstack); -} - - -void luaD_reallocCI (lua_State *L, int newsize) { - CallInfo *oldci = L->base_ci; - luaM_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo); - L->size_ci = newsize; - L->ci = (L->ci - oldci) + L->base_ci; - L->end_ci = L->base_ci + L->size_ci - 1; -} - - -void luaD_growstack (lua_State *L, int n) { - if (n <= L->stacksize) /* double size is enough? */ - luaD_reallocstack(L, 2*L->stacksize); - else - luaD_reallocstack(L, L->stacksize + n); -} - - -static CallInfo *growCI (lua_State *L) { - if (L->size_ci > LUAI_MAXCALLS) /* overflow while handling overflow? */ - luaD_throw(L, LUA_ERRERR); - else { - luaD_reallocCI(L, 2*L->size_ci); - if (L->size_ci > LUAI_MAXCALLS) - luaG_runerror(L, "stack overflow"); - } - return ++L->ci; -} - - -void luaD_callhook (lua_State *L, int event, int line) { - lua_Hook hook = L->hook; - if (hook && L->allowhook) { - ptrdiff_t top = savestack(L, L->top); - ptrdiff_t ci_top = savestack(L, L->ci->top); - lua_Debug ar; - ar.event = event; - ar.currentline = line; - if (event == LUA_HOOKTAILRET) - ar.i_ci = 0; /* tail call; no debug information about it */ - else - ar.i_ci = cast_int(L->ci - L->base_ci); - luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ - L->ci->top = L->top + LUA_MINSTACK; - lua_assert(L->ci->top <= L->stack_last); - L->allowhook = 0; /* cannot call hooks inside a hook */ - lua_unlock(L); - (*hook)(L, &ar); - lua_lock(L); - lua_assert(!L->allowhook); - L->allowhook = 1; - L->ci->top = restorestack(L, ci_top); - L->top = restorestack(L, top); - } -} - - -static StkId adjust_varargs (lua_State *L, Proto *p, int actual) { - int i; - int nfixargs = p->numparams; - Table *htab = NULL; - StkId base, fixed; - for (; actual < nfixargs; ++actual) - setnilvalue(L->top++); -#if defined(LUA_COMPAT_VARARG) - if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */ - int nvar = actual - nfixargs; /* number of extra arguments */ - lua_assert(p->is_vararg & VARARG_HASARG); - luaC_checkGC(L); - luaD_checkstack(L, p->maxstacksize); - htab = luaH_new(L, nvar, 1); /* create `arg' table */ - for (i=0; itop - nvar + i); - /* store counter in field `n' */ - setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, "n")), cast_num(nvar)); - } -#endif - /* move fixed parameters to final position */ - fixed = L->top - actual; /* first fixed argument */ - base = L->top; /* final position of first argument */ - for (i=0; itop++, fixed+i); - setnilvalue(fixed+i); - } - /* add `arg' parameter */ - if (htab) { - sethvalue(L, L->top++, htab); - lua_assert(iswhite(obj2gco(htab))); - } - return base; -} - - -static StkId tryfuncTM (lua_State *L, StkId func) { - const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL); - StkId p; - ptrdiff_t funcr = savestack(L, func); - if (!ttisfunction(tm)) - luaG_typeerror(L, func, "call"); - /* Open a hole inside the stack at `func' */ - for (p = L->top; p > func; p--) setobjs2s(L, p, p-1); - incr_top(L); - func = restorestack(L, funcr); /* previous call may change stack */ - setobj2s(L, func, tm); /* tag method is the new function to be called */ - return func; -} - - - -#define inc_ci(L) \ - ((L->ci == L->end_ci) ? growCI(L) : \ - (condhardstacktests(luaD_reallocCI(L, L->size_ci)), ++L->ci)) - - -int luaD_precall (lua_State *L, StkId func, int nresults) { - LClosure *cl; - ptrdiff_t funcr; - if (!ttisfunction(func)) /* `func' is not a function? */ - func = tryfuncTM(L, func); /* check the `function' tag method */ - funcr = savestack(L, func); - cl = &clvalue(func)->l; - L->ci->savedpc = L->savedpc; - if (!cl->isC) { /* Lua function? prepare its call */ - CallInfo *ci; - StkId st, base; - Proto *p = cl->p; - luaD_checkstack(L, p->maxstacksize); - func = restorestack(L, funcr); - if (!p->is_vararg) { /* no varargs? */ - base = func + 1; - if (L->top > base + p->numparams) - L->top = base + p->numparams; - } - else { /* vararg function */ - int nargs = cast_int(L->top - func) - 1; - base = adjust_varargs(L, p, nargs); - func = restorestack(L, funcr); /* previous call may change the stack */ - } - ci = inc_ci(L); /* now `enter' new function */ - ci->func = func; - L->base = ci->base = base; - ci->top = L->base + p->maxstacksize; - lua_assert(ci->top <= L->stack_last); - L->savedpc = p->code; /* starting point */ - ci->tailcalls = 0; - ci->nresults = nresults; - for (st = L->top; st < ci->top; st++) - setnilvalue(st); - L->top = ci->top; - if (L->hookmask & LUA_MASKCALL) { - L->savedpc++; /* hooks assume 'pc' is already incremented */ - luaD_callhook(L, LUA_HOOKCALL, -1); - L->savedpc--; /* correct 'pc' */ - } - return PCRLUA; - } - else { /* if is a C function, call it */ - CallInfo *ci; - int n; - luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ - ci = inc_ci(L); /* now `enter' new function */ - ci->func = restorestack(L, funcr); - L->base = ci->base = ci->func + 1; - ci->top = L->top + LUA_MINSTACK; - lua_assert(ci->top <= L->stack_last); - ci->nresults = nresults; - if (L->hookmask & LUA_MASKCALL) - luaD_callhook(L, LUA_HOOKCALL, -1); - lua_unlock(L); - n = (*curr_func(L)->c.f)(L); /* do the actual call */ - lua_lock(L); - if (n < 0) /* yielding? */ - return PCRYIELD; - else { - luaD_poscall(L, L->top - n); - return PCRC; - } - } -} - - -static StkId callrethooks (lua_State *L, StkId firstResult) { - ptrdiff_t fr = savestack(L, firstResult); /* next call may change stack */ - luaD_callhook(L, LUA_HOOKRET, -1); - if (f_isLua(L->ci)) { /* Lua function? */ - while ((L->hookmask & LUA_MASKRET) && L->ci->tailcalls--) /* tail calls */ - luaD_callhook(L, LUA_HOOKTAILRET, -1); - } - return restorestack(L, fr); -} - - -int luaD_poscall (lua_State *L, StkId firstResult) { - StkId res; - int wanted, i; - CallInfo *ci; - if (L->hookmask & LUA_MASKRET) - firstResult = callrethooks(L, firstResult); - ci = L->ci--; - res = ci->func; /* res == final position of 1st result */ - wanted = ci->nresults; - L->base = (ci - 1)->base; /* restore base */ - L->savedpc = (ci - 1)->savedpc; /* restore savedpc */ - /* move results to correct place */ - for (i = wanted; i != 0 && firstResult < L->top; i--) - setobjs2s(L, res++, firstResult++); - while (i-- > 0) - setnilvalue(res++); - L->top = res; - return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */ -} - - -/* -** Call a function (C or Lua). The function to be called is at *func. -** The arguments are on the stack, right after the function. -** When returns, all the results are on the stack, starting at the original -** function position. -*/ -void luaD_call (lua_State *L, StkId func, int nResults) { - if (++L->nCcalls >= LUAI_MAXCCALLS) { - if (L->nCcalls == LUAI_MAXCCALLS) - luaG_runerror(L, "C stack overflow"); - else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) - luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ - } - if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */ - luaV_execute(L, 1); /* call it */ - L->nCcalls--; - luaC_checkGC(L); -} - - -static void resume (lua_State *L, void *ud) { - StkId firstArg = cast(StkId, ud); - CallInfo *ci = L->ci; - if (L->status == 0) { /* start coroutine? */ - lua_assert(ci == L->base_ci && firstArg > L->base); - if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA) - return; - } - else { /* resuming from previous yield */ - lua_assert(L->status == LUA_YIELD); - L->status = 0; - if (!f_isLua(ci)) { /* `common' yield? */ - /* finish interrupted execution of `OP_CALL' */ - lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL || - GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL); - if (luaD_poscall(L, firstArg)) /* complete it... */ - L->top = L->ci->top; /* and correct top if not multiple results */ - } - else /* yielded inside a hook: just continue its execution */ - L->base = L->ci->base; - } - luaV_execute(L, cast_int(L->ci - L->base_ci)); -} - - -static int resume_error (lua_State *L, const char *msg) { - L->top = L->ci->base; - setsvalue2s(L, L->top, luaS_new(L, msg)); - incr_top(L); - lua_unlock(L); - return LUA_ERRRUN; -} - - -LUA_API int lua_resume (lua_State *L, int nargs) { - int status; - lua_lock(L); - if (L->status != LUA_YIELD && (L->status != 0 || L->ci != L->base_ci)) - return resume_error(L, "cannot resume non-suspended coroutine"); - if (L->nCcalls >= LUAI_MAXCCALLS) - return resume_error(L, "C stack overflow"); - luai_userstateresume(L, nargs); - lua_assert(L->errfunc == 0); - L->baseCcalls = ++L->nCcalls; - status = luaD_rawrunprotected(L, resume, L->top - nargs); - if (status != 0) { /* error? */ - L->status = cast_byte(status); /* mark thread as `dead' */ - luaD_seterrorobj(L, status, L->top); - L->ci->top = L->top; - } - else { - lua_assert(L->nCcalls == L->baseCcalls); - status = L->status; - } - --L->nCcalls; - lua_unlock(L); - return status; -} - - -LUA_API int lua_yield (lua_State *L, int nresults) { - luai_userstateyield(L, nresults); - lua_lock(L); - if (L->nCcalls > L->baseCcalls) - luaG_runerror(L, "attempt to yield across metamethod/C-call boundary"); - L->base = L->top - nresults; /* protect stack slots below */ - L->status = LUA_YIELD; - lua_unlock(L); - return -1; -} - - -int luaD_pcall (lua_State *L, Pfunc func, void *u, - ptrdiff_t old_top, ptrdiff_t ef) { - int status; - unsigned short oldnCcalls = L->nCcalls; - ptrdiff_t old_ci = saveci(L, L->ci); - lu_byte old_allowhooks = L->allowhook; - ptrdiff_t old_errfunc = L->errfunc; - L->errfunc = ef; - status = luaD_rawrunprotected(L, func, u); - if (status != 0) { /* an error occurred? */ - StkId oldtop = restorestack(L, old_top); - luaF_close(L, oldtop); /* close eventual pending closures */ - luaD_seterrorobj(L, status, oldtop); - L->nCcalls = oldnCcalls; - L->ci = restoreci(L, old_ci); - L->base = L->ci->base; - L->savedpc = L->ci->savedpc; - L->allowhook = old_allowhooks; - restore_stack_limit(L); - } - L->errfunc = old_errfunc; - return status; -} - - - -/* -** Execute a protected parser. -*/ -struct SParser { /* data to `f_parser' */ - ZIO *z; - Mbuffer buff; /* buffer to be used by the scanner */ - const char *name; -}; - -static void f_parser (lua_State *L, void *ud) { - int i; - Proto *tf; - Closure *cl; - struct SParser *p = cast(struct SParser *, ud); - int c = luaZ_lookahead(p->z); - luaC_checkGC(L); - tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z, - &p->buff, p->name); - cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L))); - cl->l.p = tf; - for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */ - cl->l.upvals[i] = luaF_newupval(L); - setclvalue(L, L->top, cl); - incr_top(L); -} - - -int luaD_protectedparser (lua_State *L, ZIO *z, const char *name) { - struct SParser p; - int status; - p.z = z; p.name = name; - luaZ_initbuffer(L, &p.buff); - status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); - luaZ_freebuffer(L, &p.buff); - return status; -} - - diff --git a/libraries/lua/ldo.h b/libraries/lua/ldo.h deleted file mode 100644 index 98fddac59..000000000 --- a/libraries/lua/ldo.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $ -** Stack and Call structure of Lua -** See Copyright Notice in lua.h -*/ - -#ifndef ldo_h -#define ldo_h - - -#include "lobject.h" -#include "lstate.h" -#include "lzio.h" - - -#define luaD_checkstack(L,n) \ - if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \ - luaD_growstack(L, n); \ - else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); - - -#define incr_top(L) {luaD_checkstack(L,1); L->top++;} - -#define savestack(L,p) ((char *)(p) - (char *)L->stack) -#define restorestack(L,n) ((TValue *)((char *)L->stack + (n))) - -#define saveci(L,p) ((char *)(p) - (char *)L->base_ci) -#define restoreci(L,n) ((CallInfo *)((char *)L->base_ci + (n))) - - -/* results from luaD_precall */ -#define PCRLUA 0 /* initiated a call to a Lua function */ -#define PCRC 1 /* did a call to a C function */ -#define PCRYIELD 2 /* C funtion yielded */ - - -/* type of protected functions, to be ran by `runprotected' */ -typedef void (*Pfunc) (lua_State *L, void *ud); - -LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name); -LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line); -LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults); -LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); -LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, - ptrdiff_t oldtop, ptrdiff_t ef); -LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult); -LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize); -LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize); -LUAI_FUNC void luaD_growstack (lua_State *L, int n); - -LUAI_FUNC void luaD_throw (lua_State *L, int errcode); -LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); - -LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); - -#endif - diff --git a/libraries/lua/ldump.c b/libraries/lua/ldump.c deleted file mode 100644 index c9d3d4870..000000000 --- a/libraries/lua/ldump.c +++ /dev/null @@ -1,164 +0,0 @@ -/* -** $Id: ldump.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ -** save precompiled Lua chunks -** See Copyright Notice in lua.h -*/ - -#include - -#define ldump_c -#define LUA_CORE - -#include "lua.h" - -#include "lobject.h" -#include "lstate.h" -#include "lundump.h" - -typedef struct { - lua_State* L; - lua_Writer writer; - void* data; - int strip; - int status; -} DumpState; - -#define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D) -#define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D) - -static void DumpBlock(const void* b, size_t size, DumpState* D) -{ - if (D->status==0) - { - lua_unlock(D->L); - D->status=(*D->writer)(D->L,b,size,D->data); - lua_lock(D->L); - } -} - -static void DumpChar(int y, DumpState* D) -{ - char x=(char)y; - DumpVar(x,D); -} - -static void DumpInt(int x, DumpState* D) -{ - DumpVar(x,D); -} - -static void DumpNumber(lua_Number x, DumpState* D) -{ - DumpVar(x,D); -} - -static void DumpVector(const void* b, int n, size_t size, DumpState* D) -{ - DumpInt(n,D); - DumpMem(b,n,size,D); -} - -static void DumpString(const TString* s, DumpState* D) -{ - if (s==NULL || getstr(s)==NULL) - { - size_t size=0; - DumpVar(size,D); - } - else - { - size_t size=s->tsv.len+1; /* include trailing '\0' */ - DumpVar(size,D); - DumpBlock(getstr(s),size,D); - } -} - -#define DumpCode(f,D) DumpVector(f->code,f->sizecode,sizeof(Instruction),D) - -static void DumpFunction(const Proto* f, const TString* p, DumpState* D); - -static void DumpConstants(const Proto* f, DumpState* D) -{ - int i,n=f->sizek; - DumpInt(n,D); - for (i=0; ik[i]; - DumpChar(ttype(o),D); - switch (ttype(o)) - { - case LUA_TNIL: - break; - case LUA_TBOOLEAN: - DumpChar(bvalue(o),D); - break; - case LUA_TNUMBER: - DumpNumber(nvalue(o),D); - break; - case LUA_TSTRING: - DumpString(rawtsvalue(o),D); - break; - default: - lua_assert(0); /* cannot happen */ - break; - } - } - n=f->sizep; - DumpInt(n,D); - for (i=0; ip[i],f->source,D); -} - -static void DumpDebug(const Proto* f, DumpState* D) -{ - int i,n; - n= (D->strip) ? 0 : f->sizelineinfo; - DumpVector(f->lineinfo,n,sizeof(int),D); - n= (D->strip) ? 0 : f->sizelocvars; - DumpInt(n,D); - for (i=0; ilocvars[i].varname,D); - DumpInt(f->locvars[i].startpc,D); - DumpInt(f->locvars[i].endpc,D); - } - n= (D->strip) ? 0 : f->sizeupvalues; - DumpInt(n,D); - for (i=0; iupvalues[i],D); -} - -static void DumpFunction(const Proto* f, const TString* p, DumpState* D) -{ - DumpString((f->source==p || D->strip) ? NULL : f->source,D); - DumpInt(f->linedefined,D); - DumpInt(f->lastlinedefined,D); - DumpChar(f->nups,D); - DumpChar(f->numparams,D); - DumpChar(f->is_vararg,D); - DumpChar(f->maxstacksize,D); - DumpCode(f,D); - DumpConstants(f,D); - DumpDebug(f,D); -} - -static void DumpHeader(DumpState* D) -{ - char h[LUAC_HEADERSIZE]; - luaU_header(h); - DumpBlock(h,LUAC_HEADERSIZE,D); -} - -/* -** dump Lua function as precompiled chunk -*/ -int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip) -{ - DumpState D; - D.L=L; - D.writer=w; - D.data=data; - D.strip=strip; - D.status=0; - DumpHeader(&D); - DumpFunction(f,NULL,&D); - return D.status; -} diff --git a/libraries/lua/lfunc.c b/libraries/lua/lfunc.c deleted file mode 100644 index 813e88f58..000000000 --- a/libraries/lua/lfunc.c +++ /dev/null @@ -1,174 +0,0 @@ -/* -** $Id: lfunc.c,v 2.12.1.2 2007/12/28 14:58:43 roberto Exp $ -** Auxiliary functions to manipulate prototypes and closures -** See Copyright Notice in lua.h -*/ - - -#include - -#define lfunc_c -#define LUA_CORE - -#include "lua.h" - -#include "lfunc.h" -#include "lgc.h" -#include "lmem.h" -#include "lobject.h" -#include "lstate.h" - - - -Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e) { - Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems))); - luaC_link(L, obj2gco(c), LUA_TFUNCTION); - c->c.isC = 1; - c->c.env = e; - c->c.nupvalues = cast_byte(nelems); - return c; -} - - -Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e) { - Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems))); - luaC_link(L, obj2gco(c), LUA_TFUNCTION); - c->l.isC = 0; - c->l.env = e; - c->l.nupvalues = cast_byte(nelems); - while (nelems--) c->l.upvals[nelems] = NULL; - return c; -} - - -UpVal *luaF_newupval (lua_State *L) { - UpVal *uv = luaM_new(L, UpVal); - luaC_link(L, obj2gco(uv), LUA_TUPVAL); - uv->v = &uv->u.value; - setnilvalue(uv->v); - return uv; -} - - -UpVal *luaF_findupval (lua_State *L, StkId level) { - global_State *g = G(L); - GCObject **pp = &L->openupval; - UpVal *p; - UpVal *uv; - while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) { - lua_assert(p->v != &p->u.value); - if (p->v == level) { /* found a corresponding upvalue? */ - if (isdead(g, obj2gco(p))) /* is it dead? */ - changewhite(obj2gco(p)); /* ressurect it */ - return p; - } - pp = &p->next; - } - uv = luaM_new(L, UpVal); /* not found: create a new one */ - uv->tt = LUA_TUPVAL; - uv->marked = luaC_white(g); - uv->v = level; /* current value lives in the stack */ - uv->next = *pp; /* chain it in the proper position */ - *pp = obj2gco(uv); - uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */ - uv->u.l.next = g->uvhead.u.l.next; - uv->u.l.next->u.l.prev = uv; - g->uvhead.u.l.next = uv; - lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); - return uv; -} - - -static void unlinkupval (UpVal *uv) { - lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); - uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */ - uv->u.l.prev->u.l.next = uv->u.l.next; -} - - -void luaF_freeupval (lua_State *L, UpVal *uv) { - if (uv->v != &uv->u.value) /* is it open? */ - unlinkupval(uv); /* remove from open list */ - luaM_free(L, uv); /* free upvalue */ -} - - -void luaF_close (lua_State *L, StkId level) { - UpVal *uv; - global_State *g = G(L); - while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) { - GCObject *o = obj2gco(uv); - lua_assert(!isblack(o) && uv->v != &uv->u.value); - L->openupval = uv->next; /* remove from `open' list */ - if (isdead(g, o)) - luaF_freeupval(L, uv); /* free upvalue */ - else { - unlinkupval(uv); - setobj(L, &uv->u.value, uv->v); - uv->v = &uv->u.value; /* now current value lives here */ - luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */ - } - } -} - - -Proto *luaF_newproto (lua_State *L) { - Proto *f = luaM_new(L, Proto); - luaC_link(L, obj2gco(f), LUA_TPROTO); - f->k = NULL; - f->sizek = 0; - f->p = NULL; - f->sizep = 0; - f->code = NULL; - f->sizecode = 0; - f->sizelineinfo = 0; - f->sizeupvalues = 0; - f->nups = 0; - f->upvalues = NULL; - f->numparams = 0; - f->is_vararg = 0; - f->maxstacksize = 0; - f->lineinfo = NULL; - f->sizelocvars = 0; - f->locvars = NULL; - f->linedefined = 0; - f->lastlinedefined = 0; - f->source = NULL; - return f; -} - - -void luaF_freeproto (lua_State *L, Proto *f) { - luaM_freearray(L, f->code, f->sizecode, Instruction); - luaM_freearray(L, f->p, f->sizep, Proto *); - luaM_freearray(L, f->k, f->sizek, TValue); - luaM_freearray(L, f->lineinfo, f->sizelineinfo, int); - luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar); - luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *); - luaM_free(L, f); -} - - -void luaF_freeclosure (lua_State *L, Closure *c) { - int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) : - sizeLclosure(c->l.nupvalues); - luaM_freemem(L, c, size); -} - - -/* -** Look for n-th local variable at line `line' in function `func'. -** Returns NULL if not found. -*/ -const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { - int i; - for (i = 0; isizelocvars && f->locvars[i].startpc <= pc; i++) { - if (pc < f->locvars[i].endpc) { /* is variable active? */ - local_number--; - if (local_number == 0) - return getstr(f->locvars[i].varname); - } - } - return NULL; /* not found */ -} - diff --git a/libraries/lua/lfunc.h b/libraries/lua/lfunc.h deleted file mode 100644 index a68cf5151..000000000 --- a/libraries/lua/lfunc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* -** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $ -** Auxiliary functions to manipulate prototypes and closures -** See Copyright Notice in lua.h -*/ - -#ifndef lfunc_h -#define lfunc_h - - -#include "lobject.h" - - -#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \ - cast(int, sizeof(TValue)*((n)-1))) - -#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \ - cast(int, sizeof(TValue *)*((n)-1))) - - -LUAI_FUNC Proto *luaF_newproto (lua_State *L); -LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e); -LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e); -LUAI_FUNC UpVal *luaF_newupval (lua_State *L); -LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); -LUAI_FUNC void luaF_close (lua_State *L, StkId level); -LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); -LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c); -LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv); -LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, - int pc); - - -#endif diff --git a/libraries/lua/lgc.c b/libraries/lua/lgc.c deleted file mode 100644 index e909c79a9..000000000 --- a/libraries/lua/lgc.c +++ /dev/null @@ -1,710 +0,0 @@ -/* -** $Id: lgc.c,v 2.38.1.2 2011/03/18 18:05:38 roberto Exp $ -** Garbage Collector -** See Copyright Notice in lua.h -*/ - -#include - -#define lgc_c -#define LUA_CORE - -#include "lua.h" - -#include "ldebug.h" -#include "ldo.h" -#include "lfunc.h" -#include "lgc.h" -#include "lmem.h" -#include "lobject.h" -#include "lstate.h" -#include "lstring.h" -#include "ltable.h" -#include "ltm.h" - - -#define GCSTEPSIZE 1024u -#define GCSWEEPMAX 40 -#define GCSWEEPCOST 10 -#define GCFINALIZECOST 100 - - -#define maskmarks cast_byte(~(bitmask(BLACKBIT)|WHITEBITS)) - -#define makewhite(g,x) \ - ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g))) - -#define white2gray(x) reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) -#define black2gray(x) resetbit((x)->gch.marked, BLACKBIT) - -#define stringmark(s) reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT) - - -#define isfinalized(u) testbit((u)->marked, FINALIZEDBIT) -#define markfinalized(u) l_setbit((u)->marked, FINALIZEDBIT) - - -#define KEYWEAK bitmask(KEYWEAKBIT) -#define VALUEWEAK bitmask(VALUEWEAKBIT) - - - -#define markvalue(g,o) { checkconsistency(o); \ - if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); } - -#define markobject(g,t) { if (iswhite(obj2gco(t))) \ - reallymarkobject(g, obj2gco(t)); } - - -#define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) - - -static void removeentry (Node *n) { - lua_assert(ttisnil(gval(n))); - if (iscollectable(gkey(n))) - setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */ -} - - -static void reallymarkobject (global_State *g, GCObject *o) { - lua_assert(iswhite(o) && !isdead(g, o)); - white2gray(o); - switch (o->gch.tt) { - case LUA_TSTRING: { - return; - } - case LUA_TUSERDATA: { - Table *mt = gco2u(o)->metatable; - gray2black(o); /* udata are never gray */ - if (mt) markobject(g, mt); - markobject(g, gco2u(o)->env); - return; - } - case LUA_TUPVAL: { - UpVal *uv = gco2uv(o); - markvalue(g, uv->v); - if (uv->v == &uv->u.value) /* closed? */ - gray2black(o); /* open upvalues are never black */ - return; - } - case LUA_TFUNCTION: { - gco2cl(o)->c.gclist = g->gray; - g->gray = o; - break; - } - case LUA_TTABLE: { - gco2h(o)->gclist = g->gray; - g->gray = o; - break; - } - case LUA_TTHREAD: { - gco2th(o)->gclist = g->gray; - g->gray = o; - break; - } - case LUA_TPROTO: { - gco2p(o)->gclist = g->gray; - g->gray = o; - break; - } - default: lua_assert(0); - } -} - - -static void marktmu (global_State *g) { - GCObject *u = g->tmudata; - if (u) { - do { - u = u->gch.next; - makewhite(g, u); /* may be marked, if left from previous GC */ - reallymarkobject(g, u); - } while (u != g->tmudata); - } -} - - -/* move `dead' udata that need finalization to list `tmudata' */ -size_t luaC_separateudata (lua_State *L, int all) { - global_State *g = G(L); - size_t deadmem = 0; - GCObject **p = &g->mainthread->next; - GCObject *curr; - while ((curr = *p) != NULL) { - if (!(iswhite(curr) || all) || isfinalized(gco2u(curr))) - p = &curr->gch.next; /* don't bother with them */ - else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) { - markfinalized(gco2u(curr)); /* don't need finalization */ - p = &curr->gch.next; - } - else { /* must call its gc method */ - deadmem += sizeudata(gco2u(curr)); - markfinalized(gco2u(curr)); - *p = curr->gch.next; - /* link `curr' at the end of `tmudata' list */ - if (g->tmudata == NULL) /* list is empty? */ - g->tmudata = curr->gch.next = curr; /* creates a circular list */ - else { - curr->gch.next = g->tmudata->gch.next; - g->tmudata->gch.next = curr; - g->tmudata = curr; - } - } - } - return deadmem; -} - - -static int traversetable (global_State *g, Table *h) { - int i; - int weakkey = 0; - int weakvalue = 0; - const TValue *mode; - if (h->metatable) - markobject(g, h->metatable); - mode = gfasttm(g, h->metatable, TM_MODE); - if (mode && ttisstring(mode)) { /* is there a weak mode? */ - weakkey = (strchr(svalue(mode), 'k') != NULL); - weakvalue = (strchr(svalue(mode), 'v') != NULL); - if (weakkey || weakvalue) { /* is really weak? */ - h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ - h->marked |= cast_byte((weakkey << KEYWEAKBIT) | - (weakvalue << VALUEWEAKBIT)); - h->gclist = g->weak; /* must be cleared after GC, ... */ - g->weak = obj2gco(h); /* ... so put in the appropriate list */ - } - } - if (weakkey && weakvalue) return 1; - if (!weakvalue) { - i = h->sizearray; - while (i--) - markvalue(g, &h->array[i]); - } - i = sizenode(h); - while (i--) { - Node *n = gnode(h, i); - lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); - if (ttisnil(gval(n))) - removeentry(n); /* remove empty entries */ - else { - lua_assert(!ttisnil(gkey(n))); - if (!weakkey) markvalue(g, gkey(n)); - if (!weakvalue) markvalue(g, gval(n)); - } - } - return weakkey || weakvalue; -} - - -/* -** All marks are conditional because a GC may happen while the -** prototype is still being created -*/ -static void traverseproto (global_State *g, Proto *f) { - int i; - if (f->source) stringmark(f->source); - for (i=0; isizek; i++) /* mark literals */ - markvalue(g, &f->k[i]); - for (i=0; isizeupvalues; i++) { /* mark upvalue names */ - if (f->upvalues[i]) - stringmark(f->upvalues[i]); - } - for (i=0; isizep; i++) { /* mark nested protos */ - if (f->p[i]) - markobject(g, f->p[i]); - } - for (i=0; isizelocvars; i++) { /* mark local-variable names */ - if (f->locvars[i].varname) - stringmark(f->locvars[i].varname); - } -} - - - -static void traverseclosure (global_State *g, Closure *cl) { - markobject(g, cl->c.env); - if (cl->c.isC) { - int i; - for (i=0; ic.nupvalues; i++) /* mark its upvalues */ - markvalue(g, &cl->c.upvalue[i]); - } - else { - int i; - lua_assert(cl->l.nupvalues == cl->l.p->nups); - markobject(g, cl->l.p); - for (i=0; il.nupvalues; i++) /* mark its upvalues */ - markobject(g, cl->l.upvals[i]); - } -} - - -static void checkstacksizes (lua_State *L, StkId max) { - int ci_used = cast_int(L->ci - L->base_ci); /* number of `ci' in use */ - int s_used = cast_int(max - L->stack); /* part of stack in use */ - if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */ - return; /* do not touch the stacks */ - if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci) - luaD_reallocCI(L, L->size_ci/2); /* still big enough... */ - condhardstacktests(luaD_reallocCI(L, ci_used + 1)); - if (4*s_used < L->stacksize && - 2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize) - luaD_reallocstack(L, L->stacksize/2); /* still big enough... */ - condhardstacktests(luaD_reallocstack(L, s_used)); -} - - -static void traversestack (global_State *g, lua_State *l) { - StkId o, lim; - CallInfo *ci; - markvalue(g, gt(l)); - lim = l->top; - for (ci = l->base_ci; ci <= l->ci; ci++) { - lua_assert(ci->top <= l->stack_last); - if (lim < ci->top) lim = ci->top; - } - for (o = l->stack; o < l->top; o++) - markvalue(g, o); - for (; o <= lim; o++) - setnilvalue(o); - checkstacksizes(l, lim); -} - - -/* -** traverse one gray object, turning it to black. -** Returns `quantity' traversed. -*/ -static l_mem propagatemark (global_State *g) { - GCObject *o = g->gray; - lua_assert(isgray(o)); - gray2black(o); - switch (o->gch.tt) { - case LUA_TTABLE: { - Table *h = gco2h(o); - g->gray = h->gclist; - if (traversetable(g, h)) /* table is weak? */ - black2gray(o); /* keep it gray */ - return sizeof(Table) + sizeof(TValue) * h->sizearray + - sizeof(Node) * sizenode(h); - } - case LUA_TFUNCTION: { - Closure *cl = gco2cl(o); - g->gray = cl->c.gclist; - traverseclosure(g, cl); - return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) : - sizeLclosure(cl->l.nupvalues); - } - case LUA_TTHREAD: { - lua_State *th = gco2th(o); - g->gray = th->gclist; - th->gclist = g->grayagain; - g->grayagain = o; - black2gray(o); - traversestack(g, th); - return sizeof(lua_State) + sizeof(TValue) * th->stacksize + - sizeof(CallInfo) * th->size_ci; - } - case LUA_TPROTO: { - Proto *p = gco2p(o); - g->gray = p->gclist; - traverseproto(g, p); - return sizeof(Proto) + sizeof(Instruction) * p->sizecode + - sizeof(Proto *) * p->sizep + - sizeof(TValue) * p->sizek + - sizeof(int) * p->sizelineinfo + - sizeof(LocVar) * p->sizelocvars + - sizeof(TString *) * p->sizeupvalues; - } - default: lua_assert(0); return 0; - } -} - - -static size_t propagateall (global_State *g) { - size_t m = 0; - while (g->gray) m += propagatemark(g); - return m; -} - - -/* -** The next function tells whether a key or value can be cleared from -** a weak table. Non-collectable objects are never removed from weak -** tables. Strings behave as `values', so are never removed too. for -** other objects: if really collected, cannot keep them; for userdata -** being finalized, keep them in keys, but not in values -*/ -static int iscleared (const TValue *o, int iskey) { - if (!iscollectable(o)) return 0; - if (ttisstring(o)) { - stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ - return 0; - } - return iswhite(gcvalue(o)) || - (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); -} - - -/* -** clear collected entries from weaktables -*/ -static void cleartable (GCObject *l) { - while (l) { - Table *h = gco2h(l); - int i = h->sizearray; - lua_assert(testbit(h->marked, VALUEWEAKBIT) || - testbit(h->marked, KEYWEAKBIT)); - if (testbit(h->marked, VALUEWEAKBIT)) { - while (i--) { - TValue *o = &h->array[i]; - if (iscleared(o, 0)) /* value was collected? */ - setnilvalue(o); /* remove value */ - } - } - i = sizenode(h); - while (i--) { - Node *n = gnode(h, i); - if (!ttisnil(gval(n)) && /* non-empty entry? */ - (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) { - setnilvalue(gval(n)); /* remove value ... */ - removeentry(n); /* remove entry from table */ - } - } - l = h->gclist; - } -} - - -static void freeobj (lua_State *L, GCObject *o) { - switch (o->gch.tt) { - case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; - case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break; - case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; - case LUA_TTABLE: luaH_free(L, gco2h(o)); break; - case LUA_TTHREAD: { - lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread); - luaE_freethread(L, gco2th(o)); - break; - } - case LUA_TSTRING: { - G(L)->strt.nuse--; - luaM_freemem(L, o, sizestring(gco2ts(o))); - break; - } - case LUA_TUSERDATA: { - luaM_freemem(L, o, sizeudata(gco2u(o))); - break; - } - default: lua_assert(0); - } -} - - - -#define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) - - -static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { - GCObject *curr; - global_State *g = G(L); - int deadmask = otherwhite(g); - while ((curr = *p) != NULL && count-- > 0) { - if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */ - sweepwholelist(L, &gco2th(curr)->openupval); - if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */ - lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); - makewhite(g, curr); /* make it white (for next cycle) */ - p = &curr->gch.next; - } - else { /* must erase `curr' */ - lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); - *p = curr->gch.next; - if (curr == g->rootgc) /* is the first element of the list? */ - g->rootgc = curr->gch.next; /* adjust first */ - freeobj(L, curr); - } - } - return p; -} - - -static void checkSizes (lua_State *L) { - global_State *g = G(L); - /* check size of string hash */ - if (g->strt.nuse < cast(lu_int32, g->strt.size/4) && - g->strt.size > MINSTRTABSIZE*2) - luaS_resize(L, g->strt.size/2); /* table is too big */ - /* check size of buffer */ - if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */ - size_t newsize = luaZ_sizebuffer(&g->buff) / 2; - luaZ_resizebuffer(L, &g->buff, newsize); - } -} - - -static void GCTM (lua_State *L) { - global_State *g = G(L); - GCObject *o = g->tmudata->gch.next; /* get first element */ - Udata *udata = rawgco2u(o); - const TValue *tm; - /* remove udata from `tmudata' */ - if (o == g->tmudata) /* last element? */ - g->tmudata = NULL; - else - g->tmudata->gch.next = udata->uv.next; - udata->uv.next = g->mainthread->next; /* return it to `root' list */ - g->mainthread->next = o; - makewhite(g, o); - tm = fasttm(L, udata->uv.metatable, TM_GC); - if (tm != NULL) { - lu_byte oldah = L->allowhook; - lu_mem oldt = g->GCthreshold; - L->allowhook = 0; /* stop debug hooks during GC tag method */ - g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */ - setobj2s(L, L->top, tm); - setuvalue(L, L->top+1, udata); - L->top += 2; - luaD_call(L, L->top - 2, 0); - L->allowhook = oldah; /* restore hooks */ - g->GCthreshold = oldt; /* restore threshold */ - } -} - - -/* -** Call all GC tag methods -*/ -void luaC_callGCTM (lua_State *L) { - while (G(L)->tmudata) - GCTM(L); -} - - -void luaC_freeall (lua_State *L) { - global_State *g = G(L); - int i; - g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* mask to collect all elements */ - sweepwholelist(L, &g->rootgc); - for (i = 0; i < g->strt.size; i++) /* free all string lists */ - sweepwholelist(L, &g->strt.hash[i]); -} - - -static void markmt (global_State *g) { - int i; - for (i=0; imt[i]) markobject(g, g->mt[i]); -} - - -/* mark root set */ -static void markroot (lua_State *L) { - global_State *g = G(L); - g->gray = NULL; - g->grayagain = NULL; - g->weak = NULL; - markobject(g, g->mainthread); - /* make global table be traversed before main stack */ - markvalue(g, gt(g->mainthread)); - markvalue(g, registry(L)); - markmt(g); - g->gcstate = GCSpropagate; -} - - -static void remarkupvals (global_State *g) { - UpVal *uv; - for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { - lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); - if (isgray(obj2gco(uv))) - markvalue(g, uv->v); - } -} - - -static void atomic (lua_State *L) { - global_State *g = G(L); - size_t udsize; /* total size of userdata to be finalized */ - /* remark occasional upvalues of (maybe) dead threads */ - remarkupvals(g); - /* traverse objects cautch by write barrier and by 'remarkupvals' */ - propagateall(g); - /* remark weak tables */ - g->gray = g->weak; - g->weak = NULL; - lua_assert(!iswhite(obj2gco(g->mainthread))); - markobject(g, L); /* mark running thread */ - markmt(g); /* mark basic metatables (again) */ - propagateall(g); - /* remark gray again */ - g->gray = g->grayagain; - g->grayagain = NULL; - propagateall(g); - udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ - marktmu(g); /* mark `preserved' userdata */ - udsize += propagateall(g); /* remark, to propagate `preserveness' */ - cleartable(g->weak); /* remove collected objects from weak tables */ - /* flip current white */ - g->currentwhite = cast_byte(otherwhite(g)); - g->sweepstrgc = 0; - g->sweepgc = &g->rootgc; - g->gcstate = GCSsweepstring; - g->estimate = g->totalbytes - udsize; /* first estimate */ -} - - -static l_mem singlestep (lua_State *L) { - global_State *g = G(L); - /*lua_checkmemory(L);*/ - switch (g->gcstate) { - case GCSpause: { - markroot(L); /* start a new collection */ - return 0; - } - case GCSpropagate: { - if (g->gray) - return propagatemark(g); - else { /* no more `gray' objects */ - atomic(L); /* finish mark phase */ - return 0; - } - } - case GCSsweepstring: { - lu_mem old = g->totalbytes; - sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); - if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ - g->gcstate = GCSsweep; /* end sweep-string phase */ - lua_assert(old >= g->totalbytes); - g->estimate -= old - g->totalbytes; - return GCSWEEPCOST; - } - case GCSsweep: { - lu_mem old = g->totalbytes; - g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); - if (*g->sweepgc == NULL) { /* nothing more to sweep? */ - checkSizes(L); - g->gcstate = GCSfinalize; /* end sweep phase */ - } - lua_assert(old >= g->totalbytes); - g->estimate -= old - g->totalbytes; - return GCSWEEPMAX*GCSWEEPCOST; - } - case GCSfinalize: { - if (g->tmudata) { - GCTM(L); - if (g->estimate > GCFINALIZECOST) - g->estimate -= GCFINALIZECOST; - return GCFINALIZECOST; - } - else { - g->gcstate = GCSpause; /* end collection */ - g->gcdept = 0; - return 0; - } - } - default: lua_assert(0); return 0; - } -} - - -void luaC_step (lua_State *L) { - global_State *g = G(L); - l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul; - if (lim == 0) - lim = (MAX_LUMEM-1)/2; /* no limit */ - g->gcdept += g->totalbytes - g->GCthreshold; - do { - lim -= singlestep(L); - if (g->gcstate == GCSpause) - break; - } while (lim > 0); - if (g->gcstate != GCSpause) { - if (g->gcdept < GCSTEPSIZE) - g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/ - else { - g->gcdept -= GCSTEPSIZE; - g->GCthreshold = g->totalbytes; - } - } - else { - setthreshold(g); - } -} - - -void luaC_fullgc (lua_State *L) { - global_State *g = G(L); - if (g->gcstate <= GCSpropagate) { - /* reset sweep marks to sweep all elements (returning them to white) */ - g->sweepstrgc = 0; - g->sweepgc = &g->rootgc; - /* reset other collector lists */ - g->gray = NULL; - g->grayagain = NULL; - g->weak = NULL; - g->gcstate = GCSsweepstring; - } - lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); - /* finish any pending sweep phase */ - while (g->gcstate != GCSfinalize) { - lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep); - singlestep(L); - } - markroot(L); - while (g->gcstate != GCSpause) { - singlestep(L); - } - setthreshold(g); -} - - -void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { - global_State *g = G(L); - lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); - lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); - lua_assert(ttype(&o->gch) != LUA_TTABLE); - /* must keep invariant? */ - if (g->gcstate == GCSpropagate) - reallymarkobject(g, v); /* restore invariant */ - else /* don't mind */ - makewhite(g, o); /* mark as white just to avoid other barriers */ -} - - -void luaC_barrierback (lua_State *L, Table *t) { - global_State *g = G(L); - GCObject *o = obj2gco(t); - lua_assert(isblack(o) && !isdead(g, o)); - lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); - black2gray(o); /* make table gray (again) */ - t->gclist = g->grayagain; - g->grayagain = o; -} - - -void luaC_link (lua_State *L, GCObject *o, lu_byte tt) { - global_State *g = G(L); - o->gch.next = g->rootgc; - g->rootgc = o; - o->gch.marked = luaC_white(g); - o->gch.tt = tt; -} - - -void luaC_linkupval (lua_State *L, UpVal *uv) { - global_State *g = G(L); - GCObject *o = obj2gco(uv); - o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */ - g->rootgc = o; - if (isgray(o)) { - if (g->gcstate == GCSpropagate) { - gray2black(o); /* closed upvalues need barrier */ - luaC_barrier(L, uv, uv->v); - } - else { /* sweep phase: sweep it (turning it into white) */ - makewhite(g, o); - lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); - } - } -} - diff --git a/libraries/lua/lgc.h b/libraries/lua/lgc.h deleted file mode 100644 index 5a8dc605b..000000000 --- a/libraries/lua/lgc.h +++ /dev/null @@ -1,110 +0,0 @@ -/* -** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $ -** Garbage Collector -** See Copyright Notice in lua.h -*/ - -#ifndef lgc_h -#define lgc_h - - -#include "lobject.h" - - -/* -** Possible states of the Garbage Collector -*/ -#define GCSpause 0 -#define GCSpropagate 1 -#define GCSsweepstring 2 -#define GCSsweep 3 -#define GCSfinalize 4 - - -/* -** some userful bit tricks -*/ -#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m))) -#define setbits(x,m) ((x) |= (m)) -#define testbits(x,m) ((x) & (m)) -#define bitmask(b) (1<<(b)) -#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) -#define l_setbit(x,b) setbits(x, bitmask(b)) -#define resetbit(x,b) resetbits(x, bitmask(b)) -#define testbit(x,b) testbits(x, bitmask(b)) -#define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2))) -#define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2))) -#define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2))) - - - -/* -** Layout for bit use in `marked' field: -** bit 0 - object is white (type 0) -** bit 1 - object is white (type 1) -** bit 2 - object is black -** bit 3 - for userdata: has been finalized -** bit 3 - for tables: has weak keys -** bit 4 - for tables: has weak values -** bit 5 - object is fixed (should not be collected) -** bit 6 - object is "super" fixed (only the main thread) -*/ - - -#define WHITE0BIT 0 -#define WHITE1BIT 1 -#define BLACKBIT 2 -#define FINALIZEDBIT 3 -#define KEYWEAKBIT 3 -#define VALUEWEAKBIT 4 -#define FIXEDBIT 5 -#define SFIXEDBIT 6 -#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) - - -#define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) -#define isblack(x) testbit((x)->gch.marked, BLACKBIT) -#define isgray(x) (!isblack(x) && !iswhite(x)) - -#define otherwhite(g) (g->currentwhite ^ WHITEBITS) -#define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS) - -#define changewhite(x) ((x)->gch.marked ^= WHITEBITS) -#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT) - -#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) - -#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) - - -#define luaC_checkGC(L) { \ - condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \ - if (G(L)->totalbytes >= G(L)->GCthreshold) \ - luaC_step(L); } - - -#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \ - luaC_barrierf(L,obj2gco(p),gcvalue(v)); } - -#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) \ - luaC_barrierback(L,t); } - -#define luaC_objbarrier(L,p,o) \ - { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \ - luaC_barrierf(L,obj2gco(p),obj2gco(o)); } - -#define luaC_objbarriert(L,t,o) \ - { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); } - -LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all); -LUAI_FUNC void luaC_callGCTM (lua_State *L); -LUAI_FUNC void luaC_freeall (lua_State *L); -LUAI_FUNC void luaC_step (lua_State *L); -LUAI_FUNC void luaC_fullgc (lua_State *L); -LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt); -LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv); -LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v); -LUAI_FUNC void luaC_barrierback (lua_State *L, Table *t); - - -#endif diff --git a/libraries/lua/linit.c b/libraries/lua/linit.c deleted file mode 100644 index c1f90dfab..000000000 --- a/libraries/lua/linit.c +++ /dev/null @@ -1,38 +0,0 @@ -/* -** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $ -** Initialization of libraries for lua.c -** See Copyright Notice in lua.h -*/ - - -#define linit_c -#define LUA_LIB - -#include "lua.h" - -#include "lualib.h" -#include "lauxlib.h" - - -static const luaL_Reg lualibs[] = { - {"", luaopen_base}, - {LUA_LOADLIBNAME, luaopen_package}, - {LUA_TABLIBNAME, luaopen_table}, - {LUA_IOLIBNAME, luaopen_io}, - {LUA_OSLIBNAME, luaopen_os}, - {LUA_STRLIBNAME, luaopen_string}, - {LUA_MATHLIBNAME, luaopen_math}, - {LUA_DBLIBNAME, luaopen_debug}, - {NULL, NULL} -}; - - -LUALIB_API void luaL_openlibs (lua_State *L) { - const luaL_Reg *lib = lualibs; - for (; lib->func; lib++) { - lua_pushcfunction(L, lib->func); - lua_pushstring(L, lib->name); - lua_call(L, 1, 0); - } -} - diff --git a/libraries/lua/liolib.c b/libraries/lua/liolib.c deleted file mode 100644 index 649f9a595..000000000 --- a/libraries/lua/liolib.c +++ /dev/null @@ -1,556 +0,0 @@ -/* -** $Id: liolib.c,v 2.73.1.4 2010/05/14 15:33:51 roberto Exp $ -** Standard I/O (and system) library -** See Copyright Notice in lua.h -*/ - - -#include -#include -#include -#include - -#define liolib_c -#define LUA_LIB - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - - -#define IO_INPUT 1 -#define IO_OUTPUT 2 - - -static const char *const fnames[] = {"input", "output"}; - - -static int pushresult (lua_State *L, int i, const char *filename) { - int en = errno; /* calls to Lua API may change this value */ - if (i) { - lua_pushboolean(L, 1); - return 1; - } - else { - lua_pushnil(L); - if (filename) - lua_pushfstring(L, "%s: %s", filename, strerror(en)); - else - lua_pushfstring(L, "%s", strerror(en)); - lua_pushinteger(L, en); - return 3; - } -} - - -static void fileerror (lua_State *L, int arg, const char *filename) { - lua_pushfstring(L, "%s: %s", filename, strerror(errno)); - luaL_argerror(L, arg, lua_tostring(L, -1)); -} - - -#define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE)) - - -static int io_type (lua_State *L) { - void *ud; - luaL_checkany(L, 1); - ud = lua_touserdata(L, 1); - lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE); - if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1)) - lua_pushnil(L); /* not a file */ - else if (*((FILE **)ud) == NULL) - lua_pushliteral(L, "closed file"); - else - lua_pushliteral(L, "file"); - return 1; -} - - -static FILE *tofile (lua_State *L) { - FILE **f = tofilep(L); - if (*f == NULL) - luaL_error(L, "attempt to use a closed file"); - return *f; -} - - - -/* -** When creating file handles, always creates a `closed' file handle -** before opening the actual file; so, if there is a memory error, the -** file is not left opened. -*/ -static FILE **newfile (lua_State *L) { - FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); - *pf = NULL; /* file handle is currently `closed' */ - luaL_getmetatable(L, LUA_FILEHANDLE); - lua_setmetatable(L, -2); - return pf; -} - - -/* -** function to (not) close the standard files stdin, stdout, and stderr -*/ -static int io_noclose (lua_State *L) { - lua_pushnil(L); - lua_pushliteral(L, "cannot close standard file"); - return 2; -} - - -/* -** function to close 'popen' files -*/ -static int io_pclose (lua_State *L) { - FILE **p = tofilep(L); - int ok = lua_pclose(L, *p); - *p = NULL; - return pushresult(L, ok, NULL); -} - - -/* -** function to close regular files -*/ -static int io_fclose (lua_State *L) { - FILE **p = tofilep(L); - int ok = (fclose(*p) == 0); - *p = NULL; - return pushresult(L, ok, NULL); -} - - -static int aux_close (lua_State *L) { - lua_getfenv(L, 1); - lua_getfield(L, -1, "__close"); - return (lua_tocfunction(L, -1))(L); -} - - -static int io_close (lua_State *L) { - if (lua_isnone(L, 1)) - lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT); - tofile(L); /* make sure argument is a file */ - return aux_close(L); -} - - -static int io_gc (lua_State *L) { - FILE *f = *tofilep(L); - /* ignore closed files */ - if (f != NULL) - aux_close(L); - return 0; -} - - -static int io_tostring (lua_State *L) { - FILE *f = *tofilep(L); - if (f == NULL) - lua_pushliteral(L, "file (closed)"); - else - lua_pushfstring(L, "file (%p)", f); - return 1; -} - - -static int io_open (lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - const char *mode = luaL_optstring(L, 2, "r"); - FILE **pf = newfile(L); - *pf = fopen(filename, mode); - return (*pf == NULL) ? pushresult(L, 0, filename) : 1; -} - - -/* -** this function has a separated environment, which defines the -** correct __close for 'popen' files -*/ -static int io_popen (lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - const char *mode = luaL_optstring(L, 2, "r"); - FILE **pf = newfile(L); - *pf = lua_popen(L, filename, mode); - return (*pf == NULL) ? pushresult(L, 0, filename) : 1; -} - - -static int io_tmpfile (lua_State *L) { - FILE **pf = newfile(L); - *pf = tmpfile(); - return (*pf == NULL) ? pushresult(L, 0, NULL) : 1; -} - - -static FILE *getiofile (lua_State *L, int findex) { - FILE *f; - lua_rawgeti(L, LUA_ENVIRONINDEX, findex); - f = *(FILE **)lua_touserdata(L, -1); - if (f == NULL) - luaL_error(L, "standard %s file is closed", fnames[findex - 1]); - return f; -} - - -static int g_iofile (lua_State *L, int f, const char *mode) { - if (!lua_isnoneornil(L, 1)) { - const char *filename = lua_tostring(L, 1); - if (filename) { - FILE **pf = newfile(L); - *pf = fopen(filename, mode); - if (*pf == NULL) - fileerror(L, 1, filename); - } - else { - tofile(L); /* check that it's a valid file handle */ - lua_pushvalue(L, 1); - } - lua_rawseti(L, LUA_ENVIRONINDEX, f); - } - /* return current value */ - lua_rawgeti(L, LUA_ENVIRONINDEX, f); - return 1; -} - - -static int io_input (lua_State *L) { - return g_iofile(L, IO_INPUT, "r"); -} - - -static int io_output (lua_State *L) { - return g_iofile(L, IO_OUTPUT, "w"); -} - - -static int io_readline (lua_State *L); - - -static void aux_lines (lua_State *L, int idx, int toclose) { - lua_pushvalue(L, idx); - lua_pushboolean(L, toclose); /* close/not close file when finished */ - lua_pushcclosure(L, io_readline, 2); -} - - -static int f_lines (lua_State *L) { - tofile(L); /* check that it's a valid file handle */ - aux_lines(L, 1, 0); - return 1; -} - - -static int io_lines (lua_State *L) { - if (lua_isnoneornil(L, 1)) { /* no arguments? */ - /* will iterate over default input */ - lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT); - return f_lines(L); - } - else { - const char *filename = luaL_checkstring(L, 1); - FILE **pf = newfile(L); - *pf = fopen(filename, "r"); - if (*pf == NULL) - fileerror(L, 1, filename); - aux_lines(L, lua_gettop(L), 1); - return 1; - } -} - - -/* -** {====================================================== -** READ -** ======================================================= -*/ - - -static int read_number (lua_State *L, FILE *f) { - lua_Number d; - if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { - lua_pushnumber(L, d); - return 1; - } - else { - lua_pushnil(L); /* "result" to be removed */ - return 0; /* read fails */ - } -} - - -static int test_eof (lua_State *L, FILE *f) { - int c = getc(f); - ungetc(c, f); - lua_pushlstring(L, NULL, 0); - return (c != EOF); -} - - -static int read_line (lua_State *L, FILE *f) { - luaL_Buffer b; - luaL_buffinit(L, &b); - for (;;) { - size_t l; - char *p = luaL_prepbuffer(&b); - if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ - luaL_pushresult(&b); /* close buffer */ - return (lua_objlen(L, -1) > 0); /* check whether read something */ - } - l = strlen(p); - if (l == 0 || p[l-1] != '\n') - luaL_addsize(&b, l); - else { - luaL_addsize(&b, l - 1); /* do not include `eol' */ - luaL_pushresult(&b); /* close buffer */ - return 1; /* read at least an `eol' */ - } - } -} - - -static int read_chars (lua_State *L, FILE *f, size_t n) { - size_t rlen; /* how much to read */ - size_t nr; /* number of chars actually read */ - luaL_Buffer b; - luaL_buffinit(L, &b); - rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ - do { - char *p = luaL_prepbuffer(&b); - if (rlen > n) rlen = n; /* cannot read more than asked */ - nr = fread(p, sizeof(char), rlen, f); - luaL_addsize(&b, nr); - n -= nr; /* still have to read `n' chars */ - } while (n > 0 && nr == rlen); /* until end of count or eof */ - luaL_pushresult(&b); /* close buffer */ - return (n == 0 || lua_objlen(L, -1) > 0); -} - - -static int g_read (lua_State *L, FILE *f, int first) { - int nargs = lua_gettop(L) - 1; - int success; - int n; - clearerr(f); - if (nargs == 0) { /* no arguments? */ - success = read_line(L, f); - n = first+1; /* to return 1 result */ - } - else { /* ensure stack space for all results and for auxlib's buffer */ - luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); - success = 1; - for (n = first; nargs-- && success; n++) { - if (lua_type(L, n) == LUA_TNUMBER) { - size_t l = (size_t)lua_tointeger(L, n); - success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); - } - else { - const char *p = lua_tostring(L, n); - luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); - switch (p[1]) { - case 'n': /* number */ - success = read_number(L, f); - break; - case 'l': /* line */ - success = read_line(L, f); - break; - case 'a': /* file */ - read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ - success = 1; /* always success */ - break; - default: - return luaL_argerror(L, n, "invalid format"); - } - } - } - } - if (ferror(f)) - return pushresult(L, 0, NULL); - if (!success) { - lua_pop(L, 1); /* remove last result */ - lua_pushnil(L); /* push nil instead */ - } - return n - first; -} - - -static int io_read (lua_State *L) { - return g_read(L, getiofile(L, IO_INPUT), 1); -} - - -static int f_read (lua_State *L) { - return g_read(L, tofile(L), 2); -} - - -static int io_readline (lua_State *L) { - FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1)); - int sucess; - if (f == NULL) /* file is already closed? */ - luaL_error(L, "file is already closed"); - sucess = read_line(L, f); - if (ferror(f)) - return luaL_error(L, "%s", strerror(errno)); - if (sucess) return 1; - else { /* EOF */ - if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */ - lua_settop(L, 0); - lua_pushvalue(L, lua_upvalueindex(1)); - aux_close(L); /* close it */ - } - return 0; - } -} - -/* }====================================================== */ - - -static int g_write (lua_State *L, FILE *f, int arg) { - int nargs = lua_gettop(L) - 1; - int status = 1; - for (; nargs--; arg++) { - if (lua_type(L, arg) == LUA_TNUMBER) { - /* optimization: could be done exactly as for strings */ - status = status && - fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; - } - else { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - status = status && (fwrite(s, sizeof(char), l, f) == l); - } - } - return pushresult(L, status, NULL); -} - - -static int io_write (lua_State *L) { - return g_write(L, getiofile(L, IO_OUTPUT), 1); -} - - -static int f_write (lua_State *L) { - return g_write(L, tofile(L), 2); -} - - -static int f_seek (lua_State *L) { - static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; - static const char *const modenames[] = {"set", "cur", "end", NULL}; - FILE *f = tofile(L); - int op = luaL_checkoption(L, 2, "cur", modenames); - long offset = luaL_optlong(L, 3, 0); - op = fseek(f, offset, mode[op]); - if (op) - return pushresult(L, 0, NULL); /* error */ - else { - lua_pushinteger(L, ftell(f)); - return 1; - } -} - - -static int f_setvbuf (lua_State *L) { - static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; - static const char *const modenames[] = {"no", "full", "line", NULL}; - FILE *f = tofile(L); - int op = luaL_checkoption(L, 2, NULL, modenames); - lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); - int res = setvbuf(f, NULL, mode[op], sz); - return pushresult(L, res == 0, NULL); -} - - - -static int io_flush (lua_State *L) { - return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); -} - - -static int f_flush (lua_State *L) { - return pushresult(L, fflush(tofile(L)) == 0, NULL); -} - - -static const luaL_Reg iolib[] = { - {"close", io_close}, - {"flush", io_flush}, - {"input", io_input}, - {"lines", io_lines}, - {"open", io_open}, - {"output", io_output}, - {"popen", io_popen}, - {"read", io_read}, - {"tmpfile", io_tmpfile}, - {"type", io_type}, - {"write", io_write}, - {NULL, NULL} -}; - - -static const luaL_Reg flib[] = { - {"close", io_close}, - {"flush", f_flush}, - {"lines", f_lines}, - {"read", f_read}, - {"seek", f_seek}, - {"setvbuf", f_setvbuf}, - {"write", f_write}, - {"__gc", io_gc}, - {"__tostring", io_tostring}, - {NULL, NULL} -}; - - -static void createmeta (lua_State *L) { - luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */ - lua_pushvalue(L, -1); /* push metatable */ - lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ - luaL_register(L, NULL, flib); /* file methods */ -} - - -static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) { - *newfile(L) = f; - if (k > 0) { - lua_pushvalue(L, -1); - lua_rawseti(L, LUA_ENVIRONINDEX, k); - } - lua_pushvalue(L, -2); /* copy environment */ - lua_setfenv(L, -2); /* set it */ - lua_setfield(L, -3, fname); -} - - -static void newfenv (lua_State *L, lua_CFunction cls) { - lua_createtable(L, 0, 1); - lua_pushcfunction(L, cls); - lua_setfield(L, -2, "__close"); -} - - -LUALIB_API int luaopen_io (lua_State *L) { - createmeta(L); - /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */ - newfenv(L, io_fclose); - lua_replace(L, LUA_ENVIRONINDEX); - /* open library */ - luaL_register(L, LUA_IOLIBNAME, iolib); - /* create (and set) default files */ - newfenv(L, io_noclose); /* close function for default files */ - createstdfile(L, stdin, IO_INPUT, "stdin"); - createstdfile(L, stdout, IO_OUTPUT, "stdout"); - createstdfile(L, stderr, 0, "stderr"); - lua_pop(L, 1); /* pop environment for default files */ - lua_getfield(L, -1, "popen"); - newfenv(L, io_pclose); /* create environment for 'popen' */ - lua_setfenv(L, -2); /* set fenv for 'popen' */ - lua_pop(L, 1); /* pop 'popen' */ - return 1; -} - diff --git a/libraries/lua/llex.c b/libraries/lua/llex.c deleted file mode 100644 index 88c6790c0..000000000 --- a/libraries/lua/llex.c +++ /dev/null @@ -1,463 +0,0 @@ -/* -** $Id: llex.c,v 2.20.1.2 2009/11/23 14:58:22 roberto Exp $ -** Lexical Analyzer -** See Copyright Notice in lua.h -*/ - - -#include -#include -#include - -#define llex_c -#define LUA_CORE - -#include "lua.h" - -#include "ldo.h" -#include "llex.h" -#include "lobject.h" -#include "lparser.h" -#include "lstate.h" -#include "lstring.h" -#include "ltable.h" -#include "lzio.h" - - - -#define next(ls) (ls->current = zgetc(ls->z)) - - - - -#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') - - -/* ORDER RESERVED */ -const char *const luaX_tokens [] = { - "and", "break", "do", "else", "elseif", - "end", "false", "for", "function", "if", - "in", "local", "nil", "not", "or", "repeat", - "return", "then", "true", "until", "while", - "..", "...", "==", ">=", "<=", "~=", - "", "", "", "", - NULL -}; - - -#define save_and_next(ls) (save(ls, ls->current), next(ls)) - - -static void save (LexState *ls, int c) { - Mbuffer *b = ls->buff; - if (b->n + 1 > b->buffsize) { - size_t newsize; - if (b->buffsize >= MAX_SIZET/2) - luaX_lexerror(ls, "lexical element too long", 0); - newsize = b->buffsize * 2; - luaZ_resizebuffer(ls->L, b, newsize); - } - b->buffer[b->n++] = cast(char, c); -} - - -void luaX_init (lua_State *L) { - int i; - for (i=0; itsv.reserved = cast_byte(i+1); /* reserved word */ - } -} - - -#define MAXSRC 80 - - -const char *luaX_token2str (LexState *ls, int token) { - if (token < FIRST_RESERVED) { - lua_assert(token == cast(unsigned char, token)); - return (iscntrl(token)) ? luaO_pushfstring(ls->L, "char(%d)", token) : - luaO_pushfstring(ls->L, "%c", token); - } - else - return luaX_tokens[token-FIRST_RESERVED]; -} - - -static const char *txtToken (LexState *ls, int token) { - switch (token) { - case TK_NAME: - case TK_STRING: - case TK_NUMBER: - save(ls, '\0'); - return luaZ_buffer(ls->buff); - default: - return luaX_token2str(ls, token); - } -} - - -void luaX_lexerror (LexState *ls, const char *msg, int token) { - char buff[MAXSRC]; - luaO_chunkid(buff, getstr(ls->source), MAXSRC); - msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg); - if (token) - luaO_pushfstring(ls->L, "%s near " LUA_QS, msg, txtToken(ls, token)); - luaD_throw(ls->L, LUA_ERRSYNTAX); -} - - -void luaX_syntaxerror (LexState *ls, const char *msg) { - luaX_lexerror(ls, msg, ls->t.token); -} - - -TString *luaX_newstring (LexState *ls, const char *str, size_t l) { - lua_State *L = ls->L; - TString *ts = luaS_newlstr(L, str, l); - TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */ - if (ttisnil(o)) { - setbvalue(o, 1); /* make sure `str' will not be collected */ - luaC_checkGC(L); - } - return ts; -} - - -static void inclinenumber (LexState *ls) { - int old = ls->current; - lua_assert(currIsNewline(ls)); - next(ls); /* skip `\n' or `\r' */ - if (currIsNewline(ls) && ls->current != old) - next(ls); /* skip `\n\r' or `\r\n' */ - if (++ls->linenumber >= MAX_INT) - luaX_syntaxerror(ls, "chunk has too many lines"); -} - - -void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) { - ls->decpoint = '.'; - ls->L = L; - ls->lookahead.token = TK_EOS; /* no look-ahead token */ - ls->z = z; - ls->fs = NULL; - ls->linenumber = 1; - ls->lastline = 1; - ls->source = source; - luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ - next(ls); /* read first char */ -} - - - -/* -** ======================================================= -** LEXICAL ANALYZER -** ======================================================= -*/ - - - -static int check_next (LexState *ls, const char *set) { - if (!strchr(set, ls->current)) - return 0; - save_and_next(ls); - return 1; -} - - -static void buffreplace (LexState *ls, char from, char to) { - size_t n = luaZ_bufflen(ls->buff); - char *p = luaZ_buffer(ls->buff); - while (n--) - if (p[n] == from) p[n] = to; -} - - -static void trydecpoint (LexState *ls, SemInfo *seminfo) { - /* format error: try to update decimal point separator */ - struct lconv *cv = localeconv(); - char old = ls->decpoint; - ls->decpoint = (cv ? cv->decimal_point[0] : '.'); - buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */ - if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) { - /* format error with correct decimal point: no more options */ - buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */ - luaX_lexerror(ls, "malformed number", TK_NUMBER); - } -} - - -/* LUA_NUMBER */ -static void read_numeral (LexState *ls, SemInfo *seminfo) { - lua_assert(isdigit(ls->current)); - do { - save_and_next(ls); - } while (isdigit(ls->current) || ls->current == '.'); - if (check_next(ls, "Ee")) /* `E'? */ - check_next(ls, "+-"); /* optional exponent sign */ - while (isalnum(ls->current) || ls->current == '_') - save_and_next(ls); - save(ls, '\0'); - buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ - if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */ - trydecpoint(ls, seminfo); /* try to update decimal point separator */ -} - - -static int skip_sep (LexState *ls) { - int count = 0; - int s = ls->current; - lua_assert(s == '[' || s == ']'); - save_and_next(ls); - while (ls->current == '=') { - save_and_next(ls); - count++; - } - return (ls->current == s) ? count : (-count) - 1; -} - - -static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) { - int cont = 0; - (void)(cont); /* avoid warnings when `cont' is not used */ - save_and_next(ls); /* skip 2nd `[' */ - if (currIsNewline(ls)) /* string starts with a newline? */ - inclinenumber(ls); /* skip it */ - for (;;) { - switch (ls->current) { - case EOZ: - luaX_lexerror(ls, (seminfo) ? "unfinished long string" : - "unfinished long comment", TK_EOS); - break; /* to avoid warnings */ -#if defined(LUA_COMPAT_LSTR) - case '[': { - if (skip_sep(ls) == sep) { - save_and_next(ls); /* skip 2nd `[' */ - cont++; -#if LUA_COMPAT_LSTR == 1 - if (sep == 0) - luaX_lexerror(ls, "nesting of [[...]] is deprecated", '['); -#endif - } - break; - } -#endif - case ']': { - if (skip_sep(ls) == sep) { - save_and_next(ls); /* skip 2nd `]' */ -#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2 - cont--; - if (sep == 0 && cont >= 0) break; -#endif - goto endloop; - } - break; - } - case '\n': - case '\r': { - save(ls, '\n'); - inclinenumber(ls); - if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */ - break; - } - default: { - if (seminfo) save_and_next(ls); - else next(ls); - } - } - } endloop: - if (seminfo) - seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep), - luaZ_bufflen(ls->buff) - 2*(2 + sep)); -} - - -static void read_string (LexState *ls, int del, SemInfo *seminfo) { - save_and_next(ls); - while (ls->current != del) { - switch (ls->current) { - case EOZ: - luaX_lexerror(ls, "unfinished string", TK_EOS); - continue; /* to avoid warnings */ - case '\n': - case '\r': - luaX_lexerror(ls, "unfinished string", TK_STRING); - continue; /* to avoid warnings */ - case '\\': { - int c; - next(ls); /* do not save the `\' */ - switch (ls->current) { - case 'a': c = '\a'; break; - case 'b': c = '\b'; break; - case 'f': c = '\f'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'v': c = '\v'; break; - case '\n': /* go through */ - case '\r': save(ls, '\n'); inclinenumber(ls); continue; - case EOZ: continue; /* will raise an error next loop */ - default: { - if (!isdigit(ls->current)) - save_and_next(ls); /* handles \\, \", \', and \? */ - else { /* \xxx */ - int i = 0; - c = 0; - do { - c = 10*c + (ls->current-'0'); - next(ls); - } while (++i<3 && isdigit(ls->current)); - if (c > UCHAR_MAX) - luaX_lexerror(ls, "escape sequence too large", TK_STRING); - save(ls, c); - } - continue; - } - } - save(ls, c); - next(ls); - continue; - } - default: - save_and_next(ls); - } - } - save_and_next(ls); /* skip delimiter */ - seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1, - luaZ_bufflen(ls->buff) - 2); -} - - -static int llex (LexState *ls, SemInfo *seminfo) { - luaZ_resetbuffer(ls->buff); - for (;;) { - switch (ls->current) { - case '\n': - case '\r': { - inclinenumber(ls); - continue; - } - case '-': { - next(ls); - if (ls->current != '-') return '-'; - /* else is a comment */ - next(ls); - if (ls->current == '[') { - int sep = skip_sep(ls); - luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */ - if (sep >= 0) { - read_long_string(ls, NULL, sep); /* long comment */ - luaZ_resetbuffer(ls->buff); - continue; - } - } - /* else short comment */ - while (!currIsNewline(ls) && ls->current != EOZ) - next(ls); - continue; - } - case '[': { - int sep = skip_sep(ls); - if (sep >= 0) { - read_long_string(ls, seminfo, sep); - return TK_STRING; - } - else if (sep == -1) return '['; - else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING); - } - case '=': { - next(ls); - if (ls->current != '=') return '='; - else { next(ls); return TK_EQ; } - } - case '<': { - next(ls); - if (ls->current != '=') return '<'; - else { next(ls); return TK_LE; } - } - case '>': { - next(ls); - if (ls->current != '=') return '>'; - else { next(ls); return TK_GE; } - } - case '~': { - next(ls); - if (ls->current != '=') return '~'; - else { next(ls); return TK_NE; } - } - case '"': - case '\'': { - read_string(ls, ls->current, seminfo); - return TK_STRING; - } - case '.': { - save_and_next(ls); - if (check_next(ls, ".")) { - if (check_next(ls, ".")) - return TK_DOTS; /* ... */ - else return TK_CONCAT; /* .. */ - } - else if (!isdigit(ls->current)) return '.'; - else { - read_numeral(ls, seminfo); - return TK_NUMBER; - } - } - case EOZ: { - return TK_EOS; - } - default: { - if (isspace(ls->current)) { - lua_assert(!currIsNewline(ls)); - next(ls); - continue; - } - else if (isdigit(ls->current)) { - read_numeral(ls, seminfo); - return TK_NUMBER; - } - else if (isalpha(ls->current) || ls->current == '_') { - /* identifier or reserved word */ - TString *ts; - do { - save_and_next(ls); - } while (isalnum(ls->current) || ls->current == '_'); - ts = luaX_newstring(ls, luaZ_buffer(ls->buff), - luaZ_bufflen(ls->buff)); - if (ts->tsv.reserved > 0) /* reserved word? */ - return ts->tsv.reserved - 1 + FIRST_RESERVED; - else { - seminfo->ts = ts; - return TK_NAME; - } - } - else { - int c = ls->current; - next(ls); - return c; /* single-char tokens (+ - / ...) */ - } - } - } - } -} - - -void luaX_next (LexState *ls) { - ls->lastline = ls->linenumber; - if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ - ls->t = ls->lookahead; /* use this one */ - ls->lookahead.token = TK_EOS; /* and discharge it */ - } - else - ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */ -} - - -void luaX_lookahead (LexState *ls) { - lua_assert(ls->lookahead.token == TK_EOS); - ls->lookahead.token = llex(ls, &ls->lookahead.seminfo); -} - diff --git a/libraries/lua/llex.h b/libraries/lua/llex.h deleted file mode 100644 index a9201cee4..000000000 --- a/libraries/lua/llex.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -** $Id: llex.h,v 1.58.1.1 2007/12/27 13:02:25 roberto Exp $ -** Lexical Analyzer -** See Copyright Notice in lua.h -*/ - -#ifndef llex_h -#define llex_h - -#include "lobject.h" -#include "lzio.h" - - -#define FIRST_RESERVED 257 - -/* maximum length of a reserved word */ -#define TOKEN_LEN (sizeof("function")/sizeof(char)) - - -/* -* WARNING: if you change the order of this enumeration, -* grep "ORDER RESERVED" -*/ -enum RESERVED { - /* terminal symbols denoted by reserved words */ - TK_AND = FIRST_RESERVED, TK_BREAK, - TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, - TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, - TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, - /* other terminal symbols */ - TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER, - TK_NAME, TK_STRING, TK_EOS -}; - -/* number of reserved words */ -#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1)) - - -/* array with token `names' */ -LUAI_DATA const char *const luaX_tokens []; - - -typedef union { - lua_Number r; - TString *ts; -} SemInfo; /* semantics information */ - - -typedef struct Token { - int token; - SemInfo seminfo; -} Token; - - -typedef struct LexState { - int current; /* current character (charint) */ - int linenumber; /* input line counter */ - int lastline; /* line of last token `consumed' */ - Token t; /* current token */ - Token lookahead; /* look ahead token */ - struct FuncState *fs; /* `FuncState' is private to the parser */ - struct lua_State *L; - ZIO *z; /* input stream */ - Mbuffer *buff; /* buffer for tokens */ - TString *source; /* current source name */ - char decpoint; /* locale decimal point */ -} LexState; - - -LUAI_FUNC void luaX_init (lua_State *L); -LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, - TString *source); -LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); -LUAI_FUNC void luaX_next (LexState *ls); -LUAI_FUNC void luaX_lookahead (LexState *ls); -LUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token); -LUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s); -LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); - - -#endif diff --git a/libraries/lua/llimits.h b/libraries/lua/llimits.h deleted file mode 100644 index ca8dcb722..000000000 --- a/libraries/lua/llimits.h +++ /dev/null @@ -1,128 +0,0 @@ -/* -** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $ -** Limits, basic types, and some other `installation-dependent' definitions -** See Copyright Notice in lua.h -*/ - -#ifndef llimits_h -#define llimits_h - - -#include -#include - - -#include "lua.h" - - -typedef LUAI_UINT32 lu_int32; - -typedef LUAI_UMEM lu_mem; - -typedef LUAI_MEM l_mem; - - - -/* chars used as small naturals (so that `char' is reserved for characters) */ -typedef unsigned char lu_byte; - - -#define MAX_SIZET ((size_t)(~(size_t)0)-2) - -#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2) - - -#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */ - -/* -** conversion of pointer to integer -** this is for hashing only; there is no problem if the integer -** cannot hold the whole pointer value -*/ -#define IntPoint(p) ((unsigned int)(lu_mem)(p)) - - - -/* type to ensure maximum alignment */ -typedef LUAI_USER_ALIGNMENT_T L_Umaxalign; - - -/* result of a `usual argument conversion' over lua_Number */ -typedef LUAI_UACNUMBER l_uacNumber; - - -/* internal assertions for in-house debugging */ -#ifdef lua_assert - -#define check_exp(c,e) (lua_assert(c), (e)) -#define api_check(l,e) lua_assert(e) - -#else - -#define lua_assert(c) ((void)0) -#define check_exp(c,e) (e) -#define api_check luai_apicheck - -#endif - - -#ifndef UNUSED -#define UNUSED(x) ((void)(x)) /* to avoid warnings */ -#endif - - -#ifndef cast -#define cast(t, exp) ((t)(exp)) -#endif - -#define cast_byte(i) cast(lu_byte, (i)) -#define cast_num(i) cast(lua_Number, (i)) -#define cast_int(i) cast(int, (i)) - - - -/* -** type for virtual-machine instructions -** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) -*/ -typedef lu_int32 Instruction; - - - -/* maximum stack for a Lua function */ -#define MAXSTACK 250 - - - -/* minimum size for the string table (must be power of 2) */ -#ifndef MINSTRTABSIZE -#define MINSTRTABSIZE 32 -#endif - - -/* minimum size for string buffer */ -#ifndef LUA_MINBUFFER -#define LUA_MINBUFFER 32 -#endif - - -#ifndef lua_lock -#define lua_lock(L) ((void) 0) -#define lua_unlock(L) ((void) 0) -#endif - -#ifndef luai_threadyield -#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} -#endif - - -/* -** macro to control inclusion of some hard tests on stack reallocation -*/ -#ifndef HARDSTACKTESTS -#define condhardstacktests(x) ((void)0) -#else -#define condhardstacktests(x) x -#endif - -#endif diff --git a/libraries/lua/lmathlib.c b/libraries/lua/lmathlib.c deleted file mode 100644 index 441fbf736..000000000 --- a/libraries/lua/lmathlib.c +++ /dev/null @@ -1,263 +0,0 @@ -/* -** $Id: lmathlib.c,v 1.67.1.1 2007/12/27 13:02:25 roberto Exp $ -** Standard mathematical library -** See Copyright Notice in lua.h -*/ - - -#include -#include - -#define lmathlib_c -#define LUA_LIB - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -#undef PI -#define PI (3.14159265358979323846) -#define RADIANS_PER_DEGREE (PI/180.0) - - - -static int math_abs (lua_State *L) { - lua_pushnumber(L, fabs(luaL_checknumber(L, 1))); - return 1; -} - -static int math_sin (lua_State *L) { - lua_pushnumber(L, sin(luaL_checknumber(L, 1))); - return 1; -} - -static int math_sinh (lua_State *L) { - lua_pushnumber(L, sinh(luaL_checknumber(L, 1))); - return 1; -} - -static int math_cos (lua_State *L) { - lua_pushnumber(L, cos(luaL_checknumber(L, 1))); - return 1; -} - -static int math_cosh (lua_State *L) { - lua_pushnumber(L, cosh(luaL_checknumber(L, 1))); - return 1; -} - -static int math_tan (lua_State *L) { - lua_pushnumber(L, tan(luaL_checknumber(L, 1))); - return 1; -} - -static int math_tanh (lua_State *L) { - lua_pushnumber(L, tanh(luaL_checknumber(L, 1))); - return 1; -} - -static int math_asin (lua_State *L) { - lua_pushnumber(L, asin(luaL_checknumber(L, 1))); - return 1; -} - -static int math_acos (lua_State *L) { - lua_pushnumber(L, acos(luaL_checknumber(L, 1))); - return 1; -} - -static int math_atan (lua_State *L) { - lua_pushnumber(L, atan(luaL_checknumber(L, 1))); - return 1; -} - -static int math_atan2 (lua_State *L) { - lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); - return 1; -} - -static int math_ceil (lua_State *L) { - lua_pushnumber(L, ceil(luaL_checknumber(L, 1))); - return 1; -} - -static int math_floor (lua_State *L) { - lua_pushnumber(L, floor(luaL_checknumber(L, 1))); - return 1; -} - -static int math_fmod (lua_State *L) { - lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); - return 1; -} - -static int math_modf (lua_State *L) { - double ip; - double fp = modf(luaL_checknumber(L, 1), &ip); - lua_pushnumber(L, ip); - lua_pushnumber(L, fp); - return 2; -} - -static int math_sqrt (lua_State *L) { - lua_pushnumber(L, sqrt(luaL_checknumber(L, 1))); - return 1; -} - -static int math_pow (lua_State *L) { - lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); - return 1; -} - -static int math_log (lua_State *L) { - lua_pushnumber(L, log(luaL_checknumber(L, 1))); - return 1; -} - -static int math_log10 (lua_State *L) { - lua_pushnumber(L, log10(luaL_checknumber(L, 1))); - return 1; -} - -static int math_exp (lua_State *L) { - lua_pushnumber(L, exp(luaL_checknumber(L, 1))); - return 1; -} - -static int math_deg (lua_State *L) { - lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE); - return 1; -} - -static int math_rad (lua_State *L) { - lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); - return 1; -} - -static int math_frexp (lua_State *L) { - int e; - lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e)); - lua_pushinteger(L, e); - return 2; -} - -static int math_ldexp (lua_State *L) { - lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2))); - return 1; -} - - - -static int math_min (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - lua_Number dmin = luaL_checknumber(L, 1); - int i; - for (i=2; i<=n; i++) { - lua_Number d = luaL_checknumber(L, i); - if (d < dmin) - dmin = d; - } - lua_pushnumber(L, dmin); - return 1; -} - - -static int math_max (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - lua_Number dmax = luaL_checknumber(L, 1); - int i; - for (i=2; i<=n; i++) { - lua_Number d = luaL_checknumber(L, i); - if (d > dmax) - dmax = d; - } - lua_pushnumber(L, dmax); - return 1; -} - - -static int math_random (lua_State *L) { - /* the `%' avoids the (rare) case of r==1, and is needed also because on - some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ - lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; - switch (lua_gettop(L)) { /* check number of arguments */ - case 0: { /* no arguments */ - lua_pushnumber(L, r); /* Number between 0 and 1 */ - break; - } - case 1: { /* only upper limit */ - int u = luaL_checkint(L, 1); - luaL_argcheck(L, 1<=u, 1, "interval is empty"); - lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */ - break; - } - case 2: { /* lower and upper limits */ - int l = luaL_checkint(L, 1); - int u = luaL_checkint(L, 2); - luaL_argcheck(L, l<=u, 2, "interval is empty"); - lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */ - break; - } - default: return luaL_error(L, "wrong number of arguments"); - } - return 1; -} - - -static int math_randomseed (lua_State *L) { - srand(luaL_checkint(L, 1)); - return 0; -} - - -static const luaL_Reg mathlib[] = { - {"abs", math_abs}, - {"acos", math_acos}, - {"asin", math_asin}, - {"atan2", math_atan2}, - {"atan", math_atan}, - {"ceil", math_ceil}, - {"cosh", math_cosh}, - {"cos", math_cos}, - {"deg", math_deg}, - {"exp", math_exp}, - {"floor", math_floor}, - {"fmod", math_fmod}, - {"frexp", math_frexp}, - {"ldexp", math_ldexp}, - {"log10", math_log10}, - {"log", math_log}, - {"max", math_max}, - {"min", math_min}, - {"modf", math_modf}, - {"pow", math_pow}, - {"rad", math_rad}, - {"random", math_random}, - {"randomseed", math_randomseed}, - {"sinh", math_sinh}, - {"sin", math_sin}, - {"sqrt", math_sqrt}, - {"tanh", math_tanh}, - {"tan", math_tan}, - {NULL, NULL} -}; - - -/* -** Open math library -*/ -LUALIB_API int luaopen_math (lua_State *L) { - luaL_register(L, LUA_MATHLIBNAME, mathlib); - lua_pushnumber(L, PI); - lua_setfield(L, -2, "pi"); - lua_pushnumber(L, HUGE_VAL); - lua_setfield(L, -2, "huge"); -#if defined(LUA_COMPAT_MOD) - lua_getfield(L, -1, "fmod"); - lua_setfield(L, -2, "mod"); -#endif - return 1; -} - diff --git a/libraries/lua/lmem.c b/libraries/lua/lmem.c deleted file mode 100644 index ae7d8c965..000000000 --- a/libraries/lua/lmem.c +++ /dev/null @@ -1,86 +0,0 @@ -/* -** $Id: lmem.c,v 1.70.1.1 2007/12/27 13:02:25 roberto Exp $ -** Interface to Memory Manager -** See Copyright Notice in lua.h -*/ - - -#include - -#define lmem_c -#define LUA_CORE - -#include "lua.h" - -#include "ldebug.h" -#include "ldo.h" -#include "lmem.h" -#include "lobject.h" -#include "lstate.h" - - - -/* -** About the realloc function: -** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize); -** (`osize' is the old size, `nsize' is the new size) -** -** Lua ensures that (ptr == NULL) iff (osize == 0). -** -** * frealloc(ud, NULL, 0, x) creates a new block of size `x' -** -** * frealloc(ud, p, x, 0) frees the block `p' -** (in this specific case, frealloc must return NULL). -** particularly, frealloc(ud, NULL, 0, 0) does nothing -** (which is equivalent to free(NULL) in ANSI C) -** -** frealloc returns NULL if it cannot create or reallocate the area -** (any reallocation to an equal or smaller size cannot fail!) -*/ - - - -#define MINSIZEARRAY 4 - - -void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems, - int limit, const char *errormsg) { - void *newblock; - int newsize; - if (*size >= limit/2) { /* cannot double it? */ - if (*size >= limit) /* cannot grow even a little? */ - luaG_runerror(L, errormsg); - newsize = limit; /* still have at least one free place */ - } - else { - newsize = (*size)*2; - if (newsize < MINSIZEARRAY) - newsize = MINSIZEARRAY; /* minimum size */ - } - newblock = luaM_reallocv(L, block, *size, newsize, size_elems); - *size = newsize; /* update only when everything else is OK */ - return newblock; -} - - -void *luaM_toobig (lua_State *L) { - luaG_runerror(L, "memory allocation error: block too big"); - return NULL; /* to avoid warnings */ -} - - - -/* -** generic allocation routine. -*/ -void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { - global_State *g = G(L); - lua_assert((osize == 0) == (block == NULL)); - block = (*g->frealloc)(g->ud, block, osize, nsize); - if (block == NULL && nsize > 0) - luaD_throw(L, LUA_ERRMEM); - lua_assert((nsize == 0) == (block == NULL)); - g->totalbytes = (g->totalbytes - osize) + nsize; - return block; -} - diff --git a/libraries/lua/lmem.h b/libraries/lua/lmem.h deleted file mode 100644 index 7c2dcb322..000000000 --- a/libraries/lua/lmem.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -** $Id: lmem.h,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $ -** Interface to Memory Manager -** See Copyright Notice in lua.h -*/ - -#ifndef lmem_h -#define lmem_h - - -#include - -#include "llimits.h" -#include "lua.h" - -#define MEMERRMSG "not enough memory" - - -#define luaM_reallocv(L,b,on,n,e) \ - ((cast(size_t, (n)+1) <= MAX_SIZET/(e)) ? /* +1 to avoid warnings */ \ - luaM_realloc_(L, (b), (on)*(e), (n)*(e)) : \ - luaM_toobig(L)) - -#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0) -#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0) -#define luaM_freearray(L, b, n, t) luaM_reallocv(L, (b), n, 0, sizeof(t)) - -#define luaM_malloc(L,t) luaM_realloc_(L, NULL, 0, (t)) -#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t))) -#define luaM_newvector(L,n,t) \ - cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t))) - -#define luaM_growvector(L,v,nelems,size,t,limit,e) \ - if ((nelems)+1 > (size)) \ - ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e))) - -#define luaM_reallocvector(L, v,oldn,n,t) \ - ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t)))) - - -LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, - size_t size); -LUAI_FUNC void *luaM_toobig (lua_State *L); -LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size, - size_t size_elem, int limit, - const char *errormsg); - -#endif - diff --git a/libraries/lua/loadlib.c b/libraries/lua/loadlib.c deleted file mode 100644 index 6158c5353..000000000 --- a/libraries/lua/loadlib.c +++ /dev/null @@ -1,666 +0,0 @@ -/* -** $Id: loadlib.c,v 1.52.1.4 2009/09/09 13:17:16 roberto Exp $ -** Dynamic library loader for Lua -** See Copyright Notice in lua.h -** -** This module contains an implementation of loadlib for Unix systems -** that have dlfcn, an implementation for Darwin (Mac OS X), an -** implementation for Windows, and a stub for other systems. -*/ - - -#include -#include - - -#define loadlib_c -#define LUA_LIB - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -/* prefix for open functions in C libraries */ -#define LUA_POF "luaopen_" - -/* separator for open functions in C libraries */ -#define LUA_OFSEP "_" - - -#define LIBPREFIX "LOADLIB: " - -#define POF LUA_POF -#define LIB_FAIL "open" - - -/* error codes for ll_loadfunc */ -#define ERRLIB 1 -#define ERRFUNC 2 - -#define setprogdir(L) ((void)0) - - -static void ll_unloadlib (void *lib); -static void *ll_load (lua_State *L, const char *path); -static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym); - - - -#if defined(LUA_DL_DLOPEN) -/* -** {======================================================================== -** This is an implementation of loadlib based on the dlfcn interface. -** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, -** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least -** as an emulation layer on top of native functions. -** ========================================================================= -*/ - -#include - -static void ll_unloadlib (void *lib) { - dlclose(lib); -} - - -static void *ll_load (lua_State *L, const char *path) { - void *lib = dlopen(path, RTLD_NOW); - if (lib == NULL) lua_pushstring(L, dlerror()); - return lib; -} - - -static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { - lua_CFunction f = (lua_CFunction)dlsym(lib, sym); - if (f == NULL) lua_pushstring(L, dlerror()); - return f; -} - -/* }====================================================== */ - - - -#elif defined(LUA_DL_DLL) -/* -** {====================================================================== -** This is an implementation of loadlib for Windows using native functions. -** ======================================================================= -*/ - -#include - - -#undef setprogdir - -static void setprogdir (lua_State *L) { - char buff[MAX_PATH + 1]; - char *lb; - DWORD nsize = sizeof(buff)/sizeof(char); - DWORD n = GetModuleFileNameA(NULL, buff, nsize); - if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) - luaL_error(L, "unable to get ModuleFileName"); - else { - *lb = '\0'; - luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff); - lua_remove(L, -2); /* remove original string */ - } -} - - -static void pusherror (lua_State *L) { - int error = GetLastError(); - char buffer[128]; - if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, error, 0, buffer, sizeof(buffer), NULL)) - lua_pushstring(L, buffer); - else - lua_pushfstring(L, "system error %d\n", error); -} - -static void ll_unloadlib (void *lib) { - FreeLibrary((HINSTANCE)lib); -} - - -static void *ll_load (lua_State *L, const char *path) { - HINSTANCE lib = LoadLibraryA(path); - if (lib == NULL) pusherror(L); - return lib; -} - - -static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { - lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym); - if (f == NULL) pusherror(L); - return f; -} - -/* }====================================================== */ - - - -#elif defined(LUA_DL_DYLD) -/* -** {====================================================================== -** Native Mac OS X / Darwin Implementation -** ======================================================================= -*/ - -#include - - -/* Mac appends a `_' before C function names */ -#undef POF -#define POF "_" LUA_POF - - -static void pusherror (lua_State *L) { - const char *err_str; - const char *err_file; - NSLinkEditErrors err; - int err_num; - NSLinkEditError(&err, &err_num, &err_file, &err_str); - lua_pushstring(L, err_str); -} - - -static const char *errorfromcode (NSObjectFileImageReturnCode ret) { - switch (ret) { - case NSObjectFileImageInappropriateFile: - return "file is not a bundle"; - case NSObjectFileImageArch: - return "library is for wrong CPU type"; - case NSObjectFileImageFormat: - return "bad format"; - case NSObjectFileImageAccess: - return "cannot access file"; - case NSObjectFileImageFailure: - default: - return "unable to load library"; - } -} - - -static void ll_unloadlib (void *lib) { - NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES); -} - - -static void *ll_load (lua_State *L, const char *path) { - NSObjectFileImage img; - NSObjectFileImageReturnCode ret; - /* this would be a rare case, but prevents crashing if it happens */ - if(!_dyld_present()) { - lua_pushliteral(L, "dyld not present"); - return NULL; - } - ret = NSCreateObjectFileImageFromFile(path, &img); - if (ret == NSObjectFileImageSuccess) { - NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE | - NSLINKMODULE_OPTION_RETURN_ON_ERROR); - NSDestroyObjectFileImage(img); - if (mod == NULL) pusherror(L); - return mod; - } - lua_pushstring(L, errorfromcode(ret)); - return NULL; -} - - -static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { - NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym); - if (nss == NULL) { - lua_pushfstring(L, "symbol " LUA_QS " not found", sym); - return NULL; - } - return (lua_CFunction)NSAddressOfSymbol(nss); -} - -/* }====================================================== */ - - - -#else -/* -** {====================================================== -** Fallback for other systems -** ======================================================= -*/ - -#undef LIB_FAIL -#define LIB_FAIL "absent" - - -#define DLMSG "dynamic libraries not enabled; check your Lua installation" - - -static void ll_unloadlib (void *lib) { - (void)lib; /* to avoid warnings */ -} - - -static void *ll_load (lua_State *L, const char *path) { - (void)path; /* to avoid warnings */ - lua_pushliteral(L, DLMSG); - return NULL; -} - - -static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { - (void)lib; (void)sym; /* to avoid warnings */ - lua_pushliteral(L, DLMSG); - return NULL; -} - -/* }====================================================== */ -#endif - - - -static void **ll_register (lua_State *L, const char *path) { - void **plib; - lua_pushfstring(L, "%s%s", LIBPREFIX, path); - lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */ - if (!lua_isnil(L, -1)) /* is there an entry? */ - plib = (void **)lua_touserdata(L, -1); - else { /* no entry yet; create one */ - lua_pop(L, 1); - plib = (void **)lua_newuserdata(L, sizeof(const void *)); - *plib = NULL; - luaL_getmetatable(L, "_LOADLIB"); - lua_setmetatable(L, -2); - lua_pushfstring(L, "%s%s", LIBPREFIX, path); - lua_pushvalue(L, -2); - lua_settable(L, LUA_REGISTRYINDEX); - } - return plib; -} - - -/* -** __gc tag method: calls library's `ll_unloadlib' function with the lib -** handle -*/ -static int gctm (lua_State *L) { - void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB"); - if (*lib) ll_unloadlib(*lib); - *lib = NULL; /* mark library as closed */ - return 0; -} - - -static int ll_loadfunc (lua_State *L, const char *path, const char *sym) { - void **reg = ll_register(L, path); - if (*reg == NULL) *reg = ll_load(L, path); - if (*reg == NULL) - return ERRLIB; /* unable to load library */ - else { - lua_CFunction f = ll_sym(L, *reg, sym); - if (f == NULL) - return ERRFUNC; /* unable to find function */ - lua_pushcfunction(L, f); - return 0; /* return function */ - } -} - - -static int ll_loadlib (lua_State *L) { - const char *path = luaL_checkstring(L, 1); - const char *init = luaL_checkstring(L, 2); - int stat = ll_loadfunc(L, path, init); - if (stat == 0) /* no errors? */ - return 1; /* return the loaded function */ - else { /* error; error message is on stack top */ - lua_pushnil(L); - lua_insert(L, -2); - lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init"); - return 3; /* return nil, error message, and where */ - } -} - - - -/* -** {====================================================== -** 'require' function -** ======================================================= -*/ - - -static int readable (const char *filename) { - FILE *f = fopen(filename, "r"); /* try to open file */ - if (f == NULL) return 0; /* open failed */ - fclose(f); - return 1; -} - - -static const char *pushnexttemplate (lua_State *L, const char *path) { - const char *l; - while (*path == *LUA_PATHSEP) path++; /* skip separators */ - if (*path == '\0') return NULL; /* no more templates */ - l = strchr(path, *LUA_PATHSEP); /* find next separator */ - if (l == NULL) l = path + strlen(path); - lua_pushlstring(L, path, l - path); /* template */ - return l; -} - - -static const char *findfile (lua_State *L, const char *name, - const char *pname) { - const char *path; - name = luaL_gsub(L, name, ".", LUA_DIRSEP); - lua_getfield(L, LUA_ENVIRONINDEX, pname); - path = lua_tostring(L, -1); - if (path == NULL) - luaL_error(L, LUA_QL("package.%s") " must be a string", pname); - lua_pushliteral(L, ""); /* error accumulator */ - while ((path = pushnexttemplate(L, path)) != NULL) { - const char *filename; - filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name); - lua_remove(L, -2); /* remove path template */ - if (readable(filename)) /* does file exist and is readable? */ - return filename; /* return that file name */ - lua_pushfstring(L, "\n\tno file " LUA_QS, filename); - lua_remove(L, -2); /* remove file name */ - lua_concat(L, 2); /* add entry to possible error message */ - } - return NULL; /* not found */ -} - - -static void loaderror (lua_State *L, const char *filename) { - luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s", - lua_tostring(L, 1), filename, lua_tostring(L, -1)); -} - - -static int loader_Lua (lua_State *L) { - const char *filename; - const char *name = luaL_checkstring(L, 1); - filename = findfile(L, name, "path"); - if (filename == NULL) return 1; /* library not found in this path */ - if (luaL_loadfile(L, filename) != 0) - loaderror(L, filename); - return 1; /* library loaded successfully */ -} - - -static const char *mkfuncname (lua_State *L, const char *modname) { - const char *funcname; - const char *mark = strchr(modname, *LUA_IGMARK); - if (mark) modname = mark + 1; - funcname = luaL_gsub(L, modname, ".", LUA_OFSEP); - funcname = lua_pushfstring(L, POF"%s", funcname); - lua_remove(L, -2); /* remove 'gsub' result */ - return funcname; -} - - -static int loader_C (lua_State *L) { - const char *funcname; - const char *name = luaL_checkstring(L, 1); - const char *filename = findfile(L, name, "cpath"); - if (filename == NULL) return 1; /* library not found in this path */ - funcname = mkfuncname(L, name); - if (ll_loadfunc(L, filename, funcname) != 0) - loaderror(L, filename); - return 1; /* library loaded successfully */ -} - - -static int loader_Croot (lua_State *L) { - const char *funcname; - const char *filename; - const char *name = luaL_checkstring(L, 1); - const char *p = strchr(name, '.'); - int stat; - if (p == NULL) return 0; /* is root */ - lua_pushlstring(L, name, p - name); - filename = findfile(L, lua_tostring(L, -1), "cpath"); - if (filename == NULL) return 1; /* root not found */ - funcname = mkfuncname(L, name); - if ((stat = ll_loadfunc(L, filename, funcname)) != 0) { - if (stat != ERRFUNC) loaderror(L, filename); /* real error */ - lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS, - name, filename); - return 1; /* function not found */ - } - return 1; -} - - -static int loader_preload (lua_State *L) { - const char *name = luaL_checkstring(L, 1); - lua_getfield(L, LUA_ENVIRONINDEX, "preload"); - if (!lua_istable(L, -1)) - luaL_error(L, LUA_QL("package.preload") " must be a table"); - lua_getfield(L, -1, name); - if (lua_isnil(L, -1)) /* not found? */ - lua_pushfstring(L, "\n\tno field package.preload['%s']", name); - return 1; -} - - -static const int sentinel_ = 0; -#define sentinel ((void *)&sentinel_) - - -static int ll_require (lua_State *L) { - const char *name = luaL_checkstring(L, 1); - int i; - lua_settop(L, 1); /* _LOADED table will be at index 2 */ - lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); - lua_getfield(L, 2, name); - if (lua_toboolean(L, -1)) { /* is it there? */ - if (lua_touserdata(L, -1) == sentinel) /* check loops */ - luaL_error(L, "loop or previous error loading module " LUA_QS, name); - return 1; /* package is already loaded */ - } - /* else must load it; iterate over available loaders */ - lua_getfield(L, LUA_ENVIRONINDEX, "loaders"); - if (!lua_istable(L, -1)) - luaL_error(L, LUA_QL("package.loaders") " must be a table"); - lua_pushliteral(L, ""); /* error message accumulator */ - for (i=1; ; i++) { - lua_rawgeti(L, -2, i); /* get a loader */ - if (lua_isnil(L, -1)) - luaL_error(L, "module " LUA_QS " not found:%s", - name, lua_tostring(L, -2)); - lua_pushstring(L, name); - lua_call(L, 1, 1); /* call it */ - if (lua_isfunction(L, -1)) /* did it find module? */ - break; /* module loaded successfully */ - else if (lua_isstring(L, -1)) /* loader returned error message? */ - lua_concat(L, 2); /* accumulate it */ - else - lua_pop(L, 1); - } - lua_pushlightuserdata(L, sentinel); - lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */ - lua_pushstring(L, name); /* pass name as argument to module */ - lua_call(L, 1, 1); /* run loaded module */ - if (!lua_isnil(L, -1)) /* non-nil return? */ - lua_setfield(L, 2, name); /* _LOADED[name] = returned value */ - lua_getfield(L, 2, name); - if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */ - lua_pushboolean(L, 1); /* use true as result */ - lua_pushvalue(L, -1); /* extra copy to be returned */ - lua_setfield(L, 2, name); /* _LOADED[name] = true */ - } - return 1; -} - -/* }====================================================== */ - - - -/* -** {====================================================== -** 'module' function -** ======================================================= -*/ - - -static void setfenv (lua_State *L) { - lua_Debug ar; - if (lua_getstack(L, 1, &ar) == 0 || - lua_getinfo(L, "f", &ar) == 0 || /* get calling function */ - lua_iscfunction(L, -1)) - luaL_error(L, LUA_QL("module") " not called from a Lua function"); - lua_pushvalue(L, -2); - lua_setfenv(L, -2); - lua_pop(L, 1); -} - - -static void dooptions (lua_State *L, int n) { - int i; - for (i = 2; i <= n; i++) { - lua_pushvalue(L, i); /* get option (a function) */ - lua_pushvalue(L, -2); /* module */ - lua_call(L, 1, 0); - } -} - - -static void modinit (lua_State *L, const char *modname) { - const char *dot; - lua_pushvalue(L, -1); - lua_setfield(L, -2, "_M"); /* module._M = module */ - lua_pushstring(L, modname); - lua_setfield(L, -2, "_NAME"); - dot = strrchr(modname, '.'); /* look for last dot in module name */ - if (dot == NULL) dot = modname; - else dot++; - /* set _PACKAGE as package name (full module name minus last part) */ - lua_pushlstring(L, modname, dot - modname); - lua_setfield(L, -2, "_PACKAGE"); -} - - -static int ll_module (lua_State *L) { - const char *modname = luaL_checkstring(L, 1); - int loaded = lua_gettop(L) + 1; /* index of _LOADED table */ - lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); - lua_getfield(L, loaded, modname); /* get _LOADED[modname] */ - if (!lua_istable(L, -1)) { /* not found? */ - lua_pop(L, 1); /* remove previous result */ - /* try global variable (and create one if it does not exist) */ - if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL) - return luaL_error(L, "name conflict for module " LUA_QS, modname); - lua_pushvalue(L, -1); - lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */ - } - /* check whether table already has a _NAME field */ - lua_getfield(L, -1, "_NAME"); - if (!lua_isnil(L, -1)) /* is table an initialized module? */ - lua_pop(L, 1); - else { /* no; initialize it */ - lua_pop(L, 1); - modinit(L, modname); - } - lua_pushvalue(L, -1); - setfenv(L); - dooptions(L, loaded - 1); - return 0; -} - - -static int ll_seeall (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - if (!lua_getmetatable(L, 1)) { - lua_createtable(L, 0, 1); /* create new metatable */ - lua_pushvalue(L, -1); - lua_setmetatable(L, 1); - } - lua_pushvalue(L, LUA_GLOBALSINDEX); - lua_setfield(L, -2, "__index"); /* mt.__index = _G */ - return 0; -} - - -/* }====================================================== */ - - - -/* auxiliary mark (for internal use) */ -#define AUXMARK "\1" - -static void setpath (lua_State *L, const char *fieldname, const char *envname, - const char *def) { - const char *path = getenv(envname); - if (path == NULL) /* no environment variable? */ - lua_pushstring(L, def); /* use default */ - else { - /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */ - path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP, - LUA_PATHSEP AUXMARK LUA_PATHSEP); - luaL_gsub(L, path, AUXMARK, def); - lua_remove(L, -2); - } - setprogdir(L); - lua_setfield(L, -2, fieldname); -} - - -static const luaL_Reg pk_funcs[] = { - {"loadlib", ll_loadlib}, - {"seeall", ll_seeall}, - {NULL, NULL} -}; - - -static const luaL_Reg ll_funcs[] = { - {"module", ll_module}, - {"require", ll_require}, - {NULL, NULL} -}; - - -static const lua_CFunction loaders[] = - {loader_preload, loader_Lua, loader_C, loader_Croot, NULL}; - - -LUALIB_API int luaopen_package (lua_State *L) { - int i; - /* create new type _LOADLIB */ - luaL_newmetatable(L, "_LOADLIB"); - lua_pushcfunction(L, gctm); - lua_setfield(L, -2, "__gc"); - /* create `package' table */ - luaL_register(L, LUA_LOADLIBNAME, pk_funcs); -#if defined(LUA_COMPAT_LOADLIB) - lua_getfield(L, -1, "loadlib"); - lua_setfield(L, LUA_GLOBALSINDEX, "loadlib"); -#endif - lua_pushvalue(L, -1); - lua_replace(L, LUA_ENVIRONINDEX); - /* create `loaders' table */ - lua_createtable(L, sizeof(loaders)/sizeof(loaders[0]) - 1, 0); - /* fill it with pre-defined loaders */ - for (i=0; loaders[i] != NULL; i++) { - lua_pushcfunction(L, loaders[i]); - lua_rawseti(L, -2, i+1); - } - lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */ - setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */ - setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */ - /* store config information */ - lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" - LUA_EXECDIR "\n" LUA_IGMARK); - lua_setfield(L, -2, "config"); - /* set field `loaded' */ - luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2); - lua_setfield(L, -2, "loaded"); - /* set field `preload' */ - lua_newtable(L); - lua_setfield(L, -2, "preload"); - lua_pushvalue(L, LUA_GLOBALSINDEX); - luaL_register(L, NULL, ll_funcs); /* open lib into global table */ - lua_pop(L, 1); - return 1; /* return 'package' table */ -} - diff --git a/libraries/lua/lobject.c b/libraries/lua/lobject.c deleted file mode 100644 index 4ff50732a..000000000 --- a/libraries/lua/lobject.c +++ /dev/null @@ -1,214 +0,0 @@ -/* -** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $ -** Some generic functions over Lua objects -** See Copyright Notice in lua.h -*/ - -#include -#include -#include -#include -#include - -#define lobject_c -#define LUA_CORE - -#include "lua.h" - -#include "ldo.h" -#include "lmem.h" -#include "lobject.h" -#include "lstate.h" -#include "lstring.h" -#include "lvm.h" - - - -const TValue luaO_nilobject_ = {{NULL}, LUA_TNIL}; - - -/* -** converts an integer to a "floating point byte", represented as -** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if -** eeeee != 0 and (xxx) otherwise. -*/ -int luaO_int2fb (unsigned int x) { - int e = 0; /* expoent */ - while (x >= 16) { - x = (x+1) >> 1; - e++; - } - if (x < 8) return x; - else return ((e+1) << 3) | (cast_int(x) - 8); -} - - -/* converts back */ -int luaO_fb2int (int x) { - int e = (x >> 3) & 31; - if (e == 0) return x; - else return ((x & 7)+8) << (e - 1); -} - - -int luaO_log2 (unsigned int x) { - static const lu_byte log_2[256] = { - 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 - }; - int l = -1; - while (x >= 256) { l += 8; x >>= 8; } - return l + log_2[x]; - -} - - -int luaO_rawequalObj (const TValue *t1, const TValue *t2) { - if (ttype(t1) != ttype(t2)) return 0; - else switch (ttype(t1)) { - case LUA_TNIL: - return 1; - case LUA_TNUMBER: - return luai_numeq(nvalue(t1), nvalue(t2)); - case LUA_TBOOLEAN: - return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */ - case LUA_TLIGHTUSERDATA: - return pvalue(t1) == pvalue(t2); - default: - lua_assert(iscollectable(t1)); - return gcvalue(t1) == gcvalue(t2); - } -} - - -int luaO_str2d (const char *s, lua_Number *result) { - char *endptr; - *result = lua_str2number(s, &endptr); - if (endptr == s) return 0; /* conversion failed */ - if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */ - *result = cast_num(strtoul(s, &endptr, 16)); - if (*endptr == '\0') return 1; /* most common case */ - while (isspace(cast(unsigned char, *endptr))) endptr++; - if (*endptr != '\0') return 0; /* invalid trailing characters? */ - return 1; -} - - - -static void pushstr (lua_State *L, const char *str) { - setsvalue2s(L, L->top, luaS_new(L, str)); - incr_top(L); -} - - -/* this function handles only `%d', `%c', %f, %p, and `%s' formats */ -const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { - int n = 1; - pushstr(L, ""); - for (;;) { - const char *e = strchr(fmt, '%'); - if (e == NULL) break; - setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt)); - incr_top(L); - switch (*(e+1)) { - case 's': { - const char *s = va_arg(argp, char *); - if (s == NULL) s = "(null)"; - pushstr(L, s); - break; - } - case 'c': { - char buff[2]; - buff[0] = cast(char, va_arg(argp, int)); - buff[1] = '\0'; - pushstr(L, buff); - break; - } - case 'd': { - setnvalue(L->top, cast_num(va_arg(argp, int))); - incr_top(L); - break; - } - case 'f': { - setnvalue(L->top, cast_num(va_arg(argp, l_uacNumber))); - incr_top(L); - break; - } - case 'p': { - char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */ - sprintf(buff, "%p", va_arg(argp, void *)); - pushstr(L, buff); - break; - } - case '%': { - pushstr(L, "%"); - break; - } - default: { - char buff[3]; - buff[0] = '%'; - buff[1] = *(e+1); - buff[2] = '\0'; - pushstr(L, buff); - break; - } - } - n += 2; - fmt = e+2; - } - pushstr(L, fmt); - luaV_concat(L, n+1, cast_int(L->top - L->base) - 1); - L->top -= n; - return svalue(L->top - 1); -} - - -const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) { - const char *msg; - va_list argp; - va_start(argp, fmt); - msg = luaO_pushvfstring(L, fmt, argp); - va_end(argp); - return msg; -} - - -void luaO_chunkid (char *out, const char *source, size_t bufflen) { - if (*source == '=') { - strncpy(out, source+1, bufflen); /* remove first char */ - out[bufflen-1] = '\0'; /* ensures null termination */ - } - else { /* out = "source", or "...source" */ - if (*source == '@') { - size_t l; - source++; /* skip the `@' */ - bufflen -= sizeof(" '...' "); - l = strlen(source); - strcpy(out, ""); - if (l > bufflen) { - source += (l-bufflen); /* get last part of file name */ - strcat(out, "..."); - } - strcat(out, source); - } - else { /* out = [string "string"] */ - size_t len = strcspn(source, "\n\r"); /* stop at first newline */ - bufflen -= sizeof(" [string \"...\"] "); - if (len > bufflen) len = bufflen; - strcpy(out, "[string \""); - if (source[len] != '\0') { /* must truncate? */ - strncat(out, source, len); - strcat(out, "..."); - } - else - strcat(out, source); - strcat(out, "\"]"); - } - } -} diff --git a/libraries/lua/lobject.h b/libraries/lua/lobject.h deleted file mode 100644 index f1e447ef3..000000000 --- a/libraries/lua/lobject.h +++ /dev/null @@ -1,381 +0,0 @@ -/* -** $Id: lobject.h,v 2.20.1.2 2008/08/06 13:29:48 roberto Exp $ -** Type definitions for Lua objects -** See Copyright Notice in lua.h -*/ - - -#ifndef lobject_h -#define lobject_h - - -#include - - -#include "llimits.h" -#include "lua.h" - - -/* tags for values visible from Lua */ -#define LAST_TAG LUA_TTHREAD - -#define NUM_TAGS (LAST_TAG+1) - - -/* -** Extra tags for non-values -*/ -#define LUA_TPROTO (LAST_TAG+1) -#define LUA_TUPVAL (LAST_TAG+2) -#define LUA_TDEADKEY (LAST_TAG+3) - - -/* -** Union of all collectable objects -*/ -typedef union GCObject GCObject; - - -/* -** Common Header for all collectable objects (in macro form, to be -** included in other objects) -*/ -#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked - - -/* -** Common header in struct form -*/ -typedef struct GCheader { - CommonHeader; -} GCheader; - - - - -/* -** Union of all Lua values -*/ -typedef union { - GCObject *gc; - void *p; - lua_Number n; - int b; -} Value; - - -/* -** Tagged Values -*/ - -#define TValuefields Value value; int tt - -typedef struct lua_TValue { - TValuefields; -} TValue; - - -/* Macros to test type */ -#define ttisnil(o) (ttype(o) == LUA_TNIL) -#define ttisnumber(o) (ttype(o) == LUA_TNUMBER) -#define ttisstring(o) (ttype(o) == LUA_TSTRING) -#define ttistable(o) (ttype(o) == LUA_TTABLE) -#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION) -#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN) -#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA) -#define ttisthread(o) (ttype(o) == LUA_TTHREAD) -#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA) - -/* Macros to access values */ -#define ttype(o) ((o)->tt) -#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc) -#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p) -#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n) -#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts) -#define tsvalue(o) (&rawtsvalue(o)->tsv) -#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u) -#define uvalue(o) (&rawuvalue(o)->uv) -#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl) -#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h) -#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b) -#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th) - -#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0)) - -/* -** for internal debug only -*/ -#define checkconsistency(obj) \ - lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt)) - -#define checkliveness(g,obj) \ - lua_assert(!iscollectable(obj) || \ - ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc))) - - -/* Macros to set values */ -#define setnilvalue(obj) ((obj)->tt=LUA_TNIL) - -#define setnvalue(obj,x) \ - { TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; } - -#define setpvalue(obj,x) \ - { TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; } - -#define setbvalue(obj,x) \ - { TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; } - -#define setsvalue(L,obj,x) \ - { TValue *i_o=(obj); \ - i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \ - checkliveness(G(L),i_o); } - -#define setuvalue(L,obj,x) \ - { TValue *i_o=(obj); \ - i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \ - checkliveness(G(L),i_o); } - -#define setthvalue(L,obj,x) \ - { TValue *i_o=(obj); \ - i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \ - checkliveness(G(L),i_o); } - -#define setclvalue(L,obj,x) \ - { TValue *i_o=(obj); \ - i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \ - checkliveness(G(L),i_o); } - -#define sethvalue(L,obj,x) \ - { TValue *i_o=(obj); \ - i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \ - checkliveness(G(L),i_o); } - -#define setptvalue(L,obj,x) \ - { TValue *i_o=(obj); \ - i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \ - checkliveness(G(L),i_o); } - - - - -#define setobj(L,obj1,obj2) \ - { const TValue *o2=(obj2); TValue *o1=(obj1); \ - o1->value = o2->value; o1->tt=o2->tt; \ - checkliveness(G(L),o1); } - - -/* -** different types of sets, according to destination -*/ - -/* from stack to (same) stack */ -#define setobjs2s setobj -/* to stack (not from same stack) */ -#define setobj2s setobj -#define setsvalue2s setsvalue -#define sethvalue2s sethvalue -#define setptvalue2s setptvalue -/* from table to same table */ -#define setobjt2t setobj -/* to table */ -#define setobj2t setobj -/* to new object */ -#define setobj2n setobj -#define setsvalue2n setsvalue - -#define setttype(obj, tt) (ttype(obj) = (tt)) - - -#define iscollectable(o) (ttype(o) >= LUA_TSTRING) - - - -typedef TValue *StkId; /* index to stack elements */ - - -/* -** String headers for string table -*/ -typedef union TString { - L_Umaxalign dummy; /* ensures maximum alignment for strings */ - struct { - CommonHeader; - lu_byte reserved; - unsigned int hash; - size_t len; - } tsv; -} TString; - - -#define getstr(ts) cast(const char *, (ts) + 1) -#define svalue(o) getstr(rawtsvalue(o)) - - - -typedef union Udata { - L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */ - struct { - CommonHeader; - struct Table *metatable; - struct Table *env; - size_t len; - } uv; -} Udata; - - - - -/* -** Function Prototypes -*/ -typedef struct Proto { - CommonHeader; - TValue *k; /* constants used by the function */ - Instruction *code; - struct Proto **p; /* functions defined inside the function */ - int *lineinfo; /* map from opcodes to source lines */ - struct LocVar *locvars; /* information about local variables */ - TString **upvalues; /* upvalue names */ - TString *source; - int sizeupvalues; - int sizek; /* size of `k' */ - int sizecode; - int sizelineinfo; - int sizep; /* size of `p' */ - int sizelocvars; - int linedefined; - int lastlinedefined; - GCObject *gclist; - lu_byte nups; /* number of upvalues */ - lu_byte numparams; - lu_byte is_vararg; - lu_byte maxstacksize; -} Proto; - - -/* masks for new-style vararg */ -#define VARARG_HASARG 1 -#define VARARG_ISVARARG 2 -#define VARARG_NEEDSARG 4 - - -typedef struct LocVar { - TString *varname; - int startpc; /* first point where variable is active */ - int endpc; /* first point where variable is dead */ -} LocVar; - - - -/* -** Upvalues -*/ - -typedef struct UpVal { - CommonHeader; - TValue *v; /* points to stack or to its own value */ - union { - TValue value; /* the value (when closed) */ - struct { /* double linked list (when open) */ - struct UpVal *prev; - struct UpVal *next; - } l; - } u; -} UpVal; - - -/* -** Closures -*/ - -#define ClosureHeader \ - CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \ - struct Table *env - -typedef struct CClosure { - ClosureHeader; - lua_CFunction f; - TValue upvalue[1]; -} CClosure; - - -typedef struct LClosure { - ClosureHeader; - struct Proto *p; - UpVal *upvals[1]; -} LClosure; - - -typedef union Closure { - CClosure c; - LClosure l; -} Closure; - - -#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC) -#define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC) - - -/* -** Tables -*/ - -typedef union TKey { - struct { - TValuefields; - struct Node *next; /* for chaining */ - } nk; - TValue tvk; -} TKey; - - -typedef struct Node { - TValue i_val; - TKey i_key; -} Node; - - -typedef struct Table { - CommonHeader; - lu_byte flags; /* 1<

lsizenode)) - - -#define luaO_nilobject (&luaO_nilobject_) - -LUAI_DATA const TValue luaO_nilobject_; - -#define ceillog2(x) (luaO_log2((x)-1) + 1) - -LUAI_FUNC int luaO_log2 (unsigned int x); -LUAI_FUNC int luaO_int2fb (unsigned int x); -LUAI_FUNC int luaO_fb2int (int x); -LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2); -LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result); -LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, - va_list argp); -LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...); -LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len); - - -#endif - diff --git a/libraries/lua/lopcodes.c b/libraries/lua/lopcodes.c deleted file mode 100644 index 4cc745230..000000000 --- a/libraries/lua/lopcodes.c +++ /dev/null @@ -1,102 +0,0 @@ -/* -** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $ -** See Copyright Notice in lua.h -*/ - - -#define lopcodes_c -#define LUA_CORE - - -#include "lopcodes.h" - - -/* ORDER OP */ - -const char *const luaP_opnames[NUM_OPCODES+1] = { - "MOVE", - "LOADK", - "LOADBOOL", - "LOADNIL", - "GETUPVAL", - "GETGLOBAL", - "GETTABLE", - "SETGLOBAL", - "SETUPVAL", - "SETTABLE", - "NEWTABLE", - "SELF", - "ADD", - "SUB", - "MUL", - "DIV", - "MOD", - "POW", - "UNM", - "NOT", - "LEN", - "CONCAT", - "JMP", - "EQ", - "LT", - "LE", - "TEST", - "TESTSET", - "CALL", - "TAILCALL", - "RETURN", - "FORLOOP", - "FORPREP", - "TFORLOOP", - "SETLIST", - "CLOSE", - "CLOSURE", - "VARARG", - NULL -}; - - -#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m)) - -const lu_byte luaP_opmodes[NUM_OPCODES] = { -/* T A B C mode opcode */ - opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */ - ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */ - ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */ - ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LOADNIL */ - ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */ - ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_GETGLOBAL */ - ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */ - ,opmode(0, 0, OpArgK, OpArgN, iABx) /* OP_SETGLOBAL */ - ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */ - ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */ - ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */ - ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */ - ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */ - ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */ - ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */ - ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */ - ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */ - ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */ - ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */ - ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ - ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ - ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ - ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */ - ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */ - ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */ - ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */ - ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TEST */ - ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */ - ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */ - ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */ - ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */ - ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */ - ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */ - ,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TFORLOOP */ - ,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */ - ,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_CLOSE */ - ,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */ - ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */ -}; - diff --git a/libraries/lua/lopcodes.h b/libraries/lua/lopcodes.h deleted file mode 100644 index 41224d6ee..000000000 --- a/libraries/lua/lopcodes.h +++ /dev/null @@ -1,268 +0,0 @@ -/* -** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $ -** Opcodes for Lua virtual machine -** See Copyright Notice in lua.h -*/ - -#ifndef lopcodes_h -#define lopcodes_h - -#include "llimits.h" - - -/*=========================================================================== - We assume that instructions are unsigned numbers. - All instructions have an opcode in the first 6 bits. - Instructions can have the following fields: - `A' : 8 bits - `B' : 9 bits - `C' : 9 bits - `Bx' : 18 bits (`B' and `C' together) - `sBx' : signed Bx - - A signed argument is represented in excess K; that is, the number - value is the unsigned value minus K. K is exactly the maximum value - for that argument (so that -max is represented by 0, and +max is - represented by 2*max), which is half the maximum for the corresponding - unsigned argument. -===========================================================================*/ - - -enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */ - - -/* -** size and position of opcode arguments. -*/ -#define SIZE_C 9 -#define SIZE_B 9 -#define SIZE_Bx (SIZE_C + SIZE_B) -#define SIZE_A 8 - -#define SIZE_OP 6 - -#define POS_OP 0 -#define POS_A (POS_OP + SIZE_OP) -#define POS_C (POS_A + SIZE_A) -#define POS_B (POS_C + SIZE_C) -#define POS_Bx POS_C - - -/* -** limits for opcode arguments. -** we use (signed) int to manipulate most arguments, -** so they must fit in LUAI_BITSINT-1 bits (-1 for sign) -*/ -#if SIZE_Bx < LUAI_BITSINT-1 -#define MAXARG_Bx ((1<>1) /* `sBx' is signed */ -#else -#define MAXARG_Bx MAX_INT -#define MAXARG_sBx MAX_INT -#endif - - -#define MAXARG_A ((1<>POS_OP) & MASK1(SIZE_OP,0))) -#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ - ((cast(Instruction, o)<>POS_A) & MASK1(SIZE_A,0))) -#define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \ - ((cast(Instruction, u)<>POS_B) & MASK1(SIZE_B,0))) -#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \ - ((cast(Instruction, b)<>POS_C) & MASK1(SIZE_C,0))) -#define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \ - ((cast(Instruction, b)<>POS_Bx) & MASK1(SIZE_Bx,0))) -#define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \ - ((cast(Instruction, b)< C) then pc++ */ -OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ - -OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ -OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ -OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */ - -OP_FORLOOP,/* A sBx R(A)+=R(A+2); - if R(A) =) R(A)*/ -OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ - -OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ -} OpCode; - - -#define NUM_OPCODES (cast(int, OP_VARARG) + 1) - - - -/*=========================================================================== - Notes: - (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1, - and can be 0: OP_CALL then sets `top' to last_result+1, so - next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'. - - (*) In OP_VARARG, if (B == 0) then use actual number of varargs and - set top (like in OP_CALL with C == 0). - - (*) In OP_RETURN, if (B == 0) then return up to `top' - - (*) In OP_SETLIST, if (B == 0) then B = `top'; - if (C == 0) then next `instruction' is real C - - (*) For comparisons, A specifies what condition the test should accept - (true or false). - - (*) All `skips' (pc++) assume that next instruction is a jump -===========================================================================*/ - - -/* -** masks for instruction properties. The format is: -** bits 0-1: op mode -** bits 2-3: C arg mode -** bits 4-5: B arg mode -** bit 6: instruction set register A -** bit 7: operator is a test -*/ - -enum OpArgMask { - OpArgN, /* argument is not used */ - OpArgU, /* argument is used */ - OpArgR, /* argument is a register or a jump offset */ - OpArgK /* argument is a constant or register/constant */ -}; - -LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES]; - -#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3)) -#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3)) -#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3)) -#define testAMode(m) (luaP_opmodes[m] & (1 << 6)) -#define testTMode(m) (luaP_opmodes[m] & (1 << 7)) - - -LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */ - - -/* number of list items to accumulate before a SETLIST instruction */ -#define LFIELDS_PER_FLUSH 50 - - -#endif diff --git a/libraries/lua/loslib.c b/libraries/lua/loslib.c deleted file mode 100644 index da06a572a..000000000 --- a/libraries/lua/loslib.c +++ /dev/null @@ -1,243 +0,0 @@ -/* -** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $ -** Standard Operating System library -** See Copyright Notice in lua.h -*/ - - -#include -#include -#include -#include -#include - -#define loslib_c -#define LUA_LIB - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -static int os_pushresult (lua_State *L, int i, const char *filename) { - int en = errno; /* calls to Lua API may change this value */ - if (i) { - lua_pushboolean(L, 1); - return 1; - } - else { - lua_pushnil(L); - lua_pushfstring(L, "%s: %s", filename, strerror(en)); - lua_pushinteger(L, en); - return 3; - } -} - - -static int os_execute (lua_State *L) { - lua_pushinteger(L, system(luaL_optstring(L, 1, NULL))); - return 1; -} - - -static int os_remove (lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - return os_pushresult(L, remove(filename) == 0, filename); -} - - -static int os_rename (lua_State *L) { - const char *fromname = luaL_checkstring(L, 1); - const char *toname = luaL_checkstring(L, 2); - return os_pushresult(L, rename(fromname, toname) == 0, fromname); -} - - -static int os_tmpname (lua_State *L) { - char buff[LUA_TMPNAMBUFSIZE]; - int err; - lua_tmpnam(buff, err); - if (err) - return luaL_error(L, "unable to generate a unique filename"); - lua_pushstring(L, buff); - return 1; -} - - -static int os_getenv (lua_State *L) { - lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ - return 1; -} - - -static int os_clock (lua_State *L) { - lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); - return 1; -} - - -/* -** {====================================================== -** Time/Date operations -** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, -** wday=%w+1, yday=%j, isdst=? } -** ======================================================= -*/ - -static void setfield (lua_State *L, const char *key, int value) { - lua_pushinteger(L, value); - lua_setfield(L, -2, key); -} - -static void setboolfield (lua_State *L, const char *key, int value) { - if (value < 0) /* undefined? */ - return; /* does not set field */ - lua_pushboolean(L, value); - lua_setfield(L, -2, key); -} - -static int getboolfield (lua_State *L, const char *key) { - int res; - lua_getfield(L, -1, key); - res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1); - lua_pop(L, 1); - return res; -} - - -static int getfield (lua_State *L, const char *key, int d) { - int res; - lua_getfield(L, -1, key); - if (lua_isnumber(L, -1)) - res = (int)lua_tointeger(L, -1); - else { - if (d < 0) - return luaL_error(L, "field " LUA_QS " missing in date table", key); - res = d; - } - lua_pop(L, 1); - return res; -} - - -static int os_date (lua_State *L) { - const char *s = luaL_optstring(L, 1, "%c"); - time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL)); - struct tm *stm; - if (*s == '!') { /* UTC? */ - stm = gmtime(&t); - s++; /* skip `!' */ - } - else - stm = localtime(&t); - if (stm == NULL) /* invalid date? */ - lua_pushnil(L); - else if (strcmp(s, "*t") == 0) { - lua_createtable(L, 0, 9); /* 9 = number of fields */ - setfield(L, "sec", stm->tm_sec); - setfield(L, "min", stm->tm_min); - setfield(L, "hour", stm->tm_hour); - setfield(L, "day", stm->tm_mday); - setfield(L, "month", stm->tm_mon+1); - setfield(L, "year", stm->tm_year+1900); - setfield(L, "wday", stm->tm_wday+1); - setfield(L, "yday", stm->tm_yday+1); - setboolfield(L, "isdst", stm->tm_isdst); - } - else { - char cc[3]; - luaL_Buffer b; - cc[0] = '%'; cc[2] = '\0'; - luaL_buffinit(L, &b); - for (; *s; s++) { - if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */ - luaL_addchar(&b, *s); - else { - size_t reslen; - char buff[200]; /* should be big enough for any conversion result */ - cc[1] = *(++s); - reslen = strftime(buff, sizeof(buff), cc, stm); - luaL_addlstring(&b, buff, reslen); - } - } - luaL_pushresult(&b); - } - return 1; -} - - -static int os_time (lua_State *L) { - time_t t; - if (lua_isnoneornil(L, 1)) /* called without args? */ - t = time(NULL); /* get current time */ - else { - struct tm ts; - luaL_checktype(L, 1, LUA_TTABLE); - lua_settop(L, 1); /* make sure table is at the top */ - ts.tm_sec = getfield(L, "sec", 0); - ts.tm_min = getfield(L, "min", 0); - ts.tm_hour = getfield(L, "hour", 12); - ts.tm_mday = getfield(L, "day", -1); - ts.tm_mon = getfield(L, "month", -1) - 1; - ts.tm_year = getfield(L, "year", -1) - 1900; - ts.tm_isdst = getboolfield(L, "isdst"); - t = mktime(&ts); - } - if (t == (time_t)(-1)) - lua_pushnil(L); - else - lua_pushnumber(L, (lua_Number)t); - return 1; -} - - -static int os_difftime (lua_State *L) { - lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), - (time_t)(luaL_optnumber(L, 2, 0)))); - return 1; -} - -/* }====================================================== */ - - -static int os_setlocale (lua_State *L) { - static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, - LC_NUMERIC, LC_TIME}; - static const char *const catnames[] = {"all", "collate", "ctype", "monetary", - "numeric", "time", NULL}; - const char *l = luaL_optstring(L, 1, NULL); - int op = luaL_checkoption(L, 2, "all", catnames); - lua_pushstring(L, setlocale(cat[op], l)); - return 1; -} - - -static int os_exit (lua_State *L) { - exit(luaL_optint(L, 1, EXIT_SUCCESS)); -} - -static const luaL_Reg syslib[] = { - {"clock", os_clock}, - {"date", os_date}, - {"difftime", os_difftime}, - {"execute", os_execute}, - {"exit", os_exit}, - {"getenv", os_getenv}, - {"remove", os_remove}, - {"rename", os_rename}, - {"setlocale", os_setlocale}, - {"time", os_time}, - {"tmpname", os_tmpname}, - {NULL, NULL} -}; - -/* }====================================================== */ - - - -LUALIB_API int luaopen_os (lua_State *L) { - luaL_register(L, LUA_OSLIBNAME, syslib); - return 1; -} - diff --git a/libraries/lua/lparser.c b/libraries/lua/lparser.c deleted file mode 100644 index dda7488dc..000000000 --- a/libraries/lua/lparser.c +++ /dev/null @@ -1,1339 +0,0 @@ -/* -** $Id: lparser.c,v 2.42.1.4 2011/10/21 19:31:42 roberto Exp $ -** Lua Parser -** See Copyright Notice in lua.h -*/ - - -#include - -#define lparser_c -#define LUA_CORE - -#include "lua.h" - -#include "lcode.h" -#include "ldebug.h" -#include "ldo.h" -#include "lfunc.h" -#include "llex.h" -#include "lmem.h" -#include "lobject.h" -#include "lopcodes.h" -#include "lparser.h" -#include "lstate.h" -#include "lstring.h" -#include "ltable.h" - - - -#define hasmultret(k) ((k) == VCALL || (k) == VVARARG) - -#define getlocvar(fs, i) ((fs)->f->locvars[(fs)->actvar[i]]) - -#define luaY_checklimit(fs,v,l,m) if ((v)>(l)) errorlimit(fs,l,m) - - -/* -** nodes for block list (list of active blocks) -*/ -typedef struct BlockCnt { - struct BlockCnt *previous; /* chain */ - int breaklist; /* list of jumps out of this loop */ - lu_byte nactvar; /* # active locals outside the breakable structure */ - lu_byte upval; /* true if some variable in the block is an upvalue */ - lu_byte isbreakable; /* true if `block' is a loop */ -} BlockCnt; - - - -/* -** prototypes for recursive non-terminal functions -*/ -static void chunk (LexState *ls); -static void expr (LexState *ls, expdesc *v); - - -static void anchor_token (LexState *ls) { - if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) { - TString *ts = ls->t.seminfo.ts; - luaX_newstring(ls, getstr(ts), ts->tsv.len); - } -} - - -static void error_expected (LexState *ls, int token) { - luaX_syntaxerror(ls, - luaO_pushfstring(ls->L, LUA_QS " expected", luaX_token2str(ls, token))); -} - - -static void errorlimit (FuncState *fs, int limit, const char *what) { - const char *msg = (fs->f->linedefined == 0) ? - luaO_pushfstring(fs->L, "main function has more than %d %s", limit, what) : - luaO_pushfstring(fs->L, "function at line %d has more than %d %s", - fs->f->linedefined, limit, what); - luaX_lexerror(fs->ls, msg, 0); -} - - -static int testnext (LexState *ls, int c) { - if (ls->t.token == c) { - luaX_next(ls); - return 1; - } - else return 0; -} - - -static void check (LexState *ls, int c) { - if (ls->t.token != c) - error_expected(ls, c); -} - -static void checknext (LexState *ls, int c) { - check(ls, c); - luaX_next(ls); -} - - -#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } - - - -static void check_match (LexState *ls, int what, int who, int where) { - if (!testnext(ls, what)) { - if (where == ls->linenumber) - error_expected(ls, what); - else { - luaX_syntaxerror(ls, luaO_pushfstring(ls->L, - LUA_QS " expected (to close " LUA_QS " at line %d)", - luaX_token2str(ls, what), luaX_token2str(ls, who), where)); - } - } -} - - -static TString *str_checkname (LexState *ls) { - TString *ts; - check(ls, TK_NAME); - ts = ls->t.seminfo.ts; - luaX_next(ls); - return ts; -} - - -static void init_exp (expdesc *e, expkind k, int i) { - e->f = e->t = NO_JUMP; - e->k = k; - e->u.s.info = i; -} - - -static void codestring (LexState *ls, expdesc *e, TString *s) { - init_exp(e, VK, luaK_stringK(ls->fs, s)); -} - - -static void checkname(LexState *ls, expdesc *e) { - codestring(ls, e, str_checkname(ls)); -} - - -static int registerlocalvar (LexState *ls, TString *varname) { - FuncState *fs = ls->fs; - Proto *f = fs->f; - int oldsize = f->sizelocvars; - luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars, - LocVar, SHRT_MAX, "too many local variables"); - while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL; - f->locvars[fs->nlocvars].varname = varname; - luaC_objbarrier(ls->L, f, varname); - return fs->nlocvars++; -} - - -#define new_localvarliteral(ls,v,n) \ - new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n) - - -static void new_localvar (LexState *ls, TString *name, int n) { - FuncState *fs = ls->fs; - luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, "local variables"); - fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name)); -} - - -static void adjustlocalvars (LexState *ls, int nvars) { - FuncState *fs = ls->fs; - fs->nactvar = cast_byte(fs->nactvar + nvars); - for (; nvars; nvars--) { - getlocvar(fs, fs->nactvar - nvars).startpc = fs->pc; - } -} - - -static void removevars (LexState *ls, int tolevel) { - FuncState *fs = ls->fs; - while (fs->nactvar > tolevel) - getlocvar(fs, --fs->nactvar).endpc = fs->pc; -} - - -static int indexupvalue (FuncState *fs, TString *name, expdesc *v) { - int i; - Proto *f = fs->f; - int oldsize = f->sizeupvalues; - for (i=0; inups; i++) { - if (fs->upvalues[i].k == v->k && fs->upvalues[i].info == v->u.s.info) { - lua_assert(f->upvalues[i] == name); - return i; - } - } - /* new one */ - luaY_checklimit(fs, f->nups + 1, LUAI_MAXUPVALUES, "upvalues"); - luaM_growvector(fs->L, f->upvalues, f->nups, f->sizeupvalues, - TString *, MAX_INT, ""); - while (oldsize < f->sizeupvalues) f->upvalues[oldsize++] = NULL; - f->upvalues[f->nups] = name; - luaC_objbarrier(fs->L, f, name); - lua_assert(v->k == VLOCAL || v->k == VUPVAL); - fs->upvalues[f->nups].k = cast_byte(v->k); - fs->upvalues[f->nups].info = cast_byte(v->u.s.info); - return f->nups++; -} - - -static int searchvar (FuncState *fs, TString *n) { - int i; - for (i=fs->nactvar-1; i >= 0; i--) { - if (n == getlocvar(fs, i).varname) - return i; - } - return -1; /* not found */ -} - - -static void markupval (FuncState *fs, int level) { - BlockCnt *bl = fs->bl; - while (bl && bl->nactvar > level) bl = bl->previous; - if (bl) bl->upval = 1; -} - - -static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { - if (fs == NULL) { /* no more levels? */ - init_exp(var, VGLOBAL, NO_REG); /* default is global variable */ - return VGLOBAL; - } - else { - int v = searchvar(fs, n); /* look up at current level */ - if (v >= 0) { - init_exp(var, VLOCAL, v); - if (!base) - markupval(fs, v); /* local will be used as an upval */ - return VLOCAL; - } - else { /* not found at current level; try upper one */ - if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL) - return VGLOBAL; - var->u.s.info = indexupvalue(fs, n, var); /* else was LOCAL or UPVAL */ - var->k = VUPVAL; /* upvalue in this level */ - return VUPVAL; - } - } -} - - -static void singlevar (LexState *ls, expdesc *var) { - TString *varname = str_checkname(ls); - FuncState *fs = ls->fs; - if (singlevaraux(fs, varname, var, 1) == VGLOBAL) - var->u.s.info = luaK_stringK(fs, varname); /* info points to global name */ -} - - -static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { - FuncState *fs = ls->fs; - int extra = nvars - nexps; - if (hasmultret(e->k)) { - extra++; /* includes call itself */ - if (extra < 0) extra = 0; - luaK_setreturns(fs, e, extra); /* last exp. provides the difference */ - if (extra > 1) luaK_reserveregs(fs, extra-1); - } - else { - if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */ - if (extra > 0) { - int reg = fs->freereg; - luaK_reserveregs(fs, extra); - luaK_nil(fs, reg, extra); - } - } -} - - -static void enterlevel (LexState *ls) { - if (++ls->L->nCcalls > LUAI_MAXCCALLS) - luaX_lexerror(ls, "chunk has too many syntax levels", 0); -} - - -#define leavelevel(ls) ((ls)->L->nCcalls--) - - -static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) { - bl->breaklist = NO_JUMP; - bl->isbreakable = isbreakable; - bl->nactvar = fs->nactvar; - bl->upval = 0; - bl->previous = fs->bl; - fs->bl = bl; - lua_assert(fs->freereg == fs->nactvar); -} - - -static void leaveblock (FuncState *fs) { - BlockCnt *bl = fs->bl; - fs->bl = bl->previous; - removevars(fs->ls, bl->nactvar); - if (bl->upval) - luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); - /* a block either controls scope or breaks (never both) */ - lua_assert(!bl->isbreakable || !bl->upval); - lua_assert(bl->nactvar == fs->nactvar); - fs->freereg = fs->nactvar; /* free registers */ - luaK_patchtohere(fs, bl->breaklist); -} - - -static void pushclosure (LexState *ls, FuncState *func, expdesc *v) { - FuncState *fs = ls->fs; - Proto *f = fs->f; - int oldsize = f->sizep; - int i; - luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *, - MAXARG_Bx, "constant table overflow"); - while (oldsize < f->sizep) f->p[oldsize++] = NULL; - f->p[fs->np++] = func->f; - luaC_objbarrier(ls->L, f, func->f); - init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1)); - for (i=0; if->nups; i++) { - OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; - luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0); - } -} - - -static void open_func (LexState *ls, FuncState *fs) { - lua_State *L = ls->L; - Proto *f = luaF_newproto(L); - fs->f = f; - fs->prev = ls->fs; /* linked list of funcstates */ - fs->ls = ls; - fs->L = L; - ls->fs = fs; - fs->pc = 0; - fs->lasttarget = -1; - fs->jpc = NO_JUMP; - fs->freereg = 0; - fs->nk = 0; - fs->np = 0; - fs->nlocvars = 0; - fs->nactvar = 0; - fs->bl = NULL; - f->source = ls->source; - f->maxstacksize = 2; /* registers 0/1 are always valid */ - fs->h = luaH_new(L, 0, 0); - /* anchor table of constants and prototype (to avoid being collected) */ - sethvalue2s(L, L->top, fs->h); - incr_top(L); - setptvalue2s(L, L->top, f); - incr_top(L); -} - - -static void close_func (LexState *ls) { - lua_State *L = ls->L; - FuncState *fs = ls->fs; - Proto *f = fs->f; - removevars(ls, 0); - luaK_ret(fs, 0, 0); /* final return */ - luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction); - f->sizecode = fs->pc; - luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int); - f->sizelineinfo = fs->pc; - luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue); - f->sizek = fs->nk; - luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *); - f->sizep = fs->np; - luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar); - f->sizelocvars = fs->nlocvars; - luaM_reallocvector(L, f->upvalues, f->sizeupvalues, f->nups, TString *); - f->sizeupvalues = f->nups; - lua_assert(luaG_checkcode(f)); - lua_assert(fs->bl == NULL); - ls->fs = fs->prev; - /* last token read was anchored in defunct function; must reanchor it */ - if (fs) anchor_token(ls); - L->top -= 2; /* remove table and prototype from the stack */ -} - - -Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) { - struct LexState lexstate; - struct FuncState funcstate; - lexstate.buff = buff; - luaX_setinput(L, &lexstate, z, luaS_new(L, name)); - open_func(&lexstate, &funcstate); - funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */ - luaX_next(&lexstate); /* read first token */ - chunk(&lexstate); - check(&lexstate, TK_EOS); - close_func(&lexstate); - lua_assert(funcstate.prev == NULL); - lua_assert(funcstate.f->nups == 0); - lua_assert(lexstate.fs == NULL); - return funcstate.f; -} - - - -/*============================================================*/ -/* GRAMMAR RULES */ -/*============================================================*/ - - -static void field (LexState *ls, expdesc *v) { - /* field -> ['.' | ':'] NAME */ - FuncState *fs = ls->fs; - expdesc key; - luaK_exp2anyreg(fs, v); - luaX_next(ls); /* skip the dot or colon */ - checkname(ls, &key); - luaK_indexed(fs, v, &key); -} - - -static void yindex (LexState *ls, expdesc *v) { - /* index -> '[' expr ']' */ - luaX_next(ls); /* skip the '[' */ - expr(ls, v); - luaK_exp2val(ls->fs, v); - checknext(ls, ']'); -} - - -/* -** {====================================================================== -** Rules for Constructors -** ======================================================================= -*/ - - -struct ConsControl { - expdesc v; /* last list item read */ - expdesc *t; /* table descriptor */ - int nh; /* total number of `record' elements */ - int na; /* total number of array elements */ - int tostore; /* number of array elements pending to be stored */ -}; - - -static void recfield (LexState *ls, struct ConsControl *cc) { - /* recfield -> (NAME | `['exp1`]') = exp1 */ - FuncState *fs = ls->fs; - int reg = ls->fs->freereg; - expdesc key, val; - int rkkey; - if (ls->t.token == TK_NAME) { - luaY_checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); - checkname(ls, &key); - } - else /* ls->t.token == '[' */ - yindex(ls, &key); - cc->nh++; - checknext(ls, '='); - rkkey = luaK_exp2RK(fs, &key); - expr(ls, &val); - luaK_codeABC(fs, OP_SETTABLE, cc->t->u.s.info, rkkey, luaK_exp2RK(fs, &val)); - fs->freereg = reg; /* free registers */ -} - - -static void closelistfield (FuncState *fs, struct ConsControl *cc) { - if (cc->v.k == VVOID) return; /* there is no list item */ - luaK_exp2nextreg(fs, &cc->v); - cc->v.k = VVOID; - if (cc->tostore == LFIELDS_PER_FLUSH) { - luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); /* flush */ - cc->tostore = 0; /* no more items pending */ - } -} - - -static void lastlistfield (FuncState *fs, struct ConsControl *cc) { - if (cc->tostore == 0) return; - if (hasmultret(cc->v.k)) { - luaK_setmultret(fs, &cc->v); - luaK_setlist(fs, cc->t->u.s.info, cc->na, LUA_MULTRET); - cc->na--; /* do not count last expression (unknown number of elements) */ - } - else { - if (cc->v.k != VVOID) - luaK_exp2nextreg(fs, &cc->v); - luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); - } -} - - -static void listfield (LexState *ls, struct ConsControl *cc) { - expr(ls, &cc->v); - luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor"); - cc->na++; - cc->tostore++; -} - - -static void constructor (LexState *ls, expdesc *t) { - /* constructor -> ?? */ - FuncState *fs = ls->fs; - int line = ls->linenumber; - int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); - struct ConsControl cc; - cc.na = cc.nh = cc.tostore = 0; - cc.t = t; - init_exp(t, VRELOCABLE, pc); - init_exp(&cc.v, VVOID, 0); /* no value (yet) */ - luaK_exp2nextreg(ls->fs, t); /* fix it at stack top (for gc) */ - checknext(ls, '{'); - do { - lua_assert(cc.v.k == VVOID || cc.tostore > 0); - if (ls->t.token == '}') break; - closelistfield(fs, &cc); - switch(ls->t.token) { - case TK_NAME: { /* may be listfields or recfields */ - luaX_lookahead(ls); - if (ls->lookahead.token != '=') /* expression? */ - listfield(ls, &cc); - else - recfield(ls, &cc); - break; - } - case '[': { /* constructor_item -> recfield */ - recfield(ls, &cc); - break; - } - default: { /* constructor_part -> listfield */ - listfield(ls, &cc); - break; - } - } - } while (testnext(ls, ',') || testnext(ls, ';')); - check_match(ls, '}', '{', line); - lastlistfield(fs, &cc); - SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */ - SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */ -} - -/* }====================================================================== */ - - - -static void parlist (LexState *ls) { - /* parlist -> [ param { `,' param } ] */ - FuncState *fs = ls->fs; - Proto *f = fs->f; - int nparams = 0; - f->is_vararg = 0; - if (ls->t.token != ')') { /* is `parlist' not empty? */ - do { - switch (ls->t.token) { - case TK_NAME: { /* param -> NAME */ - new_localvar(ls, str_checkname(ls), nparams++); - break; - } - case TK_DOTS: { /* param -> `...' */ - luaX_next(ls); -#if defined(LUA_COMPAT_VARARG) - /* use `arg' as default name */ - new_localvarliteral(ls, "arg", nparams++); - f->is_vararg = VARARG_HASARG | VARARG_NEEDSARG; -#endif - f->is_vararg |= VARARG_ISVARARG; - break; - } - default: luaX_syntaxerror(ls, " or " LUA_QL("...") " expected"); - } - } while (!f->is_vararg && testnext(ls, ',')); - } - adjustlocalvars(ls, nparams); - f->numparams = cast_byte(fs->nactvar - (f->is_vararg & VARARG_HASARG)); - luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */ -} - - -static void body (LexState *ls, expdesc *e, int needself, int line) { - /* body -> `(' parlist `)' chunk END */ - FuncState new_fs; - open_func(ls, &new_fs); - new_fs.f->linedefined = line; - checknext(ls, '('); - if (needself) { - new_localvarliteral(ls, "self", 0); - adjustlocalvars(ls, 1); - } - parlist(ls); - checknext(ls, ')'); - chunk(ls); - new_fs.f->lastlinedefined = ls->linenumber; - check_match(ls, TK_END, TK_FUNCTION, line); - close_func(ls); - pushclosure(ls, &new_fs, e); -} - - -static int explist1 (LexState *ls, expdesc *v) { - /* explist1 -> expr { `,' expr } */ - int n = 1; /* at least one expression */ - expr(ls, v); - while (testnext(ls, ',')) { - luaK_exp2nextreg(ls->fs, v); - expr(ls, v); - n++; - } - return n; -} - - -static void funcargs (LexState *ls, expdesc *f) { - FuncState *fs = ls->fs; - expdesc args; - int base, nparams; - int line = ls->linenumber; - switch (ls->t.token) { - case '(': { /* funcargs -> `(' [ explist1 ] `)' */ - if (line != ls->lastline) - luaX_syntaxerror(ls,"ambiguous syntax (function call x new statement)"); - luaX_next(ls); - if (ls->t.token == ')') /* arg list is empty? */ - args.k = VVOID; - else { - explist1(ls, &args); - luaK_setmultret(fs, &args); - } - check_match(ls, ')', '(', line); - break; - } - case '{': { /* funcargs -> constructor */ - constructor(ls, &args); - break; - } - case TK_STRING: { /* funcargs -> STRING */ - codestring(ls, &args, ls->t.seminfo.ts); - luaX_next(ls); /* must use `seminfo' before `next' */ - break; - } - default: { - luaX_syntaxerror(ls, "function arguments expected"); - return; - } - } - lua_assert(f->k == VNONRELOC); - base = f->u.s.info; /* base register for call */ - if (hasmultret(args.k)) - nparams = LUA_MULTRET; /* open call */ - else { - if (args.k != VVOID) - luaK_exp2nextreg(fs, &args); /* close last argument */ - nparams = fs->freereg - (base+1); - } - init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); - luaK_fixline(fs, line); - fs->freereg = base+1; /* call remove function and arguments and leaves - (unless changed) one result */ -} - - - - -/* -** {====================================================================== -** Expression parsing -** ======================================================================= -*/ - - -static void prefixexp (LexState *ls, expdesc *v) { - /* prefixexp -> NAME | '(' expr ')' */ - switch (ls->t.token) { - case '(': { - int line = ls->linenumber; - luaX_next(ls); - expr(ls, v); - check_match(ls, ')', '(', line); - luaK_dischargevars(ls->fs, v); - return; - } - case TK_NAME: { - singlevar(ls, v); - return; - } - default: { - luaX_syntaxerror(ls, "unexpected symbol"); - return; - } - } -} - - -static void primaryexp (LexState *ls, expdesc *v) { - /* primaryexp -> - prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */ - FuncState *fs = ls->fs; - prefixexp(ls, v); - for (;;) { - switch (ls->t.token) { - case '.': { /* field */ - field(ls, v); - break; - } - case '[': { /* `[' exp1 `]' */ - expdesc key; - luaK_exp2anyreg(fs, v); - yindex(ls, &key); - luaK_indexed(fs, v, &key); - break; - } - case ':': { /* `:' NAME funcargs */ - expdesc key; - luaX_next(ls); - checkname(ls, &key); - luaK_self(fs, v, &key); - funcargs(ls, v); - break; - } - case '(': case TK_STRING: case '{': { /* funcargs */ - luaK_exp2nextreg(fs, v); - funcargs(ls, v); - break; - } - default: return; - } - } -} - - -static void simpleexp (LexState *ls, expdesc *v) { - /* simpleexp -> NUMBER | STRING | NIL | true | false | ... | - constructor | FUNCTION body | primaryexp */ - switch (ls->t.token) { - case TK_NUMBER: { - init_exp(v, VKNUM, 0); - v->u.nval = ls->t.seminfo.r; - break; - } - case TK_STRING: { - codestring(ls, v, ls->t.seminfo.ts); - break; - } - case TK_NIL: { - init_exp(v, VNIL, 0); - break; - } - case TK_TRUE: { - init_exp(v, VTRUE, 0); - break; - } - case TK_FALSE: { - init_exp(v, VFALSE, 0); - break; - } - case TK_DOTS: { /* vararg */ - FuncState *fs = ls->fs; - check_condition(ls, fs->f->is_vararg, - "cannot use " LUA_QL("...") " outside a vararg function"); - fs->f->is_vararg &= ~VARARG_NEEDSARG; /* don't need 'arg' */ - init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0)); - break; - } - case '{': { /* constructor */ - constructor(ls, v); - return; - } - case TK_FUNCTION: { - luaX_next(ls); - body(ls, v, 0, ls->linenumber); - return; - } - default: { - primaryexp(ls, v); - return; - } - } - luaX_next(ls); -} - - -static UnOpr getunopr (int op) { - switch (op) { - case TK_NOT: return OPR_NOT; - case '-': return OPR_MINUS; - case '#': return OPR_LEN; - default: return OPR_NOUNOPR; - } -} - - -static BinOpr getbinopr (int op) { - switch (op) { - case '+': return OPR_ADD; - case '-': return OPR_SUB; - case '*': return OPR_MUL; - case '/': return OPR_DIV; - case '%': return OPR_MOD; - case '^': return OPR_POW; - case TK_CONCAT: return OPR_CONCAT; - case TK_NE: return OPR_NE; - case TK_EQ: return OPR_EQ; - case '<': return OPR_LT; - case TK_LE: return OPR_LE; - case '>': return OPR_GT; - case TK_GE: return OPR_GE; - case TK_AND: return OPR_AND; - case TK_OR: return OPR_OR; - default: return OPR_NOBINOPR; - } -} - - -static const struct { - lu_byte left; /* left priority for each binary operator */ - lu_byte right; /* right priority */ -} priority[] = { /* ORDER OPR */ - {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `/' `%' */ - {10, 9}, {5, 4}, /* power and concat (right associative) */ - {3, 3}, {3, 3}, /* equality and inequality */ - {3, 3}, {3, 3}, {3, 3}, {3, 3}, /* order */ - {2, 2}, {1, 1} /* logical (and/or) */ -}; - -#define UNARY_PRIORITY 8 /* priority for unary operators */ - - -/* -** subexpr -> (simpleexp | unop subexpr) { binop subexpr } -** where `binop' is any binary operator with a priority higher than `limit' -*/ -static BinOpr subexpr (LexState *ls, expdesc *v, unsigned int limit) { - BinOpr op; - UnOpr uop; - enterlevel(ls); - uop = getunopr(ls->t.token); - if (uop != OPR_NOUNOPR) { - luaX_next(ls); - subexpr(ls, v, UNARY_PRIORITY); - luaK_prefix(ls->fs, uop, v); - } - else simpleexp(ls, v); - /* expand while operators have priorities higher than `limit' */ - op = getbinopr(ls->t.token); - while (op != OPR_NOBINOPR && priority[op].left > limit) { - expdesc v2; - BinOpr nextop; - luaX_next(ls); - luaK_infix(ls->fs, op, v); - /* read sub-expression with higher priority */ - nextop = subexpr(ls, &v2, priority[op].right); - luaK_posfix(ls->fs, op, v, &v2); - op = nextop; - } - leavelevel(ls); - return op; /* return first untreated operator */ -} - - -static void expr (LexState *ls, expdesc *v) { - subexpr(ls, v, 0); -} - -/* }==================================================================== */ - - - -/* -** {====================================================================== -** Rules for Statements -** ======================================================================= -*/ - - -static int block_follow (int token) { - switch (token) { - case TK_ELSE: case TK_ELSEIF: case TK_END: - case TK_UNTIL: case TK_EOS: - return 1; - default: return 0; - } -} - - -static void block (LexState *ls) { - /* block -> chunk */ - FuncState *fs = ls->fs; - BlockCnt bl; - enterblock(fs, &bl, 0); - chunk(ls); - lua_assert(bl.breaklist == NO_JUMP); - leaveblock(fs); -} - - -/* -** structure to chain all variables in the left-hand side of an -** assignment -*/ -struct LHS_assign { - struct LHS_assign *prev; - expdesc v; /* variable (global, local, upvalue, or indexed) */ -}; - - -/* -** check whether, in an assignment to a local variable, the local variable -** is needed in a previous assignment (to a table). If so, save original -** local value in a safe place and use this safe copy in the previous -** assignment. -*/ -static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { - FuncState *fs = ls->fs; - int extra = fs->freereg; /* eventual position to save local variable */ - int conflict = 0; - for (; lh; lh = lh->prev) { - if (lh->v.k == VINDEXED) { - if (lh->v.u.s.info == v->u.s.info) { /* conflict? */ - conflict = 1; - lh->v.u.s.info = extra; /* previous assignment will use safe copy */ - } - if (lh->v.u.s.aux == v->u.s.info) { /* conflict? */ - conflict = 1; - lh->v.u.s.aux = extra; /* previous assignment will use safe copy */ - } - } - } - if (conflict) { - luaK_codeABC(fs, OP_MOVE, fs->freereg, v->u.s.info, 0); /* make copy */ - luaK_reserveregs(fs, 1); - } -} - - -static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) { - expdesc e; - check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED, - "syntax error"); - if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */ - struct LHS_assign nv; - nv.prev = lh; - primaryexp(ls, &nv.v); - if (nv.v.k == VLOCAL) - check_conflict(ls, lh, &nv.v); - luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls, - "variables in assignment"); - assignment(ls, &nv, nvars+1); - } - else { /* assignment -> `=' explist1 */ - int nexps; - checknext(ls, '='); - nexps = explist1(ls, &e); - if (nexps != nvars) { - adjust_assign(ls, nvars, nexps, &e); - if (nexps > nvars) - ls->fs->freereg -= nexps - nvars; /* remove extra values */ - } - else { - luaK_setoneret(ls->fs, &e); /* close last expression */ - luaK_storevar(ls->fs, &lh->v, &e); - return; /* avoid default */ - } - } - init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ - luaK_storevar(ls->fs, &lh->v, &e); -} - - -static int cond (LexState *ls) { - /* cond -> exp */ - expdesc v; - expr(ls, &v); /* read condition */ - if (v.k == VNIL) v.k = VFALSE; /* `falses' are all equal here */ - luaK_goiftrue(ls->fs, &v); - return v.f; -} - - -static void breakstat (LexState *ls) { - FuncState *fs = ls->fs; - BlockCnt *bl = fs->bl; - int upval = 0; - while (bl && !bl->isbreakable) { - upval |= bl->upval; - bl = bl->previous; - } - if (!bl) - luaX_syntaxerror(ls, "no loop to break"); - if (upval) - luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); - luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); -} - - -static void whilestat (LexState *ls, int line) { - /* whilestat -> WHILE cond DO block END */ - FuncState *fs = ls->fs; - int whileinit; - int condexit; - BlockCnt bl; - luaX_next(ls); /* skip WHILE */ - whileinit = luaK_getlabel(fs); - condexit = cond(ls); - enterblock(fs, &bl, 1); - checknext(ls, TK_DO); - block(ls); - luaK_patchlist(fs, luaK_jump(fs), whileinit); - check_match(ls, TK_END, TK_WHILE, line); - leaveblock(fs); - luaK_patchtohere(fs, condexit); /* false conditions finish the loop */ -} - - -static void repeatstat (LexState *ls, int line) { - /* repeatstat -> REPEAT block UNTIL cond */ - int condexit; - FuncState *fs = ls->fs; - int repeat_init = luaK_getlabel(fs); - BlockCnt bl1, bl2; - enterblock(fs, &bl1, 1); /* loop block */ - enterblock(fs, &bl2, 0); /* scope block */ - luaX_next(ls); /* skip REPEAT */ - chunk(ls); - check_match(ls, TK_UNTIL, TK_REPEAT, line); - condexit = cond(ls); /* read condition (inside scope block) */ - if (!bl2.upval) { /* no upvalues? */ - leaveblock(fs); /* finish scope */ - luaK_patchlist(ls->fs, condexit, repeat_init); /* close the loop */ - } - else { /* complete semantics when there are upvalues */ - breakstat(ls); /* if condition then break */ - luaK_patchtohere(ls->fs, condexit); /* else... */ - leaveblock(fs); /* finish scope... */ - luaK_patchlist(ls->fs, luaK_jump(fs), repeat_init); /* and repeat */ - } - leaveblock(fs); /* finish loop */ -} - - -static int exp1 (LexState *ls) { - expdesc e; - int k; - expr(ls, &e); - k = e.k; - luaK_exp2nextreg(ls->fs, &e); - return k; -} - - -static void forbody (LexState *ls, int base, int line, int nvars, int isnum) { - /* forbody -> DO block */ - BlockCnt bl; - FuncState *fs = ls->fs; - int prep, endfor; - adjustlocalvars(ls, 3); /* control variables */ - checknext(ls, TK_DO); - prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs); - enterblock(fs, &bl, 0); /* scope for declared variables */ - adjustlocalvars(ls, nvars); - luaK_reserveregs(fs, nvars); - block(ls); - leaveblock(fs); /* end of scope for declared variables */ - luaK_patchtohere(fs, prep); - endfor = (isnum) ? luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP) : - luaK_codeABC(fs, OP_TFORLOOP, base, 0, nvars); - luaK_fixline(fs, line); /* pretend that `OP_FOR' starts the loop */ - luaK_patchlist(fs, (isnum ? endfor : luaK_jump(fs)), prep + 1); -} - - -static void fornum (LexState *ls, TString *varname, int line) { - /* fornum -> NAME = exp1,exp1[,exp1] forbody */ - FuncState *fs = ls->fs; - int base = fs->freereg; - new_localvarliteral(ls, "(for index)", 0); - new_localvarliteral(ls, "(for limit)", 1); - new_localvarliteral(ls, "(for step)", 2); - new_localvar(ls, varname, 3); - checknext(ls, '='); - exp1(ls); /* initial value */ - checknext(ls, ','); - exp1(ls); /* limit */ - if (testnext(ls, ',')) - exp1(ls); /* optional step */ - else { /* default step = 1 */ - luaK_codeABx(fs, OP_LOADK, fs->freereg, luaK_numberK(fs, 1)); - luaK_reserveregs(fs, 1); - } - forbody(ls, base, line, 1, 1); -} - - -static void forlist (LexState *ls, TString *indexname) { - /* forlist -> NAME {,NAME} IN explist1 forbody */ - FuncState *fs = ls->fs; - expdesc e; - int nvars = 0; - int line; - int base = fs->freereg; - /* create control variables */ - new_localvarliteral(ls, "(for generator)", nvars++); - new_localvarliteral(ls, "(for state)", nvars++); - new_localvarliteral(ls, "(for control)", nvars++); - /* create declared variables */ - new_localvar(ls, indexname, nvars++); - while (testnext(ls, ',')) - new_localvar(ls, str_checkname(ls), nvars++); - checknext(ls, TK_IN); - line = ls->linenumber; - adjust_assign(ls, 3, explist1(ls, &e), &e); - luaK_checkstack(fs, 3); /* extra space to call generator */ - forbody(ls, base, line, nvars - 3, 0); -} - - -static void forstat (LexState *ls, int line) { - /* forstat -> FOR (fornum | forlist) END */ - FuncState *fs = ls->fs; - TString *varname; - BlockCnt bl; - enterblock(fs, &bl, 1); /* scope for loop and control variables */ - luaX_next(ls); /* skip `for' */ - varname = str_checkname(ls); /* first variable name */ - switch (ls->t.token) { - case '=': fornum(ls, varname, line); break; - case ',': case TK_IN: forlist(ls, varname); break; - default: luaX_syntaxerror(ls, LUA_QL("=") " or " LUA_QL("in") " expected"); - } - check_match(ls, TK_END, TK_FOR, line); - leaveblock(fs); /* loop scope (`break' jumps to this point) */ -} - - -static int test_then_block (LexState *ls) { - /* test_then_block -> [IF | ELSEIF] cond THEN block */ - int condexit; - luaX_next(ls); /* skip IF or ELSEIF */ - condexit = cond(ls); - checknext(ls, TK_THEN); - block(ls); /* `then' part */ - return condexit; -} - - -static void ifstat (LexState *ls, int line) { - /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ - FuncState *fs = ls->fs; - int flist; - int escapelist = NO_JUMP; - flist = test_then_block(ls); /* IF cond THEN block */ - while (ls->t.token == TK_ELSEIF) { - luaK_concat(fs, &escapelist, luaK_jump(fs)); - luaK_patchtohere(fs, flist); - flist = test_then_block(ls); /* ELSEIF cond THEN block */ - } - if (ls->t.token == TK_ELSE) { - luaK_concat(fs, &escapelist, luaK_jump(fs)); - luaK_patchtohere(fs, flist); - luaX_next(ls); /* skip ELSE (after patch, for correct line info) */ - block(ls); /* `else' part */ - } - else - luaK_concat(fs, &escapelist, flist); - luaK_patchtohere(fs, escapelist); - check_match(ls, TK_END, TK_IF, line); -} - - -static void localfunc (LexState *ls) { - expdesc v, b; - FuncState *fs = ls->fs; - new_localvar(ls, str_checkname(ls), 0); - init_exp(&v, VLOCAL, fs->freereg); - luaK_reserveregs(fs, 1); - adjustlocalvars(ls, 1); - body(ls, &b, 0, ls->linenumber); - luaK_storevar(fs, &v, &b); - /* debug information will only see the variable after this point! */ - getlocvar(fs, fs->nactvar - 1).startpc = fs->pc; -} - - -static void localstat (LexState *ls) { - /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */ - int nvars = 0; - int nexps; - expdesc e; - do { - new_localvar(ls, str_checkname(ls), nvars++); - } while (testnext(ls, ',')); - if (testnext(ls, '=')) - nexps = explist1(ls, &e); - else { - e.k = VVOID; - nexps = 0; - } - adjust_assign(ls, nvars, nexps, &e); - adjustlocalvars(ls, nvars); -} - - -static int funcname (LexState *ls, expdesc *v) { - /* funcname -> NAME {field} [`:' NAME] */ - int needself = 0; - singlevar(ls, v); - while (ls->t.token == '.') - field(ls, v); - if (ls->t.token == ':') { - needself = 1; - field(ls, v); - } - return needself; -} - - -static void funcstat (LexState *ls, int line) { - /* funcstat -> FUNCTION funcname body */ - int needself; - expdesc v, b; - luaX_next(ls); /* skip FUNCTION */ - needself = funcname(ls, &v); - body(ls, &b, needself, line); - luaK_storevar(ls->fs, &v, &b); - luaK_fixline(ls->fs, line); /* definition `happens' in the first line */ -} - - -static void exprstat (LexState *ls) { - /* stat -> func | assignment */ - FuncState *fs = ls->fs; - struct LHS_assign v; - primaryexp(ls, &v.v); - if (v.v.k == VCALL) /* stat -> func */ - SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */ - else { /* stat -> assignment */ - v.prev = NULL; - assignment(ls, &v, 1); - } -} - - -static void retstat (LexState *ls) { - /* stat -> RETURN explist */ - FuncState *fs = ls->fs; - expdesc e; - int first, nret; /* registers with returned values */ - luaX_next(ls); /* skip RETURN */ - if (block_follow(ls->t.token) || ls->t.token == ';') - first = nret = 0; /* return no values */ - else { - nret = explist1(ls, &e); /* optional return values */ - if (hasmultret(e.k)) { - luaK_setmultret(fs, &e); - if (e.k == VCALL && nret == 1) { /* tail call? */ - SET_OPCODE(getcode(fs,&e), OP_TAILCALL); - lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar); - } - first = fs->nactvar; - nret = LUA_MULTRET; /* return all values */ - } - else { - if (nret == 1) /* only one single value? */ - first = luaK_exp2anyreg(fs, &e); - else { - luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */ - first = fs->nactvar; /* return all `active' values */ - lua_assert(nret == fs->freereg - first); - } - } - } - luaK_ret(fs, first, nret); -} - - -static int statement (LexState *ls) { - int line = ls->linenumber; /* may be needed for error messages */ - switch (ls->t.token) { - case TK_IF: { /* stat -> ifstat */ - ifstat(ls, line); - return 0; - } - case TK_WHILE: { /* stat -> whilestat */ - whilestat(ls, line); - return 0; - } - case TK_DO: { /* stat -> DO block END */ - luaX_next(ls); /* skip DO */ - block(ls); - check_match(ls, TK_END, TK_DO, line); - return 0; - } - case TK_FOR: { /* stat -> forstat */ - forstat(ls, line); - return 0; - } - case TK_REPEAT: { /* stat -> repeatstat */ - repeatstat(ls, line); - return 0; - } - case TK_FUNCTION: { - funcstat(ls, line); /* stat -> funcstat */ - return 0; - } - case TK_LOCAL: { /* stat -> localstat */ - luaX_next(ls); /* skip LOCAL */ - if (testnext(ls, TK_FUNCTION)) /* local function? */ - localfunc(ls); - else - localstat(ls); - return 0; - } - case TK_RETURN: { /* stat -> retstat */ - retstat(ls); - return 1; /* must be last statement */ - } - case TK_BREAK: { /* stat -> breakstat */ - luaX_next(ls); /* skip BREAK */ - breakstat(ls); - return 1; /* must be last statement */ - } - default: { - exprstat(ls); - return 0; /* to avoid warnings */ - } - } -} - - -static void chunk (LexState *ls) { - /* chunk -> { stat [`;'] } */ - int islast = 0; - enterlevel(ls); - while (!islast && !block_follow(ls->t.token)) { - islast = statement(ls); - testnext(ls, ';'); - lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && - ls->fs->freereg >= ls->fs->nactvar); - ls->fs->freereg = ls->fs->nactvar; /* free registers */ - } - leavelevel(ls); -} - -/* }====================================================================== */ diff --git a/libraries/lua/lparser.h b/libraries/lua/lparser.h deleted file mode 100644 index 18836afd1..000000000 --- a/libraries/lua/lparser.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -** $Id: lparser.h,v 1.57.1.1 2007/12/27 13:02:25 roberto Exp $ -** Lua Parser -** See Copyright Notice in lua.h -*/ - -#ifndef lparser_h -#define lparser_h - -#include "llimits.h" -#include "lobject.h" -#include "lzio.h" - - -/* -** Expression descriptor -*/ - -typedef enum { - VVOID, /* no value */ - VNIL, - VTRUE, - VFALSE, - VK, /* info = index of constant in `k' */ - VKNUM, /* nval = numerical value */ - VLOCAL, /* info = local register */ - VUPVAL, /* info = index of upvalue in `upvalues' */ - VGLOBAL, /* info = index of table; aux = index of global name in `k' */ - VINDEXED, /* info = table register; aux = index register (or `k') */ - VJMP, /* info = instruction pc */ - VRELOCABLE, /* info = instruction pc */ - VNONRELOC, /* info = result register */ - VCALL, /* info = instruction pc */ - VVARARG /* info = instruction pc */ -} expkind; - -typedef struct expdesc { - expkind k; - union { - struct { int info, aux; } s; - lua_Number nval; - } u; - int t; /* patch list of `exit when true' */ - int f; /* patch list of `exit when false' */ -} expdesc; - - -typedef struct upvaldesc { - lu_byte k; - lu_byte info; -} upvaldesc; - - -struct BlockCnt; /* defined in lparser.c */ - - -/* state needed to generate code for a given function */ -typedef struct FuncState { - Proto *f; /* current function header */ - Table *h; /* table to find (and reuse) elements in `k' */ - struct FuncState *prev; /* enclosing function */ - struct LexState *ls; /* lexical state */ - struct lua_State *L; /* copy of the Lua state */ - struct BlockCnt *bl; /* chain of current blocks */ - int pc; /* next position to code (equivalent to `ncode') */ - int lasttarget; /* `pc' of last `jump target' */ - int jpc; /* list of pending jumps to `pc' */ - int freereg; /* first free register */ - int nk; /* number of elements in `k' */ - int np; /* number of elements in `p' */ - short nlocvars; /* number of elements in `locvars' */ - lu_byte nactvar; /* number of active local variables */ - upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */ - unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */ -} FuncState; - - -LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, - const char *name); - - -#endif diff --git a/libraries/lua/lstate.c b/libraries/lua/lstate.c deleted file mode 100644 index 4313b83a0..000000000 --- a/libraries/lua/lstate.c +++ /dev/null @@ -1,214 +0,0 @@ -/* -** $Id: lstate.c,v 2.36.1.2 2008/01/03 15:20:39 roberto Exp $ -** Global State -** See Copyright Notice in lua.h -*/ - - -#include - -#define lstate_c -#define LUA_CORE - -#include "lua.h" - -#include "ldebug.h" -#include "ldo.h" -#include "lfunc.h" -#include "lgc.h" -#include "llex.h" -#include "lmem.h" -#include "lstate.h" -#include "lstring.h" -#include "ltable.h" -#include "ltm.h" - - -#define state_size(x) (sizeof(x) + LUAI_EXTRASPACE) -#define fromstate(l) (cast(lu_byte *, (l)) - LUAI_EXTRASPACE) -#define tostate(l) (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE)) - - -/* -** Main thread combines a thread state and the global state -*/ -typedef struct LG { - lua_State l; - global_State g; -} LG; - - - -static void stack_init (lua_State *L1, lua_State *L) { - /* initialize CallInfo array */ - L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo); - L1->ci = L1->base_ci; - L1->size_ci = BASIC_CI_SIZE; - L1->end_ci = L1->base_ci + L1->size_ci - 1; - /* initialize stack array */ - L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue); - L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK; - L1->top = L1->stack; - L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1; - /* initialize first ci */ - L1->ci->func = L1->top; - setnilvalue(L1->top++); /* `function' entry for this `ci' */ - L1->base = L1->ci->base = L1->top; - L1->ci->top = L1->top + LUA_MINSTACK; -} - - -static void freestack (lua_State *L, lua_State *L1) { - luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo); - luaM_freearray(L, L1->stack, L1->stacksize, TValue); -} - - -/* -** open parts that may cause memory-allocation errors -*/ -static void f_luaopen (lua_State *L, void *ud) { - global_State *g = G(L); - UNUSED(ud); - stack_init(L, L); /* init stack */ - sethvalue(L, gt(L), luaH_new(L, 0, 2)); /* table of globals */ - sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */ - luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ - luaT_init(L); - luaX_init(L); - luaS_fix(luaS_newliteral(L, MEMERRMSG)); - g->GCthreshold = 4*g->totalbytes; -} - - -static void preinit_state (lua_State *L, global_State *g) { - G(L) = g; - L->stack = NULL; - L->stacksize = 0; - L->errorJmp = NULL; - L->hook = NULL; - L->hookmask = 0; - L->basehookcount = 0; - L->allowhook = 1; - resethookcount(L); - L->openupval = NULL; - L->size_ci = 0; - L->nCcalls = L->baseCcalls = 0; - L->status = 0; - L->base_ci = L->ci = NULL; - L->savedpc = NULL; - L->errfunc = 0; - setnilvalue(gt(L)); -} - - -static void close_state (lua_State *L) { - global_State *g = G(L); - luaF_close(L, L->stack); /* close all upvalues for this thread */ - luaC_freeall(L); /* collect all objects */ - lua_assert(g->rootgc == obj2gco(L)); - lua_assert(g->strt.nuse == 0); - luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *); - luaZ_freebuffer(L, &g->buff); - freestack(L, L); - lua_assert(g->totalbytes == sizeof(LG)); - (*g->frealloc)(g->ud, fromstate(L), state_size(LG), 0); -} - - -lua_State *luaE_newthread (lua_State *L) { - lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State))); - luaC_link(L, obj2gco(L1), LUA_TTHREAD); - preinit_state(L1, G(L)); - stack_init(L1, L); /* init stack */ - setobj2n(L, gt(L1), gt(L)); /* share table of globals */ - L1->hookmask = L->hookmask; - L1->basehookcount = L->basehookcount; - L1->hook = L->hook; - resethookcount(L1); - lua_assert(iswhite(obj2gco(L1))); - return L1; -} - - -void luaE_freethread (lua_State *L, lua_State *L1) { - luaF_close(L1, L1->stack); /* close all upvalues for this thread */ - lua_assert(L1->openupval == NULL); - luai_userstatefree(L1); - freestack(L, L1); - luaM_freemem(L, fromstate(L1), state_size(lua_State)); -} - - -LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { - int i; - lua_State *L; - global_State *g; - void *l = (*f)(ud, NULL, 0, state_size(LG)); - if (l == NULL) return NULL; - L = tostate(l); - g = &((LG *)L)->g; - L->next = NULL; - L->tt = LUA_TTHREAD; - g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT); - L->marked = luaC_white(g); - set2bits(L->marked, FIXEDBIT, SFIXEDBIT); - preinit_state(L, g); - g->frealloc = f; - g->ud = ud; - g->mainthread = L; - g->uvhead.u.l.prev = &g->uvhead; - g->uvhead.u.l.next = &g->uvhead; - g->GCthreshold = 0; /* mark it as unfinished state */ - g->strt.size = 0; - g->strt.nuse = 0; - g->strt.hash = NULL; - setnilvalue(registry(L)); - luaZ_initbuffer(L, &g->buff); - g->panic = NULL; - g->gcstate = GCSpause; - g->rootgc = obj2gco(L); - g->sweepstrgc = 0; - g->sweepgc = &g->rootgc; - g->gray = NULL; - g->grayagain = NULL; - g->weak = NULL; - g->tmudata = NULL; - g->totalbytes = sizeof(LG); - g->gcpause = LUAI_GCPAUSE; - g->gcstepmul = LUAI_GCMUL; - g->gcdept = 0; - for (i=0; imt[i] = NULL; - if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) { - /* memory allocation error: free partial state */ - close_state(L); - L = NULL; - } - else - luai_userstateopen(L); - return L; -} - - -static void callallgcTM (lua_State *L, void *ud) { - UNUSED(ud); - luaC_callGCTM(L); /* call GC metamethods for all udata */ -} - - -LUA_API void lua_close (lua_State *L) { - L = G(L)->mainthread; /* only the main thread can be closed */ - lua_lock(L); - luaF_close(L, L->stack); /* close all upvalues for this thread */ - luaC_separateudata(L, 1); /* separate udata that have GC metamethods */ - L->errfunc = 0; /* no error function during GC metamethods */ - do { /* repeat until no more errors */ - L->ci = L->base_ci; - L->base = L->top = L->ci->base; - L->nCcalls = L->baseCcalls = 0; - } while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0); - lua_assert(G(L)->tmudata == NULL); - luai_userstateclose(L); - close_state(L); -} - diff --git a/libraries/lua/lstate.h b/libraries/lua/lstate.h deleted file mode 100644 index 3bc575b6b..000000000 --- a/libraries/lua/lstate.h +++ /dev/null @@ -1,169 +0,0 @@ -/* -** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $ -** Global State -** See Copyright Notice in lua.h -*/ - -#ifndef lstate_h -#define lstate_h - -#include "lua.h" - -#include "lobject.h" -#include "ltm.h" -#include "lzio.h" - - - -struct lua_longjmp; /* defined in ldo.c */ - - -/* table of globals */ -#define gt(L) (&L->l_gt) - -/* registry */ -#define registry(L) (&G(L)->l_registry) - - -/* extra stack space to handle TM calls and some other extras */ -#define EXTRA_STACK 5 - - -#define BASIC_CI_SIZE 8 - -#define BASIC_STACK_SIZE (2*LUA_MINSTACK) - - - -typedef struct stringtable { - GCObject **hash; - lu_int32 nuse; /* number of elements */ - int size; -} stringtable; - - -/* -** informations about a call -*/ -typedef struct CallInfo { - StkId base; /* base for this function */ - StkId func; /* function index in the stack */ - StkId top; /* top for this function */ - const Instruction *savedpc; - int nresults; /* expected number of results from this function */ - int tailcalls; /* number of tail calls lost under this entry */ -} CallInfo; - - - -#define curr_func(L) (clvalue(L->ci->func)) -#define ci_func(ci) (clvalue((ci)->func)) -#define f_isLua(ci) (!ci_func(ci)->c.isC) -#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci)) - - -/* -** `global state', shared by all threads of this state -*/ -typedef struct global_State { - stringtable strt; /* hash table for strings */ - lua_Alloc frealloc; /* function to reallocate memory */ - void *ud; /* auxiliary data to `frealloc' */ - lu_byte currentwhite; - lu_byte gcstate; /* state of garbage collector */ - int sweepstrgc; /* position of sweep in `strt' */ - GCObject *rootgc; /* list of all collectable objects */ - GCObject **sweepgc; /* position of sweep in `rootgc' */ - GCObject *gray; /* list of gray objects */ - GCObject *grayagain; /* list of objects to be traversed atomically */ - GCObject *weak; /* list of weak tables (to be cleared) */ - GCObject *tmudata; /* last element of list of userdata to be GC */ - Mbuffer buff; /* temporary buffer for string concatentation */ - lu_mem GCthreshold; - lu_mem totalbytes; /* number of bytes currently allocated */ - lu_mem estimate; /* an estimate of number of bytes actually in use */ - lu_mem gcdept; /* how much GC is `behind schedule' */ - int gcpause; /* size of pause between successive GCs */ - int gcstepmul; /* GC `granularity' */ - lua_CFunction panic; /* to be called in unprotected errors */ - TValue l_registry; - struct lua_State *mainthread; - UpVal uvhead; /* head of double-linked list of all open upvalues */ - struct Table *mt[NUM_TAGS]; /* metatables for basic types */ - TString *tmname[TM_N]; /* array with tag-method names */ -} global_State; - - -/* -** `per thread' state -*/ -struct lua_State { - CommonHeader; - lu_byte status; - StkId top; /* first free slot in the stack */ - StkId base; /* base of current function */ - global_State *l_G; - CallInfo *ci; /* call info for current function */ - const Instruction *savedpc; /* `savedpc' of current function */ - StkId stack_last; /* last free slot in the stack */ - StkId stack; /* stack base */ - CallInfo *end_ci; /* points after end of ci array*/ - CallInfo *base_ci; /* array of CallInfo's */ - int stacksize; - int size_ci; /* size of array `base_ci' */ - unsigned short nCcalls; /* number of nested C calls */ - unsigned short baseCcalls; /* nested C calls when resuming coroutine */ - lu_byte hookmask; - lu_byte allowhook; - int basehookcount; - int hookcount; - lua_Hook hook; - TValue l_gt; /* table of globals */ - TValue env; /* temporary place for environments */ - GCObject *openupval; /* list of open upvalues in this stack */ - GCObject *gclist; - struct lua_longjmp *errorJmp; /* current error recover point */ - ptrdiff_t errfunc; /* current error handling function (stack index) */ -}; - - -#define G(L) (L->l_G) - - -/* -** Union of all collectable objects -*/ -union GCObject { - GCheader gch; - union TString ts; - union Udata u; - union Closure cl; - struct Table h; - struct Proto p; - struct UpVal uv; - struct lua_State th; /* thread */ -}; - - -/* macros to convert a GCObject into a specific value */ -#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts)) -#define gco2ts(o) (&rawgco2ts(o)->tsv) -#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u)) -#define gco2u(o) (&rawgco2u(o)->uv) -#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl)) -#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h)) -#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p)) -#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv)) -#define ngcotouv(o) \ - check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv)) -#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th)) - -/* macro to convert any Lua object into a GCObject */ -#define obj2gco(v) (cast(GCObject *, (v))) - - -LUAI_FUNC lua_State *luaE_newthread (lua_State *L); -LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); - -#endif - diff --git a/libraries/lua/lstring.c b/libraries/lua/lstring.c deleted file mode 100644 index 49113151c..000000000 --- a/libraries/lua/lstring.c +++ /dev/null @@ -1,111 +0,0 @@ -/* -** $Id: lstring.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ -** String table (keeps all strings handled by Lua) -** See Copyright Notice in lua.h -*/ - - -#include - -#define lstring_c -#define LUA_CORE - -#include "lua.h" - -#include "lmem.h" -#include "lobject.h" -#include "lstate.h" -#include "lstring.h" - - - -void luaS_resize (lua_State *L, int newsize) { - GCObject **newhash; - stringtable *tb; - int i; - if (G(L)->gcstate == GCSsweepstring) - return; /* cannot resize during GC traverse */ - newhash = luaM_newvector(L, newsize, GCObject *); - tb = &G(L)->strt; - for (i=0; isize; i++) { - GCObject *p = tb->hash[i]; - while (p) { /* for each node in the list */ - GCObject *next = p->gch.next; /* save next */ - unsigned int h = gco2ts(p)->hash; - int h1 = lmod(h, newsize); /* new position */ - lua_assert(cast_int(h%newsize) == lmod(h, newsize)); - p->gch.next = newhash[h1]; /* chain it */ - newhash[h1] = p; - p = next; - } - } - luaM_freearray(L, tb->hash, tb->size, TString *); - tb->size = newsize; - tb->hash = newhash; -} - - -static TString *newlstr (lua_State *L, const char *str, size_t l, - unsigned int h) { - TString *ts; - stringtable *tb; - if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char)) - luaM_toobig(L); - ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString))); - ts->tsv.len = l; - ts->tsv.hash = h; - ts->tsv.marked = luaC_white(G(L)); - ts->tsv.tt = LUA_TSTRING; - ts->tsv.reserved = 0; - memcpy(ts+1, str, l*sizeof(char)); - ((char *)(ts+1))[l] = '\0'; /* ending 0 */ - tb = &G(L)->strt; - h = lmod(h, tb->size); - ts->tsv.next = tb->hash[h]; /* chain new entry */ - tb->hash[h] = obj2gco(ts); - tb->nuse++; - if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2) - luaS_resize(L, tb->size*2); /* too crowded */ - return ts; -} - - -TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { - GCObject *o; - unsigned int h = cast(unsigned int, l); /* seed */ - size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ - size_t l1; - for (l1=l; l1>=step; l1-=step) /* compute hash */ - h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1])); - for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)]; - o != NULL; - o = o->gch.next) { - TString *ts = rawgco2ts(o); - if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) { - /* string may be dead */ - if (isdead(G(L), o)) changewhite(o); - return ts; - } - } - return newlstr(L, str, l, h); /* not found */ -} - - -Udata *luaS_newudata (lua_State *L, size_t s, Table *e) { - Udata *u; - if (s > MAX_SIZET - sizeof(Udata)) - luaM_toobig(L); - u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata))); - u->uv.marked = luaC_white(G(L)); /* is not finalized */ - u->uv.tt = LUA_TUSERDATA; - u->uv.len = s; - u->uv.metatable = NULL; - u->uv.env = e; - /* chain it on udata list (after main thread) */ - u->uv.next = G(L)->mainthread->next; - G(L)->mainthread->next = obj2gco(u); - return u; -} - diff --git a/libraries/lua/lstring.h b/libraries/lua/lstring.h deleted file mode 100644 index 73a2ff8b3..000000000 --- a/libraries/lua/lstring.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $ -** String table (keep all strings handled by Lua) -** See Copyright Notice in lua.h -*/ - -#ifndef lstring_h -#define lstring_h - - -#include "lgc.h" -#include "lobject.h" -#include "lstate.h" - - -#define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char)) - -#define sizeudata(u) (sizeof(union Udata)+(u)->len) - -#define luaS_new(L, s) (luaS_newlstr(L, s, strlen(s))) -#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ - (sizeof(s)/sizeof(char))-1)) - -#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT) - -LUAI_FUNC void luaS_resize (lua_State *L, int newsize); -LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e); -LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); - - -#endif diff --git a/libraries/lua/lstrlib.c b/libraries/lua/lstrlib.c deleted file mode 100644 index 7a03489be..000000000 --- a/libraries/lua/lstrlib.c +++ /dev/null @@ -1,871 +0,0 @@ -/* -** $Id: lstrlib.c,v 1.132.1.5 2010/05/14 15:34:19 roberto Exp $ -** Standard library for string operations and pattern-matching -** See Copyright Notice in lua.h -*/ - - -#include -#include -#include -#include -#include - -#define lstrlib_c -#define LUA_LIB - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -/* macro to `unsign' a character */ -#define uchar(c) ((unsigned char)(c)) - - - -static int str_len (lua_State *L) { - size_t l; - luaL_checklstring(L, 1, &l); - lua_pushinteger(L, l); - return 1; -} - - -static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) { - /* relative string position: negative means back from end */ - if (pos < 0) pos += (ptrdiff_t)len + 1; - return (pos >= 0) ? pos : 0; -} - - -static int str_sub (lua_State *L) { - size_t l; - const char *s = luaL_checklstring(L, 1, &l); - ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l); - ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l); - if (start < 1) start = 1; - if (end > (ptrdiff_t)l) end = (ptrdiff_t)l; - if (start <= end) - lua_pushlstring(L, s+start-1, end-start+1); - else lua_pushliteral(L, ""); - return 1; -} - - -static int str_reverse (lua_State *L) { - size_t l; - luaL_Buffer b; - const char *s = luaL_checklstring(L, 1, &l); - luaL_buffinit(L, &b); - while (l--) luaL_addchar(&b, s[l]); - luaL_pushresult(&b); - return 1; -} - - -static int str_lower (lua_State *L) { - size_t l; - size_t i; - luaL_Buffer b; - const char *s = luaL_checklstring(L, 1, &l); - luaL_buffinit(L, &b); - for (i=0; i 0) - luaL_addlstring(&b, s, l); - luaL_pushresult(&b); - return 1; -} - - -static int str_byte (lua_State *L) { - size_t l; - const char *s = luaL_checklstring(L, 1, &l); - ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l); - ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l); - int n, i; - if (posi <= 0) posi = 1; - if ((size_t)pose > l) pose = l; - if (posi > pose) return 0; /* empty interval; return no values */ - n = (int)(pose - posi + 1); - if (posi + n <= pose) /* overflow? */ - luaL_error(L, "string slice too long"); - luaL_checkstack(L, n, "string slice too long"); - for (i=0; i= ms->level || ms->capture[l].len == CAP_UNFINISHED) - return luaL_error(ms->L, "invalid capture index"); - return l; -} - - -static int capture_to_close (MatchState *ms) { - int level = ms->level; - for (level--; level>=0; level--) - if (ms->capture[level].len == CAP_UNFINISHED) return level; - return luaL_error(ms->L, "invalid pattern capture"); -} - - -static const char *classend (MatchState *ms, const char *p) { - switch (*p++) { - case L_ESC: { - if (*p == '\0') - luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")"); - return p+1; - } - case '[': { - if (*p == '^') p++; - do { /* look for a `]' */ - if (*p == '\0') - luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")"); - if (*(p++) == L_ESC && *p != '\0') - p++; /* skip escapes (e.g. `%]') */ - } while (*p != ']'); - return p+1; - } - default: { - return p; - } - } -} - - -static int match_class (int c, int cl) { - int res; - switch (tolower(cl)) { - case 'a' : res = isalpha(c); break; - case 'c' : res = iscntrl(c); break; - case 'd' : res = isdigit(c); break; - case 'l' : res = islower(c); break; - case 'p' : res = ispunct(c); break; - case 's' : res = isspace(c); break; - case 'u' : res = isupper(c); break; - case 'w' : res = isalnum(c); break; - case 'x' : res = isxdigit(c); break; - case 'z' : res = (c == 0); break; - default: return (cl == c); - } - return (islower(cl) ? res : !res); -} - - -static int matchbracketclass (int c, const char *p, const char *ec) { - int sig = 1; - if (*(p+1) == '^') { - sig = 0; - p++; /* skip the `^' */ - } - while (++p < ec) { - if (*p == L_ESC) { - p++; - if (match_class(c, uchar(*p))) - return sig; - } - else if ((*(p+1) == '-') && (p+2 < ec)) { - p+=2; - if (uchar(*(p-2)) <= c && c <= uchar(*p)) - return sig; - } - else if (uchar(*p) == c) return sig; - } - return !sig; -} - - -static int singlematch (int c, const char *p, const char *ep) { - switch (*p) { - case '.': return 1; /* matches any char */ - case L_ESC: return match_class(c, uchar(*(p+1))); - case '[': return matchbracketclass(c, p, ep-1); - default: return (uchar(*p) == c); - } -} - - -static const char *match (MatchState *ms, const char *s, const char *p); - - -static const char *matchbalance (MatchState *ms, const char *s, - const char *p) { - if (*p == 0 || *(p+1) == 0) - luaL_error(ms->L, "unbalanced pattern"); - if (*s != *p) return NULL; - else { - int b = *p; - int e = *(p+1); - int cont = 1; - while (++s < ms->src_end) { - if (*s == e) { - if (--cont == 0) return s+1; - } - else if (*s == b) cont++; - } - } - return NULL; /* string ends out of balance */ -} - - -static const char *max_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - ptrdiff_t i = 0; /* counts maximum expand for item */ - while ((s+i)src_end && singlematch(uchar(*(s+i)), p, ep)) - i++; - /* keeps trying to match with the maximum repetitions */ - while (i>=0) { - const char *res = match(ms, (s+i), ep+1); - if (res) return res; - i--; /* else didn't match; reduce 1 repetition to try again */ - } - return NULL; -} - - -static const char *min_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - for (;;) { - const char *res = match(ms, s, ep+1); - if (res != NULL) - return res; - else if (ssrc_end && singlematch(uchar(*s), p, ep)) - s++; /* try with one more repetition */ - else return NULL; - } -} - - -static const char *start_capture (MatchState *ms, const char *s, - const char *p, int what) { - const char *res; - int level = ms->level; - if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); - ms->capture[level].init = s; - ms->capture[level].len = what; - ms->level = level+1; - if ((res=match(ms, s, p)) == NULL) /* match failed? */ - ms->level--; /* undo capture */ - return res; -} - - -static const char *end_capture (MatchState *ms, const char *s, - const char *p) { - int l = capture_to_close(ms); - const char *res; - ms->capture[l].len = s - ms->capture[l].init; /* close capture */ - if ((res = match(ms, s, p)) == NULL) /* match failed? */ - ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ - return res; -} - - -static const char *match_capture (MatchState *ms, const char *s, int l) { - size_t len; - l = check_capture(ms, l); - len = ms->capture[l].len; - if ((size_t)(ms->src_end-s) >= len && - memcmp(ms->capture[l].init, s, len) == 0) - return s+len; - else return NULL; -} - - -static const char *match (MatchState *ms, const char *s, const char *p) { - init: /* using goto's to optimize tail recursion */ - switch (*p) { - case '(': { /* start capture */ - if (*(p+1) == ')') /* position capture? */ - return start_capture(ms, s, p+2, CAP_POSITION); - else - return start_capture(ms, s, p+1, CAP_UNFINISHED); - } - case ')': { /* end capture */ - return end_capture(ms, s, p+1); - } - case L_ESC: { - switch (*(p+1)) { - case 'b': { /* balanced string? */ - s = matchbalance(ms, s, p+2); - if (s == NULL) return NULL; - p+=4; goto init; /* else return match(ms, s, p+4); */ - } - case 'f': { /* frontier? */ - const char *ep; char previous; - p += 2; - if (*p != '[') - luaL_error(ms->L, "missing " LUA_QL("[") " after " - LUA_QL("%%f") " in pattern"); - ep = classend(ms, p); /* points to what is next */ - previous = (s == ms->src_init) ? '\0' : *(s-1); - if (matchbracketclass(uchar(previous), p, ep-1) || - !matchbracketclass(uchar(*s), p, ep-1)) return NULL; - p=ep; goto init; /* else return match(ms, s, ep); */ - } - default: { - if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ - s = match_capture(ms, s, uchar(*(p+1))); - if (s == NULL) return NULL; - p+=2; goto init; /* else return match(ms, s, p+2) */ - } - goto dflt; /* case default */ - } - } - } - case '\0': { /* end of pattern */ - return s; /* match succeeded */ - } - case '$': { - if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ - return (s == ms->src_end) ? s : NULL; /* check end of string */ - else goto dflt; - } - default: dflt: { /* it is a pattern item */ - const char *ep = classend(ms, p); /* points to what is next */ - int m = ssrc_end && singlematch(uchar(*s), p, ep); - switch (*ep) { - case '?': { /* optional */ - const char *res; - if (m && ((res=match(ms, s+1, ep+1)) != NULL)) - return res; - p=ep+1; goto init; /* else return match(ms, s, ep+1); */ - } - case '*': { /* 0 or more repetitions */ - return max_expand(ms, s, p, ep); - } - case '+': { /* 1 or more repetitions */ - return (m ? max_expand(ms, s+1, p, ep) : NULL); - } - case '-': { /* 0 or more repetitions (minimum) */ - return min_expand(ms, s, p, ep); - } - default: { - if (!m) return NULL; - s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ - } - } - } - } -} - - - -static const char *lmemfind (const char *s1, size_t l1, - const char *s2, size_t l2) { - if (l2 == 0) return s1; /* empty strings are everywhere */ - else if (l2 > l1) return NULL; /* avoids a negative `l1' */ - else { - const char *init; /* to search for a `*s2' inside `s1' */ - l2--; /* 1st char will be checked by `memchr' */ - l1 = l1-l2; /* `s2' cannot be found after that */ - while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { - init++; /* 1st char is already checked */ - if (memcmp(init, s2+1, l2) == 0) - return init-1; - else { /* correct `l1' and `s1' to try again */ - l1 -= init-s1; - s1 = init; - } - } - return NULL; /* not found */ - } -} - - -static void push_onecapture (MatchState *ms, int i, const char *s, - const char *e) { - if (i >= ms->level) { - if (i == 0) /* ms->level == 0, too */ - lua_pushlstring(ms->L, s, e - s); /* add whole match */ - else - luaL_error(ms->L, "invalid capture index"); - } - else { - ptrdiff_t l = ms->capture[i].len; - if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); - if (l == CAP_POSITION) - lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); - else - lua_pushlstring(ms->L, ms->capture[i].init, l); - } -} - - -static int push_captures (MatchState *ms, const char *s, const char *e) { - int i; - int nlevels = (ms->level == 0 && s) ? 1 : ms->level; - luaL_checkstack(ms->L, nlevels, "too many captures"); - for (i = 0; i < nlevels; i++) - push_onecapture(ms, i, s, e); - return nlevels; /* number of strings pushed */ -} - - -static int str_find_aux (lua_State *L, int find) { - size_t l1, l2; - const char *s = luaL_checklstring(L, 1, &l1); - const char *p = luaL_checklstring(L, 2, &l2); - ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1; - if (init < 0) init = 0; - else if ((size_t)(init) > l1) init = (ptrdiff_t)l1; - if (find && (lua_toboolean(L, 4) || /* explicit request? */ - strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */ - /* do a plain search */ - const char *s2 = lmemfind(s+init, l1-init, p, l2); - if (s2) { - lua_pushinteger(L, s2-s+1); - lua_pushinteger(L, s2-s+l2); - return 2; - } - } - else { - MatchState ms; - int anchor = (*p == '^') ? (p++, 1) : 0; - const char *s1=s+init; - ms.L = L; - ms.src_init = s; - ms.src_end = s+l1; - do { - const char *res; - ms.level = 0; - if ((res=match(&ms, s1, p)) != NULL) { - if (find) { - lua_pushinteger(L, s1-s+1); /* start */ - lua_pushinteger(L, res-s); /* end */ - return push_captures(&ms, NULL, 0) + 2; - } - else - return push_captures(&ms, s1, res); - } - } while (s1++ < ms.src_end && !anchor); - } - lua_pushnil(L); /* not found */ - return 1; -} - - -static int str_find (lua_State *L) { - return str_find_aux(L, 1); -} - - -static int str_match (lua_State *L) { - return str_find_aux(L, 0); -} - - -static int gmatch_aux (lua_State *L) { - MatchState ms; - size_t ls; - const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); - const char *p = lua_tostring(L, lua_upvalueindex(2)); - const char *src; - ms.L = L; - ms.src_init = s; - ms.src_end = s+ls; - for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); - src <= ms.src_end; - src++) { - const char *e; - ms.level = 0; - if ((e = match(&ms, src, p)) != NULL) { - lua_Integer newstart = e-s; - if (e == src) newstart++; /* empty match? go at least one position */ - lua_pushinteger(L, newstart); - lua_replace(L, lua_upvalueindex(3)); - return push_captures(&ms, src, e); - } - } - return 0; /* not found */ -} - - -static int gmatch (lua_State *L) { - luaL_checkstring(L, 1); - luaL_checkstring(L, 2); - lua_settop(L, 2); - lua_pushinteger(L, 0); - lua_pushcclosure(L, gmatch_aux, 3); - return 1; -} - - -static int gfind_nodef (lua_State *L) { - return luaL_error(L, LUA_QL("string.gfind") " was renamed to " - LUA_QL("string.gmatch")); -} - - -static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, - const char *e) { - size_t l, i; - const char *news = lua_tolstring(ms->L, 3, &l); - for (i = 0; i < l; i++) { - if (news[i] != L_ESC) - luaL_addchar(b, news[i]); - else { - i++; /* skip ESC */ - if (!isdigit(uchar(news[i]))) - luaL_addchar(b, news[i]); - else if (news[i] == '0') - luaL_addlstring(b, s, e - s); - else { - push_onecapture(ms, news[i] - '1', s, e); - luaL_addvalue(b); /* add capture to accumulated result */ - } - } - } -} - - -static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, - const char *e) { - lua_State *L = ms->L; - switch (lua_type(L, 3)) { - case LUA_TNUMBER: - case LUA_TSTRING: { - add_s(ms, b, s, e); - return; - } - case LUA_TFUNCTION: { - int n; - lua_pushvalue(L, 3); - n = push_captures(ms, s, e); - lua_call(L, n, 1); - break; - } - case LUA_TTABLE: { - push_onecapture(ms, 0, s, e); - lua_gettable(L, 3); - break; - } - } - if (!lua_toboolean(L, -1)) { /* nil or false? */ - lua_pop(L, 1); - lua_pushlstring(L, s, e - s); /* keep original text */ - } - else if (!lua_isstring(L, -1)) - luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); - luaL_addvalue(b); /* add result to accumulator */ -} - - -static int str_gsub (lua_State *L) { - size_t srcl; - const char *src = luaL_checklstring(L, 1, &srcl); - const char *p = luaL_checkstring(L, 2); - int tr = lua_type(L, 3); - int max_s = luaL_optint(L, 4, srcl+1); - int anchor = (*p == '^') ? (p++, 1) : 0; - int n = 0; - MatchState ms; - luaL_Buffer b; - luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || - tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, - "string/function/table expected"); - luaL_buffinit(L, &b); - ms.L = L; - ms.src_init = src; - ms.src_end = src+srcl; - while (n < max_s) { - const char *e; - ms.level = 0; - e = match(&ms, src, p); - if (e) { - n++; - add_value(&ms, &b, src, e); - } - if (e && e>src) /* non empty match? */ - src = e; /* skip it */ - else if (src < ms.src_end) - luaL_addchar(&b, *src++); - else break; - if (anchor) break; - } - luaL_addlstring(&b, src, ms.src_end-src); - luaL_pushresult(&b); - lua_pushinteger(L, n); /* number of substitutions */ - return 2; -} - -/* }====================================================== */ - - -/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ -#define MAX_ITEM 512 -/* valid flags in a format specification */ -#define FLAGS "-+ #0" -/* -** maximum size of each format specification (such as '%-099.99d') -** (+10 accounts for %99.99x plus margin of error) -*/ -#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10) - - -static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - luaL_addchar(b, '"'); - while (l--) { - switch (*s) { - case '"': case '\\': case '\n': { - luaL_addchar(b, '\\'); - luaL_addchar(b, *s); - break; - } - case '\r': { - luaL_addlstring(b, "\\r", 2); - break; - } - case '\0': { - luaL_addlstring(b, "\\000", 4); - break; - } - default: { - luaL_addchar(b, *s); - break; - } - } - s++; - } - luaL_addchar(b, '"'); -} - -static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { - const char *p = strfrmt; - while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ - if ((size_t)(p - strfrmt) >= sizeof(FLAGS)) - luaL_error(L, "invalid format (repeated flags)"); - if (isdigit(uchar(*p))) p++; /* skip width */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - if (*p == '.') { - p++; - if (isdigit(uchar(*p))) p++; /* skip precision */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - } - if (isdigit(uchar(*p))) - luaL_error(L, "invalid format (width or precision too long)"); - *(form++) = '%'; - strncpy(form, strfrmt, p - strfrmt + 1); - form += p - strfrmt + 1; - *form = '\0'; - return p; -} - - -static void addintlen (char *form) { - size_t l = strlen(form); - char spec = form[l - 1]; - strcpy(form + l - 1, LUA_INTFRMLEN); - form[l + sizeof(LUA_INTFRMLEN) - 2] = spec; - form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0'; -} - - -static int str_format (lua_State *L) { - int top = lua_gettop(L); - int arg = 1; - size_t sfl; - const char *strfrmt = luaL_checklstring(L, arg, &sfl); - const char *strfrmt_end = strfrmt+sfl; - luaL_Buffer b; - luaL_buffinit(L, &b); - while (strfrmt < strfrmt_end) { - if (*strfrmt != L_ESC) - luaL_addchar(&b, *strfrmt++); - else if (*++strfrmt == L_ESC) - luaL_addchar(&b, *strfrmt++); /* %% */ - else { /* format item */ - char form[MAX_FORMAT]; /* to store the format (`%...') */ - char buff[MAX_ITEM]; /* to store the formatted item */ - if (++arg > top) - luaL_argerror(L, arg, "no value"); - strfrmt = scanformat(L, strfrmt, form); - switch (*strfrmt++) { - case 'c': { - sprintf(buff, form, (int)luaL_checknumber(L, arg)); - break; - } - case 'd': case 'i': { - addintlen(form); - sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg)); - break; - } - case 'o': case 'u': case 'x': case 'X': { - addintlen(form); - sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg)); - break; - } - case 'e': case 'E': case 'f': - case 'g': case 'G': { - sprintf(buff, form, (double)luaL_checknumber(L, arg)); - break; - } - case 'q': { - addquoted(L, &b, arg); - continue; /* skip the 'addsize' at the end */ - } - case 's': { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - if (!strchr(form, '.') && l >= 100) { - /* no precision and string is too long to be formatted; - keep original string */ - lua_pushvalue(L, arg); - luaL_addvalue(&b); - continue; /* skip the `addsize' at the end */ - } - else { - sprintf(buff, form, s); - break; - } - } - default: { /* also treat cases `pnLlh' */ - return luaL_error(L, "invalid option " LUA_QL("%%%c") " to " - LUA_QL("format"), *(strfrmt - 1)); - } - } - luaL_addlstring(&b, buff, strlen(buff)); - } - } - luaL_pushresult(&b); - return 1; -} - - -static const luaL_Reg strlib[] = { - {"byte", str_byte}, - {"char", str_char}, - {"dump", str_dump}, - {"find", str_find}, - {"format", str_format}, - {"gfind", gfind_nodef}, - {"gmatch", gmatch}, - {"gsub", str_gsub}, - {"len", str_len}, - {"lower", str_lower}, - {"match", str_match}, - {"rep", str_rep}, - {"reverse", str_reverse}, - {"sub", str_sub}, - {"upper", str_upper}, - {NULL, NULL} -}; - - -static void createmetatable (lua_State *L) { - lua_createtable(L, 0, 1); /* create metatable for strings */ - lua_pushliteral(L, ""); /* dummy string */ - lua_pushvalue(L, -2); - lua_setmetatable(L, -2); /* set string metatable */ - lua_pop(L, 1); /* pop dummy string */ - lua_pushvalue(L, -2); /* string library... */ - lua_setfield(L, -2, "__index"); /* ...is the __index metamethod */ - lua_pop(L, 1); /* pop metatable */ -} - - -/* -** Open string library -*/ -LUALIB_API int luaopen_string (lua_State *L) { - luaL_register(L, LUA_STRLIBNAME, strlib); -#if defined(LUA_COMPAT_GFIND) - lua_getfield(L, -1, "gmatch"); - lua_setfield(L, -2, "gfind"); -#endif - createmetatable(L); - return 1; -} - diff --git a/libraries/lua/ltable.c b/libraries/lua/ltable.c deleted file mode 100644 index ec84f4fab..000000000 --- a/libraries/lua/ltable.c +++ /dev/null @@ -1,588 +0,0 @@ -/* -** $Id: ltable.c,v 2.32.1.2 2007/12/28 15:32:23 roberto Exp $ -** Lua tables (hash) -** See Copyright Notice in lua.h -*/ - - -/* -** Implementation of tables (aka arrays, objects, or hash tables). -** Tables keep its elements in two parts: an array part and a hash part. -** Non-negative integer keys are all candidates to be kept in the array -** part. The actual size of the array is the largest `n' such that at -** least half the slots between 0 and n are in use. -** Hash uses a mix of chained scatter table with Brent's variation. -** A main invariant of these tables is that, if an element is not -** in its main position (i.e. the `original' position that its hash gives -** to it), then the colliding element is in its own main position. -** Hence even when the load factor reaches 100%, performance remains good. -*/ - -#include -#include - -#define ltable_c -#define LUA_CORE - -#include "lua.h" - -#include "ldebug.h" -#include "ldo.h" -#include "lgc.h" -#include "lmem.h" -#include "lobject.h" -#include "lstate.h" -#include "ltable.h" - - -/* -** max size of array part is 2^MAXBITS -*/ -#if LUAI_BITSINT > 26 -#define MAXBITS 26 -#else -#define MAXBITS (LUAI_BITSINT-2) -#endif - -#define MAXASIZE (1 << MAXBITS) - - -#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) - -#define hashstr(t,str) hashpow2(t, (str)->tsv.hash) -#define hashboolean(t,p) hashpow2(t, p) - - -/* -** for some types, it is better to avoid modulus by power of 2, as -** they tend to have many 2 factors. -*/ -#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) - - -#define hashpointer(t,p) hashmod(t, IntPoint(p)) - - -/* -** number of ints inside a lua_Number -*/ -#define numints cast_int(sizeof(lua_Number)/sizeof(int)) - - - -#define dummynode (&dummynode_) - -static const Node dummynode_ = { - {{NULL}, LUA_TNIL}, /* value */ - {{{NULL}, LUA_TNIL, NULL}} /* key */ -}; - - -/* -** hash for lua_Numbers -*/ -static Node *hashnum (const Table *t, lua_Number n) { - unsigned int a[numints]; - int i; - if (luai_numeq(n, 0)) /* avoid problems with -0 */ - return gnode(t, 0); - memcpy(a, &n, sizeof(a)); - for (i = 1; i < numints; i++) a[0] += a[i]; - return hashmod(t, a[0]); -} - - - -/* -** returns the `main' position of an element in a table (that is, the index -** of its hash value) -*/ -static Node *mainposition (const Table *t, const TValue *key) { - switch (ttype(key)) { - case LUA_TNUMBER: - return hashnum(t, nvalue(key)); - case LUA_TSTRING: - return hashstr(t, rawtsvalue(key)); - case LUA_TBOOLEAN: - return hashboolean(t, bvalue(key)); - case LUA_TLIGHTUSERDATA: - return hashpointer(t, pvalue(key)); - default: - return hashpointer(t, gcvalue(key)); - } -} - - -/* -** returns the index for `key' if `key' is an appropriate key to live in -** the array part of the table, -1 otherwise. -*/ -static int arrayindex (const TValue *key) { - if (ttisnumber(key)) { - lua_Number n = nvalue(key); - int k; - lua_number2int(k, n); - if (luai_numeq(cast_num(k), n)) - return k; - } - return -1; /* `key' did not match some condition */ -} - - -/* -** returns the index of a `key' for table traversals. First goes all -** elements in the array part, then elements in the hash part. The -** beginning of a traversal is signalled by -1. -*/ -static int findindex (lua_State *L, Table *t, StkId key) { - int i; - if (ttisnil(key)) return -1; /* first iteration */ - i = arrayindex(key); - if (0 < i && i <= t->sizearray) /* is `key' inside array part? */ - return i-1; /* yes; that's the index (corrected to C) */ - else { - Node *n = mainposition(t, key); - do { /* check whether `key' is somewhere in the chain */ - /* key may be dead already, but it is ok to use it in `next' */ - if (luaO_rawequalObj(key2tval(n), key) || - (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) && - gcvalue(gkey(n)) == gcvalue(key))) { - i = cast_int(n - gnode(t, 0)); /* key index in hash table */ - /* hash elements are numbered after array ones */ - return i + t->sizearray; - } - else n = gnext(n); - } while (n); - luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */ - return 0; /* to avoid warnings */ - } -} - - -int luaH_next (lua_State *L, Table *t, StkId key) { - int i = findindex(L, t, key); /* find original element */ - for (i++; i < t->sizearray; i++) { /* try first array part */ - if (!ttisnil(&t->array[i])) { /* a non-nil value? */ - setnvalue(key, cast_num(i+1)); - setobj2s(L, key+1, &t->array[i]); - return 1; - } - } - for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ - if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ - setobj2s(L, key, key2tval(gnode(t, i))); - setobj2s(L, key+1, gval(gnode(t, i))); - return 1; - } - } - return 0; /* no more elements */ -} - - -/* -** {============================================================= -** Rehash -** ============================================================== -*/ - - -static int computesizes (int nums[], int *narray) { - int i; - int twotoi; /* 2^i */ - int a = 0; /* number of elements smaller than 2^i */ - int na = 0; /* number of elements to go to array part */ - int n = 0; /* optimal size for array part */ - for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { - if (nums[i] > 0) { - a += nums[i]; - if (a > twotoi/2) { /* more than half elements present? */ - n = twotoi; /* optimal size (till now) */ - na = a; /* all elements smaller than n will go to array part */ - } - } - if (a == *narray) break; /* all elements already counted */ - } - *narray = n; - lua_assert(*narray/2 <= na && na <= *narray); - return na; -} - - -static int countint (const TValue *key, int *nums) { - int k = arrayindex(key); - if (0 < k && k <= MAXASIZE) { /* is `key' an appropriate array index? */ - nums[ceillog2(k)]++; /* count as such */ - return 1; - } - else - return 0; -} - - -static int numusearray (const Table *t, int *nums) { - int lg; - int ttlg; /* 2^lg */ - int ause = 0; /* summation of `nums' */ - int i = 1; /* count to traverse all array keys */ - for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) { /* for each slice */ - int lc = 0; /* counter */ - int lim = ttlg; - if (lim > t->sizearray) { - lim = t->sizearray; /* adjust upper limit */ - if (i > lim) - break; /* no more elements to count */ - } - /* count elements in range (2^(lg-1), 2^lg] */ - for (; i <= lim; i++) { - if (!ttisnil(&t->array[i-1])) - lc++; - } - nums[lg] += lc; - ause += lc; - } - return ause; -} - - -static int numusehash (const Table *t, int *nums, int *pnasize) { - int totaluse = 0; /* total number of elements */ - int ause = 0; /* summation of `nums' */ - int i = sizenode(t); - while (i--) { - Node *n = &t->node[i]; - if (!ttisnil(gval(n))) { - ause += countint(key2tval(n), nums); - totaluse++; - } - } - *pnasize += ause; - return totaluse; -} - - -static void setarrayvector (lua_State *L, Table *t, int size) { - int i; - luaM_reallocvector(L, t->array, t->sizearray, size, TValue); - for (i=t->sizearray; iarray[i]); - t->sizearray = size; -} - - -static void setnodevector (lua_State *L, Table *t, int size) { - int lsize; - if (size == 0) { /* no elements to hash part? */ - t->node = cast(Node *, dummynode); /* use common `dummynode' */ - lsize = 0; - } - else { - int i; - lsize = ceillog2(size); - if (lsize > MAXBITS) - luaG_runerror(L, "table overflow"); - size = twoto(lsize); - t->node = luaM_newvector(L, size, Node); - for (i=0; ilsizenode = cast_byte(lsize); - t->lastfree = gnode(t, size); /* all positions are free */ -} - - -static void resize (lua_State *L, Table *t, int nasize, int nhsize) { - int i; - int oldasize = t->sizearray; - int oldhsize = t->lsizenode; - Node *nold = t->node; /* save old hash ... */ - if (nasize > oldasize) /* array part must grow? */ - setarrayvector(L, t, nasize); - /* create new hash part with appropriate size */ - setnodevector(L, t, nhsize); - if (nasize < oldasize) { /* array part must shrink? */ - t->sizearray = nasize; - /* re-insert elements from vanishing slice */ - for (i=nasize; iarray[i])) - setobjt2t(L, luaH_setnum(L, t, i+1), &t->array[i]); - } - /* shrink array */ - luaM_reallocvector(L, t->array, oldasize, nasize, TValue); - } - /* re-insert elements from hash part */ - for (i = twoto(oldhsize) - 1; i >= 0; i--) { - Node *old = nold+i; - if (!ttisnil(gval(old))) - setobjt2t(L, luaH_set(L, t, key2tval(old)), gval(old)); - } - if (nold != dummynode) - luaM_freearray(L, nold, twoto(oldhsize), Node); /* free old array */ -} - - -void luaH_resizearray (lua_State *L, Table *t, int nasize) { - int nsize = (t->node == dummynode) ? 0 : sizenode(t); - resize(L, t, nasize, nsize); -} - - -static void rehash (lua_State *L, Table *t, const TValue *ek) { - int nasize, na; - int nums[MAXBITS+1]; /* nums[i] = number of keys between 2^(i-1) and 2^i */ - int i; - int totaluse; - for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */ - nasize = numusearray(t, nums); /* count keys in array part */ - totaluse = nasize; /* all those keys are integer keys */ - totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ - /* count extra key */ - nasize += countint(ek, nums); - totaluse++; - /* compute new size for array part */ - na = computesizes(nums, &nasize); - /* resize the table to new computed sizes */ - resize(L, t, nasize, totaluse - na); -} - - - -/* -** }============================================================= -*/ - - -Table *luaH_new (lua_State *L, int narray, int nhash) { - Table *t = luaM_new(L, Table); - luaC_link(L, obj2gco(t), LUA_TTABLE); - t->metatable = NULL; - t->flags = cast_byte(~0); - /* temporary values (kept only if some malloc fails) */ - t->array = NULL; - t->sizearray = 0; - t->lsizenode = 0; - t->node = cast(Node *, dummynode); - setarrayvector(L, t, narray); - setnodevector(L, t, nhash); - return t; -} - - -void luaH_free (lua_State *L, Table *t) { - if (t->node != dummynode) - luaM_freearray(L, t->node, sizenode(t), Node); - luaM_freearray(L, t->array, t->sizearray, TValue); - luaM_free(L, t); -} - - -static Node *getfreepos (Table *t) { - while (t->lastfree-- > t->node) { - if (ttisnil(gkey(t->lastfree))) - return t->lastfree; - } - return NULL; /* could not find a free place */ -} - - - -/* -** inserts a new key into a hash table; first, check whether key's main -** position is free. If not, check whether colliding node is in its main -** position or not: if it is not, move colliding node to an empty place and -** put new key in its main position; otherwise (colliding node is in its main -** position), new key goes to an empty position. -*/ -static TValue *newkey (lua_State *L, Table *t, const TValue *key) { - Node *mp = mainposition(t, key); - if (!ttisnil(gval(mp)) || mp == dummynode) { - Node *othern; - Node *n = getfreepos(t); /* get a free place */ - if (n == NULL) { /* cannot find a free place? */ - rehash(L, t, key); /* grow table */ - return luaH_set(L, t, key); /* re-insert key into grown table */ - } - lua_assert(n != dummynode); - othern = mainposition(t, key2tval(mp)); - if (othern != mp) { /* is colliding node out of its main position? */ - /* yes; move colliding node into free position */ - while (gnext(othern) != mp) othern = gnext(othern); /* find previous */ - gnext(othern) = n; /* redo the chain with `n' in place of `mp' */ - *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ - gnext(mp) = NULL; /* now `mp' is free */ - setnilvalue(gval(mp)); - } - else { /* colliding node is in its own main position */ - /* new node will go into free position */ - gnext(n) = gnext(mp); /* chain new position */ - gnext(mp) = n; - mp = n; - } - } - gkey(mp)->value = key->value; gkey(mp)->tt = key->tt; - luaC_barriert(L, t, key); - lua_assert(ttisnil(gval(mp))); - return gval(mp); -} - - -/* -** search function for integers -*/ -const TValue *luaH_getnum (Table *t, int key) { - /* (1 <= key && key <= t->sizearray) */ - if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray)) - return &t->array[key-1]; - else { - lua_Number nk = cast_num(key); - Node *n = hashnum(t, nk); - do { /* check whether `key' is somewhere in the chain */ - if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk)) - return gval(n); /* that's it */ - else n = gnext(n); - } while (n); - return luaO_nilobject; - } -} - - -/* -** search function for strings -*/ -const TValue *luaH_getstr (Table *t, TString *key) { - Node *n = hashstr(t, key); - do { /* check whether `key' is somewhere in the chain */ - if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key) - return gval(n); /* that's it */ - else n = gnext(n); - } while (n); - return luaO_nilobject; -} - - -/* -** main search function -*/ -const TValue *luaH_get (Table *t, const TValue *key) { - switch (ttype(key)) { - case LUA_TNIL: return luaO_nilobject; - case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key)); - case LUA_TNUMBER: { - int k; - lua_Number n = nvalue(key); - lua_number2int(k, n); - if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */ - return luaH_getnum(t, k); /* use specialized version */ - /* else go through */ - } - default: { - Node *n = mainposition(t, key); - do { /* check whether `key' is somewhere in the chain */ - if (luaO_rawequalObj(key2tval(n), key)) - return gval(n); /* that's it */ - else n = gnext(n); - } while (n); - return luaO_nilobject; - } - } -} - - -TValue *luaH_set (lua_State *L, Table *t, const TValue *key) { - const TValue *p = luaH_get(t, key); - t->flags = 0; - if (p != luaO_nilobject) - return cast(TValue *, p); - else { - if (ttisnil(key)) luaG_runerror(L, "table index is nil"); - else if (ttisnumber(key) && luai_numisnan(nvalue(key))) - luaG_runerror(L, "table index is NaN"); - return newkey(L, t, key); - } -} - - -TValue *luaH_setnum (lua_State *L, Table *t, int key) { - const TValue *p = luaH_getnum(t, key); - if (p != luaO_nilobject) - return cast(TValue *, p); - else { - TValue k; - setnvalue(&k, cast_num(key)); - return newkey(L, t, &k); - } -} - - -TValue *luaH_setstr (lua_State *L, Table *t, TString *key) { - const TValue *p = luaH_getstr(t, key); - if (p != luaO_nilobject) - return cast(TValue *, p); - else { - TValue k; - setsvalue(L, &k, key); - return newkey(L, t, &k); - } -} - - -static int unbound_search (Table *t, unsigned int j) { - unsigned int i = j; /* i is zero or a present index */ - j++; - /* find `i' and `j' such that i is present and j is not */ - while (!ttisnil(luaH_getnum(t, j))) { - i = j; - j *= 2; - if (j > cast(unsigned int, MAX_INT)) { /* overflow? */ - /* table was built with bad purposes: resort to linear search */ - i = 1; - while (!ttisnil(luaH_getnum(t, i))) i++; - return i - 1; - } - } - /* now do a binary search between them */ - while (j - i > 1) { - unsigned int m = (i+j)/2; - if (ttisnil(luaH_getnum(t, m))) j = m; - else i = m; - } - return i; -} - - -/* -** Try to find a boundary in table `t'. A `boundary' is an integer index -** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). -*/ -int luaH_getn (Table *t) { - unsigned int j = t->sizearray; - if (j > 0 && ttisnil(&t->array[j - 1])) { - /* there is a boundary in the array part: (binary) search for it */ - unsigned int i = 0; - while (j - i > 1) { - unsigned int m = (i+j)/2; - if (ttisnil(&t->array[m - 1])) j = m; - else i = m; - } - return i; - } - /* else must find a boundary in hash part */ - else if (t->node == dummynode) /* hash part is empty? */ - return j; /* that is easy... */ - else return unbound_search(t, j); -} - - - -#if defined(LUA_DEBUG) - -Node *luaH_mainposition (const Table *t, const TValue *key) { - return mainposition(t, key); -} - -int luaH_isdummy (Node *n) { return n == dummynode; } - -#endif diff --git a/libraries/lua/ltable.h b/libraries/lua/ltable.h deleted file mode 100644 index f5b9d5ead..000000000 --- a/libraries/lua/ltable.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -** $Id: ltable.h,v 2.10.1.1 2007/12/27 13:02:25 roberto Exp $ -** Lua tables (hash) -** See Copyright Notice in lua.h -*/ - -#ifndef ltable_h -#define ltable_h - -#include "lobject.h" - - -#define gnode(t,i) (&(t)->node[i]) -#define gkey(n) (&(n)->i_key.nk) -#define gval(n) (&(n)->i_val) -#define gnext(n) ((n)->i_key.nk.next) - -#define key2tval(n) (&(n)->i_key.tvk) - - -LUAI_FUNC const TValue *luaH_getnum (Table *t, int key); -LUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key); -LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); -LUAI_FUNC TValue *luaH_setstr (lua_State *L, Table *t, TString *key); -LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); -LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key); -LUAI_FUNC Table *luaH_new (lua_State *L, int narray, int lnhash); -LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize); -LUAI_FUNC void luaH_free (lua_State *L, Table *t); -LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); -LUAI_FUNC int luaH_getn (Table *t); - - -#if defined(LUA_DEBUG) -LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); -LUAI_FUNC int luaH_isdummy (Node *n); -#endif - - -#endif diff --git a/libraries/lua/ltablib.c b/libraries/lua/ltablib.c deleted file mode 100644 index f33c4a3df..000000000 --- a/libraries/lua/ltablib.c +++ /dev/null @@ -1,286 +0,0 @@ -/* -** $Id: ltablib.c,v 1.38.1.3 2008/02/14 16:46:58 roberto Exp $ -** Library for Table Manipulation -** See Copyright Notice in lua.h -*/ - - -#include - -#define ltablib_c -#define LUA_LIB - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) - - -static int foreachi (lua_State *L) { - int i; - int n = aux_getn(L, 1); - luaL_checktype(L, 2, LUA_TFUNCTION); - for (i=1; i <= n; i++) { - lua_pushvalue(L, 2); /* function */ - lua_pushinteger(L, i); /* 1st argument */ - lua_rawgeti(L, 1, i); /* 2nd argument */ - lua_call(L, 2, 1); - if (!lua_isnil(L, -1)) - return 1; - lua_pop(L, 1); /* remove nil result */ - } - return 0; -} - - -static int foreach (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_checktype(L, 2, LUA_TFUNCTION); - lua_pushnil(L); /* first key */ - while (lua_next(L, 1)) { - lua_pushvalue(L, 2); /* function */ - lua_pushvalue(L, -3); /* key */ - lua_pushvalue(L, -3); /* value */ - lua_call(L, 2, 1); - if (!lua_isnil(L, -1)) - return 1; - lua_pop(L, 2); /* remove value and result */ - } - return 0; -} - - -static int maxn (lua_State *L) { - lua_Number max = 0; - luaL_checktype(L, 1, LUA_TTABLE); - lua_pushnil(L); /* first key */ - while (lua_next(L, 1)) { - lua_pop(L, 1); /* remove value */ - if (lua_type(L, -1) == LUA_TNUMBER) { - lua_Number v = lua_tonumber(L, -1); - if (v > max) max = v; - } - } - lua_pushnumber(L, max); - return 1; -} - - -static int getn (lua_State *L) { - lua_pushinteger(L, aux_getn(L, 1)); - return 1; -} - - -static int setn (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); -#ifndef luaL_setn - luaL_setn(L, 1, luaL_checkint(L, 2)); -#else - luaL_error(L, LUA_QL("setn") " is obsolete"); -#endif - lua_pushvalue(L, 1); - return 1; -} - - -static int tinsert (lua_State *L) { - int e = aux_getn(L, 1) + 1; /* first empty element */ - int pos; /* where to insert new element */ - switch (lua_gettop(L)) { - case 2: { /* called with only 2 arguments */ - pos = e; /* insert new element at the end */ - break; - } - case 3: { - int i; - pos = luaL_checkint(L, 2); /* 2nd argument is the position */ - if (pos > e) e = pos; /* `grow' array if necessary */ - for (i = e; i > pos; i--) { /* move up elements */ - lua_rawgeti(L, 1, i-1); - lua_rawseti(L, 1, i); /* t[i] = t[i-1] */ - } - break; - } - default: { - return luaL_error(L, "wrong number of arguments to " LUA_QL("insert")); - } - } - luaL_setn(L, 1, e); /* new size */ - lua_rawseti(L, 1, pos); /* t[pos] = v */ - return 0; -} - - -static int tremove (lua_State *L) { - int e = aux_getn(L, 1); - int pos = luaL_optint(L, 2, e); - if (!(1 <= pos && pos <= e)) /* position is outside bounds? */ - return 0; /* nothing to remove */ - luaL_setn(L, 1, e - 1); /* t.n = n-1 */ - lua_rawgeti(L, 1, pos); /* result = t[pos] */ - for ( ;pos= P */ - while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { - if (i>u) luaL_error(L, "invalid order function for sorting"); - lua_pop(L, 1); /* remove a[i] */ - } - /* repeat --j until a[j] <= P */ - while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { - if (j - -#define ltm_c -#define LUA_CORE - -#include "lua.h" - -#include "lobject.h" -#include "lstate.h" -#include "lstring.h" -#include "ltable.h" -#include "ltm.h" - - - -const char *const luaT_typenames[] = { - "nil", "boolean", "userdata", "number", - "string", "table", "function", "userdata", "thread", - "proto", "upval" -}; - - -void luaT_init (lua_State *L) { - static const char *const luaT_eventname[] = { /* ORDER TM */ - "__index", "__newindex", - "__gc", "__mode", "__eq", - "__add", "__sub", "__mul", "__div", "__mod", - "__pow", "__unm", "__len", "__lt", "__le", - "__concat", "__call" - }; - int i; - for (i=0; itmname[i] = luaS_new(L, luaT_eventname[i]); - luaS_fix(G(L)->tmname[i]); /* never collect these names */ - } -} - - -/* -** function to be used with macro "fasttm": optimized for absence of -** tag methods -*/ -const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { - const TValue *tm = luaH_getstr(events, ename); - lua_assert(event <= TM_EQ); - if (ttisnil(tm)) { /* no tag method? */ - events->flags |= cast_byte(1u<metatable; - break; - case LUA_TUSERDATA: - mt = uvalue(o)->metatable; - break; - default: - mt = G(L)->mt[ttype(o)]; - } - return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject); -} - diff --git a/libraries/lua/ltm.h b/libraries/lua/ltm.h deleted file mode 100644 index 64343b781..000000000 --- a/libraries/lua/ltm.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $ -** Tag methods -** See Copyright Notice in lua.h -*/ - -#ifndef ltm_h -#define ltm_h - - -#include "lobject.h" - - -/* -* WARNING: if you change the order of this enumeration, -* grep "ORDER TM" -*/ -typedef enum { - TM_INDEX, - TM_NEWINDEX, - TM_GC, - TM_MODE, - TM_EQ, /* last tag method with `fast' access */ - TM_ADD, - TM_SUB, - TM_MUL, - TM_DIV, - TM_MOD, - TM_POW, - TM_UNM, - TM_LEN, - TM_LT, - TM_LE, - TM_CONCAT, - TM_CALL, - TM_N /* number of elements in the enum */ -} TMS; - - - -#define gfasttm(g,et,e) ((et) == NULL ? NULL : \ - ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) - -#define fasttm(l,et,e) gfasttm(G(l), et, e) - -LUAI_DATA const char *const luaT_typenames[]; - - -LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); -LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, - TMS event); -LUAI_FUNC void luaT_init (lua_State *L); - -#endif diff --git a/libraries/lua/lua.h b/libraries/lua/lua.h deleted file mode 100644 index a4b73e743..000000000 --- a/libraries/lua/lua.h +++ /dev/null @@ -1,388 +0,0 @@ -/* -** $Id: lua.h,v 1.218.1.7 2012/01/13 20:36:20 roberto Exp $ -** Lua - An Extensible Extension Language -** Lua.org, PUC-Rio, Brazil (http://www.lua.org) -** See Copyright Notice at the end of this file -*/ - - -#ifndef lua_h -#define lua_h - -#include -#include - - -#include "luaconf.h" - - -#define LUA_VERSION "Lua 5.1" -#define LUA_RELEASE "Lua 5.1.5" -#define LUA_VERSION_NUM 501 -#define LUA_COPYRIGHT "Copyright (C) 1994-2012 Lua.org, PUC-Rio" -#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes" - - -/* mark for precompiled code (`Lua') */ -#define LUA_SIGNATURE "\033Lua" - -/* option for multiple returns in `lua_pcall' and `lua_call' */ -#define LUA_MULTRET (-1) - - -/* -** pseudo-indices -*/ -#define LUA_REGISTRYINDEX (-10000) -#define LUA_ENVIRONINDEX (-10001) -#define LUA_GLOBALSINDEX (-10002) -#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i)) - - -/* thread status; 0 is OK */ -#define LUA_YIELD 1 -#define LUA_ERRRUN 2 -#define LUA_ERRSYNTAX 3 -#define LUA_ERRMEM 4 -#define LUA_ERRERR 5 - - -typedef struct lua_State lua_State; - -typedef int (*lua_CFunction) (lua_State *L); - - -/* -** functions that read/write blocks when loading/dumping Lua chunks -*/ -typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); - -typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); - - -/* -** prototype for memory-allocation functions -*/ -typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); - - -/* -** basic types -*/ -#define LUA_TNONE (-1) - -#define LUA_TNIL 0 -#define LUA_TBOOLEAN 1 -#define LUA_TLIGHTUSERDATA 2 -#define LUA_TNUMBER 3 -#define LUA_TSTRING 4 -#define LUA_TTABLE 5 -#define LUA_TFUNCTION 6 -#define LUA_TUSERDATA 7 -#define LUA_TTHREAD 8 - - - -/* minimum Lua stack available to a C function */ -#define LUA_MINSTACK 20 - - -/* -** generic extra include file -*/ -#if defined(LUA_USER_H) -#include LUA_USER_H -#endif - - -/* type of numbers in Lua */ -typedef LUA_NUMBER lua_Number; - - -/* type for integer functions */ -typedef LUA_INTEGER lua_Integer; - - - -/* -** state manipulation -*/ -LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); -LUA_API void (lua_close) (lua_State *L); -LUA_API lua_State *(lua_newthread) (lua_State *L); - -LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); - - -/* -** basic stack manipulation -*/ -LUA_API int (lua_gettop) (lua_State *L); -LUA_API void (lua_settop) (lua_State *L, int idx); -LUA_API void (lua_pushvalue) (lua_State *L, int idx); -LUA_API void (lua_remove) (lua_State *L, int idx); -LUA_API void (lua_insert) (lua_State *L, int idx); -LUA_API void (lua_replace) (lua_State *L, int idx); -LUA_API int (lua_checkstack) (lua_State *L, int sz); - -LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); - - -/* -** access functions (stack -> C) -*/ - -LUA_API int (lua_isnumber) (lua_State *L, int idx); -LUA_API int (lua_isstring) (lua_State *L, int idx); -LUA_API int (lua_iscfunction) (lua_State *L, int idx); -LUA_API int (lua_isuserdata) (lua_State *L, int idx); -LUA_API int (lua_type) (lua_State *L, int idx); -LUA_API const char *(lua_typename) (lua_State *L, int tp); - -LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2); -LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); -LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2); - -LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx); -LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx); -LUA_API int (lua_toboolean) (lua_State *L, int idx); -LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); -LUA_API size_t (lua_objlen) (lua_State *L, int idx); -LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); -LUA_API void *(lua_touserdata) (lua_State *L, int idx); -LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); -LUA_API const void *(lua_topointer) (lua_State *L, int idx); - - -/* -** push functions (C -> stack) -*/ -LUA_API void (lua_pushnil) (lua_State *L); -LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); -LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); -LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l); -LUA_API void (lua_pushstring) (lua_State *L, const char *s); -LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, - va_list argp); -LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); -LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); -LUA_API void (lua_pushboolean) (lua_State *L, int b); -LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); -LUA_API int (lua_pushthread) (lua_State *L); - - -/* -** get functions (Lua -> stack) -*/ -LUA_API void (lua_gettable) (lua_State *L, int idx); -LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k); -LUA_API void (lua_rawget) (lua_State *L, int idx); -LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n); -LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); -LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); -LUA_API int (lua_getmetatable) (lua_State *L, int objindex); -LUA_API void (lua_getfenv) (lua_State *L, int idx); - - -/* -** set functions (stack -> Lua) -*/ -LUA_API void (lua_settable) (lua_State *L, int idx); -LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); -LUA_API void (lua_rawset) (lua_State *L, int idx); -LUA_API void (lua_rawseti) (lua_State *L, int idx, int n); -LUA_API int (lua_setmetatable) (lua_State *L, int objindex); -LUA_API int (lua_setfenv) (lua_State *L, int idx); - - -/* -** `load' and `call' functions (load and run Lua code) -*/ -LUA_API void (lua_call) (lua_State *L, int nargs, int nresults); -LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); -LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud); -LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, - const char *chunkname); - -LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); - - -/* -** coroutine functions -*/ -LUA_API int (lua_yield) (lua_State *L, int nresults); -LUA_API int (lua_resume) (lua_State *L, int narg); -LUA_API int (lua_status) (lua_State *L); - -/* -** garbage-collection function and options -*/ - -#define LUA_GCSTOP 0 -#define LUA_GCRESTART 1 -#define LUA_GCCOLLECT 2 -#define LUA_GCCOUNT 3 -#define LUA_GCCOUNTB 4 -#define LUA_GCSTEP 5 -#define LUA_GCSETPAUSE 6 -#define LUA_GCSETSTEPMUL 7 - -LUA_API int (lua_gc) (lua_State *L, int what, int data); - - -/* -** miscellaneous functions -*/ - -LUA_API int (lua_error) (lua_State *L); - -LUA_API int (lua_next) (lua_State *L, int idx); - -LUA_API void (lua_concat) (lua_State *L, int n); - -LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); -LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); - - - -/* -** =============================================================== -** some useful macros -** =============================================================== -*/ - -#define lua_pop(L,n) lua_settop(L, -(n)-1) - -#define lua_newtable(L) lua_createtable(L, 0, 0) - -#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) - -#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) - -#define lua_strlen(L,i) lua_objlen(L, (i)) - -#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) -#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) -#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) -#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) -#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) -#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) -#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) -#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) - -#define lua_pushliteral(L, s) \ - lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) - -#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s)) -#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s)) - -#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) - - - -/* -** compatibility macros and functions -*/ - -#define lua_open() luaL_newstate() - -#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX) - -#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0) - -#define lua_Chunkreader lua_Reader -#define lua_Chunkwriter lua_Writer - - -/* hack */ -LUA_API void lua_setlevel (lua_State *from, lua_State *to); - - -/* -** {====================================================================== -** Debug API -** ======================================================================= -*/ - - -/* -** Event codes -*/ -#define LUA_HOOKCALL 0 -#define LUA_HOOKRET 1 -#define LUA_HOOKLINE 2 -#define LUA_HOOKCOUNT 3 -#define LUA_HOOKTAILRET 4 - - -/* -** Event masks -*/ -#define LUA_MASKCALL (1 << LUA_HOOKCALL) -#define LUA_MASKRET (1 << LUA_HOOKRET) -#define LUA_MASKLINE (1 << LUA_HOOKLINE) -#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) - -typedef struct lua_Debug lua_Debug; /* activation record */ - - -/* Functions to be called by the debuger in specific events */ -typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); - - -LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar); -LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); -LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); -LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n); -LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n); -LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n); - -LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count); -LUA_API lua_Hook lua_gethook (lua_State *L); -LUA_API int lua_gethookmask (lua_State *L); -LUA_API int lua_gethookcount (lua_State *L); - - -struct lua_Debug { - int event; - const char *name; /* (n) */ - const char *namewhat; /* (n) `global', `local', `field', `method' */ - const char *what; /* (S) `Lua', `C', `main', `tail' */ - const char *source; /* (S) */ - int currentline; /* (l) */ - int nups; /* (u) number of upvalues */ - int linedefined; /* (S) */ - int lastlinedefined; /* (S) */ - char short_src[LUA_IDSIZE]; /* (S) */ - /* private part */ - int i_ci; /* active function */ -}; - -/* }====================================================================== */ - - -/****************************************************************************** -* Copyright (C) 1994-2012 Lua.org, PUC-Rio. All rights reserved. -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -******************************************************************************/ - - -#endif diff --git a/libraries/lua/luaconf.h b/libraries/lua/luaconf.h deleted file mode 100644 index e2cb26163..000000000 --- a/libraries/lua/luaconf.h +++ /dev/null @@ -1,763 +0,0 @@ -/* -** $Id: luaconf.h,v 1.82.1.7 2008/02/11 16:25:08 roberto Exp $ -** Configuration file for Lua -** See Copyright Notice in lua.h -*/ - - -#ifndef lconfig_h -#define lconfig_h - -#include -#include - - -/* -** ================================================================== -** Search for "@@" to find all configurable definitions. -** =================================================================== -*/ - - -/* -@@ LUA_ANSI controls the use of non-ansi features. -** CHANGE it (define it) if you want Lua to avoid the use of any -** non-ansi feature or library. -*/ -#if defined(__STRICT_ANSI__) -#define LUA_ANSI -#endif - - -#if !defined(LUA_ANSI) && defined(_WIN32) -#define LUA_WIN -#endif - -#if defined(LUA_USE_LINUX) -#define LUA_USE_POSIX -#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ -#define LUA_USE_READLINE /* needs some extra libraries */ -#endif - -#if defined(LUA_USE_MACOSX) -#define LUA_USE_POSIX -#define LUA_DL_DYLD /* does not need extra library */ -#endif - - - -/* -@@ LUA_USE_POSIX includes all functionallity listed as X/Open System -@* Interfaces Extension (XSI). -** CHANGE it (define it) if your system is XSI compatible. -*/ -#if defined(LUA_USE_POSIX) -#define LUA_USE_MKSTEMP -#define LUA_USE_ISATTY -#define LUA_USE_POPEN -#define LUA_USE_ULONGJMP -#endif - - -/* -@@ LUA_PATH and LUA_CPATH are the names of the environment variables that -@* Lua check to set its paths. -@@ LUA_INIT is the name of the environment variable that Lua -@* checks for initialization code. -** CHANGE them if you want different names. -*/ -#define LUA_PATH "LUA_PATH" -#define LUA_CPATH "LUA_CPATH" -#define LUA_INIT "LUA_INIT" - - -/* -@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for -@* Lua libraries. -@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for -@* C libraries. -** CHANGE them if your machine has a non-conventional directory -** hierarchy or if you want to install your libraries in -** non-conventional directories. -*/ -#if defined(_WIN32) -/* -** In Windows, any exclamation mark ('!') in the path is replaced by the -** path of the directory of the executable file of the current process. -*/ -#define LUA_LDIR "!\\lua\\" -#define LUA_CDIR "!\\" -#define LUA_PATH_DEFAULT \ - ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ - LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua" -#define LUA_CPATH_DEFAULT \ - ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" - -#else -#define LUA_ROOT "/usr/local/" -#define LUA_LDIR LUA_ROOT "share/lua/5.1/" -#define LUA_CDIR LUA_ROOT "lib/lua/5.1/" -#define LUA_PATH_DEFAULT \ - "./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ - LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua" -#define LUA_CPATH_DEFAULT \ - "./?.so;" LUA_CDIR"?.so;" LUA_CDIR"loadall.so" -#endif - - -/* -@@ LUA_DIRSEP is the directory separator (for submodules). -** CHANGE it if your machine does not use "/" as the directory separator -** and is not Windows. (On Windows Lua automatically uses "\".) -*/ -#if defined(_WIN32) -#define LUA_DIRSEP "\\" -#else -#define LUA_DIRSEP "/" -#endif - - -/* -@@ LUA_PATHSEP is the character that separates templates in a path. -@@ LUA_PATH_MARK is the string that marks the substitution points in a -@* template. -@@ LUA_EXECDIR in a Windows path is replaced by the executable's -@* directory. -@@ LUA_IGMARK is a mark to ignore all before it when bulding the -@* luaopen_ function name. -** CHANGE them if for some reason your system cannot use those -** characters. (E.g., if one of those characters is a common character -** in file/directory names.) Probably you do not need to change them. -*/ -#define LUA_PATHSEP ";" -#define LUA_PATH_MARK "?" -#define LUA_EXECDIR "!" -#define LUA_IGMARK "-" - - -/* -@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger. -** CHANGE that if ptrdiff_t is not adequate on your machine. (On most -** machines, ptrdiff_t gives a good choice between int or long.) -*/ -#define LUA_INTEGER ptrdiff_t - - -/* -@@ LUA_API is a mark for all core API functions. -@@ LUALIB_API is a mark for all standard library functions. -** CHANGE them if you need to define those functions in some special way. -** For instance, if you want to create one Windows DLL with the core and -** the libraries, you may want to use the following definition (define -** LUA_BUILD_AS_DLL to get it). -*/ -#if defined(LUA_BUILD_AS_DLL) - -#if defined(LUA_CORE) || defined(LUA_LIB) -#define LUA_API __declspec(dllexport) -#else -#define LUA_API __declspec(dllimport) -#endif - -#else - -#define LUA_API extern - -#endif - -/* more often than not the libs go together with the core */ -#define LUALIB_API LUA_API - - -/* -@@ LUAI_FUNC is a mark for all extern functions that are not to be -@* exported to outside modules. -@@ LUAI_DATA is a mark for all extern (const) variables that are not to -@* be exported to outside modules. -** CHANGE them if you need to mark them in some special way. Elf/gcc -** (versions 3.2 and later) mark them as "hidden" to optimize access -** when Lua is compiled as a shared library. -*/ -#if defined(luaall_c) -#define LUAI_FUNC static -#define LUAI_DATA /* empty */ - -#elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ - defined(__ELF__) -#define LUAI_FUNC __attribute__((visibility("hidden"))) extern -#define LUAI_DATA LUAI_FUNC - -#else -#define LUAI_FUNC extern -#define LUAI_DATA extern -#endif - - - -/* -@@ LUA_QL describes how error messages quote program elements. -** CHANGE it if you want a different appearance. -*/ -#define LUA_QL(x) "'" x "'" -#define LUA_QS LUA_QL("%s") - - -/* -@@ LUA_IDSIZE gives the maximum size for the description of the source -@* of a function in debug information. -** CHANGE it if you want a different size. -*/ -#define LUA_IDSIZE 60 - - -/* -** {================================================================== -** Stand-alone configuration -** =================================================================== -*/ - -#if defined(lua_c) || defined(luaall_c) - -/* -@@ lua_stdin_is_tty detects whether the standard input is a 'tty' (that -@* is, whether we're running lua interactively). -** CHANGE it if you have a better definition for non-POSIX/non-Windows -** systems. -*/ -#if defined(LUA_USE_ISATTY) -#include -#define lua_stdin_is_tty() isatty(0) -#elif defined(LUA_WIN) -#include -#include -#define lua_stdin_is_tty() _isatty(_fileno(stdin)) -#else -#define lua_stdin_is_tty() 1 /* assume stdin is a tty */ -#endif - - -/* -@@ LUA_PROMPT is the default prompt used by stand-alone Lua. -@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua. -** CHANGE them if you want different prompts. (You can also change the -** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.) -*/ -#define LUA_PROMPT "> " -#define LUA_PROMPT2 ">> " - - -/* -@@ LUA_PROGNAME is the default name for the stand-alone Lua program. -** CHANGE it if your stand-alone interpreter has a different name and -** your system is not able to detect that name automatically. -*/ -#define LUA_PROGNAME "lua" - - -/* -@@ LUA_MAXINPUT is the maximum length for an input line in the -@* stand-alone interpreter. -** CHANGE it if you need longer lines. -*/ -#define LUA_MAXINPUT 512 - - -/* -@@ lua_readline defines how to show a prompt and then read a line from -@* the standard input. -@@ lua_saveline defines how to "save" a read line in a "history". -@@ lua_freeline defines how to free a line read by lua_readline. -** CHANGE them if you want to improve this functionality (e.g., by using -** GNU readline and history facilities). -*/ -#if defined(LUA_USE_READLINE) -#include -#include -#include -#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) -#define lua_saveline(L,idx) \ - if (lua_strlen(L,idx) > 0) /* non-empty line? */ \ - add_history(lua_tostring(L, idx)); /* add it to history */ -#define lua_freeline(L,b) ((void)L, free(b)) -#else -#define lua_readline(L,b,p) \ - ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ - fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ -#define lua_saveline(L,idx) { (void)L; (void)idx; } -#define lua_freeline(L,b) { (void)L; (void)b; } -#endif - -#endif - -/* }================================================================== */ - - -/* -@@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles -@* as a percentage. -** CHANGE it if you want the GC to run faster or slower (higher values -** mean larger pauses which mean slower collection.) You can also change -** this value dynamically. -*/ -#define LUAI_GCPAUSE 200 /* 200% (wait memory to double before next GC) */ - - -/* -@@ LUAI_GCMUL defines the default speed of garbage collection relative to -@* memory allocation as a percentage. -** CHANGE it if you want to change the granularity of the garbage -** collection. (Higher values mean coarser collections. 0 represents -** infinity, where each step performs a full collection.) You can also -** change this value dynamically. -*/ -#define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */ - - - -/* -@@ LUA_COMPAT_GETN controls compatibility with old getn behavior. -** CHANGE it (define it) if you want exact compatibility with the -** behavior of setn/getn in Lua 5.0. -*/ -#undef LUA_COMPAT_GETN - -/* -@@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib. -** CHANGE it to undefined as soon as you do not need a global 'loadlib' -** function (the function is still available as 'package.loadlib'). -*/ -#undef LUA_COMPAT_LOADLIB - -/* -@@ LUA_COMPAT_VARARG controls compatibility with old vararg feature. -** CHANGE it to undefined as soon as your programs use only '...' to -** access vararg parameters (instead of the old 'arg' table). -*/ -#define LUA_COMPAT_VARARG - -/* -@@ LUA_COMPAT_MOD controls compatibility with old math.mod function. -** CHANGE it to undefined as soon as your programs use 'math.fmod' or -** the new '%' operator instead of 'math.mod'. -*/ -#define LUA_COMPAT_MOD - -/* -@@ LUA_COMPAT_LSTR controls compatibility with old long string nesting -@* facility. -** CHANGE it to 2 if you want the old behaviour, or undefine it to turn -** off the advisory error when nesting [[...]]. -*/ -#define LUA_COMPAT_LSTR 1 - -/* -@@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name. -** CHANGE it to undefined as soon as you rename 'string.gfind' to -** 'string.gmatch'. -*/ -#define LUA_COMPAT_GFIND - -/* -@@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib' -@* behavior. -** CHANGE it to undefined as soon as you replace to 'luaL_register' -** your uses of 'luaL_openlib' -*/ -#define LUA_COMPAT_OPENLIB - - - -/* -@@ luai_apicheck is the assert macro used by the Lua-C API. -** CHANGE luai_apicheck if you want Lua to perform some checks in the -** parameters it gets from API calls. This may slow down the interpreter -** a bit, but may be quite useful when debugging C code that interfaces -** with Lua. A useful redefinition is to use assert.h. -*/ -#if defined(LUA_USE_APICHECK) -#include -#define luai_apicheck(L,o) { (void)L; assert(o); } -#else -#define luai_apicheck(L,o) { (void)L; } -#endif - - -/* -@@ LUAI_BITSINT defines the number of bits in an int. -** CHANGE here if Lua cannot automatically detect the number of bits of -** your machine. Probably you do not need to change this. -*/ -/* avoid overflows in comparison */ -#if INT_MAX-20 < 32760 -#define LUAI_BITSINT 16 -#elif INT_MAX > 2147483640L -/* int has at least 32 bits */ -#define LUAI_BITSINT 32 -#else -#error "you must define LUA_BITSINT with number of bits in an integer" -#endif - - -/* -@@ LUAI_UINT32 is an unsigned integer with at least 32 bits. -@@ LUAI_INT32 is an signed integer with at least 32 bits. -@@ LUAI_UMEM is an unsigned integer big enough to count the total -@* memory used by Lua. -@@ LUAI_MEM is a signed integer big enough to count the total memory -@* used by Lua. -** CHANGE here if for some weird reason the default definitions are not -** good enough for your machine. (The definitions in the 'else' -** part always works, but may waste space on machines with 64-bit -** longs.) Probably you do not need to change this. -*/ -#if LUAI_BITSINT >= 32 -#define LUAI_UINT32 unsigned int -#define LUAI_INT32 int -#define LUAI_MAXINT32 INT_MAX -#define LUAI_UMEM size_t -#define LUAI_MEM ptrdiff_t -#else -/* 16-bit ints */ -#define LUAI_UINT32 unsigned long -#define LUAI_INT32 long -#define LUAI_MAXINT32 LONG_MAX -#define LUAI_UMEM unsigned long -#define LUAI_MEM long -#endif - - -/* -@@ LUAI_MAXCALLS limits the number of nested calls. -** CHANGE it if you need really deep recursive calls. This limit is -** arbitrary; its only purpose is to stop infinite recursion before -** exhausting memory. -*/ -#define LUAI_MAXCALLS 20000 - - -/* -@@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function -@* can use. -** CHANGE it if you need lots of (Lua) stack space for your C -** functions. This limit is arbitrary; its only purpose is to stop C -** functions to consume unlimited stack space. (must be smaller than -** -LUA_REGISTRYINDEX) -*/ -#define LUAI_MAXCSTACK 8000 - - - -/* -** {================================================================== -** CHANGE (to smaller values) the following definitions if your system -** has a small C stack. (Or you may want to change them to larger -** values if your system has a large C stack and these limits are -** too rigid for you.) Some of these constants control the size of -** stack-allocated arrays used by the compiler or the interpreter, while -** others limit the maximum number of recursive calls that the compiler -** or the interpreter can perform. Values too large may cause a C stack -** overflow for some forms of deep constructs. -** =================================================================== -*/ - - -/* -@@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and -@* syntactical nested non-terminals in a program. -*/ -#define LUAI_MAXCCALLS 200 - - -/* -@@ LUAI_MAXVARS is the maximum number of local variables per function -@* (must be smaller than 250). -*/ -#define LUAI_MAXVARS 200 - - -/* -@@ LUAI_MAXUPVALUES is the maximum number of upvalues per function -@* (must be smaller than 250). -*/ -#define LUAI_MAXUPVALUES 60 - - -/* -@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. -*/ -#define LUAL_BUFFERSIZE BUFSIZ - -/* }================================================================== */ - - - - -/* -** {================================================================== -@@ LUA_NUMBER is the type of numbers in Lua. -** CHANGE the following definitions only if you want to build Lua -** with a number type different from double. You may also need to -** change lua_number2int & lua_number2integer. -** =================================================================== -*/ - -#define LUA_NUMBER_DOUBLE -#define LUA_NUMBER double - -/* -@@ LUAI_UACNUMBER is the result of an 'usual argument conversion' -@* over a number. -*/ -#define LUAI_UACNUMBER double - - -/* -@@ LUA_NUMBER_SCAN is the format for reading numbers. -@@ LUA_NUMBER_FMT is the format for writing numbers. -@@ lua_number2str converts a number to a string. -@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. -@@ lua_str2number converts a string to a number. -*/ -#define LUA_NUMBER_SCAN "%lf" -#define LUA_NUMBER_FMT "%.14g" -#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n)) -#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ -#define lua_str2number(s,p) strtod((s), (p)) - - -/* -@@ The luai_num* macros define the primitive operations over numbers. -*/ -#if defined(LUA_CORE) -#include -#define luai_numadd(a,b) ((a)+(b)) -#define luai_numsub(a,b) ((a)-(b)) -#define luai_nummul(a,b) ((a)*(b)) -#define luai_numdiv(a,b) ((a)/(b)) -#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b)) -#define luai_numpow(a,b) (pow(a,b)) -#define luai_numunm(a) (-(a)) -#define luai_numeq(a,b) ((a)==(b)) -#define luai_numlt(a,b) ((a)<(b)) -#define luai_numle(a,b) ((a)<=(b)) -#define luai_numisnan(a) (!luai_numeq((a), (a))) -#endif - - -/* -@@ lua_number2int is a macro to convert lua_Number to int. -@@ lua_number2integer is a macro to convert lua_Number to lua_Integer. -** CHANGE them if you know a faster way to convert a lua_Number to -** int (with any rounding method and without throwing errors) in your -** system. In Pentium machines, a naive typecast from double to int -** in C is extremely slow, so any alternative is worth trying. -*/ - -/* On a Pentium, resort to a trick */ -#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \ - (defined(__i386) || defined (_M_IX86) || defined(__i386__)) - -/* On a Microsoft compiler, use assembler */ -#if defined(_MSC_VER) - -#define lua_number2int(i,d) __asm fld d __asm fistp i -#define lua_number2integer(i,n) lua_number2int(i, n) - -/* the next trick should work on any Pentium, but sometimes clashes - with a DirectX idiosyncrasy */ -#else - -union luai_Cast { double l_d; long l_l; }; -#define lua_number2int(i,d) \ - { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; } -#define lua_number2integer(i,n) lua_number2int(i, n) - -#endif - - -/* this option always works, but may be slow */ -#else -#define lua_number2int(i,d) ((i)=(int)(d)) -#define lua_number2integer(i,d) ((i)=(lua_Integer)(d)) - -#endif - -/* }================================================================== */ - - -/* -@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment. -** CHANGE it if your system requires alignments larger than double. (For -** instance, if your system supports long doubles and they must be -** aligned in 16-byte boundaries, then you should add long double in the -** union.) Probably you do not need to change this. -*/ -#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; } - - -/* -@@ LUAI_THROW/LUAI_TRY define how Lua does exception handling. -** CHANGE them if you prefer to use longjmp/setjmp even with C++ -** or if want/don't to use _longjmp/_setjmp instead of regular -** longjmp/setjmp. By default, Lua handles errors with exceptions when -** compiling as C++ code, with _longjmp/_setjmp when asked to use them, -** and with longjmp/setjmp otherwise. -*/ -#if defined(__cplusplus) -/* C++ exceptions */ -#define LUAI_THROW(L,c) throw(c) -#define LUAI_TRY(L,c,a) try { a } catch(...) \ - { if ((c)->status == 0) (c)->status = -1; } -#define luai_jmpbuf int /* dummy variable */ - -#elif defined(LUA_USE_ULONGJMP) -/* in Unix, try _longjmp/_setjmp (more efficient) */ -#define LUAI_THROW(L,c) _longjmp((c)->b, 1) -#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } -#define luai_jmpbuf jmp_buf - -#else -/* default handling with long jumps */ -#define LUAI_THROW(L,c) longjmp((c)->b, 1) -#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } -#define luai_jmpbuf jmp_buf - -#endif - - -/* -@@ LUA_MAXCAPTURES is the maximum number of captures that a pattern -@* can do during pattern-matching. -** CHANGE it if you need more captures. This limit is arbitrary. -*/ -#define LUA_MAXCAPTURES 32 - - -/* -@@ lua_tmpnam is the function that the OS library uses to create a -@* temporary name. -@@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam. -** CHANGE them if you have an alternative to tmpnam (which is considered -** insecure) or if you want the original tmpnam anyway. By default, Lua -** uses tmpnam except when POSIX is available, where it uses mkstemp. -*/ -#if defined(loslib_c) || defined(luaall_c) - -#if defined(LUA_USE_MKSTEMP) -#include -#define LUA_TMPNAMBUFSIZE 32 -#define lua_tmpnam(b,e) { \ - strcpy(b, "/tmp/lua_XXXXXX"); \ - e = mkstemp(b); \ - if (e != -1) close(e); \ - e = (e == -1); } - -#else -#define LUA_TMPNAMBUFSIZE L_tmpnam -#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } -#endif - -#endif - - -/* -@@ lua_popen spawns a new process connected to the current one through -@* the file streams. -** CHANGE it if you have a way to implement it in your system. -*/ -#if defined(LUA_USE_POPEN) - -#define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m)) -#define lua_pclose(L,file) ((void)L, (pclose(file) != -1)) - -#elif defined(LUA_WIN) - -#define lua_popen(L,c,m) ((void)L, _popen(c,m)) -#define lua_pclose(L,file) ((void)L, (_pclose(file) != -1)) - -#else - -#define lua_popen(L,c,m) ((void)((void)c, m), \ - luaL_error(L, LUA_QL("popen") " not supported"), (FILE*)0) -#define lua_pclose(L,file) ((void)((void)L, file), 0) - -#endif - -/* -@@ LUA_DL_* define which dynamic-library system Lua should use. -** CHANGE here if Lua has problems choosing the appropriate -** dynamic-library system for your platform (either Windows' DLL, Mac's -** dyld, or Unix's dlopen). If your system is some kind of Unix, there -** is a good chance that it has dlopen, so LUA_DL_DLOPEN will work for -** it. To use dlopen you also need to adapt the src/Makefile (probably -** adding -ldl to the linker options), so Lua does not select it -** automatically. (When you change the makefile to add -ldl, you must -** also add -DLUA_USE_DLOPEN.) -** If you do not want any kind of dynamic library, undefine all these -** options. -** By default, _WIN32 gets LUA_DL_DLL and MAC OS X gets LUA_DL_DYLD. -*/ -#if defined(LUA_USE_DLOPEN) -#define LUA_DL_DLOPEN -#endif - -#if defined(LUA_WIN) -#define LUA_DL_DLL -#endif - - -/* -@@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State -@* (the data goes just *before* the lua_State pointer). -** CHANGE (define) this if you really need that. This value must be -** a multiple of the maximum alignment required for your machine. -*/ -#define LUAI_EXTRASPACE 0 - - -/* -@@ luai_userstate* allow user-specific actions on threads. -** CHANGE them if you defined LUAI_EXTRASPACE and need to do something -** extra when a thread is created/deleted/resumed/yielded. -*/ -#define luai_userstateopen(L) ((void)L) -#define luai_userstateclose(L) ((void)L) -#define luai_userstatethread(L,L1) ((void)L) -#define luai_userstatefree(L) ((void)L) -#define luai_userstateresume(L,n) ((void)L) -#define luai_userstateyield(L,n) ((void)L) - - -/* -@@ LUA_INTFRMLEN is the length modifier for integer conversions -@* in 'string.format'. -@@ LUA_INTFRM_T is the integer type correspoding to the previous length -@* modifier. -** CHANGE them if your system supports long long or does not support long. -*/ - -#if defined(LUA_USELONGLONG) - -#define LUA_INTFRMLEN "ll" -#define LUA_INTFRM_T long long - -#else - -#define LUA_INTFRMLEN "l" -#define LUA_INTFRM_T long - -#endif - - - -/* =================================================================== */ - -/* -** Local configuration. You can use this space to add your redefinitions -** without modifying the main part of the file. -*/ - - - -#endif - diff --git a/libraries/lua/lualib.h b/libraries/lua/lualib.h deleted file mode 100644 index 469417f67..000000000 --- a/libraries/lua/lualib.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $ -** Lua standard libraries -** See Copyright Notice in lua.h -*/ - - -#ifndef lualib_h -#define lualib_h - -#include "lua.h" - - -/* Key to file-handle type */ -#define LUA_FILEHANDLE "FILE*" - - -#define LUA_COLIBNAME "coroutine" -LUALIB_API int (luaopen_base) (lua_State *L); - -#define LUA_TABLIBNAME "table" -LUALIB_API int (luaopen_table) (lua_State *L); - -#define LUA_IOLIBNAME "io" -LUALIB_API int (luaopen_io) (lua_State *L); - -#define LUA_OSLIBNAME "os" -LUALIB_API int (luaopen_os) (lua_State *L); - -#define LUA_STRLIBNAME "string" -LUALIB_API int (luaopen_string) (lua_State *L); - -#define LUA_MATHLIBNAME "math" -LUALIB_API int (luaopen_math) (lua_State *L); - -#define LUA_DBLIBNAME "debug" -LUALIB_API int (luaopen_debug) (lua_State *L); - -#define LUA_LOADLIBNAME "package" -LUALIB_API int (luaopen_package) (lua_State *L); - - -/* open all previous libraries */ -LUALIB_API void (luaL_openlibs) (lua_State *L); - - - -#ifndef lua_assert -#define lua_assert(x) ((void)0) -#endif - - -#endif diff --git a/libraries/lua/lundump.c b/libraries/lua/lundump.c deleted file mode 100644 index 8010a4579..000000000 --- a/libraries/lua/lundump.c +++ /dev/null @@ -1,227 +0,0 @@ -/* -** $Id: lundump.c,v 2.7.1.4 2008/04/04 19:51:41 roberto Exp $ -** load precompiled Lua chunks -** See Copyright Notice in lua.h -*/ - -#include - -#define lundump_c -#define LUA_CORE - -#include "lua.h" - -#include "ldebug.h" -#include "ldo.h" -#include "lfunc.h" -#include "lmem.h" -#include "lobject.h" -#include "lstring.h" -#include "lundump.h" -#include "lzio.h" - -typedef struct { - lua_State* L; - ZIO* Z; - Mbuffer* b; - const char* name; -} LoadState; - -#ifdef LUAC_TRUST_BINARIES -#define IF(c,s) -#define error(S,s) -#else -#define IF(c,s) if (c) error(S,s) - -static void error(LoadState* S, const char* why) -{ - luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why); - luaD_throw(S->L,LUA_ERRSYNTAX); -} -#endif - -#define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size)) -#define LoadByte(S) (lu_byte)LoadChar(S) -#define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x)) -#define LoadVector(S,b,n,size) LoadMem(S,b,n,size) - -static void LoadBlock(LoadState* S, void* b, size_t size) -{ - size_t r=luaZ_read(S->Z,b,size); - IF (r!=0, "unexpected end"); -} - -static int LoadChar(LoadState* S) -{ - char x; - LoadVar(S,x); - return x; -} - -static int LoadInt(LoadState* S) -{ - int x; - LoadVar(S,x); - IF (x<0, "bad integer"); - return x; -} - -static lua_Number LoadNumber(LoadState* S) -{ - lua_Number x; - LoadVar(S,x); - return x; -} - -static TString* LoadString(LoadState* S) -{ - size_t size; - LoadVar(S,size); - if (size==0) - return NULL; - else - { - char* s=luaZ_openspace(S->L,S->b,size); - LoadBlock(S,s,size); - return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */ - } -} - -static void LoadCode(LoadState* S, Proto* f) -{ - int n=LoadInt(S); - f->code=luaM_newvector(S->L,n,Instruction); - f->sizecode=n; - LoadVector(S,f->code,n,sizeof(Instruction)); -} - -static Proto* LoadFunction(LoadState* S, TString* p); - -static void LoadConstants(LoadState* S, Proto* f) -{ - int i,n; - n=LoadInt(S); - f->k=luaM_newvector(S->L,n,TValue); - f->sizek=n; - for (i=0; ik[i]); - for (i=0; ik[i]; - int t=LoadChar(S); - switch (t) - { - case LUA_TNIL: - setnilvalue(o); - break; - case LUA_TBOOLEAN: - setbvalue(o,LoadChar(S)!=0); - break; - case LUA_TNUMBER: - setnvalue(o,LoadNumber(S)); - break; - case LUA_TSTRING: - setsvalue2n(S->L,o,LoadString(S)); - break; - default: - error(S,"bad constant"); - break; - } - } - n=LoadInt(S); - f->p=luaM_newvector(S->L,n,Proto*); - f->sizep=n; - for (i=0; ip[i]=NULL; - for (i=0; ip[i]=LoadFunction(S,f->source); -} - -static void LoadDebug(LoadState* S, Proto* f) -{ - int i,n; - n=LoadInt(S); - f->lineinfo=luaM_newvector(S->L,n,int); - f->sizelineinfo=n; - LoadVector(S,f->lineinfo,n,sizeof(int)); - n=LoadInt(S); - f->locvars=luaM_newvector(S->L,n,LocVar); - f->sizelocvars=n; - for (i=0; ilocvars[i].varname=NULL; - for (i=0; ilocvars[i].varname=LoadString(S); - f->locvars[i].startpc=LoadInt(S); - f->locvars[i].endpc=LoadInt(S); - } - n=LoadInt(S); - f->upvalues=luaM_newvector(S->L,n,TString*); - f->sizeupvalues=n; - for (i=0; iupvalues[i]=NULL; - for (i=0; iupvalues[i]=LoadString(S); -} - -static Proto* LoadFunction(LoadState* S, TString* p) -{ - Proto* f; - if (++S->L->nCcalls > LUAI_MAXCCALLS) error(S,"code too deep"); - f=luaF_newproto(S->L); - setptvalue2s(S->L,S->L->top,f); incr_top(S->L); - f->source=LoadString(S); if (f->source==NULL) f->source=p; - f->linedefined=LoadInt(S); - f->lastlinedefined=LoadInt(S); - f->nups=LoadByte(S); - f->numparams=LoadByte(S); - f->is_vararg=LoadByte(S); - f->maxstacksize=LoadByte(S); - LoadCode(S,f); - LoadConstants(S,f); - LoadDebug(S,f); - IF (!luaG_checkcode(f), "bad code"); - S->L->top--; - S->L->nCcalls--; - return f; -} - -static void LoadHeader(LoadState* S) -{ - char h[LUAC_HEADERSIZE]; - char s[LUAC_HEADERSIZE]; - luaU_header(h); - LoadBlock(S,s,LUAC_HEADERSIZE); - IF (memcmp(h,s,LUAC_HEADERSIZE)!=0, "bad header"); -} - -/* -** load precompiled chunk -*/ -Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name) -{ - LoadState S; - if (*name=='@' || *name=='=') - S.name=name+1; - else if (*name==LUA_SIGNATURE[0]) - S.name="binary string"; - else - S.name=name; - S.L=L; - S.Z=Z; - S.b=buff; - LoadHeader(&S); - return LoadFunction(&S,luaS_newliteral(L,"=?")); -} - -/* -* make header -*/ -void luaU_header (char* h) -{ - int x=1; - memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1); - h+=sizeof(LUA_SIGNATURE)-1; - *h++=(char)LUAC_VERSION; - *h++=(char)LUAC_FORMAT; - *h++=(char)*(char*)&x; /* endianness */ - *h++=(char)sizeof(int); - *h++=(char)sizeof(size_t); - *h++=(char)sizeof(Instruction); - *h++=(char)sizeof(lua_Number); - *h++=(char)(((lua_Number)0.5)==0); /* is lua_Number integral? */ -} diff --git a/libraries/lua/lundump.h b/libraries/lua/lundump.h deleted file mode 100644 index c80189dbf..000000000 --- a/libraries/lua/lundump.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -** $Id: lundump.h,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $ -** load precompiled Lua chunks -** See Copyright Notice in lua.h -*/ - -#ifndef lundump_h -#define lundump_h - -#include "lobject.h" -#include "lzio.h" - -/* load one chunk; from lundump.c */ -LUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name); - -/* make header; from lundump.c */ -LUAI_FUNC void luaU_header (char* h); - -/* dump one chunk; from ldump.c */ -LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip); - -#ifdef luac_c -/* print one chunk; from print.c */ -LUAI_FUNC void luaU_print (const Proto* f, int full); -#endif - -/* for header of binary files -- this is Lua 5.1 */ -#define LUAC_VERSION 0x51 - -/* for header of binary files -- this is the official format */ -#define LUAC_FORMAT 0 - -/* size of header of binary files */ -#define LUAC_HEADERSIZE 12 - -#endif diff --git a/libraries/lua/lvm.c b/libraries/lua/lvm.c deleted file mode 100644 index e0a0cd852..000000000 --- a/libraries/lua/lvm.c +++ /dev/null @@ -1,767 +0,0 @@ -/* -** $Id: lvm.c,v 2.63.1.5 2011/08/17 20:43:11 roberto Exp $ -** Lua virtual machine -** See Copyright Notice in lua.h -*/ - - -#include -#include -#include - -#define lvm_c -#define LUA_CORE - -#include "lua.h" - -#include "ldebug.h" -#include "ldo.h" -#include "lfunc.h" -#include "lgc.h" -#include "lobject.h" -#include "lopcodes.h" -#include "lstate.h" -#include "lstring.h" -#include "ltable.h" -#include "ltm.h" -#include "lvm.h" - - - -/* limit for table tag-method chains (to avoid loops) */ -#define MAXTAGLOOP 100 - - -const TValue *luaV_tonumber (const TValue *obj, TValue *n) { - lua_Number num; - if (ttisnumber(obj)) return obj; - if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) { - setnvalue(n, num); - return n; - } - else - return NULL; -} - - -int luaV_tostring (lua_State *L, StkId obj) { - if (!ttisnumber(obj)) - return 0; - else { - char s[LUAI_MAXNUMBER2STR]; - lua_Number n = nvalue(obj); - lua_number2str(s, n); - setsvalue2s(L, obj, luaS_new(L, s)); - return 1; - } -} - - -static void traceexec (lua_State *L, const Instruction *pc) { - lu_byte mask = L->hookmask; - const Instruction *oldpc = L->savedpc; - L->savedpc = pc; - if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) { - resethookcount(L); - luaD_callhook(L, LUA_HOOKCOUNT, -1); - } - if (mask & LUA_MASKLINE) { - Proto *p = ci_func(L->ci)->l.p; - int npc = pcRel(pc, p); - int newline = getline(p, npc); - /* call linehook when enter a new function, when jump back (loop), - or when enter a new line */ - if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p))) - luaD_callhook(L, LUA_HOOKLINE, newline); - } -} - - -static void callTMres (lua_State *L, StkId res, const TValue *f, - const TValue *p1, const TValue *p2) { - ptrdiff_t result = savestack(L, res); - setobj2s(L, L->top, f); /* push function */ - setobj2s(L, L->top+1, p1); /* 1st argument */ - setobj2s(L, L->top+2, p2); /* 2nd argument */ - luaD_checkstack(L, 3); - L->top += 3; - luaD_call(L, L->top - 3, 1); - res = restorestack(L, result); - L->top--; - setobjs2s(L, res, L->top); -} - - - -static void callTM (lua_State *L, const TValue *f, const TValue *p1, - const TValue *p2, const TValue *p3) { - setobj2s(L, L->top, f); /* push function */ - setobj2s(L, L->top+1, p1); /* 1st argument */ - setobj2s(L, L->top+2, p2); /* 2nd argument */ - setobj2s(L, L->top+3, p3); /* 3th argument */ - luaD_checkstack(L, 4); - L->top += 4; - luaD_call(L, L->top - 4, 0); -} - - -void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { - int loop; - for (loop = 0; loop < MAXTAGLOOP; loop++) { - const TValue *tm; - if (ttistable(t)) { /* `t' is a table? */ - Table *h = hvalue(t); - const TValue *res = luaH_get(h, key); /* do a primitive get */ - if (!ttisnil(res) || /* result is no nil? */ - (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ - setobj2s(L, val, res); - return; - } - /* else will try the tag method */ - } - else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) - luaG_typeerror(L, t, "index"); - if (ttisfunction(tm)) { - callTMres(L, val, tm, t, key); - return; - } - t = tm; /* else repeat with `tm' */ - } - luaG_runerror(L, "loop in gettable"); -} - - -void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { - int loop; - TValue temp; - for (loop = 0; loop < MAXTAGLOOP; loop++) { - const TValue *tm; - if (ttistable(t)) { /* `t' is a table? */ - Table *h = hvalue(t); - TValue *oldval = luaH_set(L, h, key); /* do a primitive set */ - if (!ttisnil(oldval) || /* result is no nil? */ - (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */ - setobj2t(L, oldval, val); - h->flags = 0; - luaC_barriert(L, h, val); - return; - } - /* else will try the tag method */ - } - else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) - luaG_typeerror(L, t, "index"); - if (ttisfunction(tm)) { - callTM(L, tm, t, key, val); - return; - } - /* else repeat with `tm' */ - setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */ - t = &temp; - } - luaG_runerror(L, "loop in settable"); -} - - -static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2, - StkId res, TMS event) { - const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ - if (ttisnil(tm)) - tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ - if (ttisnil(tm)) return 0; - callTMres(L, res, tm, p1, p2); - return 1; -} - - -static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2, - TMS event) { - const TValue *tm1 = fasttm(L, mt1, event); - const TValue *tm2; - if (tm1 == NULL) return NULL; /* no metamethod */ - if (mt1 == mt2) return tm1; /* same metatables => same metamethods */ - tm2 = fasttm(L, mt2, event); - if (tm2 == NULL) return NULL; /* no metamethod */ - if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */ - return tm1; - return NULL; -} - - -static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2, - TMS event) { - const TValue *tm1 = luaT_gettmbyobj(L, p1, event); - const TValue *tm2; - if (ttisnil(tm1)) return -1; /* no metamethod? */ - tm2 = luaT_gettmbyobj(L, p2, event); - if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */ - return -1; - callTMres(L, L->top, tm1, p1, p2); - return !l_isfalse(L->top); -} - - -static int l_strcmp (const TString *ls, const TString *rs) { - const char *l = getstr(ls); - size_t ll = ls->tsv.len; - const char *r = getstr(rs); - size_t lr = rs->tsv.len; - for (;;) { - int temp = strcoll(l, r); - if (temp != 0) return temp; - else { /* strings are equal up to a `\0' */ - size_t len = strlen(l); /* index of first `\0' in both strings */ - if (len == lr) /* r is finished? */ - return (len == ll) ? 0 : 1; - else if (len == ll) /* l is finished? */ - return -1; /* l is smaller than r (because r is not finished) */ - /* both strings longer than `len'; go on comparing (after the `\0') */ - len++; - l += len; ll -= len; r += len; lr -= len; - } - } -} - - -int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { - int res; - if (ttype(l) != ttype(r)) - return luaG_ordererror(L, l, r); - else if (ttisnumber(l)) - return luai_numlt(nvalue(l), nvalue(r)); - else if (ttisstring(l)) - return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0; - else if ((res = call_orderTM(L, l, r, TM_LT)) != -1) - return res; - return luaG_ordererror(L, l, r); -} - - -static int lessequal (lua_State *L, const TValue *l, const TValue *r) { - int res; - if (ttype(l) != ttype(r)) - return luaG_ordererror(L, l, r); - else if (ttisnumber(l)) - return luai_numle(nvalue(l), nvalue(r)); - else if (ttisstring(l)) - return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0; - else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) /* first try `le' */ - return res; - else if ((res = call_orderTM(L, r, l, TM_LT)) != -1) /* else try `lt' */ - return !res; - return luaG_ordererror(L, l, r); -} - - -int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) { - const TValue *tm; - lua_assert(ttype(t1) == ttype(t2)); - switch (ttype(t1)) { - case LUA_TNIL: return 1; - case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2)); - case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ - case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); - case LUA_TUSERDATA: { - if (uvalue(t1) == uvalue(t2)) return 1; - tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, - TM_EQ); - break; /* will try TM */ - } - case LUA_TTABLE: { - if (hvalue(t1) == hvalue(t2)) return 1; - tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); - break; /* will try TM */ - } - default: return gcvalue(t1) == gcvalue(t2); - } - if (tm == NULL) return 0; /* no TM? */ - callTMres(L, L->top, tm, t1, t2); /* call TM */ - return !l_isfalse(L->top); -} - - -void luaV_concat (lua_State *L, int total, int last) { - do { - StkId top = L->base + last + 1; - int n = 2; /* number of elements handled in this pass (at least 2) */ - if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) { - if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT)) - luaG_concaterror(L, top-2, top-1); - } else if (tsvalue(top-1)->len == 0) /* second op is empty? */ - (void)tostring(L, top - 2); /* result is first op (as string) */ - else { - /* at least two string values; get as many as possible */ - size_t tl = tsvalue(top-1)->len; - char *buffer; - int i; - /* collect total length */ - for (n = 1; n < total && tostring(L, top-n-1); n++) { - size_t l = tsvalue(top-n-1)->len; - if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow"); - tl += l; - } - buffer = luaZ_openspace(L, &G(L)->buff, tl); - tl = 0; - for (i=n; i>0; i--) { /* concat all strings */ - size_t l = tsvalue(top-i)->len; - memcpy(buffer+tl, svalue(top-i), l); - tl += l; - } - setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl)); - } - total -= n-1; /* got `n' strings to create 1 new */ - last -= n-1; - } while (total > 1); /* repeat until only 1 result left */ -} - - -static void Arith (lua_State *L, StkId ra, const TValue *rb, - const TValue *rc, TMS op) { - TValue tempb, tempc; - const TValue *b, *c; - if ((b = luaV_tonumber(rb, &tempb)) != NULL && - (c = luaV_tonumber(rc, &tempc)) != NULL) { - lua_Number nb = nvalue(b), nc = nvalue(c); - switch (op) { - case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break; - case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break; - case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break; - case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break; - case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break; - case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break; - case TM_UNM: setnvalue(ra, luai_numunm(nb)); break; - default: lua_assert(0); break; - } - } - else if (!call_binTM(L, rb, rc, ra, op)) - luaG_aritherror(L, rb, rc); -} - - - -/* -** some macros for common tasks in `luaV_execute' -*/ - -#define runtime_check(L, c) { if (!(c)) break; } - -#define RA(i) (base+GETARG_A(i)) -/* to be used after possible stack reallocation */ -#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) -#define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i)) -#define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \ - ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)) -#define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \ - ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)) -#define KBx(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i)) - - -#define dojump(L,pc,i) {(pc) += (i); luai_threadyield(L);} - - -#define Protect(x) { L->savedpc = pc; {x;}; base = L->base; } - - -#define arith_op(op,tm) { \ - TValue *rb = RKB(i); \ - TValue *rc = RKC(i); \ - if (ttisnumber(rb) && ttisnumber(rc)) { \ - lua_Number nb = nvalue(rb), nc = nvalue(rc); \ - setnvalue(ra, op(nb, nc)); \ - } \ - else \ - Protect(Arith(L, ra, rb, rc, tm)); \ - } - - - -void luaV_execute (lua_State *L, int nexeccalls) { - LClosure *cl; - StkId base; - TValue *k; - const Instruction *pc; - reentry: /* entry point */ - lua_assert(isLua(L->ci)); - pc = L->savedpc; - cl = &clvalue(L->ci->func)->l; - base = L->base; - k = cl->p->k; - /* main loop of interpreter */ - for (;;) { - const Instruction i = *pc++; - StkId ra; - if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && - (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { - traceexec(L, pc); - if (L->status == LUA_YIELD) { /* did hook yield? */ - L->savedpc = pc - 1; - return; - } - base = L->base; - } - /* warning!! several calls may realloc the stack and invalidate `ra' */ - ra = RA(i); - lua_assert(base == L->base && L->base == L->ci->base); - lua_assert(base <= L->top && L->top <= L->stack + L->stacksize); - lua_assert(L->top == L->ci->top || luaG_checkopenop(i)); - switch (GET_OPCODE(i)) { - case OP_MOVE: { - setobjs2s(L, ra, RB(i)); - continue; - } - case OP_LOADK: { - setobj2s(L, ra, KBx(i)); - continue; - } - case OP_LOADBOOL: { - setbvalue(ra, GETARG_B(i)); - if (GETARG_C(i)) pc++; /* skip next instruction (if C) */ - continue; - } - case OP_LOADNIL: { - TValue *rb = RB(i); - do { - setnilvalue(rb--); - } while (rb >= ra); - continue; - } - case OP_GETUPVAL: { - int b = GETARG_B(i); - setobj2s(L, ra, cl->upvals[b]->v); - continue; - } - case OP_GETGLOBAL: { - TValue g; - TValue *rb = KBx(i); - sethvalue(L, &g, cl->env); - lua_assert(ttisstring(rb)); - Protect(luaV_gettable(L, &g, rb, ra)); - continue; - } - case OP_GETTABLE: { - Protect(luaV_gettable(L, RB(i), RKC(i), ra)); - continue; - } - case OP_SETGLOBAL: { - TValue g; - sethvalue(L, &g, cl->env); - lua_assert(ttisstring(KBx(i))); - Protect(luaV_settable(L, &g, KBx(i), ra)); - continue; - } - case OP_SETUPVAL: { - UpVal *uv = cl->upvals[GETARG_B(i)]; - setobj(L, uv->v, ra); - luaC_barrier(L, uv, ra); - continue; - } - case OP_SETTABLE: { - Protect(luaV_settable(L, ra, RKB(i), RKC(i))); - continue; - } - case OP_NEWTABLE: { - int b = GETARG_B(i); - int c = GETARG_C(i); - sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c))); - Protect(luaC_checkGC(L)); - continue; - } - case OP_SELF: { - StkId rb = RB(i); - setobjs2s(L, ra+1, rb); - Protect(luaV_gettable(L, rb, RKC(i), ra)); - continue; - } - case OP_ADD: { - arith_op(luai_numadd, TM_ADD); - continue; - } - case OP_SUB: { - arith_op(luai_numsub, TM_SUB); - continue; - } - case OP_MUL: { - arith_op(luai_nummul, TM_MUL); - continue; - } - case OP_DIV: { - arith_op(luai_numdiv, TM_DIV); - continue; - } - case OP_MOD: { - arith_op(luai_nummod, TM_MOD); - continue; - } - case OP_POW: { - arith_op(luai_numpow, TM_POW); - continue; - } - case OP_UNM: { - TValue *rb = RB(i); - if (ttisnumber(rb)) { - lua_Number nb = nvalue(rb); - setnvalue(ra, luai_numunm(nb)); - } - else { - Protect(Arith(L, ra, rb, rb, TM_UNM)); - } - continue; - } - case OP_NOT: { - int res = l_isfalse(RB(i)); /* next assignment may change this value */ - setbvalue(ra, res); - continue; - } - case OP_LEN: { - const TValue *rb = RB(i); - switch (ttype(rb)) { - case LUA_TTABLE: { - setnvalue(ra, cast_num(luaH_getn(hvalue(rb)))); - break; - } - case LUA_TSTRING: { - setnvalue(ra, cast_num(tsvalue(rb)->len)); - break; - } - default: { /* try metamethod */ - Protect( - if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN)) - luaG_typeerror(L, rb, "get length of"); - ) - } - } - continue; - } - case OP_CONCAT: { - int b = GETARG_B(i); - int c = GETARG_C(i); - Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L)); - setobjs2s(L, RA(i), base+b); - continue; - } - case OP_JMP: { - dojump(L, pc, GETARG_sBx(i)); - continue; - } - case OP_EQ: { - TValue *rb = RKB(i); - TValue *rc = RKC(i); - Protect( - if (equalobj(L, rb, rc) == GETARG_A(i)) - dojump(L, pc, GETARG_sBx(*pc)); - ) - pc++; - continue; - } - case OP_LT: { - Protect( - if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i)) - dojump(L, pc, GETARG_sBx(*pc)); - ) - pc++; - continue; - } - case OP_LE: { - Protect( - if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i)) - dojump(L, pc, GETARG_sBx(*pc)); - ) - pc++; - continue; - } - case OP_TEST: { - if (l_isfalse(ra) != GETARG_C(i)) - dojump(L, pc, GETARG_sBx(*pc)); - pc++; - continue; - } - case OP_TESTSET: { - TValue *rb = RB(i); - if (l_isfalse(rb) != GETARG_C(i)) { - setobjs2s(L, ra, rb); - dojump(L, pc, GETARG_sBx(*pc)); - } - pc++; - continue; - } - case OP_CALL: { - int b = GETARG_B(i); - int nresults = GETARG_C(i) - 1; - if (b != 0) L->top = ra+b; /* else previous instruction set top */ - L->savedpc = pc; - switch (luaD_precall(L, ra, nresults)) { - case PCRLUA: { - nexeccalls++; - goto reentry; /* restart luaV_execute over new Lua function */ - } - case PCRC: { - /* it was a C function (`precall' called it); adjust results */ - if (nresults >= 0) L->top = L->ci->top; - base = L->base; - continue; - } - default: { - return; /* yield */ - } - } - } - case OP_TAILCALL: { - int b = GETARG_B(i); - if (b != 0) L->top = ra+b; /* else previous instruction set top */ - L->savedpc = pc; - lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); - switch (luaD_precall(L, ra, LUA_MULTRET)) { - case PCRLUA: { - /* tail call: put new frame in place of previous one */ - CallInfo *ci = L->ci - 1; /* previous frame */ - int aux; - StkId func = ci->func; - StkId pfunc = (ci+1)->func; /* previous function index */ - if (L->openupval) luaF_close(L, ci->base); - L->base = ci->base = ci->func + ((ci+1)->base - pfunc); - for (aux = 0; pfunc+aux < L->top; aux++) /* move frame down */ - setobjs2s(L, func+aux, pfunc+aux); - ci->top = L->top = func+aux; /* correct top */ - lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize); - ci->savedpc = L->savedpc; - ci->tailcalls++; /* one more call lost */ - L->ci--; /* remove new frame */ - goto reentry; - } - case PCRC: { /* it was a C function (`precall' called it) */ - base = L->base; - continue; - } - default: { - return; /* yield */ - } - } - } - case OP_RETURN: { - int b = GETARG_B(i); - if (b != 0) L->top = ra+b-1; - if (L->openupval) luaF_close(L, base); - L->savedpc = pc; - b = luaD_poscall(L, ra); - if (--nexeccalls == 0) /* was previous function running `here'? */ - return; /* no: return */ - else { /* yes: continue its execution */ - if (b) L->top = L->ci->top; - lua_assert(isLua(L->ci)); - lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL); - goto reentry; - } - } - case OP_FORLOOP: { - lua_Number step = nvalue(ra+2); - lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */ - lua_Number limit = nvalue(ra+1); - if (luai_numlt(0, step) ? luai_numle(idx, limit) - : luai_numle(limit, idx)) { - dojump(L, pc, GETARG_sBx(i)); /* jump back */ - setnvalue(ra, idx); /* update internal index... */ - setnvalue(ra+3, idx); /* ...and external index */ - } - continue; - } - case OP_FORPREP: { - const TValue *init = ra; - const TValue *plimit = ra+1; - const TValue *pstep = ra+2; - L->savedpc = pc; /* next steps may throw errors */ - if (!tonumber(init, ra)) - luaG_runerror(L, LUA_QL("for") " initial value must be a number"); - else if (!tonumber(plimit, ra+1)) - luaG_runerror(L, LUA_QL("for") " limit must be a number"); - else if (!tonumber(pstep, ra+2)) - luaG_runerror(L, LUA_QL("for") " step must be a number"); - setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep))); - dojump(L, pc, GETARG_sBx(i)); - continue; - } - case OP_TFORLOOP: { - StkId cb = ra + 3; /* call base */ - setobjs2s(L, cb+2, ra+2); - setobjs2s(L, cb+1, ra+1); - setobjs2s(L, cb, ra); - L->top = cb+3; /* func. + 2 args (state and index) */ - Protect(luaD_call(L, cb, GETARG_C(i))); - L->top = L->ci->top; - cb = RA(i) + 3; /* previous call may change the stack */ - if (!ttisnil(cb)) { /* continue loop? */ - setobjs2s(L, cb-1, cb); /* save control variable */ - dojump(L, pc, GETARG_sBx(*pc)); /* jump back */ - } - pc++; - continue; - } - case OP_SETLIST: { - int n = GETARG_B(i); - int c = GETARG_C(i); - int last; - Table *h; - if (n == 0) { - n = cast_int(L->top - ra) - 1; - L->top = L->ci->top; - } - if (c == 0) c = cast_int(*pc++); - runtime_check(L, ttistable(ra)); - h = hvalue(ra); - last = ((c-1)*LFIELDS_PER_FLUSH) + n; - if (last > h->sizearray) /* needs more space? */ - luaH_resizearray(L, h, last); /* pre-alloc it at once */ - for (; n > 0; n--) { - TValue *val = ra+n; - setobj2t(L, luaH_setnum(L, h, last--), val); - luaC_barriert(L, h, val); - } - continue; - } - case OP_CLOSE: { - luaF_close(L, ra); - continue; - } - case OP_CLOSURE: { - Proto *p; - Closure *ncl; - int nup, j; - p = cl->p->p[GETARG_Bx(i)]; - nup = p->nups; - ncl = luaF_newLclosure(L, nup, cl->env); - ncl->l.p = p; - for (j=0; jl.upvals[j] = cl->upvals[GETARG_B(*pc)]; - else { - lua_assert(GET_OPCODE(*pc) == OP_MOVE); - ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc)); - } - } - setclvalue(L, ra, ncl); - Protect(luaC_checkGC(L)); - continue; - } - case OP_VARARG: { - int b = GETARG_B(i) - 1; - int j; - CallInfo *ci = L->ci; - int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1; - if (b == LUA_MULTRET) { - Protect(luaD_checkstack(L, n)); - ra = RA(i); /* previous call may change the stack */ - b = n; - L->top = ra + n; - } - for (j = 0; j < b; j++) { - if (j < n) { - setobjs2s(L, ra + j, ci->base - n + j); - } - else { - setnilvalue(ra + j); - } - } - continue; - } - } - } -} - diff --git a/libraries/lua/lvm.h b/libraries/lua/lvm.h deleted file mode 100644 index bfe4f5678..000000000 --- a/libraries/lua/lvm.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -** $Id: lvm.h,v 2.5.1.1 2007/12/27 13:02:25 roberto Exp $ -** Lua virtual machine -** See Copyright Notice in lua.h -*/ - -#ifndef lvm_h -#define lvm_h - - -#include "ldo.h" -#include "lobject.h" -#include "ltm.h" - - -#define tostring(L,o) ((ttype(o) == LUA_TSTRING) || (luaV_tostring(L, o))) - -#define tonumber(o,n) (ttype(o) == LUA_TNUMBER || \ - (((o) = luaV_tonumber(o,n)) != NULL)) - -#define equalobj(L,o1,o2) \ - (ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2)) - - -LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); -LUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2); -LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n); -LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj); -LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key, - StkId val); -LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key, - StkId val); -LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls); -LUAI_FUNC void luaV_concat (lua_State *L, int total, int last); - -#endif diff --git a/libraries/lua/lzio.c b/libraries/lua/lzio.c deleted file mode 100644 index 293edd59b..000000000 --- a/libraries/lua/lzio.c +++ /dev/null @@ -1,82 +0,0 @@ -/* -** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $ -** a generic input stream interface -** See Copyright Notice in lua.h -*/ - - -#include - -#define lzio_c -#define LUA_CORE - -#include "lua.h" - -#include "llimits.h" -#include "lmem.h" -#include "lstate.h" -#include "lzio.h" - - -int luaZ_fill (ZIO *z) { - size_t size; - lua_State *L = z->L; - const char *buff; - lua_unlock(L); - buff = z->reader(L, z->data, &size); - lua_lock(L); - if (buff == NULL || size == 0) return EOZ; - z->n = size - 1; - z->p = buff; - return char2int(*(z->p++)); -} - - -int luaZ_lookahead (ZIO *z) { - if (z->n == 0) { - if (luaZ_fill(z) == EOZ) - return EOZ; - else { - z->n++; /* luaZ_fill removed first byte; put back it */ - z->p--; - } - } - return char2int(*z->p); -} - - -void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { - z->L = L; - z->reader = reader; - z->data = data; - z->n = 0; - z->p = NULL; -} - - -/* --------------------------------------------------------------- read --- */ -size_t luaZ_read (ZIO *z, void *b, size_t n) { - while (n) { - size_t m; - if (luaZ_lookahead(z) == EOZ) - return n; /* return number of missing bytes */ - m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ - memcpy(b, z->p, m); - z->n -= m; - z->p += m; - b = (char *)b + m; - n -= m; - } - return 0; -} - -/* ------------------------------------------------------------------------ */ -char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) { - if (n > buff->buffsize) { - if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; - luaZ_resizebuffer(L, buff, n); - } - return buff->buffer; -} - - diff --git a/libraries/lua/lzio.h b/libraries/lua/lzio.h deleted file mode 100644 index 51d695d8c..000000000 --- a/libraries/lua/lzio.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $ -** Buffered streams -** See Copyright Notice in lua.h -*/ - - -#ifndef lzio_h -#define lzio_h - -#include "lua.h" - -#include "lmem.h" - - -#define EOZ (-1) /* end of stream */ - -typedef struct Zio ZIO; - -#define char2int(c) cast(int, cast(unsigned char, (c))) - -#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : luaZ_fill(z)) - -typedef struct Mbuffer { - char *buffer; - size_t n; - size_t buffsize; -} Mbuffer; - -#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) - -#define luaZ_buffer(buff) ((buff)->buffer) -#define luaZ_sizebuffer(buff) ((buff)->buffsize) -#define luaZ_bufflen(buff) ((buff)->n) - -#define luaZ_resetbuffer(buff) ((buff)->n = 0) - - -#define luaZ_resizebuffer(L, buff, size) \ - (luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ - (buff)->buffsize = size) - -#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) - - -LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n); -LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, - void *data); -LUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n); /* read next n bytes */ -LUAI_FUNC int luaZ_lookahead (ZIO *z); - - - -/* --------- Private Part ------------------ */ - -struct Zio { - size_t n; /* bytes still unread */ - const char *p; /* current position in buffer */ - lua_Reader reader; - void* data; /* additional data */ - lua_State *L; /* Lua state (for reader) */ -}; - - -LUAI_FUNC int luaZ_fill (ZIO *z); - -#endif diff --git a/libraries/lua/print.c b/libraries/lua/print.c deleted file mode 100644 index e240cfc3c..000000000 --- a/libraries/lua/print.c +++ /dev/null @@ -1,227 +0,0 @@ -/* -** $Id: print.c,v 1.55a 2006/05/31 13:30:05 lhf Exp $ -** print bytecodes -** See Copyright Notice in lua.h -*/ - -#include -#include - -#define luac_c -#define LUA_CORE - -#include "ldebug.h" -#include "lobject.h" -#include "lopcodes.h" -#include "lundump.h" - -#define PrintFunction luaU_print - -#define Sizeof(x) ((int)sizeof(x)) -#define VOID(p) ((const void*)(p)) - -static void PrintString(const TString* ts) -{ - const char* s=getstr(ts); - size_t i,n=ts->tsv.len; - putchar('"'); - for (i=0; ik[i]; - switch (ttype(o)) - { - case LUA_TNIL: - printf("nil"); - break; - case LUA_TBOOLEAN: - printf(bvalue(o) ? "true" : "false"); - break; - case LUA_TNUMBER: - printf(LUA_NUMBER_FMT,nvalue(o)); - break; - case LUA_TSTRING: - PrintString(rawtsvalue(o)); - break; - default: /* cannot happen */ - printf("? type=%d",ttype(o)); - break; - } -} - -static void PrintCode(const Proto* f) -{ - const Instruction* code=f->code; - int pc,n=f->sizecode; - for (pc=0; pc0) printf("[%d]\t",line); else printf("[-]\t"); - printf("%-9s\t",luaP_opnames[o]); - switch (getOpMode(o)) - { - case iABC: - printf("%d",a); - if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (-1-INDEXK(b)) : b); - if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (-1-INDEXK(c)) : c); - break; - case iABx: - if (getBMode(o)==OpArgK) printf("%d %d",a,-1-bx); else printf("%d %d",a,bx); - break; - case iAsBx: - if (o==OP_JMP) printf("%d",sbx); else printf("%d %d",a,sbx); - break; - } - switch (o) - { - case OP_LOADK: - printf("\t; "); PrintConstant(f,bx); - break; - case OP_GETUPVAL: - case OP_SETUPVAL: - printf("\t; %s", (f->sizeupvalues>0) ? getstr(f->upvalues[b]) : "-"); - break; - case OP_GETGLOBAL: - case OP_SETGLOBAL: - printf("\t; %s",svalue(&f->k[bx])); - break; - case OP_GETTABLE: - case OP_SELF: - if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); } - break; - case OP_SETTABLE: - case OP_ADD: - case OP_SUB: - case OP_MUL: - case OP_DIV: - case OP_POW: - case OP_EQ: - case OP_LT: - case OP_LE: - if (ISK(b) || ISK(c)) - { - printf("\t; "); - if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-"); - printf(" "); - if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-"); - } - break; - case OP_JMP: - case OP_FORLOOP: - case OP_FORPREP: - printf("\t; to %d",sbx+pc+2); - break; - case OP_CLOSURE: - printf("\t; %p",VOID(f->p[bx])); - break; - case OP_SETLIST: - if (c==0) printf("\t; %d",(int)code[++pc]); - else printf("\t; %d",c); - break; - default: - break; - } - printf("\n"); - } -} - -#define SS(x) (x==1)?"":"s" -#define S(x) x,SS(x) - -static void PrintHeader(const Proto* f) -{ - const char* s=getstr(f->source); - if (*s=='@' || *s=='=') - s++; - else if (*s==LUA_SIGNATURE[0]) - s="(bstring)"; - else - s="(string)"; - printf("\n%s <%s:%d,%d> (%d instruction%s, %d bytes at %p)\n", - (f->linedefined==0)?"main":"function",s, - f->linedefined,f->lastlinedefined, - S(f->sizecode),f->sizecode*Sizeof(Instruction),VOID(f)); - printf("%d%s param%s, %d slot%s, %d upvalue%s, ", - f->numparams,f->is_vararg?"+":"",SS(f->numparams), - S(f->maxstacksize),S(f->nups)); - printf("%d local%s, %d constant%s, %d function%s\n", - S(f->sizelocvars),S(f->sizek),S(f->sizep)); -} - -static void PrintConstants(const Proto* f) -{ - int i,n=f->sizek; - printf("constants (%d) for %p:\n",n,VOID(f)); - for (i=0; isizelocvars; - printf("locals (%d) for %p:\n",n,VOID(f)); - for (i=0; ilocvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1); - } -} - -static void PrintUpvalues(const Proto* f) -{ - int i,n=f->sizeupvalues; - printf("upvalues (%d) for %p:\n",n,VOID(f)); - if (f->upvalues==NULL) return; - for (i=0; iupvalues[i])); - } -} - -void PrintFunction(const Proto* f, int full) -{ - int i,n=f->sizep; - PrintHeader(f); - PrintCode(f); - if (full) - { - PrintConstants(f); - PrintLocals(f); - PrintUpvalues(f); - } - for (i=0; ip[i],full); -} diff --git a/libraries/lua/bit.c b/libraries/luabit/bit.c similarity index 100% rename from libraries/lua/bit.c rename to libraries/luabit/bit.c diff --git a/libraries/luahttps/common/Connection.h b/libraries/luahttps/common/Connection.h new file mode 100644 index 000000000..815c265ed --- /dev/null +++ b/libraries/luahttps/common/Connection.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +class Connection +{ +public: + virtual bool connect(const std::string &hostname, uint16_t port) = 0; + virtual size_t read(char *buffer, size_t size) = 0; + virtual size_t write(const char *buffer, size_t size) = 0; + virtual void close() = 0; + virtual ~Connection() {}; +}; diff --git a/libraries/luahttps/common/ConnectionClient.h b/libraries/luahttps/common/ConnectionClient.h new file mode 100644 index 000000000..8ddbf671f --- /dev/null +++ b/libraries/luahttps/common/ConnectionClient.h @@ -0,0 +1,35 @@ +#pragma once + +#include "HTTPSClient.h" +#include "HTTPRequest.h" +#include "Connection.h" + +template +class ConnectionClient : public HTTPSClient +{ +public: + virtual bool valid() const override; + virtual HTTPSClient::Reply request(const HTTPSClient::Request &req) override; + +private: + static Connection *factory(); +}; + +template +bool ConnectionClient::valid() const +{ + return Connection::valid(); +} + +template +Connection *ConnectionClient::factory() +{ + return new Connection(); +} + +template +HTTPSClient::Reply ConnectionClient::request(const HTTPSClient::Request &req) +{ + HTTPRequest request(factory); + return request.request(req); +} diff --git a/libraries/luahttps/common/HTTPRequest.cpp b/libraries/luahttps/common/HTTPRequest.cpp new file mode 100644 index 000000000..5d86fd889 --- /dev/null +++ b/libraries/luahttps/common/HTTPRequest.cpp @@ -0,0 +1,151 @@ +#include +#include +#include +#include +#include + +#include "HTTPRequest.h" +#include "PlaintextConnection.h" + +HTTPRequest::HTTPRequest(ConnectionFactory factory) + : factory(factory) +{ +} + +HTTPSClient::Reply HTTPRequest::request(const HTTPSClient::Request &req) +{ + HTTPSClient::Reply reply; + reply.responseCode = 0; + + auto info = parseUrl(req.url); + if (!info.valid) + return reply; + + std::unique_ptr conn; + if (info.schema == "http") + conn.reset(new PlaintextConnection()); + else if (info.schema == "https") + conn.reset(factory()); + else + throw std::runtime_error("Unknown url schema"); + + if (!conn->connect(info.hostname, info.port)) + return reply; + + // Build the request + { + std::stringstream request; + std::string method = req.method; + bool hasData = req.postdata.length() > 0; + + if (method.length() == 0) + method = hasData ? "POST" : "GET"; + + request << method << " " << info.query << " HTTP/1.1\r\n"; + + for (auto &header : req.headers) + request << header.first << ": " << header.second << "\r\n"; + + request << "Connection: Close\r\n"; + + request << "Host: " << info.hostname << "\r\n"; + + if (hasData) + request << "Content-Length: " << req.postdata.size() << "\r\n"; + + request << "\r\n"; + + if (hasData) + request << req.postdata; + + // Send it + std::string requestData = request.str(); + conn->write(requestData.c_str(), requestData.size()); + } + + // Now receive the reply + std::stringstream response; + { + char buffer[8192]; + + while (true) + { + size_t read = conn->read(buffer, sizeof(buffer)); + response.write(buffer, read); + if (read == 0) + break; + } + + conn->close(); + } + + reply.responseCode = 500; + // And parse it + { + std::string protocol; + response >> protocol; + if (protocol != "HTTP/1.1") + return reply; + + response >> reply.responseCode; + response.ignore(std::numeric_limits::max(), '\n'); + + for (std::string line; getline(response, line, '\n') && line != "\r"; ) + { + auto sep = line.find(':'); + reply.headers[line.substr(0, sep)] = line.substr(sep+1, line.size()-sep-1); + } + + auto begin = std::istreambuf_iterator(response); + auto end = std::istreambuf_iterator(); + reply.body = std::string(begin, end); + } + + return reply; +} + +HTTPRequest::DissectedURL HTTPRequest::parseUrl(const std::string &url) +{ + DissectedURL dis; + dis.valid = false; + + // Schema + auto schemaStart = 0; + auto schemaEnd = url.find("://"); + dis.schema = url.substr(schemaStart, schemaEnd-schemaStart); + + // Auth+Hostname+Port + auto connStart = schemaEnd+3; + auto connEnd = url.find('/', connStart); + if (connEnd == std::string::npos) + connEnd = url.size(); + + // TODO: Auth + if (url.find("@", connStart, connEnd-connStart) != std::string::npos) + return dis; + + // Port + auto portStart = url.find(':', connStart); + auto portEnd = connEnd; + if (portStart == std::string::npos || portStart > portEnd) + { + dis.port = dis.schema == "http" ? 80 : 443; + portStart = portEnd; + } + else + dis.port = std::stoi(url.substr(portStart+1, portEnd-portStart-1)); + + // Hostname + auto hostnameStart = connStart; + auto hostnameEnd = portStart; + dis.hostname = url.substr(hostnameStart, hostnameEnd-hostnameStart); + + // And the query + dis.query = url.substr(connEnd); + if (dis.query.size() == 0) + dis.query = "/"; + + dis.valid = true; + + return dis; +} diff --git a/libraries/luahttps/common/HTTPRequest.h b/libraries/luahttps/common/HTTPRequest.h new file mode 100644 index 000000000..fe21c12ce --- /dev/null +++ b/libraries/luahttps/common/HTTPRequest.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "HTTPSClient.h" +#include "Connection.h" + +class HTTPRequest +{ +public: + struct DissectedURL + { + bool valid; + std::string schema; + std::string hostname; + uint16_t port; + std::string query; + // TODO: Auth? + }; + typedef std::function ConnectionFactory; + + HTTPRequest(ConnectionFactory factory); + + HTTPSClient::Reply request(const HTTPSClient::Request &req); + + static DissectedURL parseUrl(const std::string &url); + +private: + ConnectionFactory factory; +}; diff --git a/libraries/luahttps/common/HTTPS.cpp b/libraries/luahttps/common/HTTPS.cpp new file mode 100644 index 000000000..2667f2a4d --- /dev/null +++ b/libraries/luahttps/common/HTTPS.cpp @@ -0,0 +1,79 @@ +#include "HTTPS.h" +#include "config.h" +#include "ConnectionClient.h" + +#include + +#ifdef HTTPS_BACKEND_CURL +# include "../generic/CurlClient.h" +#endif +#ifdef HTTPS_BACKEND_OPENSSL +# include "../generic/OpenSSLConnection.h" +#endif +#ifdef HTTPS_BACKEND_SCHANNEL +# include "../windows/SChannelConnection.h" +#endif +#ifdef HTTPS_BACKEND_NSURL +# include "../apple/NSURLClient.h" +#endif +#ifdef HTTPS_BACKEND_ANDROID +# include "../android/AndroidClient.h" +#endif +#ifdef HTTPS_BACKEND_WININET +# include "../windows/WinINetClient.h" +#endif + +#ifdef HTTPS_BACKEND_CURL + static CurlClient curlclient; +#endif +#ifdef HTTPS_BACKEND_OPENSSL + static ConnectionClient opensslclient; +#endif +#ifdef HTTPS_BACKEND_SCHANNEL + static ConnectionClient schannelclient; +#endif +#ifdef HTTPS_BACKEND_NSURL + static NSURLClient nsurlclient; +#endif +#ifdef HTTPS_BACKEND_ANDROID + static AndroidClient androidclient; +#endif +#ifdef HTTPS_BACKEND_WININET + static WinINetClient wininetclient; +#endif + +static HTTPSClient *clients[] = { +#ifdef HTTPS_BACKEND_CURL + &curlclient, +#endif +#ifdef HTTPS_BACKEND_OPENSSL + &opensslclient, +#endif + // WinINet must be above SChannel +#ifdef HTTPS_BACKEND_WININET + &wininetclient, +#endif +#ifdef HTTPS_BACKEND_SCHANNEL + &schannelclient, +#endif +#ifdef HTTPS_BACKEND_NSURL + &nsurlclient, +#endif +#ifdef HTTPS_BACKEND_ANDROID + &androidclient, +#endif + nullptr, +}; + +HTTPSClient::Reply request(const HTTPSClient::Request &req) +{ + for (size_t i = 0; clients[i]; ++i) + { + HTTPSClient &client = *clients[i]; + + if (client.valid()) + return client.request(req); + } + + throw std::runtime_error("No applicable HTTPS implementation found"); +} diff --git a/libraries/luahttps/common/HTTPS.h b/libraries/luahttps/common/HTTPS.h new file mode 100644 index 000000000..65f72e39b --- /dev/null +++ b/libraries/luahttps/common/HTTPS.h @@ -0,0 +1,5 @@ +#pragma once + +#include "HTTPSClient.h" + +HTTPSClient::Reply request(const HTTPSClient::Request &req); diff --git a/libraries/luahttps/common/HTTPSClient.cpp b/libraries/luahttps/common/HTTPSClient.cpp new file mode 100644 index 000000000..25876ef28 --- /dev/null +++ b/libraries/luahttps/common/HTTPSClient.cpp @@ -0,0 +1,37 @@ +#include +#include + +#include "HTTPSClient.h" + +// This may not be the order you expect, as shorter strings always compare less, +// but it's sufficient for our map +bool HTTPSClient::ci_string_less::operator()(const std::string &lhs, const std::string &rhs) const +{ + const size_t lhs_size = lhs.size(); + const size_t rhs_size = rhs.size(); + const size_t steps = std::min(lhs_size, rhs_size); + + if (lhs_size < rhs_size) + return true; + else if (lhs_size > rhs_size) + return false; + + for (size_t i = 0; i < steps; ++i) + { + char l = std::tolower(lhs[i]); + char r = std::tolower(rhs[i]); + if (l < r) + return true; + else if (l > r) + return false; + } + + return false; +} + +HTTPSClient::Request::Request(const std::string &url) +: url(url) +, method("GET") +{ +} + diff --git a/libraries/luahttps/common/HTTPSClient.h b/libraries/luahttps/common/HTTPSClient.h new file mode 100644 index 000000000..8b2b23d88 --- /dev/null +++ b/libraries/luahttps/common/HTTPSClient.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +class HTTPSClient +{ +public: + struct ci_string_less + { + bool operator()(const std::string &lhs, const std::string &rhs) const; + }; + using header_map = std::map; + + struct Request + { + Request(const std::string &url); + + header_map headers; + std::string url; + std::string postdata; + std::string method; + }; + + struct Reply + { + header_map headers; + std::string body; + int responseCode; + }; + + virtual ~HTTPSClient() {} + virtual bool valid() const = 0; + virtual Reply request(const Request &req) = 0; +}; diff --git a/libraries/https/common/HTTPSCommon.cpp b/libraries/luahttps/common/HTTPSCommon.cpp similarity index 100% rename from libraries/https/common/HTTPSCommon.cpp rename to libraries/luahttps/common/HTTPSCommon.cpp diff --git a/libraries/https/common/HTTPSCommon.h b/libraries/luahttps/common/HTTPSCommon.h similarity index 100% rename from libraries/https/common/HTTPSCommon.h rename to libraries/luahttps/common/HTTPSCommon.h diff --git a/libraries/luahttps/common/PlaintextConnection.cpp b/libraries/luahttps/common/PlaintextConnection.cpp new file mode 100644 index 000000000..95bcd37dc --- /dev/null +++ b/libraries/luahttps/common/PlaintextConnection.cpp @@ -0,0 +1,99 @@ +#include "config.h" +#include +#ifndef HTTPS_USE_WINSOCK +# include +# include +# include +# include +#else +# include +# include +#endif // HTTPS_USE_WINSOCK + +#include "PlaintextConnection.h" + +#ifdef HTTPS_USE_WINSOCK + static void close(int fd) + { + closesocket(fd); + } +#endif // HTTPS_USE_WINSOCK + +PlaintextConnection::PlaintextConnection() + : fd(-1) +{ +#ifdef HTTPS_USE_WINSOCK + static bool wsaInit = false; + if (!wsaInit) + { + WSADATA data; + WSAStartup(MAKEWORD(2, 2), &data); + } +#endif // HTTPS_USE_WINSOCK +} + +PlaintextConnection::~PlaintextConnection() +{ + if (fd != -1) + ::close(fd); +} + +bool PlaintextConnection::connect(const std::string &hostname, uint16_t port) +{ + addrinfo hints; + std::memset(&hints, 0, sizeof(hints)); + hints.ai_flags = hints.ai_protocol = 0; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + addrinfo *addrs = nullptr; + std::string portString = std::to_string(port); + getaddrinfo(hostname.c_str(), portString.c_str(), &hints, &addrs); + + // Try all addresses returned + bool connected = false; + for (addrinfo *addr = addrs; !connected && addr; addr = addr->ai_next) + { + fd = socket(addr->ai_family, SOCK_STREAM, 0); + connected = ::connect(fd, addr->ai_addr, addr->ai_addrlen) == 0; + if (!connected) + ::close(fd); + } + + freeaddrinfo(addrs); + + if (!connected) + { + fd = -1; + return false; + } + + return true; +} + +size_t PlaintextConnection::read(char *buffer, size_t size) +{ + auto read = ::recv(fd, buffer, size, 0); + if (read < 0) + read = 0; + return static_cast(read); +} + +size_t PlaintextConnection::write(const char *buffer, size_t size) +{ + auto written = ::send(fd, buffer, size, 0); + if (written < 0) + written = 0; + return static_cast(written); +} + +void PlaintextConnection::close() +{ + ::close(fd); + fd = -1; +} + +int PlaintextConnection::getFd() const +{ + return fd; +} diff --git a/libraries/https/common/PlaintextConnection.h b/libraries/luahttps/common/PlaintextConnection.h similarity index 100% rename from libraries/https/common/PlaintextConnection.h rename to libraries/luahttps/common/PlaintextConnection.h diff --git a/libraries/luahttps/common/config.h b/libraries/luahttps/common/config.h new file mode 100644 index 000000000..2bf6def6d --- /dev/null +++ b/libraries/luahttps/common/config.h @@ -0,0 +1,32 @@ +#pragma once + +#if defined(HTTPS_HAVE_CONFIG_GENERATED_H) + #include "common/config-generated.h" +#elif defined(WIN32) || defined(_WIN32) + #define HTTPS_BACKEND_SCHANNEL + #define HTTPS_USE_WINSOCK + #include + #if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) + // WinINet is only supported on desktop. + #define HTTPS_BACKEND_WININET + #endif +#elif defined(__ANDROID__) + #define HTTPS_BACKEND_ANDROID +#elif defined(__APPLE__) + #define HTTPS_BACKEND_NSURL +#elif defined(__3DS__) || defined(__SWITCH__) || defined(__WIIU__) + #define HTTPS_BACKEND_CURL +#elif defined(linux) || defined(__linux) || defined(__linux__) + #if defined __has_include + #if __has_include() + #define HTTPS_BACKEND_CURL + #endif + #if __has_include() + #define HTTPS_BACKEND_OPENSSL + #endif + #else + // Hope for the best... + #define HTTPS_BACKEND_CURL + #define HTTPS_BACKEND_OPENSSL + #endif +#endif diff --git a/libraries/luahttps/generic/CurlClient.cpp b/libraries/luahttps/generic/CurlClient.cpp new file mode 100644 index 000000000..3ed2c57ae --- /dev/null +++ b/libraries/luahttps/generic/CurlClient.cpp @@ -0,0 +1,166 @@ +#ifdef _WIN32 +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#endif + +#include "CurlClient.h" + +#ifdef HTTPS_BACKEND_CURL + +#include +#include +#include +#include + +typedef struct StringReader +{ + const std::string* str; + size_t pos; +} StringReader; + +CurlClient::Curl::Curl() : handle(nullptr), loaded(false) +{ + handle = curl_easy_init(); + + if (!handle) + return; + + curl_global_init(CURL_GLOBAL_DEFAULT); + loaded = true; +} + +CurlClient::Curl::~Curl() +{ + if (loaded) + curl_global_cleanup(); + + if (handle) + curl_easy_cleanup(handle); +} + +static char toUppercase(char c) +{ + int ch = (unsigned char)c; + return toupper(ch); +} + +static size_t stringReader(char* ptr, size_t size, size_t nmemb, StringReader* reader) +{ + const char* data = reader->str->data(); + size_t len = reader->str->length(); + size_t maxCount = (len - reader->pos) / size; + size_t desiredCount = std::min(maxCount, nmemb); + size_t desiredBytes = desiredCount * size; + + std::copy(data + reader->pos, data + desiredBytes, ptr); + reader->pos += desiredBytes; + + return desiredCount; +} + +static size_t stringstreamWriter(char* ptr, size_t size, size_t nmemb, std::stringstream* ss) +{ + size_t count = size * nmemb; + ss->write(ptr, count); + return count; +} + +static size_t headerWriter( + char* ptr, size_t size, size_t nmemb, std::map* userdata) +{ + std::map& headers = *userdata; + size_t count = size * nmemb; + std::string line(ptr, count); + size_t split = line.find(':'); + size_t newline = line.find('\r'); + if (newline == std::string::npos) + newline = line.size(); + + if (split != std::string::npos) + headers[line.substr(0, split)] = line.substr(split + 1, newline - split - 1); + return count; +} + +bool CurlClient::valid() const +{ + return curl.loaded; +} + +HTTPSClient::Reply CurlClient::request(const HTTPSClient::Request& req) +{ + Reply reply; + reply.responseCode = 0; + + // Use sensible default header for later + HTTPSClient::header_map newHeaders = req.headers; + + CURL* handle = curl_easy_init(); + + if (!handle) + throw std::runtime_error("Could not create curl request"); + + curl_easy_setopt(handle, CURLOPT_URL, req.url.c_str()); + curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L); /* do not verify */ + curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, req.method.c_str()); + + StringReader reader{}; + + if (req.postdata.size() > 0 && (req.method != "GET" && req.method != "HEAD")) + { + reader.str = &req.postdata; + reader.pos = 0; + + curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L); + curl_easy_setopt(handle, CURLOPT_READFUNCTION, stringReader); + curl_easy_setopt(handle, CURLOPT_READDATA, &reader); + curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)req.postdata.length()); + } + + if (req.method == "HEAD") + curl_easy_setopt(handle, CURLOPT_NOBODY, 1L); + + // Curl doesn't copy memory, keep the strings around + std::vector lines; + for (auto& header : newHeaders) + { + std::stringstream line; + line << header.first << ": " << header.second; + lines.push_back(line.str()); + } + + curl_slist* sendHeaders = nullptr; + for (auto& line : lines) + sendHeaders = curl_slist_append(sendHeaders, line.c_str()); + + if (sendHeaders) + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, sendHeaders); + + std::stringstream body; + + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, stringstreamWriter); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, &body); + + curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerWriter); + curl_easy_setopt(handle, CURLOPT_HEADERDATA, &reply.headers); + + curl_easy_perform(handle); + + if (sendHeaders) + curl_slist_free_all(sendHeaders); + + { + long responseCode; + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode); + reply.responseCode = (int)responseCode; + } + + reply.body = body.str(); + + curl_easy_cleanup(handle); + return reply; +} + +CurlClient::Curl CurlClient::curl; + +#endif // HTTPS_BACKEND_CURL diff --git a/libraries/luahttps/generic/CurlClient.h b/libraries/luahttps/generic/CurlClient.h new file mode 100644 index 000000000..3ed90356c --- /dev/null +++ b/libraries/luahttps/generic/CurlClient.h @@ -0,0 +1,27 @@ +#pragma once + +#include "../common/config.h" + +#ifdef HTTPS_BACKEND_CURL + +#include + +#include "../common/HTTPSClient.h" + +class CurlClient : public HTTPSClient +{ +public: + virtual bool valid() const override; + virtual HTTPSClient::Reply request(const HTTPSClient::Request &req) override; + +private: + static struct Curl + { + Curl(); + ~Curl(); + void *handle; + bool loaded; + } curl; +}; + +#endif // HTTPS_BACKEND_CURL diff --git a/libraries/luahttps/https.cpp b/libraries/luahttps/https.cpp new file mode 100644 index 000000000..44847c08e --- /dev/null +++ b/libraries/luahttps/https.cpp @@ -0,0 +1,137 @@ +#include +#include + +extern "C" +{ +#include +#include +} + +#include "common/HTTPS.h" +#include "common/config.h" + +static std::string validMethod[] = { "GET", "HEAD", "POST", "PUT", "DELETE", "PATCH" }; + +static int str_toupper(char c) +{ + unsigned char uc = (unsigned char)c; + return toupper(uc); +} + +static std::string w_checkstring(lua_State* L, int idx) +{ + size_t len; + const char* str = luaL_checklstring(L, idx, &len); + return std::string(str, len); +} + +static void w_pushstring(lua_State* L, const std::string& str) +{ + lua_pushlstring(L, str.data(), str.size()); +} + +static void w_readheaders(lua_State* L, int idx, HTTPSClient::header_map& headers) +{ + if (idx < 0) + idx += lua_gettop(L) + 1; + + lua_pushnil(L); + while (lua_next(L, idx)) + { + auto header = w_checkstring(L, -2); + headers[header] = w_checkstring(L, -1); + lua_pop(L, 1); + } + lua_pop(L, 1); +} + +static std::string w_optmethod(lua_State* L, int idx, const std::string& defaultMethod) +{ + std::string* const validMethodEnd = validMethod + sizeof(validMethod) / sizeof(std::string); + + if (lua_isnoneornil(L, idx)) + return defaultMethod; + + std::string str = w_checkstring(L, idx); + std::transform(str.begin(), str.end(), str.begin(), str_toupper); + + if (std::find(validMethod, validMethodEnd, str) == validMethodEnd) + luaL_argerror( + L, idx, + "expected one of \"get\", \"head\", \"post\", \"put\", \"delete\", or \"patch\""); + + return str; +} + +static int w_request(lua_State* L) +{ + auto url = w_checkstring(L, 1); + HTTPSClient::Request req(url); + + bool advanced = false; + + if (lua_istable(L, 2)) + { + advanced = true; + + std::string defaultMethod = "GET"; + + lua_getfield(L, 2, "data"); + if (!lua_isnoneornil(L, -1)) + { + req.postdata = w_checkstring(L, -1); + req.headers["Content-Type"] = "application/x-www-form-urlencoded"; + defaultMethod = "POST"; + } + lua_pop(L, 1); + + lua_getfield(L, 2, "method"); + req.method = w_optmethod(L, -1, defaultMethod); + lua_pop(L, 1); + + lua_getfield(L, 2, "headers"); + if (!lua_isnoneornil(L, -1)) + w_readheaders(L, -1, req.headers); + lua_pop(L, 1); + } + + HTTPSClient::Reply reply; + + try + { + reply = request(req); + } + catch (const std::exception& e) + { + std::string errorMessage = e.what(); + lua_pushnil(L); + lua_pushstring(L, errorMessage.c_str()); + return 2; + } + + lua_pushinteger(L, reply.responseCode); + w_pushstring(L, reply.body); + + if (advanced) + { + lua_newtable(L); + for (const auto& header : reply.headers) + { + w_pushstring(L, header.first); + w_pushstring(L, header.second); + lua_settable(L, -3); + } + } + + return advanced ? 3 : 2; +} + +extern "C" int luaopen_https(lua_State* L) +{ + lua_newtable(L); + + lua_pushcfunction(L, w_request); + lua_setfield(L, -2, "request"); + + return 1; +} diff --git a/libraries/luasocket/libluasocket/auxiliar.c b/libraries/luasocket/libluasocket/auxiliar.c index 18fa8e4c3..93a66a09f 100644 --- a/libraries/luasocket/libluasocket/auxiliar.c +++ b/libraries/luasocket/libluasocket/auxiliar.c @@ -2,14 +2,11 @@ * Auxiliar routines for class hierarchy manipulation * LuaSocket toolkit \*=========================================================================*/ +#include "luasocket.h" +#include "auxiliar.h" #include #include -#include "auxiliar.h" - -/*=========================================================================*\ -* Exported functions -\*=========================================================================*/ /*-------------------------------------------------------------------------*\ * Initializes the module \*-------------------------------------------------------------------------*/ @@ -143,7 +140,7 @@ void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx) { * otherwise \*-------------------------------------------------------------------------*/ void *auxiliar_getclassudata(lua_State *L, const char *classname, int objidx) { - return luaL_checkudata(L, objidx, classname); + return luaL_testudata(L, objidx, classname); } /*-------------------------------------------------------------------------*\ @@ -155,4 +152,3 @@ int auxiliar_typeerror (lua_State *L, int narg, const char *tname) { luaL_typename(L, narg)); return luaL_argerror(L, narg, msg); } - diff --git a/libraries/luasocket/libluasocket/auxiliar.h b/libraries/luasocket/libluasocket/auxiliar.h index 65511d4de..e8c3ead82 100644 --- a/libraries/luasocket/libluasocket/auxiliar.h +++ b/libraries/luasocket/libluasocket/auxiliar.h @@ -29,20 +29,26 @@ * reverse mapping are done using lauxlib. \*=========================================================================*/ -#include "lua.h" -#include "lauxlib.h" -#include "compat.h" +#include "luasocket.h" + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif int auxiliar_open(lua_State *L); void auxiliar_newclass(lua_State *L, const char *classname, luaL_Reg *func); +int auxiliar_tostring(lua_State *L); void auxiliar_add2group(lua_State *L, const char *classname, const char *group); -void auxiliar_setclass(lua_State *L, const char *classname, int objidx); +int auxiliar_checkboolean(lua_State *L, int objidx); void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx); void *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx); -void *auxiliar_getclassudata(lua_State *L, const char *groupname, int objidx); +void auxiliar_setclass(lua_State *L, const char *classname, int objidx); void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx); -int auxiliar_checkboolean(lua_State *L, int objidx); -int auxiliar_tostring(lua_State *L); +void *auxiliar_getclassudata(lua_State *L, const char *groupname, int objidx); int auxiliar_typeerror(lua_State *L, int narg, const char *tname); +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + #endif /* AUXILIAR_H */ diff --git a/libraries/luasocket/libluasocket/buffer.c b/libraries/luasocket/libluasocket/buffer.c index fff16346f..7148be34f 100644 --- a/libraries/luasocket/libluasocket/buffer.c +++ b/libraries/luasocket/libluasocket/buffer.c @@ -2,10 +2,7 @@ * Input/Output interface for Lua programs * LuaSocket toolkit \*=========================================================================*/ -#include "lua.h" -#include "lauxlib.h" -#include "compat.h" - +#include "luasocket.h" #include "buffer.h" /*=========================================================================*\ @@ -106,11 +103,14 @@ int buffer_meth_send(lua_State *L, p_buffer buf) { * object:receive() interface \*-------------------------------------------------------------------------*/ int buffer_meth_receive(lua_State *L, p_buffer buf) { - int err = IO_DONE, top = lua_gettop(L); + int err = IO_DONE, top; luaL_Buffer b; size_t size; const char *part = luaL_optlstring(L, 3, "", &size); timeout_markstart(buf->tm); + /* make sure we don't confuse buffer stuff with arguments */ + lua_settop(L, 3); + top = lua_gettop(L); /* initialize buffer with optional extra prefix * (useful for concatenating previous partial results) */ luaL_buffinit(L, &b); diff --git a/libraries/luasocket/libluasocket/buffer.h b/libraries/luasocket/libluasocket/buffer.h index 1281bb391..a0901fcc8 100644 --- a/libraries/luasocket/libluasocket/buffer.h +++ b/libraries/luasocket/libluasocket/buffer.h @@ -15,8 +15,7 @@ * The module is built on top of the I/O abstraction defined in io.h and the * timeout management is done with the timeout.h interface. \*=========================================================================*/ -#include "lua.h" - +#include "luasocket.h" #include "io.h" #include "timeout.h" @@ -34,12 +33,20 @@ typedef struct t_buffer_ { } t_buffer; typedef t_buffer *p_buffer; +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + int buffer_open(lua_State *L); void buffer_init(p_buffer buf, p_io io, p_timeout tm); -int buffer_meth_send(lua_State *L, p_buffer buf); -int buffer_meth_receive(lua_State *L, p_buffer buf); int buffer_meth_getstats(lua_State *L, p_buffer buf); int buffer_meth_setstats(lua_State *L, p_buffer buf); +int buffer_meth_send(lua_State *L, p_buffer buf); +int buffer_meth_receive(lua_State *L, p_buffer buf); int buffer_isempty(p_buffer buf); +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + #endif /* BUF_H */ diff --git a/libraries/luasocket/libluasocket/compat.c b/libraries/luasocket/libluasocket/compat.c index c2d99cb20..34ffdaf71 100644 --- a/libraries/luasocket/libluasocket/compat.c +++ b/libraries/luasocket/libluasocket/compat.c @@ -1,10 +1,12 @@ +#include "luasocket.h" #include "compat.h" #if LUA_VERSION_NUM==501 + /* ** Adapted from Lua 5.2 */ -void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { +void luasocket_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { luaL_checkstack(L, nup+1, "too many upvalues"); for (; l->name != NULL; l++) { /* fill the table with given functions */ int i; @@ -16,4 +18,22 @@ void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { } lua_pop(L, nup); /* remove upvalues */ } + +/* +** Duplicated from Lua 5.2 +*/ +void *luasocket_testudata (lua_State *L, int ud, const char *tname) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + luaL_getmetatable(L, tname); /* get correct metatable */ + if (!lua_rawequal(L, -1, -2)) /* not the same? */ + p = NULL; /* value is a userdata with wrong metatable */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + return NULL; /* value is not a userdata with a metatable */ +} + #endif diff --git a/libraries/luasocket/libluasocket/compat.h b/libraries/luasocket/libluasocket/compat.h index 7bf8010ea..fa2d7d7c6 100644 --- a/libraries/luasocket/libluasocket/compat.h +++ b/libraries/luasocket/libluasocket/compat.h @@ -1,11 +1,22 @@ #ifndef COMPAT_H #define COMPAT_H -#include "lua.h" -#include "lauxlib.h" - #if LUA_VERSION_NUM==501 -void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +void luasocket_setfuncs (lua_State *L, const luaL_Reg *l, int nup); +void *luasocket_testudata ( lua_State *L, int arg, const char *tname); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#define luaL_setfuncs luasocket_setfuncs +#define luaL_testudata luasocket_testudata + #endif #endif diff --git a/libraries/luasocket/libluasocket/except.c b/libraries/luasocket/libluasocket/except.c index 60b500582..9c3317f26 100644 --- a/libraries/luasocket/libluasocket/except.c +++ b/libraries/luasocket/libluasocket/except.c @@ -2,13 +2,9 @@ * Simple exception support * LuaSocket toolkit \*=========================================================================*/ -#include - -#include "lua.h" -#include "lauxlib.h" -#include "compat.h" - +#include "luasocket.h" #include "except.h" +#include #if LUA_VERSION_NUM < 502 #define lua_pcallk(L, na, nr, err, ctx, cont) \ diff --git a/libraries/luasocket/libluasocket/except.h b/libraries/luasocket/libluasocket/except.h index 2497c0566..71c31fd4d 100644 --- a/libraries/luasocket/libluasocket/except.h +++ b/libraries/luasocket/libluasocket/except.h @@ -31,8 +31,16 @@ * exceptions on error, but that don't interrupt the user script. \*=========================================================================*/ -#include "lua.h" +#include "luasocket.h" + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif int except_open(lua_State *L); +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + #endif diff --git a/libraries/luasocket/libluasocket/ftp.lua b/libraries/luasocket/libluasocket/ftp.lua index bd528caa2..a2f6563b8 100644 --- a/libraries/luasocket/libluasocket/ftp.lua +++ b/libraries/luasocket/libluasocket/ftp.lua @@ -1,3 +1,4 @@ +R"luastring"--( ----------------------------------------------------------------------------- -- FTP support for the Lua language -- LuaSocket toolkit. @@ -56,7 +57,7 @@ end function metat.__index:login(user, password) self.try(self.tp:command("user", user or _M.USER)) - local code, reply = self.try(self.tp:check{"2..", 331}) + local code, _ = self.try(self.tp:check{"2..", 331}) if code == 331 then self.try(self.tp:command("pass", password or _M.PASSWORD)) self.try(self.tp:check("2..")) @@ -66,7 +67,7 @@ end function metat.__index:pasv() self.try(self.tp:command("pasv")) - local code, reply = self.try(self.tp:check("2..")) + local _, reply = self.try(self.tp:check("2..")) local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)" local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) self.try(a and b and c and d and p1 and p2, reply) @@ -83,9 +84,9 @@ end function metat.__index:epsv() self.try(self.tp:command("epsv")) - local code, reply = self.try(self.tp:check("229")) + local _, reply = self.try(self.tp:check("229")) local pattern = "%((.)(.-)%1(.-)%1(.-)%1%)" - local d, prt, address, port = string.match(reply, pattern) + local _, _, _, port = string.match(reply, pattern) self.try(port, "invalid epsv response") self.pasvt = { address = self.tp:getpeername(), @@ -102,7 +103,7 @@ end function metat.__index:port(address, port) self.pasvt = nil if not address then - address, port = self.try(self.tp:getsockname()) + address = self.try(self.tp:getsockname()) self.server = self.try(socket.bind(address, 0)) address, port = self.try(self.server:getsockname()) self.try(self.server:settimeout(_M.TIMEOUT)) @@ -118,7 +119,7 @@ end function metat.__index:eprt(family, address, port) self.pasvt = nil if not address then - address, port = self.try(self.tp:getsockname()) + address = self.try(self.tp:getsockname()) self.server = self.try(socket.bind(address, 0)) address, port = self.try(self.server:getsockname()) self.try(self.server:settimeout(_M.TIMEOUT)) @@ -142,7 +143,7 @@ function metat.__index:send(sendt) local command = sendt.command or "stor" -- send the transfer command and check the reply self.try(self.tp:command(command, argument)) - local code, reply = self.try(self.tp:check{"2..", "1.."}) + local code, _ = self.try(self.tp:check{"2..", "1.."}) -- if there is not a pasvt table, then there is a server -- and we already sent a PORT command if not self.pasvt then self:portconnect() end @@ -327,3 +328,5 @@ _M.get = socket.protect(function(gett) end) return _M +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/ftp.lua.h b/libraries/luasocket/libluasocket/ftp.lua.h deleted file mode 100644 index fcb0d403a..000000000 --- a/libraries/luasocket/libluasocket/ftp.lua.h +++ /dev/null @@ -1,544 +0,0 @@ -/* code automatically generated by bin2c -- DO NOT EDIT */ -{ -/* #include'ing this file in a C program is equivalent to calling - if (luaL_loadfile(L,"ftp.lua")==0) lua_call(L, 0, LUA_MULTRET); -*/ -/* ftp.lua */ -static const unsigned char B1[]={ - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, - 32, 70, 84, 80, 32,115,117,112,112,111,114,116, 32,102,111,114, 32,116,104,101, - 32, 76,117, 97, 32,108, 97,110,103,117, 97,103,101, 10, 45, 45, 32, 76,117, 97, - 83,111, 99,107,101,116, 32,116,111,111,108,107,105,116, 46, 10, 45, 45, 32, 65, -117,116,104,111,114, 58, 32, 68,105,101,103,111, 32, 78,101,104, 97, 98, 10, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 10, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 68, -101, 99,108, 97,114,101, 32,109,111,100,117,108,101, 32, 97,110,100, 32,105,109, -112,111,114,116, 32,100,101,112,101,110,100,101,110, 99,105,101,115, 10, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10,108,111, 99, 97, -108, 32, 98, 97,115,101, 32, 61, 32, 95, 71, 10,108,111, 99, 97,108, 32,116, 97, - 98,108,101, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,116, 97, 98,108,101, - 34, 41, 10,108,111, 99, 97,108, 32,115,116,114,105,110,103, 32, 61, 32,114,101, -113,117,105,114,101, 40, 34,115,116,114,105,110,103, 34, 41, 10,108,111, 99, 97, -108, 32,109, 97,116,104, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,109, 97, -116,104, 34, 41, 10,108,111, 99, 97,108, 32,115,111, 99,107,101,116, 32, 61, 32, -114,101,113,117,105,114,101, 40, 34,115,111, 99,107,101,116, 34, 41, 10,108,111, - 99, 97,108, 32,117,114,108, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,115, -111, 99,107,101,116, 46,117,114,108, 34, 41, 10,108,111, 99, 97,108, 32,116,112, - 32, 61, 32,114,101,113,117,105,114,101, 40, 34,115,111, 99,107,101,116, 46,116, -112, 34, 41, 10,108,111, 99, 97,108, 32,108,116,110, 49, 50, 32, 61, 32,114,101, -113,117,105,114,101, 40, 34,108,116,110, 49, 50, 34, 41, 10,115,111, 99,107,101, -116, 46,102,116,112, 32, 61, 32,123,125, 10,108,111, 99, 97,108, 32, 95, 77, 32, - 61, 32,115,111, 99,107,101,116, 46,102,116,112, 10, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 80,114,111,103,114, 97, -109, 32, 99,111,110,115,116, 97,110,116,115, 10, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32,116,105,109,101,111,117,116, - 32,105,110, 32,115,101, 99,111,110,100,115, 32, 98,101,102,111,114,101, 32,116, -104,101, 32,112,114,111,103,114, 97,109, 32,103,105,118,101,115, 32,117,112, 32, -111,110, 32, 97, 32, 99,111,110,110,101, 99,116,105,111,110, 10, 95, 77, 46, 84, - 73, 77, 69, 79, 85, 84, 32, 61, 32, 54, 48, 10, 45, 45, 32,100,101,102, 97,117, -108,116, 32,112,111,114,116, 32,102,111,114, 32,102,116,112, 32,115,101,114,118, -105, 99,101, 10,108,111, 99, 97,108, 32, 80, 79, 82, 84, 32, 61, 32, 50, 49, 10, - 45, 45, 32,116,104,105,115, 32,105,115, 32,116,104,101, 32,100,101,102, 97,117, -108,116, 32, 97,110,111,110,121,109,111,117,115, 32,112, 97,115,115,119,111,114, -100, 46, 32,117,115,101,100, 32,119,104,101,110, 32,110,111, 32,112, 97,115,115, -119,111,114,100, 32,105,115, 10, 45, 45, 32,112,114,111,118,105,100,101,100, 32, -105,110, 32,117,114,108, 46, 32,115,104,111,117,108,100, 32, 98,101, 32, 99,104, - 97,110,103,101,100, 32,116,111, 32,121,111,117,114, 32,101, 45,109, 97,105,108, - 46, 10, 95, 77, 46, 85, 83, 69, 82, 32, 61, 32, 34,102,116,112, 34, 10, 95, 77, - 46, 80, 65, 83, 83, 87, 79, 82, 68, 32, 61, 32, 34, 97,110,111,110,121,109,111, -117,115, 64, 97,110,111,110,121,109,111,117,115, 46,111,114,103, 34, 10, 10, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, - 76,111,119, 32,108,101,118,101,108, 32, 70, 84, 80, 32, 65, 80, 73, 10, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10,108,111, 99, 97, -108, 32,109,101,116, 97,116, 32, 61, 32,123, 32, 95, 95,105,110,100,101,120, 32, - 61, 32,123,125, 32,125, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 77, 46, -111,112,101,110, 40,115,101,114,118,101,114, 44, 32,112,111,114,116, 44, 32, 99, -114,101, 97,116,101, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,116,112, 32, - 61, 32,115,111, 99,107,101,116, 46,116,114,121, 40,116,112, 46, 99,111,110,110, -101, 99,116, 40,115,101,114,118,101,114, 44, 32,112,111,114,116, 32,111,114, 32, - 80, 79, 82, 84, 44, 32, 95, 77, 46, 84, 73, 77, 69, 79, 85, 84, 44, 32, 99,114, -101, 97,116,101, 41, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,102, 32, 61, - 32, 98, 97,115,101, 46,115,101,116,109,101,116, 97,116, 97, 98,108,101, 40,123, - 32,116,112, 32, 61, 32,116,112, 32,125, 44, 32,109,101,116, 97,116, 41, 10, 32, - 32, 32, 32, 45, 45, 32,109, 97,107,101, 32,115,117,114,101, 32,101,118,101,114, -121,116,104,105,110,103, 32,103,101,116,115, 32, 99,108,111,115,101,100, 32,105, -110, 32, 97,110, 32,101,120, 99,101,112,116,105,111,110, 10, 32, 32, 32, 32,102, - 46,116,114,121, 32, 61, 32,115,111, 99,107,101,116, 46,110,101,119,116,114,121, - 40,102,117,110, 99,116,105,111,110, 40, 41, 32,102, 58, 99,108,111,115,101, 40, - 41, 32,101,110,100, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,102, 10, -101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, - 95, 95,105,110,100,101,120, 58,112,111,114,116, 99,111,110,110,101, 99,116, 40, - 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46, -115,101,114,118,101,114, 58,115,101,116,116,105,109,101,111,117,116, 40, 95, 77, - 46, 84, 73, 77, 69, 79, 85, 84, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, 46, -100, 97,116, 97, 32, 61, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, - 46,115,101,114,118,101,114, 58, 97, 99, 99,101,112,116, 40, 41, 41, 10, 32, 32, - 32, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,100, 97,116, 97, - 58,115,101,116,116,105,109,101,111,117,116, 40, 95, 77, 46, 84, 73, 77, 69, 79, - 85, 84, 41, 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,109, -101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,112, 97,115,118, 99,111,110, -110,101, 99,116, 40, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,100, 97,116, 97, - 32, 61, 32,115,101,108,102, 46,116,114,121, 40,115,111, 99,107,101,116, 46,116, - 99,112, 40, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115, -101,108,102, 46,100, 97,116, 97, 58,115,101,116,116,105,109,101,111,117,116, 40, - 95, 77, 46, 84, 73, 77, 69, 79, 85, 84, 41, 41, 10, 32, 32, 32, 32,115,101,108, -102, 46,116,114,121, 40,115,101,108,102, 46,100, 97,116, 97, 58, 99,111,110,110, -101, 99,116, 40,115,101,108,102, 46,112, 97,115,118,116, 46, 97,100,100,114,101, -115,115, 44, 32,115,101,108,102, 46,112, 97,115,118,116, 46,112,111,114,116, 41, - 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97, -116, 46, 95, 95,105,110,100,101,120, 58,108,111,103,105,110, 40,117,115,101,114, - 44, 32,112, 97,115,115,119,111,114,100, 41, 10, 32, 32, 32, 32,115,101,108,102, - 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,111,109,109, 97,110,100, - 40, 34,117,115,101,114, 34, 44, 32,117,115,101,114, 32,111,114, 32, 95, 77, 46, - 85, 83, 69, 82, 41, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 99,111,100, -101, 44, 32,114,101,112,108,121, 32, 61, 32,115,101,108,102, 46,116,114,121, 40, -115,101,108,102, 46,116,112, 58, 99,104,101, 99,107,123, 34, 50, 46, 46, 34, 44, - 32, 51, 51, 49,125, 41, 10, 32, 32, 32, 32,105,102, 32, 99,111,100,101, 32, 61, - 61, 32, 51, 51, 49, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,115, -101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,111,109,109, - 97,110,100, 40, 34,112, 97,115,115, 34, 44, 32,112, 97,115,115,119,111,114,100, - 32,111,114, 32, 95, 77, 46, 80, 65, 83, 83, 87, 79, 82, 68, 41, 41, 10, 32, 32, - 32, 32, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46, -116,112, 58, 99,104,101, 99,107, 40, 34, 50, 46, 46, 34, 41, 41, 10, 32, 32, 32, - 32,101,110,100, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, 49, 10,101,110, -100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95, -105,110,100,101,120, 58,112, 97,115,118, 40, 41, 10, 32, 32, 32, 32,115,101,108, -102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,111,109,109, 97,110, -100, 40, 34,112, 97,115,118, 34, 41, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, - 32, 99,111,100,101, 44, 32,114,101,112,108,121, 32, 61, 32,115,101,108,102, 46, -116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,104,101, 99,107, 40, 34, 50, - 46, 46, 34, 41, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,112, 97,116,116, -101,114,110, 32, 61, 32, 34, 40, 37,100, 43, 41, 37, 68, 40, 37,100, 43, 41, 37, - 68, 40, 37,100, 43, 41, 37, 68, 40, 37,100, 43, 41, 37, 68, 40, 37,100, 43, 41, - 37, 68, 40, 37,100, 43, 41, 34, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 97, - 44, 32, 98, 44, 32, 99, 44, 32,100, 44, 32,112, 49, 44, 32,112, 50, 32, 61, 32, -115,111, 99,107,101,116, 46,115,107,105,112, 40, 50, 44, 32,115,116,114,105,110, -103, 46,102,105,110,100, 40,114,101,112,108,121, 44, 32,112, 97,116,116,101,114, -110, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40, 97, 32, 97, -110,100, 32, 98, 32, 97,110,100, 32, 99, 32, 97,110,100, 32,100, 32, 97,110,100, - 32,112, 49, 32, 97,110,100, 32,112, 50, 44, 32,114,101,112,108,121, 41, 10, 32, - 32, 32, 32,115,101,108,102, 46,112, 97,115,118,116, 32, 61, 32,123, 10, 32, 32, - 32, 32, 32, 32, 32, 32, 97,100,100,114,101,115,115, 32, 61, 32,115,116,114,105, -110,103, 46,102,111,114,109, 97,116, 40, 34, 37,100, 46, 37,100, 46, 37,100, 46, - 37,100, 34, 44, 32, 97, 44, 32, 98, 44, 32, 99, 44, 32,100, 41, 44, 10, 32, 32, - 32, 32, 32, 32, 32, 32,112,111,114,116, 32, 61, 32,112, 49, 42, 50, 53, 54, 32, - 43, 32,112, 50, 10, 32, 32, 32, 32,125, 10, 32, 32, 32, 32,105,102, 32,115,101, -108,102, 46,115,101,114,118,101,114, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, - 32, 32, 32,115,101,108,102, 46,115,101,114,118,101,114, 58, 99,108,111,115,101, - 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,115,101,108,102, 46,115,101,114,118, -101,114, 32, 61, 32,110,105,108, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, - 32,114,101,116,117,114,110, 32,115,101,108,102, 46,112, 97,115,118,116, 46, 97, -100,100,114,101,115,115, 44, 32,115,101,108,102, 46,112, 97,115,118,116, 46,112, -111,114,116, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101, -116, 97,116, 46, 95, 95,105,110,100,101,120, 58,101,112,115,118, 40, 41, 10, 32, - 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, - 99,111,109,109, 97,110,100, 40, 34,101,112,115,118, 34, 41, 41, 10, 32, 32, 32, - 32,108,111, 99, 97,108, 32, 99,111,100,101, 44, 32,114,101,112,108,121, 32, 61, - 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,104, -101, 99,107, 40, 34, 50, 50, 57, 34, 41, 41, 10, 32, 32, 32, 32,108,111, 99, 97, -108, 32,112, 97,116,116,101,114,110, 32, 61, 32, 34, 37, 40, 40, 46, 41, 40, 46, - 45, 41, 37, 49, 40, 46, 45, 41, 37, 49, 40, 46, 45, 41, 37, 49, 37, 41, 34, 10, - 32, 32, 32, 32,108,111, 99, 97,108, 32,100, 44, 32,112,114,116, 44, 32, 97,100, -100,114,101,115,115, 44, 32,112,111,114,116, 32, 61, 32,115,116,114,105,110,103, - 46,109, 97,116, 99,104, 40,114,101,112,108,121, 44, 32,112, 97,116,116,101,114, -110, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,112,111,114,116, - 44, 32, 34,105,110,118, 97,108,105,100, 32,101,112,115,118, 32,114,101,115,112, -111,110,115,101, 34, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,112, 97,115,118, -116, 32, 61, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 97,100,100,114,101,115, -115, 32, 61, 32,115,101,108,102, 46,116,112, 58,103,101,116,112,101,101,114,110, - 97,109,101, 40, 41, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32,112,111,114,116, 32, - 61, 32,112,111,114,116, 10, 32, 32, 32, 32,125, 10, 32, 32, 32, 32,105,102, 32, -115,101,108,102, 46,115,101,114,118,101,114, 32,116,104,101,110, 10, 32, 32, 32, - 32, 32, 32, 32, 32,115,101,108,102, 46,115,101,114,118,101,114, 58, 99,108,111, -115,101, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,115,101,108,102, 46,115,101, -114,118,101,114, 32, 61, 32,110,105,108, 10, 32, 32, 32, 32,101,110,100, 10, 32, - 32, 32, 32,114,101,116,117,114,110, 32,115,101,108,102, 46,112, 97,115,118,116, - 46, 97,100,100,114,101,115,115, 44, 32,115,101,108,102, 46,112, 97,115,118,116, - 46,112,111,114,116, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, - 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,112,111,114,116, 40, - 97,100,100,114,101,115,115, 44, 32,112,111,114,116, 41, 10, 32, 32, 32, 32,115, -101,108,102, 46,112, 97,115,118,116, 32, 61, 32,110,105,108, 10, 32, 32, 32, 32, -105,102, 32,110,111,116, 32, 97,100,100,114,101,115,115, 32,116,104,101,110, 10, - 32, 32, 32, 32, 32, 32, 32, 32, 97,100,100,114,101,115,115, 44, 32,112,111,114, -116, 32, 61, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, - 58,103,101,116,115,111, 99,107,110, 97,109,101, 40, 41, 41, 10, 32, 32, 32, 32, - 32, 32, 32, 32,115,101,108,102, 46,115,101,114,118,101,114, 32, 61, 32,115,101, -108,102, 46,116,114,121, 40,115,111, 99,107,101,116, 46, 98,105,110,100, 40, 97, -100,100,114,101,115,115, 44, 32, 48, 41, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 97,100,100,114,101,115,115, 44, 32,112,111,114,116, 32, 61, 32,115,101,108,102, - 46,116,114,121, 40,115,101,108,102, 46,115,101,114,118,101,114, 58,103,101,116, -115,111, 99,107,110, 97,109,101, 40, 41, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, -115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,115,101,114,118,101,114, - 58,115,101,116,116,105,109,101,111,117,116, 40, 95, 77, 46, 84, 73, 77, 69, 79, - 85, 84, 41, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,108,111, 99, - 97,108, 32,112,108, 32, 61, 32,109, 97,116,104, 46,109,111,100, 40,112,111,114, -116, 44, 32, 50, 53, 54, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,112,104, - 32, 61, 32, 40,112,111,114,116, 32, 45, 32,112,108, 41, 47, 50, 53, 54, 10, 32, - 32, 32, 32,108,111, 99, 97,108, 32, 97,114,103, 32, 61, 32,115,116,114,105,110, -103, 46,103,115,117, 98, 40,115,116,114,105,110,103, 46,102,111,114,109, 97,116, - 40, 34, 37,115, 44, 37,100, 44, 37,100, 34, 44, 32, 97,100,100,114,101,115,115, - 44, 32,112,104, 44, 32,112,108, 41, 44, 32, 34, 37, 46, 34, 44, 32, 34, 44, 34, - 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46, -116,112, 58, 99,111,109,109, 97,110,100, 40, 34,112,111,114,116, 34, 44, 32, 97, -114,103, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115,101, -108,102, 46,116,112, 58, 99,104,101, 99,107, 40, 34, 50, 46, 46, 34, 41, 41, 10, - 32, 32, 32, 32,114,101,116,117,114,110, 32, 49, 10,101,110,100, 10, 10,102,117, -110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, - 58,101,112,114,116, 40,102, 97,109,105,108,121, 44, 32, 97,100,100,114,101,115, -115, 44, 32,112,111,114,116, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,112, 97, -115,118,116, 32, 61, 32,110,105,108, 10, 32, 32, 32, 32,105,102, 32,110,111,116, - 32, 97,100,100,114,101,115,115, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, - 32, 32, 97,100,100,114,101,115,115, 44, 32,112,111,114,116, 32, 61, 32,115,101, -108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58,103,101,116,115,111, - 99,107,110, 97,109,101, 40, 41, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,115,101, -108,102, 46,115,101,114,118,101,114, 32, 61, 32,115,101,108,102, 46,116,114,121, - 40,115,111, 99,107,101,116, 46, 98,105,110,100, 40, 97,100,100,114,101,115,115, - 44, 32, 48, 41, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 97,100,100,114,101,115, -115, 44, 32,112,111,114,116, 32, 61, 32,115,101,108,102, 46,116,114,121, 40,115, -101,108,102, 46,115,101,114,118,101,114, 58,103,101,116,115,111, 99,107,110, 97, -109,101, 40, 41, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,115,101,108,102, 46,116, -114,121, 40,115,101,108,102, 46,115,101,114,118,101,114, 58,115,101,116,116,105, -109,101,111,117,116, 40, 95, 77, 46, 84, 73, 77, 69, 79, 85, 84, 41, 41, 10, 32, - 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 97,114,103, - 32, 61, 32,115,116,114,105,110,103, 46,102,111,114,109, 97,116, 40, 34,124, 37, -115,124, 37,115,124, 37,100,124, 34, 44, 32,102, 97,109,105,108,121, 44, 32, 97, -100,100,114,101,115,115, 44, 32,112,111,114,116, 41, 10, 32, 32, 32, 32,115,101, -108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,111,109,109, 97, -110,100, 40, 34,101,112,114,116, 34, 44, 32, 97,114,103, 41, 41, 10, 32, 32, 32, - 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,104, -101, 99,107, 40, 34, 50, 46, 46, 34, 41, 41, 10, 32, 32, 32, 32,114,101,116,117, -114,110, 32, 49, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, -109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,115,101,110,100, 40,115, -101,110,100,116, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115, -101,108,102, 46,112, 97,115,118,116, 32,111,114, 32,115,101,108,102, 46,115,101, -114,118,101,114, 44, 32, 34,110,101,101,100, 32,112,111,114,116, 32,111,114, 32, -112, 97,115,118, 32,102,105,114,115,116, 34, 41, 10, 32, 32, 32, 32, 45, 45, 32, -105,102, 32,116,104,101,114,101, 32,105,115, 32, 97, 32,112, 97,115,118,116, 32, -116, 97, 98,108,101, 44, 32,119,101, 32, 97,108,114,101, 97,100,121, 32,115,101, -110,116, 32, 97, 32, 80, 65, 83, 86, 32, 99,111,109,109, 97,110,100, 10, 32, 32, - 32, 32, 45, 45, 32,119,101, 32,106,117,115,116, 32,103,101,116, 32,116,104,101, - 32,100, 97,116, 97, 32, 99,111,110,110,101, 99,116,105,111,110, 32,105,110,116, -111, 32,115,101,108,102, 46,100, 97,116, 97, 10, 32, 32, 32, 32,105,102, 32,115, -101,108,102, 46,112, 97,115,118,116, 32,116,104,101,110, 32,115,101,108,102, 58, -112, 97,115,118, 99,111,110,110,101, 99,116, 40, 41, 32,101,110,100, 10, 32, 32, - 32, 32, 45, 45, 32,103,101,116, 32,116,104,101, 32,116,114, 97,110,115,102,101, -114, 32, 97,114,103,117,109,101,110,116, 32, 97,110,100, 32, 99,111,109,109, 97, -110,100, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 97,114,103,117,109,101,110, -116, 32, 61, 32,115,101,110,100,116, 46, 97,114,103,117,109,101,110,116, 32,111, -114, 10, 32, 32, 32, 32, 32, 32, 32, 32,117,114,108, 46,117,110,101,115, 99, 97, -112,101, 40,115,116,114,105,110,103, 46,103,115,117, 98, 40,115,101,110,100,116, - 46,112, 97,116,104, 32,111,114, 32, 34, 34, 44, 32, 34, 94, 91, 47, 92, 92, 93, - 34, 44, 32, 34, 34, 41, 41, 10, 32, 32, 32, 32,105,102, 32, 97,114,103,117,109, -101,110,116, 32, 61, 61, 32, 34, 34, 32,116,104,101,110, 32, 97,114,103,117,109, -101,110,116, 32, 61, 32,110,105,108, 32,101,110,100, 10, 32, 32, 32, 32,108,111, - 99, 97,108, 32, 99,111,109,109, 97,110,100, 32, 61, 32,115,101,110,100,116, 46, - 99,111,109,109, 97,110,100, 32,111,114, 32, 34,115,116,111,114, 34, 10, 32, 32, - 32, 32, 45, 45, 32,115,101,110,100, 32,116,104,101, 32,116,114, 97,110,115,102, -101,114, 32, 99,111,109,109, 97,110,100, 32, 97,110,100, 32, 99,104,101, 99,107, - 32,116,104,101, 32,114,101,112,108,121, 10, 32, 32, 32, 32,115,101,108,102, 46, -116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,111,109,109, 97,110,100, 40, - 99,111,109,109, 97,110,100, 44, 32, 97,114,103,117,109,101,110,116, 41, 41, 10, - 32, 32, 32, 32,108,111, 99, 97,108, 32, 99,111,100,101, 44, 32,114,101,112,108, -121, 32, 61, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, - 58, 99,104,101, 99,107,123, 34, 50, 46, 46, 34, 44, 32, 34, 49, 46, 46, 34,125, - 41, 10, 32, 32, 32, 32, 45, 45, 32,105,102, 32,116,104,101,114,101, 32,105,115, - 32,110,111,116, 32, 97, 32,112, 97,115,118,116, 32,116, 97, 98,108,101, 44, 32, -116,104,101,110, 32,116,104,101,114,101, 32,105,115, 32, 97, 32,115,101,114,118, -101,114, 10, 32, 32, 32, 32, 45, 45, 32, 97,110,100, 32,119,101, 32, 97,108,114, -101, 97,100,121, 32,115,101,110,116, 32, 97, 32, 80, 79, 82, 84, 32, 99,111,109, -109, 97,110,100, 10, 32, 32, 32, 32,105,102, 32,110,111,116, 32,115,101,108,102, - 46,112, 97,115,118,116, 32,116,104,101,110, 32,115,101,108,102, 58,112,111,114, -116, 99,111,110,110,101, 99,116, 40, 41, 32,101,110,100, 10, 32, 32, 32, 32, 45, - 45, 32,103,101,116, 32,116,104,101, 32,115,105,110,107, 44, 32,115,111,117,114, - 99,101, 32, 97,110,100, 32,115,116,101,112, 32,102,111,114, 32,116,104,101, 32, -116,114, 97,110,115,102,101,114, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,115, -116,101,112, 32, 61, 32,115,101,110,100,116, 46,115,116,101,112, 32,111,114, 32, -108,116,110, 49, 50, 46,112,117,109,112, 46,115,116,101,112, 10, 32, 32, 32, 32, -108,111, 99, 97,108, 32,114,101, 97,100,116, 32, 61, 32,123, 32,115,101,108,102, - 46,116,112, 32,125, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 99,104,101, 99, -107,115,116,101,112, 32, 61, 32,102,117,110, 99,116,105,111,110, 40,115,114, 99, - 44, 32,115,110,107, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 32, 99,104, -101, 99,107, 32,115,116, 97,116,117,115, 32,105,110, 32, 99,111,110,116,114,111, -108, 32, 99,111,110,110,101, 99,116,105,111,110, 32,119,104,105,108,101, 32,100, -111,119,110,108,111, 97,100,105,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32,108, -111, 99, 97,108, 32,114,101, 97,100,121,116, 32, 61, 32,115,111, 99,107,101,116, - 46,115,101,108,101, 99,116, 40,114,101, 97,100,116, 44, 32,110,105,108, 44, 32, - 48, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,114,101, 97,100,121,116, - 91,116,112, 93, 32,116,104,101,110, 32, 99,111,100,101, 32, 61, 32,115,101,108, -102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,104,101, 99,107, 40, - 34, 50, 46, 46, 34, 41, 41, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, -114,101,116,117,114,110, 32,115,116,101,112, 40,115,114, 99, 44, 32,115,110,107, - 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, -115,105,110,107, 32, 61, 32,115,111, 99,107,101,116, 46,115,105,110,107, 40, 34, - 99,108,111,115,101, 45,119,104,101,110, 45,100,111,110,101, 34, 44, 32,115,101, -108,102, 46,100, 97,116, 97, 41, 10, 32, 32, 32, 32, 45, 45, 32,116,114, 97,110, -115,102,101,114, 32, 97,108,108, 32,100, 97,116, 97, 32, 97,110,100, 32, 99,104, -101, 99,107, 32,101,114,114,111,114, 10, 32, 32, 32, 32,115,101,108,102, 46,116, -114,121, 40,108,116,110, 49, 50, 46,112,117,109,112, 46, 97,108,108, 40,115,101, -110,100,116, 46,115,111,117,114, 99,101, 44, 32,115,105,110,107, 44, 32, 99,104, -101, 99,107,115,116,101,112, 41, 41, 10, 32, 32, 32, 32,105,102, 32,115,116,114, -105,110,103, 46,102,105,110,100, 40, 99,111,100,101, 44, 32, 34, 49, 46, 46, 34, - 41, 32,116,104,101,110, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, - 46,116,112, 58, 99,104,101, 99,107, 40, 34, 50, 46, 46, 34, 41, 41, 32,101,110, -100, 10, 32, 32, 32, 32, 45, 45, 32,100,111,110,101, 32,119,105,116,104, 32,100, - 97,116, 97, 32, 99,111,110,110,101, 99,116,105,111,110, 10, 32, 32, 32, 32,115, -101,108,102, 46,100, 97,116, 97, 58, 99,108,111,115,101, 40, 41, 10, 32, 32, 32, - 32, 45, 45, 32,102,105,110,100, 32,111,117,116, 32,104,111,119, 32,109, 97,110, -121, 32, 98,121,116,101,115, 32,119,101,114,101, 32,115,101,110,116, 10, 32, 32, - 32, 32,108,111, 99, 97,108, 32,115,101,110,116, 32, 61, 32,115,111, 99,107,101, -116, 46,115,107,105,112, 40, 49, 44, 32,115,101,108,102, 46,100, 97,116, 97, 58, -103,101,116,115,116, 97,116,115, 40, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, - 46,100, 97,116, 97, 32, 61, 32,110,105,108, 10, 32, 32, 32, 32,114,101,116,117, -114,110, 32,115,101,110,116, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111, -110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,114,101, 99,101, -105,118,101, 40,114,101, 99,118,116, 41, 10, 32, 32, 32, 32,115,101,108,102, 46, -116,114,121, 40,115,101,108,102, 46,112, 97,115,118,116, 32,111,114, 32,115,101, -108,102, 46,115,101,114,118,101,114, 44, 32, 34,110,101,101,100, 32,112,111,114, -116, 32,111,114, 32,112, 97,115,118, 32,102,105,114,115,116, 34, 41, 10, 32, 32, - 32, 32,105,102, 32,115,101,108,102, 46,112, 97,115,118,116, 32,116,104,101,110, - 32,115,101,108,102, 58,112, 97,115,118, 99,111,110,110,101, 99,116, 40, 41, 32, -101,110,100, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 97,114,103,117,109,101, -110,116, 32, 61, 32,114,101, 99,118,116, 46, 97,114,103,117,109,101,110,116, 32, -111,114, 10, 32, 32, 32, 32, 32, 32, 32, 32,117,114,108, 46,117,110,101,115, 99, - 97,112,101, 40,115,116,114,105,110,103, 46,103,115,117, 98, 40,114,101, 99,118, -116, 46,112, 97,116,104, 32,111,114, 32, 34, 34, 44, 32, 34, 94, 91, 47, 92, 92, - 93, 34, 44, 32, 34, 34, 41, 41, 10, 32, 32, 32, 32,105,102, 32, 97,114,103,117, -109,101,110,116, 32, 61, 61, 32, 34, 34, 32,116,104,101,110, 32, 97,114,103,117, -109,101,110,116, 32, 61, 32,110,105,108, 32,101,110,100, 10, 32, 32, 32, 32,108, -111, 99, 97,108, 32, 99,111,109,109, 97,110,100, 32, 61, 32,114,101, 99,118,116, - 46, 99,111,109,109, 97,110,100, 32,111,114, 32, 34,114,101,116,114, 34, 10, 32, - 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, - 99,111,109,109, 97,110,100, 40, 99,111,109,109, 97,110,100, 44, 32, 97,114,103, -117,109,101,110,116, 41, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 99,111, -100,101, 44,114,101,112,108,121, 32, 61, 32,115,101,108,102, 46,116,114,121, 40, -115,101,108,102, 46,116,112, 58, 99,104,101, 99,107,123, 34, 49, 46, 46, 34, 44, - 32, 34, 50, 46, 46, 34,125, 41, 10, 32, 32, 32, 32,105,102, 32, 40, 99,111,100, -101, 32, 62, 61, 32, 50, 48, 48, 41, 32, 97,110,100, 32, 40, 99,111,100,101, 32, - 60, 61, 32, 50, 57, 57, 41, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, - 32,114,101, 99,118,116, 46,115,105,110,107, 40,114,101,112,108,121, 41, 10, 32, - 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32, 49, 10, 32, 32, 32, 32, -101,110,100, 10, 32, 32, 32, 32,105,102, 32,110,111,116, 32,115,101,108,102, 46, -112, 97,115,118,116, 32,116,104,101,110, 32,115,101,108,102, 58,112,111,114,116, - 99,111,110,110,101, 99,116, 40, 41, 32,101,110,100, 10, 32, 32, 32, 32,108,111, - 99, 97,108, 32,115,111,117,114, 99,101, 32, 61, 32,115,111, 99,107,101,116, 46, -115,111,117,114, 99,101, 40, 34,117,110,116,105,108, 45, 99,108,111,115,101,100, - 34, 44, 32,115,101,108,102, 46,100, 97,116, 97, 41, 10, 32, 32, 32, 32,108,111, - 99, 97,108, 32,115,116,101,112, 32, 61, 32,114,101, 99,118,116, 46,115,116,101, -112, 32,111,114, 32,108,116,110, 49, 50, 46,112,117,109,112, 46,115,116,101,112, - 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,108,116,110, 49, 50, 46, -112,117,109,112, 46, 97,108,108, 40,115,111,117,114, 99,101, 44, 32,114,101, 99, -118,116, 46,115,105,110,107, 44, 32,115,116,101,112, 41, 41, 10, 32, 32, 32, 32, -105,102, 32,115,116,114,105,110,103, 46,102,105,110,100, 40, 99,111,100,101, 44, - 32, 34, 49, 46, 46, 34, 41, 32,116,104,101,110, 32,115,101,108,102, 46,116,114, -121, 40,115,101,108,102, 46,116,112, 58, 99,104,101, 99,107, 40, 34, 50, 46, 46, - 34, 41, 41, 32,101,110,100, 10, 32, 32, 32, 32,115,101,108,102, 46,100, 97,116, - 97, 58, 99,108,111,115,101, 40, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,100, - 97,116, 97, 32, 61, 32,110,105,108, 10, 32, 32, 32, 32,114,101,116,117,114,110, - 32, 49, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, - 97,116, 46, 95, 95,105,110,100,101,120, 58, 99,119,100, 40,100,105,114, 41, 10, - 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, - 58, 99,111,109,109, 97,110,100, 40, 34, 99,119,100, 34, 44, 32,100,105,114, 41, - 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46, -116,112, 58, 99,104,101, 99,107, 40, 50, 53, 48, 41, 41, 10, 32, 32, 32, 32,114, -101,116,117,114,110, 32, 49, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111, -110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,116,121,112,101, - 40,116,121,112,101, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40, -115,101,108,102, 46,116,112, 58, 99,111,109,109, 97,110,100, 40, 34,116,121,112, -101, 34, 44, 32,116,121,112,101, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, 46, -116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,104,101, 99,107, 40, 50, 48, - 48, 41, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, 49, 10,101,110,100, - 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105, -110,100,101,120, 58,103,114,101,101,116, 40, 41, 10, 32, 32, 32, 32,108,111, 99, - 97,108, 32, 99,111,100,101, 32, 61, 32,115,101,108,102, 46,116,114,121, 40,115, -101,108,102, 46,116,112, 58, 99,104,101, 99,107,123, 34, 49, 46, 46, 34, 44, 32, - 34, 50, 46, 46, 34,125, 41, 10, 32, 32, 32, 32,105,102, 32,115,116,114,105,110, -103, 46,102,105,110,100, 40, 99,111,100,101, 44, 32, 34, 49, 46, 46, 34, 41, 32, -116,104,101,110, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116, -112, 58, 99,104,101, 99,107, 40, 34, 50, 46, 46, 34, 41, 41, 32,101,110,100, 10, - 32, 32, 32, 32,114,101,116,117,114,110, 32, 49, 10,101,110,100, 10, 10,102,117, -110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, - 58,113,117,105,116, 40, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, - 40,115,101,108,102, 46,116,112, 58, 99,111,109,109, 97,110,100, 40, 34,113,117, -105,116, 34, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115, -101,108,102, 46,116,112, 58, 99,104,101, 99,107, 40, 34, 50, 46, 46, 34, 41, 41, - 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, 49, 10,101,110,100, 10, 10,102, -117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101, -120, 58, 99,108,111,115,101, 40, 41, 10, 32, 32, 32, 32,105,102, 32,115,101,108, -102, 46,100, 97,116, 97, 32,116,104,101,110, 32,115,101,108,102, 46,100, 97,116, - 97, 58, 99,108,111,115,101, 40, 41, 32,101,110,100, 10, 32, 32, 32, 32,105,102, - 32,115,101,108,102, 46,115,101,114,118,101,114, 32,116,104,101,110, 32,115,101, -108,102, 46,115,101,114,118,101,114, 58, 99,108,111,115,101, 40, 41, 32,101,110, -100, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,101,108,102, 46,116,112, - 58, 99,108,111,115,101, 40, 41, 10,101,110,100, 10, 10, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 72,105,103,104, 32, -108,101,118,101,108, 32, 70, 84, 80, 32, 65, 80, 73, 10, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10,108,111, 99, 97,108, 32,102,117, -110, 99,116,105,111,110, 32,111,118,101,114,114,105,100,101, 40,116, 41, 10, 32, - 32, 32, 32,105,102, 32,116, 46,117,114,108, 32,116,104,101,110, 10, 32, 32, 32, - 32, 32, 32, 32, 32,108,111, 99, 97,108, 32,117, 32, 61, 32,117,114,108, 46,112, - 97,114,115,101, 40,116, 46,117,114,108, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, -102,111,114, 32,105, 44,118, 32,105,110, 32, 98, 97,115,101, 46,112, 97,105,114, -115, 40,116, 41, 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -117, 91,105, 93, 32, 61, 32,118, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, - 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32,117, 10, 32, 32, - 32, 32,101,108,115,101, 32,114,101,116,117,114,110, 32,116, 32,101,110,100, 10, -101,110,100, 10, 10,108,111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32, -116,112,117,116, 40,112,117,116,116, 41, 10, 32, 32, 32, 32,112,117,116,116, 32, - 61, 32,111,118,101,114,114,105,100,101, 40,112,117,116,116, 41, 10, 32, 32, 32, - 32,115,111, 99,107,101,116, 46,116,114,121, 40,112,117,116,116, 46,104,111,115, -116, 44, 32, 34,109,105,115,115,105,110,103, 32,104,111,115,116,110, 97,109,101, - 34, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,102, 32, 61, 32, 95, 77, 46, -111,112,101,110, 40,112,117,116,116, 46,104,111,115,116, 44, 32,112,117,116,116, - 46,112,111,114,116, 44, 32,112,117,116,116, 46, 99,114,101, 97,116,101, 41, 10, - 32, 32, 32, 32,102, 58,103,114,101,101,116, 40, 41, 10, 32, 32, 32, 32,102, 58, -108,111,103,105,110, 40,112,117,116,116, 46,117,115,101,114, 44, 32,112,117,116, -116, 46,112, 97,115,115,119,111,114,100, 41, 10, 32, 32, 32, 32,105,102, 32,112, -117,116,116, 46,116,121,112,101, 32,116,104,101,110, 32,102, 58,116,121,112,101, - 40,112,117,116,116, 46,116,121,112,101, 41, 32,101,110,100, 10, 32, 32, 32, 32, -102, 58,101,112,115,118, 40, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,115, -101,110,116, 32, 61, 32,102, 58,115,101,110,100, 40,112,117,116,116, 41, 10, 32, - 32, 32, 32,102, 58,113,117,105,116, 40, 41, 10, 32, 32, 32, 32,102, 58, 99,108, -111,115,101, 40, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,101,110, -116, 10,101,110,100, 10, 10,108,111, 99, 97,108, 32,100,101,102, 97,117,108,116, - 32, 61, 32,123, 10, 32, 32, 32, 32,112, 97,116,104, 32, 61, 32, 34, 47, 34, 44, - 10, 32, 32, 32, 32,115, 99,104,101,109,101, 32, 61, 32, 34,102,116,112, 34, 10, -125, 10, 10,108,111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32,103,101, -110,101,114,105, 99,102,111,114,109, 40,117, 41, 10, 32, 32, 32, 32,108,111, 99, - 97,108, 32,116, 32, 61, 32,115,111, 99,107,101,116, 46,116,114,121, 40,117,114, -108, 46,112, 97,114,115,101, 40,117, 44, 32,100,101,102, 97,117,108,116, 41, 41, - 10, 32, 32, 32, 32,115,111, 99,107,101,116, 46,116,114,121, 40,116, 46,115, 99, -104,101,109,101, 32, 61, 61, 32, 34,102,116,112, 34, 44, 32, 34,119,114,111,110, -103, 32,115, 99,104,101,109,101, 32, 39, 34, 32, 46, 46, 32,116, 46,115, 99,104, -101,109,101, 32, 46, 46, 32, 34, 39, 34, 41, 10, 32, 32, 32, 32,115,111, 99,107, -101,116, 46,116,114,121, 40,116, 46,104,111,115,116, 44, 32, 34,109,105,115,115, -105,110,103, 32,104,111,115,116,110, 97,109,101, 34, 41, 10, 32, 32, 32, 32,108, -111, 99, 97,108, 32,112, 97,116, 32, 61, 32, 34, 94,116,121,112,101, 61, 40, 46, - 41, 36, 34, 10, 32, 32, 32, 32,105,102, 32,116, 46,112, 97,114, 97,109,115, 32, -116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,116, 46,116,121,112,101, 32, - 61, 32,115,111, 99,107,101,116, 46,115,107,105,112, 40, 50, 44, 32,115,116,114, -105,110,103, 46,102,105,110,100, 40,116, 46,112, 97,114, 97,109,115, 44, 32,112, - 97,116, 41, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,115,111, 99,107,101,116, 46, -116,114,121, 40,116, 46,116,121,112,101, 32, 61, 61, 32, 34, 97, 34, 32,111,114, - 32,116, 46,116,121,112,101, 32, 61, 61, 32, 34,105, 34, 44, 10, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 34,105,110,118, 97,108,105,100, 32,116,121,112, -101, 32, 39, 34, 32, 46, 46, 32,116, 46,116,121,112,101, 32, 46, 46, 32, 34, 39, - 34, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,114,101,116,117,114, -110, 32,116, 10,101,110,100, 10, 10, 95, 77, 46,103,101,110,101,114,105, 99,102, -111,114,109, 32, 61, 32,103,101,110,101,114,105, 99,102,111,114,109, 10, 10,108, -111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32,115,112,117,116, 40,117, - 44, 32, 98,111,100,121, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,112,117, -116,116, 32, 61, 32,103,101,110,101,114,105, 99,102,111,114,109, 40,117, 41, 10, - 32, 32, 32, 32,112,117,116,116, 46,115,111,117,114, 99,101, 32, 61, 32,108,116, -110, 49, 50, 46,115,111,117,114, 99,101, 46,115,116,114,105,110,103, 40, 98,111, -100,121, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,116,112,117,116, 40, -112,117,116,116, 41, 10,101,110,100, 10, 10, 95, 77, 46,112,117,116, 32, 61, 32, -115,111, 99,107,101,116, 46,112,114,111,116,101, 99,116, 40,102,117,110, 99,116, -105,111,110, 40,112,117,116,116, 44, 32, 98,111,100,121, 41, 10, 32, 32, 32, 32, -105,102, 32, 98, 97,115,101, 46,116,121,112,101, 40,112,117,116,116, 41, 32, 61, - 61, 32, 34,115,116,114,105,110,103, 34, 32,116,104,101,110, 32,114,101,116,117, -114,110, 32,115,112,117,116, 40,112,117,116,116, 44, 32, 98,111,100,121, 41, 10, - 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114,110, 32,116,112,117,116, - 40,112,117,116,116, 41, 32,101,110,100, 10,101,110,100, 41, 10, 10,108,111, 99, - 97,108, 32,102,117,110, 99,116,105,111,110, 32,116,103,101,116, 40,103,101,116, -116, 41, 10, 32, 32, 32, 32,103,101,116,116, 32, 61, 32,111,118,101,114,114,105, -100,101, 40,103,101,116,116, 41, 10, 32, 32, 32, 32,115,111, 99,107,101,116, 46, -116,114,121, 40,103,101,116,116, 46,104,111,115,116, 44, 32, 34,109,105,115,115, -105,110,103, 32,104,111,115,116,110, 97,109,101, 34, 41, 10, 32, 32, 32, 32,108, -111, 99, 97,108, 32,102, 32, 61, 32, 95, 77, 46,111,112,101,110, 40,103,101,116, -116, 46,104,111,115,116, 44, 32,103,101,116,116, 46,112,111,114,116, 44, 32,103, -101,116,116, 46, 99,114,101, 97,116,101, 41, 10, 32, 32, 32, 32,102, 58,103,114, -101,101,116, 40, 41, 10, 32, 32, 32, 32,102, 58,108,111,103,105,110, 40,103,101, -116,116, 46,117,115,101,114, 44, 32,103,101,116,116, 46,112, 97,115,115,119,111, -114,100, 41, 10, 32, 32, 32, 32,105,102, 32,103,101,116,116, 46,116,121,112,101, - 32,116,104,101,110, 32,102, 58,116,121,112,101, 40,103,101,116,116, 46,116,121, -112,101, 41, 32,101,110,100, 10, 32, 32, 32, 32,102, 58,101,112,115,118, 40, 41, - 10, 32, 32, 32, 32,102, 58,114,101, 99,101,105,118,101, 40,103,101,116,116, 41, - 10, 32, 32, 32, 32,102, 58,113,117,105,116, 40, 41, 10, 32, 32, 32, 32,114,101, -116,117,114,110, 32,102, 58, 99,108,111,115,101, 40, 41, 10,101,110,100, 10, 10, -108,111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32,115,103,101,116, 40, -117, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,103,101,116,116, 32, 61, 32, -103,101,110,101,114,105, 99,102,111,114,109, 40,117, 41, 10, 32, 32, 32, 32,108, -111, 99, 97,108, 32,116, 32, 61, 32,123,125, 10, 32, 32, 32, 32,103,101,116,116, - 46,115,105,110,107, 32, 61, 32,108,116,110, 49, 50, 46,115,105,110,107, 46,116, - 97, 98,108,101, 40,116, 41, 10, 32, 32, 32, 32,116,103,101,116, 40,103,101,116, -116, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,116, 97, 98,108,101, 46, - 99,111,110, 99, 97,116, 40,116, 41, 10,101,110,100, 10, 10, 95, 77, 46, 99,111, -109,109, 97,110,100, 32, 61, 32,115,111, 99,107,101,116, 46,112,114,111,116,101, - 99,116, 40,102,117,110, 99,116,105,111,110, 40, 99,109,100,116, 41, 10, 32, 32, - 32, 32, 99,109,100,116, 32, 61, 32,111,118,101,114,114,105,100,101, 40, 99,109, -100,116, 41, 10, 32, 32, 32, 32,115,111, 99,107,101,116, 46,116,114,121, 40, 99, -109,100,116, 46,104,111,115,116, 44, 32, 34,109,105,115,115,105,110,103, 32,104, -111,115,116,110, 97,109,101, 34, 41, 10, 32, 32, 32, 32,115,111, 99,107,101,116, - 46,116,114,121, 40, 99,109,100,116, 46, 99,111,109,109, 97,110,100, 44, 32, 34, -109,105,115,115,105,110,103, 32, 99,111,109,109, 97,110,100, 34, 41, 10, 32, 32, - 32, 32,108,111, 99, 97,108, 32,102, 32, 61, 32, 95, 77, 46,111,112,101,110, 40, - 99,109,100,116, 46,104,111,115,116, 44, 32, 99,109,100,116, 46,112,111,114,116, - 44, 32, 99,109,100,116, 46, 99,114,101, 97,116,101, 41, 10, 32, 32, 32, 32,102, - 58,103,114,101,101,116, 40, 41, 10, 32, 32, 32, 32,102, 58,108,111,103,105,110, - 40, 99,109,100,116, 46,117,115,101,114, 44, 32, 99,109,100,116, 46,112, 97,115, -115,119,111,114,100, 41, 10, 32, 32, 32, 32,105,102, 32,116,121,112,101, 40, 99, -109,100,116, 46, 99,111,109,109, 97,110,100, 41, 32, 61, 61, 32, 34,116, 97, 98, -108,101, 34, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, - 97,108, 32, 97,114,103,117,109,101,110,116, 32, 61, 32, 99,109,100,116, 46, 97, -114,103,117,109,101,110,116, 32,111,114, 32,123,125, 10, 32, 32, 32, 32, 32, 32, - 32, 32,108,111, 99, 97,108, 32, 99,104,101, 99,107, 32, 61, 32, 99,109,100,116, - 46, 99,104,101, 99,107, 32,111,114, 32,123,125, 10, 32, 32, 32, 32, 32, 32, 32, - 32,102,111,114, 32,105, 44, 99,109,100, 32,105,110, 32,105,112, 97,105,114,115, - 40, 99,109,100,116, 46, 99,111,109,109, 97,110,100, 41, 32,100,111, 10, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,102, 46,116,114,121, 40,102, 46,116,112, - 58, 99,111,109,109, 97,110,100, 40, 99,109,100, 44, 32, 97,114,103,117,109,101, -110,116, 91,105, 93, 41, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -105,102, 32, 99,104,101, 99,107, 91,105, 93, 32,116,104,101,110, 32,102, 46,116, -114,121, 40,102, 46,116,112, 58, 99,104,101, 99,107, 40, 99,104,101, 99,107, 91, -105, 93, 41, 41, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, - 10, 32, 32, 32, 32,101,108,115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32,102, 46, -116,114,121, 40,102, 46,116,112, 58, 99,111,109,109, 97,110,100, 40, 99,109,100, -116, 46, 99,111,109,109, 97,110,100, 44, 32, 99,109,100,116, 46, 97,114,103,117, -109,101,110,116, 41, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, 99,109, -100,116, 46, 99,104,101, 99,107, 32,116,104,101,110, 32,102, 46,116,114,121, 40, -102, 46,116,112, 58, 99,104,101, 99,107, 40, 99,109,100,116, 46, 99,104,101, 99, -107, 41, 41, 32,101,110,100, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, -102, 58,113,117,105,116, 40, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, -102, 58, 99,108,111,115,101, 40, 41, 10,101,110,100, 41, 10, 10, 95, 77, 46,103, -101,116, 32, 61, 32,115,111, 99,107,101,116, 46,112,114,111,116,101, 99,116, 40, -102,117,110, 99,116,105,111,110, 40,103,101,116,116, 41, 10, 32, 32, 32, 32,105, -102, 32, 98, 97,115,101, 46,116,121,112,101, 40,103,101,116,116, 41, 32, 61, 61, - 32, 34,115,116,114,105,110,103, 34, 32,116,104,101,110, 32,114,101,116,117,114, -110, 32,115,103,101,116, 40,103,101,116,116, 41, 10, 32, 32, 32, 32,101,108,115, -101, 32,114,101,116,117,114,110, 32,116,103,101,116, 40,103,101,116,116, 41, 32, -101,110,100, 10,101,110,100, 41, 10, 10,114,101,116,117,114,110, 32, 95, 77, 10, - -}; - - if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"ftp.lua")==0) lua_call(L, 0, LUA_MULTRET); -} diff --git a/libraries/luasocket/libluasocket/headers.lua b/libraries/luasocket/libluasocket/headers.lua index 1eb8223b9..05818703f 100644 --- a/libraries/luasocket/libluasocket/headers.lua +++ b/libraries/luasocket/libluasocket/headers.lua @@ -1,3 +1,4 @@ +R"luastring"--( ----------------------------------------------------------------------------- -- Canonic header field capitalization -- LuaSocket toolkit. @@ -101,4 +102,6 @@ _M.canonic = { ["x-mailer"] = "X-Mailer", } -return _M \ No newline at end of file +return _M +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/headers.lua.h b/libraries/luasocket/libluasocket/headers.lua.h deleted file mode 100644 index 0d8d891a2..000000000 --- a/libraries/luasocket/libluasocket/headers.lua.h +++ /dev/null @@ -1,196 +0,0 @@ -/* code automatically generated by bin2c -- DO NOT EDIT */ -{ -/* #include'ing this file in a C program is equivalent to calling - if (luaL_loadfile(L,"headers.lua")==0) lua_call(L, 0, LUA_MULTRET); -*/ -/* headers.lua */ -static const unsigned char B1[]={ - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, - 32, 67, 97,110,111,110,105, 99, 32,104,101, 97,100,101,114, 32,102,105,101,108, -100, 32, 99, 97,112,105,116, 97,108,105,122, 97,116,105,111,110, 10, 45, 45, 32, - 76,117, 97, 83,111, 99,107,101,116, 32,116,111,111,108,107,105,116, 46, 10, 45, - 45, 32, 65,117,116,104,111,114, 58, 32, 68,105,101,103,111, 32, 78,101,104, 97, - 98, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, -108,111, 99, 97,108, 32,115,111, 99,107,101,116, 32, 61, 32,114,101,113,117,105, -114,101, 40, 34,115,111, 99,107,101,116, 34, 41, 10,115,111, 99,107,101,116, 46, -104,101, 97,100,101,114,115, 32, 61, 32,123,125, 10,108,111, 99, 97,108, 32, 95, - 77, 32, 61, 32,115,111, 99,107,101,116, 46,104,101, 97,100,101,114,115, 10, 10, - 95, 77, 46, 99, 97,110,111,110,105, 99, 32, 61, 32,123, 10, 32, 32, 32, 32, 91, - 34, 97, 99, 99,101,112,116, 34, 93, 32, 61, 32, 34, 65, 99, 99,101,112,116, 34, - 44, 10, 32, 32, 32, 32, 91, 34, 97, 99, 99,101,112,116, 45, 99,104, 97,114,115, -101,116, 34, 93, 32, 61, 32, 34, 65, 99, 99,101,112,116, 45, 67,104, 97,114,115, -101,116, 34, 44, 10, 32, 32, 32, 32, 91, 34, 97, 99, 99,101,112,116, 45,101,110, - 99,111,100,105,110,103, 34, 93, 32, 61, 32, 34, 65, 99, 99,101,112,116, 45, 69, -110, 99,111,100,105,110,103, 34, 44, 10, 32, 32, 32, 32, 91, 34, 97, 99, 99,101, -112,116, 45,108, 97,110,103,117, 97,103,101, 34, 93, 32, 61, 32, 34, 65, 99, 99, -101,112,116, 45, 76, 97,110,103,117, 97,103,101, 34, 44, 10, 32, 32, 32, 32, 91, - 34, 97, 99, 99,101,112,116, 45,114, 97,110,103,101,115, 34, 93, 32, 61, 32, 34, - 65, 99, 99,101,112,116, 45, 82, 97,110,103,101,115, 34, 44, 10, 32, 32, 32, 32, - 91, 34, 97, 99,116,105,111,110, 34, 93, 32, 61, 32, 34, 65, 99,116,105,111,110, - 34, 44, 10, 32, 32, 32, 32, 91, 34, 97,108,116,101,114,110, 97,116,101, 45,114, -101, 99,105,112,105,101,110,116, 34, 93, 32, 61, 32, 34, 65,108,116,101,114,110, - 97,116,101, 45, 82,101, 99,105,112,105,101,110,116, 34, 44, 10, 32, 32, 32, 32, - 91, 34, 97,103,101, 34, 93, 32, 61, 32, 34, 65,103,101, 34, 44, 10, 32, 32, 32, - 32, 91, 34, 97,108,108,111,119, 34, 93, 32, 61, 32, 34, 65,108,108,111,119, 34, - 44, 10, 32, 32, 32, 32, 91, 34, 97,114,114,105,118, 97,108, 45,100, 97,116,101, - 34, 93, 32, 61, 32, 34, 65,114,114,105,118, 97,108, 45, 68, 97,116,101, 34, 44, - 10, 32, 32, 32, 32, 91, 34, 97,117,116,104,111,114,105,122, 97,116,105,111,110, - 34, 93, 32, 61, 32, 34, 65,117,116,104,111,114,105,122, 97,116,105,111,110, 34, - 44, 10, 32, 32, 32, 32, 91, 34, 98, 99, 99, 34, 93, 32, 61, 32, 34, 66, 99, 99, - 34, 44, 10, 32, 32, 32, 32, 91, 34, 99, 97, 99,104,101, 45, 99,111,110,116,114, -111,108, 34, 93, 32, 61, 32, 34, 67, 97, 99,104,101, 45, 67,111,110,116,114,111, -108, 34, 44, 10, 32, 32, 32, 32, 91, 34, 99, 99, 34, 93, 32, 61, 32, 34, 67, 99, - 34, 44, 10, 32, 32, 32, 32, 91, 34, 99,111,109,109,101,110,116,115, 34, 93, 32, - 61, 32, 34, 67,111,109,109,101,110,116,115, 34, 44, 10, 32, 32, 32, 32, 91, 34, - 99,111,110,110,101, 99,116,105,111,110, 34, 93, 32, 61, 32, 34, 67,111,110,110, -101, 99,116,105,111,110, 34, 44, 10, 32, 32, 32, 32, 91, 34, 99,111,110,116,101, -110,116, 45,100,101,115, 99,114,105,112,116,105,111,110, 34, 93, 32, 61, 32, 34, - 67,111,110,116,101,110,116, 45, 68,101,115, 99,114,105,112,116,105,111,110, 34, - 44, 10, 32, 32, 32, 32, 91, 34, 99,111,110,116,101,110,116, 45,100,105,115,112, -111,115,105,116,105,111,110, 34, 93, 32, 61, 32, 34, 67,111,110,116,101,110,116, - 45, 68,105,115,112,111,115,105,116,105,111,110, 34, 44, 10, 32, 32, 32, 32, 91, - 34, 99,111,110,116,101,110,116, 45,101,110, 99,111,100,105,110,103, 34, 93, 32, - 61, 32, 34, 67,111,110,116,101,110,116, 45, 69,110, 99,111,100,105,110,103, 34, - 44, 10, 32, 32, 32, 32, 91, 34, 99,111,110,116,101,110,116, 45,105,100, 34, 93, - 32, 61, 32, 34, 67,111,110,116,101,110,116, 45, 73, 68, 34, 44, 10, 32, 32, 32, - 32, 91, 34, 99,111,110,116,101,110,116, 45,108, 97,110,103,117, 97,103,101, 34, - 93, 32, 61, 32, 34, 67,111,110,116,101,110,116, 45, 76, 97,110,103,117, 97,103, -101, 34, 44, 10, 32, 32, 32, 32, 91, 34, 99,111,110,116,101,110,116, 45,108,101, -110,103,116,104, 34, 93, 32, 61, 32, 34, 67,111,110,116,101,110,116, 45, 76,101, -110,103,116,104, 34, 44, 10, 32, 32, 32, 32, 91, 34, 99,111,110,116,101,110,116, - 45,108,111, 99, 97,116,105,111,110, 34, 93, 32, 61, 32, 34, 67,111,110,116,101, -110,116, 45, 76,111, 99, 97,116,105,111,110, 34, 44, 10, 32, 32, 32, 32, 91, 34, - 99,111,110,116,101,110,116, 45,109,100, 53, 34, 93, 32, 61, 32, 34, 67,111,110, -116,101,110,116, 45, 77, 68, 53, 34, 44, 10, 32, 32, 32, 32, 91, 34, 99,111,110, -116,101,110,116, 45,114, 97,110,103,101, 34, 93, 32, 61, 32, 34, 67,111,110,116, -101,110,116, 45, 82, 97,110,103,101, 34, 44, 10, 32, 32, 32, 32, 91, 34, 99,111, -110,116,101,110,116, 45,116,114, 97,110,115,102,101,114, 45,101,110, 99,111,100, -105,110,103, 34, 93, 32, 61, 32, 34, 67,111,110,116,101,110,116, 45, 84,114, 97, -110,115,102,101,114, 45, 69,110, 99,111,100,105,110,103, 34, 44, 10, 32, 32, 32, - 32, 91, 34, 99,111,110,116,101,110,116, 45,116,121,112,101, 34, 93, 32, 61, 32, - 34, 67,111,110,116,101,110,116, 45, 84,121,112,101, 34, 44, 10, 32, 32, 32, 32, - 91, 34, 99,111,111,107,105,101, 34, 93, 32, 61, 32, 34, 67,111,111,107,105,101, - 34, 44, 10, 32, 32, 32, 32, 91, 34,100, 97,116,101, 34, 93, 32, 61, 32, 34, 68, - 97,116,101, 34, 44, 10, 32, 32, 32, 32, 91, 34,100,105, 97,103,110,111,115,116, -105, 99, 45, 99,111,100,101, 34, 93, 32, 61, 32, 34, 68,105, 97,103,110,111,115, -116,105, 99, 45, 67,111,100,101, 34, 44, 10, 32, 32, 32, 32, 91, 34,100,115,110, - 45,103, 97,116,101,119, 97,121, 34, 93, 32, 61, 32, 34, 68, 83, 78, 45, 71, 97, -116,101,119, 97,121, 34, 44, 10, 32, 32, 32, 32, 91, 34,101,116, 97,103, 34, 93, - 32, 61, 32, 34, 69, 84, 97,103, 34, 44, 10, 32, 32, 32, 32, 91, 34,101,120,112, -101, 99,116, 34, 93, 32, 61, 32, 34, 69,120,112,101, 99,116, 34, 44, 10, 32, 32, - 32, 32, 91, 34,101,120,112,105,114,101,115, 34, 93, 32, 61, 32, 34, 69,120,112, -105,114,101,115, 34, 44, 10, 32, 32, 32, 32, 91, 34,102,105,110, 97,108, 45,108, -111,103, 45,105,100, 34, 93, 32, 61, 32, 34, 70,105,110, 97,108, 45, 76,111,103, - 45, 73, 68, 34, 44, 10, 32, 32, 32, 32, 91, 34,102,105,110, 97,108, 45,114,101, - 99,105,112,105,101,110,116, 34, 93, 32, 61, 32, 34, 70,105,110, 97,108, 45, 82, -101, 99,105,112,105,101,110,116, 34, 44, 10, 32, 32, 32, 32, 91, 34,102,114,111, -109, 34, 93, 32, 61, 32, 34, 70,114,111,109, 34, 44, 10, 32, 32, 32, 32, 91, 34, -104,111,115,116, 34, 93, 32, 61, 32, 34, 72,111,115,116, 34, 44, 10, 32, 32, 32, - 32, 91, 34,105,102, 45,109, 97,116, 99,104, 34, 93, 32, 61, 32, 34, 73,102, 45, - 77, 97,116, 99,104, 34, 44, 10, 32, 32, 32, 32, 91, 34,105,102, 45,109,111,100, -105,102,105,101,100, 45,115,105,110, 99,101, 34, 93, 32, 61, 32, 34, 73,102, 45, - 77,111,100,105,102,105,101,100, 45, 83,105,110, 99,101, 34, 44, 10, 32, 32, 32, - 32, 91, 34,105,102, 45,110,111,110,101, 45,109, 97,116, 99,104, 34, 93, 32, 61, - 32, 34, 73,102, 45, 78,111,110,101, 45, 77, 97,116, 99,104, 34, 44, 10, 32, 32, - 32, 32, 91, 34,105,102, 45,114, 97,110,103,101, 34, 93, 32, 61, 32, 34, 73,102, - 45, 82, 97,110,103,101, 34, 44, 10, 32, 32, 32, 32, 91, 34,105,102, 45,117,110, -109,111,100,105,102,105,101,100, 45,115,105,110, 99,101, 34, 93, 32, 61, 32, 34, - 73,102, 45, 85,110,109,111,100,105,102,105,101,100, 45, 83,105,110, 99,101, 34, - 44, 10, 32, 32, 32, 32, 91, 34,105,110, 45,114,101,112,108,121, 45,116,111, 34, - 93, 32, 61, 32, 34, 73,110, 45, 82,101,112,108,121, 45, 84,111, 34, 44, 10, 32, - 32, 32, 32, 91, 34,107,101,121,119,111,114,100,115, 34, 93, 32, 61, 32, 34, 75, -101,121,119,111,114,100,115, 34, 44, 10, 32, 32, 32, 32, 91, 34,108, 97,115,116, - 45, 97,116,116,101,109,112,116, 45,100, 97,116,101, 34, 93, 32, 61, 32, 34, 76, - 97,115,116, 45, 65,116,116,101,109,112,116, 45, 68, 97,116,101, 34, 44, 10, 32, - 32, 32, 32, 91, 34,108, 97,115,116, 45,109,111,100,105,102,105,101,100, 34, 93, - 32, 61, 32, 34, 76, 97,115,116, 45, 77,111,100,105,102,105,101,100, 34, 44, 10, - 32, 32, 32, 32, 91, 34,108,111, 99, 97,116,105,111,110, 34, 93, 32, 61, 32, 34, - 76,111, 99, 97,116,105,111,110, 34, 44, 10, 32, 32, 32, 32, 91, 34,109, 97,120, - 45,102,111,114,119, 97,114,100,115, 34, 93, 32, 61, 32, 34, 77, 97,120, 45, 70, -111,114,119, 97,114,100,115, 34, 44, 10, 32, 32, 32, 32, 91, 34,109,101,115,115, - 97,103,101, 45,105,100, 34, 93, 32, 61, 32, 34, 77,101,115,115, 97,103,101, 45, - 73, 68, 34, 44, 10, 32, 32, 32, 32, 91, 34,109,105,109,101, 45,118,101,114,115, -105,111,110, 34, 93, 32, 61, 32, 34, 77, 73, 77, 69, 45, 86,101,114,115,105,111, -110, 34, 44, 10, 32, 32, 32, 32, 91, 34,111,114,105,103,105,110, 97,108, 45,101, -110,118,101,108,111,112,101, 45,105,100, 34, 93, 32, 61, 32, 34, 79,114,105,103, -105,110, 97,108, 45, 69,110,118,101,108,111,112,101, 45, 73, 68, 34, 44, 10, 32, - 32, 32, 32, 91, 34,111,114,105,103,105,110, 97,108, 45,114,101, 99,105,112,105, -101,110,116, 34, 93, 32, 61, 32, 34, 79,114,105,103,105,110, 97,108, 45, 82,101, - 99,105,112,105,101,110,116, 34, 44, 10, 32, 32, 32, 32, 91, 34,112,114, 97,103, -109, 97, 34, 93, 32, 61, 32, 34, 80,114, 97,103,109, 97, 34, 44, 10, 32, 32, 32, - 32, 91, 34,112,114,111,120,121, 45, 97,117,116,104,101,110,116,105, 99, 97,116, -101, 34, 93, 32, 61, 32, 34, 80,114,111,120,121, 45, 65,117,116,104,101,110,116, -105, 99, 97,116,101, 34, 44, 10, 32, 32, 32, 32, 91, 34,112,114,111,120,121, 45, - 97,117,116,104,111,114,105,122, 97,116,105,111,110, 34, 93, 32, 61, 32, 34, 80, -114,111,120,121, 45, 65,117,116,104,111,114,105,122, 97,116,105,111,110, 34, 44, - 10, 32, 32, 32, 32, 91, 34,114, 97,110,103,101, 34, 93, 32, 61, 32, 34, 82, 97, -110,103,101, 34, 44, 10, 32, 32, 32, 32, 91, 34,114,101, 99,101,105,118,101,100, - 34, 93, 32, 61, 32, 34, 82,101, 99,101,105,118,101,100, 34, 44, 10, 32, 32, 32, - 32, 91, 34,114,101, 99,101,105,118,101,100, 45,102,114,111,109, 45,109,116, 97, - 34, 93, 32, 61, 32, 34, 82,101, 99,101,105,118,101,100, 45, 70,114,111,109, 45, - 77, 84, 65, 34, 44, 10, 32, 32, 32, 32, 91, 34,114,101,102,101,114,101,110, 99, -101,115, 34, 93, 32, 61, 32, 34, 82,101,102,101,114,101,110, 99,101,115, 34, 44, - 10, 32, 32, 32, 32, 91, 34,114,101,102,101,114,101,114, 34, 93, 32, 61, 32, 34, - 82,101,102,101,114,101,114, 34, 44, 10, 32, 32, 32, 32, 91, 34,114,101,109,111, -116,101, 45,109,116, 97, 34, 93, 32, 61, 32, 34, 82,101,109,111,116,101, 45, 77, - 84, 65, 34, 44, 10, 32, 32, 32, 32, 91, 34,114,101,112,108,121, 45,116,111, 34, - 93, 32, 61, 32, 34, 82,101,112,108,121, 45, 84,111, 34, 44, 10, 32, 32, 32, 32, - 91, 34,114,101,112,111,114,116,105,110,103, 45,109,116, 97, 34, 93, 32, 61, 32, - 34, 82,101,112,111,114,116,105,110,103, 45, 77, 84, 65, 34, 44, 10, 32, 32, 32, - 32, 91, 34,114,101,115,101,110,116, 45, 98, 99, 99, 34, 93, 32, 61, 32, 34, 82, -101,115,101,110,116, 45, 66, 99, 99, 34, 44, 10, 32, 32, 32, 32, 91, 34,114,101, -115,101,110,116, 45, 99, 99, 34, 93, 32, 61, 32, 34, 82,101,115,101,110,116, 45, - 67, 99, 34, 44, 10, 32, 32, 32, 32, 91, 34,114,101,115,101,110,116, 45,100, 97, -116,101, 34, 93, 32, 61, 32, 34, 82,101,115,101,110,116, 45, 68, 97,116,101, 34, - 44, 10, 32, 32, 32, 32, 91, 34,114,101,115,101,110,116, 45,102,114,111,109, 34, - 93, 32, 61, 32, 34, 82,101,115,101,110,116, 45, 70,114,111,109, 34, 44, 10, 32, - 32, 32, 32, 91, 34,114,101,115,101,110,116, 45,109,101,115,115, 97,103,101, 45, -105,100, 34, 93, 32, 61, 32, 34, 82,101,115,101,110,116, 45, 77,101,115,115, 97, -103,101, 45, 73, 68, 34, 44, 10, 32, 32, 32, 32, 91, 34,114,101,115,101,110,116, - 45,114,101,112,108,121, 45,116,111, 34, 93, 32, 61, 32, 34, 82,101,115,101,110, -116, 45, 82,101,112,108,121, 45, 84,111, 34, 44, 10, 32, 32, 32, 32, 91, 34,114, -101,115,101,110,116, 45,115,101,110,100,101,114, 34, 93, 32, 61, 32, 34, 82,101, -115,101,110,116, 45, 83,101,110,100,101,114, 34, 44, 10, 32, 32, 32, 32, 91, 34, -114,101,115,101,110,116, 45,116,111, 34, 93, 32, 61, 32, 34, 82,101,115,101,110, -116, 45, 84,111, 34, 44, 10, 32, 32, 32, 32, 91, 34,114,101,116,114,121, 45, 97, -102,116,101,114, 34, 93, 32, 61, 32, 34, 82,101,116,114,121, 45, 65,102,116,101, -114, 34, 44, 10, 32, 32, 32, 32, 91, 34,114,101,116,117,114,110, 45,112, 97,116, -104, 34, 93, 32, 61, 32, 34, 82,101,116,117,114,110, 45, 80, 97,116,104, 34, 44, - 10, 32, 32, 32, 32, 91, 34,115,101,110,100,101,114, 34, 93, 32, 61, 32, 34, 83, -101,110,100,101,114, 34, 44, 10, 32, 32, 32, 32, 91, 34,115,101,114,118,101,114, - 34, 93, 32, 61, 32, 34, 83,101,114,118,101,114, 34, 44, 10, 32, 32, 32, 32, 91, - 34,115,109,116,112, 45,114,101,109,111,116,101, 45,114,101, 99,105,112,105,101, -110,116, 34, 93, 32, 61, 32, 34, 83, 77, 84, 80, 45, 82,101,109,111,116,101, 45, - 82,101, 99,105,112,105,101,110,116, 34, 44, 10, 32, 32, 32, 32, 91, 34,115,116, - 97,116,117,115, 34, 93, 32, 61, 32, 34, 83,116, 97,116,117,115, 34, 44, 10, 32, - 32, 32, 32, 91, 34,115,117, 98,106,101, 99,116, 34, 93, 32, 61, 32, 34, 83,117, - 98,106,101, 99,116, 34, 44, 10, 32, 32, 32, 32, 91, 34,116,101, 34, 93, 32, 61, - 32, 34, 84, 69, 34, 44, 10, 32, 32, 32, 32, 91, 34,116,111, 34, 93, 32, 61, 32, - 34, 84,111, 34, 44, 10, 32, 32, 32, 32, 91, 34,116,114, 97,105,108,101,114, 34, - 93, 32, 61, 32, 34, 84,114, 97,105,108,101,114, 34, 44, 10, 32, 32, 32, 32, 91, - 34,116,114, 97,110,115,102,101,114, 45,101,110, 99,111,100,105,110,103, 34, 93, - 32, 61, 32, 34, 84,114, 97,110,115,102,101,114, 45, 69,110, 99,111,100,105,110, -103, 34, 44, 10, 32, 32, 32, 32, 91, 34,117,112,103,114, 97,100,101, 34, 93, 32, - 61, 32, 34, 85,112,103,114, 97,100,101, 34, 44, 10, 32, 32, 32, 32, 91, 34,117, -115,101,114, 45, 97,103,101,110,116, 34, 93, 32, 61, 32, 34, 85,115,101,114, 45, - 65,103,101,110,116, 34, 44, 10, 32, 32, 32, 32, 91, 34,118, 97,114,121, 34, 93, - 32, 61, 32, 34, 86, 97,114,121, 34, 44, 10, 32, 32, 32, 32, 91, 34,118,105, 97, - 34, 93, 32, 61, 32, 34, 86,105, 97, 34, 44, 10, 32, 32, 32, 32, 91, 34,119, 97, -114,110,105,110,103, 34, 93, 32, 61, 32, 34, 87, 97,114,110,105,110,103, 34, 44, - 10, 32, 32, 32, 32, 91, 34,119,105,108,108, 45,114,101,116,114,121, 45,117,110, -116,105,108, 34, 93, 32, 61, 32, 34, 87,105,108,108, 45, 82,101,116,114,121, 45, - 85,110,116,105,108, 34, 44, 10, 32, 32, 32, 32, 91, 34,119,119,119, 45, 97,117, -116,104,101,110,116,105, 99, 97,116,101, 34, 93, 32, 61, 32, 34, 87, 87, 87, 45, - 65,117,116,104,101,110,116,105, 99, 97,116,101, 34, 44, 10, 32, 32, 32, 32, 91, - 34,120, 45,109, 97,105,108,101,114, 34, 93, 32, 61, 32, 34, 88, 45, 77, 97,105, -108,101,114, 34, 44, 10,125, 10, 10,114,101,116,117,114,110, 32, 95, 77, -}; - - if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"headers.lua")==0) lua_call(L, 0, LUA_MULTRET); -} diff --git a/libraries/luasocket/libluasocket/http.lua b/libraries/luasocket/libluasocket/http.lua index ab5daf61d..1d7672924 100644 --- a/libraries/luasocket/libluasocket/http.lua +++ b/libraries/luasocket/libluasocket/http.lua @@ -1,3 +1,4 @@ +R"luastring"--( ----------------------------------------------------------------------------- -- HTTP/1.1 client support for the Lua language. -- LuaSocket toolkit. @@ -26,10 +27,20 @@ _M.TIMEOUT = 60 -- user agent field sent in request _M.USERAGENT = socket._VERSION --- supported schemes -local SCHEMES = { ["http"] = true } --- default port for document retrieval -local PORT = 80 +-- supported schemes and their particulars +local SCHEMES = { + http = { + port = 80 + , create = function(t) + return socket.tcp end } + , https = { + port = 443 + , create = function(t) + local https = assert( + require("ssl.https"), 'LuaSocket: LuaSec not found') + local tcp = assert( + https.tcp, 'LuaSocket: Function tcp() not available from LuaSec') + return tcp(t) end }} ----------------------------------------------------------------------------- -- Reads MIME headers from a connection, unfolding where needed @@ -44,7 +55,7 @@ local function receiveheaders(sock, headers) while line ~= "" do -- get field-name and value name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) - if not (name and value) then return nil, "malformed response headers" end + if not (name and value) then return nil, "malformed reponse headers" end name = string.lower(name) -- get next line (value might be folded) line, err = sock:receive() @@ -71,7 +82,7 @@ socket.sourcet["http-chunked"] = function(sock, headers) dirty = function() return sock:dirty() end }, { __call = function() - -- get chunk size, skip extension + -- get chunk size, skip extention local line, err = sock:receive() if err then return nil, err end local size = base.tonumber(string.gsub(line, ";.*", ""), 16) @@ -79,7 +90,7 @@ socket.sourcet["http-chunked"] = function(sock, headers) -- was it the last chunk? if size > 0 then -- if not, get chunk and skip terminating CRLF - local chunk, err, part = sock:receive(size) + local chunk, err, _ = sock:receive(size) if chunk then sock:receive() end return chunk, err else @@ -111,13 +122,13 @@ local metat = { __index = {} } function _M.open(host, port, create) -- create socket with user connect function, or with default - local c = socket.try((create or socket.tcp)()) + local c = socket.try(create()) local h = base.setmetatable({ c = c }, metat) -- create finalized try h.try = socket.newtry(function() h:close() end) -- set timeout before connecting h.try(c:settimeout(_M.TIMEOUT)) - h.try(c:connect(host, port or PORT)) + h.try(c:connect(host, port)) -- here everything worked return h end @@ -147,10 +158,15 @@ function metat.__index:sendbody(headers, source, step) end function metat.__index:receivestatusline() - local status = self.try(self.c:receive(5)) + local status,ec = self.try(self.c:receive(5)) -- identify HTTP/0.9 responses, which do not contain a status line -- this is just a heuristic, but is what the RFC recommends - if status ~= "HTTP/" then return nil, status end + if status ~= "HTTP/" then + if ec == "timeout" then + return 408 + end + return nil, status + end -- otherwise proceed reading a status line status = self.try(self.c:receive("*l", status)) local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) @@ -212,7 +228,10 @@ end local function adjustheaders(reqt) -- default headers - local host = string.gsub(reqt.authority, "^.-@", "") + local host = reqt.host + local port = tostring(reqt.port) + if port ~= tostring(SCHEMES[reqt.scheme].port) then + host = host .. ':' .. port end local lower = { ["user-agent"] = _M.USERAGENT, ["host"] = host, @@ -243,10 +262,8 @@ end -- default url parts local default = { - host = "", - port = PORT, - path ="/", - scheme = "http" + path ="/" + , scheme = "http" } local function adjustrequest(reqt) @@ -254,14 +271,26 @@ local function adjustrequest(reqt) local nreqt = reqt.url and url.parse(reqt.url, default) or {} -- explicit components override url for i,v in base.pairs(reqt) do nreqt[i] = v end - if nreqt.port == "" then nreqt.port = PORT end - if not (nreqt.host and nreqt.host ~= "") then + -- default to scheme particulars + local schemedefs, host, port, method + = SCHEMES[nreqt.scheme], nreqt.host, nreqt.port, nreqt.method + if not nreqt.create then nreqt.create = schemedefs.create(nreqt) end + if not (port and port ~= '') then nreqt.port = schemedefs.port end + if not (method and method ~= '') then nreqt.method = 'GET' end + if not (host and host ~= "") then socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'") end - -- compute uri if user hasn't overridden + -- compute uri if user hasn't overriden nreqt.uri = reqt.uri or adjusturi(nreqt) -- adjust headers in request nreqt.headers = adjustheaders(nreqt) + if nreqt.source + and not nreqt.headers["content-length"] + and not nreqt.headers["transfer-encoding"] + then + nreqt.headers["transfer-encoding"] = "chunked" + end + -- ajust host and port if there is a proxy nreqt.host, nreqt.port = adjustproxy(nreqt) return nreqt @@ -272,12 +301,16 @@ local function shouldredirect(reqt, code, headers) if not location then return false end location = string.gsub(location, "%s", "") if location == "" then return false end - local scheme = string.match(location, "^([%w][%w%+%-%.]*)%:") - if scheme and not SCHEMES[scheme] then return false end + local scheme = url.parse(location).scheme + if scheme and (not SCHEMES[scheme]) then return false end + -- avoid https downgrades + if ('https' == reqt.scheme) and ('https' ~= scheme) then return false end return (reqt.redirect ~= false) and (code == 301 or code == 302 or code == 303 or code == 307) and (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") - and (not reqt.nredirects or reqt.nredirects < 5) + and ((false == reqt.maxredirects) + or ((reqt.nredirects or 0) + < (reqt.maxredirects or 5))) end local function shouldreceivebody(reqt, code) @@ -291,14 +324,21 @@ end local trequest, tredirect --[[local]] function tredirect(reqt, location) + -- the RFC says the redirect URL has to be absolute, but some + -- servers do not respect that + local newurl = url.absolute(reqt.url, location) + -- if switching schemes, reset port and create function + if url.parse(newurl).scheme ~= reqt.scheme then + reqt.port = nil + reqt.create = nil end + -- make new request local result, code, headers, status = trequest { - -- the RFC says the redirect URL has to be absolute, but some - -- servers do not respect that - url = url.absolute(reqt.url, location), + url = newurl, source = reqt.source, sink = reqt.sink, headers = reqt.headers, proxy = reqt.proxy, + maxredirects = reqt.maxredirects, nredirects = (reqt.nredirects or 0) + 1, create = reqt.create } @@ -325,11 +365,13 @@ end if not code then h:receive09body(status, nreqt.sink, nreqt.step) return 1, 200 + elseif code == 408 then + return 1, code end local headers -- ignore any 100-continue messages while code == 100 do - headers = h:receiveheaders() + h:receiveheaders() code, status = h:receivestatusline() end headers = h:receiveheaders() @@ -379,4 +421,7 @@ _M.request = socket.protect(function(reqt, body) else return trequest(reqt) end end) +_M.schemes = SCHEMES return _M +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/http.lua.h b/libraries/luasocket/libluasocket/http.lua.h deleted file mode 100644 index 840cd85ca..000000000 --- a/libraries/luasocket/libluasocket/http.lua.h +++ /dev/null @@ -1,672 +0,0 @@ -/* code automatically generated by bin2c -- DO NOT EDIT */ -{ -/* #include'ing this file in a C program is equivalent to calling - if (luaL_loadfile(L,"http.lua")==0) lua_call(L, 0, LUA_MULTRET); -*/ -/* http.lua */ -static const unsigned char B1[]={ - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, - 32, 72, 84, 84, 80, 47, 49, 46, 49, 32, 99,108,105,101,110,116, 32,115,117,112, -112,111,114,116, 32,102,111,114, 32,116,104,101, 32, 76,117, 97, 32,108, 97,110, -103,117, 97,103,101, 46, 10, 45, 45, 32, 76,117, 97, 83,111, 99,107,101,116, 32, -116,111,111,108,107,105,116, 46, 10, 45, 45, 32, 65,117,116,104,111,114, 58, 32, - 68,105,101,103,111, 32, 78,101,104, 97, 98, 10, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 68,101, 99,108, 97,114,101, 32, -109,111,100,117,108,101, 32, 97,110,100, 32,105,109,112,111,114,116, 32,100,101, -112,101,110,100,101,110, 99,105,101,115, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10,108,111, 99, 97,108, 32,115,111, 99, -107,101,116, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,115,111, 99,107,101, -116, 34, 41, 10,108,111, 99, 97,108, 32,117,114,108, 32, 61, 32,114,101,113,117, -105,114,101, 40, 34,115,111, 99,107,101,116, 46,117,114,108, 34, 41, 10,108,111, - 99, 97,108, 32,108,116,110, 49, 50, 32, 61, 32,114,101,113,117,105,114,101, 40, - 34,108,116,110, 49, 50, 34, 41, 10,108,111, 99, 97,108, 32,109,105,109,101, 32, - 61, 32,114,101,113,117,105,114,101, 40, 34,109,105,109,101, 34, 41, 10,108,111, - 99, 97,108, 32,115,116,114,105,110,103, 32, 61, 32,114,101,113,117,105,114,101, - 40, 34,115,116,114,105,110,103, 34, 41, 10,108,111, 99, 97,108, 32,104,101, 97, -100,101,114,115, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,115,111, 99,107, -101,116, 46,104,101, 97,100,101,114,115, 34, 41, 10,108,111, 99, 97,108, 32, 98, - 97,115,101, 32, 61, 32, 95, 71, 10,108,111, 99, 97,108, 32,116, 97, 98,108,101, - 32, 61, 32,114,101,113,117,105,114,101, 40, 34,116, 97, 98,108,101, 34, 41, 10, -115,111, 99,107,101,116, 46,104,116,116,112, 32, 61, 32,123,125, 10,108,111, 99, - 97,108, 32, 95, 77, 32, 61, 32,115,111, 99,107,101,116, 46,104,116,116,112, 10, - 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, - 45, 32, 80,114,111,103,114, 97,109, 32, 99,111,110,115,116, 97,110,116,115, 10, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, - 32, 99,111,110,110,101, 99,116,105,111,110, 32,116,105,109,101,111,117,116, 32, -105,110, 32,115,101, 99,111,110,100,115, 10, 95, 77, 46, 84, 73, 77, 69, 79, 85, - 84, 32, 61, 32, 54, 48, 10, 45, 45, 32,117,115,101,114, 32, 97,103,101,110,116, - 32,102,105,101,108,100, 32,115,101,110,116, 32,105,110, 32,114,101,113,117,101, -115,116, 10, 95, 77, 46, 85, 83, 69, 82, 65, 71, 69, 78, 84, 32, 61, 32,115,111, - 99,107,101,116, 46, 95, 86, 69, 82, 83, 73, 79, 78, 10, 10, 45, 45, 32,115,117, -112,112,111,114,116,101,100, 32,115, 99,104,101,109,101,115, 10,108,111, 99, 97, -108, 32, 83, 67, 72, 69, 77, 69, 83, 32, 61, 32,123, 32, 91, 34,104,116,116,112, - 34, 93, 32, 61, 32,116,114,117,101, 32,125, 10, 45, 45, 32,100,101,102, 97,117, -108,116, 32,112,111,114,116, 32,102,111,114, 32,100,111, 99,117,109,101,110,116, - 32,114,101,116,114,105,101,118, 97,108, 10,108,111, 99, 97,108, 32, 80, 79, 82, - 84, 32, 61, 32, 56, 48, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 10, 45, 45, 32, 82,101, 97,100,115, 32, 77, 73, 77, 69, 32, -104,101, 97,100,101,114,115, 32,102,114,111,109, 32, 97, 32, 99,111,110,110,101, - 99,116,105,111,110, 44, 32,117,110,102,111,108,100,105,110,103, 32,119,104,101, -114,101, 32,110,101,101,100,101,100, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 10,108,111, 99, 97,108, 32,102,117,110, 99,116,105, -111,110, 32,114,101, 99,101,105,118,101,104,101, 97,100,101,114,115, 40,115,111, - 99,107, 44, 32,104,101, 97,100,101,114,115, 41, 10, 32, 32, 32, 32,108,111, 99, - 97,108, 32,108,105,110,101, 44, 32,110, 97,109,101, 44, 32,118, 97,108,117,101, - 44, 32,101,114,114, 10, 32, 32, 32, 32,104,101, 97,100,101,114,115, 32, 61, 32, -104,101, 97,100,101,114,115, 32,111,114, 32,123,125, 10, 32, 32, 32, 32, 45, 45, - 32,103,101,116, 32,102,105,114,115,116, 32,108,105,110,101, 10, 32, 32, 32, 32, -108,105,110,101, 44, 32,101,114,114, 32, 61, 32,115,111, 99,107, 58,114,101, 99, -101,105,118,101, 40, 41, 10, 32, 32, 32, 32,105,102, 32,101,114,114, 32,116,104, -101,110, 32,114,101,116,117,114,110, 32,110,105,108, 44, 32,101,114,114, 32,101, -110,100, 10, 32, 32, 32, 32, 45, 45, 32,104,101, 97,100,101,114,115, 32,103,111, - 32,117,110,116,105,108, 32, 97, 32, 98,108, 97,110,107, 32,108,105,110,101, 32, -105,115, 32,102,111,117,110,100, 10, 32, 32, 32, 32,119,104,105,108,101, 32,108, -105,110,101, 32,126, 61, 32, 34, 34, 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 45, 45, 32,103,101,116, 32,102,105,101,108,100, 45,110, 97,109,101, 32, 97, -110,100, 32,118, 97,108,117,101, 10, 32, 32, 32, 32, 32, 32, 32, 32,110, 97,109, -101, 44, 32,118, 97,108,117,101, 32, 61, 32,115,111, 99,107,101,116, 46,115,107, -105,112, 40, 50, 44, 32,115,116,114,105,110,103, 46,102,105,110,100, 40,108,105, -110,101, 44, 32, 34, 94, 40, 46, 45, 41, 58, 37,115, 42, 40, 46, 42, 41, 34, 41, - 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,110,111,116, 32, 40,110, 97, -109,101, 32, 97,110,100, 32,118, 97,108,117,101, 41, 32,116,104,101,110, 32,114, -101,116,117,114,110, 32,110,105,108, 44, 32, 34,109, 97,108,102,111,114,109,101, -100, 32,114,101,112,111,110,115,101, 32,104,101, 97,100,101,114,115, 34, 32,101, -110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,110, 97,109,101, 32, 61, 32,115,116, -114,105,110,103, 46,108,111,119,101,114, 40,110, 97,109,101, 41, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 45, 45, 32,103,101,116, 32,110,101,120,116, 32,108,105,110, -101, 32, 40,118, 97,108,117,101, 32,109,105,103,104,116, 32, 98,101, 32,102,111, -108,100,101,100, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,108,105,110,101, 44, 32, -101,114,114, 32, 32, 61, 32,115,111, 99,107, 58,114,101, 99,101,105,118,101, 40, - 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,101,114,114, 32,116,104,101, -110, 32,114,101,116,117,114,110, 32,110,105,108, 44, 32,101,114,114, 32,101,110, -100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 32,117,110,102,111,108,100, 32, - 97,110,121, 32,102,111,108,100,101,100, 32,118, 97,108,117,101,115, 10, 32, 32, - 32, 32, 32, 32, 32, 32,119,104,105,108,101, 32,115,116,114,105,110,103, 46,102, -105,110,100, 40,108,105,110,101, 44, 32, 34, 94, 37,115, 34, 41, 32,100,111, 10, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,118, 97,108,117,101, 32, 61, 32, -118, 97,108,117,101, 32, 46, 46, 32,108,105,110,101, 10, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32,108,105,110,101, 32, 61, 32,115,111, 99,107, 58,114,101, - 99,101,105,118,101, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -105,102, 32,101,114,114, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,110, -105,108, 44, 32,101,114,114, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, -101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 32,115, 97,118,101, 32, -112, 97,105,114, 32,105,110, 32,116, 97, 98,108,101, 10, 32, 32, 32, 32, 32, 32, - 32, 32,105,102, 32,104,101, 97,100,101,114,115, 91,110, 97,109,101, 93, 32,116, -104,101,110, 32,104,101, 97,100,101,114,115, 91,110, 97,109,101, 93, 32, 61, 32, -104,101, 97,100,101,114,115, 91,110, 97,109,101, 93, 32, 46, 46, 32, 34, 44, 32, - 34, 32, 46, 46, 32,118, 97,108,117,101, 10, 32, 32, 32, 32, 32, 32, 32, 32,101, -108,115,101, 32,104,101, 97,100,101,114,115, 91,110, 97,109,101, 93, 32, 61, 32, -118, 97,108,117,101, 32,101,110,100, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, - 32, 32,114,101,116,117,114,110, 32,104,101, 97,100,101,114,115, 10,101,110,100, - 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, - 45, 45, 32, 69,120,116,114, 97, 32,115,111,117,114, 99,101,115, 32, 97,110,100, - 32,115,105,110,107,115, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 10,115,111, 99,107,101,116, 46,115,111,117,114, 99,101,116, 91, - 34,104,116,116,112, 45, 99,104,117,110,107,101,100, 34, 93, 32, 61, 32,102,117, -110, 99,116,105,111,110, 40,115,111, 99,107, 44, 32,104,101, 97,100,101,114,115, - 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, 98, 97,115,101, 46,115,101, -116,109,101,116, 97,116, 97, 98,108,101, 40,123, 10, 32, 32, 32, 32, 32, 32, 32, - 32,103,101,116,102,100, 32, 61, 32,102,117,110, 99,116,105,111,110, 40, 41, 32, -114,101,116,117,114,110, 32,115,111, 99,107, 58,103,101,116,102,100, 40, 41, 32, -101,110,100, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32,100,105,114,116,121, 32, 61, - 32,102,117,110, 99,116,105,111,110, 40, 41, 32,114,101,116,117,114,110, 32,115, -111, 99,107, 58,100,105,114,116,121, 40, 41, 32,101,110,100, 10, 32, 32, 32, 32, -125, 44, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 95, 95, 99, 97,108,108, 32, - 61, 32,102,117,110, 99,116,105,111,110, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 45, 45, 32,103,101,116, 32, 99,104,117,110,107, 32,115,105, -122,101, 44, 32,115,107,105,112, 32,101,120,116,101,110,116,105,111,110, 10, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32,108,105,110, -101, 44, 32,101,114,114, 32, 61, 32,115,111, 99,107, 58,114,101, 99,101,105,118, -101, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,101, -114,114, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,110,105,108, 44, 32, -101,114,114, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -108,111, 99, 97,108, 32,115,105,122,101, 32, 61, 32, 98, 97,115,101, 46,116,111, -110,117,109, 98,101,114, 40,115,116,114,105,110,103, 46,103,115,117, 98, 40,108, -105,110,101, 44, 32, 34, 59, 46, 42, 34, 44, 32, 34, 34, 41, 44, 32, 49, 54, 41, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,110,111,116, 32, -115,105,122,101, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,110,105,108, - 44, 32, 34,105,110,118, 97,108,105,100, 32, 99,104,117,110,107, 32,115,105,122, -101, 34, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, - 45, 32,119, 97,115, 32,105,116, 32,116,104,101, 32,108, 97,115,116, 32, 99,104, -117,110,107, 63, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, -115,105,122,101, 32, 62, 32, 48, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 32,105,102, 32,110,111,116, 44, - 32,103,101,116, 32, 99,104,117,110,107, 32, 97,110,100, 32,115,107,105,112, 32, -116,101,114,109,105,110, 97,116,105,110,103, 32, 67, 82, 76, 70, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32, 99, -104,117,110,107, 44, 32,101,114,114, 44, 32,112, 97,114,116, 32, 61, 32,115,111, - 99,107, 58,114,101, 99,101,105,118,101, 40,115,105,122,101, 41, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, 99,104,117,110, -107, 32,116,104,101,110, 32,115,111, 99,107, 58,114,101, 99,101,105,118,101, 40, - 41, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32,114,101,116,117,114,110, 32, 99,104,117,110,107, 44, 32,101,114,114, 10, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 32,105,102, 32,105, -116, 32,119, 97,115, 44, 32,114,101, 97,100, 32,116,114, 97,105,108,101,114,115, - 32,105,110,116,111, 32,104,101, 97,100,101,114,115, 32,116, 97, 98,108,101, 10, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,104,101, 97,100, -101,114,115, 44, 32,101,114,114, 32, 61, 32,114,101, 99,101,105,118,101,104,101, - 97,100,101,114,115, 40,115,111, 99,107, 44, 32,104,101, 97,100,101,114,115, 41, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, -110,111,116, 32,104,101, 97,100,101,114,115, 32,116,104,101,110, 32,114,101,116, -117,114,110, 32,110,105,108, 44, 32,101,114,114, 32,101,110,100, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, - 32,101,110,100, 10, 32, 32, 32, 32,125, 41, 10,101,110,100, 10, 10,115,111, 99, -107,101,116, 46,115,105,110,107,116, 91, 34,104,116,116,112, 45, 99,104,117,110, -107,101,100, 34, 93, 32, 61, 32,102,117,110, 99,116,105,111,110, 40,115,111, 99, -107, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, 98, 97,115,101, 46,115, -101,116,109,101,116, 97,116, 97, 98,108,101, 40,123, 10, 32, 32, 32, 32, 32, 32, - 32, 32,103,101,116,102,100, 32, 61, 32,102,117,110, 99,116,105,111,110, 40, 41, - 32,114,101,116,117,114,110, 32,115,111, 99,107, 58,103,101,116,102,100, 40, 41, - 32,101,110,100, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32,100,105,114,116,121, 32, - 61, 32,102,117,110, 99,116,105,111,110, 40, 41, 32,114,101,116,117,114,110, 32, -115,111, 99,107, 58,100,105,114,116,121, 40, 41, 32,101,110,100, 10, 32, 32, 32, - 32,125, 44, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 95, 95, 99, 97,108,108, - 32, 61, 32,102,117,110, 99,116,105,111,110, 40,115,101,108,102, 44, 32, 99,104, -117,110,107, 44, 32,101,114,114, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32,105,102, 32,110,111,116, 32, 99,104,117,110,107, 32,116,104,101,110, 32, -114,101,116,117,114,110, 32,115,111, 99,107, 58,115,101,110,100, 40, 34, 48, 92, -114, 92,110, 92,114, 92,110, 34, 41, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32,115,105,122,101, 32, 61, 32,115, -116,114,105,110,103, 46,102,111,114,109, 97,116, 40, 34, 37, 88, 92,114, 92,110, - 34, 44, 32,115,116,114,105,110,103, 46,108,101,110, 40, 99,104,117,110,107, 41, - 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, - 32,115,111, 99,107, 58,115,101,110,100, 40,115,105,122,101, 32, 46, 46, 32, 32, - 99,104,117,110,107, 32, 46, 46, 32, 34, 92,114, 92,110, 34, 41, 10, 32, 32, 32, - 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,125, 41, 10,101,110,100, 10, - 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, - 45, 32, 76,111,119, 32,108,101,118,101,108, 32, 72, 84, 84, 80, 32, 65, 80, 73, - 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10,108, -111, 99, 97,108, 32,109,101,116, 97,116, 32, 61, 32,123, 32, 95, 95,105,110,100, -101,120, 32, 61, 32,123,125, 32,125, 10, 10,102,117,110, 99,116,105,111,110, 32, - 95, 77, 46,111,112,101,110, 40,104,111,115,116, 44, 32,112,111,114,116, 44, 32, - 99,114,101, 97,116,101, 41, 10, 32, 32, 32, 32, 45, 45, 32, 99,114,101, 97,116, -101, 32,115,111, 99,107,101,116, 32,119,105,116,104, 32,117,115,101,114, 32, 99, -111,110,110,101, 99,116, 32,102,117,110, 99,116,105,111,110, 44, 32,111,114, 32, -119,105,116,104, 32,100,101,102, 97,117,108,116, 10, 32, 32, 32, 32,108,111, 99, - 97,108, 32, 99, 32, 61, 32,115,111, 99,107,101,116, 46,116,114,121, 40, 40, 99, -114,101, 97,116,101, 32,111,114, 32,115,111, 99,107,101,116, 46,116, 99,112, 41, - 40, 41, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,104, 32, 61, 32, 98, 97, -115,101, 46,115,101,116,109,101,116, 97,116, 97, 98,108,101, 40,123, 32, 99, 32, - 61, 32, 99, 32,125, 44, 32,109,101,116, 97,116, 41, 10, 32, 32, 32, 32, 45, 45, - 32, 99,114,101, 97,116,101, 32,102,105,110, 97,108,105,122,101,100, 32,116,114, -121, 10, 32, 32, 32, 32,104, 46,116,114,121, 32, 61, 32,115,111, 99,107,101,116, - 46,110,101,119,116,114,121, 40,102,117,110, 99,116,105,111,110, 40, 41, 32,104, - 58, 99,108,111,115,101, 40, 41, 32,101,110,100, 41, 10, 32, 32, 32, 32, 45, 45, - 32,115,101,116, 32,116,105,109,101,111,117,116, 32, 98,101,102,111,114,101, 32, - 99,111,110,110,101, 99,116,105,110,103, 10, 32, 32, 32, 32,104, 46,116,114,121, - 40, 99, 58,115,101,116,116,105,109,101,111,117,116, 40, 95, 77, 46, 84, 73, 77, - 69, 79, 85, 84, 41, 41, 10, 32, 32, 32, 32,104, 46,116,114,121, 40, 99, 58, 99, -111,110,110,101, 99,116, 40,104,111,115,116, 44, 32,112,111,114,116, 32,111,114, - 32, 80, 79, 82, 84, 41, 41, 10, 32, 32, 32, 32, 45, 45, 32,104,101,114,101, 32, -101,118,101,114,121,116,104,105,110,103, 32,119,111,114,107,101,100, 10, 32, 32, - 32, 32,114,101,116,117,114,110, 32,104, 10,101,110,100, 10, 10,102,117,110, 99, -116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,115, -101,110,100,114,101,113,117,101,115,116,108,105,110,101, 40,109,101,116,104,111, -100, 44, 32,117,114,105, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,114,101, -113,108,105,110,101, 32, 61, 32,115,116,114,105,110,103, 46,102,111,114,109, 97, -116, 40, 34, 37,115, 32, 37,115, 32, 72, 84, 84, 80, 47, 49, 46, 49, 92,114, 92, -110, 34, 44, 32,109,101,116,104,111,100, 32,111,114, 32, 34, 71, 69, 84, 34, 44, - 32,117,114,105, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,101,108, -102, 46,116,114,121, 40,115,101,108,102, 46, 99, 58,115,101,110,100, 40,114,101, -113,108,105,110,101, 41, 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111, -110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,115,101,110,100, -104,101, 97,100,101,114,115, 40,116,111,115,101,110,100, 41, 10, 32, 32, 32, 32, -108,111, 99, 97,108, 32, 99, 97,110,111,110,105, 99, 32, 61, 32,104,101, 97,100, -101,114,115, 46, 99, 97,110,111,110,105, 99, 10, 32, 32, 32, 32,108,111, 99, 97, -108, 32,104, 32, 61, 32, 34, 92,114, 92,110, 34, 10, 32, 32, 32, 32,102,111,114, - 32,102, 44, 32,118, 32,105,110, 32, 98, 97,115,101, 46,112, 97,105,114,115, 40, -116,111,115,101,110,100, 41, 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32,104, - 32, 61, 32, 40, 99, 97,110,111,110,105, 99, 91,102, 93, 32,111,114, 32,102, 41, - 32, 46, 46, 32, 34, 58, 32, 34, 32, 46, 46, 32,118, 32, 46, 46, 32, 34, 92,114, - 92,110, 34, 32, 46, 46, 32,104, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, - 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46, 99, 58,115,101,110, -100, 40,104, 41, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, 49, 10,101, -110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, - 95,105,110,100,101,120, 58,115,101,110,100, 98,111,100,121, 40,104,101, 97,100, -101,114,115, 44, 32,115,111,117,114, 99,101, 44, 32,115,116,101,112, 41, 10, 32, - 32, 32, 32,115,111,117,114, 99,101, 32, 61, 32,115,111,117,114, 99,101, 32,111, -114, 32,108,116,110, 49, 50, 46,115,111,117,114, 99,101, 46,101,109,112,116,121, - 40, 41, 10, 32, 32, 32, 32,115,116,101,112, 32, 61, 32,115,116,101,112, 32,111, -114, 32,108,116,110, 49, 50, 46,112,117,109,112, 46,115,116,101,112, 10, 32, 32, - 32, 32, 45, 45, 32,105,102, 32,119,101, 32,100,111,110, 39,116, 32,107,110,111, -119, 32,116,104,101, 32,115,105,122,101, 32,105,110, 32, 97,100,118, 97,110, 99, -101, 44, 32,115,101,110,100, 32, 99,104,117,110,107,101,100, 32, 97,110,100, 32, -104,111,112,101, 32,102,111,114, 32,116,104,101, 32, 98,101,115,116, 10, 32, 32, - 32, 32,108,111, 99, 97,108, 32,109,111,100,101, 32, 61, 32, 34,104,116,116,112, - 45, 99,104,117,110,107,101,100, 34, 10, 32, 32, 32, 32,105,102, 32,104,101, 97, -100,101,114,115, 91, 34, 99,111,110,116,101,110,116, 45,108,101,110,103,116,104, - 34, 93, 32,116,104,101,110, 32,109,111,100,101, 32, 61, 32, 34,107,101,101,112, - 45,111,112,101,110, 34, 32,101,110,100, 10, 32, 32, 32, 32,114,101,116,117,114, -110, 32,115,101,108,102, 46,116,114,121, 40,108,116,110, 49, 50, 46,112,117,109, -112, 46, 97,108,108, 40,115,111,117,114, 99,101, 44, 32,115,111, 99,107,101,116, - 46,115,105,110,107, 40,109,111,100,101, 44, 32,115,101,108,102, 46, 99, 41, 44, - 32,115,116,101,112, 41, 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111, -110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,114,101, 99,101, -105,118,101,115,116, 97,116,117,115,108,105,110,101, 40, 41, 10, 32, 32, 32, 32, -108,111, 99, 97,108, 32,115,116, 97,116,117,115, 32, 61, 32,115,101,108,102, 46, -116,114,121, 40,115,101,108,102, 46, 99, 58,114,101, 99,101,105,118,101, 40, 53, - 41, 41, 10, 32, 32, 32, 32, 45, 45, 32,105,100,101,110,116,105,102,121, 32, 72, - 84, 84, 80, 47, 48, 46, 57, 32,114,101,115,112,111,110,115,101,115, 44, 32,119, -104,105, 99,104, 32,100,111, 32,110,111,116, 32, 99,111,110,116, 97,105,110, 32, - 97, 32,115,116, 97,116,117,115, 32,108,105,110,101, 10, 32, 32, 32, 32, 45, 45, - 32,116,104,105,115, 32,105,115, 32,106,117,115,116, 32, 97, 32,104,101,117,114, -105,115,116,105, 99, 44, 32, 98,117,116, 32,105,115, 32,119,104, 97,116, 32,116, -104,101, 32, 82, 70, 67, 32,114,101, 99,111,109,109,101,110,100,115, 10, 32, 32, - 32, 32,105,102, 32,115,116, 97,116,117,115, 32,126, 61, 32, 34, 72, 84, 84, 80, - 47, 34, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,110,105,108, 44, 32, -115,116, 97,116,117,115, 32,101,110,100, 10, 32, 32, 32, 32, 45, 45, 32,111,116, -104,101,114,119,105,115,101, 32,112,114,111, 99,101,101,100, 32,114,101, 97,100, -105,110,103, 32, 97, 32,115,116, 97,116,117,115, 32,108,105,110,101, 10, 32, 32, - 32, 32,115,116, 97,116,117,115, 32, 61, 32,115,101,108,102, 46,116,114,121, 40, -115,101,108,102, 46, 99, 58,114,101, 99,101,105,118,101, 40, 34, 42,108, 34, 44, - 32,115,116, 97,116,117,115, 41, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, - 99,111,100,101, 32, 61, 32,115,111, 99,107,101,116, 46,115,107,105,112, 40, 50, - 44, 32,115,116,114,105,110,103, 46,102,105,110,100, 40,115,116, 97,116,117,115, - 44, 32, 34, 72, 84, 84, 80, 47, 37,100, 42, 37, 46, 37,100, 42, 32, 40, 37,100, - 37,100, 37,100, 41, 34, 41, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, -115,101,108,102, 46,116,114,121, 40, 98, 97,115,101, 46,116,111,110,117,109, 98, -101,114, 40, 99,111,100,101, 41, 44, 32,115,116, 97,116,117,115, 41, 10,101,110, -100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95, -105,110,100,101,120, 58,114,101, 99,101,105,118,101,104,101, 97,100,101,114,115, - 40, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,101,108,102, 46,116, -114,121, 40,114,101, 99,101,105,118,101,104,101, 97,100,101,114,115, 40,115,101, -108,102, 46, 99, 41, 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, - 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,114,101, 99,101,105, -118,101, 98,111,100,121, 40,104,101, 97,100,101,114,115, 44, 32,115,105,110,107, - 44, 32,115,116,101,112, 41, 10, 32, 32, 32, 32,115,105,110,107, 32, 61, 32,115, -105,110,107, 32,111,114, 32,108,116,110, 49, 50, 46,115,105,110,107, 46,110,117, -108,108, 40, 41, 10, 32, 32, 32, 32,115,116,101,112, 32, 61, 32,115,116,101,112, - 32,111,114, 32,108,116,110, 49, 50, 46,112,117,109,112, 46,115,116,101,112, 10, - 32, 32, 32, 32,108,111, 99, 97,108, 32,108,101,110,103,116,104, 32, 61, 32, 98, - 97,115,101, 46,116,111,110,117,109, 98,101,114, 40,104,101, 97,100,101,114,115, - 91, 34, 99,111,110,116,101,110,116, 45,108,101,110,103,116,104, 34, 93, 41, 10, - 32, 32, 32, 32,108,111, 99, 97,108, 32,116, 32, 61, 32,104,101, 97,100,101,114, -115, 91, 34,116,114, 97,110,115,102,101,114, 45,101,110, 99,111,100,105,110,103, - 34, 93, 32, 45, 45, 32,115,104,111,114,116, 99,117,116, 10, 32, 32, 32, 32,108, -111, 99, 97,108, 32,109,111,100,101, 32, 61, 32, 34,100,101,102, 97,117,108,116, - 34, 32, 45, 45, 32, 99,111,110,110,101, 99,116,105,111,110, 32, 99,108,111,115, -101, 10, 32, 32, 32, 32,105,102, 32,116, 32, 97,110,100, 32,116, 32,126, 61, 32, - 34,105,100,101,110,116,105,116,121, 34, 32,116,104,101,110, 32,109,111,100,101, - 32, 61, 32, 34,104,116,116,112, 45, 99,104,117,110,107,101,100, 34, 10, 32, 32, - 32, 32,101,108,115,101,105,102, 32, 98, 97,115,101, 46,116,111,110,117,109, 98, -101,114, 40,104,101, 97,100,101,114,115, 91, 34, 99,111,110,116,101,110,116, 45, -108,101,110,103,116,104, 34, 93, 41, 32,116,104,101,110, 32,109,111,100,101, 32, - 61, 32, 34, 98,121, 45,108,101,110,103,116,104, 34, 32,101,110,100, 10, 32, 32, - 32, 32,114,101,116,117,114,110, 32,115,101,108,102, 46,116,114,121, 40,108,116, -110, 49, 50, 46,112,117,109,112, 46, 97,108,108, 40,115,111, 99,107,101,116, 46, -115,111,117,114, 99,101, 40,109,111,100,101, 44, 32,115,101,108,102, 46, 99, 44, - 32,108,101,110,103,116,104, 41, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32,115,105, -110,107, 44, 32,115,116,101,112, 41, 41, 10,101,110,100, 10, 10,102,117,110, 99, -116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,114, -101, 99,101,105,118,101, 48, 57, 98,111,100,121, 40,115,116, 97,116,117,115, 44, - 32,115,105,110,107, 44, 32,115,116,101,112, 41, 10, 32, 32, 32, 32,108,111, 99, - 97,108, 32,115,111,117,114, 99,101, 32, 61, 32,108,116,110, 49, 50, 46,115,111, -117,114, 99,101, 46,114,101,119,105,110,100, 40,115,111, 99,107,101,116, 46,115, -111,117,114, 99,101, 40, 34,117,110,116,105,108, 45, 99,108,111,115,101,100, 34, - 44, 32,115,101,108,102, 46, 99, 41, 41, 10, 32, 32, 32, 32,115,111,117,114, 99, -101, 40,115,116, 97,116,117,115, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, - 32,115,101,108,102, 46,116,114,121, 40,108,116,110, 49, 50, 46,112,117,109,112, - 46, 97,108,108, 40,115,111,117,114, 99,101, 44, 32,115,105,110,107, 44, 32,115, -116,101,112, 41, 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, -109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58, 99,108,111,115,101, 40, - 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,101,108,102, 46, 99, 58, - 99,108,111,115,101, 40, 41, 10,101,110,100, 10, 10, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 72,105,103,104, 32,108, -101,118,101,108, 32, 72, 84, 84, 80, 32, 65, 80, 73, 10, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10,108,111, 99, 97,108, 32,102,117, -110, 99,116,105,111,110, 32, 97,100,106,117,115,116,117,114,105, 40,114,101,113, -116, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,117, 32, 61, 32,114,101,113, -116, 10, 32, 32, 32, 32, 45, 45, 32,105,102, 32,116,104,101,114,101, 32,105,115, - 32, 97, 32,112,114,111,120,121, 44, 32,119,101, 32,110,101,101,100, 32,116,104, -101, 32,102,117,108,108, 32,117,114,108, 46, 32,111,116,104,101,114,119,105,115, -101, 44, 32,106,117,115,116, 32, 97, 32,112, 97,114,116, 46, 10, 32, 32, 32, 32, -105,102, 32,110,111,116, 32,114,101,113,116, 46,112,114,111,120,121, 32, 97,110, -100, 32,110,111,116, 32, 95, 77, 46, 80, 82, 79, 88, 89, 32,116,104,101,110, 10, - 32, 32, 32, 32, 32, 32, 32, 32,117, 32, 61, 32,123, 10, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32,112, 97,116,104, 32, 61, 32,115,111, 99,107,101,116, 46,116, -114,121, 40,114,101,113,116, 46,112, 97,116,104, 44, 32, 34,105,110,118, 97,108, -105,100, 32,112, 97,116,104, 32, 39,110,105,108, 39, 34, 41, 44, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32,112, 97,114, 97,109,115, 32, 61, 32,114,101,113, -116, 46,112, 97,114, 97,109,115, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32,113,117,101,114,121, 32, 61, 32,114,101,113,116, 46,113,117,101,114,121, 44, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,102,114, 97,103,109,101,110,116, - 32, 61, 32,114,101,113,116, 46,102,114, 97,103,109,101,110,116, 10, 32, 32, 32, - 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,114, -101,116,117,114,110, 32,117,114,108, 46, 98,117,105,108,100, 40,117, 41, 10,101, -110,100, 10, 10,108,111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32, 97, -100,106,117,115,116,112,114,111,120,121, 40,114,101,113,116, 41, 10, 32, 32, 32, - 32,108,111, 99, 97,108, 32,112,114,111,120,121, 32, 61, 32,114,101,113,116, 46, -112,114,111,120,121, 32,111,114, 32, 95, 77, 46, 80, 82, 79, 88, 89, 10, 32, 32, - 32, 32,105,102, 32,112,114,111,120,121, 32,116,104,101,110, 10, 32, 32, 32, 32, - 32, 32, 32, 32,112,114,111,120,121, 32, 61, 32,117,114,108, 46,112, 97,114,115, -101, 40,112,114,111,120,121, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116, -117,114,110, 32,112,114,111,120,121, 46,104,111,115,116, 44, 32,112,114,111,120, -121, 46,112,111,114,116, 32,111,114, 32, 51, 49, 50, 56, 10, 32, 32, 32, 32,101, -108,115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32,114, -101,113,116, 46,104,111,115,116, 44, 32,114,101,113,116, 46,112,111,114,116, 10, - 32, 32, 32, 32,101,110,100, 10,101,110,100, 10, 10,108,111, 99, 97,108, 32,102, -117,110, 99,116,105,111,110, 32, 97,100,106,117,115,116,104,101, 97,100,101,114, -115, 40,114,101,113,116, 41, 10, 32, 32, 32, 32, 45, 45, 32,100,101,102, 97,117, -108,116, 32,104,101, 97,100,101,114,115, 10, 32, 32, 32, 32,108,111, 99, 97,108, - 32,104,111,115,116, 32, 61, 32,115,116,114,105,110,103, 46,103,115,117, 98, 40, -114,101,113,116, 46, 97,117,116,104,111,114,105,116,121, 44, 32, 34, 94, 46, 45, - 64, 34, 44, 32, 34, 34, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,108,111, -119,101,114, 32, 61, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 91, 34,117,115, -101,114, 45, 97,103,101,110,116, 34, 93, 32, 61, 32, 95, 77, 46, 85, 83, 69, 82, - 65, 71, 69, 78, 84, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 91, 34,104,111,115, -116, 34, 93, 32, 61, 32,104,111,115,116, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 91, 34, 99,111,110,110,101, 99,116,105,111,110, 34, 93, 32, 61, 32, 34, 99,108, -111,115,101, 44, 32, 84, 69, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 91, 34, -116,101, 34, 93, 32, 61, 32, 34,116,114, 97,105,108,101,114,115, 34, 10, 32, 32, - 32, 32,125, 10, 32, 32, 32, 32, 45, 45, 32,105,102, 32,119,101, 32,104, 97,118, -101, 32, 97,117,116,104,101,110,116,105, 99, 97,116,105,111,110, 32,105,110,102, -111,114,109, 97,116,105,111,110, 44, 32,112, 97,115,115, 32,105,116, 32, 97,108, -111,110,103, 10, 32, 32, 32, 32,105,102, 32,114,101,113,116, 46,117,115,101,114, - 32, 97,110,100, 32,114,101,113,116, 46,112, 97,115,115,119,111,114,100, 32,116, -104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,108,111,119,101,114, 91, 34, 97, -117,116,104,111,114,105,122, 97,116,105,111,110, 34, 93, 32, 61, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 66, 97,115,105, 99, 32, 34, 32, 46, 46, - 32, 32, 40,109,105,109,101, 46, 98, 54, 52, 40,114,101,113,116, 46,117,115,101, -114, 32, 46, 46, 32, 34, 58, 34, 32, 46, 46, 10, 9, 9,117,114,108, 46,117,110, -101,115, 99, 97,112,101, 40,114,101,113,116, 46,112, 97,115,115,119,111,114,100, - 41, 41, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 45, 45, 32,105, -102, 32,119,101, 32,104, 97,118,101, 32,112,114,111,120,121, 32, 97,117,116,104, -101,110,116,105, 99, 97,116,105,111,110, 32,105,110,102,111,114,109, 97,116,105, -111,110, 44, 32,112, 97,115,115, 32,105,116, 32, 97,108,111,110,103, 10, 32, 32, - 32, 32,108,111, 99, 97,108, 32,112,114,111,120,121, 32, 61, 32,114,101,113,116, - 46,112,114,111,120,121, 32,111,114, 32, 95, 77, 46, 80, 82, 79, 88, 89, 10, 32, - 32, 32, 32,105,102, 32,112,114,111,120,121, 32,116,104,101,110, 10, 32, 32, 32, - 32, 32, 32, 32, 32,112,114,111,120,121, 32, 61, 32,117,114,108, 46,112, 97,114, -115,101, 40,112,114,111,120,121, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, - 32,112,114,111,120,121, 46,117,115,101,114, 32, 97,110,100, 32,112,114,111,120, -121, 46,112, 97,115,115,119,111,114,100, 32,116,104,101,110, 10, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32,108,111,119,101,114, 91, 34,112,114,111,120,121, - 45, 97,117,116,104,111,114,105,122, 97,116,105,111,110, 34, 93, 32, 61, 10, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 66, 97,115,105, - 99, 32, 34, 32, 46, 46, 32, 32, 40,109,105,109,101, 46, 98, 54, 52, 40,112,114, -111,120,121, 46,117,115,101,114, 32, 46, 46, 32, 34, 58, 34, 32, 46, 46, 32,112, -114,111,120,121, 46,112, 97,115,115,119,111,114,100, 41, 41, 10, 32, 32, 32, 32, - 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, - 45, 45, 32,111,118,101,114,114,105,100,101, 32,119,105,116,104, 32,117,115,101, -114, 32,104,101, 97,100,101,114,115, 10, 32, 32, 32, 32,102,111,114, 32,105, 44, -118, 32,105,110, 32, 98, 97,115,101, 46,112, 97,105,114,115, 40,114,101,113,116, - 46,104,101, 97,100,101,114,115, 32,111,114, 32,108,111,119,101,114, 41, 32,100, -111, 10, 32, 32, 32, 32, 32, 32, 32, 32,108,111,119,101,114, 91,115,116,114,105, -110,103, 46,108,111,119,101,114, 40,105, 41, 93, 32, 61, 32,118, 10, 32, 32, 32, - 32,101,110,100, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,108,111,119,101, -114, 10,101,110,100, 10, 10, 45, 45, 32,100,101,102, 97,117,108,116, 32,117,114, -108, 32,112, 97,114,116,115, 10,108,111, 99, 97,108, 32,100,101,102, 97,117,108, -116, 32, 61, 32,123, 10, 32, 32, 32, 32,104,111,115,116, 32, 61, 32, 34, 34, 44, - 10, 32, 32, 32, 32,112,111,114,116, 32, 61, 32, 80, 79, 82, 84, 44, 10, 32, 32, - 32, 32,112, 97,116,104, 32, 61, 34, 47, 34, 44, 10, 32, 32, 32, 32,115, 99,104, -101,109,101, 32, 61, 32, 34,104,116,116,112, 34, 10,125, 10, 10,108,111, 99, 97, -108, 32,102,117,110, 99,116,105,111,110, 32, 97,100,106,117,115,116,114,101,113, -117,101,115,116, 40,114,101,113,116, 41, 10, 32, 32, 32, 32, 45, 45, 32,112, 97, -114,115,101, 32,117,114,108, 32,105,102, 32,112,114,111,118,105,100,101,100, 10, - 32, 32, 32, 32,108,111, 99, 97,108, 32,110,114,101,113,116, 32, 61, 32,114,101, -113,116, 46,117,114,108, 32, 97,110,100, 32,117,114,108, 46,112, 97,114,115,101, - 40,114,101,113,116, 46,117,114,108, 44, 32,100,101,102, 97,117,108,116, 41, 32, -111,114, 32,123,125, 10, 32, 32, 32, 32, 45, 45, 32,101,120,112,108,105, 99,105, -116, 32, 99,111,109,112,111,110,101,110,116,115, 32,111,118,101,114,114,105,100, -101, 32,117,114,108, 10, 32, 32, 32, 32,102,111,114, 32,105, 44,118, 32,105,110, - 32, 98, 97,115,101, 46,112, 97,105,114,115, 40,114,101,113,116, 41, 32,100,111, - 32,110,114,101,113,116, 91,105, 93, 32, 61, 32,118, 32,101,110,100, 10, 32, 32, - 32, 32,105,102, 32,110,114,101,113,116, 46,112,111,114,116, 32, 61, 61, 32, 34, - 34, 32,116,104,101,110, 32,110,114,101,113,116, 46,112,111,114,116, 32, 61, 32, - 80, 79, 82, 84, 32,101,110,100, 10, 32, 32, 32, 32,105,102, 32,110,111,116, 32, - 40,110,114,101,113,116, 46,104,111,115,116, 32, 97,110,100, 32,110,114,101,113, -116, 46,104,111,115,116, 32,126, 61, 32, 34, 34, 41, 32,116,104,101,110, 10, 32, - 32, 32, 32, 32, 32, 32, 32,115,111, 99,107,101,116, 46,116,114,121, 40,110,105, -108, 44, 32, 34,105,110,118, 97,108,105,100, 32,104,111,115,116, 32, 39, 34, 32, - 46, 46, 32, 98, 97,115,101, 46,116,111,115,116,114,105,110,103, 40,110,114,101, -113,116, 46,104,111,115,116, 41, 32, 46, 46, 32, 34, 39, 34, 41, 10, 32, 32, 32, - 32,101,110,100, 10, 32, 32, 32, 32, 45, 45, 32, 99,111,109,112,117,116,101, 32, -117,114,105, 32,105,102, 32,117,115,101,114, 32,104, 97,115,110, 39,116, 32,111, -118,101,114,114,105,100,101,110, 10, 32, 32, 32, 32,110,114,101,113,116, 46,117, -114,105, 32, 61, 32,114,101,113,116, 46,117,114,105, 32,111,114, 32, 97,100,106, -117,115,116,117,114,105, 40,110,114,101,113,116, 41, 10, 32, 32, 32, 32, 45, 45, - 32, 97,100,106,117,115,116, 32,104,101, 97,100,101,114,115, 32,105,110, 32,114, -101,113,117,101,115,116, 10, 32, 32, 32, 32,110,114,101,113,116, 46,104,101, 97, -100,101,114,115, 32, 61, 32, 97,100,106,117,115,116,104,101, 97,100,101,114,115, - 40,110,114,101,113,116, 41, 10, 32, 32, 32, 32, 45, 45, 32, 97,106,117,115,116, - 32,104,111,115,116, 32, 97,110,100, 32,112,111,114,116, 32,105,102, 32,116,104, -101,114,101, 32,105,115, 32, 97, 32,112,114,111,120,121, 10, 32, 32, 32, 32,110, -114,101,113,116, 46,104,111,115,116, 44, 32,110,114,101,113,116, 46,112,111,114, -116, 32, 61, 32, 97,100,106,117,115,116,112,114,111,120,121, 40,110,114,101,113, -116, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,110,114,101,113,116, 10, -101,110,100, 10, 10,108,111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32, -115,104,111,117,108,100,114,101,100,105,114,101, 99,116, 40,114,101,113,116, 44, - 32, 99,111,100,101, 44, 32,104,101, 97,100,101,114,115, 41, 10, 32, 32, 32, 32, -108,111, 99, 97,108, 32,108,111, 99, 97,116,105,111,110, 32, 61, 32,104,101, 97, -100,101,114,115, 46,108,111, 99, 97,116,105,111,110, 10, 32, 32, 32, 32,105,102, - 32,110,111,116, 32,108,111, 99, 97,116,105,111,110, 32,116,104,101,110, 32,114, -101,116,117,114,110, 32,102, 97,108,115,101, 32,101,110,100, 10, 32, 32, 32, 32, -108,111, 99, 97,116,105,111,110, 32, 61, 32,115,116,114,105,110,103, 46,103,115, -117, 98, 40,108,111, 99, 97,116,105,111,110, 44, 32, 34, 37,115, 34, 44, 32, 34, - 34, 41, 10, 32, 32, 32, 32,105,102, 32,108,111, 99, 97,116,105,111,110, 32, 61, - 61, 32, 34, 34, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,102, 97,108, -115,101, 32,101,110,100, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,115, 99,104, -101,109,101, 32, 61, 32,115,116,114,105,110,103, 46,109, 97,116, 99,104, 40,108, -111, 99, 97,116,105,111,110, 44, 32, 34, 94, 40, 91, 37,119, 93, 91, 37,119, 37, - 43, 37, 45, 37, 46, 93, 42, 41, 37, 58, 34, 41, 10, 32, 32, 32, 32,105,102, 32, -115, 99,104,101,109,101, 32, 97,110,100, 32,110,111,116, 32, 83, 67, 72, 69, 77, - 69, 83, 91,115, 99,104,101,109,101, 93, 32,116,104,101,110, 32,114,101,116,117, -114,110, 32,102, 97,108,115,101, 32,101,110,100, 10, 32, 32, 32, 32,114,101,116, -117,114,110, 32, 40,114,101,113,116, 46,114,101,100,105,114,101, 99,116, 32,126, - 61, 32,102, 97,108,115,101, 41, 32, 97,110,100, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 40, 99,111,100,101, 32, 61, 61, 32, 51, 48, 49, 32,111,114, 32, - 99,111,100,101, 32, 61, 61, 32, 51, 48, 50, 32,111,114, 32, 99,111,100,101, 32, - 61, 61, 32, 51, 48, 51, 32,111,114, 32, 99,111,100,101, 32, 61, 61, 32, 51, 48, - 55, 41, 32, 97,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 40,110, -111,116, 32,114,101,113,116, 46,109,101,116,104,111,100, 32,111,114, 32,114,101, -113,116, 46,109,101,116,104,111,100, 32, 61, 61, 32, 34, 71, 69, 84, 34, 32,111, -114, 32,114,101,113,116, 46,109,101,116,104,111,100, 32, 61, 61, 32, 34, 72, 69, - 65, 68, 34, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 97,110,100, 32, - 40,110,111,116, 32,114,101,113,116, 46,110,114,101,100,105,114,101, 99,116,115, - 32,111,114, 32,114,101,113,116, 46,110,114,101,100,105,114,101, 99,116,115, 32, - 60, 32, 53, 41, 10,101,110,100, 10, 10,108,111, 99, 97,108, 32,102,117,110, 99, -116,105,111,110, 32,115,104,111,117,108,100,114,101, 99,101,105,118,101, 98,111, -100,121, 40,114,101,113,116, 44, 32, 99,111,100,101, 41, 10, 32, 32, 32, 32,105, -102, 32,114,101,113,116, 46,109,101,116,104,111,100, 32, 61, 61, 32, 34, 72, 69, - 65, 68, 34, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,110,105,108, 32, -101,110,100, 10, 32, 32, 32, 32,105,102, 32, 99,111,100,101, 32, 61, 61, 32, 50, - 48, 52, 32,111,114, 32, 99,111,100,101, 32, 61, 61, 32, 51, 48, 52, 32,116,104, -101,110, 32,114,101,116,117,114,110, 32,110,105,108, 32,101,110,100, 10, 32, 32, - 32, 32,105,102, 32, 99,111,100,101, 32, 62, 61, 32, 49, 48, 48, 32, 97,110,100, - 32, 99,111,100,101, 32, 60, 32, 50, 48, 48, 32,116,104,101,110, 32,114,101,116, -117,114,110, 32,110,105,108, 32,101,110,100, 10, 32, 32, 32, 32,114,101,116,117, -114,110, 32, 49, 10,101,110,100, 10, 10, 45, 45, 32,102,111,114,119, 97,114,100, - 32,100,101, 99,108, 97,114, 97,116,105,111,110,115, 10,108,111, 99, 97,108, 32, -116,114,101,113,117,101,115,116, 44, 32,116,114,101,100,105,114,101, 99,116, 10, - 10, 45, 45, 91, 91,108,111, 99, 97,108, 93, 93, 32,102,117,110, 99,116,105,111, -110, 32,116,114,101,100,105,114,101, 99,116, 40,114,101,113,116, 44, 32,108,111, - 99, 97,116,105,111,110, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,114,101, -115,117,108,116, 44, 32, 99,111,100,101, 44, 32,104,101, 97,100,101,114,115, 44, - 32,115,116, 97,116,117,115, 32, 61, 32,116,114,101,113,117,101,115,116, 32,123, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 32,116,104,101, 32, 82, 70, 67, 32, -115, 97,121,115, 32,116,104,101, 32,114,101,100,105,114,101, 99,116, 32, 85, 82, - 76, 32,104, 97,115, 32,116,111, 32, 98,101, 32, 97, 98,115,111,108,117,116,101, - 44, 32, 98,117,116, 32,115,111,109,101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 45, - 45, 32,115,101,114,118,101,114,115, 32,100,111, 32,110,111,116, 32,114,101,115, -112,101, 99,116, 32,116,104, 97,116, 10, 32, 32, 32, 32, 32, 32, 32, 32,117,114, -108, 32, 61, 32,117,114,108, 46, 97, 98,115,111,108,117,116,101, 40,114,101,113, -116, 46,117,114,108, 44, 32,108,111, 99, 97,116,105,111,110, 41, 44, 10, 32, 32, - 32, 32, 32, 32, 32, 32,115,111,117,114, 99,101, 32, 61, 32,114,101,113,116, 46, -115,111,117,114, 99,101, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32,115,105,110,107, - 32, 61, 32,114,101,113,116, 46,115,105,110,107, 44, 10, 32, 32, 32, 32, 32, 32, - 32, 32,104,101, 97,100,101,114,115, 32, 61, 32,114,101,113,116, 46,104,101, 97, -100,101,114,115, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32,112,114,111,120,121, 32, - 61, 32,114,101,113,116, 46,112,114,111,120,121, 44, 10, 32, 32, 32, 32, 32, 32, - 32, 32,110,114,101,100,105,114,101, 99,116,115, 32, 61, 32, 40,114,101,113,116, - 46,110,114,101,100,105,114,101, 99,116,115, 32,111,114, 32, 48, 41, 32, 43, 32, - 49, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99,114,101, 97,116,101, 32, 61, 32, -114,101,113,116, 46, 99,114,101, 97,116,101, 10, 32, 32, 32, 32,125, 10, 32, 32, - 32, 32, 45, 45, 32,112, 97,115,115, 32,108,111, 99, 97,116,105,111,110, 32,104, -101, 97,100,101,114, 32, 98, 97, 99,107, 32, 97,115, 32, 97, 32,104,105,110,116, - 32,119,101, 32,114,101,100,105,114,101, 99,116,101,100, 10, 32, 32, 32, 32,104, -101, 97,100,101,114,115, 32, 61, 32,104,101, 97,100,101,114,115, 32,111,114, 32, -123,125, 10, 32, 32, 32, 32,104,101, 97,100,101,114,115, 46,108,111, 99, 97,116, -105,111,110, 32, 61, 32,104,101, 97,100,101,114,115, 46,108,111, 99, 97,116,105, -111,110, 32,111,114, 32,108,111, 99, 97,116,105,111,110, 10, 32, 32, 32, 32,114, -101,116,117,114,110, 32,114,101,115,117,108,116, 44, 32, 99,111,100,101, 44, 32, -104,101, 97,100,101,114,115, 44, 32,115,116, 97,116,117,115, 10,101,110,100, 10, - 10, 45, 45, 91, 91,108,111, 99, 97,108, 93, 93, 32,102,117,110, 99,116,105,111, -110, 32,116,114,101,113,117,101,115,116, 40,114,101,113,116, 41, 10, 32, 32, 32, - 32, 45, 45, 32,119,101, 32,108,111,111,112, 32,117,110,116,105,108, 32,119,101, - 32,103,101,116, 32,119,104, 97,116, 32,119,101, 32,119, 97,110,116, 44, 32,111, -114, 10, 32, 32, 32, 32, 45, 45, 32,117,110,116,105,108, 32,119,101, 32, 97,114, -101, 32,115,117,114,101, 32,116,104,101,114,101, 32,105,115, 32,110,111, 32,119, - 97,121, 32,116,111, 32,103,101,116, 32,105,116, 10, 32, 32, 32, 32,108,111, 99, - 97,108, 32,110,114,101,113,116, 32, 61, 32, 97,100,106,117,115,116,114,101,113, -117,101,115,116, 40,114,101,113,116, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, - 32,104, 32, 61, 32, 95, 77, 46,111,112,101,110, 40,110,114,101,113,116, 46,104, -111,115,116, 44, 32,110,114,101,113,116, 46,112,111,114,116, 44, 32,110,114,101, -113,116, 46, 99,114,101, 97,116,101, 41, 10, 32, 32, 32, 32, 45, 45, 32,115,101, -110,100, 32,114,101,113,117,101,115,116, 32,108,105,110,101, 32, 97,110,100, 32, -104,101, 97,100,101,114,115, 10, 32, 32, 32, 32,104, 58,115,101,110,100,114,101, -113,117,101,115,116,108,105,110,101, 40,110,114,101,113,116, 46,109,101,116,104, -111,100, 44, 32,110,114,101,113,116, 46,117,114,105, 41, 10, 32, 32, 32, 32,104, - 58,115,101,110,100,104,101, 97,100,101,114,115, 40,110,114,101,113,116, 46,104, -101, 97,100,101,114,115, 41, 10, 32, 32, 32, 32, 45, 45, 32,105,102, 32,116,104, -101,114,101, 32,105,115, 32, 97, 32, 98,111,100,121, 44, 32,115,101,110,100, 32, -105,116, 10, 32, 32, 32, 32,105,102, 32,110,114,101,113,116, 46,115,111,117,114, - 99,101, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,104, 58,115,101, -110,100, 98,111,100,121, 40,110,114,101,113,116, 46,104,101, 97,100,101,114,115, - 44, 32,110,114,101,113,116, 46,115,111,117,114, 99,101, 44, 32,110,114,101,113, -116, 46,115,116,101,112, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, -108,111, 99, 97,108, 32, 99,111,100,101, 44, 32,115,116, 97,116,117,115, 32, 61, - 32,104, 58,114,101, 99,101,105,118,101,115,116, 97,116,117,115,108,105,110,101, - 40, 41, 10, 32, 32, 32, 32, 45, 45, 32,105,102, 32,105,116, 32,105,115, 32, 97, -110, 32, 72, 84, 84, 80, 47, 48, 46, 57, 32,115,101,114,118,101,114, 44, 32,115, -105,109,112,108,121, 32,103,101,116, 32,116,104,101, 32, 98,111,100,121, 32, 97, -110,100, 32,119,101, 32, 97,114,101, 32,100,111,110,101, 10, 32, 32, 32, 32,105, -102, 32,110,111,116, 32, 99,111,100,101, 32,116,104,101,110, 10, 32, 32, 32, 32, - 32, 32, 32, 32,104, 58,114,101, 99,101,105,118,101, 48, 57, 98,111,100,121, 40, -115,116, 97,116,117,115, 44, 32,110,114,101,113,116, 46,115,105,110,107, 44, 32, -110,114,101,113,116, 46,115,116,101,112, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, -114,101,116,117,114,110, 32, 49, 44, 32, 50, 48, 48, 10, 32, 32, 32, 32,101,110, -100, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,104,101, 97,100,101,114,115, 10, - 32, 32, 32, 32, 45, 45, 32,105,103,110,111,114,101, 32, 97,110,121, 32, 49, 48, - 48, 45, 99,111,110,116,105,110,117,101, 32,109,101,115,115, 97,103,101,115, 10, - 32, 32, 32, 32,119,104,105,108,101, 32, 99,111,100,101, 32, 61, 61, 32, 49, 48, - 48, 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32,104,101, 97,100,101,114,115, - 32, 61, 32,104, 58,114,101, 99,101,105,118,101,104,101, 97,100,101,114,115, 40, - 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99,111,100,101, 44, 32,115,116, 97,116, -117,115, 32, 61, 32,104, 58,114,101, 99,101,105,118,101,115,116, 97,116,117,115, -108,105,110,101, 40, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,104, -101, 97,100,101,114,115, 32, 61, 32,104, 58,114,101, 99,101,105,118,101,104,101, - 97,100,101,114,115, 40, 41, 10, 32, 32, 32, 32, 45, 45, 32, 97,116, 32,116,104, -105,115, 32,112,111,105,110,116, 32,119,101, 32,115,104,111,117,108,100, 32,104, - 97,118,101, 32, 97, 32,104,111,110,101,115,116, 32,114,101,112,108,121, 32,102, -114,111,109, 32,116,104,101, 32,115,101,114,118,101,114, 10, 32, 32, 32, 32, 45, - 45, 32,119,101, 32, 99, 97,110, 39,116, 32,114,101,100,105,114,101, 99,116, 32, -105,102, 32,119,101, 32, 97,108,114,101, 97,100,121, 32,117,115,101,100, 32,116, -104,101, 32,115,111,117,114, 99,101, 44, 32,115,111, 32,119,101, 32,114,101,112, -111,114,116, 32,116,104,101, 32,101,114,114,111,114, 10, 32, 32, 32, 32,105,102, - 32,115,104,111,117,108,100,114,101,100,105,114,101, 99,116, 40,110,114,101,113, -116, 44, 32, 99,111,100,101, 44, 32,104,101, 97,100,101,114,115, 41, 32, 97,110, -100, 32,110,111,116, 32,110,114,101,113,116, 46,115,111,117,114, 99,101, 32,116, -104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,104, 58, 99,108,111,115,101, 40, - 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32,116,114,101, -100,105,114,101, 99,116, 40,114,101,113,116, 44, 32,104,101, 97,100,101,114,115, - 46,108,111, 99, 97,116,105,111,110, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, - 32, 32, 32, 45, 45, 32,104,101,114,101, 32,119,101, 32, 97,114,101, 32,102,105, -110, 97,108,108,121, 32,100,111,110,101, 10, 32, 32, 32, 32,105,102, 32,115,104, -111,117,108,100,114,101, 99,101,105,118,101, 98,111,100,121, 40,110,114,101,113, -116, 44, 32, 99,111,100,101, 41, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, - 32, 32,104, 58,114,101, 99,101,105,118,101, 98,111,100,121, 40,104,101, 97,100, -101,114,115, 44, 32,110,114,101,113,116, 46,115,105,110,107, 44, 32,110,114,101, -113,116, 46,115,116,101,112, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, - 32,104, 58, 99,108,111,115,101, 40, 41, 10, 32, 32, 32, 32,114,101,116,117,114, -110, 32, 49, 44, 32, 99,111,100,101, 44, 32,104,101, 97,100,101,114,115, 44, 32, -115,116, 97,116,117,115, 10,101,110,100, 10, 10, 45, 45, 32,116,117,114,110,115, - 32, 97,110, 32,117,114,108, 32, 97,110,100, 32, 97, 32, 98,111,100,121, 32,105, -110,116,111, 32, 97, 32,103,101,110,101,114,105, 99, 32,114,101,113,117,101,115, -116, 10,108,111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32,103,101,110, -101,114,105, 99,102,111,114,109, 40,117, 44, 32, 98, 41, 10, 32, 32, 32, 32,108, -111, 99, 97,108, 32,116, 32, 61, 32,123,125, 10, 32, 32, 32, 32,108,111, 99, 97, -108, 32,114,101,113,116, 32, 61, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32,117, -114,108, 32, 61, 32,117, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32,115,105,110,107, - 32, 61, 32,108,116,110, 49, 50, 46,115,105,110,107, 46,116, 97, 98,108,101, 40, -116, 41, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32,116, 97,114,103,101,116, 32, 61, - 32,116, 10, 32, 32, 32, 32,125, 10, 32, 32, 32, 32,105,102, 32, 98, 32,116,104, -101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,113,116, 46,115,111,117,114, - 99,101, 32, 61, 32,108,116,110, 49, 50, 46,115,111,117,114, 99,101, 46,115,116, -114,105,110,103, 40, 98, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,113,116, - 46,104,101, 97,100,101,114,115, 32, 61, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 91, 34, 99,111,110,116,101,110,116, 45,108,101,110,103,116, -104, 34, 93, 32, 61, 32,115,116,114,105,110,103, 46,108,101,110, 40, 98, 41, 44, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 91, 34, 99,111,110,116,101, -110,116, 45,116,121,112,101, 34, 93, 32, 61, 32, 34, 97,112,112,108,105, 99, 97, -116,105,111,110, 47,120, 45,119,119,119, 45,102,111,114,109, 45,117,114,108,101, -110, 99,111,100,101,100, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, - 32, 32, 32, 32, 32, 32,114,101,113,116, 46,109,101,116,104,111,100, 32, 61, 32, - 34, 80, 79, 83, 84, 34, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,114, -101,116,117,114,110, 32,114,101,113,116, 10,101,110,100, 10, 10, 95, 77, 46,103, -101,110,101,114,105, 99,102,111,114,109, 32, 61, 32,103,101,110,101,114,105, 99, -102,111,114,109, 10, 10,108,111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, - 32,115,114,101,113,117,101,115,116, 40,117, 44, 32, 98, 41, 10, 32, 32, 32, 32, -108,111, 99, 97,108, 32,114,101,113,116, 32, 61, 32,103,101,110,101,114,105, 99, -102,111,114,109, 40,117, 44, 32, 98, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, - 32, 95, 44, 32, 99,111,100,101, 44, 32,104,101, 97,100,101,114,115, 44, 32,115, -116, 97,116,117,115, 32, 61, 32,116,114,101,113,117,101,115,116, 40,114,101,113, -116, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,116, 97, 98,108,101, 46, - 99,111,110, 99, 97,116, 40,114,101,113,116, 46,116, 97,114,103,101,116, 41, 44, - 32, 99,111,100,101, 44, 32,104,101, 97,100,101,114,115, 44, 32,115,116, 97,116, -117,115, 10,101,110,100, 10, 10, 95, 77, 46,114,101,113,117,101,115,116, 32, 61, - 32,115,111, 99,107,101,116, 46,112,114,111,116,101, 99,116, 40,102,117,110, 99, -116,105,111,110, 40,114,101,113,116, 44, 32, 98,111,100,121, 41, 10, 32, 32, 32, - 32,105,102, 32, 98, 97,115,101, 46,116,121,112,101, 40,114,101,113,116, 41, 32, - 61, 61, 32, 34,115,116,114,105,110,103, 34, 32,116,104,101,110, 32,114,101,116, -117,114,110, 32,115,114,101,113,117,101,115,116, 40,114,101,113,116, 44, 32, 98, -111,100,121, 41, 10, 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114,110, - 32,116,114,101,113,117,101,115,116, 40,114,101,113,116, 41, 32,101,110,100, 10, -101,110,100, 41, 10, 10,114,101,116,117,114,110, 32, 95, 77, 10, -}; - - if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"http.lua")==0) lua_call(L, 0, LUA_MULTRET); -} diff --git a/libraries/luasocket/libluasocket/inet.c b/libraries/luasocket/libluasocket/inet.c index 7b2433ec3..138c9abe8 100644 --- a/libraries/luasocket/libluasocket/inet.c +++ b/libraries/luasocket/libluasocket/inet.c @@ -2,16 +2,13 @@ * Internet domain functions * LuaSocket toolkit \*=========================================================================*/ +#include "luasocket.h" +#include "inet.h" + #include #include #include -#include "lua.h" -#include "lauxlib.h" -#include "compat.h" - -#include "inet.h" - /*=========================================================================*\ * Internal function prototypes. \*=========================================================================*/ @@ -32,9 +29,6 @@ static luaL_Reg func[] = { { NULL, NULL} }; -/*=========================================================================*\ -* Exported functions -\*=========================================================================*/ /*-------------------------------------------------------------------------*\ * Initializes module \*-------------------------------------------------------------------------*/ @@ -247,7 +241,7 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family) int err; struct sockaddr_storage peer; socklen_t peer_len = sizeof(peer); - char name[INET_ADDRSTRLEN]; + char name[INET6_ADDRSTRLEN]; char port[6]; /* 65535 = 5 bytes + 0 to terminate it */ if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { lua_pushnil(L); @@ -255,18 +249,18 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family) return 2; } err = getnameinfo((struct sockaddr *) &peer, peer_len, - name, INET_ADDRSTRLEN, + name, INET6_ADDRSTRLEN, port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); if (err) { lua_pushnil(L); - lua_pushstring(L, gai_strerror(err)); + lua_pushstring(L, LUA_GAI_STRERROR(err)); return 2; } lua_pushstring(L, name); lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10)); switch (family) { case AF_INET: lua_pushliteral(L, "inet"); break; - // case AF_INET6: lua_pushliteral(L, "inet6"); break; + case AF_INET6: lua_pushliteral(L, "inet6"); break; case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; default: lua_pushliteral(L, "unknown"); break; } @@ -281,7 +275,7 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) int err; struct sockaddr_storage peer; socklen_t peer_len = sizeof(peer); - char name[INET_ADDRSTRLEN]; + char name[INET6_ADDRSTRLEN]; char port[6]; /* 65535 = 5 bytes + 0 to terminate it */ if (getsockname(*ps, (SA *) &peer, &peer_len) < 0) { lua_pushnil(L); @@ -289,17 +283,17 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) return 2; } err=getnameinfo((struct sockaddr *)&peer, peer_len, - name, INET_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV); + name, INET6_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV); if (err) { lua_pushnil(L); - lua_pushstring(L, gai_strerror(err)); + lua_pushstring(L, LUA_GAI_STRERROR(err)); return 2; } lua_pushstring(L, name); lua_pushstring(L, port); switch (family) { case AF_INET: lua_pushliteral(L, "inet"); break; - // case AF_INET6: lua_pushliteral(L, "inet6"); break; + case AF_INET6: lua_pushliteral(L, "inet6"); break; case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; default: lua_pushliteral(L, "unknown"); break; } @@ -354,10 +348,10 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp) \*-------------------------------------------------------------------------*/ const char *inet_trycreate(p_socket ps, int family, int type, int protocol) { const char *err = socket_strerror(socket_create(ps, family, type, protocol)); - // if (err == NULL && family == AF_INET6) { - // int yes = 1; - // setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); - // } + if (err == NULL && family == AF_INET6) { + int yes = 1; + setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); + } return err; } @@ -367,9 +361,7 @@ const char *inet_trycreate(p_socket ps, int family, int type, int protocol) { const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) { switch (family) { - case AF_INET: - default: - { + case AF_INET: { struct sockaddr_in sin; memset((char *) &sin, 0, sizeof(sin)); sin.sin_family = AF_UNSPEC; @@ -377,15 +369,15 @@ const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) return socket_strerror(socket_connect(ps, (SA *) &sin, sizeof(sin), tm)); } - // case AF_INET6: { - // struct sockaddr_in6 sin6; - // struct in6_addr addrany = IN6ADDR_ANY_INIT; - // memset((char *) &sin6, 0, sizeof(sin6)); - // sin6.sin6_family = AF_UNSPEC; - // sin6.sin6_addr = addrany; - // return socket_strerror(socket_connect(ps, (SA *) &sin6, - // sizeof(sin6), tm)); - // } + case AF_INET6: { + struct sockaddr_in6 sin6; + struct in6_addr addrany = IN6ADDR_ANY_INIT; + memset((char *) &sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_UNSPEC; + sin6.sin6_addr = addrany; + return socket_strerror(socket_connect(ps, (SA *) &sin6, + sizeof(sin6), tm)); + } } return NULL; } @@ -444,7 +436,7 @@ const char *inet_tryaccept(p_socket server, int family, p_socket client, socklen_t len; t_sockaddr_storage addr; switch (family) { - // case AF_INET6: len = sizeof(struct sockaddr_in6); break; + case AF_INET6: len = sizeof(struct sockaddr_in6); break; case AF_INET: len = sizeof(struct sockaddr_in); break; default: len = sizeof(addr); break; } diff --git a/libraries/luasocket/libluasocket/inet.h b/libraries/luasocket/libluasocket/inet.h index feb3541d4..5618b61b3 100644 --- a/libraries/luasocket/libluasocket/inet.h +++ b/libraries/luasocket/libluasocket/inet.h @@ -14,7 +14,7 @@ * * The Lua functions toip and tohostname are also implemented here. \*=========================================================================*/ -#include "lua.h" +#include "luasocket.h" #include "socket.h" #include "timeout.h" @@ -22,21 +22,23 @@ #define LUASOCKET_INET_ATON #endif +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + int inet_open(lua_State *L); -const char *inet_trycreate(p_socket ps, int family, int type, int protocol); -const char *inet_tryconnect(p_socket ps, int *family, const char *address, - const char *serv, p_timeout tm, struct addrinfo *connecthints); -const char *inet_trybind(p_socket ps, int *family, const char *address, - const char *serv, struct addrinfo *bindhints); -const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm); -const char *inet_tryaccept(p_socket server, int family, p_socket client, p_timeout tm); +int inet_optfamily(lua_State* L, int narg, const char* def); +int inet_optsocktype(lua_State* L, int narg, const char* def); int inet_meth_getpeername(lua_State *L, p_socket ps, int family); int inet_meth_getsockname(lua_State *L, p_socket ps, int family); -int inet_optfamily(lua_State* L, int narg, const char* def); -int inet_optsocktype(lua_State* L, int narg, const char* def); +const char *inet_trycreate(p_socket ps, int family, int type, int protocol); +const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm); +const char *inet_tryconnect(p_socket ps, int *family, const char *address, const char *serv, p_timeout tm, struct addrinfo *connecthints); +const char *inet_tryaccept(p_socket server, int family, p_socket client, p_timeout tm); +const char *inet_trybind(p_socket ps, int *family, const char *address, const char *serv, struct addrinfo *bindhints); #ifdef LUASOCKET_INET_ATON int inet_aton(const char *cp, struct in_addr *inp); @@ -47,4 +49,8 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt); int inet_pton(int af, const char *src, void *dst); #endif +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + #endif /* INET_H */ diff --git a/libraries/luasocket/libluasocket/io.c b/libraries/luasocket/libluasocket/io.c index a4230ce82..5ad4b3afc 100644 --- a/libraries/luasocket/libluasocket/io.c +++ b/libraries/luasocket/libluasocket/io.c @@ -2,11 +2,9 @@ * Input/Output abstraction * LuaSocket toolkit \*=========================================================================*/ +#include "luasocket.h" #include "io.h" -/*=========================================================================*\ -* Exported functions -\*=========================================================================*/ /*-------------------------------------------------------------------------*\ * Initializes C structure \*-------------------------------------------------------------------------*/ diff --git a/libraries/luasocket/libluasocket/io.h b/libraries/luasocket/libluasocket/io.h index 8cca08a86..b8a54df6e 100644 --- a/libraries/luasocket/libluasocket/io.h +++ b/libraries/luasocket/libluasocket/io.h @@ -12,9 +12,7 @@ * The module socket.h implements this interface, and thus the module tcp.h * is very simple. \*=========================================================================*/ -#include -#include "lua.h" - +#include "luasocket.h" #include "timeout.h" /* IO error codes */ @@ -58,8 +56,15 @@ typedef struct t_io_ { } t_io; typedef t_io *p_io; +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx); const char *io_strerror(int err); -#endif /* IO_H */ +#ifndef _WIN32 +#pragma GCC visibility pop +#endif +#endif /* IO_H */ diff --git a/libraries/luasocket/libluasocket/libluasocket.c b/libraries/luasocket/libluasocket/libluasocket.c deleted file mode 100644 index 86df6b949..000000000 --- a/libraries/luasocket/libluasocket/libluasocket.c +++ /dev/null @@ -1,115 +0,0 @@ -/*=========================================================================*\ -* LuaSocket toolkit -* Networking support for the Lua language -* Diego Nehab -* 26/11/1999 -* -* This library is part of an effort to progressively increase the network -* connectivity of the Lua language. The Lua interface to networking -* functions follows the Sockets API closely, trying to simplify all tasks -* involved in setting up both client and server connections. The provided -* IO routines, however, follow the Lua style, being very similar to the -* standard Lua read and write functions. -\*=========================================================================*/ - -/*=========================================================================*\ -* Standard include files -\*=========================================================================*/ -#include -#include -#include -// #include "compat.h" - -/*=========================================================================*\ -* LuaSocket includes -\*=========================================================================*/ -#include "libluasocket.h" -#include "auxiliar.h" -#include "except.h" -#include "timeout.h" -#include "buffer.h" -#include "inet.h" -#include "tcp.h" -#include "udp.h" -#include "select.h" - -/*-------------------------------------------------------------------------*\ -* Internal function prototypes -\*-------------------------------------------------------------------------*/ -static int global_skip(lua_State *L); -static int global_unload(lua_State *L); -static int base_open(lua_State *L); - -/*-------------------------------------------------------------------------*\ -* Modules and functions -\*-------------------------------------------------------------------------*/ -static const luaL_Reg mod[] = { - {"auxiliar", auxiliar_open}, - {"except", except_open}, - {"timeout", timeout_open}, - {"buffer", buffer_open}, - {"inet", inet_open}, - {"tcp", tcp_open}, - {"udp", udp_open}, - {"select", select_open}, - {NULL, NULL} -}; - -static luaL_Reg func[] = { - {"skip", global_skip}, - {"__unload", global_unload}, - {NULL, NULL} -}; - -/*-------------------------------------------------------------------------*\ -* Skip a few arguments -\*-------------------------------------------------------------------------*/ -static int global_skip(lua_State *L) { - int amount = luaL_checkinteger(L, 1); - int ret = lua_gettop(L) - amount - 1; - return ret >= 0 ? ret : 0; -} - -/*-------------------------------------------------------------------------*\ -* Unloads the library -\*-------------------------------------------------------------------------*/ -static int global_unload(lua_State *L) { - (void) L; - socket_close(); - return 0; -} - -/*-------------------------------------------------------------------------*\ -* Setup basic stuff. -\*-------------------------------------------------------------------------*/ -static int base_open(lua_State *L) { - if (socket_open()) { - /* export functions (and leave namespace table on top of stack) */ - lua_newtable(L); - luaL_setfuncs(L, func, 0); -#ifdef LUASOCKET_DEBUG - lua_pushstring(L, "_DEBUG"); - lua_pushboolean(L, 1); - lua_rawset(L, -3); -#endif - /* make version string available to scripts */ - lua_pushstring(L, "_VERSION"); - lua_pushstring(L, LUASOCKET_VERSION); - lua_rawset(L, -3); - return 1; - } else { - lua_pushstring(L, "unable to initialize library"); - lua_error(L); - return 0; - } -} - -/*-------------------------------------------------------------------------*\ -* Initializes all library modules. -\*-------------------------------------------------------------------------*/ -LUASOCKET_API int luaopen_socket_core(lua_State *L) { - int i; - base_open(L); - for (i = 0; mod[i].name; i++) mod[i].func(L); - return 1; -} diff --git a/libraries/luasocket/libluasocket/libluasocket.h b/libraries/luasocket/libluasocket/libluasocket.h deleted file mode 100644 index 3957c59fc..000000000 --- a/libraries/luasocket/libluasocket/libluasocket.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef LUASOCKET_H -#define LUASOCKET_H -/*=========================================================================*\ -* LuaSocket toolkit -* Networking support for the Lua language -* Diego Nehab -* 9/11/1999 -\*=========================================================================*/ -#include "lua.h" - -/*-------------------------------------------------------------------------*\ -* Current socket library version -\*-------------------------------------------------------------------------*/ -#define LUASOCKET_VERSION "LuaSocket 3.0-rc1" -#define LUASOCKET_COPYRIGHT "Copyright (C) 1999-2013 Diego Nehab" - -/*-------------------------------------------------------------------------*\ -* This macro prefixes all exported API functions -\*-------------------------------------------------------------------------*/ -#ifndef LUASOCKET_API -#if defined(WIN32) || defined(_WIN32) -# define LUASOCKET_API __declspec(dllexport) -#else -# define LUASOCKET_API __attribute__((visibility("default"))) -#endif -#endif - -/*-------------------------------------------------------------------------*\ -* Initializes the library. -\*-------------------------------------------------------------------------*/ -LUASOCKET_API int luaopen_socket_core(lua_State *L); - -#endif /* LUASOCKET_H */ diff --git a/libraries/luasocket/libluasocket/ltn12.lua b/libraries/luasocket/libluasocket/ltn12.lua index 575c5a7f1..e210b568a 100644 --- a/libraries/luasocket/libluasocket/ltn12.lua +++ b/libraries/luasocket/libluasocket/ltn12.lua @@ -1,3 +1,4 @@ +R"luastring"--( ----------------------------------------------------------------------------- -- LTN12 - Filters, sources, sinks and pumps. -- LuaSocket toolkit. @@ -11,9 +12,11 @@ local string = require("string") local table = require("table") local unpack = unpack or table.unpack local base = _G +local select = select + local _M = {} if module then -- heuristic for exporting a global package table - ltn12 = _M + ltn12 = _M -- luacheck: ignore end local filter,source,sink,pump = {},{},{},{} @@ -22,9 +25,6 @@ _M.source = source _M.sink = sink _M.pump = pump -local unpack = unpack or table.unpack -local select = base.select - -- 2048 seems to be better in windows... _M.BLOCKSIZE = 2048 _M._VERSION = "LTN12 1.0.3" @@ -46,7 +46,7 @@ end -- (thanks to Wim Couwenberg) function filter.chain(...) local arg = {...} - local n = base.select('#',...) + local n = select('#',...) local top, index = 1, 1 local retry = "" return function(chunk) @@ -128,6 +128,16 @@ function source.string(s) else return source.empty() end end +-- creates table source +function source.table(t) + base.assert('table' == type(t)) + local i = 0 + return function() + i = i + 1 + return t[i] + end +end + -- creates rewindable source function source.rewind(src) base.assert(src) @@ -307,3 +317,5 @@ function pump.all(src, snk, step) end return _M +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/ltn12.lua.h b/libraries/luasocket/libluasocket/ltn12.lua.h deleted file mode 100644 index 6fcfed99a..000000000 --- a/libraries/luasocket/libluasocket/ltn12.lua.h +++ /dev/null @@ -1,446 +0,0 @@ -/* code automatically generated by bin2c -- DO NOT EDIT */ -{ -/* #include'ing this file in a C program is equivalent to calling - if (luaL_loadfile(L,"ltn12.lua")==0) lua_call(L, 0, LUA_MULTRET); -*/ -/* ltn12.lua */ -static const unsigned char B1[]={ - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, - 32, 76, 84, 78, 49, 50, 32, 45, 32, 70,105,108,116,101,114,115, 44, 32,115,111, -117,114, 99,101,115, 44, 32,115,105,110,107,115, 32, 97,110,100, 32,112,117,109, -112,115, 46, 10, 45, 45, 32, 76,117, 97, 83,111, 99,107,101,116, 32,116,111,111, -108,107,105,116, 46, 10, 45, 45, 32, 65,117,116,104,111,114, 58, 32, 68,105,101, -103,111, 32, 78,101,104, 97, 98, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 10, 45, 45, 32, 68,101, 99,108, 97,114,101, 32,109,111,100, -117,108,101, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 10,108,111, 99, 97,108, 32,115,116,114,105,110,103, 32, 61, 32,114,101,113, -117,105,114,101, 40, 34,115,116,114,105,110,103, 34, 41, 10,108,111, 99, 97,108, - 32,116, 97, 98,108,101, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,116, 97, - 98,108,101, 34, 41, 10,108,111, 99, 97,108, 32,117,110,112, 97, 99,107, 32, 61, - 32,117,110,112, 97, 99,107, 32,111,114, 32,116, 97, 98,108,101, 46,117,110,112, - 97, 99,107, 10,108,111, 99, 97,108, 32, 98, 97,115,101, 32, 61, 32, 95, 71, 10, -108,111, 99, 97,108, 32, 95, 77, 32, 61, 32,123,125, 10,105,102, 32,109,111,100, -117,108,101, 32,116,104,101,110, 32, 45, 45, 32,104,101,117,114,105,115,116,105, - 99, 32,102,111,114, 32,101,120,112,111,114,116,105,110,103, 32, 97, 32,103,108, -111, 98, 97,108, 32,112, 97, 99,107, 97,103,101, 32,116, 97, 98,108,101, 10, 32, - 32, 32, 32,108,116,110, 49, 50, 32, 61, 32, 95, 77, 10,101,110,100, 10,108,111, - 99, 97,108, 32,102,105,108,116,101,114, 44,115,111,117,114, 99,101, 44,115,105, -110,107, 44,112,117,109,112, 32, 61, 32,123,125, 44,123,125, 44,123,125, 44,123, -125, 10, 10, 95, 77, 46,102,105,108,116,101,114, 32, 61, 32,102,105,108,116,101, -114, 10, 95, 77, 46,115,111,117,114, 99,101, 32, 61, 32,115,111,117,114, 99,101, - 10, 95, 77, 46,115,105,110,107, 32, 61, 32,115,105,110,107, 10, 95, 77, 46,112, -117,109,112, 32, 61, 32,112,117,109,112, 10, 10,108,111, 99, 97,108, 32,117,110, -112, 97, 99,107, 32, 61, 32,117,110,112, 97, 99,107, 32,111,114, 32,116, 97, 98, -108,101, 46,117,110,112, 97, 99,107, 10,108,111, 99, 97,108, 32,115,101,108,101, - 99,116, 32, 61, 32, 98, 97,115,101, 46,115,101,108,101, 99,116, 10, 10, 45, 45, - 32, 50, 48, 52, 56, 32,115,101,101,109,115, 32,116,111, 32, 98,101, 32, 98,101, -116,116,101,114, 32,105,110, 32,119,105,110,100,111,119,115, 46, 46, 46, 10, 95, - 77, 46, 66, 76, 79, 67, 75, 83, 73, 90, 69, 32, 61, 32, 50, 48, 52, 56, 10, 95, - 77, 46, 95, 86, 69, 82, 83, 73, 79, 78, 32, 61, 32, 34, 76, 84, 78, 49, 50, 32, - 49, 46, 48, 46, 51, 34, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 10, 45, 45, 32, 70,105,108,116,101,114, 32,115,116,117,102, -102, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, - 45, 45, 32,114,101,116,117,114,110,115, 32, 97, 32,104,105,103,104, 32,108,101, -118,101,108, 32,102,105,108,116,101,114, 32,116,104, 97,116, 32, 99,121, 99,108, -101,115, 32, 97, 32,108,111,119, 45,108,101,118,101,108, 32,102,105,108,116,101, -114, 10,102,117,110, 99,116,105,111,110, 32,102,105,108,116,101,114, 46, 99,121, - 99,108,101, 40,108,111,119, 44, 32, 99,116,120, 44, 32,101,120,116,114, 97, 41, - 10, 32, 32, 32, 32, 98, 97,115,101, 46, 97,115,115,101,114,116, 40,108,111,119, - 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,102,117,110, 99,116,105,111, -110, 40, 99,104,117,110,107, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, - 97,108, 32,114,101,116, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116, 44, 32, - 99,116,120, 32, 61, 32,108,111,119, 40, 99,116,120, 44, 32, 99,104,117,110,107, - 44, 32,101,120,116,114, 97, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116, -117,114,110, 32,114,101,116, 10, 32, 32, 32, 32,101,110,100, 10,101,110,100, 10, - 10, 45, 45, 32, 99,104, 97,105,110,115, 32, 97, 32, 98,117,110, 99,104, 32,111, -102, 32,102,105,108,116,101,114,115, 32,116,111,103,101,116,104,101,114, 10, 45, - 45, 32, 40,116,104, 97,110,107,115, 32,116,111, 32, 87,105,109, 32, 67,111,117, -119,101,110, 98,101,114,103, 41, 10,102,117,110, 99,116,105,111,110, 32,102,105, -108,116,101,114, 46, 99,104, 97,105,110, 40, 46, 46, 46, 41, 10, 32, 32, 32, 32, -108,111, 99, 97,108, 32, 97,114,103, 32, 61, 32,123, 46, 46, 46,125, 10, 32, 32, - 32, 32,108,111, 99, 97,108, 32,110, 32, 61, 32, 98, 97,115,101, 46,115,101,108, -101, 99,116, 40, 39, 35, 39, 44, 46, 46, 46, 41, 10, 32, 32, 32, 32,108,111, 99, - 97,108, 32,116,111,112, 44, 32,105,110,100,101,120, 32, 61, 32, 49, 44, 32, 49, - 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,114,101,116,114,121, 32, 61, 32, 34, - 34, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,102,117,110, 99,116,105,111, -110, 40, 99,104,117,110,107, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116, -114,121, 32, 61, 32, 99,104,117,110,107, 32, 97,110,100, 32,114,101,116,114,121, - 10, 32, 32, 32, 32, 32, 32, 32, 32,119,104,105,108,101, 32,116,114,117,101, 32, -100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,105,110, -100,101,120, 32, 61, 61, 32,116,111,112, 32,116,104,101,110, 10, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 99,104,117,110,107, 32, 61, 32, - 97,114,103, 91,105,110,100,101,120, 93, 40, 99,104,117,110,107, 41, 10, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, 99,104,117, -110,107, 32, 61, 61, 32, 34, 34, 32,111,114, 32,116,111,112, 32, 61, 61, 32,110, - 32,116,104,101,110, 32,114,101,116,117,114,110, 32, 99,104,117,110,107, 10, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101,105, -102, 32, 99,104,117,110,107, 32,116,104,101,110, 32,105,110,100,101,120, 32, 61, - 32,105,110,100,101,120, 32, 43, 32, 49, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,116,111,112, 32, 61, 32,116,111, -112, 43, 49, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,105,110,100,101,120, 32, 61, 32,116,111,112, 10, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 99,104,117,110,107, 32, 61, 32, 97,114,103, - 91,105,110,100,101,120, 93, 40, 99,104,117,110,107, 32,111,114, 32, 34, 34, 41, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, - 99,104,117,110,107, 32, 61, 61, 32, 34, 34, 32,116,104,101,110, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,110,100, -101,120, 32, 61, 32,105,110,100,101,120, 32, 45, 32, 49, 10, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 99,104,117,110,107, - 32, 61, 32,114,101,116,114,121, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32,101,108,115,101,105,102, 32, 99,104,117,110,107, 32,116,104, -101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32,105,102, 32,105,110,100,101,120, 32, 61, 61, 32,110, 32,116,104,101, -110, 32,114,101,116,117,114,110, 32, 99,104,117,110,107, 10, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 32, -105,110,100,101,120, 32, 61, 32,105,110,100,101,120, 32, 43, 32, 49, 32,101,110, -100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,108, -115,101, 32, 98, 97,115,101, 46,101,114,114,111,114, 40, 34,102,105,108,116,101, -114, 32,114,101,116,117,114,110,101,100, 32,105,110, 97,112,112,114,111,112,114, -105, 97,116,101, 32,110,105,108, 34, 41, 32,101,110,100, 10, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,101, -110,100, 10, 32, 32, 32, 32,101,110,100, 10,101,110,100, 10, 10, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 83,111,117, -114, 99,101, 32,115,116,117,102,102, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 99,114,101, 97,116,101, 32, 97,110, - 32,101,109,112,116,121, 32,115,111,117,114, 99,101, 10,108,111, 99, 97,108, 32, -102,117,110, 99,116,105,111,110, 32,101,109,112,116,121, 40, 41, 10, 32, 32, 32, - 32,114,101,116,117,114,110, 32,110,105,108, 10,101,110,100, 10, 10,102,117,110, - 99,116,105,111,110, 32,115,111,117,114, 99,101, 46,101,109,112,116,121, 40, 41, - 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,101,109,112,116,121, 10,101,110, -100, 10, 10, 45, 45, 32,114,101,116,117,114,110,115, 32, 97, 32,115,111,117,114, - 99,101, 32,116,104, 97,116, 32,106,117,115,116, 32,111,117,116,112,117,116,115, - 32, 97,110, 32,101,114,114,111,114, 10,102,117,110, 99,116,105,111,110, 32,115, -111,117,114, 99,101, 46,101,114,114,111,114, 40,101,114,114, 41, 10, 32, 32, 32, - 32,114,101,116,117,114,110, 32,102,117,110, 99,116,105,111,110, 40, 41, 10, 32, - 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32,110,105,108, 44, 32,101, -114,114, 10, 32, 32, 32, 32,101,110,100, 10,101,110,100, 10, 10, 45, 45, 32, 99, -114,101, 97,116,101,115, 32, 97, 32,102,105,108,101, 32,115,111,117,114, 99,101, - 10,102,117,110, 99,116,105,111,110, 32,115,111,117,114, 99,101, 46,102,105,108, -101, 40,104, 97,110,100,108,101, 44, 32,105,111, 95,101,114,114, 41, 10, 32, 32, - 32, 32,105,102, 32,104, 97,110,100,108,101, 32,116,104,101,110, 10, 32, 32, 32, - 32, 32, 32, 32, 32,114,101,116,117,114,110, 32,102,117,110, 99,116,105,111,110, - 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, - 32, 99,104,117,110,107, 32, 61, 32,104, 97,110,100,108,101, 58,114,101, 97,100, - 40, 95, 77, 46, 66, 76, 79, 67, 75, 83, 73, 90, 69, 41, 10, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32,105,102, 32,110,111,116, 32, 99,104,117,110,107, 32, -116,104,101,110, 32,104, 97,110,100,108,101, 58, 99,108,111,115,101, 40, 41, 32, -101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117, -114,110, 32, 99,104,117,110,107, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, - 10, 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114,110, 32,115,111,117, -114, 99,101, 46,101,114,114,111,114, 40,105,111, 95,101,114,114, 32,111,114, 32, - 34,117,110, 97, 98,108,101, 32,116,111, 32,111,112,101,110, 32,102,105,108,101, - 34, 41, 32,101,110,100, 10,101,110,100, 10, 10, 45, 45, 32,116,117,114,110,115, - 32, 97, 32,102, 97,110, 99,121, 32,115,111,117,114, 99,101, 32,105,110,116,111, - 32, 97, 32,115,105,109,112,108,101, 32,115,111,117,114, 99,101, 10,102,117,110, - 99,116,105,111,110, 32,115,111,117,114, 99,101, 46,115,105,109,112,108,105,102, -121, 40,115,114, 99, 41, 10, 32, 32, 32, 32, 98, 97,115,101, 46, 97,115,115,101, -114,116, 40,115,114, 99, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,102, -117,110, 99,116,105,111,110, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,108,111, - 99, 97,108, 32, 99,104,117,110,107, 44, 32,101,114,114, 95,111,114, 95,110,101, -119, 32, 61, 32,115,114, 99, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,115,114, - 99, 32, 61, 32,101,114,114, 95,111,114, 95,110,101,119, 32,111,114, 32,115,114, - 99, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,110,111,116, 32, 99,104,117, -110,107, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,110,105,108, 44, 32, -101,114,114, 95,111,114, 95,110,101,119, 10, 32, 32, 32, 32, 32, 32, 32, 32,101, -108,115,101, 32,114,101,116,117,114,110, 32, 99,104,117,110,107, 32,101,110,100, - 10, 32, 32, 32, 32,101,110,100, 10,101,110,100, 10, 10, 45, 45, 32, 99,114,101, - 97,116,101,115, 32,115,116,114,105,110,103, 32,115,111,117,114, 99,101, 10,102, -117,110, 99,116,105,111,110, 32,115,111,117,114, 99,101, 46,115,116,114,105,110, -103, 40,115, 41, 10, 32, 32, 32, 32,105,102, 32,115, 32,116,104,101,110, 10, 32, - 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32,105, 32, 61, 32, 49, 10, 32, - 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32,102,117,110, 99,116,105, -111,110, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, - 97,108, 32, 99,104,117,110,107, 32, 61, 32,115,116,114,105,110,103, 46,115,117, - 98, 40,115, 44, 32,105, 44, 32,105, 43, 95, 77, 46, 66, 76, 79, 67, 75, 83, 73, - 90, 69, 45, 49, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105, 32, - 61, 32,105, 32, 43, 32, 95, 77, 46, 66, 76, 79, 67, 75, 83, 73, 90, 69, 10, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, 99,104,117,110,107, 32, -126, 61, 32, 34, 34, 32,116,104,101,110, 32,114,101,116,117,114,110, 32, 99,104, -117,110,107, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, - 32,114,101,116,117,114,110, 32,110,105,108, 32,101,110,100, 10, 32, 32, 32, 32, - 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,101,108,115,101, 32,114,101,116, -117,114,110, 32,115,111,117,114, 99,101, 46,101,109,112,116,121, 40, 41, 32,101, -110,100, 10,101,110,100, 10, 10, 45, 45, 32, 99,114,101, 97,116,101,115, 32,114, -101,119,105,110,100, 97, 98,108,101, 32,115,111,117,114, 99,101, 10,102,117,110, - 99,116,105,111,110, 32,115,111,117,114, 99,101, 46,114,101,119,105,110,100, 40, -115,114, 99, 41, 10, 32, 32, 32, 32, 98, 97,115,101, 46, 97,115,115,101,114,116, - 40,115,114, 99, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,116, 32, 61, 32, -123,125, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,102,117,110, 99,116,105, -111,110, 40, 99,104,117,110,107, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, - 32,110,111,116, 32, 99,104,117,110,107, 32,116,104,101,110, 10, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 99,104,117,110,107, 32, 61, 32,116, 97, 98,108, -101, 46,114,101,109,111,118,101, 40,116, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,105,102, 32,110,111,116, 32, 99,104,117,110,107, 32,116,104,101, -110, 32,114,101,116,117,114,110, 32,115,114, 99, 40, 41, 10, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114,110, 32, 99, -104,117,110,107, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115, -101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,116, 97, 98,108,101, 46, -105,110,115,101,114,116, 40,116, 44, 32, 99,104,117,110,107, 41, 10, 32, 32, 32, - 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,101,110,100, 10,101,110,100, - 10, 10, 45, 45, 32, 99,104, 97,105,110,115, 32, 97, 32,115,111,117,114, 99,101, - 32,119,105,116,104, 32,111,110,101, 32,111,114, 32,115,101,118,101,114, 97,108, - 32,102,105,108,116,101,114, 40,115, 41, 10,102,117,110, 99,116,105,111,110, 32, -115,111,117,114, 99,101, 46, 99,104, 97,105,110, 40,115,114, 99, 44, 32,102, 44, - 32, 46, 46, 46, 41, 10, 32, 32, 32, 32,105,102, 32, 46, 46, 46, 32,116,104,101, -110, 32,102, 61,102,105,108,116,101,114, 46, 99,104, 97,105,110, 40,102, 44, 32, - 46, 46, 46, 41, 32,101,110,100, 10, 32, 32, 32, 32, 98, 97,115,101, 46, 97,115, -115,101,114,116, 40,115,114, 99, 32, 97,110,100, 32,102, 41, 10, 32, 32, 32, 32, -108,111, 99, 97,108, 32,108, 97,115,116, 95,105,110, 44, 32,108, 97,115,116, 95, -111,117,116, 32, 61, 32, 34, 34, 44, 32, 34, 34, 10, 32, 32, 32, 32,108,111, 99, - 97,108, 32,115,116, 97,116,101, 32, 61, 32, 34,102,101,101,100,105,110,103, 34, - 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,101,114,114, 10, 32, 32, 32, 32,114, -101,116,117,114,110, 32,102,117,110, 99,116,105,111,110, 40, 41, 10, 32, 32, 32, - 32, 32, 32, 32, 32,105,102, 32,110,111,116, 32,108, 97,115,116, 95,111,117,116, - 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 98, 97, -115,101, 46,101,114,114,111,114, 40, 39,115,111,117,114, 99,101, 32,105,115, 32, -101,109,112,116,121, 33, 39, 44, 32, 50, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, -101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,119,104,105,108,101, 32,116,114, -117,101, 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, - 32,115,116, 97,116,101, 32, 61, 61, 32, 34,102,101,101,100,105,110,103, 34, 32, -116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32,108, 97,115,116, 95,105,110, 44, 32,101,114,114, 32, 61, 32,115,114, 99, 40, - 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, - 32,101,114,114, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,110,105,108, - 44, 32,101,114,114, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32,108, 97,115,116, 95,111,117,116, 32, 61, 32,102, 40,108, - 97,115,116, 95,105,110, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,105,102, 32,110,111,116, 32,108, 97,115,116, 95,111,117,116, 32, -116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32,105,102, 32,108, 97,115,116, 95,105,110, 32,116,104,101,110, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 98, 97,115,101, 46,101,114,114,111,114, 40, 39,102,105,108, -116,101,114, 32,114,101,116,117,114,110,101,100, 32,105,110, 97,112,112,114,111, -112,114,105, 97,116,101, 32,110,105,108, 39, 41, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 10, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32,114,101,116,117,114,110, 32,110,105,108, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101,105,102, 32, -108, 97,115,116, 95,111,117,116, 32,126, 61, 32, 34, 34, 32,116,104,101,110, 10, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -115,116, 97,116,101, 32, 61, 32, 34,101, 97,116,105,110,103, 34, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, -108, 97,115,116, 95,105,110, 32,116,104,101,110, 32,108, 97,115,116, 95,105,110, - 32, 61, 32, 34, 34, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32,108, 97,115, -116, 95,111,117,116, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,108, -115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,108, - 97,115,116, 95,111,117,116, 32, 61, 32,102, 40,108, 97,115,116, 95,105,110, 41, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, -108, 97,115,116, 95,111,117,116, 32, 61, 61, 32, 34, 34, 32,116,104,101,110, 10, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -105,102, 32,108, 97,115,116, 95,105,110, 32, 61, 61, 32, 34, 34, 32,116,104,101, -110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32,115,116, 97,116,101, 32, 61, 32, 34,102,101,101,100,105, -110,103, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,101,108,115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 98, 97,115,101, 46,101,114, -114,111,114, 40, 39,102,105,108,116,101,114, 32,114,101,116,117,114,110,101,100, - 32, 34, 34, 39, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32,101,108,115,101,105,102, 32,110,111,116, 32,108, 97,115, -116, 95,111,117,116, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,108, 97,115,116, 95,105, -110, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 98, 97,115,101, 46,101,114,114,111, -114, 40, 39,102,105,108,116,101,114, 32,114,101,116,117,114,110,101,100, 32,105, -110, 97,112,112,114,111,112,114,105, 97,116,101, 32,110,105,108, 39, 41, 10, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101, -108,115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32,110,105,108, 10, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101, -110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101, -108,115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,114,101,116,117,114,110, 32,108, 97,115,116, 95,111,117,116, 10, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, - 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,101,110,100, 10,101,110,100, 10, - 10, 45, 45, 32, 99,114,101, 97,116,101,115, 32, 97, 32,115,111,117,114, 99,101, - 32,116,104, 97,116, 32,112,114,111,100,117, 99,101,115, 32, 99,111,110,116,101, -110,116,115, 32,111,102, 32,115,101,118,101,114, 97,108, 32,115,111,117,114, 99, -101,115, 44, 32,111,110,101, 32, 97,102,116,101,114, 32,116,104,101, 10, 45, 45, - 32,111,116,104,101,114, 44, 32, 97,115, 32,105,102, 32,116,104,101,121, 32,119, -101,114,101, 32, 99,111,110, 99, 97,116,101,110, 97,116,101,100, 10, 45, 45, 32, - 40,116,104, 97,110,107,115, 32,116,111, 32, 87,105,109, 32, 67,111,117,119,101, -110, 98,101,114,103, 41, 10,102,117,110, 99,116,105,111,110, 32,115,111,117,114, - 99,101, 46, 99, 97,116, 40, 46, 46, 46, 41, 10, 32, 32, 32, 32,108,111, 99, 97, -108, 32, 97,114,103, 32, 61, 32,123, 46, 46, 46,125, 10, 32, 32, 32, 32,108,111, - 99, 97,108, 32,115,114, 99, 32, 61, 32,116, 97, 98,108,101, 46,114,101,109,111, -118,101, 40, 97,114,103, 44, 32, 49, 41, 10, 32, 32, 32, 32,114,101,116,117,114, -110, 32,102,117,110, 99,116,105,111,110, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, - 32,119,104,105,108,101, 32,115,114, 99, 32,100,111, 10, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32, 99,104,117,110,107, 44, 32,101, -114,114, 32, 61, 32,115,114, 99, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32,105,102, 32, 99,104,117,110,107, 32,116,104,101,110, 32,114,101,116, -117,114,110, 32, 99,104,117,110,107, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32,105,102, 32,101,114,114, 32,116,104,101,110, 32,114,101, -116,117,114,110, 32,110,105,108, 44, 32,101,114,114, 32,101,110,100, 10, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,115,114, 99, 32, 61, 32,116, 97, 98,108, -101, 46,114,101,109,111,118,101, 40, 97,114,103, 44, 32, 49, 41, 10, 32, 32, 32, - 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,101,110,100, 10,101,110,100, - 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, - 45, 45, 32, 83,105,110,107, 32,115,116,117,102,102, 10, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 99,114,101, 97,116, -101,115, 32, 97, 32,115,105,110,107, 32,116,104, 97,116, 32,115,116,111,114,101, -115, 32,105,110,116,111, 32, 97, 32,116, 97, 98,108,101, 10,102,117,110, 99,116, -105,111,110, 32,115,105,110,107, 46,116, 97, 98,108,101, 40,116, 41, 10, 32, 32, - 32, 32,116, 32, 61, 32,116, 32,111,114, 32,123,125, 10, 32, 32, 32, 32,108,111, - 99, 97,108, 32,102, 32, 61, 32,102,117,110, 99,116,105,111,110, 40, 99,104,117, -110,107, 44, 32,101,114,114, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, - 99,104,117,110,107, 32,116,104,101,110, 32,116, 97, 98,108,101, 46,105,110,115, -101,114,116, 40,116, 44, 32, 99,104,117,110,107, 41, 32,101,110,100, 10, 32, 32, - 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32, 49, 10, 32, 32, 32, 32,101, -110,100, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,102, 44, 32,116, 10,101, -110,100, 10, 10, 45, 45, 32,116,117,114,110,115, 32, 97, 32,102, 97,110, 99,121, - 32,115,105,110,107, 32,105,110,116,111, 32, 97, 32,115,105,109,112,108,101, 32, -115,105,110,107, 10,102,117,110, 99,116,105,111,110, 32,115,105,110,107, 46,115, -105,109,112,108,105,102,121, 40,115,110,107, 41, 10, 32, 32, 32, 32, 98, 97,115, -101, 46, 97,115,115,101,114,116, 40,115,110,107, 41, 10, 32, 32, 32, 32,114,101, -116,117,114,110, 32,102,117,110, 99,116,105,111,110, 40, 99,104,117,110,107, 44, - 32,101,114,114, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32, -114,101,116, 44, 32,101,114,114, 95,111,114, 95,110,101,119, 32, 61, 32,115,110, -107, 40, 99,104,117,110,107, 44, 32,101,114,114, 41, 10, 32, 32, 32, 32, 32, 32, - 32, 32,105,102, 32,110,111,116, 32,114,101,116, 32,116,104,101,110, 32,114,101, -116,117,114,110, 32,110,105,108, 44, 32,101,114,114, 95,111,114, 95,110,101,119, - 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,115,110,107, 32, 61, 32,101, -114,114, 95,111,114, 95,110,101,119, 32,111,114, 32,115,110,107, 10, 32, 32, 32, - 32, 32, 32, 32, 32,114,101,116,117,114,110, 32, 49, 10, 32, 32, 32, 32,101,110, -100, 10,101,110,100, 10, 10, 45, 45, 32, 99,114,101, 97,116,101,115, 32, 97, 32, -102,105,108,101, 32,115,105,110,107, 10,102,117,110, 99,116,105,111,110, 32,115, -105,110,107, 46,102,105,108,101, 40,104, 97,110,100,108,101, 44, 32,105,111, 95, -101,114,114, 41, 10, 32, 32, 32, 32,105,102, 32,104, 97,110,100,108,101, 32,116, -104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32,102, -117,110, 99,116,105,111,110, 40, 99,104,117,110,107, 44, 32,101,114,114, 41, 10, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,110,111,116, 32, 99, -104,117,110,107, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32,104, 97,110,100,108,101, 58, 99,108,111,115,101, 40, 41, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116, -117,114,110, 32, 49, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,108, -115,101, 32,114,101,116,117,114,110, 32,104, 97,110,100,108,101, 58,119,114,105, -116,101, 40, 99,104,117,110,107, 41, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, - 32, 32,101,110,100, 10, 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114, -110, 32,115,105,110,107, 46,101,114,114,111,114, 40,105,111, 95,101,114,114, 32, -111,114, 32, 34,117,110, 97, 98,108,101, 32,116,111, 32,111,112,101,110, 32,102, -105,108,101, 34, 41, 32,101,110,100, 10,101,110,100, 10, 10, 45, 45, 32, 99,114, -101, 97,116,101,115, 32, 97, 32,115,105,110,107, 32,116,104, 97,116, 32,100,105, -115, 99, 97,114,100,115, 32,100, 97,116, 97, 10,108,111, 99, 97,108, 32,102,117, -110, 99,116,105,111,110, 32,110,117,108,108, 40, 41, 10, 32, 32, 32, 32,114,101, -116,117,114,110, 32, 49, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, - 32,115,105,110,107, 46,110,117,108,108, 40, 41, 10, 32, 32, 32, 32,114,101,116, -117,114,110, 32,110,117,108,108, 10,101,110,100, 10, 10, 45, 45, 32, 99,114,101, - 97,116,101,115, 32, 97, 32,115,105,110,107, 32,116,104, 97,116, 32,106,117,115, -116, 32,114,101,116,117,114,110,115, 32, 97,110, 32,101,114,114,111,114, 10,102, -117,110, 99,116,105,111,110, 32,115,105,110,107, 46,101,114,114,111,114, 40,101, -114,114, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,102,117,110, 99,116, -105,111,110, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, - 32,110,105,108, 44, 32,101,114,114, 10, 32, 32, 32, 32,101,110,100, 10,101,110, -100, 10, 10, 45, 45, 32, 99,104, 97,105,110,115, 32, 97, 32,115,105,110,107, 32, -119,105,116,104, 32,111,110,101, 32,111,114, 32,115,101,118,101,114, 97,108, 32, -102,105,108,116,101,114, 40,115, 41, 10,102,117,110, 99,116,105,111,110, 32,115, -105,110,107, 46, 99,104, 97,105,110, 40,102, 44, 32,115,110,107, 44, 32, 46, 46, - 46, 41, 10, 32, 32, 32, 32,105,102, 32, 46, 46, 46, 32,116,104,101,110, 10, 32, - 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32, 97,114,103,115, 32, 61, 32, -123, 32,102, 44, 32,115,110,107, 44, 32, 46, 46, 46, 32,125, 10, 32, 32, 32, 32, - 32, 32, 32, 32,115,110,107, 32, 61, 32,116, 97, 98,108,101, 46,114,101,109,111, -118,101, 40, 97,114,103,115, 44, 32, 35, 97,114,103,115, 41, 10, 32, 32, 32, 32, - 32, 32, 32, 32,102, 32, 61, 32,102,105,108,116,101,114, 46, 99,104, 97,105,110, - 40,117,110,112, 97, 99,107, 40, 97,114,103,115, 41, 41, 10, 32, 32, 32, 32,101, -110,100, 10, 32, 32, 32, 32, 98, 97,115,101, 46, 97,115,115,101,114,116, 40,102, - 32, 97,110,100, 32,115,110,107, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, - 32,102,117,110, 99,116,105,111,110, 40, 99,104,117,110,107, 44, 32,101,114,114, - 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, 99,104,117,110,107, 32,126, - 61, 32, 34, 34, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32,108,111, 99, 97,108, 32,102,105,108,116,101,114,101,100, 32, 61, 32,102, - 40, 99,104,117,110,107, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -108,111, 99, 97,108, 32,100,111,110,101, 32, 61, 32, 99,104,117,110,107, 32, 97, -110,100, 32, 34, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,119,104, -105,108,101, 32,116,114,117,101, 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32,114,101,116, 44, 32,115, -110,107,101,114,114, 32, 61, 32,115,110,107, 40,102,105,108,116,101,114,101,100, - 44, 32,101,114,114, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32,105,102, 32,110,111,116, 32,114,101,116, 32,116,104,101,110, 32,114, -101,116,117,114,110, 32,110,105,108, 44, 32,115,110,107,101,114,114, 32,101,110, -100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, - 32,102,105,108,116,101,114,101,100, 32, 61, 61, 32,100,111,110,101, 32,116,104, -101,110, 32,114,101,116,117,114,110, 32, 49, 32,101,110,100, 10, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,102,105,108,116,101,114,101,100, - 32, 61, 32,102, 40,100,111,110,101, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 32, -114,101,116,117,114,110, 32, 49, 32,101,110,100, 10, 32, 32, 32, 32,101,110,100, - 10,101,110,100, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 10, 45, 45, 32, 80,117,109,112, 32,115,116,117,102,102, 10, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32,112, -117,109,112,115, 32,111,110,101, 32, 99,104,117,110,107, 32,102,114,111,109, 32, -116,104,101, 32,115,111,117,114, 99,101, 32,116,111, 32,116,104,101, 32,115,105, -110,107, 10,102,117,110, 99,116,105,111,110, 32,112,117,109,112, 46,115,116,101, -112, 40,115,114, 99, 44, 32,115,110,107, 41, 10, 32, 32, 32, 32,108,111, 99, 97, -108, 32, 99,104,117,110,107, 44, 32,115,114, 99, 95,101,114,114, 32, 61, 32,115, -114, 99, 40, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,114,101,116, 44, 32, -115,110,107, 95,101,114,114, 32, 61, 32,115,110,107, 40, 99,104,117,110,107, 44, - 32,115,114, 99, 95,101,114,114, 41, 10, 32, 32, 32, 32,105,102, 32, 99,104,117, -110,107, 32, 97,110,100, 32,114,101,116, 32,116,104,101,110, 32,114,101,116,117, -114,110, 32, 49, 10, 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114,110, - 32,110,105,108, 44, 32,115,114, 99, 95,101,114,114, 32,111,114, 32,115,110,107, - 95,101,114,114, 32,101,110,100, 10,101,110,100, 10, 10, 45, 45, 32,112,117,109, -112,115, 32, 97,108,108, 32,100, 97,116, 97, 32,102,114,111,109, 32, 97, 32,115, -111,117,114, 99,101, 32,116,111, 32, 97, 32,115,105,110,107, 44, 32,117,115,105, -110,103, 32, 97, 32,115,116,101,112, 32,102,117,110, 99,116,105,111,110, 10,102, -117,110, 99,116,105,111,110, 32,112,117,109,112, 46, 97,108,108, 40,115,114, 99, - 44, 32,115,110,107, 44, 32,115,116,101,112, 41, 10, 32, 32, 32, 32, 98, 97,115, -101, 46, 97,115,115,101,114,116, 40,115,114, 99, 32, 97,110,100, 32,115,110,107, - 41, 10, 32, 32, 32, 32,115,116,101,112, 32, 61, 32,115,116,101,112, 32,111,114, - 32,112,117,109,112, 46,115,116,101,112, 10, 32, 32, 32, 32,119,104,105,108,101, - 32,116,114,117,101, 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, - 97,108, 32,114,101,116, 44, 32,101,114,114, 32, 61, 32,115,116,101,112, 40,115, -114, 99, 44, 32,115,110,107, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, -110,111,116, 32,114,101,116, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32,105,102, 32,101,114,114, 32,116,104,101,110, 32,114,101,116, -117,114,110, 32,110,105,108, 44, 32,101,114,114, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114,110, 32, 49, 32,101, -110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,101, -110,100, 10,101,110,100, 10, 10,114,101,116,117,114,110, 32, 95, 77, 10, -}; - - if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"ltn12.lua")==0) lua_call(L, 0, LUA_MULTRET); -} diff --git a/libraries/luasocket/libluasocket/luasocket.c b/libraries/luasocket/libluasocket/luasocket.c new file mode 100644 index 000000000..0fd99f703 --- /dev/null +++ b/libraries/luasocket/libluasocket/luasocket.c @@ -0,0 +1,104 @@ +/*=========================================================================*\ +* LuaSocket toolkit +* Networking support for the Lua language +* Diego Nehab +* 26/11/1999 +* +* This library is part of an effort to progressively increase the network +* connectivity of the Lua language. The Lua interface to networking +* functions follows the Sockets API closely, trying to simplify all tasks +* involved in setting up both client and server connections. The provided +* IO routines, however, follow the Lua style, being very similar to the +* standard Lua read and write functions. +\*=========================================================================*/ + +#include "luasocket.h" +#include "auxiliar.h" +#include "except.h" +#include "timeout.h" +#include "buffer.h" +#include "inet.h" +#include "tcp.h" +#include "udp.h" +#include "select.h" + +/*-------------------------------------------------------------------------*\ +* Internal function prototypes +\*-------------------------------------------------------------------------*/ +static int global_skip(lua_State *L); +static int global_unload(lua_State *L); +static int base_open(lua_State *L); + +/*-------------------------------------------------------------------------*\ +* Modules and functions +\*-------------------------------------------------------------------------*/ +static const luaL_Reg mod[] = { + {"auxiliar", auxiliar_open}, + {"except", except_open}, + {"timeout", timeout_open}, + {"buffer", buffer_open}, + {"inet", inet_open}, + {"tcp", tcp_open}, + {"udp", udp_open}, + {"select", select_open}, + {NULL, NULL} +}; + +static luaL_Reg func[] = { + {"skip", global_skip}, + {"__unload", global_unload}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Skip a few arguments +\*-------------------------------------------------------------------------*/ +static int global_skip(lua_State *L) { + int amount = (int) luaL_checkinteger(L, 1); + int ret = lua_gettop(L) - amount - 1; + return ret >= 0 ? ret : 0; +} + +/*-------------------------------------------------------------------------*\ +* Unloads the library +\*-------------------------------------------------------------------------*/ +static int global_unload(lua_State *L) { + (void) L; + socket_close(); + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Setup basic stuff. +\*-------------------------------------------------------------------------*/ +static int base_open(lua_State *L) { + if (socket_open()) { + /* export functions (and leave namespace table on top of stack) */ + lua_newtable(L); + luaL_setfuncs(L, func, 0); +#ifdef LUASOCKET_DEBUG + lua_pushstring(L, "_DEBUG"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); +#endif + /* make version string available to scripts */ + lua_pushstring(L, "_VERSION"); + lua_pushstring(L, LUASOCKET_VERSION); + lua_rawset(L, -3); + return 1; + } else { + lua_pushstring(L, "unable to initialize library"); + lua_error(L); + return 0; + } +} + +/*-------------------------------------------------------------------------*\ +* Initializes all library modules. +\*-------------------------------------------------------------------------*/ +LUASOCKET_API int luaopen_socket_core(lua_State *L) { + int i; + base_open(L); + for (i = 0; mod[i].name; i++) mod[i].func(L); + return 1; +} diff --git a/libraries/luasocket/libluasocket/luasocket.h b/libraries/luasocket/libluasocket/luasocket.h new file mode 100644 index 000000000..1017fbaab --- /dev/null +++ b/libraries/luasocket/libluasocket/luasocket.h @@ -0,0 +1,36 @@ +#ifndef LUASOCKET_H +#define LUASOCKET_H +/*=========================================================================*\ +* LuaSocket toolkit +* Networking support for the Lua language +* Diego Nehab +* 9/11/1999 +\*=========================================================================*/ + +/*-------------------------------------------------------------------------* \ +* Current socket library version +\*-------------------------------------------------------------------------*/ +#define LUASOCKET_VERSION "LuaSocket 3.0.0" +#define LUASOCKET_COPYRIGHT "Copyright (C) 1999-2013 Diego Nehab" + +/*-------------------------------------------------------------------------*\ +* This macro prefixes all exported API functions +\*-------------------------------------------------------------------------*/ +#ifndef LUASOCKET_API +#ifdef _WIN32 +#define LUASOCKET_API __declspec(dllexport) +#else +#define LUASOCKET_API __attribute__ ((visibility ("default"))) +#endif +#endif + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +/*-------------------------------------------------------------------------*\ +* Initializes the library. +\*-------------------------------------------------------------------------*/ +LUASOCKET_API int luaopen_socket_core(lua_State *L); + +#endif /* LUASOCKET_H */ diff --git a/libraries/luasocket/libluasocket/makefile b/libraries/luasocket/libluasocket/makefile new file mode 100644 index 000000000..06f4d1927 --- /dev/null +++ b/libraries/luasocket/libluasocket/makefile @@ -0,0 +1,461 @@ +# luasocket src/makefile +# +# Definitions in this section can be overriden on the command line or in the +# environment. +# +# These are equivalent: +# +# export PLAT=linux DEBUG=DEBUG LUAV=5.2 prefix=/sw +# make +# +# and +# +# make PLAT=linux DEBUG=DEBUG LUAV=5.2 prefix=/sw + +# PLAT: linux macosx win32 win64 mingw +# platform to build for +PLAT?=linux + +# LUAV: 5.1 5.2 5.3 5.4 +# lua version to build against +LUAV?=5.1 + +# MYCFLAGS: to be set by user if needed +MYCFLAGS?= + +# MYLDFLAGS: to be set by user if needed +MYLDFLAGS?= + +# DEBUG: NODEBUG DEBUG +# debug mode causes luasocket to collect and returns timing information useful +# for testing and debugging luasocket itself +DEBUG?=NODEBUG + +# where lua headers are found for macosx builds +# LUAINC_macosx: +# /opt/local/include +LUAINC_macosx_base?=/opt/local/include +LUAINC_macosx?=$(LUAINC_macosx_base)/lua/$(LUAV) $(LUAINC_macosx_base)/lua$(LUAV) $(LUAINC_macosx_base)/lua-$(LUAV) + +# FIXME default should this default to fink or to macports? +# What happens when more than one Lua version is installed? +LUAPREFIX_macosx?=/opt/local +CDIR_macosx?=lib/lua/$(LUAV) +LDIR_macosx?=share/lua/$(LUAV) + +# LUAINC_linux: +# /usr/include/lua$(LUAV) +# /usr/local/include +# /usr/local/include/lua$(LUAV) +# where lua headers are found for linux builds +LUAINC_linux_base?=/usr/include +LUAINC_linux?=$(LUAINC_linux_base)/lua/$(LUAV) $(LUAINC_linux_base)/lua$(LUAV) +LUAPREFIX_linux?=/usr/local +CDIR_linux?=lib/lua/$(LUAV) +LDIR_linux?=share/lua/$(LUAV) + +# LUAINC_freebsd: +# /usr/local/include/lua$(LUAV) +# where lua headers are found for freebsd builds +LUAINC_freebsd_base?=/usr/local/include/ +LUAINC_freebsd?=$(LUAINC_freebsd_base)/lua/$(LUAV) $(LUAINC_freebsd_base)/lua$(LUAV) +LUAPREFIX_freebsd?=/usr/local/ +CDIR_freebsd?=lib/lua/$(LUAV) +LDIR_freebsd?=share/lua/$(LUAV) + +# where lua headers are found for mingw builds +# LUAINC_mingw: +# /opt/local/include +LUAINC_mingw_base?=/usr/include +LUAINC_mingw?=$(LUAINC_mingw_base)/lua/$(LUAV) $(LUAINC_mingw_base)/lua$(LUAV) +LUALIB_mingw_base?=/usr/bin +LUALIB_mingw?=$(LUALIB_mingw_base)/lua/$(LUAV)/lua$(subst .,,$(LUAV)).dll +LUAPREFIX_mingw?=/usr +CDIR_mingw?=lua/$(LUAV) +LDIR_mingw?=lua/$(LUAV)/lua + + +# LUAINC_win32: +# LUALIB_win32: +# where lua headers and libraries are found for win32 builds +LUAPREFIX_win32?= +LUAINC_win32?=$(LUAPREFIX_win32)/include/lua/$(LUAV) $(LUAPREFIX_win32)/include/lua$(LUAV) +PLATFORM_win32?=Release +CDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32) +LDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32)/lua +LUALIB_win32?=$(LUAPREFIX_win32)/lib/lua/$(LUAV)/$(PLATFORM_win32) +LUALIBNAME_win32?=lua$(subst .,,$(LUAV)).lib + +# LUAINC_win64: +# LUALIB_win64: +# where lua headers and libraries are found for win64 builds +LUAPREFIX_win64?= +LUAINC_win64?=$(LUAPREFIX_win64)/include/lua/$(LUAV) $(LUAPREFIX_win64)/include/lua$(LUAV) +PLATFORM_win64?=x64/Release +CDIR_win64?=bin/lua/$(LUAV)/$(PLATFORM_win64) +LDIR_win64?=bin/lua/$(LUAV)/$(PLATFORM_win64)/lua +LUALIB_win64?=$(LUAPREFIX_win64)/lib/lua/$(LUAV)/$(PLATFORM_win64) +LUALIBNAME_win64?=lua$(subst .,,$(LUAV)).lib + + +# LUAINC_solaris: +LUAINC_solaris_base?=/usr/include +LUAINC_solaris?=$(LUAINC_solaris_base)/lua/$(LUAV) $(LUAINC_solaris_base)/lua$(LUAV) +LUAPREFIX_solaris?=/usr/local +CDIR_solaris?=lib/lua/$(LUAV) +LDIR_solaris?=share/lua/$(LUAV) + +# prefix: /usr/local /usr /opt/local /sw +# the top of the default install tree +prefix?=$(LUAPREFIX_$(PLAT)) + +CDIR?=$(CDIR_$(PLAT)) +LDIR?=$(LDIR_$(PLAT)) + +# DESTDIR: (no default) +# used by package managers to install into a temporary destination +DESTDIR?= + +#------ +# Definitions below can be overridden on the make command line, but +# shouldn't have to be. + + +#------ +# Install directories +# + +INSTALL_DIR=install -d +INSTALL_DATA=install -m644 +INSTALL_EXEC=install +INSTALL_TOP=$(DESTDIR)$(prefix) + +INSTALL_TOP_LDIR=$(INSTALL_TOP)/$(LDIR) +INSTALL_TOP_CDIR=$(INSTALL_TOP)/$(CDIR) + +INSTALL_SOCKET_LDIR=$(INSTALL_TOP_LDIR)/socket +INSTALL_SOCKET_CDIR=$(INSTALL_TOP_CDIR)/socket +INSTALL_MIME_LDIR=$(INSTALL_TOP_LDIR)/mime +INSTALL_MIME_CDIR=$(INSTALL_TOP_CDIR)/mime + +print: + @echo PLAT=$(PLAT) + @echo LUAV=$(LUAV) + @echo DEBUG=$(DEBUG) + @echo prefix=$(prefix) + @echo LUAINC_$(PLAT)=$(LUAINC_$(PLAT)) + @echo LUALIB_$(PLAT)=$(LUALIB_$(PLAT)) + @echo INSTALL_TOP_CDIR=$(INSTALL_TOP_CDIR) + @echo INSTALL_TOP_LDIR=$(INSTALL_TOP_LDIR) + @echo CFLAGS=$(CFLAGS) + @echo LDFLAGS=$(LDFLAGS) + +#------ +# Supported platforms +# +PLATS= macosx linux win32 win64 mingw solaris + +#------ +# Compiler and linker settings +# for Mac OS X +SO_macosx=so +O_macosx=o +CC_macosx=gcc +DEF_macosx= -DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN +CFLAGS_macosx=$(LUAINC:%=-I%) $(DEF) -Wall -O2 -fno-common +LDFLAGS_macosx= -bundle -undefined dynamic_lookup -o +LD_macosx=gcc +SOCKET_macosx=usocket.o + +#------ +# Compiler and linker settings +# for Linux +SO_linux=so +O_linux=o +CC_linux=gcc +DEF_linux=-DLUASOCKET_$(DEBUG) +CFLAGS_linux=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ + -Wimplicit -O2 -ggdb3 -fpic +LDFLAGS_linux=-O -shared -fpic -o +LD_linux=gcc +SOCKET_linux=usocket.o + +#------ +# Compiler and linker settings +# for FreeBSD +SO_freebsd=so +O_freebsd=o +CC_freebsd=gcc +DEF_freebsd=-DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN +CFLAGS_freebsd=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ + -Wimplicit -O2 -ggdb3 -fpic +LDFLAGS_freebsd=-O -shared -fpic -o +LD_freebsd=gcc +SOCKET_freebsd=usocket.o + +#------ +# Compiler and linker settings +# for Solaris +SO_solaris=so +O_solaris=o +CC_solaris=gcc +DEF_solaris=-DLUASOCKET_$(DEBUG) +CFLAGS_solaris=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ + -Wimplicit -O2 -ggdb3 -fpic +LDFLAGS_solaris=-lnsl -lsocket -lresolv -O -shared -fpic -o +LD_solaris=gcc +SOCKET_solaris=usocket.o + +#------ +# Compiler and linker settings +# for MingW +SO_mingw=dll +O_mingw=o +CC_mingw=gcc +DEF_mingw= -DLUASOCKET_$(DEBUG) \ + -DWINVER=0x0501 +CFLAGS_mingw=$(LUAINC:%=-I%) $(DEF) -Wall -O2 -fno-common +LDFLAGS_mingw= $(LUALIB) -shared -Wl,-s -lws2_32 -o +LD_mingw=gcc +SOCKET_mingw=wsocket.o + + +#------ +# Compiler and linker settings +# for Win32 +SO_win32=dll +O_win32=obj +CC_win32=cl +DEF_win32= //D "WIN32" //D "NDEBUG" //D "_WINDOWS" //D "_USRDLL" \ + //D "_CRT_SECURE_NO_WARNINGS" \ + //D "_WINDLL" \ + //D "LUASOCKET_$(DEBUG)" +CFLAGS_win32=$(LUAINC:%=//I "%") $(DEF) //O2 //Ot //MD //W3 //nologo +LDFLAGS_win32= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \ + //MANIFEST //MANIFESTFILE:"intermediate.manifest" \ + /MANIFESTUAC:"level='asInvoker' uiAccess='false'" \ + //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \ + //MACHINE:X86 /LIBPATH:"$(LUALIB)" \ + $(LUALIBNAME_win32) ws2_32.lib //OUT: + +LD_win32=cl +SOCKET_win32=wsocket.obj + +#------ +# Compiler and linker settings +# for Win64 +SO_win64=dll +O_win64=obj +CC_win64=cl +DEF_win64= //D "WIN32" //D "NDEBUG" //D "_WINDOWS" //D "_USRDLL" \ + //D "_CRT_SECURE_NO_WARNINGS" \ + //D "_WINDLL" \ + //D "LUASOCKET_$(DEBUG)" +CFLAGS_win64=$(LUAINC:%=//I "%") $(DEF) //O2 //Ot //MD //W3 //nologo +LDFLAGS_win64= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \ + //MANIFEST //MANIFESTFILE:"intermediate.manifest" \ + /MANIFESTUAC:"level='asInvoker' uiAccess='false'" \ + //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \ + /LIBPATH:"$(LUALIB)" \ + $(LUALIBNAME_win64) ws2_32.lib //OUT: + +LD_win64=cl +SOCKET_win64=wsocket.obj + +.SUFFIXES: .obj + +.c.obj: + $(CC) $(CFLAGS) //Fo"$@" //c $< + +#------ +# Output file names +# +SO=$(SO_$(PLAT)) +O=$(O_$(PLAT)) +SOCKET_V=3.0.0 +MIME_V=1.0.3 +SOCKET_SO=socket-$(SOCKET_V).$(SO) +MIME_SO=mime-$(MIME_V).$(SO) +UNIX_SO=unix.$(SO) +SERIAL_SO=serial.$(SO) +SOCKET=$(SOCKET_$(PLAT)) + +#------ +# Settings selected for platform +# +CC=$(CC_$(PLAT)) +DEF=$(DEF_$(PLAT)) +CFLAGS=$(MYCFLAGS) $(CFLAGS_$(PLAT)) +LDFLAGS=$(MYLDFLAGS) $(LDFLAGS_$(PLAT)) +LD=$(LD_$(PLAT)) +LUAINC= $(LUAINC_$(PLAT)) +LUALIB= $(LUALIB_$(PLAT)) + +#------ +# Modules belonging to socket-core +# +SOCKET_OBJS= \ + luasocket.$(O) \ + timeout.$(O) \ + buffer.$(O) \ + io.$(O) \ + auxiliar.$(O) \ + compat.$(O) \ + options.$(O) \ + inet.$(O) \ + $(SOCKET) \ + except.$(O) \ + select.$(O) \ + tcp.$(O) \ + udp.$(O) + +#------ +# Modules belonging mime-core +# +MIME_OBJS= \ + mime.$(O) \ + compat.$(O) + +#------ +# Modules belonging unix (local domain sockets) +# +UNIX_OBJS=\ + buffer.$(O) \ + auxiliar.$(O) \ + options.$(O) \ + timeout.$(O) \ + io.$(O) \ + usocket.$(O) \ + unixstream.$(O) \ + unixdgram.$(O) \ + compat.$(O) \ + unix.$(O) + +#------ +# Modules belonging to serial (device streams) +# +SERIAL_OBJS=\ + buffer.$(O) \ + compat.$(O) \ + auxiliar.$(O) \ + options.$(O) \ + timeout.$(O) \ + io.$(O) \ + usocket.$(O) \ + serial.$(O) + +#------ +# Files to install +# +TO_SOCKET_LDIR= \ + http.lua \ + url.lua \ + tp.lua \ + ftp.lua \ + headers.lua \ + smtp.lua + +TO_TOP_LDIR= \ + ltn12.lua \ + socket.lua \ + mime.lua + +#------ +# Targets +# +default: $(PLAT) + + +freebsd: + $(MAKE) all-unix PLAT=freebsd + +macosx: + $(MAKE) all-unix PLAT=macosx + +win32: + $(MAKE) all PLAT=win32 + +win64: + $(MAKE) all PLAT=win64 + +linux: + $(MAKE) all-unix PLAT=linux + +mingw: + $(MAKE) all PLAT=mingw + +solaris: + $(MAKE) all-unix PLAT=solaris + +none: + @echo "Please run" + @echo " make PLATFORM" + @echo "where PLATFORM is one of these:" + @echo " $(PLATS)" + +all: $(SOCKET_SO) $(MIME_SO) + +$(SOCKET_SO): $(SOCKET_OBJS) + $(LD) $(SOCKET_OBJS) $(LDFLAGS)$@ + +$(MIME_SO): $(MIME_OBJS) + $(LD) $(MIME_OBJS) $(LDFLAGS)$@ + +all-unix: all $(UNIX_SO) $(SERIAL_SO) + +$(UNIX_SO): $(UNIX_OBJS) + $(LD) $(UNIX_OBJS) $(LDFLAGS)$@ + +$(SERIAL_SO): $(SERIAL_OBJS) + $(LD) $(SERIAL_OBJS) $(LDFLAGS)$@ + +install: + $(INSTALL_DIR) $(INSTALL_TOP_LDIR) + $(INSTALL_DATA) $(TO_TOP_LDIR) $(INSTALL_TOP_LDIR) + $(INSTALL_DIR) $(INSTALL_SOCKET_LDIR) + $(INSTALL_DATA) $(TO_SOCKET_LDIR) $(INSTALL_SOCKET_LDIR) + $(INSTALL_DIR) $(INSTALL_SOCKET_CDIR) + $(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET_CDIR)/core.$(SO) + $(INSTALL_DIR) $(INSTALL_MIME_CDIR) + $(INSTALL_EXEC) $(MIME_SO) $(INSTALL_MIME_CDIR)/core.$(SO) + +install-unix: install + $(INSTALL_EXEC) $(UNIX_SO) $(INSTALL_SOCKET_CDIR)/$(UNIX_SO) + $(INSTALL_EXEC) $(SERIAL_SO) $(INSTALL_SOCKET_CDIR)/$(SERIAL_SO) + +local: + $(MAKE) install INSTALL_TOP_CDIR=.. INSTALL_TOP_LDIR=.. + +clean: + rm -f $(SOCKET_SO) $(SOCKET_OBJS) $(SERIAL_OBJS) + rm -f $(MIME_SO) $(UNIX_SO) $(SERIAL_SO) $(MIME_OBJS) $(UNIX_OBJS) + +.PHONY: all $(PLATS) default clean echo none + +#------ +# List of dependencies +# +compat.$(O): compat.c compat.h +auxiliar.$(O): auxiliar.c auxiliar.h +buffer.$(O): buffer.c buffer.h io.h timeout.h +except.$(O): except.c except.h +inet.$(O): inet.c inet.h socket.h io.h timeout.h usocket.h +io.$(O): io.c io.h timeout.h +luasocket.$(O): luasocket.c luasocket.h auxiliar.h except.h \ + timeout.h buffer.h io.h inet.h socket.h usocket.h tcp.h \ + udp.h select.h +mime.$(O): mime.c mime.h +options.$(O): options.c auxiliar.h options.h socket.h io.h \ + timeout.h usocket.h inet.h +select.$(O): select.c socket.h io.h timeout.h usocket.h select.h +serial.$(O): serial.c auxiliar.h socket.h io.h timeout.h usocket.h \ + options.h unix.h buffer.h +tcp.$(O): tcp.c auxiliar.h socket.h io.h timeout.h usocket.h \ + inet.h options.h tcp.h buffer.h +timeout.$(O): timeout.c auxiliar.h timeout.h +udp.$(O): udp.c auxiliar.h socket.h io.h timeout.h usocket.h \ + inet.h options.h udp.h +unix.$(O): unix.c auxiliar.h socket.h io.h timeout.h usocket.h \ + options.h unix.h buffer.h +usocket.$(O): usocket.c socket.h io.h timeout.h usocket.h +wsocket.$(O): wsocket.c socket.h io.h timeout.h usocket.h diff --git a/libraries/luasocket/libluasocket/mbox.lua b/libraries/luasocket/libluasocket/mbox.lua index ed9e7814e..f343d7168 100644 --- a/libraries/luasocket/libluasocket/mbox.lua +++ b/libraries/luasocket/libluasocket/mbox.lua @@ -1,8 +1,9 @@ +R"luastring"--( local _M = {} if module then - mbox = _M -end + mbox = _M -- luacheck: ignore +end function _M.split_message(message_s) local message = {} @@ -29,7 +30,7 @@ end function _M.parse_header(header_s) header_s = string.gsub(header_s, "\n[ ]+", " ") header_s = string.gsub(header_s, "\n+", "") - local _, __, name, value = string.find(header_s, "([^%s:]-):%s*(.*)") + local _, _, name, value = string.find(header_s, "([^%s:]-):%s*(.*)") return name, value end @@ -49,9 +50,9 @@ function _M.parse_headers(headers_s) end function _M.parse_from(from) - local _, __, name, address = string.find(from, "^%s*(.-)%s*%<(.-)%>") + local _, _, name, address = string.find(from, "^%s*(.-)%s*%<(.-)%>") if not address then - _, __, address = string.find(from, "%s*(.+)%s*") + _, _, address = string.find(from, "%s*(.+)%s*") end name = name or "" address = address or "" @@ -63,7 +64,8 @@ end function _M.split_mbox(mbox_s) local mbox = {} mbox_s = string.gsub(mbox_s, "\r\n", "\n") .."\n\nFrom \n" - local nj, i, j = 1, 1, 1 + local nj, i + local j = 1 while 1 do i, nj = string.find(mbox_s, "\n\nFrom .-\n", j) if not i then break end @@ -90,3 +92,5 @@ function _M.parse_message(message_s) end return _M +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/mbox.lua.h b/libraries/luasocket/libluasocket/mbox.lua.h deleted file mode 100644 index f3edca978..000000000 --- a/libraries/luasocket/libluasocket/mbox.lua.h +++ /dev/null @@ -1,144 +0,0 @@ -/* code automatically generated by bin2c -- DO NOT EDIT */ -{ -/* #include'ing this file in a C program is equivalent to calling - if (luaL_loadfile(L,"mbox.lua")==0) lua_call(L, 0, LUA_MULTRET); -*/ -/* mbox.lua */ -static const unsigned char B1[]={ -108,111, 99, 97,108, 32, 95, 77, 32, 61, 32,123,125, 10, 10,105,102, 32,109,111, -100,117,108,101, 32,116,104,101,110, 10, 32, 32, 32, 32,109, 98,111,120, 32, 61, - 32, 95, 77, 10,101,110,100, 32, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, - 77, 46,115,112,108,105,116, 95,109,101,115,115, 97,103,101, 40,109,101,115,115, - 97,103,101, 95,115, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,109,101,115, -115, 97,103,101, 32, 61, 32,123,125, 10, 32, 32, 32, 32,109,101,115,115, 97,103, -101, 95,115, 32, 61, 32,115,116,114,105,110,103, 46,103,115,117, 98, 40,109,101, -115,115, 97,103,101, 95,115, 44, 32, 34, 92,114, 92,110, 34, 44, 32, 34, 92,110, - 34, 41, 10, 32, 32, 32, 32,115,116,114,105,110,103, 46,103,115,117, 98, 40,109, -101,115,115, 97,103,101, 95,115, 44, 32, 34, 94, 40, 46, 45, 92,110, 41, 92,110, - 34, 44, 32,102,117,110, 99,116,105,111,110, 32, 40,104, 41, 32,109,101,115,115, - 97,103,101, 46,104,101, 97,100,101,114,115, 32, 61, 32,104, 32,101,110,100, 41, - 10, 32, 32, 32, 32,115,116,114,105,110,103, 46,103,115,117, 98, 40,109,101,115, -115, 97,103,101, 95,115, 44, 32, 34, 94, 46, 45, 92,110, 92,110, 40, 46, 42, 41, - 34, 44, 32,102,117,110, 99,116,105,111,110, 32, 40, 98, 41, 32,109,101,115,115, - 97,103,101, 46, 98,111,100,121, 32, 61, 32, 98, 32,101,110,100, 41, 10, 32, 32, - 32, 32,105,102, 32,110,111,116, 32,109,101,115,115, 97,103,101, 46, 98,111,100, -121, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,115,116,114,105,110, -103, 46,103,115,117, 98, 40,109,101,115,115, 97,103,101, 95,115, 44, 32, 34, 94, - 92,110, 40, 46, 42, 41, 34, 44, 32,102,117,110, 99,116,105,111,110, 32, 40, 98, - 41, 32,109,101,115,115, 97,103,101, 46, 98,111,100,121, 32, 61, 32, 98, 32,101, -110,100, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,105,102, 32,110, -111,116, 32,109,101,115,115, 97,103,101, 46,104,101, 97,100,101,114,115, 32, 97, -110,100, 32,110,111,116, 32,109,101,115,115, 97,103,101, 46, 98,111,100,121, 32, -116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,109,101,115,115, 97,103,101, - 46,104,101, 97,100,101,114,115, 32, 61, 32,109,101,115,115, 97,103,101, 95,115, - 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, -109,101,115,115, 97,103,101, 46,104,101, 97,100,101,114,115, 32,111,114, 32, 34, - 34, 44, 32,109,101,115,115, 97,103,101, 46, 98,111,100,121, 32,111,114, 32, 34, - 34, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 77, 46,115, -112,108,105,116, 95,104,101, 97,100,101,114,115, 40,104,101, 97,100,101,114,115, - 95,115, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,104,101, 97,100,101,114, -115, 32, 61, 32,123,125, 10, 32, 32, 32, 32,104,101, 97,100,101,114,115, 95,115, - 32, 61, 32,115,116,114,105,110,103, 46,103,115,117, 98, 40,104,101, 97,100,101, -114,115, 95,115, 44, 32, 34, 92,114, 92,110, 34, 44, 32, 34, 92,110, 34, 41, 10, - 32, 32, 32, 32,104,101, 97,100,101,114,115, 95,115, 32, 61, 32,115,116,114,105, -110,103, 46,103,115,117, 98, 40,104,101, 97,100,101,114,115, 95,115, 44, 32, 34, - 92,110, 91, 32, 93, 43, 34, 44, 32, 34, 32, 34, 41, 10, 32, 32, 32, 32,115,116, -114,105,110,103, 46,103,115,117, 98, 40, 34, 92,110, 34, 32, 46, 46, 32,104,101, - 97,100,101,114,115, 95,115, 44, 32, 34, 92,110, 40, 91, 94, 92,110, 93, 43, 41, - 34, 44, 32,102,117,110, 99,116,105,111,110, 32, 40,104, 41, 32,116, 97, 98,108, -101, 46,105,110,115,101,114,116, 40,104,101, 97,100,101,114,115, 44, 32,104, 41, - 32,101,110,100, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,104,101, 97, -100,101,114,115, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, - 77, 46,112, 97,114,115,101, 95,104,101, 97,100,101,114, 40,104,101, 97,100,101, -114, 95,115, 41, 10, 32, 32, 32, 32,104,101, 97,100,101,114, 95,115, 32, 61, 32, -115,116,114,105,110,103, 46,103,115,117, 98, 40,104,101, 97,100,101,114, 95,115, - 44, 32, 34, 92,110, 91, 32, 93, 43, 34, 44, 32, 34, 32, 34, 41, 10, 32, 32, 32, - 32,104,101, 97,100,101,114, 95,115, 32, 61, 32,115,116,114,105,110,103, 46,103, -115,117, 98, 40,104,101, 97,100,101,114, 95,115, 44, 32, 34, 92,110, 43, 34, 44, - 32, 34, 34, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 95, 44, 32, 95, 95, - 44, 32,110, 97,109,101, 44, 32,118, 97,108,117,101, 32, 61, 32,115,116,114,105, -110,103, 46,102,105,110,100, 40,104,101, 97,100,101,114, 95,115, 44, 32, 34, 40, - 91, 94, 37,115, 58, 93, 45, 41, 58, 37,115, 42, 40, 46, 42, 41, 34, 41, 10, 32, - 32, 32, 32,114,101,116,117,114,110, 32,110, 97,109,101, 44, 32,118, 97,108,117, -101, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 77, 46,112, - 97,114,115,101, 95,104,101, 97,100,101,114,115, 40,104,101, 97,100,101,114,115, - 95,115, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,104,101, 97,100,101,114, -115, 95,116, 32, 61, 32, 95, 77, 46,115,112,108,105,116, 95,104,101, 97,100,101, -114,115, 40,104,101, 97,100,101,114,115, 95,115, 41, 10, 32, 32, 32, 32,108,111, - 99, 97,108, 32,104,101, 97,100,101,114,115, 32, 61, 32,123,125, 10, 32, 32, 32, - 32,102,111,114, 32,105, 32, 61, 32, 49, 44, 32, 35,104,101, 97,100,101,114,115, - 95,116, 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32, -110, 97,109,101, 44, 32,118, 97,108,117,101, 32, 61, 32, 95, 77, 46,112, 97,114, -115,101, 95,104,101, 97,100,101,114, 40,104,101, 97,100,101,114,115, 95,116, 91, -105, 93, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,110, 97,109,101, 32, -116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,110, 97,109, -101, 32, 61, 32,115,116,114,105,110,103, 46,108,111,119,101,114, 40,110, 97,109, -101, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,104,101, - 97,100,101,114,115, 91,110, 97,109,101, 93, 32,116,104,101,110, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,104,101, 97,100,101,114,115, - 91,110, 97,109,101, 93, 32, 61, 32,104,101, 97,100,101,114,115, 91,110, 97,109, -101, 93, 32, 46, 46, 32, 34, 44, 32, 34, 32, 46, 46, 32,118, 97,108,117,101, 10, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 32,104,101, 97, -100,101,114,115, 91,110, 97,109,101, 93, 32, 61, 32,118, 97,108,117,101, 32,101, -110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,101, -110,100, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,104,101, 97,100,101,114, -115, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 77, 46,112, - 97,114,115,101, 95,102,114,111,109, 40,102,114,111,109, 41, 10, 32, 32, 32, 32, -108,111, 99, 97,108, 32, 95, 44, 32, 95, 95, 44, 32,110, 97,109,101, 44, 32, 97, -100,100,114,101,115,115, 32, 61, 32,115,116,114,105,110,103, 46,102,105,110,100, - 40,102,114,111,109, 44, 32, 34, 94, 37,115, 42, 40, 46, 45, 41, 37,115, 42, 37, - 60, 40, 46, 45, 41, 37, 62, 34, 41, 10, 32, 32, 32, 32,105,102, 32,110,111,116, - 32, 97,100,100,114,101,115,115, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, - 32, 32, 95, 44, 32, 95, 95, 44, 32, 97,100,100,114,101,115,115, 32, 61, 32,115, -116,114,105,110,103, 46,102,105,110,100, 40,102,114,111,109, 44, 32, 34, 37,115, - 42, 40, 46, 43, 41, 37,115, 42, 34, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, - 32, 32, 32,110, 97,109,101, 32, 61, 32,110, 97,109,101, 32,111,114, 32, 34, 34, - 10, 32, 32, 32, 32, 97,100,100,114,101,115,115, 32, 61, 32, 97,100,100,114,101, -115,115, 32,111,114, 32, 34, 34, 10, 32, 32, 32, 32,105,102, 32,110, 97,109,101, - 32, 61, 61, 32, 34, 34, 32,116,104,101,110, 32,110, 97,109,101, 32, 61, 32, 97, -100,100,114,101,115,115, 32,101,110,100, 10, 32, 32, 32, 32,110, 97,109,101, 32, - 61, 32,115,116,114,105,110,103, 46,103,115,117, 98, 40,110, 97,109,101, 44, 32, - 39, 34, 39, 44, 32, 34, 34, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, -110, 97,109,101, 44, 32, 97,100,100,114,101,115,115, 10,101,110,100, 10, 10,102, -117,110, 99,116,105,111,110, 32, 95, 77, 46,115,112,108,105,116, 95,109, 98,111, -120, 40,109, 98,111,120, 95,115, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, -109, 98,111,120, 32, 61, 32,123,125, 10, 32, 32, 32, 32,109, 98,111,120, 95,115, - 32, 61, 32,115,116,114,105,110,103, 46,103,115,117, 98, 40,109, 98,111,120, 95, -115, 44, 32, 34, 92,114, 92,110, 34, 44, 32, 34, 92,110, 34, 41, 32, 46, 46, 34, - 92,110, 92,110, 70,114,111,109, 32, 92,110, 34, 10, 32, 32, 32, 32,108,111, 99, - 97,108, 32,110,106, 44, 32,105, 44, 32,106, 32, 61, 32, 49, 44, 32, 49, 44, 32, - 49, 10, 32, 32, 32, 32,119,104,105,108,101, 32, 49, 32,100,111, 10, 32, 32, 32, - 32, 32, 32, 32, 32,105, 44, 32,110,106, 32, 61, 32,115,116,114,105,110,103, 46, -102,105,110,100, 40,109, 98,111,120, 95,115, 44, 32, 34, 92,110, 92,110, 70,114, -111,109, 32, 46, 45, 92,110, 34, 44, 32,106, 41, 10, 32, 32, 32, 32, 32, 32, 32, - 32,105,102, 32,110,111,116, 32,105, 32,116,104,101,110, 32, 98,114,101, 97,107, - 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32,109, -101,115,115, 97,103,101, 32, 61, 32,115,116,114,105,110,103, 46,115,117, 98, 40, -109, 98,111,120, 95,115, 44, 32,106, 44, 32,105, 45, 49, 41, 10, 32, 32, 32, 32, - 32, 32, 32, 32,116, 97, 98,108,101, 46,105,110,115,101,114,116, 40,109, 98,111, -120, 44, 32,109,101,115,115, 97,103,101, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, -106, 32, 61, 32,110,106, 43, 49, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, - 32,114,101,116,117,114,110, 32,109, 98,111,120, 10,101,110,100, 10, 10,102,117, -110, 99,116,105,111,110, 32, 95, 77, 46,112, 97,114,115,101, 40,109, 98,111,120, - 95,115, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,109, 98,111,120, 32, 61, - 32, 95, 77, 46,115,112,108,105,116, 95,109, 98,111,120, 40,109, 98,111,120, 95, -115, 41, 10, 32, 32, 32, 32,102,111,114, 32,105, 32, 61, 32, 49, 44, 32, 35,109, - 98,111,120, 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32,109, 98,111,120, 91, -105, 93, 32, 61, 32, 95, 77, 46,112, 97,114,115,101, 95,109,101,115,115, 97,103, -101, 40,109, 98,111,120, 91,105, 93, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, - 32, 32, 32,114,101,116,117,114,110, 32,109, 98,111,120, 10,101,110,100, 10, 10, -102,117,110, 99,116,105,111,110, 32, 95, 77, 46,112, 97,114,115,101, 95,109,101, -115,115, 97,103,101, 40,109,101,115,115, 97,103,101, 95,115, 41, 10, 32, 32, 32, - 32,108,111, 99, 97,108, 32,109,101,115,115, 97,103,101, 32, 61, 32,123,125, 10, - 32, 32, 32, 32,109,101,115,115, 97,103,101, 46,104,101, 97,100,101,114,115, 44, - 32,109,101,115,115, 97,103,101, 46, 98,111,100,121, 32, 61, 32, 95, 77, 46,115, -112,108,105,116, 95,109,101,115,115, 97,103,101, 40,109,101,115,115, 97,103,101, - 95,115, 41, 10, 32, 32, 32, 32,109,101,115,115, 97,103,101, 46,104,101, 97,100, -101,114,115, 32, 61, 32, 95, 77, 46,112, 97,114,115,101, 95,104,101, 97,100,101, -114,115, 40,109,101,115,115, 97,103,101, 46,104,101, 97,100,101,114,115, 41, 10, - 32, 32, 32, 32,114,101,116,117,114,110, 32,109,101,115,115, 97,103,101, 10,101, -110,100, 10, 10,114,101,116,117,114,110, 32, 95, 77, 10, -}; - - if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"mbox.lua")==0) lua_call(L, 0, LUA_MULTRET); -} diff --git a/libraries/luasocket/libluasocket/mime.c b/libraries/luasocket/libluasocket/mime.c index ed441046f..05602f566 100644 --- a/libraries/luasocket/libluasocket/mime.c +++ b/libraries/luasocket/libluasocket/mime.c @@ -2,13 +2,10 @@ * MIME support functions * LuaSocket toolkit \*=========================================================================*/ -#include - -#include "lua.h" -#include "lauxlib.h" -#include "compat.h" - +#include "luasocket.h" #include "mime.h" +#include +#include /*=========================================================================*\ * Don't want to trust escape character constants @@ -30,12 +27,12 @@ static int mime_global_eol(lua_State *L); static int mime_global_dot(lua_State *L); static size_t dot(int c, size_t state, luaL_Buffer *buffer); -static void b64setup(UC *base); +/*static void b64setup(UC *base);*/ static size_t b64encode(UC c, UC *input, size_t size, luaL_Buffer *buffer); static size_t b64pad(const UC *input, size_t size, luaL_Buffer *buffer); static size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer); -static void qpsetup(UC *class, UC *unbase); +/*static void qpsetup(UC *class, UC *unbase);*/ static void qpquote(UC c, luaL_Buffer *buffer); static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer); static size_t qpencode(UC c, UC *input, size_t size, @@ -58,17 +55,111 @@ static luaL_Reg func[] = { /*-------------------------------------------------------------------------*\ * Quoted-printable globals \*-------------------------------------------------------------------------*/ -static UC qpclass[256]; -static UC qpbase[] = "0123456789ABCDEF"; -static UC qpunbase[256]; enum {QP_PLAIN, QP_QUOTED, QP_CR, QP_IF_LAST}; +static const UC qpclass[] = { + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_IF_LAST, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_CR, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_IF_LAST, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_QUOTED, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED +}; + +static const UC qpbase[] = "0123456789ABCDEF"; + +static const UC qpunbase[] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, + 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255 +}; + /*-------------------------------------------------------------------------*\ * Base64 globals \*-------------------------------------------------------------------------*/ static const UC b64base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static UC b64unbase[256]; + +static const UC b64unbase[] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0, + 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, + 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255 +}; /*=========================================================================*\ * Exported functions @@ -76,7 +167,7 @@ static UC b64unbase[256]; /*-------------------------------------------------------------------------*\ * Initializes module \*-------------------------------------------------------------------------*/ -MIME_API int luaopen_mime_core(lua_State *L) +LUASOCKET_API int luaopen_mime_core(lua_State *L) { lua_newtable(L); luaL_setfuncs(L, func, 0); @@ -85,8 +176,8 @@ MIME_API int luaopen_mime_core(lua_State *L) lua_pushstring(L, MIME_VERSION); lua_rawset(L, -3); /* initialize lookup tables */ - qpsetup(qpclass, qpunbase); - b64setup(b64unbase); + /*qpsetup(qpclass, qpunbase);*/ + /*b64setup(b64unbase);*/ return 1; } @@ -142,6 +233,7 @@ static int mime_global_wrp(lua_State *L) return 2; } +#if 0 /*-------------------------------------------------------------------------*\ * Fill base64 decode map. \*-------------------------------------------------------------------------*/ @@ -151,7 +243,14 @@ static void b64setup(UC *unbase) for (i = 0; i <= 255; i++) unbase[i] = (UC) 255; for (i = 0; i < 64; i++) unbase[b64base[i]] = (UC) i; unbase['='] = 0; + + printf("static const UC b64unbase[] = {\n"); + for (int i = 0; i < 256; i++) { + printf("%d, ", unbase[i]); + } + printf("\n}\n;"); } +#endif /*-------------------------------------------------------------------------*\ * Acumulates bytes in input buffer until 3 bytes are available. @@ -345,12 +444,14 @@ static int mime_global_unb64(lua_State *L) * To encode one byte, we need to see the next two. * Worst case is when we see a space, and wonder if a CRLF is comming \*-------------------------------------------------------------------------*/ +#if 0 /*-------------------------------------------------------------------------*\ * Split quoted-printable characters into classes * Precompute reverse map for encoding \*-------------------------------------------------------------------------*/ static void qpsetup(UC *cl, UC *unbase) { + int i; for (i = 0; i < 256; i++) cl[i] = QP_QUOTED; for (i = 33; i <= 60; i++) cl[i] = QP_PLAIN; @@ -367,7 +468,37 @@ static void qpsetup(UC *cl, UC *unbase) unbase['c'] = 12; unbase['D'] = 13; unbase['d'] = 13; unbase['E'] = 14; unbase['e'] = 14; unbase['F'] = 15; unbase['f'] = 15; + +printf("static UC qpclass[] = {"); + for (int i = 0; i < 256; i++) { + if (i % 6 == 0) { + printf("\n "); + } + switch(cl[i]) { + case QP_QUOTED: + printf("QP_QUOTED, "); + break; + case QP_PLAIN: + printf("QP_PLAIN, "); + break; + case QP_CR: + printf("QP_CR, "); + break; + case QP_IF_LAST: + printf("QP_IF_LAST, "); + break; + } + } +printf("\n};\n"); + +printf("static const UC qpunbase[] = {"); + for (int i = 0; i < 256; i++) { + int c = qpunbase[i]; + printf("%d, ", c); + } +printf("\";\n"); } +#endif /*-------------------------------------------------------------------------*\ * Output one character in form =XX @@ -447,7 +578,6 @@ static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer) \*-------------------------------------------------------------------------*/ static int mime_global_qp(lua_State *L) { - size_t asize = 0, isize = 0; UC atom[3]; const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize); @@ -654,7 +784,7 @@ static int eolprocess(int c, int last, const char *marker, \*-------------------------------------------------------------------------*/ static int mime_global_eol(lua_State *L) { - int ctx = luaL_checkinteger(L, 1); + int ctx = (int) luaL_checkinteger(L, 1); size_t isize = 0; const char *input = luaL_optlstring(L, 2, NULL, &isize); const char *last = input + isize; @@ -689,6 +819,7 @@ static size_t dot(int c, size_t state, luaL_Buffer *buffer) case '.': if (state == 2) luaL_addchar(buffer, '.'); + /* Falls through. */ default: return 0; } diff --git a/libraries/luasocket/libluasocket/mime.h b/libraries/luasocket/libluasocket/mime.h index 99968a55d..4d938f46e 100644 --- a/libraries/luasocket/libluasocket/mime.h +++ b/libraries/luasocket/libluasocket/mime.h @@ -8,7 +8,7 @@ * and formatting conforming to RFC 2045. It is used by mime.lua, which * provide a higher level interface to this functionality. \*=========================================================================*/ -#include "lua.h" +#include "luasocket.h" /*-------------------------------------------------------------------------*\ * Current MIME library version @@ -17,13 +17,6 @@ #define MIME_COPYRIGHT "Copyright (C) 2004-2013 Diego Nehab" #define MIME_AUTHORS "Diego Nehab" -/*-------------------------------------------------------------------------*\ -* This macro prefixes all exported API functions -\*-------------------------------------------------------------------------*/ -#ifndef MIME_API -#define MIME_API extern -#endif - -MIME_API int luaopen_mime_core(lua_State *L); +LUASOCKET_API int luaopen_mime_core(lua_State *L); #endif /* MIME_H */ diff --git a/libraries/luasocket/libluasocket/mime.lua b/libraries/luasocket/libluasocket/mime.lua index 642cd9ca6..4831ebc44 100644 --- a/libraries/luasocket/libluasocket/mime.lua +++ b/libraries/luasocket/libluasocket/mime.lua @@ -1,3 +1,4 @@ +R"luastring"--( ----------------------------------------------------------------------------- -- MIME support for the Lua language. -- Author: Diego Nehab @@ -10,8 +11,6 @@ local base = _G local ltn12 = require("ltn12") local mime = require("mime.core") -local io = require("io") -local string = require("string") local _M = mime -- encode, decode and wrap algorithm tables @@ -19,7 +18,7 @@ local encodet, decodet, wrapt = {},{},{} _M.encodet = encodet _M.decodet = decodet -_M.wrapt = wrapt +_M.wrapt = wrapt -- creates a function that chooses a filter by name from a given table local function choose(table) @@ -28,7 +27,7 @@ local function choose(table) name, opt1, opt2 = "default", name, opt1 end local f = table[name or "nil"] - if not f then + if not f then base.error("unknown key (" .. base.tostring(name) .. ")", 3) else return f(opt1, opt2) end end @@ -53,13 +52,6 @@ decodet['quoted-printable'] = function() return ltn12.filter.cycle(_M.unqp, "") end -local function format(chunk) - if chunk then - if chunk == "" then return "''" - else return string.len(chunk) end - else return "nil" end -end - -- define the line-wrap filters wrapt['text'] = function(length) length = length or 76 @@ -87,4 +79,6 @@ function _M.stuff() return ltn12.filter.cycle(_M.dot, 2) end -return _M \ No newline at end of file +return _M +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/mime.lua.h b/libraries/luasocket/libluasocket/mime.lua.h deleted file mode 100644 index 78a14cb27..000000000 --- a/libraries/luasocket/libluasocket/mime.lua.h +++ /dev/null @@ -1,136 +0,0 @@ -/* code automatically generated by bin2c -- DO NOT EDIT */ -{ -/* #include'ing this file in a C program is equivalent to calling - if (luaL_loadfile(L,"mime.lua")==0) lua_call(L, 0, LUA_MULTRET); -*/ -/* mime.lua */ -static const unsigned char B1[]={ - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, - 32, 77, 73, 77, 69, 32,115,117,112,112,111,114,116, 32,102,111,114, 32,116,104, -101, 32, 76,117, 97, 32,108, 97,110,103,117, 97,103,101, 46, 10, 45, 45, 32, 65, -117,116,104,111,114, 58, 32, 68,105,101,103,111, 32, 78,101,104, 97, 98, 10, 45, - 45, 32, 67,111,110,102,111,114,109,105,110,103, 32,116,111, 32, 82, 70, 67,115, - 32, 50, 48, 52, 53, 45, 50, 48, 52, 57, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 68,101, 99,108, 97,114,101, 32,109, -111,100,117,108,101, 32, 97,110,100, 32,105,109,112,111,114,116, 32,100,101,112, -101,110,100,101,110, 99,105,101,115, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 10,108,111, 99, 97,108, 32, 98, 97,115,101, 32, 61, - 32, 95, 71, 10,108,111, 99, 97,108, 32,108,116,110, 49, 50, 32, 61, 32,114,101, -113,117,105,114,101, 40, 34,108,116,110, 49, 50, 34, 41, 10,108,111, 99, 97,108, - 32,109,105,109,101, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,109,105,109, -101, 46, 99,111,114,101, 34, 41, 10,108,111, 99, 97,108, 32,105,111, 32, 61, 32, -114,101,113,117,105,114,101, 40, 34,105,111, 34, 41, 10,108,111, 99, 97,108, 32, -115,116,114,105,110,103, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,115,116, -114,105,110,103, 34, 41, 10,108,111, 99, 97,108, 32, 95, 77, 32, 61, 32,109,105, -109,101, 10, 10, 45, 45, 32,101,110, 99,111,100,101, 44, 32,100,101, 99,111,100, -101, 32, 97,110,100, 32,119,114, 97,112, 32, 97,108,103,111,114,105,116,104,109, - 32,116, 97, 98,108,101,115, 10,108,111, 99, 97,108, 32,101,110, 99,111,100,101, -116, 44, 32,100,101, 99,111,100,101,116, 44, 32,119,114, 97,112,116, 32, 61, 32, -123,125, 44,123,125, 44,123,125, 10, 10, 95, 77, 46,101,110, 99,111,100,101,116, - 32, 61, 32,101,110, 99,111,100,101,116, 10, 95, 77, 46,100,101, 99,111,100,101, -116, 32, 61, 32,100,101, 99,111,100,101,116, 10, 95, 77, 46,119,114, 97,112,116, - 32, 32, 32, 61, 32,119,114, 97,112,116, 32, 32, 10, 10, 45, 45, 32, 99,114,101, - 97,116,101,115, 32, 97, 32,102,117,110, 99,116,105,111,110, 32,116,104, 97,116, - 32, 99,104,111,111,115,101,115, 32, 97, 32,102,105,108,116,101,114, 32, 98,121, - 32,110, 97,109,101, 32,102,114,111,109, 32, 97, 32,103,105,118,101,110, 32,116, - 97, 98,108,101, 10,108,111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32, - 99,104,111,111,115,101, 40,116, 97, 98,108,101, 41, 10, 32, 32, 32, 32,114,101, -116,117,114,110, 32,102,117,110, 99,116,105,111,110, 40,110, 97,109,101, 44, 32, -111,112,116, 49, 44, 32,111,112,116, 50, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, -105,102, 32, 98, 97,115,101, 46,116,121,112,101, 40,110, 97,109,101, 41, 32,126, - 61, 32, 34,115,116,114,105,110,103, 34, 32,116,104,101,110, 10, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32,110, 97,109,101, 44, 32,111,112,116, 49, 44, 32, -111,112,116, 50, 32, 61, 32, 34,100,101,102, 97,117,108,116, 34, 44, 32,110, 97, -109,101, 44, 32,111,112,116, 49, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, - 10, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32,102, 32, 61, 32,116, - 97, 98,108,101, 91,110, 97,109,101, 32,111,114, 32, 34,110,105,108, 34, 93, 10, - 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,110,111,116, 32,102, 32,116,104,101, -110, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 98, 97,115,101, 46, -101,114,114,111,114, 40, 34,117,110,107,110,111,119,110, 32,107,101,121, 32, 40, - 34, 32, 46, 46, 32, 98, 97,115,101, 46,116,111,115,116,114,105,110,103, 40,110, - 97,109,101, 41, 32, 46, 46, 32, 34, 41, 34, 44, 32, 51, 41, 10, 32, 32, 32, 32, - 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114,110, 32,102, 40,111,112, -116, 49, 44, 32,111,112,116, 50, 41, 32,101,110,100, 10, 32, 32, 32, 32,101,110, -100, 10,101,110,100, 10, 10, 45, 45, 32,100,101,102,105,110,101, 32,116,104,101, - 32,101,110, 99,111,100,105,110,103, 32,102,105,108,116,101,114,115, 10,101,110, - 99,111,100,101,116, 91, 39, 98, 97,115,101, 54, 52, 39, 93, 32, 61, 32,102,117, -110, 99,116,105,111,110, 40, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, -108,116,110, 49, 50, 46,102,105,108,116,101,114, 46, 99,121, 99,108,101, 40, 95, - 77, 46, 98, 54, 52, 44, 32, 34, 34, 41, 10,101,110,100, 10, 10,101,110, 99,111, -100,101,116, 91, 39,113,117,111,116,101,100, 45,112,114,105,110,116, 97, 98,108, -101, 39, 93, 32, 61, 32,102,117,110, 99,116,105,111,110, 40,109,111,100,101, 41, - 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,108,116,110, 49, 50, 46,102,105, -108,116,101,114, 46, 99,121, 99,108,101, 40, 95, 77, 46,113,112, 44, 32, 34, 34, - 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 40,109,111,100,101, 32, 61, 61, 32, 34, - 98,105,110, 97,114,121, 34, 41, 32, 97,110,100, 32, 34, 61, 48, 68, 61, 48, 65, - 34, 32,111,114, 32, 34, 92,114, 92,110, 34, 41, 10,101,110,100, 10, 10, 45, 45, - 32,100,101,102,105,110,101, 32,116,104,101, 32,100,101, 99,111,100,105,110,103, - 32,102,105,108,116,101,114,115, 10,100,101, 99,111,100,101,116, 91, 39, 98, 97, -115,101, 54, 52, 39, 93, 32, 61, 32,102,117,110, 99,116,105,111,110, 40, 41, 10, - 32, 32, 32, 32,114,101,116,117,114,110, 32,108,116,110, 49, 50, 46,102,105,108, -116,101,114, 46, 99,121, 99,108,101, 40, 95, 77, 46,117,110, 98, 54, 52, 44, 32, - 34, 34, 41, 10,101,110,100, 10, 10,100,101, 99,111,100,101,116, 91, 39,113,117, -111,116,101,100, 45,112,114,105,110,116, 97, 98,108,101, 39, 93, 32, 61, 32,102, -117,110, 99,116,105,111,110, 40, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, - 32,108,116,110, 49, 50, 46,102,105,108,116,101,114, 46, 99,121, 99,108,101, 40, - 95, 77, 46,117,110,113,112, 44, 32, 34, 34, 41, 10,101,110,100, 10, 10,108,111, - 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32,102,111,114,109, 97,116, 40, - 99,104,117,110,107, 41, 10, 32, 32, 32, 32,105,102, 32, 99,104,117,110,107, 32, -116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, 99,104,117,110, -107, 32, 61, 61, 32, 34, 34, 32,116,104,101,110, 32,114,101,116,117,114,110, 32, - 34, 39, 39, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 32,114,101, -116,117,114,110, 32,115,116,114,105,110,103, 46,108,101,110, 40, 99,104,117,110, -107, 41, 32,101,110,100, 10, 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117, -114,110, 32, 34,110,105,108, 34, 32,101,110,100, 10,101,110,100, 10, 10, 45, 45, - 32,100,101,102,105,110,101, 32,116,104,101, 32,108,105,110,101, 45,119,114, 97, -112, 32,102,105,108,116,101,114,115, 10,119,114, 97,112,116, 91, 39,116,101,120, -116, 39, 93, 32, 61, 32,102,117,110, 99,116,105,111,110, 40,108,101,110,103,116, -104, 41, 10, 32, 32, 32, 32,108,101,110,103,116,104, 32, 61, 32,108,101,110,103, -116,104, 32,111,114, 32, 55, 54, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, -108,116,110, 49, 50, 46,102,105,108,116,101,114, 46, 99,121, 99,108,101, 40, 95, - 77, 46,119,114,112, 44, 32,108,101,110,103,116,104, 44, 32,108,101,110,103,116, -104, 41, 10,101,110,100, 10,119,114, 97,112,116, 91, 39, 98, 97,115,101, 54, 52, - 39, 93, 32, 61, 32,119,114, 97,112,116, 91, 39,116,101,120,116, 39, 93, 10,119, -114, 97,112,116, 91, 39,100,101,102, 97,117,108,116, 39, 93, 32, 61, 32,119,114, - 97,112,116, 91, 39,116,101,120,116, 39, 93, 10, 10,119,114, 97,112,116, 91, 39, -113,117,111,116,101,100, 45,112,114,105,110,116, 97, 98,108,101, 39, 93, 32, 61, - 32,102,117,110, 99,116,105,111,110, 40, 41, 10, 32, 32, 32, 32,114,101,116,117, -114,110, 32,108,116,110, 49, 50, 46,102,105,108,116,101,114, 46, 99,121, 99,108, -101, 40, 95, 77, 46,113,112,119,114,112, 44, 32, 55, 54, 44, 32, 55, 54, 41, 10, -101,110,100, 10, 10, 45, 45, 32,102,117,110, 99,116,105,111,110, 32,116,104, 97, -116, 32, 99,104,111,111,115,101, 32,116,104,101, 32,101,110, 99,111,100,105,110, -103, 44, 32,100,101, 99,111,100,105,110,103, 32,111,114, 32,119,114, 97,112, 32, - 97,108,103,111,114,105,116,104,109, 10, 95, 77, 46,101,110, 99,111,100,101, 32, - 61, 32, 99,104,111,111,115,101, 40,101,110, 99,111,100,101,116, 41, 10, 95, 77, - 46,100,101, 99,111,100,101, 32, 61, 32, 99,104,111,111,115,101, 40,100,101, 99, -111,100,101,116, 41, 10, 95, 77, 46,119,114, 97,112, 32, 61, 32, 99,104,111,111, -115,101, 40,119,114, 97,112,116, 41, 10, 10, 45, 45, 32,100,101,102,105,110,101, - 32,116,104,101, 32,101,110,100, 45,111,102, 45,108,105,110,101, 32,110,111,114, -109, 97,108,105,122, 97,116,105,111,110, 32,102,105,108,116,101,114, 10,102,117, -110, 99,116,105,111,110, 32, 95, 77, 46,110,111,114,109, 97,108,105,122,101, 40, -109, 97,114,107,101,114, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,108, -116,110, 49, 50, 46,102,105,108,116,101,114, 46, 99,121, 99,108,101, 40, 95, 77, - 46,101,111,108, 44, 32, 48, 44, 32,109, 97,114,107,101,114, 41, 10,101,110,100, - 10, 10, 45, 45, 32,104,105,103,104, 32,108,101,118,101,108, 32,115,116,117,102, -102,105,110,103, 32,102,105,108,116,101,114, 10,102,117,110, 99,116,105,111,110, - 32, 95, 77, 46,115,116,117,102,102, 40, 41, 10, 32, 32, 32, 32,114,101,116,117, -114,110, 32,108,116,110, 49, 50, 46,102,105,108,116,101,114, 46, 99,121, 99,108, -101, 40, 95, 77, 46,100,111,116, 44, 32, 50, 41, 10,101,110,100, 10, 10,114,101, -116,117,114,110, 32, 95, 77, -}; - - if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"mime.lua")==0) lua_call(L, 0, LUA_MULTRET); -} diff --git a/libraries/luasocket/libluasocket/options.c b/libraries/luasocket/libluasocket/options.c index 3a2c7f251..3280c51d7 100644 --- a/libraries/luasocket/libluasocket/options.c +++ b/libraries/luasocket/libluasocket/options.c @@ -2,24 +2,21 @@ * Common option interface * LuaSocket toolkit \*=========================================================================*/ -#include - -#include "lauxlib.h" - +#include "luasocket.h" #include "auxiliar.h" #include "options.h" #include "inet.h" - +#include /*=========================================================================*\ * Internal functions prototypes \*=========================================================================*/ static int opt_setmembership(lua_State *L, p_socket ps, int level, int name); -// static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name); +static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name); static int opt_setboolean(lua_State *L, p_socket ps, int level, int name); static int opt_getboolean(lua_State *L, p_socket ps, int level, int name); static int opt_setint(lua_State *L, p_socket ps, int level, int name); -// static int opt_getint(lua_State *L, p_socket ps, int level, int name); +static int opt_getint(lua_State *L, p_socket ps, int level, int name); static int opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len); static int opt_get(lua_State *L, p_socket ps, int level, int name, @@ -34,35 +31,30 @@ static int opt_get(lua_State *L, p_socket ps, int level, int name, int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps) { const char *name = luaL_checkstring(L, 2); /* obj, name, ... */ - while (opt->name && strcmp(name, opt->name)) opt++; - if (!opt->func) { - char msg[19 + strlen(name)]; - sprintf(msg, "unsupported option '%s'", name); + char msg[57]; + sprintf(msg, "unsupported option `%.35s'", name); luaL_argerror(L, 2, msg); } - return opt->func(L, ps); } int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps) { const char *name = luaL_checkstring(L, 2); /* obj, name, ... */ - while (opt->name && strcmp(name, opt->name)) opt++; - if (!opt->func) { - char msg[19 + strlen(name)]; - sprintf(msg, "unsupported option '%s'", name); + char msg[57]; + sprintf(msg, "unsupported option `%.35s'", name); luaL_argerror(L, 2, msg); } - return opt->func(L, ps); } +/*------------------------------------------------------*/ /* enables reuse of local address */ int opt_set_reuseaddr(lua_State *L, p_socket ps) { @@ -74,6 +66,7 @@ int opt_get_reuseaddr(lua_State *L, p_socket ps) return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); } +/*------------------------------------------------------*/ /* enables reuse of local port */ int opt_set_reuseport(lua_State *L, p_socket ps) { @@ -85,7 +78,8 @@ int opt_get_reuseport(lua_State *L, p_socket ps) return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEPORT); } -/* disables the Naggle algorithm */ +/*------------------------------------------------------*/ +/* disables the Nagle algorithm */ int opt_set_tcp_nodelay(lua_State *L, p_socket ps) { return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); @@ -96,6 +90,52 @@ int opt_get_tcp_nodelay(lua_State *L, p_socket ps) return opt_getboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); } +/*------------------------------------------------------*/ +#ifdef TCP_KEEPIDLE + +int opt_get_tcp_keepidle(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_TCP, TCP_KEEPIDLE); +} + +int opt_set_tcp_keepidle(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_TCP, TCP_KEEPIDLE); +} + +#endif + +/*------------------------------------------------------*/ +#ifdef TCP_KEEPCNT + +int opt_get_tcp_keepcnt(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_TCP, TCP_KEEPCNT); +} + +int opt_set_tcp_keepcnt(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_TCP, TCP_KEEPCNT); +} + +#endif + +/*------------------------------------------------------*/ +#ifdef TCP_KEEPINTVL + +int opt_get_tcp_keepintvl(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_TCP, TCP_KEEPINTVL); +} + +int opt_set_tcp_keepintvl(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_TCP, TCP_KEEPINTVL); +} + +#endif + +/*------------------------------------------------------*/ int opt_set_keepalive(lua_State *L, p_socket ps) { return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); @@ -106,6 +146,7 @@ int opt_get_keepalive(lua_State *L, p_socket ps) return opt_getboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); } +/*------------------------------------------------------*/ int opt_set_dontroute(lua_State *L, p_socket ps) { return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); @@ -116,6 +157,7 @@ int opt_get_dontroute(lua_State *L, p_socket ps) return opt_getboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); } +/*------------------------------------------------------*/ int opt_set_broadcast(lua_State *L, p_socket ps) { return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST); @@ -126,26 +168,76 @@ int opt_get_broadcast(lua_State *L, p_socket ps) return opt_getboolean(L, ps, SOL_SOCKET, SO_BROADCAST); } -// int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) -// { -// return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); -// } +/*------------------------------------------------------*/ +int opt_set_recv_buf_size(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, SOL_SOCKET, SO_RCVBUF); +} + +int opt_get_recv_buf_size(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, SOL_SOCKET, SO_RCVBUF); +} + +/*------------------------------------------------------*/ +int opt_get_send_buf_size(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, SOL_SOCKET, SO_SNDBUF); +} + +int opt_set_send_buf_size(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, SOL_SOCKET, SO_SNDBUF); +} + +// /*------------------------------------------------------*/ + +#ifdef TCP_FASTOPEN +int opt_set_tcp_fastopen(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_TCP, TCP_FASTOPEN); +} +#endif + +#ifdef TCP_FASTOPEN_CONNECT +int opt_set_tcp_fastopen_connect(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_TCP, TCP_FASTOPEN_CONNECT); +} +#endif -// int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps) -// { -// return opt_getint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); -// } +/*------------------------------------------------------*/ -// int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps) -// { -// return opt_setint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); -// } +#ifdef TCP_DEFER_ACCEPT +int opt_set_tcp_defer_accept(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_TCP, TCP_DEFER_ACCEPT); +} +#endif -// int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps) -// { -// return opt_getint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); -// } +/*------------------------------------------------------*/ +int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); +} + +int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); +} +/*------------------------------------------------------*/ +int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); +} + +int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); +} + +/*------------------------------------------------------*/ int opt_set_ip_multicast_loop(lua_State *L, p_socket ps) { return opt_setboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP); @@ -156,16 +248,18 @@ int opt_get_ip_multicast_loop(lua_State *L, p_socket ps) return opt_getboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP); } -// int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps) -// { -// return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); -// } +/*------------------------------------------------------*/ +int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +} -// int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps) -// { -// return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); -// } +int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +} +/*------------------------------------------------------*/ int opt_set_linger(lua_State *L, p_socket ps) { struct linger li; /* obj, name, table */ @@ -198,35 +292,38 @@ int opt_get_linger(lua_State *L, p_socket ps) return 1; } +/*------------------------------------------------------*/ int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps) { return opt_setint(L, ps, IPPROTO_IP, IP_MULTICAST_TTL); } -// int opt_set_ip_multicast_if(lua_State *L, p_socket ps) -// { -// const char *address = luaL_checkstring(L, 3); /* obj, name, ip */ -// struct in_addr val; -// val.s_addr = htonl(INADDR_ANY); -// if (strcmp(address, "*") && !inet_aton(address, &val)) -// luaL_argerror(L, 3, "ip expected"); -// return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_IF, -// (char *) &val, sizeof(val)); -// } - -// int opt_get_ip_multicast_if(lua_State *L, p_socket ps) -// { -// struct in_addr val; -// socklen_t len = sizeof(val); -// if (getsockopt(*ps, IPPROTO_IP, IP_MULTICAST_IF, (char *) &val, &len) < 0) { -// lua_pushnil(L); -// lua_pushstring(L, "getsockopt failed"); -// return 2; -// } -// lua_pushstring(L, inet_ntoa(val)); -// return 1; -// } +/*------------------------------------------------------*/ +int opt_set_ip_multicast_if(lua_State *L, p_socket ps) +{ + const char *address = luaL_checkstring(L, 3); /* obj, name, ip */ + struct in_addr val; + val.s_addr = htonl(INADDR_ANY); + if (strcmp(address, "*") && !inet_aton(address, &val)) + luaL_argerror(L, 3, "ip expected"); + return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_IF, + (char *) &val, sizeof(val)); +} +int opt_get_ip_multicast_if(lua_State *L, p_socket ps) +{ + struct in_addr val; + socklen_t len = sizeof(val); + if (getsockopt(*ps, IPPROTO_IP, IP_MULTICAST_IF, (char *) &val, &len) < 0) { + lua_pushnil(L); + lua_pushstring(L, "getsockopt failed"); + return 2; + } + lua_pushstring(L, inet_ntoa(val)); + return 1; +} + +/*------------------------------------------------------*/ int opt_set_ip_add_membership(lua_State *L, p_socket ps) { return opt_setmembership(L, ps, IPPROTO_IP, IP_ADD_MEMBERSHIP); @@ -237,25 +334,41 @@ int opt_set_ip_drop_membersip(lua_State *L, p_socket ps) return opt_setmembership(L, ps, IPPROTO_IP, IP_DROP_MEMBERSHIP); } -// int opt_set_ip6_add_membership(lua_State *L, p_socket ps) -// { -// return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP); -// } +/*------------------------------------------------------*/ +int opt_set_ip6_add_membership(lua_State *L, p_socket ps) +{ + return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP); +} + +int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps) +{ + return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP); +} -// int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps) -// { -// return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP); -// } +/*------------------------------------------------------*/ +int opt_get_ip6_v6only(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); +} -// int opt_get_ip6_v6only(lua_State *L, p_socket ps) -// { -// return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); -// } +int opt_set_ip6_v6only(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); +} -// int opt_set_ip6_v6only(lua_State *L, p_socket ps) -// { -// return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); -// } +/*------------------------------------------------------*/ +int opt_get_error(lua_State *L, p_socket ps) +{ + int val = 0; + socklen_t len = sizeof(val); + if (getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *) &val, &len) < 0) { + lua_pushnil(L); + lua_pushstring(L, "getsockopt failed"); + return 2; + } + lua_pushstring(L, socket_strerror(val)); + return 1; +} /*=========================================================================*\ * Auxiliar functions @@ -281,31 +394,31 @@ static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); } -// static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) -// { -// struct ipv6_mreq val; /* obj, opt-name, table */ -// memset(&val, 0, sizeof(val)); -// if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE)); -// lua_pushstring(L, "multiaddr"); -// lua_gettable(L, 3); -// if (!lua_isstring(L, -1)) -// luaL_argerror(L, 3, "string 'multiaddr' field expected"); -// if (!inet_pton(AF_INET6, lua_tostring(L, -1), &val.ipv6mr_multiaddr)) -// luaL_argerror(L, 3, "invalid 'multiaddr' ip address"); -// lua_pushstring(L, "interface"); -// lua_gettable(L, 3); -// /* By default we listen to interface on default route -// * (sigh). However, interface= can override it. We should -// * support either number, or name for it. Waiting for -// * windows port of if_nametoindex */ -// if (!lua_isnil(L, -1)) { -// if (lua_isnumber(L, -1)) { -// val.ipv6mr_interface = (unsigned int) lua_tonumber(L, -1); -// } else -// luaL_argerror(L, -1, "number 'interface' field expected"); -// } -// return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); -// } +static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) +{ + struct ipv6_mreq val; /* obj, opt-name, table */ + memset(&val, 0, sizeof(val)); + if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "multiaddr"); + lua_gettable(L, 3); + if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'multiaddr' field expected"); + if (!inet_pton(AF_INET6, lua_tostring(L, -1), &val.ipv6mr_multiaddr)) + luaL_argerror(L, 3, "invalid 'multiaddr' ip address"); + lua_pushstring(L, "interface"); + lua_gettable(L, 3); + /* By default we listen to interface on default route + * (sigh). However, interface= can override it. We should + * support either number, or name for it. Waiting for + * windows port of if_nametoindex */ + if (!lua_isnil(L, -1)) { + if (lua_isnumber(L, -1)) { + val.ipv6mr_interface = (unsigned int) lua_tonumber(L, -1); + } else + luaL_argerror(L, -1, "number 'interface' field expected"); + } + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} static int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len) @@ -343,35 +456,22 @@ static int opt_getboolean(lua_State *L, p_socket ps, int level, int name) return 1; } -int opt_get_error(lua_State *L, p_socket ps) -{ - int val = 0; - socklen_t len = sizeof(val); - if (getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *) &val, &len) < 0) { - lua_pushnil(L); - lua_pushstring(L, "getsockopt failed"); - return 2; - } - lua_pushstring(L, socket_strerror(val)); - return 1; -} - static int opt_setboolean(lua_State *L, p_socket ps, int level, int name) { int val = auxiliar_checkboolean(L, 3); /* obj, name, bool */ return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); } -// static int opt_getint(lua_State *L, p_socket ps, int level, int name) -// { -// int val = 0; -// int len = sizeof(val); -// int err = opt_get(L, ps, level, name, (char *) &val, &len); -// if (err) -// return err; -// lua_pushnumber(L, val); -// return 1; -// } +static int opt_getint(lua_State *L, p_socket ps, int level, int name) +{ + int val = 0; + int len = sizeof(val); + int err = opt_get(L, ps, level, name, (char *) &val, &len); + if (err) + return err; + lua_pushnumber(L, val); + return 1; +} static int opt_setint(lua_State *L, p_socket ps, int level, int name) { diff --git a/libraries/luasocket/libluasocket/options.h b/libraries/luasocket/libluasocket/options.h index 19ba0dfc2..456eeb5f4 100644 --- a/libraries/luasocket/libluasocket/options.h +++ b/libraries/luasocket/libluasocket/options.h @@ -8,7 +8,7 @@ * modules UDP and TCP. \*=========================================================================*/ -#include "lua.h" +#include "luasocket.h" #include "socket.h" /* option registry */ @@ -18,45 +18,96 @@ typedef struct t_opt { } t_opt; typedef t_opt *p_opt; -/* supported options for setoption */ -int opt_set_dontroute(lua_State *L, p_socket ps); -int opt_set_broadcast(lua_State *L, p_socket ps); +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps); +int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps); + +int opt_set_reuseaddr(lua_State *L, p_socket ps); +int opt_get_reuseaddr(lua_State *L, p_socket ps); + +int opt_set_reuseport(lua_State *L, p_socket ps); +int opt_get_reuseport(lua_State *L, p_socket ps); + int opt_set_tcp_nodelay(lua_State *L, p_socket ps); +int opt_get_tcp_nodelay(lua_State *L, p_socket ps); + +#ifdef TCP_KEEPIDLE +int opt_set_tcp_keepidle(lua_State *L, p_socket ps); +int opt_get_tcp_keepidle(lua_State *L, p_socket ps); +#endif + +#ifdef TCP_KEEPCNT +int opt_set_tcp_keepcnt(lua_State *L, p_socket ps); +int opt_get_tcp_keepcnt(lua_State *L, p_socket ps); +#endif + +#ifdef TCP_KEEPINTVL +int opt_set_tcp_keepintvl(lua_State *L, p_socket ps); +int opt_get_tcp_keepintvl(lua_State *L, p_socket ps); +#endif + +#ifdef TCP_DEFER_ACCEPT +int opt_set_tcp_defer_accept(lua_State *L, p_socket ps); +#endif + int opt_set_keepalive(lua_State *L, p_socket ps); +int opt_get_keepalive(lua_State *L, p_socket ps); + +int opt_set_dontroute(lua_State *L, p_socket ps); +int opt_get_dontroute(lua_State *L, p_socket ps); + +int opt_set_broadcast(lua_State *L, p_socket ps); +int opt_get_broadcast(lua_State *L, p_socket ps); + +int opt_set_recv_buf_size(lua_State *L, p_socket ps); +int opt_get_recv_buf_size(lua_State *L, p_socket ps); + +int opt_set_send_buf_size(lua_State *L, p_socket ps); +int opt_get_send_buf_size(lua_State *L, p_socket ps); + +#ifdef TCP_FASTOPEN +int opt_set_tcp_fastopen(lua_State *L, p_socket ps); +#endif +#ifdef TCP_FASTOPEN_CONNECT +int opt_set_tcp_fastopen_connect(lua_State *L, p_socket ps); +#endif + +int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps); +int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps); + +int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps); +int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps); + +int opt_set_ip_multicast_loop(lua_State *L, p_socket ps); +int opt_get_ip_multicast_loop(lua_State *L, p_socket ps); + +int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps); +int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps); + int opt_set_linger(lua_State *L, p_socket ps); -int opt_set_reuseaddr(lua_State *L, p_socket ps); -int opt_set_reuseport(lua_State *L, p_socket ps); -int opt_set_ip_multicast_if(lua_State *L, p_socket ps); +int opt_get_linger(lua_State *L, p_socket ps); + int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps); -int opt_set_ip_multicast_loop(lua_State *L, p_socket ps); + +int opt_set_ip_multicast_if(lua_State *L, p_socket ps); +int opt_get_ip_multicast_if(lua_State *L, p_socket ps); + int opt_set_ip_add_membership(lua_State *L, p_socket ps); int opt_set_ip_drop_membersip(lua_State *L, p_socket ps); -int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps); -int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps); -int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps); + int opt_set_ip6_add_membership(lua_State *L, p_socket ps); int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps); + int opt_set_ip6_v6only(lua_State *L, p_socket ps); +int opt_get_ip6_v6only(lua_State *L, p_socket ps); -/* supported options for getoption */ -int opt_get_dontroute(lua_State *L, p_socket ps); -int opt_get_broadcast(lua_State *L, p_socket ps); -int opt_get_reuseaddr(lua_State *L, p_socket ps); -int opt_get_reuseport(lua_State *L, p_socket ps); -int opt_get_tcp_nodelay(lua_State *L, p_socket ps); -int opt_get_keepalive(lua_State *L, p_socket ps); -int opt_get_linger(lua_State *L, p_socket ps); -int opt_get_ip_multicast_loop(lua_State *L, p_socket ps); -int opt_get_ip_multicast_if(lua_State *L, p_socket ps); int opt_get_error(lua_State *L, p_socket ps); -int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps); -int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps); -int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps); -int opt_get_ip6_v6only(lua_State *L, p_socket ps); -int opt_get_reuseport(lua_State *L, p_socket ps); -/* invokes the appropriate option handler */ -int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps); -int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps); +#ifndef _WIN32 +#pragma GCC visibility pop +#endif #endif diff --git a/libraries/luasocket/libluasocket/select.c b/libraries/luasocket/libluasocket/select.c index 9d133b7ab..bb47c4592 100644 --- a/libraries/luasocket/libluasocket/select.c +++ b/libraries/luasocket/libluasocket/select.c @@ -2,16 +2,14 @@ * Select implementation * LuaSocket toolkit \*=========================================================================*/ -#include - -#include "lua.h" -#include "lauxlib.h" -#include "compat.h" +#include "luasocket.h" #include "socket.h" #include "timeout.h" #include "select.h" +#include + /*=========================================================================*\ * Internal function prototypes. \*=========================================================================*/ @@ -31,9 +29,6 @@ static luaL_Reg func[] = { {NULL, NULL} }; -/*=========================================================================*\ -* Exported functions -\*=========================================================================*/ /*-------------------------------------------------------------------------*\ * Initializes module \*-------------------------------------------------------------------------*/ @@ -217,4 +212,3 @@ static void make_assoc(lua_State *L, int tab) { i = i+1; } } - diff --git a/libraries/luasocket/libluasocket/select.h b/libraries/luasocket/libluasocket/select.h index 875020039..5d45fe753 100644 --- a/libraries/luasocket/libluasocket/select.h +++ b/libraries/luasocket/libluasocket/select.h @@ -10,6 +10,14 @@ * true if there is data ready for reading (required for buffered input). \*=========================================================================*/ +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + int select_open(lua_State *L); +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + #endif /* SELECT_H */ diff --git a/libraries/luasocket/libluasocket/serial.c b/libraries/luasocket/libluasocket/serial.c new file mode 100644 index 000000000..21485d3e2 --- /dev/null +++ b/libraries/luasocket/libluasocket/serial.c @@ -0,0 +1,171 @@ +/*=========================================================================*\ +* Serial stream +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unix.h" + +#include +#include + +/* +Reuses userdata definition from unix.h, since it is useful for all +stream-like objects. + +If we stored the serial path for use in error messages or userdata +printing, we might need our own userdata definition. + +Group usage is semi-inherited from unix.c, but unnecessary since we +have only one object type. +*/ + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_send(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_close(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); +static int meth_getstats(lua_State *L); +static int meth_setstats(lua_State *L); + +/* serial object methods */ +static luaL_Reg serial_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"close", meth_close}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"receive", meth_receive}, + {"send", meth_send}, + {"setfd", meth_setfd}, + {"settimeout", meth_settimeout}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +LUASOCKET_API int luaopen_socket_serial(lua_State *L) { + /* create classes */ + auxiliar_newclass(L, "serial{client}", serial_methods); + /* create class groups */ + auxiliar_add2group(L, "serial{client}", "serial{any}"); + lua_pushcfunction(L, global_create); + return 1; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Just call buffered IO methods +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_send(L, &un->buf); +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_receive(L, &un->buf); +} + +static int meth_getstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_getstats(L, &un->buf); +} + +static int meth_setstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_setstats(L, &un->buf); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + lua_pushnumber(L, (int) un->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + lua_pushboolean(L, !buffer_isempty(&un->buf)); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + socket_destroy(&un->sock); + lua_pushnumber(L, 1); + return 1; +} + + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ + + +/*-------------------------------------------------------------------------*\ +* Creates a serial object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) { + const char* path = luaL_checkstring(L, 1); + + /* allocate unix object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + + /* open serial device */ + t_socket sock = open(path, O_NOCTTY|O_RDWR); + + /*printf("open %s on %d\n", path, sock);*/ + + if (sock < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + lua_pushnumber(L, errno); + return 3; + } + /* set its type as client object */ + auxiliar_setclass(L, "serial{client}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_write, (p_recv) socket_read, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; +} diff --git a/libraries/luasocket/libluasocket/smtp.lua b/libraries/luasocket/libluasocket/smtp.lua index 11e3451d7..8a8bf4de1 100644 --- a/libraries/luasocket/libluasocket/smtp.lua +++ b/libraries/luasocket/libluasocket/smtp.lua @@ -1,3 +1,4 @@ +R"luastring"--( ----------------------------------------------------------------------------- -- SMTP client support for the Lua language. -- LuaSocket toolkit. @@ -219,13 +220,13 @@ function send_message(mesgt) else send_string(mesgt) end end --- set default headers +-- set defaul headers local function adjust_headers(mesgt) local lower = lower_headers(mesgt.headers) lower["date"] = lower["date"] or os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or _M.ZONE) lower["x-mailer"] = lower["x-mailer"] or socket._VERSION - -- this can't be overridden + -- this can't be overriden lower["mime-version"] = "1.0" return lower end @@ -253,4 +254,6 @@ _M.send = socket.protect(function(mailt) return s:close() end) -return _M \ No newline at end of file +return _M +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/smtp.lua.h b/libraries/luasocket/libluasocket/smtp.lua.h deleted file mode 100644 index 2faf479cf..000000000 --- a/libraries/luasocket/libluasocket/smtp.lua.h +++ /dev/null @@ -1,415 +0,0 @@ -/* code automatically generated by bin2c -- DO NOT EDIT */ -{ -/* #include'ing this file in a C program is equivalent to calling - if (luaL_loadfile(L,"smtp.lua")==0) lua_call(L, 0, LUA_MULTRET); -*/ -/* smtp.lua */ -static const unsigned char B1[]={ - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, - 32, 83, 77, 84, 80, 32, 99,108,105,101,110,116, 32,115,117,112,112,111,114,116, - 32,102,111,114, 32,116,104,101, 32, 76,117, 97, 32,108, 97,110,103,117, 97,103, -101, 46, 10, 45, 45, 32, 76,117, 97, 83,111, 99,107,101,116, 32,116,111,111,108, -107,105,116, 46, 10, 45, 45, 32, 65,117,116,104,111,114, 58, 32, 68,105,101,103, -111, 32, 78,101,104, 97, 98, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 10, 45, 45, 32, 68,101, 99,108, 97,114,101, 32,109,111,100,117, -108,101, 32, 97,110,100, 32,105,109,112,111,114,116, 32,100,101,112,101,110,100, -101,110, 99,105,101,115, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 10,108,111, 99, 97,108, 32, 98, 97,115,101, 32, 61, 32, 95, 71, - 10,108,111, 99, 97,108, 32, 99,111,114,111,117,116,105,110,101, 32, 61, 32,114, -101,113,117,105,114,101, 40, 34, 99,111,114,111,117,116,105,110,101, 34, 41, 10, -108,111, 99, 97,108, 32,115,116,114,105,110,103, 32, 61, 32,114,101,113,117,105, -114,101, 40, 34,115,116,114,105,110,103, 34, 41, 10,108,111, 99, 97,108, 32,109, - 97,116,104, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,109, 97,116,104, 34, - 41, 10,108,111, 99, 97,108, 32,111,115, 32, 61, 32,114,101,113,117,105,114,101, - 40, 34,111,115, 34, 41, 10,108,111, 99, 97,108, 32,115,111, 99,107,101,116, 32, - 61, 32,114,101,113,117,105,114,101, 40, 34,115,111, 99,107,101,116, 34, 41, 10, -108,111, 99, 97,108, 32,116,112, 32, 61, 32,114,101,113,117,105,114,101, 40, 34, -115,111, 99,107,101,116, 46,116,112, 34, 41, 10,108,111, 99, 97,108, 32,108,116, -110, 49, 50, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,108,116,110, 49, 50, - 34, 41, 10,108,111, 99, 97,108, 32,104,101, 97,100,101,114,115, 32, 61, 32,114, -101,113,117,105,114,101, 40, 34,115,111, 99,107,101,116, 46,104,101, 97,100,101, -114,115, 34, 41, 10,108,111, 99, 97,108, 32,109,105,109,101, 32, 61, 32,114,101, -113,117,105,114,101, 40, 34,109,105,109,101, 34, 41, 10, 10,115,111, 99,107,101, -116, 46,115,109,116,112, 32, 61, 32,123,125, 10,108,111, 99, 97,108, 32, 95, 77, - 32, 61, 32,115,111, 99,107,101,116, 46,115,109,116,112, 10, 10, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 80,114,111, -103,114, 97,109, 32, 99,111,110,115,116, 97,110,116,115, 10, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32,116,105,109,101, -111,117,116, 32,102,111,114, 32, 99,111,110,110,101, 99,116,105,111,110, 10, 95, - 77, 46, 84, 73, 77, 69, 79, 85, 84, 32, 61, 32, 54, 48, 10, 45, 45, 32,100,101, -102, 97,117,108,116, 32,115,101,114,118,101,114, 32,117,115,101,100, 32,116,111, - 32,115,101,110,100, 32,101, 45,109, 97,105,108,115, 10, 95, 77, 46, 83, 69, 82, - 86, 69, 82, 32, 61, 32, 34,108,111, 99, 97,108,104,111,115,116, 34, 10, 45, 45, - 32,100,101,102, 97,117,108,116, 32,112,111,114,116, 10, 95, 77, 46, 80, 79, 82, - 84, 32, 61, 32, 50, 53, 10, 45, 45, 32,100,111,109, 97,105,110, 32,117,115,101, -100, 32,105,110, 32, 72, 69, 76, 79, 32, 99,111,109,109, 97,110,100, 32, 97,110, -100, 32,100,101,102, 97,117,108,116, 32,115,101,110,100,109, 97,105,108, 10, 45, - 45, 32, 73,102, 32,119,101, 32, 97,114,101, 32,117,110,100,101,114, 32, 97, 32, - 67, 71, 73, 44, 32,116,114,121, 32,116,111, 32,103,101,116, 32,102,114,111,109, - 32,101,110,118,105,114,111,110,109,101,110,116, 10, 95, 77, 46, 68, 79, 77, 65, - 73, 78, 32, 61, 32,111,115, 46,103,101,116,101,110,118, 40, 34, 83, 69, 82, 86, - 69, 82, 95, 78, 65, 77, 69, 34, 41, 32,111,114, 32, 34,108,111, 99, 97,108,104, -111,115,116, 34, 10, 45, 45, 32,100,101,102, 97,117,108,116, 32,116,105,109,101, - 32,122,111,110,101, 32, 40,109,101, 97,110,115, 32,119,101, 32,100,111,110, 39, -116, 32,107,110,111,119, 41, 10, 95, 77, 46, 90, 79, 78, 69, 32, 61, 32, 34, 45, - 48, 48, 48, 48, 34, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 10, 45, 45, 32, 76,111,119, 32,108,101,118,101,108, 32, 83, 77, 84, 80, - 32, 65, 80, 73, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 10,108,111, 99, 97,108, 32,109,101,116, 97,116, 32, 61, 32,123, 32, 95, - 95,105,110,100,101,120, 32, 61, 32,123,125, 32,125, 10, 10,102,117,110, 99,116, -105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,103,114, -101,101,116, 40,100,111,109, 97,105,110, 41, 10, 32, 32, 32, 32,115,101,108,102, - 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,104,101, 99,107, 40, 34, - 50, 46, 46, 34, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40, -115,101,108,102, 46,116,112, 58, 99,111,109,109, 97,110,100, 40, 34, 69, 72, 76, - 79, 34, 44, 32,100,111,109, 97,105,110, 32,111,114, 32, 95, 77, 46, 68, 79, 77, - 65, 73, 78, 41, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,111, 99, -107,101,116, 46,115,107,105,112, 40, 49, 44, 32,115,101,108,102, 46,116,114,121, - 40,115,101,108,102, 46,116,112, 58, 99,104,101, 99,107, 40, 34, 50, 46, 46, 34, - 41, 41, 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101, -116, 97,116, 46, 95, 95,105,110,100,101,120, 58,109, 97,105,108, 40,102,114,111, -109, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, - 46,116,112, 58, 99,111,109,109, 97,110,100, 40, 34, 77, 65, 73, 76, 34, 44, 32, - 34, 70, 82, 79, 77, 58, 34, 32, 46, 46, 32,102,114,111,109, 41, 41, 10, 32, 32, - 32, 32,114,101,116,117,114,110, 32,115,101,108,102, 46,116,114,121, 40,115,101, -108,102, 46,116,112, 58, 99,104,101, 99,107, 40, 34, 50, 46, 46, 34, 41, 41, 10, -101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, - 95, 95,105,110,100,101,120, 58,114, 99,112,116, 40,116,111, 41, 10, 32, 32, 32, - 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,111, -109,109, 97,110,100, 40, 34, 82, 67, 80, 84, 34, 44, 32, 34, 84, 79, 58, 34, 32, - 46, 46, 32,116,111, 41, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115, -101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,104,101, 99, -107, 40, 34, 50, 46, 46, 34, 41, 41, 10,101,110,100, 10, 10,102,117,110, 99,116, -105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,100, 97, -116, 97, 40,115,114, 99, 44, 32,115,116,101,112, 41, 10, 32, 32, 32, 32,115,101, -108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,111,109,109, 97, -110,100, 40, 34, 68, 65, 84, 65, 34, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, - 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,104,101, 99,107, 40, 34, - 51, 46, 46, 34, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40, -115,101,108,102, 46,116,112, 58,115,111,117,114, 99,101, 40,115,114, 99, 44, 32, -115,116,101,112, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40, -115,101,108,102, 46,116,112, 58,115,101,110,100, 40, 34, 92,114, 92,110, 46, 92, -114, 92,110, 34, 41, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,101, -108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,104,101, 99,107, - 40, 34, 50, 46, 46, 34, 41, 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105, -111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,113,117,105, -116, 40, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115,101,108, -102, 46,116,112, 58, 99,111,109,109, 97,110,100, 40, 34, 81, 85, 73, 84, 34, 41, - 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,101,108,102, 46,116,114, -121, 40,115,101,108,102, 46,116,112, 58, 99,104,101, 99,107, 40, 34, 50, 46, 46, - 34, 41, 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101, -116, 97,116, 46, 95, 95,105,110,100,101,120, 58, 99,108,111,115,101, 40, 41, 10, - 32, 32, 32, 32,114,101,116,117,114,110, 32,115,101,108,102, 46,116,112, 58, 99, -108,111,115,101, 40, 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, - 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,108,111,103,105,110, - 40,117,115,101,114, 44, 32,112, 97,115,115,119,111,114,100, 41, 10, 32, 32, 32, - 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,111, -109,109, 97,110,100, 40, 34, 65, 85, 84, 72, 34, 44, 32, 34, 76, 79, 71, 73, 78, - 34, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115,101,108, -102, 46,116,112, 58, 99,104,101, 99,107, 40, 34, 51, 46, 46, 34, 41, 41, 10, 32, - 32, 32, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, -115,101,110,100, 40,109,105,109,101, 46, 98, 54, 52, 40,117,115,101,114, 41, 32, - 46, 46, 32, 34, 92,114, 92,110, 34, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, - 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,104,101, 99,107, 40, 34, - 51, 46, 46, 34, 41, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, 40, -115,101,108,102, 46,116,112, 58,115,101,110,100, 40,109,105,109,101, 46, 98, 54, - 52, 40,112, 97,115,115,119,111,114,100, 41, 32, 46, 46, 32, 34, 92,114, 92,110, - 34, 41, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,101,108,102, 46, -116,114,121, 40,115,101,108,102, 46,116,112, 58, 99,104,101, 99,107, 40, 34, 50, - 46, 46, 34, 41, 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, -109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,112,108, 97,105,110, 40, -117,115,101,114, 44, 32,112, 97,115,115,119,111,114,100, 41, 10, 32, 32, 32, 32, -108,111, 99, 97,108, 32, 97,117,116,104, 32, 61, 32, 34, 80, 76, 65, 73, 78, 32, - 34, 32, 46, 46, 32,109,105,109,101, 46, 98, 54, 52, 40, 34, 92, 48, 34, 32, 46, - 46, 32,117,115,101,114, 32, 46, 46, 32, 34, 92, 48, 34, 32, 46, 46, 32,112, 97, -115,115,119,111,114,100, 41, 10, 32, 32, 32, 32,115,101,108,102, 46,116,114,121, - 40,115,101,108,102, 46,116,112, 58, 99,111,109,109, 97,110,100, 40, 34, 65, 85, - 84, 72, 34, 44, 32, 97,117,116,104, 41, 41, 10, 32, 32, 32, 32,114,101,116,117, -114,110, 32,115,101,108,102, 46,116,114,121, 40,115,101,108,102, 46,116,112, 58, - 99,104,101, 99,107, 40, 34, 50, 46, 46, 34, 41, 41, 10,101,110,100, 10, 10,102, -117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101, -120, 58, 97,117,116,104, 40,117,115,101,114, 44, 32,112, 97,115,115,119,111,114, -100, 44, 32,101,120,116, 41, 10, 32, 32, 32, 32,105,102, 32,110,111,116, 32,117, -115,101,114, 32,111,114, 32,110,111,116, 32,112, 97,115,115,119,111,114,100, 32, -116,104,101,110, 32,114,101,116,117,114,110, 32, 49, 32,101,110,100, 10, 32, 32, - 32, 32,105,102, 32,115,116,114,105,110,103, 46,102,105,110,100, 40,101,120,116, - 44, 32, 34, 65, 85, 84, 72, 91, 94, 92,110, 93, 43, 76, 79, 71, 73, 78, 34, 41, - 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, - 32,115,101,108,102, 58,108,111,103,105,110, 40,117,115,101,114, 44, 32,112, 97, -115,115,119,111,114,100, 41, 10, 32, 32, 32, 32,101,108,115,101,105,102, 32,115, -116,114,105,110,103, 46,102,105,110,100, 40,101,120,116, 44, 32, 34, 65, 85, 84, - 72, 91, 94, 92,110, 93, 43, 80, 76, 65, 73, 78, 34, 41, 32,116,104,101,110, 10, - 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,101,108,102, 58, -112,108, 97,105,110, 40,117,115,101,114, 44, 32,112, 97,115,115,119,111,114,100, - 41, 10, 32, 32, 32, 32,101,108,115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32,115, -101,108,102, 46,116,114,121, 40,110,105,108, 44, 32, 34, 97,117,116,104,101,110, -116,105, 99, 97,116,105,111,110, 32,110,111,116, 32,115,117,112,112,111,114,116, -101,100, 34, 41, 10, 32, 32, 32, 32,101,110,100, 10,101,110,100, 10, 10, 45, 45, - 32,115,101,110,100, 32,109,101,115,115, 97,103,101, 32,111,114, 32,116,104,114, -111,119, 32, 97,110, 32,101,120, 99,101,112,116,105,111,110, 10,102,117,110, 99, -116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,115, -101,110,100, 40,109, 97,105,108,116, 41, 10, 32, 32, 32, 32,115,101,108,102, 58, -109, 97,105,108, 40,109, 97,105,108,116, 46,102,114,111,109, 41, 10, 32, 32, 32, - 32,105,102, 32, 98, 97,115,101, 46,116,121,112,101, 40,109, 97,105,108,116, 46, -114, 99,112,116, 41, 32, 61, 61, 32, 34,116, 97, 98,108,101, 34, 32,116,104,101, -110, 10, 32, 32, 32, 32, 32, 32, 32, 32,102,111,114, 32,105, 44,118, 32,105,110, - 32, 98, 97,115,101, 46,105,112, 97,105,114,115, 40,109, 97,105,108,116, 46,114, - 99,112,116, 41, 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -115,101,108,102, 58,114, 99,112,116, 40,118, 41, 10, 32, 32, 32, 32, 32, 32, 32, - 32,101,110,100, 10, 32, 32, 32, 32,101,108,115,101, 10, 32, 32, 32, 32, 32, 32, - 32, 32,115,101,108,102, 58,114, 99,112,116, 40,109, 97,105,108,116, 46,114, 99, -112,116, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,115,101,108,102, - 58,100, 97,116, 97, 40,108,116,110, 49, 50, 46,115,111,117,114, 99,101, 46, 99, -104, 97,105,110, 40,109, 97,105,108,116, 46,115,111,117,114, 99,101, 44, 32,109, -105,109,101, 46,115,116,117,102,102, 40, 41, 41, 44, 32,109, 97,105,108,116, 46, -115,116,101,112, 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, - 95, 77, 46,111,112,101,110, 40,115,101,114,118,101,114, 44, 32,112,111,114,116, - 44, 32, 99,114,101, 97,116,101, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, -116,112, 32, 61, 32,115,111, 99,107,101,116, 46,116,114,121, 40,116,112, 46, 99, -111,110,110,101, 99,116, 40,115,101,114,118,101,114, 32,111,114, 32, 95, 77, 46, - 83, 69, 82, 86, 69, 82, 44, 32,112,111,114,116, 32,111,114, 32, 95, 77, 46, 80, - 79, 82, 84, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 95, 77, 46, 84, 73, 77, 69, - 79, 85, 84, 44, 32, 99,114,101, 97,116,101, 41, 41, 10, 32, 32, 32, 32,108,111, - 99, 97,108, 32,115, 32, 61, 32, 98, 97,115,101, 46,115,101,116,109,101,116, 97, -116, 97, 98,108,101, 40,123,116,112, 32, 61, 32,116,112,125, 44, 32,109,101,116, - 97,116, 41, 10, 32, 32, 32, 32, 45, 45, 32,109, 97,107,101, 32,115,117,114,101, - 32,116,112, 32,105,115, 32, 99,108,111,115,101,100, 32,105,102, 32,119,101, 32, -103,101,116, 32, 97,110, 32,101,120, 99,101,112,116,105,111,110, 10, 32, 32, 32, - 32,115, 46,116,114,121, 32, 61, 32,115,111, 99,107,101,116, 46,110,101,119,116, -114,121, 40,102,117,110, 99,116,105,111,110, 40, 41, 10, 32, 32, 32, 32, 32, 32, - 32, 32,115, 58, 99,108,111,115,101, 40, 41, 10, 32, 32, 32, 32,101,110,100, 41, - 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115, 10,101,110,100, 10, 10, 45, - 45, 32, 99,111,110,118,101,114,116, 32,104,101, 97,100,101,114,115, 32,116,111, - 32,108,111,119,101,114, 99, 97,115,101, 10,108,111, 99, 97,108, 32,102,117,110, - 99,116,105,111,110, 32,108,111,119,101,114, 95,104,101, 97,100,101,114,115, 40, -104,101, 97,100,101,114,115, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,108, -111,119,101,114, 32, 61, 32,123,125, 10, 32, 32, 32, 32,102,111,114, 32,105, 44, -118, 32,105,110, 32, 98, 97,115,101, 46,112, 97,105,114,115, 40,104,101, 97,100, -101,114,115, 32,111,114, 32,108,111,119,101,114, 41, 32,100,111, 10, 32, 32, 32, - 32, 32, 32, 32, 32,108,111,119,101,114, 91,115,116,114,105,110,103, 46,108,111, -119,101,114, 40,105, 41, 93, 32, 61, 32,118, 10, 32, 32, 32, 32,101,110,100, 10, - 32, 32, 32, 32,114,101,116,117,114,110, 32,108,111,119,101,114, 10,101,110,100, - 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, - 32, 77,117,108,116,105,112, 97,114,116, 32,109,101,115,115, 97,103,101, 32,115, -111,117,114, 99,101, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 10, 45, 45, 32,114,101,116,117,114,110,115, 32, 97, 32,104,111,112, -101,102,117,108,108,121, 32,117,110,105,113,117,101, 32,109,105,109,101, 32, 98, -111,117,110,100, 97,114,121, 10,108,111, 99, 97,108, 32,115,101,113,110,111, 32, - 61, 32, 48, 10,108,111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32,110, -101,119, 98,111,117,110,100, 97,114,121, 40, 41, 10, 32, 32, 32, 32,115,101,113, -110,111, 32, 61, 32,115,101,113,110,111, 32, 43, 32, 49, 10, 32, 32, 32, 32,114, -101,116,117,114,110, 32,115,116,114,105,110,103, 46,102,111,114,109, 97,116, 40, - 39, 37,115, 37, 48, 53,100, 61, 61, 37, 48, 53,117, 39, 44, 32,111,115, 46,100, - 97,116,101, 40, 39, 37,100, 37,109, 37, 89, 37, 72, 37, 77, 37, 83, 39, 41, 44, - 10, 32, 32, 32, 32, 32, 32, 32, 32,109, 97,116,104, 46,114, 97,110,100,111,109, - 40, 48, 44, 32, 57, 57, 57, 57, 57, 41, 44, 32,115,101,113,110,111, 41, 10,101, -110,100, 10, 10, 45, 45, 32,115,101,110,100, 95,109,101,115,115, 97,103,101, 32, -102,111,114,119, 97,114,100, 32,100,101, 99,108, 97,114, 97,116,105,111,110, 10, -108,111, 99, 97,108, 32,115,101,110,100, 95,109,101,115,115, 97,103,101, 10, 10, - 45, 45, 32,121,105,101,108,100, 32,116,104,101, 32,104,101, 97,100,101,114,115, - 32, 97,108,108, 32, 97,116, 32,111,110, 99,101, 44, 32,105,116, 39,115, 32,102, - 97,115,116,101,114, 10,108,111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, - 32,115,101,110,100, 95,104,101, 97,100,101,114,115, 40,116,111,115,101,110,100, - 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 99, 97,110,111,110,105, 99, 32, - 61, 32,104,101, 97,100,101,114,115, 46, 99, 97,110,111,110,105, 99, 10, 32, 32, - 32, 32,108,111, 99, 97,108, 32,104, 32, 61, 32, 34, 92,114, 92,110, 34, 10, 32, - 32, 32, 32,102,111,114, 32,102, 44,118, 32,105,110, 32, 98, 97,115,101, 46,112, - 97,105,114,115, 40,116,111,115,101,110,100, 41, 32,100,111, 10, 32, 32, 32, 32, - 32, 32, 32, 32,104, 32, 61, 32, 40, 99, 97,110,111,110,105, 99, 91,102, 93, 32, -111,114, 32,102, 41, 32, 46, 46, 32, 39, 58, 32, 39, 32, 46, 46, 32,118, 32, 46, - 46, 32, 34, 92,114, 92,110, 34, 32, 46, 46, 32,104, 10, 32, 32, 32, 32,101,110, -100, 10, 32, 32, 32, 32, 99,111,114,111,117,116,105,110,101, 46,121,105,101,108, -100, 40,104, 41, 10,101,110,100, 10, 10, 45, 45, 32,121,105,101,108,100, 32,109, -117,108,116,105,112, 97,114,116, 32,109,101,115,115, 97,103,101, 32, 98,111,100, -121, 32,102,114,111,109, 32, 97, 32,109,117,108,116,105,112, 97,114,116, 32,109, -101,115,115, 97,103,101, 32,116, 97, 98,108,101, 10,108,111, 99, 97,108, 32,102, -117,110, 99,116,105,111,110, 32,115,101,110,100, 95,109,117,108,116,105,112, 97, -114,116, 40,109,101,115,103,116, 41, 10, 32, 32, 32, 32, 45, 45, 32,109, 97,107, -101, 32,115,117,114,101, 32,119,101, 32,104, 97,118,101, 32,111,117,114, 32, 98, -111,117,110,100, 97,114,121, 32, 97,110,100, 32,115,101,110,100, 32,104,101, 97, -100,101,114,115, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 98,100, 32, 61, 32, -110,101,119, 98,111,117,110,100, 97,114,121, 40, 41, 10, 32, 32, 32, 32,108,111, - 99, 97,108, 32,104,101, 97,100,101,114,115, 32, 61, 32,108,111,119,101,114, 95, -104,101, 97,100,101,114,115, 40,109,101,115,103,116, 46,104,101, 97,100,101,114, -115, 32,111,114, 32,123,125, 41, 10, 32, 32, 32, 32,104,101, 97,100,101,114,115, - 91, 39, 99,111,110,116,101,110,116, 45,116,121,112,101, 39, 93, 32, 61, 32,104, -101, 97,100,101,114,115, 91, 39, 99,111,110,116,101,110,116, 45,116,121,112,101, - 39, 93, 32,111,114, 32, 39,109,117,108,116,105,112, 97,114,116, 47,109,105,120, -101,100, 39, 10, 32, 32, 32, 32,104,101, 97,100,101,114,115, 91, 39, 99,111,110, -116,101,110,116, 45,116,121,112,101, 39, 93, 32, 61, 32,104,101, 97,100,101,114, -115, 91, 39, 99,111,110,116,101,110,116, 45,116,121,112,101, 39, 93, 32, 46, 46, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 39, 59, 32, 98,111,117,110,100, 97,114,121, - 61, 34, 39, 32, 46, 46, 32, 32, 98,100, 32, 46, 46, 32, 39, 34, 39, 10, 32, 32, - 32, 32,115,101,110,100, 95,104,101, 97,100,101,114,115, 40,104,101, 97,100,101, -114,115, 41, 10, 32, 32, 32, 32, 45, 45, 32,115,101,110,100, 32,112,114,101, 97, -109, 98,108,101, 10, 32, 32, 32, 32,105,102, 32,109,101,115,103,116, 46, 98,111, -100,121, 46,112,114,101, 97,109, 98,108,101, 32,116,104,101,110, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 99,111,114,111,117,116,105,110,101, 46,121,105,101,108,100, - 40,109,101,115,103,116, 46, 98,111,100,121, 46,112,114,101, 97,109, 98,108,101, - 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99,111,114,111,117,116,105,110,101, 46, -121,105,101,108,100, 40, 34, 92,114, 92,110, 34, 41, 10, 32, 32, 32, 32,101,110, -100, 10, 32, 32, 32, 32, 45, 45, 32,115,101,110,100, 32,101, 97, 99,104, 32,112, - 97,114,116, 32,115,101,112, 97,114, 97,116,101,100, 32, 98,121, 32, 97, 32, 98, -111,117,110,100, 97,114,121, 10, 32, 32, 32, 32,102,111,114, 32,105, 44, 32,109, - 32,105,110, 32, 98, 97,115,101, 46,105,112, 97,105,114,115, 40,109,101,115,103, -116, 46, 98,111,100,121, 41, 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99, -111,114,111,117,116,105,110,101, 46,121,105,101,108,100, 40, 34, 92,114, 92,110, - 45, 45, 34, 32, 46, 46, 32, 98,100, 32, 46, 46, 32, 34, 92,114, 92,110, 34, 41, - 10, 32, 32, 32, 32, 32, 32, 32, 32,115,101,110,100, 95,109,101,115,115, 97,103, -101, 40,109, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 45, 45, 32, -115,101,110,100, 32,108, 97,115,116, 32, 98,111,117,110,100, 97,114,121, 10, 32, - 32, 32, 32, 99,111,114,111,117,116,105,110,101, 46,121,105,101,108,100, 40, 34, - 92,114, 92,110, 45, 45, 34, 32, 46, 46, 32, 98,100, 32, 46, 46, 32, 34, 45, 45, - 92,114, 92,110, 92,114, 92,110, 34, 41, 10, 32, 32, 32, 32, 45, 45, 32,115,101, -110,100, 32,101,112,105,108,111,103,117,101, 10, 32, 32, 32, 32,105,102, 32,109, -101,115,103,116, 46, 98,111,100,121, 46,101,112,105,108,111,103,117,101, 32,116, -104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99,111,114,111,117,116,105,110, -101, 46,121,105,101,108,100, 40,109,101,115,103,116, 46, 98,111,100,121, 46,101, -112,105,108,111,103,117,101, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99,111,114, -111,117,116,105,110,101, 46,121,105,101,108,100, 40, 34, 92,114, 92,110, 34, 41, - 10, 32, 32, 32, 32,101,110,100, 10,101,110,100, 10, 10, 45, 45, 32,121,105,101, -108,100, 32,109,101,115,115, 97,103,101, 32, 98,111,100,121, 32,102,114,111,109, - 32, 97, 32,115,111,117,114, 99,101, 10,108,111, 99, 97,108, 32,102,117,110, 99, -116,105,111,110, 32,115,101,110,100, 95,115,111,117,114, 99,101, 40,109,101,115, -103,116, 41, 10, 32, 32, 32, 32, 45, 45, 32,109, 97,107,101, 32,115,117,114,101, - 32,119,101, 32,104, 97,118,101, 32, 97, 32, 99,111,110,116,101,110,116, 45,116, -121,112,101, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,104,101, 97,100,101,114, -115, 32, 61, 32,108,111,119,101,114, 95,104,101, 97,100,101,114,115, 40,109,101, -115,103,116, 46,104,101, 97,100,101,114,115, 32,111,114, 32,123,125, 41, 10, 32, - 32, 32, 32,104,101, 97,100,101,114,115, 91, 39, 99,111,110,116,101,110,116, 45, -116,121,112,101, 39, 93, 32, 61, 32,104,101, 97,100,101,114,115, 91, 39, 99,111, -110,116,101,110,116, 45,116,121,112,101, 39, 93, 32,111,114, 10, 32, 32, 32, 32, - 32, 32, 32, 32, 39,116,101,120,116, 47,112,108, 97,105,110, 59, 32, 99,104, 97, -114,115,101,116, 61, 34,105,115,111, 45, 56, 56, 53, 57, 45, 49, 34, 39, 10, 32, - 32, 32, 32,115,101,110,100, 95,104,101, 97,100,101,114,115, 40,104,101, 97,100, -101,114,115, 41, 10, 32, 32, 32, 32, 45, 45, 32,115,101,110,100, 32, 98,111,100, -121, 32,102,114,111,109, 32,115,111,117,114, 99,101, 10, 32, 32, 32, 32,119,104, -105,108,101, 32,116,114,117,101, 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32, -108,111, 99, 97,108, 32, 99,104,117,110,107, 44, 32,101,114,114, 32, 61, 32,109, -101,115,103,116, 46, 98,111,100,121, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, -105,102, 32,101,114,114, 32,116,104,101,110, 32, 99,111,114,111,117,116,105,110, -101, 46,121,105,101,108,100, 40,110,105,108, 44, 32,101,114,114, 41, 10, 32, 32, - 32, 32, 32, 32, 32, 32,101,108,115,101,105,102, 32, 99,104,117,110,107, 32,116, -104,101,110, 32, 99,111,114,111,117,116,105,110,101, 46,121,105,101,108,100, 40, - 99,104,117,110,107, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 32, - 98,114,101, 97,107, 32,101,110,100, 10, 32, 32, 32, 32,101,110,100, 10,101,110, -100, 10, 10, 45, 45, 32,121,105,101,108,100, 32,109,101,115,115, 97,103,101, 32, - 98,111,100,121, 32,102,114,111,109, 32, 97, 32,115,116,114,105,110,103, 10,108, -111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32,115,101,110,100, 95,115, -116,114,105,110,103, 40,109,101,115,103,116, 41, 10, 32, 32, 32, 32, 45, 45, 32, -109, 97,107,101, 32,115,117,114,101, 32,119,101, 32,104, 97,118,101, 32, 97, 32, - 99,111,110,116,101,110,116, 45,116,121,112,101, 10, 32, 32, 32, 32,108,111, 99, - 97,108, 32,104,101, 97,100,101,114,115, 32, 61, 32,108,111,119,101,114, 95,104, -101, 97,100,101,114,115, 40,109,101,115,103,116, 46,104,101, 97,100,101,114,115, - 32,111,114, 32,123,125, 41, 10, 32, 32, 32, 32,104,101, 97,100,101,114,115, 91, - 39, 99,111,110,116,101,110,116, 45,116,121,112,101, 39, 93, 32, 61, 32,104,101, - 97,100,101,114,115, 91, 39, 99,111,110,116,101,110,116, 45,116,121,112,101, 39, - 93, 32,111,114, 10, 32, 32, 32, 32, 32, 32, 32, 32, 39,116,101,120,116, 47,112, -108, 97,105,110, 59, 32, 99,104, 97,114,115,101,116, 61, 34,105,115,111, 45, 56, - 56, 53, 57, 45, 49, 34, 39, 10, 32, 32, 32, 32,115,101,110,100, 95,104,101, 97, -100,101,114,115, 40,104,101, 97,100,101,114,115, 41, 10, 32, 32, 32, 32, 45, 45, - 32,115,101,110,100, 32, 98,111,100,121, 32,102,114,111,109, 32,115,116,114,105, -110,103, 10, 32, 32, 32, 32, 99,111,114,111,117,116,105,110,101, 46,121,105,101, -108,100, 40,109,101,115,103,116, 46, 98,111,100,121, 41, 10,101,110,100, 10, 10, - 45, 45, 32,109,101,115,115, 97,103,101, 32,115,111,117,114, 99,101, 10,102,117, -110, 99,116,105,111,110, 32,115,101,110,100, 95,109,101,115,115, 97,103,101, 40, -109,101,115,103,116, 41, 10, 32, 32, 32, 32,105,102, 32, 98, 97,115,101, 46,116, -121,112,101, 40,109,101,115,103,116, 46, 98,111,100,121, 41, 32, 61, 61, 32, 34, -116, 97, 98,108,101, 34, 32,116,104,101,110, 32,115,101,110,100, 95,109,117,108, -116,105,112, 97,114,116, 40,109,101,115,103,116, 41, 10, 32, 32, 32, 32,101,108, -115,101,105,102, 32, 98, 97,115,101, 46,116,121,112,101, 40,109,101,115,103,116, - 46, 98,111,100,121, 41, 32, 61, 61, 32, 34,102,117,110, 99,116,105,111,110, 34, - 32,116,104,101,110, 32,115,101,110,100, 95,115,111,117,114, 99,101, 40,109,101, -115,103,116, 41, 10, 32, 32, 32, 32,101,108,115,101, 32,115,101,110,100, 95,115, -116,114,105,110,103, 40,109,101,115,103,116, 41, 32,101,110,100, 10,101,110,100, - 10, 10, 45, 45, 32,115,101,116, 32,100,101,102, 97,117,108, 32,104,101, 97,100, -101,114,115, 10,108,111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32, 97, -100,106,117,115,116, 95,104,101, 97,100,101,114,115, 40,109,101,115,103,116, 41, - 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,108,111,119,101,114, 32, 61, 32,108, -111,119,101,114, 95,104,101, 97,100,101,114,115, 40,109,101,115,103,116, 46,104, -101, 97,100,101,114,115, 41, 10, 32, 32, 32, 32,108,111,119,101,114, 91, 34,100, - 97,116,101, 34, 93, 32, 61, 32,108,111,119,101,114, 91, 34,100, 97,116,101, 34, - 93, 32,111,114, 10, 32, 32, 32, 32, 32, 32, 32, 32,111,115, 46,100, 97,116,101, - 40, 34, 33, 37, 97, 44, 32, 37,100, 32, 37, 98, 32, 37, 89, 32, 37, 72, 58, 37, - 77, 58, 37, 83, 32, 34, 41, 32, 46, 46, 32, 40,109,101,115,103,116, 46,122,111, -110,101, 32,111,114, 32, 95, 77, 46, 90, 79, 78, 69, 41, 10, 32, 32, 32, 32,108, -111,119,101,114, 91, 34,120, 45,109, 97,105,108,101,114, 34, 93, 32, 61, 32,108, -111,119,101,114, 91, 34,120, 45,109, 97,105,108,101,114, 34, 93, 32,111,114, 32, -115,111, 99,107,101,116, 46, 95, 86, 69, 82, 83, 73, 79, 78, 10, 32, 32, 32, 32, - 45, 45, 32,116,104,105,115, 32, 99, 97,110, 39,116, 32, 98,101, 32,111,118,101, -114,114,105,100,101,110, 10, 32, 32, 32, 32,108,111,119,101,114, 91, 34,109,105, -109,101, 45,118,101,114,115,105,111,110, 34, 93, 32, 61, 32, 34, 49, 46, 48, 34, - 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,108,111,119,101,114, 10,101,110, -100, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 77, 46,109,101,115,115, 97, -103,101, 40,109,101,115,103,116, 41, 10, 32, 32, 32, 32,109,101,115,103,116, 46, -104,101, 97,100,101,114,115, 32, 61, 32, 97,100,106,117,115,116, 95,104,101, 97, -100,101,114,115, 40,109,101,115,103,116, 41, 10, 32, 32, 32, 32, 45, 45, 32, 99, -114,101, 97,116,101, 32, 97,110,100, 32,114,101,116,117,114,110, 32,109,101,115, -115, 97,103,101, 32,115,111,117,114, 99,101, 10, 32, 32, 32, 32,108,111, 99, 97, -108, 32, 99,111, 32, 61, 32, 99,111,114,111,117,116,105,110,101, 46, 99,114,101, - 97,116,101, 40,102,117,110, 99,116,105,111,110, 40, 41, 32,115,101,110,100, 95, -109,101,115,115, 97,103,101, 40,109,101,115,103,116, 41, 32,101,110,100, 41, 10, - 32, 32, 32, 32,114,101,116,117,114,110, 32,102,117,110, 99,116,105,111,110, 40, - 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32,114,101,116, 44, - 32, 97, 44, 32, 98, 32, 61, 32, 99,111,114,111,117,116,105,110,101, 46,114,101, -115,117,109,101, 40, 99,111, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, -114,101,116, 32,116,104,101,110, 32,114,101,116,117,114,110, 32, 97, 44, 32, 98, - 10, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114,110, - 32,110,105,108, 44, 32, 97, 32,101,110,100, 10, 32, 32, 32, 32,101,110,100, 10, -101,110,100, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 10, 45, 45, 32, 72,105,103,104, 32,108,101,118,101,108, 32, 83, 77, 84, 80, 32, - 65, 80, 73, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 10, 95, 77, 46,115,101,110,100, 32, 61, 32,115,111, 99,107,101,116, 46,112, -114,111,116,101, 99,116, 40,102,117,110, 99,116,105,111,110, 40,109, 97,105,108, -116, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,115, 32, 61, 32, 95, 77, 46, -111,112,101,110, 40,109, 97,105,108,116, 46,115,101,114,118,101,114, 44, 32,109, - 97,105,108,116, 46,112,111,114,116, 44, 32,109, 97,105,108,116, 46, 99,114,101, - 97,116,101, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,101,120,116, 32, 61, - 32,115, 58,103,114,101,101,116, 40,109, 97,105,108,116, 46,100,111,109, 97,105, -110, 41, 10, 32, 32, 32, 32,115, 58, 97,117,116,104, 40,109, 97,105,108,116, 46, -117,115,101,114, 44, 32,109, 97,105,108,116, 46,112, 97,115,115,119,111,114,100, - 44, 32,101,120,116, 41, 10, 32, 32, 32, 32,115, 58,115,101,110,100, 40,109, 97, -105,108,116, 41, 10, 32, 32, 32, 32,115, 58,113,117,105,116, 40, 41, 10, 32, 32, - 32, 32,114,101,116,117,114,110, 32,115, 58, 99,108,111,115,101, 40, 41, 10,101, -110,100, 41, 10, 10,114,101,116,117,114,110, 32, 95, 77, -}; - - if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"smtp.lua")==0) lua_call(L, 0, LUA_MULTRET); -} diff --git a/libraries/luasocket/libluasocket/socket.h b/libraries/luasocket/libluasocket/socket.h index 63573de5d..2555bab64 100644 --- a/libraries/luasocket/libluasocket/socket.h +++ b/libraries/luasocket/libluasocket/socket.h @@ -16,8 +16,10 @@ \*=========================================================================*/ #ifdef _WIN32 #include "wsocket.h" +#define LUA_GAI_STRERROR gai_strerrorA #else #include "usocket.h" +#define LUA_GAI_STRERROR gai_strerror #endif /*=========================================================================*\ @@ -28,51 +30,46 @@ \*=========================================================================*/ #include "timeout.h" -/* we are lazy... */ +/* convenient shorthand */ typedef struct sockaddr SA; /*=========================================================================*\ * Functions bellow implement a comfortable platform independent * interface to sockets \*=========================================================================*/ -int socket_open(void); -int socket_close(void); -void socket_destroy(p_socket ps); -void socket_shutdown(p_socket ps, int how); -int socket_sendto(p_socket ps, const char *data, size_t count, - size_t *sent, SA *addr, socklen_t addr_len, p_timeout tm); -int socket_recvfrom(p_socket ps, char *data, size_t count, - size_t *got, SA *addr, socklen_t *addr_len, p_timeout tm); -void socket_setnonblocking(p_socket ps); -void socket_setblocking(p_socket ps); +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif int socket_waitfd(p_socket ps, int sw, p_timeout tm); -int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, - p_timeout tm); - -int socket_connect(p_socket ps, SA *addr, socklen_t addr_len, p_timeout tm); +int socket_open(void); +int socket_close(void); +void socket_destroy(p_socket ps); +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_timeout tm); int socket_create(p_socket ps, int domain, int type, int protocol); int socket_bind(p_socket ps, SA *addr, socklen_t addr_len); int socket_listen(p_socket ps, int backlog); -int socket_accept(p_socket ps, p_socket pa, SA *addr, - socklen_t *addr_len, p_timeout tm); - -const char *socket_hoststrerror(int err); -const char *socket_gaistrerror(int err); -const char *socket_strerror(int err); - -/* these are perfect to use with the io abstraction module - and the buffered input module */ -int socket_send(p_socket ps, const char *data, size_t count, - size_t *sent, p_timeout tm); +void socket_shutdown(p_socket ps, int how); +int socket_connect(p_socket ps, SA *addr, socklen_t addr_len, p_timeout tm); +int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *addr_len, p_timeout tm); +int socket_send(p_socket ps, const char *data, size_t count, size_t *sent, p_timeout tm); +int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, SA *addr, socklen_t addr_len, p_timeout tm); int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); -int socket_write(p_socket ps, const char *data, size_t count, - size_t *sent, p_timeout tm); +int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, SA *addr, socklen_t *addr_len, p_timeout tm); +int socket_write(p_socket ps, const char *data, size_t count, size_t *sent, p_timeout tm); int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); -const char *socket_ioerror(p_socket ps, int err); - +void socket_setblocking(p_socket ps); +void socket_setnonblocking(p_socket ps); int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp); int socket_gethostbyname(const char *addr, struct hostent **hp); +const char *socket_hoststrerror(int err); +const char *socket_strerror(int err); +const char *socket_ioerror(p_socket ps, int err); +const char *socket_gaistrerror(int err); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif #endif /* SOCKET_H */ diff --git a/libraries/luasocket/libluasocket/socket.lua b/libraries/luasocket/libluasocket/socket.lua index d1c0b1649..f70f3e1e4 100644 --- a/libraries/luasocket/libluasocket/socket.lua +++ b/libraries/luasocket/libluasocket/socket.lua @@ -1,3 +1,4 @@ +R"luastring"--( ----------------------------------------------------------------------------- -- LuaSocket helper module -- Author: Diego Nehab @@ -147,3 +148,5 @@ sourcet["default"] = sourcet["until-closed"] _M.source = _M.choose(sourcet) return _M +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/socket.lua.h b/libraries/luasocket/libluasocket/socket.lua.h deleted file mode 100644 index f99e58ec9..000000000 --- a/libraries/luasocket/libluasocket/socket.lua.h +++ /dev/null @@ -1,234 +0,0 @@ -/* code automatically generated by bin2c -- DO NOT EDIT */ -{ -/* #include'ing this file in a C program is equivalent to calling - if (luaL_loadfile(L,"socket.lua")==0) lua_call(L, 0, LUA_MULTRET); -*/ -/* socket.lua */ -static const unsigned char B1[]={ - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, - 32, 76,117, 97, 83,111, 99,107,101,116, 32,104,101,108,112,101,114, 32,109,111, -100,117,108,101, 10, 45, 45, 32, 65,117,116,104,111,114, 58, 32, 68,105,101,103, -111, 32, 78,101,104, 97, 98, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 10, 45, 45, 32, 68,101, 99,108, 97,114,101, 32,109,111,100,117, -108,101, 32, 97,110,100, 32,105,109,112,111,114,116, 32,100,101,112,101,110,100, -101,110, 99,105,101,115, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 10,108,111, 99, 97,108, 32, 98, 97,115,101, 32, 61, 32, 95, 71, - 10,108,111, 99, 97,108, 32,115,116,114,105,110,103, 32, 61, 32,114,101,113,117, -105,114,101, 40, 34,115,116,114,105,110,103, 34, 41, 10,108,111, 99, 97,108, 32, -109, 97,116,104, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,109, 97,116,104, - 34, 41, 10,108,111, 99, 97,108, 32,115,111, 99,107,101,116, 32, 61, 32,114,101, -113,117,105,114,101, 40, 34,115,111, 99,107,101,116, 46, 99,111,114,101, 34, 41, - 10, 10,108,111, 99, 97,108, 32, 95, 77, 32, 61, 32,115,111, 99,107,101,116, 10, - 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, - 45, 32, 69,120,112,111,114,116,101,100, 32, 97,117,120,105,108,105, 97,114, 32, -102,117,110, 99,116,105,111,110,115, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 10,102,117,110, 99,116,105,111,110, 32, 95, 77, 46, - 99,111,110,110,101, 99,116, 52, 40, 97,100,100,114,101,115,115, 44, 32,112,111, -114,116, 44, 32,108, 97,100,100,114,101,115,115, 44, 32,108,112,111,114,116, 41, - 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,111, 99,107,101,116, 46, 99, -111,110,110,101, 99,116, 40, 97,100,100,114,101,115,115, 44, 32,112,111,114,116, - 44, 32,108, 97,100,100,114,101,115,115, 44, 32,108,112,111,114,116, 44, 32, 34, -105,110,101,116, 34, 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, - 32, 95, 77, 46, 99,111,110,110,101, 99,116, 54, 40, 97,100,100,114,101,115,115, - 44, 32,112,111,114,116, 44, 32,108, 97,100,100,114,101,115,115, 44, 32,108,112, -111,114,116, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,111, 99,107, -101,116, 46, 99,111,110,110,101, 99,116, 40, 97,100,100,114,101,115,115, 44, 32, -112,111,114,116, 44, 32,108, 97,100,100,114,101,115,115, 44, 32,108,112,111,114, -116, 44, 32, 34,105,110,101,116, 54, 34, 41, 10,101,110,100, 10, 10,102,117,110, - 99,116,105,111,110, 32, 95, 77, 46, 98,105,110,100, 40,104,111,115,116, 44, 32, -112,111,114,116, 44, 32, 98, 97, 99,107,108,111,103, 41, 10, 32, 32, 32, 32,105, -102, 32,104,111,115,116, 32, 61, 61, 32, 34, 42, 34, 32,116,104,101,110, 32,104, -111,115,116, 32, 61, 32, 34, 48, 46, 48, 46, 48, 46, 48, 34, 32,101,110,100, 10, - 32, 32, 32, 32,108,111, 99, 97,108, 32, 97,100,100,114,105,110,102,111, 44, 32, -101,114,114, 32, 61, 32,115,111, 99,107,101,116, 46,100,110,115, 46,103,101,116, - 97,100,100,114,105,110,102,111, 40,104,111,115,116, 41, 59, 10, 32, 32, 32, 32, -105,102, 32,110,111,116, 32, 97,100,100,114,105,110,102,111, 32,116,104,101,110, - 32,114,101,116,117,114,110, 32,110,105,108, 44, 32,101,114,114, 32,101,110,100, - 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,115,111, 99,107, 44, 32,114,101,115, - 10, 32, 32, 32, 32,101,114,114, 32, 61, 32, 34,110,111, 32,105,110,102,111, 32, -111,110, 32, 97,100,100,114,101,115,115, 34, 10, 32, 32, 32, 32,102,111,114, 32, -105, 44, 32, 97,108,116, 32,105,110, 32, 98, 97,115,101, 46,105,112, 97,105,114, -115, 40, 97,100,100,114,105,110,102,111, 41, 32,100,111, 10, 32, 32, 32, 32, 32, - 32, 32, 32,105,102, 32, 97,108,116, 46,102, 97,109,105,108,121, 32, 61, 61, 32, - 34,105,110,101,116, 34, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,115,111, 99,107, 44, 32,101,114,114, 32, 61, 32,115,111, 99,107, -101,116, 46,116, 99,112, 52, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,108, -115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,115,111, 99,107, 44, - 32,101,114,114, 32, 61, 32,115,111, 99,107,101,116, 46,116, 99,112, 54, 40, 41, - 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, - 32,105,102, 32,110,111,116, 32,115,111, 99,107, 32,116,104,101,110, 32,114,101, -116,117,114,110, 32,110,105,108, 44, 32,101,114,114, 32,101,110,100, 10, 32, 32, - 32, 32, 32, 32, 32, 32,115,111, 99,107, 58,115,101,116,111,112,116,105,111,110, - 40, 34,114,101,117,115,101, 97,100,100,114, 34, 44, 32,116,114,117,101, 41, 10, - 32, 32, 32, 32, 32, 32, 32, 32,114,101,115, 44, 32,101,114,114, 32, 61, 32,115, -111, 99,107, 58, 98,105,110,100, 40, 97,108,116, 46, 97,100,100,114, 44, 32,112, -111,114,116, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,110,111,116, 32, -114,101,115, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32,115,111, 99,107, 58, 99,108,111,115,101, 40, 41, 10, 32, 32, 32, 32, 32, 32, - 32, 32,101,108,115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,114, -101,115, 44, 32,101,114,114, 32, 61, 32,115,111, 99,107, 58,108,105,115,116,101, -110, 40, 98, 97, 99,107,108,111,103, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32,105,102, 32,110,111,116, 32,114,101,115, 32,116,104,101,110, 10, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,115,111, 99,107, 58, - 99,108,111,115,101, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -101,108,115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32,114,101,116,117,114,110, 32,115,111, 99,107, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, - 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, -110,105,108, 44, 32,101,114,114, 10,101,110,100, 10, 10, 95, 77, 46,116,114,121, - 32, 61, 32, 95, 77, 46,110,101,119,116,114,121, 40, 41, 10, 10,102,117,110, 99, -116,105,111,110, 32, 95, 77, 46, 99,104,111,111,115,101, 40,116, 97, 98,108,101, - 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,102,117,110, 99,116,105,111, -110, 40,110, 97,109,101, 44, 32,111,112,116, 49, 44, 32,111,112,116, 50, 41, 10, - 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, 98, 97,115,101, 46,116,121,112,101, - 40,110, 97,109,101, 41, 32,126, 61, 32, 34,115,116,114,105,110,103, 34, 32,116, -104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,110, 97,109,101, - 44, 32,111,112,116, 49, 44, 32,111,112,116, 50, 32, 61, 32, 34,100,101,102, 97, -117,108,116, 34, 44, 32,110, 97,109,101, 44, 32,111,112,116, 49, 10, 32, 32, 32, - 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, - 97,108, 32,102, 32, 61, 32,116, 97, 98,108,101, 91,110, 97,109,101, 32,111,114, - 32, 34,110,105,108, 34, 93, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,110, -111,116, 32,102, 32,116,104,101,110, 32, 98, 97,115,101, 46,101,114,114,111,114, - 40, 34,117,110,107,110,111,119,110, 32,107,101,121, 32, 40, 34, 46, 46, 32, 98, - 97,115,101, 46,116,111,115,116,114,105,110,103, 40,110, 97,109,101, 41, 32, 46, - 46, 34, 41, 34, 44, 32, 51, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115, -101, 32,114,101,116,117,114,110, 32,102, 40,111,112,116, 49, 44, 32,111,112,116, - 50, 41, 32,101,110,100, 10, 32, 32, 32, 32,101,110,100, 10,101,110,100, 10, 10, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, - 32, 83,111, 99,107,101,116, 32,115,111,117,114, 99,101,115, 32, 97,110,100, 32, -115,105,110,107,115, 44, 32, 99,111,110,102,111,114,109,105,110,103, 32,116,111, - 32, 76, 84, 78, 49, 50, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 10, 45, 45, 32, 99,114,101, 97,116,101, 32,110, 97,109,101,115, -112, 97, 99,101,115, 32,105,110,115,105,100,101, 32, 76,117, 97, 83,111, 99,107, -101,116, 32,110, 97,109,101,115,112, 97, 99,101, 10,108,111, 99, 97,108, 32,115, -111,117,114, 99,101,116, 44, 32,115,105,110,107,116, 32, 61, 32,123,125, 44, 32, -123,125, 10, 95, 77, 46,115,111,117,114, 99,101,116, 32, 61, 32,115,111,117,114, - 99,101,116, 10, 95, 77, 46,115,105,110,107,116, 32, 61, 32,115,105,110,107,116, - 10, 10, 95, 77, 46, 66, 76, 79, 67, 75, 83, 73, 90, 69, 32, 61, 32, 50, 48, 52, - 56, 10, 10,115,105,110,107,116, 91, 34, 99,108,111,115,101, 45,119,104,101,110, - 45,100,111,110,101, 34, 93, 32, 61, 32,102,117,110, 99,116,105,111,110, 40,115, -111, 99,107, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, 98, 97,115,101, - 46,115,101,116,109,101,116, 97,116, 97, 98,108,101, 40,123, 10, 32, 32, 32, 32, - 32, 32, 32, 32,103,101,116,102,100, 32, 61, 32,102,117,110, 99,116,105,111,110, - 40, 41, 32,114,101,116,117,114,110, 32,115,111, 99,107, 58,103,101,116,102,100, - 40, 41, 32,101,110,100, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32,100,105,114,116, -121, 32, 61, 32,102,117,110, 99,116,105,111,110, 40, 41, 32,114,101,116,117,114, -110, 32,115,111, 99,107, 58,100,105,114,116,121, 40, 41, 32,101,110,100, 10, 32, - 32, 32, 32,125, 44, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 95, 95, 99, 97, -108,108, 32, 61, 32,102,117,110, 99,116,105,111,110, 40,115,101,108,102, 44, 32, - 99,104,117,110,107, 44, 32,101,114,114, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,105,102, 32,110,111,116, 32, 99,104,117,110,107, 32,116,104,101, -110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,115,111, - 99,107, 58, 99,108,111,115,101, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32, 49, 10, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114,110, 32, -115,111, 99,107, 58,115,101,110,100, 40, 99,104,117,110,107, 41, 32,101,110,100, - 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,125, 41, 10, -101,110,100, 10, 10,115,105,110,107,116, 91, 34,107,101,101,112, 45,111,112,101, -110, 34, 93, 32, 61, 32,102,117,110, 99,116,105,111,110, 40,115,111, 99,107, 41, - 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, 98, 97,115,101, 46,115,101,116, -109,101,116, 97,116, 97, 98,108,101, 40,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, -103,101,116,102,100, 32, 61, 32,102,117,110, 99,116,105,111,110, 40, 41, 32,114, -101,116,117,114,110, 32,115,111, 99,107, 58,103,101,116,102,100, 40, 41, 32,101, -110,100, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32,100,105,114,116,121, 32, 61, 32, -102,117,110, 99,116,105,111,110, 40, 41, 32,114,101,116,117,114,110, 32,115,111, - 99,107, 58,100,105,114,116,121, 40, 41, 32,101,110,100, 10, 32, 32, 32, 32,125, - 44, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 95, 95, 99, 97,108,108, 32, 61, - 32,102,117,110, 99,116,105,111,110, 40,115,101,108,102, 44, 32, 99,104,117,110, -107, 44, 32,101,114,114, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -105,102, 32, 99,104,117,110,107, 32,116,104,101,110, 32,114,101,116,117,114,110, - 32,115,111, 99,107, 58,115,101,110,100, 40, 99,104,117,110,107, 41, 10, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114, -110, 32, 49, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, - 32, 32, 32, 32,125, 41, 10,101,110,100, 10, 10,115,105,110,107,116, 91, 34,100, -101,102, 97,117,108,116, 34, 93, 32, 61, 32,115,105,110,107,116, 91, 34,107,101, -101,112, 45,111,112,101,110, 34, 93, 10, 10, 95, 77, 46,115,105,110,107, 32, 61, - 32, 95, 77, 46, 99,104,111,111,115,101, 40,115,105,110,107,116, 41, 10, 10,115, -111,117,114, 99,101,116, 91, 34, 98,121, 45,108,101,110,103,116,104, 34, 93, 32, - 61, 32,102,117,110, 99,116,105,111,110, 40,115,111, 99,107, 44, 32,108,101,110, -103,116,104, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, 98, 97,115,101, - 46,115,101,116,109,101,116, 97,116, 97, 98,108,101, 40,123, 10, 32, 32, 32, 32, - 32, 32, 32, 32,103,101,116,102,100, 32, 61, 32,102,117,110, 99,116,105,111,110, - 40, 41, 32,114,101,116,117,114,110, 32,115,111, 99,107, 58,103,101,116,102,100, - 40, 41, 32,101,110,100, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32,100,105,114,116, -121, 32, 61, 32,102,117,110, 99,116,105,111,110, 40, 41, 32,114,101,116,117,114, -110, 32,115,111, 99,107, 58,100,105,114,116,121, 40, 41, 32,101,110,100, 10, 32, - 32, 32, 32,125, 44, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 95, 95, 99, 97, -108,108, 32, 61, 32,102,117,110, 99,116,105,111,110, 40, 41, 10, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,108,101,110,103,116,104, 32, 60, 61, - 32, 48, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,110,105,108, 32,101, -110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, - 32,115,105,122,101, 32, 61, 32,109, 97,116,104, 46,109,105,110, 40,115,111, 99, -107,101,116, 46, 66, 76, 79, 67, 75, 83, 73, 90, 69, 44, 32,108,101,110,103,116, -104, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, - 32, 99,104,117,110,107, 44, 32,101,114,114, 32, 61, 32,115,111, 99,107, 58,114, -101, 99,101,105,118,101, 40,115,105,122,101, 41, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32,105,102, 32,101,114,114, 32,116,104,101,110, 32,114,101,116, -117,114,110, 32,110,105,108, 44, 32,101,114,114, 32,101,110,100, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32,108,101,110,103,116,104, 32, 61, 32,108,101, -110,103,116,104, 32, 45, 32,115,116,114,105,110,103, 46,108,101,110, 40, 99,104, -117,110,107, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116, -117,114,110, 32, 99,104,117,110,107, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110, -100, 10, 32, 32, 32, 32,125, 41, 10,101,110,100, 10, 10,115,111,117,114, 99,101, -116, 91, 34,117,110,116,105,108, 45, 99,108,111,115,101,100, 34, 93, 32, 61, 32, -102,117,110, 99,116,105,111,110, 40,115,111, 99,107, 41, 10, 32, 32, 32, 32,108, -111, 99, 97,108, 32,100,111,110,101, 10, 32, 32, 32, 32,114,101,116,117,114,110, - 32, 98, 97,115,101, 46,115,101,116,109,101,116, 97,116, 97, 98,108,101, 40,123, - 10, 32, 32, 32, 32, 32, 32, 32, 32,103,101,116,102,100, 32, 61, 32,102,117,110, - 99,116,105,111,110, 40, 41, 32,114,101,116,117,114,110, 32,115,111, 99,107, 58, -103,101,116,102,100, 40, 41, 32,101,110,100, 44, 10, 32, 32, 32, 32, 32, 32, 32, - 32,100,105,114,116,121, 32, 61, 32,102,117,110, 99,116,105,111,110, 40, 41, 32, -114,101,116,117,114,110, 32,115,111, 99,107, 58,100,105,114,116,121, 40, 41, 32, -101,110,100, 10, 32, 32, 32, 32,125, 44, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 95, 95, 99, 97,108,108, 32, 61, 32,102,117,110, 99,116,105,111,110, 40, 41, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,100,111,110,101, - 32,116,104,101,110, 32,114,101,116,117,114,110, 32,110,105,108, 32,101,110,100, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32, 99, -104,117,110,107, 44, 32,101,114,114, 44, 32,112, 97,114,116,105, 97,108, 32, 61, - 32,115,111, 99,107, 58,114,101, 99,101,105,118,101, 40,115,111, 99,107,101,116, - 46, 66, 76, 79, 67, 75, 83, 73, 90, 69, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,105,102, 32,110,111,116, 32,101,114,114, 32,116,104,101,110, 32, -114,101,116,117,114,110, 32, 99,104,117,110,107, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32,101,108,115,101,105,102, 32,101,114,114, 32, 61, 61, 32, 34, - 99,108,111,115,101,100, 34, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32,115,111, 99,107, 58, 99,108,111,115,101, 40, - 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,100,111, -110,101, 32, 61, 32, 49, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32,114,101,116,117,114,110, 32,112, 97,114,116,105, 97,108, 10, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114, -110, 32,110,105,108, 44, 32,101,114,114, 32,101,110,100, 10, 32, 32, 32, 32, 32, - 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,125, 41, 10,101,110,100, 10, 10, 10, -115,111,117,114, 99,101,116, 91, 34,100,101,102, 97,117,108,116, 34, 93, 32, 61, - 32,115,111,117,114, 99,101,116, 91, 34,117,110,116,105,108, 45, 99,108,111,115, -101,100, 34, 93, 10, 10, 95, 77, 46,115,111,117,114, 99,101, 32, 61, 32, 95, 77, - 46, 99,104,111,111,115,101, 40,115,111,117,114, 99,101,116, 41, 10, 10,114,101, -116,117,114,110, 32, 95, 77, 10, -}; - - if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"socket.lua")==0) lua_call(L, 0, LUA_MULTRET); -} diff --git a/libraries/luasocket/libluasocket/tcp.c b/libraries/luasocket/libluasocket/tcp.c index aa4c85a8a..e84db8454 100644 --- a/libraries/luasocket/libluasocket/tcp.c +++ b/libraries/luasocket/libluasocket/tcp.c @@ -2,11 +2,7 @@ * TCP object * LuaSocket toolkit \*=========================================================================*/ -#include - -#include "lua.h" -#include "lauxlib.h" -#include "compat.h" +#include "luasocket.h" #include "auxiliar.h" #include "socket.h" @@ -14,12 +10,14 @@ #include "options.h" #include "tcp.h" +#include + /*=========================================================================*\ * Internal function prototypes \*=========================================================================*/ static int global_create(lua_State *L); static int global_create4(lua_State *L); -// static int global_create6(lua_State *L); +static int global_create6(lua_State *L); static int global_connect(lua_State *L); static int meth_connect(lua_State *L); static int meth_listen(lua_State *L); @@ -77,8 +75,19 @@ static t_opt optget[] = { {"reuseaddr", opt_get_reuseaddr}, {"reuseport", opt_get_reuseport}, {"tcp-nodelay", opt_get_tcp_nodelay}, +#ifdef TCP_KEEPIDLE + {"tcp-keepidle", opt_get_tcp_keepidle}, +#endif +#ifdef TCP_KEEPCNT + {"tcp-keepcnt", opt_get_tcp_keepcnt}, +#endif +#ifdef TCP_KEEPINTVL + {"tcp-keepintvl", opt_get_tcp_keepintvl}, +#endif {"linger", opt_get_linger}, {"error", opt_get_error}, + {"recv-buffer-size", opt_get_recv_buf_size}, + {"send-buffer-size", opt_get_send_buf_size}, {NULL, NULL} }; @@ -87,8 +96,28 @@ static t_opt optset[] = { {"reuseaddr", opt_set_reuseaddr}, {"reuseport", opt_set_reuseport}, {"tcp-nodelay", opt_set_tcp_nodelay}, - // {"ipv6-v6only", opt_set_ip6_v6only}, +#ifdef TCP_KEEPIDLE + {"tcp-keepidle", opt_set_tcp_keepidle}, +#endif +#ifdef TCP_KEEPCNT + {"tcp-keepcnt", opt_set_tcp_keepcnt}, +#endif +#ifdef TCP_KEEPINTVL + {"tcp-keepintvl", opt_set_tcp_keepintvl}, +#endif + {"ipv6-v6only", opt_set_ip6_v6only}, {"linger", opt_set_linger}, + {"recv-buffer-size", opt_set_recv_buf_size}, + {"send-buffer-size", opt_set_send_buf_size}, +#ifdef TCP_DEFER_ACCEPT + {"tcp-defer-accept", opt_set_tcp_defer_accept}, +#endif +#ifdef TCP_FASTOPEN + {"tcp-fastopen", opt_set_tcp_fastopen}, +#endif +#ifdef TCP_FASTOPEN_CONNECT + {"tcp-fastopen-connect", opt_set_tcp_fastopen_connect}, +#endif {NULL, NULL} }; @@ -96,7 +125,7 @@ static t_opt optset[] = { static luaL_Reg func[] = { {"tcp", global_create}, {"tcp4", global_create4}, - // {"tcp6", global_create6}, + {"tcp6", global_create6}, {"connect", global_connect}, {NULL, NULL} }; @@ -398,9 +427,9 @@ static int global_create4(lua_State *L) { return tcp_create(L, AF_INET); } -// static int global_create6(lua_State *L) { -// return tcp_create(L, AF_INET6); -// } +static int global_create6(lua_State *L) { + return tcp_create(L, AF_INET6); +} static int global_connect(lua_State *L) { const char *remoteaddr = luaL_checkstring(L, 1); diff --git a/libraries/luasocket/libluasocket/tcp.h b/libraries/luasocket/libluasocket/tcp.h index eded62032..9b282efeb 100644 --- a/libraries/luasocket/libluasocket/tcp.h +++ b/libraries/luasocket/libluasocket/tcp.h @@ -14,7 +14,7 @@ * tcp objects either connected to some address or returned by the accept * method of a server object. \*=========================================================================*/ -#include "lua.h" +#include "luasocket.h" #include "buffer.h" #include "timeout.h" @@ -30,6 +30,14 @@ typedef struct t_tcp_ { typedef t_tcp *p_tcp; +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + int tcp_open(lua_State *L); +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + #endif /* TCP_H */ diff --git a/libraries/luasocket/libluasocket/timeout.c b/libraries/luasocket/libluasocket/timeout.c index 5a601d541..2bdc0698c 100644 --- a/libraries/luasocket/libluasocket/timeout.c +++ b/libraries/luasocket/libluasocket/timeout.c @@ -2,17 +2,15 @@ * Timeout management functions * LuaSocket toolkit \*=========================================================================*/ -#include -#include -#include - -#include "lua.h" -#include "lauxlib.h" -#include "compat.h" +#include "luasocket.h" #include "auxiliar.h" #include "timeout.h" +#include +#include +#include + #ifdef _WIN32 #include #else diff --git a/libraries/luasocket/libluasocket/timeout.h b/libraries/luasocket/libluasocket/timeout.h index af902318b..9e5250d33 100644 --- a/libraries/luasocket/libluasocket/timeout.h +++ b/libraries/luasocket/libluasocket/timeout.h @@ -4,7 +4,7 @@ * Timeout management functions * LuaSocket toolkit \*=========================================================================*/ -#include "lua.h" +#include "luasocket.h" /* timeout control structure */ typedef struct t_timeout_ { @@ -14,16 +14,27 @@ typedef struct t_timeout_ { } t_timeout; typedef t_timeout *p_timeout; -int timeout_open(lua_State *L); +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + void timeout_init(p_timeout tm, double block, double total); double timeout_get(p_timeout tm); +double timeout_getstart(p_timeout tm); double timeout_getretry(p_timeout tm); p_timeout timeout_markstart(p_timeout tm); -double timeout_getstart(p_timeout tm); + double timeout_gettime(void); + +int timeout_open(lua_State *L); + int timeout_meth_settimeout(lua_State *L, p_timeout tm); int timeout_meth_gettimeout(lua_State *L, p_timeout tm); +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + #define timeout_iszero(tm) ((tm)->block == 0.0) #endif /* TIMEOUT_H */ diff --git a/libraries/luasocket/libluasocket/tp.lua b/libraries/luasocket/libluasocket/tp.lua index b8ebc56d1..03a5344d7 100644 --- a/libraries/luasocket/libluasocket/tp.lua +++ b/libraries/luasocket/libluasocket/tp.lua @@ -1,3 +1,4 @@ +R"luastring"--( ----------------------------------------------------------------------------- -- Unified SMTP/FTP subsystem -- LuaSocket toolkit. @@ -132,3 +133,5 @@ function _M.connect(host, port, timeout, create) end return _M +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/tp.lua.h b/libraries/luasocket/libluasocket/tp.lua.h deleted file mode 100644 index cde8b6480..000000000 --- a/libraries/luasocket/libluasocket/tp.lua.h +++ /dev/null @@ -1,200 +0,0 @@ -/* code automatically generated by bin2c -- DO NOT EDIT */ -{ -/* #include'ing this file in a C program is equivalent to calling - if (luaL_loadfile(L,"tp.lua")==0) lua_call(L, 0, LUA_MULTRET); -*/ -/* tp.lua */ -static const unsigned char B1[]={ - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, - 32, 85,110,105,102,105,101,100, 32, 83, 77, 84, 80, 47, 70, 84, 80, 32,115,117, - 98,115,121,115,116,101,109, 10, 45, 45, 32, 76,117, 97, 83,111, 99,107,101,116, - 32,116,111,111,108,107,105,116, 46, 10, 45, 45, 32, 65,117,116,104,111,114, 58, - 32, 68,105,101,103,111, 32, 78,101,104, 97, 98, 10, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 68,101, 99,108, 97,114,101, - 32,109,111,100,117,108,101, 32, 97,110,100, 32,105,109,112,111,114,116, 32,100, -101,112,101,110,100,101,110, 99,105,101,115, 10, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 10,108,111, 99, 97,108, 32, 98, 97,115,101, - 32, 61, 32, 95, 71, 10,108,111, 99, 97,108, 32,115,116,114,105,110,103, 32, 61, - 32,114,101,113,117,105,114,101, 40, 34,115,116,114,105,110,103, 34, 41, 10,108, -111, 99, 97,108, 32,115,111, 99,107,101,116, 32, 61, 32,114,101,113,117,105,114, -101, 40, 34,115,111, 99,107,101,116, 34, 41, 10,108,111, 99, 97,108, 32,108,116, -110, 49, 50, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,108,116,110, 49, 50, - 34, 41, 10, 10,115,111, 99,107,101,116, 46,116,112, 32, 61, 32,123,125, 10,108, -111, 99, 97,108, 32, 95, 77, 32, 61, 32,115,111, 99,107,101,116, 46,116,112, 10, - 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, - 45, 32, 80,114,111,103,114, 97,109, 32, 99,111,110,115,116, 97,110,116,115, 10, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 95, 77, - 46, 84, 73, 77, 69, 79, 85, 84, 32, 61, 32, 54, 48, 10, 10, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 73,109,112,108, -101,109,101,110,116, 97,116,105,111,110, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32,103,101,116,115, 32,115,101,114, -118,101,114, 32,114,101,112,108,121, 32, 40,119,111,114,107,115, 32,102,111,114, - 32, 83, 77, 84, 80, 32, 97,110,100, 32, 70, 84, 80, 41, 10,108,111, 99, 97,108, - 32,102,117,110, 99,116,105,111,110, 32,103,101,116, 95,114,101,112,108,121, 40, - 99, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 99,111,100,101, 44, 32, 99, -117,114,114,101,110,116, 44, 32,115,101,112, 10, 32, 32, 32, 32,108,111, 99, 97, -108, 32,108,105,110,101, 44, 32,101,114,114, 32, 61, 32, 99, 58,114,101, 99,101, -105,118,101, 40, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,114,101,112,108, -121, 32, 61, 32,108,105,110,101, 10, 32, 32, 32, 32,105,102, 32,101,114,114, 32, -116,104,101,110, 32,114,101,116,117,114,110, 32,110,105,108, 44, 32,101,114,114, - 32,101,110,100, 10, 32, 32, 32, 32, 99,111,100,101, 44, 32,115,101,112, 32, 61, - 32,115,111, 99,107,101,116, 46,115,107,105,112, 40, 50, 44, 32,115,116,114,105, -110,103, 46,102,105,110,100, 40,108,105,110,101, 44, 32, 34, 94, 40, 37,100, 37, -100, 37,100, 41, 40, 46, 63, 41, 34, 41, 41, 10, 32, 32, 32, 32,105,102, 32,110, -111,116, 32, 99,111,100,101, 32,116,104,101,110, 32,114,101,116,117,114,110, 32, -110,105,108, 44, 32, 34,105,110,118, 97,108,105,100, 32,115,101,114,118,101,114, - 32,114,101,112,108,121, 34, 32,101,110,100, 10, 32, 32, 32, 32,105,102, 32,115, -101,112, 32, 61, 61, 32, 34, 45, 34, 32,116,104,101,110, 32, 45, 45, 32,114,101, -112,108,121, 32,105,115, 32,109,117,108,116,105,108,105,110,101, 10, 32, 32, 32, - 32, 32, 32, 32, 32,114,101,112,101, 97,116, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,108,105,110,101, 44, 32,101,114,114, 32, 61, 32, 99, 58,114,101, - 99,101,105,118,101, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -105,102, 32,101,114,114, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,110, -105,108, 44, 32,101,114,114, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 99,117,114,114,101,110,116, 44, 32,115,101,112, 32, 61, 32,115, -111, 99,107,101,116, 46,115,107,105,112, 40, 50, 44, 32,115,116,114,105,110,103, - 46,102,105,110,100, 40,108,105,110,101, 44, 32, 34, 94, 40, 37,100, 37,100, 37, -100, 41, 40, 46, 63, 41, 34, 41, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32,114,101,112,108,121, 32, 61, 32,114,101,112,108,121, 32, 46, 46, 32, 34, - 92,110, 34, 32, 46, 46, 32,108,105,110,101, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 45, 45, 32,114,101,112,108,121, 32,101,110,100,115, 32,119,105,116,104, 32,115, - 97,109,101, 32, 99,111,100,101, 10, 32, 32, 32, 32, 32, 32, 32, 32,117,110,116, -105,108, 32, 99,111,100,101, 32, 61, 61, 32, 99,117,114,114,101,110,116, 32, 97, -110,100, 32,115,101,112, 32, 61, 61, 32, 34, 32, 34, 10, 32, 32, 32, 32,101,110, -100, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, 99,111,100,101, 44, 32,114, -101,112,108,121, 10,101,110,100, 10, 10, 45, 45, 32,109,101,116, 97,116, 97, 98, -108,101, 32,102,111,114, 32,115,111, 99,107, 32,111, 98,106,101, 99,116, 10,108, -111, 99, 97,108, 32,109,101,116, 97,116, 32, 61, 32,123, 32, 95, 95,105,110,100, -101,120, 32, 61, 32,123,125, 32,125, 10, 10,102,117,110, 99,116,105,111,110, 32, -109,101,116, 97,116, 46, 95, 95,105,110,100,101,120, 58,103,101,116,112,101,101, -114,110, 97,109,101, 40, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115, -101,108,102, 46, 99, 58,103,101,116,112,101,101,114,110, 97,109,101, 40, 41, 10, -101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, - 95, 95,105,110,100,101,120, 58,103,101,116,115,111, 99,107,110, 97,109,101, 40, - 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,101,108,102, 46, 99, 58, -103,101,116,112,101,101,114,110, 97,109,101, 40, 41, 10,101,110,100, 10, 10,102, -117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101, -120, 58, 99,104,101, 99,107, 40,111,107, 41, 10, 32, 32, 32, 32,108,111, 99, 97, -108, 32, 99,111,100,101, 44, 32,114,101,112,108,121, 32, 61, 32,103,101,116, 95, -114,101,112,108,121, 40,115,101,108,102, 46, 99, 41, 10, 32, 32, 32, 32,105,102, - 32,110,111,116, 32, 99,111,100,101, 32,116,104,101,110, 32,114,101,116,117,114, -110, 32,110,105,108, 44, 32,114,101,112,108,121, 32,101,110,100, 10, 32, 32, 32, - 32,105,102, 32, 98, 97,115,101, 46,116,121,112,101, 40,111,107, 41, 32,126, 61, - 32, 34,102,117,110, 99,116,105,111,110, 34, 32,116,104,101,110, 10, 32, 32, 32, - 32, 32, 32, 32, 32,105,102, 32, 98, 97,115,101, 46,116,121,112,101, 40,111,107, - 41, 32, 61, 61, 32, 34,116, 97, 98,108,101, 34, 32,116,104,101,110, 10, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,102,111,114, 32,105, 44, 32,118, 32,105, -110, 32, 98, 97,115,101, 46,105,112, 97,105,114,115, 40,111,107, 41, 32,100,111, - 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, -115,116,114,105,110,103, 46,102,105,110,100, 40, 99,111,100,101, 44, 32,118, 41, - 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32, 98, 97,115,101, 46,116,111, -110,117,109, 98,101,114, 40, 99,111,100,101, 41, 44, 32,114,101,112,108,121, 10, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32,110,105,108, 44, 32, -114,101,112,108,121, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 10, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,115,116,114,105,110,103, - 46,102,105,110,100, 40, 99,111,100,101, 44, 32,111,107, 41, 32,116,104,101,110, - 32,114,101,116,117,114,110, 32, 98, 97,115,101, 46,116,111,110,117,109, 98,101, -114, 40, 99,111,100,101, 41, 44, 32,114,101,112,108,121, 10, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114,110, 32,110, -105,108, 44, 32,114,101,112,108,121, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, - 32, 32,101,110,100, 10, 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114, -110, 32,111,107, 40, 98, 97,115,101, 46,116,111,110,117,109, 98,101,114, 40, 99, -111,100,101, 41, 44, 32,114,101,112,108,121, 41, 32,101,110,100, 10,101,110,100, - 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105, -110,100,101,120, 58, 99,111,109,109, 97,110,100, 40, 99,109,100, 44, 32, 97,114, -103, 41, 10, 32, 32, 32, 32, 99,109,100, 32, 61, 32,115,116,114,105,110,103, 46, -117,112,112,101,114, 40, 99,109,100, 41, 10, 32, 32, 32, 32,105,102, 32, 97,114, -103, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114, -110, 32,115,101,108,102, 46, 99, 58,115,101,110,100, 40, 99,109,100, 32, 46, 46, - 32, 34, 32, 34, 32, 46, 46, 32, 97,114,103, 46, 46, 32, 34, 92,114, 92,110, 34, - 41, 10, 32, 32, 32, 32,101,108,115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32,114, -101,116,117,114,110, 32,115,101,108,102, 46, 99, 58,115,101,110,100, 40, 99,109, -100, 32, 46, 46, 32, 34, 92,114, 92,110, 34, 41, 10, 32, 32, 32, 32,101,110,100, - 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97,116, - 46, 95, 95,105,110,100,101,120, 58,115,105,110,107, 40,115,110,107, 44, 32,112, - 97,116, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 99,104,117,110,107, 44, - 32,101,114,114, 32, 61, 32,115,101,108,102, 46, 99, 58,114,101, 99,101,105,118, -101, 40,112, 97,116, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,110, -107, 40, 99,104,117,110,107, 44, 32,101,114,114, 41, 10,101,110,100, 10, 10,102, -117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101, -120, 58,115,101,110,100, 40,100, 97,116, 97, 41, 10, 32, 32, 32, 32,114,101,116, -117,114,110, 32,115,101,108,102, 46, 99, 58,115,101,110,100, 40,100, 97,116, 97, - 41, 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97, -116, 46, 95, 95,105,110,100,101,120, 58,114,101, 99,101,105,118,101, 40,112, 97, -116, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,101,108,102, 46, 99, - 58,114,101, 99,101,105,118,101, 40,112, 97,116, 41, 10,101,110,100, 10, 10,102, -117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110,100,101, -120, 58,103,101,116,102,100, 40, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, - 32,115,101,108,102, 46, 99, 58,103,101,116,102,100, 40, 41, 10,101,110,100, 10, - 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105,110, -100,101,120, 58,100,105,114,116,121, 40, 41, 10, 32, 32, 32, 32,114,101,116,117, -114,110, 32,115,101,108,102, 46, 99, 58,100,105,114,116,121, 40, 41, 10,101,110, -100, 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95, -105,110,100,101,120, 58,103,101,116, 99,111,110,116,114,111,108, 40, 41, 10, 32, - 32, 32, 32,114,101,116,117,114,110, 32,115,101,108,102, 46, 99, 10,101,110,100, - 10, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95,105, -110,100,101,120, 58,115,111,117,114, 99,101, 40,115,111,117,114, 99,101, 44, 32, -115,116,101,112, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,115,105,110,107, - 32, 61, 32,115,111, 99,107,101,116, 46,115,105,110,107, 40, 34,107,101,101,112, - 45,111,112,101,110, 34, 44, 32,115,101,108,102, 46, 99, 41, 10, 32, 32, 32, 32, -108,111, 99, 97,108, 32,114,101,116, 44, 32,101,114,114, 32, 61, 32,108,116,110, - 49, 50, 46,112,117,109,112, 46, 97,108,108, 40,115,111,117,114, 99,101, 44, 32, -115,105,110,107, 44, 32,115,116,101,112, 32,111,114, 32,108,116,110, 49, 50, 46, -112,117,109,112, 46,115,116,101,112, 41, 10, 32, 32, 32, 32,114,101,116,117,114, -110, 32,114,101,116, 44, 32,101,114,114, 10,101,110,100, 10, 10, 45, 45, 32, 99, -108,111,115,101,115, 32,116,104,101, 32,117,110,100,101,114,108,121,105,110,103, - 32, 99, 10,102,117,110, 99,116,105,111,110, 32,109,101,116, 97,116, 46, 95, 95, -105,110,100,101,120, 58, 99,108,111,115,101, 40, 41, 10, 32, 32, 32, 32,115,101, -108,102, 46, 99, 58, 99,108,111,115,101, 40, 41, 10, 32, 32, 32, 32,114,101,116, -117,114,110, 32, 49, 10,101,110,100, 10, 10, 45, 45, 32, 99,111,110,110,101, 99, -116, 32,119,105,116,104, 32,115,101,114,118,101,114, 32, 97,110,100, 32,114,101, -116,117,114,110, 32, 99, 32,111, 98,106,101, 99,116, 10,102,117,110, 99,116,105, -111,110, 32, 95, 77, 46, 99,111,110,110,101, 99,116, 40,104,111,115,116, 44, 32, -112,111,114,116, 44, 32,116,105,109,101,111,117,116, 44, 32, 99,114,101, 97,116, -101, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 99, 44, 32,101, 32, 61, 32, - 40, 99,114,101, 97,116,101, 32,111,114, 32,115,111, 99,107,101,116, 46,116, 99, -112, 41, 40, 41, 10, 32, 32, 32, 32,105,102, 32,110,111,116, 32, 99, 32,116,104, -101,110, 32,114,101,116,117,114,110, 32,110,105,108, 44, 32,101, 32,101,110,100, - 10, 32, 32, 32, 32, 99, 58,115,101,116,116,105,109,101,111,117,116, 40,116,105, -109,101,111,117,116, 32,111,114, 32, 95, 77, 46, 84, 73, 77, 69, 79, 85, 84, 41, - 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,114, 44, 32,101, 32, 61, 32, 99, 58, - 99,111,110,110,101, 99,116, 40,104,111,115,116, 44, 32,112,111,114,116, 41, 10, - 32, 32, 32, 32,105,102, 32,110,111,116, 32,114, 32,116,104,101,110, 10, 32, 32, - 32, 32, 32, 32, 32, 32, 99, 58, 99,108,111,115,101, 40, 41, 10, 32, 32, 32, 32, - 32, 32, 32, 32,114,101,116,117,114,110, 32,110,105,108, 44, 32,101, 10, 32, 32, - 32, 32,101,110,100, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, 98, 97,115, -101, 46,115,101,116,109,101,116, 97,116, 97, 98,108,101, 40,123, 99, 32, 61, 32, - 99,125, 44, 32,109,101,116, 97,116, 41, 10,101,110,100, 10, 10,114,101,116,117, -114,110, 32, 95, 77, 10, -}; - - if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"tp.lua")==0) lua_call(L, 0, LUA_MULTRET); -} diff --git a/libraries/luasocket/libluasocket/udp.c b/libraries/luasocket/libluasocket/udp.c index 33f62d4b6..712ad50fe 100644 --- a/libraries/luasocket/libluasocket/udp.c +++ b/libraries/luasocket/libluasocket/udp.c @@ -2,12 +2,7 @@ * UDP object * LuaSocket toolkit \*=========================================================================*/ -#include -#include - -#include "lua.h" -#include "lauxlib.h" -#include "compat.h" +#include "luasocket.h" #include "auxiliar.h" #include "socket.h" @@ -15,6 +10,9 @@ #include "options.h" #include "udp.h" +#include +#include + /* min and max macros */ #ifndef MIN #define MIN(x, y) ((x) < (y) ? x : y) @@ -28,7 +26,7 @@ \*=========================================================================*/ static int global_create(lua_State *L); static int global_create4(lua_State *L); -// static int global_create6(lua_State *L); +static int global_create6(lua_State *L); static int meth_send(lua_State *L); static int meth_sendto(lua_State *L); static int meth_receive(lua_State *L); @@ -77,17 +75,19 @@ static t_opt optset[] = { {"broadcast", opt_set_broadcast}, {"reuseaddr", opt_set_reuseaddr}, {"reuseport", opt_set_reuseport}, - // {"ip-multicast-if", opt_set_ip_multicast_if}, - // {"ip-multicast-ttl", opt_set_ip_multicast_ttl}, + {"ip-multicast-if", opt_set_ip_multicast_if}, + {"ip-multicast-ttl", opt_set_ip_multicast_ttl}, {"ip-multicast-loop", opt_set_ip_multicast_loop}, {"ip-add-membership", opt_set_ip_add_membership}, {"ip-drop-membership", opt_set_ip_drop_membersip}, - // {"ipv6-unicast-hops", opt_set_ip6_unicast_hops}, - // {"ipv6-multicast-hops", opt_set_ip6_unicast_hops}, - // {"ipv6-multicast-loop", opt_set_ip6_multicast_loop}, - // {"ipv6-add-membership", opt_set_ip6_add_membership}, - // {"ipv6-drop-membership", opt_set_ip6_drop_membersip}, - // {"ipv6-v6only", opt_set_ip6_v6only}, + {"ipv6-unicast-hops", opt_set_ip6_unicast_hops}, + {"ipv6-multicast-hops", opt_set_ip6_unicast_hops}, + {"ipv6-multicast-loop", opt_set_ip6_multicast_loop}, + {"ipv6-add-membership", opt_set_ip6_add_membership}, + {"ipv6-drop-membership", opt_set_ip6_drop_membersip}, + {"ipv6-v6only", opt_set_ip6_v6only}, + {"recv-buffer-size", opt_set_recv_buf_size}, + {"send-buffer-size", opt_set_send_buf_size}, {NULL, NULL} }; @@ -97,13 +97,15 @@ static t_opt optget[] = { {"broadcast", opt_get_broadcast}, {"reuseaddr", opt_get_reuseaddr}, {"reuseport", opt_get_reuseport}, - // {"ip-multicast-if", opt_get_ip_multicast_if}, + {"ip-multicast-if", opt_get_ip_multicast_if}, {"ip-multicast-loop", opt_get_ip_multicast_loop}, {"error", opt_get_error}, - // {"ipv6-unicast-hops", opt_get_ip6_unicast_hops}, - // {"ipv6-multicast-hops", opt_get_ip6_unicast_hops}, - // {"ipv6-multicast-loop", opt_get_ip6_multicast_loop}, - // {"ipv6-v6only", opt_get_ip6_v6only}, + {"ipv6-unicast-hops", opt_get_ip6_unicast_hops}, + {"ipv6-multicast-hops", opt_get_ip6_unicast_hops}, + {"ipv6-multicast-loop", opt_get_ip6_multicast_loop}, + {"ipv6-v6only", opt_get_ip6_v6only}, + {"recv-buffer-size", opt_get_recv_buf_size}, + {"send-buffer-size", opt_get_send_buf_size}, {NULL, NULL} }; @@ -111,7 +113,7 @@ static t_opt optget[] = { static luaL_Reg func[] = { {"udp", global_create}, {"udp4", global_create4}, - // {"udp6", global_create6}, + {"udp6", global_create6}, {NULL, NULL} }; @@ -182,13 +184,37 @@ static int meth_sendto(lua_State *L) { memset(&aihint, 0, sizeof(aihint)); aihint.ai_family = udp->family; aihint.ai_socktype = SOCK_DGRAM; - aihint.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; + aihint.ai_flags = AI_NUMERICHOST; +#ifdef AI_NUMERICSERV + aihint.ai_flags |= AI_NUMERICSERV; +#endif err = getaddrinfo(ip, port, &aihint, &ai); if (err) { lua_pushnil(L); - lua_pushstring(L, gai_strerror(err)); + lua_pushstring(L, LUA_GAI_STRERROR(err)); return 2; } + + /* create socket if on first sendto if AF_UNSPEC was set */ + if (udp->family == AF_UNSPEC && udp->sock == SOCKET_INVALID) { + struct addrinfo *ap; + const char *errstr = NULL; + for (ap = ai; ap != NULL; ap = ap->ai_next) { + errstr = inet_trycreate(&udp->sock, ap->ai_family, SOCK_DGRAM, 0); + if (errstr == NULL) { + socket_setnonblocking(&udp->sock); + udp->family = ap->ai_family; + break; + } + } + if (errstr != NULL) { + lua_pushnil(L); + lua_pushstring(L, errstr); + freeaddrinfo(ai); + return 2; + } + } + timeout_markstart(tm); err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr, (socklen_t) ai->ai_addrlen, tm); @@ -241,7 +267,7 @@ static int meth_receivefrom(lua_State *L) { char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; struct sockaddr_storage addr; socklen_t addr_len = sizeof(addr); - char addrstr[INET_ADDRSTRLEN]; + char addrstr[INET6_ADDRSTRLEN]; char portstr[6]; int err; p_timeout tm = &udp->tm; @@ -261,10 +287,10 @@ static int meth_receivefrom(lua_State *L) { return 2; } err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, - INET_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV); + INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV); if (err) { lua_pushnil(L); - lua_pushstring(L, gai_strerror(err)); + lua_pushstring(L, LUA_GAI_STRERROR(err)); if (wanted > sizeof(buf)) free(dgram); return 2; } @@ -457,6 +483,6 @@ static int global_create4(lua_State *L) { return udp_create(L, AF_INET); } -// static int global_create6(lua_State *L) { -// return udp_create(L, AF_INET6); -// } +static int global_create6(lua_State *L) { + return udp_create(L, AF_INET6); +} diff --git a/libraries/luasocket/libluasocket/udp.h b/libraries/luasocket/libluasocket/udp.h index be9b6a553..07d5247fc 100644 --- a/libraries/luasocket/libluasocket/udp.h +++ b/libraries/luasocket/libluasocket/udp.h @@ -12,7 +12,7 @@ * with a call to the setpeername function. The same function can be used to * break the connection. \*=========================================================================*/ -#include "lua.h" +#include "luasocket.h" #include "timeout.h" #include "socket.h" @@ -26,6 +26,14 @@ typedef struct t_udp_ { } t_udp; typedef t_udp *p_udp; +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + int udp_open(lua_State *L); +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + #endif /* UDP_H */ diff --git a/libraries/luasocket/libluasocket/unix.c b/libraries/luasocket/libluasocket/unix.c new file mode 100644 index 000000000..268d8b212 --- /dev/null +++ b/libraries/luasocket/libluasocket/unix.c @@ -0,0 +1,69 @@ +/*=========================================================================*\ +* Unix domain socket +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" + +#include "unixstream.h" +#include "unixdgram.h" + +/*-------------------------------------------------------------------------*\ +* Modules and functions +\*-------------------------------------------------------------------------*/ +static const luaL_Reg mod[] = { + {"stream", unixstream_open}, + {"dgram", unixdgram_open}, + {NULL, NULL} +}; + +static void add_alias(lua_State *L, int index, const char *name, const char *target) +{ + lua_getfield(L, index, target); + lua_setfield(L, index, name); +} + +static int compat_socket_unix_call(lua_State *L) +{ + /* Look up socket.unix.stream in the socket.unix table (which is the first + * argument). */ + lua_getfield(L, 1, "stream"); + + /* Replace the stack entry for the socket.unix table with the + * socket.unix.stream function. */ + lua_replace(L, 1); + + /* Call socket.unix.stream, passing along any arguments. */ + int n = lua_gettop(L); + lua_call(L, n-1, LUA_MULTRET); + + /* Pass along the return values from socket.unix.stream. */ + n = lua_gettop(L); + return n; +} + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +LUASOCKET_API int luaopen_socket_unix(lua_State *L) +{ + int i; + lua_newtable(L); + int socket_unix_table = lua_gettop(L); + + for (i = 0; mod[i].name; i++) + mod[i].func(L); + + /* Add backwards compatibility aliases "tcp" and "udp" for the "stream" and + * "dgram" functions. */ + add_alias(L, socket_unix_table, "tcp", "stream"); + add_alias(L, socket_unix_table, "udp", "dgram"); + + /* Add a backwards compatibility function and a metatable setup to call it + * for the old socket.unix() interface. */ + lua_pushcfunction(L, compat_socket_unix_call); + lua_setfield(L, socket_unix_table, "__call"); + lua_pushvalue(L, socket_unix_table); + lua_setmetatable(L, socket_unix_table); + + return 1; +} diff --git a/libraries/luasocket/libluasocket/unix.h b/libraries/luasocket/libluasocket/unix.h new file mode 100644 index 000000000..c20356189 --- /dev/null +++ b/libraries/luasocket/libluasocket/unix.h @@ -0,0 +1,26 @@ +#ifndef UNIX_H +#define UNIX_H +/*=========================================================================*\ +* Unix domain object +* LuaSocket toolkit +* +* This module is just an example of how to extend LuaSocket with a new +* domain. +\*=========================================================================*/ +#include "luasocket.h" + +#include "buffer.h" +#include "timeout.h" +#include "socket.h" + +typedef struct t_unix_ { + t_socket sock; + t_io io; + t_buffer buf; + t_timeout tm; +} t_unix; +typedef t_unix *p_unix; + +LUASOCKET_API int luaopen_socket_unix(lua_State *L); + +#endif /* UNIX_H */ diff --git a/libraries/luasocket/libluasocket/unixdgram.c b/libraries/luasocket/libluasocket/unixdgram.c new file mode 100644 index 000000000..69093d734 --- /dev/null +++ b/libraries/luasocket/libluasocket/unixdgram.c @@ -0,0 +1,405 @@ +/*=========================================================================*\ +* Unix domain socket dgram submodule +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unix.h" + +#include +#include + +#include + +#define UNIXDGRAM_DATAGRAMSIZE 8192 + +/* provide a SUN_LEN macro if sys/un.h doesn't (e.g. Android) */ +#ifndef SUN_LEN +#define SUN_LEN(ptr) \ + ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + + strlen ((ptr)->sun_path)) +#endif + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_connect(lua_State *L); +static int meth_bind(lua_State *L); +static int meth_send(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_gettimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); +static int meth_receivefrom(lua_State *L); +static int meth_sendto(lua_State *L); +static int meth_getsockname(lua_State *L); + +static const char *unixdgram_tryconnect(p_unix un, const char *path); +static const char *unixdgram_trybind(p_unix un, const char *path); + +/* unixdgram object methods */ +static luaL_Reg unixdgram_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"bind", meth_bind}, + {"close", meth_close}, + {"connect", meth_connect}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"send", meth_send}, + {"sendto", meth_sendto}, + {"receive", meth_receive}, + {"receivefrom", meth_receivefrom}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_connect}, + {"setsockname", meth_bind}, + {"getsockname", meth_getsockname}, + {"settimeout", meth_settimeout}, + {"gettimeout", meth_gettimeout}, + {NULL, NULL} +}; + +/* socket option handlers */ +static t_opt optset[] = { + {"reuseaddr", opt_set_reuseaddr}, + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"dgram", global_create}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int unixdgram_open(lua_State *L) +{ + /* create classes */ + auxiliar_newclass(L, "unixdgram{connected}", unixdgram_methods); + auxiliar_newclass(L, "unixdgram{unconnected}", unixdgram_methods); + /* create class groups */ + auxiliar_add2group(L, "unixdgram{connected}", "unixdgram{any}"); + auxiliar_add2group(L, "unixdgram{unconnected}", "unixdgram{any}"); + auxiliar_add2group(L, "unixdgram{connected}", "select{able}"); + auxiliar_add2group(L, "unixdgram{unconnected}", "select{able}"); + + luaL_setfuncs(L, func, 0); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +static const char *unixdgram_strerror(int err) +{ + /* a 'closed' error on an unconnected means the target address was not + * accepted by the transport layer */ + if (err == IO_CLOSED) return "refused"; + else return socket_strerror(err); +} + +static int meth_send(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{connected}", 1); + p_timeout tm = &un->tm; + size_t count, sent = 0; + int err; + const char *data = luaL_checklstring(L, 2, &count); + timeout_markstart(tm); + err = socket_send(&un->sock, data, count, &sent, tm); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, unixdgram_strerror(err)); + return 2; + } + lua_pushnumber(L, (lua_Number) sent); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Send data through unconnected unixdgram socket +\*-------------------------------------------------------------------------*/ +static int meth_sendto(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); + size_t count, sent = 0; + const char *data = luaL_checklstring(L, 2, &count); + const char *path = luaL_checkstring(L, 3); + p_timeout tm = &un->tm; + int err; + struct sockaddr_un remote; + size_t len = strlen(path); + + if (len >= sizeof(remote.sun_path)) { + lua_pushnil(L); + lua_pushstring(L, "path too long"); + return 2; + } + + memset(&remote, 0, sizeof(remote)); + strcpy(remote.sun_path, path); + remote.sun_family = AF_UNIX; + timeout_markstart(tm); +#ifdef UNIX_HAS_SUN_LEN + remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) + + len + 1; + err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, remote.sun_len, tm); +#else + err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, + sizeof(remote.sun_family) + len, tm); +#endif + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, unixdgram_strerror(err)); + return 2; + } + lua_pushnumber(L, (lua_Number) sent); + return 1; +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + char buf[UNIXDGRAM_DATAGRAMSIZE]; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + int err; + p_timeout tm = &un->tm; + timeout_markstart(tm); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } + err = socket_recv(&un->sock, dgram, wanted, &got, tm); + /* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */ + if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, unixdgram_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + lua_pushlstring(L, dgram, got); + if (wanted > sizeof(buf)) free(dgram); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Receives data and sender from a DGRAM socket +\*-------------------------------------------------------------------------*/ +static int meth_receivefrom(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); + char buf[UNIXDGRAM_DATAGRAMSIZE]; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + struct sockaddr_un addr; + socklen_t addr_len = sizeof(addr); + int err; + p_timeout tm = &un->tm; + timeout_markstart(tm); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } + addr.sun_path[0] = '\0'; + err = socket_recvfrom(&un->sock, dgram, wanted, &got, (SA *) &addr, + &addr_len, tm); + /* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */ + if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, unixdgram_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + + lua_pushlstring(L, dgram, got); + /* the path may be empty, when client send without bind */ + lua_pushstring(L, addr.sun_path); + if (wanted > sizeof(buf)) free(dgram); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + return opt_meth_setoption(L, optset, &un->sock); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + lua_pushnumber(L, (int) un->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + (void) un; + lua_pushboolean(L, 0); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Binds an object to an address +\*-------------------------------------------------------------------------*/ +static const char *unixdgram_trybind(p_unix un, const char *path) { + struct sockaddr_un local; + size_t len = strlen(path); + if (len >= sizeof(local.sun_path)) return "path too long"; + memset(&local, 0, sizeof(local)); + strcpy(local.sun_path, path); + local.sun_family = AF_UNIX; + size_t addrlen = SUN_LEN(&local); +#ifdef UNIX_HAS_SUN_LEN + local.sun_len = addrlen + 1; +#endif + int err = socket_bind(&un->sock, (SA *) &local, addrlen); + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_bind(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixdgram_trybind(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +static int meth_getsockname(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + struct sockaddr_un peer = {0}; + socklen_t peer_len = sizeof(peer); + + if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + + lua_pushstring(L, peer.sun_path); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master unixdgram object into a client object. +\*-------------------------------------------------------------------------*/ +static const char *unixdgram_tryconnect(p_unix un, const char *path) +{ + struct sockaddr_un remote; + size_t len = strlen(path); + if (len >= sizeof(remote.sun_path)) return "path too long"; + memset(&remote, 0, sizeof(remote)); + strcpy(remote.sun_path, path); + remote.sun_family = AF_UNIX; + timeout_markstart(&un->tm); + size_t addrlen = SUN_LEN(&remote); +#ifdef UNIX_HAS_SUN_LEN + remote.sun_len = addrlen + 1; +#endif + int err = socket_connect(&un->sock, (SA *) &remote, addrlen, &un->tm); + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_connect(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixdgram_tryconnect(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + /* turn unconnected object into a connected object */ + auxiliar_setclass(L, "unixdgram{connected}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + socket_destroy(&un->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +static int meth_gettimeout(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + return timeout_meth_gettimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master unixdgram object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) +{ + t_socket sock; + int err = socket_create(&sock, AF_UNIX, SOCK_DGRAM, 0); + /* try to allocate a system socket */ + if (err == IO_DONE) { + /* allocate unixdgram object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + /* set its type as master object */ + auxiliar_setclass(L, "unixdgram{unconnected}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} diff --git a/libraries/luasocket/libluasocket/unixdgram.h b/libraries/luasocket/libluasocket/unixdgram.h new file mode 100644 index 000000000..a1a0166bd --- /dev/null +++ b/libraries/luasocket/libluasocket/unixdgram.h @@ -0,0 +1,28 @@ +#ifndef UNIXDGRAM_H +#define UNIXDGRAM_H +/*=========================================================================*\ +* DGRAM object +* LuaSocket toolkit +* +* The dgram.h module provides LuaSocket with support for DGRAM protocol +* (AF_INET, SOCK_DGRAM). +* +* Two classes are defined: connected and unconnected. DGRAM objects are +* originally unconnected. They can be "connected" to a given address +* with a call to the setpeername function. The same function can be used to +* break the connection. +\*=========================================================================*/ + +#include "unix.h" + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int unixdgram_open(lua_State *L); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif /* UNIXDGRAM_H */ diff --git a/libraries/luasocket/libluasocket/unixstream.c b/libraries/luasocket/libluasocket/unixstream.c new file mode 100644 index 000000000..02aced9c8 --- /dev/null +++ b/libraries/luasocket/libluasocket/unixstream.c @@ -0,0 +1,355 @@ +/*=========================================================================*\ +* Unix domain socket stream sub module +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unixstream.h" + +#include +#include + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_connect(lua_State *L); +static int meth_listen(lua_State *L); +static int meth_bind(lua_State *L); +static int meth_send(lua_State *L); +static int meth_shutdown(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_accept(lua_State *L); +static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); +static int meth_getstats(lua_State *L); +static int meth_setstats(lua_State *L); +static int meth_getsockname(lua_State *L); + +static const char *unixstream_tryconnect(p_unix un, const char *path); +static const char *unixstream_trybind(p_unix un, const char *path); + +/* unixstream object methods */ +static luaL_Reg unixstream_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"accept", meth_accept}, + {"bind", meth_bind}, + {"close", meth_close}, + {"connect", meth_connect}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"listen", meth_listen}, + {"receive", meth_receive}, + {"send", meth_send}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_connect}, + {"setsockname", meth_bind}, + {"getsockname", meth_getsockname}, + {"settimeout", meth_settimeout}, + {"shutdown", meth_shutdown}, + {NULL, NULL} +}; + +/* socket option handlers */ +static t_opt optset[] = { + {"keepalive", opt_set_keepalive}, + {"reuseaddr", opt_set_reuseaddr}, + {"linger", opt_set_linger}, + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"stream", global_create}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int unixstream_open(lua_State *L) +{ + /* create classes */ + auxiliar_newclass(L, "unixstream{master}", unixstream_methods); + auxiliar_newclass(L, "unixstream{client}", unixstream_methods); + auxiliar_newclass(L, "unixstream{server}", unixstream_methods); + + /* create class groups */ + auxiliar_add2group(L, "unixstream{master}", "unixstream{any}"); + auxiliar_add2group(L, "unixstream{client}", "unixstream{any}"); + auxiliar_add2group(L, "unixstream{server}", "unixstream{any}"); + + luaL_setfuncs(L, func, 0); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Just call buffered IO methods +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + return buffer_meth_send(L, &un->buf); +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + return buffer_meth_receive(L, &un->buf); +} + +static int meth_getstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + return buffer_meth_getstats(L, &un->buf); +} + +static int meth_setstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + return buffer_meth_setstats(L, &un->buf); +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + return opt_meth_setoption(L, optset, &un->sock); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + lua_pushnumber(L, (int) un->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + lua_pushboolean(L, !buffer_isempty(&un->buf)); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Waits for and returns a client object attempting connection to the +* server object +\*-------------------------------------------------------------------------*/ +static int meth_accept(lua_State *L) { + p_unix server = (p_unix) auxiliar_checkclass(L, "unixstream{server}", 1); + p_timeout tm = timeout_markstart(&server->tm); + t_socket sock; + int err = socket_accept(&server->sock, &sock, NULL, NULL, tm); + /* if successful, push client socket */ + if (err == IO_DONE) { + p_unix clnt = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + auxiliar_setclass(L, "unixstream{client}", -1); + /* initialize structure fields */ + socket_setnonblocking(&sock); + clnt->sock = sock; + io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv, + (p_error) socket_ioerror, &clnt->sock); + timeout_init(&clnt->tm, -1, -1); + buffer_init(&clnt->buf, &clnt->io, &clnt->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} + +/*-------------------------------------------------------------------------*\ +* Binds an object to an address +\*-------------------------------------------------------------------------*/ +static const char *unixstream_trybind(p_unix un, const char *path) { + struct sockaddr_un local; + size_t len = strlen(path); + int err; + if (len >= sizeof(local.sun_path)) return "path too long"; + memset(&local, 0, sizeof(local)); + strcpy(local.sun_path, path); + local.sun_family = AF_UNIX; +#ifdef UNIX_HAS_SUN_LEN + local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) + + len + 1; + err = socket_bind(&un->sock, (SA *) &local, local.sun_len); + +#else + err = socket_bind(&un->sock, (SA *) &local, + sizeof(local.sun_family) + len); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_bind(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixstream_trybind(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +static int meth_getsockname(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + struct sockaddr_un peer = {0}; + socklen_t peer_len = sizeof(peer); + + if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + + lua_pushstring(L, peer.sun_path); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master unixstream object into a client object. +\*-------------------------------------------------------------------------*/ +static const char *unixstream_tryconnect(p_unix un, const char *path) +{ + struct sockaddr_un remote; + int err; + size_t len = strlen(path); + if (len >= sizeof(remote.sun_path)) return "path too long"; + memset(&remote, 0, sizeof(remote)); + strcpy(remote.sun_path, path); + remote.sun_family = AF_UNIX; + timeout_markstart(&un->tm); +#ifdef UNIX_HAS_SUN_LEN + remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) + + len + 1; + err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm); +#else + err = socket_connect(&un->sock, (SA *) &remote, + sizeof(remote.sun_family) + len, &un->tm); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_connect(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixstream_tryconnect(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + /* turn master object into a client object */ + auxiliar_setclass(L, "unixstream{client}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + socket_destroy(&un->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Puts the sockt in listen mode +\*-------------------------------------------------------------------------*/ +static int meth_listen(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); + int backlog = (int) luaL_optnumber(L, 2, 32); + int err = socket_listen(&un->sock, backlog); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } + /* turn master object into a server object */ + auxiliar_setclass(L, "unixstream{server}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Shuts the connection down partially +\*-------------------------------------------------------------------------*/ +static int meth_shutdown(lua_State *L) +{ + /* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */ + static const char* methods[] = { "receive", "send", "both", NULL }; + p_unix stream = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + int how = luaL_checkoption(L, 2, "both", methods); + socket_shutdown(&stream->sock, how); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master unixstream object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) { + t_socket sock; + int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0); + /* try to allocate a system socket */ + if (err == IO_DONE) { + /* allocate unixstream object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + /* set its type as master object */ + auxiliar_setclass(L, "unixstream{master}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} diff --git a/libraries/luasocket/libluasocket/unixstream.h b/libraries/luasocket/libluasocket/unixstream.h new file mode 100644 index 000000000..7916affa7 --- /dev/null +++ b/libraries/luasocket/libluasocket/unixstream.h @@ -0,0 +1,29 @@ +#ifndef UNIXSTREAM_H +#define UNIXSTREAM_H +/*=========================================================================*\ +* UNIX STREAM object +* LuaSocket toolkit +* +* The unixstream.h module is basicly a glue that puts together modules buffer.h, +* timeout.h socket.h and inet.h to provide the LuaSocket UNIX STREAM (AF_UNIX, +* SOCK_STREAM) support. +* +* Three classes are defined: master, client and server. The master class is +* a newly created unixstream object, that has not been bound or connected. Server +* objects are unixstream objects bound to some local address. Client objects are +* unixstream objects either connected to some address or returned by the accept +* method of a server object. +\*=========================================================================*/ +#include "unix.h" + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int unixstream_open(lua_State *L); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif /* UNIXSTREAM_H */ diff --git a/libraries/luasocket/libluasocket/url.lua b/libraries/luasocket/libluasocket/url.lua index 3f1359c80..3bf1c17e4 100644 --- a/libraries/luasocket/libluasocket/url.lua +++ b/libraries/luasocket/libluasocket/url.lua @@ -1,3 +1,4 @@ +R"luastring"--( ----------------------------------------------------------------------------- -- URI parsing, composition and relative URL resolution -- LuaSocket toolkit. @@ -59,7 +60,7 @@ local segment_set = make_set { local function protect_segment(s) return string.gsub(s, "([^A-Za-z0-9_])", function (c) if segment_set[c] then return c - else return string.format("%%%02x", string.byte(c)) end + else return string.format("%%%02X", string.byte(c)) end end) end @@ -76,6 +77,34 @@ function _M.unescape(s) end)) end +----------------------------------------------------------------------------- +-- Removes '..' and '.' components appropriately from a path. +-- Input +-- path +-- Returns +-- dot-normalized path +local function remove_dot_components(path) + local marker = string.char(1) + repeat + local was = path + path = path:gsub('//', '/'..marker..'/', 1) + until path == was + repeat + local was = path + path = path:gsub('/%./', '/', 1) + until path == was + repeat + local was = path + path = path:gsub('[^/]+/%.%./([^/]+)', '%1', 1) + until path == was + path = path:gsub('[^/]+/%.%./*$', '') + path = path:gsub('/%.%.$', '/') + path = path:gsub('/%.$', '/') + path = path:gsub('^/%.%./', '/') + path = path:gsub(marker, '') + return path +end + ----------------------------------------------------------------------------- -- Builds a path from a base path and a relative path -- Input @@ -85,23 +114,12 @@ end -- corresponding absolute path ----------------------------------------------------------------------------- local function absolute_path(base_path, relative_path) - if string.sub(relative_path, 1, 1) == "/" then return relative_path end - local path = string.gsub(base_path, "[^/]*$", "") - path = path .. relative_path - path = string.gsub(path, "([^/]*%./)", function (s) - if s ~= "./" then return s else return "" end - end) - path = string.gsub(path, "/%.$", "/") - local reduced - while reduced ~= path do - reduced = path - path = string.gsub(reduced, "([^/]*/%.%./)", function (s) - if s ~= "../../" then return "" else return s end - end) - end - path = string.gsub(reduced, "([^/]*/%.%.)$", function (s) - if s ~= "../.." then return "" else return s end - end) + if string.sub(relative_path, 1, 1) == "/" then + return remove_dot_components(relative_path) end + base_path = base_path:gsub("[^/]*$", "") + if not base_path:find'/$' then base_path = base_path .. '/' end + local path = base_path .. relative_path + path = remove_dot_components(path) return path end @@ -131,11 +149,6 @@ function _M.parse(url, default) if not url or url == "" then return nil, "invalid url" end -- remove whitespace -- url = string.gsub(url, "%s", "") - -- get fragment - url = string.gsub(url, "#(.*)$", function(f) - parsed.fragment = f - return "" - end) -- get scheme url = string.gsub(url, "^([%w][%w%+%-%.]*)%:", function(s) parsed.scheme = s; return "" end) @@ -144,6 +157,11 @@ function _M.parse(url, default) parsed.authority = n return "" end) + -- get fragment + url = string.gsub(url, "#(.*)$", function(f) + parsed.fragment = f + return "" + end) -- get query string url = string.gsub(url, "%?(.*)", function(q) parsed.query = q @@ -162,9 +180,9 @@ function _M.parse(url, default) function(u) parsed.userinfo = u; return "" end) authority = string.gsub(authority, ":([^:%]]*)$", function(p) parsed.port = p; return "" end) - if authority ~= "" then + if authority ~= "" then -- IPv6? - parsed.host = string.match(authority, "^%[(.+)%]$") or authority + parsed.host = string.match(authority, "^%[(.+)%]$") or authority end local userinfo = parsed.userinfo if not userinfo then return parsed end @@ -183,8 +201,9 @@ end -- a stringing with the corresponding URL ----------------------------------------------------------------------------- function _M.build(parsed) - local ppath = _M.parse_path(parsed.path or "") - local url = _M.build_path(ppath) + --local ppath = _M.parse_path(parsed.path or "") + --local url = _M.build_path(ppath) + local url = parsed.path or "" if parsed.params then url = url .. ";" .. parsed.params end if parsed.query then url = url .. "?" .. parsed.query end local authority = parsed.authority @@ -193,7 +212,7 @@ function _M.build(parsed) if string.find(authority, ":") then -- IPv6? authority = "[" .. authority .. "]" end - if parsed.port then authority = authority .. ":" .. parsed.port end + if parsed.port then authority = authority .. ":" .. base.tostring(parsed.port) end local userinfo = parsed.userinfo if parsed.user then userinfo = parsed.user @@ -226,10 +245,14 @@ function _M.absolute(base_url, relative_url) else base_parsed = _M.parse(base_url) end + local result local relative_parsed = _M.parse(relative_url) - if not base_parsed then return relative_url - elseif not relative_parsed then return base_url - elseif relative_parsed.scheme then return relative_url + if not base_parsed then + result = relative_url + elseif not relative_parsed then + result = base_url + elseif relative_parsed.scheme then + result = relative_url else relative_parsed.scheme = base_parsed.scheme if not relative_parsed.authority then @@ -242,13 +265,14 @@ function _M.absolute(base_url, relative_url) relative_parsed.query = base_parsed.query end end - else + else relative_parsed.path = absolute_path(base_parsed.path or "", relative_parsed.path) end end - return _M.build(relative_parsed) + result = _M.build(relative_parsed) end + return remove_dot_components(result) end ----------------------------------------------------------------------------- @@ -304,5 +328,6 @@ function _M.build_path(parsed, unsafe) if parsed.is_absolute then path = "/" .. path end return path end - return _M +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/url.lua.h b/libraries/luasocket/libluasocket/url.lua.h deleted file mode 100644 index 251472148..000000000 --- a/libraries/luasocket/libluasocket/url.lua.h +++ /dev/null @@ -1,567 +0,0 @@ -/* code automatically generated by bin2c -- DO NOT EDIT */ -{ -/* #include'ing this file in a C program is equivalent to calling - if (luaL_loadfile(L,"url.lua")==0) lua_call(L, 0, LUA_MULTRET); -*/ -/* url.lua */ -static const unsigned char B1[]={ - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, - 32, 85, 82, 73, 32,112, 97,114,115,105,110,103, 44, 32, 99,111,109,112,111,115, -105,116,105,111,110, 32, 97,110,100, 32,114,101,108, 97,116,105,118,101, 32, 85, - 82, 76, 32,114,101,115,111,108,117,116,105,111,110, 10, 45, 45, 32, 76,117, 97, - 83,111, 99,107,101,116, 32,116,111,111,108,107,105,116, 46, 10, 45, 45, 32, 65, -117,116,104,111,114, 58, 32, 68,105,101,103,111, 32, 78,101,104, 97, 98, 10, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 10, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 68, -101, 99,108, 97,114,101, 32,109,111,100,117,108,101, 10, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10,108,111, 99, 97,108, 32,115,116, -114,105,110,103, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,115,116,114,105, -110,103, 34, 41, 10,108,111, 99, 97,108, 32, 98, 97,115,101, 32, 61, 32, 95, 71, - 10,108,111, 99, 97,108, 32,116, 97, 98,108,101, 32, 61, 32,114,101,113,117,105, -114,101, 40, 34,116, 97, 98,108,101, 34, 41, 10,108,111, 99, 97,108, 32,115,111, - 99,107,101,116, 32, 61, 32,114,101,113,117,105,114,101, 40, 34,115,111, 99,107, -101,116, 34, 41, 10, 10,115,111, 99,107,101,116, 46,117,114,108, 32, 61, 32,123, -125, 10,108,111, 99, 97,108, 32, 95, 77, 32, 61, 32,115,111, 99,107,101,116, 46, -117,114,108, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 10, 45, 45, 32, 77,111,100,117,108,101, 32,118,101,114,115,105,111,110, - 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 95, - 77, 46, 95, 86, 69, 82, 83, 73, 79, 78, 32, 61, 32, 34, 85, 82, 76, 32, 49, 46, - 48, 46, 51, 34, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 10, 45, 45, 32, 69,110, 99,111,100,101,115, 32, 97, 32,115,116,114, -105,110,103, 32,105,110,116,111, 32,105,116,115, 32,101,115, 99, 97,112,101,100, - 32,104,101,120, 97,100,101, 99,105,109, 97,108, 32,114,101,112,114,101,115,101, -110,116, 97,116,105,111,110, 10, 45, 45, 32, 73,110,112,117,116, 10, 45, 45, 32, - 32, 32,115, 58, 32, 98,105,110, 97,114,121, 32,115,116,114,105,110,103, 32,116, -111, 32, 98,101, 32,101,110, 99,111,100,101,100, 10, 45, 45, 32, 82,101,116,117, -114,110,115, 10, 45, 45, 32, 32, 32,101,115, 99, 97,112,101,100, 32,114,101,112, -114,101,115,101,110,116, 97,116,105,111,110, 32,111,102, 32,115,116,114,105,110, -103, 32, 98,105,110, 97,114,121, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 10,102,117,110, 99,116,105,111,110, 32, 95, 77, 46,101, -115, 99, 97,112,101, 40,115, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, - 40,115,116,114,105,110,103, 46,103,115,117, 98, 40,115, 44, 32, 34, 40, 91, 94, - 65, 45, 90, 97, 45,122, 48, 45, 57, 95, 93, 41, 34, 44, 32,102,117,110, 99,116, -105,111,110, 40, 99, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114, -110, 32,115,116,114,105,110,103, 46,102,111,114,109, 97,116, 40, 34, 37, 37, 37, - 48, 50,120, 34, 44, 32,115,116,114,105,110,103, 46, 98,121,116,101, 40, 99, 41, - 41, 10, 32, 32, 32, 32,101,110,100, 41, 41, 10,101,110,100, 10, 10, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 80,114, -111,116,101, 99,116,115, 32, 97, 32,112, 97,116,104, 32,115,101,103,109,101,110, -116, 44, 32,116,111, 32,112,114,101,118,101,110,116, 32,105,116, 32,102,114,111, -109, 32,105,110,116,101,114,102,101,114,105,110,103, 32,119,105,116,104, 32,116, -104,101, 10, 45, 45, 32,117,114,108, 32,112, 97,114,115,105,110,103, 46, 10, 45, - 45, 32, 73,110,112,117,116, 10, 45, 45, 32, 32, 32,115, 58, 32, 98,105,110, 97, -114,121, 32,115,116,114,105,110,103, 32,116,111, 32, 98,101, 32,101,110, 99,111, -100,101,100, 10, 45, 45, 32, 82,101,116,117,114,110,115, 10, 45, 45, 32, 32, 32, -101,115, 99, 97,112,101,100, 32,114,101,112,114,101,115,101,110,116, 97,116,105, -111,110, 32,111,102, 32,115,116,114,105,110,103, 32, 98,105,110, 97,114,121, 10, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10,108,111, - 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32,109, 97,107,101, 95,115,101, -116, 40,116, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,115, 32, 61, 32,123, -125, 10, 32, 32, 32, 32,102,111,114, 32,105, 44,118, 32,105,110, 32, 98, 97,115, -101, 46,105,112, 97,105,114,115, 40,116, 41, 32,100,111, 10, 32, 32, 32, 32, 32, - 32, 32, 32,115, 91,116, 91,105, 93, 93, 32, 61, 32, 49, 10, 32, 32, 32, 32,101, -110,100, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,115, 10,101,110,100, 10, - 10, 45, 45, 32,116,104,101,115,101, 32, 97,114,101, 32, 97,108,108,111,119,101, -100, 32,119,105,116,104,105,110,103, 32, 97, 32,112, 97,116,104, 32,115,101,103, -109,101,110,116, 44, 32, 97,108,111,110,103, 32,119,105,116,104, 32, 97,108,112, -104, 97,110,117,109, 10, 45, 45, 32,111,116,104,101,114, 32, 99,104, 97,114, 97, - 99,116,101,114,115, 32,109,117,115,116, 32, 98,101, 32,101,115, 99, 97,112,101, -100, 10,108,111, 99, 97,108, 32,115,101,103,109,101,110,116, 95,115,101,116, 32, - 61, 32,109, 97,107,101, 95,115,101,116, 32,123, 10, 32, 32, 32, 32, 34, 45, 34, - 44, 32, 34, 95, 34, 44, 32, 34, 46, 34, 44, 32, 34, 33, 34, 44, 32, 34,126, 34, - 44, 32, 34, 42, 34, 44, 32, 34, 39, 34, 44, 32, 34, 40, 34, 44, 10, 32, 32, 32, - 32, 34, 41, 34, 44, 32, 34, 58, 34, 44, 32, 34, 64, 34, 44, 32, 34, 38, 34, 44, - 32, 34, 61, 34, 44, 32, 34, 43, 34, 44, 32, 34, 36, 34, 44, 32, 34, 44, 34, 44, - 10,125, 10, 10,108,111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32,112, -114,111,116,101, 99,116, 95,115,101,103,109,101,110,116, 40,115, 41, 10, 32, 32, - 32, 32,114,101,116,117,114,110, 32,115,116,114,105,110,103, 46,103,115,117, 98, - 40,115, 44, 32, 34, 40, 91, 94, 65, 45, 90, 97, 45,122, 48, 45, 57, 95, 93, 41, - 34, 44, 32,102,117,110, 99,116,105,111,110, 32, 40, 99, 41, 10, 32, 32, 32, 32, - 32, 32, 32, 32,105,102, 32,115,101,103,109,101,110,116, 95,115,101,116, 91, 99, - 93, 32,116,104,101,110, 32,114,101,116,117,114,110, 32, 99, 10, 32, 32, 32, 32, - 32, 32, 32, 32,101,108,115,101, 32,114,101,116,117,114,110, 32,115,116,114,105, -110,103, 46,102,111,114,109, 97,116, 40, 34, 37, 37, 37, 48, 50,120, 34, 44, 32, -115,116,114,105,110,103, 46, 98,121,116,101, 40, 99, 41, 41, 32,101,110,100, 10, - 32, 32, 32, 32,101,110,100, 41, 10,101,110,100, 10, 10, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 85,110,101,110, 99, -111,100,101,115, 32, 97, 32,101,115, 99, 97,112,101,100, 32,104,101,120, 97,100, -101, 99,105,109, 97,108, 32,115,116,114,105,110,103, 32,105,110,116,111, 32,105, -116,115, 32, 98,105,110, 97,114,121, 32,114,101,112,114,101,115,101,110,116, 97, -116,105,111,110, 10, 45, 45, 32, 73,110,112,117,116, 10, 45, 45, 32, 32, 32,115, - 58, 32,101,115, 99, 97,112,101,100, 32,104,101,120, 97,100,101, 99,105,109, 97, -108, 32,115,116,114,105,110,103, 32,116,111, 32, 98,101, 32,117,110,101,110, 99, -111,100,101,100, 10, 45, 45, 32, 82,101,116,117,114,110,115, 10, 45, 45, 32, 32, - 32,117,110,101,115, 99, 97,112,101,100, 32, 98,105,110, 97,114,121, 32,114,101, -112,114,101,115,101,110,116, 97,116,105,111,110, 32,111,102, 32,101,115, 99, 97, -112,101,100, 32,104,101,120, 97,100,101, 99,105,109, 97,108, 32, 32, 98,105,110, - 97,114,121, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 10,102,117,110, 99,116,105,111,110, 32, 95, 77, 46,117,110,101,115, 99, 97, -112,101, 40,115, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32, 40,115,116, -114,105,110,103, 46,103,115,117, 98, 40,115, 44, 32, 34, 37, 37, 40, 37,120, 37, -120, 41, 34, 44, 32,102,117,110, 99,116,105,111,110, 40,104,101,120, 41, 10, 32, - 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32,115,116,114,105,110,103, - 46, 99,104, 97,114, 40, 98, 97,115,101, 46,116,111,110,117,109, 98,101,114, 40, -104,101,120, 44, 32, 49, 54, 41, 41, 10, 32, 32, 32, 32,101,110,100, 41, 41, 10, -101,110,100, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 10, 45, 45, 32, 66,117,105,108,100,115, 32, 97, 32,112, 97,116,104, 32, -102,114,111,109, 32, 97, 32, 98, 97,115,101, 32,112, 97,116,104, 32, 97,110,100, - 32, 97, 32,114,101,108, 97,116,105,118,101, 32,112, 97,116,104, 10, 45, 45, 32, - 73,110,112,117,116, 10, 45, 45, 32, 32, 32, 98, 97,115,101, 95,112, 97,116,104, - 10, 45, 45, 32, 32, 32,114,101,108, 97,116,105,118,101, 95,112, 97,116,104, 10, - 45, 45, 32, 82,101,116,117,114,110,115, 10, 45, 45, 32, 32, 32, 99,111,114,114, -101,115,112,111,110,100,105,110,103, 32, 97, 98,115,111,108,117,116,101, 32,112, - 97,116,104, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 10,108,111, 99, 97,108, 32,102,117,110, 99,116,105,111,110, 32, 97, 98,115, -111,108,117,116,101, 95,112, 97,116,104, 40, 98, 97,115,101, 95,112, 97,116,104, - 44, 32,114,101,108, 97,116,105,118,101, 95,112, 97,116,104, 41, 10, 32, 32, 32, - 32,105,102, 32,115,116,114,105,110,103, 46,115,117, 98, 40,114,101,108, 97,116, -105,118,101, 95,112, 97,116,104, 44, 32, 49, 44, 32, 49, 41, 32, 61, 61, 32, 34, - 47, 34, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,114,101,108, 97,116, -105,118,101, 95,112, 97,116,104, 32,101,110,100, 10, 32, 32, 32, 32,108,111, 99, - 97,108, 32,112, 97,116,104, 32, 61, 32,115,116,114,105,110,103, 46,103,115,117, - 98, 40, 98, 97,115,101, 95,112, 97,116,104, 44, 32, 34, 91, 94, 47, 93, 42, 36, - 34, 44, 32, 34, 34, 41, 10, 32, 32, 32, 32,112, 97,116,104, 32, 61, 32,112, 97, -116,104, 32, 46, 46, 32,114,101,108, 97,116,105,118,101, 95,112, 97,116,104, 10, - 32, 32, 32, 32,112, 97,116,104, 32, 61, 32,115,116,114,105,110,103, 46,103,115, -117, 98, 40,112, 97,116,104, 44, 32, 34, 40, 91, 94, 47, 93, 42, 37, 46, 47, 41, - 34, 44, 32,102,117,110, 99,116,105,111,110, 32, 40,115, 41, 10, 32, 32, 32, 32, - 32, 32, 32, 32,105,102, 32,115, 32,126, 61, 32, 34, 46, 47, 34, 32,116,104,101, -110, 32,114,101,116,117,114,110, 32,115, 32,101,108,115,101, 32,114,101,116,117, -114,110, 32, 34, 34, 32,101,110,100, 10, 32, 32, 32, 32,101,110,100, 41, 10, 32, - 32, 32, 32,112, 97,116,104, 32, 61, 32,115,116,114,105,110,103, 46,103,115,117, - 98, 40,112, 97,116,104, 44, 32, 34, 47, 37, 46, 36, 34, 44, 32, 34, 47, 34, 41, - 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,114,101,100,117, 99,101,100, 10, 32, - 32, 32, 32,119,104,105,108,101, 32,114,101,100,117, 99,101,100, 32,126, 61, 32, -112, 97,116,104, 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,100,117, - 99,101,100, 32, 61, 32,112, 97,116,104, 10, 32, 32, 32, 32, 32, 32, 32, 32,112, - 97,116,104, 32, 61, 32,115,116,114,105,110,103, 46,103,115,117, 98, 40,114,101, -100,117, 99,101,100, 44, 32, 34, 40, 91, 94, 47, 93, 42, 47, 37, 46, 37, 46, 47, - 41, 34, 44, 32,102,117,110, 99,116,105,111,110, 32, 40,115, 41, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,115, 32,126, 61, 32, 34, 46, 46, - 47, 46, 46, 47, 34, 32,116,104,101,110, 32,114,101,116,117,114,110, 32, 34, 34, - 32,101,108,115,101, 32,114,101,116,117,114,110, 32,115, 32,101,110,100, 10, 32, - 32, 32, 32, 32, 32, 32, 32,101,110,100, 41, 10, 32, 32, 32, 32,101,110,100, 10, - 32, 32, 32, 32,112, 97,116,104, 32, 61, 32,115,116,114,105,110,103, 46,103,115, -117, 98, 40,114,101,100,117, 99,101,100, 44, 32, 34, 40, 91, 94, 47, 93, 42, 47, - 37, 46, 37, 46, 41, 36, 34, 44, 32,102,117,110, 99,116,105,111,110, 32, 40,115, - 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,115, 32,126, 61, 32, 34, 46, - 46, 47, 46, 46, 34, 32,116,104,101,110, 32,114,101,116,117,114,110, 32, 34, 34, - 32,101,108,115,101, 32,114,101,116,117,114,110, 32,115, 32,101,110,100, 10, 32, - 32, 32, 32,101,110,100, 41, 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,112, - 97,116,104, 10,101,110,100, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 80, 97,114,115,101,115, 32, 97, 32,117, -114,108, 32, 97,110,100, 32,114,101,116,117,114,110,115, 32, 97, 32,116, 97, 98, -108,101, 32,119,105,116,104, 32, 97,108,108, 32,105,116,115, 32,112, 97,114,116, -115, 32, 97, 99, 99,111,114,100,105,110,103, 32,116,111, 32, 82, 70, 67, 32, 50, - 51, 57, 54, 10, 45, 45, 32, 84,104,101, 32,102,111,108,108,111,119,105,110,103, - 32,103,114, 97,109,109, 97,114, 32,100,101,115, 99,114,105, 98,101,115, 32,116, -104,101, 32,110, 97,109,101,115, 32,103,105,118,101,110, 32,116,111, 32,116,104, -101, 32, 85, 82, 76, 32,112, 97,114,116,115, 10, 45, 45, 32, 60,117,114,108, 62, - 32, 58, 58, 61, 32, 60,115, 99,104,101,109,101, 62, 58, 47, 47, 60, 97,117,116, -104,111,114,105,116,121, 62, 47, 60,112, 97,116,104, 62, 59, 60,112, 97,114, 97, -109,115, 62, 63, 60,113,117,101,114,121, 62, 35, 60,102,114, 97,103,109,101,110, -116, 62, 10, 45, 45, 32, 60, 97,117,116,104,111,114,105,116,121, 62, 32, 58, 58, - 61, 32, 60,117,115,101,114,105,110,102,111, 62, 64, 60,104,111,115,116, 62, 58, - 60,112,111,114,116, 62, 10, 45, 45, 32, 60,117,115,101,114,105,110,102,111, 62, - 32, 58, 58, 61, 32, 60,117,115,101,114, 62, 91, 58, 60,112, 97,115,115,119,111, -114,100, 62, 93, 10, 45, 45, 32, 60,112, 97,116,104, 62, 32, 58, 58, 32, 61, 32, -123, 60,115,101,103,109,101,110,116, 62, 47,125, 60,115,101,103,109,101,110,116, - 62, 10, 45, 45, 32, 73,110,112,117,116, 10, 45, 45, 32, 32, 32,117,114,108, 58, - 32,117,110,105,102,111,114,109, 32,114,101,115,111,117,114, 99,101, 32,108,111, - 99, 97,116,111,114, 32,111,102, 32,114,101,113,117,101,115,116, 10, 45, 45, 32, - 32, 32,100,101,102, 97,117,108,116, 58, 32,116, 97, 98,108,101, 32,119,105,116, -104, 32,100,101,102, 97,117,108,116, 32,118, 97,108,117,101,115, 32,102,111,114, - 32,101, 97, 99,104, 32,102,105,101,108,100, 10, 45, 45, 32, 82,101,116,117,114, -110,115, 10, 45, 45, 32, 32, 32,116, 97, 98,108,101, 32,119,105,116,104, 32,116, -104,101, 32,102,111,108,108,111,119,105,110,103, 32,102,105,101,108,100,115, 44, - 32,119,104,101,114,101, 32, 82, 70, 67, 32,110, 97,109,105,110,103, 32, 99,111, -110,118,101,110,116,105,111,110,115, 32,104, 97,118,101, 10, 45, 45, 32, 32, 32, - 98,101,101,110, 32,112,114,101,115,101,114,118,101,100, 58, 10, 45, 45, 32, 32, - 32, 32, 32,115, 99,104,101,109,101, 44, 32, 97,117,116,104,111,114,105,116,121, - 44, 32,117,115,101,114,105,110,102,111, 44, 32,117,115,101,114, 44, 32,112, 97, -115,115,119,111,114,100, 44, 32,104,111,115,116, 44, 32,112,111,114,116, 44, 10, - 45, 45, 32, 32, 32, 32, 32,112, 97,116,104, 44, 32,112, 97,114, 97,109,115, 44, - 32,113,117,101,114,121, 44, 32,102,114, 97,103,109,101,110,116, 10, 45, 45, 32, - 79, 98,115, 58, 10, 45, 45, 32, 32, 32,116,104,101, 32,108,101, 97,100,105,110, -103, 32, 39, 47, 39, 32,105,110, 32,123, 47, 60,112, 97,116,104, 62,125, 32,105, -115, 32, 99,111,110,115,105,100,101,114,101,100, 32,112, 97,114,116, 32,111,102, - 32, 60,112, 97,116,104, 62, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 10,102,117,110, 99,116,105,111,110, 32, 95, 77, 46,112, 97, -114,115,101, 40,117,114,108, 44, 32,100,101,102, 97,117,108,116, 41, 10, 32, 32, - 32, 32, 45, 45, 32,105,110,105,116,105, 97,108,105,122,101, 32,100,101,102, 97, -117,108,116, 32,112, 97,114, 97,109,101,116,101,114,115, 10, 32, 32, 32, 32,108, -111, 99, 97,108, 32,112, 97,114,115,101,100, 32, 61, 32,123,125, 10, 32, 32, 32, - 32,102,111,114, 32,105, 44,118, 32,105,110, 32, 98, 97,115,101, 46,112, 97,105, -114,115, 40,100,101,102, 97,117,108,116, 32,111,114, 32,112, 97,114,115,101,100, - 41, 32,100,111, 32,112, 97,114,115,101,100, 91,105, 93, 32, 61, 32,118, 32,101, -110,100, 10, 32, 32, 32, 32, 45, 45, 32,101,109,112,116,121, 32,117,114,108, 32, -105,115, 32,112, 97,114,115,101,100, 32,116,111, 32,110,105,108, 10, 32, 32, 32, - 32,105,102, 32,110,111,116, 32,117,114,108, 32,111,114, 32,117,114,108, 32, 61, - 61, 32, 34, 34, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,110,105,108, - 44, 32, 34,105,110,118, 97,108,105,100, 32,117,114,108, 34, 32,101,110,100, 10, - 32, 32, 32, 32, 45, 45, 32,114,101,109,111,118,101, 32,119,104,105,116,101,115, -112, 97, 99,101, 10, 32, 32, 32, 32, 45, 45, 32,117,114,108, 32, 61, 32,115,116, -114,105,110,103, 46,103,115,117, 98, 40,117,114,108, 44, 32, 34, 37,115, 34, 44, - 32, 34, 34, 41, 10, 32, 32, 32, 32, 45, 45, 32,103,101,116, 32,102,114, 97,103, -109,101,110,116, 10, 32, 32, 32, 32,117,114,108, 32, 61, 32,115,116,114,105,110, -103, 46,103,115,117, 98, 40,117,114,108, 44, 32, 34, 35, 40, 46, 42, 41, 36, 34, - 44, 32,102,117,110, 99,116,105,111,110, 40,102, 41, 10, 32, 32, 32, 32, 32, 32, - 32, 32,112, 97,114,115,101,100, 46,102,114, 97,103,109,101,110,116, 32, 61, 32, -102, 10, 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32, 34, 34, 10, - 32, 32, 32, 32,101,110,100, 41, 10, 32, 32, 32, 32, 45, 45, 32,103,101,116, 32, -115, 99,104,101,109,101, 10, 32, 32, 32, 32,117,114,108, 32, 61, 32,115,116,114, -105,110,103, 46,103,115,117, 98, 40,117,114,108, 44, 32, 34, 94, 40, 91, 37,119, - 93, 91, 37,119, 37, 43, 37, 45, 37, 46, 93, 42, 41, 37, 58, 34, 44, 10, 32, 32, - 32, 32, 32, 32, 32, 32,102,117,110, 99,116,105,111,110, 40,115, 41, 32,112, 97, -114,115,101,100, 46,115, 99,104,101,109,101, 32, 61, 32,115, 59, 32,114,101,116, -117,114,110, 32, 34, 34, 32,101,110,100, 41, 10, 32, 32, 32, 32, 45, 45, 32,103, -101,116, 32, 97,117,116,104,111,114,105,116,121, 10, 32, 32, 32, 32,117,114,108, - 32, 61, 32,115,116,114,105,110,103, 46,103,115,117, 98, 40,117,114,108, 44, 32, - 34, 94, 47, 47, 40, 91, 94, 47, 93, 42, 41, 34, 44, 32,102,117,110, 99,116,105, -111,110, 40,110, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32,112, 97,114,115,101,100, - 46, 97,117,116,104,111,114,105,116,121, 32, 61, 32,110, 10, 32, 32, 32, 32, 32, - 32, 32, 32,114,101,116,117,114,110, 32, 34, 34, 10, 32, 32, 32, 32,101,110,100, - 41, 10, 32, 32, 32, 32, 45, 45, 32,103,101,116, 32,113,117,101,114,121, 32,115, -116,114,105,110,103, 10, 32, 32, 32, 32,117,114,108, 32, 61, 32,115,116,114,105, -110,103, 46,103,115,117, 98, 40,117,114,108, 44, 32, 34, 37, 63, 40, 46, 42, 41, - 34, 44, 32,102,117,110, 99,116,105,111,110, 40,113, 41, 10, 32, 32, 32, 32, 32, - 32, 32, 32,112, 97,114,115,101,100, 46,113,117,101,114,121, 32, 61, 32,113, 10, - 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32, 34, 34, 10, 32, 32, - 32, 32,101,110,100, 41, 10, 32, 32, 32, 32, 45, 45, 32,103,101,116, 32,112, 97, -114, 97,109,115, 10, 32, 32, 32, 32,117,114,108, 32, 61, 32,115,116,114,105,110, -103, 46,103,115,117, 98, 40,117,114,108, 44, 32, 34, 37, 59, 40, 46, 42, 41, 34, - 44, 32,102,117,110, 99,116,105,111,110, 40,112, 41, 10, 32, 32, 32, 32, 32, 32, - 32, 32,112, 97,114,115,101,100, 46,112, 97,114, 97,109,115, 32, 61, 32,112, 10, - 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32, 34, 34, 10, 32, 32, - 32, 32,101,110,100, 41, 10, 32, 32, 32, 32, 45, 45, 32,112, 97,116,104, 32,105, -115, 32,119,104, 97,116,101,118,101,114, 32,119, 97,115, 32,108,101,102,116, 10, - 32, 32, 32, 32,105,102, 32,117,114,108, 32,126, 61, 32, 34, 34, 32,116,104,101, -110, 32,112, 97,114,115,101,100, 46,112, 97,116,104, 32, 61, 32,117,114,108, 32, -101,110,100, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32, 97,117,116,104,111,114, -105,116,121, 32, 61, 32,112, 97,114,115,101,100, 46, 97,117,116,104,111,114,105, -116,121, 10, 32, 32, 32, 32,105,102, 32,110,111,116, 32, 97,117,116,104,111,114, -105,116,121, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,112, 97,114,115, -101,100, 32,101,110,100, 10, 32, 32, 32, 32, 97,117,116,104,111,114,105,116,121, - 32, 61, 32,115,116,114,105,110,103, 46,103,115,117, 98, 40, 97,117,116,104,111, -114,105,116,121, 44, 34, 94, 40, 91, 94, 64, 93, 42, 41, 64, 34, 44, 10, 32, 32, - 32, 32, 32, 32, 32, 32,102,117,110, 99,116,105,111,110, 40,117, 41, 32,112, 97, -114,115,101,100, 46,117,115,101,114,105,110,102,111, 32, 61, 32,117, 59, 32,114, -101,116,117,114,110, 32, 34, 34, 32,101,110,100, 41, 10, 32, 32, 32, 32, 97,117, -116,104,111,114,105,116,121, 32, 61, 32,115,116,114,105,110,103, 46,103,115,117, - 98, 40, 97,117,116,104,111,114,105,116,121, 44, 32, 34, 58, 40, 91, 94, 58, 37, - 93, 93, 42, 41, 36, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32,102,117,110, 99, -116,105,111,110, 40,112, 41, 32,112, 97,114,115,101,100, 46,112,111,114,116, 32, - 61, 32,112, 59, 32,114,101,116,117,114,110, 32, 34, 34, 32,101,110,100, 41, 10, - 32, 32, 32, 32,105,102, 32, 97,117,116,104,111,114,105,116,121, 32,126, 61, 32, - 34, 34, 32,116,104,101,110, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 32, - 73, 80,118, 54, 63, 10, 32, 32, 32, 32, 32, 32, 32, 32,112, 97,114,115,101,100, - 46,104,111,115,116, 32, 61, 32,115,116,114,105,110,103, 46,109, 97,116, 99,104, - 40, 97,117,116,104,111,114,105,116,121, 44, 32, 34, 94, 37, 91, 40, 46, 43, 41, - 37, 93, 36, 34, 41, 32,111,114, 32, 97,117,116,104,111,114,105,116,121, 32, 10, - 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,117,115, -101,114,105,110,102,111, 32, 61, 32,112, 97,114,115,101,100, 46,117,115,101,114, -105,110,102,111, 10, 32, 32, 32, 32,105,102, 32,110,111,116, 32,117,115,101,114, -105,110,102,111, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,112, 97,114, -115,101,100, 32,101,110,100, 10, 32, 32, 32, 32,117,115,101,114,105,110,102,111, - 32, 61, 32,115,116,114,105,110,103, 46,103,115,117, 98, 40,117,115,101,114,105, -110,102,111, 44, 32, 34, 58, 40, 91, 94, 58, 93, 42, 41, 36, 34, 44, 10, 32, 32, - 32, 32, 32, 32, 32, 32,102,117,110, 99,116,105,111,110, 40,112, 41, 32,112, 97, -114,115,101,100, 46,112, 97,115,115,119,111,114,100, 32, 61, 32,112, 59, 32,114, -101,116,117,114,110, 32, 34, 34, 32,101,110,100, 41, 10, 32, 32, 32, 32,112, 97, -114,115,101,100, 46,117,115,101,114, 32, 61, 32,117,115,101,114,105,110,102,111, - 10, 32, 32, 32, 32,114,101,116,117,114,110, 32,112, 97,114,115,101,100, 10,101, -110,100, 10, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 10, 45, 45, 32, 82,101, 98,117,105,108,100,115, 32, 97, 32,112, 97,114,115, -101,100, 32, 85, 82, 76, 32,102,114,111,109, 32,105,116,115, 32, 99,111,109,112, -111,110,101,110,116,115, 46, 10, 45, 45, 32, 67,111,109,112,111,110,101,110,116, -115, 32, 97,114,101, 32,112,114,111,116,101, 99,116,101,100, 32,105,102, 32, 97, -110,121, 32,114,101,115,101,114,118,101,100, 32,111,114, 32,117,110, 97,108,108, -111,119,101,100, 32, 99,104, 97,114, 97, 99,116,101,114,115, 32, 97,114,101, 32, -102,111,117,110,100, 10, 45, 45, 32, 73,110,112,117,116, 10, 45, 45, 32, 32, 32, -112, 97,114,115,101,100, 58, 32,112, 97,114,115,101,100, 32, 85, 82, 76, 44, 32, - 97,115, 32,114,101,116,117,114,110,101,100, 32, 98,121, 32,112, 97,114,115,101, - 10, 45, 45, 32, 82,101,116,117,114,110,115, 10, 45, 45, 32, 32, 32, 97, 32,115, -116,114,105,110,103,105,110,103, 32,119,105,116,104, 32,116,104,101, 32, 99,111, -114,114,101,115,112,111,110,100,105,110,103, 32, 85, 82, 76, 10, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10,102,117,110, 99,116,105, -111,110, 32, 95, 77, 46, 98,117,105,108,100, 40,112, 97,114,115,101,100, 41, 10, - 32, 32, 32, 32,108,111, 99, 97,108, 32,112,112, 97,116,104, 32, 61, 32, 95, 77, - 46,112, 97,114,115,101, 95,112, 97,116,104, 40,112, 97,114,115,101,100, 46,112, - 97,116,104, 32,111,114, 32, 34, 34, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, - 32,117,114,108, 32, 61, 32, 95, 77, 46, 98,117,105,108,100, 95,112, 97,116,104, - 40,112,112, 97,116,104, 41, 10, 32, 32, 32, 32,105,102, 32,112, 97,114,115,101, -100, 46,112, 97,114, 97,109,115, 32,116,104,101,110, 32,117,114,108, 32, 61, 32, -117,114,108, 32, 46, 46, 32, 34, 59, 34, 32, 46, 46, 32,112, 97,114,115,101,100, - 46,112, 97,114, 97,109,115, 32,101,110,100, 10, 32, 32, 32, 32,105,102, 32,112, - 97,114,115,101,100, 46,113,117,101,114,121, 32,116,104,101,110, 32,117,114,108, - 32, 61, 32,117,114,108, 32, 46, 46, 32, 34, 63, 34, 32, 46, 46, 32,112, 97,114, -115,101,100, 46,113,117,101,114,121, 32,101,110,100, 10, 32, 32, 32, 32,108,111, - 99, 97,108, 32, 97,117,116,104,111,114,105,116,121, 32, 61, 32,112, 97,114,115, -101,100, 46, 97,117,116,104,111,114,105,116,121, 10, 32, 32, 32, 32,105,102, 32, -112, 97,114,115,101,100, 46,104,111,115,116, 32,116,104,101,110, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 97,117,116,104,111,114,105,116,121, 32, 61, 32,112, 97,114, -115,101,100, 46,104,111,115,116, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32, -115,116,114,105,110,103, 46,102,105,110,100, 40, 97,117,116,104,111,114,105,116, -121, 44, 32, 34, 58, 34, 41, 32,116,104,101,110, 32, 45, 45, 32, 73, 80,118, 54, - 63, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 97,117,116,104,111,114, -105,116,121, 32, 61, 32, 34, 91, 34, 32, 46, 46, 32, 97,117,116,104,111,114,105, -116,121, 32, 46, 46, 32, 34, 93, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110, -100, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,112, 97,114,115,101,100, 46, -112,111,114,116, 32,116,104,101,110, 32, 97,117,116,104,111,114,105,116,121, 32, - 61, 32, 97,117,116,104,111,114,105,116,121, 32, 46, 46, 32, 34, 58, 34, 32, 46, - 46, 32,112, 97,114,115,101,100, 46,112,111,114,116, 32,101,110,100, 10, 32, 32, - 32, 32, 32, 32, 32, 32,108,111, 99, 97,108, 32,117,115,101,114,105,110,102,111, - 32, 61, 32,112, 97,114,115,101,100, 46,117,115,101,114,105,110,102,111, 10, 32, - 32, 32, 32, 32, 32, 32, 32,105,102, 32,112, 97,114,115,101,100, 46,117,115,101, -114, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,117, -115,101,114,105,110,102,111, 32, 61, 32,112, 97,114,115,101,100, 46,117,115,101, -114, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,112, 97,114, -115,101,100, 46,112, 97,115,115,119,111,114,100, 32,116,104,101,110, 10, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,117,115,101,114,105,110, -102,111, 32, 61, 32,117,115,101,114,105,110,102,111, 32, 46, 46, 32, 34, 58, 34, - 32, 46, 46, 32,112, 97,114,115,101,100, 46,112, 97,115,115,119,111,114,100, 10, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, - 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,117, -115,101,114,105,110,102,111, 32,116,104,101,110, 32, 97,117,116,104,111,114,105, -116,121, 32, 61, 32,117,115,101,114,105,110,102,111, 32, 46, 46, 32, 34, 64, 34, - 32, 46, 46, 32, 97,117,116,104,111,114,105,116,121, 32,101,110,100, 10, 32, 32, - 32, 32,101,110,100, 10, 32, 32, 32, 32,105,102, 32, 97,117,116,104,111,114,105, -116,121, 32,116,104,101,110, 32,117,114,108, 32, 61, 32, 34, 47, 47, 34, 32, 46, - 46, 32, 97,117,116,104,111,114,105,116,121, 32, 46, 46, 32,117,114,108, 32,101, -110,100, 10, 32, 32, 32, 32,105,102, 32,112, 97,114,115,101,100, 46,115, 99,104, -101,109,101, 32,116,104,101,110, 32,117,114,108, 32, 61, 32,112, 97,114,115,101, -100, 46,115, 99,104,101,109,101, 32, 46, 46, 32, 34, 58, 34, 32, 46, 46, 32,117, -114,108, 32,101,110,100, 10, 32, 32, 32, 32,105,102, 32,112, 97,114,115,101,100, - 46,102,114, 97,103,109,101,110,116, 32,116,104,101,110, 32,117,114,108, 32, 61, - 32,117,114,108, 32, 46, 46, 32, 34, 35, 34, 32, 46, 46, 32,112, 97,114,115,101, -100, 46,102,114, 97,103,109,101,110,116, 32,101,110,100, 10, 32, 32, 32, 32, 45, - 45, 32,117,114,108, 32, 61, 32,115,116,114,105,110,103, 46,103,115,117, 98, 40, -117,114,108, 44, 32, 34, 37,115, 34, 44, 32, 34, 34, 41, 10, 32, 32, 32, 32,114, -101,116,117,114,110, 32,117,114,108, 10,101,110,100, 10, 10, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 66,117,105,108, -100,115, 32, 97, 32, 97, 98,115,111,108,117,116,101, 32, 85, 82, 76, 32,102,114, -111,109, 32, 97, 32, 98, 97,115,101, 32, 97,110,100, 32, 97, 32,114,101,108, 97, -116,105,118,101, 32, 85, 82, 76, 32, 97, 99, 99,111,114,100,105,110,103, 32,116, -111, 32, 82, 70, 67, 32, 50, 51, 57, 54, 10, 45, 45, 32, 73,110,112,117,116, 10, - 45, 45, 32, 32, 32, 98, 97,115,101, 95,117,114,108, 10, 45, 45, 32, 32, 32,114, -101,108, 97,116,105,118,101, 95,117,114,108, 10, 45, 45, 32, 82,101,116,117,114, -110,115, 10, 45, 45, 32, 32, 32, 99,111,114,114,101,115,112,111,110,100,105,110, -103, 32, 97, 98,115,111,108,117,116,101, 32,117,114,108, 10, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10,102,117,110, 99,116,105,111, -110, 32, 95, 77, 46, 97, 98,115,111,108,117,116,101, 40, 98, 97,115,101, 95,117, -114,108, 44, 32,114,101,108, 97,116,105,118,101, 95,117,114,108, 41, 10, 32, 32, - 32, 32,108,111, 99, 97,108, 32, 98, 97,115,101, 95,112, 97,114,115,101,100, 10, - 32, 32, 32, 32,105,102, 32, 98, 97,115,101, 46,116,121,112,101, 40, 98, 97,115, -101, 95,117,114,108, 41, 32, 61, 61, 32, 34,116, 97, 98,108,101, 34, 32,116,104, -101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 98, 97,115,101, 95,112, 97,114,115, -101,100, 32, 61, 32, 98, 97,115,101, 95,117,114,108, 10, 32, 32, 32, 32, 32, 32, - 32, 32, 98, 97,115,101, 95,117,114,108, 32, 61, 32, 95, 77, 46, 98,117,105,108, -100, 40, 98, 97,115,101, 95,112, 97,114,115,101,100, 41, 10, 32, 32, 32, 32,101, -108,115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 98, 97,115,101, 95,112, 97,114, -115,101,100, 32, 61, 32, 95, 77, 46,112, 97,114,115,101, 40, 98, 97,115,101, 95, -117,114,108, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,108,111, 99, - 97,108, 32,114,101,108, 97,116,105,118,101, 95,112, 97,114,115,101,100, 32, 61, - 32, 95, 77, 46,112, 97,114,115,101, 40,114,101,108, 97,116,105,118,101, 95,117, -114,108, 41, 10, 32, 32, 32, 32,105,102, 32,110,111,116, 32, 98, 97,115,101, 95, -112, 97,114,115,101,100, 32,116,104,101,110, 32,114,101,116,117,114,110, 32,114, -101,108, 97,116,105,118,101, 95,117,114,108, 10, 32, 32, 32, 32,101,108,115,101, -105,102, 32,110,111,116, 32,114,101,108, 97,116,105,118,101, 95,112, 97,114,115, -101,100, 32,116,104,101,110, 32,114,101,116,117,114,110, 32, 98, 97,115,101, 95, -117,114,108, 10, 32, 32, 32, 32,101,108,115,101,105,102, 32,114,101,108, 97,116, -105,118,101, 95,112, 97,114,115,101,100, 46,115, 99,104,101,109,101, 32,116,104, -101,110, 32,114,101,116,117,114,110, 32,114,101,108, 97,116,105,118,101, 95,117, -114,108, 10, 32, 32, 32, 32,101,108,115,101, 10, 32, 32, 32, 32, 32, 32, 32, 32, -114,101,108, 97,116,105,118,101, 95,112, 97,114,115,101,100, 46,115, 99,104,101, -109,101, 32, 61, 32, 98, 97,115,101, 95,112, 97,114,115,101,100, 46,115, 99,104, -101,109,101, 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,110,111,116, 32,114, -101,108, 97,116,105,118,101, 95,112, 97,114,115,101,100, 46, 97,117,116,104,111, -114,105,116,121, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32,114,101,108, 97,116,105,118,101, 95,112, 97,114,115,101,100, 46, 97,117, -116,104,111,114,105,116,121, 32, 61, 32, 98, 97,115,101, 95,112, 97,114,115,101, -100, 46, 97,117,116,104,111,114,105,116,121, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,105,102, 32,110,111,116, 32,114,101,108, 97,116,105,118,101, 95, -112, 97,114,115,101,100, 46,112, 97,116,104, 32,116,104,101,110, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,114,101,108, 97,116,105,118, -101, 95,112, 97,114,115,101,100, 46,112, 97,116,104, 32, 61, 32, 98, 97,115,101, - 95,112, 97,114,115,101,100, 46,112, 97,116,104, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,110,111,116, 32,114,101,108, 97, -116,105,118,101, 95,112, 97,114,115,101,100, 46,112, 97,114, 97,109,115, 32,116, -104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,114,101,108, 97,116,105,118,101, 95,112, 97,114,115,101,100, 46, -112, 97,114, 97,109,115, 32, 61, 32, 98, 97,115,101, 95,112, 97,114,115,101,100, - 46,112, 97,114, 97,109,115, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,110,111,116, 32,114,101,108, 97,116, -105,118,101, 95,112, 97,114,115,101,100, 46,113,117,101,114,121, 32,116,104,101, -110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32,114,101,108, 97,116,105,118,101, 95,112, 97,114,115,101, -100, 46,113,117,101,114,121, 32, 61, 32, 98, 97,115,101, 95,112, 97,114,115,101, -100, 46,113,117,101,114,121, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,101,108,115,101, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32,114,101,108, 97,116,105,118,101, 95,112, 97, -114,115,101,100, 46,112, 97,116,104, 32, 61, 32, 97, 98,115,111,108,117,116,101, - 95,112, 97,116,104, 40, 98, 97,115,101, 95,112, 97,114,115,101,100, 46,112, 97, -116,104, 32,111,114, 32, 34, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,114,101,108, 97,116,105,118,101, 95,112, - 97,114,115,101,100, 46,112, 97,116,104, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, - 32, 32, 32, 32, 32, 32, 32, 32,114,101,116,117,114,110, 32, 95, 77, 46, 98,117, -105,108,100, 40,114,101,108, 97,116,105,118,101, 95,112, 97,114,115,101,100, 41, - 10, 32, 32, 32, 32,101,110,100, 10,101,110,100, 10, 10, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, 66,114,101, 97,107, -115, 32, 97, 32,112, 97,116,104, 32,105,110,116,111, 32,105,116,115, 32,115,101, -103,109,101,110,116,115, 44, 32,117,110,101,115, 99, 97,112,105,110,103, 32,116, -104,101, 32,115,101,103,109,101,110,116,115, 10, 45, 45, 32, 73,110,112,117,116, - 10, 45, 45, 32, 32, 32,112, 97,116,104, 10, 45, 45, 32, 82,101,116,117,114,110, -115, 10, 45, 45, 32, 32, 32,115,101,103,109,101,110,116, 58, 32, 97, 32,116, 97, - 98,108,101, 32,119,105,116,104, 32,111,110,101, 32,101,110,116,114,121, 32,112, -101,114, 32,115,101,103,109,101,110,116, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 10,102,117,110, 99,116,105,111,110, 32, 95, 77, - 46,112, 97,114,115,101, 95,112, 97,116,104, 40,112, 97,116,104, 41, 10, 32, 32, - 32, 32,108,111, 99, 97,108, 32,112, 97,114,115,101,100, 32, 61, 32,123,125, 10, - 32, 32, 32, 32,112, 97,116,104, 32, 61, 32,112, 97,116,104, 32,111,114, 32, 34, - 34, 10, 32, 32, 32, 32, 45, 45,112, 97,116,104, 32, 61, 32,115,116,114,105,110, -103, 46,103,115,117, 98, 40,112, 97,116,104, 44, 32, 34, 37,115, 34, 44, 32, 34, - 34, 41, 10, 32, 32, 32, 32,115,116,114,105,110,103, 46,103,115,117, 98, 40,112, - 97,116,104, 44, 32, 34, 40, 91, 94, 47, 93, 43, 41, 34, 44, 32,102,117,110, 99, -116,105,111,110, 32, 40,115, 41, 32,116, 97, 98,108,101, 46,105,110,115,101,114, -116, 40,112, 97,114,115,101,100, 44, 32,115, 41, 32,101,110,100, 41, 10, 32, 32, - 32, 32,102,111,114, 32,105, 32, 61, 32, 49, 44, 32, 35,112, 97,114,115,101,100, - 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32,112, 97,114,115,101,100, 91,105, - 93, 32, 61, 32, 95, 77, 46,117,110,101,115, 99, 97,112,101, 40,112, 97,114,115, -101,100, 91,105, 93, 41, 10, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,105, -102, 32,115,116,114,105,110,103, 46,115,117, 98, 40,112, 97,116,104, 44, 32, 49, - 44, 32, 49, 41, 32, 61, 61, 32, 34, 47, 34, 32,116,104,101,110, 32,112, 97,114, -115,101,100, 46,105,115, 95, 97, 98,115,111,108,117,116,101, 32, 61, 32, 49, 32, -101,110,100, 10, 32, 32, 32, 32,105,102, 32,115,116,114,105,110,103, 46,115,117, - 98, 40,112, 97,116,104, 44, 32, 45, 49, 44, 32, 45, 49, 41, 32, 61, 61, 32, 34, - 47, 34, 32,116,104,101,110, 32,112, 97,114,115,101,100, 46,105,115, 95,100,105, -114,101, 99,116,111,114,121, 32, 61, 32, 49, 32,101,110,100, 10, 32, 32, 32, 32, -114,101,116,117,114,110, 32,112, 97,114,115,101,100, 10,101,110,100, 10, 10, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 10, 45, 45, 32, - 66,117,105,108,100,115, 32, 97, 32,112, 97,116,104, 32, 99,111,109,112,111,110, -101,110,116, 32,102,114,111,109, 32,105,116,115, 32,115,101,103,109,101,110,116, -115, 44, 32,101,115, 99, 97,112,105,110,103, 32,112,114,111,116,101, 99,116,101, -100, 32, 99,104, 97,114, 97, 99,116,101,114,115, 46, 10, 45, 45, 32, 73,110,112, -117,116, 10, 45, 45, 32, 32, 32,112, 97,114,115,101,100, 58, 32,112, 97,116,104, - 32,115,101,103,109,101,110,116,115, 10, 45, 45, 32, 32, 32,117,110,115, 97,102, -101, 58, 32,105,102, 32,116,114,117,101, 44, 32,115,101,103,109,101,110,116,115, - 32, 97,114,101, 32,110,111,116, 32,112,114,111,116,101, 99,116,101,100, 32, 98, -101,102,111,114,101, 32,112, 97,116,104, 32,105,115, 32, 98,117,105,108,116, 10, - 45, 45, 32, 82,101,116,117,114,110,115, 10, 45, 45, 32, 32, 32,112, 97,116,104, - 58, 32, 99,111,114,114,101,115,112,111,110,100,105,110,103, 32,112, 97,116,104, - 32,115,116,114,105,110,103,105,110,103, 10, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 10,102,117,110, 99,116,105,111,110, 32, 95, 77, - 46, 98,117,105,108,100, 95,112, 97,116,104, 40,112, 97,114,115,101,100, 44, 32, -117,110,115, 97,102,101, 41, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,112, 97, -116,104, 32, 61, 32, 34, 34, 10, 32, 32, 32, 32,108,111, 99, 97,108, 32,110, 32, - 61, 32, 35,112, 97,114,115,101,100, 10, 32, 32, 32, 32,105,102, 32,117,110,115, - 97,102,101, 32,116,104,101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32,102,111,114, - 32,105, 32, 61, 32, 49, 44, 32,110, 45, 49, 32,100,111, 10, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32,112, 97,116,104, 32, 61, 32,112, 97,116,104, 32, 46, - 46, 32,112, 97,114,115,101,100, 91,105, 93, 10, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32,112, 97,116,104, 32, 61, 32,112, 97,116,104, 32, 46, 46, 32, 34, - 47, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32, 32, - 32, 32, 32,105,102, 32,110, 32, 62, 32, 48, 32,116,104,101,110, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32,112, 97,116,104, 32, 61, 32,112, 97,116,104, - 32, 46, 46, 32,112, 97,114,115,101,100, 91,110, 93, 10, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32,105,102, 32,112, 97,114,115,101,100, 46,105,115, 95,100, -105,114,101, 99,116,111,114,121, 32,116,104,101,110, 32,112, 97,116,104, 32, 61, - 32,112, 97,116,104, 32, 46, 46, 32, 34, 47, 34, 32,101,110,100, 10, 32, 32, 32, - 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,101,108,115,101, 10, 32, 32, - 32, 32, 32, 32, 32, 32,102,111,114, 32,105, 32, 61, 32, 49, 44, 32,110, 45, 49, - 32,100,111, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,112, 97,116,104, - 32, 61, 32,112, 97,116,104, 32, 46, 46, 32,112,114,111,116,101, 99,116, 95,115, -101,103,109,101,110,116, 40,112, 97,114,115,101,100, 91,105, 93, 41, 10, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,112, 97,116,104, 32, 61, 32,112, 97,116, -104, 32, 46, 46, 32, 34, 47, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, - 10, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,110, 32, 62, 32, 48, 32,116,104, -101,110, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,112, 97,116,104, 32, - 61, 32,112, 97,116,104, 32, 46, 46, 32,112,114,111,116,101, 99,116, 95,115,101, -103,109,101,110,116, 40,112, 97,114,115,101,100, 91,110, 93, 41, 10, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32,105,102, 32,112, 97,114,115,101,100, 46,105, -115, 95,100,105,114,101, 99,116,111,114,121, 32,116,104,101,110, 32,112, 97,116, -104, 32, 61, 32,112, 97,116,104, 32, 46, 46, 32, 34, 47, 34, 32,101,110,100, 10, - 32, 32, 32, 32, 32, 32, 32, 32,101,110,100, 10, 32, 32, 32, 32,101,110,100, 10, - 32, 32, 32, 32,105,102, 32,112, 97,114,115,101,100, 46,105,115, 95, 97, 98,115, -111,108,117,116,101, 32,116,104,101,110, 32,112, 97,116,104, 32, 61, 32, 34, 47, - 34, 32, 46, 46, 32,112, 97,116,104, 32,101,110,100, 10, 32, 32, 32, 32,114,101, -116,117,114,110, 32,112, 97,116,104, 10,101,110,100, 10, 10,114,101,116,117,114, -110, 32, 95, 77, 10, -}; - - if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"url.lua")==0) lua_call(L, 0, LUA_MULTRET); -} diff --git a/libraries/luasocket/libluasocket/usocket.c b/libraries/luasocket/libluasocket/usocket.c index 9966e889b..69635daa6 100644 --- a/libraries/luasocket/libluasocket/usocket.c +++ b/libraries/luasocket/libluasocket/usocket.c @@ -6,19 +6,19 @@ * The penalty of calling select to avoid busy-wait is only paid when * the I/O call fail in the first place. \*=========================================================================*/ -#include -#include +#include "luasocket.h" #include "socket.h" #include "pierror.h" -static int SOCKET_BUFFSIZE = 0x2000; +#include +#include /*-------------------------------------------------------------------------*\ * Wait for readable/writable/connected socket with timeout \*-------------------------------------------------------------------------*/ #ifndef SOCKET_SELECT -#include +#include #define WAITFD_R POLLIN #define WAITFD_W POLLOUT @@ -78,8 +78,8 @@ int socket_waitfd(p_socket ps, int sw, p_timeout tm) { * Initializes module \*-------------------------------------------------------------------------*/ int socket_open(void) { - /* instals a handler to ignore sigpipe or it will crash us */ - // signal(SIGPIPE, SIG_IGN); + /* installs a handler to ignore sigpipe or it will crash us */ + signal(SIGPIPE, SIG_IGN); return 1; } @@ -122,15 +122,8 @@ int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, \*-------------------------------------------------------------------------*/ int socket_create(p_socket ps, int domain, int type, int protocol) { *ps = socket(domain, type, protocol); - if (*ps != SOCKET_INVALID) - { - setsockopt(*ps, SOL_SOCKET, SO_RCVBUF, &SOCKET_BUFFSIZE, sizeof(SOCKET_BUFFSIZE)); - setsockopt(*ps, SOL_SOCKET, SO_SNDBUF, &SOCKET_BUFFSIZE, sizeof(SOCKET_BUFFSIZE)); - - return IO_DONE; - } - else - return errno; + if (*ps != SOCKET_INVALID) return IO_DONE; + else return errno; } /*-------------------------------------------------------------------------*\ @@ -243,7 +236,7 @@ int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, *sent = 0; if (*ps == SOCKET_INVALID) return IO_CLOSED; for ( ;; ) { - long put = (long) sendto(*ps, data, count, 0, addr, len); + long put = (long) sendto(*ps, data, count, 0, addr, len); if (put >= 0) { *sent = put; return IO_DONE; @@ -438,20 +431,24 @@ const char *socket_ioerror(p_socket ps, int err) { const char *socket_gaistrerror(int err) { if (err == 0) return NULL; switch (err) { - #if defined (__SWITCH__) - case EAI_AGAIN: return PIE_AGAIN; - case EAI_BADFLAGS: return PIE_BADFLAGS; - case EAI_BADHINTS: return PIE_BADHINTS; - case EAI_FAIL: return PIE_FAIL; - case EAI_OVERFLOW: return PIE_OVERFLOW; - case EAI_PROTOCOL: return PIE_PROTOCOL; - case EAI_SERVICE: return PIE_SERVICE; - case EAI_SOCKTYPE: return PIE_SOCKTYPE; - case EAI_SYSTEM: return strerror(errno); - #endif + case EAI_AGAIN: return PIE_AGAIN; + case EAI_BADFLAGS: return PIE_BADFLAGS; +#ifdef EAI_BADHINTS + case EAI_BADHINTS: return PIE_BADHINTS; +#endif + case EAI_FAIL: return PIE_FAIL; case EAI_FAMILY: return PIE_FAMILY; case EAI_MEMORY: return PIE_MEMORY; case EAI_NONAME: return PIE_NONAME; - default: return gai_strerror(err); +#ifdef EAI_OVERFLOW + case EAI_OVERFLOW: return PIE_OVERFLOW; +#endif +#ifdef EAI_PROTOCOL + case EAI_PROTOCOL: return PIE_PROTOCOL; +#endif + case EAI_SERVICE: return PIE_SERVICE; + case EAI_SOCKTYPE: return PIE_SOCKTYPE; + case EAI_SYSTEM: return strerror(errno); + default: return LUA_GAI_STRERROR(err); } } diff --git a/libraries/luasocket/libluasocket/usocket.h b/libraries/luasocket/libluasocket/usocket.h index 500b10a1d..45f2f99f7 100644 --- a/libraries/luasocket/libluasocket/usocket.h +++ b/libraries/luasocket/libluasocket/usocket.h @@ -25,41 +25,33 @@ /* sigpipe handling */ #include /* IP stuff*/ -#include #include +#include /* TCP options (nagle algorithm disable) */ #include - -#if defined(__3DS__) - #define SO_KEEPALIVE 0x0008 - #define SO_DONTROUTE 0x0010 -#endif - -#if defined(__SWITCH__) - #include -#endif +#include #ifndef SO_REUSEPORT - #define SO_REUSEPORT SO_REUSEADDR +#define SO_REUSEPORT SO_REUSEADDR #endif /* Some platforms use IPV6_JOIN_GROUP instead if * IPV6_ADD_MEMBERSHIP. The semantics are same, though. */ #ifndef IPV6_ADD_MEMBERSHIP - #ifdef IPV6_JOIN_GROUP - #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP - #endif /* IPV6_JOIN_GROUP */ -#endif /* !IPV6_ADD_MEMBERSHIP */ +#ifdef IPV6_JOIN_GROUP +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif /* IPV6_JOIN_GROUP */ +#endif /* !IPV6_ADD_MEMBERSHIP */ /* Same with IPV6_DROP_MEMBERSHIP / IPV6_LEAVE_GROUP. */ #ifndef IPV6_DROP_MEMBERSHIP - #ifdef IPV6_LEAVE_GROUP - #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP - #endif /* IPV6_LEAVE_GROUP */ -#endif /* !IPV6_DROP_MEMBERSHIP */ +#ifdef IPV6_LEAVE_GROUP +#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif /* IPV6_LEAVE_GROUP */ +#endif /* !IPV6_DROP_MEMBERSHIP */ typedef int t_socket; -typedef t_socket* p_socket; +typedef t_socket *p_socket; typedef struct sockaddr_storage t_sockaddr_storage; #define SOCKET_INVALID (-1) diff --git a/libraries/luasocket/libluasocket/wsocket.c b/libraries/luasocket/libluasocket/wsocket.c new file mode 100644 index 000000000..6cb1e415c --- /dev/null +++ b/libraries/luasocket/libluasocket/wsocket.c @@ -0,0 +1,434 @@ +/*=========================================================================*\ +* Socket compatibilization module for Win32 +* LuaSocket toolkit +* +* The penalty of calling select to avoid busy-wait is only paid when +* the I/O call fail in the first place. +\*=========================================================================*/ +#include "luasocket.h" + +#include + +#include "socket.h" +#include "pierror.h" + +/* WinSock doesn't have a strerror... */ +static const char *wstrerror(int err); + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int socket_open(void) { + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 0); + int err = WSAStartup(wVersionRequested, &wsaData ); + if (err != 0) return 0; + if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) && + (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) { + WSACleanup(); + return 0; + } + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close module +\*-------------------------------------------------------------------------*/ +int socket_close(void) { + WSACleanup(); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Wait for readable/writable/connected socket with timeout +\*-------------------------------------------------------------------------*/ +#define WAITFD_R 1 +#define WAITFD_W 2 +#define WAITFD_E 4 +#define WAITFD_C (WAITFD_E|WAITFD_W) + +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL; + struct timeval tv, *tp = NULL; + double t; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + if (sw & WAITFD_R) { + FD_ZERO(&rfds); + FD_SET(*ps, &rfds); + rp = &rfds; + } + if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } + if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(*ps, &efds); ep = &efds; } + if ((t = timeout_get(tm)) >= 0.0) { + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t-tv.tv_sec)*1.0e6); + tp = &tv; + } + ret = select(0, rp, wp, ep, tp); + if (ret == -1) return WSAGetLastError(); + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && FD_ISSET(*ps, &efds)) return IO_CLOSED; + return IO_DONE; +} + +/*-------------------------------------------------------------------------*\ +* Select with int timeout in ms +\*-------------------------------------------------------------------------*/ +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm) { + struct timeval tv; + double t = timeout_get(tm); + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); + if (n <= 0) { + Sleep((DWORD) (1000*t)); + return 0; + } else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL); +} + +/*-------------------------------------------------------------------------*\ +* Close and inutilize socket +\*-------------------------------------------------------------------------*/ +void socket_destroy(p_socket ps) { + if (*ps != SOCKET_INVALID) { + socket_setblocking(ps); /* close can take a long time on WIN32 */ + closesocket(*ps); + *ps = SOCKET_INVALID; + } +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +void socket_shutdown(p_socket ps, int how) { + socket_setblocking(ps); + shutdown(*ps, how); + socket_setnonblocking(ps); +} + +/*-------------------------------------------------------------------------*\ +* Creates and sets up a socket +\*-------------------------------------------------------------------------*/ +int socket_create(p_socket ps, int domain, int type, int protocol) { + *ps = socket(domain, type, protocol); + if (*ps != SOCKET_INVALID) return IO_DONE; + else return WSAGetLastError(); +} + +/*-------------------------------------------------------------------------*\ +* Connects or returns error message +\*-------------------------------------------------------------------------*/ +int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + int err; + /* don't call on closed socket */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* ask system to connect */ + if (connect(*ps, addr, len) == 0) return IO_DONE; + /* make sure the system is trying to connect */ + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) return err; + /* zero timeout case optimization */ + if (timeout_iszero(tm)) return IO_TIMEOUT; + /* we wait until something happens */ + err = socket_waitfd(ps, WAITFD_C, tm); + if (err == IO_CLOSED) { + int elen = sizeof(err); + /* give windows time to set the error (yes, disgusting) */ + Sleep(10); + /* find out why we failed */ + getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &elen); + /* we KNOW there was an error. if 'why' is 0, we will return + * "unknown error", but it's not really our fault */ + return err > 0? err: IO_UNKNOWN; + } else return err; + +} + +/*-------------------------------------------------------------------------*\ +* Binds or returns error message +\*-------------------------------------------------------------------------*/ +int socket_bind(p_socket ps, SA *addr, socklen_t len) { + int err = IO_DONE; + socket_setblocking(ps); + if (bind(*ps, addr, len) < 0) err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +int socket_listen(p_socket ps, int backlog) { + int err = IO_DONE; + socket_setblocking(ps); + if (listen(*ps, backlog) < 0) err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* Accept with timeout +\*-------------------------------------------------------------------------*/ +int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, + p_timeout tm) { + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int err; + /* try to get client socket */ + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; + /* find out why we failed */ + err = WSAGetLastError(); + /* if we failed because there was no connectoin, keep trying */ + if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err; + /* call select to avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Send with timeout +* On windows, if you try to send 10MB, the OS will buffer EVERYTHING +* this can take an awful lot of time and we will end up blocked. +* Therefore, whoever calls this function should not pass a huge buffer. +\*-------------------------------------------------------------------------*/ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + /* try to send something */ + int put = send(*ps, data, (int) count, 0); + /* if we sent something, we are done */ + if (put > 0) { + *sent = put; + return IO_DONE; + } + /* deal with failure */ + err = WSAGetLastError(); + /* we can only proceed if there was no serious error */ + if (err != WSAEWOULDBLOCK) return err; + /* avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Sendto with timeout +\*-------------------------------------------------------------------------*/ +int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + SA *addr, socklen_t len, p_timeout tm) +{ + int err; + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int put = sendto(*ps, data, (int) count, 0, addr, len); + if (put > 0) { + *sent = put; + return IO_DONE; + } + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) return err; + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Receive with timeout +\*-------------------------------------------------------------------------*/ +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, + p_timeout tm) +{ + int err, prev = IO_DONE; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int taken = recv(*ps, data, (int) count, 0); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); + /* On UDP, a connreset simply means the previous send failed. + * So we try again. + * On TCP, it means our socket is now useless, so the error passes. + * (We will loop again, exiting because the same error will happen) */ + if (err != WSAEWOULDBLOCK) { + if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; + prev = err; + } + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Recvfrom with timeout +\*-------------------------------------------------------------------------*/ +int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + SA *addr, socklen_t *len, p_timeout tm) +{ + int err, prev = IO_DONE; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int taken = recvfrom(*ps, data, (int) count, 0, addr, len); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); + /* On UDP, a connreset simply means the previous send failed. + * So we try again. + * On TCP, it means our socket is now useless, so the error passes. + * (We will loop again, exiting because the same error will happen) */ + if (err != WSAEWOULDBLOCK) { + if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; + prev = err; + } + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Put socket into blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setblocking(p_socket ps) { + u_long argp = 0; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* Put socket into non-blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setnonblocking(p_socket ps) { + u_long argp = 1; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* DNS helpers +\*-------------------------------------------------------------------------*/ +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { + *hp = gethostbyaddr(addr, len, AF_INET); + if (*hp) return IO_DONE; + else return WSAGetLastError(); +} + +int socket_gethostbyname(const char *addr, struct hostent **hp) { + *hp = gethostbyname(addr); + if (*hp) return IO_DONE; + else return WSAGetLastError(); +} + +/*-------------------------------------------------------------------------*\ +* Error translation functions +\*-------------------------------------------------------------------------*/ +const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; + default: return wstrerror(err); + } +} + +const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case WSAEADDRINUSE: return PIE_ADDRINUSE; + case WSAECONNREFUSED : return PIE_CONNREFUSED; + case WSAEISCONN: return PIE_ISCONN; + case WSAEACCES: return PIE_ACCESS; + case WSAECONNABORTED: return PIE_CONNABORTED; + case WSAECONNRESET: return PIE_CONNRESET; + case WSAETIMEDOUT: return PIE_TIMEDOUT; + default: return wstrerror(err); + } +} + +const char *socket_ioerror(p_socket ps, int err) { + (void) ps; + return socket_strerror(err); +} + +static const char *wstrerror(int err) { + switch (err) { + case WSAEINTR: return "Interrupted function call"; + case WSAEACCES: return PIE_ACCESS; /* "Permission denied"; */ + case WSAEFAULT: return "Bad address"; + case WSAEINVAL: return "Invalid argument"; + case WSAEMFILE: return "Too many open files"; + case WSAEWOULDBLOCK: return "Resource temporarily unavailable"; + case WSAEINPROGRESS: return "Operation now in progress"; + case WSAEALREADY: return "Operation already in progress"; + case WSAENOTSOCK: return "Socket operation on nonsocket"; + case WSAEDESTADDRREQ: return "Destination address required"; + case WSAEMSGSIZE: return "Message too long"; + case WSAEPROTOTYPE: return "Protocol wrong type for socket"; + case WSAENOPROTOOPT: return "Bad protocol option"; + case WSAEPROTONOSUPPORT: return "Protocol not supported"; + case WSAESOCKTNOSUPPORT: return PIE_SOCKTYPE; /* "Socket type not supported"; */ + case WSAEOPNOTSUPP: return "Operation not supported"; + case WSAEPFNOSUPPORT: return "Protocol family not supported"; + case WSAEAFNOSUPPORT: return PIE_FAMILY; /* "Address family not supported by protocol family"; */ + case WSAEADDRINUSE: return PIE_ADDRINUSE; /* "Address already in use"; */ + case WSAEADDRNOTAVAIL: return "Cannot assign requested address"; + case WSAENETDOWN: return "Network is down"; + case WSAENETUNREACH: return "Network is unreachable"; + case WSAENETRESET: return "Network dropped connection on reset"; + case WSAECONNABORTED: return "Software caused connection abort"; + case WSAECONNRESET: return PIE_CONNRESET; /* "Connection reset by peer"; */ + case WSAENOBUFS: return "No buffer space available"; + case WSAEISCONN: return PIE_ISCONN; /* "Socket is already connected"; */ + case WSAENOTCONN: return "Socket is not connected"; + case WSAESHUTDOWN: return "Cannot send after socket shutdown"; + case WSAETIMEDOUT: return PIE_TIMEDOUT; /* "Connection timed out"; */ + case WSAECONNREFUSED: return PIE_CONNREFUSED; /* "Connection refused"; */ + case WSAEHOSTDOWN: return "Host is down"; + case WSAEHOSTUNREACH: return "No route to host"; + case WSAEPROCLIM: return "Too many processes"; + case WSASYSNOTREADY: return "Network subsystem is unavailable"; + case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range"; + case WSANOTINITIALISED: + return "Successful WSAStartup not yet performed"; + case WSAEDISCON: return "Graceful shutdown in progress"; + case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; /* "Host not found"; */ + case WSATRY_AGAIN: return "Nonauthoritative host not found"; + case WSANO_RECOVERY: return PIE_FAIL; /* "Nonrecoverable name lookup error"; */ + case WSANO_DATA: return "Valid name, no data record of requested type"; + default: return "Unknown error"; + } +} + +const char *socket_gaistrerror(int err) { + if (err == 0) return NULL; + switch (err) { + case EAI_AGAIN: return PIE_AGAIN; + case EAI_BADFLAGS: return PIE_BADFLAGS; +#ifdef EAI_BADHINTS + case EAI_BADHINTS: return PIE_BADHINTS; +#endif + case EAI_FAIL: return PIE_FAIL; + case EAI_FAMILY: return PIE_FAMILY; + case EAI_MEMORY: return PIE_MEMORY; + case EAI_NONAME: return PIE_NONAME; +#ifdef EAI_OVERFLOW + case EAI_OVERFLOW: return PIE_OVERFLOW; +#endif +#ifdef EAI_PROTOCOL + case EAI_PROTOCOL: return PIE_PROTOCOL; +#endif + case EAI_SERVICE: return PIE_SERVICE; + case EAI_SOCKTYPE: return PIE_SOCKTYPE; +#ifdef EAI_SYSTEM + case EAI_SYSTEM: return strerror(errno); +#endif + default: return LUA_GAI_STRERROR(err); + } +} diff --git a/libraries/luasocket/libluasocket/wsocket.h b/libraries/luasocket/libluasocket/wsocket.h new file mode 100644 index 000000000..398664026 --- /dev/null +++ b/libraries/luasocket/libluasocket/wsocket.h @@ -0,0 +1,33 @@ +#ifndef WSOCKET_H +#define WSOCKET_H +/*=========================================================================*\ +* Socket compatibilization module for Win32 +* LuaSocket toolkit +\*=========================================================================*/ + +/*=========================================================================*\ +* WinSock include files +\*=========================================================================*/ +#include +#include + +typedef int socklen_t; +typedef SOCKADDR_STORAGE t_sockaddr_storage; +typedef SOCKET t_socket; +typedef t_socket *p_socket; + +#ifndef IPV6_V6ONLY +#define IPV6_V6ONLY 27 +#endif + +#define SOCKET_INVALID (INVALID_SOCKET) + +#ifndef SO_REUSEPORT +#define SO_REUSEPORT SO_REUSEADDR +#endif + +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV (0) +#endif + +#endif /* WSOCKET_H */ diff --git a/libraries/luasocket/luasocket.cpp b/libraries/luasocket/luasocket.cpp index b234b8f92..9c973a1fb 100644 --- a/libraries/luasocket/luasocket.cpp +++ b/libraries/luasocket/luasocket.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2006-2020 LOVE Development Team + * Copyright (c) 2006-2022 LOVE Development Team * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -18,111 +18,113 @@ * 3. This notice may not be removed or altered from any source distribution. **/ -#include "luasocket/luasocket.h" +#include "luasocket.hpp" // LuaSocket extern "C" { - #include "luasocket/libluasocket/libluasocket.h" - #include "luasocket/libluasocket/mime.h" +#include "libluasocket/luasocket.h" +#include "libluasocket/mime.h" } -// Quick macro for adding functions to -// the preloder. -#define PRELOAD(name, function) \ - lua_getglobal(L, "package"); \ - lua_getfield(L, -1, "preload"); \ - lua_pushcfunction(L, function); \ - lua_setfield(L, -2, name); \ +// Lua files +static constexpr char ftp_lua[] = { +#include "libluasocket/ftp.lua" +}; + +static constexpr char headers_lua[] = { +#include "libluasocket/headers.lua" +}; + +static constexpr char http_lua[] = { +#include "libluasocket/http.lua" +}; + +static constexpr char ltn12_lua[] = { +#include "libluasocket/ltn12.lua" +}; + +static constexpr char mbox_lua[] = { +#include "libluasocket/mbox.lua" +}; + +static constexpr char mime_lua[] = { +#include "libluasocket/mime.lua" +}; + +static constexpr char smtp_lua[] = { +#include "libluasocket/smtp.lua" +}; + +static constexpr char socket_lua[] = { +#include "libluasocket/socket.lua" +}; + +static constexpr char tp_lua[] = { +#include "libluasocket/tp.lua" +}; + +static constexpr char url_lua[] = { +#include "libluasocket/url.lua" +}; + +static void preload(lua_State* L, const char* name, lua_CFunction func) +{ + lua_getglobal(L, "package"); + lua_getfield(L, -1, "preload"); + lua_pushcfunction(L, func); + lua_setfield(L, -2, name); lua_pop(L, 2); +} -namespace love +static void preload(lua_State* L, const char* name, const char* chunkname, const void* lua, + size_t size) { - namespace luasocket + if (luaL_loadbuffer(L, (const char*)lua, size, name) != 0) { + luaL_loadstring(L, "local name, msg = ... return function() error(name..\": \"..msg) end"); + lua_pushstring(L, name); + lua_pushvalue(L, -3); + // Before: + // -1: error message + // -2: module name + // -3: loadstring function + // -4: error message + lua_call(L, 2, 1); + // After: + // -1: function + // -2: error message + lua_remove(L, -2); + } + + lua_getglobal(L, "package"); + lua_getfield(L, -1, "preload"); + lua_pushvalue(L, -3); + lua_setfield(L, -2, name); + lua_pop(L, 3); +} - int __open_luasocket_socket(lua_State * L) - { - #include "libluasocket/socket.lua.h" - return 1; - } - - int __open_luasocket_ftp(lua_State * L) - { - #include "libluasocket/ftp.lua.h" - return 1; - } - - int __open_luasocket_http(lua_State * L) - { - #include "libluasocket/http.lua.h" - return 1; - } - - int __open_luasocket_ltn12(lua_State * L) - { - #include "libluasocket/ltn12.lua.h" - return 1; - } - - int __open_luasocket_mime(lua_State * L) - { - #include "libluasocket/mime.lua.h" - return 1; - } - - int __open_luasocket_smtp(lua_State * L) - { - #include "libluasocket/smtp.lua.h" - return 1; - } - - int __open_luasocket_tp(lua_State * L) - { - #include "libluasocket/tp.lua.h" - return 1; - } - - int __open_luasocket_url(lua_State * L) - { - #include "libluasocket/url.lua.h" - return 1; - } - - int __open_luasocket_headers(lua_State * L) - { - #include "libluasocket/headers.lua.h" - return 1; - } - - int __open_luasocket_mbox(lua_State * L) - { - #include "libluasocket/mbox.lua.h" - return 1; - } - - - int __open(lua_State * L) - { - - // Preload code from LuaSocket. - PRELOAD("socket.core", luaopen_socket_core); - PRELOAD("mime.core", luaopen_mime_core); - - PRELOAD("socket", __open_luasocket_socket); - PRELOAD("socket.ftp", __open_luasocket_ftp) - PRELOAD("socket.http", __open_luasocket_http); - PRELOAD("ltn12", __open_luasocket_ltn12); - PRELOAD("mime", __open_luasocket_mime) - PRELOAD("socket.smtp", __open_luasocket_smtp); - PRELOAD("socket.tp", __open_luasocket_tp) - PRELOAD("socket.url", __open_luasocket_url) - PRELOAD("socket.headers", __open_luasocket_headers) - PRELOAD("mbox", __open_luasocket_mbox) - - // No need to register garbage collector function. - - return 0; - } - } // luasocket -} // love +namespace love::luasocket +{ + int preload(lua_State* L) + { + // Preload code from LuaSocket. + preload(L, "socket.core", luaopen_socket_core); + preload(L, "mime.core", luaopen_mime_core); + + preload(L, "socket", "=[socket \"socket.lua\"]", socket_lua, sizeof(socket_lua)); + preload(L, "socket.ftp", "=[socket \"ftp.lua\"]", ftp_lua, sizeof(ftp_lua)); + preload(L, "socket.http", "=[socket \"http.lua\"]", http_lua, sizeof(http_lua)); + preload(L, "ltn12", "=[socket \"ltn12.lua\"]", ltn12_lua, sizeof(ltn12_lua)); + preload(L, "mime", "=[socket \"mime.lua\"]", mime_lua, sizeof(mime_lua)); + preload(L, "socket.smtp", "=[socket \"smtp.lua\"]", smtp_lua, sizeof(smtp_lua)); + preload(L, "socket.tp", "=[socket \"tp.lua\"]", tp_lua, sizeof(tp_lua)); + preload(L, "socket.url", "=[socket \"url.lua\"]", url_lua, sizeof(url_lua)); + preload(L, "socket.headers", "=[socket \"headers.lua\"]", headers_lua, sizeof(headers_lua)); + preload(L, "mbox", "=[socket \"mbox.lua\"]", mbox_lua, sizeof(mbox_lua)); + + // No need to register garbage collector function. + + return 0; + } +} // namespace love::luasocket diff --git a/libraries/luasocket/luasocket.h b/libraries/luasocket/luasocket.h deleted file mode 100644 index 5a43e3664..000000000 --- a/libraries/luasocket/luasocket.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2006-2020 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ - -#ifndef LOVE_LUASOCKET_LUASOCKET_H -#define LOVE_LUASOCKET_LUASOCKET_H - -// LOVE -#if defined(__3DS__) - #include <3ds.h> -#elif defined(__SWITCH__) - #include -#endif - -extern "C" -{ -#include -#include -#include -} - -namespace love -{ - namespace luasocket - { - - int __open(lua_State* L); - - // Loaders for all lua files. We want to be able - // to load these dynamically. (Identical in the LuaSocket - // documentation. These functions wrap the parsing of code, etc). - int __open_luasocket_socket(lua_State* L); - int __open_luasocket_ftp(lua_State* L); - int __open_luasocket_http(lua_State* L); - int __open_luasocket_ltn12(lua_State* L); - int __open_luasocket_mime(lua_State* L); - int __open_luasocket_smtp(lua_State* L); - int __open_luasocket_tp(lua_State* L); - int __open_luasocket_url(lua_State* L); - int __open_luasocket_headers(lua_State* L); - int __open_luasocket_mbox(lua_State* L); - - } // namespace luasocket -} // namespace love - -#endif // LOVE_LUASOCKET_LUASOCKET_H diff --git a/libraries/luasocket/luasocket.hpp b/libraries/luasocket/luasocket.hpp new file mode 100644 index 000000000..4ef5879ca --- /dev/null +++ b/libraries/luasocket/luasocket.hpp @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2006-2022 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifndef LOVE_LUASOCKET_LUASOCKET_H +#define LOVE_LUASOCKET_LUASOCKET_H + +// LOVE + +extern "C" +{ +#include +#include +#include +} + +namespace love +{ + namespace luasocket + { + int preload(lua_State* L); + } // namespace luasocket +} // namespace love + +#endif // LOVE_LUASOCKET_LUASOCKET_H diff --git a/libraries/noise1234/simplexnoise1234.cpp b/libraries/noise1234/simplexnoise1234.cpp index 2765f38e1..de497c1c9 100644 --- a/libraries/noise1234/simplexnoise1234.cpp +++ b/libraries/noise1234/simplexnoise1234.cpp @@ -1,5 +1,5 @@ // SimplexNoise1234 -// Copyright 2003-2011, Stefan Gustavson +// Copyright © 2003-2011, Stefan Gustavson // // Contact: stegu@itn.liu.se // @@ -13,8 +13,7 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. -// Modified by the LOVE Development Team to remove 3D and 4D implementations due -// to patent issues. +// Modified by the LOVE Development Team to use double precision. /** \file \brief Implements the SimplexNoise1234 class for producing Perlin simplex noise. @@ -85,7 +84,7 @@ unsigned char SimplexNoise1234::perm[512] = {151,160,137,91,90,15, 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, - 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 + 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 }; //--------------------------------------------------------------------- @@ -103,65 +102,91 @@ unsigned char SimplexNoise1234::perm[512] = {151,160,137,91,90,15, * float SLnoise = (SimplexNoise1234::noise(x,y,z) + 1.0) * 0.5; */ -float SimplexNoise1234::grad( int hash, float x ) { +double SimplexNoise1234::grad( int hash, double x ) { int h = hash & 15; - float grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0 + double grad = 1.0 + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0 if (h&8) grad = -grad; // Set a random sign for the gradient return ( grad * x ); // Multiply the gradient with the distance } -float SimplexNoise1234::grad( int hash, float x, float y ) { +double SimplexNoise1234::grad( int hash, double x, double y ) { int h = hash & 7; // Convert low 3 bits of hash code - float u = h<4 ? x : y; // into 8 simple gradient directions, - float v = h<4 ? y : x; // and compute the dot product with (x,y). - return ((h&1)? -u : u) + ((h&2)? -2.0f*v : 2.0f*v); + double u = h<4 ? x : y; // into 8 simple gradient directions, + double v = h<4 ? y : x; // and compute the dot product with (x,y). + return ((h&1)? -u : u) + ((h&2)? -2.0*v : 2.0*v); } +double SimplexNoise1234::grad( int hash, double x, double y , double z ) { + int h = hash & 15; // Convert low 4 bits of hash code into 12 simple + double u = h<8 ? x : y; // gradient directions, and compute dot product. + double v = h<4 ? y : h==12||h==14 ? x : z; // Fix repeats at h = 12 to 15 + return ((h&1)? -u : u) + ((h&2)? -v : v); +} + +double SimplexNoise1234::grad( int hash, double x, double y, double z, double t ) { + int h = hash & 31; // Convert low 5 bits of hash code into 32 simple + double u = h<24 ? x : y; // gradient directions, and compute dot product. + double v = h<16 ? y : z; + double w = h<8 ? z : t; + return ((h&1)? -u : u) + ((h&2)? -v : v) + ((h&4)? -w : w); +} + +// A lookup table to traverse the simplex around a given point in 4D. +// Details can be found where this table is used, in the 4D noise method. +/* TODO: This should not be required, backport it from Bill's GLSL code! */ +static unsigned char simplex[64][4] = { + {0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,3,0}, + {0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,3,2,0}, + {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}, + {1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0}, + {1,0,2,3},{1,0,3,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0}, + {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}, + {2,0,1,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0}, + {2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1},{3,2,1,0}}; + // 1D simplex noise -float SimplexNoise1234::noise(float x) { +double SimplexNoise1234::noise(double x) { int i0 = FASTFLOOR(x); int i1 = i0 + 1; - float x0 = x - i0; - float x1 = x0 - 1.0f; + double x0 = x - i0; + double x1 = x0 - 1.0; - float n0, n1; + double n0, n1; - float t0 = 1.0f - x0*x0; -// if(t0 < 0.0f) t0 = 0.0f; + double t0 = 1.0 - x0*x0; t0 *= t0; n0 = t0 * t0 * grad(perm[i0 & 0xff], x0); - float t1 = 1.0f - x1*x1; -// if(t1 < 0.0f) t1 = 0.0f; + double t1 = 1.0 - x1*x1; t1 *= t1; n1 = t1 * t1 * grad(perm[i1 & 0xff], x1); // The maximum value of this noise is 8*(3/4)^4 = 2.53125 // A factor of 0.395 will scale to fit exactly within [-1,1] - return 0.395f * (n0 + n1); + return 0.395 * (n0 + n1); } // 2D simplex noise -float SimplexNoise1234::noise(float x, float y) { +double SimplexNoise1234::noise(double x, double y) { #define F2 0.366025403 // F2 = 0.5*(sqrt(3.0)-1.0) #define G2 0.211324865 // G2 = (3.0-Math.sqrt(3.0))/6.0 - float n0, n1, n2; // Noise contributions from the three corners + double n0, n1, n2; // Noise contributions from the three corners // Skew the input space to determine which simplex cell we're in - float s = (x+y)*F2; // Hairy factor for 2D - float xs = x + s; - float ys = y + s; + double s = (x+y)*F2; // Hairy factor for 2D + double xs = x + s; + double ys = y + s; int i = FASTFLOOR(xs); int j = FASTFLOOR(ys); - float t = (float)(i+j)*G2; - float X0 = i-t; // Unskew the cell origin back to (x,y) space - float Y0 = j-t; - float x0 = x-X0; // The x,y distances from the cell origin - float y0 = y-Y0; + double t = (i+j)*G2; + double X0 = i-t; // Unskew the cell origin back to (x,y) space + double Y0 = j-t; + double x0 = x-X0; // The x,y distances from the cell origin + double y0 = y-Y0; // For the 2D case, the simplex shape is an equilateral triangle. // Determine which simplex we are in. @@ -173,32 +198,32 @@ float SimplexNoise1234::noise(float x, float y) { // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where // c = (3-sqrt(3))/6 - float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords - float y1 = y0 - j1 + G2; - float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords - float y2 = y0 - 1.0f + 2.0f * G2; + double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + double y1 = y0 - j1 + G2; + double x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords + double y2 = y0 - 1.0 + 2.0 * G2; // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds int ii = i & 0xff; int jj = j & 0xff; // Calculate the contribution from the three corners - float t0 = 0.5f - x0*x0-y0*y0; - if(t0 < 0.0f) n0 = 0.0f; + double t0 = 0.5 - x0*x0-y0*y0; + if(t0 < 0.0) n0 = 0.0; else { t0 *= t0; - n0 = t0 * t0 * grad(perm[ii+perm[jj]], x0, y0); + n0 = t0 * t0 * grad(perm[ii+perm[jj]], x0, y0); } - float t1 = 0.5f - x1*x1-y1*y1; - if(t1 < 0.0f) n1 = 0.0f; + double t1 = 0.5 - x1*x1-y1*y1; + if(t1 < 0.0) n1 = 0.0; else { t1 *= t1; n1 = t1 * t1 * grad(perm[ii+i1+perm[jj+j1]], x1, y1); } - float t2 = 0.5f - x2*x2-y2*y2; - if(t2 < 0.0f) n2 = 0.0f; + double t2 = 0.5 - x2*x2-y2*y2; + if(t2 < 0.0) n2 = 0.0; else { t2 *= t2; n2 = t2 * t2 * grad(perm[ii+1+perm[jj+1]], x2, y2); @@ -206,5 +231,240 @@ float SimplexNoise1234::noise(float x, float y) { // Add contributions from each corner to get the final noise value. // The result is scaled to return values in the interval [-1,1]. - return 45.23f * (n0 + n1 + n2); // TODO: The scale factor is preliminary! + return 45.23 * (n0 + n1 + n2); // TODO: The scale factor is preliminary! } + +// 3D simplex noise +double SimplexNoise1234::noise(double x, double y, double z) { + + // Simple skewing factors for the 3D case +#define F3 0.333333333 +#define G3 0.166666667 + + double n0, n1, n2, n3; // Noise contributions from the four corners + + // Skew the input space to determine which simplex cell we're in + double s = (x+y+z)*F3; // Very nice and simple skew factor for 3D + double xs = x+s; + double ys = y+s; + double zs = z+s; + int i = FASTFLOOR(xs); + int j = FASTFLOOR(ys); + int k = FASTFLOOR(zs); + + double t = (float)(i+j+k)*G3; + double X0 = i-t; // Unskew the cell origin back to (x,y,z) space + double Y0 = j-t; + double Z0 = k-t; + double x0 = x-X0; // The x,y,z distances from the cell origin + double y0 = y-Y0; + double z0 = z-Z0; + + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + // Determine which simplex we are in. + int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords + int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + + /* This code would benefit from a backport from the GLSL version! */ + if(x0>=y0) { + if(y0>=z0) + { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order + else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order + else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order + } + else { // x0 y0) ? 32 : 0; + int c2 = (x0 > z0) ? 16 : 0; + int c3 = (y0 > z0) ? 8 : 0; + int c4 = (x0 > w0) ? 4 : 0; + int c5 = (y0 > w0) ? 2 : 0; + int c6 = (z0 > w0) ? 1 : 0; + int c = c1 + c2 + c3 + c4 + c5 + c6; + + int i1, j1, k1, l1; // The integer offsets for the second simplex corner + int i2, j2, k2, l2; // The integer offsets for the third simplex corner + int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner + + // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. + // Many values of c will never occur, since e.g. x>y>z>w makes x=3 ? 1 : 0; + j1 = simplex[c][1]>=3 ? 1 : 0; + k1 = simplex[c][2]>=3 ? 1 : 0; + l1 = simplex[c][3]>=3 ? 1 : 0; + // The number 2 in the "simplex" array is at the second largest coordinate. + i2 = simplex[c][0]>=2 ? 1 : 0; + j2 = simplex[c][1]>=2 ? 1 : 0; + k2 = simplex[c][2]>=2 ? 1 : 0; + l2 = simplex[c][3]>=2 ? 1 : 0; + // The number 1 in the "simplex" array is at the second smallest coordinate. + i3 = simplex[c][0]>=1 ? 1 : 0; + j3 = simplex[c][1]>=1 ? 1 : 0; + k3 = simplex[c][2]>=1 ? 1 : 0; + l3 = simplex[c][3]>=1 ? 1 : 0; + // The fifth corner has all coordinate offsets = 1, so no need to look that up. + + double x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords + double y1 = y0 - j1 + G4; + double z1 = z0 - k1 + G4; + double w1 = w0 - l1 + G4; + double x2 = x0 - i2 + 2.0f*G4; // Offsets for third corner in (x,y,z,w) coords + double y2 = y0 - j2 + 2.0f*G4; + double z2 = z0 - k2 + 2.0f*G4; + double w2 = w0 - l2 + 2.0f*G4; + double x3 = x0 - i3 + 3.0f*G4; // Offsets for fourth corner in (x,y,z,w) coords + double y3 = y0 - j3 + 3.0f*G4; + double z3 = z0 - k3 + 3.0f*G4; + double w3 = w0 - l3 + 3.0f*G4; + double x4 = x0 - 1.0f + 4.0f*G4; // Offsets for last corner in (x,y,z,w) coords + double y4 = y0 - 1.0f + 4.0f*G4; + double z4 = z0 - 1.0f + 4.0f*G4; + double w4 = w0 - 1.0f + 4.0f*G4; + + // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds + int ii = i & 0xff; + int jj = j & 0xff; + int kk = k & 0xff; + int ll = l & 0xff; + + // Calculate the contribution from the five corners + double t0 = 0.6f - x0*x0 - y0*y0 - z0*z0 - w0*w0; + if(t0 < 0.0f) n0 = 0.0f; + else { + t0 *= t0; + n0 = t0 * t0 * grad(perm[ii+perm[jj+perm[kk+perm[ll]]]], x0, y0, z0, w0); + } + + double t1 = 0.6f - x1*x1 - y1*y1 - z1*z1 - w1*w1; + if(t1 < 0.0f) n1 = 0.0f; + else { + t1 *= t1; + n1 = t1 * t1 * grad(perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]], x1, y1, z1, w1); + } + + double t2 = 0.6f - x2*x2 - y2*y2 - z2*z2 - w2*w2; + if(t2 < 0.0f) n2 = 0.0f; + else { + t2 *= t2; + n2 = t2 * t2 * grad(perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]], x2, y2, z2, w2); + } + + double t3 = 0.6f - x3*x3 - y3*y3 - z3*z3 - w3*w3; + if(t3 < 0.0f) n3 = 0.0f; + else { + t3 *= t3; + n3 = t3 * t3 * grad(perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]], x3, y3, z3, w3); + } + + double t4 = 0.6f - x4*x4 - y4*y4 - z4*z4 - w4*w4; + if(t4 < 0.0f) n4 = 0.0f; + else { + t4 *= t4; + n4 = t4 * t4 * grad(perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]], x4, y4, z4, w4); + } + + // Sum up and scale the result to cover the range [-1,1] + return 27.3 * (n0 + n1 + n2 + n3 + n4); // TODO: The scale factor is preliminary! +} +//--------------------------------------------------------------------- diff --git a/libraries/noise1234/simplexnoise1234.h b/libraries/noise1234/simplexnoise1234.h index 6aceb6b61..2be13d28e 100644 --- a/libraries/noise1234/simplexnoise1234.h +++ b/libraries/noise1234/simplexnoise1234.h @@ -1,5 +1,5 @@ // SimplexNoise1234 -// Copyright � 2003-2011, Stefan Gustavson +// Copyright © 2003-2011, Stefan Gustavson // // Contact: stegu@itn.liu.se // @@ -13,39 +13,39 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. -// Modified by the LOVE Development Team to remove 3D and 4D implementations due -// to patent issues. +// Modified by the LOVE Development Team to use double precision. /** \file - \brief Declares the SimplexNoise1234 class for producing Perlin simplex noise. - \author Stefan Gustavson (stegu@itn.liu.se) + \brief Declares the SimplexNoise1234 class for producing Perlin simplex noise. + \author Stefan Gustavson (stegu@itn.liu.se) */ /* * This is a clean, fast, modern and free Perlin Simplex noise class in C++. * Being a stand-alone class with no external dependencies, it is * highly reusable without source code modifications. - * - * - * Note: - * Replacing the "float" type with "double" can actually make this run faster - * on some platforms. A templatized version of SimplexNoise1234 could be useful. */ -class SimplexNoise1234 { +class SimplexNoise1234 +{ public: - SimplexNoise1234() {} - ~SimplexNoise1234() {} - -/** 1D and 2D float Perlin noise - */ - static float noise( float x ); - static float noise( float x, float y ); + SimplexNoise1234() + {} + ~SimplexNoise1234() + {} + + /** 1D and 2D float Perlin noise + */ + static double noise(double x); + static double noise(double x, double y); + static double noise(double x, double y, double z); + static double noise(double x, double y, double z, double w); private: static unsigned char perm[]; - static float grad( int hash, float x ); - static float grad( int hash, float x, float y ); - + static double grad(int hash, double x); + static double grad(int hash, double x, double y); + static double grad(int hash, double x, double y, double z); + static double grad(int hash, double x, double y, double z, double t); }; diff --git a/libraries/utf8/utf8.h b/libraries/utf8/utf8.h index 4e4451403..5bd7e7495 100644 --- a/libraries/utf8/utf8.h +++ b/libraries/utf8/utf8.h @@ -24,7 +24,6 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 diff --git a/libraries/utf8/utf8/checked.h b/libraries/utf8/utf8/checked.h index b8b450699..ea228c5d0 100644 --- a/libraries/utf8/utf8/checked.h +++ b/libraries/utf8/utf8/checked.h @@ -1,4 +1,4 @@ -// Copyright 2006 Nemanja Trifunovic +// Copyright 2006-2016 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization @@ -24,7 +24,6 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 @@ -34,88 +33,110 @@ DEALINGS IN THE SOFTWARE. namespace utf8 { // Base for the exceptions that may be thrown from the library - class exception : public ::std::exception { + class exception : public ::std::exception + { }; // Exceptions that may be thrown from the library functions. - class invalid_code_point : public exception { + class invalid_code_point : public exception + { uint32_t cp; - public: - invalid_code_point(uint32_t cp) : cp(cp) {} - virtual const char* what() const throw() { return "Invalid code point"; } - uint32_t code_point() const {return cp;} + + public: + invalid_code_point(uint32_t codepoint) : cp(codepoint) + {} + virtual const char* what() const UTF_CPP_NOEXCEPT UTF_CPP_OVERRIDE + { + return "Invalid code point"; + } + uint32_t code_point() const + { + return cp; + } }; - class invalid_utf8 : public exception { + class invalid_utf8 : public exception + { uint8_t u8; - public: - invalid_utf8 (uint8_t u) : u8(u) {} - virtual const char* what() const throw() { return "Invalid UTF-8"; } - uint8_t utf8_octet() const {return u8;} + + public: + invalid_utf8(uint8_t u) : u8(u) + {} + invalid_utf8(char c) : u8(static_cast(c)) + {} + virtual const char* what() const UTF_CPP_NOEXCEPT UTF_CPP_OVERRIDE + { + return "Invalid UTF-8"; + } + uint8_t utf8_octet() const + { + return u8; + } }; - class invalid_utf16 : public exception { + class invalid_utf16 : public exception + { uint16_t u16; - public: - invalid_utf16 (uint16_t u) : u16(u) {} - virtual const char* what() const throw() { return "Invalid UTF-16"; } - uint16_t utf16_word() const {return u16;} + + public: + invalid_utf16(uint16_t u) : u16(u) + {} + virtual const char* what() const UTF_CPP_NOEXCEPT UTF_CPP_OVERRIDE + { + return "Invalid UTF-16"; + } + uint16_t utf16_word() const + { + return u16; + } }; - class not_enough_room : public exception { - public: - virtual const char* what() const throw() { return "Not enough space"; } + class not_enough_room : public exception + { + public: + virtual const char* what() const UTF_CPP_NOEXCEPT UTF_CPP_OVERRIDE + { + return "Not enough space"; + } }; /// The library API - functions intended to be called by the users - template + template octet_iterator append(uint32_t cp, octet_iterator result) { if (!utf8::internal::is_code_point_valid(cp)) throw invalid_code_point(cp); - if (cp < 0x80) // one octet - *(result++) = static_cast(cp); - else if (cp < 0x800) { // two octets - *(result++) = static_cast((cp >> 6) | 0xc0); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - else if (cp < 0x10000) { // three octets - *(result++) = static_cast((cp >> 12) | 0xe0); - *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - else { // four octets - *(result++) = static_cast((cp >> 18) | 0xf0); - *(result++) = static_cast(((cp >> 12) & 0x3f) | 0x80); - *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - return result; + return internal::append(cp, result); } - template - output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) + template + output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, + uint32_t replacement) { - while (start != end) { + while (start != end) + { octet_iterator sequence_start = start; - internal::utf_error err_code = utf8::internal::validate_next(start, end); - switch (err_code) { - case internal::UTF8_OK : + internal::utf_error err_code = utf8::internal::validate_next(start, end); + switch (err_code) + { + case internal::UTF8_OK: for (octet_iterator it = sequence_start; it != start; ++it) *out++ = *it; break; case internal::NOT_ENOUGH_ROOM: - throw not_enough_room(); + out = utf8::append(replacement, out); + start = end; + break; case internal::INVALID_LEAD: - out = utf8::append (replacement, out); + out = utf8::append(replacement, out); ++start; break; case internal::INCOMPLETE_SEQUENCE: case internal::OVERLONG_SEQUENCE: case internal::INVALID_CODE_POINT: - out = utf8::append (replacement, out); + out = utf8::append(replacement, out); ++start; // just one replacement mark for the sequence while (start != end && utf8::internal::is_trail(*start)) @@ -126,40 +147,42 @@ namespace utf8 return out; } - template - inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) + template + inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, + output_iterator out) { static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); return utf8::replace_invalid(start, end, out, replacement_marker); } - template + template uint32_t next(octet_iterator& it, octet_iterator end) { - uint32_t cp = 0; + uint32_t cp = 0; internal::utf_error err_code = utf8::internal::validate_next(it, end, cp); - switch (err_code) { - case internal::UTF8_OK : + switch (err_code) + { + case internal::UTF8_OK: break; - case internal::NOT_ENOUGH_ROOM : + case internal::NOT_ENOUGH_ROOM: throw not_enough_room(); - case internal::INVALID_LEAD : - case internal::INCOMPLETE_SEQUENCE : - case internal::OVERLONG_SEQUENCE : - throw invalid_utf8(*it); - case internal::INVALID_CODE_POINT : + case internal::INVALID_LEAD: + case internal::INCOMPLETE_SEQUENCE: + case internal::OVERLONG_SEQUENCE: + throw invalid_utf8(static_cast(*it)); + case internal::INVALID_CODE_POINT: throw invalid_code_point(cp); } return cp; } - template + template uint32_t peek_next(octet_iterator it, octet_iterator end) { return utf8::next(it, end); } - template + template uint32_t prior(octet_iterator& it, octet_iterator start) { // can't do much if it == start @@ -174,28 +197,27 @@ namespace utf8 return utf8::peek_next(it, end); } - /// Deprecated in versions that include "prior" - template - uint32_t previous(octet_iterator& it, octet_iterator pass_start) - { - octet_iterator end = it; - while (utf8::internal::is_trail(*(--it))) - if (it == pass_start) - throw invalid_utf8(*it); // error - no lead byte in the sequence - octet_iterator temp = it; - return utf8::next(temp, end); - } - - template - void advance (octet_iterator& it, distance_type n, octet_iterator end) + template + void advance(octet_iterator& it, distance_type n, octet_iterator end) { - for (distance_type i = 0; i < n; ++i) - utf8::next(it, end); + const distance_type zero(0); + if (n < zero) + { + // backward + for (distance_type i = n; i < zero; ++i) + utf8::prior(it, end); + } + else + { + // forward + for (distance_type i = zero; i < n; ++i) + utf8::next(it, end); + } } - template - typename std::iterator_traits::difference_type - distance (octet_iterator first, octet_iterator last) + template + typename std::iterator_traits::difference_type distance(octet_iterator first, + octet_iterator last) { typename std::iterator_traits::difference_type dist; for (dist = 0; first < last; ++dist) @@ -203,14 +225,17 @@ namespace utf8 return dist; } - template - octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) + template + octet_iterator utf16to8(u16bit_iterator start, u16bit_iterator end, octet_iterator result) { - while (start != end) { + while (start != end) + { uint32_t cp = utf8::internal::mask16(*start++); // Take care of surrogate pairs first - if (utf8::internal::is_lead_surrogate(cp)) { - if (start != end) { + if (utf8::internal::is_lead_surrogate(cp)) + { + if (start != end) + { uint32_t trail_surrogate = utf8::internal::mask16(*start++); if (utf8::internal::is_trail_surrogate(trail_surrogate)) cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; @@ -219,7 +244,6 @@ namespace utf8 } else throw invalid_utf16(static_cast(cp)); - } // Lone trail surrogate else if (utf8::internal::is_trail_surrogate(cp)) @@ -230,13 +254,15 @@ namespace utf8 return result; } - template - u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) + template + u16bit_iterator utf8to16(octet_iterator start, octet_iterator end, u16bit_iterator result) { - while (start != end) { + while (start < end) + { uint32_t cp = utf8::next(start, end); - if (cp > 0xffff) { //make a surrogate pair - *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); + if (cp > 0xffff) + { // make a surrogate pair + *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); } else @@ -245,8 +271,8 @@ namespace utf8 return result; } - template - octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) + template + octet_iterator utf32to8(u32bit_iterator start, u32bit_iterator end, octet_iterator result) { while (start != end) result = utf8::append(*(start++), result); @@ -254,74 +280,90 @@ namespace utf8 return result; } - template - u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) + template + u32bit_iterator utf8to32(octet_iterator start, octet_iterator end, u32bit_iterator result) { - while (start != end) + while (start < end) (*result++) = utf8::next(start, end); return result; } // The iterator class - template - class iterator : public std::iterator { - octet_iterator it; - octet_iterator range_start; - octet_iterator range_end; + template + class iterator + { + octet_iterator it; + octet_iterator range_start; + octet_iterator range_end; + public: - iterator () {} - explicit iterator (const octet_iterator& octet_it, - const octet_iterator& range_start, - const octet_iterator& range_end) : - it(octet_it), range_start(range_start), range_end(range_end) - { - if (it < range_start || it > range_end) - throw std::out_of_range("Invalid utf-8 iterator position"); - } - // the default "big three" are OK - octet_iterator base () const { return it; } - uint32_t operator * () const - { - octet_iterator temp = it; - return utf8::next(temp, range_end); - } - bool operator == (const iterator& rhs) const - { - if (range_start != rhs.range_start || range_end != rhs.range_end) - throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); - return (it == rhs.it); - } - bool operator != (const iterator& rhs) const - { - return !(operator == (rhs)); - } - iterator& operator ++ () - { - utf8::next(it, range_end); - return *this; - } - iterator operator ++ (int) - { - iterator temp = *this; - utf8::next(it, range_end); - return temp; - } - iterator& operator -- () - { - utf8::prior(it, range_start); - return *this; - } - iterator operator -- (int) - { - iterator temp = *this; - utf8::prior(it, range_start); - return temp; - } + typedef uint32_t value_type; + typedef uint32_t* pointer; + typedef uint32_t& reference; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + iterator() + {} + explicit iterator(const octet_iterator& octet_it, const octet_iterator& rangestart, + const octet_iterator& rangeend) : + it(octet_it), + range_start(rangestart), + range_end(rangeend) + { + if (it < range_start || it > range_end) + throw std::out_of_range("Invalid utf-8 iterator position"); + } + // the default "big three" are OK + octet_iterator base() const + { + return it; + } + uint32_t operator*() const + { + octet_iterator temp = it; + return utf8::next(temp, range_end); + } + bool operator==(const iterator& rhs) const + { + if (range_start != rhs.range_start || range_end != rhs.range_end) + throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); + return (it == rhs.it); + } + bool operator!=(const iterator& rhs) const + { + return !(operator==(rhs)); + } + iterator& operator++() + { + utf8::next(it, range_end); + return *this; + } + iterator operator++(int) + { + iterator temp = *this; + utf8::next(it, range_end); + return temp; + } + iterator& operator--() + { + utf8::prior(it, range_start); + return *this; + } + iterator operator--(int) + { + iterator temp = *this; + utf8::prior(it, range_start); + return temp; + } }; // class iterator } // namespace utf8 -#endif //header guard - +#if UTF_CPP_CPLUSPLUS >= 201703L // C++ 17 or later + #include "cpp17.h" +#elif UTF_CPP_CPLUSPLUS >= 201103L // C++ 11 or later + #include "cpp11.h" +#endif // C++ 11 or later +#endif // header guard diff --git a/libraries/utf8/utf8/core.h b/libraries/utf8/utf8/core.h index c540dfa8c..69a8c4fc3 100644 --- a/libraries/utf8/utf8/core.h +++ b/libraries/utf8/utf8/core.h @@ -24,271 +24,372 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include +// Determine the C++ standard version. +// If the user defines UTF_CPP_CPLUSPLUS, use that. +// Otherwise, trust the unreliable predefined macro __cplusplus + +#if !defined UTF_CPP_CPLUSPLUS + #define UTF_CPP_CPLUSPLUS __cplusplus +#endif + +#if UTF_CPP_CPLUSPLUS >= 201103L // C++ 11 or later + #define UTF_CPP_OVERRIDE override + #define UTF_CPP_NOEXCEPT noexcept +#else // C++ 98/03 + #define UTF_CPP_OVERRIDE + #define UTF_CPP_NOEXCEPT throw() +#endif // C++ 11 or later + namespace utf8 { // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers // You may need to change them to match your system. // These typedefs have the same names as ones from cstdint, or boost/cstdint - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; - -// Helper code - not intended to be directly called by the library users. May be changed at any time -namespace internal -{ - // Unicode constants - // Leading (high) surrogates: 0xd800 - 0xdbff - // Trailing (low) surrogates: 0xdc00 - 0xdfff - const uint16_t LEAD_SURROGATE_MIN = 0xd800u; - const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; - const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; - const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; - const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); - const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; - - // Maximum valid value for a Unicode code point - const uint32_t CODE_POINT_MAX = 0x0010ffffu; - - template - inline uint8_t mask8(octet_type oc) - { - return static_cast(0xff & oc); - } - template - inline uint16_t mask16(u16_type oc) - { - return static_cast(0xffff & oc); - } - template - inline bool is_trail(octet_type oc) - { - return ((utf8::internal::mask8(oc) >> 6) == 0x2); - } + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; - template - inline bool is_lead_surrogate(u16 cp) + // Helper code - not intended to be directly called by the library users. May be changed at any + // time + namespace internal { - return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX); - } + // Unicode constants + // Leading (high) surrogates: 0xd800 - 0xdbff + // Trailing (low) surrogates: 0xdc00 - 0xdfff + const uint16_t LEAD_SURROGATE_MIN = 0xd800u; + const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; + const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; + const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; + const uint16_t LEAD_OFFSET = 0xd7c0u; // LEAD_SURROGATE_MIN - (0x10000 >> 10) + const uint32_t SURROGATE_OFFSET = + 0xfca02400u; // 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN + + // Maximum valid value for a Unicode code point + const uint32_t CODE_POINT_MAX = 0x0010ffffu; + + template + inline uint8_t mask8(octet_type oc) + { + return static_cast(0xff & oc); + } + template + inline uint16_t mask16(u16_type oc) + { + return static_cast(0xffff & oc); + } + template + inline bool is_trail(octet_type oc) + { + return ((utf8::internal::mask8(oc) >> 6) == 0x2); + } - template - inline bool is_trail_surrogate(u16 cp) - { - return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); - } + template + inline bool is_lead_surrogate(u16 cp) + { + return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX); + } - template - inline bool is_surrogate(u16 cp) - { - return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); - } + template + inline bool is_trail_surrogate(u16 cp) + { + return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + } - template - inline bool is_code_point_valid(u32 cp) - { - return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp)); - } + template + inline bool is_surrogate(u16 cp) + { + return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + } - template - inline typename std::iterator_traits::difference_type - sequence_length(octet_iterator lead_it) - { - uint8_t lead = utf8::internal::mask8(*lead_it); - if (lead < 0x80) - return 1; - else if ((lead >> 5) == 0x6) - return 2; - else if ((lead >> 4) == 0xe) - return 3; - else if ((lead >> 3) == 0x1e) - return 4; - else - return 0; - } + template + inline bool is_code_point_valid(u32 cp) + { + return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp)); + } - template - inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length) - { - if (cp < 0x80) { - if (length != 1) - return true; + template + inline typename std::iterator_traits::difference_type sequence_length( + octet_iterator lead_it) + { + uint8_t lead = utf8::internal::mask8(*lead_it); + if (lead < 0x80) + return 1; + else if ((lead >> 5) == 0x6) + return 2; + else if ((lead >> 4) == 0xe) + return 3; + else if ((lead >> 3) == 0x1e) + return 4; + else + return 0; } - else if (cp < 0x800) { - if (length != 2) - return true; + + template + inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length) + { + if (cp < 0x80) + { + if (length != 1) + return true; + } + else if (cp < 0x800) + { + if (length != 2) + return true; + } + else if (cp < 0x10000) + { + if (length != 3) + return true; + } + + return false; } - else if (cp < 0x10000) { - if (length != 3) - return true; + + enum utf_error + { + UTF8_OK, + NOT_ENOUGH_ROOM, + INVALID_LEAD, + INCOMPLETE_SEQUENCE, + OVERLONG_SEQUENCE, + INVALID_CODE_POINT + }; + + /// Helper for get_sequence_x + template + utf_error increase_safely(octet_iterator& it, octet_iterator end) + { + if (++it == end) + return NOT_ENOUGH_ROOM; + + if (!utf8::internal::is_trail(*it)) + return INCOMPLETE_SEQUENCE; + + return UTF8_OK; } - return false; +#define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) \ + { \ + utf_error ret = increase_safely(IT, END); \ + if (ret != UTF8_OK) \ + return ret; \ } - enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT}; + /// get_sequence_x functions decode utf-8 sequences of the length x + template + utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; - /// Helper for get_sequence_x - template - utf_error increase_safely(octet_iterator& it, octet_iterator end) - { - if (++it == end) - return NOT_ENOUGH_ROOM; + code_point = utf8::internal::mask8(*it); - if (!utf8::internal::is_trail(*it)) - return INCOMPLETE_SEQUENCE; - - return UTF8_OK; - } + return UTF8_OK; + } - #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} + template + utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; - /// get_sequence_x functions decode utf-8 sequences of the length x - template - utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - if (it == end) - return NOT_ENOUGH_ROOM; + code_point = utf8::internal::mask8(*it); - code_point = utf8::internal::mask8(*it); + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - return UTF8_OK; - } + code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f); - template - utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - if (it == end) - return NOT_ENOUGH_ROOM; - - code_point = utf8::internal::mask8(*it); + return UTF8_OK; + } - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + template + utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; - code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f); + code_point = utf8::internal::mask8(*it); - return UTF8_OK; - } + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - template - utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - if (it == end) - return NOT_ENOUGH_ROOM; - - code_point = utf8::internal::mask8(*it); + code_point = + ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); + code_point += (*it) & 0x3f; - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + return UTF8_OK; + } - code_point += (*it) & 0x3f; + template + utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; - return UTF8_OK; - } + code_point = utf8::internal::mask8(*it); - template - utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - if (it == end) - return NOT_ENOUGH_ROOM; + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - code_point = utf8::internal::mask8(*it); + code_point = + ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); + code_point += (utf8::internal::mask8(*it) << 6) & 0xfff; - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - code_point += (utf8::internal::mask8(*it) << 6) & 0xfff; + code_point += (*it) & 0x3f; - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + return UTF8_OK; + } - code_point += (*it) & 0x3f; +#undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR + + template + utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + // Save the original value of it so we can go back in case of failure + // Of course, it does not make much sense with i.e. stream iterators + octet_iterator original_it = it; + + uint32_t cp = 0; + // Determine the sequence length based on the lead octet + typedef typename std::iterator_traits::difference_type + octet_difference_type; + const octet_difference_type length = utf8::internal::sequence_length(it); + + // Get trail octets and calculate the code point + utf_error err = UTF8_OK; + switch (length) + { + case 0: + return INVALID_LEAD; + case 1: + err = utf8::internal::get_sequence_1(it, end, cp); + break; + case 2: + err = utf8::internal::get_sequence_2(it, end, cp); + break; + case 3: + err = utf8::internal::get_sequence_3(it, end, cp); + break; + case 4: + err = utf8::internal::get_sequence_4(it, end, cp); + break; + } - return UTF8_OK; - } + if (err == UTF8_OK) + { + // Decoding succeeded. Now, security checks... + if (utf8::internal::is_code_point_valid(cp)) + { + if (!utf8::internal::is_overlong_sequence(cp, length)) + { + // Passed! Return here. + code_point = cp; + ++it; + return UTF8_OK; + } + else + err = OVERLONG_SEQUENCE; + } + else + err = INVALID_CODE_POINT; + } - #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR + // Failure branch - restore the original value of the iterator + it = original_it; + return err; + } - template - utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - // Save the original value of it so we can go back in case of failure - // Of course, it does not make much sense with i.e. stream iterators - octet_iterator original_it = it; - - uint32_t cp = 0; - // Determine the sequence length based on the lead octet - typedef typename std::iterator_traits::difference_type octet_difference_type; - const octet_difference_type length = utf8::internal::sequence_length(it); - - // Get trail octets and calculate the code point - utf_error err = UTF8_OK; - switch (length) { - case 0: - return INVALID_LEAD; - case 1: - err = utf8::internal::get_sequence_1(it, end, cp); - break; - case 2: - err = utf8::internal::get_sequence_2(it, end, cp); - break; - case 3: - err = utf8::internal::get_sequence_3(it, end, cp); - break; - case 4: - err = utf8::internal::get_sequence_4(it, end, cp); - break; + template + inline utf_error validate_next(octet_iterator& it, octet_iterator end) + { + uint32_t ignored; + return utf8::internal::validate_next(it, end, ignored); } - if (err == UTF8_OK) { - // Decoding succeeded. Now, security checks... - if (utf8::internal::is_code_point_valid(cp)) { - if (!utf8::internal::is_overlong_sequence(cp, length)){ - // Passed! Return here. - code_point = cp; - ++it; - return UTF8_OK; - } - else - err = OVERLONG_SEQUENCE; + // Internal implementation of both checked and unchecked append() function + // This function will be invoked by the overloads below, as they will know + // the octet_type. + template + octet_iterator append(uint32_t cp, octet_iterator result) + { + if (cp < 0x80) // one octet + *(result++) = static_cast(cp); + else if (cp < 0x800) + { // two octets + *(result++) = static_cast((cp >> 6) | 0xc0); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + else if (cp < 0x10000) + { // three octets + *(result++) = static_cast((cp >> 12) | 0xe0); + *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast((cp & 0x3f) | 0x80); } - else - err = INVALID_CODE_POINT; + else + { // four octets + *(result++) = static_cast((cp >> 18) | 0xf0); + *(result++) = static_cast(((cp >> 12) & 0x3f) | 0x80); + *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + return result; } - // Failure branch - restore the original value of the iterator - it = original_it; - return err; - } + // One of the following overloads will be invoked from the API calls - template - inline utf_error validate_next(octet_iterator& it, octet_iterator end) { - uint32_t ignored; - return utf8::internal::validate_next(it, end, ignored); - } + // A simple (but dangerous) case: the caller appends byte(s) to a char array + inline char* append(uint32_t cp, char* result) + { + return append(cp, result); + } -} // namespace internal + // Hopefully, most common case: the caller uses back_inserter + // i.e. append(cp, std::back_inserter(str)); + template + std::back_insert_iterator append( + uint32_t cp, std::back_insert_iterator result) + { + return append, + typename container_type::value_type>(cp, result); + } + + // The caller uses some other kind of output operator - not covered above + // Note that in this case we are not able to determine octet_type + // so we assume it's uint_8; that can cause a conversion warning if we are wrong. + template + octet_iterator append(uint32_t cp, octet_iterator result) + { + return append(cp, result); + } + + } // namespace internal /// The library API - functions intended to be called by the users // Byte order mark - const uint8_t bom[] = {0xef, 0xbb, 0xbf}; + const uint8_t bom[] = { 0xef, 0xbb, 0xbf }; - template + template octet_iterator find_invalid(octet_iterator start, octet_iterator end) { octet_iterator result = start; - while (result != end) { + while (result != end) + { utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end); if (err_code != internal::UTF8_OK) return result; @@ -296,34 +397,19 @@ namespace internal return result; } - template + template inline bool is_valid(octet_iterator start, octet_iterator end) { return (utf8::find_invalid(start, end) == end); } - template - inline bool starts_with_bom (octet_iterator it, octet_iterator end) + template + inline bool starts_with_bom(octet_iterator it, octet_iterator end) { - return ( - ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) && - ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) && - ((it != end) && (utf8::internal::mask8(*it)) == bom[2]) - ); - } - - //Deprecated in release 2.3 - template - inline bool is_bom (octet_iterator it) - { - return ( - (utf8::internal::mask8(*it++)) == bom[0] && - (utf8::internal::mask8(*it++)) == bom[1] && - (utf8::internal::mask8(*it)) == bom[2] - ); + return (((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) && + ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) && + ((it != end) && (utf8::internal::mask8(*it)) == bom[2])); } } // namespace utf8 #endif // header guard - - diff --git a/libraries/utf8/utf8/cpp17.h b/libraries/utf8/utf8/cpp17.h new file mode 100644 index 000000000..bcdeb86e4 --- /dev/null +++ b/libraries/utf8/utf8/cpp17.h @@ -0,0 +1,102 @@ +// Copyright 2018 Nemanja Trifunovic + +/* +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. +*/ + +#ifndef UTF8_FOR_CPP_7e906c01_03a3_4daf_b420_ea7ea952b3c9 +#define UTF8_FOR_CPP_7e906c01_03a3_4daf_b420_ea7ea952b3c9 + +#include "checked.h" +#include + +namespace utf8 +{ + + inline void append(char32_t cp, std::string& s) + { + append(uint32_t(cp), std::back_inserter(s)); + } + + inline std::string utf16to8(std::u16string_view s) + { + std::string result; + utf16to8(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline std::u16string utf8to16(std::string_view s) + { + std::u16string result; + utf8to16(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline std::string utf32to8(std::u32string_view s) + { + std::string result; + utf32to8(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline std::u32string utf8to32(std::string_view s) + { + std::u32string result; + utf8to32(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline std::size_t find_invalid(std::string_view s) + { + std::string_view::const_iterator invalid = find_invalid(s.begin(), s.end()); + return (invalid == s.end()) ? std::string_view::npos + : static_cast(invalid - s.begin()); + } + + inline bool is_valid(std::string_view s) + { + return is_valid(s.begin(), s.end()); + } + + inline std::string replace_invalid(std::string_view s, char32_t replacement) + { + std::string result; + replace_invalid(s.begin(), s.end(), std::back_inserter(result), replacement); + return result; + } + + inline std::string replace_invalid(std::string_view s) + { + std::string result; + replace_invalid(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline bool starts_with_bom(std::string_view s) + { + return starts_with_bom(s.begin(), s.end()); + } + +} // namespace utf8 + +#endif // header guard diff --git a/libraries/utf8/utf8/unchecked.h b/libraries/utf8/utf8/unchecked.h index 8bf381cbd..c3bd2cea0 100644 --- a/libraries/utf8/utf8/unchecked.h +++ b/libraries/utf8/utf8/unchecked.h @@ -24,7 +24,6 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 @@ -32,37 +31,66 @@ DEALINGS IN THE SOFTWARE. namespace utf8 { - namespace unchecked + namespace unchecked { - template + template octet_iterator append(uint32_t cp, octet_iterator result) { - if (cp < 0x80) // one octet - *(result++) = static_cast(cp); - else if (cp < 0x800) { // two octets - *(result++) = static_cast((cp >> 6) | 0xc0); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - else if (cp < 0x10000) { // three octets - *(result++) = static_cast((cp >> 12) | 0xe0); - *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - else { // four octets - *(result++) = static_cast((cp >> 18) | 0xf0); - *(result++) = static_cast(((cp >> 12) & 0x3f)| 0x80); - *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); - *(result++) = static_cast((cp & 0x3f) | 0x80); + return internal::append(cp, result); + } + + template + output_iterator replace_invalid(octet_iterator start, octet_iterator end, + output_iterator out, uint32_t replacement) + { + while (start != end) + { + octet_iterator sequence_start = start; + internal::utf_error err_code = utf8::internal::validate_next(start, end); + switch (err_code) + { + case internal::UTF8_OK: + for (octet_iterator it = sequence_start; it != start; ++it) + *out++ = *it; + break; + case internal::NOT_ENOUGH_ROOM: + out = utf8::unchecked::append(replacement, out); + start = end; + break; + case internal::INVALID_LEAD: + out = utf8::unchecked::append(replacement, out); + ++start; + break; + case internal::INCOMPLETE_SEQUENCE: + case internal::OVERLONG_SEQUENCE: + case internal::INVALID_CODE_POINT: + out = utf8::unchecked::append(replacement, out); + ++start; + // just one replacement mark for the sequence + while (start != end && utf8::internal::is_trail(*start)) + ++start; + break; + } } - return result; + return out; } - template + template + inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, + output_iterator out) + { + static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); + return utf8::unchecked::replace_invalid(start, end, out, replacement_marker); + } + + template uint32_t next(octet_iterator& it) { uint32_t cp = utf8::internal::mask8(*it); - typename std::iterator_traits::difference_type length = utf8::internal::sequence_length(it); - switch (length) { + typename std::iterator_traits::difference_type length = + utf8::internal::sequence_length(it); + switch (length) + { case 1: break; case 2: @@ -70,84 +98,93 @@ namespace utf8 cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); break; case 3: - ++it; + ++it; cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); ++it; cp += (*it) & 0x3f; break; case 4: ++it; - cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); + cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); ++it; cp += (utf8::internal::mask8(*it) << 6) & 0xfff; ++it; - cp += (*it) & 0x3f; + cp += (*it) & 0x3f; break; } ++it; - return cp; + return cp; } - template + template uint32_t peek_next(octet_iterator it) { - return utf8::unchecked::next(it); + return utf8::unchecked::next(it); } - template + template uint32_t prior(octet_iterator& it) { - while (utf8::internal::is_trail(*(--it))) ; + while (utf8::internal::is_trail(*(--it))) + ; octet_iterator temp = it; return utf8::unchecked::next(temp); } - // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous) - template - inline uint32_t previous(octet_iterator& it) - { - return utf8::unchecked::prior(it); - } - - template - void advance (octet_iterator& it, distance_type n) + template + void advance(octet_iterator& it, distance_type n) { - for (distance_type i = 0; i < n; ++i) - utf8::unchecked::next(it); + const distance_type zero(0); + if (n < zero) + { + // backward + for (distance_type i = n; i < zero; ++i) + utf8::unchecked::prior(it); + } + else + { + // forward + for (distance_type i = zero; i < n; ++i) + utf8::unchecked::next(it); + } } - template - typename std::iterator_traits::difference_type - distance (octet_iterator first, octet_iterator last) + template + typename std::iterator_traits::difference_type distance( + octet_iterator first, octet_iterator last) { typename std::iterator_traits::difference_type dist; - for (dist = 0; first < last; ++dist) + for (dist = 0; first < last; ++dist) utf8::unchecked::next(first); return dist; } - template - octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) - { - while (start != end) { + template + octet_iterator utf16to8(u16bit_iterator start, u16bit_iterator end, octet_iterator result) + { + while (start != end) + { uint32_t cp = utf8::internal::mask16(*start++); - // Take care of surrogate pairs first - if (utf8::internal::is_lead_surrogate(cp)) { + // Take care of surrogate pairs first + if (utf8::internal::is_lead_surrogate(cp)) + { uint32_t trail_surrogate = utf8::internal::mask16(*start++); cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; } result = utf8::unchecked::append(cp, result); } - return result; + return result; } - template - u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) + template + u16bit_iterator utf8to16(octet_iterator start, octet_iterator end, u16bit_iterator result) { - while (start < end) { + while (start < end) + { uint32_t cp = utf8::unchecked::next(start); - if (cp > 0xffff) { //make a surrogate pair - *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); + if (cp > 0xffff) + { // make a surrogate pair + *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); } else @@ -156,8 +193,8 @@ namespace utf8 return result; } - template - octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) + template + octet_iterator utf32to8(u32bit_iterator start, u32bit_iterator end, octet_iterator result) { while (start != end) result = utf8::unchecked::append(*(start++), result); @@ -165,8 +202,8 @@ namespace utf8 return result; } - template - u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) + template + u32bit_iterator utf8to32(octet_iterator start, octet_iterator end, u32bit_iterator result) { while (start < end) (*result++) = utf8::unchecked::next(start); @@ -175,54 +212,64 @@ namespace utf8 } // The iterator class - template - class iterator : public std::iterator { + template + class iterator + { octet_iterator it; - public: - iterator () {} - explicit iterator (const octet_iterator& octet_it): it(octet_it) {} + + public: + typedef uint32_t value_type; + typedef uint32_t* pointer; + typedef uint32_t& reference; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + iterator() + {} + explicit iterator(const octet_iterator& octet_it) : it(octet_it) + {} // the default "big three" are OK - octet_iterator base () const { return it; } - uint32_t operator * () const + octet_iterator base() const + { + return it; + } + uint32_t operator*() const { octet_iterator temp = it; return utf8::unchecked::next(temp); } - bool operator == (const iterator& rhs) const - { + bool operator==(const iterator& rhs) const + { return (it == rhs.it); } - bool operator != (const iterator& rhs) const + bool operator!=(const iterator& rhs) const { - return !(operator == (rhs)); + return !(operator==(rhs)); } - iterator& operator ++ () + iterator& operator++() { ::std::advance(it, utf8::internal::sequence_length(it)); return *this; } - iterator operator ++ (int) + iterator operator++(int) { iterator temp = *this; ::std::advance(it, utf8::internal::sequence_length(it)); return temp; - } - iterator& operator -- () + } + iterator& operator--() { utf8::unchecked::prior(it); return *this; } - iterator operator -- (int) + iterator operator--(int) { iterator temp = *this; utf8::unchecked::prior(it); return temp; } - }; // class iterator - - } // namespace utf8::unchecked -} // namespace utf8 + }; // class iterator + } // namespace unchecked +} // namespace utf8 #endif // header guard - diff --git a/libraries/libwuff/wuff.c b/libraries/wuff/wuff.c similarity index 100% rename from libraries/libwuff/wuff.c rename to libraries/wuff/wuff.c diff --git a/libraries/libwuff/wuff.h b/libraries/wuff/wuff.h similarity index 100% rename from libraries/libwuff/wuff.h rename to libraries/wuff/wuff.h diff --git a/libraries/libwuff/wuff_config.h b/libraries/wuff/wuff_config.h similarity index 100% rename from libraries/libwuff/wuff_config.h rename to libraries/wuff/wuff_config.h diff --git a/libraries/libwuff/wuff_convert.c b/libraries/wuff/wuff_convert.c similarity index 100% rename from libraries/libwuff/wuff_convert.c rename to libraries/wuff/wuff_convert.c diff --git a/libraries/libwuff/wuff_convert.h b/libraries/wuff/wuff_convert.h similarity index 100% rename from libraries/libwuff/wuff_convert.h rename to libraries/wuff/wuff_convert.h diff --git a/libraries/libwuff/wuff_internal.c b/libraries/wuff/wuff_internal.c similarity index 100% rename from libraries/libwuff/wuff_internal.c rename to libraries/wuff/wuff_internal.c diff --git a/libraries/libwuff/wuff_internal.h b/libraries/wuff/wuff_internal.h similarity index 100% rename from libraries/libwuff/wuff_internal.h rename to libraries/wuff/wuff_internal.h diff --git a/libraries/libwuff/wuff_memory.c b/libraries/wuff/wuff_memory.c similarity index 100% rename from libraries/libwuff/wuff_memory.c rename to libraries/wuff/wuff_memory.c diff --git a/lovepotion.code-workspace b/lovepotion.code-workspace deleted file mode 100644 index 65b907981..000000000 --- a/lovepotion.code-workspace +++ /dev/null @@ -1,78 +0,0 @@ -{ - "folders": [ - { - "path": "." - } - ], - "settings": { - "files.associations": { - "*.yue": "moonscript", - "array": "cpp", - "atomic": "cpp", - "bit": "cpp", - "*.tcc": "cpp", - "cctype": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "list": "cpp", - "map": "cpp", - "set": "cpp", - "unordered_map": "cpp", - "vector": "cpp", - "exception": "cpp", - "algorithm": "cpp", - "functional": "cpp", - "iterator": "cpp", - "memory": "cpp", - "memory_resource": "cpp", - "numeric": "cpp", - "optional": "cpp", - "random": "cpp", - "string": "cpp", - "string_view": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "utility": "cpp", - "fstream": "cpp", - "initializer_list": "cpp", - "iosfwd": "cpp", - "istream": "cpp", - "limits": "cpp", - "new": "cpp", - "ostream": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "cinttypes": "cpp", - "typeinfo": "cpp", - "variant": "cpp", - "bitset": "cpp", - "chrono": "cpp", - "codecvt": "cpp", - "ctime": "cpp", - "ratio": "cpp", - "iomanip": "cpp", - "iostream": "cpp", - "compare": "cpp", - "concepts": "cpp", - "ranges": "cpp", - "numbers": "cpp", - "span": "cpp", - "*.lua": "cpp" - }, - "terminal.integrated.defaultProfile.windows": "msys2", - "yaml.schemas": { - "https://json.schemastore.org/github-workflow.json": "file:///g%3A/GitHub/C%2B%2B/lovepotion/.github/workflows/build.yml" - } - } -} diff --git a/pkglist.txt b/pkglist.txt deleted file mode 100644 index e94768c44..000000000 --- a/pkglist.txt +++ /dev/null @@ -1,49 +0,0 @@ -3ds-examples -3ds-libvorbisidec -3ds-box2d -3ds-curl -3ds-flac -3ds-lz4 -3ds-mpg123 -3ds-physfs -3ds-pkg-config -3ds-libmodplug -3ds-libjpeg-turbo -3ds-libpng -3ds-libtheora -3ds-mbedtls -3ds-zlib -3dslink -3dstools -citro2d -citro3d -deko3d -devkitA64 -devkitARM -devkitarm-crtls -devkitarm-rules -devkitpro-pkgbuild-helpers -general-tools -libctru -libnx -picasso -switch-curl -switch-examples -switch-flac -switch-libvorbisidec -switch-lz4 -switch-mpg123 -switch-physfs -switch-pkg-config -switch-libmodplug -switch-libjpeg-turbo -switch-libtheora -switch-bzip2 -switch-libpng -switch-glm -switch-freetype -switch-tools -switch-box2d -switch-zlib -tex3ds -uam diff --git a/platform/3ds/Makefile b/platform/3ds/Makefile deleted file mode 100644 index dac4ecf60..000000000 --- a/platform/3ds/Makefile +++ /dev/null @@ -1,274 +0,0 @@ -#--------------------------------------------------------------------------------- -.SUFFIXES: -#--------------------------------------------------------------------------------- - -ifeq ($(strip $(DEVKITARM)),) -$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") -endif - -TOPDIR ?= $(CURDIR) -include $(DEVKITARM)/3ds_rules - -#--------------------------------------------------------------------------------- -# TARGET is the name of the output -# BUILD is the directory where object files & intermediate files will be placed -# SOURCES is a list of directories containing source code -# DATA is a list of directories containing data files -# INCLUDES is a list of directories containing header files -# GRAPHICS is a list of directories containing graphics files -# GFXBUILD is the directory where converted graphics files will be placed -# If set to $(BUILD), it will statically link in the converted -# files as if they were data files. -# -# NO_SMDH: if set to anything, no SMDH file is generated. -# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) -# APP_TITLE is the name of the app stored in the SMDH file (Optional) -# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) -# APP_AUTHOR is the author of the app stored in the SMDH file (Optional) -# ICON is the filename of the icon (.png), relative to the project folder. -# If not set, it attempts to use one of the following (in this order): -# - .png -# - icon.png -# - /default_icon.png -#--------------------------------------------------------------------------------- -TARGET := LOVEPotion -BUILD := build - -CONSOLE_INCLUDE := include $(foreach d, $(wildcard include/*), $(if $(wildcard $d/.), $(call DIR_WILDCARD, $d) $d,)) -CONSOLE_SOURCES := source $(foreach d, $(wildcard source/*), $(if $(wildcard $d/.), $(call DIR_WILDCARD, $d) $d,)) - -SOURCES ?= ${LOVE_SOURCES} \ - ${LOVE_LIBRARIES} \ - ${CONSOLE_SOURCES} - -DATA := ${LOVE_DATA_FILES} \ - source/scripts - -INCLUDES ?= ${LOVE_INCLUDES} \ - ${LOVE_LIBRARIES} \ - ${LOVE_MAIN_DATA_FILES} \ - ${CONSOLE_INCLUDE} - -GRAPHICS := graphics -GFXBUILD := $(BUILD) -ROMFS := romfs -GFXBUILD := $(ROMFS)/$(GRAPHICS) - -APP_DESCRIPTION := LÖVE for 3DS • $(APP_VERSION) - - -#--------------------------------------------------------------------------------- -# options for code generation -#--------------------------------------------------------------------------------- -ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft - -CFLAGS := -g -Wall -mword-relocations \ - -fomit-frame-pointer -ffunction-sections $(ARCH) - -CFLAGS += $(INCLUDE) -D__3DS__ $(DEFINES) -CXXFLAGS := $(CFLAGS) -Wno-psabi -fno-rtti -fexceptions -std=gnu++20 - -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) - -LIBS := ${LOVE_PORTLIBS} `$(PREFIX)pkg-config libmpg123 --libs` - -ifeq ($(strip $(DEBUG)),) - CFLAGS += -O2 - LIBS += -lcitro2d -lcitro3d -lctru - ICON := icon.png -else - CFLAGS += -Og - LIBS += -lcitro2dd -lcitro3dd -lctrud - ICON := icon-dev.png -endif - -#--------------------------------------------------------------------------------- -# list of directories containing libraries, this must be the top level containing -# include and lib -#--------------------------------------------------------------------------------- -LIBDIRS := $(PORTLIBS) $(CTRULIB) - - -#--------------------------------------------------------------------------------- -# no real need to edit anything past this point unless you need to add additional -# rules for different file extensions -#--------------------------------------------------------------------------------- -ifneq ($(BUILD),$(notdir $(CURDIR))) -#--------------------------------------------------------------------------------- - -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) - -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) - -export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) -CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) -SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) -PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) -SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) -GFXFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) -BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) - -#--------------------------------------------------------------------------------- -# use CXX for linking C++ projects, CC for standard C -#--------------------------------------------------------------------------------- -ifeq ($(strip $(CPPFILES)),) -#--------------------------------------------------------------------------------- - export LD := $(CC) -#--------------------------------------------------------------------------------- -else -#--------------------------------------------------------------------------------- - export LD := $(CXX) -#--------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------- - -#--------------------------------------------------------------------------------- -ifeq ($(GFXBUILD),$(BUILD)) -#--------------------------------------------------------------------------------- -export T3XFILES := $(GFXFILES:.png=.t3x) -#--------------------------------------------------------------------------------- -else -#--------------------------------------------------------------------------------- -export ROMFS_T3XFILES := $(patsubst %.png, $(GFXBUILD)/%.t3x, $(GFXFILES)) -# export T3XHFILES := $(patsubst %.t3s, $(BUILD)/%.h, $(GFXFILES)) -#--------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------- - -export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) - -export OFILES_BIN := $(addsuffix .o,$(BINFILES)) \ - $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) - -export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) - -export HFILES := $(PICAFILES:.v.pica=_shbin.h) $(SHLISTFILES:.shlist=_shbin.h) \ - $(addsuffix .h,$(subst .,_,$(BINFILES))) - -export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ - $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ - -I$(CURDIR)/$(BUILD) - -export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) - -export _3DSXDEPS := $(if $(NO_SMDH),,$(OUTPUT).smdh) - -ifeq ($(strip $(ICON)),) - icons := $(wildcard *.png) - ifneq (,$(findstring $(TARGET).png,$(icons))) - export APP_ICON := $(TOPDIR)/$(TARGET).png - else - ifneq (,$(findstring icon.png,$(icons))) - export APP_ICON := $(TOPDIR)/icon.png - endif - endif -else - export APP_ICON := $(TOPDIR)/$(ICON) -endif - -ifeq ($(strip $(NO_SMDH)),) - export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh -endif - -ifneq ($(ROMFS),) - export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) -endif - -.PHONY: all clean - -#--------------------------------------------------------------------------------- -all: $(BUILD) $(GFXBUILD) $(DEPSDIR) $(ROMFS_T3XFILES) $(T3XHFILES) - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -$(BUILD): - @mkdir -p $@ - -ifneq ($(GFXBUILD),$(BUILD)) -$(GFXBUILD): - @mkdir -p $@ -endif - -ifneq ($(DEPSDIR),$(BUILD)) -$(DEPSDIR): - @mkdir -p $@ -endif - -#--------------------------------------------------------------------------------- -clean: - @echo clean ... - @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf $(GFXBUILD) - -#--------------------------------------------------------------------------------- -$(GFXBUILD)/%.t3x : %.png -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @tex3ds $< -d $(DEPSDIR)/$*.d --format=rgba8888 -z=auto --border=transparent -o $@ - -#--------------------------------------------------------------------------------- -else - -#--------------------------------------------------------------------------------- -# main targets -#--------------------------------------------------------------------------------- -$(OUTPUT).3dsx : $(OUTPUT).elf $(_3DSXDEPS) - -$(OFILES_SOURCES) : $(HFILES) - -$(OUTPUT).elf : $(OFILES) - -#--------------------------------------------------------------------------------- -# you need a rule like this for each extension you use as binary data -#--------------------------------------------------------------------------------- -%.bin.o %_bin.h : %.bin -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) -#--------------------------------------------------------------------------------- -%.lua.o %_lua.h : %.lua -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) -.PRECIOUS : %.t3x -#--------------------------------------------------------------------------------- -%.t3x.o %_t3x.h : %.t3x -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - -#--------------------------------------------------------------------------------- -# rules for assembling GPU shaders -#--------------------------------------------------------------------------------- -define shader-as - $(eval CURBIN := $*.shbin) - $(eval DEPSFILE := $(DEPSDIR)/$*.shbin.d) - echo "$(CURBIN).o: $< $1" > $(DEPSFILE) - echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h - echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h - echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h - picasso -o $(CURBIN) $1 - bin2s $(CURBIN) | $(AS) -o $*.shbin.o -endef - -%.shbin.o %_shbin.h : %.v.pica %.g.pica - @echo $(notdir $^) - @$(call shader-as,$^) - -%.shbin.o %_shbin.h : %.v.pica - @echo $(notdir $<) - @$(call shader-as,$<) - -%.shbin.o %_shbin.h : %.shlist - @echo $(notdir $<) - @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)$(file))) - --include $(DEPSDIR)/*.d - -#--------------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------------- diff --git a/platform/3ds/graphics/messagebox_single_none.png b/platform/3ds/graphics/messagebox_single_none.png deleted file mode 100644 index 4ebc1cbef..000000000 Binary files a/platform/3ds/graphics/messagebox_single_none.png and /dev/null differ diff --git a/platform/3ds/graphics/messagebox_single_pressed.png b/platform/3ds/graphics/messagebox_single_pressed.png deleted file mode 100644 index ee3430440..000000000 Binary files a/platform/3ds/graphics/messagebox_single_pressed.png and /dev/null differ diff --git a/platform/3ds/graphics/messagebox_two_none.png b/platform/3ds/graphics/messagebox_two_none.png deleted file mode 100644 index a3e233aeb..000000000 Binary files a/platform/3ds/graphics/messagebox_two_none.png and /dev/null differ diff --git a/platform/3ds/graphics/messagebox_two_pressed_left.png b/platform/3ds/graphics/messagebox_two_pressed_left.png deleted file mode 100644 index c2401fa37..000000000 Binary files a/platform/3ds/graphics/messagebox_two_pressed_left.png and /dev/null differ diff --git a/platform/3ds/graphics/messagebox_two_pressed_right.png b/platform/3ds/graphics/messagebox_two_pressed_right.png deleted file mode 100644 index 97fc85abb..000000000 Binary files a/platform/3ds/graphics/messagebox_two_pressed_right.png and /dev/null differ diff --git a/platform/3ds/graphics/readme.md b/platform/3ds/graphics/readme.md deleted file mode 100644 index fa14a035d..000000000 --- a/platform/3ds/graphics/readme.md +++ /dev/null @@ -1 +0,0 @@ -These assets were generated via LÖVE using referenced screenshots taken on hardware. diff --git a/platform/3ds/icon-dev.png b/platform/3ds/icon-dev.png deleted file mode 100644 index 7372b3b89..000000000 Binary files a/platform/3ds/icon-dev.png and /dev/null differ diff --git a/platform/3ds/icon.png b/platform/3ds/icon.png deleted file mode 100644 index 14513adbd..000000000 Binary files a/platform/3ds/icon.png and /dev/null differ diff --git a/platform/3ds/include/citro2d/citro.h b/platform/3ds/include/citro2d/citro.h deleted file mode 100644 index 036881337..000000000 --- a/platform/3ds/include/citro2d/citro.h +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once - -#include "common/lmath.h" -#include "objects/canvas/canvas.h" - -#include - -#include "common/pixelformat.h" -#include "graphics/graphics.h" -#include - -class citro2d -{ - private: - citro2d(); - - public: - struct GPUFilter - { - GPU_TEXTURE_FILTER_PARAM min; - GPU_TEXTURE_FILTER_PARAM mag; - - GPU_PROCTEX_FILTER mipMap; - }; - - static citro2d& Instance(); - - ~citro2d(); - - void DestroyFramebuffers(); - - void CreateFramebuffers(); - - void BindFramebuffer(love::Canvas* canvas = nullptr); - - void ClearColor(const Colorf& color); - - void SetScissor(GPU_SCISSORMODE mode, const love::Rect& scissor, bool canvasActive); - - void SetStencil(GPU_TESTFUNC compare, int value); - - void Present(); - - void SetTextureFilter(const love::Texture::Filter& filter); - - void SetTextureFilter(love::Texture* texture, const love::Texture::Filter& filter); - - void SetTextureWrap(const love::Texture::Wrap& wrap); - - void SetTextureWrap(love::Texture* texture, const love::Texture::Wrap& filter); - - template - void ModeChange(const T& func) - { - this->DestroyFramebuffers(); - func(); - this->CreateFramebuffers(); - } - - void SetWideMode(bool enable) - { - this->ModeChange([enable]() { gfxSetWide(enable); }); - } - - void Set3D(bool enable) - { - this->ModeChange([enable]() { gfxSet3D(enable); }); - } - - const bool Get3D() const - { - return gfxIs3D(); - } - - const bool GetWide() const - { - return gfxIsWide(); - } - - void DeferCallToEndOfFrame(std::function&& func) - { - this->deferredFunctions.emplace_back(std::move(func)); - } - - static bool IsSizeValid(int size); - - void SetBlendMode(GPU_BLENDEQUATION func, GPU_BLENDFACTOR srcColor, GPU_BLENDFACTOR srcAlpha, - GPU_BLENDFACTOR dstColor, GPU_BLENDFACTOR dstAlpha); - - void SetColorMask(const love::Graphics::ColorMask& mask); - - static GPU_TEXTURE_WRAP_PARAM GetCitroWrapMode(love::Texture::WrapMode wrap); - - static GPU_TEXTURE_FILTER_PARAM GetCitroFilterMode(love::Texture::FilterMode mode); - - static GPUFilter GetCitroFilterMode(const love::Texture::Filter& mode); - - static bool GetConstant(love::PixelFormat in, GPU_TEXCOLOR& out); - - static bool GetConstant(GPU_TEXCOLOR in, love::PixelFormat& out); - - private: - GPUFilter filter; - - std::vector> deferredFunctions; - std::vector targets; - C3D_RenderTarget* current; - - struct - { - GPU_TEXTURE_WRAP_PARAM s; - GPU_TEXTURE_WRAP_PARAM t; - } wrap; - - bool inFrame = false; - - void EnsureInFrame(); -}; diff --git a/platform/3ds/include/citro2d/graphics.h b/platform/3ds/include/citro2d/graphics.h deleted file mode 100644 index 258062c66..000000000 --- a/platform/3ds/include/citro2d/graphics.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "citro2d/citro.h" -#include "modules/graphics/graphics.h" - -#include - -#define RENDERER_NAME "citro3d" -#define RENDERER_VERSION "1.7.0" -#define RENDERER_VENDOR "devkitPro" -#define RENDERER_DEVICE "DMP PICA200" - -namespace love::citro2d -{ - class Graphics : public love::Graphics - { - public: - Graphics(); - - RendererInfo GetRendererInfo() const override; - - void Clear(std::optional color, std::optional stencil, - std::optional depth) override; - - void Clear(std::vector>& colors, std::optional stencil, - std::optional depth) override; - - void Present() override; - - void SetColor(Colorf color) override; - - Font* NewDefaultFont(int size, const Texture::Filter& filter) override; - - Font* NewFont(Rasterizer* rasterizer, - const Texture::Filter& filter = Texture::defaultFilter) override; - - /* Primitives */ - - void Polygon(DrawMode mode, const Vector2* points, size_t count) override; - - void Polyfill(const Vector2* points, size_t count, u32 color, float depth); - - void Polyline(const Vector2* points, size_t count); - - void Rectangle(DrawMode mode, float x, float y, float width, float height) override; - - void Rectangle(DrawMode mode, float x, float y, float width, float height, float rx, - float ry); - - void Rectangle(DrawMode mode, float x, float y, float width, float height, float rx, - float ry, int points) override - { - this->Rectangle(mode, x, y, width, height, rx, ry); - }; - - void Ellipse(DrawMode mode, float x, float y, float a, float b) override; - - void Ellipse(DrawMode mode, float x, float y, float a, float b, int points) override - { - this->Ellipse(mode, x, y, a, b); - }; - - void Circle(DrawMode mode, float x, float y, float radius) override; - - void Circle(DrawMode mode, float x, float y, float radius, int points) override {}; - - void Arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, float angle1, - float angle2); - - void Arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, float angle1, - float angle2, int points) {}; - - void Points(const Vector2* points, size_t count, const Colorf* colors, - size_t colorCount) override; - - void SetPointSize(float size) override; - - void Line(const Vector2* points, int count) override; - - /* End Primitives */ - - void SetBlendMode(BlendMode mode, BlendAlpha alphaMode) override; - - void SetLineWidth(float width) override; - - void SetDefaultFilter(const Texture::Filter& filter); - - void SetScissor(const Rect& scissor) override; - - void SetScissor() override; - - /* Nintendo 3DS */ - - void Set3D(bool enable); - - const bool Get3D() const; - - void SetWide(bool enable); - - const bool GetWide() const; - /* End Nintendo 3DS */ - - /* Useless */ - - void SetColorMask(ColorMask mask) override; - }; -} // namespace love::citro2d diff --git a/platform/3ds/include/common/debugger.h b/platform/3ds/include/common/debugger.h deleted file mode 100644 index 05916ae7c..000000000 --- a/platform/3ds/include/common/debugger.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "common/debug/debuggerc.h" - -namespace love -{ - class Debugger : public common::Debugger - { - public: - static Debugger& Instance() - { - static Debugger instance; - return instance; - } - - bool Initialize() override; - - ~Debugger(); - - private: - Debugger(); - }; -} // namespace love diff --git a/platform/3ds/include/common/screen.h b/platform/3ds/include/common/screen.h deleted file mode 100644 index d9865a0aa..000000000 --- a/platform/3ds/include/common/screen.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "common/screenc.h" - -namespace love -{ - class Screen : common::Screen - { - public: - static Screen& Instance() - { - static Screen screen; - return screen; - }; - - static constexpr int TOP_WIDTH = 0x190; - static constexpr int BOTTOM_WIDTH = 0x140; - static constexpr int HEIGHT = 0x0F0; - - static constexpr int TOP_WIDE_WIDTH = 0x320; - - /* - ** 3D screens when 3D is enabled - */ - enum class CtrScreen : RenderScreen - { - CTR_SCREEN_LEFT, - CTR_SCREEN_RIGHT, - CTR_SCREEN_BOTTOM, - CTR_SCREEN_MAX_ENUM - }; - - /* - ** 2D screens when 3D is disabled - ** BOTTOM is 0x02 to properly index it in the citro2d class - */ - enum class Ctr2dScreen : RenderScreen - { - CTR_2D_SCREEN_TOP, - CTR_2D_SCREEN_BOTTOM = 0x02, - CTR_2D_SCREEN_MAX_ENUM - }; - - int GetWidth(RenderScreen screen = 0) override; - - int GetHeight() override; - - static bool GetConstant(const char* in, RenderScreen& out); - static bool GetConstant(RenderScreen in, const char*& out); - static std::vector GetConstants(RenderScreen); - }; -} // namespace love diff --git a/platform/3ds/include/driver/audiodrv.h b/platform/3ds/include/driver/audiodrv.h deleted file mode 100644 index 15eeb3324..000000000 --- a/platform/3ds/include/driver/audiodrv.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "driver/audiodrvc.h" -#include <3ds.h> - -namespace love::driver -{ - class Audrv : public common::driver::Audrv - { - public: - static Audrv& Instance() - { - static Audrv instance; - return instance; - } - - ~Audrv(); - - Audrv(const Audrv&) = delete; - - Audrv(Audrv&&) = delete; - - Audrv operator=(const Audrv&) = delete; - - Audrv operator=(Audrv&&) = delete; - - LightEvent& GetEvent(); - - private: - Audrv(); - - LightEvent ndspEvent; - }; -} // namespace love::driver diff --git a/platform/3ds/include/driver/hidrv.h b/platform/3ds/include/driver/hidrv.h deleted file mode 100644 index eb5c5d5df..000000000 --- a/platform/3ds/include/driver/hidrv.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "common/driver/hidrvc.h" - -/* -** HID backend class for Nintendo 3DS -*/ -namespace love::driver -{ - class Hidrv : public common::driver::Hidrv - { - public: - Hidrv(); - - bool Poll(LOVE_Event* event) override; - - private: - circlePosition sticks[2]; - circlePosition oldSticks[2]; - - touchPosition touchState; - touchPosition oldTouchState; - - bool isTouchHeld; - }; -} // namespace love::driver diff --git a/platform/3ds/include/modules/event/event.h b/platform/3ds/include/modules/event/event.h deleted file mode 100644 index 537b1210d..000000000 --- a/platform/3ds/include/modules/event/event.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "modules/event/eventc.h" - -namespace love -{ - class Event : public common::Event - { - public: - Event(); - - ~Event(); - }; -} // namespace love \ No newline at end of file diff --git a/platform/3ds/include/modules/font/fontmodule.h b/platform/3ds/include/modules/font/fontmodule.h deleted file mode 100644 index 139029fa3..000000000 --- a/platform/3ds/include/modules/font/fontmodule.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "modules/font/fontmodulec.h" -#include "objects/bcfntrasterizer/bcfntrasterizer.h" - -namespace love -{ - class FontModule : public common::FontModule - { - public: - virtual ~FontModule() {}; - - Rasterizer* NewRasterizer(FileData* data) override; - - Rasterizer* NewBCFNTRasterizer(Data* data, int size); - - Rasterizer* NewBCFNTRasterizer(int size, common::Font::SystemFontType type = - common::Font::SystemFontType::TYPE_STANDARD); - }; -} // namespace love diff --git a/platform/3ds/include/modules/joystick/joystick.h b/platform/3ds/include/modules/joystick/joystick.h deleted file mode 100644 index 444ecb3b7..000000000 --- a/platform/3ds/include/modules/joystick/joystick.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "modules/joystick/joystickc.h" - -namespace love -{ - using Joystick = common::Joystick; -} // namespace love diff --git a/platform/3ds/include/modules/keyboard/keyboard.h b/platform/3ds/include/modules/keyboard/keyboard.h deleted file mode 100644 index 6da9a3f22..000000000 --- a/platform/3ds/include/modules/keyboard/keyboard.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "modules/keyboard/keyboardc.h" -#include - -namespace love -{ - enum class common::Keyboard::KeyboardType : uint8_t - { - TYPE_NORMAL = SWKBD_TYPE_NORMAL, - TYPE_QWERTY = SWKBD_TYPE_QWERTY, - TYPE_NUMPAD = SWKBD_TYPE_NUMPAD - }; - - class Keyboard : public common::Keyboard - { - public: - static constexpr uint32_t MAX_INPUT_LENGTH = 0x100; - - constexpr uint32_t ENCODING_MULTIPLIER() override - { - return 0x03; - } - - Keyboard(); - - virtual ~Keyboard() - {} - - std::string SetTextInput(const SwkbdOpt& options) override; - - static bool GetConstant(const char* in, KeyboardType& out); - static bool GetConstant(KeyboardType in, const char*& out); - static std::vector GetConstants(KeyboardType); - - private: - SwkbdState keyboard; - }; -} // namespace love diff --git a/platform/3ds/include/modules/system/system.h b/platform/3ds/include/modules/system/system.h deleted file mode 100644 index 949beb02e..000000000 --- a/platform/3ds/include/modules/system/system.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "modules/system/systemc.h" - -#include - -#define USERNAME_LENGTH 0x1C -static std::string THEME_NAME = "light"; - -namespace love -{ - class System : public common::System - { - public: - virtual ~System() - {} - - int GetProcessorCount() override; - - const std::string& GetModel() override; - - const std::string& GetUsername() override; - - PowerState GetPowerInfo(uint8_t& percent) const override; - - NetworkState GetNetworkInfo(uint8_t& signal) const override; - - const std::string& GetSystemTheme() override; - - const std::string& GetPreferredLocales() override; - - const std::string& GetVersion() override; - - const std::string& GetRegion() override; - - const std::string& GetFriendCode() override; - - int GetPlayCoins() const; - - void SetPlayCoins(int); - - /* constants */ - - static constexpr uint8_t MAX_MODELS = 6; - static constexpr uint8_t MAX_LANGUAGES = 12; - static constexpr uint8_t MAX_REGIONS = 7; - - static bool GetConstant(const char* in, CFG_Language& out); - static bool GetConstant(CFG_Language in, const char*& out); - static std::vector GetConstants(CFG_Language); - - static bool GetConstant(const char* in, CFG_SystemModel& out); - static bool GetConstant(CFG_SystemModel in, const char*& out); - static std::vector GetConstants(CFG_SystemModel); - - static bool GetConstant(const char* in, CFG_Region& out); - static bool GetConstant(CFG_Region in, const char*& out); - static std::vector GetConstants(CFG_Region); - - private: - static Handle OpenPlayCoinsFile(); - }; -} // namespace love diff --git a/platform/3ds/include/modules/timer/timer.h b/platform/3ds/include/modules/timer/timer.h deleted file mode 100644 index 160629e32..000000000 --- a/platform/3ds/include/modules/timer/timer.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "modules/timer/timerc.h" -#include <3ds.h> - -namespace love -{ - class Timer : public common::Timer - { - public: - Timer(); - - virtual ~Timer() - {} - }; -} // namespace love \ No newline at end of file diff --git a/platform/3ds/include/modules/window/window.h b/platform/3ds/include/modules/window/window.h deleted file mode 100644 index 18245128d..000000000 --- a/platform/3ds/include/modules/window/window.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "modules/window/windowc.h" - -namespace love -{ - class Window : public common::Window - { - public: - Window(); - - virtual ~Window() - {} - - int GetDisplayCount() override; - }; -} // namespace love diff --git a/platform/3ds/include/objects/bcfntrasterizer/bcfntrasterizer.h b/platform/3ds/include/objects/bcfntrasterizer/bcfntrasterizer.h deleted file mode 100644 index 2db732d6c..000000000 --- a/platform/3ds/include/objects/bcfntrasterizer/bcfntrasterizer.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include "objects/glyphdata/glyphdata.h" -#include "objects/rasterizer/rasterizer.h" - -#include "objects/font/font.h" - -#include - -namespace love -{ - class BCFNTRasterizer : public Rasterizer - { - public: - BCFNTRasterizer(Data* data, int size); - - BCFNTRasterizer(common::Font::SystemFontType type, int size); - - virtual ~BCFNTRasterizer(); - - int GetLineHeight() const override - { - return 1; - } - - float GetScale() const - { - return this->scale; - } - - C2D_Font GetFont() const - { - return this->font; - } - - GlyphData* GetGlyphData(uint32_t glyph) const override; - - int GetGlyphCount() const override; - - bool HasGlyph(uint32_t glyph) const override; - - float GetKerning(uint32_t leftGlyph, uint32_t rightGlyph) const override; - - DataType GetDataType() const override - { - return DATA_BCFNT; - } - - static bool Accepts(Data* data); - - private: - C2D_Font font; - float scale; - - StrongReference data; - - mutable int glyphCount; - - void InitMetrics(int size); - }; -} // namespace love diff --git a/platform/3ds/include/objects/canvas/canvas.h b/platform/3ds/include/objects/canvas/canvas.h deleted file mode 100644 index cb2ae9399..000000000 --- a/platform/3ds/include/objects/canvas/canvas.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "common/colors.h" -#include "objects/canvas/canvasc.h" -#include - -namespace love -{ - class Canvas : public common::Canvas - { - public: - Canvas(const Settings& settings); - - virtual ~Canvas(); - - void Draw(Graphics* gfx, Quad* quad, const Matrix4& localTransform) override; - - void Clear(const Colorf& color); - - C3D_RenderTarget* GetRenderer(); - - private: - C3D_Tex citroTex; - Tex3DS_SubTexture subtex; - C3D_RenderTarget* renderer; - }; -} // namespace love diff --git a/platform/3ds/include/objects/font/font.h b/platform/3ds/include/objects/font/font.h deleted file mode 100644 index 02597a3e3..000000000 --- a/platform/3ds/include/objects/font/font.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include "common/data.h" -#include "objects/font/fontc.h" - -#include -#include - -#include "objects/bcfntrasterizer/bcfntrasterizer.h" - -enum class love::common::Font::SystemFontType : uint8_t -{ - TYPE_STANDARD = CFG_REGION_USA, - TYPE_CHINESE = CFG_REGION_CHN, - TYPE_TAIWANESE = CFG_REGION_TWN, - TYPE_KOREAN = CFG_REGION_KOR, - TYPE_MAX_ENUM -}; - -namespace love -{ - class Font : public love::common::Font - { - public: - static constexpr int FONT_BUFFER_SIZE = 0x200; - - Font(Rasterizer* r, const Texture::Filter& filter); - - virtual ~Font(); - - void Print(Graphics* gfx, const std::vector& text, - const Matrix4& localTransform, const Colorf& color) override; - - void Printf(Graphics* gfx, const std::vector& text, float wrap, - AlignMode align, const Matrix4& localTransform, const Colorf& color) override; - - int GetWidth(uint32_t prevGlyph, uint32_t codepoint) override; - - void GetWrap(const std::vector& text, float wraplimit, - std::vector& lines, std::vector* lineWidths = nullptr); - - using love::common::Font::GetFilter; - - void SetFilter(const Texture::Filter& filter) override; - - float GetKerning(uint32_t leftGlyph, uint32_t rightGlyph) override; - - float GetKerning(const std::string& leftChar, const std::string& rightChar) override; - - float GetDPIScale() const override; - - float GetAscent() const override; - - float GetBaseline() const override; - - float GetDescent() const override; - - bool HasGlyph(uint32_t glyph) const override; - - void SetFallbacks(const std::vector& fallbacks) override; - - using love::common::Font::GetWidth; - - float GetHeight() const override; - - const C2D_Font GetFont(); - - void ClearBuffer(); - - float GetScale() const; - - private: - StrongReference rasterizer; - C2D_TextBuf buffer; - - std::unordered_map glyphWidths; - }; -} // namespace love diff --git a/platform/3ds/include/objects/gamepad/gamepad.h b/platform/3ds/include/objects/gamepad/gamepad.h deleted file mode 100644 index 8dee25b89..000000000 --- a/platform/3ds/include/objects/gamepad/gamepad.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -#include "objects/gamepad/gamepadc.h" - -#include "common/bidirectionalmap.h" - -enum class love::common::Gamepad::GamepadAxis : uint64_t -{ - GAMEPAD_AXIS_LEFTX = KEY_CPAD_LEFT | KEY_CPAD_RIGHT, - GAMEPAD_AXIS_LEFTY = KEY_CPAD_UP | KEY_CPAD_DOWN, - GAMEPAD_AXIS_RIGHTX = KEY_CSTICK_LEFT | KEY_CSTICK_RIGHT, - GAMEPAD_AXIS_RIGHTY = KEY_CSTICK_UP | KEY_CSTICK_DOWN, - GAMEPAD_AXIS_TRIGGERLEFT = KEY_ZL, - GAMEPAD_AXIS_TRIGGERRIGHT = KEY_ZR -}; - -enum class love::common::Gamepad::GamepadButton : uint64_t -{ - GAMEPAD_BUTTON_A = KEY_A, - GAMEPAD_BUTTON_B = KEY_B, - GAMEPAD_BUTTON_X = KEY_X, - GAMEPAD_BUTTON_Y = KEY_Y, - GAMEPAD_BUTTON_BACK = KEY_SELECT, - GAMEPAD_BUTTON_START = KEY_START, - GAMEPAD_BUTTON_LEFT_SHOULDER = KEY_L, - GAMEPAD_BUTTON_RIGHT_SHOULDER = KEY_R, - GAMEPAD_BUTTON_DPAD_UP = KEY_DUP, - GAMEPAD_BUTTON_DPAD_RIGHT = KEY_DRIGHT, - GAMEPAD_BUTTON_DPAD_DOWN = KEY_DDOWN, - GAMEPAD_BUTTON_DPAD_LEFT = KEY_DLEFT -}; - -namespace love -{ - class Gamepad : public common::Gamepad - { - public: - static constexpr float JOYSTICK_MAX = 150.0f; - - Gamepad(size_t id); - - Gamepad(size_t id, size_t index); - - virtual ~Gamepad(); - - bool Open(size_t id) override; - - void Close() override; - - bool IsConnected() const override; - - const char* GetName() const override; - - size_t GetAxisCount() const override; - - size_t GetButtonCount() const override; - - float GetAxis(size_t axis) const override; - - std::vector GetAxes() const override; - - void Update(); - - bool IsDown(size_t index, ButtonMapping& button) override; - - bool IsHeld(size_t index, ButtonMapping& button) const override; - - bool IsUp(size_t index, ButtonMapping& button) override; - - bool IsDown(const std::vector& buttons) const override; - - float GetGamepadAxis(GamepadAxis axis) const override; - - bool IsGamepadDown(const std::vector& buttons) const override; - - bool IsVibrationSupported() override; - - bool SetVibration(float left, float right, float duration = -1.0f) override; - - bool SetVibration() override; - - void GetVibration(float& left, float& right) override; - - static bool GetConstant(const char* in, GamepadAxis& out); - static bool GetConstant(GamepadAxis in, const char*& out); - - static bool GetConstant(const char* in, GamepadButton& out); - static bool GetConstant(GamepadButton in, const char*& out); - - static constexpr uint8_t MAX_BUTTONS = 12; - static constexpr uint8_t MAX_AXES = 6; - - const static auto& GetButtonMapping() - { - return Gamepad::buttons; - } - - struct - { - uint32_t pressed; - uint32_t released; - } buttonStates; - - private: - // clang-format off - static constexpr auto axes = BidirectionalMap<>::Create( - "leftx", Gamepad::GamepadAxis::GAMEPAD_AXIS_LEFTX, - "lefty", Gamepad::GamepadAxis::GAMEPAD_AXIS_LEFTY, - "rightx", Gamepad::GamepadAxis::GAMEPAD_AXIS_RIGHTX, - "righty", Gamepad::GamepadAxis::GAMEPAD_AXIS_RIGHTY, - "triggerleft", Gamepad::GamepadAxis::GAMEPAD_AXIS_TRIGGERLEFT, - "triggerright", Gamepad::GamepadAxis::GAMEPAD_AXIS_TRIGGERRIGHT - ); - - static constexpr auto buttons = BidirectionalMap<>::Create( - "a", Gamepad::GamepadButton::GAMEPAD_BUTTON_A, - "b", Gamepad::GamepadButton::GAMEPAD_BUTTON_B, - "x", Gamepad::GamepadButton::GAMEPAD_BUTTON_X, - "y", Gamepad::GamepadButton::GAMEPAD_BUTTON_Y, - "back", Gamepad::GamepadButton::GAMEPAD_BUTTON_BACK, - "start", Gamepad::GamepadButton::GAMEPAD_BUTTON_START, - "leftshoulder", Gamepad::GamepadButton::GAMEPAD_BUTTON_LEFT_SHOULDER, - "rightshoulder", Gamepad::GamepadButton::GAMEPAD_BUTTON_RIGHT_SHOULDER, - "dpup", Gamepad::GamepadButton::GAMEPAD_BUTTON_DPAD_UP, - "dpdown", Gamepad::GamepadButton::GAMEPAD_BUTTON_DPAD_DOWN, - "dpleft", Gamepad::GamepadButton::GAMEPAD_BUTTON_DPAD_LEFT, - "dpright", Gamepad::GamepadButton::GAMEPAD_BUTTON_DPAD_RIGHT - ); - // clang-format on - }; -} // namespace love diff --git a/platform/3ds/include/objects/glyphdata/glyphdata.h b/platform/3ds/include/objects/glyphdata/glyphdata.h deleted file mode 100644 index 997542d49..000000000 --- a/platform/3ds/include/objects/glyphdata/glyphdata.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "objects/glyphdata/glyphdatac.h" - -namespace love -{ - class GlyphData : public common::GlyphData - { - public: - GlyphData(uint32_t glyph, GlyphMetrics metrics); - - GlyphData(const GlyphData& other); - - GlyphData* Clone() const override; - }; -} // namespace love diff --git a/platform/3ds/include/objects/quad/quad.h b/platform/3ds/include/objects/quad/quad.h deleted file mode 100644 index e30cd1cae..000000000 --- a/platform/3ds/include/objects/quad/quad.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "objects/quad/quadc.h" -#include - -namespace love -{ - class Quad : public common::Quad - { - public: - Quad(const Viewport& viewport, double sw, double sh); - - virtual ~Quad() - {} - - void Refresh(const Viewport& viewport, double sw, double sh) override; - - const Tex3DS_SubTexture& CalculateTex3DSViewport(const Viewport& viewport, - C3D_Tex* texture); - - private: - Tex3DS_SubTexture subTex; - }; -} // namespace love diff --git a/platform/3ds/include/objects/source/source.h b/platform/3ds/include/objects/source/source.h deleted file mode 100644 index cfc867e55..000000000 --- a/platform/3ds/include/objects/source/source.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include "objects/source/sourcec.h" - -namespace love -{ - class Source : public common::Source - { - public: - Source(Pool* pool, SoundData* sound); - - Source(Pool* pool, Decoder* decoder); - - Source(const Source& other); - - virtual ~Source(); - - Source* Clone(); - - void SetLooping(bool should) override; - - bool Update() override; - - bool IsPlaying() const override; - - bool IsFinished() const override; - - void SetVolume(float volume) override; - - void StopAtomic() override; - - protected: - double GetSampleOffset() override; - - void ClearChannel() override; - - private: - ndspWaveBuf sources[Source::MAX_BUFFERS]; - - void Reset() override; - - void InitializeStreamBuffers(Decoder* decoder) override; - - void PrepareAtomic() override; - - int StreamAtomic(size_t which) override; - - bool PlayAtomic() override; - - void PauseAtomic() override; - - void ResumeAtomic() override; - - void FreeBuffer() override; - }; -} // namespace love diff --git a/platform/3ds/include/objects/text/text.h b/platform/3ds/include/objects/text/text.h deleted file mode 100644 index c4e08eb95..000000000 --- a/platform/3ds/include/objects/text/text.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "objects/text/textc.h" - -namespace love -{ - class Text : public common::Text - { - public: - Text(love::Font* font, const std::vector& text = {}); - - virtual ~Text(); - - void SetFont(love::Font* font); - - void Set(const std::vector& text) override; - - void Set(const std::vector& text, float wrap, - Font::AlignMode align) override; - - int Add(const std::vector& text, - const Matrix4& localTransform) override; - - int Addf(const std::vector& text, float wrap, Font::AlignMode align, - const Matrix4& localTransform) override; - - int GetWidth(int index = 0) const override; - - int GetHeight(int index = 0) const override; - - void Draw(Graphics* gfx, const Matrix4& localTransform) override; - - void Clear() override; - - std::string GetString(const std::vector& text); - - private: - std::vector> wrapData; - - C2D_TextBuf buffer; - C2D_Text text; - - std::string textCache; - - float wrap; - Font::AlignMode align; - }; -} // namespace love \ No newline at end of file diff --git a/platform/3ds/include/objects/texture/texture.h b/platform/3ds/include/objects/texture/texture.h deleted file mode 100644 index 126bcd252..000000000 --- a/platform/3ds/include/objects/texture/texture.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "objects/texture/texturec.h" -#include - -namespace love -{ - class Texture : public common::Texture - { - public: - Texture(TextureType type); - - virtual ~Texture() - {} - - const C2D_Image& GetHandle(); - - void Draw(Graphics* gfx, const Matrix4& localTransform) override; - - void Draw(Graphics* gfx, love::Quad* quad, const Matrix4& localTransform) override; - - bool SetWrap(const Wrap& wrap) override; - - void SetFilter(const Filter& filter) override; - - protected: - C2D_Image texture; - C2D_SpriteSheet sheet; - }; -} // namespace love diff --git a/platform/3ds/include/objects/thread/thread.h b/platform/3ds/include/objects/thread/thread.h deleted file mode 100644 index 4f8a0aa38..000000000 --- a/platform/3ds/include/objects/thread/thread.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "thread/threadc.h" - -namespace love -{ - class Thread : public common::Thread - { - public: - Thread(Threadable* t); - - /* Detach the thread */ - virtual ~Thread(); - - virtual bool Start() override; - - /* - ** In SDL, SDL_WaitThread will wait - ** for the thread to be finished and then - ** SDL frees the pointer to the thread - */ - virtual void Wait() override; - }; - - inline Thread* newThread(Threadable* t) - { - return new Thread(t); - } -} // namespace love diff --git a/platform/3ds/include/objects/video/video.h b/platform/3ds/include/objects/video/video.h deleted file mode 100644 index 3ba7471e5..000000000 --- a/platform/3ds/include/objects/video/video.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "objects/video/videoc.h" - -#include - -namespace love -{ - class Video : public common::Video - { - public: - Video(Graphics* graphics, VideoStream* stream, float dpiScale = 1.0f); - - virtual ~Video(); - - void Draw(Graphics* graphics, const Matrix4& matrix) override; - - protected: - void Update() override; - - private: - C2D_Image current; - }; -} // namespace love diff --git a/platform/3ds/include/objects/videostream/theorastream.h b/platform/3ds/include/objects/videostream/theorastream.h deleted file mode 100644 index d2d174ecc..000000000 --- a/platform/3ds/include/objects/videostream/theorastream.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -#include "objects/videostream/theora/theorastreamc.h" - -namespace love -{ - class TheoraStream : public common::TheoraStream - { - public: - TheoraStream(File* file); - - struct Frame : VideoStream::IFrame - { - Frame(); - - ~Frame(); - - C3D_Tex* buffer; - int width, height; - }; - - virtual size_t GetSize() const override; - - virtual void SetupBuffers() override; - - virtual void FillBufferData(th_ycbcr_buffer bufferInfo) override; - - private: - void SetPostProcessingLevel(); - - th_pixel_fmt format; - int width, height; - Handle handle; - }; -} // namespace love diff --git a/platform/3ds/source/citro2d/citro.cpp b/platform/3ds/source/citro2d/citro.cpp deleted file mode 100644 index d421bf221..000000000 --- a/platform/3ds/source/citro2d/citro.cpp +++ /dev/null @@ -1,281 +0,0 @@ -#include <3ds.h> -#include - -#include "common/colors.h" -#include "common/luax.h" - -#include "citro2d/citro.h" - -#include "common/bidirectionalmap.h" -#include "modules/graphics/graphics.h" - -#include "common/pixelformat.h" - -using namespace love; - -citro2d::citro2d() -{ - gfxInitDefault(); - gfxSet3D(true); - - if (!C3D_Init(C3D_DEFAULT_CMDBUF_SIZE)) - svcBreak(USERBREAK_PANIC); - - if (!C2D_Init(C2D_DEFAULT_MAX_OBJECTS)) - svcBreak(USERBREAK_PANIC); - - C2D_Prepare(); - - C2D_Flush(); - C3D_AlphaTest(true, GPU_GREATER, 0); - - C2D_SetTintMode(C2D_TintMult); - - love::Texture::Filter filter; - filter.min = filter.mag = love::Texture::FILTER_NEAREST; - this->SetTextureFilter(filter); - - love::Texture::Wrap wrap; - wrap.s = wrap.t = wrap.r = love::Texture::WRAP_CLAMP; - this->SetTextureWrap(wrap); - - this->targets.reserve(4); - this->CreateFramebuffers(); -} - -void citro2d::DestroyFramebuffers() -{ - for (auto framebuffer : this->targets) - C3D_RenderTargetDelete(framebuffer); -} - -void citro2d::CreateFramebuffers() -{ - this->targets = { C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT), - C2D_CreateScreenTarget(GFX_TOP, GFX_RIGHT), - C2D_CreateScreenTarget(GFX_BOTTOM, GFX_LEFT) }; -} - -citro2d::~citro2d() -{ - C2D_Fini(); - C3D_Fini(); - gfxExit(); -} - -citro2d& citro2d::Instance() -{ - static citro2d c2d; - return c2d; -} - -void citro2d::SetBlendMode(GPU_BLENDEQUATION func, GPU_BLENDFACTOR srcColor, - GPU_BLENDFACTOR srcAlpha, GPU_BLENDFACTOR dstColor, - GPU_BLENDFACTOR dstAlpha) -{ - C2D_Flush(); - C3D_AlphaBlend(func, func, srcColor, dstColor, srcAlpha, dstAlpha); -} - -void citro2d::SetColorMask(const love::Graphics::ColorMask& mask) -{ - C2D_Flush(); - - uint8_t writeMask = GPU_WRITE_DEPTH; - writeMask |= mask.GetColorMask(); - - C3D_DepthTest(true, GPU_GEQUAL, static_cast(writeMask)); -} - -void citro2d::EnsureInFrame() -{ - if (!this->inFrame) - { - C3D_FrameBegin(C3D_FRAME_SYNCDRAW); - this->inFrame = true; - } -} - -void citro2d::BindFramebuffer(love::Canvas* canvas) -{ - this->EnsureInFrame(); - - if (canvas != nullptr) - this->current = canvas->GetRenderer(); - else - this->current = this->targets[love::Graphics::ACTIVE_SCREEN]; - - C2D_SceneBegin(this->current); -} - -void citro2d::ClearColor(const Colorf& color) -{ - C2D_TargetClear(this->current, C2D_Color32f(color.r, color.g, color.b, color.a)); -} - -void citro2d::Present() -{ - if (this->inFrame) - { - C3D_FrameEnd(0); - this->inFrame = false; - } - - for (size_t i = this->deferredFunctions.size(); i > 0; i--) - { - this->deferredFunctions[i - 1](); - this->deferredFunctions.erase(deferredFunctions.begin() + i - 1); - } -} - -void citro2d::SetScissor(GPU_SCISSORMODE mode, const love::Rect& scissor, bool canvasActive) -{ - C2D_Flush(); - - size_t width = Screen::Instance().GetWidth(Graphics::ACTIVE_SCREEN); - - uint32_t left = 240 > (scissor.y + scissor.h) ? 240 - (scissor.y + scissor.h) : 0; - uint32_t top = width > (scissor.x + scissor.w) ? width - (scissor.x + scissor.w) : 0; - uint32_t right = 240 - scissor.y; - uint32_t bottom = width - scissor.x; - - C3D_SetScissor(mode, left, top, right, bottom); -} - -void citro2d::SetStencil(GPU_TESTFUNC compare, int value) -{ - bool enabled = (compare == GPU_ALWAYS) ? false : true; - - C3D_StencilTest(enabled, compare, value, 0xFFFFFFFF, 0xFFFFFFFF); - C3D_StencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); -} - -// Set the global filter mode for textures -void citro2d::SetTextureFilter(const love::Texture::Filter& filter) -{ - GPU_TEXTURE_FILTER_PARAM min = - (filter.min == love::Texture::FILTER_NEAREST) ? GPU_NEAREST : GPU_LINEAR; - GPU_TEXTURE_FILTER_PARAM mag = - (filter.min == love::Texture::FILTER_NEAREST) ? GPU_NEAREST : GPU_LINEAR; - - GPU_PROCTEX_FILTER mipFilter = GPU_PT_LINEAR; - - if (filter.mipmap != love::Texture::FILTER_NONE) - { - if (filter.min == love::Texture::FILTER_NEAREST && - filter.mipmap == love::Texture::FILTER_NEAREST) - mipFilter = GPU_PT_NEAREST; - else if (filter.min == love::Texture::FILTER_NEAREST && - filter.mipmap == love::Texture::FILTER_LINEAR) - mipFilter = GPU_PT_LINEAR; - else if (filter.min == love::Texture::FILTER_LINEAR && - filter.mipmap == love::Texture::FILTER_NEAREST) - mipFilter = GPU_PT_NEAREST; - else if (filter.min == love::Texture::FILTER_LINEAR && - filter.mipmap == love::Texture::FILTER_LINEAR) - mipFilter = GPU_PT_LINEAR; - else - mipFilter = GPU_PT_LINEAR; - } - - this->filter.min = min; - this->filter.mag = mag; - - this->filter.mipMap = mipFilter; -} - -void citro2d::SetTextureFilter(love::Texture* texture, const love::Texture::Filter& filter) -{ - this->SetTextureFilter(filter); - - const C2D_Image& image = texture->GetHandle(); - C3D_TexSetFilter(image.tex, this->filter.mag, this->filter.min); -} - -void citro2d::SetTextureWrap(const love::Texture::Wrap& wrap) -{ - GPU_TEXTURE_WRAP_PARAM u = citro2d::GetCitroWrapMode(wrap.s); - GPU_TEXTURE_WRAP_PARAM v = citro2d::GetCitroWrapMode(wrap.t); - - this->wrap.s = u; - this->wrap.t = v; -} - -void citro2d::SetTextureWrap(love::Texture* texture, const love::Texture::Wrap& wrap) -{ - this->SetTextureWrap(wrap); - - const C2D_Image& image = texture->GetHandle(); - C3D_TexSetWrap(image.tex, this->wrap.s, this->wrap.t); -} - -/* -** Check if a texture size is valid -** because `size` should be power of two -*/ -bool citro2d::IsSizeValid(int size) -{ - if (size == 0) - return false; - - return (size & (size - 1)) == 0; -} - -GPU_TEXTURE_FILTER_PARAM citro2d::GetCitroFilterMode(love::Texture::FilterMode mode) -{ - switch (mode) - { - default: - case love::Texture::FilterMode::FILTER_LINEAR: - return GPU_LINEAR; - case love::Texture::FilterMode::FILTER_NEAREST: - return GPU_NEAREST; - } -} - -citro2d::GPUFilter citro2d::GetCitroFilterMode(const love::Texture::Filter& filter) -{ - citro2d::GPUFilter gpuFilter {}; - - gpuFilter.mag = citro2d::GetCitroFilterMode(filter.mag); - gpuFilter.min = citro2d::GetCitroFilterMode(filter.min); - - return gpuFilter; -} - -GPU_TEXTURE_WRAP_PARAM citro2d::GetCitroWrapMode(love::Texture::WrapMode wrap) -{ - switch (wrap) - { - case love::Texture::WRAP_CLAMP: - default: - return GPU_CLAMP_TO_EDGE; - case love::Texture::WRAP_CLAMP_ZERO: - return GPU_CLAMP_TO_BORDER; - case love::Texture::WRAP_REPEAT: - return GPU_REPEAT; - case love::Texture::WRAP_MIRRORED_REPEAT: - return GPU_MIRRORED_REPEAT; - } -} - -// clang-format off -constexpr auto pixelFormats = BidirectionalMap<>::Create( - PIXELFORMAT_TEX3DS_RGBA8, GPU_RGBA8, - PIXELFORMAT_RGBA8, GPU_RGBA8, - PIXELFORMAT_RGB8, GPU_RGB8, - PIXELFORMAT_RGB565, GPU_RGB565, - PIXELFORMAT_LA8, GPU_LA8, - PIXELFORMAT_ETC1, GPU_ETC1 -); -// clang-format on - -bool citro2d::GetConstant(PixelFormat in, GPU_TEXCOLOR& out) -{ - return pixelFormats.Find(in, out); -} - -bool citro2d::GetConstant(GPU_TEXCOLOR in, PixelFormat& out) -{ - return pixelFormats.ReverseFind(in, out); -} diff --git a/platform/3ds/source/citro2d/graphics.cpp b/platform/3ds/source/citro2d/graphics.cpp deleted file mode 100644 index d727f6132..000000000 --- a/platform/3ds/source/citro2d/graphics.cpp +++ /dev/null @@ -1,598 +0,0 @@ -#include - -#include "citro2d/graphics.h" -#include "common/bidirectionalmap.h" - -using namespace love; - -#define TRANSPARENCY C2D_Color32(0, 0, 0, 1) -#define TRANSPARENCY_DEBUG C2D_Color32(255, 0, 0, 96) - -love::citro2d::Graphics::Graphics() -{ - this->RestoreState(this->states.back()); -} - -void love::citro2d::Graphics::SetBlendMode(BlendMode mode, BlendAlpha alphaMode) -{ - if (alphaMode != BLENDALPHA_PREMULTIPLIED) - { - const char* modestr = "unknown"; - switch (mode) - { - case BLEND_LIGHTEN: - case BLEND_DARKEN: - case BLEND_MULTIPLY: - love::Graphics::GetConstant(mode, modestr); - throw love::Exception("The '%s' blend mode must be used with premultiplied alpha.", - modestr); - break; - default: - break; - } - } - - GPU_BLENDEQUATION func = GPU_BLEND_ADD; - - GPU_BLENDFACTOR srcColor = GPU_ONE; - GPU_BLENDFACTOR srcAlpha = GPU_ONE; - - GPU_BLENDFACTOR dstColor = GPU_ZERO; - GPU_BLENDFACTOR dstAlpha = GPU_ZERO; - - switch (mode) - { - case love::Graphics::BLEND_ALPHA: - srcColor = srcAlpha = GPU_ONE; - dstColor = dstAlpha = GPU_ONE_MINUS_SRC_ALPHA; - - break; - case love::Graphics::BLEND_MULTIPLY: - srcColor = srcAlpha = GPU_DST_COLOR; - dstColor = dstAlpha = GPU_ZERO; - - break; - case love::Graphics::BLEND_SUBTRACT: - func = GPU_BLEND_REVERSE_SUBTRACT; - - break; - case love::Graphics::BLEND_ADD: - srcColor = GPU_ONE; - srcAlpha = GPU_ZERO; - - dstColor = dstAlpha = GPU_ONE; - - break; - case love::Graphics::BLEND_LIGHTEN: - func = GPU_BLEND_MAX; - - break; - case love::Graphics::BLEND_DARKEN: - func = GPU_BLEND_MIN; - - break; - case love::Graphics::BLEND_SCREEN: - srcColor = srcAlpha = GPU_ONE; - dstColor = dstAlpha = GPU_ONE_MINUS_SRC_COLOR; - - break; - case love::Graphics::BLEND_REPLACE: - case love::Graphics::BLEND_NONE: - default: - srcColor = srcAlpha = GPU_ONE; - dstColor = dstAlpha = GPU_ZERO; - - break; - } - - // We can only do alpha-multiplication when srcRGB would have been unmodified. - if (srcColor == GPU_ONE && alphaMode == BLENDALPHA_MULTIPLY && mode != BLEND_NONE) - srcColor = GPU_SRC_ALPHA; - - ::citro2d::Instance().SetBlendMode(func, srcColor, srcAlpha, dstColor, dstAlpha); - - this->states.back().blendMode = mode; - this->states.back().blendAlphaMode = alphaMode; -} - -void love::citro2d::Graphics::Set3D(bool enabled) -{ - ::citro2d::Instance().Set3D(enabled); -} - -const bool love::citro2d::Graphics::Get3D() const -{ - return ::citro2d::Instance().Get3D(); -} - -void love::citro2d::Graphics::SetWide(bool enabled) -{ - ::citro2d::Instance().SetWideMode(enabled); -} - -const bool love::citro2d::Graphics::GetWide() const -{ - return ::citro2d::Instance().GetWide(); -} - -void love::citro2d::Graphics::Clear(std::optional color, std::optional stencil, - std::optional depth) -{ - if (!this->IsCanvasActive()) - ::citro2d::Instance().BindFramebuffer(); - - if (color.has_value()) - { - Graphics::GammaCorrectColor(color.value()); - ::citro2d::Instance().ClearColor(color.value()); - } -} - -void love::citro2d::Graphics::Clear(std::vector>& colors, - std::optional stencil, std::optional depth) -{ - int numColors = colors.size(); - - if (numColors == 0 || !stencil.has_value() || !depth.has_value()) - return; - - if (numColors <= 1) - this->Clear(numColors > 0 ? colors[0] : std::optional(), stencil, depth); -} - -void love::citro2d::Graphics::Present() -{ - if (this->IsCanvasActive()) - throw love::Exception("present cannot be called while a Canvas is active."); - - ::citro2d::Instance().Present(); -} - -/* Keep out from common */ -void Graphics::SetCanvas(Canvas* canvas) -{ - DisplayState& state = this->states.back(); - state.canvas.Set(canvas); - - ::citro2d::Instance().BindFramebuffer(canvas); - - if (this->states.back().scissor) - this->SetScissor(this->states.back().scissorRect); -} - -void love::citro2d::Graphics::SetColor(Colorf color) -{ - this->states.back().foreground = color; -} - -void love::citro2d::Graphics::SetColorMask(ColorMask mask) -{ - ::citro2d::Instance().SetColorMask(mask); - states.back().colorMask = mask; -} - -Font* love::citro2d::Graphics::NewDefaultFont(int size, const Texture::Filter& filter) -{ - auto fontModule = Module::GetInstance(M_FONT); - if (!fontModule) - throw love::Exception("Font module has not been loaded."); - - StrongReference r(fontModule->NewBCFNTRasterizer(size), Acquire::NORETAIN); - - return new Font(r.Get(), filter); -} - -Font* love::citro2d::Graphics::NewFont(Rasterizer* rasterizer, const Texture::Filter& filter) -{ - return new Font(rasterizer, filter); -} - -/* Primitives */ - -inline const auto normalizeAngle = [](float angle) { - angle = fmodf(angle, M_TAU); - if (angle < 0) - angle += M_TAU; - - return angle; -}; - -inline std::vector GenerateOutline(const Vector2* points, size_t count, float lineWidth) -{ - std::vector innerPoints(count); - - for (size_t startPoint = 0; startPoint < count; startPoint++) - { - const auto& middle = points[startPoint]; - const auto& after = points[(startPoint + 1) % count]; - const auto& before = points[startPoint == 0 ? count - 1 : startPoint - 1]; - - const float theta = normalizeAngle(atan2f(middle.y - after.y, middle.x - after.x)); - const float phi = normalizeAngle(atan2f(middle.y - before.y, middle.x - before.x)); - - const float angleWithinPolygon = normalizeAngle(phi - theta); - const float angleOfRightTriangle = (M_PI - angleWithinPolygon) / 2; - - const float lengthOfKite = lineWidth * (1 / cosf(angleOfRightTriangle)); - - const float offsetX = cosf(theta + (M_PI_2 - angleOfRightTriangle)) * lengthOfKite; - const float offsetY = sinf(theta + (M_PI_2 - angleOfRightTriangle)) * lengthOfKite; - - innerPoints[startPoint] = Vector2(middle.x - offsetX, middle.y - offsetY); - } - - return innerPoints; -} - -void love::citro2d::Graphics::SetPointSize(float size) -{ - this->states.back().pointSize = size; -} - -void love::citro2d::Graphics::Points(const Vector2* points, size_t count, const Colorf* colors, - size_t colorCount) -{ - const Matrix4& t = this->GetTransform(); - C2D_ViewRestore(&t.GetElements()); - - for (size_t index = 0; index < count; index++) - { - Colorf color = colors[0]; - const Vector2 point = points[index]; - - if (index < colorCount) - color = colors[index]; - - u32 pointColor = C2D_Color32f(color.r, color.g, color.b, color.a); - C2D_DrawCircleSolid(point.x, point.y, Graphics::CURRENT_DEPTH, - this->states.back().pointSize, pointColor); - } -} - -void love::citro2d::Graphics::Polyfill(const Vector2* points, size_t count, u32 color, float depth) -{ - for (size_t currentPoint = 2; currentPoint < count; currentPoint++) - { - C2D_DrawTriangle(points[0].x, points[0].y, color, points[currentPoint - 1].x, - points[currentPoint - 1].y, color, points[currentPoint].x, - points[currentPoint].y, color, depth); - } -} - -void love::citro2d::Graphics::Polygon(DrawMode mode, const Vector2* points, size_t count) -{ - Colorf color = this->GetColor(); - u32 foreground = C2D_Color32f(color.r, color.g, color.b, color.a); - - const Matrix4& t = this->GetTransform(); - C2D_ViewRestore(&t.GetElements()); - - if (mode == DRAW_LINE) - this->Polyline(points, count); - else - this->Polyfill(points, count, foreground, Graphics::CURRENT_DEPTH); -} - -void love::citro2d::Graphics::Polyline(const Vector2* points, size_t count) -{ - // Generate the outline and draw it - std::vector outline = GenerateOutline(points, count, this->states.back().lineWidth); - this->Polyfill(outline.data(), outline.size(), TRANSPARENCY, - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH); - - // Draw our filled polygon - this->Polygon(DRAW_FILL, points, count); - - Graphics::CURRENT_DEPTH += Graphics::MIN_DEPTH; -} - -void love::citro2d::Graphics::Rectangle(DrawMode mode, float x, float y, float width, float height) -{ - Vector2 points[4] = { - { x, y }, { x + width, y }, { x + width, y + height }, { x, y + height } - }; - - this->Polygon(mode, points, 4); -} - -void love::citro2d::Graphics::Rectangle(DrawMode mode, float x, float y, float width, float height, - float rx, float ry) -{ - if (rx == 0 && ry == 0) - { - this->Rectangle(mode, x, y, width, height); - return; - } - - Colorf color = this->GetColor(); - u32 foreground = C2D_Color32f(color.r, color.g, color.b, color.a); - - const Matrix4& t = this->GetTransform(); - C2D_ViewRestore(&t.GetElements()); - - /* Offset the radii *properly* */ - Vector2 offset(x + rx, y + ry); - Vector2 size(rx * 2, ry * 2); - - /* - ** Ellipse Drawing Order - ** 1 - 4 - ** | | - ** 2 - 3 - */ - - if (mode == DRAW_FILL) - { - /* Draw Ellipses first on Fill mode */ - - C2D_DrawEllipseSolid(x, y, Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 2, size.x, - size.y, foreground); - - C2D_DrawEllipseSolid(x, y + (height - size.y), - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 2, size.x, size.y, - foreground); - - C2D_DrawEllipseSolid(x + (width - size.x), y + (height - size.y), - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 2, size.x, size.y, - foreground); - - C2D_DrawEllipseSolid(x + (width - size.x), y, - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 2, size.x, size.y, - foreground); - - /* Draw Rectangles */ - - C2D_DrawRectSolid(offset.x, y, Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH, - width - size.x, height, foreground); - - C2D_DrawRectSolid(x, offset.y, Graphics::CURRENT_DEPTH, width, height - size.y, foreground); - - Graphics::CURRENT_DEPTH += Graphics::MIN_DEPTH * 2; - } - else - { - float& lineWidth = this->states.back().lineWidth; - - Vector2 innerDiameter((rx - lineWidth) * 2, (ry - lineWidth) * 2); - if (innerDiameter.x <= 0 || innerDiameter.y <= 0) - { - innerDiameter.x = 0; - innerDiameter.y = 0; - } - - /* normal rect offset + line width */ - Vector2 lineOffset((x + rx) + lineWidth, (y + ry) + lineWidth); - - /* normal radii size - line width */ - Vector2 lineSize((rx * 2) - lineWidth, (ry * 2) - lineWidth); - - /* normal radii pos + line width */ - Vector2 linePos(x + lineWidth, y + lineWidth); - - /* normal rect size - line width */ - Vector2 rectSize(width - (lineWidth * 2), height - (lineWidth * 2)); - - /* Transparent rectangles first */ - - C2D_DrawRectSolid(x + innerDiameter.x / 2 + lineWidth, y + lineWidth, - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 3, - width - (lineWidth * 2 + innerDiameter.x), height - lineWidth * 2, - TRANSPARENCY); - - C2D_DrawRectSolid(x + lineWidth, y + innerDiameter.y / 2 + lineWidth, - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 3, - width - (lineWidth * 2), height - (lineWidth * 2 + innerDiameter.y), - TRANSPARENCY); - - /* Transparent ellipses second, if they aren't nonexistent */ - - if (innerDiameter.x > 0 && innerDiameter.y > 0) - { - C2D_DrawEllipseSolid(x + lineWidth, y + lineWidth, - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 3, innerDiameter.x, - innerDiameter.y, TRANSPARENCY); - - C2D_DrawEllipseSolid(x + lineWidth, y + height - ry - innerDiameter.y / 2, - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 3, innerDiameter.x, - innerDiameter.y, TRANSPARENCY); - - C2D_DrawEllipseSolid(x + width - rx - innerDiameter.x / 2, - y + height - ry - innerDiameter.y / 2, - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 3, innerDiameter.x, - innerDiameter.y, TRANSPARENCY); - - C2D_DrawEllipseSolid(x + width - rx - innerDiameter.x / 2, y + lineWidth, - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 3, innerDiameter.x, - innerDiameter.y, TRANSPARENCY); - } - - /* Solid stuff -- Start with ellipses */ - - C2D_DrawEllipseSolid(x, y, Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 2, size.x, - size.y, foreground); - - C2D_DrawEllipseSolid(x, y + (height - size.y), - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 2, size.x, size.y, - foreground); - - C2D_DrawEllipseSolid(x + (width - size.x), y + (height - size.y), - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 2, size.x, size.y, - foreground); - - C2D_DrawEllipseSolid(x + (width - size.x), y, - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH * 2, size.x, size.y, - foreground); - - /* Rectangles */ - - C2D_DrawRectSolid(offset.x, y, Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH, - width - size.x, height, foreground); - - C2D_DrawRectSolid(x, offset.y, Graphics::CURRENT_DEPTH, width, height - size.y, foreground); - - /* Ellipses */ - - Graphics::CURRENT_DEPTH += Graphics::MIN_DEPTH * 3; - } -} - -void love::citro2d::Graphics::Ellipse(DrawMode mode, float x, float y, float a, float b) -{ - Colorf color = this->GetColor(); - u32 foreground = C2D_Color32f(color.r, color.g, color.b, color.a); - - const Matrix4& t = this->GetTransform(); - C2D_ViewRestore(&t.GetElements()); - - if (mode == DRAW_FILL) - C2D_DrawEllipseSolid(x - a, y - b, Graphics::CURRENT_DEPTH, a * 2, b * 2, foreground); - else - { - float lineWidth = this->states.back().lineWidth; - - C2D_DrawEllipseSolid((x - a) + lineWidth, (y - b) + lineWidth, - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH, (a - lineWidth) * 2, - (b - lineWidth) * 2, TRANSPARENCY); - - C2D_DrawEllipseSolid(x - a, y - b, Graphics::CURRENT_DEPTH, a * 2, b * 2, foreground); - - Graphics::CURRENT_DEPTH += Graphics::MIN_DEPTH; - } -} - -void love::citro2d::Graphics::Circle(DrawMode mode, float x, float y, float radius) -{ - Colorf color = this->GetColor(); - u32 foreground = C2D_Color32f(color.r, color.g, color.b, color.a); - - const Matrix4& t = this->GetTransform(); - C2D_ViewRestore(&t.GetElements()); - - if (mode == DRAW_FILL) - C2D_DrawCircleSolid(x, y, Graphics::CURRENT_DEPTH, radius, foreground); - else - { - C2D_DrawCircleSolid(x, y, Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH, - radius - this->states.back().lineWidth, TRANSPARENCY); - - C2D_DrawCircleSolid(x, y, Graphics::CURRENT_DEPTH, radius, foreground); - - Graphics::CURRENT_DEPTH += Graphics::MIN_DEPTH; - } -} - -void love::citro2d::Graphics::Arc(DrawMode mode, ArcMode arcmode, float x, float y, float radius, - float angle1, float angle2) -{ - const float diag_radius = M_SQRT2 * radius; - const auto calc90Triangle = [radius = diag_radius](float x, float y, - float angle) -> std::array { - return { Vector2(x, y), Vector2(x + radius * cosf(angle), y + radius * sinf(angle)), - Vector2(x + radius * sqrtf(2) * cosf(angle + M_PI_2), - y + radius * sqrtf(2) * sinf(angle + M_PI_2)) }; - }; - - angle1 = normalizeAngle(angle1); - angle2 = normalizeAngle(angle2); - - // Only go around counterclockwise rather than having a conditional - if (angle2 > angle1) - angle2 -= M_TAU; - - const Matrix4& t = this->GetTransform(); - C2D_ViewRestore(&t.GetElements()); - - while (angle2 + M_PI_2 < angle1) - { - const auto& pts = calc90Triangle(x, y, angle2); - C2D_DrawTriangle(pts[0].x, pts[0].y, TRANSPARENCY, pts[1].x, pts[1].y, TRANSPARENCY, - pts[2].x, pts[2].y, TRANSPARENCY, - Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH); - angle2 += M_PI_2; - } - - const std::array finalTriangle = { - Vector2(x, y), Vector2(x + diag_radius * cosf(angle2), y + diag_radius * sinf(angle2)), - Vector2(x + diag_radius * cosf(angle1), y + diag_radius * sinf(angle1)) - }; - - C2D_DrawTriangle(finalTriangle[0].x, finalTriangle[0].y, TRANSPARENCY, finalTriangle[1].x, - finalTriangle[1].y, TRANSPARENCY, finalTriangle[2].x, finalTriangle[2].y, - TRANSPARENCY, Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH); - - /* Sort of code duplication, but uh.. fix the arcs! */ - - Colorf color = this->GetColor(); - u32 foreground = C2D_Color32f(color.r, color.g, color.b, color.a); - - if (mode == DRAW_FILL) - C2D_DrawCircleSolid(x, y, Graphics::CURRENT_DEPTH, radius, foreground); - else - { - C2D_DrawCircleSolid(x, y, Graphics::CURRENT_DEPTH + Graphics::MIN_DEPTH, - radius - this->states.back().lineWidth, TRANSPARENCY); - - C2D_DrawCircleSolid(x, y, Graphics::CURRENT_DEPTH, radius, foreground); - - Graphics::CURRENT_DEPTH += Graphics::MIN_DEPTH; - } - - Graphics::CURRENT_DEPTH += Graphics::MIN_DEPTH; -} - -void love::citro2d::Graphics::Line(const Vector2* points, int count) -{ - Colorf color = this->GetColor(); - u32 foreground = C2D_Color32f(color.r, color.g, color.b, color.a); - - const Matrix4& t = this->GetTransform(); - C2D_ViewRestore(&t.GetElements()); - - for (size_t index = 1; index < (size_t)count; index++) - C2D_DrawLine(points[index - 1].x, points[index - 1].y, foreground, points[index].x, - points[index].y, foreground, this->states.back().lineWidth, - Graphics::CURRENT_DEPTH); -} - -void love::citro2d::Graphics::SetLineWidth(float width) -{ - this->states.back().lineWidth = width; -} - -void love::citro2d::Graphics::SetDefaultFilter(const Texture::Filter& filter) -{ - Texture::defaultFilter = filter; -} - -/* End Primitives */ - -void love::citro2d::Graphics::SetScissor(const Rect& scissor) -{ - DisplayState& state = this->states.back(); - - ::citro2d::Instance().SetScissor(GPU_SCISSOR_NORMAL, scissor, false); - - state.scissor = true; - state.scissorRect = scissor; -} - -void love::citro2d::Graphics::SetScissor() -{ - ::citro2d::Instance().SetScissor(GPU_SCISSOR_DISABLE, { 0, 0, 0, 0 }, false); - - states.back().scissor = false; -} - -Graphics::RendererInfo love::citro2d::Graphics::GetRendererInfo() const -{ - RendererInfo info {}; - - info.name = RENDERER_NAME; - info.device = RENDERER_DEVICE; - info.vendor = RENDERER_VENDOR; - info.version = RENDERER_VERSION; - - return info; -} - -/* 2D Screens */ diff --git a/platform/3ds/source/common/matrix.cpp b/platform/3ds/source/common/matrix.cpp deleted file mode 100644 index 0a522895f..000000000 --- a/platform/3ds/source/common/matrix.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "common/matrix.h" - -using namespace love; - -void Matrix4::Multiply(const Matrix4& a, const Matrix4& b, C3D_Mtx& c) -{ - Mtx_Multiply(&c, &a.matrix, &b.matrix); -} - -void Matrix4::Multiply(const Matrix4& a, const Matrix4& b, Matrix4& c) -{ - Multiply(a, b, c.matrix); -} - -Matrix4::Matrix4() -{ - this->SetIdentity(); -} - -Matrix4::Matrix4(const C3D_Mtx& a) -{ - Mtx_Copy(&this->matrix, &a); -} - -bool Matrix4::IsAffine2DTransform() const -{ - /* - return fabsf(this->matrix[2] + this->matrix[3] + this->matrix[6] + this->matrix[7] + - this->matrix[8] + this->matrix[9] + this->matrix[11] + this->matrix[14]) < 0.00001f - && fabsf(this->matrix[10] + this->matrix[15] - 2.0f) < 0.00001f; - */ - return true; //? -} - -Matrix4::Matrix4(const Matrix4& a, const Matrix4& b) -{ - Matrix4::Multiply(a, b, this->matrix); -} - -Matrix4::Matrix4(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, - float ky) -{ - this->SetTransformation(x, y, angle, sx, sy, ox, oy, ky, ky); -} - -void Matrix4::operator*=(const Matrix4& m) -{ - Matrix4::Multiply(*this, m, this->matrix); -} - -Matrix4 Matrix4::operator*(const Matrix4& m) const -{ - return Matrix4(*this, m); -} - -void Matrix4::SetIdentity() -{ - Mtx_Identity(&this->matrix); -} - -void Matrix4::SetRawTransformation(float t00, float t10, float t01, float t11, float x, float y) -{ - Mtx_Zeros(&this->matrix); - - this->matrix.r[2].z = this->matrix.r[3].w = 1.0f; - - this->matrix.r[0].x = t00; - this->matrix.r[1].x = t10; - - this->matrix.r[0].y = t01; - this->matrix.r[1].y = t11; - - this->matrix.r[0].w = x; - this->matrix.r[1].w = y; -} - -void Matrix4::SetTransformation(float x, float y, float angle, float sx, float sy, float ox, - float oy, float kx, float ky) -{ - Mtx_Zeros(&this->matrix); - - /* - ** Note that the offsets are applied before rotation, scaling, or shearing; - ** scaling and shearing are applied before rotation. - */ - float c = cosf(angle), s = sinf(angle); - - this->matrix.r[2].z = this->matrix.r[3].w = 1.0f; - - this->matrix.r[0].x = c * sx - ky * s * sy; // = a - this->matrix.r[1].x = s * sx + ky * c * sy; // = b - - this->matrix.r[0].y = kx * c * sx - s * sy; // = c - this->matrix.r[1].y = kx * s * sx + c * sy; // = d - - this->matrix.r[0].w = x - ox * this->matrix.r[0].x - oy * this->matrix.r[0].y; // = c - this->matrix.r[1].w = y - ox * this->matrix.r[1].x - oy * this->matrix.r[1].y; // = d -} - -void Matrix4::Translate(float x, float y) -{ - Mtx_Translate(&this->matrix, x, y, 0.0f, true); -} - -void Matrix4::Rotate(float r) -{ - Mtx_RotateZ(&this->matrix, r, true); -} - -void Matrix4::Scale(float sx, float sy) -{ - Mtx_Scale(&this->matrix, sx, sy, 1.0f); -} - -void Matrix4::GetApproximateScale(float& sx, float& sy) const -{ - sx = sqrtf(this->matrix.r[0].x * this->matrix.r[0].x + - this->matrix.r[0].y * this->matrix.r[0].y); - sy = sqrtf(this->matrix.r[1].x * this->matrix.r[1].x + - this->matrix.r[1].y * this->matrix.r[1].y); -} - -void Matrix4::Shear(float kx, float ky) -{ - C3D_Mtx mtx; - Mtx_Identity(&mtx); - - mtx.r[0].y = kx; - mtx.r[1].x = ky; - - Mtx_Multiply(&this->matrix, &this->matrix, &mtx); -} - -const C3D_Mtx& Matrix4::GetElements() const -{ - return this->matrix; -} - -void Matrix4::TransformXY() -{ - C2D_ViewRestore(&this->matrix); -} - -void Matrix4::TransformXY(const Elements& elements) -{ - C2D_ViewRestore(&elements); -} - -Matrix4 Matrix4::Inverse() const -{ - Matrix4 inv; - Mtx_Copy(&inv.matrix, &this->matrix); - - Mtx_Inverse(&inv.matrix); - - return inv; -} - -Matrix4 Matrix4::Ortho(float left, float right, float bottom, float top, float near, float far) -{ - Matrix4 ortho; - Mtx_Ortho(&ortho.matrix, left, right, bottom, top, near, far, true); - - return ortho; -} \ No newline at end of file diff --git a/platform/3ds/source/common/screen.cpp b/platform/3ds/source/common/screen.cpp deleted file mode 100644 index 74c7ff7c2..000000000 --- a/platform/3ds/source/common/screen.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "common/screen.h" - -#include "citro2d/citro.h" -#include "common/bidirectionalmap.h" -#include "modules/graphics/graphics.h" - -using namespace love; - -int Screen::GetWidth(RenderScreen screen) -{ - if (::citro2d::Instance().Get3D()) - { - switch (screen) - { - case (int)Screen::CtrScreen::CTR_SCREEN_LEFT: - case (int)Screen::CtrScreen::CTR_SCREEN_RIGHT: - default: - return Screen::TOP_WIDTH; - case (int)Screen::CtrScreen::CTR_SCREEN_BOTTOM: - return Screen::BOTTOM_WIDTH; - } - } - else - { - switch (screen) - { - case (int)Screen::Ctr2dScreen::CTR_2D_SCREEN_TOP: - default: - if (::citro2d::Instance().GetWide()) - return Screen::TOP_WIDE_WIDTH; - return Screen::TOP_WIDTH; - case (int)Screen::Ctr2dScreen::CTR_2D_SCREEN_BOTTOM: - return Screen::BOTTOM_WIDTH; - } - } -} - -int Screen::GetHeight() -{ - return Screen::HEIGHT; -} - -// clang-format off -constexpr auto ScreenTypes = BidirectionalMap<>::Create( - "left", Screen::CtrScreen::CTR_SCREEN_LEFT, - "right", Screen::CtrScreen::CTR_SCREEN_RIGHT, - "bottom", Screen::CtrScreen::CTR_SCREEN_BOTTOM -); - -constexpr auto ScreenTypes2d = BidirectionalMap<>::Create( - "top", Screen::Ctr2dScreen::CTR_2D_SCREEN_TOP, - "bottom", Screen::Ctr2dScreen::CTR_2D_SCREEN_BOTTOM -); -// clang-format on - -bool Screen::GetConstant(const char* in, RenderScreen& out) -{ - if (::citro2d::Instance().Get3D()) - return Screen::FindSetCast(ScreenTypes, in, out); - - return Screen::FindSetCast(ScreenTypes2d, in, out); -} - -bool Screen::GetConstant(RenderScreen in, const char*& out) -{ - if (::citro2d::Instance().Get3D()) - return Screen::ReverseFindSetCast(ScreenTypes, in, out); - - return Screen::ReverseFindSetCast(ScreenTypes2d, in, out); -} - -std::vector Screen::GetConstants(RenderScreen) -{ - if (::citro2d::Instance().Get3D()) - return ScreenTypes.GetNames(); - - return ScreenTypes2d.GetNames(); -} diff --git a/platform/3ds/source/conditional.cpp b/platform/3ds/source/conditional.cpp deleted file mode 100644 index c482806e7..000000000 --- a/platform/3ds/source/conditional.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "modules/thread/types/conditional.h" - -using namespace love::thread; - -Conditional::Conditional() -{ - CondVar_Init(&this->condVar); -} - -Conditional::~Conditional() -{} - -void Conditional::Signal() -{ - CondVar_Signal(&this->condVar); -} - -void Conditional::Broadcast() -{ - CondVar_Broadcast(&this->condVar); -} - -bool Conditional::Wait(thread::Mutex* _mutex, s64 timeout) -{ - if (timeout < 0) - CondVar_Wait(&this->condVar, &_mutex->mutex); - else if (CondVar_WaitTimeout(&this->condVar, &_mutex->mutex, timeout)) - return false; - - return true; -} diff --git a/platform/3ds/source/driver/audiodrv.cpp b/platform/3ds/source/driver/audiodrv.cpp deleted file mode 100644 index a310dac89..000000000 --- a/platform/3ds/source/driver/audiodrv.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "driver/audiodrv.h" - -using namespace love::driver; - -void audioCallback(void* data) -{ - auto event = (LightEvent*)data; - LightEvent_Signal(event); -} - -Audrv::Audrv() -{ - this->initialized = R_SUCCEEDED(ndspInit()); - - if (!this->initialized) - return; - - LightEvent_Init(&this->ndspEvent, RESET_ONESHOT); - ndspSetCallback(audioCallback, &this->ndspEvent); -} - -Audrv::~Audrv() -{ - if (this->initialized) - ndspExit(); -} - -LightEvent& Audrv::GetEvent() -{ - return this->ndspEvent; -} \ No newline at end of file diff --git a/platform/3ds/source/driver/hidrv.cpp b/platform/3ds/source/driver/hidrv.cpp deleted file mode 100644 index 1a0792ebb..000000000 --- a/platform/3ds/source/driver/hidrv.cpp +++ /dev/null @@ -1,185 +0,0 @@ -#include "driver/hidrv.h" -#include "modules/joystick/joystick.h" -#include "objects/gamepad/gamepad.h" - -#include - -#define MODULE() love::Module::GetInstance(love::Module::M_JOYSTICK) - -using namespace love::driver; - -Hidrv::Hidrv() : oldSticks {}, oldTouchState {}, isTouchHeld(false) -{} - -bool Hidrv::Poll(LOVE_Event* event) -{ - if (!this->events.empty()) - { - *event = this->events.front(); - this->events.pop_front(); - - return true; - } - - if (this->hysteresis) - return this->hysteresis = false; - - hidScanInput(); - - /* touch screen */ - - u32 touchDown = hidKeysDown(); - u32 touchHeld = hidKeysHeld(); - u32 touchReleased = hidKeysUp(); - - if (touchDown & KEY_TOUCH || touchHeld & KEY_TOUCH) - hidTouchRead(&this->touchState); - - if (!this->isTouchHeld && (touchDown & KEY_TOUCH)) - { - auto& newEvent = this->events.emplace_back(); - - newEvent.type = TYPE_TOUCHPRESS; - - newEvent.touch.id = 0; - newEvent.touch.x = this->touchState.px; - newEvent.touch.y = this->touchState.py; - newEvent.touch.dx = 0.0f; - newEvent.touch.dy = 0.0f; - newEvent.touch.pressure = 1.0f; - - this->oldTouchState = this->touchState; - } - - if (touchHeld & KEY_TOUCH) - { - float dx = this->oldTouchState.px - this->touchState.px; - float dy = this->oldTouchState.py - this->touchState.py; - - if (dx != 0.0f || dy != 0.0f) - { - auto& newEvent = this->events.emplace_back(); - - newEvent.type = TYPE_TOUCHMOVED; - - newEvent.touch.id = 0; - newEvent.touch.x = this->touchState.px; - newEvent.touch.y = this->touchState.py; - newEvent.touch.dx = dx; - newEvent.touch.dy = dy; - newEvent.touch.pressure = 1.0f; - - this->isTouchHeld = true; - } - } - - if (touchReleased & KEY_TOUCH) - { - auto& newEvent = this->events.emplace_back(); - this->oldTouchState = this->touchState; - - newEvent.type = TYPE_TOUCHRELEASE; - - newEvent.touch.id = 0; - newEvent.touch.x = this->oldTouchState.px; - newEvent.touch.y = this->oldTouchState.py; - newEvent.touch.dx = 0.0f; - newEvent.touch.dy = 0.0f; - newEvent.touch.pressure = 0.0f; - - if (this->isTouchHeld) - this->isTouchHeld = false; - } - - Gamepad* gamepad = MODULE()->GetJoystickFromID(0); - - if (gamepad) - { - gamepad->Update(); - - /* handle button inputs */ - - Gamepad::ButtonMapping button; - - const auto& mappings = gamepad->GetButtonMapping(); - const auto entries = mappings.GetEntries(); - - for (size_t index = 0; index < entries.second; index++) - { - if (gamepad->IsDown(index, button)) - { - auto& newEvent = this->events.emplace_back(); - - newEvent.type = TYPE_GAMEPADDOWN; - - newEvent.button.name = button.first; - newEvent.button.which = gamepad->GetID(); - newEvent.button.button = button.second; - } - } - - for (size_t index = 0; index < entries.second; index++) - { - if (gamepad->IsUp(index, button)) - { - auto& newEvent = this->events.emplace_back(); - - newEvent.type = TYPE_GAMEPADUP; - - newEvent.button.name = button.first; - newEvent.button.which = gamepad->GetID(); - newEvent.button.button = button.second; - } - } - - /* handle trigger inputs */ - for (size_t i = 5; i <= 6; i++) - { - auto& newEvent = this->events.emplace_back(); - - newEvent.type = TYPE_GAMEPADAXIS; - newEvent.axis.which = gamepad->GetID(); - - newEvent.axis.axis = (i == 5) ? "triggerleft" : "triggerright"; - newEvent.axis.value = gamepad->GetAxis(i); - newEvent.axis.number = i; - } - - /* handle stick inputs */ - for (size_t i = 1; i <= 4; i++) - { - auto& newEvent = this->events.emplace_back(); - - newEvent.type = TYPE_GAMEPADAXIS; - newEvent.axis.which = gamepad->GetID(); - - const char* axis = nullptr; - if (i < 3) // left - { - if ((i % 2) != 0) - axis = "leftx"; - else - axis = "lefty"; - } - else - { - if ((i % 2) != 0) - axis = "rightx"; - else - axis = "righty"; - } - - newEvent.axis.axis = axis; - newEvent.axis.value = gamepad->GetAxis(i); - newEvent.axis.number = i; - } - } - - if (this->events.empty()) - return false; - - *event = this->events.front(); - this->events.pop_front(); - - return this->hysteresis = true; -} diff --git a/platform/3ds/source/modules/audio.cpp b/platform/3ds/source/modules/audio.cpp deleted file mode 100644 index df8ed7f74..000000000 --- a/platform/3ds/source/modules/audio.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "modules/audio/audio.h" - -#include "common/exception.h" -#include "modules/audio/pool/pool.h" - -using namespace love; - -void Audio::SetVolume(float volume) -{ - ndspSetMasterVol(volume); - this->volume = volume; -} diff --git a/platform/3ds/source/modules/event.cpp b/platform/3ds/source/modules/event.cpp deleted file mode 100644 index 6031a3764..000000000 --- a/platform/3ds/source/modules/event.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "modules/event/event.h" -#include "driver/hidrv.h" - -#include "modules/thread/types/mutex.h" - -static aptHookCookie s_aptHookCookie; - -#define Instance() (love::Module::GetInstance(love::Module::M_EVENT)) - -static void aptEventHook(const APT_HookType type, void* param) -{ - switch (type) - { - case APTHOOK_ONRESTORE: - case APTHOOK_ONWAKEUP: - Instance()->GetDriver()->SendFocus(true); - break; - case APTHOOK_ONSUSPEND: - case APTHOOK_ONSLEEP: - Instance()->GetDriver()->SendFocus(false); - break; - case APTHOOK_ONEXIT: - Instance()->GetDriver()->SendQuit(); - break; - default: - break; - } -} - -love::Event::Event() -{ - aptHook(&s_aptHookCookie, aptEventHook, nullptr); -} - -love::Event::~Event() -{ - aptUnhook(&s_aptHookCookie); -} diff --git a/platform/3ds/source/modules/fontmodule.cpp b/platform/3ds/source/modules/fontmodule.cpp deleted file mode 100644 index dd58ed84d..000000000 --- a/platform/3ds/source/modules/fontmodule.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "modules/font/fontmodule.h" - -using namespace love; - -/* -- BCFNT Rasterizer -- */ - -/* love.font.newRasterizer */ -Rasterizer* FontModule::NewRasterizer(FileData* data) -{ - if (BCFNTRasterizer::Accepts(data)) - return NewBCFNTRasterizer(data, 12); - - throw love::Exception("Invalid font file: %s", data->GetFilename().c_str()); -} - -/* If accepted, create the new BCFNTRasterizer */ -Rasterizer* FontModule::NewBCFNTRasterizer(Data* data, int size) -{ - return new BCFNTRasterizer(data, size); -} - -Rasterizer* FontModule::NewBCFNTRasterizer(int size, common::Font::SystemFontType type) -{ - return new BCFNTRasterizer(type, size); -} diff --git a/platform/3ds/source/modules/keyboard.cpp b/platform/3ds/source/modules/keyboard.cpp deleted file mode 100644 index d2ebf7532..000000000 --- a/platform/3ds/source/modules/keyboard.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "modules/keyboard/keyboard.h" -#include "common/bidirectionalmap.h" -#include <3ds.h> - -using namespace love; - -Keyboard::Keyboard() : common::Keyboard((MAX_INPUT_LENGTH * 3) + 1) -{} - -std::string Keyboard::SetTextInput(const Keyboard::SwkbdOpt& options) -{ - uint32_t maxLength = this->CalculateEncodingMaxLength(options.maxLength); - memset(this->text, 0, maxLength); - - SwkbdType type = static_cast(options.type); - - swkbdInit(&this->keyboard, type, 2, maxLength); - swkbdSetInitialText(&this->keyboard, text); - - swkbdSetHintText(&this->keyboard, options.hint.c_str()); - - if (options.isPassword) - swkbdSetPasswordMode(&this->keyboard, SWKBD_PASSWORD_HIDE_DELAY); - - SwkbdButton button = swkbdInputText(&this->keyboard, text, maxLength); - - if (button == SWKBD_BUTTON_NONE || button == SWKBD_BUTTON_LEFT) - return std::string(); - - return text; -} - -// clang-format off -constexpr auto keyboardTypes = BidirectionalMap<>::Create( - "normal", Keyboard::KeyboardType::TYPE_NORMAL, - "qwerty", Keyboard::KeyboardType::TYPE_QWERTY, - "numpad", Keyboard::KeyboardType::TYPE_NUMPAD -); -// clang-format on - -bool Keyboard::GetConstant(const char* in, KeyboardType& out) -{ - return keyboardTypes.Find(in, out); -} - -bool Keyboard::GetConstant(KeyboardType in, const char*& out) -{ - return keyboardTypes.ReverseFind(in, out); -} - -std::vector Keyboard::GetConstants(KeyboardType) -{ - return keyboardTypes.GetNames(); -} diff --git a/platform/3ds/source/modules/system.cpp b/platform/3ds/source/modules/system.cpp deleted file mode 100644 index efa9256f3..000000000 --- a/platform/3ds/source/modules/system.cpp +++ /dev/null @@ -1,320 +0,0 @@ -#include "modules/system/system.h" -#include <3ds.h> - -#include "common/bidirectionalmap.h" -#include "common/results.h" - -using namespace love; - -int System::GetProcessorCount() -{ - if (this->systemInfo.processors != 0) - return this->systemInfo.processors; - - uint8_t model = 0; - CFGU_GetSystemModel(&model); - - int processorCount = 2; - - switch (model) - { - case CFG_MODEL_N3DS: - case CFG_MODEL_N3DSXL: - case CFG_MODEL_N2DSXL: - processorCount = 4; - break; - } - - this->systemInfo.processors = processorCount; - - return this->systemInfo.processors; -} - -const std::string& System::GetModel() -{ - if (!this->systemInfo.model.empty()) - return this->systemInfo.model; - - uint8_t model = 0; - - R_UNLESS(CFGU_GetSystemModel(&model), LOVE_STRING_EMPTY); - - const char* name = nullptr; - if (!System::GetConstant(static_cast(model), name)) - name = "Unknown"; - - this->systemInfo.model = name; - - return this->systemInfo.model; -} - -const std::string& System::GetUsername() -{ - if (!this->systemInfo.username.empty()) - return this->systemInfo.username; - - char username[USERNAME_LENGTH] = { 0 }; - - R_UNLESS(FRD_GetMyScreenName(username, USERNAME_LENGTH), LOVE_STRING_EMPTY); - - this->systemInfo.username = username; - - return this->systemInfo.username; -} - -const std::string& System::GetRegion() -{ - if (!this->systemInfo.region.empty()) - return this->systemInfo.region; - - uint8_t region = 0; - - R_UNLESS(CFGU_SecureInfoGetRegion(®ion), LOVE_STRING_EMPTY); - - const char* name = nullptr; - if (!System::GetConstant(static_cast(region), name)) - name = "Unknown"; - - this->systemInfo.region = name; - - return this->systemInfo.region; -} - -System::PowerState System::GetPowerInfo(uint8_t& percent) const -{ - uint8_t batteryState = 0; - PowerState state = PowerState::POWER_UNKNOWN; - - MCUHWC_GetBatteryLevel(&percent); - PTMU_GetBatteryChargeState(&batteryState); - - state = (batteryState) ? PowerState::POWER_CHARGING : PowerState::POWER_BATTERY; - - if (percent == 100 && !batteryState) - state = PowerState::POWER_CHARGED; - - return state; -} - -System::NetworkState System::GetNetworkInfo(uint8_t& signal) const -{ - uint32_t status = 0; - ACU_GetWifiStatus(&status); - - NetworkState state = NetworkState::NETWORK_UNKNOWN; - - signal = osGetWifiStrength(); - state = (status > 0) ? NetworkState::NETWORK_CONNECTED : NetworkState::NETWORK_DISCONNECTED; - - return state; -} - -const std::string& System::GetPreferredLocales() -{ - if (!this->systemInfo.language.empty()) - return this->systemInfo.language; - - uint8_t language; - - R_UNLESS(CFGU_GetSystemLanguage(&language), LOVE_STRING_EMPTY); - - const char* name = nullptr; - if (!System::GetConstant(static_cast(language), name)) - name = "Unknown"; - - this->systemInfo.language = name; - - return this->systemInfo.language; -} - -const std::string& System::GetVersion() -{ - if (!this->systemInfo.version.empty()) - return this->systemInfo.version; - - char out[256] = { 0 }; - - R_UNLESS(osGetSystemVersionDataString(NULL, NULL, out, 256), LOVE_STRING_EMPTY); - - this->systemInfo.version = out; - return this->systemInfo.version; -} - -/* Friend Code stuff */ - -static inline std::string MAKE_FRIEND_CODE(uint64_t friendCode) -{ - std::string out = "####-####-####"; - - sprintf(out.data(), "%04i-%04i-%04i", (int)(friendCode / 100000000) % 10000, - (int)(friendCode / 10000) % 10000, (int)(friendCode % 10000)); - - return out; -} - -const std::string& System::GetFriendCode() -{ - if (!this->systemInfo.friendCode.empty()) - return this->systemInfo.friendCode; - - FriendKey key; - uint64_t friendCode; - - /* Get the Friend Key for the user */ - R_UNLESS(FRD_GetMyFriendKey(&key), LOVE_STRING_EMPTY); - - /* Convert the principalId to friendCode */ - R_UNLESS(FRD_PrincipalIdToFriendCode(key.principalId, &friendCode), LOVE_STRING_EMPTY); - - this->systemInfo.friendCode = MAKE_FRIEND_CODE(friendCode); - - return this->systemInfo.friendCode; -} - -const std::string& System::GetSystemTheme() -{ - return THEME_NAME; -} - -Handle System::OpenPlayCoinsFile() -{ - Handle playCoinsFile; - const uint32_t path[3] = { MEDIATYPE_NAND, 0xF000000B, 0x00048000 }; - - const FS_Path archivePath = { PATH_BINARY, 0xC, path }; - const FS_Path filePath = fsMakePath(PATH_UTF16, u"/gamecoin.dat"); - - Result res = FSUSER_OpenFileDirectly(&playCoinsFile, ARCHIVE_SHARED_EXTDATA, archivePath, - filePath, FS_OPEN_READ | FS_OPEN_WRITE, 0); - - if (R_FAILED(res)) - throw love::Exception("Failed to open gamecoin.dat!"); - - return playCoinsFile; -} - -int System::GetPlayCoins() const -{ - Handle playCoinsFile = System::OpenPlayCoinsFile(); - - uint8_t buffer[2] = { 0 }; - - Result res = FSFILE_Read(playCoinsFile, nullptr, 4, buffer, 2); - - if (R_FAILED(res)) - { - FSFILE_Close(playCoinsFile); - throw love::Exception("Failed to read gamecoin.dat!"); - } - - FSFILE_Close(playCoinsFile); - - return ((int)buffer[1] << 8) | buffer[0]; -} - -void System::SetPlayCoins(int amount) -{ - if (amount < 0 || amount > 300) - throw love::Exception("Cannot set Play Coin count to %d! Must be within [0, 300].", amount); - - Handle playCoinsFile = System::OpenPlayCoinsFile(); - - const uint8_t buffer[2] = { (uint8_t)amount, (uint8_t)(amount >> 8) }; - - Result res = FSFILE_Write(playCoinsFile, nullptr, 4, buffer, 2, 0); - - if (R_FAILED(res)) - { - FSFILE_Close(playCoinsFile); - throw love::Exception("Failed to write to gamecoin.dat!"); - } - - FSFILE_Close(playCoinsFile); -} - -// clang-format off -constexpr auto languages = BidirectionalMap<>::Create( - "jp", CFG_LANGUAGE_JP, - "en", CFG_LANGUAGE_EN, - "fr", CFG_LANGUAGE_FR, - "de", CFG_LANGUAGE_DE, - "it", CFG_LANGUAGE_IT, - "es", CFG_LANGUAGE_ES, - "zh_CN", CFG_LANGUAGE_ZH, - "ko", CFG_LANGUAGE_KO, - "nl", CFG_LANGUAGE_NL, - "pt", CFG_LANGUAGE_PT, - "ru", CFG_LANGUAGE_RU, - "zh_TW", CFG_LANGUAGE_TW -); - -constexpr auto models = BidirectionalMap<>::Create( - "3DS", CFG_MODEL_3DS, - "3DSXL", CFG_MODEL_3DSXL, - "New 3DS", CFG_MODEL_N3DS, - "2DS", CFG_MODEL_2DS, - "New 3DSXL", CFG_MODEL_N3DSXL, - "New 2DSXL", CFG_MODEL_N2DSXL -); - -constexpr auto regions = BidirectionalMap<>::Create( - "Japan", CFG_REGION_JPN, - "United States", CFG_REGION_USA, - "Europe", CFG_REGION_EUR, - "Australia", CFG_REGION_AUS, - "China", CFG_REGION_CHN, - "Korea", CFG_REGION_KOR, - "Taiwan", CFG_REGION_TWN -); -// clang-format on - -/* LANGUAGE CONSTANTS */ - -bool System::GetConstant(const char* in, CFG_Language& out) -{ - return languages.Find(in, out); -} - -bool System::GetConstant(CFG_Language in, const char*& out) -{ - return languages.ReverseFind(in, out); -} - -std::vector System::GetConstants(CFG_Language) -{ - return languages.GetNames(); -} - -/* MODEL CONSTANTS */ - -bool System::GetConstant(const char* in, CFG_SystemModel& out) -{ - return models.Find(in, out); -} - -bool System::GetConstant(CFG_SystemModel in, const char*& out) -{ - return models.ReverseFind(in, out); -} - -std::vector System::GetConstants(CFG_SystemModel) -{ - return models.GetNames(); -} - -/* REGION CONSTANTS */ - -bool System::GetConstant(const char* in, CFG_Region& out) -{ - return regions.Find(in, out); -} - -bool System::GetConstant(CFG_Region in, const char*& out) -{ - return regions.ReverseFind(in, out); -} - -std::vector System::GetConstants(CFG_Region) -{ - return regions.GetNames(); -} diff --git a/platform/3ds/source/modules/timer.cpp b/platform/3ds/source/modules/timer.cpp deleted file mode 100644 index 954cdba43..000000000 --- a/platform/3ds/source/modules/timer.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "modules/timer/timer.h" - -using namespace love; - -static TickCounter counter; - -Timer::Timer() -{ - osTickCounterStart(&counter); - this->prevFPSUpdate = currentTime = this->GetTime(); -} - -double common::Timer::GetTime() -{ - counter.elapsed = svcGetSystemTick() - counter.reference; - return osTickCounterRead(&counter) / 1000.0; -} \ No newline at end of file diff --git a/platform/3ds/source/modules/window.cpp b/platform/3ds/source/modules/window.cpp deleted file mode 100644 index ec7ecb547..000000000 --- a/platform/3ds/source/modules/window.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "modules/window/window.h" - -using namespace love; - -Window::Window() -{ - this->fullscreenModes = { { 400, 240 }, { 320, 240 } }; -} - -int Window::GetDisplayCount() -{ - return 2; -} diff --git a/platform/3ds/source/objects/bcfntrasterizer.cpp b/platform/3ds/source/objects/bcfntrasterizer.cpp deleted file mode 100644 index 73f94a995..000000000 --- a/platform/3ds/source/objects/bcfntrasterizer.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "objects/bcfntrasterizer/bcfntrasterizer.h" - -using namespace love; - -BCFNTRasterizer::BCFNTRasterizer(Data* data, int size) : data(data), glyphCount(-1) -{ - this->font = C2D_FontLoadFromMem(data->GetData(), data->GetSize()); - - if (!this->font) - throw love::Exception( - "BCFNT loading error: C2D_FontLoadFromMem failed (problem with font file?)"); - - this->InitMetrics(size); -} - -BCFNTRasterizer::BCFNTRasterizer(common::Font::SystemFontType type, int size) : glyphCount(-1) -{ - this->font = C2D_FontLoadSystem(static_cast(type)); - - this->InitMetrics(size); -} - -void BCFNTRasterizer::InitMetrics(int size) -{ - /* Set global metrics */ - FINF_s* s = C2D_FontGetInfo(this->font); - TGLP_s* textureSheetInfo = s->tglp; - - this->scale = size / 30.0f; - - this->metrics.advance = (int)textureSheetInfo->maxCharWidth * this->scale; - this->metrics.ascent = (int)s->ascent * this->scale; - this->metrics.descent = (int)(s->height - s->ascent) * this->scale; - this->metrics.height = (int)s->height * this->scale; -} - -BCFNTRasterizer::~BCFNTRasterizer() -{ - C2D_FontFree(this->font); -} - -GlyphData* BCFNTRasterizer::GetGlyphData(uint32_t glyph) const -{ - GlyphData::GlyphMetrics gMetrics {}; - - int glyphIndex = C2D_FontGlyphIndexFromCodePoint(this->font, glyph); - - fontGlyphPos_s glyphPosition; - C2D_FontCalcGlyphPos(this->font, &glyphPosition, glyphIndex, 0, this->scale, this->scale); - - gMetrics.height = this->metrics.height; - gMetrics.width = glyphPosition.width; - gMetrics.advance = glyphPosition.xAdvance; - gMetrics.bearingX = glyphPosition.xOffset; - gMetrics.bearingY = this->metrics.ascent; - - return new GlyphData(glyph, gMetrics); -} - -int BCFNTRasterizer::GetGlyphCount() const -{ - if (this->glyphCount != -1) - return this->glyphCount; - - /* cache this data, as it's slow and stupid */ - - FINF_s* fontInfo = C2D_FontGetInfo(this->font); - - for (auto map = fontInfo->cmap; map; map = map->next) - this->glyphCount += (map->codeEnd - map->codeBegin) + 1; - - return this->glyphCount; -} - -bool BCFNTRasterizer::HasGlyph(uint32_t glyph) const -{ - int glyphIndex = C2D_FontGlyphIndexFromCodePoint(this->font, glyph); - FINF_s* fontInfo = C2D_FontGetInfo(this->font); - - return glyphIndex != fontInfo->alterCharIndex; -} - -float BCFNTRasterizer::GetKerning(uint32_t /* leftGlyph */, uint32_t /* rightGlyph */) const -{ - return 0.0f; -} - -bool BCFNTRasterizer::Accepts(Data* data) -{ - return (!memcmp(data->GetData(), "CFNT", 4) || !memcmp(data->GetData(), "CFNU", 4)); -} diff --git a/platform/3ds/source/objects/canvas.cpp b/platform/3ds/source/objects/canvas.cpp deleted file mode 100644 index 54771bd97..000000000 --- a/platform/3ds/source/objects/canvas.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "objects/canvas/canvas.h" - -#include "citro2d/citro.h" -#include "modules/graphics/graphics.h" -#include "modules/window/window.h" - -using namespace love; - -Canvas::Canvas(const Canvas::Settings& settings) : common::Canvas(settings) -{ - C3D_TexInitVRAM(&this->citroTex, NextPO2(this->width), NextPO2(this->height), GPU_RGBA8); - - this->renderer = - C3D_RenderTargetCreateFromTex(&this->citroTex, GPU_TEXFACE_2D, 0, GPU_RB_DEPTH16); - - u16 width = this->citroTex.width; - u16 height = this->citroTex.height; - - this->subtex = { width, height, 0.0f, 1.0f, 1.0f, 0.0f }; - - // C2D_Image - this->texture = { &this->citroTex, &this->subtex }; - - ::citro2d::Instance().BindFramebuffer(this); - ::citro2d::Instance().ClearColor({ 0, 0, 0, 0 }); - ::citro2d::Instance().BindFramebuffer(); - - this->InitQuad(); -} - -Canvas::~Canvas() -{ - ::citro2d::Instance().DeferCallToEndOfFrame(([target = renderer, tex = citroTex]() mutable { - C3D_RenderTargetDelete(target); - C3D_TexDelete(&tex); - })); -} - -C3D_RenderTarget* Canvas::GetRenderer() -{ - return this->renderer; -} - -void Canvas::Draw(Graphics* gfx, Quad* quad, const Matrix4& localTransform) -{ - if (gfx->IsCanvasActive(this)) - throw love::Exception("Cannot render a Canvas to itself!"); - - Texture::Draw(gfx, quad, localTransform); -} - -void Canvas::Clear(const Colorf& color) -{ - C2D_TargetClear(this->renderer, C2D_Color32f(color.r, color.g, color.b, color.a)); - - if (!this->cleared) - this->cleared = true; -} diff --git a/platform/3ds/source/objects/font.cpp b/platform/3ds/source/objects/font.cpp deleted file mode 100644 index 18c23d07f..000000000 --- a/platform/3ds/source/objects/font.cpp +++ /dev/null @@ -1,180 +0,0 @@ -#include "objects/font/font.h" -#include "common/bidirectionalmap.h" -#include "modules/graphics/graphics.h" - -#include "citro2d/citro.h" - -#include - -using namespace love; - -Font::Font(Rasterizer* rasterizer, const Texture::Filter& filter) : - common::Font(filter), - rasterizer(rasterizer), - buffer(C2D_TextBufNew(Font::FONT_BUFFER_SIZE)) -{ - this->dpiScale = rasterizer->GetDPIScale(); - this->height = rasterizer->GetHeight(); - - this->lineHeight = rasterizer->GetLineHeight(); - - ::citro2d::GPUFilter gpuFilter = ::citro2d::GetCitroFilterMode(filter); - C2D_FontSetFilter(this->GetFont(), gpuFilter.mag, gpuFilter.min); -} - -Font::~Font() -{ - C2D_TextBufClear(this->buffer); - C2D_TextBufDelete(this->buffer); -} - -const C2D_Font Font::GetFont() -{ - auto r = static_cast(this->rasterizer.Get()); - return r->GetFont(); -} - -float Font::GetScale() const -{ - auto r = static_cast(this->rasterizer.Get()); - - return r->GetScale(); -} - -float Font::GetDPIScale() const -{ - return this->dpiScale; -} - -void Font::SetFilter(const Texture::Filter& filter) -{ - this->filter = filter; -} - -float Font::GetAscent() const -{ - return floorf(this->rasterizer->GetAscent() / this->dpiScale + 0.5f); -} - -float Font::GetDescent() const -{ - return floorf(this->rasterizer->GetDescent() / this->dpiScale + 0.5f); -} - -bool Font::HasGlyph(uint32_t glyph) const -{ - return this->rasterizer->HasGlyph(glyph); -} - -float Font::GetKerning(const std::string&, const std::string&) -{ - return 0.0f; -} - -float Font::GetKerning(uint32_t leftGlyph, uint32_t rightGlyph) -{ - return 0.0f; -} - -void Font::SetFallbacks(const std::vector& fallbacks) -{} - -float Font::GetBaseline() const -{ - float ascent = this->GetAscent(); - - if (ascent != 0.0f) - return ascent; - else if (this->rasterizer->GetDataType() == love::Rasterizer::DATA_BCFNT) - return floorf(this->GetHeight() / 1.0f); - else - return 0.0f; -} - -void Font::GetWrap(const std::vector& strings, float wrapLimit, - std::vector& lines, std::vector* lineWidths) -{} - -void Font::Print(Graphics* gfx, const std::vector& text, - const Matrix4& localTransform, const Colorf& color) -{ - C2D_Text citroText; - - std::string result = std::accumulate( - text.begin(), text.end(), std::string {}, - [](const std::string& s1, const ColoredString& piece) { return s1 + piece.string; }); - - C2D_TextFontParse(&citroText, this->GetFont(), this->buffer, result.c_str()); - C2D_TextOptimize(&citroText); - - Matrix4 t(gfx->GetTransform(), localTransform); - C2D_ViewRestore(&t.GetElements()); - - u32 renderColorf = C2D_Color32f(color.r, color.g, color.b, color.a); - C2D_DrawText(&citroText, C2D_WithColor, 0, 0, Graphics::CURRENT_DEPTH, this->GetScale(), - this->GetScale(), renderColorf); - - C2D_TextBufClear(this->buffer); -} - -void Font::Printf(Graphics* gfx, const std::vector& text, float wrap, - AlignMode align, const Matrix4& localTransform, const Colorf& color) -{ - C2D_Text citroText; - - u32 alignMode = C2D_WordWrap; - float offset = 0.0f; - - switch (align) - { - case Font::ALIGN_LEFT: - default: - alignMode |= C2D_AlignLeft; - break; - case Font::ALIGN_CENTER: - alignMode |= C2D_AlignCenter; - offset += wrap / 2; - break; - case Font::ALIGN_RIGHT: - alignMode |= C2D_AlignRight; - break; - case Font::ALIGN_JUSTIFY: - alignMode |= C2D_AlignJustified; - break; - } - - std::string result = std::accumulate( - text.begin(), text.end(), std::string {}, - [](const std::string& s1, const ColoredString& piece) { return s1 + piece.string; }); - - C2D_TextFontParse(&citroText, this->GetFont(), this->buffer, result.c_str()); - C2D_TextOptimize(&citroText); - - Matrix4 t(gfx->GetTransform(), localTransform); - C2D_ViewRestore(&t.GetElements()); - - u32 renderColorf = C2D_Color32f(color.r, color.g, color.b, color.a); - C2D_DrawText(&citroText, C2D_WithColor | alignMode, offset, 0, Graphics::CURRENT_DEPTH, - this->GetScale(), this->GetScale(), renderColorf, wrap); - - C2D_TextBufClear(this->buffer); -} - -int Font::GetWidth(uint32_t /* prevGlyph */, uint32_t current) -{ - auto found = this->glyphWidths.find(current); - - if (found != this->glyphWidths.end()) - return found->second; - - GlyphData* glyphData = this->rasterizer->GetGlyphData(current); - - this->glyphWidths[current] = glyphData->GetAdvance(); - - return this->glyphWidths[current]; -} - -float Font::GetHeight() const -{ - return 30 * this->GetScale(); -} diff --git a/platform/3ds/source/objects/gamepad.cpp b/platform/3ds/source/objects/gamepad.cpp deleted file mode 100644 index 9d53e01ef..000000000 --- a/platform/3ds/source/objects/gamepad.cpp +++ /dev/null @@ -1,339 +0,0 @@ -#include "objects/gamepad/gamepad.h" -#include "common/bidirectionalmap.h" -#include "driver/hidrv.h" - -using namespace love; - -#define INVALID_GAMEPAD_BUTTON static_cast(-1) - -Gamepad::Gamepad(size_t id) : common::Gamepad(id), buttonStates() -{} - -Gamepad::Gamepad(size_t id, size_t index) : common::Gamepad(id) -{ - this->Open(index); -} - -Gamepad::~Gamepad() -{ - this->Close(); -} - -bool Gamepad::Open(size_t index) -{ - this->Close(); - - this->name = "Nintendo 3DS"; - - return this->IsConnected(); -} - -void Gamepad::Close() -{ - this->instanceID = -1; - this->vibration = Vibration(); -} - -bool Gamepad::IsConnected() const -{ - return true; -} - -const char* Gamepad::GetName() const -{ - return this->name.c_str(); -} - -size_t Gamepad::GetAxisCount() const -{ - bool isN3DS = false; - APT_CheckNew3DS(&isN3DS); - - /* total axes */ - size_t axes = 12; - - if (!isN3DS) - return axes - 2; - - return axes; -} - -size_t Gamepad::GetButtonCount() const -{ - /* constant value anyways */ - return Gamepad::MAX_BUTTONS - 1; -} - -float Gamepad::GetAxis(size_t axis) const -{ - if (axis < 0 || axis >= this->GetAxisCount()) - return 0.0f; - - float value = 0.0f; - - if (axis == 1 || axis == 2) - { - circlePosition leftStick; - hidCircleRead(&leftStick); - - if (axis == 1) - value = leftStick.dx; - else - value = -leftStick.dy; - - return Gamepad::ClampValue(value / JOYSTICK_MAX); - } - else if (axis == 3 || axis == 4) - { - circlePosition rightStick; - irrstCstickRead(&rightStick); - - if (axis == 3) - value = rightStick.dx; - else - value = -rightStick.dy; - - return Gamepad::ClampValue(value / JOYSTICK_MAX); - } - else if (axis == 5) - { - if (hidKeysHeld() & KEY_ZL) - return 1.0f; - - return 0.0f; - } - else if (axis == 6) - { - if (hidKeysHeld() & KEY_ZR) - return 1.0f; - - return 0.0f; - } - else - { - if (axis >= 7 && axis < 10) - { - angularRate gyroscope; - hidGyroRead(&gyroscope); - - if (axis == 7) - return gyroscope.x; - else if (axis == 8) - return gyroscope.y; - - return gyroscope.z; - } - else if (axis >= 10 && axis < 13) - { - accelVector accelerometer; - hidAccelRead(&accelerometer); - - if (axis == 10) - return accelerometer.x; - else if (axis == 11) - return accelerometer.y; - - return accelerometer.z; - } - } - - return 0.0f; -} - -std::vector Gamepad::GetAxes() const -{ - std::vector axes; - size_t count = this->GetAxisCount(); - - if (count <= 0) - return axes; - - axes.reserve(count); - - for (size_t index = 0; index < count; index++) - axes.push_back(this->GetAxis(index)); - - return axes; -} - -void Gamepad::Update() -{ - this->buttonStates.pressed = hidKeysDown(); - this->buttonStates.released = hidKeysUp(); -} - -/* helper functions */ -bool Gamepad::IsDown(size_t index, ButtonMapping& button) -{ - uint32_t hidButton = 0; - - if (!this->buttonStates.pressed) - return false; - - const auto records = buttons.GetEntries().first; - - hidButton = static_cast(records[index].second); - - if (hidButton & this->buttonStates.pressed) - { - this->buttonStates.pressed ^= hidButton; - button = std::make_pair(records[index].first, index); - - return true; - } - - return false; -} - -bool Gamepad::IsUp(size_t index, ButtonMapping& button) -{ - uint32_t hidButton = 0; - - if (!this->buttonStates.released) - return false; - - const auto records = buttons.GetEntries().first; - - hidButton = static_cast(records[index].second); - - if (hidButton & this->buttonStates.released) - { - this->buttonStates.released ^= hidButton; - button = std::make_pair(records[index].first, index); - - return true; - } - - return false; -} - -bool Gamepad::IsHeld(size_t index, ButtonMapping& button) const -{ - uint32_t heldSet = hidKeysHeld(); - uint32_t hidButton; - - auto recordPair = buttons.GetEntries(); - auto records = recordPair.first; - - for (size_t i = 0; i < recordPair.second; i++) - { - if ((hidButton = static_cast(records[i].second)) & heldSet) - { - button = { records[i].first, i }; - break; - } - } - - return (heldSet & hidButton); -} - -bool Gamepad::IsDown(const std::vector& buttonsVector) const -{ - auto recordPair = buttons.GetEntries(); - - uint32_t heldSet = hidKeysHeld(); - auto records = recordPair.first; - - for (size_t button : buttonsVector) - { - if (button < 0 || button >= recordPair.second) - continue; - - if (heldSet & static_cast(records[button].second)) - return true; - } - - return false; -} - -bool Gamepad::IsGamepadDown(const std::vector& buttonsVector) const -{ - uint32_t heldSet = hidKeysHeld(); - - GamepadButton consoleButton = INVALID_GAMEPAD_BUTTON; - const char* name = nullptr; - - for (GamepadButton button : buttonsVector) - { - /* make sure our out button isn't invalid */ - if (!GetConstant(button, name)) - continue; - - /* convert to the proper button */ - if (!GetConstant(name, consoleButton)) - continue; - - if (heldSet & static_cast(consoleButton)) - return true; - } - - return false; -} - -float Gamepad::GetGamepadAxis(Gamepad::GamepadAxis axis) const -{ - const char* name = nullptr; - if (!Gamepad::GetConstant(axis, name)) - return 0.0f; - - switch (axis) - { - case GamepadAxis::GAMEPAD_AXIS_LEFTX: - return this->GetAxis(1); - case GamepadAxis::GAMEPAD_AXIS_LEFTY: - return this->GetAxis(2); - case GamepadAxis::GAMEPAD_AXIS_RIGHTX: - return this->GetAxis(3); - case GamepadAxis::GAMEPAD_AXIS_RIGHTY: - return this->GetAxis(4); - case GamepadAxis::GAMEPAD_AXIS_TRIGGERLEFT: - return this->GetAxis(5); - case GamepadAxis::GAMEPAD_AXIS_TRIGGERRIGHT: - return this->GetAxis(6); - default: - break; - } - - return 0.0f; -} - -bool Gamepad::IsVibrationSupported() -{ - return false; -} - -bool Gamepad::SetVibration(float left, float right, float duration) -{ - return false; -} - -bool Gamepad::SetVibration() -{ - return false; -} - -void Gamepad::GetVibration(float& left, float& right) -{ - left = 0.0f; - right = 0.0f; -} - -bool Gamepad::GetConstant(const char* in, Gamepad::GamepadAxis& out) -{ - return axes.Find(in, out); -} - -bool Gamepad::GetConstant(Gamepad::GamepadAxis in, const char*& out) -{ - return axes.ReverseFind(in, out); -} - -bool Gamepad::GetConstant(const char* in, GamepadButton& out) -{ - return buttons.Find(in, out); -} - -bool Gamepad::GetConstant(GamepadButton in, const char*& out) -{ - return buttons.ReverseFind(in, out); -} diff --git a/platform/3ds/source/objects/glyphdata.cpp b/platform/3ds/source/objects/glyphdata.cpp deleted file mode 100644 index 6af795dd7..000000000 --- a/platform/3ds/source/objects/glyphdata.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "objects/glyphdata/glyphdata.h" - -using namespace love; - -GlyphData::GlyphData(uint32_t glyph, GlyphMetrics metrics) : common::GlyphData(glyph, metrics) -{} - -GlyphData::GlyphData(const GlyphData& other) : common::GlyphData(other) -{} - -GlyphData* GlyphData::Clone() const -{ - return new GlyphData(*this); -} diff --git a/platform/3ds/source/objects/image.cpp b/platform/3ds/source/objects/image.cpp deleted file mode 100644 index e89b81e3b..000000000 --- a/platform/3ds/source/objects/image.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "objects/image/image.h" -#include "modules/graphics/graphics.h" - -#include "citro2d/citro.h" -#include "common/pixelformat.h" - -using namespace love; - -Image::Image(const Slices& slices, bool validate) : - Texture(data.GetTextureType()), - data(slices), - mipmapsType(MIPMAPS_NONE), - sRGB(false) -{ - if (validate && this->data.Validate() == MIPMAPS_DATA) - mipmapsType = MIPMAPS_DATA; -} - -Image::Image(const Slices& slices) : Image(slices, true) -{ - this->Init(slices.Get(0, 0)); -} - -Image::Image(TextureType type, PixelFormat format, int width, int height, int slices) : - Image(Slices(type), false) -{ - this->Init(format, width, height); -} - -void Image::Init(ImageDataBase* data) -{ - this->Init(data->GetFormat(), data->GetWidth(), data->GetHeight()); -} - -void Image::Init(PixelFormat format, int width, int height) -{ - this->texture.tex = new C3D_Tex(); - - int copyWidth = width; - if (!::citro2d::IsSizeValid(width)) - copyWidth = NextPO2(width); - - int copyHeight = height; - if (!::citro2d::IsSizeValid(height)) - copyHeight = NextPO2(height); - - GPU_TEXCOLOR color; - ::citro2d::GetConstant(format, color); - - if (!C3D_TexInit(this->texture.tex, copyWidth, copyHeight, color)) - throw love::Exception("Failed to initialize texture!"); - - size_t copySize = copyWidth * copyHeight * GetPixelFormatSize(format); - - if (this->data.Get(0, 0)) - memcpy(this->texture.tex->data, this->data.Get(0, 0)->GetData(), copySize); - else - memset(this->texture.tex->data, 0, copySize); - - C3D_TexFlush(this->texture.tex); - - this->format = format; - - this->width = width; - this->height = height; - - this->InitQuad(); - - this->SetFilter(this->filter); - this->SetWrap(this->wrap); -} - -void Image::ReplacePixels(const void* data, size_t size, const Rect& rect) -{ - if (!this->texture.tex) - throw love::Exception("Failed to replace pixels. Texture is uninitialized."); - - if (size == 0) - throw love::Exception("Failed to replace pixels. Data is nullptr."); - - size_t srcPowTwoWidth = NextPO2(rect.w); - size_t srcPowTwoHeight = NextPO2(rect.h); - - if (this->texture.tex->width == srcPowTwoWidth && this->texture.tex->height == srcPowTwoHeight) - { - memcpy(this->texture.tex->data, data, size); - return; - } - - /* love::Rect should be Po2 already */ - - auto getFunction = ImageData::GetPixelGetFunction(this->format); - auto setFunction = ImageData::GetPixelSetFunction(this->format); - - for (int y = 0; y < rect.h; y++) - { - for (int x = 0; x < rect.w; x++) - { - unsigned srcIndex = coordToIndex(srcPowTwoWidth, x, y); - unsigned dstIndex = coordToIndex(this->texture.tex->width, x, y); - - Colorf color {}; - - /* grab the pixel data from our source */ - const ImageData::Pixel* srcPixel = - reinterpret_cast((uint32_t*)data + srcIndex); - getFunction(srcPixel, color); - - /* set the pixel we got to ours */ - ImageData::Pixel* dstPixel = - reinterpret_cast((uint32_t*)this->texture.tex->data + dstIndex); - setFunction(color, dstPixel); - } - } - - C3D_TexFlush(this->texture.tex); -} - -Image::~Image() -{ - C3D_TexDelete(this->texture.tex); - delete this->texture.tex; -} diff --git a/platform/3ds/source/objects/quad.cpp b/platform/3ds/source/objects/quad.cpp deleted file mode 100644 index 4df9c9ae6..000000000 --- a/platform/3ds/source/objects/quad.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "objects/quad/quad.h" - -using namespace love; - -Quad::Quad(const Viewport& viewport, double sw, double sh) : common::Quad(sw, sh) -{ - this->Refresh(viewport, sw, sh); -} - -void Quad::Refresh(const Viewport& viewport, double sw, double sh) -{ - this->RefreshViewport(viewport, sw, sh); -} - -const Tex3DS_SubTexture& Quad::CalculateTex3DSViewport(const Viewport& viewport, C3D_Tex* texture) -{ - this->subTex.top = 1.0f - (viewport.y) / texture->height; - this->subTex.left = (viewport.x) / texture->width; - - this->subTex.right = (viewport.x + viewport.w) / texture->width; - this->subTex.bottom = 1.0f - ((viewport.y + viewport.h) / texture->height); - - this->subTex.width = viewport.w; - this->subTex.height = viewport.h; - - return this->subTex; -} diff --git a/platform/3ds/source/objects/source.cpp b/platform/3ds/source/objects/source.cpp deleted file mode 100644 index bbb4cdc19..000000000 --- a/platform/3ds/source/objects/source.cpp +++ /dev/null @@ -1,272 +0,0 @@ -#include "objects/source/source.h" - -using namespace love; - -StaticDataBuffer::StaticDataBuffer(void* data, size_t size) -{ - this->buffer.first = (s16*)linearAlloc(size); - this->buffer.second = size; - - memcpy(this->buffer.first, data, size); -} - -StaticDataBuffer::~StaticDataBuffer() -{ - linearFree(this->buffer.first); -} - -/* SOURCE IMPLEMENTATION */ - -Source::Source(Pool* pool, SoundData* sound) : common::Source(pool, sound) -{ - this->sources[0] = ndspWaveBuf(); - this->sources[0].nsamples = sound->GetSampleCount(); -} - -Source::Source(Pool* pool, Decoder* decoder) : common::Source(pool, decoder) -{ - this->InitializeStreamBuffers(decoder); -} - -Source::Source(const Source& other) : common::Source(other) -{ - this->InitializeStreamBuffers(this->decoder.Get()); -} - -love::Source* Source::Clone() -{ - return new Source(*this); -} - -Source::~Source() -{ - this->Stop(); - this->FreeBuffer(); -} - -void Source::InitializeStreamBuffers(Decoder* decoder) -{ - if (!this->sourceBuffer) - this->sourceBuffer = linearAlloc(decoder->GetSize() * 2); - - for (size_t i = 0; i < MAX_BUFFERS; i++) - { - auto buffer = (s16*)(((size_t)this->sourceBuffer) + i * decoder->GetSize()); - this->sources[i] = - ndspWaveBuf { .data_pcm16 = buffer, .nsamples = 0, .status = NDSP_WBUF_DONE }; - } -} - -void Source::FreeBuffer() -{ - if (this->sourceType != TYPE_STATIC) - linearFree(this->sourceBuffer); -} - -void Source::SetVolume(float volume) -{ - this->volume = volume; - - float mix[12]; - memset(mix, 0, sizeof(mix)); - - mix[0] = mix[1] = volume; - - ndspChnSetMix(this->channel, mix); -} - -/* IMPORTANT STUFF */ - -void Source::Reset() -{ - ndspChnReset(this->channel); - - u16 format = NDSP_FORMAT_STEREO_PCM16; - - switch (this->channels) - { - case 1: - if (this->bitDepth == 8) - format = NDSP_FORMAT_MONO_PCM8; - else - format = NDSP_FORMAT_MONO_PCM16; - - break; - case 2: - if (this->bitDepth == 8) - format = NDSP_FORMAT_STEREO_PCM8; - else - format = NDSP_FORMAT_STEREO_PCM16; - - break; - default: - break; - } - - ndspInterpType interpType = (this->channels == 2) ? NDSP_INTERP_POLYPHASE : NDSP_INTERP_LINEAR; - - ndspChnSetFormat(this->channel, format); - ndspChnSetRate(this->channel, this->sampleRate); - ndspChnSetInterp(this->channel, interpType); - this->SetVolume(this->GetVolume()); -} - -bool Source::Update() -{ - if (!this->valid) - return false; - - switch (this->sourceType) - { - case TYPE_STATIC: - return !this->IsFinished(); - case TYPE_STREAM: - { - if (this->IsFinished()) - return false; - - for (int which = 0; which < Source::MAX_BUFFERS; which++) - { - if (this->sources[which].status == NDSP_WBUF_DONE) - { - this->offsetSamples += this->sources[which].nsamples; - - int decoded = this->StreamAtomic(which); - - if (decoded == 0) - return false; - - ndspChnWaveBufAdd(this->channel, &this->sources[which]); - } - } - - return true; - } - case TYPE_QUEUE: - break; - case TYPE_MAX_ENUM: - default: - break; - } - - return false; -} - -double Source::GetSampleOffset() -{ - return ndspChnGetSamplePos(this->channel); -} - -void Source::PrepareAtomic() -{ - this->Reset(); - - switch (this->sourceType) - { - case TYPE_STATIC: - this->sources[0].data_pcm16 = (s16*)this->staticBuffer->GetBuffer(); - this->sources[0].looping = this->looping; - DSP_FlushDataCache(this->sources[0].data_pcm16, this->staticBuffer->GetSize()); - break; - case TYPE_STREAM: - { - if (this->StreamAtomic(0) == 0) - break; - - if (this->decoder->IsFinished()) - break; - - break; - } - case TYPE_QUEUE: - default: - break; - } -} - -int Source::StreamAtomic(size_t which) -{ - auto buffer = this->sources[which].data_pcm16; - int decoded = std::max(decoder->Decode(buffer), 0); - - if (decoded > 0) - DSP_FlushDataCache(buffer, decoded); - - if (this->decoder->IsFinished() && this->IsLooping()) - this->decoder->Rewind(); - - this->sources[which].nsamples = (int)((decoded / this->channels) / (this->bitDepth / 8)); - - return decoded; -} - -/* IS IT DOING STUFF */ - -bool Source::IsPlaying() const -{ - return this->valid && !ndspChnIsPaused(this->channel); -} - -bool Source::IsFinished() const -{ - if (!this->valid) - return false; - - if (this->sourceType == TYPE_STREAM && (this->IsLooping() || !this->decoder->IsFinished())) - return false; - - if (this->sourceType == TYPE_STATIC) - return this->sources[0].status == NDSP_WBUF_DONE; - - return ndspChnIsPlaying(this->channel) == false; -} - -/* ATOMIC STATES */ - -bool Source::PlayAtomic() -{ - this->PrepareAtomic(); - - /* add the initial wavebuffer */ - ndspChnWaveBufAdd(this->channel, &this->sources[0]); - - if (this->sourceType != TYPE_STREAM) - this->offsetSamples = 0; - - if (this->sourceType == TYPE_STREAM) - this->valid = true; - - return true; -} - -void Source::PauseAtomic() -{ - if (this->valid) - ndspChnSetPaused(this->channel, true); -} - -void Source::ResumeAtomic() -{ - if (this->valid && !this->IsPlaying()) - ndspChnSetPaused(this->channel, false); -} - -void Source::ClearChannel() -{ - ndspChnWaveBufClear(this->channel); -} - -void Source::StopAtomic() -{ - if (!this->valid) - return; - - this->TeardownAtomic(); -} - -void Source::SetLooping(bool should) -{ - this->looping = should; - if (this->valid && this->sourceType == TYPE_STATIC) - this->sources[0].looping = should; -} diff --git a/platform/3ds/source/objects/text.cpp b/platform/3ds/source/objects/text.cpp deleted file mode 100644 index 5fb1cc641..000000000 --- a/platform/3ds/source/objects/text.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "objects/text/text.h" -#include "modules/graphics/graphics.h" - -#include - -using namespace love; - -Text::Text(love::Font* font, const std::vector& text) : - common::Text(font, text) -{ - this->buffer = C2D_TextBufNew(Font::FONT_BUFFER_SIZE); - this->Set(text); -} - -Text::~Text() -{ - C2D_TextBufDelete(this->buffer); -} - -void Text::SetFont(love::Font* font) -{ - this->font.Set(font); -} - -std::string Text::GetString(const std::vector& text) -{ - std::string result = std::accumulate( - text.begin(), text.end(), std::string {}, - [](const std::string& s1, const Font::ColoredString& piece) { return s1 + piece.string; }); - - return result; -} - -/* -** Text objects on 3DS can't have -** a lot of .. text? Like using add(f) -** So just return the entire string width -*/ -int Text::GetWidth(int index) const -{ - int width = this->font->GetWidth(this->textCache); - return width; -} - -/* -** Text objects on 3DS can't have -** a lot of .. text? Like using add(f)\ -** So just return the entire string height -*/ -int Text::GetHeight(int index) const -{ - return this->font->GetHeight(); -} - -void Text::Set(const std::vector& text) -{ - return this->Set(text, -1.0f, Font::ALIGN_MAX_ENUM); -} - -void Text::Set(const std::vector& text, float wrap, Font::AlignMode align) -{ - if (text.empty() || (text.size() == 1 && text[0].string.empty())) - return this->Clear(); - - this->textCache = this->GetString(text); - - this->wrap = wrap; - this->align = align; - - C2D_TextBufClear(this->buffer); - C2D_TextFontParse(&this->text, this->font->GetFont(), this->buffer, this->textCache.c_str()); - C2D_TextOptimize(&this->text); -} - -int Text::Add(const std::vector& text, const Matrix4& localTransform) -{ - return this->Addf(text, -1.0f, Font::ALIGN_MAX_ENUM, localTransform); -} - -/* -** Galaxy brain move here: -** always return 0 because we don't -** use a vector of data for strings on 3DS -*/ -int Text::Addf(const std::vector& text, float wrap, Font::AlignMode align, - const Matrix4& localTransform) -{ - if (!this->wrapData.empty() && - (wrap != this->wrapData.back().first || - (align != this->wrapData.back().second && align != Font::ALIGN_MAX_ENUM))) - throw love::Exception("addf cannot handle multiple wraps and aligns on this console."); - - this->textCache += this->GetString(text); - - this->wrap = wrap; - this->align = align; - - this->wrapData.push_back(std::make_pair(wrap, align)); - - C2D_TextBufClear(this->buffer); - C2D_TextFontParse(&this->text, this->font->GetFont(), this->buffer, this->textCache.c_str()); - C2D_TextOptimize(&this->text); - - return 0; -} - -void Text::Draw(Graphics* gfx, const Matrix4& localTransform) -{ - Matrix4 t(gfx->GetTransform(), localTransform); - C2D_ViewRestore(&t.GetElements()); - - Colorf color = gfx->GetColor(); - - u32 renderColorf = C2D_Color32f(color.r, color.g, color.b, color.a); - u32 flags = C2D_WithColor; - - u32 alignMode = C2D_WordWrap; - float offset = 0.0f; - - switch (this->align) - { - case Font::ALIGN_LEFT: - alignMode |= C2D_AlignLeft; - break; - case Font::ALIGN_CENTER: - alignMode |= C2D_AlignCenter; - offset += wrap / 2; - break; - case Font::ALIGN_RIGHT: - alignMode |= C2D_AlignRight; - break; - case Font::ALIGN_JUSTIFY: - alignMode |= C2D_AlignJustified; - break; - default: - break; - } - - if (this->align != Font::ALIGN_MAX_ENUM) - flags |= alignMode; - - /* wrap will be discarded if there's no align mode specified */ - C2D_DrawText(&this->text, flags, offset, 0, Graphics::CURRENT_DEPTH, this->font->GetScale(), - this->font->GetScale(), renderColorf, this->wrap); -} - -void Text::Clear() -{ - C2D_TextBufClear(this->buffer); - this->textCache.clear(); - this->wrapData.clear(); -} \ No newline at end of file diff --git a/platform/3ds/source/objects/texture.cpp b/platform/3ds/source/objects/texture.cpp deleted file mode 100644 index 9cdc71da5..000000000 --- a/platform/3ds/source/objects/texture.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "citro2d/citro.h" - -#include "modules/graphics/graphics.h" -#include "objects/texture/texture.h" - -using namespace love; - -Texture::Texture(TextureType type) : common::Texture(TEXTURE_2D) -{} - -void Texture::Draw(Graphics* gfx, const Matrix4& localTransform) -{ - this->Draw(gfx, this->quad, localTransform); -} - -const C2D_Image& Texture::GetHandle() -{ - return this->texture; -} - -bool Texture::SetWrap(const Wrap& wrap) -{ - ::citro2d::Instance().SetTextureWrap(this, wrap); - return true; -} - -void Texture::SetFilter(const Filter& filter) -{ - ::citro2d::Instance().SetTextureFilter(filter); -} - -void Texture::Draw(Graphics* gfx, love::Quad* quad, const Matrix4& localTransform) -{ - Quad::Viewport v = quad->GetViewport(); - - Tex3DS_SubTexture tv = quad->CalculateTex3DSViewport(v, this->texture.tex); - this->texture.subtex = &tv; - - // Multiply the current and local transforms - Matrix4 t(gfx->GetTransform(), localTransform); - - C2D_DrawParams params; - - params.pos = { 0.0f, 0.0f, (float)v.w, (float)v.h }; - params.depth = Graphics::CURRENT_DEPTH; - params.angle = 0.0f; - params.center = { 0.0f, 0.0f }; - - C2D_ViewRestore(&t.GetElements()); - - C2D_ImageTint tint; - Colorf color = gfx->GetColor(); - - C2D_PlainImageTint(&tint, C2D_Color32f(color.r, color.g, color.b, color.a), 1); - C2D_DrawImage(this->texture, ¶ms, &tint); -} diff --git a/platform/3ds/source/objects/theorastream.cpp b/platform/3ds/source/objects/theorastream.cpp deleted file mode 100644 index f65fe2802..000000000 --- a/platform/3ds/source/objects/theorastream.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "common/lmath.h" - -#include "modules/thread/types/lock.h" -#include "objects/videostream/theorastream.h" - -#include "citro2d/citro.h" -#include "common/pixelformat.h" - -using namespace love; - -TheoraStream::Frame::Frame() -{ - this->buffer = new C3D_Tex(); -} - -TheoraStream::Frame::~Frame() -{ - C3D_TexDelete(this->buffer); - delete this->buffer; -} - -TheoraStream::TheoraStream(File* file) : common::TheoraStream(file) -{ - this->frontBuffer = new Frame(); - this->backBuffer = new Frame(); - - th_info_init(&this->info); - - try - { - this->ParseHeader(); - } - catch (love::Exception& exception) - { - delete this->frontBuffer; - delete this->backBuffer; - - th_info_clear(&this->info); - - throw exception; - } -} - -size_t TheoraStream::GetSize() const -{ - return sizeof(Frame); -} - -void TheoraStream::SetupBuffers() -{ - switch (this->info.pixel_fmt) - { - case TH_PF_420: - break; - case TH_PF_422: - break; - case TH_PF_444: - throw love::Exception("YUV444 is not supported by Y2R"); - return; - case TH_PF_RSVD: - default: - throw love::Exception("UNKNOWN Chroma sampling!"); - return; - } - - this->format = this->info.pixel_fmt; - - int calcWidth = - ((this->info.pic_x + this->info.frame_width + 1) & ~1) - (this->info.pic_x & ~1); - int calcHeight = - ((this->info.pic_y + this->info.frame_height + 1) & ~1) - (this->info.pic_y & ~1); - - this->width = calcWidth; - this->height = calcHeight; - - int powTwoWidth = NextPO2(calcWidth); - int powTwoHeight = NextPO2(calcHeight); - - Frame* buffers[2] = { (Frame*)this->backBuffer, (Frame*)this->frontBuffer }; - - for (int index = 0; index < 2; index++) - { - C3D_Tex* texture = buffers[index]->buffer; - - C3D_TexInit(texture, powTwoWidth, powTwoHeight, GPU_RGB8); - C3D_TexSetFilter(texture, GPU_LINEAR, GPU_LINEAR); - - memset(texture->data, 0, texture->size); - - buffers[index]->width = calcWidth; - buffers[index]->height = calcHeight; - } -} - -void TheoraStream::SetPostProcessingLevel() -{ - if (this->quality.offset) - { - this->quality.current += this->quality.offset; - th_decode_ctl(this->decoder, TH_DECCTL_SET_PPLEVEL, &this->quality.current, - sizeof(this->quality.current)); - this->quality.offset = 0; - } -} - -void TheoraStream::FillBufferData(th_ycbcr_buffer bufferInfo) -{ - bool isBusy = true; - - Y2RU_StopConversion(); - - while (isBusy) - Y2RU_IsBusyConversion(&isBusy); - - switch (this->format) - { - case TH_PF_420: - Y2RU_SetInputFormat(Y2RU_InputFormat::INPUT_YUV420_INDIV_8); - break; - case TH_PF_422: - Y2RU_SetInputFormat(Y2RU_InputFormat::INPUT_YUV422_INDIV_8); - break; - default: - break; - } - - Y2RU_SetOutputFormat(Y2RU_OutputFormat::OUTPUT_RGB_24); - Y2RU_SetRotation(Y2RU_Rotation::ROTATION_NONE); - Y2RU_SetBlockAlignment(Y2RU_BlockAlignment::BLOCK_8_BY_8); - Y2RU_SetTransferEndInterrupt(true); - Y2RU_SetInputLineWidth(this->width); - Y2RU_SetInputLines(this->height); - Y2RU_SetStandardCoefficient(Y2RU_StandardCoefficient::COEFFICIENT_ITU_R_BT_601_SCALING); - Y2RU_SetAlpha(0xFF); - - /* set up the YUV data for Y2RU */ - - Y2RU_SetSendingY(bufferInfo[0].data, this->width * this->height, this->width, - bufferInfo[0].stride - this->width); - - Y2RU_SetSendingU(bufferInfo[1].data, (this->width / 2) * (this->height / 2), this->width / 2, - bufferInfo[1].stride - (this->width >> 1)); - - Y2RU_SetSendingV(bufferInfo[2].data, (this->width / 2) * (this->height / 2), this->width / 2, - bufferInfo[2].stride - (this->width >> 1)); - - PixelFormat format; - ::citro2d::GetConstant(((Frame*)this->backBuffer)->buffer->fmt, format); - - size_t formatSize = GetPixelFormatSize(format); - - Y2RU_SetReceiving(((Frame*)this->backBuffer)->buffer->data, - this->width * this->height * formatSize, this->width * 8 * formatSize, - (NextPO2(this->width) - this->width) * 8 * formatSize); - - /* convert the data */ - - Y2RU_StartConversion(); - - Y2RU_GetTransferEndEvent(&this->handle); -} diff --git a/platform/3ds/source/objects/thread.cpp b/platform/3ds/source/objects/thread.cpp deleted file mode 100644 index fd1a58421..000000000 --- a/platform/3ds/source/objects/thread.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "objects/thread/thread.h" -#include "thread/types/lock.h" - -love::Thread::Thread(Threadable* t) : common::Thread(t) -{} - -love::Thread::~Thread() -{ - if (this->thread) - threadDetach(this->thread); -} - -void love::Thread::Wait() -{ - { - thread::Lock lock(this->mutex); - - if (!this->thread) - return; - } - - threadJoin(this->thread, U64_MAX); - threadFree(this->thread); - - thread::Lock(this->mutex); - - this->running = false; - this->thread = nullptr; -} - -bool love::Thread::Start() -{ - thread::Lock lock(this->mutex); - - if (this->running) - return false; - - /* Clean up the old handle properly */ - - if (this->thread) - { - threadJoin(this->thread, U64_MAX); - threadFree(this->thread); - } - - s32 priority = love::common::Thread::GetCurrentThreadPriority(); - - /* do not detach because otherwise it cannot be freed or joined */ - this->thread = threadCreate(Runner, this, Thread::STACK_SIZE, priority - 1, 0, false); - - this->running = (this->thread != nullptr); - - return this->running; -} diff --git a/platform/3ds/source/objects/video.cpp b/platform/3ds/source/objects/video.cpp deleted file mode 100644 index a80a55855..000000000 --- a/platform/3ds/source/objects/video.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "objects/video/video.h" -#include "objects/videostream/theorastream.h" - -#include "modules/graphics/graphics.h" - -using namespace love; - -Video::Video(Graphics* graphics, VideoStream* stream, float dpiScale) : - common::Video(graphics, stream, dpiScale) -{ - this->stream->FillBackBuffer(); - - auto frame = (const TheoraStream::Frame*)this->stream->GetFrontBuffer(); - Rect rect = { 0, 0, frame->width, frame->height }; - - /* initialize all these StrongReference items, so setFilter will be happy */ - for (int index = 0; index < 3; index++) - { - Image* image = graphics->NewImage(Texture::TEXTURE_2D, PixelFormat::PIXELFORMAT_RGB8, - rect.w, rect.h, 1); - - image->ReplacePixels(frame->buffer->data, frame->buffer->size, rect); - this->images[index].Set(image, Acquire::NORETAIN); - } -} - -Video::~Video() -{} - -void Video::Update() -{ - bool buffersChanged = this->stream->SwapBuffers(); - this->stream->FillBackBuffer(); - - if (buffersChanged) - { - auto frame = (const TheoraStream::Frame*)this->stream->GetFrontBuffer(); - Rect rect = { 0, 0, frame->width, frame->height }; - - for (int index = 0; index < 1; index++) - this->images[index]->ReplacePixels(frame->buffer->data, frame->buffer->size, rect); - } -} - -void Video::Draw(Graphics* graphics, const Matrix4& localTransform) -{ - this->Update(); - - this->images[0]->Draw(graphics, localTransform); -} diff --git a/platform/3ds/source/pools/pool.cpp b/platform/3ds/source/pools/pool.cpp deleted file mode 100644 index f5f31734b..000000000 --- a/platform/3ds/source/pools/pool.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "modules/audio/pool/pool.h" -#include "modules/audio/audio.h" - -using namespace love; - -void Pool::Sleep() -{ - LightEvent_Wait(&driver::Audrv::Instance().GetEvent()); -} diff --git a/platform/3ds/source/runtime.cpp b/platform/3ds/source/runtime.cpp deleted file mode 100644 index 51324ea93..000000000 --- a/platform/3ds/source/runtime.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include <3ds.h> -#include - -#include "common/results.h" - -#define SOC_BUFSIZE 0x100000 -#define BUFFER_ALIGN 0x1000 - -u32* SOCKET_BUFFER = nullptr; - -extern "C" -{ - void userAppInit() - { - osSetSpeedupEnable(true); - - /* raw battery info */ - R_ABORT_UNLESS(mcuHwcInit()); - - /* battery state */ - R_ABORT_UNLESS(ptmuInit()); - - /* region info and fonts */ - R_ABORT_UNLESS(cfguInit()); - - /* wifi state */ - R_ABORT_UNLESS(acInit()); - - /* friends */ - R_ABORT_UNLESS(frdInit()); - - /* wireless */ - SOCKET_BUFFER = (u32*)memalign(BUFFER_ALIGN, SOC_BUFSIZE); - R_ABORT_LAMBDA_UNLESS(socInit(SOCKET_BUFFER, SOC_BUFSIZE), [&]() { free(SOCKET_BUFFER); }); - - R_ABORT_UNLESS(y2rInit()); - - /* accelerometer */ - HIDUSER_EnableAccelerometer(); - - /* gyroscope */ - HIDUSER_EnableGyroscope(); - - /* "enable" preemptive threads */ - APT_SetAppCpuTimeLimit(30); - } - - void userAppExit() - { - y2rExit(); - - HIDUSER_DisableGyroscope(); - - HIDUSER_DisableAccelerometer(); - - socExit(); - free(SOCKET_BUFFER); - - frdExit(); - - acExit(); - - cfguExit(); - - ptmuExit(); - - mcuHwcExit(); - } -} diff --git a/platform/3ds/source/scripts/window_button.lua b/platform/3ds/source/scripts/window_button.lua deleted file mode 100644 index 4a0aea019..000000000 --- a/platform/3ds/source/scripts/window_button.lua +++ /dev/null @@ -1,52 +0,0 @@ -local __metatable = {} -local Button = {} - -function Button.new(text, index, position, width) - local button = {} - - button.text = text - button.index = index - - button.position = position - button.size = { width = width, height = 40 } - - button.textColorNormal = {0.33, 0.33, 0.33} - button.textColorPressed = {0.99, 0.99, 0.98} - button.textColorCurrent = button.textColorNormal - - function button:getIndex() - return self.index - end - - function button:draw(opacity) - local r, g, b = unpack(button.textColorCurrent) - love.graphics.setColor(r, g, b, opacity) - - local font = love.graphics.getFont() - love.graphics.printf(self.text, self.position.x, self.position.y + (self.size.height - font:getHeight()) * 0.5, self.size.width, "center") - end - - local function coordsInside(x, y, w, h, mouse_x, mouse_y) - return (mouse_x > x and mouse_x + 1 < x + w and mouse_y > y and mouse_y + 1 < y + h) - end - - function button:touchpressed(x, y) - if coordsInside(self.position.x, self.position.y, self.size.width, self.size.height, x, y) then - self.textColorCurrent = self.textColorPressed - return true - end - end - - function button:touchreleased(x, y) - if coordsInside(self.position.x, self.position.y, self.size.width, self.size.height, x, y) then - self.textColorCurrent = self.textColorNormal - return true - end - end - - return setmetatable(button, __metatable) -end - -return setmetatable(Button, {__call = function(_, ...) - return Button.new(...) -end}) diff --git a/platform/3ds/source/scripts/window_messagebox.lua b/platform/3ds/source/scripts/window_messagebox.lua deleted file mode 100644 index 0e5230c9a..000000000 --- a/platform/3ds/source/scripts/window_messagebox.lua +++ /dev/null @@ -1,144 +0,0 @@ -local Button = require("window_button") -local MessageBox = {} - -local __metatable = {} - -local _SCREEN_SIZE = { WIDTH = 320, HEIGHT = 240 } - -local _MESSGB_SIZE = { WIDTH = 300, HEIGHT = 216 } -local _MESSGB_POSN = { (_SCREEN_SIZE.WIDTH - _MESSGB_SIZE.WIDTH) * 0.5, - (_SCREEN_SIZE.HEIGHT - _MESSGB_SIZE.HEIGHT) * 0.5 } - -MessageBox.States = -{ - SINGLE = "single", - DOUBLE = "double" -} - --- Pressed Events -MessageBox.PRESSED_EVENTS = {} - -MessageBox.PRESSED_EVENTS.mousepressed = true -MessageBox.PRESSED_EVENTS.touchpressed = true - --- Released Events -MessageBox.RELEASED_EVENTS = {} - -MessageBox.RELEASED_EVENTS.mousereleased = true -MessageBox.RELEASED_EVENTS.touchreleased = true - -MessageBox.Button = 1 -MessageBox.Inited = false - -MessageBox.Textures = {} - -function MessageBox.initTextures() - if MessageBox.Inited then - return - end - - MessageBox.Textures.single = - { - love.graphics.newImage("internal/graphics/messagebox_single_none.png"), - love.graphics.newImage("internal/graphics/messagebox_single_pressed.png") - } - - MessageBox.Textures.double = - { - love.graphics.newImage("internal/graphics/messagebox_two_none.png"), - love.graphics.newImage("internal/graphics/messagebox_two_pressed_left.png"), - love.graphics.newImage("internal/graphics/messagebox_two_pressed_right.png") - } - - MessageBox.Inited = true -end - -function MessageBox.new(text, buttons) - MessageBox.initTextures() - - local messagebox = {} - - messagebox.text = text - - messagebox.state = #buttons == 1 and MessageBox.States.SINGLE or MessageBox.States.DOUBLE - messagebox.defaultTexture = MessageBox.Textures[messagebox.state][1] - - messagebox.currentTexture = messagebox.defaultTexture - messagebox.opacity = 0 - messagebox.released = false - - messagebox.buttons = {} - for index = 1, #buttons do - local width = (#buttons == 1 and _MESSGB_SIZE.WIDTH or _MESSGB_SIZE.WIDTH * 0.5) - local position = (#buttons == 2 and width or 0) - - local button = Button(buttons[index], index, { x = _MESSGB_POSN[1] + (index - 1) * position, y = (_MESSGB_POSN[2] + _MESSGB_SIZE.HEIGHT) - 40 }, width) - table.insert(messagebox.buttons, button) - end - - function messagebox:poll(name, ...) - local args = {...} - table.remove(args, 1) - - local x, y = args[1], args[2] - - if MessageBox.PRESSED_EVENTS[name] then - for _, button in ipairs(self.buttons) do - if button:touchpressed(x, y) then - MessageBox.Button = button:getIndex() + 1 - self.currentTexture = MessageBox.Textures[self.state][MessageBox.Button] - break - end - end - elseif MessageBox.RELEASED_EVENTS[name] then - self.currentTexture = self.defaultTexture - self.released = true - end - end - - function messagebox:getPressedButton() - return MessageBox.Button - end - - function messagebox:update(dt) - if self.released and self.opacity == 0 then - return true - end - - if MessageBox.Button ~= 1 and self.released then - self.opacity = math.max(self.opacity - dt / 0.20, 0) - else - self.opacity = math.min(self.opacity + dt / 0.20, 1) - end - end - - local font = love.graphics.newFont(20) - - function messagebox:draw() - love.graphics.push("all") - - love.graphics.setColor(1, 1, 1, self.opacity) - love.graphics.draw(self.currentTexture, _MESSGB_POSN[1], _MESSGB_POSN[2]) - - love.graphics.setFont(font) - - love.graphics.setColor(0.33, 0.33, 0.33) - love.graphics.printf(self.text, _MESSGB_POSN[1], _MESSGB_POSN[2] + 12, _MESSGB_SIZE.WIDTH, "center") - - for _, button in ipairs(self.buttons) do - button:draw(self.opacity) - end - - love.graphics.pop() - end - - return setmetatable(messagebox, __metatable) -end - -__metatable.__call = function(_, ...) - return MessageBox.new(...) -end - -return setmetatable(MessageBox, {__call = function(_, ...) - return MessageBox.new(...) -end}) diff --git a/platform/3ds/source/scripts/wrap_window.lua b/platform/3ds/source/scripts/wrap_window.lua deleted file mode 100644 index dd0b1942f..000000000 --- a/platform/3ds/source/scripts/wrap_window.lua +++ /dev/null @@ -1,62 +0,0 @@ -local MessageBox = require("window_messagebox") - -local dt = 0 -local messageBox = nil - -local function main() - local normalScreens = love.graphics.getScreens() - local plainScreens = {"top", "bottom"} - - while true do - if love.event and love.event.pump then - love.event.pump() - - for name, a, b, c, d, e, f in love.event.poll() do - if name == "quit" then - return 0 - else - messageBox:poll(name, a, b, c, d, e, f) - end - end - end - - if love.timer then - dt = love.timer.step() - end - - if messageBox:update(dt) then - g_windowShown = false - return messageBox:getPressedButton() - end - - if love.graphics then - local screens = love.graphics.get3D() and normalScreens or plainScreens - - for _, screen in ipairs(screens) do - love.graphics.origin() - - love.graphics.setActiveScreen(screen) - love.graphics.clear(love.graphics.getBackgroundColor()) - - if screen == "bottom" then - messageBox:draw() - end - end - - love.graphics.present() - end - - if love.timer then - love.timer.sleep(0.001) - end - end -end - -function love.window.showMessageBox(_, text, buttons) - assert(#buttons > 0 and #buttons <= 2, "Cannot have more than two buttons") - - messageBox = MessageBox(text, buttons) - g_windowShown = true - - return main() -end diff --git a/platform/cafe/CMakeLists.txt b/platform/cafe/CMakeLists.txt new file mode 100644 index 000000000..08b46614b --- /dev/null +++ b/platform/cafe/CMakeLists.txt @@ -0,0 +1,50 @@ +# declare an asset target for the executable's content (optional) +dkp_add_asset_target(${PROJECT_NAME}_cafe_content content) + +dkp_track_assets(${PROJECT_NAME}_cafe_content + FOLDER shaders + FILES + color.gsh + texture.gsh +) + +dkp_track_assets(${PROJECT_NAME}_cafe_content + FOLDER nogame + FILES + cartridge.png + nogame.png +) + +target_include_directories(${PROJECT_NAME} PRIVATE + include +) + +# find source -type f | grep "\.cpp$" | clip +target_sources(${PROJECT_NAME} PRIVATE + source/common/matrix_ext.cpp + source/common/screen_ext.cpp + source/modules/fontmodule_ext.cpp + source/modules/graphics_ext.cpp + source/modules/imagemodule_ext.cpp + source/modules/joystickmodule_ext.cpp + source/modules/keyboard_ext.cpp + source/modules/love_ext.cpp + source/modules/system_ext.cpp + source/modules/timer_ext.cpp + source/modules/window_ext.cpp + source/objects/gamepad.cpp + source/objects/procontroller.cpp + ${PROJECT_SOURCE_DIR}/source/objects/truetyperasterizer/truetyperasterizer.cpp + source/objects/shader_ext.cpp + source/objects/source_ext.cpp + source/objects/texture_ext.cpp + source/objects/wrap_imagedata_ext.cpp + source/utilities/driver/dsp_ext.cpp + source/utilities/driver/framebuffer.cpp + source/utilities/driver/hid_ext.cpp + source/utilities/driver/renderer_ext.cpp + source/utilities/haptics/vibration_ext.cpp + source/utilities/sensor/accelerometer.cpp + source/utilities/sensor/gyroscope.cpp + source/utilities/wpad.cpp +) diff --git a/platform/cafe/content/nogame/cartridge.png b/platform/cafe/content/nogame/cartridge.png new file mode 100644 index 000000000..cf0529bd9 Binary files /dev/null and b/platform/cafe/content/nogame/cartridge.png differ diff --git a/platform/cafe/content/nogame/nogame.png b/platform/cafe/content/nogame/nogame.png new file mode 100644 index 000000000..ed4892350 Binary files /dev/null and b/platform/cafe/content/nogame/nogame.png differ diff --git a/platform/cafe/content/shaders/color.gsh b/platform/cafe/content/shaders/color.gsh new file mode 100644 index 000000000..edbc8688c Binary files /dev/null and b/platform/cafe/content/shaders/color.gsh differ diff --git a/platform/cafe/content/shaders/texture.gsh b/platform/cafe/content/shaders/texture.gsh new file mode 100644 index 000000000..88f915a67 Binary files /dev/null and b/platform/cafe/content/shaders/texture.gsh differ diff --git a/platform/cafe/icon-dev.png b/platform/cafe/icon-dev.png new file mode 100644 index 000000000..9ed94e32b Binary files /dev/null and b/platform/cafe/icon-dev.png differ diff --git a/platform/cafe/icon.png b/platform/cafe/icon.png new file mode 100644 index 000000000..341a1b569 Binary files /dev/null and b/platform/cafe/icon.png differ diff --git a/platform/cafe/include/common/matrix_ext.hpp b/platform/cafe/include/common/matrix_ext.hpp new file mode 100644 index 000000000..15f77240d --- /dev/null +++ b/platform/cafe/include/common/matrix_ext.hpp @@ -0,0 +1,159 @@ +#pragma once + +#include + +namespace love +{ + template<> + class Matrix4 : public Matrix4 + { + public: + Matrix4(); + + Matrix4(const float elements[16]); + + Matrix4(const Matrix4& a, const Matrix4& b); + + Matrix4(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, + float ky); + + Matrix4(float t00, float t10, float t01, float t11, float x, float y); + + void SetIdentity(); + + void Transpose() + {} + + void SetTranslation(float x, float y); + + void Translate(float x, float y); + + void Rotate(float r); + + void Scale(float sx, float sy); + + void Shear(float kx, float ky); + + bool IsAffine2DTransform() const; + + bool IsAffine3DTransform() const; + + Matrix4 Inverse() const; + + void SetRawTransformation(float t00, float t10, float t01, float t11, float x, float y); + + void SetTransformation(float x, float y, float angle, float sx, float sy, float ox, + float oy, float kx, float ky); + + void Translation(float x, float y); + + void SetRotation(float r); + + void SetScale(float x, float y); + + void SetShear(float kx, float ky); + + void GetApproximateScale(float& sx, float& sy) const; + + Matrix4 operator*(const Matrix4& m) const; + + void operator*=(const Matrix4& m); + + static Matrix4 Ortho(float left, float right, float bottom, float top, float near, + float far); + + static void Multiply(const Matrix4& a, const Matrix4& b, Matrix4& result); + + float Get(const unsigned row, const unsigned column) const + { + return this->elements[column * 4 + row]; + } + + void Set(const unsigned row, const unsigned column, const float value) + { + this->elements[column * 4 + row] = value; + } + + void TransformXY(float elements[16]); + + void TransformXY(); + + /** + * Transforms an array of 2-component vertices by this Matrix. The source + * and destination arrays may be the same. + **/ + template + void TransformXY(Vdst* dst, const Vsrc* src, int size) const; + + template + void TransformXYVert(Vdst* dst, const Vsrc* src, int size) const; + + template + void TransformXYVertPure(Vdst* dst, const Vsrc* src, int size) const; + + /** + * Transforms an array of 2-component vertices by this Matrix, and stores + * them in an array of 3-component vertices. + **/ + template + void TransformXY0(Vdst* dst, const Vsrc* src, int size) const; + + private: + static void Multiply(const Matrix4& a, const Matrix4& b, float elements[16]); + + float elements[16]; + }; + + /* use with Vector2 */ + template + void Matrix4::TransformXY(Vdst* dst, const Vsrc* src, int size) const + { + for (int i = 0; i < size; i++) + { + // Store in temp variables in case src = dst + float x = (this->elements[0] * src[i].x) + (this->elements[4] * src[i].y) + (0) + + (this->elements[12]); + float y = (this->elements[1] * src[i].x) + (this->elements[5] * src[i].y) + (0) + + (this->elements[13]); + + dst[i].x = x; + dst[i].y = y; + } + } + + /* use with Vertex */ + template + void Matrix4::TransformXYVert(Vdst* dst, const Vsrc* src, int size) const + { + for (int i = 0; i < size; i++) + { + // Store in temp variables in case src = dst + float x = (this->elements[0] * src[i].position[0]) + + (this->elements[4] * src[i].position[1]) + (0) + (this->elements[12]); + + float y = (this->elements[1] * src[i].position[0]) + + (this->elements[5] * src[i].position[1]) + (0) + (this->elements[13]); + + dst[i].x = x; + dst[i].y = y; + } + } + + /* use with Vertex */ + template + void Matrix4::TransformXYVertPure(Vdst* dst, const Vsrc* src, int size) const + { + for (int i = 0; i < size; i++) + { + // Store in temp variables in case src = dst + float x = (this->elements[0] * src[i].position[0]) + + (this->elements[4] * src[i].position[1]) + (0) + (this->elements[12]); + + float y = (this->elements[1] * src[i].position[0]) + + (this->elements[5] * src[i].position[1]) + (0) + (this->elements[13]); + + dst[i].position[0] = x; + dst[i].position[1] = y; + } + } +} // namespace love diff --git a/platform/cafe/include/common/screen_ext.hpp b/platform/cafe/include/common/screen_ext.hpp new file mode 100644 index 000000000..dc9f41ff3 --- /dev/null +++ b/platform/cafe/include/common/screen_ext.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace love +{ + enum Screen : int8_t + { + TV, + GAMEPAD + }; + + // clang-format off + inline constinit ScreenInfo screenInfo[0x02] = + { + { Screen::TV, "tv", -1, -1 }, + { Screen::GAMEPAD, "gamepad", -1, -1 } + }; + // clang-format on + + inline void SetScreenSize(Screen id, int width, int height) + { + auto& info = screenInfo[id]; + + info.width = width; + info.height = height; + } +} // namespace love diff --git a/platform/cafe/include/modules/fontmodule_ext.hpp b/platform/cafe/include/modules/fontmodule_ext.hpp new file mode 100644 index 000000000..ec93ce6ea --- /dev/null +++ b/platform/cafe/include/modules/fontmodule_ext.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include + +#include + +#include + +#include + +namespace love +{ + using SystemFontType = OSSharedDataType; + + class SystemFont : public Data + { + public: + SystemFont(OSSharedDataType type = OS_SHAREDDATATYPE_FONT_STANDARD) : size(0), type(type) + { + OSGetSharedData(type, 0, &this->data, &this->size); + } + + ~SystemFont() + {} + + SystemFont* Clone() const override + { + return new SystemFont(this->type); + } + + void* GetData() const override + { + return this->data; + } + + size_t GetSize() const override + { + return this->size; + } + + private: + void* data; + size_t size; + + OSSharedDataType type; + }; + + template<> + class FontModule : public FontModule + { + public: + FontModule(); + + virtual ~FontModule() {}; + + using FontModule::NewTrueTypeRasterizer; + + Rasterizer* NewImageRasterizer(ImageData* data, const std::string& text, + int extraSpacing, float dpiScale) const override; + + Rasterizer* NewImageRasterizer(ImageData* data, uint32_t* glyphs, + int glyphCount, int extraSpacing, + float dpiScale) const override; + + Rasterizer* NewTrueTypeRasterizer(Data* data, int size, + TrueTypeRasterizer<>::Hinting hinting) const override; + + Rasterizer* NewTrueTypeRasterizer(Data* data, int size, float dpiScale, + TrueTypeRasterizer<>::Hinting hinting) const override; + + Rasterizer* NewRasterizer(FileData* data) const; + + // clang-format off + static constexpr BidirectionalMap systemFonts = { + "standard", OS_SHAREDDATATYPE_FONT_STANDARD, + "chinese", OS_SHAREDDATATYPE_FONT_CHINESE, + "korean", OS_SHAREDDATATYPE_FONT_KOREAN, + "taiwanese", OS_SHAREDDATATYPE_FONT_TAIWANESE + }; + // clang-format on + + private: + FT_Library library; + }; +} // namespace love diff --git a/platform/cafe/include/modules/graphics_ext.hpp b/platform/cafe/include/modules/graphics_ext.hpp new file mode 100644 index 000000000..f5bdadb54 --- /dev/null +++ b/platform/cafe/include/modules/graphics_ext.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace love +{ + template<> + class Graphics : public Graphics + { + public: + static constexpr const char* DEFAULT_SCREEN = "tv"; + + Graphics(); + + bool SetMode(int x, int y, int width, int height); + + Texture* NewTexture(const Texture<>::Settings& settings, + const Texture<>::Slices* slices = nullptr) const; + + void Draw(Texture* texture, Quad* quad, + const Matrix4& matrix); + + void Draw(Drawable* drawable, const Matrix4& matrix); + + void SetViewportSize(int width, int height); + + void SetShader(); + + void SetShader(Shader* shader); + }; // namespace love +} // namespace love diff --git a/platform/cafe/include/modules/joystickmodule_ext.hpp b/platform/cafe/include/modules/joystickmodule_ext.hpp new file mode 100644 index 000000000..2af95ec74 --- /dev/null +++ b/platform/cafe/include/modules/joystickmodule_ext.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include +#include + +namespace love +{ + template<> + class JoystickModule : public JoystickModule + { + public: + JoystickModule(); + + virtual ~JoystickModule(); + + Joystick* AddJoystick(int index); + + void AddVibration(::Vibration* vibration); + + private: + VibrationPool* pool; + PoolThread* thread; + }; +} // namespace love diff --git a/platform/cafe/include/modules/keyboard_ext.hpp b/platform/cafe/include/modules/keyboard_ext.hpp new file mode 100644 index 000000000..42e425060 --- /dev/null +++ b/platform/cafe/include/modules/keyboard_ext.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include + +#include +#include + +#include +#include + +namespace love +{ + template<> + class Keyboard : public Keyboard + { + public: + static constexpr uint32_t MAX_INPUT_LENGTH = 0x100; + + Keyboard(); + + void Initialize(); + + virtual ~Keyboard(); + + void SetTextInput(const KeyboardOptions& options); + + const uint32_t GetMaxEncodingLength(const uint32_t in) + { + return in * 0x04; + } + + const nn::swkbd::State GetState() const + { + return nn::swkbd::GetStateInputForm(); + } + + const bool HasTextInput() const + { + return this->IsShowing(); + } + + const bool IsShowing() const + { + return this->GetState() != nn::swkbd::State::Hidden; + } + + const bool IsHiding() const + { + return this->GetState() == nn::swkbd::State::FadeOut; + } + + void HideKeyboard() + { + nn::swkbd::DisappearInputForm(); + } + + void Utf16toUtf8Text(); + + // clang-format off + static constexpr BidirectionalMap keyboardTypes = { + "normal", (uint8_t)nn::swkbd::KeyboardMode::Full, + "qwerty", (uint8_t)nn::swkbd::KeyboardMode::Utf8, + "numpad", (uint8_t)nn::swkbd::KeyboardMode::Numpad + }; + // clang-format on + + private: + GX2ContextState* state; + + nn::swkbd::CreateArg createArgs; + nn::swkbd::AppearArg appearArgs; + FSClient* client; + + bool inited; + }; +} // namespace love diff --git a/platform/cafe/include/modules/system_ext.hpp b/platform/cafe/include/modules/system_ext.hpp new file mode 100644 index 000000000..e05513a2e --- /dev/null +++ b/platform/cafe/include/modules/system_ext.hpp @@ -0,0 +1,131 @@ +#pragma once + +#include + +#include +#include +#include + +#include + +extern "C" +{ + typedef enum USCLanguage : int8_t + { + USCLanguage_JA = 0, + USCLanguage_EN = 1, + USCLanguage_FR = 2, + USCLanguage_DE = 3, + USCLanguage_IT = 4, + USCLanguage_ES = 5, + USCLanguage_ZH = 6, + USCLanguage_KO = 7, + USCLanguage_NL = 8, + USCLanguage_PT = 9, + USCLanguage_RU = 10, + USCLanguage_TW = 11, + }; +} + +namespace love +{ + template<> + class System : public System + { + public: + enum SystemModel + { + SYSTEM_MODEL_BASIC, + SYSTEM_MODEL_DELUXE, + SYSTEM_MODEL_MAX_ENUM + }; + + System(); + + ~System(); + + PowerState GetPowerInfo(uint8_t& percent) const; + + NetworkState GetNetworkInfo(uint8_t& signal) const; + + int GetProcessorCount(); + + std::string_view GetUsername(); + + std::string_view GetSystemTheme(); + + std::string_view GetPreferredLocales(); + + std::string_view GetVersion(); + + std::string_view GetModel(); + + std::string_view GetFriendInfo(); + + // clang-format off + static constexpr BidirectionalMap systemModels = { + "Hollywood English Sample 1", BSP_HARDWARE_VERSION_HOLLYWOOD_ENG_SAMPLE_1, + "Hollywood English Sample 2", BSP_HARDWARE_VERSION_HOLLYWOOD_ENG_SAMPLE_2, + "Hollywood Production For Wii", BSP_HARDWARE_VERSION_HOLLYWOOD_PROD_FOR_WII, + "Hollywood Cortado", BSP_HARDWARE_VERSION_HOLLYWOOD_CORTADO, + "Hollywood Cortado Espresso", BSP_HARDWARE_VERSION_HOLLYWOOD_CORTADO_ESPRESSO, + "Bollywood", BSP_HARDWARE_VERSION_BOLLYWOOD, + "Bollywood Production For Wii", BSP_HARDWARE_VERSION_BOLLYWOOD_PROD_FOR_WII, + "Latte A11 Ev", BSP_HARDWARE_VERSION_LATTE_A11_EV, + "Latte A11 Cat", BSP_HARDWARE_VERSION_LATTE_A11_CAT, + "Latte A12 Ev", BSP_HARDWARE_VERSION_LATTE_A12_EV, + "Latte A12 Cat", BSP_HARDWARE_VERSION_LATTE_A12_CAT, + "Latte A2x Ev", BSP_HARDWARE_VERSION_LATTE_A2X_EV, + "Latte A2x Cat", BSP_HARDWARE_VERSION_LATTE_A2X_CAT, + "Latte A3x Ev", BSP_HARDWARE_VERSION_LATTE_A3X_EV, + "Latte A3x Cat", BSP_HARDWARE_VERSION_LATTE_A3X_CAT, + "Latte A3x Cafe", BSP_HARDWARE_VERSION_LATTE_A3X_CAFE, + "Latte A4x Ev", BSP_HARDWARE_VERSION_LATTE_A4X_EV, + "Latte A4x Cat", BSP_HARDWARE_VERSION_LATTE_A4X_CAT, + "Latte A4x Cafe", BSP_HARDWARE_VERSION_LATTE_A4X_CAFE, + "Latte A5x Ev", BSP_HARDWARE_VERSION_LATTE_A5X_EV, + "Latte A5x Ev Y", BSP_HARDWARE_VERSION_LATTE_A5X_EV_Y, + "Latte A5x Cat", BSP_HARDWARE_VERSION_LATTE_A5X_CAT, + "Latte A5x Cafe", BSP_HARDWARE_VERSION_LATTE_A5X_CAFE, + "Latte B1x Ev", BSP_HARDWARE_VERSION_LATTE_B1X_EV, + "Latte B1x Ev Y", BSP_HARDWARE_VERSION_LATTE_B1X_EV_Y, + "Latte B1x Cat", BSP_HARDWARE_VERSION_LATTE_B1X_CAT, + "Latte B1x Cafe", BSP_HARDWARE_VERSION_LATTE_B1X_CAFE + }; + + static constexpr BidirectionalMap languages = { + "jp", USCLanguage_JA, + "en", USCLanguage_EN, + "fr", USCLanguage_FR, + "de", USCLanguage_DE, + "it", USCLanguage_IT, + "es", USCLanguage_ES, + "zh_CN", USCLanguage_ZH, + "ko", USCLanguage_KO, + "nl", USCLanguage_NL, + "pt", USCLanguage_PT, + "ru", USCLanguage_RU, + "zh_TW", USCLanguage_TW + }; + + static constexpr BidirectionalMap countryCodes = { + "JP", MCP_REGION_JAPAN, + "US", MCP_REGION_USA, + "EU", MCP_REGION_EUROPE, + "CN", MCP_REGION_CHINA, + "KR", MCP_REGION_KOREA, + "TW", MCP_REGION_TAIWAN + }; + + // clang-format on + + private: + struct + { + int32_t mcp; + UCHandle userConfig; + } handles; + + uint8_t accountSlot; + }; +} // namespace love diff --git a/platform/cafe/include/modules/timer_ext.hpp b/platform/cafe/include/modules/timer_ext.hpp new file mode 100644 index 000000000..f7c171aaf --- /dev/null +++ b/platform/cafe/include/modules/timer_ext.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include + +namespace love +{ + template<> + class Timer : public Timer + { + public: + Timer(); + + double Step(); + + void Sleep(double seconds) const; + + static double GetTime(); + + private: + static constexpr double NANOSECONDS_TO_SECONDS = 1000000000.0; + + static constexpr double NANOSECONDS = 1000000ULL; + static constexpr double MILLISECONDS = 1000.0; + + static OSTick reference; + }; +} // namespace love diff --git a/platform/cafe/include/modules/window_ext.hpp b/platform/cafe/include/modules/window_ext.hpp new file mode 100644 index 000000000..6b49a653f --- /dev/null +++ b/platform/cafe/include/modules/window_ext.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include + +#include + +namespace love +{ + template<> + class Window : public Window + { + public: + Window(); + + virtual ~Window(); + + void SetGraphics(Graphics* graphics) + { + this->graphics.Set(graphics); + } + + bool CreateWindowAndContext(int x, int y, int width, int height); + + bool SetWindow(int width = 800, int height = 600, WindowSettings* settings = nullptr); + + void GetWindow(int& width, int& height, WindowSettings& settings); + + void Close(); + + bool SetFullscreen(bool fullscreen, FullscreenType fullScreenType) + { + return true; + } + + bool SetFullscreen(bool fullscreen) + { + return true; + } + + bool OnSizeChanged(int width, int height); + + int GetDisplayCount() const + { + return 2; + } + + std::string_view GetDisplayName(int displayIndex) const; + + std::vector GetFullscreenSizes(int displayIndex); + + void GetDesktopDimensions(int displayIndex, int& width, int& height); + + void SetPosition(int x, int y, int displayIndex); + + void GetPosition(int& x, int& y, int& displayIndex); + + void SetDisplaySleepEnabled(bool enable); + + bool IsDisplaySleepEnabled() const; + + private: + StrongReference> graphics; + }; +} // namespace love diff --git a/platform/cafe/include/objects/gamepad.hpp b/platform/cafe/include/objects/gamepad.hpp new file mode 100644 index 000000000..ec5554af5 --- /dev/null +++ b/platform/cafe/include/objects/gamepad.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include + +#include + +namespace love +{ + class Gamepad : public Joystick + { + public: + Gamepad() + {} + + Gamepad(int id); + + Gamepad(int id, int index); + + virtual ~Gamepad() + {} + + bool Open(int index) override; + + bool IsConnected() const + { + return true; + } + + virtual bool IsDown(JoystickInput& result) override; + + virtual bool IsDown(const std::vector& buttons) const override; + + virtual bool IsGamepadDown(const std::vector& buttons) const override; + + virtual bool IsAxisChanged(GamepadAxis axis) override; + + virtual bool IsUp(JoystickInput& result) override; + + virtual float GetAxis(int index) override; + + virtual float GetGamepadAxis(GamepadAxis axis) override; + + virtual std::vector GetAxes() override; + + virtual void Update() override; + + bool IsGamepad() const + { + return true; + } + + guid::GamepadType GetGamepadType() const override + { + return guid::GAMEPAD_TYPE_WII_U_GAMEPAD; + } + + bool IsVibrationSupported() override + { + return true; + } + + bool SetVibration(float left, float right, float duration = -1.0f) override; + + bool SetVibration() override; + + void GetVibration(float& left, float& right) override; + + bool HasSensor(Sensor::SensorType type) const override; + + bool IsSensorEnabled(Sensor::SensorType type) override; + + void SetSensorEnabled(Sensor::SensorType type, bool enabled) override; + + std::vector GetSensorData(Sensor::SensorType type) override; + + /* Wii U Gamepad Specifics */ + + VPADStatus GetVPADStatus() const + { + return this->state; + } + + private: + VPADStatus state; + Vibration vibration; + }; +} // namespace love diff --git a/platform/cafe/include/objects/imagedata_ext.hpp b/platform/cafe/include/objects/imagedata_ext.hpp new file mode 100644 index 000000000..0120a0370 --- /dev/null +++ b/platform/cafe/include/objects/imagedata_ext.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace love +{ + template<> + class ImageData : public ImageData + { + public: + using ImageData::ImageData; + + ImageData* Clone() const override + { + return new ImageData(*this); + } + }; +} // namespace love diff --git a/platform/cafe/include/objects/joystick_ext.hpp b/platform/cafe/include/objects/joystick_ext.hpp new file mode 100644 index 000000000..a5046a9d9 --- /dev/null +++ b/platform/cafe/include/objects/joystick_ext.hpp @@ -0,0 +1,142 @@ +#pragma once + +#include + +#include + +namespace love +{ + template<> + class Joystick : public Joystick + { + public: + Joystick() + {} + + Joystick(int id) : buttonStates {}, connected(false) + { + this->instanceId = -1; + this->id = id; + } + + virtual bool Open(int index) = 0; + + virtual ~Joystick() + { + this->Close(); + } + + void Close() + { + this->instanceId = -1; + } + + bool IsConnected() const + { + return this->connected; + } + + bool IsGamepad() const + { + return true; + } + + virtual bool IsDown(JoystickInput& result) = 0; + + virtual bool IsDown(const std::vector& buttons) const = 0; + + virtual bool IsGamepadDown(const std::vector& buttons) const = 0; + + virtual bool IsUp(JoystickInput& result) = 0; + + virtual bool IsAxisChanged(GamepadAxis axis) = 0; + + virtual guid::GamepadType GetGamepadType() const = 0; + + void GetDeviceInfo(int& vendor, int& product, int& version) + { + guid::DeviceInfo info {}; + + if (!guid::GetDeviceInfo(this->GetGamepadType(), info)) + return; + + vendor = info.vendorId; + product = info.productId; + version = info.productVersion; + } + + int GetAxisCount() const + { + if (!this->IsConnected()) + return 0; + + return guid::GetGamepadAxisCount(this->GetGamepadType()); + } + + int GetButtonCount() const + { + if (!this->IsConnected()) + return 0; + + return guid::GetGamepadButtonCount(this->GetGamepadType()); + } + + virtual float GetAxis(int index) = 0; + + virtual float GetGamepadAxis(GamepadAxis axis) = 0; + + virtual std::vector GetAxes() = 0; + + virtual void Update() = 0; + + void SetPlayerIndex(int index) + {} + + int GetPlayerIndex() const + { + return this->id; + } + + virtual bool IsVibrationSupported() = 0; + + virtual bool SetVibration(float left, float right, float duration = -1.0f) = 0; + + virtual bool SetVibration() = 0; + + virtual void GetVibration(float& left, float& right) = 0; + + virtual bool HasSensor(Sensor::SensorType type) const = 0; + + virtual bool IsSensorEnabled(Sensor::SensorType type) = 0; + + virtual void SetSensorEnabled(Sensor::SensorType type, bool enabled) = 0; + + virtual std::vector GetSensorData(Sensor::SensorType type) = 0; + + protected: + struct Stick + { + float dx; + float dy; + }; + + struct Trigger + { + bool down; + }; + + struct ButtonStates + { + int32_t pressed; + int32_t released; + int32_t held; + } buttonStates; + + Stick leftStick; + Stick rightStick; + + Trigger triggers[0x02]; + + bool connected; + }; +} // namespace love diff --git a/platform/cafe/include/objects/procontroller.hpp b/platform/cafe/include/objects/procontroller.hpp new file mode 100644 index 000000000..4e4c70b68 --- /dev/null +++ b/platform/cafe/include/objects/procontroller.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include +#include + +namespace love +{ + class ProController : public Joystick + { + public: + ProController() + {} + + ProController(int id); + + ProController(int id, int index); + + virtual ~ProController() + {} + + bool Open(int index) override; + + bool IsConnected() const + { + return true; + } + + virtual bool IsDown(JoystickInput& result) override; + + virtual bool IsDown(const std::vector& buttons) const override; + + virtual bool IsGamepadDown(const std::vector& buttons) const override; + + virtual bool IsAxisChanged(GamepadAxis axis) override; + + virtual bool IsUp(JoystickInput& result) override; + + virtual float GetAxis(int index) override; + + virtual float GetGamepadAxis(GamepadAxis axis) override; + + virtual std::vector GetAxes() override; + + virtual void Update() override; + + bool IsGamepad() const + { + return true; + } + + guid::GamepadType GetGamepadType() const override + { + return guid::GAMEPAD_TYPE_WII_PRO; + } + + bool IsVibrationSupported() override + { + return true; + } + + bool SetVibration(float left, float right, float duration = -1.0f) override; + + bool SetVibration() override; + + void GetVibration(float& left, float& right) override; + + bool HasSensor(Sensor::SensorType type) const override; + + bool IsSensorEnabled(Sensor::SensorType type) override; + + void SetSensorEnabled(Sensor::SensorType type, bool enabled) override; + + std::vector GetSensorData(Sensor::SensorType type) override; + + private: + KPADStatus state; + Vibration vibration; + }; +} // namespace love diff --git a/platform/cafe/include/objects/shader_ext.hpp b/platform/cafe/include/objects/shader_ext.hpp new file mode 100644 index 000000000..8c7d5a739 --- /dev/null +++ b/platform/cafe/include/objects/shader_ext.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include + +namespace love +{ + template<> + class Shader : public Shader + { + public: + Shader(); + + Shader(Data* group); + + virtual ~Shader(); + + void Attach(bool forced = false); + + static void AttachDefault(StandardShader type); + + void LoadDefaults(StandardShader type); + + void BindTexture(int location, GX2Texture* texture, GX2Sampler* sampler); + + uint32_t GetPixelSamplerLocation(int index); + + GX2UniformBlock* GetUniformBlock(const char* name); + + WHBGfxShaderGroup& GetGroup() + { + return this->program; + } + + private: + static constexpr auto GX2_FORMAT_VEC3 = GX2_ATTRIB_FORMAT_FLOAT_32_32_32; + static constexpr auto GX2_FORMAT_VEC4 = GX2_ATTRIB_FORMAT_FLOAT_32_32_32_32; + static constexpr auto GX2_FORMAT_VEC2 = GX2_ATTRIB_FORMAT_FLOAT_32_32; + + WHBGfxShaderGroup program; + }; +} // namespace love diff --git a/platform/cafe/include/objects/source_ext.hpp b/platform/cafe/include/objects/source_ext.hpp new file mode 100644 index 000000000..0b58bd08f --- /dev/null +++ b/platform/cafe/include/objects/source_ext.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include +#include + +#include +#include + +#include + +namespace love +{ + class Audio; + class AudioPool; + + template<> + class Source : public Source + { + public: + Source(AudioPool* pool, SoundData* soundData); + + Source(AudioPool* pool, Decoder* decoder); + + Source(AudioPool* pool, int sampleRate, int bitDepth, int channels, int buffers); + + Source(const Source& other); + + virtual ~Source(); + + virtual Source* Clone(); + + /* normal stuff */ + + bool Play(); + + void Stop(); + + void Pause(); + + bool IsPlaying() const; + + bool IsFinished() const; + + bool Update(); + + void SetVolume(float volume); + + float GetVolume() const; + + void Seek(double offset, Unit unit); + + double Tell(Unit unit); + + double GetDuration(Unit unit); + + void SetLooping(bool looping); + + int GetChannelCount() const; + + int GetFreeBufferCount() const; + + bool Queue(void* data, size_t length, int sampleRate, int bitDepth, int channels); + + /* atomic things */ + + void PrepareAtomic(); + + void TeardownAtomic(); + + bool PlayAtomic(Mix_Chunk& chunk); + + void StopAtomic(); + + void PauseAtomic(); + + void ResumeAtomic(); + + /* global state stuff */ + + static bool Play(const std::vector*>& sources); + + static void Stop(const std::vector*>& sources); + + static void Pause(const std::vector*>& sources); + + static std::vector*> Pause(AudioPool* pool); + + static void Stop(AudioPool* pool); + + private: + static constexpr size_t MAX_BUFFERS = 2; + + void Reset(); + + int StreamAtomic(Mix_Chunk& buffer, Decoder* decoder); + + AudioPool* pool; + + Mix_Chunk buffers[2]; + + StrongReference decoder; + }; +} // namespace love diff --git a/platform/cafe/include/objects/texture_ext.hpp b/platform/cafe/include/objects/texture_ext.hpp new file mode 100644 index 000000000..0fdf19128 --- /dev/null +++ b/platform/cafe/include/objects/texture_ext.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include + +#include +#include + +namespace love +{ + template<> + class Texture : public Texture + { + public: + Texture(const Graphics* graphics, const Settings& settings, + const Slices* data); + + virtual ~Texture(); + + virtual void Draw(Graphics& graphics, + const Matrix4& matrix) override; + + virtual void Draw(Graphics& graphics, Quad* quad, + const Matrix4& transform) override; + + void ReplacePixels(ImageDataBase* data, int slice, int mipmap, int x, int y, + bool reloadMipmaps); + + void ReplacePixels(const void* data, size_t size, int slice, int mipmap, const Rect& rect, + bool reloadMipmaps); + + void SetSamplerState(const SamplerState& state); + + void GenerateMipmaps() + {} + + bool LoadVolatile(); + + void UnloadVolatile(); + + GX2Sampler& GetSampler() + { + return this->sampler; + } + + GX2Texture* GetHandle() + { + return this->texture; + } + + GX2ColorBuffer* GetFramebuffer() + { + return this->framebuffer; + } + + private: + static constexpr auto INVALIDATE_MODE = GX2_INVALIDATE_MODE_CPU_TEXTURE; + + void CreateTexture(); + + GX2ColorBuffer* framebuffer; + GX2Texture* texture; + + GX2Sampler sampler; + }; +} // namespace love diff --git a/platform/cafe/include/scripts/wrap_window.lua b/platform/cafe/include/scripts/wrap_window.lua new file mode 100644 index 000000000..b9c0ac5c3 --- /dev/null +++ b/platform/cafe/include/scripts/wrap_window.lua @@ -0,0 +1,4 @@ +R"luastring"--( +function love.window.showMessageBox(title, text, buttons) +end +--)luastring"--" diff --git a/platform/cafe/include/scripts/wrap_window_messagebox.lua b/platform/cafe/include/scripts/wrap_window_messagebox.lua new file mode 100644 index 000000000..9385054bf --- /dev/null +++ b/platform/cafe/include/scripts/wrap_window_messagebox.lua @@ -0,0 +1,6 @@ +R"luastring"--( +local empty = {} +empty.__index = empty + +return empty +--)luastring"--" diff --git a/platform/cafe/include/utilities/abort.hpp b/platform/cafe/include/utilities/abort.hpp new file mode 100644 index 000000000..6c613d692 --- /dev/null +++ b/platform/cafe/include/utilities/abort.hpp @@ -0,0 +1,23 @@ +#include + +namespace love +{ + enum AbortCode + { + ABORT_AC, + ABORT_BSP + }; + + static constexpr const char* ABORT_TITLE = "Failed to initalize %s!"; + + static constexpr const char* ABORT_FORMAT = + "Result: %ld\n" + "Please visit https://wiiubrew.org/wiki/Error_codes for more information.\n"; + + // clang-format off + static constexpr BidirectionalMap abortTypes = { + ABORT_AC, "nn:ac", + ABORT_AC, "bsp" + }; + // clang-format on +} // namespace love diff --git a/platform/cafe/include/utilities/driver/dsp_ext.hpp b/platform/cafe/include/utilities/driver/dsp_ext.hpp new file mode 100644 index 000000000..0d57df17b --- /dev/null +++ b/platform/cafe/include/utilities/driver/dsp_ext.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include + +#include + +#include + +#include + +extern "C" +{ + /* todo - remove when wut supports these */ + void AXSetMasterVolume(uint32_t volume); + int16_t AXGetMasterVolume(); +} + +namespace love +{ + template<> + class DSP : public DSP + { + public: + static DSP& Instance() + { + static DSP instance; + return instance; + } + + DSP(); + + ~DSP(); + + void Initialize(); + + void Update(); + + void SetMasterVolume(float volume); + + float GetMasterVolume() const; + + bool ChannelReset(size_t id); + + void ChannelSetVolume(size_t id, float volume); + + float ChannelGetVolume(size_t id); + + size_t ChannelGetSampleOffset(size_t id, int bitDepth); + + bool ChannelAddBuffer(size_t id, Mix_Chunk* buffer, bool looping); + + void ChannelPause(size_t id, bool paused = true); + + bool IsChannelPaused(size_t id); + + bool IsChannelPlaying(size_t id); + + void ChannelStop(size_t id); + + OSEvent& GetEvent() + { + return this->event; + } + + private: + OSEvent event; + }; +} // namespace love diff --git a/platform/cafe/include/utilities/driver/framebuffer.hpp b/platform/cafe/include/utilities/driver/framebuffer.hpp new file mode 100644 index 000000000..89c7379ba --- /dev/null +++ b/platform/cafe/include/utilities/driver/framebuffer.hpp @@ -0,0 +1,183 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include + +namespace love +{ + class Framebuffer + { + private: + static constexpr float Z_NEAR = -10.0f; + static constexpr float Z_FAR = 10.0f; + + public: + struct Transform + { + glm::mat4 modelView; + glm::mat4 projection; + }; + + static constexpr size_t TRANSFORM_SIZE = sizeof(Transform); + + Framebuffer(); + + ~Framebuffer(); + + void Create(Screen id); + + int GetWidth() const + { + return this->width; + } + + int GetHeight() const + { + return this->height; + } + + Vector2 GetSize() const + { + return { (float)this->width, (float)this->height }; + } + + GX2ColorBuffer& GetBuffer() + { + return this->colorBuffer; + } + + GX2DepthBuffer& GetDepthBuffer() + { + return this->depthBuffer; + } + + /* + ** Sets the projection matrix to the specified projection 4x4 + ** Then it converts the main Transform data to little endian + */ + void SetProjection(const glm::highp_mat4& projection); + + void SetViewport(const Rect& viewport = Rect::EMPTY); + + void SetScissor(const Rect& scissor = Rect::EMPTY); + + /* + ** Invalidates the little endian Transform + ** to be used in the Uniform Block + */ + void UseProjection(); + + /* + ** Copy our GX2ColorBuffer to the Scan Target + ** This is used in Renderer::Present + */ + void CopyScanBuffer(); + + operator GX2ColorBuffer*() + { + return &this->colorBuffer; + } + + operator GX2DepthBuffer*() + { + return &this->depthBuffer; + } + + /* Called on Renderer::OnForegroundAcquired */ + bool AllocateScanBuffer(MEMHeapHandle handle); + + /* Called on Renderer::OnForegroundAcquired */ + bool InvalidateColorBuffer(MEMHeapHandle handle); + + /* Called on Renderer::OnForegroundAcquired */ + bool InvalidateDepthBuffer(MEMHeapHandle handle); + + void SetContext() + { + GX2SetContextState(this->state); + } + + Rect GetViewport() const + { + return this->viewport; + } + + Rect GetScissor() const + { + return this->scissor; + } + + private: + static constexpr GX2ScanTarget SCAN_TARGETS[0x02] { GX2_SCAN_TARGET_TV, + GX2_SCAN_TARGET_DRC }; + + /* Called on Create */ + void ScanSystemMode(); + + /* Called by ScanSystemMode */ + void SetSize(int width, int height); + + /* Called by SetSize when id is TV */ + void SetTVSize(); + + /* Called by SetSize when id is Gamepad */ + void SetDRCSize(); + + /* Called by SetSize at the end */ + void InitColorBuffer(); + + /* Called by SetSize at the end */ + void InitDepthBuffer(); + + /* Called by AllocateScanBuffer */ + void SetTVScanBuffer(); + + /* Called by AllocateScanBuffer */ + void SetDRCScanBuffer(); + + bool Is(Screen id) + { + return this->id == id; + } + + static constexpr auto FORMAT = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8; + static constexpr auto BUFFERING = GX2_BUFFERING_MODE_DOUBLE; + static constexpr auto INVALIDATE_UNIFORM = + GX2_INVALIDATE_MODE_CPU | GX2_INVALIDATE_MODE_UNIFORM_BLOCK; + static constexpr auto INVALIDATE_COLOR_BUFFER = + GX2_INVALIDATE_MODE_CPU | GX2_INVALIDATE_MODE_COLOR_BUFFER; + + Screen id; + + glm::mat4 modelView; + Transform* transform; + + GX2ColorBuffer colorBuffer; + GX2DepthBuffer depthBuffer; + + GX2ContextState* state; + + uint8_t mode; + + void* scanBuffer; + uint32_t scanBufferSize; + + int width; + int height; + + Rect viewport; + Rect scissor; + }; +} // namespace love diff --git a/platform/cafe/include/utilities/driver/hid_ext.hpp b/platform/cafe/include/utilities/driver/hid_ext.hpp new file mode 100644 index 000000000..7019c24f6 --- /dev/null +++ b/platform/cafe/include/utilities/driver/hid_ext.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include +#include + +#include + +namespace love +{ + template<> + class HID : public HID + { + public: + static HID& Instance() + { + static HID instance; + return instance; + } + + ~HID(); + + private: + HID(); + + virtual void _Poll() override; + + void CheckFocus(); + + void CheckSoftwareKeyboard(VPADStatus status); + + VPADTouchData previousTouch; + }; +} // namespace love diff --git a/platform/cafe/include/utilities/driver/renderer_ext.hpp b/platform/cafe/include/utilities/driver/renderer_ext.hpp new file mode 100644 index 000000000..c68f08263 --- /dev/null +++ b/platform/cafe/include/utilities/driver/renderer_ext.hpp @@ -0,0 +1,269 @@ +#pragma once + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +/* Enforces GLSL std140/std430 alignment rules for glm types */ +#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES +/* Enables usage of SIMD CPU instructions (requiring the above as well) */ +#define GLM_FORCE_INTRINSICS +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace love +{ + using namespace vertex; + + template<> + class Renderer : public Renderer + { + private: + static constexpr const char* RENDERER_NAME = "GX2"; + static constexpr const char* RENDERER_VERSION = "1.0.0"; + static constexpr const char* RENDERER_VENDOR = "AMD"; + static constexpr const char* RENDERER_DEVICE = "GPU7"; + + static inline constexpr int MAX_OBJECTS = 0x1000; + static inline constexpr int VERTEX_BUFFER_SIZE = 4 * MAX_OBJECTS; + static constexpr auto BUFFER_CREATE_FLAGS = + GX2R_RESOURCE_BIND_VERTEX_BUFFER | GX2R_RESOURCE_USAGE_CPU_READ | + GX2R_RESOURCE_USAGE_CPU_WRITE | GX2R_RESOURCE_USAGE_GPU_READ; + + static constexpr uint8_t MAX_RENDERTARGETS = 0x02; + + Renderer(); + + public: + static constexpr auto INVALIDATE_UNIFORM = + GX2_INVALIDATE_MODE_CPU | GX2_INVALIDATE_MODE_UNIFORM_BLOCK; + + struct Transform + { + glm::mat4 projection; + glm::mat4 modelView; + }; + + static Renderer& Instance() + + { + static Renderer instance; + return instance; + } + + ~Renderer(); + + Info GetRendererInfo(); + + void DestroyFramebuffers(); + + void CreateFramebuffers(); + + void Clear(const Color& color); + + void SetDepthWrites(bool write); + + void ClearDepthStencil(int stencil, uint8_t mask, double depth); + + void SetBlendColor(const Color& color); + + void SetBlendMode(const RenderState::BlendState& state); + + void EnsureInFrame(); + + /* todo: canvases */ + void BindFramebuffer(Texture* texture = nullptr); + + void Present(); + + void SetViewport(const Rect& viewport); + + void SetScissor(const Rect& scissor, bool canvasActive); + + void SetStencil(RenderState::CompareMode mode, int value); + + void SetMeshCullMode(vertex::CullMode mode); + + void SetVertexWinding(vertex::Winding winding); + + void SetSamplerState(Texture* texture, SamplerState& state); + + void SetColorMask(const RenderState::ColorMask& mask); + + void SetLineWidth(float lineWidth); + + void SetLineStyle(RenderState::LineStyle style); + + void SetPointSize(float size); + + bool Render(DrawCommand& command); + + void UseProgram(const WHBGfxShaderGroup& group); + + static void FlushVertices(); + + // clang-format off + static constexpr BidirectionalMap pixelFormats = { + PIXELFORMAT_R8_UNORM, GX2_SURFACE_FORMAT_UNORM_R8, + PIXELFORMAT_R16_UNORM, GX2_SURFACE_FORMAT_UNORM_R16, + PIXELFORMAT_RG8_UNORM, GX2_SURFACE_FORMAT_UNORM_R8_G8, + PIXELFORMAT_RGBA8_UNORM, GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8, + PIXELFORMAT_RGB565_UNORM, GX2_SURFACE_FORMAT_UNORM_R5_G6_B5, + PIXELFORMAT_RGBA8_UNORM_SRGB, GX2_SURFACE_FORMAT_SRGB_R8_G8_B8_A8, + PIXELFORMAT_DXT1_UNORM, GX2_SURFACE_FORMAT_UNORM_BC1, + PIXELFORMAT_DXT3_UNORM, GX2_SURFACE_FORMAT_UNORM_BC2, + PIXELFORMAT_DXT5_UNORM, GX2_SURFACE_FORMAT_UNORM_BC3, + PIXELFORMAT_BC4_UNORM, GX2_SURFACE_FORMAT_UNORM_BC4, + PIXELFORMAT_BC5_UNORM, GX2_SURFACE_FORMAT_UNORM_BC5 + }; + + static constexpr BidirectionalMap blendEquations = { + RenderState::BLENDOP_ADD, GX2_BLEND_COMBINE_MODE_ADD, + RenderState::BLENDOP_SUBTRACT, GX2_BLEND_COMBINE_MODE_SUB, + RenderState::BLENDOP_REVERSE_SUBTRACT, GX2_BLEND_COMBINE_MODE_REV_SUB, + RenderState::BLENDOP_MIN, GX2_BLEND_COMBINE_MODE_MIN, + RenderState::BLENDOP_MAX, GX2_BLEND_COMBINE_MODE_MAX + }; + + static constexpr BidirectionalMap blendFactors = { + RenderState::BLENDFACTOR_ZERO, GX2_BLEND_MODE_ZERO, + RenderState::BLENDFACTOR_ONE, GX2_BLEND_MODE_ONE, + RenderState::BLENDFACTOR_SRC_COLOR, GX2_BLEND_MODE_SRC_COLOR, + RenderState::BLENDFACTOR_ONE_MINUS_SRC_COLOR, GX2_BLEND_MODE_INV_SRC_COLOR, + RenderState::BLENDFACTOR_SRC_ALPHA, GX2_BLEND_MODE_SRC_ALPHA, + RenderState::BLENDFACTOR_ONE_MINUS_SRC_ALPHA, GX2_BLEND_MODE_INV_SRC_ALPHA, + RenderState::BLENDFACTOR_DST_COLOR, GX2_BLEND_MODE_DST_COLOR, + RenderState::BLENDFACTOR_ONE_MINUS_DST_COLOR, GX2_BLEND_MODE_INV_DST_COLOR, + RenderState::BLENDFACTOR_DST_ALPHA, GX2_BLEND_MODE_DST_ALPHA, + RenderState::BLENDFACTOR_ONE_MINUS_DST_ALPHA, GX2_BLEND_MODE_INV_DST_ALPHA, + RenderState::BLENDFACTOR_SRC_ALPHA_SATURATED, GX2_BLEND_MODE_SRC_ALPHA_SAT + }; + + static constexpr BidirectionalMap filterModes = { + SamplerState::FILTER_LINEAR, GX2_TEX_XY_FILTER_MODE_LINEAR, + SamplerState::FILTER_NEAREST, GX2_TEX_XY_FILTER_MODE_POINT + }; + + static constexpr BidirectionalMap wrapModes = { + SamplerState::WRAP_CLAMP, GX2_TEX_CLAMP_MODE_CLAMP, + SamplerState::WRAP_CLAMP_ZERO, GX2_TEX_CLAMP_MODE_CLAMP_BORDER, + SamplerState::WRAP_REPEAT, GX2_TEX_CLAMP_MODE_MIRROR, + SamplerState::WRAP_MIRRORED_REPEAT, GX2_TEX_CLAMP_MODE_MIRROR + }; + + static constexpr BidirectionalMap cullModes = { + vertex::CULL_NONE, -1, + vertex::CULL_BACK, -2, + vertex::CULL_FRONT, -3 + }; + + static constexpr BidirectionalMap windingModes = { + vertex::WINDING_CW, GX2_FRONT_FACE_CW, + vertex::WINDING_CCW, GX2_FRONT_FACE_CCW + }; + + static constexpr BidirectionalMap compareModes = { + RenderState::COMPARE_LESS, GX2_COMPARE_FUNC_LESS, + RenderState::COMPARE_LEQUAL, GX2_COMPARE_FUNC_LEQUAL, + RenderState::COMPARE_EQUAL, GX2_COMPARE_FUNC_EQUAL, + RenderState::COMPARE_GEQUAL, GX2_COMPARE_FUNC_GEQUAL, + RenderState::COMPARE_GREATER, GX2_COMPARE_FUNC_GREATER, + RenderState::COMPARE_NOTEQUAL, GX2_COMPARE_FUNC_NOT_EQUAL, + RenderState::COMPARE_ALWAYS, GX2_COMPARE_FUNC_ALWAYS, + RenderState::COMPARE_NEVER, GX2_COMPARE_FUNC_NEVER + }; + + static constexpr BidirectionalMap primitiveModes = { + vertex::PRIMITIVE_TRIANGLES, GX2_PRIMITIVE_MODE_TRIANGLES, + vertex::PRIMITIVE_TRIANGLE_FAN, GX2_PRIMITIVE_MODE_TRIANGLE_FAN, + vertex::PRIMITIVE_TRIANGLE_STRIP, GX2_PRIMITIVE_MODE_TRIANGLE_STRIP, + vertex::PRIMITIVE_QUADS, GX2_PRIMITIVE_MODE_QUADS, + vertex::PRIMITIVE_POINTS, GX2_PRIMITIVE_MODE_POINTS + }; + // clang-format on + + private: + struct GX2RendererState + { + GX2FrontFace winding; + bool cullFront; + bool cullBack; + + bool depthWrite; + bool depthTest; + GX2CompareFunction compareMode; + + uint32_t writeMask; + } renderState; + + struct Context + { + GX2ColorBuffer* target; + Transform* transform; + } context; + + static constexpr auto TRANSFORM_SIZE = sizeof(Transform); + + static uint32_t ProcUIAcquired(void* args); + + static uint32_t ProcUIReleased(void* args); + + int OnForegroundAcquired(); + + int OnForegroundReleased(); + + bool inForeground; + void* commandBuffer; + + Framebuffer* current; + GX2ContextState* state; + + static inline std::vector> m_commands {}; + static inline CommonFormat m_format = CommonFormat::NONE; + static inline GX2RBuffer m_buffer {}; + static inline size_t m_vertexOffset = 0; + + OSTick cpuTickReference; + static inline OSTick gpuTickReference = 0; + + std::vector*> currentTextures; + glm::mat4 modelView; + + std::map framebuffers; + }; +} // namespace love diff --git a/platform/cafe/include/utilities/driver/vertex_ext.hpp b/platform/cafe/include/utilities/driver/vertex_ext.hpp new file mode 100644 index 000000000..1089b93b3 --- /dev/null +++ b/platform/cafe/include/utilities/driver/vertex_ext.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + +#include + +#include + +namespace love +{ + namespace vertex + { + static constexpr size_t POSITION_OFFSET = offsetof(Vertex, position); + static constexpr size_t COLOR_OFFSET = offsetof(Vertex, color); + static constexpr size_t TEXCOORD_OFFSET = offsetof(Vertex, texcoord); + } // namespace vertex +} // namespace love diff --git a/platform/cafe/include/utilities/haptics/vibration_ext.hpp b/platform/cafe/include/utilities/haptics/vibration_ext.hpp new file mode 100644 index 000000000..5ced1348f --- /dev/null +++ b/platform/cafe/include/utilities/haptics/vibration_ext.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include +#include + +namespace love +{ + template<> + class Vibration : public Vibration + { + public: + Vibration(WPADChan channel); + + Vibration(); + + virtual ~Vibration(); + + bool SendValues(float left, float right); + + bool Stop(); + + private: + bool isGamepad; + WPADChan channel; + }; +} // namespace love diff --git a/platform/cafe/include/utilities/sensor/accelerometer.hpp b/platform/cafe/include/utilities/sensor/accelerometer.hpp new file mode 100644 index 000000000..43a2e8afe --- /dev/null +++ b/platform/cafe/include/utilities/sensor/accelerometer.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include + +#include +#include + +namespace love +{ + class Accelerometer : public SensorBase + { + public: + Accelerometer(); + + ~Accelerometer(); + + void Update(VPADVec3D input) + { + this->data = { input.x, input.y, input.z }; + } + + void Update(KPADVec3D input) + { + this->data = { input.x, input.y, input.z }; + } + + std::vector GetData() override + { + return this->data; + } + + private: + std::vector data; + }; +} // namespace love diff --git a/platform/cafe/include/utilities/sensor/gyroscope.hpp b/platform/cafe/include/utilities/sensor/gyroscope.hpp new file mode 100644 index 000000000..35affba13 --- /dev/null +++ b/platform/cafe/include/utilities/sensor/gyroscope.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include + +#include +#include + +namespace love +{ + class Gyroscope : public SensorBase + { + public: + Gyroscope(); + + ~Gyroscope(); + + void Update(VPADVec3D input) + { + this->data = { input.x, input.y, input.z }; + } + + void Update(KPADVec3D input) + { + this->data = { input.x, input.y, input.z }; + } + + std::vector GetData() override + { + return this->data; + } + + private: + std::vector data; + }; +} // namespace love diff --git a/platform/cafe/include/utilities/threads/threads.hpp b/platform/cafe/include/utilities/threads/threads.hpp new file mode 100644 index 000000000..47d538f4b --- /dev/null +++ b/platform/cafe/include/utilities/threads/threads.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +namespace love +{ + using mutex = std::mutex; + using conditional = std::condition_variable; + using thread = std::thread; +} // namespace love diff --git a/platform/cafe/include/utilities/wpad.hpp b/platform/cafe/include/utilities/wpad.hpp new file mode 100644 index 000000000..4776d0ed3 --- /dev/null +++ b/platform/cafe/include/utilities/wpad.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace love::wpad +{ + static constexpr size_t MAX_JOYSTICKS = 4; + + guid::GamepadType GetWPADType(KPADExtensionType extension); +} // namespace love::wpad diff --git a/platform/cafe/libraries/luasocket.patch b/platform/cafe/libraries/luasocket.patch new file mode 100644 index 000000000..8d2a9fb65 --- /dev/null +++ b/platform/cafe/libraries/luasocket.patch @@ -0,0 +1,2109 @@ +diff --git a/inet.c b/inet.c +index 138c9ab..35b8131 100755 +--- a/inet.c ++++ b/inet.c +@@ -139,9 +139,13 @@ static int inet_global_toip(lua_State *L) + + int inet_optfamily(lua_State* L, int narg, const char* def) + { ++ #if !defined(__WIIU__) + static const char* optname[] = { "unspec", "inet", "inet6", NULL }; + static int optvalue[] = { AF_UNSPEC, AF_INET, AF_INET6, 0 }; +- ++ #else ++ static const char* optname[] = { "unspec", "inet", NULL }; ++ static int optvalue[] = { AF_UNSPEC, AF_INET, 0 }; ++ #endif + return optvalue[luaL_checkoption(L, narg, def, optname)]; + } + +@@ -187,11 +191,13 @@ static int inet_global_getaddrinfo(lua_State *L) + lua_pushliteral(L, "inet"); + lua_settable(L, -3); + break; ++ #if !defined(__WIIU__) + case AF_INET6: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "inet6"); + lua_settable(L, -3); + break; ++ #endif + case AF_UNSPEC: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "unspec"); +@@ -241,16 +247,26 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family) + int err; + struct sockaddr_storage peer; + socklen_t peer_len = sizeof(peer); ++ #if !defined(__WIIU__) + char name[INET6_ADDRSTRLEN]; ++ #else ++ char name[INET_ADDRSTRLEN]; ++ #endif + char port[6]; /* 65535 = 5 bytes + 0 to terminate it */ + if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } ++ #if !defined(__WIIU__) + err = getnameinfo((struct sockaddr *) &peer, peer_len, + name, INET6_ADDRSTRLEN, + port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); ++ #else ++ err = getnameinfo((struct sockaddr *) &peer, peer_len, ++ name, INET_ADDRSTRLEN, ++ port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); ++ #endif + if (err) { + lua_pushnil(L); + lua_pushstring(L, LUA_GAI_STRERROR(err)); +@@ -260,7 +276,9 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family) + lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10)); + switch (family) { + case AF_INET: lua_pushliteral(L, "inet"); break; ++ #if !defined(__WIIU__) + case AF_INET6: lua_pushliteral(L, "inet6"); break; ++ #endif + case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; + default: lua_pushliteral(L, "unknown"); break; + } +@@ -275,15 +293,24 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) + int err; + struct sockaddr_storage peer; + socklen_t peer_len = sizeof(peer); ++ #if !defined(__WIIU__) + char name[INET6_ADDRSTRLEN]; ++ #else ++ char name[INET_ADDRSTRLEN]; ++ #endif + char port[6]; /* 65535 = 5 bytes + 0 to terminate it */ + if (getsockname(*ps, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } ++ #if !defined(__WIIU__) + err=getnameinfo((struct sockaddr *)&peer, peer_len, + name, INET6_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV); ++ #else ++ err=getnameinfo((struct sockaddr *)&peer, peer_len, ++ name, INET_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV); ++ #endif + if (err) { + lua_pushnil(L); + lua_pushstring(L, LUA_GAI_STRERROR(err)); +@@ -293,7 +320,9 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) + lua_pushstring(L, port); + switch (family) { + case AF_INET: lua_pushliteral(L, "inet"); break; ++ #if !defined(__WIIU__) + case AF_INET6: lua_pushliteral(L, "inet6"); break; ++ #endif + case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; + default: lua_pushliteral(L, "unknown"); break; + } +@@ -348,10 +377,12 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp) + \*-------------------------------------------------------------------------*/ + const char *inet_trycreate(p_socket ps, int family, int type, int protocol) { + const char *err = socket_strerror(socket_create(ps, family, type, protocol)); ++ #if !defined(__WIIU__) + if (err == NULL && family == AF_INET6) { + int yes = 1; + setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); + } ++ #endif + return err; + } + +@@ -369,6 +400,7 @@ const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) + return socket_strerror(socket_connect(ps, (SA *) &sin, + sizeof(sin), tm)); + } ++ #if !defined(__WIIU__) + case AF_INET6: { + struct sockaddr_in6 sin6; + struct in6_addr addrany = IN6ADDR_ANY_INIT; +@@ -378,6 +410,7 @@ const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) + return socket_strerror(socket_connect(ps, (SA *) &sin6, + sizeof(sin6), tm)); + } ++ #endif + } + return NULL; + } +@@ -436,7 +469,9 @@ const char *inet_tryaccept(p_socket server, int family, p_socket client, + socklen_t len; + t_sockaddr_storage addr; + switch (family) { ++ #if !defined(__WIIU__) + case AF_INET6: len = sizeof(struct sockaddr_in6); break; ++ #endif + case AF_INET: len = sizeof(struct sockaddr_in); break; + default: len = sizeof(addr); break; + } +diff --git a/makefile b/makefile +deleted file mode 100755 +index 06f4d19..0000000 +--- a/makefile ++++ /dev/null +@@ -1,461 +0,0 @@ +-# luasocket src/makefile +-# +-# Definitions in this section can be overriden on the command line or in the +-# environment. +-# +-# These are equivalent: +-# +-# export PLAT=linux DEBUG=DEBUG LUAV=5.2 prefix=/sw +-# make +-# +-# and +-# +-# make PLAT=linux DEBUG=DEBUG LUAV=5.2 prefix=/sw +- +-# PLAT: linux macosx win32 win64 mingw +-# platform to build for +-PLAT?=linux +- +-# LUAV: 5.1 5.2 5.3 5.4 +-# lua version to build against +-LUAV?=5.1 +- +-# MYCFLAGS: to be set by user if needed +-MYCFLAGS?= +- +-# MYLDFLAGS: to be set by user if needed +-MYLDFLAGS?= +- +-# DEBUG: NODEBUG DEBUG +-# debug mode causes luasocket to collect and returns timing information useful +-# for testing and debugging luasocket itself +-DEBUG?=NODEBUG +- +-# where lua headers are found for macosx builds +-# LUAINC_macosx: +-# /opt/local/include +-LUAINC_macosx_base?=/opt/local/include +-LUAINC_macosx?=$(LUAINC_macosx_base)/lua/$(LUAV) $(LUAINC_macosx_base)/lua$(LUAV) $(LUAINC_macosx_base)/lua-$(LUAV) +- +-# FIXME default should this default to fink or to macports? +-# What happens when more than one Lua version is installed? +-LUAPREFIX_macosx?=/opt/local +-CDIR_macosx?=lib/lua/$(LUAV) +-LDIR_macosx?=share/lua/$(LUAV) +- +-# LUAINC_linux: +-# /usr/include/lua$(LUAV) +-# /usr/local/include +-# /usr/local/include/lua$(LUAV) +-# where lua headers are found for linux builds +-LUAINC_linux_base?=/usr/include +-LUAINC_linux?=$(LUAINC_linux_base)/lua/$(LUAV) $(LUAINC_linux_base)/lua$(LUAV) +-LUAPREFIX_linux?=/usr/local +-CDIR_linux?=lib/lua/$(LUAV) +-LDIR_linux?=share/lua/$(LUAV) +- +-# LUAINC_freebsd: +-# /usr/local/include/lua$(LUAV) +-# where lua headers are found for freebsd builds +-LUAINC_freebsd_base?=/usr/local/include/ +-LUAINC_freebsd?=$(LUAINC_freebsd_base)/lua/$(LUAV) $(LUAINC_freebsd_base)/lua$(LUAV) +-LUAPREFIX_freebsd?=/usr/local/ +-CDIR_freebsd?=lib/lua/$(LUAV) +-LDIR_freebsd?=share/lua/$(LUAV) +- +-# where lua headers are found for mingw builds +-# LUAINC_mingw: +-# /opt/local/include +-LUAINC_mingw_base?=/usr/include +-LUAINC_mingw?=$(LUAINC_mingw_base)/lua/$(LUAV) $(LUAINC_mingw_base)/lua$(LUAV) +-LUALIB_mingw_base?=/usr/bin +-LUALIB_mingw?=$(LUALIB_mingw_base)/lua/$(LUAV)/lua$(subst .,,$(LUAV)).dll +-LUAPREFIX_mingw?=/usr +-CDIR_mingw?=lua/$(LUAV) +-LDIR_mingw?=lua/$(LUAV)/lua +- +- +-# LUAINC_win32: +-# LUALIB_win32: +-# where lua headers and libraries are found for win32 builds +-LUAPREFIX_win32?= +-LUAINC_win32?=$(LUAPREFIX_win32)/include/lua/$(LUAV) $(LUAPREFIX_win32)/include/lua$(LUAV) +-PLATFORM_win32?=Release +-CDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32) +-LDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32)/lua +-LUALIB_win32?=$(LUAPREFIX_win32)/lib/lua/$(LUAV)/$(PLATFORM_win32) +-LUALIBNAME_win32?=lua$(subst .,,$(LUAV)).lib +- +-# LUAINC_win64: +-# LUALIB_win64: +-# where lua headers and libraries are found for win64 builds +-LUAPREFIX_win64?= +-LUAINC_win64?=$(LUAPREFIX_win64)/include/lua/$(LUAV) $(LUAPREFIX_win64)/include/lua$(LUAV) +-PLATFORM_win64?=x64/Release +-CDIR_win64?=bin/lua/$(LUAV)/$(PLATFORM_win64) +-LDIR_win64?=bin/lua/$(LUAV)/$(PLATFORM_win64)/lua +-LUALIB_win64?=$(LUAPREFIX_win64)/lib/lua/$(LUAV)/$(PLATFORM_win64) +-LUALIBNAME_win64?=lua$(subst .,,$(LUAV)).lib +- +- +-# LUAINC_solaris: +-LUAINC_solaris_base?=/usr/include +-LUAINC_solaris?=$(LUAINC_solaris_base)/lua/$(LUAV) $(LUAINC_solaris_base)/lua$(LUAV) +-LUAPREFIX_solaris?=/usr/local +-CDIR_solaris?=lib/lua/$(LUAV) +-LDIR_solaris?=share/lua/$(LUAV) +- +-# prefix: /usr/local /usr /opt/local /sw +-# the top of the default install tree +-prefix?=$(LUAPREFIX_$(PLAT)) +- +-CDIR?=$(CDIR_$(PLAT)) +-LDIR?=$(LDIR_$(PLAT)) +- +-# DESTDIR: (no default) +-# used by package managers to install into a temporary destination +-DESTDIR?= +- +-#------ +-# Definitions below can be overridden on the make command line, but +-# shouldn't have to be. +- +- +-#------ +-# Install directories +-# +- +-INSTALL_DIR=install -d +-INSTALL_DATA=install -m644 +-INSTALL_EXEC=install +-INSTALL_TOP=$(DESTDIR)$(prefix) +- +-INSTALL_TOP_LDIR=$(INSTALL_TOP)/$(LDIR) +-INSTALL_TOP_CDIR=$(INSTALL_TOP)/$(CDIR) +- +-INSTALL_SOCKET_LDIR=$(INSTALL_TOP_LDIR)/socket +-INSTALL_SOCKET_CDIR=$(INSTALL_TOP_CDIR)/socket +-INSTALL_MIME_LDIR=$(INSTALL_TOP_LDIR)/mime +-INSTALL_MIME_CDIR=$(INSTALL_TOP_CDIR)/mime +- +-print: +- @echo PLAT=$(PLAT) +- @echo LUAV=$(LUAV) +- @echo DEBUG=$(DEBUG) +- @echo prefix=$(prefix) +- @echo LUAINC_$(PLAT)=$(LUAINC_$(PLAT)) +- @echo LUALIB_$(PLAT)=$(LUALIB_$(PLAT)) +- @echo INSTALL_TOP_CDIR=$(INSTALL_TOP_CDIR) +- @echo INSTALL_TOP_LDIR=$(INSTALL_TOP_LDIR) +- @echo CFLAGS=$(CFLAGS) +- @echo LDFLAGS=$(LDFLAGS) +- +-#------ +-# Supported platforms +-# +-PLATS= macosx linux win32 win64 mingw solaris +- +-#------ +-# Compiler and linker settings +-# for Mac OS X +-SO_macosx=so +-O_macosx=o +-CC_macosx=gcc +-DEF_macosx= -DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN +-CFLAGS_macosx=$(LUAINC:%=-I%) $(DEF) -Wall -O2 -fno-common +-LDFLAGS_macosx= -bundle -undefined dynamic_lookup -o +-LD_macosx=gcc +-SOCKET_macosx=usocket.o +- +-#------ +-# Compiler and linker settings +-# for Linux +-SO_linux=so +-O_linux=o +-CC_linux=gcc +-DEF_linux=-DLUASOCKET_$(DEBUG) +-CFLAGS_linux=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ +- -Wimplicit -O2 -ggdb3 -fpic +-LDFLAGS_linux=-O -shared -fpic -o +-LD_linux=gcc +-SOCKET_linux=usocket.o +- +-#------ +-# Compiler and linker settings +-# for FreeBSD +-SO_freebsd=so +-O_freebsd=o +-CC_freebsd=gcc +-DEF_freebsd=-DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN +-CFLAGS_freebsd=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ +- -Wimplicit -O2 -ggdb3 -fpic +-LDFLAGS_freebsd=-O -shared -fpic -o +-LD_freebsd=gcc +-SOCKET_freebsd=usocket.o +- +-#------ +-# Compiler and linker settings +-# for Solaris +-SO_solaris=so +-O_solaris=o +-CC_solaris=gcc +-DEF_solaris=-DLUASOCKET_$(DEBUG) +-CFLAGS_solaris=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ +- -Wimplicit -O2 -ggdb3 -fpic +-LDFLAGS_solaris=-lnsl -lsocket -lresolv -O -shared -fpic -o +-LD_solaris=gcc +-SOCKET_solaris=usocket.o +- +-#------ +-# Compiler and linker settings +-# for MingW +-SO_mingw=dll +-O_mingw=o +-CC_mingw=gcc +-DEF_mingw= -DLUASOCKET_$(DEBUG) \ +- -DWINVER=0x0501 +-CFLAGS_mingw=$(LUAINC:%=-I%) $(DEF) -Wall -O2 -fno-common +-LDFLAGS_mingw= $(LUALIB) -shared -Wl,-s -lws2_32 -o +-LD_mingw=gcc +-SOCKET_mingw=wsocket.o +- +- +-#------ +-# Compiler and linker settings +-# for Win32 +-SO_win32=dll +-O_win32=obj +-CC_win32=cl +-DEF_win32= //D "WIN32" //D "NDEBUG" //D "_WINDOWS" //D "_USRDLL" \ +- //D "_CRT_SECURE_NO_WARNINGS" \ +- //D "_WINDLL" \ +- //D "LUASOCKET_$(DEBUG)" +-CFLAGS_win32=$(LUAINC:%=//I "%") $(DEF) //O2 //Ot //MD //W3 //nologo +-LDFLAGS_win32= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \ +- //MANIFEST //MANIFESTFILE:"intermediate.manifest" \ +- /MANIFESTUAC:"level='asInvoker' uiAccess='false'" \ +- //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \ +- //MACHINE:X86 /LIBPATH:"$(LUALIB)" \ +- $(LUALIBNAME_win32) ws2_32.lib //OUT: +- +-LD_win32=cl +-SOCKET_win32=wsocket.obj +- +-#------ +-# Compiler and linker settings +-# for Win64 +-SO_win64=dll +-O_win64=obj +-CC_win64=cl +-DEF_win64= //D "WIN32" //D "NDEBUG" //D "_WINDOWS" //D "_USRDLL" \ +- //D "_CRT_SECURE_NO_WARNINGS" \ +- //D "_WINDLL" \ +- //D "LUASOCKET_$(DEBUG)" +-CFLAGS_win64=$(LUAINC:%=//I "%") $(DEF) //O2 //Ot //MD //W3 //nologo +-LDFLAGS_win64= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \ +- //MANIFEST //MANIFESTFILE:"intermediate.manifest" \ +- /MANIFESTUAC:"level='asInvoker' uiAccess='false'" \ +- //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \ +- /LIBPATH:"$(LUALIB)" \ +- $(LUALIBNAME_win64) ws2_32.lib //OUT: +- +-LD_win64=cl +-SOCKET_win64=wsocket.obj +- +-.SUFFIXES: .obj +- +-.c.obj: +- $(CC) $(CFLAGS) //Fo"$@" //c $< +- +-#------ +-# Output file names +-# +-SO=$(SO_$(PLAT)) +-O=$(O_$(PLAT)) +-SOCKET_V=3.0.0 +-MIME_V=1.0.3 +-SOCKET_SO=socket-$(SOCKET_V).$(SO) +-MIME_SO=mime-$(MIME_V).$(SO) +-UNIX_SO=unix.$(SO) +-SERIAL_SO=serial.$(SO) +-SOCKET=$(SOCKET_$(PLAT)) +- +-#------ +-# Settings selected for platform +-# +-CC=$(CC_$(PLAT)) +-DEF=$(DEF_$(PLAT)) +-CFLAGS=$(MYCFLAGS) $(CFLAGS_$(PLAT)) +-LDFLAGS=$(MYLDFLAGS) $(LDFLAGS_$(PLAT)) +-LD=$(LD_$(PLAT)) +-LUAINC= $(LUAINC_$(PLAT)) +-LUALIB= $(LUALIB_$(PLAT)) +- +-#------ +-# Modules belonging to socket-core +-# +-SOCKET_OBJS= \ +- luasocket.$(O) \ +- timeout.$(O) \ +- buffer.$(O) \ +- io.$(O) \ +- auxiliar.$(O) \ +- compat.$(O) \ +- options.$(O) \ +- inet.$(O) \ +- $(SOCKET) \ +- except.$(O) \ +- select.$(O) \ +- tcp.$(O) \ +- udp.$(O) +- +-#------ +-# Modules belonging mime-core +-# +-MIME_OBJS= \ +- mime.$(O) \ +- compat.$(O) +- +-#------ +-# Modules belonging unix (local domain sockets) +-# +-UNIX_OBJS=\ +- buffer.$(O) \ +- auxiliar.$(O) \ +- options.$(O) \ +- timeout.$(O) \ +- io.$(O) \ +- usocket.$(O) \ +- unixstream.$(O) \ +- unixdgram.$(O) \ +- compat.$(O) \ +- unix.$(O) +- +-#------ +-# Modules belonging to serial (device streams) +-# +-SERIAL_OBJS=\ +- buffer.$(O) \ +- compat.$(O) \ +- auxiliar.$(O) \ +- options.$(O) \ +- timeout.$(O) \ +- io.$(O) \ +- usocket.$(O) \ +- serial.$(O) +- +-#------ +-# Files to install +-# +-TO_SOCKET_LDIR= \ +- http.lua \ +- url.lua \ +- tp.lua \ +- ftp.lua \ +- headers.lua \ +- smtp.lua +- +-TO_TOP_LDIR= \ +- ltn12.lua \ +- socket.lua \ +- mime.lua +- +-#------ +-# Targets +-# +-default: $(PLAT) +- +- +-freebsd: +- $(MAKE) all-unix PLAT=freebsd +- +-macosx: +- $(MAKE) all-unix PLAT=macosx +- +-win32: +- $(MAKE) all PLAT=win32 +- +-win64: +- $(MAKE) all PLAT=win64 +- +-linux: +- $(MAKE) all-unix PLAT=linux +- +-mingw: +- $(MAKE) all PLAT=mingw +- +-solaris: +- $(MAKE) all-unix PLAT=solaris +- +-none: +- @echo "Please run" +- @echo " make PLATFORM" +- @echo "where PLATFORM is one of these:" +- @echo " $(PLATS)" +- +-all: $(SOCKET_SO) $(MIME_SO) +- +-$(SOCKET_SO): $(SOCKET_OBJS) +- $(LD) $(SOCKET_OBJS) $(LDFLAGS)$@ +- +-$(MIME_SO): $(MIME_OBJS) +- $(LD) $(MIME_OBJS) $(LDFLAGS)$@ +- +-all-unix: all $(UNIX_SO) $(SERIAL_SO) +- +-$(UNIX_SO): $(UNIX_OBJS) +- $(LD) $(UNIX_OBJS) $(LDFLAGS)$@ +- +-$(SERIAL_SO): $(SERIAL_OBJS) +- $(LD) $(SERIAL_OBJS) $(LDFLAGS)$@ +- +-install: +- $(INSTALL_DIR) $(INSTALL_TOP_LDIR) +- $(INSTALL_DATA) $(TO_TOP_LDIR) $(INSTALL_TOP_LDIR) +- $(INSTALL_DIR) $(INSTALL_SOCKET_LDIR) +- $(INSTALL_DATA) $(TO_SOCKET_LDIR) $(INSTALL_SOCKET_LDIR) +- $(INSTALL_DIR) $(INSTALL_SOCKET_CDIR) +- $(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET_CDIR)/core.$(SO) +- $(INSTALL_DIR) $(INSTALL_MIME_CDIR) +- $(INSTALL_EXEC) $(MIME_SO) $(INSTALL_MIME_CDIR)/core.$(SO) +- +-install-unix: install +- $(INSTALL_EXEC) $(UNIX_SO) $(INSTALL_SOCKET_CDIR)/$(UNIX_SO) +- $(INSTALL_EXEC) $(SERIAL_SO) $(INSTALL_SOCKET_CDIR)/$(SERIAL_SO) +- +-local: +- $(MAKE) install INSTALL_TOP_CDIR=.. INSTALL_TOP_LDIR=.. +- +-clean: +- rm -f $(SOCKET_SO) $(SOCKET_OBJS) $(SERIAL_OBJS) +- rm -f $(MIME_SO) $(UNIX_SO) $(SERIAL_SO) $(MIME_OBJS) $(UNIX_OBJS) +- +-.PHONY: all $(PLATS) default clean echo none +- +-#------ +-# List of dependencies +-# +-compat.$(O): compat.c compat.h +-auxiliar.$(O): auxiliar.c auxiliar.h +-buffer.$(O): buffer.c buffer.h io.h timeout.h +-except.$(O): except.c except.h +-inet.$(O): inet.c inet.h socket.h io.h timeout.h usocket.h +-io.$(O): io.c io.h timeout.h +-luasocket.$(O): luasocket.c luasocket.h auxiliar.h except.h \ +- timeout.h buffer.h io.h inet.h socket.h usocket.h tcp.h \ +- udp.h select.h +-mime.$(O): mime.c mime.h +-options.$(O): options.c auxiliar.h options.h socket.h io.h \ +- timeout.h usocket.h inet.h +-select.$(O): select.c socket.h io.h timeout.h usocket.h select.h +-serial.$(O): serial.c auxiliar.h socket.h io.h timeout.h usocket.h \ +- options.h unix.h buffer.h +-tcp.$(O): tcp.c auxiliar.h socket.h io.h timeout.h usocket.h \ +- inet.h options.h tcp.h buffer.h +-timeout.$(O): timeout.c auxiliar.h timeout.h +-udp.$(O): udp.c auxiliar.h socket.h io.h timeout.h usocket.h \ +- inet.h options.h udp.h +-unix.$(O): unix.c auxiliar.h socket.h io.h timeout.h usocket.h \ +- options.h unix.h buffer.h +-usocket.$(O): usocket.c socket.h io.h timeout.h usocket.h +-wsocket.$(O): wsocket.c socket.h io.h timeout.h usocket.h +diff --git a/options.c b/options.c +index 3280c51..ce8fcb4 100644 +--- a/options.c ++++ b/options.c +@@ -22,6 +22,22 @@ static int opt_set(lua_State *L, p_socket ps, int level, int name, + static int opt_get(lua_State *L, p_socket ps, int level, int name, + void *val, int* len); + ++static int set_opt_error(lua_State* L) ++{ ++ lua_pushnil(L); ++ lua_pushstring(L, "setsockopt failed: not supported"); ++ ++ return 2; ++} ++ ++static int get_opt_error(lua_State* L) ++{ ++ lua_pushnil(L); ++ lua_pushstring(L, "getsockopt failed: not supported"); ++ ++ return 2; ++} ++ + /*=========================================================================*\ + * Exported functions + \*=========================================================================*/ +@@ -147,6 +163,7 @@ int opt_get_keepalive(lua_State *L, p_socket ps) + } + + /*------------------------------------------------------*/ ++#if !defined(__WIIU__) + int opt_set_dontroute(lua_State *L, p_socket ps) + { + return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); +@@ -156,6 +173,10 @@ int opt_get_dontroute(lua_State *L, p_socket ps) + { + return opt_getboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); + } ++#else ++int opt_set_dontroute(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_dontroute(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ + int opt_set_broadcast(lua_State *L, p_socket ps) +@@ -216,6 +237,7 @@ int opt_set_tcp_defer_accept(lua_State *L, p_socket ps) + #endif + + /*------------------------------------------------------*/ ++#if !defined(__WIIU__) + int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) + { + return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); +@@ -236,7 +258,6 @@ int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps) + { + return opt_getint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); + } +- + /*------------------------------------------------------*/ + int opt_set_ip_multicast_loop(lua_State *L, p_socket ps) + { +@@ -247,8 +268,19 @@ int opt_get_ip_multicast_loop(lua_State *L, p_socket ps) + { + return opt_getboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP); + } ++#else ++int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps) { return get_opt_error(L); } ++ ++int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps) { return get_opt_error(L); } ++ ++int opt_set_ip_multicast_loop(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip_multicast_loop(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ ++#if !defined(__WIIU__) + int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps) + { + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +@@ -258,7 +290,10 @@ int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps) + { + return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); + } +- ++#else ++int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + /*------------------------------------------------------*/ + int opt_set_linger(lua_State *L, p_socket ps) + { +@@ -293,6 +328,7 @@ int opt_get_linger(lua_State *L, p_socket ps) + } + + /*------------------------------------------------------*/ ++#if !defined(__WIIU__) + int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps) + { + return opt_setint(L, ps, IPPROTO_IP, IP_MULTICAST_TTL); +@@ -355,6 +391,21 @@ int opt_set_ip6_v6only(lua_State *L, p_socket ps) + { + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); + } ++#else ++int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps) { return set_opt_error(L); } ++ ++int opt_set_ip_multicast_if(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip_multicast_if(lua_State *L, p_socket ps) { return get_opt_error(L); } ++ ++int opt_set_ip_add_membership(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_set_ip_drop_membersip(lua_State *L, p_socket ps) { return set_opt_error(L); } ++ ++int opt_set_ip6_add_membership(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps) { return set_opt_error(L); } ++ ++int opt_get_ip6_v6only(lua_State *L, p_socket ps) { return get_opt_error(L); } ++int opt_set_ip6_v6only(lua_State *L, p_socket ps) { return set_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ + int opt_get_error(lua_State *L, p_socket ps) +@@ -373,6 +424,7 @@ int opt_get_error(lua_State *L, p_socket ps) + /*=========================================================================*\ + * Auxiliar functions + \*=========================================================================*/ ++#if !defined(__WIIU__) + static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) + { + struct ip_mreq val; /* obj, name, table */ +@@ -419,6 +471,10 @@ static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) + } + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); + } ++#else ++static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) { return set_opt_error(L); } ++static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) { return set_opt_error(L); } ++#endif + + static + int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len) +diff --git a/serial.c b/serial.c +deleted file mode 100644 +index 21485d3..0000000 +--- a/serial.c ++++ /dev/null +@@ -1,171 +0,0 @@ +-/*=========================================================================*\ +-* Serial stream +-* LuaSocket toolkit +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "auxiliar.h" +-#include "socket.h" +-#include "options.h" +-#include "unix.h" +- +-#include +-#include +- +-/* +-Reuses userdata definition from unix.h, since it is useful for all +-stream-like objects. +- +-If we stored the serial path for use in error messages or userdata +-printing, we might need our own userdata definition. +- +-Group usage is semi-inherited from unix.c, but unnecessary since we +-have only one object type. +-*/ +- +-/*=========================================================================*\ +-* Internal function prototypes +-\*=========================================================================*/ +-static int global_create(lua_State *L); +-static int meth_send(lua_State *L); +-static int meth_receive(lua_State *L); +-static int meth_close(lua_State *L); +-static int meth_settimeout(lua_State *L); +-static int meth_getfd(lua_State *L); +-static int meth_setfd(lua_State *L); +-static int meth_dirty(lua_State *L); +-static int meth_getstats(lua_State *L); +-static int meth_setstats(lua_State *L); +- +-/* serial object methods */ +-static luaL_Reg serial_methods[] = { +- {"__gc", meth_close}, +- {"__tostring", auxiliar_tostring}, +- {"close", meth_close}, +- {"dirty", meth_dirty}, +- {"getfd", meth_getfd}, +- {"getstats", meth_getstats}, +- {"setstats", meth_setstats}, +- {"receive", meth_receive}, +- {"send", meth_send}, +- {"setfd", meth_setfd}, +- {"settimeout", meth_settimeout}, +- {NULL, NULL} +-}; +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-LUASOCKET_API int luaopen_socket_serial(lua_State *L) { +- /* create classes */ +- auxiliar_newclass(L, "serial{client}", serial_methods); +- /* create class groups */ +- auxiliar_add2group(L, "serial{client}", "serial{any}"); +- lua_pushcfunction(L, global_create); +- return 1; +-} +- +-/*=========================================================================*\ +-* Lua methods +-\*=========================================================================*/ +-/*-------------------------------------------------------------------------*\ +-* Just call buffered IO methods +-\*-------------------------------------------------------------------------*/ +-static int meth_send(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_send(L, &un->buf); +-} +- +-static int meth_receive(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_receive(L, &un->buf); +-} +- +-static int meth_getstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_getstats(L, &un->buf); +-} +- +-static int meth_setstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_setstats(L, &un->buf); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Select support methods +-\*-------------------------------------------------------------------------*/ +-static int meth_getfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- lua_pushnumber(L, (int) un->sock); +- return 1; +-} +- +-/* this is very dangerous, but can be handy for those that are brave enough */ +-static int meth_setfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- un->sock = (t_socket) luaL_checknumber(L, 2); +- return 0; +-} +- +-static int meth_dirty(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- lua_pushboolean(L, !buffer_isempty(&un->buf)); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Closes socket used by object +-\*-------------------------------------------------------------------------*/ +-static int meth_close(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- socket_destroy(&un->sock); +- lua_pushnumber(L, 1); +- return 1; +-} +- +- +-/*-------------------------------------------------------------------------*\ +-* Just call tm methods +-\*-------------------------------------------------------------------------*/ +-static int meth_settimeout(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- return timeout_meth_settimeout(L, &un->tm); +-} +- +-/*=========================================================================*\ +-* Library functions +-\*=========================================================================*/ +- +- +-/*-------------------------------------------------------------------------*\ +-* Creates a serial object +-\*-------------------------------------------------------------------------*/ +-static int global_create(lua_State *L) { +- const char* path = luaL_checkstring(L, 1); +- +- /* allocate unix object */ +- p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); +- +- /* open serial device */ +- t_socket sock = open(path, O_NOCTTY|O_RDWR); +- +- /*printf("open %s on %d\n", path, sock);*/ +- +- if (sock < 0) { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(errno)); +- lua_pushnumber(L, errno); +- return 3; +- } +- /* set its type as client object */ +- auxiliar_setclass(L, "serial{client}", -1); +- /* initialize remaining structure fields */ +- socket_setnonblocking(&sock); +- un->sock = sock; +- io_init(&un->io, (p_send) socket_write, (p_recv) socket_read, +- (p_error) socket_ioerror, &un->sock); +- timeout_init(&un->tm, -1, -1); +- buffer_init(&un->buf, &un->io, &un->tm); +- return 1; +-} +diff --git a/tcp.c b/tcp.c +index e84db84..d718a85 100644 +--- a/tcp.c ++++ b/tcp.c +@@ -312,6 +312,7 @@ static int meth_close(lua_State *L) + static int meth_getfamily(lua_State *L) + { + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); ++ #if !defined(__WIIU__) + if (tcp->family == AF_INET6) { + lua_pushliteral(L, "inet6"); + return 1; +@@ -322,6 +323,15 @@ static int meth_getfamily(lua_State *L) + lua_pushliteral(L, "inet4"); + return 1; + } ++ #else ++ if (tcp->family == AF_INET) { ++ lua_pushliteral(L, "inet4"); ++ return 1; ++ } else { ++ lua_pushliteral(L, "inet4"); ++ return 1; ++ } ++ #endif + } + + /*-------------------------------------------------------------------------*\ +@@ -427,9 +437,18 @@ static int global_create4(lua_State *L) { + return tcp_create(L, AF_INET); + } + ++#if !defined(__WIIU__) + static int global_create6(lua_State *L) { + return tcp_create(L, AF_INET6); + } ++#else ++static int global_create6(lua_State *L) { ++ lua_pushnil(L); ++ lua_pushstring(L, "Setting local interface error: not supported"); ++ ++ return 2; ++} ++#endif + + static int global_connect(lua_State *L) { + const char *remoteaddr = luaL_checkstring(L, 1); +diff --git a/udp.c b/udp.c +index 712ad50..7262c8e 100755 +--- a/udp.c ++++ b/udp.c +@@ -267,7 +267,11 @@ static int meth_receivefrom(lua_State *L) { + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); ++ #if !defined(__WIIU__) + char addrstr[INET6_ADDRSTRLEN]; ++ #else ++ char addrstr[INET_ADDRSTRLEN]; ++ #endif + char portstr[6]; + int err; + p_timeout tm = &udp->tm; +@@ -286,8 +290,13 @@ static int meth_receivefrom(lua_State *L) { + if (wanted > sizeof(buf)) free(dgram); + return 2; + } ++ #if !defined(__WIIU__) + err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, + INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV); ++ #else ++ err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, ++ INET_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV); ++ #endif + if (err) { + lua_pushnil(L); + lua_pushstring(L, LUA_GAI_STRERROR(err)); +@@ -306,6 +315,7 @@ static int meth_receivefrom(lua_State *L) { + \*-------------------------------------------------------------------------*/ + static int meth_getfamily(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); ++ #if !defined(__WIIU__) + if (udp->family == AF_INET6) { + lua_pushliteral(L, "inet6"); + return 1; +@@ -313,6 +323,10 @@ static int meth_getfamily(lua_State *L) { + lua_pushliteral(L, "inet4"); + return 1; + } ++ #else ++ lua_pushliteral(L, "inet4"); ++ return 1; ++ #endif + } + + /*-------------------------------------------------------------------------*\ +@@ -483,6 +497,15 @@ static int global_create4(lua_State *L) { + return udp_create(L, AF_INET); + } + ++#if !defined(__WIIU__) + static int global_create6(lua_State *L) { + return udp_create(L, AF_INET6); + } ++#else ++static int global_create6(lua_State *L) { ++ lua_pushnil(L); ++ lua_pushstring(L, "Setting local interface error: not supported"); ++ ++ return 2; ++} ++#endif +diff --git a/unix.c b/unix.c +deleted file mode 100644 +index 268d8b2..0000000 +--- a/unix.c ++++ /dev/null +@@ -1,69 +0,0 @@ +-/*=========================================================================*\ +-* Unix domain socket +-* LuaSocket toolkit +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "unixstream.h" +-#include "unixdgram.h" +- +-/*-------------------------------------------------------------------------*\ +-* Modules and functions +-\*-------------------------------------------------------------------------*/ +-static const luaL_Reg mod[] = { +- {"stream", unixstream_open}, +- {"dgram", unixdgram_open}, +- {NULL, NULL} +-}; +- +-static void add_alias(lua_State *L, int index, const char *name, const char *target) +-{ +- lua_getfield(L, index, target); +- lua_setfield(L, index, name); +-} +- +-static int compat_socket_unix_call(lua_State *L) +-{ +- /* Look up socket.unix.stream in the socket.unix table (which is the first +- * argument). */ +- lua_getfield(L, 1, "stream"); +- +- /* Replace the stack entry for the socket.unix table with the +- * socket.unix.stream function. */ +- lua_replace(L, 1); +- +- /* Call socket.unix.stream, passing along any arguments. */ +- int n = lua_gettop(L); +- lua_call(L, n-1, LUA_MULTRET); +- +- /* Pass along the return values from socket.unix.stream. */ +- n = lua_gettop(L); +- return n; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-LUASOCKET_API int luaopen_socket_unix(lua_State *L) +-{ +- int i; +- lua_newtable(L); +- int socket_unix_table = lua_gettop(L); +- +- for (i = 0; mod[i].name; i++) +- mod[i].func(L); +- +- /* Add backwards compatibility aliases "tcp" and "udp" for the "stream" and +- * "dgram" functions. */ +- add_alias(L, socket_unix_table, "tcp", "stream"); +- add_alias(L, socket_unix_table, "udp", "dgram"); +- +- /* Add a backwards compatibility function and a metatable setup to call it +- * for the old socket.unix() interface. */ +- lua_pushcfunction(L, compat_socket_unix_call); +- lua_setfield(L, socket_unix_table, "__call"); +- lua_pushvalue(L, socket_unix_table); +- lua_setmetatable(L, socket_unix_table); +- +- return 1; +-} +diff --git a/unix.h b/unix.h +deleted file mode 100644 +index c203561..0000000 +--- a/unix.h ++++ /dev/null +@@ -1,26 +0,0 @@ +-#ifndef UNIX_H +-#define UNIX_H +-/*=========================================================================*\ +-* Unix domain object +-* LuaSocket toolkit +-* +-* This module is just an example of how to extend LuaSocket with a new +-* domain. +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "buffer.h" +-#include "timeout.h" +-#include "socket.h" +- +-typedef struct t_unix_ { +- t_socket sock; +- t_io io; +- t_buffer buf; +- t_timeout tm; +-} t_unix; +-typedef t_unix *p_unix; +- +-LUASOCKET_API int luaopen_socket_unix(lua_State *L); +- +-#endif /* UNIX_H */ +diff --git a/unixdgram.h b/unixdgram.h +deleted file mode 100644 +index a1a0166..0000000 +--- a/unixdgram.h ++++ /dev/null +@@ -1,28 +0,0 @@ +-#ifndef UNIXDGRAM_H +-#define UNIXDGRAM_H +-/*=========================================================================*\ +-* DGRAM object +-* LuaSocket toolkit +-* +-* The dgram.h module provides LuaSocket with support for DGRAM protocol +-* (AF_INET, SOCK_DGRAM). +-* +-* Two classes are defined: connected and unconnected. DGRAM objects are +-* originally unconnected. They can be "connected" to a given address +-* with a call to the setpeername function. The same function can be used to +-* break the connection. +-\*=========================================================================*/ +- +-#include "unix.h" +- +-#ifndef _WIN32 +-#pragma GCC visibility push(hidden) +-#endif +- +-int unixdgram_open(lua_State *L); +- +-#ifndef _WIN32 +-#pragma GCC visibility pop +-#endif +- +-#endif /* UNIXDGRAM_H */ +diff --git a/unixstream.c b/unixstream.c +deleted file mode 100644 +index 02aced9..0000000 +--- a/unixstream.c ++++ /dev/null +@@ -1,355 +0,0 @@ +-/*=========================================================================*\ +-* Unix domain socket stream sub module +-* LuaSocket toolkit +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "auxiliar.h" +-#include "socket.h" +-#include "options.h" +-#include "unixstream.h" +- +-#include +-#include +- +-/*=========================================================================*\ +-* Internal function prototypes +-\*=========================================================================*/ +-static int global_create(lua_State *L); +-static int meth_connect(lua_State *L); +-static int meth_listen(lua_State *L); +-static int meth_bind(lua_State *L); +-static int meth_send(lua_State *L); +-static int meth_shutdown(lua_State *L); +-static int meth_receive(lua_State *L); +-static int meth_accept(lua_State *L); +-static int meth_close(lua_State *L); +-static int meth_setoption(lua_State *L); +-static int meth_settimeout(lua_State *L); +-static int meth_getfd(lua_State *L); +-static int meth_setfd(lua_State *L); +-static int meth_dirty(lua_State *L); +-static int meth_getstats(lua_State *L); +-static int meth_setstats(lua_State *L); +-static int meth_getsockname(lua_State *L); +- +-static const char *unixstream_tryconnect(p_unix un, const char *path); +-static const char *unixstream_trybind(p_unix un, const char *path); +- +-/* unixstream object methods */ +-static luaL_Reg unixstream_methods[] = { +- {"__gc", meth_close}, +- {"__tostring", auxiliar_tostring}, +- {"accept", meth_accept}, +- {"bind", meth_bind}, +- {"close", meth_close}, +- {"connect", meth_connect}, +- {"dirty", meth_dirty}, +- {"getfd", meth_getfd}, +- {"getstats", meth_getstats}, +- {"setstats", meth_setstats}, +- {"listen", meth_listen}, +- {"receive", meth_receive}, +- {"send", meth_send}, +- {"setfd", meth_setfd}, +- {"setoption", meth_setoption}, +- {"setpeername", meth_connect}, +- {"setsockname", meth_bind}, +- {"getsockname", meth_getsockname}, +- {"settimeout", meth_settimeout}, +- {"shutdown", meth_shutdown}, +- {NULL, NULL} +-}; +- +-/* socket option handlers */ +-static t_opt optset[] = { +- {"keepalive", opt_set_keepalive}, +- {"reuseaddr", opt_set_reuseaddr}, +- {"linger", opt_set_linger}, +- {NULL, NULL} +-}; +- +-/* functions in library namespace */ +-static luaL_Reg func[] = { +- {"stream", global_create}, +- {NULL, NULL} +-}; +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-int unixstream_open(lua_State *L) +-{ +- /* create classes */ +- auxiliar_newclass(L, "unixstream{master}", unixstream_methods); +- auxiliar_newclass(L, "unixstream{client}", unixstream_methods); +- auxiliar_newclass(L, "unixstream{server}", unixstream_methods); +- +- /* create class groups */ +- auxiliar_add2group(L, "unixstream{master}", "unixstream{any}"); +- auxiliar_add2group(L, "unixstream{client}", "unixstream{any}"); +- auxiliar_add2group(L, "unixstream{server}", "unixstream{any}"); +- +- luaL_setfuncs(L, func, 0); +- return 0; +-} +- +-/*=========================================================================*\ +-* Lua methods +-\*=========================================================================*/ +-/*-------------------------------------------------------------------------*\ +-* Just call buffered IO methods +-\*-------------------------------------------------------------------------*/ +-static int meth_send(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- return buffer_meth_send(L, &un->buf); +-} +- +-static int meth_receive(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- return buffer_meth_receive(L, &un->buf); +-} +- +-static int meth_getstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- return buffer_meth_getstats(L, &un->buf); +-} +- +-static int meth_setstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- return buffer_meth_setstats(L, &un->buf); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Just call option handler +-\*-------------------------------------------------------------------------*/ +-static int meth_setoption(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- return opt_meth_setoption(L, optset, &un->sock); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Select support methods +-\*-------------------------------------------------------------------------*/ +-static int meth_getfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- lua_pushnumber(L, (int) un->sock); +- return 1; +-} +- +-/* this is very dangerous, but can be handy for those that are brave enough */ +-static int meth_setfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- un->sock = (t_socket) luaL_checknumber(L, 2); +- return 0; +-} +- +-static int meth_dirty(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- lua_pushboolean(L, !buffer_isempty(&un->buf)); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Waits for and returns a client object attempting connection to the +-* server object +-\*-------------------------------------------------------------------------*/ +-static int meth_accept(lua_State *L) { +- p_unix server = (p_unix) auxiliar_checkclass(L, "unixstream{server}", 1); +- p_timeout tm = timeout_markstart(&server->tm); +- t_socket sock; +- int err = socket_accept(&server->sock, &sock, NULL, NULL, tm); +- /* if successful, push client socket */ +- if (err == IO_DONE) { +- p_unix clnt = (p_unix) lua_newuserdata(L, sizeof(t_unix)); +- auxiliar_setclass(L, "unixstream{client}", -1); +- /* initialize structure fields */ +- socket_setnonblocking(&sock); +- clnt->sock = sock; +- io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv, +- (p_error) socket_ioerror, &clnt->sock); +- timeout_init(&clnt->tm, -1, -1); +- buffer_init(&clnt->buf, &clnt->io, &clnt->tm); +- return 1; +- } else { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(err)); +- return 2; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Binds an object to an address +-\*-------------------------------------------------------------------------*/ +-static const char *unixstream_trybind(p_unix un, const char *path) { +- struct sockaddr_un local; +- size_t len = strlen(path); +- int err; +- if (len >= sizeof(local.sun_path)) return "path too long"; +- memset(&local, 0, sizeof(local)); +- strcpy(local.sun_path, path); +- local.sun_family = AF_UNIX; +-#ifdef UNIX_HAS_SUN_LEN +- local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) +- + len + 1; +- err = socket_bind(&un->sock, (SA *) &local, local.sun_len); +- +-#else +- err = socket_bind(&un->sock, (SA *) &local, +- sizeof(local.sun_family) + len); +-#endif +- if (err != IO_DONE) socket_destroy(&un->sock); +- return socket_strerror(err); +-} +- +-static int meth_bind(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); +- const char *path = luaL_checkstring(L, 2); +- const char *err = unixstream_trybind(un, path); +- if (err) { +- lua_pushnil(L); +- lua_pushstring(L, err); +- return 2; +- } +- lua_pushnumber(L, 1); +- return 1; +-} +- +-static int meth_getsockname(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- struct sockaddr_un peer = {0}; +- socklen_t peer_len = sizeof(peer); +- +- if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(errno)); +- return 2; +- } +- +- lua_pushstring(L, peer.sun_path); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Turns a master unixstream object into a client object. +-\*-------------------------------------------------------------------------*/ +-static const char *unixstream_tryconnect(p_unix un, const char *path) +-{ +- struct sockaddr_un remote; +- int err; +- size_t len = strlen(path); +- if (len >= sizeof(remote.sun_path)) return "path too long"; +- memset(&remote, 0, sizeof(remote)); +- strcpy(remote.sun_path, path); +- remote.sun_family = AF_UNIX; +- timeout_markstart(&un->tm); +-#ifdef UNIX_HAS_SUN_LEN +- remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) +- + len + 1; +- err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm); +-#else +- err = socket_connect(&un->sock, (SA *) &remote, +- sizeof(remote.sun_family) + len, &un->tm); +-#endif +- if (err != IO_DONE) socket_destroy(&un->sock); +- return socket_strerror(err); +-} +- +-static int meth_connect(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); +- const char *path = luaL_checkstring(L, 2); +- const char *err = unixstream_tryconnect(un, path); +- if (err) { +- lua_pushnil(L); +- lua_pushstring(L, err); +- return 2; +- } +- /* turn master object into a client object */ +- auxiliar_setclass(L, "unixstream{client}", 1); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Closes socket used by object +-\*-------------------------------------------------------------------------*/ +-static int meth_close(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- socket_destroy(&un->sock); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Puts the sockt in listen mode +-\*-------------------------------------------------------------------------*/ +-static int meth_listen(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); +- int backlog = (int) luaL_optnumber(L, 2, 32); +- int err = socket_listen(&un->sock, backlog); +- if (err != IO_DONE) { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(err)); +- return 2; +- } +- /* turn master object into a server object */ +- auxiliar_setclass(L, "unixstream{server}", 1); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Shuts the connection down partially +-\*-------------------------------------------------------------------------*/ +-static int meth_shutdown(lua_State *L) +-{ +- /* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */ +- static const char* methods[] = { "receive", "send", "both", NULL }; +- p_unix stream = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- int how = luaL_checkoption(L, 2, "both", methods); +- socket_shutdown(&stream->sock, how); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Just call tm methods +-\*-------------------------------------------------------------------------*/ +-static int meth_settimeout(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- return timeout_meth_settimeout(L, &un->tm); +-} +- +-/*=========================================================================*\ +-* Library functions +-\*=========================================================================*/ +-/*-------------------------------------------------------------------------*\ +-* Creates a master unixstream object +-\*-------------------------------------------------------------------------*/ +-static int global_create(lua_State *L) { +- t_socket sock; +- int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0); +- /* try to allocate a system socket */ +- if (err == IO_DONE) { +- /* allocate unixstream object */ +- p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); +- /* set its type as master object */ +- auxiliar_setclass(L, "unixstream{master}", -1); +- /* initialize remaining structure fields */ +- socket_setnonblocking(&sock); +- un->sock = sock; +- io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, +- (p_error) socket_ioerror, &un->sock); +- timeout_init(&un->tm, -1, -1); +- buffer_init(&un->buf, &un->io, &un->tm); +- return 1; +- } else { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(err)); +- return 2; +- } +-} +diff --git a/unixstream.h b/unixstream.h +deleted file mode 100644 +index 7916aff..0000000 +--- a/unixstream.h ++++ /dev/null +@@ -1,29 +0,0 @@ +-#ifndef UNIXSTREAM_H +-#define UNIXSTREAM_H +-/*=========================================================================*\ +-* UNIX STREAM object +-* LuaSocket toolkit +-* +-* The unixstream.h module is basicly a glue that puts together modules buffer.h, +-* timeout.h socket.h and inet.h to provide the LuaSocket UNIX STREAM (AF_UNIX, +-* SOCK_STREAM) support. +-* +-* Three classes are defined: master, client and server. The master class is +-* a newly created unixstream object, that has not been bound or connected. Server +-* objects are unixstream objects bound to some local address. Client objects are +-* unixstream objects either connected to some address or returned by the accept +-* method of a server object. +-\*=========================================================================*/ +-#include "unix.h" +- +-#ifndef _WIN32 +-#pragma GCC visibility push(hidden) +-#endif +- +-int unixstream_open(lua_State *L); +- +-#ifndef _WIN32 +-#pragma GCC visibility pop +-#endif +- +-#endif /* UNIXSTREAM_H */ +diff --git a/usocket.c b/usocket.c +index 69635da..ecf6d2a 100644 +--- a/usocket.c ++++ b/usocket.c +@@ -18,7 +18,7 @@ + * Wait for readable/writable/connected socket with timeout + \*-------------------------------------------------------------------------*/ + #ifndef SOCKET_SELECT +-#include ++#include + + #define WAITFD_R POLLIN + #define WAITFD_W POLLOUT +@@ -236,7 +236,7 @@ int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { +- long put = (long) sendto(*ps, data, count, 0, addr, len); ++ long put = (long) sendto(*ps, data, count, 0, addr, len); + if (put >= 0) { + *sent = put; + return IO_DONE; +@@ -403,7 +403,15 @@ const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case HOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; ++ #if !defined(__WIIU__) + default: return hstrerror(err); ++ #else ++ case TRY_AGAIN: return "Host name lookup failure"; ++ case NO_RECOVERY: return "Unknown server error"; ++ case NO_ADDRESS: ++ return "No address associated with name"; ++ ++ #endif + } + } + +diff --git a/usocket.h b/usocket.h +index 45f2f99..12c25ca 100644 +--- a/usocket.h ++++ b/usocket.h +@@ -29,7 +29,6 @@ + #include + /* TCP options (nagle algorithm disable) */ + #include +-#include + + #ifndef SO_REUSEPORT + #define SO_REUSEPORT SO_REUSEADDR +diff --git a/wsocket.c b/wsocket.c +deleted file mode 100755 +index 6cb1e41..0000000 +--- a/wsocket.c ++++ /dev/null +@@ -1,434 +0,0 @@ +-/*=========================================================================*\ +-* Socket compatibilization module for Win32 +-* LuaSocket toolkit +-* +-* The penalty of calling select to avoid busy-wait is only paid when +-* the I/O call fail in the first place. +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include +- +-#include "socket.h" +-#include "pierror.h" +- +-/* WinSock doesn't have a strerror... */ +-static const char *wstrerror(int err); +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-int socket_open(void) { +- WSADATA wsaData; +- WORD wVersionRequested = MAKEWORD(2, 0); +- int err = WSAStartup(wVersionRequested, &wsaData ); +- if (err != 0) return 0; +- if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) && +- (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) { +- WSACleanup(); +- return 0; +- } +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Close module +-\*-------------------------------------------------------------------------*/ +-int socket_close(void) { +- WSACleanup(); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Wait for readable/writable/connected socket with timeout +-\*-------------------------------------------------------------------------*/ +-#define WAITFD_R 1 +-#define WAITFD_W 2 +-#define WAITFD_E 4 +-#define WAITFD_C (WAITFD_E|WAITFD_W) +- +-int socket_waitfd(p_socket ps, int sw, p_timeout tm) { +- int ret; +- fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL; +- struct timeval tv, *tp = NULL; +- double t; +- if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ +- if (sw & WAITFD_R) { +- FD_ZERO(&rfds); +- FD_SET(*ps, &rfds); +- rp = &rfds; +- } +- if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } +- if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(*ps, &efds); ep = &efds; } +- if ((t = timeout_get(tm)) >= 0.0) { +- tv.tv_sec = (int) t; +- tv.tv_usec = (int) ((t-tv.tv_sec)*1.0e6); +- tp = &tv; +- } +- ret = select(0, rp, wp, ep, tp); +- if (ret == -1) return WSAGetLastError(); +- if (ret == 0) return IO_TIMEOUT; +- if (sw == WAITFD_C && FD_ISSET(*ps, &efds)) return IO_CLOSED; +- return IO_DONE; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Select with int timeout in ms +-\*-------------------------------------------------------------------------*/ +-int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, +- p_timeout tm) { +- struct timeval tv; +- double t = timeout_get(tm); +- tv.tv_sec = (int) t; +- tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); +- if (n <= 0) { +- Sleep((DWORD) (1000*t)); +- return 0; +- } else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Close and inutilize socket +-\*-------------------------------------------------------------------------*/ +-void socket_destroy(p_socket ps) { +- if (*ps != SOCKET_INVALID) { +- socket_setblocking(ps); /* close can take a long time on WIN32 */ +- closesocket(*ps); +- *ps = SOCKET_INVALID; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* +-\*-------------------------------------------------------------------------*/ +-void socket_shutdown(p_socket ps, int how) { +- socket_setblocking(ps); +- shutdown(*ps, how); +- socket_setnonblocking(ps); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Creates and sets up a socket +-\*-------------------------------------------------------------------------*/ +-int socket_create(p_socket ps, int domain, int type, int protocol) { +- *ps = socket(domain, type, protocol); +- if (*ps != SOCKET_INVALID) return IO_DONE; +- else return WSAGetLastError(); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Connects or returns error message +-\*-------------------------------------------------------------------------*/ +-int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { +- int err; +- /* don't call on closed socket */ +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- /* ask system to connect */ +- if (connect(*ps, addr, len) == 0) return IO_DONE; +- /* make sure the system is trying to connect */ +- err = WSAGetLastError(); +- if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) return err; +- /* zero timeout case optimization */ +- if (timeout_iszero(tm)) return IO_TIMEOUT; +- /* we wait until something happens */ +- err = socket_waitfd(ps, WAITFD_C, tm); +- if (err == IO_CLOSED) { +- int elen = sizeof(err); +- /* give windows time to set the error (yes, disgusting) */ +- Sleep(10); +- /* find out why we failed */ +- getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &elen); +- /* we KNOW there was an error. if 'why' is 0, we will return +- * "unknown error", but it's not really our fault */ +- return err > 0? err: IO_UNKNOWN; +- } else return err; +- +-} +- +-/*-------------------------------------------------------------------------*\ +-* Binds or returns error message +-\*-------------------------------------------------------------------------*/ +-int socket_bind(p_socket ps, SA *addr, socklen_t len) { +- int err = IO_DONE; +- socket_setblocking(ps); +- if (bind(*ps, addr, len) < 0) err = WSAGetLastError(); +- socket_setnonblocking(ps); +- return err; +-} +- +-/*-------------------------------------------------------------------------*\ +-* +-\*-------------------------------------------------------------------------*/ +-int socket_listen(p_socket ps, int backlog) { +- int err = IO_DONE; +- socket_setblocking(ps); +- if (listen(*ps, backlog) < 0) err = WSAGetLastError(); +- socket_setnonblocking(ps); +- return err; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Accept with timeout +-\*-------------------------------------------------------------------------*/ +-int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, +- p_timeout tm) { +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- for ( ;; ) { +- int err; +- /* try to get client socket */ +- if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; +- /* find out why we failed */ +- err = WSAGetLastError(); +- /* if we failed because there was no connectoin, keep trying */ +- if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err; +- /* call select to avoid busy wait */ +- if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Send with timeout +-* On windows, if you try to send 10MB, the OS will buffer EVERYTHING +-* this can take an awful lot of time and we will end up blocked. +-* Therefore, whoever calls this function should not pass a huge buffer. +-\*-------------------------------------------------------------------------*/ +-int socket_send(p_socket ps, const char *data, size_t count, +- size_t *sent, p_timeout tm) +-{ +- int err; +- *sent = 0; +- /* avoid making system calls on closed sockets */ +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- /* loop until we send something or we give up on error */ +- for ( ;; ) { +- /* try to send something */ +- int put = send(*ps, data, (int) count, 0); +- /* if we sent something, we are done */ +- if (put > 0) { +- *sent = put; +- return IO_DONE; +- } +- /* deal with failure */ +- err = WSAGetLastError(); +- /* we can only proceed if there was no serious error */ +- if (err != WSAEWOULDBLOCK) return err; +- /* avoid busy wait */ +- if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Sendto with timeout +-\*-------------------------------------------------------------------------*/ +-int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, +- SA *addr, socklen_t len, p_timeout tm) +-{ +- int err; +- *sent = 0; +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- for ( ;; ) { +- int put = sendto(*ps, data, (int) count, 0, addr, len); +- if (put > 0) { +- *sent = put; +- return IO_DONE; +- } +- err = WSAGetLastError(); +- if (err != WSAEWOULDBLOCK) return err; +- if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Receive with timeout +-\*-------------------------------------------------------------------------*/ +-int socket_recv(p_socket ps, char *data, size_t count, size_t *got, +- p_timeout tm) +-{ +- int err, prev = IO_DONE; +- *got = 0; +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- for ( ;; ) { +- int taken = recv(*ps, data, (int) count, 0); +- if (taken > 0) { +- *got = taken; +- return IO_DONE; +- } +- if (taken == 0) return IO_CLOSED; +- err = WSAGetLastError(); +- /* On UDP, a connreset simply means the previous send failed. +- * So we try again. +- * On TCP, it means our socket is now useless, so the error passes. +- * (We will loop again, exiting because the same error will happen) */ +- if (err != WSAEWOULDBLOCK) { +- if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; +- prev = err; +- } +- if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Recvfrom with timeout +-\*-------------------------------------------------------------------------*/ +-int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, +- SA *addr, socklen_t *len, p_timeout tm) +-{ +- int err, prev = IO_DONE; +- *got = 0; +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- for ( ;; ) { +- int taken = recvfrom(*ps, data, (int) count, 0, addr, len); +- if (taken > 0) { +- *got = taken; +- return IO_DONE; +- } +- if (taken == 0) return IO_CLOSED; +- err = WSAGetLastError(); +- /* On UDP, a connreset simply means the previous send failed. +- * So we try again. +- * On TCP, it means our socket is now useless, so the error passes. +- * (We will loop again, exiting because the same error will happen) */ +- if (err != WSAEWOULDBLOCK) { +- if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; +- prev = err; +- } +- if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Put socket into blocking mode +-\*-------------------------------------------------------------------------*/ +-void socket_setblocking(p_socket ps) { +- u_long argp = 0; +- ioctlsocket(*ps, FIONBIO, &argp); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Put socket into non-blocking mode +-\*-------------------------------------------------------------------------*/ +-void socket_setnonblocking(p_socket ps) { +- u_long argp = 1; +- ioctlsocket(*ps, FIONBIO, &argp); +-} +- +-/*-------------------------------------------------------------------------*\ +-* DNS helpers +-\*-------------------------------------------------------------------------*/ +-int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { +- *hp = gethostbyaddr(addr, len, AF_INET); +- if (*hp) return IO_DONE; +- else return WSAGetLastError(); +-} +- +-int socket_gethostbyname(const char *addr, struct hostent **hp) { +- *hp = gethostbyname(addr); +- if (*hp) return IO_DONE; +- else return WSAGetLastError(); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Error translation functions +-\*-------------------------------------------------------------------------*/ +-const char *socket_hoststrerror(int err) { +- if (err <= 0) return io_strerror(err); +- switch (err) { +- case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; +- default: return wstrerror(err); +- } +-} +- +-const char *socket_strerror(int err) { +- if (err <= 0) return io_strerror(err); +- switch (err) { +- case WSAEADDRINUSE: return PIE_ADDRINUSE; +- case WSAECONNREFUSED : return PIE_CONNREFUSED; +- case WSAEISCONN: return PIE_ISCONN; +- case WSAEACCES: return PIE_ACCESS; +- case WSAECONNABORTED: return PIE_CONNABORTED; +- case WSAECONNRESET: return PIE_CONNRESET; +- case WSAETIMEDOUT: return PIE_TIMEDOUT; +- default: return wstrerror(err); +- } +-} +- +-const char *socket_ioerror(p_socket ps, int err) { +- (void) ps; +- return socket_strerror(err); +-} +- +-static const char *wstrerror(int err) { +- switch (err) { +- case WSAEINTR: return "Interrupted function call"; +- case WSAEACCES: return PIE_ACCESS; /* "Permission denied"; */ +- case WSAEFAULT: return "Bad address"; +- case WSAEINVAL: return "Invalid argument"; +- case WSAEMFILE: return "Too many open files"; +- case WSAEWOULDBLOCK: return "Resource temporarily unavailable"; +- case WSAEINPROGRESS: return "Operation now in progress"; +- case WSAEALREADY: return "Operation already in progress"; +- case WSAENOTSOCK: return "Socket operation on nonsocket"; +- case WSAEDESTADDRREQ: return "Destination address required"; +- case WSAEMSGSIZE: return "Message too long"; +- case WSAEPROTOTYPE: return "Protocol wrong type for socket"; +- case WSAENOPROTOOPT: return "Bad protocol option"; +- case WSAEPROTONOSUPPORT: return "Protocol not supported"; +- case WSAESOCKTNOSUPPORT: return PIE_SOCKTYPE; /* "Socket type not supported"; */ +- case WSAEOPNOTSUPP: return "Operation not supported"; +- case WSAEPFNOSUPPORT: return "Protocol family not supported"; +- case WSAEAFNOSUPPORT: return PIE_FAMILY; /* "Address family not supported by protocol family"; */ +- case WSAEADDRINUSE: return PIE_ADDRINUSE; /* "Address already in use"; */ +- case WSAEADDRNOTAVAIL: return "Cannot assign requested address"; +- case WSAENETDOWN: return "Network is down"; +- case WSAENETUNREACH: return "Network is unreachable"; +- case WSAENETRESET: return "Network dropped connection on reset"; +- case WSAECONNABORTED: return "Software caused connection abort"; +- case WSAECONNRESET: return PIE_CONNRESET; /* "Connection reset by peer"; */ +- case WSAENOBUFS: return "No buffer space available"; +- case WSAEISCONN: return PIE_ISCONN; /* "Socket is already connected"; */ +- case WSAENOTCONN: return "Socket is not connected"; +- case WSAESHUTDOWN: return "Cannot send after socket shutdown"; +- case WSAETIMEDOUT: return PIE_TIMEDOUT; /* "Connection timed out"; */ +- case WSAECONNREFUSED: return PIE_CONNREFUSED; /* "Connection refused"; */ +- case WSAEHOSTDOWN: return "Host is down"; +- case WSAEHOSTUNREACH: return "No route to host"; +- case WSAEPROCLIM: return "Too many processes"; +- case WSASYSNOTREADY: return "Network subsystem is unavailable"; +- case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range"; +- case WSANOTINITIALISED: +- return "Successful WSAStartup not yet performed"; +- case WSAEDISCON: return "Graceful shutdown in progress"; +- case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; /* "Host not found"; */ +- case WSATRY_AGAIN: return "Nonauthoritative host not found"; +- case WSANO_RECOVERY: return PIE_FAIL; /* "Nonrecoverable name lookup error"; */ +- case WSANO_DATA: return "Valid name, no data record of requested type"; +- default: return "Unknown error"; +- } +-} +- +-const char *socket_gaistrerror(int err) { +- if (err == 0) return NULL; +- switch (err) { +- case EAI_AGAIN: return PIE_AGAIN; +- case EAI_BADFLAGS: return PIE_BADFLAGS; +-#ifdef EAI_BADHINTS +- case EAI_BADHINTS: return PIE_BADHINTS; +-#endif +- case EAI_FAIL: return PIE_FAIL; +- case EAI_FAMILY: return PIE_FAMILY; +- case EAI_MEMORY: return PIE_MEMORY; +- case EAI_NONAME: return PIE_NONAME; +-#ifdef EAI_OVERFLOW +- case EAI_OVERFLOW: return PIE_OVERFLOW; +-#endif +-#ifdef EAI_PROTOCOL +- case EAI_PROTOCOL: return PIE_PROTOCOL; +-#endif +- case EAI_SERVICE: return PIE_SERVICE; +- case EAI_SOCKTYPE: return PIE_SOCKTYPE; +-#ifdef EAI_SYSTEM +- case EAI_SYSTEM: return strerror(errno); +-#endif +- default: return LUA_GAI_STRERROR(err); +- } +-} +diff --git a/wsocket.h b/wsocket.h +deleted file mode 100644 +index 3986640..0000000 +--- a/wsocket.h ++++ /dev/null +@@ -1,33 +0,0 @@ +-#ifndef WSOCKET_H +-#define WSOCKET_H +-/*=========================================================================*\ +-* Socket compatibilization module for Win32 +-* LuaSocket toolkit +-\*=========================================================================*/ +- +-/*=========================================================================*\ +-* WinSock include files +-\*=========================================================================*/ +-#include +-#include +- +-typedef int socklen_t; +-typedef SOCKADDR_STORAGE t_sockaddr_storage; +-typedef SOCKET t_socket; +-typedef t_socket *p_socket; +- +-#ifndef IPV6_V6ONLY +-#define IPV6_V6ONLY 27 +-#endif +- +-#define SOCKET_INVALID (INVALID_SOCKET) +- +-#ifndef SO_REUSEPORT +-#define SO_REUSEPORT SO_REUSEADDR +-#endif +- +-#ifndef AI_NUMERICSERV +-#define AI_NUMERICSERV (0) +-#endif +- +-#endif /* WSOCKET_H */ diff --git a/platform/cafe/pkglist.txt b/platform/cafe/pkglist.txt new file mode 100644 index 000000000..df8ed940b --- /dev/null +++ b/platform/cafe/pkglist.txt @@ -0,0 +1,18 @@ +ppc-box2d +ppc-bzip2 +ppc-freetype +ppc-glm +ppc-libjpeg-turbo +ppc-libmodplug +ppc-libogg +ppc-libpng +ppc-libvorbisidec +ppc-liblua51 +ppc-lz4 +ppc-zlib +wiiu-curl +wiiu-mbedtls +wiiu-physfs +wiiu-sdl2 +wiiu-sdl2_mixer +wiiu-cmake diff --git a/platform/cafe/source/common/matrix_ext.cpp b/platform/cafe/source/common/matrix_ext.cpp new file mode 100644 index 000000000..e786d4379 --- /dev/null +++ b/platform/cafe/source/common/matrix_ext.cpp @@ -0,0 +1,343 @@ +#include + +#include + +using namespace love; + +void Matrix4::Multiply(const Matrix4& a, const Matrix4& b, float elements[16]) +{ + elements[0] = (a.elements[0] * b.elements[0]) + (a.elements[4] * b.elements[1]) + + (a.elements[8] * b.elements[2]) + (a.elements[12] * b.elements[3]); + elements[4] = (a.elements[0] * b.elements[4]) + (a.elements[4] * b.elements[5]) + + (a.elements[8] * b.elements[6]) + (a.elements[12] * b.elements[7]); + elements[8] = (a.elements[0] * b.elements[8]) + (a.elements[4] * b.elements[9]) + + (a.elements[8] * b.elements[10]) + (a.elements[12] * b.elements[11]); + elements[12] = (a.elements[0] * b.elements[12]) + (a.elements[4] * b.elements[13]) + + (a.elements[8] * b.elements[14]) + (a.elements[12] * b.elements[15]); + + elements[1] = (a.elements[1] * b.elements[0]) + (a.elements[5] * b.elements[1]) + + (a.elements[9] * b.elements[2]) + (a.elements[13] * b.elements[3]); + elements[5] = (a.elements[1] * b.elements[4]) + (a.elements[5] * b.elements[5]) + + (a.elements[9] * b.elements[6]) + (a.elements[13] * b.elements[7]); + elements[9] = (a.elements[1] * b.elements[8]) + (a.elements[5] * b.elements[9]) + + (a.elements[9] * b.elements[10]) + (a.elements[13] * b.elements[11]); + elements[13] = (a.elements[1] * b.elements[12]) + (a.elements[5] * b.elements[13]) + + (a.elements[9] * b.elements[14]) + (a.elements[13] * b.elements[15]); + + elements[2] = (a.elements[2] * b.elements[0]) + (a.elements[6] * b.elements[1]) + + (a.elements[10] * b.elements[2]) + (a.elements[14] * b.elements[3]); + elements[6] = (a.elements[2] * b.elements[4]) + (a.elements[6] * b.elements[5]) + + (a.elements[10] * b.elements[6]) + (a.elements[14] * b.elements[7]); + elements[10] = (a.elements[2] * b.elements[8]) + (a.elements[6] * b.elements[9]) + + (a.elements[10] * b.elements[10]) + (a.elements[14] * b.elements[11]); + elements[14] = (a.elements[2] * b.elements[12]) + (a.elements[6] * b.elements[13]) + + (a.elements[10] * b.elements[14]) + (a.elements[14] * b.elements[15]); + + elements[3] = (a.elements[3] * b.elements[0]) + (a.elements[7] * b.elements[1]) + + (a.elements[11] * b.elements[2]) + (a.elements[15] * b.elements[3]); + elements[7] = (a.elements[3] * b.elements[4]) + (a.elements[7] * b.elements[5]) + + (a.elements[11] * b.elements[6]) + (a.elements[15] * b.elements[7]); + elements[11] = (a.elements[3] * b.elements[8]) + (a.elements[7] * b.elements[9]) + + (a.elements[11] * b.elements[10]) + (a.elements[15] * b.elements[11]); + elements[15] = (a.elements[3] * b.elements[12]) + (a.elements[7] * b.elements[13]) + + (a.elements[11] * b.elements[14]) + (a.elements[15] * b.elements[15]); +} + +void Matrix4::Multiply(const Matrix4& a, const Matrix4& b, Matrix4& t) +{ + Matrix4::Multiply(a, b, t.elements); +} + +Matrix4::Matrix4() +{ + this->SetIdentity(); +} + +Matrix4::Matrix4(const float elements[16]) +{ + memcpy(this->elements, elements, sizeof(float) * 16); +} + +Matrix4::Matrix4(const Matrix4& a, const Matrix4& b) +{ + Matrix4::Multiply(a, b, this->elements); +} + +Matrix4::Matrix4(float t00, float t10, float t01, float t11, float x, float y) +{ + this->SetRawTransformation(t00, t10, t01, t11, x, y); +} + +Matrix4::Matrix4(float x, float y, float angle, float sx, float sy, float ox, + float oy, float kx, float ky) +{ + this->SetTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); +} + +bool Matrix4::IsAffine2DTransform() const +{ + return fabsf(this->elements[2] + this->elements[3] + this->elements[6] + this->elements[7] + + this->elements[8] + this->elements[9] + this->elements[11] + this->elements[14]) < + 0.00001f && + fabsf(this->elements[10] + this->elements[15] - 2.0f) < 0.00001f; +} + +Matrix4 Matrix4::operator*(const Matrix4& m) const +{ + return Matrix4(*this, m); +} + +void Matrix4::operator*=(const Matrix4& m) +{ + float elements[16]; + + Matrix4::Multiply(*this, m, elements); + memcpy(this->elements, elements, sizeof(float) * 16); +} + +void Matrix4::SetIdentity() +{ + memset(this->elements, 0, sizeof(float) * 16); + this->elements[15] = this->elements[10] = this->elements[5] = this->elements[0] = 1.0f; +} + +void Matrix4::SetTranslation(float x, float y) +{ + this->SetIdentity(); + + this->elements[12] = x; + this->elements[13] = y; +} + +void Matrix4::Translate(float x, float y) +{ + Matrix4 t; + t.SetTranslation(x, y); + this->operator*=(t); +} + +void Matrix4::SetRotation(float rad) +{ + this->SetIdentity(); + float c = cosf(rad), s = sinf(rad); + + this->elements[0] = c; + this->elements[4] = -s; + this->elements[1] = s; + this->elements[5] = c; +} + +void Matrix4::Rotate(float rad) +{ + Matrix4 t; + t.SetRotation(rad); + this->operator*=(t); +} + +void Matrix4::SetScale(float sx, float sy) +{ + this->SetIdentity(); + + this->elements[0] = sx; + this->elements[5] = sy; +} + +void Matrix4::Scale(float sx, float sy) +{ + Matrix4 t; + t.SetScale(sx, sy); + this->operator*=(t); +} + +void Matrix4::SetShear(float kx, float ky) +{ + this->SetIdentity(); + + this->elements[1] = ky; + this->elements[4] = kx; +} + +void Matrix4::Shear(float kx, float ky) +{ + Matrix4 t; + t.SetShear(kx, ky); + this->operator*=(t); +} + +void Matrix4::GetApproximateScale(float& sx, float& sy) const +{ + sx = sqrtf(this->elements[0] * this->elements[0] + this->elements[4] * this->elements[4]); + sy = sqrtf(this->elements[1] * this->elements[1] + this->elements[5] * this->elements[5]); +} + +void Matrix4::SetRawTransformation(float t00, float t10, float t01, float t11, + float x, float y) +{ + memset(this->elements, 0, sizeof(float) * 16); // zero out matrix + + this->elements[10] = this->elements[15] = 1.0f; + this->elements[0] = t00; + this->elements[1] = t10; + this->elements[4] = t01; + this->elements[5] = t11; + this->elements[12] = x; + this->elements[13] = y; +} + +void Matrix4::SetTransformation(float x, float y, float angle, float sx, float sy, + float ox, float oy, float kx, float ky) +{ + memset(this->elements, 0, sizeof(float) * 16); // zero out matrix + float c = cosf(angle), s = sinf(angle); + + this->elements[10] = this->elements[15] = 1.0f; + this->elements[0] = c * sx - ky * s * sy; // = a + this->elements[1] = s * sx + ky * c * sy; // = b + this->elements[4] = kx * c * sx - s * sy; // = c + this->elements[5] = kx * s * sx + c * sy; // = d + this->elements[12] = x - ox * this->elements[0] - oy * this->elements[4]; + this->elements[13] = y - ox * this->elements[1] - oy * this->elements[5]; +} + +Matrix4 Matrix4::Inverse() const +{ + Matrix4 inv; + + inv.elements[0] = this->elements[5] * this->elements[10] * this->elements[15] - + this->elements[5] * this->elements[11] * this->elements[14] - + this->elements[9] * this->elements[6] * this->elements[15] + + this->elements[9] * this->elements[7] * this->elements[14] + + this->elements[13] * this->elements[6] * this->elements[11] - + this->elements[13] * this->elements[7] * this->elements[10]; + + inv.elements[4] = -this->elements[4] * this->elements[10] * this->elements[15] + + this->elements[4] * this->elements[11] * this->elements[14] + + this->elements[8] * this->elements[6] * this->elements[15] - + this->elements[8] * this->elements[7] * this->elements[14] - + this->elements[12] * this->elements[6] * this->elements[11] + + this->elements[12] * this->elements[7] * this->elements[10]; + + inv.elements[8] = this->elements[4] * this->elements[9] * this->elements[15] - + this->elements[4] * this->elements[11] * this->elements[13] - + this->elements[8] * this->elements[5] * this->elements[15] + + this->elements[8] * this->elements[7] * this->elements[13] + + this->elements[12] * this->elements[5] * this->elements[11] - + this->elements[12] * this->elements[7] * this->elements[9]; + + inv.elements[12] = -this->elements[4] * this->elements[9] * this->elements[14] + + this->elements[4] * this->elements[10] * this->elements[13] + + this->elements[8] * this->elements[5] * this->elements[14] - + this->elements[8] * this->elements[6] * this->elements[13] - + this->elements[12] * this->elements[5] * this->elements[10] + + this->elements[12] * this->elements[6] * this->elements[9]; + + inv.elements[1] = -this->elements[1] * this->elements[10] * this->elements[15] + + this->elements[1] * this->elements[11] * this->elements[14] + + this->elements[9] * this->elements[2] * this->elements[15] - + this->elements[9] * this->elements[3] * this->elements[14] - + this->elements[13] * this->elements[2] * this->elements[11] + + this->elements[13] * this->elements[3] * this->elements[10]; + + inv.elements[5] = this->elements[0] * this->elements[10] * this->elements[15] - + this->elements[0] * this->elements[11] * this->elements[14] - + this->elements[8] * this->elements[2] * this->elements[15] + + this->elements[8] * this->elements[3] * this->elements[14] + + this->elements[12] * this->elements[2] * this->elements[11] - + this->elements[12] * this->elements[3] * this->elements[10]; + + inv.elements[9] = -this->elements[0] * this->elements[9] * this->elements[15] + + this->elements[0] * this->elements[11] * this->elements[13] + + this->elements[8] * this->elements[1] * this->elements[15] - + this->elements[8] * this->elements[3] * this->elements[13] - + this->elements[12] * this->elements[1] * this->elements[11] + + this->elements[12] * this->elements[3] * this->elements[9]; + + inv.elements[13] = this->elements[0] * this->elements[9] * this->elements[14] - + this->elements[0] * this->elements[10] * this->elements[13] - + this->elements[8] * this->elements[1] * this->elements[14] + + this->elements[8] * this->elements[2] * this->elements[13] + + this->elements[12] * this->elements[1] * this->elements[10] - + this->elements[12] * this->elements[2] * this->elements[9]; + + inv.elements[2] = this->elements[1] * this->elements[6] * this->elements[15] - + this->elements[1] * this->elements[7] * this->elements[14] - + this->elements[5] * this->elements[2] * this->elements[15] + + this->elements[5] * this->elements[3] * this->elements[14] + + this->elements[13] * this->elements[2] * this->elements[7] - + this->elements[13] * this->elements[3] * this->elements[6]; + + inv.elements[6] = -this->elements[0] * this->elements[6] * this->elements[15] + + this->elements[0] * this->elements[7] * this->elements[14] + + this->elements[4] * this->elements[2] * this->elements[15] - + this->elements[4] * this->elements[3] * this->elements[14] - + this->elements[12] * this->elements[2] * this->elements[7] + + this->elements[12] * this->elements[3] * this->elements[6]; + + inv.elements[10] = this->elements[0] * this->elements[5] * this->elements[15] - + this->elements[0] * this->elements[7] * this->elements[13] - + this->elements[4] * this->elements[1] * this->elements[15] + + this->elements[4] * this->elements[3] * this->elements[13] + + this->elements[12] * this->elements[1] * this->elements[7] - + this->elements[12] * this->elements[3] * this->elements[5]; + + inv.elements[14] = -this->elements[0] * this->elements[5] * this->elements[14] + + this->elements[0] * this->elements[6] * this->elements[13] + + this->elements[4] * this->elements[1] * this->elements[14] - + this->elements[4] * this->elements[2] * this->elements[13] - + this->elements[12] * this->elements[1] * this->elements[6] + + this->elements[12] * this->elements[2] * this->elements[5]; + + inv.elements[3] = -this->elements[1] * this->elements[6] * this->elements[11] + + this->elements[1] * this->elements[7] * this->elements[10] + + this->elements[5] * this->elements[2] * this->elements[11] - + this->elements[5] * this->elements[3] * this->elements[10] - + this->elements[9] * this->elements[2] * this->elements[7] + + this->elements[9] * this->elements[3] * this->elements[6]; + + inv.elements[7] = this->elements[0] * this->elements[6] * this->elements[11] - + this->elements[0] * this->elements[7] * this->elements[10] - + this->elements[4] * this->elements[2] * this->elements[11] + + this->elements[4] * this->elements[3] * this->elements[10] + + this->elements[8] * this->elements[2] * this->elements[7] - + this->elements[8] * this->elements[3] * this->elements[6]; + + inv.elements[11] = -this->elements[0] * this->elements[5] * this->elements[11] + + this->elements[0] * this->elements[7] * this->elements[9] + + this->elements[4] * this->elements[1] * this->elements[11] - + this->elements[4] * this->elements[3] * this->elements[9] - + this->elements[8] * this->elements[1] * this->elements[7] + + this->elements[8] * this->elements[3] * this->elements[5]; + + inv.elements[15] = this->elements[0] * this->elements[5] * this->elements[10] - + this->elements[0] * this->elements[6] * this->elements[9] - + this->elements[4] * this->elements[1] * this->elements[10] + + this->elements[4] * this->elements[2] * this->elements[9] + + this->elements[8] * this->elements[1] * this->elements[6] - + this->elements[8] * this->elements[2] * this->elements[5]; + + float det = this->elements[0] * inv.elements[0] + this->elements[1] * inv.elements[4] + + this->elements[2] * inv.elements[8] + this->elements[3] * inv.elements[12]; + + float invdet = 1.0f / det; + + for (int i = 0; i < 16; i++) + inv.elements[i] *= invdet; + + return inv; +} + +Matrix4 Matrix4::Ortho(float left, float right, float bottom, + float top, float near, float far) +{ + Matrix4 matrix; + + matrix.elements[0] = 2.0f / (right - left); + matrix.elements[5] = 2.0f / (top - bottom); + matrix.elements[10] = -2.0f / (far - near); + + matrix.elements[12] = -(right + left) / (right - left); + matrix.elements[13] = -(top + bottom) / (top - bottom); + matrix.elements[14] = -(far + near) / (far - near); + + return matrix; +} diff --git a/platform/cafe/source/common/screen_ext.cpp b/platform/cafe/source/common/screen_ext.cpp new file mode 100644 index 000000000..af050b476 --- /dev/null +++ b/platform/cafe/source/common/screen_ext.cpp @@ -0,0 +1,16 @@ +#include + +namespace love +{ + std::span GetScreenInfo() + { + return { screenInfo }; + } + + const ScreenInfo& GetScreenInfo(Screen id) + { + const auto& info = GetScreenInfo(); + + return info[id]; + } +} // namespace love diff --git a/platform/cafe/source/modules/fontmodule_ext.cpp b/platform/cafe/source/modules/fontmodule_ext.cpp new file mode 100644 index 000000000..42c84c936 --- /dev/null +++ b/platform/cafe/source/modules/fontmodule_ext.cpp @@ -0,0 +1,72 @@ +#include + +#include + +#include +#include + +using namespace love; + +FontModule::FontModule() : FontModule() +{ + this->defaultFontData.Set(new SystemFont(), Acquire::NORETAIN); +} + +Rasterizer* FontModule::NewImageRasterizer(ImageData* data, + const std::string& text, int extraSpacing, + float dpiScale) const +{ + std::vector glyphs {}; + glyphs.reserve(text.size()); + + try + { + utf8::iterator it(text.begin(), text.begin(), text.end()); + utf8::iterator end(text.end(), text.begin(), text.end()); + + while (it != end) + glyphs.push_back(*it++); + } + catch (utf8::exception& e) + { + throw love::Exception("UTF-8 decoding error: %s", e.what()); + } + + return this->NewImageRasterizer(data, &glyphs[0], (int)glyphs.size(), extraSpacing, dpiScale); +} + +Rasterizer* FontModule::NewImageRasterizer(ImageData* data, + uint32_t* glyphs, int glyphCount, + int extraSpacing, float dpiScale) const +{ + return new ImageRasterizer(data, glyphs, glyphCount, extraSpacing, dpiScale); +} + +Rasterizer* FontModule::NewRasterizer(FileData* data) const +{ + if (TrueTypeRasterizer::Accepts(this->library, data)) + return this->NewTrueTypeRasterizer(data, 12, TrueTypeRasterizer<>::HINTING_NORMAL); + else if (BMFontRasterizer::Accepts(data)) + return this->NewBMFontRasterizer(data, {}, 1.0f); + + throw love::Exception("Invalid font file: %s", data->GetFilename().c_str()); + return nullptr; +} + +Rasterizer* FontModule::NewTrueTypeRasterizer( + Data* data, int size, TrueTypeRasterizer<>::Hinting hinting) const +{ + float dpiScale = 1.0f; + auto window = Module::GetInstance>(Module::M_WINDOW); + + if (window != nullptr) + dpiScale = window->GetDPIScale(); + + return this->NewTrueTypeRasterizer(data, size, dpiScale, hinting); +} + +Rasterizer* FontModule::NewTrueTypeRasterizer( + Data* data, int size, float dpiScale, TrueTypeRasterizer<>::Hinting hinting) const +{ + return new TrueTypeRasterizer(this->library, data, size, dpiScale, hinting); +} \ No newline at end of file diff --git a/platform/cafe/source/modules/graphics_ext.cpp b/platform/cafe/source/modules/graphics_ext.cpp new file mode 100644 index 000000000..99e0a5a14 --- /dev/null +++ b/platform/cafe/source/modules/graphics_ext.cpp @@ -0,0 +1,108 @@ +#include + +#include + +#include + +#include +#include + +#include + +#include + +using Renderer = love::Renderer; +using namespace love; + +Graphics::Graphics() +{ + auto* window = Module::GetInstance>(Module::M_WINDOW); + + if (window != nullptr) + { + window->SetGraphics(this); + + if (window->IsOpen()) + window->SetWindow(); + } +} + +bool Graphics::SetMode(int x, int y, int width, int height) +{ + try + { + ::Renderer::Instance(); + } + catch (love::Exception&) + { + throw; + } + + this->RestoreState(this->states.back()); + + this->SetViewportSize(width, height); + + for (int index = 0; index < Shader<>::STANDARD_MAX_ENUM; index++) + { + const auto type = (Shader<>::StandardShader)index; + + try + { + if (!Shader::defaults[index]) + { + auto* shader = new Shader(); + shader->LoadDefaults(type); + + Shader::defaults[index] = shader; + } + } + catch (love::Exception&) + { + throw; + } + } + + if (!Shader::current) + Shader::defaults[Shader<>::STANDARD_DEFAULT]->Attach(); + + this->created = true; + return true; +} + +Texture* Graphics::NewTexture(const Texture<>::Settings& settings, + const Texture<>::Slices* slices) const +{ + return new Texture(this, settings, slices); +} + +void Graphics::Draw(Drawable* drawable, const Matrix4& matrix) +{ + drawable->Draw(*this, matrix); +} + +void Graphics::Draw(Texture* texture, Quad* quad, + const Matrix4& matrix) +{ + texture->Draw(*this, quad, matrix); +} + +void Graphics::SetShader() +{ + Shader::AttachDefault(Shader<>::STANDARD_DEFAULT); + this->states.back().shader.Set(nullptr); +} + +void Graphics::SetShader(Shader* shader) +{ + if (shader == nullptr) + return this->SetShader(); + + shader->Attach(); + this->states.back().shader.Set(shader); +} + +void Graphics::SetViewportSize(int width, int height) +{ + ::Renderer::Instance().SetViewport(Rect::EMPTY); + this->SetScissor(Rect::EMPTY); +} diff --git a/platform/cafe/source/modules/imagemodule_ext.cpp b/platform/cafe/source/modules/imagemodule_ext.cpp new file mode 100644 index 000000000..9d904c9f6 --- /dev/null +++ b/platform/cafe/source/modules/imagemodule_ext.cpp @@ -0,0 +1,9 @@ +#include + +using namespace love; + +ImageModule::ImageModule() +{ + this->formatHandlers = { new JPGHandler(), new PNGHandler(), new DDSHandler(), + new KTXHandler() }; +} diff --git a/platform/cafe/source/modules/joystickmodule_ext.cpp b/platform/cafe/source/modules/joystickmodule_ext.cpp new file mode 100644 index 000000000..2eba184e9 --- /dev/null +++ b/platform/cafe/source/modules/joystickmodule_ext.cpp @@ -0,0 +1,104 @@ +#include + +#include +#include + +#include + +using namespace love; + +JoystickModule::JoystickModule() +{ + this->AddJoystick(0); + + for (size_t index = 0; index < wpad::MAX_JOYSTICKS; index++) + this->AddJoystick(index + 1); + + try + { + this->pool = new VibrationPool(); + } + catch (love::Exception&) + { + throw; + } + + this->thread = new PoolThread(this->pool); + this->thread->Start(); +} + +JoystickModule::~JoystickModule() +{ + this->thread->SetFinish(); + this->thread->Wait(); + + delete this->thread; + delete this->pool; +} + +void JoystickModule::AddVibration(::Vibration* vibration) +{ + this->pool->AddVibration(vibration); +} + +static Joystick* openByType(guid::GamepadType type, int index) +{ + switch (type) + { + case guid::GAMEPAD_TYPE_WII_U_GAMEPAD: + return new Gamepad(index); + default: + return nullptr; + } +} + +Joystick* JoystickModule::AddJoystick(int index) +{ + if (index < 0 || index >= (int)wpad::MAX_JOYSTICKS + 1) + return nullptr; + + bool reused = false; + guid::GamepadType type = guid::GAMEPAD_TYPE_WII_U_GAMEPAD; + + if (index != 0) + { + KPADStatus status {}; + KPADError error = KPAD_ERROR_OK; + + KPADReadEx((KPADChan)index, &status, 1, &error); + + if (error == KPAD_ERROR_OK && status.extensionType != 0xFF) + type = wpad::GetWPADType((KPADExtensionType)status.extensionType); + else + return nullptr; + } + + std::string guid = guid::GetGamepadGUID(type); + Joystick* joystick = nullptr; + + for (auto stick : this->joysticks) + { + if (!stick->IsConnected() && stick->GetGUID() == guid) + { + joystick = stick; + reused = true; + break; + } + } + + if (!joystick) + { + joystick = openByType(type, (int)this->joysticks.size()); + this->joysticks.push_back(joystick); + } + + this->RemoveJoystick(joystick); + + if (!joystick->Open(index)) + return nullptr; + + this->recentGUIDs[joystick->GetGUID()] = true; + this->active.push_back(joystick); + + return joystick; +} diff --git a/platform/cafe/source/modules/keyboard_ext.cpp b/platform/cafe/source/modules/keyboard_ext.cpp new file mode 100644 index 000000000..bdb26e286 --- /dev/null +++ b/platform/cafe/source/modules/keyboard_ext.cpp @@ -0,0 +1,82 @@ +#include + +#include + +#include + +#include + +using namespace love; + +Keyboard::Keyboard() : + Keyboard<>(this->GetMaxEncodingLength(MAX_INPUT_LENGTH)), + createArgs {}, + appearArgs {}, + client(nullptr), + inited(false) +{} + +void Keyboard::Initialize() +{ + this->client = (FSClient*)MEMAllocFromDefaultHeap(sizeof(FSClient)); + + if (!this->client) + throw love::Exception("Failed to allocate FSClient for nn::swkbd!"); + + const auto result = FSAddClient(this->client, FS_ERROR_FLAG_ALL); + + if (ResultCode(result).Failed()) + throw love::Exception("FSAddClient: %x", result); + + this->createArgs.regionType = nn::swkbd::RegionType::USA; + this->createArgs.workMemory = MEMAllocFromDefaultHeap(nn::swkbd::GetWorkMemorySize(0)); + + if (!this->createArgs.workMemory) + throw love::Exception("No work memory for nn::swkbd!"); + + this->createArgs.fsClient = this->client; + + if (!nn::swkbd::Create(this->createArgs)) + throw love::Exception("Failed to initialize nn:swkbd!"); +} + +Keyboard::~Keyboard() +{ + FSDelClient(this->client, FS_ERROR_FLAG_ALL); + MEMFreeToDefaultHeap(this->client); + + nn::swkbd::Destroy(); + MEMFreeToDefaultHeap(this->createArgs.workMemory); +} + +void Keyboard::Utf16toUtf8Text() +{ + const auto* line = nn::swkbd::GetInputFormString(); + const auto utf8 = utf8::utf16to8(line); + + std::copy_n(utf8.c_str(), utf8.size(), this->text.get()); +} + +static nn::swkbd::PasswordMode GetPasswordMode(bool isPassword) +{ + if (isPassword) + return nn::swkbd::PasswordMode::Fade; + + return nn::swkbd::PasswordMode::Clear; +} + +void Keyboard::SetTextInput(const KeyboardOptions& options) +{ + uint32_t maxLength = this->GetMaxEncodingLength(options.maxLength); + this->text = std::make_unique(maxLength); + + this->appearArgs.keyboardArg.configArg.languageType = nn::swkbd::LanguageType::English; + + const auto hintText = utf8::utf8to16(options.hint); + + this->appearArgs.inputFormArg.hintText = hintText.c_str(); + this->appearArgs.inputFormArg.maxTextLength = options.maxLength; + this->appearArgs.inputFormArg.passwordMode = GetPasswordMode(options.isPassword); + + nn::swkbd::AppearInputForm(this->appearArgs); +} diff --git a/platform/cafe/source/modules/love_ext.cpp b/platform/cafe/source/modules/love_ext.cpp new file mode 100644 index 000000000..441e035fa --- /dev/null +++ b/platform/cafe/source/modules/love_ext.cpp @@ -0,0 +1,112 @@ +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +using namespace love; + +template<> +void love::PreInit() +{ + ProcUIInit(OSSavesDone_ReadyToRelease); + + VPADInit(); + + KPADInit(); + + ACInitialize(); + + FSInit(); + + bspInitializeShimInterface(); + + Console::SetMainCore(OSGetCoreId()); +} + +static bool isRunning() +{ + + if (Console::GetMainCoreId() != OSGetMainCoreId()) + { + ProcUISubProcessMessages(true); + return true; + } + + ProcUIStatus status = ProcUIProcessMessages(true); + + switch (status) + { + case PROCUI_STATUS_IN_FOREGROUND: + { + HID::Instance().SendFocus(true); + break; + } + case PROCUI_STATUS_RELEASE_FOREGROUND: + { + HID::Instance().SendFocus(false); + ProcUIDrawDoneRelease(); + break; + } + case PROCUI_STATUS_EXITING: + { + HID::Instance().SendQuit(); + return false; + } + default: + break; + } + + return true; +} + +static bool sShutdown = false; + +template<> +bool love::MainLoop(lua_State* L, int numArgs) +{ + if (!sShutdown) + { + const auto yielding = (luax::Resume(L, numArgs) == LUA_YIELD); + + if (!yielding) + { + SYSLaunchMenu(); + sShutdown = true; + } + } + + return isRunning(); +} + +template<> +void love::OnExit() +{ + FSShutdown(); + + ACFinalize(); + + KPADShutdown(); + + VPADShutdown(); + + ProcUIShutdown(); +} diff --git a/platform/cafe/source/modules/system_ext.cpp b/platform/cafe/source/modules/system_ext.cpp new file mode 100644 index 000000000..36dfb5e5f --- /dev/null +++ b/platform/cafe/source/modules/system_ext.cpp @@ -0,0 +1,161 @@ +#include +#include + +#include +#include + +#include +#include + +#include + +using namespace love; + +System::System() +{ + this->handles.mcp = MCP_Open(); + this->handles.userConfig = UCOpen(); + + nn::act::Initialize(); + + this->accountSlot = nn::act::GetDefaultAccount(); +} + +System::~System() +{ + if (this->handles.mcp != 0) + MCP_Close(this->handles.mcp); + + if (this->handles.userConfig != 0) + UCClose(this->handles.userConfig); + + nn::act::Finalize(); +} + +System<>::PowerState System::GetPowerInfo(uint8_t& percent) const +{ + percent = 100; + + return PowerState::POWER_UNKNOWN; +} + +System<>::NetworkState System::GetNetworkInfo(uint8_t& signal) const +{ + int32_t status = 0; + NetworkState state = NetworkState::NETWORK_UNKNOWN; + + R_UNLESS(ACIsApplicationConnected(&status).value, state); + + signal = (status > 0) ? 100 : 0; + state = (status > 0) ? NetworkState::NETWORK_CONNECTED : NetworkState::NETWORK_DISCONNECTED; + + return state; +} + +int System::GetProcessorCount() +{ + return OSGetCoreCount(); +} + +std::string_view System::GetSystemTheme() +{ + return "light"; +} + +std::string_view System::GetPreferredLocales() +{ + if (!this->info.locale.empty()) + return this->info.locale; + + UCSysConfig config {}; + uint32_t data = 0xFFFFFFFF; + + config.dataType = UC_DATATYPE_UNSIGNED_INT; + config.dataSize = 0x04; + config.data = &data; + + strncpy(config.name, "cafe.language", 0x40); + + R_UNLESS(UCReadSysConfig(this->handles.userConfig, 1, &config), std::string {}); + + int32_t languageData = (*(uint32_t*)config.data); + + std::optional language; + if (language = System::languages.ReverseFind((USCLanguage)languageData)) + this->info.locale = *language; + else + this->info.locale = "Unknown"; + + this->info.locale += "_"; + + MCPSysProdSettings settings {}; + R_UNLESS(MCP_GetSysProdSettings(this->handles.mcp, &settings), std::string {}); + + std::optional region; + if (region = System::countryCodes.ReverseFind(settings.product_area)) + this->info.locale += *region; + else + this->info.locale += "Unknown"; + + return this->info.locale; +} + +std::string_view System::GetVersion() +{ + if (!this->info.version.empty()) + return this->info.version; + + MCPSystemVersion systemVersion {}; + R_UNLESS(MCP_GetSystemVersion(this->handles.mcp, &systemVersion), std::string {}); + + std::string version {}; + sprintf(version.data(), "%d.%d.%d", systemVersion.major, systemVersion.minor, + systemVersion.patch); + + this->info.version = version; + + return version; +} + +std::string_view System::GetModel() +{ + if (!this->info.model.empty()) + return this->info.model; + + BSPHardwareVersion version; + R_UNLESS(bspGetHardwareVersion(&version), std::string {}); + + std::optional model; + if (!(model = System::systemModels.ReverseFind((BSPHardwareVersions)version))) + model = "Unknown"; + + this->info.model = *model; + + return this->info.model; +} + +std::string_view System::GetUsername() +{ + int16_t outName[nn::act::MiiNameSize] { 0 }; + nn::act::GetMiiNameEx(outName, this->accountSlot); + + std::copy_n(outName, nn::act::MiiNameSize, this->info.username.data()); + + return this->info.username; +} + +std::string_view System::GetFriendInfo() +{ + char username[nn::act::AccountIdSize]; + + if (!nn::act::IsNetworkAccount()) + return std::string {}; + + auto success = nn::act::GetAccountId(username); + + if (!success.IsSuccess()) + return std::string {}; + + this->info.friendCode = username; + return this->info.friendCode; +} diff --git a/platform/cafe/source/modules/timer_ext.cpp b/platform/cafe/source/modules/timer_ext.cpp new file mode 100644 index 000000000..227ab80cd --- /dev/null +++ b/platform/cafe/source/modules/timer_ext.cpp @@ -0,0 +1,55 @@ +#include + +#include + +using namespace love; + +OSTick Timer::reference = 0; + +Timer::Timer() +{ + Timer::reference = OSGetSystemTick(); + this->previousFpsUpdate = this->currentTime = Timer::GetTime(); +} + +double Timer::GetTime() +{ + auto nanoseconds = OSTicksToNanoseconds(OSGetSystemTick() - Timer::reference); + return nanoseconds / NANOSECONDS_TO_SECONDS; +} + +void Timer::Sleep(double seconds) const +{ + if (seconds >= 0) + { + auto milliseconds = seconds * MILLISECONDS; + auto nanoseconds = milliseconds * NANOSECONDS; + + OSSleepTicks(OSNanosecondsToTicks(nanoseconds)); + } +} + +double Timer::Step() +{ + this->frames++; + + this->previousTime = this->currentTime; + this->currentTime = Timer::GetTime(); + + this->delta = this->currentTime - this->previousTime; + + if (this->delta < 0) + this->delta = 0; + + double timeSinceLast = (this->currentTime - this->previousFpsUpdate); + + if (timeSinceLast > this->fpsUpdateFrequency) + { + this->fps = int((this->frames / timeSinceLast) + 0.5); + this->averageDelta = timeSinceLast / frames; + this->previousFpsUpdate = this->currentTime; + this->frames = 0; + } + + return this->delta; +} diff --git a/platform/cafe/source/modules/window_ext.cpp b/platform/cafe/source/modules/window_ext.cpp new file mode 100644 index 000000000..896731f65 --- /dev/null +++ b/platform/cafe/source/modules/window_ext.cpp @@ -0,0 +1,109 @@ +#include + +// #include - not in stable + +using namespace love; + +Window::Window() +{ + this->sleepAllowed = this->IsDisplaySleepEnabled(); +} + +Window::~Window() +{ + this->Close(); + + this->SetDisplaySleepEnabled(this->sleepAllowed); + this->graphics.Set(nullptr); +} + +bool Window::SetWindow(int width, int height, WindowSettings* settings) +{ + if (!this->graphics.Get()) + this->graphics.Set((Module::GetInstance>(Module::M_GRAPHICS))); + + bool setMode = false; + + /* handled internally */ + if (!this->IsOpen()) + { + if (!this->CreateWindowAndContext(0, 0, width, height)) + return false; + + setMode = true; + } + + if (this->graphics.Get()) + { + if (setMode) + this->graphics->SetMode(0, 0, width, height); + else + this->graphics->SetViewportSize(width, height); + } + + return true; +} + +bool Window::CreateWindowAndContext(int x, int y, int width, int height) +{ + this->open = true; + return true; +} + +void Window::GetWindow(int& width, int& height, WindowSettings& settings) +{ + width = 0; + height = 0; +} + +void Window::Close() +{ + this->open = false; +} + +bool Window::OnSizeChanged(int width, int height) +{ + return true; +} + +std::string_view Window::GetDisplayName(int displayIndex) const +{ + switch (displayIndex) + { + case 0: + default: + return "tv"; + case 1: + return "gamepad"; + } + + return std::string_view {}; +} + +std::vector::WindowSize> Window::GetFullscreenSizes(int displayIndex) +{ + return {}; +} + +void Window::GetDesktopDimensions(int displayIndex, int& width, int& height) +{} + +void Window::SetPosition(int x, int y, int displayIndex) +{} + +void Window::GetPosition(int& x, int& y, int& displayIndex) +{} + +void Window::SetDisplaySleepEnabled(bool enabled) +{ + // IM_SetRuntimeParameter(IM_PARAMETER_DIM_ENABLED, enabled); +} + +bool Window::IsDisplaySleepEnabled() const +{ + // IMParameters parameters {}; + // IM_GetParameters(¶meters) + // return parameters.dimEnabled; + + return true; +} diff --git a/platform/cafe/source/objects/gamepad.cpp b/platform/cafe/source/objects/gamepad.cpp new file mode 100644 index 000000000..e739dbf1a --- /dev/null +++ b/platform/cafe/source/objects/gamepad.cpp @@ -0,0 +1,329 @@ +#include +#include + +#include +#include + +#include +#include + +using namespace love; + +#define Module() (Module::GetInstance>(Module::M_JOYSTICK)) + +// clang-format off +constexpr BidirectionalMap buttons = { + Joystick<>::GAMEPAD_BUTTON_A, VPAD_BUTTON_A, + Joystick<>::GAMEPAD_BUTTON_B, VPAD_BUTTON_B, + Joystick<>::GAMEPAD_BUTTON_X, VPAD_BUTTON_X, + Joystick<>::GAMEPAD_BUTTON_Y, VPAD_BUTTON_Y, + Joystick<>::GAMEPAD_BUTTON_BACK, VPAD_BUTTON_MINUS, + Joystick<>::GAMEPAD_BUTTON_GUIDE, VPAD_BUTTON_HOME, + Joystick<>::GAMEPAD_BUTTON_START, VPAD_BUTTON_PLUS, + Joystick<>::GAMEPAD_BUTTON_LEFTSHOULDER, VPAD_BUTTON_L, + Joystick<>::GAMEPAD_BUTTON_RIGHTSHOULDER, VPAD_BUTTON_R, + Joystick<>::GAMEPAD_BUTTON_LEFTSTICK, VPAD_BUTTON_STICK_L, + Joystick<>::GAMEPAD_BUTTON_RIGHTSTICK, VPAD_BUTTON_STICK_R, + Joystick<>::GAMEPAD_BUTTON_DPAD_UP, VPAD_BUTTON_UP, + Joystick<>::GAMEPAD_BUTTON_DPAD_DOWN, VPAD_BUTTON_DOWN, + Joystick<>::GAMEPAD_BUTTON_DPAD_RIGHT, VPAD_BUTTON_RIGHT, + Joystick<>::GAMEPAD_BUTTON_DPAD_LEFT, VPAD_BUTTON_LEFT +}; + +constexpr BidirectionalMap axes = { + Joystick<>::GAMEPAD_AXIS_LEFTX, VPAD_STICK_L_EMULATION_LEFT | VPAD_STICK_L_EMULATION_RIGHT, + Joystick<>::GAMEPAD_AXIS_LEFTY, VPAD_STICK_L_EMULATION_UP | VPAD_STICK_L_EMULATION_DOWN, + Joystick<>::GAMEPAD_AXIS_RIGHTX, VPAD_STICK_R_EMULATION_LEFT | VPAD_STICK_R_EMULATION_RIGHT, + Joystick<>::GAMEPAD_AXIS_RIGHTY, VPAD_STICK_R_EMULATION_UP | VPAD_STICK_R_EMULATION_DOWN, + Joystick<>::GAMEPAD_AXIS_TRIGGERLEFT, VPAD_BUTTON_ZL, + Joystick<>::GAMEPAD_AXIS_TRIGGERRIGHT, VPAD_BUTTON_ZR +}; +// clang-format on + +Gamepad::Gamepad(int id) : state {} +{} + +Gamepad::Gamepad(int id, int index) : Gamepad(id) +{ + this->Open(index); +} + +bool Gamepad::Open(int index) +{ + this->Close(); + + this->name = guid::GetGamepadName(this->GetGamepadType()); + this->guid = guid::GetGamepadGUID(this->GetGamepadType()); + + this->instanceId = index; + + return this->IsConnected(); +} + +void Gamepad::Update() +{ + VPADReadError error {}; + VPADRead(VPAD_CHAN_0, &this->state, 1, &error); + + std::fill_n(this->triggers, 0x2, Trigger {}); + + if (error == VPAD_READ_NO_SAMPLES) + return; + + this->buttonStates.pressed = this->state.trigger; + this->buttonStates.released = this->state.release; + this->buttonStates.held = this->state.hold; + + this->leftStick = { this->state.leftStick.x, this->state.leftStick.y }; + this->rightStick = { this->state.rightStick.x, this->state.rightStick.y }; + + if (this->buttonStates.held & VPAD_BUTTON_ZL) + this->triggers[0x0].down = true; + + if (this->buttonStates.held & VPAD_BUTTON_ZR) + this->triggers[0x1].down = true; + + // clang-format off + if (this->IsSensorEnabled(Sensor::SENSOR_ACCELEROMETER)) + ((Accelerometer*)this->sensors[Sensor::SENSOR_ACCELEROMETER])->Update(this->state.accelorometer.acc); + + if (this->IsSensorEnabled(Sensor::SENSOR_GYROSCOPE)) + ((Gyroscope*)this->sensors[Sensor::SENSOR_GYROSCOPE])->Update(this->state.gyro); + // clang-format on +} + +bool Gamepad::IsDown(JoystickInput& result) +{ + if (!this->IsConnected()) + return false; + + if (!this->buttonStates.pressed) + return false; + + uint32_t button = 0; + + const auto entries = buttons.GetEntries(); + + for (size_t index = 0; index < entries.size(); index++) + { + button = (uint32_t)entries[index].second; + + if ((int)entries[index].second == -1) + continue; + + if (button & this->buttonStates.pressed) + { + this->buttonStates.pressed ^= button; + result = { .type = InputType::INPUT_TYPE_BUTTON, + .button = entries[index].first, + .buttonNumber = (int)index }; + + return true; + } + } + + return false; +} + +bool Gamepad::IsDown(const std::vector& inputs) const +{ + if (!this->IsConnected()) + return false; + + int count = this->GetButtonCount(); + auto records = buttons.GetEntries(); + + for (int button : inputs) + { + if (button < 0 || button >= count) + continue; + + if (this->buttonStates.held & records[button].second) + return true; + } + + return false; +} + +bool Gamepad::IsGamepadDown(const std::vector& inputs) const +{ + uint32_t heldSet = this->buttonStates.held; + + for (auto button : inputs) + { + if (auto found = buttons.Find(button)) + return heldSet & (uint32_t)*found; + } + + return false; +} + +bool Gamepad::IsUp(JoystickInput& result) +{ + uint32_t button = 0; + + if (!this->buttonStates.released) + return false; + + const auto entries = buttons.GetEntries(); + + for (size_t index = 0; index < entries.size(); index++) + { + button = (uint32_t)entries[index].second; + + if ((int)entries[index].second == -1) + continue; + + if (button & this->buttonStates.released) + { + this->buttonStates.released ^= button; + result = { .type = InputType::INPUT_TYPE_BUTTON, + .button = entries[index].first, + .buttonNumber = (int)index }; + + return true; + } + } + + return false; +} + +float Gamepad::GetAxis(int index) +{ + if (index == 0 || index == 1) + return (index == 1) ? this->leftStick.dx : this->leftStick.dy; + else if (index == 2 || index == 3) + return (index == 3) ? this->rightStick.dx : this->rightStick.dy; + else if (index == 4) + return this->triggers[0x0].down; + else if (index == 5) + return this->triggers[0x1].down; + + return 0.0f; +} + +/* internal use for the callback */ +bool Gamepad::IsAxisChanged(GamepadAxis axis) +{ + auto vpadAxis = *axes.Find(axis); + + if (vpadAxis & this->buttonStates.held) + { + this->buttonStates.held ^= vpadAxis; + return true; + } + + if (vpadAxis & this->buttonStates.released) + { + this->buttonStates.released ^= vpadAxis; + return true; + } + + return false; +} + +float Gamepad::GetGamepadAxis(GamepadAxis axis) +{ + if (!this->IsConnected()) + return 0.0f; + + int getAxis = (int)axis; + return this->GetAxis(getAxis - 1); +} + +std::vector Gamepad::GetAxes() +{ + std::vector axes; + int count = this->GetAxisCount(); + + if (!this->IsConnected() || count <= 0) + return axes; + + axes.reserve(count); + + for (int index = 0; index < count; index++) + axes.push_back(this->GetAxis(index)); + + return axes; +} + +bool Gamepad::SetVibration(float left, float right, float duration) +{ + left = std::clamp(left, 0.0f, 1.0f); + right = std::clamp(right, 0.0f, 1.0f); + + uint32_t length = Vibration<>::MAX; + + if (left == 0.0f && right == 0.0f) + return this->SetVibration(); + + if (!this->IsConnected()) + { + this->SetVibration(); + return false; + } + + if (duration >= 0.0f) + length = std::min(duration, Vibration<>::MAX / 1000.0f); + + if (length == Vibration<>::HAPTYIC_INFINITY) + this->vibration.SetDuration(length); + else + this->vibration.SetDuration(Timer::GetTime() + length); + + bool success = this->vibration.SendValues(left, right); + + if (success) + Module()->AddVibration(&this->vibration); + + return success; +} + +bool Gamepad::SetVibration() +{ + return this->vibration.Stop(); +} + +void Gamepad::GetVibration(float& left, float& right) +{ + this->vibration.GetValues(left, right); +} + +bool Gamepad::HasSensor(Sensor::SensorType type) const +{ + return true; +} + +bool Gamepad::IsSensorEnabled(Sensor::SensorType type) +{ + return this->sensors[type]; +} + +void Gamepad::SetSensorEnabled(Sensor::SensorType type, bool enabled) +{ + if (this->sensors[type] && !enabled) + this->sensors[type] = nullptr; + else if (this->sensors[type] == nullptr && enabled) + { + SensorBase* sensor = nullptr; + + if (type == Sensor::SENSOR_ACCELEROMETER) + sensor = new Accelerometer(); + else if (type == Sensor::SENSOR_GYROSCOPE) + sensor = new Gyroscope(); + + sensor->SetEnabled(enabled); + this->sensors[type] = sensor; + } +} + +std::vector Gamepad::GetSensorData(Sensor::SensorType type) +{ + if (!this->IsSensorEnabled(type)) + { + auto name = Sensor::sensorTypes.ReverseFind(type); + throw love::Exception("\"%s\" sensor is not enabled", *name); + } + + return this->sensors[type]->GetData(); +} diff --git a/platform/cafe/source/objects/procontroller.cpp b/platform/cafe/source/objects/procontroller.cpp new file mode 100644 index 000000000..bfbae719d --- /dev/null +++ b/platform/cafe/source/objects/procontroller.cpp @@ -0,0 +1,322 @@ +#include +#include + +#include +#include + +#include +#include + +using namespace love; + +#define Module() (Module::GetInstance>(Module::M_JOYSTICK)) + +// clang-format off +constexpr BidirectionalMap buttons = { + Joystick<>::GAMEPAD_BUTTON_A, WPAD_PRO_BUTTON_A, + Joystick<>::GAMEPAD_BUTTON_B, WPAD_PRO_BUTTON_B, + Joystick<>::GAMEPAD_BUTTON_X, WPAD_PRO_BUTTON_X, + Joystick<>::GAMEPAD_BUTTON_Y, WPAD_PRO_BUTTON_Y, + Joystick<>::GAMEPAD_BUTTON_BACK, WPAD_PRO_BUTTON_MINUS, + Joystick<>::GAMEPAD_BUTTON_GUIDE, WPAD_PRO_BUTTON_HOME, + Joystick<>::GAMEPAD_BUTTON_START, WPAD_PRO_BUTTON_PLUS, + Joystick<>::GAMEPAD_BUTTON_LEFTSHOULDER, WPAD_PRO_TRIGGER_L, + Joystick<>::GAMEPAD_BUTTON_RIGHTSHOULDER, WPAD_PRO_TRIGGER_R, + Joystick<>::GAMEPAD_BUTTON_LEFTSTICK, WPAD_PRO_BUTTON_STICK_L, + Joystick<>::GAMEPAD_BUTTON_RIGHTSTICK, WPAD_PRO_BUTTON_STICK_R, + Joystick<>::GAMEPAD_BUTTON_DPAD_UP, WPAD_PRO_BUTTON_UP, + Joystick<>::GAMEPAD_BUTTON_DPAD_DOWN, WPAD_PRO_BUTTON_DOWN, + Joystick<>::GAMEPAD_BUTTON_DPAD_RIGHT, WPAD_PRO_BUTTON_RIGHT, + Joystick<>::GAMEPAD_BUTTON_DPAD_LEFT, WPAD_PRO_BUTTON_LEFT +}; + +constexpr BidirectionalMap axes = { + Joystick<>::GAMEPAD_AXIS_LEFTX, WPAD_PRO_STICK_L_EMULATION_LEFT | WPAD_PRO_STICK_L_EMULATION_RIGHT, + Joystick<>::GAMEPAD_AXIS_LEFTY, WPAD_PRO_STICK_L_EMULATION_UP | WPAD_PRO_STICK_L_EMULATION_DOWN, + Joystick<>::GAMEPAD_AXIS_RIGHTX, WPAD_PRO_STICK_R_EMULATION_LEFT | WPAD_PRO_STICK_R_EMULATION_RIGHT, + Joystick<>::GAMEPAD_AXIS_RIGHTY, WPAD_PRO_STICK_R_EMULATION_UP | WPAD_PRO_STICK_R_EMULATION_DOWN, +}; +// clang-format on + +ProController::ProController(int id) : state {} +{} + +ProController::ProController(int id, int index) : ProController(id) +{ + this->Open(index); +} + +bool ProController::Open(int index) +{ + this->Close(); + + this->name = guid::GetGamepadName(this->GetGamepadType()); + this->guid = guid::GetGamepadGUID(this->GetGamepadType()); + + this->instanceId = index; + + return this->IsConnected(); +} + +void ProController::Update() +{ + KPADError error {}; + KPADReadEx((WPADChan)(this->instanceId - 1), &this->state, 1, &error); + + std::fill_n(this->triggers, 0x2, Trigger {}); + + if (error == KPAD_ERROR_NO_SAMPLES) + return; + + this->buttonStates.pressed = this->state.trigger; + this->buttonStates.released = this->state.release; + this->buttonStates.held = this->state.hold; + + this->leftStick = { this->state.pro.leftStick.x, this->state.pro.leftStick.y }; + this->rightStick = { this->state.pro.rightStick.x, this->state.pro.rightStick.y }; + + if (this->buttonStates.held & WPAD_PRO_TRIGGER_ZL) + this->triggers[0x0].down = true; + + if (this->buttonStates.held & WPAD_PRO_TRIGGER_ZR) + this->triggers[0x1].down = true; +} + +bool ProController::IsDown(JoystickInput& result) +{ + if (!this->IsConnected()) + return false; + + if (!this->buttonStates.pressed) + return false; + + uint32_t button = 0; + + const auto entries = buttons.GetEntries(); + + for (size_t index = 0; index < entries.size(); index++) + { + button = (uint32_t)entries[index].second; + + if ((int)entries[index].second == -1) + continue; + + if (button & this->buttonStates.pressed) + { + this->buttonStates.pressed ^= button; + result = { .type = InputType::INPUT_TYPE_BUTTON, + .button = entries[index].first, + .buttonNumber = (int)index }; + + return true; + } + } + + return false; +} + +bool ProController::IsDown(const std::vector& inputs) const +{ + if (!this->IsConnected()) + return false; + + int count = this->GetButtonCount(); + auto records = buttons.GetEntries(); + + for (int button : inputs) + { + if (button < 0 || button >= count) + continue; + + if (this->buttonStates.held & records[button].second) + return true; + } + + return false; +} + +bool ProController::IsGamepadDown(const std::vector& inputs) const +{ + uint32_t heldSet = this->buttonStates.held; + + for (auto button : inputs) + { + if (auto found = buttons.Find(button)) + return heldSet & (uint32_t)*found; + } + + return false; +} + +bool ProController::IsUp(JoystickInput& result) +{ + uint32_t button = 0; + + if (!this->buttonStates.released) + return false; + + const auto entries = buttons.GetEntries(); + + for (size_t index = 0; index < entries.size(); index++) + { + button = (uint32_t)entries[index].second; + + if ((int)entries[index].second == -1) + continue; + + if (button & this->buttonStates.released) + { + this->buttonStates.released ^= button; + result = { .type = InputType::INPUT_TYPE_BUTTON, + .button = entries[index].first, + .buttonNumber = (int)index }; + + return true; + } + } + + return false; +} + +float ProController::GetAxis(int index) +{ + if (index == 0 || index == 1) + return (index == 1) ? this->leftStick.dx : this->leftStick.dy; + else if (index == 2 || index == 3) + return (index == 3) ? this->rightStick.dx : this->rightStick.dy; + else if (index == 4) + return this->triggers[0x0].down; + else if (index == 5) + return this->triggers[0x1].down; + + return 0.0f; +} + +/* internal use for the callback */ +bool ProController::IsAxisChanged(GamepadAxis axis) +{ + auto vpadAxis = *axes.Find(axis); + + if (vpadAxis & this->buttonStates.held) + { + this->buttonStates.held ^= vpadAxis; + return true; + } + + if (vpadAxis & this->buttonStates.released) + { + this->buttonStates.released ^= vpadAxis; + return true; + } + + return false; +} + +float ProController::GetGamepadAxis(GamepadAxis axis) +{ + if (!this->IsConnected()) + return 0.0f; + + int getAxis = (int)axis; + return this->GetAxis(getAxis - 1); +} + +std::vector ProController::GetAxes() +{ + std::vector axes; + int count = this->GetAxisCount(); + + if (!this->IsConnected() || count <= 0) + return axes; + + axes.reserve(count); + + for (int index = 0; index < count; index++) + axes.push_back(this->GetAxis(index)); + + return axes; +} + +bool ProController::SetVibration(float left, float right, float duration) +{ + left = std::clamp(left, 0.0f, 1.0f); + right = std::clamp(right, 0.0f, 1.0f); + + uint32_t length = Vibration<>::MAX; + + if (left == 0.0f && right == 0.0f) + return this->SetVibration(); + + if (!this->IsConnected()) + { + this->SetVibration(); + return false; + } + + if (duration >= 0.0f) + length = std::min(duration, Vibration<>::MAX / 1000.0f); + + if (length == Vibration<>::HAPTYIC_INFINITY) + this->vibration.SetDuration(length); + else + this->vibration.SetDuration(Timer::GetTime() + length); + + bool success = this->vibration.SendValues(left, right); + + if (success) + Module()->AddVibration(&this->vibration); + + return success; +} + +bool ProController::SetVibration() +{ + return this->vibration.Stop(); +} + +void ProController::GetVibration(float& left, float& right) +{ + this->vibration.GetValues(left, right); +} + +bool ProController::HasSensor(Sensor::SensorType type) const +{ + return false; +} + +bool ProController::IsSensorEnabled(Sensor::SensorType type) +{ + return this->sensors[type]; +} + +void ProController::SetSensorEnabled(Sensor::SensorType type, bool enabled) +{ + if (this->sensors[type] && !enabled) + this->sensors[type] = nullptr; + else if (this->sensors[type] == nullptr && enabled) + { + if (this->HasSensor(type)) + { + SensorBase* sensor = nullptr; + + if (type == Sensor::SENSOR_ACCELEROMETER) + sensor = new Accelerometer(); + else if (type == Sensor::SENSOR_GYROSCOPE) + sensor = new Gyroscope(); + + sensor->SetEnabled(enabled); + this->sensors[type] = sensor; + } + } +} + +std::vector ProController::GetSensorData(Sensor::SensorType type) +{ + if (!this->IsSensorEnabled(type)) + { + auto name = Sensor::sensorTypes.ReverseFind(type); + throw love::Exception("\"%s\" sensor is not enabled", *name); + } + + return this->sensors[type]->GetData(); +} diff --git a/platform/cafe/source/objects/shader_ext.cpp b/platform/cafe/source/objects/shader_ext.cpp new file mode 100644 index 000000000..6103646ef --- /dev/null +++ b/platform/cafe/source/objects/shader_ext.cpp @@ -0,0 +1,182 @@ +#include + +#include +#include + +#include +#include +#include + +#include + +using namespace love; +using namespace vertex; + +#define SHADERS_DIR "/vol/content/shaders/" + +#define DEFAULT_PRIMITIVE_SHADER (SHADERS_DIR "color.gsh") +#define DEFAULT_TEXTURE_SHADER (SHADERS_DIR "texture.gsh") +#define DEFAULT_VIDEO_SHADER (SHADERS_DIR "video.gsh") + +Shader::Shader() : program {} +{} + +Shader::Shader(Data* group) : Shader() +{} + +Shader::~Shader() +{ + for (int i = 0; i < STANDARD_MAX_ENUM; i++) + { + if (this == Shader::defaults[i]) + Shader::defaults[i] = nullptr; + } + + if (current == this) + Shader::AttachDefault(STANDARD_DEFAULT); + + WHBGfxFreeShaderGroup(&this->program); +} + +uint32_t Shader::GetPixelSamplerLocation(int index) +{ + if (this->shaderType != Shader::STANDARD_TEXTURE) + throw love::Exception("Cannot fetch location from non-Texture pixel shader"); + + size_t count = this->program.pixelShader->samplerVarCount; + + if (index > count) + throw love::Exception("Sampler variable #%d is out of bounds (max %d)", index, count); + + return this->program.pixelShader->samplerVars[index].location; +} + +GX2UniformBlock* Shader::GetUniformBlock(const char* name) +{ + return GX2GetVertexUniformBlock(this->program.vertexShader, name); +} + +void Shader::AttachDefault(StandardShader type) +{ + Shader* defaultshader = Shader::defaults[type]; + + if (defaultshader == nullptr) + { + current = nullptr; + return; + } + + if (current != defaultshader) + defaultshader->Attach(); +} + +void Shader::Attach(bool forced) +{ + if (Shader::current != this || forced) + { + Renderer::Instance().UseProgram(this->program); + Renderer<>::shaderSwitches++; + + Shader::current = this; + } +} + +void Shader::BindTexture(int index, GX2Texture* texture, GX2Sampler* sampler) +{ + const auto location = this->GetPixelSamplerLocation(index); + + GX2SetPixelTexture(texture, location); + GX2SetPixelSampler(sampler, location); +} + +static bool loadShaderFile(const char* filepath, WHBGfxShaderGroup& program, std::string& error) +{ + FILE* file = std::fopen(filepath, "r"); + + if (!file) + { + error = "File does not exist."; + std::fclose(file); + return false; + } + + std::unique_ptr data; + + std::fseek(file, 0, SEEK_END); + long size = std::ftell(file); + std::rewind(file); + + try + { + data = std::make_unique(size); + } + catch (std::bad_alloc&) + { + error = "Not enough memory."; + return false; + } + + long readSize = (long)std::fread(data.get(), 1, size, file); + + if (readSize != size) + { + error = "Failed to read whole file."; + std::fclose(file); + return false; + } + + std::fclose(file); + + if (!WHBGfxLoadGFDShaderGroup(&program, 0, data.get())) + { + error = "Failed to load Shader Group"; + return false; + } + + return true; +} + +void Shader::LoadDefaults(StandardShader type) +{ + std::string error; + bool success = false; + + switch (type) + { + case STANDARD_DEFAULT: + default: + { + success = loadShaderFile(DEFAULT_PRIMITIVE_SHADER, this->program, error); + break; + } + case STANDARD_TEXTURE: + { + success = loadShaderFile(DEFAULT_TEXTURE_SHADER, this->program, error); + break; + } + case STANDARD_VIDEO: + { + /* todo */ + return; + } + } + + std::optional shaderName; + shaderName = Shader::standardShaders.ReverseFind(type); + + if (!success) + { + throw love::Exception("Failed to initialize the '%s' shader: %s", *shaderName, + error.c_str()); + } + + // clang-format off + WHBGfxInitShaderAttribute(&this->program, "inPos", 0, POSITION_OFFSET, Shader::GX2_FORMAT_VEC3); + WHBGfxInitShaderAttribute(&this->program, "inColor", 0, COLOR_OFFSET, Shader::GX2_FORMAT_VEC4); + WHBGfxInitShaderAttribute(&this->program, "inTexCoord", 0, TEXCOORD_OFFSET, Shader::GX2_FORMAT_VEC2); + // clang-format on + + WHBGfxInitFetchShader(&this->program); + + this->shaderType = type; +} diff --git a/platform/cafe/source/objects/source_ext.cpp b/platform/cafe/source/objects/source_ext.cpp new file mode 100644 index 000000000..4b370c9a6 --- /dev/null +++ b/platform/cafe/source/objects/source_ext.cpp @@ -0,0 +1,660 @@ +#include +#include + +#include + +#include + +using namespace love; + +using DSP = love::DSP; + +template<> +Source::DataBuffer::DataBuffer(const void* data, size_t size) : size(size) +{ + this->buffer = (int16_t*)malloc(size); + std::memcpy(this->buffer, (int16_t*)data, size); +} + +template<> +Source::DataBuffer::~DataBuffer() +{ + free(this->buffer); +} + +Source::Source(AudioPool* pool, SoundData* soundData) : + Source<>(TYPE_STATIC), + pool(pool), + buffers {} +{ + this->sampleRate = soundData->GetSampleRate(); + this->channels = soundData->GetChannelCount(); + this->bitDepth = soundData->GetBitDepth(); + this->samplesOffset = 0; + this->bufferCount = 1; + + this->staticBuffer = std::make_shared(soundData->GetData(), soundData->GetSize()); +} + +Source::Source(AudioPool* pool, Decoder* decoder) : + Source<>(TYPE_STREAM), + pool(pool), + buffers {} +{ + this->decoder = decoder; + this->sampleRate = decoder->GetSampleRate(); + this->channels = decoder->GetChannelCount(); + this->bitDepth = decoder->GetBitDepth(); + this->bufferCount = MAX_BUFFERS; + this->samplesOffset = 0; +} + +Source::Source(AudioPool* pool, int sampleRate, int bitDepth, int channels, + int buffers) : + Source<>(TYPE_QUEUE), + pool(pool), + buffers {} +{ + this->sampleRate = sampleRate; + this->channels = channels; + this->bitDepth = bitDepth; + this->bufferCount = buffers; + this->samplesOffset = 0; + + if (buffers < 1 || buffers > Source::MAX_BUFFERS) + buffers = MAX_BUFFERS; + + std::memset(this->buffers, 0, sizeof(this->buffers)); +} + +Source::Source(const Source& other) : + Source<>(other.sourceType), + pool(other.pool), + buffers {} +{ + this->staticBuffer = other.staticBuffer; + this->decoder = nullptr; + this->sampleRate = other.sampleRate; + this->channels = other.channels; + this->bitDepth = other.bitDepth; + this->bufferCount = other.bufferCount; + this->samplesOffset = other.samplesOffset; + + if (this->sourceType == TYPE_STREAM) + { + if (other.decoder.Get()) + this->decoder.Set(other.decoder->Clone(), Acquire::NORETAIN); + } +} + +Source::~Source() +{ + this->Stop(); +} + +Source* Source::Clone() +{ + return new Source(*this); +} + +bool Source::Play() +{ + uint8_t wasPlaying = false; + + { + auto lock = this->pool->Lock(); + if (!this->pool->AssignSource(this, this->channel, wasPlaying)) + return this->valid = false; + } + + if (!wasPlaying) + { + if (!(this->valid = this->PlayAtomic(this->buffers[0]))) + return false; + + this->ResumeAtomic(); + + { + auto lock = this->pool->Lock(); + this->pool->AddSource(this, this->channel); + } + + return this->valid; + } + + this->ResumeAtomic(); + + return this->valid = true; +} + +void Source::Reset() +{} + +void Source::Stop() +{ + if (!this->valid) + return; + + auto lock = this->pool->Lock(); + this->pool->ReleaseSource(this); +} + +void Source::Pause() +{ + auto lock = this->pool->Lock(); + + if (this->pool->IsPlaying(this)) + this->PauseAtomic(); +} + +bool Source::IsPlaying() const +{ + return this->valid && !::DSP::Instance().IsChannelPaused(this->channel); +} + +bool Source::IsFinished() const +{ + if (!this->valid) + return false; + + if (this->sourceType == TYPE_STREAM && (this->IsLooping() || !this->decoder->IsFinished())) + return false; + + return ::DSP::Instance().IsChannelPlaying(this->channel) == false; +} + +bool Source::Update() +{ + if (!this->valid) + return false; + + switch (this->sourceType) + { + case TYPE_STATIC: + return !this->IsFinished(); + case TYPE_STREAM: + { + if (this->IsFinished()) + return false; + + bool other = !this->current; + + if (!Mix_Playing(this->channel)) + { + int decoded = this->StreamAtomic(this->buffers[other], this->decoder.Get()); + + if (decoded == 0) + return false; + + ::DSP::Instance().ChannelAddBuffer(this->channel, &this->buffers[other], false); + this->samplesOffset += + ::DSP::Instance().ChannelGetSampleOffset(this->channel, this->bitDepth); + + this->current = !this->current; + } + + return true; + } + case TYPE_QUEUE: + break; + default: + break; + } + + return false; +} + +void Source::SetVolume(float volume) +{ + if (volume < this->GetMinVolume() || volume > this->GetMaxVolume()) + return; + + if (this->valid) + ::DSP::Instance().ChannelSetVolume(this->channel, volume); + + this->volume = volume; +} + +float Source::GetVolume() const +{ + if (this->valid) + return ::DSP::Instance().ChannelGetVolume(this->channel); + + return this->volume; +} + +/* todo */ +void Source::Seek(double offset, Unit unit) +{ + // auto lock = this->pool->Lock(); + + int offsetSamples = 0; + double offsetSeconds = 0.0f; + + switch (unit) + { + case UNIT_SAMPLES: + { + offsetSamples = (int)offset; + offsetSeconds = offset / ((double)this->sampleRate / this->channels); + break; + } + case UNIT_SECONDS: + default: + { + offsetSeconds = offset; + offsetSamples = (int)(offset * sampleRate * this->channels); + } + } + + bool wasPlaying = this->IsPlaying(); + + switch (this->sourceType) + { + case TYPE_STATIC: + { + if (this->valid) + this->Stop(); + + this->samplesOffset = offsetSamples; + + if (wasPlaying) + this->Play(); + + break; + } + case TYPE_STREAM: + { + if (this->valid) + this->Stop(); + + this->decoder->Seek(offsetSeconds); + + if (wasPlaying) + this->Play(); + + break; + } + case TYPE_QUEUE: + { + /* todo */ + } + default: + break; + } + + if (wasPlaying && (this->sourceType == TYPE_STREAM && !this->IsPlaying())) + { + this->Stop(); + + if (this->IsLooping()) + this->Play(); + + return; + } + + this->samplesOffset = offsetSamples; +} + +/* todo */ +double Source::Tell(Unit unit) +{ + auto lock = this->pool->Lock(); + + int offset = 0; + + if (this->valid) + { + if (this->sourceType == TYPE_STATIC) + offset += ::DSP::Instance().ChannelGetSampleOffset(this->channel, this->bitDepth); + else + offset = this->samplesOffset; + } + + if (unit == UNIT_SECONDS) + return offset / (double)sampleRate / this->channels; + + return offset; +} + +double Source::GetDuration(Unit unit) +{ + auto lock = this->pool->Lock(); + + switch (this->sourceType) + { + case TYPE_STATIC: + { + size_t size = this->staticBuffer->GetSize(); + size_t samples = (size / this->channels) / (this->bitDepth / 8); + + if (unit == UNIT_SAMPLES) + return (double)samples; + + return (double)samples / (double)sampleRate; + } + case TYPE_STREAM: + { + /* vorbisidec 1.2.1 uses ms, not sec, convert */ + double seconds = this->decoder->GetDuration() / 1000.0; + + if (unit == UNIT_SECONDS) + return seconds; + + return seconds * decoder->GetSampleRate(); + } + case TYPE_QUEUE: + { + /* todo */ + break; + } + default: + return 0.0; + } + + return 0.0; +} + +void Source::SetLooping(bool loop) +{ + if (this->sourceType == TYPE_QUEUE) + throw QueueLoopingException(); + + this->looping = loop; +} + +/* todo */ +bool Source::Queue(void* data, size_t length, int sampleRate, int bitDepth, + int channels) +{ + if (this->sourceType != TYPE_QUEUE) + throw QueueTypeMismatchException(); + + if (sampleRate != this->sampleRate || bitDepth != this->bitDepth || channels != this->channels) + throw QueueFormatMismatchException(); + + if (length % (bitDepth / 8 * channels) != 0) + throw QueueMalformedLengthException(bitDepth / 8 * channels); + + if (length == 0) + return true; + + return true; +} + +int Source::GetFreeBufferCount() const +{ + if (this->sourceType == TYPE_STATIC) + return 0; + + size_t count = 0; + // for (auto& buffer : this->buffers) + // count += (buffer->state == ::DSP::STATE_FINISHED) ? 1 : 0; + + return count; +} + +static size_t samplesToBytes(size_t samples, size_t bitSize) +{ + return samples * bitSize / 8; +} + +void Source::PrepareAtomic() +{ + ::DSP::Instance().ChannelReset(this->channel); + + switch (this->sourceType) + { + case TYPE_STATIC: + { + const auto bytesOffset = samplesToBytes(this->samplesOffset, this->bitDepth); + const auto rawSize = this->staticBuffer->GetSize() - bytesOffset; + + // clang-format off + this->buffers[0].allocated = 0; + this->buffers[0].abuf = (uint8_t*)this->staticBuffer->GetBuffer() + bytesOffset; + + this->buffers[0].alen = rawSize; + this->buffers[0].volume = MIX_MAX_VOLUME; + // clang-format on + + break; + } + case TYPE_STREAM: + { + if (this->StreamAtomic(this->buffers[0], this->decoder.Get()) == 0) + break; + + if (this->decoder->IsFinished()) + break; + + break; + } + case TYPE_QUEUE: + break; /* todo */ + default: + break; + } +} + +int Source::StreamAtomic(Mix_Chunk& buffer, Decoder* decoder) +{ + int decoded = std::max(decoder->Decode(), 0); + + if (decoded > 0) + { + buffer.allocated = 0; + buffer.abuf = (uint8_t*)decoder->GetBuffer(); + buffer.alen = decoded; + buffer.volume = MIX_MAX_VOLUME; + } + + if (decoder->IsFinished() && this->IsLooping()) + decoder->Rewind(); + + return decoded; +} + +/* todo */ +void Source::TeardownAtomic() +{ + ::DSP::Instance().ChannelStop(this->channel); + + switch (this->sourceType) + { + case TYPE_STATIC: + break; + case TYPE_STREAM: + { + this->decoder->Rewind(); + + break; + } + case TYPE_QUEUE: + break; /* todo */ + default: + break; + } + + this->valid = false; + this->samplesOffset = 0; +} + +bool Source::PlayAtomic(Mix_Chunk& buffer) +{ + this->PrepareAtomic(); + + bool looping = (this->sourceType == TYPE_STREAM) ? false : this->looping; + if (!(this->valid = ::DSP::Instance().ChannelAddBuffer(this->channel, &buffer, looping))) + return false; + + if (this->sourceType != TYPE_STREAM) + this->samplesOffset = 0; + + if (this->sourceType == TYPE_STREAM) + this->valid = true; + + return true; +} + +void Source::StopAtomic() +{ + if (!this->valid) + return; + + this->TeardownAtomic(); +} + +void Source::PauseAtomic() +{ + if (this->valid) + ::DSP::Instance().ChannelPause(this->channel); +} + +void Source::ResumeAtomic() +{ + if (this->valid) + ::DSP::Instance().ChannelPause(this->channel, false); +} + +bool Source::Play(const std::vector& sources) +{ + if (sources.size() == 0) + return true; + + auto* pool = ((Source*)sources[0])->pool; + auto lock = pool->Lock(); + + std::vector wasPlaying(sources.size()); + std::vector channels(sources.size()); + + for (size_t index = 0; index < sources.size(); index++) + { + if (!pool->AssignSource((Source*)sources[index], channels[index], wasPlaying[index])) + { + for (size_t j = 0; j < index; j++) + { + if (!wasPlaying[j]) + pool->ReleaseSource((Source*)sources[index], false); + } + + return false; + } + } + + std::vector toPlay; + toPlay.reserve(sources.size()); + + for (size_t index = 0; index < sources.size(); index++) + { + if (wasPlaying[index] && sources[index]->IsPlaying()) + continue; + + if (!wasPlaying[index]) + { + auto* source = (Source*)sources[index]; + source->channel = channels[index]; + + source->PrepareAtomic(); + } + + toPlay.push_back(sources[index]); + } + + for (auto& _source : toPlay) + { + auto* source = (Source*)_source; + + if (source->sourceType != TYPE_STREAM) + source->samplesOffset = 0; + + if (!(_source->valid = _source->Play())) + return false; + + pool->AddSource(_source, source->channel); + } + + return true; +} + +void Source::Stop(const std::vector& sources) +{ + if (sources.size() == 0) + return; + + auto* pool = ((Source*)sources[0])->pool; + auto lock = pool->Lock(); + + std::vector toStop; + toStop.reserve(sources.size()); + + for (auto& _source : sources) + { + auto* source = (Source*)_source; + + if (source->valid) + toStop.push_back(source); + } + + for (auto& _source : toStop) + { + auto* source = (Source*)_source; + + if (source->valid) + source->TeardownAtomic(); + + pool->ReleaseSource(source, false); + } +} + +void Source::Pause(const std::vector& sources) +{ + if (sources.size() == 0) + return; + + auto lock = ((Source*)sources[0])->pool->Lock(); + + for (auto& _source : sources) + { + auto* source = (Source*)_source; + + if (source->valid) + source->PauseAtomic(); + } +} + +std::vector*> Source::Pause(AudioPool* pool) +{ + std::vector sources; + + { + auto lock = pool->Lock(); + sources = pool->GetPlayingSources(); + + auto end = std::remove_if(sources.begin(), sources.end(), + [](Source* source) { return !source->IsPlaying(); }); + + sources.erase(end, sources.end()); + } + + Source::Pause(sources); + + return sources; +} + +void Source::Stop(AudioPool* pool) +{ + std::vector sources; + + { + auto lock = pool->Lock(); + sources = pool->GetPlayingSources(); + } + + Source::Stop(sources); +} + +int Source::GetChannelCount() const +{ + return this->channels; +} diff --git a/platform/cafe/source/objects/texture_ext.cpp b/platform/cafe/source/objects/texture_ext.cpp new file mode 100644 index 000000000..6adf6c90d --- /dev/null +++ b/platform/cafe/source/objects/texture_ext.cpp @@ -0,0 +1,351 @@ +#include + +#include +#include + +#include + +#include + +using namespace love; + +static void createFramebufferObject(GX2ColorBuffer*& buffer, GX2Texture*& texture, int width, + int height) +{ + buffer = new GX2ColorBuffer(); + + if (!buffer) + throw love::Exception("Failed to create GX2Texture."); + + std::memset(&buffer->surface, 0, sizeof(GX2Surface)); + + buffer->surface.use = GX2_SURFACE_USE_TEXTURE_COLOR_BUFFER_TV; + buffer->surface.dim = GX2_SURFACE_DIM_TEXTURE_2D; + buffer->surface.aa = GX2_AA_MODE1X; + buffer->surface.width = width; + buffer->surface.height = height; + buffer->surface.depth = 1; + buffer->surface.mipLevels = 1; + buffer->surface.format = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8; + buffer->surface.swizzle = 0; + buffer->surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED; + buffer->surface.mipmaps = nullptr; + buffer->viewFirstSlice = 0; + buffer->viewMip = 0; + buffer->viewNumSlices = 1; + + GX2CalcSurfaceSizeAndAlignment(&buffer->surface); + GX2InitColorBufferRegs(buffer); + + const auto size = buffer->surface.imageSize; + const auto alignment = buffer->surface.alignment; + + auto handle = MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM1); + buffer->surface.image = MEMAllocFromFrmHeapEx(handle, size, alignment); + + texture->surface.image = buffer->surface.image; +} + +static void createTextureObject(GX2Texture*& texture, PixelFormat format, int width, int height) +{ + texture = new GX2Texture(); + + if (!texture) + throw love::Exception("Failed to create GX2Texture."); + + std::memset(&texture->surface, 0, sizeof(GX2Surface)); + + texture->surface.use = GX2_SURFACE_USE_TEXTURE; + texture->surface.dim = GX2_SURFACE_DIM_TEXTURE_2D; + texture->surface.width = width; + texture->surface.height = height; + + texture->surface.depth = 1; + texture->surface.mipLevels = 1; + + std::optional gxFormat; + if (!(gxFormat = Renderer::pixelFormats.Find(format))) + throw love::Exception("Invalid pixel format."); + + texture->surface.format = *gxFormat; + texture->surface.aa = GX2_AA_MODE1X; + texture->surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED; + texture->viewFirstMip = 0; + texture->viewNumMips = 1; + texture->viewFirstSlice = 0; + texture->viewNumSlices = 1; + texture->compMap = GX2_COMP_MAP(GX2_SQ_SEL_R, GX2_SQ_SEL_G, GX2_SQ_SEL_B, GX2_SQ_SEL_A); + + GX2CalcSurfaceSizeAndAlignment(&texture->surface); + GX2InitTextureRegs(texture); + + texture->surface.image = memalign(texture->surface.alignment, texture->surface.imageSize); + + if (!texture->surface.image) + throw love::Exception("Failed to create GX2Surface."); + + std::memset(texture->surface.image, 0, texture->surface.imageSize); + + GX2Invalidate(GX2_INVALIDATE_MODE_CPU_TEXTURE, texture->surface.image, + texture->surface.imageSize); +} + +Texture::Texture(const Graphics* graphics, const Settings& settings, + const Slices* data) : + Texture(settings, data), + framebuffer(nullptr), + texture(nullptr), + sampler {} +{ + this->format = graphics->GetSizedFormat(format, this->renderTarget, this->readable); + + if (this->mipmapMode == MIPMAPS_AUTO && this->IsCompressed()) + this->mipmapMode = MIPMAPS_MANUAL; + + if (this->mipmapMode != MIPMAPS_NONE) + this->mipmapCount = + Texture<>::GetTotalMipmapCount(this->pixelWidth, this->pixelHeight, this->depth); + + bool invalidDimensions = this->pixelWidth <= 0 || this->pixelHeight <= 0; + if (invalidDimensions || this->layers <= 0 || this->depth <= 0) + throw love::Exception("Texture dimensions must be greater than zero."); + + if (this->textureType != TEXTURE_2D && this->requestedMSAA > 1) + throw love::Exception("MSAA is only supported for 2D textures."); + + if (!this->renderTarget && this->requestedMSAA > 1) + throw love::Exception("MSAA is only supported with render target textures."); + + bool isDepthStencilFormat = love::IsPixelFormatDepthStencil(this->format); + if (this->readable && isDepthStencilFormat && settings.msaa > 1) + throw love::Exception("Readable depth/stencil textures with MSAA are not supported."); + + if ((!this->readable || settings.msaa > 1) && this->mipmapMode != MIPMAPS_NONE) + throw love::Exception("Non-readable and MSAA textures cannot have mipmaps."); + + if (!this->readable && this->textureType != TEXTURE_2D) + throw love::Exception("Non-readable pixel formats are only supported for 2D textures."); + + if (this->IsCompressed() && this->renderTarget) + throw love::Exception("Compressed textures cannot be render targets."); + + this->state = graphics->GetDefaultSamplerState(); + if (this->GetMipmapCount() == 1) + this->state.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE; + + Quad::Viewport view { 0, 0, (double)this->width, (double)this->height }; + this->quad.Set(new Quad(view, this->width, this->height), Acquire::NORETAIN); + + ++textureCount; + + if (data != nullptr) + slices = *data; + + this->LoadVolatile(); + + slices.Clear(); +} + +Texture::~Texture() +{ + this->UnloadVolatile(); +} + +bool Texture::LoadVolatile() +{ + if (this->IsReadable()) + this->CreateTexture(); + + int64_t memorySize = 0; + + for (int mipmap = 0; mipmap < this->GetMipmapCount(); mipmap++) + { + const auto width = this->GetPixelWidth(mipmap); + const auto height = this->GetPixelHeight(mipmap); + + const auto faceCount = this->textureType == TEXTURE_CUBE ? 6 : 1; + const auto slices = this->GetDepth(mipmap) * this->layers * faceCount; + + memorySize += love::GetPixelFormatSliceSize(this->format, width, height) * slices; + } + + this->SetGraphicsMemorySize(memorySize); + + return true; +} + +void Texture::CreateTexture() +{ + Texture::CreateTexture(); + bool hasData = this->slices.Get(0, 0) != nullptr; + + int _width = this->pixelWidth; + int _height = this->pixelHeight; + + if (this->IsRenderTarget()) + { + bool clear = !hasData; + + createTextureObject(this->texture, PixelFormat::PIXELFORMAT_RGBA8_UNORM, width, height); + createFramebufferObject(this->framebuffer, this->texture, _width, _height); + + if (clear) + { + Renderer::Instance().BindFramebuffer(this); + Renderer::Instance().Clear({ 0, 0, 0, 0 }); + Renderer::Instance().BindFramebuffer(); + } + } + else + { + createTextureObject(this->texture, this->format, _width, _height); + + if (!hasData) + { + std::vector empty(_width * _height, 0); + this->ReplacePixels(empty.data(), empty.size(), 0, 0, { 0, 0, _width, _height }, false); + } + else + this->ReplacePixels(this->slices.Get(0, 0), 0, 0, 0, 0, false); + } + + this->SetSamplerState(this->state); +} + +void Texture::UnloadVolatile() +{ + if (this->texture) + delete this->texture; + + if (this->framebuffer) + delete this->framebuffer; +} + +void Texture::ReplacePixels(ImageDataBase* data, int slice, int mipmap, int x, int y, + bool reloadMipmaps) +{ + if (!this->IsReadable()) + throw love::Exception("replacePixels can only be called on readable Textures."); + + if (this->GetMSAA() > 1) + throw love::Exception("replacePixels cannot be called on an MSAA Texture."); + + auto* graphics = Module::GetInstance>(Module::M_GRAPHICS); + + if (graphics != nullptr && graphics->IsRenderTargetActive(this)) + throw love::Exception( + "replacePixels cannot be called on this Texture while it's an active render target."); + + if (this->texture == nullptr) + return; + + if (data->GetFormat() != this->GetPixelFormat()) + throw love::Exception("Pixel formats must match."); + + if (mipmap < 0 || mipmap >= this->GetMipmapCount()) + throw love::Exception("Invalid texture mipmap index %d.", mipmap + 1); + + const bool isCubeType = this->textureType == TEXTURE_CUBE; + const bool isVolumeType = this->textureType == TEXTURE_VOLUME; + const bool isArrayType = this->textureType == TEXTURE_2D_ARRAY; + + if (slice < 0 || (isCubeType && slice >= 6) || + (isVolumeType && slice >= this->GetDepth(mipmap)) || + (isArrayType && slice >= this->GetLayerCount())) + { + throw love::Exception("Invalid texture slice index %d", slice + 1); + } + + Rect rectangle = { x, y, data->GetWidth(), data->GetHeight() }; + + int mipWidth = this->GetPixelWidth(mipmap); + int mipHeight = this->GetPixelHeight(mipmap); + + if (rectangle.x < 0 || rectangle.y < 0 || rectangle.w <= 0 || rectangle.h <= 0 || + (rectangle.x + rectangle.w) > mipWidth || (rectangle.y + rectangle.h) > mipHeight) + { + throw love::Exception( + "Invalid rectangle dimensions (x = %d, y = %d, w = %d, h = %d) for %dx%d Texture.", + rectangle.x, rectangle.y, rectangle.w, rectangle.h, mipWidth, mipHeight); + } + + this->ReplacePixels(data->GetData(), data->GetSize(), 0, 0, rectangle, false); +} + +void Texture::ReplacePixels(const void* data, size_t size, int slice, int mipmap, + const Rect& rectangle, bool reloadMipmaps) +{ + if (!this->IsReadable() || this->GetMSAA() > 1) + return; + + auto* graphics = Module::GetInstance>(Module::M_GRAPHICS); + + if (graphics != nullptr && graphics->IsRenderTargetActive(this)) + return; + + const auto pitch = this->texture->surface.pitch; + + uint8_t* dest = (uint8_t*)this->texture->surface.image; + uint8_t* source = (uint8_t*)data; + + size_t pixelSize = GetPixelFormatBlockSize(this->format); + + /* copy by row */ + for (uint32_t y = 0; y < (uint32_t)rectangle.h; y++) + { + const auto srcRow = (y * rectangle.w * pixelSize); + const auto destRow = (rectangle.x + (y + rectangle.y) * pitch) * pixelSize; + + std::memcpy(dest + destRow, source + srcRow, rectangle.w * pixelSize); + } + + const auto imageSize = this->texture->surface.imageSize; + GX2Invalidate(Texture::INVALIDATE_MODE, this->texture->surface.image, imageSize); +} + +void Texture::Draw(Graphics& graphics, + const Matrix4& matrix) +{ + this->Draw(graphics, this->quad, matrix); +} + +void Texture::Draw(Graphics& graphics, Quad* quad, + const Matrix4& matrix) +{ + if (!this->readable) + throw love::Exception("Textures with non-readable formats cannot be drawn."); + + if (this->renderTarget && graphics.IsRenderTargetActive(this)) + throw love::Exception("Cannot render a Texture to itself."); + + const auto& stateTransform = graphics.GetTransform(); + bool is2D = stateTransform.IsAffine2DTransform(); + + Matrix4 transform(stateTransform, matrix); + + love::DrawCommand command(4); + command.shader = Shader<>::STANDARD_TEXTURE; + command.format = vertex::CommonFormat::TEXTURE; + command.type = vertex::PRIMITIVE_QUADS; + command.handles = { this }; + + if (is2D) + transform.TransformXY(command.Positions().get(), quad->GetVertexPositions(), command.count); + + const auto* textureCoords = quad->GetVertexTextureCoords(); + command.FillVertices(graphics.GetColor(), textureCoords); + + Renderer::Instance().Render(command); +} + +void Texture::SetSamplerState(const SamplerState& state) +{ + Texture<>::SetSamplerState(state); + + this->state.magFilter = this->state.minFilter = SamplerState::FILTER_NEAREST; + + if (this->state.mipmapFilter == SamplerState::MIPMAP_FILTER_LINEAR) + this->state.mipmapFilter = SamplerState::MIPMAP_FILTER_NEAREST; + + Renderer::Instance().SetSamplerState(this, this->state); +} diff --git a/platform/cafe/source/objects/wrap_imagedata_ext.cpp b/platform/cafe/source/objects/wrap_imagedata_ext.cpp new file mode 100644 index 000000000..6e65e6871 --- /dev/null +++ b/platform/cafe/source/objects/wrap_imagedata_ext.cpp @@ -0,0 +1,81 @@ +#include + +using namespace love; +using ImageData = love::ImageData; + +int Wrap_ImageData::__MapPixelUnsafe(lua_State* L) +{ + auto* self = Wrap_ImageData::CheckImageData(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + + int sourceX = lua_tonumber(L, 3); + int sourceY = lua_tonumber(L, 4); + int width = lua_tonumber(L, 5); + int height = lua_tonumber(L, 6); + + if (!(self->Inside(sourceX, sourceY) && + self->Inside(sourceX + width - 1, sourceY + height - 1))) + { + return luaL_error(L, "Invalid rectangle dimensions."); + } + + int imageWidth = self->GetWidth(); + + PixelFormat format = self->GetFormat(); + int components = love::GetPixelFormatColorComponents(format); + + auto pixelSetFunction = self->GetPixelSetFunction(); + auto pixelGetFunction = self->GetPixelGetFunction(); + + uint8_t* data = (uint8_t*)self->GetData(); + size_t pixelSize = self->GetPixelSize(); + + for (int y = sourceY; y < sourceY + height; y++) + { + for (int x = sourceX; x < sourceX + width; x++) + { + auto pixelData = (::ImageData::Pixel*)(data + (y * imageWidth + x) * pixelSize); + + Color color {}; + pixelGetFunction(pixelData, color); + + lua_pushvalue(L, 2); + + lua_pushnumber(L, x); + lua_pushnumber(L, y); + + lua_pushnumber(L, color.r); + lua_pushnumber(L, color.g); + lua_pushnumber(L, color.b); + lua_pushnumber(L, color.a); + + lua_call(L, 6, 4); + + color.r = luaL_checknumber(L, -4); + + if (components > 1) + color.g = luaL_checknumber(L, -3); + + if (components > 2) + color.b = luaL_checknumber(L, -2); + + if (components > 3) + color.a = luaL_checknumber(L, -1); + + pixelSetFunction(color, pixelData); + + lua_pop(L, 4); + } + } + + return 0; +} + +// clang-format off +static constexpr luaL_Reg functions[] = +{ + { "_mapPixelUnsafe", Wrap_ImageData::__MapPixelUnsafe } +}; +// clang-forma ton + +std::span Wrap_ImageData::extensions = functions; diff --git a/platform/cafe/source/utilities/driver/dsp_ext.cpp b/platform/cafe/source/utilities/driver/dsp_ext.cpp new file mode 100644 index 000000000..8d116438d --- /dev/null +++ b/platform/cafe/source/utilities/driver/dsp_ext.cpp @@ -0,0 +1,116 @@ +#include + +#include + +#include + +#include +#include + +using namespace love; + +static std::array channelOffsets; + +static void SDL_ChnEffectEvent(int channel, void* stream, int length, void* userdata) +{ + channelOffsets[channel] += length; +} + +DSP::DSP() +{} + +void DSP::Initialize() +{ + SDL_InitSubSystem(SDL_INIT_AUDIO); + + this->initialized = Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 4096) == 0; + + if (!this->initialized) + throw love::Exception("Failed to initialize DSP driver: (%s)!", Mix_GetError()); + + Mix_AllocateChannels(24); + OSInitEvent(&this->event, 1, OS_EVENT_MODE_AUTO); +} + +DSP::~DSP() +{ + for (size_t channel = 0; channel < 24; channel++) + Mix_UnregisterAllEffects(channel); + + OSResetEvent(&this->event); + + Mix_CloseAudio(); + SDL_QuitSubSystem(SDL_INIT_AUDIO); +} + +void DSP::Update() +{} + +void DSP::SetMasterVolume(float volume) +{ + Mix_Volume(-1, (volume * 128)); +} + +float DSP::GetMasterVolume() const +{ + return Mix_Volume(-1, -1) / 128.0f; +} + +bool DSP::ChannelReset(size_t id) +{ + this->ChannelSetVolume(id, this->ChannelGetVolume(id)); + channelOffsets[id] = 0; + + return true; +} + +void DSP::ChannelSetVolume(size_t id, float volume) +{ + /* sets between 0-128 */ + Mix_Volume(id, (volume * 128.0f)); +} + +float DSP::ChannelGetVolume(size_t id) +{ + /* query volume for channel */ + return Mix_Volume(id, -1) / 128.0f; +} + +size_t DSP::ChannelGetSampleOffset(size_t id, int bitDepth) +{ + return channelOffsets[id] / bitDepth * 8; +} + +bool DSP::ChannelAddBuffer(size_t id, Mix_Chunk* buffer, bool looping) +{ + int loops = (looping) ? -1 : 0; + + Mix_RegisterEffect(id, SDL_ChnEffectEvent, nullptr, nullptr); + if (Mix_PlayChannel(id, buffer, loops) < 0) + return false; + + return true; +} + +void DSP::ChannelPause(size_t id, bool paused) +{ + if (paused) + Mix_Pause(id); + else + Mix_Resume(id); +} + +bool DSP::IsChannelPaused(size_t id) +{ + return Mix_Paused(id); +} + +bool DSP::IsChannelPlaying(size_t id) +{ + return Mix_Playing(id); +} + +void DSP::ChannelStop(size_t id) +{ + Mix_HaltChannel(id); +} diff --git a/platform/cafe/source/utilities/driver/framebuffer.cpp b/platform/cafe/source/utilities/driver/framebuffer.cpp new file mode 100644 index 000000000..bc5b1d41e --- /dev/null +++ b/platform/cafe/source/utilities/driver/framebuffer.cpp @@ -0,0 +1,270 @@ +#include + +#include + +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +using namespace love; + +#define Keyboard() (Module::GetInstance>(Module::M_KEYBOARD)) + +Framebuffer::Framebuffer() : + modelView(1.0f), + transform(nullptr), + colorBuffer {}, + depthBuffer {}, + mode(0), + scanBuffer(nullptr), + scanBufferSize(0), + width(0), + height(0), + viewport {}, + scissor {} +{} + +Framebuffer::~Framebuffer() +{} + +void Framebuffer::Create(Screen screen) +{ + this->id = screen; + this->modelView = glm::mat4(1.0f); + this->transform = (Transform*)memalign(0x100, sizeof(Transform)); + + if (this->Is(Screen::TV)) + GX2SetTVEnable(true); + else + GX2SetDRCEnable(true); + + this->ScanSystemMode(); +} + +void Framebuffer::ScanSystemMode() +{ + /* early logic return */ + if (this->Is(Screen::GAMEPAD)) + { + this->mode = GX2_DRC_RENDER_MODE_SINGLE; + return this->SetSize(854, 480); + } + + switch (GX2GetSystemTVScanMode()) + { + case GX2_TV_SCAN_MODE_480I: + case GX2_TV_SCAN_MODE_480P: + { + this->mode = GX2_TV_RENDER_MODE_WIDE_480P; + this->SetSize(854, 480); + break; + } + case GX2_TV_SCAN_MODE_720P: + { + this->mode = GX2_TV_RENDER_MODE_WIDE_720P; + this->SetSize(1280, 720); + break; + } + case GX2_TV_SCAN_MODE_1080I: + case GX2_TV_SCAN_MODE_1080P: + default: + { + this->mode = GX2_TV_RENDER_MODE_WIDE_1080P; + this->SetSize(1920, 1080); + break; + } + } +} + +void Framebuffer::InitColorBuffer() +{ + memset(&this->colorBuffer, 0, sizeof(GX2ColorBuffer)); + + this->colorBuffer.surface.use = GX2_SURFACE_USE_TEXTURE_COLOR_BUFFER_TV; + this->colorBuffer.surface.dim = GX2_SURFACE_DIM_TEXTURE_2D; + this->colorBuffer.surface.aa = GX2_AA_MODE1X; + this->colorBuffer.surface.width = this->width; + this->colorBuffer.surface.height = this->height; + this->colorBuffer.surface.depth = 1; + this->colorBuffer.surface.mipLevels = 1; + this->colorBuffer.surface.format = Framebuffer::FORMAT; + this->colorBuffer.surface.tileMode = GX2_TILE_MODE_DEFAULT; + this->colorBuffer.viewNumSlices = 1; + + GX2CalcSurfaceSizeAndAlignment(&this->colorBuffer.surface); + GX2InitColorBufferRegs(&this->colorBuffer); +} + +void Framebuffer::InitDepthBuffer() +{ + std::memset(&this->depthBuffer, 0, sizeof(GX2DepthBuffer)); + + this->depthBuffer.surface.dim = GX2_SURFACE_DIM_TEXTURE_2D; + this->depthBuffer.surface.width = this->width; + this->depthBuffer.surface.height = this->height; + this->depthBuffer.surface.depth = 1; + this->depthBuffer.surface.mipLevels = 1; + this->depthBuffer.surface.format = GX2_SURFACE_FORMAT_FLOAT_D24_S8; + this->depthBuffer.surface.aa = GX2_AA_MODE1X; + this->depthBuffer.surface.use = GX2_SURFACE_USE_TEXTURE | GX2_SURFACE_USE_DEPTH_BUFFER; + this->depthBuffer.surface.tileMode = GX2_TILE_MODE_DEFAULT; + this->depthBuffer.viewNumSlices = 1; + this->depthBuffer.depthClear = 1.0f; + + GX2CalcSurfaceSizeAndAlignment(&this->depthBuffer.surface); + GX2InitDepthBufferRegs(&this->depthBuffer); +} + +bool Framebuffer::AllocateScanBuffer(MEMHeapHandle handle) +{ + const auto alignment = GX2_SCAN_BUFFER_ALIGNMENT; + this->scanBuffer = MEMAllocFromFrmHeapEx(handle, this->scanBufferSize, alignment); + + if (this->scanBuffer == nullptr) + return false; + + GX2Invalidate(GX2_INVALIDATE_MODE_CPU, this->scanBuffer, this->scanBufferSize); + + if (this->Is(Screen::TV)) + this->SetTVScanBuffer(); + else + this->SetDRCScanBuffer(); + + return true; +} + +bool Framebuffer::InvalidateColorBuffer(MEMHeapHandle handle) +{ + const auto size = this->colorBuffer.surface.imageSize; + const auto alignment = this->colorBuffer.surface.alignment; + + this->colorBuffer.surface.image = MEMAllocFromFrmHeapEx(handle, size, alignment); + + if (this->colorBuffer.surface.image == nullptr) + return false; + + GX2Invalidate(Framebuffer::INVALIDATE_COLOR_BUFFER, this->colorBuffer.surface.image, size); + + return true; +} + +bool Framebuffer::InvalidateDepthBuffer(MEMHeapHandle handle) +{ + const auto size = this->depthBuffer.surface.imageSize; + const auto alignment = this->depthBuffer.surface.alignment; + + this->depthBuffer.surface.image = MEMAllocFromFrmHeapEx(handle, size, alignment); + + if (this->depthBuffer.surface.image == nullptr) + return false; + + GX2Invalidate(GX2_INVALIDATE_MODE_CPU, this->depthBuffer.surface.image, size); + + return true; +} + +void Framebuffer::SetTVScanBuffer() +{ + const auto mode = (GX2TVRenderMode)this->mode; + GX2SetTVBuffer(this->scanBuffer, this->scanBufferSize, mode, Framebuffer::FORMAT, + Framebuffer::BUFFERING); +} + +void Framebuffer::SetDRCScanBuffer() +{ + const auto mode = (GX2DrcRenderMode)this->mode; + GX2SetDRCBuffer(this->scanBuffer, this->scanBufferSize, mode, Framebuffer::FORMAT, + Framebuffer::BUFFERING); +} + +void Framebuffer::CopyScanBuffer() +{ + auto target = (this->Is(Screen::TV)) ? GX2_SCAN_TARGET_TV : GX2_SCAN_TARGET_DRC; + GX2CopyColorBufferToScanBuffer(&this->colorBuffer, target); +} + +void Framebuffer::SetSize(int width, int height) +{ + this->width = width; + this->height = height; + + if (this->Is(Screen::TV)) + this->SetTVSize(); + else + this->SetDRCSize(); + + love::SetScreenSize(this->id, width, height); + + this->InitColorBuffer(); + this->InitDepthBuffer(); + + this->viewport = { 0, 0, width, height }; + this->scissor = { 0, 0, width, height }; +} + +void Framebuffer::SetTVSize() +{ + uint32_t unk = 0; + const auto mode = (GX2TVRenderMode)this->mode; + + GX2CalcTVSize(mode, Framebuffer::FORMAT, Framebuffer::BUFFERING, &this->scanBufferSize, &unk); + GX2SetTVScale(this->width, this->height); +} + +void Framebuffer::SetDRCSize() +{ + uint32_t unk = 0; + const auto mode = (GX2DrcRenderMode)this->mode; + + GX2CalcDRCSize(mode, Framebuffer::FORMAT, Framebuffer::BUFFERING, &this->scanBufferSize, &unk); + GX2SetDRCScale(this->width, this->height); +} + +void Framebuffer::SetViewport(const Rect& viewport) +{ + if (viewport == Rect::EMPTY) + GX2SetViewport(0, 0, (float)this->width, (float)this->height, Z_NEAR, Z_FAR); + else + GX2SetViewport(viewport.x, viewport.y, viewport.w, viewport.h, Z_NEAR, Z_FAR); +} + +void Framebuffer::SetScissor(const Rect& scissor) +{ + if (scissor == Rect::EMPTY) + GX2SetScissor(0, 0, this->width, this->height); + else + GX2SetScissor(scissor.x, scissor.y, scissor.w, scissor.h); +} + +void Framebuffer::SetProjection(const glm::highp_mat4& _projection) +{ + /* glm::value_ptr lets us access the data linearly rather than an XxY matrix */ + unsigned int* dstModel = (unsigned int*)glm::value_ptr(this->transform->modelView); + unsigned int* dstProj = (unsigned int*)glm::value_ptr(this->transform->projection); + + const size_t count = sizeof(glm::mat4) / sizeof(uint32_t); + + unsigned int* model = (unsigned int*)glm::value_ptr(this->modelView); + for (size_t index = 0; index < count; index++) + dstModel[index] = __builtin_bswap32(model[index]); + + unsigned int* projection = (unsigned int*)glm::value_ptr(_projection); + for (size_t index = 0; index < count; index++) + dstProj[index] = __builtin_bswap32(projection[index]); +} + +void Framebuffer::UseProjection() +{ + GX2Invalidate(INVALIDATE_UNIFORM, (void*)this->transform, TRANSFORM_SIZE); + + GX2SetVertexUniformBlock(1, Framebuffer::TRANSFORM_SIZE, (const void*)this->transform); +} diff --git a/platform/cafe/source/utilities/driver/hid_ext.cpp b/platform/cafe/source/utilities/driver/hid_ext.cpp new file mode 100644 index 000000000..c942cf211 --- /dev/null +++ b/platform/cafe/source/utilities/driver/hid_ext.cpp @@ -0,0 +1,134 @@ +#include +#include + +#include + +#include + +#include + +#include + +#include + +#define Keyboard() (Module::GetInstance>(Module::M_KEYBOARD)) +#define Module() (Module::GetInstance>(Module::M_JOYSTICK)) + +using namespace love; + +HID::HID() : previousTouch {} +{} + +HID::~HID() +{} + +void HID::CheckFocus() +{} + +void HID::CheckSoftwareKeyboard(VPADStatus vpadStatus) +{ + VPADGetTPCalibratedPoint(VPAD_CHAN_0, &vpadStatus.tpNormal, &vpadStatus.tpNormal); + + nn::swkbd::ControllerInfo controllerInfo {}; + controllerInfo.vpad = &vpadStatus; + + nn::swkbd::Calc(controllerInfo); + + if (nn::swkbd::IsNeedCalcSubThreadFont()) + nn::swkbd::CalcSubThreadFont(); + + if (nn::swkbd::IsNeedCalcSubThreadPredict()) + nn::swkbd::CalcSubThreadPredict(); + + bool isOkButtonPressed = nn::swkbd::IsDecideOkButton(nullptr); + bool isCancelPressed = nn::swkbd::IsDecideCancelButton(nullptr); + + if (isOkButtonPressed || isCancelPressed) + { + Keyboard()->HideKeyboard(); + + if (isOkButtonPressed) + { + Keyboard()->Utf16toUtf8Text(); + this->SendTextInput(Keyboard()->GetText()); + } + } +} + +void HID::_Poll() +{ + this->CheckFocus(); + + if (!Module()) + return; + + auto status = ((Gamepad*)Module()->GetJoystickFromId(0))->GetVPADStatus(); + + if (Keyboard()->IsShowing()) + this->CheckSoftwareKeyboard(status); + + auto tpNormal = status.tpNormal; + auto touchType = SUBTYPE_TOUCHPRESS; + + // TODO: find out how to fix initial touch issues + // also why the fuck is this lagging so much + if (tpNormal.touched) + { + VPADGetTPCalibratedPointEx(VPAD_CHAN_0, VPAD_TP_854X480, &tpNormal, &tpNormal); + + float x = tpNormal.x, y = tpNormal.y; + float dx = 0, dy = 0; + + dx = (tpNormal.x - this->previousTouch.x); + dy = (tpNormal.y - this->previousTouch.y); + + if (dx == 0 && dy == 0) + touchType = SUBTYPE_TOUCHPRESS; + else + touchType = SUBTYPE_TOUCHMOVED; + + this->SendTouchEvent(touchType, 0, x, y, dx, dy, 1.0f); + this->previousTouch = tpNormal; + + if (touchType == SUBTYPE_TOUCHMOVED && !dx && !dy) + this->events.pop_back(); + } + else + { + this->SendTouchEvent(SUBTYPE_TOUCHRELEASE, 0, this->previousTouch.x, this->previousTouch.y, + 0, 0, 1.0f); + } + + for (size_t index = 0; index < (size_t)Module()->GetJoystickCount(); index++) + { + auto* joystick = Module()->GetJoystickFromId(index); + + if (joystick) + { + joystick->Update(); + Joystick<>::JoystickInput input {}; + + for (int index = 0; index < Sensor::SENSOR_MAX_ENUM; index++) + { + const auto sensor = (Sensor::SensorType)index; + + if (joystick->IsSensorEnabled(sensor)) + this->SendJoystickSensorUpdated(index, sensor, joystick->GetSensorData(sensor)); + } + + if (joystick->IsDown(input)) + this->SendGamepadPress(true, joystick->GetID(), input.button, input.buttonNumber); + + if (joystick->IsUp(input)) + this->SendGamepadPress(false, joystick->GetID(), input.button, input.buttonNumber); + + for (size_t index = 0; index < Joystick<>::GAMEPAD_AXIS_MAX_ENUM; index++) + { + const auto axis = (Joystick<>::GamepadAxis)index; + const auto value = joystick->GetAxis(axis); + + this->SendGamepadAxis(joystick->GetID(), axis, index, value); + } + } + } +} \ No newline at end of file diff --git a/platform/cafe/source/utilities/driver/renderer_ext.cpp b/platform/cafe/source/utilities/driver/renderer_ext.cpp new file mode 100644 index 000000000..6aca14fd1 --- /dev/null +++ b/platform/cafe/source/utilities/driver/renderer_ext.cpp @@ -0,0 +1,507 @@ +#include + +#include + +#include + +#include + +#include +#include + +#include +#include + +using namespace love; + +#define Keyboard() (Module::GetInstance>(Module::M_KEYBOARD)) + +Renderer::Renderer() : + renderState {}, + inForeground(false), + commandBuffer(nullptr), + current {}, + state(nullptr), + framebuffers {} +{ + this->commandBuffer = memalign(GX2_COMMAND_BUFFER_ALIGNMENT, GX2_COMMAND_BUFFER_SIZE); + + if (!this->commandBuffer) + throw love::Exception("Failed to allocate command buffer."); + + // clang-format off + uint32_t attributes[9] = + { + GX2_INIT_CMD_BUF_BASE, (uintptr_t)this->commandBuffer, + GX2_INIT_CMD_BUF_POOL_SIZE, GX2_COMMAND_BUFFER_SIZE, + GX2_INIT_ARGC, 0, GX2_INIT_ARGV, 0, + GX2_INIT_END + }; + // clang-format on + + GX2Init(attributes); + + for (const auto screen : love::GetScreenEnums()) + this->framebuffers[screen].Create(screen); + + this->state = (GX2ContextState*)memalign(GX2_CONTEXT_STATE_ALIGNMENT, sizeof(GX2ContextState)); + + if (!this->state) + throw love::Exception("Failed to create GX2ContextState."); + + GX2SetupContextStateEx(this->state, false); + GX2SetContextState(this->state); + + GX2SetDepthOnlyControl(false, false, GX2_COMPARE_FUNC_ALWAYS); + GX2SetAlphaTest(true, GX2_COMPARE_FUNC_GREATER, 0); + + GX2SetColorControl(GX2_LOGIC_OP_COPY, 0xFF, false, true); + GX2SetSwapInterval(1); + + ProcUIRegisterCallback(PROCUI_CALLBACK_ACQUIRE, ProcUIAcquired, nullptr, 100); + ProcUIRegisterCallback(PROCUI_CALLBACK_RELEASE, ProcUIReleased, nullptr, 100); + + if (int result = this->OnForegroundAcquired(); result != 0) + throw love::Exception("Failed to acquire application foreground (error %d).", result); + + if (Keyboard() != nullptr) + Keyboard()->Initialize(); + + /* set up some state information */ + this->renderState.winding = GX2_FRONT_FACE_CCW; + this->renderState.cullBack = true; + this->renderState.depthTest = false; + this->renderState.depthWrite = false; + this->renderState.compareMode = GX2_COMPARE_FUNC_ALWAYS; + + m_buffer.elemCount = MAX_OBJECTS; + m_buffer.elemSize = vertex::VERTEX_SIZE; + m_buffer.flags = BUFFER_CREATE_FLAGS; + + if (!GX2RCreateBuffer(&m_buffer)) + throw love::Exception("Failed to create GX2RBuffer"); + + GX2RSetAttributeBuffer(&m_buffer, 0, VERTEX_SIZE, 0); + + this->context.transform = (Transform*)memalign(0x100, sizeof(Transform)); + + this->context.transform->projection = glm::mat4(1.0f); + this->context.transform->modelView = glm::mat4(1.0f); + + this->modelView = glm::mat4(1.0f); +} + +Renderer::Info Renderer::GetRendererInfo() +{ + if (this->info.filled) + return this->info; + + this->info.device = Renderer::RENDERER_DEVICE; + this->info.name = Renderer::RENDERER_NAME; + this->info.vendor = Renderer::RENDERER_VENDOR; + this->info.version = Renderer::RENDERER_VERSION; + + this->info.filled = true; + + return this->info; +} + +uint32_t Renderer::ProcUIAcquired(void* arg) +{ + return Renderer::Instance().OnForegroundAcquired(); +} + +uint32_t Renderer::ProcUIReleased(void* arg) +{ + return Renderer::Instance().OnForegroundReleased(); +} + +int Renderer::OnForegroundAcquired() +{ + this->inForeground = true; + + auto foregroundHeap = MEMGetBaseHeapHandle(MEM_BASE_HEAP_FG); + auto memOneHeap = MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM1); + + /* allocate tv scan buffer */ + if (!this->framebuffers[Screen::TV].AllocateScanBuffer(foregroundHeap)) + return -1; + + /* allocate gamepad scan buffer */ + if (!this->framebuffers[Screen::GAMEPAD].AllocateScanBuffer(foregroundHeap)) + return -2; + + /* invalidate tv color buffer */ + if (!this->framebuffers[Screen::TV].InvalidateColorBuffer(memOneHeap)) + return -4; + + /* invalidate gamepad color buffer */ + if (!this->framebuffers[Screen::GAMEPAD].InvalidateColorBuffer(memOneHeap)) + return -5; + + /* invalidate tv depth buffer */ + if (!this->framebuffers[Screen::TV].InvalidateDepthBuffer(memOneHeap)) + return -6; + + /* invalidate gamepad depth buffer */ + if (!this->framebuffers[Screen::GAMEPAD].InvalidateDepthBuffer(memOneHeap)) + return -7; + + return 0; +} + +int Renderer::OnForegroundReleased() +{ + auto foregroundHeap = MEMGetBaseHeapHandle(MEM_BASE_HEAP_FG); + auto memOneHeap = MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM1); + + MEMFreeToFrmHeap(foregroundHeap, MEM_FRM_HEAP_FREE_ALL); + MEMFreeToFrmHeap(memOneHeap, MEM_FRM_HEAP_FREE_ALL); + + this->inForeground = false; + + return 0; +} + +Renderer::~Renderer() +{ + if (this->inForeground) + this->OnForegroundReleased(); + + GX2RDestroyBufferEx(&m_buffer, GX2R_RESOURCE_BIND_NONE); + + GX2Shutdown(); + + free(this->state); + this->state = nullptr; + + free(this->commandBuffer); + this->commandBuffer = nullptr; +} + +void Renderer::EnsureInFrame() +{ + if (!this->inFrame) + { + this->cpuTickReference = OSGetSystemTick(); + this->inFrame = true; + } + + this->current = &this->framebuffers[love::GetActiveScreen()]; + GX2SetContextState(this->state); +} + +void Renderer::Clear(const Color& color) +{ + GX2ClearColor(this->context.target, color.r, color.g, color.b, color.a); + GX2SetContextState(this->state); +} + +void Renderer::SetDepthWrites(bool enable) +{ + GX2SetDepthOnlyControl(this->renderState.depthTest, enable, this->renderState.compareMode); + this->renderState.depthWrite = enable; +} + +void Renderer::ClearDepthStencil(int stencil, uint8_t mask, double depth) +{ + if (!this->renderState.depthWrite) + this->SetDepthWrites(true); + + GX2ClearDepthStencilEx(&this->current->GetDepthBuffer(), depth, stencil, GX2_CLEAR_FLAGS_BOTH); + GX2SetContextState(this->state); +} + +void Renderer::SetBlendColor(const Color& color) +{ + // GX2SetBlendConstantColor(color.r, color.g, color.b, color.a); + // GX2SetContextState(this->state); +} + +void Renderer::SetBlendMode(const RenderState::BlendState& state) +{ + std::optional opRGB; + if (!(opRGB = Renderer::blendEquations.Find(state.operationRGB))) + return; + + std::optional opAlpha; + if (!(opAlpha = Renderer::blendEquations.Find(state.operationA))) + return; + + std::optional srcColor; + if (!(srcColor = Renderer::blendFactors.Find(state.srcFactorRGB))) + return; + + std::optional dstColor; + if (!(dstColor = Renderer::blendFactors.Find(state.dstFactorRGB))) + return; + + std::optional srcAlpha; + if (!(srcAlpha = Renderer::blendFactors.Find(state.srcFactorA))) + return; + + std::optional dstAlpha; + if (!(dstAlpha = Renderer::blendFactors.Find(state.dstFactorA))) + return; + + GX2SetBlendControl(GX2_RENDER_TARGET_0, *srcColor, *dstColor, *opRGB, true, *srcAlpha, + *dstAlpha, *opAlpha); +} + +void Renderer::SetMeshCullMode(vertex::CullMode mode) +{ + bool enabled = (mode != vertex::CULL_NONE); + + this->renderState.cullFront = (enabled && mode == vertex::CULL_FRONT); + this->renderState.cullBack = (enabled && mode == vertex::CULL_BACK); + + GX2SetCullOnlyControl(this->renderState.winding, this->renderState.cullFront, + this->renderState.cullBack); +} + +void Renderer::SetVertexWinding(vertex::Winding winding) +{ + std::optional face; + if (!(face = Renderer::windingModes.Find(winding))) + return; + + bool front = this->renderState.cullFront; + bool back = this->renderState.cullBack; + + GX2SetCullOnlyControl(*face, front, back); + this->renderState.winding = *face; +} + +void Renderer::BindFramebuffer(Texture* texture) +{ + this->EnsureInFrame(); + FlushVertices(); + + this->context.target = &this->current->GetBuffer(); + auto viewport = this->current->GetViewport(); + + if (texture && texture->IsRenderTarget()) + { + auto* _texture = (Texture*)texture; + this->context.target = _texture->GetFramebuffer(); + + viewport = { 0, 0, texture->GetPixelWidth(), texture->GetPixelHeight() }; + } + + GX2SetColorBuffer(this->context.target, GX2_RENDER_TARGET_0); + this->SetViewport(viewport); +} + +void Renderer::FlushVertices() +{ + auto* vertices = (Vertex*)GX2RLockBufferEx(&m_buffer, GX2R_RESOURCE_BIND_NONE); + + for (const auto& command : m_commands) + { + if (command.count + m_vertexOffset > MAX_OBJECTS) + m_vertexOffset = 0; + + std::memcpy(vertices + m_vertexOffset, command.Vertices().get(), command.size); + + std::optional primitive; + if (!(primitive = primitiveModes.Find(command.type))) + throw love::Exception("Invalid primitive mode"); + + ++drawCallsBatched; + GX2DrawEx(*primitive, command.count, m_vertexOffset, 1); + m_vertexOffset += command.count; + } + + GX2RUnlockBufferEx(&m_buffer, GX2R_RESOURCE_BIND_NONE); + + gpuTickReference = OSGetSystemTick(); + m_commands.clear(); +} + +bool Renderer::Render(DrawCommand& command) +{ + if (!Shader::IsDefaultActive(command.shader)) + { + FlushVertices(); + Shader::defaults[command.shader]->Attach(); + } + + GX2Invalidate(INVALIDATE_UNIFORM, (void*)this->context.transform, TRANSFORM_SIZE); + GX2SetVertexUniformBlock(1, Renderer::TRANSFORM_SIZE, (const void*)this->context.transform); + + // todo: check for duplicate texture? + if (command.handles.empty() || + (command.handles.size() > 0 && command.handles == this->currentTextures)) + { + ++drawCalls; + m_commands.push_back(command.Clone()); + return true; + } + else + { + FlushVertices(); + + if (!command.handles.empty()) + { + for (size_t index = 0; index < command.handles.size(); index++) + { + const auto location = + Shader::current->GetPixelSamplerLocation(index); + + auto* texture = command.handles[index]->GetHandle(); + auto* sampler = &command.handles[index]->GetSampler(); + + GX2SetPixelTexture(texture, location); + GX2SetPixelSampler(sampler, location); + } + + this->currentTextures = command.handles; + } + + ++drawCalls; + m_commands.push_back(command.Clone()); + return true; + } + + return false; +} + +void Renderer::Present() +{ + GX2DrawDone(); + + FlushVertices(); + + this->inFrame = false; + m_vertexOffset = 0; + + if (Keyboard()->IsShowing()) + { + nn::swkbd::DrawDRC(); + this->ClearDepthStencil(0, 0xFF, 1.0); + + GX2SetContextState(this->state); + } + + /* copy our color buffers to their scan buffers */ + for (auto& framebuffer : this->framebuffers) + framebuffer.second.CopyScanBuffer(); + + /* swap scan buffers */ + GX2SwapScanBuffers(); + + const auto nanoSecSystem = OSTicksToNanoseconds(OSGetSystemTick() - this->cpuTickReference); + std::chrono::nanoseconds cpuNanoSec(nanoSecSystem); + Renderer::cpuTime = std::chrono::duration(cpuNanoSec).count(); + + const auto nanoSecTicks = OSTicksToNanoseconds(OSGetSystemTick() - this->gpuTickReference); + std::chrono::nanoseconds gpuNanoSec(nanoSecTicks); + Renderer::gpuTime = std::chrono::duration(gpuNanoSec).count(); + + /* + ** flush again as GX2WaitForFlip + ** will block the CPU + */ + GX2Flush(); + GX2WaitForFlip(); +} + +void Renderer::SetSamplerState(Texture* texture, SamplerState& state) +{ + auto& sampler = texture->GetSampler(); + + std::optional minFilter; + if (!(minFilter = Renderer::filterModes.Find(state.minFilter))) + return; + + std::optional magFilter; + if (!(magFilter = Renderer::filterModes.Find(state.magFilter))) + return; + + GX2InitSamplerXYFilter(&sampler, *magFilter, *minFilter, GX2_TEX_ANISO_RATIO_NONE); + + std::optional wrapU; + if (!(wrapU = Renderer::wrapModes.Find(state.wrapU))) + return; + + std::optional wrapV; + if (!(wrapV = Renderer::wrapModes.Find(state.wrapV))) + return; + + std::optional wrapW; + if (!(wrapW = Renderer::wrapModes.Find(state.wrapW))) + return; + + GX2InitSamplerClamping(&sampler, *wrapU, *wrapV, *wrapW); + GX2InitSamplerLOD(&sampler, state.minLod, state.maxLod, state.lodBias); +} + +void Renderer::UseProgram(const WHBGfxShaderGroup& group) +{ + GX2SetShaderMode(GX2_SHADER_MODE_UNIFORM_BLOCK); + GX2SetFetchShader(&group.fetchShader); + GX2SetVertexShader(group.vertexShader); + GX2SetPixelShader(group.pixelShader); +} + +void Renderer::SetColorMask(const RenderState::ColorMask& mask) +{ + auto channelMask = + GX2ChannelMask((mask.r * GX2_CHANNEL_MASK_R) + (mask.g * GX2_CHANNEL_MASK_G) + + (mask.b * GX2_CHANNEL_MASK_B) + (mask.a * GX2_CHANNEL_MASK_A)); + + GX2ChannelMask NONE = (GX2ChannelMask)0; + GX2SetTargetChannelMasks(channelMask, NONE, NONE, NONE, NONE, NONE, NONE, NONE); +} + +void Renderer::SetLineWidth(float width) +{ + GX2SetLineWidth(width); +} + +void Renderer::SetLineStyle(RenderState::LineStyle style) +{} + +void Renderer::SetPointSize(float size) +{ + GX2SetPointSize(size, size); +} + +void Renderer::SetViewport(const Rect& viewport) +{ + this->EnsureInFrame(); + + float width = viewport.w; + float height = viewport.h; + + if (viewport == Rect::EMPTY) + { + width = (float)this->current->GetWidth(); + height = (float)this->current->GetHeight(); + } + + GX2SetViewport(0, 0, width, height, Z_NEAR, Z_FAR); + + auto ortho = glm::ortho(0.0f, width, height, 0.0f, Z_NEAR, Z_FAR); + + /* glm::value_ptr lets us access the data linearly rather than an XxY matrix */ + unsigned int* dstModel = (unsigned int*)glm::value_ptr(this->context.transform->modelView); + unsigned int* dstProj = (unsigned int*)glm::value_ptr(this->context.transform->projection); + + const size_t count = sizeof(glm::mat4) / sizeof(uint32_t); + + unsigned int* model = (unsigned int*)glm::value_ptr(this->modelView); + unsigned int* projection = (unsigned int*)glm::value_ptr(ortho); + + for (size_t index = 0; index < count; index++) + { + dstModel[index] = __builtin_bswap32(model[index]); + dstProj[index] = __builtin_bswap32(projection[index]); + } +} + +void Renderer::SetScissor(const Rect& scissor, bool canvasActive) +{ + this->EnsureInFrame(); + this->current->SetScissor(scissor); +} diff --git a/platform/cafe/source/utilities/haptics/vibration_ext.cpp b/platform/cafe/source/utilities/haptics/vibration_ext.cpp new file mode 100644 index 000000000..877db6928 --- /dev/null +++ b/platform/cafe/source/utilities/haptics/vibration_ext.cpp @@ -0,0 +1,64 @@ +#include +#include + +#include + +#include + +using namespace love; + +Vibration::Vibration() : isGamepad(true) +{} + +Vibration::Vibration(WPADChan channel) : isGamepad(false) +{ + this->channel = channel; +} + +Vibration::~Vibration() +{ + this->Stop(); +} + +bool Vibration::Stop() +{ + return this->SendValues(0, 0); +} + +bool Vibration::SendValues(float left, float right) +{ + /* success */ + int32_t result = 0; + + if (this->isGamepad) + { + uint8_t average = ((left + right) * 0.5) * 0xFF; + uint8_t buffer[0x0F] { 0 }; + + if (average == 0) + VPADStopMotor(VPAD_CHAN_0); + else + { + std::fill_n(buffer, sizeof(buffer), average); + result = VPADControlMotor(VPAD_CHAN_0, buffer, sizeof(buffer)); + } + } + else + { + bool shouldRumble = (left != 0.0f || right != 0.0f); + WPADControlMotor(this->channel, shouldRumble); + } + + if (ResultCode(result).Success()) + { + this->vibrationInfo.left = left; + this->vibrationInfo.right = right; + } + else + { + this->vibrationInfo.left = this->vibrationInfo.right = 0.0f; + this->vibrationInfo.endTime = -1.0f; + } + + return ResultCode(result).Success(); +} diff --git a/platform/cafe/source/utilities/sensor/accelerometer.cpp b/platform/cafe/source/utilities/sensor/accelerometer.cpp new file mode 100644 index 000000000..bab47d1ec --- /dev/null +++ b/platform/cafe/source/utilities/sensor/accelerometer.cpp @@ -0,0 +1,11 @@ +#include + +using namespace love; + +Accelerometer::Accelerometer() : data {} +{} + +Accelerometer::~Accelerometer() +{ + this->SetEnabled(false); +} diff --git a/platform/cafe/source/utilities/sensor/gyroscope.cpp b/platform/cafe/source/utilities/sensor/gyroscope.cpp new file mode 100644 index 000000000..7ce20c42f --- /dev/null +++ b/platform/cafe/source/utilities/sensor/gyroscope.cpp @@ -0,0 +1,11 @@ +#include + +using namespace love; + +Gyroscope::Gyroscope() : data {} +{} + +Gyroscope::~Gyroscope() +{ + this->SetEnabled(false); +} diff --git a/platform/cafe/source/utilities/wpad.cpp b/platform/cafe/source/utilities/wpad.cpp new file mode 100644 index 000000000..3f59d0e91 --- /dev/null +++ b/platform/cafe/source/utilities/wpad.cpp @@ -0,0 +1,26 @@ +#include + +#include + +using namespace love; + +// clang-format off +constexpr BidirectionalMultiMap wpadTypes = { + WPAD_EXT_CORE, guid::GAMEPAD_TYPE_WII_REMOTE, + WPAD_EXT_MPLUS, guid::GAMEPAD_TYPE_WII_REMOTE, + WPAD_EXT_CLASSIC, guid::GAMEPAD_TYPE_WII_CLASSIC, + WPAD_EXT_MPLUS_CLASSIC, guid::GAMEPAD_TYPE_WII_CLASSIC, + WPAD_EXT_NUNCHUK, guid::GAMEPAD_TYPE_WII_REMOTE_NUNCHUCK, + WPAD_EXT_MPLUS_NUNCHUK, guid::GAMEPAD_TYPE_WII_REMOTE_NUNCHUCK, + WPAD_EXT_PRO_CONTROLLER, guid::GAMEPAD_TYPE_WII_PRO +}; +// clang-format on + +guid::GamepadType love::wpad::GetWPADType(KPADExtensionType extension) +{ + if (auto ret = wpadTypes.FindFirst(extension)) + return *ret; + + // Return unknown gamepad type for unknown extension, seems reasonable to me + return guid::GamepadType::GAMEPAD_TYPE_UNKNOWN; +} diff --git a/platform/ctr/CMakeLists.txt b/platform/ctr/CMakeLists.txt new file mode 100644 index 000000000..ec5aa83bf --- /dev/null +++ b/platform/ctr/CMakeLists.txt @@ -0,0 +1,68 @@ +target_include_directories(${PROJECT_NAME} PRIVATE + include + libraries +) + +# declare an asset target for the executable's RomFS (optional) +dkp_add_asset_target(${PROJECT_NAME}_ctr_romfs romfs) + +ctr_add_graphics_target(cartridge + IMAGE + OPTIONS -f rgba8888 -z auto + INPUTS "assets/nogame/cartridge.png" +) + +ctr_add_graphics_target(nogame + IMAGE + OPTIONS -f rgba8888 -z auto + INPUTS "assets/nogame/nogame.png" +) + +dkp_install_assets(${PROJECT_NAME}_ctr_romfs + DESTINATION "nogame" + TARGETS + cartridge + nogame +) + +ctr_add_shader_library(main_v_pica + "assets/shaders/main.v.pica" +) + +dkp_install_assets(${PROJECT_NAME}_ctr_romfs + DESTINATION "shaders" + TARGETS + main_v_pica +) + +# find source -type f | grep "\.cpp$" | clip +target_sources(${PROJECT_NAME} PRIVATE + source/common/matrix_ext.cpp + source/common/screen_ext.cpp + source/modules/fontmodule_ext.cpp + source/modules/graphics_ext.cpp + source/modules/imagemodule_ext.cpp + source/modules/joystickmodule_ext.cpp + source/modules/keyboard_ext.cpp + source/modules/love_ext.cpp + source/modules/system_ext.cpp + source/modules/timer_ext.cpp + source/modules/window_ext.cpp + source/modules/wrap_graphics_ext.cpp + source/modules/wrap_system_ext.cpp + source/objects/imagedata_ext.cpp + source/objects/joystick_ext.cpp + source/objects/shader_ext.cpp + source/objects/source_ext.cpp + source/objects/texture_ext.cpp + source/objects/truetyperasterizer_ext.cpp + source/objects/wrap_imagedata_ext.cpp + source/runtime.cpp + source/utilities/driver/dsp_ext.cpp + source/utilities/driver/hid_ext.cpp + source/utilities/driver/renderer/framebuffer_ext.cpp + source/utilities/driver/renderer/renderer_ext.cpp + source/utilities/formathandler/types/t3xhandler.cpp + source/utilities/sensor/accelerometer.cpp + source/utilities/sensor/gyroscope.cpp +) diff --git a/platform/ctr/assets/nogame/cartridge.png b/platform/ctr/assets/nogame/cartridge.png new file mode 100644 index 000000000..891ab70a9 Binary files /dev/null and b/platform/ctr/assets/nogame/cartridge.png differ diff --git a/platform/ctr/assets/nogame/nogame.png b/platform/ctr/assets/nogame/nogame.png new file mode 100644 index 000000000..e542b6135 Binary files /dev/null and b/platform/ctr/assets/nogame/nogame.png differ diff --git a/platform/ctr/assets/shaders/main.v.pica b/platform/ctr/assets/shaders/main.v.pica new file mode 100644 index 000000000..898152510 --- /dev/null +++ b/platform/ctr/assets/shaders/main.v.pica @@ -0,0 +1,47 @@ +; Uniforms +.fvec projMtx[4] +.fvec mdlvMtx[4] + +; Constants +.constf consts(1.0, 0.0, 0.0, 0.0) + +.alias ones consts.xxxx +.alias zeros consts.yyyy + +; Inputs +.in inPosition v0 +.in inColor v1 +.in inTexCoord v2 + +; Outputs +.out outPosition position +.out outColor color +.out outTexCoord0 texcoord0 + +; void main() +.proc main + ; r0 = vec4(inPosition, 1.0) + mov r0.xyz, inPosition + mov r0.w, ones + + ; pos = mdlvMtx * inPosition; + dp4 r1.x, mdlvMtx[0], r0 + dp4 r1.y, mdlvMtx[1], r0 + dp4 r1.z, mdlvMtx[2], r0 + dp4 r1.w, mdlvMtx[3], r0 + + ; outPosition = projMtx * pos + dp4 outPosition.x, projMtx[0], r1 + dp4 outPosition.y, projMtx[1], r1 + dp4 outPosition.z, projMtx[2], r1 + dp4 outPosition.w, projMtx[3], r1 + + ; outTexCoord = inTexCoord + mov outTexCoord0, inTexCoord + + ; outColor = inColor + mov outColor, inColor + + ; We're finished + end +.end diff --git a/platform/ctr/icon-dev.png b/platform/ctr/icon-dev.png new file mode 100644 index 000000000..bb9dfcaa1 Binary files /dev/null and b/platform/ctr/icon-dev.png differ diff --git a/platform/ctr/icon.png b/platform/ctr/icon.png new file mode 100644 index 000000000..cc056ea87 Binary files /dev/null and b/platform/ctr/icon.png differ diff --git a/platform/ctr/include/common/matrix_ext.hpp b/platform/ctr/include/common/matrix_ext.hpp new file mode 100644 index 000000000..570011c76 --- /dev/null +++ b/platform/ctr/include/common/matrix_ext.hpp @@ -0,0 +1,181 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace love +{ + template<> + class Matrix4 : public Matrix4 + { + public: + Matrix4(); + + Matrix4(const C3D_Mtx& matrix); + + Matrix4(const Matrix4& a, const Matrix4& b); + + Matrix4(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, + float ky); + + Matrix4(float t00, float t10, float t01, float t11, float x, float y); + + const C3D_Mtx& GetElements() const + { + return this->matrix; + } + + void SetIdentity(); + + void Transpose() + { + Mtx_Transpose(&this->matrix); + } + + void SetTranslation(float x, float y); + + void Translate(float x, float y); + + void Rotate(float r); + + void Scale(float sx, float sy); + + void Shear(float kx, float ky); + + bool IsAffine2DTransform() const; + + bool IsAffine3DTransform() const; + + Matrix4 Inverse() const; + + void SetRawTransformation(float t00, float t10, float t01, float t11, float x, float y); + + void SetTransformation(float x, float y, float angle, float sx, float sy, float ox, + float oy, float kx, float ky); + + void Translation(float x, float y); + + void SetRotation(float r); + + void SetScale(float x, float y); + + void SetShear(float kx, float ky); + + void GetApproximateScale(float& sx, float& sy) const; + + Matrix4 operator*(const Matrix4& m) const; + + void operator*=(const Matrix4& m); + + static Matrix4 Ortho(float left, float right, float bottom, float top, float near, + float far); + + static void Multiply(const Matrix4& a, const Matrix4& b, Matrix4& result); + + float Get(const unsigned row, const unsigned column) const + { + return this->matrix.m[row * 4 + (3 - column)]; + } + + void Set(const unsigned row, const unsigned column, const float value) + { + this->matrix.m[row * 4 + (3 - column)] = value; + } + + void TransformXY(const C3D_Mtx& elements); + + void TransformXY(); + + /** + * Transforms an array of 2-component vertices by this Matrix. The source + * and destination arrays may be the same. + **/ + template + void TransformXY(Vdst* dst, const Vsrc* src, int size) const; + + template + void TransformXYVert(Vdst* dst, const Vsrc* src, int size) const + { + for (int i = 0; i < size; i++) + { + // Store in temp variables in case src = dst + float x = (this->matrix.r[0].x * src[i].position[0]) + + (this->matrix.r[0].y * src[i].position[1]) + (0) + (this->matrix.r[0].w); + + float y = (this->matrix.r[1].x * src[i].position[0]) + + (this->matrix.r[1].y * src[i].position[1]) + (0) + (this->matrix.r[1].w); + + dst[i].x = x; + dst[i].y = y; + } + } + + template + void TransformXYVertPure(Vdst* dst, const Vsrc* src, int size) const + { + for (int i = 0; i < size; i++) + { + // Store in temp variables in case src = dst + float x = (this->matrix.r[0].x * src[i].position[0]) + + (this->matrix.r[0].y * src[i].position[1]) + (0) + (this->matrix.r[0].w); + + float y = (this->matrix.r[1].x * src[i].position[0]) + + (this->matrix.r[1].y * src[i].position[1]) + (0) + (this->matrix.r[1].w); + + dst[i].position[0] = x; + dst[i].position[1] = y; + } + } + /** + * Transforms an array of 2-component vertices by this Matrix, and stores + * them in an array of 3-component vertices. + **/ + template + void TransformXY0(Vdst* dst, const Vsrc* src, int size) const; + + void TransformXY(Vector2* dst, const vertex::Vertex* src, int size) const; + + private: + static void Multiply(const Matrix4& a, const Matrix4& b, C3D_Mtx& c); + + C3D_Mtx matrix; + }; + + template + void Matrix4::TransformXY(Vdst* dst, const Vsrc* src, int size) const + { + for (int i = 0; i < size; i++) + { + // Store in temp variables in case src = dst + + // clang-format off + float x = (this->matrix.r[0].x * src[i].x) + (this->matrix.r[0].y * src[i].y) + (0) + (this->matrix.r[0].w); + float y = (this->matrix.r[1].x * src[i].x) + (this->matrix.r[1].y * src[i].y) + (0) + (this->matrix.r[1].w); + // clang-format on + + dst[i].x = x; + dst[i].y = y; + } + } + + inline void Matrix4::TransformXY(Vector2* dst, const vertex::Vertex* src, + int size) const + { + for (int i = 0; i < size; i++) + { + // Store in temp variables in case src = dst + + // clang-format off + float x = (this->matrix.r[0].x * src[i].position[0]) + (this->matrix.r[0].y * src[i].position[1]) + (0) + (this->matrix.r[0].w); + float y = (this->matrix.r[1].x * src[i].position[0]) + (this->matrix.r[1].y * src[i].position[1]) + (0) + (this->matrix.r[1].w); + //clang-format on + + dst[i].x = x; + dst[i].y = y; + } + } +} // namespace love diff --git a/platform/ctr/include/common/screen_ext.hpp b/platform/ctr/include/common/screen_ext.hpp new file mode 100644 index 000000000..6552cedc8 --- /dev/null +++ b/platform/ctr/include/common/screen_ext.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace love +{ + enum Screen : int8_t + { + LEFT, + RIGHT, + BOTTOM + }; + + // clang-format off + inline constinit ScreenInfo screenInfo[0x03] = + { + { Screen::LEFT, "left", 400, 240 }, + { Screen::RIGHT, "right", 400, 240 }, + { Screen::BOTTOM, "bottom", 320, 240 } + }; + + inline constinit ScreenInfo altScreenInfo[0x02] = + { + { Screen::LEFT, "top", 400, 240 }, + { Screen::BOTTOM, "bottom", 320, 240 } + }; + + inline constinit ScreenInfo wideScreenInfo[0x02] = + { + { Screen::LEFT, "top", 800, 240 }, + { Screen::BOTTOM, "bottom", 320, 240 } + }; + // clang-format on +} // namespace love diff --git a/platform/ctr/include/modules/fontmodule_ext.hpp b/platform/ctr/include/modules/fontmodule_ext.hpp new file mode 100644 index 000000000..0923d4760 --- /dev/null +++ b/platform/ctr/include/modules/fontmodule_ext.hpp @@ -0,0 +1,96 @@ +#pragma once + +#include + +#include + +#include +#include + +#include <3ds.h> + +namespace love +{ + using SystemFontType = CFG_Region; + + class SystemFont : public Data + { + public: + SystemFont(CFG_Region region = CFG_REGION_USA); + + SystemFont* Clone() const override + { + return new SystemFont(*this); + } + + size_t GetSize() const override + { + return this->size; + } + + void* GetData() const override + { + return (void*)this->font; + } + + static inline Type type = Type("SystemFont", &Object::type); + + ~SystemFont() + { + if (this->font) + linearFree(this->font); + } + + private: + CFNT_s* font; + size_t size; + }; + + template<> + class FontModule : public FontModule + { + public: + static constexpr auto FONT_ARCHIVE = 0x0004009B00014002ULL; + + FontModule(); + + virtual ~FontModule() + {} + + static CFNT_s* LoadSystemFont(CFG_Region region, size_t& size); + + using FontModule::NewTrueTypeRasterizer; + + Rasterizer* NewImageRasterizer(ImageData* data, const std::string& text, + int extraSpacing, float dpiScale) const override; + + Rasterizer* NewImageRasterizer(ImageData* data, uint32_t* glyphs, + int glyphCount, int extraSpacing, + float dpiScale) const override; + + Rasterizer* NewRasterizer(FileData* data) const; + + Rasterizer* NewTrueTypeRasterizer(Data* data, int size, + TrueTypeRasterizer<>::Hinting hinting) const override; + + Rasterizer* NewTrueTypeRasterizer(Data* data, int size, float dpiScale, + TrueTypeRasterizer<>::Hinting hinting) const override; + + // clang-format off + static constexpr BidirectionalMap systemFonts = { + "standard", CFG_REGION_USA, + "chinese", CFG_REGION_CHN, + "taiwanese", CFG_REGION_TWN, + "korean", CFG_REGION_KOR + }; + // clang-format on + + private: + static inline std::array fontPaths = { + "font:/cbf_std.bcfnt.lz", + "font:/cbf_zh-Hans-CN.bcfnt.lz", + "font:/cbf_ko-Hang-KR.bcfnt.lz", + "font:/cbf_zh-Hant-TW.bcfnt.lz", + }; + }; +} // namespace love diff --git a/platform/ctr/include/modules/graphics_ext.hpp b/platform/ctr/include/modules/graphics_ext.hpp new file mode 100644 index 000000000..d38c5a8b1 --- /dev/null +++ b/platform/ctr/include/modules/graphics_ext.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include + +namespace love +{ + template<> + class Graphics : public Graphics + { + public: + static inline uint32_t TRANSPARENCY = Color(Color::CTR_TRANSPARENCY).rgba(); + static constexpr const char* DEFAULT_SCREEN = "top"; + + Graphics(); + + void SetMode(int x, int y, int width, int height); + + /* objects */ + + Texture* NewTexture(const Texture<>::Settings& settings, + const Texture<>::Slices* slices = nullptr) const; + + void Draw(Texture* texture, Quad* quad, const Matrix4& matrix); + + void Draw(Drawable* drawable, const Matrix4& matrix); + + void SetShader(); + + void SetShader(Shader* shader); + + /* specific stuff */ + + void Set3D(bool enabled); + + bool Get3D(); + + static void ResetDepth() + { + CURRENT_DEPTH = 0.0f; + } + + float GetCurrentDepth() const + { + return CURRENT_DEPTH; + } + + float PushCurrentDepth(float mul = 1.0f) + { + return CURRENT_DEPTH + MIN_DEPTH * mul; + } + + private: + static inline float CURRENT_DEPTH = 0.0f; + static constexpr float MIN_DEPTH = 1.0f / 16384.0f; + }; +} // namespace love diff --git a/platform/ctr/include/modules/joystickmodule_ext.hpp b/platform/ctr/include/modules/joystickmodule_ext.hpp new file mode 100644 index 000000000..96fb7a1ee --- /dev/null +++ b/platform/ctr/include/modules/joystickmodule_ext.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace love +{ + template<> + class JoystickModule : public JoystickModule + { + public: + JoystickModule(); + + int GetCurrentJoystickCount() const + { + return 1; + } + + Joystick* AddJoystick(int index); + }; +} // namespace love diff --git a/platform/ctr/include/modules/keyboard_ext.hpp b/platform/ctr/include/modules/keyboard_ext.hpp new file mode 100644 index 000000000..5232dd4a9 --- /dev/null +++ b/platform/ctr/include/modules/keyboard_ext.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +#include <3ds.h> + +namespace love +{ + template<> + class Keyboard : public Keyboard + { + public: + static constexpr uint32_t MAX_INPUT_LENGTH = 0x100; + + Keyboard(); + + void SetTextInput(const KeyboardOptions& options); + + const uint32_t GetMaxEncodingLength(const uint32_t in) + { + return in * 0x03; + } + + const bool HasTextInput() const + { + return this->showing; + } + + // clang-format off + static constexpr BidirectionalMap keyboardTypes = { + "normal", SWKBD_TYPE_NORMAL, + "qwerty", SWKBD_TYPE_QWERTY, + "numpad", SWKBD_TYPE_NUMPAD + }; + // clang-format on + + private: + SwkbdState state; + bool showing; + }; +} // namespace love diff --git a/platform/ctr/include/modules/system_ext.hpp b/platform/ctr/include/modules/system_ext.hpp new file mode 100644 index 000000000..9f9bd1462 --- /dev/null +++ b/platform/ctr/include/modules/system_ext.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include <3ds.h> + +#include + +#include + +namespace love +{ + template<> + class System : public System + { + public: + PowerState GetPowerInfo(uint8_t& percent) const; + + NetworkState GetNetworkInfo(uint8_t& signal) const; + + int GetProcessorCount(); + + std::string_view GetUsername(); + + std::string_view GetSystemTheme(); + + std::string_view GetPreferredLocales(); + + std::string_view GetVersion(); + + std::string_view GetModel(); + + std::string_view GetFriendInfo(); + + int GetPlayCoins() const; + + void SetPlayCoins(int amount); + + // clang-format off + static constexpr BidirectionalMap languages = { + "jp", CFG_LANGUAGE_JP, + "en", CFG_LANGUAGE_EN, + "fr", CFG_LANGUAGE_FR, + "de", CFG_LANGUAGE_DE, + "it", CFG_LANGUAGE_IT, + "es", CFG_LANGUAGE_ES, + "zh_CN", CFG_LANGUAGE_ZH, + "ko", CFG_LANGUAGE_KO, + "nl", CFG_LANGUAGE_NL, + "pt", CFG_LANGUAGE_PT, + "ru", CFG_LANGUAGE_RU, + "zh_TW", CFG_LANGUAGE_TW + }; + + static constexpr BidirectionalMap models = { + "CTR", CFG_MODEL_3DS, + "SPR", CFG_MODEL_3DSXL, + "KTR", CFG_MODEL_N3DS, + "FTR", CFG_MODEL_2DS, + "RED", CFG_MODEL_N3DSXL, + "JAN", CFG_MODEL_N2DSXL + }; + + static constexpr BidirectionalMap countryCodes = { + "JP", CFG_REGION_JPN, + "US", CFG_REGION_USA, + "EU", CFG_REGION_EUR, + "AU", CFG_REGION_AUS, + "CN", CFG_REGION_CHN, + "KR", CFG_REGION_KOR, + "TW", CFG_REGION_TWN + }; + // clang-format on + }; +} // namespace love diff --git a/platform/ctr/include/modules/timer_ext.hpp b/platform/ctr/include/modules/timer_ext.hpp new file mode 100644 index 000000000..621942563 --- /dev/null +++ b/platform/ctr/include/modules/timer_ext.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <3ds/os.h> + +#include + +namespace love +{ + template<> + class Timer : public Timer + { + public: + Timer(); + + double Step(); + + void Sleep(double seconds) const; + + static double GetTime(); + + private: + static TickCounter counter; + }; +} // namespace love diff --git a/platform/ctr/include/modules/window_ext.hpp b/platform/ctr/include/modules/window_ext.hpp new file mode 100644 index 000000000..a238dce4d --- /dev/null +++ b/platform/ctr/include/modules/window_ext.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include + +namespace love +{ + template<> + class Window : public Window + { + public: + Window(); + + virtual ~Window(); + + void SetGraphics(Graphics* graphics) + { + this->graphics.Set(graphics); + } + + bool CreateWindowAndContext(int x, int y, int width, int height); + + bool SetWindow(int width = 800, int height = 600, WindowSettings* settings = nullptr); + + void GetWindow(int& width, int& height, WindowSettings& settings); + + void Close(); + + bool SetFullscreen(bool fullscreen, FullscreenType fullScreenType) + { + return true; + } + + bool SetFullscreen(bool fullscreen) + { + return true; + } + + bool OnSizeChanged(int width, int height); + + int GetDisplayCount() const + { + return 2; + } + + std::string_view GetDisplayName(int displayIndex) const; + + std::vector GetFullscreenSizes(int displayIndex); + + void GetDesktopDimensions(int displayIndex, int& width, int& height); + + void SetPosition(int x, int y, int displayIndex); + + void GetPosition(int& x, int& y, int& displayIndex); + + void SetDisplaySleepEnabled(bool enable); + + bool IsDisplaySleepEnabled() const; + + private: + StrongReference> graphics; + }; +} // namespace love diff --git a/platform/ctr/include/objects/imagedata_ext.hpp b/platform/ctr/include/objects/imagedata_ext.hpp new file mode 100644 index 000000000..9f7b1da91 --- /dev/null +++ b/platform/ctr/include/objects/imagedata_ext.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include +#include + +namespace love +{ + template<> + class ImageData : public ImageData + { + public: + using ImageData::ImageData; + + virtual ~ImageData() + {} + + ImageData* Clone() const override + { + return new ImageData(*this); + } + + void Paste(ImageData* data, int x, int y, Rect& source); + + FileData* Encode(FormatHandler::EncodedFormat format, std::string_view filename, + bool writeFile) const + { + throw love::Exception("This platform does not support encoding images."); + return nullptr; + } + }; +} // namespace love diff --git a/platform/ctr/include/objects/joystick_ext.hpp b/platform/ctr/include/objects/joystick_ext.hpp new file mode 100644 index 000000000..292d841f0 --- /dev/null +++ b/platform/ctr/include/objects/joystick_ext.hpp @@ -0,0 +1,109 @@ +#pragma once + +#include + +#include <3ds.h> + +namespace love +{ + template<> + class Joystick : public Joystick + { + public: + Joystick(int id); + + Joystick(int id, int index); + + virtual ~Joystick(); + + bool Open(int index); + + void Close(); + + bool IsConnected() const + { + return true; + } + + bool IsDown(JoystickInput& result); + + bool IsUp(JoystickInput& result); + + void GetDeviceInfo(int& vendor, int& product, int& version); + + int GetAxisCount() const; + + int GetButtonCount() const; + + void Update(); + + float GetAxis(int index); + + std::vector GetAxes(); + + bool IsAxisChanged(GamepadAxis axis); + + bool IsDown(const std::vector& buttons) const; + + void SetPlayerIndex(int index) + {} + + int GetPlayerIndex() const + { + return 0; + } + + bool IsGamepad() const + { + return true; + } + + guid::GamepadType GetGamepadType() const + { + bool isN3DS = false; + APT_CheckNew3DS(&isN3DS); + + if (isN3DS) + return guid::GAMEPAD_TYPE_NEW_NINTENDO_3DS; + + return guid::GAMEPAD_TYPE_NINTENDO_3DS; + } + + float GetGamepadAxis(GamepadAxis axis); + + bool IsGamepadDown(const std::vector& buttons) const; + + bool IsVibrationSupported() + { + return false; + } + + bool SetVibration(float left, float right, float duration = -1.0f) + { + return false; + } + + bool SetVibration() + { + return false; + } + + void GetVibration(float& left, float& right) {}; + + bool HasSensor(Sensor::SensorType type) const; + + bool IsSensorEnabled(Sensor::SensorType type); + + void SetSensorEnabled(Sensor::SensorType type, bool enabled); + + std::vector GetSensorData(Sensor::SensorType type); + + private: + struct + { + uint32_t pressed; + uint32_t released; + uint32_t held; + } buttonStates; + }; +} // namespace love diff --git a/platform/ctr/include/objects/shader_ext.hpp b/platform/ctr/include/objects/shader_ext.hpp new file mode 100644 index 000000000..80d2a3c60 --- /dev/null +++ b/platform/ctr/include/objects/shader_ext.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include <3ds.h> + +namespace love +{ + template<> + class Shader : public Shader + { + public: + struct Uniforms + { + int8_t uLocProjMtx; + int8_t uLocMdlView; + }; + + Shader(); + + Shader(Data* data); + + virtual ~Shader(); + + void Attach(); + + static void AttachDefault(StandardShader type); + + bool Validate(const char* filepath, std::string& error); + + bool Validate(Data* data, std::string& error); + + void LoadDefaults(StandardShader type); + + Uniforms GetUniformLocations() + { + return this->uniforms; + } + + private: + DVLB_s* binary; + shaderProgram_s program; + + std::unique_ptr data; + Uniforms uniforms; + }; +} // namespace love diff --git a/platform/ctr/include/objects/source_ext.hpp b/platform/ctr/include/objects/source_ext.hpp new file mode 100644 index 000000000..fa575dada --- /dev/null +++ b/platform/ctr/include/objects/source_ext.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include +#include + +#include +#include + +#include + +namespace love +{ + class Audio; + class AudioPool; + + template<> + class Source : public Source + { + public: + Source(AudioPool* pool, SoundData* soundData); + + Source(AudioPool* pool, Decoder* decoder); + + Source(AudioPool* pool, int sampleRate, int bitDepth, int channels, int buffers); + + Source(const Source& other); + + virtual ~Source(); + + virtual Source* Clone(); + + /* normal stuff */ + + bool Play(); + + void Stop(); + + void Pause(); + + bool IsPlaying() const; + + bool IsFinished() const; + + bool Update(); + + void SetVolume(float volume); + + float GetVolume() const; + + void Seek(double offset, Unit unit); + + double Tell(Unit unit); + + double GetDuration(Unit unit); + + void SetLooping(bool looping); + + int GetChannelCount() const; + + int GetFreeBufferCount() const; + + bool Queue(void* data, size_t length, int sampleRate, int bitDepth, int channels); + + /* atomic things */ + + void PrepareAtomic(); + + void TeardownAtomic(); + + bool PlayAtomic(ndspWaveBuf& waveBuffer); + + void StopAtomic(); + + void PauseAtomic(); + + void ResumeAtomic(); + + /* global state stuff */ + + static bool Play(const std::vector*>& sources); + + static void Stop(const std::vector*>& sources); + + static void Pause(const std::vector*>& sources); + + static std::vector*> Pause(AudioPool* pool); + + static void Stop(AudioPool* pool); + + private: + static constexpr size_t MAX_BUFFERS = 2; + + void Reset(); + + int StreamAtomic(ndspWaveBuf& buffer, Decoder* decoder); + + AudioPool* pool; + + ndspWaveBuf buffers[2]; + }; +} // namespace love diff --git a/platform/ctr/include/objects/texture_ext.hpp b/platform/ctr/include/objects/texture_ext.hpp new file mode 100644 index 000000000..35d93b31c --- /dev/null +++ b/platform/ctr/include/objects/texture_ext.hpp @@ -0,0 +1,57 @@ +#include + +#include + +namespace love +{ + template<> + class Texture : public Texture + { + public: + Texture(const Graphics* graphics, const Settings& settings, + const Slices* data); + + virtual ~Texture(); + + virtual void Draw(Graphics& graphics, + const Matrix4& matrix) override; + + virtual void Draw(Graphics& graphics, Quad* quad, + const Matrix4& transform) override; + + void ReplacePixels(ImageData* data, int slice, int mipmap, int x, int y, + bool reloadMipmaps); + + void ReplacePixels(const void* data, size_t size, int slice, int mipmap, const Rect& rect, + bool reloadMipmaps); + + void SetSamplerState(const SamplerState& state); + + void GenerateMipmaps() + {} + + bool LoadVolatile(); + + void UnloadVolatile(); + + C3D_Tex* GetHandle() + { + return this->texture; + } + + C3D_RenderTarget* GetRenderTargetHandle() const + { + if (this->renderTarget) + return this->framebuffer; + + /* shouldn't happen */ + return nullptr; + } + + private: + void CreateTexture(); + + C3D_Tex* texture; + C3D_RenderTarget* framebuffer; + }; +} // namespace love diff --git a/platform/ctr/include/scripts/wrap_font.lua b/platform/ctr/include/scripts/wrap_font.lua new file mode 100644 index 000000000..b50ac138d --- /dev/null +++ b/platform/ctr/include/scripts/wrap_font.lua @@ -0,0 +1,148 @@ +R"luastring"--( +local Font_mt = ... +local Font = Font_mt.__index + +local utf8 = require("utf8") + +-- Gets formatting information for text, given a wrap limit. +-- This function accounts for newlines correctly (i.e. '\n'). +-- @param text The text that will be wrapped. +-- @param wrap_limit The maximum width in pixels of each line that text is allowed before wrapping. +-- @return width The maximum width of the wrapped text. +-- @return wrapped A sequence containing each line of text that was wrapped. +-- https://love2d.org/wiki/Font:getWrap +-- https://github.com/love2d/love/blob/main/src/modules/graphics/Font.cpp#L751-L904 +function Font:getWrap(text, wrap_limit) + local lines = {} + local line_widths = {} + + local width = 0 + local line_codepoints = {} + + local previous_glyph = 0 + + local last_space_index = -1 + local width_before_last_space = 0 + local width_of_trailing_space = 0 + + local length = utf8.len(text) + local codepoints = {} + + -- get the codepoints into a table + for index = 1, length do + local codepoint = utf8.codepoint(text, index, index) + table.insert(codepoints, codepoint) + end + + local const_codepoints = { newline = 10, carriage = 13, space = 32 } + + local index = 1 + local char_width = 0 + + while index < #codepoints + 1 do + local codepoint = codepoints[index] + + repeat + -- push the line on newline + if codepoint == const_codepoints.newline then + table.insert(lines, line_codepoints) + + -- ignore the width of trailing spaces for individual lines + table.insert(line_widths, width - width_of_trailing_space) + + width = 0 + width_before_last_space = 0 + width_of_trailing_space = 0 + + previous_glyph = 0 + last_space_index = -1 + line_codepoints = {} + + index = index + 1 + + break + end + + -- ignore carriage returns + if codepoint == const_codepoints.carriage then + index = index + 1 + break + end + + char_width = self:getWidth(codepoint) + local new_width = width + char_width + + -- push the line after limit is hit + if codepoint ~= const_codepoints.space and new_width > wrap_limit then + -- if this is the first character in the line and exceeds the limit + -- we should skip it completely + if #line_codepoints == 0 then + index = index + 1 + elseif last_space_index ~= -1 then + -- rewind to the last seen space, if the line has one + while #line_codepoints ~= 0 and line_codepoints[#line_codepoints] ~= const_codepoints.space do + table.remove(line_codepoints, #line_codepoints) + end + + -- ignore width of trailing spaces in wrapped lines + width = width_before_last_space + index = last_space_index + + -- start next line after the space + index = index + 1 + end + + table.insert(lines, line_codepoints) + table.insert(line_widths, width) + + previous_glyph = 0 + + width = 0 + width_before_last_space = 0 + width_of_trailing_space = 0 + + line_codepoints = {} + last_space_index = -1 + + break + end + + + if previous_glyph ~= const_codepoints.space and codepoint ~= const_codepoints.space then + width_before_last_space = width + end + + width = new_width + previous_glyph = codepoint + + table.insert(line_codepoints, codepoint) + + -- keep track of the last seen space to rewind to when wrapping + if codepoint == const_codepoints.space then + last_space_index = index + width_of_trailing_space = width_of_trailing_space + char_width + elseif codepoint ~= const_codepoints.newline then + width_of_trailing_space = 0 + end + index = index + 1 + until true + end + + -- push the last line + table.insert(lines, line_codepoints) + table.insert(line_widths, width - width_of_trailing_space) + + local max_length = 0 + for width_index = 1, #line_widths do + if line_widths[width_index] > max_length then + max_length = line_widths[width_index] + end + end + + for line_index, value in ipairs(lines) do + lines[line_index] = utf8.char(unpack(value)) + end + + return max_length, lines +end +--)luastring"--" diff --git a/platform/ctr/include/scripts/wrap_window.lua b/platform/ctr/include/scripts/wrap_window.lua new file mode 100644 index 000000000..779cfd318 --- /dev/null +++ b/platform/ctr/include/scripts/wrap_window.lua @@ -0,0 +1,59 @@ +R"luastring"--( +local function main(...) + local screens = love.graphics.getScreens() + local delta_time = 0 + + local messagebox = require("wrap_window_messagebox") + local message_box = messagebox.new(...) + + while true do + if love.event and love.event.pump then + love.event.pump() + + for name, a, b, c, d, e, f in love.event.poll() do + if name == "quit" then + return 0 + else + local result = message_box:poll(name, a, b, c, d, e, f) + if result then + g_windowShown = false + return result + end + end + end + end + + if love.timer then + delta_time = love.timer.step() + end + + message_box:update(delta_time) + + if love.graphics and love.graphics.isActive() then + local screens = love.graphics.getScreens() + + for _, screen in ipairs(screens) do + love.graphics.origin() + + love.graphics.setActiveScreen(screen) + love.graphics.clear(love.graphics.getBackgroundColor()) + + message_box:draw(screen) + end + + love.graphics.present() + end + + if love.timer then + love.timer.sleep(0.001) + end + end +end + +function love.window.showMessageBox(title, text, buttons) + g_windowShown = true + + return main(title, text, buttons) +end +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/platform/ctr/include/scripts/wrap_window_messagebox.lua b/platform/ctr/include/scripts/wrap_window_messagebox.lua new file mode 100644 index 000000000..5b7dc7f82 --- /dev/null +++ b/platform/ctr/include/scripts/wrap_window_messagebox.lua @@ -0,0 +1,268 @@ +R"luastring"--( +local utf8 = require("utf8") + +local SCREEN_WIDTH = 320 +local SCREEN_HEIGHT = 240 + +local colors = {} + +colors.header = { 0.26, 0.26, 0.26, 1.00 } +colors.background = { 0.93, 0.93, 0.93, 1.00 } +colors.text = { 0.26, 0.26, 0.26, 1.00 } +colors.scroll_line = { 0.81, 0.81, 0.81, 1.00 } + +local glyphs = {} + +glyphs.a = utf8.char("0xE04C") +glyphs.b = utf8.char("0xE04D") +glyphs.x = utf8.char("0xE04E") +glyphs.y = utf8.char("0xE04F") + +local fonts = {} +fonts.header = love.graphics.newFont("standard", 20.5) +fonts.body = love.graphics.newFont("standard", 18) +fonts.button = love.graphics.newFont("standard", 18) + +local function checkbounds(x, y, self) + if x >= self.x and x < (self.x + self.width) and y >= (self.y + self.height * self.value) and + y < (self.height + self.y + self.height * self.value) then + return true + end + return false +end + +-- button class + +local button = {} +button.__index = button + +function button.new(id, text, x) + local instance = setmetatable({}, button) + + instance.text = love.graphics.newTextBatch(fonts.button, text) + + instance.x = x + instance.y = SCREEN_HEIGHT * 0.915 + + return instance +end + +function button:draw() + love.graphics.draw(self.text, self.x, self.y) +end + +function button:getWidth() + return self.text:getWidth() +end + +-- header class + +local header = {} +header.__index = header + +function header.new(title) + local instance = setmetatable({}, header) + + instance.title = love.graphics.newTextBatch(fonts.header, title) + instance.height = 28 + + return instance +end + +function header:draw() + love.graphics.setColor(colors.header) + love.graphics.rectangle("fill", 0, 0, SCREEN_WIDTH, self.height); + + love.graphics.setColor(colors.background) + love.graphics.draw(self.title, 8, (self.height - fonts.header:getHeight()) / 2) +end + +-- body class + +local body = {} +body.__index = body + +function body.new(text, x, y, width, height) + local instance = setmetatable({}, body) + + instance.x = x + instance.y = y + instance.width = width + instance.height = height + + instance.text = text + + return instance +end + +function body:draw(value) + love.graphics.setScissor(self.x, self.y, self.width, self.height) + love.graphics.push() + + love.graphics.translate(0, -value) + + love.graphics.setColor(colors.text) + love.graphics.printf(self.text, fonts.body, self.x, self.y, self.width, "left") + + love.graphics.pop() + love.graphics.setScissor() +end + +-- scrollbar class + +local scrollbar = {} +scrollbar.__index = scrollbar + +function scrollbar.new(bodyObject, wrap_count, total_height) + local instance = setmetatable({}, scrollbar) + + instance.x = SCREEN_WIDTH * 0.95 + instance.width = 8 + instance.height = bodyObject.height * 0.90 + instance.y = bodyObject.y + (bodyObject.height - instance.height) * 0.5 + + instance.bar_width = instance.width * 2 + + instance.value = 0 + instance.held = false + instance.dragPosition = 0 + instance.touchId = nil + instance.items = wrap_count + instance.heightValue = total_height + + instance.barHeight = total_height / wrap_count + + return instance +end + +function scrollbar:draw() + love.graphics.setColor(colors.scroll_line) + love.graphics.rectangle("fill", self.x, self.y, self.width, self.height, 4, 4) + + love.graphics.setColor(colors.text) + love.graphics.rectangle("fill", self.x, self.y + math.min((self.height * self.value), self.height - self.barHeight), self.width, self.barHeight, 4, 4) +end + +function scrollbar:update(dt) + if not self.touchId then + return + end + + local _, y = love.touch.getPosition(self.touchId) + local scroll_y = ((y - self.dragPosition) - self.y) + local actual_range = self.height + + self.value = scroll_y / actual_range + self.value = math.min(math.max(0, self.value), 1) +end + +function scrollbar:touchpressed(id, x, y) + if checkbounds(x, y, self) then + self.held = true + self.touchId = id + self.dragPosition = y - (self.y + self.height * self.value) + end +end + +function scrollbar:touchreleased() + if self.held then + self.held = false + self.touchId = nil + end +end + +function scrollbar:getValue() + return self.value * ((self.items - 10) * fonts.body:getHeight()) +end + +-- messagebox class + +local messagebox = {} +messagebox.__index = messagebox + +local function create_button(id, text, offset) + local padding = 8 + local value = string.format("%s %s", glyphs[text[id].button], text[id].text) + local x = (SCREEN_WIDTH - (padding * 2)) - fonts.button:getWidth(value) + + if id == 2 then + x = x - (offset + padding * 2) + end + + return button.new(id, value, x) +end + +function messagebox.new(title, text, buttons) + local instance = setmetatable({}, messagebox) + + instance.header = header.new(title) + + local body_text = text:sub(1, 0x1FD) .. "..." + instance.body = body.new(body_text, 8, instance.header.height + 8, SCREEN_WIDTH * 0.80, SCREEN_HEIGHT * 0.75) + + local first_button = create_button(1, buttons) + instance.buttons = { first_button } + + if #buttons == 2 then + table.insert(instance.buttons, create_button(2, buttons, first_button:getWidth())) + end + + local width, wrapped = fonts.body:getWrap(body_text, SCREEN_WIDTH * 0.8) + if #wrapped > 10 then + instance.scrollbar = scrollbar.new(instance.body, #wrapped, #wrapped * fonts.body:getHeight()) + end + + instance.height = SCREEN_HEIGHT + + return instance +end + +function messagebox:poll(name, ...) + if name == "gamepadpressed" then + local button_name = select(2, ...) + + if button_name == "a" or button_name == "b" then + return button_name == "a" and 1 or 2 + end + end + + if self.scrollbar then + if name == "touchpressed" then + local id, x, y = select(1, ...), select(2, ...), select(3, ...) + self.scrollbar:touchpressed(id, x, y) + elseif name == "touchreleased" then + self.scrollbar:touchreleased() + end + end +end + +function messagebox:update(dt) + if self.scrollbar then + self.scrollbar:update(dt) + end +end + +function messagebox:draw(screen) + if screen ~= "bottom" then + return + end + + love.graphics.setColor(colors.background) + love.graphics.rectangle("fill", 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) + + self.body:draw(self.scrollbar:getValue()) + + for index = 1, #self.buttons do + self.buttons[index]:draw() + end + + if self.scrollbar then + self.scrollbar:draw() + end + + self.header:draw() +end + +return messagebox +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/platform/ctr/include/utilities/abort.hpp b/platform/ctr/include/utilities/abort.hpp new file mode 100644 index 000000000..fcdffc835 --- /dev/null +++ b/platform/ctr/include/utilities/abort.hpp @@ -0,0 +1,43 @@ +#include + +namespace love +{ + enum AbortCode + { + ABORT_ROMFS, + ABORT_MCU_HWC, + ABORT_PTMU, + ABORT_CFGU, + ABORT_AC, + ABORT_FRD, + ABORT_SOC, + ABORT_Y2R, + ABORT_HID_ACCEL, + ABORT_HID_GYRO + }; + + static constexpr const char* ABORT_FORMAT_KNOWN = + "Failed to initalize %s!\n\n" + "Result: %ld\n" + "Level: %ld\n" + "Summary: %ld\n" + "Description: %ld\n\n" + "Please visit https://www.3dbrew.org/wiki/Error_codes for more information.\n"; + + // clang-format off + static constexpr BidirectionalMap abortTypes = + { + ABORT_ROMFS, "romfs", + ABORT_MCU_HWC, "mcu:HWC", + ABORT_PTMU, "ptm:u", + ABORT_CFGU, "cfg:u", + ABORT_AC, "ac:u", + ABORT_FRD, "frd:u", + ABORT_SOC, "soc:u", + ABORT_Y2R, "y2r:u", + ABORT_HID_ACCEL, "HIDUSER_Accelerometer", + ABORT_HID_GYRO, "HIDUSER_Gyroscope" + }; + // clang-format on + +} // namespace love diff --git a/platform/ctr/include/utilities/driver/dsp_ext.hpp b/platform/ctr/include/utilities/driver/dsp_ext.hpp new file mode 100644 index 000000000..b9dbf385c --- /dev/null +++ b/platform/ctr/include/utilities/driver/dsp_ext.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +#include <3ds.h> + +namespace love +{ + template<> + class DSP : public DSP + { + public: + static DSP& Instance() + { + static DSP instance; + return instance; + } + + ~DSP(); + + void Initialize(); + + void Update(); + + void SetMasterVolume(float volume); + + float GetMasterVolume() const; + + bool ChannelReset(size_t id, int channels, int bitDepth, int sampleRate); + + void ChannelSetVolume(size_t id, float volume); + + float ChannelGetVolume(size_t id) const; + + bool ChannelAddBuffer(size_t id, ndspWaveBuf* buffer); + + void ChannelPause(size_t id, bool paused = true); + + bool IsChannelPaused(size_t id) const; + + bool IsChannelPlaying(size_t id) const; + + void ChannelStop(size_t id); + + size_t ChannelGetSampleOffset(size_t id) const; + + // clang-format off + static constexpr BidirectionalMap audioFormats = { + 0x08, NDSP_ENCODING_PCM8, + 0x10, NDSP_ENCODING_PCM16 + }; + + static constexpr BidirectionalMap interpTypes = { + 0x01, NDSP_INTERP_LINEAR, + 0x02, NDSP_INTERP_POLYPHASE + }; + // clang-format on + + static int8_t GetFormat(int bitDepth, int channels); + + private: + LightEvent event; + }; +} // namespace love diff --git a/platform/ctr/include/utilities/driver/framebuffer_ext.hpp b/platform/ctr/include/utilities/driver/framebuffer_ext.hpp new file mode 100644 index 000000000..b2c738b36 --- /dev/null +++ b/platform/ctr/include/utilities/driver/framebuffer_ext.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include + +#include + +namespace love +{ + template<> + class Framebuffer : public Framebuffer + { + public: + Framebuffer(); + + void Create(Screen screen); + + void Destroy(); + + void UseProjection(Shader::Uniforms uniforms); + + void SetViewport(const Rect& viewport = Rect::EMPTY, bool canvasActive = false); + + void SetScissor(const Rect& scissor = Rect::EMPTY, bool canvasActive = false); + + C3D_RenderTarget* GetTarget() + { + return this->target; + } + + C3D_Mtx& GetModelView() + { + return this->modelView; + } + + C3D_Mtx& GetProjView() + { + return this->projView; + } + + private: + static constexpr auto DISPLAY_FLAGS = GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | + GX_TRANSFER_RAW_COPY(0) | + GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | + GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO); + + const Rect CalculateBounds(const Rect& rect); + + void SetSize(int width, int height, gfxScreen_t screen, gfx3dSide_t side); + + C3D_RenderTarget* target; + + C3D_Mtx modelView; + C3D_Mtx projView; + }; +} // namespace love diff --git a/platform/ctr/include/utilities/driver/hid_ext.hpp b/platform/ctr/include/utilities/driver/hid_ext.hpp new file mode 100644 index 000000000..e0b67158f --- /dev/null +++ b/platform/ctr/include/utilities/driver/hid_ext.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include <3ds.h> + +#include + +namespace love +{ + template<> + class HID : public HID + { + public: + static HID& Instance() + { + static HID instance; + return instance; + } + + ~HID(); + + virtual void _Poll() override; + + private: + HID(); + + struct + { + touchPosition current; + touchPosition previous; + } touchState; + }; +} // namespace love diff --git a/platform/ctr/include/utilities/driver/renderer_ext.hpp b/platform/ctr/include/utilities/driver/renderer_ext.hpp new file mode 100644 index 000000000..6b58d5681 --- /dev/null +++ b/platform/ctr/include/utilities/driver/renderer_ext.hpp @@ -0,0 +1,221 @@ +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include <3ds.h> +#include + +namespace love +{ + template<> + class Renderer : public Renderer + { + private: + static constexpr const char* RENDERER_NAME = "citro3d"; + static constexpr const char* RENDERER_VERSION = "1.7.0"; + static constexpr const char* RENDERER_VENDOR = "devkitPro"; + static constexpr const char* RENDERER_DEVICE = "DMP PICA200"; + + static inline constexpr int MAX_OBJECTS = 0x1000; + static inline constexpr int VERTEX_BUFFER_SIZE = 6 * MAX_OBJECTS; + + static inline constexpr uint8_t MAX_RENDERTARGETS = 0x03; + + Renderer(); + + public: + static Renderer& Instance() + { + static Renderer instance; + return instance; + } + + ~Renderer(); + + static void FlushVertices(); + + Info GetRendererInfo(); + + void DestroyFramebuffers(); + + void CreateFramebuffers(); + + void Clear(const Color& color); + + void ClearDepthStencil(int stencil, uint8_t mask, double depth); + + void SetBlendColor(const Color& color); + + void SetBlendMode(const RenderState::BlendState& state); + + void EnsureInFrame(); + + /* todo: canvases */ + void BindFramebuffer(Texture* texture = nullptr); + + bool Render(DrawCommand& command); + + void Present(); + + void SetViewport(const Rect& viewport, bool tilt); + + void SetScissor(const Rect& scissor, bool canvasActive); + + void SetStencil(RenderState::CompareMode mode, int value); + + void SetMeshCullMode(vertex::CullMode mode); + + void SetVertexWinding(vertex::Winding winding); + + void SetSamplerState(Texture* texture, SamplerState& state); + + void SetColorMask(const RenderState::ColorMask& mask); + + Framebuffer& GetCurrent() + { + return this->targets[love::GetActiveScreen()]; + } + + C3D_Mtx& GetModelView() + { + return this->context.modelView; + } + + void SetWideMode(bool enable) + { + this->ModeChanged([enable]() { gfxSetWide(enable); }); + } + + const bool GetWide() const + { + return gfxIsWide(); + } + + void Set3D(bool enable) + { + this->ModeChanged([enable]() { gfxSet3D(enable); }); + } + + const bool Get3D() const + { + return gfxIs3D(); + } + + /* used for Canvases */ + void DeferCallToEndOfFrame(std::function&& function) + { + this->deferred.emplace_back(std::move(function)); + } + + // clang-format off + static constexpr BidirectionalMap primitiveModes = + { + vertex::PRIMITIVE_TRIANGLES, GPU_TRIANGLES, + vertex::PRIMITIVE_TRIANGLE_STRIP, GPU_TRIANGLE_STRIP, + vertex::PRIMITIVE_TRIANGLE_FAN, GPU_TRIANGLE_FAN + }; + // clang-format on + + // clang-format off + static constexpr BidirectionalMap pixelFormats = { + PIXELFORMAT_RGBA8_UNORM, GPU_RGBA8, + PIXELFORMAT_RGB8, GPU_RGB8, + PIXELFORMAT_RGB565_UNORM, GPU_RGB565, + PIXELFORMAT_LA8_UNORM, GPU_LA8 + }; + + static constexpr BidirectionalMap blendEquations = { + RenderState::BLENDOP_ADD, GPU_BLEND_ADD, + RenderState::BLENDOP_SUBTRACT, GPU_BLEND_SUBTRACT, + RenderState::BLENDOP_REVERSE_SUBTRACT, GPU_BLEND_REVERSE_SUBTRACT, + RenderState::BLENDOP_MIN, GPU_BLEND_MIN, + RenderState::BLENDOP_MAX, GPU_BLEND_MAX + }; + + static constexpr BidirectionalMap blendFactors = { + RenderState::BLENDFACTOR_ZERO, GPU_ZERO, + RenderState::BLENDFACTOR_ONE, GPU_ONE, + RenderState::BLENDFACTOR_SRC_COLOR, GPU_SRC_COLOR, + RenderState::BLENDFACTOR_ONE_MINUS_SRC_COLOR, GPU_ONE_MINUS_SRC_COLOR, + RenderState::BLENDFACTOR_SRC_ALPHA, GPU_SRC_ALPHA, + RenderState::BLENDFACTOR_ONE_MINUS_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, + RenderState::BLENDFACTOR_DST_COLOR, GPU_DST_COLOR, + RenderState::BLENDFACTOR_ONE_MINUS_DST_COLOR, GPU_ONE_MINUS_DST_COLOR, + RenderState::BLENDFACTOR_DST_ALPHA, GPU_DST_ALPHA, + RenderState::BLENDFACTOR_ONE_MINUS_DST_ALPHA, GPU_ONE_MINUS_DST_ALPHA, + RenderState::BLENDFACTOR_SRC_ALPHA_SATURATED, GPU_SRC_ALPHA_SATURATE + }; + + static constexpr BidirectionalMap filterModes = { + SamplerState::FILTER_LINEAR, GPU_LINEAR, + SamplerState::FILTER_NEAREST, GPU_NEAREST + }; + + static constexpr BidirectionalMap wrapModes = { + SamplerState::WRAP_CLAMP, GPU_CLAMP_TO_EDGE, + SamplerState::WRAP_CLAMP_ZERO, GPU_CLAMP_TO_BORDER, + SamplerState::WRAP_REPEAT, GPU_REPEAT, + SamplerState::WRAP_MIRRORED_REPEAT, GPU_MIRRORED_REPEAT + }; + + static constexpr BidirectionalMap cullModes = { + vertex::CULL_NONE, GPU_CULL_NONE, + vertex::CULL_BACK, GPU_CULL_BACK_CCW, + vertex::CULL_FRONT, GPU_CULL_FRONT_CCW + }; + + static constexpr BidirectionalMap compareModes = { + RenderState::COMPARE_LESS, GPU_LESS, + RenderState::COMPARE_LEQUAL, GPU_LEQUAL, + RenderState::COMPARE_EQUAL, GPU_EQUAL, + RenderState::COMPARE_GEQUAL, GPU_GEQUAL, + RenderState::COMPARE_GREATER, GPU_GREATER, + RenderState::COMPARE_NOTEQUAL, GPU_NOTEQUAL, + RenderState::COMPARE_ALWAYS, GPU_ALWAYS, + RenderState::COMPARE_NEVER, GPU_NEVER + }; + // clang-format on + + private: + template + void ModeChanged(const T& func) + { + this->DestroyFramebuffers(); + func(); + this->CreateFramebuffers(); + } + + struct Context + { + C3D_Mtx modelView; + C3D_Mtx projection; + C3D_RenderTarget* target; + } context; + + std::vector> deferred; + std::array, MAX_RENDERTARGETS> targets; + + C3D_Tex* currentTexture; + + static inline PrimitiveType currentPrimitiveType = PRIMITIVE_MAX_ENUM; + static inline int totalVertices = 0; + + static inline std::vector> m_commands {}; + + C3D_BufInfo bufferInfo; + + static inline CommonFormat m_format = CommonFormat::NONE; + static inline Vertex* m_vertices = nullptr; + }; +} // namespace love diff --git a/platform/ctr/include/utilities/driver/vertex_ext.hpp b/platform/ctr/include/utilities/driver/vertex_ext.hpp new file mode 100644 index 000000000..cb602544a --- /dev/null +++ b/platform/ctr/include/utilities/driver/vertex_ext.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include + +#include + +namespace love +{ + namespace vertex + { + namespace attributes + { + typedef void (*AttributeSetFunction)(void); + + enum TEXENV_MODE + { + TEXENV_MODE_PRIMITIVE, + TEXENV_MODE_TEXTURE, + TEXENV_MODE_TEXT, + TEXENV_MODE_MAX_ENUM + }; + + void SetPrimitiveAttribute() + { + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvInit(env); + + C3D_TexEnvSrc(env, C3D_Both, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, + GPU_PRIMARY_COLOR); + C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE); + } + + void SetTextureAttribute() + { + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvInit(env); + + C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR); + C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE); + } + + void SetFontAttribute() + { + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvInit(env); + + C3D_TexEnvSrc(env, C3D_RGB, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, + GPU_PRIMARY_COLOR); + C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE); + + C3D_TexEnvSrc(env, C3D_Alpha, GPU_PRIMARY_COLOR, GPU_TEXTURE0, GPU_PRIMARY_COLOR); + C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE); + } + + static inline AttributeSetFunction GetTexEnvFunction(CommonFormat format) + { + switch (format) + { + case CommonFormat::PRIMITIVE: + default: + return SetPrimitiveAttribute; + case CommonFormat::TEXTURE: + return SetTextureAttribute; + case CommonFormat::FONT: + return SetFontAttribute; + } + } + } // namespace attributes + } // namespace vertex +} // namespace love diff --git a/platform/ctr/include/utilities/formathandler/types/t3xhandler.hpp b/platform/ctr/include/utilities/formathandler/types/t3xhandler.hpp new file mode 100644 index 000000000..3455001ec --- /dev/null +++ b/platform/ctr/include/utilities/formathandler/types/t3xhandler.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace love +{ + class T3XHandler : public FormatHandler + { + public: + virtual bool CanDecode(Data* data); + + virtual DecodedImage Decode(Data* data); + + private: + struct __attribute__((packed)) Tex3DSHeader + { + uint16_t numSubTextures; //< 1 + + uint8_t width_log2 : 3; //< log2(texWidth) - 3 (powTwoWidth) + uint8_t height_log2 : 3; //< log2(texHeight) - 3 (powTwoHeight) + + uint8_t type : 1; //< 0 = GPU_TEX_2D + uint8_t format; //< 0 = GPU_RGBA8 + uint8_t mipmapLevels; //< 0 + + uint16_t width; //< subtexWidth (sourceWidth) + uint16_t height; //< subtexHeight (sourceHeight) + + uint16_t left; //< left = border + uint16_t top; //< top = texHeight + uint16_t right; //< right = subtexWidth + uint16_t bottom; //< bottom = texHeight - subtexHeight + }; + }; +} // namespace love diff --git a/platform/ctr/include/utilities/haptics/vibration_ext.hpp b/platform/ctr/include/utilities/haptics/vibration_ext.hpp new file mode 100644 index 000000000..0231b0383 --- /dev/null +++ b/platform/ctr/include/utilities/haptics/vibration_ext.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace love +{ + template<> + class Vibration : public Vibration + { + }; +} // namespace love diff --git a/platform/ctr/include/utilities/sensor/accelerometer.hpp b/platform/ctr/include/utilities/sensor/accelerometer.hpp new file mode 100644 index 000000000..777537fda --- /dev/null +++ b/platform/ctr/include/utilities/sensor/accelerometer.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include <3ds.h> + +namespace love +{ + class Accelerometer : public SensorBase + { + public: + Accelerometer(); + + ~Accelerometer(); + + std::vector GetData() override; + + void SetEnabled(bool enable); + + bool IsEnabled() const; + + private: + accelVector data; + }; +} // namespace love diff --git a/platform/ctr/include/utilities/sensor/gyroscope.hpp b/platform/ctr/include/utilities/sensor/gyroscope.hpp new file mode 100644 index 000000000..078d407a1 --- /dev/null +++ b/platform/ctr/include/utilities/sensor/gyroscope.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include <3ds.h> + +namespace love +{ + class Gyroscope : public SensorBase + { + public: + Gyroscope(); + + ~Gyroscope(); + + std::vector GetData() override; + + void SetEnabled(bool enabled); + + private: + angularRate data; + }; +} // namespace love diff --git a/platform/ctr/include/utilities/threads/threads.hpp b/platform/ctr/include/utilities/threads/threads.hpp new file mode 100644 index 000000000..89647a7fb --- /dev/null +++ b/platform/ctr/include/utilities/threads/threads.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace love +{ + using mutex = ctr::mutex; + using conditional = ctr::condition_variable; + using thread = ctr::thread; +} // namespace love diff --git a/platform/ctr/libraries/luasocket.patch b/platform/ctr/libraries/luasocket.patch new file mode 100644 index 000000000..01bbcb478 --- /dev/null +++ b/platform/ctr/libraries/luasocket.patch @@ -0,0 +1,2397 @@ +diff --git a/inet.c b/inet.c +index 138c9ab..c262cf9 100755 +--- a/inet.c ++++ b/inet.c +@@ -139,8 +139,13 @@ static int inet_global_toip(lua_State *L) + + int inet_optfamily(lua_State* L, int narg, const char* def) + { ++ #if !defined(__3DS__) + static const char* optname[] = { "unspec", "inet", "inet6", NULL }; + static int optvalue[] = { AF_UNSPEC, AF_INET, AF_INET6, 0 }; ++ #else ++ static const char* optname[] = { "unspec", "inet", NULL }; ++ static int optvalue[] = { AF_UNSPEC, AF_INET, 0 }; ++ #endif + + return optvalue[luaL_checkoption(L, narg, def, optname)]; + } +@@ -348,10 +353,12 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp) + \*-------------------------------------------------------------------------*/ + const char *inet_trycreate(p_socket ps, int family, int type, int protocol) { + const char *err = socket_strerror(socket_create(ps, family, type, protocol)); ++ #if !defined(__3DS__) + if (err == NULL && family == AF_INET6) { + int yes = 1; + setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); + } ++ #endif + return err; + } + +@@ -369,6 +376,7 @@ const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) + return socket_strerror(socket_connect(ps, (SA *) &sin, + sizeof(sin), tm)); + } ++ #if !defined(__3DS__) + case AF_INET6: { + struct sockaddr_in6 sin6; + struct in6_addr addrany = IN6ADDR_ANY_INIT; +@@ -378,6 +386,7 @@ const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) + return socket_strerror(socket_connect(ps, (SA *) &sin6, + sizeof(sin6), tm)); + } ++ #endif + } + return NULL; + } +@@ -436,7 +445,9 @@ const char *inet_tryaccept(p_socket server, int family, p_socket client, + socklen_t len; + t_sockaddr_storage addr; + switch (family) { ++ #if !defined(__3DS__) + case AF_INET6: len = sizeof(struct sockaddr_in6); break; ++ #endif + case AF_INET: len = sizeof(struct sockaddr_in); break; + default: len = sizeof(addr); break; + } +diff --git a/inet.h b/inet.h +index 5618b61..f11d5ed 100644 +--- a/inet.h ++++ b/inet.h +@@ -22,6 +22,10 @@ + #define LUASOCKET_INET_ATON + #endif + ++#ifndef INET6_ADDRSTRLEN ++#define INET6_ADDRSTRLEN INET_ADDRSTRLEN ++#endif ++ + #ifndef _WIN32 + #pragma GCC visibility push(hidden) + #endif +diff --git a/makefile b/makefile +deleted file mode 100755 +index 06f4d19..0000000 +--- a/makefile ++++ /dev/null +@@ -1,461 +0,0 @@ +-# luasocket src/makefile +-# +-# Definitions in this section can be overriden on the command line or in the +-# environment. +-# +-# These are equivalent: +-# +-# export PLAT=linux DEBUG=DEBUG LUAV=5.2 prefix=/sw +-# make +-# +-# and +-# +-# make PLAT=linux DEBUG=DEBUG LUAV=5.2 prefix=/sw +- +-# PLAT: linux macosx win32 win64 mingw +-# platform to build for +-PLAT?=linux +- +-# LUAV: 5.1 5.2 5.3 5.4 +-# lua version to build against +-LUAV?=5.1 +- +-# MYCFLAGS: to be set by user if needed +-MYCFLAGS?= +- +-# MYLDFLAGS: to be set by user if needed +-MYLDFLAGS?= +- +-# DEBUG: NODEBUG DEBUG +-# debug mode causes luasocket to collect and returns timing information useful +-# for testing and debugging luasocket itself +-DEBUG?=NODEBUG +- +-# where lua headers are found for macosx builds +-# LUAINC_macosx: +-# /opt/local/include +-LUAINC_macosx_base?=/opt/local/include +-LUAINC_macosx?=$(LUAINC_macosx_base)/lua/$(LUAV) $(LUAINC_macosx_base)/lua$(LUAV) $(LUAINC_macosx_base)/lua-$(LUAV) +- +-# FIXME default should this default to fink or to macports? +-# What happens when more than one Lua version is installed? +-LUAPREFIX_macosx?=/opt/local +-CDIR_macosx?=lib/lua/$(LUAV) +-LDIR_macosx?=share/lua/$(LUAV) +- +-# LUAINC_linux: +-# /usr/include/lua$(LUAV) +-# /usr/local/include +-# /usr/local/include/lua$(LUAV) +-# where lua headers are found for linux builds +-LUAINC_linux_base?=/usr/include +-LUAINC_linux?=$(LUAINC_linux_base)/lua/$(LUAV) $(LUAINC_linux_base)/lua$(LUAV) +-LUAPREFIX_linux?=/usr/local +-CDIR_linux?=lib/lua/$(LUAV) +-LDIR_linux?=share/lua/$(LUAV) +- +-# LUAINC_freebsd: +-# /usr/local/include/lua$(LUAV) +-# where lua headers are found for freebsd builds +-LUAINC_freebsd_base?=/usr/local/include/ +-LUAINC_freebsd?=$(LUAINC_freebsd_base)/lua/$(LUAV) $(LUAINC_freebsd_base)/lua$(LUAV) +-LUAPREFIX_freebsd?=/usr/local/ +-CDIR_freebsd?=lib/lua/$(LUAV) +-LDIR_freebsd?=share/lua/$(LUAV) +- +-# where lua headers are found for mingw builds +-# LUAINC_mingw: +-# /opt/local/include +-LUAINC_mingw_base?=/usr/include +-LUAINC_mingw?=$(LUAINC_mingw_base)/lua/$(LUAV) $(LUAINC_mingw_base)/lua$(LUAV) +-LUALIB_mingw_base?=/usr/bin +-LUALIB_mingw?=$(LUALIB_mingw_base)/lua/$(LUAV)/lua$(subst .,,$(LUAV)).dll +-LUAPREFIX_mingw?=/usr +-CDIR_mingw?=lua/$(LUAV) +-LDIR_mingw?=lua/$(LUAV)/lua +- +- +-# LUAINC_win32: +-# LUALIB_win32: +-# where lua headers and libraries are found for win32 builds +-LUAPREFIX_win32?= +-LUAINC_win32?=$(LUAPREFIX_win32)/include/lua/$(LUAV) $(LUAPREFIX_win32)/include/lua$(LUAV) +-PLATFORM_win32?=Release +-CDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32) +-LDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32)/lua +-LUALIB_win32?=$(LUAPREFIX_win32)/lib/lua/$(LUAV)/$(PLATFORM_win32) +-LUALIBNAME_win32?=lua$(subst .,,$(LUAV)).lib +- +-# LUAINC_win64: +-# LUALIB_win64: +-# where lua headers and libraries are found for win64 builds +-LUAPREFIX_win64?= +-LUAINC_win64?=$(LUAPREFIX_win64)/include/lua/$(LUAV) $(LUAPREFIX_win64)/include/lua$(LUAV) +-PLATFORM_win64?=x64/Release +-CDIR_win64?=bin/lua/$(LUAV)/$(PLATFORM_win64) +-LDIR_win64?=bin/lua/$(LUAV)/$(PLATFORM_win64)/lua +-LUALIB_win64?=$(LUAPREFIX_win64)/lib/lua/$(LUAV)/$(PLATFORM_win64) +-LUALIBNAME_win64?=lua$(subst .,,$(LUAV)).lib +- +- +-# LUAINC_solaris: +-LUAINC_solaris_base?=/usr/include +-LUAINC_solaris?=$(LUAINC_solaris_base)/lua/$(LUAV) $(LUAINC_solaris_base)/lua$(LUAV) +-LUAPREFIX_solaris?=/usr/local +-CDIR_solaris?=lib/lua/$(LUAV) +-LDIR_solaris?=share/lua/$(LUAV) +- +-# prefix: /usr/local /usr /opt/local /sw +-# the top of the default install tree +-prefix?=$(LUAPREFIX_$(PLAT)) +- +-CDIR?=$(CDIR_$(PLAT)) +-LDIR?=$(LDIR_$(PLAT)) +- +-# DESTDIR: (no default) +-# used by package managers to install into a temporary destination +-DESTDIR?= +- +-#------ +-# Definitions below can be overridden on the make command line, but +-# shouldn't have to be. +- +- +-#------ +-# Install directories +-# +- +-INSTALL_DIR=install -d +-INSTALL_DATA=install -m644 +-INSTALL_EXEC=install +-INSTALL_TOP=$(DESTDIR)$(prefix) +- +-INSTALL_TOP_LDIR=$(INSTALL_TOP)/$(LDIR) +-INSTALL_TOP_CDIR=$(INSTALL_TOP)/$(CDIR) +- +-INSTALL_SOCKET_LDIR=$(INSTALL_TOP_LDIR)/socket +-INSTALL_SOCKET_CDIR=$(INSTALL_TOP_CDIR)/socket +-INSTALL_MIME_LDIR=$(INSTALL_TOP_LDIR)/mime +-INSTALL_MIME_CDIR=$(INSTALL_TOP_CDIR)/mime +- +-print: +- @echo PLAT=$(PLAT) +- @echo LUAV=$(LUAV) +- @echo DEBUG=$(DEBUG) +- @echo prefix=$(prefix) +- @echo LUAINC_$(PLAT)=$(LUAINC_$(PLAT)) +- @echo LUALIB_$(PLAT)=$(LUALIB_$(PLAT)) +- @echo INSTALL_TOP_CDIR=$(INSTALL_TOP_CDIR) +- @echo INSTALL_TOP_LDIR=$(INSTALL_TOP_LDIR) +- @echo CFLAGS=$(CFLAGS) +- @echo LDFLAGS=$(LDFLAGS) +- +-#------ +-# Supported platforms +-# +-PLATS= macosx linux win32 win64 mingw solaris +- +-#------ +-# Compiler and linker settings +-# for Mac OS X +-SO_macosx=so +-O_macosx=o +-CC_macosx=gcc +-DEF_macosx= -DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN +-CFLAGS_macosx=$(LUAINC:%=-I%) $(DEF) -Wall -O2 -fno-common +-LDFLAGS_macosx= -bundle -undefined dynamic_lookup -o +-LD_macosx=gcc +-SOCKET_macosx=usocket.o +- +-#------ +-# Compiler and linker settings +-# for Linux +-SO_linux=so +-O_linux=o +-CC_linux=gcc +-DEF_linux=-DLUASOCKET_$(DEBUG) +-CFLAGS_linux=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ +- -Wimplicit -O2 -ggdb3 -fpic +-LDFLAGS_linux=-O -shared -fpic -o +-LD_linux=gcc +-SOCKET_linux=usocket.o +- +-#------ +-# Compiler and linker settings +-# for FreeBSD +-SO_freebsd=so +-O_freebsd=o +-CC_freebsd=gcc +-DEF_freebsd=-DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN +-CFLAGS_freebsd=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ +- -Wimplicit -O2 -ggdb3 -fpic +-LDFLAGS_freebsd=-O -shared -fpic -o +-LD_freebsd=gcc +-SOCKET_freebsd=usocket.o +- +-#------ +-# Compiler and linker settings +-# for Solaris +-SO_solaris=so +-O_solaris=o +-CC_solaris=gcc +-DEF_solaris=-DLUASOCKET_$(DEBUG) +-CFLAGS_solaris=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ +- -Wimplicit -O2 -ggdb3 -fpic +-LDFLAGS_solaris=-lnsl -lsocket -lresolv -O -shared -fpic -o +-LD_solaris=gcc +-SOCKET_solaris=usocket.o +- +-#------ +-# Compiler and linker settings +-# for MingW +-SO_mingw=dll +-O_mingw=o +-CC_mingw=gcc +-DEF_mingw= -DLUASOCKET_$(DEBUG) \ +- -DWINVER=0x0501 +-CFLAGS_mingw=$(LUAINC:%=-I%) $(DEF) -Wall -O2 -fno-common +-LDFLAGS_mingw= $(LUALIB) -shared -Wl,-s -lws2_32 -o +-LD_mingw=gcc +-SOCKET_mingw=wsocket.o +- +- +-#------ +-# Compiler and linker settings +-# for Win32 +-SO_win32=dll +-O_win32=obj +-CC_win32=cl +-DEF_win32= //D "WIN32" //D "NDEBUG" //D "_WINDOWS" //D "_USRDLL" \ +- //D "_CRT_SECURE_NO_WARNINGS" \ +- //D "_WINDLL" \ +- //D "LUASOCKET_$(DEBUG)" +-CFLAGS_win32=$(LUAINC:%=//I "%") $(DEF) //O2 //Ot //MD //W3 //nologo +-LDFLAGS_win32= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \ +- //MANIFEST //MANIFESTFILE:"intermediate.manifest" \ +- /MANIFESTUAC:"level='asInvoker' uiAccess='false'" \ +- //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \ +- //MACHINE:X86 /LIBPATH:"$(LUALIB)" \ +- $(LUALIBNAME_win32) ws2_32.lib //OUT: +- +-LD_win32=cl +-SOCKET_win32=wsocket.obj +- +-#------ +-# Compiler and linker settings +-# for Win64 +-SO_win64=dll +-O_win64=obj +-CC_win64=cl +-DEF_win64= //D "WIN32" //D "NDEBUG" //D "_WINDOWS" //D "_USRDLL" \ +- //D "_CRT_SECURE_NO_WARNINGS" \ +- //D "_WINDLL" \ +- //D "LUASOCKET_$(DEBUG)" +-CFLAGS_win64=$(LUAINC:%=//I "%") $(DEF) //O2 //Ot //MD //W3 //nologo +-LDFLAGS_win64= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \ +- //MANIFEST //MANIFESTFILE:"intermediate.manifest" \ +- /MANIFESTUAC:"level='asInvoker' uiAccess='false'" \ +- //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \ +- /LIBPATH:"$(LUALIB)" \ +- $(LUALIBNAME_win64) ws2_32.lib //OUT: +- +-LD_win64=cl +-SOCKET_win64=wsocket.obj +- +-.SUFFIXES: .obj +- +-.c.obj: +- $(CC) $(CFLAGS) //Fo"$@" //c $< +- +-#------ +-# Output file names +-# +-SO=$(SO_$(PLAT)) +-O=$(O_$(PLAT)) +-SOCKET_V=3.0.0 +-MIME_V=1.0.3 +-SOCKET_SO=socket-$(SOCKET_V).$(SO) +-MIME_SO=mime-$(MIME_V).$(SO) +-UNIX_SO=unix.$(SO) +-SERIAL_SO=serial.$(SO) +-SOCKET=$(SOCKET_$(PLAT)) +- +-#------ +-# Settings selected for platform +-# +-CC=$(CC_$(PLAT)) +-DEF=$(DEF_$(PLAT)) +-CFLAGS=$(MYCFLAGS) $(CFLAGS_$(PLAT)) +-LDFLAGS=$(MYLDFLAGS) $(LDFLAGS_$(PLAT)) +-LD=$(LD_$(PLAT)) +-LUAINC= $(LUAINC_$(PLAT)) +-LUALIB= $(LUALIB_$(PLAT)) +- +-#------ +-# Modules belonging to socket-core +-# +-SOCKET_OBJS= \ +- luasocket.$(O) \ +- timeout.$(O) \ +- buffer.$(O) \ +- io.$(O) \ +- auxiliar.$(O) \ +- compat.$(O) \ +- options.$(O) \ +- inet.$(O) \ +- $(SOCKET) \ +- except.$(O) \ +- select.$(O) \ +- tcp.$(O) \ +- udp.$(O) +- +-#------ +-# Modules belonging mime-core +-# +-MIME_OBJS= \ +- mime.$(O) \ +- compat.$(O) +- +-#------ +-# Modules belonging unix (local domain sockets) +-# +-UNIX_OBJS=\ +- buffer.$(O) \ +- auxiliar.$(O) \ +- options.$(O) \ +- timeout.$(O) \ +- io.$(O) \ +- usocket.$(O) \ +- unixstream.$(O) \ +- unixdgram.$(O) \ +- compat.$(O) \ +- unix.$(O) +- +-#------ +-# Modules belonging to serial (device streams) +-# +-SERIAL_OBJS=\ +- buffer.$(O) \ +- compat.$(O) \ +- auxiliar.$(O) \ +- options.$(O) \ +- timeout.$(O) \ +- io.$(O) \ +- usocket.$(O) \ +- serial.$(O) +- +-#------ +-# Files to install +-# +-TO_SOCKET_LDIR= \ +- http.lua \ +- url.lua \ +- tp.lua \ +- ftp.lua \ +- headers.lua \ +- smtp.lua +- +-TO_TOP_LDIR= \ +- ltn12.lua \ +- socket.lua \ +- mime.lua +- +-#------ +-# Targets +-# +-default: $(PLAT) +- +- +-freebsd: +- $(MAKE) all-unix PLAT=freebsd +- +-macosx: +- $(MAKE) all-unix PLAT=macosx +- +-win32: +- $(MAKE) all PLAT=win32 +- +-win64: +- $(MAKE) all PLAT=win64 +- +-linux: +- $(MAKE) all-unix PLAT=linux +- +-mingw: +- $(MAKE) all PLAT=mingw +- +-solaris: +- $(MAKE) all-unix PLAT=solaris +- +-none: +- @echo "Please run" +- @echo " make PLATFORM" +- @echo "where PLATFORM is one of these:" +- @echo " $(PLATS)" +- +-all: $(SOCKET_SO) $(MIME_SO) +- +-$(SOCKET_SO): $(SOCKET_OBJS) +- $(LD) $(SOCKET_OBJS) $(LDFLAGS)$@ +- +-$(MIME_SO): $(MIME_OBJS) +- $(LD) $(MIME_OBJS) $(LDFLAGS)$@ +- +-all-unix: all $(UNIX_SO) $(SERIAL_SO) +- +-$(UNIX_SO): $(UNIX_OBJS) +- $(LD) $(UNIX_OBJS) $(LDFLAGS)$@ +- +-$(SERIAL_SO): $(SERIAL_OBJS) +- $(LD) $(SERIAL_OBJS) $(LDFLAGS)$@ +- +-install: +- $(INSTALL_DIR) $(INSTALL_TOP_LDIR) +- $(INSTALL_DATA) $(TO_TOP_LDIR) $(INSTALL_TOP_LDIR) +- $(INSTALL_DIR) $(INSTALL_SOCKET_LDIR) +- $(INSTALL_DATA) $(TO_SOCKET_LDIR) $(INSTALL_SOCKET_LDIR) +- $(INSTALL_DIR) $(INSTALL_SOCKET_CDIR) +- $(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET_CDIR)/core.$(SO) +- $(INSTALL_DIR) $(INSTALL_MIME_CDIR) +- $(INSTALL_EXEC) $(MIME_SO) $(INSTALL_MIME_CDIR)/core.$(SO) +- +-install-unix: install +- $(INSTALL_EXEC) $(UNIX_SO) $(INSTALL_SOCKET_CDIR)/$(UNIX_SO) +- $(INSTALL_EXEC) $(SERIAL_SO) $(INSTALL_SOCKET_CDIR)/$(SERIAL_SO) +- +-local: +- $(MAKE) install INSTALL_TOP_CDIR=.. INSTALL_TOP_LDIR=.. +- +-clean: +- rm -f $(SOCKET_SO) $(SOCKET_OBJS) $(SERIAL_OBJS) +- rm -f $(MIME_SO) $(UNIX_SO) $(SERIAL_SO) $(MIME_OBJS) $(UNIX_OBJS) +- +-.PHONY: all $(PLATS) default clean echo none +- +-#------ +-# List of dependencies +-# +-compat.$(O): compat.c compat.h +-auxiliar.$(O): auxiliar.c auxiliar.h +-buffer.$(O): buffer.c buffer.h io.h timeout.h +-except.$(O): except.c except.h +-inet.$(O): inet.c inet.h socket.h io.h timeout.h usocket.h +-io.$(O): io.c io.h timeout.h +-luasocket.$(O): luasocket.c luasocket.h auxiliar.h except.h \ +- timeout.h buffer.h io.h inet.h socket.h usocket.h tcp.h \ +- udp.h select.h +-mime.$(O): mime.c mime.h +-options.$(O): options.c auxiliar.h options.h socket.h io.h \ +- timeout.h usocket.h inet.h +-select.$(O): select.c socket.h io.h timeout.h usocket.h select.h +-serial.$(O): serial.c auxiliar.h socket.h io.h timeout.h usocket.h \ +- options.h unix.h buffer.h +-tcp.$(O): tcp.c auxiliar.h socket.h io.h timeout.h usocket.h \ +- inet.h options.h tcp.h buffer.h +-timeout.$(O): timeout.c auxiliar.h timeout.h +-udp.$(O): udp.c auxiliar.h socket.h io.h timeout.h usocket.h \ +- inet.h options.h udp.h +-unix.$(O): unix.c auxiliar.h socket.h io.h timeout.h usocket.h \ +- options.h unix.h buffer.h +-usocket.$(O): usocket.c socket.h io.h timeout.h usocket.h +-wsocket.$(O): wsocket.c socket.h io.h timeout.h usocket.h +diff --git a/options.c b/options.c +index 3280c51..9a45e6f 100644 +--- a/options.c ++++ b/options.c +@@ -22,6 +22,22 @@ static int opt_set(lua_State *L, p_socket ps, int level, int name, + static int opt_get(lua_State *L, p_socket ps, int level, int name, + void *val, int* len); + ++static int set_opt_error(lua_State* L) ++{ ++ lua_pushnil(L); ++ lua_pushstring(L, "setsockopt failed: not supported"); ++ ++ return 2; ++} ++ ++static int get_opt_error(lua_State* L) ++{ ++ lua_pushnil(L); ++ lua_pushstring(L, "getsockopt failed: not supported"); ++ ++ return 2; ++} ++ + /*=========================================================================*\ + * Exported functions + \*=========================================================================*/ +@@ -136,6 +152,7 @@ int opt_set_tcp_keepintvl(lua_State *L, p_socket ps) + #endif + + /*------------------------------------------------------*/ ++#if !defined(__3DS__) + int opt_set_keepalive(lua_State *L, p_socket ps) + { + return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); +@@ -167,6 +184,16 @@ int opt_get_broadcast(lua_State *L, p_socket ps) + { + return opt_getboolean(L, ps, SOL_SOCKET, SO_BROADCAST); + } ++#else ++int opt_set_keepalive(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_keepalive(lua_State *L, p_socket ps) { return get_opt_error(L); } ++ ++int opt_set_dontroute(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_dontroute(lua_State *L, p_socket ps) { return get_opt_error(L); } ++ ++int opt_set_broadcast(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_broadcast(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ + int opt_set_recv_buf_size(lua_State *L, p_socket ps) +@@ -216,6 +243,7 @@ int opt_set_tcp_defer_accept(lua_State *L, p_socket ps) + #endif + + /*------------------------------------------------------*/ ++#if !defined(__3DS__) + int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) + { + return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); +@@ -236,6 +264,13 @@ int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps) + { + return opt_getint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); + } ++#else ++int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps) { return get_opt_error(L); } ++ ++int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ + int opt_set_ip_multicast_loop(lua_State *L, p_socket ps) +@@ -249,6 +284,7 @@ int opt_get_ip_multicast_loop(lua_State *L, p_socket ps) + } + + /*------------------------------------------------------*/ ++#if !defined(__3DS__) + int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps) + { + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +@@ -258,6 +294,10 @@ int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps) + { + return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); + } ++#else ++int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ + int opt_set_linger(lua_State *L, p_socket ps) +@@ -299,6 +339,7 @@ int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps) + } + + /*------------------------------------------------------*/ ++#if !defined(__3DS__) + int opt_set_ip_multicast_if(lua_State *L, p_socket ps) + { + const char *address = luaL_checkstring(L, 3); /* obj, name, ip */ +@@ -322,6 +363,10 @@ int opt_get_ip_multicast_if(lua_State *L, p_socket ps) + lua_pushstring(L, inet_ntoa(val)); + return 1; + } ++#else ++int opt_set_ip_multicast_if(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip_multicast_if(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ + int opt_set_ip_add_membership(lua_State *L, p_socket ps) +@@ -335,6 +380,7 @@ int opt_set_ip_drop_membersip(lua_State *L, p_socket ps) + } + + /*------------------------------------------------------*/ ++#if !defined(__3DS__) + int opt_set_ip6_add_membership(lua_State *L, p_socket ps) + { + return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP); +@@ -355,7 +401,13 @@ int opt_set_ip6_v6only(lua_State *L, p_socket ps) + { + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); + } ++#else ++int opt_set_ip6_add_membership(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps) { return set_opt_error(L); } + ++int opt_get_ip6_v6only(lua_State *L, p_socket ps) { return get_opt_error(L); } ++int opt_set_ip6_v6only(lua_State *L, p_socket ps) { return set_opt_error(L); } ++#endif + /*------------------------------------------------------*/ + int opt_get_error(lua_State *L, p_socket ps) + { +@@ -394,6 +446,7 @@ static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); + } + ++#if !defined(__3DS__) + static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) + { + struct ipv6_mreq val; /* obj, opt-name, table */ +@@ -419,6 +472,12 @@ static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) + } + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); + } ++#else ++static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) ++{ ++ return set_opt_error(L); ++} ++#endif + + static + int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len) +diff --git a/serial.c b/serial.c +deleted file mode 100644 +index 21485d3..0000000 +--- a/serial.c ++++ /dev/null +@@ -1,171 +0,0 @@ +-/*=========================================================================*\ +-* Serial stream +-* LuaSocket toolkit +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "auxiliar.h" +-#include "socket.h" +-#include "options.h" +-#include "unix.h" +- +-#include +-#include +- +-/* +-Reuses userdata definition from unix.h, since it is useful for all +-stream-like objects. +- +-If we stored the serial path for use in error messages or userdata +-printing, we might need our own userdata definition. +- +-Group usage is semi-inherited from unix.c, but unnecessary since we +-have only one object type. +-*/ +- +-/*=========================================================================*\ +-* Internal function prototypes +-\*=========================================================================*/ +-static int global_create(lua_State *L); +-static int meth_send(lua_State *L); +-static int meth_receive(lua_State *L); +-static int meth_close(lua_State *L); +-static int meth_settimeout(lua_State *L); +-static int meth_getfd(lua_State *L); +-static int meth_setfd(lua_State *L); +-static int meth_dirty(lua_State *L); +-static int meth_getstats(lua_State *L); +-static int meth_setstats(lua_State *L); +- +-/* serial object methods */ +-static luaL_Reg serial_methods[] = { +- {"__gc", meth_close}, +- {"__tostring", auxiliar_tostring}, +- {"close", meth_close}, +- {"dirty", meth_dirty}, +- {"getfd", meth_getfd}, +- {"getstats", meth_getstats}, +- {"setstats", meth_setstats}, +- {"receive", meth_receive}, +- {"send", meth_send}, +- {"setfd", meth_setfd}, +- {"settimeout", meth_settimeout}, +- {NULL, NULL} +-}; +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-LUASOCKET_API int luaopen_socket_serial(lua_State *L) { +- /* create classes */ +- auxiliar_newclass(L, "serial{client}", serial_methods); +- /* create class groups */ +- auxiliar_add2group(L, "serial{client}", "serial{any}"); +- lua_pushcfunction(L, global_create); +- return 1; +-} +- +-/*=========================================================================*\ +-* Lua methods +-\*=========================================================================*/ +-/*-------------------------------------------------------------------------*\ +-* Just call buffered IO methods +-\*-------------------------------------------------------------------------*/ +-static int meth_send(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_send(L, &un->buf); +-} +- +-static int meth_receive(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_receive(L, &un->buf); +-} +- +-static int meth_getstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_getstats(L, &un->buf); +-} +- +-static int meth_setstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_setstats(L, &un->buf); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Select support methods +-\*-------------------------------------------------------------------------*/ +-static int meth_getfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- lua_pushnumber(L, (int) un->sock); +- return 1; +-} +- +-/* this is very dangerous, but can be handy for those that are brave enough */ +-static int meth_setfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- un->sock = (t_socket) luaL_checknumber(L, 2); +- return 0; +-} +- +-static int meth_dirty(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- lua_pushboolean(L, !buffer_isempty(&un->buf)); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Closes socket used by object +-\*-------------------------------------------------------------------------*/ +-static int meth_close(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- socket_destroy(&un->sock); +- lua_pushnumber(L, 1); +- return 1; +-} +- +- +-/*-------------------------------------------------------------------------*\ +-* Just call tm methods +-\*-------------------------------------------------------------------------*/ +-static int meth_settimeout(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- return timeout_meth_settimeout(L, &un->tm); +-} +- +-/*=========================================================================*\ +-* Library functions +-\*=========================================================================*/ +- +- +-/*-------------------------------------------------------------------------*\ +-* Creates a serial object +-\*-------------------------------------------------------------------------*/ +-static int global_create(lua_State *L) { +- const char* path = luaL_checkstring(L, 1); +- +- /* allocate unix object */ +- p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); +- +- /* open serial device */ +- t_socket sock = open(path, O_NOCTTY|O_RDWR); +- +- /*printf("open %s on %d\n", path, sock);*/ +- +- if (sock < 0) { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(errno)); +- lua_pushnumber(L, errno); +- return 3; +- } +- /* set its type as client object */ +- auxiliar_setclass(L, "serial{client}", -1); +- /* initialize remaining structure fields */ +- socket_setnonblocking(&sock); +- un->sock = sock; +- io_init(&un->io, (p_send) socket_write, (p_recv) socket_read, +- (p_error) socket_ioerror, &un->sock); +- timeout_init(&un->tm, -1, -1); +- buffer_init(&un->buf, &un->io, &un->tm); +- return 1; +-} +diff --git a/tcp.c b/tcp.c +index e84db84..9dfa27e 100644 +--- a/tcp.c ++++ b/tcp.c +@@ -427,9 +427,18 @@ static int global_create4(lua_State *L) { + return tcp_create(L, AF_INET); + } + ++#if !defined(__3DS__) + static int global_create6(lua_State *L) { + return tcp_create(L, AF_INET6); + } ++#else ++static int global_create6(lua_State* L) { ++ lua_pushnil(L); ++ lua_pushstring(L, "Setting local interface error: not supported"); ++ ++ return 2; ++} ++#endif + + static int global_connect(lua_State *L) { + const char *remoteaddr = luaL_checkstring(L, 1); +diff --git a/udp.c b/udp.c +index 712ad50..a3c7a5f 100755 +--- a/udp.c ++++ b/udp.c +@@ -483,6 +483,15 @@ static int global_create4(lua_State *L) { + return udp_create(L, AF_INET); + } + ++#if !defined(__3DS__) + static int global_create6(lua_State *L) { + return udp_create(L, AF_INET6); + } ++#else ++static int global_create6(lua_State *L) { ++ lua_pushnil(L); ++ lua_pushstring(L, "Setting local interface error: not supported"); ++ ++ return 2; ++} ++#endif +diff --git a/unix.c b/unix.c +deleted file mode 100644 +index 268d8b2..0000000 +--- a/unix.c ++++ /dev/null +@@ -1,69 +0,0 @@ +-/*=========================================================================*\ +-* Unix domain socket +-* LuaSocket toolkit +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "unixstream.h" +-#include "unixdgram.h" +- +-/*-------------------------------------------------------------------------*\ +-* Modules and functions +-\*-------------------------------------------------------------------------*/ +-static const luaL_Reg mod[] = { +- {"stream", unixstream_open}, +- {"dgram", unixdgram_open}, +- {NULL, NULL} +-}; +- +-static void add_alias(lua_State *L, int index, const char *name, const char *target) +-{ +- lua_getfield(L, index, target); +- lua_setfield(L, index, name); +-} +- +-static int compat_socket_unix_call(lua_State *L) +-{ +- /* Look up socket.unix.stream in the socket.unix table (which is the first +- * argument). */ +- lua_getfield(L, 1, "stream"); +- +- /* Replace the stack entry for the socket.unix table with the +- * socket.unix.stream function. */ +- lua_replace(L, 1); +- +- /* Call socket.unix.stream, passing along any arguments. */ +- int n = lua_gettop(L); +- lua_call(L, n-1, LUA_MULTRET); +- +- /* Pass along the return values from socket.unix.stream. */ +- n = lua_gettop(L); +- return n; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-LUASOCKET_API int luaopen_socket_unix(lua_State *L) +-{ +- int i; +- lua_newtable(L); +- int socket_unix_table = lua_gettop(L); +- +- for (i = 0; mod[i].name; i++) +- mod[i].func(L); +- +- /* Add backwards compatibility aliases "tcp" and "udp" for the "stream" and +- * "dgram" functions. */ +- add_alias(L, socket_unix_table, "tcp", "stream"); +- add_alias(L, socket_unix_table, "udp", "dgram"); +- +- /* Add a backwards compatibility function and a metatable setup to call it +- * for the old socket.unix() interface. */ +- lua_pushcfunction(L, compat_socket_unix_call); +- lua_setfield(L, socket_unix_table, "__call"); +- lua_pushvalue(L, socket_unix_table); +- lua_setmetatable(L, socket_unix_table); +- +- return 1; +-} +diff --git a/unix.h b/unix.h +deleted file mode 100644 +index c203561..0000000 +--- a/unix.h ++++ /dev/null +@@ -1,26 +0,0 @@ +-#ifndef UNIX_H +-#define UNIX_H +-/*=========================================================================*\ +-* Unix domain object +-* LuaSocket toolkit +-* +-* This module is just an example of how to extend LuaSocket with a new +-* domain. +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "buffer.h" +-#include "timeout.h" +-#include "socket.h" +- +-typedef struct t_unix_ { +- t_socket sock; +- t_io io; +- t_buffer buf; +- t_timeout tm; +-} t_unix; +-typedef t_unix *p_unix; +- +-LUASOCKET_API int luaopen_socket_unix(lua_State *L); +- +-#endif /* UNIX_H */ +diff --git a/unixdgram.c b/unixdgram.c +deleted file mode 100644 +index 69093d7..0000000 +--- a/unixdgram.c ++++ /dev/null +@@ -1,405 +0,0 @@ +-/*=========================================================================*\ +-* Unix domain socket dgram submodule +-* LuaSocket toolkit +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "auxiliar.h" +-#include "socket.h" +-#include "options.h" +-#include "unix.h" +- +-#include +-#include +- +-#include +- +-#define UNIXDGRAM_DATAGRAMSIZE 8192 +- +-/* provide a SUN_LEN macro if sys/un.h doesn't (e.g. Android) */ +-#ifndef SUN_LEN +-#define SUN_LEN(ptr) \ +- ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ +- + strlen ((ptr)->sun_path)) +-#endif +- +-/*=========================================================================*\ +-* Internal function prototypes +-\*=========================================================================*/ +-static int global_create(lua_State *L); +-static int meth_connect(lua_State *L); +-static int meth_bind(lua_State *L); +-static int meth_send(lua_State *L); +-static int meth_receive(lua_State *L); +-static int meth_close(lua_State *L); +-static int meth_setoption(lua_State *L); +-static int meth_settimeout(lua_State *L); +-static int meth_gettimeout(lua_State *L); +-static int meth_getfd(lua_State *L); +-static int meth_setfd(lua_State *L); +-static int meth_dirty(lua_State *L); +-static int meth_receivefrom(lua_State *L); +-static int meth_sendto(lua_State *L); +-static int meth_getsockname(lua_State *L); +- +-static const char *unixdgram_tryconnect(p_unix un, const char *path); +-static const char *unixdgram_trybind(p_unix un, const char *path); +- +-/* unixdgram object methods */ +-static luaL_Reg unixdgram_methods[] = { +- {"__gc", meth_close}, +- {"__tostring", auxiliar_tostring}, +- {"bind", meth_bind}, +- {"close", meth_close}, +- {"connect", meth_connect}, +- {"dirty", meth_dirty}, +- {"getfd", meth_getfd}, +- {"send", meth_send}, +- {"sendto", meth_sendto}, +- {"receive", meth_receive}, +- {"receivefrom", meth_receivefrom}, +- {"setfd", meth_setfd}, +- {"setoption", meth_setoption}, +- {"setpeername", meth_connect}, +- {"setsockname", meth_bind}, +- {"getsockname", meth_getsockname}, +- {"settimeout", meth_settimeout}, +- {"gettimeout", meth_gettimeout}, +- {NULL, NULL} +-}; +- +-/* socket option handlers */ +-static t_opt optset[] = { +- {"reuseaddr", opt_set_reuseaddr}, +- {NULL, NULL} +-}; +- +-/* functions in library namespace */ +-static luaL_Reg func[] = { +- {"dgram", global_create}, +- {NULL, NULL} +-}; +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-int unixdgram_open(lua_State *L) +-{ +- /* create classes */ +- auxiliar_newclass(L, "unixdgram{connected}", unixdgram_methods); +- auxiliar_newclass(L, "unixdgram{unconnected}", unixdgram_methods); +- /* create class groups */ +- auxiliar_add2group(L, "unixdgram{connected}", "unixdgram{any}"); +- auxiliar_add2group(L, "unixdgram{unconnected}", "unixdgram{any}"); +- auxiliar_add2group(L, "unixdgram{connected}", "select{able}"); +- auxiliar_add2group(L, "unixdgram{unconnected}", "select{able}"); +- +- luaL_setfuncs(L, func, 0); +- return 0; +-} +- +-/*=========================================================================*\ +-* Lua methods +-\*=========================================================================*/ +-static const char *unixdgram_strerror(int err) +-{ +- /* a 'closed' error on an unconnected means the target address was not +- * accepted by the transport layer */ +- if (err == IO_CLOSED) return "refused"; +- else return socket_strerror(err); +-} +- +-static int meth_send(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{connected}", 1); +- p_timeout tm = &un->tm; +- size_t count, sent = 0; +- int err; +- const char *data = luaL_checklstring(L, 2, &count); +- timeout_markstart(tm); +- err = socket_send(&un->sock, data, count, &sent, tm); +- if (err != IO_DONE) { +- lua_pushnil(L); +- lua_pushstring(L, unixdgram_strerror(err)); +- return 2; +- } +- lua_pushnumber(L, (lua_Number) sent); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Send data through unconnected unixdgram socket +-\*-------------------------------------------------------------------------*/ +-static int meth_sendto(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); +- size_t count, sent = 0; +- const char *data = luaL_checklstring(L, 2, &count); +- const char *path = luaL_checkstring(L, 3); +- p_timeout tm = &un->tm; +- int err; +- struct sockaddr_un remote; +- size_t len = strlen(path); +- +- if (len >= sizeof(remote.sun_path)) { +- lua_pushnil(L); +- lua_pushstring(L, "path too long"); +- return 2; +- } +- +- memset(&remote, 0, sizeof(remote)); +- strcpy(remote.sun_path, path); +- remote.sun_family = AF_UNIX; +- timeout_markstart(tm); +-#ifdef UNIX_HAS_SUN_LEN +- remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) +- + len + 1; +- err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, remote.sun_len, tm); +-#else +- err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, +- sizeof(remote.sun_family) + len, tm); +-#endif +- if (err != IO_DONE) { +- lua_pushnil(L); +- lua_pushstring(L, unixdgram_strerror(err)); +- return 2; +- } +- lua_pushnumber(L, (lua_Number) sent); +- return 1; +-} +- +-static int meth_receive(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- char buf[UNIXDGRAM_DATAGRAMSIZE]; +- size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); +- char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; +- int err; +- p_timeout tm = &un->tm; +- timeout_markstart(tm); +- if (!dgram) { +- lua_pushnil(L); +- lua_pushliteral(L, "out of memory"); +- return 2; +- } +- err = socket_recv(&un->sock, dgram, wanted, &got, tm); +- /* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */ +- if (err != IO_DONE && err != IO_CLOSED) { +- lua_pushnil(L); +- lua_pushstring(L, unixdgram_strerror(err)); +- if (wanted > sizeof(buf)) free(dgram); +- return 2; +- } +- lua_pushlstring(L, dgram, got); +- if (wanted > sizeof(buf)) free(dgram); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Receives data and sender from a DGRAM socket +-\*-------------------------------------------------------------------------*/ +-static int meth_receivefrom(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); +- char buf[UNIXDGRAM_DATAGRAMSIZE]; +- size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); +- char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; +- struct sockaddr_un addr; +- socklen_t addr_len = sizeof(addr); +- int err; +- p_timeout tm = &un->tm; +- timeout_markstart(tm); +- if (!dgram) { +- lua_pushnil(L); +- lua_pushliteral(L, "out of memory"); +- return 2; +- } +- addr.sun_path[0] = '\0'; +- err = socket_recvfrom(&un->sock, dgram, wanted, &got, (SA *) &addr, +- &addr_len, tm); +- /* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */ +- if (err != IO_DONE && err != IO_CLOSED) { +- lua_pushnil(L); +- lua_pushstring(L, unixdgram_strerror(err)); +- if (wanted > sizeof(buf)) free(dgram); +- return 2; +- } +- +- lua_pushlstring(L, dgram, got); +- /* the path may be empty, when client send without bind */ +- lua_pushstring(L, addr.sun_path); +- if (wanted > sizeof(buf)) free(dgram); +- return 2; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Just call option handler +-\*-------------------------------------------------------------------------*/ +-static int meth_setoption(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- return opt_meth_setoption(L, optset, &un->sock); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Select support methods +-\*-------------------------------------------------------------------------*/ +-static int meth_getfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- lua_pushnumber(L, (int) un->sock); +- return 1; +-} +- +-/* this is very dangerous, but can be handy for those that are brave enough */ +-static int meth_setfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- un->sock = (t_socket) luaL_checknumber(L, 2); +- return 0; +-} +- +-static int meth_dirty(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- (void) un; +- lua_pushboolean(L, 0); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Binds an object to an address +-\*-------------------------------------------------------------------------*/ +-static const char *unixdgram_trybind(p_unix un, const char *path) { +- struct sockaddr_un local; +- size_t len = strlen(path); +- if (len >= sizeof(local.sun_path)) return "path too long"; +- memset(&local, 0, sizeof(local)); +- strcpy(local.sun_path, path); +- local.sun_family = AF_UNIX; +- size_t addrlen = SUN_LEN(&local); +-#ifdef UNIX_HAS_SUN_LEN +- local.sun_len = addrlen + 1; +-#endif +- int err = socket_bind(&un->sock, (SA *) &local, addrlen); +- if (err != IO_DONE) socket_destroy(&un->sock); +- return socket_strerror(err); +-} +- +-static int meth_bind(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); +- const char *path = luaL_checkstring(L, 2); +- const char *err = unixdgram_trybind(un, path); +- if (err) { +- lua_pushnil(L); +- lua_pushstring(L, err); +- return 2; +- } +- lua_pushnumber(L, 1); +- return 1; +-} +- +-static int meth_getsockname(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- struct sockaddr_un peer = {0}; +- socklen_t peer_len = sizeof(peer); +- +- if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(errno)); +- return 2; +- } +- +- lua_pushstring(L, peer.sun_path); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Turns a master unixdgram object into a client object. +-\*-------------------------------------------------------------------------*/ +-static const char *unixdgram_tryconnect(p_unix un, const char *path) +-{ +- struct sockaddr_un remote; +- size_t len = strlen(path); +- if (len >= sizeof(remote.sun_path)) return "path too long"; +- memset(&remote, 0, sizeof(remote)); +- strcpy(remote.sun_path, path); +- remote.sun_family = AF_UNIX; +- timeout_markstart(&un->tm); +- size_t addrlen = SUN_LEN(&remote); +-#ifdef UNIX_HAS_SUN_LEN +- remote.sun_len = addrlen + 1; +-#endif +- int err = socket_connect(&un->sock, (SA *) &remote, addrlen, &un->tm); +- if (err != IO_DONE) socket_destroy(&un->sock); +- return socket_strerror(err); +-} +- +-static int meth_connect(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- const char *path = luaL_checkstring(L, 2); +- const char *err = unixdgram_tryconnect(un, path); +- if (err) { +- lua_pushnil(L); +- lua_pushstring(L, err); +- return 2; +- } +- /* turn unconnected object into a connected object */ +- auxiliar_setclass(L, "unixdgram{connected}", 1); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Closes socket used by object +-\*-------------------------------------------------------------------------*/ +-static int meth_close(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- socket_destroy(&un->sock); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Just call tm methods +-\*-------------------------------------------------------------------------*/ +-static int meth_settimeout(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- return timeout_meth_settimeout(L, &un->tm); +-} +- +-static int meth_gettimeout(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- return timeout_meth_gettimeout(L, &un->tm); +-} +- +-/*=========================================================================*\ +-* Library functions +-\*=========================================================================*/ +-/*-------------------------------------------------------------------------*\ +-* Creates a master unixdgram object +-\*-------------------------------------------------------------------------*/ +-static int global_create(lua_State *L) +-{ +- t_socket sock; +- int err = socket_create(&sock, AF_UNIX, SOCK_DGRAM, 0); +- /* try to allocate a system socket */ +- if (err == IO_DONE) { +- /* allocate unixdgram object */ +- p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); +- /* set its type as master object */ +- auxiliar_setclass(L, "unixdgram{unconnected}", -1); +- /* initialize remaining structure fields */ +- socket_setnonblocking(&sock); +- un->sock = sock; +- io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, +- (p_error) socket_ioerror, &un->sock); +- timeout_init(&un->tm, -1, -1); +- buffer_init(&un->buf, &un->io, &un->tm); +- return 1; +- } else { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(err)); +- return 2; +- } +-} +diff --git a/unixdgram.h b/unixdgram.h +deleted file mode 100644 +index a1a0166..0000000 +--- a/unixdgram.h ++++ /dev/null +@@ -1,28 +0,0 @@ +-#ifndef UNIXDGRAM_H +-#define UNIXDGRAM_H +-/*=========================================================================*\ +-* DGRAM object +-* LuaSocket toolkit +-* +-* The dgram.h module provides LuaSocket with support for DGRAM protocol +-* (AF_INET, SOCK_DGRAM). +-* +-* Two classes are defined: connected and unconnected. DGRAM objects are +-* originally unconnected. They can be "connected" to a given address +-* with a call to the setpeername function. The same function can be used to +-* break the connection. +-\*=========================================================================*/ +- +-#include "unix.h" +- +-#ifndef _WIN32 +-#pragma GCC visibility push(hidden) +-#endif +- +-int unixdgram_open(lua_State *L); +- +-#ifndef _WIN32 +-#pragma GCC visibility pop +-#endif +- +-#endif /* UNIXDGRAM_H */ +diff --git a/unixstream.c b/unixstream.c +deleted file mode 100644 +index 02aced9..0000000 +--- a/unixstream.c ++++ /dev/null +@@ -1,355 +0,0 @@ +-/*=========================================================================*\ +-* Unix domain socket stream sub module +-* LuaSocket toolkit +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "auxiliar.h" +-#include "socket.h" +-#include "options.h" +-#include "unixstream.h" +- +-#include +-#include +- +-/*=========================================================================*\ +-* Internal function prototypes +-\*=========================================================================*/ +-static int global_create(lua_State *L); +-static int meth_connect(lua_State *L); +-static int meth_listen(lua_State *L); +-static int meth_bind(lua_State *L); +-static int meth_send(lua_State *L); +-static int meth_shutdown(lua_State *L); +-static int meth_receive(lua_State *L); +-static int meth_accept(lua_State *L); +-static int meth_close(lua_State *L); +-static int meth_setoption(lua_State *L); +-static int meth_settimeout(lua_State *L); +-static int meth_getfd(lua_State *L); +-static int meth_setfd(lua_State *L); +-static int meth_dirty(lua_State *L); +-static int meth_getstats(lua_State *L); +-static int meth_setstats(lua_State *L); +-static int meth_getsockname(lua_State *L); +- +-static const char *unixstream_tryconnect(p_unix un, const char *path); +-static const char *unixstream_trybind(p_unix un, const char *path); +- +-/* unixstream object methods */ +-static luaL_Reg unixstream_methods[] = { +- {"__gc", meth_close}, +- {"__tostring", auxiliar_tostring}, +- {"accept", meth_accept}, +- {"bind", meth_bind}, +- {"close", meth_close}, +- {"connect", meth_connect}, +- {"dirty", meth_dirty}, +- {"getfd", meth_getfd}, +- {"getstats", meth_getstats}, +- {"setstats", meth_setstats}, +- {"listen", meth_listen}, +- {"receive", meth_receive}, +- {"send", meth_send}, +- {"setfd", meth_setfd}, +- {"setoption", meth_setoption}, +- {"setpeername", meth_connect}, +- {"setsockname", meth_bind}, +- {"getsockname", meth_getsockname}, +- {"settimeout", meth_settimeout}, +- {"shutdown", meth_shutdown}, +- {NULL, NULL} +-}; +- +-/* socket option handlers */ +-static t_opt optset[] = { +- {"keepalive", opt_set_keepalive}, +- {"reuseaddr", opt_set_reuseaddr}, +- {"linger", opt_set_linger}, +- {NULL, NULL} +-}; +- +-/* functions in library namespace */ +-static luaL_Reg func[] = { +- {"stream", global_create}, +- {NULL, NULL} +-}; +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-int unixstream_open(lua_State *L) +-{ +- /* create classes */ +- auxiliar_newclass(L, "unixstream{master}", unixstream_methods); +- auxiliar_newclass(L, "unixstream{client}", unixstream_methods); +- auxiliar_newclass(L, "unixstream{server}", unixstream_methods); +- +- /* create class groups */ +- auxiliar_add2group(L, "unixstream{master}", "unixstream{any}"); +- auxiliar_add2group(L, "unixstream{client}", "unixstream{any}"); +- auxiliar_add2group(L, "unixstream{server}", "unixstream{any}"); +- +- luaL_setfuncs(L, func, 0); +- return 0; +-} +- +-/*=========================================================================*\ +-* Lua methods +-\*=========================================================================*/ +-/*-------------------------------------------------------------------------*\ +-* Just call buffered IO methods +-\*-------------------------------------------------------------------------*/ +-static int meth_send(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- return buffer_meth_send(L, &un->buf); +-} +- +-static int meth_receive(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- return buffer_meth_receive(L, &un->buf); +-} +- +-static int meth_getstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- return buffer_meth_getstats(L, &un->buf); +-} +- +-static int meth_setstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- return buffer_meth_setstats(L, &un->buf); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Just call option handler +-\*-------------------------------------------------------------------------*/ +-static int meth_setoption(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- return opt_meth_setoption(L, optset, &un->sock); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Select support methods +-\*-------------------------------------------------------------------------*/ +-static int meth_getfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- lua_pushnumber(L, (int) un->sock); +- return 1; +-} +- +-/* this is very dangerous, but can be handy for those that are brave enough */ +-static int meth_setfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- un->sock = (t_socket) luaL_checknumber(L, 2); +- return 0; +-} +- +-static int meth_dirty(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- lua_pushboolean(L, !buffer_isempty(&un->buf)); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Waits for and returns a client object attempting connection to the +-* server object +-\*-------------------------------------------------------------------------*/ +-static int meth_accept(lua_State *L) { +- p_unix server = (p_unix) auxiliar_checkclass(L, "unixstream{server}", 1); +- p_timeout tm = timeout_markstart(&server->tm); +- t_socket sock; +- int err = socket_accept(&server->sock, &sock, NULL, NULL, tm); +- /* if successful, push client socket */ +- if (err == IO_DONE) { +- p_unix clnt = (p_unix) lua_newuserdata(L, sizeof(t_unix)); +- auxiliar_setclass(L, "unixstream{client}", -1); +- /* initialize structure fields */ +- socket_setnonblocking(&sock); +- clnt->sock = sock; +- io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv, +- (p_error) socket_ioerror, &clnt->sock); +- timeout_init(&clnt->tm, -1, -1); +- buffer_init(&clnt->buf, &clnt->io, &clnt->tm); +- return 1; +- } else { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(err)); +- return 2; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Binds an object to an address +-\*-------------------------------------------------------------------------*/ +-static const char *unixstream_trybind(p_unix un, const char *path) { +- struct sockaddr_un local; +- size_t len = strlen(path); +- int err; +- if (len >= sizeof(local.sun_path)) return "path too long"; +- memset(&local, 0, sizeof(local)); +- strcpy(local.sun_path, path); +- local.sun_family = AF_UNIX; +-#ifdef UNIX_HAS_SUN_LEN +- local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) +- + len + 1; +- err = socket_bind(&un->sock, (SA *) &local, local.sun_len); +- +-#else +- err = socket_bind(&un->sock, (SA *) &local, +- sizeof(local.sun_family) + len); +-#endif +- if (err != IO_DONE) socket_destroy(&un->sock); +- return socket_strerror(err); +-} +- +-static int meth_bind(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); +- const char *path = luaL_checkstring(L, 2); +- const char *err = unixstream_trybind(un, path); +- if (err) { +- lua_pushnil(L); +- lua_pushstring(L, err); +- return 2; +- } +- lua_pushnumber(L, 1); +- return 1; +-} +- +-static int meth_getsockname(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- struct sockaddr_un peer = {0}; +- socklen_t peer_len = sizeof(peer); +- +- if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(errno)); +- return 2; +- } +- +- lua_pushstring(L, peer.sun_path); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Turns a master unixstream object into a client object. +-\*-------------------------------------------------------------------------*/ +-static const char *unixstream_tryconnect(p_unix un, const char *path) +-{ +- struct sockaddr_un remote; +- int err; +- size_t len = strlen(path); +- if (len >= sizeof(remote.sun_path)) return "path too long"; +- memset(&remote, 0, sizeof(remote)); +- strcpy(remote.sun_path, path); +- remote.sun_family = AF_UNIX; +- timeout_markstart(&un->tm); +-#ifdef UNIX_HAS_SUN_LEN +- remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) +- + len + 1; +- err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm); +-#else +- err = socket_connect(&un->sock, (SA *) &remote, +- sizeof(remote.sun_family) + len, &un->tm); +-#endif +- if (err != IO_DONE) socket_destroy(&un->sock); +- return socket_strerror(err); +-} +- +-static int meth_connect(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); +- const char *path = luaL_checkstring(L, 2); +- const char *err = unixstream_tryconnect(un, path); +- if (err) { +- lua_pushnil(L); +- lua_pushstring(L, err); +- return 2; +- } +- /* turn master object into a client object */ +- auxiliar_setclass(L, "unixstream{client}", 1); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Closes socket used by object +-\*-------------------------------------------------------------------------*/ +-static int meth_close(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- socket_destroy(&un->sock); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Puts the sockt in listen mode +-\*-------------------------------------------------------------------------*/ +-static int meth_listen(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); +- int backlog = (int) luaL_optnumber(L, 2, 32); +- int err = socket_listen(&un->sock, backlog); +- if (err != IO_DONE) { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(err)); +- return 2; +- } +- /* turn master object into a server object */ +- auxiliar_setclass(L, "unixstream{server}", 1); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Shuts the connection down partially +-\*-------------------------------------------------------------------------*/ +-static int meth_shutdown(lua_State *L) +-{ +- /* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */ +- static const char* methods[] = { "receive", "send", "both", NULL }; +- p_unix stream = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- int how = luaL_checkoption(L, 2, "both", methods); +- socket_shutdown(&stream->sock, how); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Just call tm methods +-\*-------------------------------------------------------------------------*/ +-static int meth_settimeout(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- return timeout_meth_settimeout(L, &un->tm); +-} +- +-/*=========================================================================*\ +-* Library functions +-\*=========================================================================*/ +-/*-------------------------------------------------------------------------*\ +-* Creates a master unixstream object +-\*-------------------------------------------------------------------------*/ +-static int global_create(lua_State *L) { +- t_socket sock; +- int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0); +- /* try to allocate a system socket */ +- if (err == IO_DONE) { +- /* allocate unixstream object */ +- p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); +- /* set its type as master object */ +- auxiliar_setclass(L, "unixstream{master}", -1); +- /* initialize remaining structure fields */ +- socket_setnonblocking(&sock); +- un->sock = sock; +- io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, +- (p_error) socket_ioerror, &un->sock); +- timeout_init(&un->tm, -1, -1); +- buffer_init(&un->buf, &un->io, &un->tm); +- return 1; +- } else { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(err)); +- return 2; +- } +-} +diff --git a/unixstream.h b/unixstream.h +deleted file mode 100644 +index 7916aff..0000000 +--- a/unixstream.h ++++ /dev/null +@@ -1,29 +0,0 @@ +-#ifndef UNIXSTREAM_H +-#define UNIXSTREAM_H +-/*=========================================================================*\ +-* UNIX STREAM object +-* LuaSocket toolkit +-* +-* The unixstream.h module is basicly a glue that puts together modules buffer.h, +-* timeout.h socket.h and inet.h to provide the LuaSocket UNIX STREAM (AF_UNIX, +-* SOCK_STREAM) support. +-* +-* Three classes are defined: master, client and server. The master class is +-* a newly created unixstream object, that has not been bound or connected. Server +-* objects are unixstream objects bound to some local address. Client objects are +-* unixstream objects either connected to some address or returned by the accept +-* method of a server object. +-\*=========================================================================*/ +-#include "unix.h" +- +-#ifndef _WIN32 +-#pragma GCC visibility push(hidden) +-#endif +- +-int unixstream_open(lua_State *L); +- +-#ifndef _WIN32 +-#pragma GCC visibility pop +-#endif +- +-#endif /* UNIXSTREAM_H */ +diff --git a/usocket.c b/usocket.c +index 69635da..3cd8cde 100644 +--- a/usocket.c ++++ b/usocket.c +@@ -18,7 +18,7 @@ + * Wait for readable/writable/connected socket with timeout + \*-------------------------------------------------------------------------*/ + #ifndef SOCKET_SELECT +-#include ++#include + + #define WAITFD_R POLLIN + #define WAITFD_W POLLOUT +@@ -431,12 +431,16 @@ const char *socket_ioerror(p_socket ps, int err) { + const char *socket_gaistrerror(int err) { + if (err == 0) return NULL; + switch (err) { ++#if !defined(__3DS__) + case EAI_AGAIN: return PIE_AGAIN; + case EAI_BADFLAGS: return PIE_BADFLAGS; ++#endif + #ifdef EAI_BADHINTS + case EAI_BADHINTS: return PIE_BADHINTS; + #endif ++#if !defined(__3DS__) + case EAI_FAIL: return PIE_FAIL; ++#endif + case EAI_FAMILY: return PIE_FAMILY; + case EAI_MEMORY: return PIE_MEMORY; + case EAI_NONAME: return PIE_NONAME; +@@ -446,9 +450,13 @@ const char *socket_gaistrerror(int err) { + #ifdef EAI_PROTOCOL + case EAI_PROTOCOL: return PIE_PROTOCOL; + #endif ++#if !defined(__3DS__) + case EAI_SERVICE: return PIE_SERVICE; ++#endif + case EAI_SOCKTYPE: return PIE_SOCKTYPE; ++#if !defined(__3DS__) + case EAI_SYSTEM: return strerror(errno); ++#endif + default: return LUA_GAI_STRERROR(err); + } + } +diff --git a/usocket.h b/usocket.h +index 45f2f99..12c25ca 100644 +--- a/usocket.h ++++ b/usocket.h +@@ -29,7 +29,6 @@ + #include + /* TCP options (nagle algorithm disable) */ + #include +-#include + + #ifndef SO_REUSEPORT + #define SO_REUSEPORT SO_REUSEADDR +diff --git a/wsocket.c b/wsocket.c +deleted file mode 100755 +index 6cb1e41..0000000 +--- a/wsocket.c ++++ /dev/null +@@ -1,434 +0,0 @@ +-/*=========================================================================*\ +-* Socket compatibilization module for Win32 +-* LuaSocket toolkit +-* +-* The penalty of calling select to avoid busy-wait is only paid when +-* the I/O call fail in the first place. +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include +- +-#include "socket.h" +-#include "pierror.h" +- +-/* WinSock doesn't have a strerror... */ +-static const char *wstrerror(int err); +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-int socket_open(void) { +- WSADATA wsaData; +- WORD wVersionRequested = MAKEWORD(2, 0); +- int err = WSAStartup(wVersionRequested, &wsaData ); +- if (err != 0) return 0; +- if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) && +- (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) { +- WSACleanup(); +- return 0; +- } +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Close module +-\*-------------------------------------------------------------------------*/ +-int socket_close(void) { +- WSACleanup(); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Wait for readable/writable/connected socket with timeout +-\*-------------------------------------------------------------------------*/ +-#define WAITFD_R 1 +-#define WAITFD_W 2 +-#define WAITFD_E 4 +-#define WAITFD_C (WAITFD_E|WAITFD_W) +- +-int socket_waitfd(p_socket ps, int sw, p_timeout tm) { +- int ret; +- fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL; +- struct timeval tv, *tp = NULL; +- double t; +- if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ +- if (sw & WAITFD_R) { +- FD_ZERO(&rfds); +- FD_SET(*ps, &rfds); +- rp = &rfds; +- } +- if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } +- if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(*ps, &efds); ep = &efds; } +- if ((t = timeout_get(tm)) >= 0.0) { +- tv.tv_sec = (int) t; +- tv.tv_usec = (int) ((t-tv.tv_sec)*1.0e6); +- tp = &tv; +- } +- ret = select(0, rp, wp, ep, tp); +- if (ret == -1) return WSAGetLastError(); +- if (ret == 0) return IO_TIMEOUT; +- if (sw == WAITFD_C && FD_ISSET(*ps, &efds)) return IO_CLOSED; +- return IO_DONE; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Select with int timeout in ms +-\*-------------------------------------------------------------------------*/ +-int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, +- p_timeout tm) { +- struct timeval tv; +- double t = timeout_get(tm); +- tv.tv_sec = (int) t; +- tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); +- if (n <= 0) { +- Sleep((DWORD) (1000*t)); +- return 0; +- } else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Close and inutilize socket +-\*-------------------------------------------------------------------------*/ +-void socket_destroy(p_socket ps) { +- if (*ps != SOCKET_INVALID) { +- socket_setblocking(ps); /* close can take a long time on WIN32 */ +- closesocket(*ps); +- *ps = SOCKET_INVALID; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* +-\*-------------------------------------------------------------------------*/ +-void socket_shutdown(p_socket ps, int how) { +- socket_setblocking(ps); +- shutdown(*ps, how); +- socket_setnonblocking(ps); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Creates and sets up a socket +-\*-------------------------------------------------------------------------*/ +-int socket_create(p_socket ps, int domain, int type, int protocol) { +- *ps = socket(domain, type, protocol); +- if (*ps != SOCKET_INVALID) return IO_DONE; +- else return WSAGetLastError(); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Connects or returns error message +-\*-------------------------------------------------------------------------*/ +-int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { +- int err; +- /* don't call on closed socket */ +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- /* ask system to connect */ +- if (connect(*ps, addr, len) == 0) return IO_DONE; +- /* make sure the system is trying to connect */ +- err = WSAGetLastError(); +- if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) return err; +- /* zero timeout case optimization */ +- if (timeout_iszero(tm)) return IO_TIMEOUT; +- /* we wait until something happens */ +- err = socket_waitfd(ps, WAITFD_C, tm); +- if (err == IO_CLOSED) { +- int elen = sizeof(err); +- /* give windows time to set the error (yes, disgusting) */ +- Sleep(10); +- /* find out why we failed */ +- getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &elen); +- /* we KNOW there was an error. if 'why' is 0, we will return +- * "unknown error", but it's not really our fault */ +- return err > 0? err: IO_UNKNOWN; +- } else return err; +- +-} +- +-/*-------------------------------------------------------------------------*\ +-* Binds or returns error message +-\*-------------------------------------------------------------------------*/ +-int socket_bind(p_socket ps, SA *addr, socklen_t len) { +- int err = IO_DONE; +- socket_setblocking(ps); +- if (bind(*ps, addr, len) < 0) err = WSAGetLastError(); +- socket_setnonblocking(ps); +- return err; +-} +- +-/*-------------------------------------------------------------------------*\ +-* +-\*-------------------------------------------------------------------------*/ +-int socket_listen(p_socket ps, int backlog) { +- int err = IO_DONE; +- socket_setblocking(ps); +- if (listen(*ps, backlog) < 0) err = WSAGetLastError(); +- socket_setnonblocking(ps); +- return err; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Accept with timeout +-\*-------------------------------------------------------------------------*/ +-int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, +- p_timeout tm) { +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- for ( ;; ) { +- int err; +- /* try to get client socket */ +- if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; +- /* find out why we failed */ +- err = WSAGetLastError(); +- /* if we failed because there was no connectoin, keep trying */ +- if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err; +- /* call select to avoid busy wait */ +- if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Send with timeout +-* On windows, if you try to send 10MB, the OS will buffer EVERYTHING +-* this can take an awful lot of time and we will end up blocked. +-* Therefore, whoever calls this function should not pass a huge buffer. +-\*-------------------------------------------------------------------------*/ +-int socket_send(p_socket ps, const char *data, size_t count, +- size_t *sent, p_timeout tm) +-{ +- int err; +- *sent = 0; +- /* avoid making system calls on closed sockets */ +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- /* loop until we send something or we give up on error */ +- for ( ;; ) { +- /* try to send something */ +- int put = send(*ps, data, (int) count, 0); +- /* if we sent something, we are done */ +- if (put > 0) { +- *sent = put; +- return IO_DONE; +- } +- /* deal with failure */ +- err = WSAGetLastError(); +- /* we can only proceed if there was no serious error */ +- if (err != WSAEWOULDBLOCK) return err; +- /* avoid busy wait */ +- if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Sendto with timeout +-\*-------------------------------------------------------------------------*/ +-int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, +- SA *addr, socklen_t len, p_timeout tm) +-{ +- int err; +- *sent = 0; +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- for ( ;; ) { +- int put = sendto(*ps, data, (int) count, 0, addr, len); +- if (put > 0) { +- *sent = put; +- return IO_DONE; +- } +- err = WSAGetLastError(); +- if (err != WSAEWOULDBLOCK) return err; +- if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Receive with timeout +-\*-------------------------------------------------------------------------*/ +-int socket_recv(p_socket ps, char *data, size_t count, size_t *got, +- p_timeout tm) +-{ +- int err, prev = IO_DONE; +- *got = 0; +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- for ( ;; ) { +- int taken = recv(*ps, data, (int) count, 0); +- if (taken > 0) { +- *got = taken; +- return IO_DONE; +- } +- if (taken == 0) return IO_CLOSED; +- err = WSAGetLastError(); +- /* On UDP, a connreset simply means the previous send failed. +- * So we try again. +- * On TCP, it means our socket is now useless, so the error passes. +- * (We will loop again, exiting because the same error will happen) */ +- if (err != WSAEWOULDBLOCK) { +- if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; +- prev = err; +- } +- if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Recvfrom with timeout +-\*-------------------------------------------------------------------------*/ +-int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, +- SA *addr, socklen_t *len, p_timeout tm) +-{ +- int err, prev = IO_DONE; +- *got = 0; +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- for ( ;; ) { +- int taken = recvfrom(*ps, data, (int) count, 0, addr, len); +- if (taken > 0) { +- *got = taken; +- return IO_DONE; +- } +- if (taken == 0) return IO_CLOSED; +- err = WSAGetLastError(); +- /* On UDP, a connreset simply means the previous send failed. +- * So we try again. +- * On TCP, it means our socket is now useless, so the error passes. +- * (We will loop again, exiting because the same error will happen) */ +- if (err != WSAEWOULDBLOCK) { +- if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; +- prev = err; +- } +- if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Put socket into blocking mode +-\*-------------------------------------------------------------------------*/ +-void socket_setblocking(p_socket ps) { +- u_long argp = 0; +- ioctlsocket(*ps, FIONBIO, &argp); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Put socket into non-blocking mode +-\*-------------------------------------------------------------------------*/ +-void socket_setnonblocking(p_socket ps) { +- u_long argp = 1; +- ioctlsocket(*ps, FIONBIO, &argp); +-} +- +-/*-------------------------------------------------------------------------*\ +-* DNS helpers +-\*-------------------------------------------------------------------------*/ +-int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { +- *hp = gethostbyaddr(addr, len, AF_INET); +- if (*hp) return IO_DONE; +- else return WSAGetLastError(); +-} +- +-int socket_gethostbyname(const char *addr, struct hostent **hp) { +- *hp = gethostbyname(addr); +- if (*hp) return IO_DONE; +- else return WSAGetLastError(); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Error translation functions +-\*-------------------------------------------------------------------------*/ +-const char *socket_hoststrerror(int err) { +- if (err <= 0) return io_strerror(err); +- switch (err) { +- case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; +- default: return wstrerror(err); +- } +-} +- +-const char *socket_strerror(int err) { +- if (err <= 0) return io_strerror(err); +- switch (err) { +- case WSAEADDRINUSE: return PIE_ADDRINUSE; +- case WSAECONNREFUSED : return PIE_CONNREFUSED; +- case WSAEISCONN: return PIE_ISCONN; +- case WSAEACCES: return PIE_ACCESS; +- case WSAECONNABORTED: return PIE_CONNABORTED; +- case WSAECONNRESET: return PIE_CONNRESET; +- case WSAETIMEDOUT: return PIE_TIMEDOUT; +- default: return wstrerror(err); +- } +-} +- +-const char *socket_ioerror(p_socket ps, int err) { +- (void) ps; +- return socket_strerror(err); +-} +- +-static const char *wstrerror(int err) { +- switch (err) { +- case WSAEINTR: return "Interrupted function call"; +- case WSAEACCES: return PIE_ACCESS; /* "Permission denied"; */ +- case WSAEFAULT: return "Bad address"; +- case WSAEINVAL: return "Invalid argument"; +- case WSAEMFILE: return "Too many open files"; +- case WSAEWOULDBLOCK: return "Resource temporarily unavailable"; +- case WSAEINPROGRESS: return "Operation now in progress"; +- case WSAEALREADY: return "Operation already in progress"; +- case WSAENOTSOCK: return "Socket operation on nonsocket"; +- case WSAEDESTADDRREQ: return "Destination address required"; +- case WSAEMSGSIZE: return "Message too long"; +- case WSAEPROTOTYPE: return "Protocol wrong type for socket"; +- case WSAENOPROTOOPT: return "Bad protocol option"; +- case WSAEPROTONOSUPPORT: return "Protocol not supported"; +- case WSAESOCKTNOSUPPORT: return PIE_SOCKTYPE; /* "Socket type not supported"; */ +- case WSAEOPNOTSUPP: return "Operation not supported"; +- case WSAEPFNOSUPPORT: return "Protocol family not supported"; +- case WSAEAFNOSUPPORT: return PIE_FAMILY; /* "Address family not supported by protocol family"; */ +- case WSAEADDRINUSE: return PIE_ADDRINUSE; /* "Address already in use"; */ +- case WSAEADDRNOTAVAIL: return "Cannot assign requested address"; +- case WSAENETDOWN: return "Network is down"; +- case WSAENETUNREACH: return "Network is unreachable"; +- case WSAENETRESET: return "Network dropped connection on reset"; +- case WSAECONNABORTED: return "Software caused connection abort"; +- case WSAECONNRESET: return PIE_CONNRESET; /* "Connection reset by peer"; */ +- case WSAENOBUFS: return "No buffer space available"; +- case WSAEISCONN: return PIE_ISCONN; /* "Socket is already connected"; */ +- case WSAENOTCONN: return "Socket is not connected"; +- case WSAESHUTDOWN: return "Cannot send after socket shutdown"; +- case WSAETIMEDOUT: return PIE_TIMEDOUT; /* "Connection timed out"; */ +- case WSAECONNREFUSED: return PIE_CONNREFUSED; /* "Connection refused"; */ +- case WSAEHOSTDOWN: return "Host is down"; +- case WSAEHOSTUNREACH: return "No route to host"; +- case WSAEPROCLIM: return "Too many processes"; +- case WSASYSNOTREADY: return "Network subsystem is unavailable"; +- case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range"; +- case WSANOTINITIALISED: +- return "Successful WSAStartup not yet performed"; +- case WSAEDISCON: return "Graceful shutdown in progress"; +- case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; /* "Host not found"; */ +- case WSATRY_AGAIN: return "Nonauthoritative host not found"; +- case WSANO_RECOVERY: return PIE_FAIL; /* "Nonrecoverable name lookup error"; */ +- case WSANO_DATA: return "Valid name, no data record of requested type"; +- default: return "Unknown error"; +- } +-} +- +-const char *socket_gaistrerror(int err) { +- if (err == 0) return NULL; +- switch (err) { +- case EAI_AGAIN: return PIE_AGAIN; +- case EAI_BADFLAGS: return PIE_BADFLAGS; +-#ifdef EAI_BADHINTS +- case EAI_BADHINTS: return PIE_BADHINTS; +-#endif +- case EAI_FAIL: return PIE_FAIL; +- case EAI_FAMILY: return PIE_FAMILY; +- case EAI_MEMORY: return PIE_MEMORY; +- case EAI_NONAME: return PIE_NONAME; +-#ifdef EAI_OVERFLOW +- case EAI_OVERFLOW: return PIE_OVERFLOW; +-#endif +-#ifdef EAI_PROTOCOL +- case EAI_PROTOCOL: return PIE_PROTOCOL; +-#endif +- case EAI_SERVICE: return PIE_SERVICE; +- case EAI_SOCKTYPE: return PIE_SOCKTYPE; +-#ifdef EAI_SYSTEM +- case EAI_SYSTEM: return strerror(errno); +-#endif +- default: return LUA_GAI_STRERROR(err); +- } +-} +diff --git a/wsocket.h b/wsocket.h +deleted file mode 100644 +index 3986640..0000000 +--- a/wsocket.h ++++ /dev/null +@@ -1,33 +0,0 @@ +-#ifndef WSOCKET_H +-#define WSOCKET_H +-/*=========================================================================*\ +-* Socket compatibilization module for Win32 +-* LuaSocket toolkit +-\*=========================================================================*/ +- +-/*=========================================================================*\ +-* WinSock include files +-\*=========================================================================*/ +-#include +-#include +- +-typedef int socklen_t; +-typedef SOCKADDR_STORAGE t_sockaddr_storage; +-typedef SOCKET t_socket; +-typedef t_socket *p_socket; +- +-#ifndef IPV6_V6ONLY +-#define IPV6_V6ONLY 27 +-#endif +- +-#define SOCKET_INVALID (INVALID_SOCKET) +- +-#ifndef SO_REUSEPORT +-#define SO_REUSEPORT SO_REUSEADDR +-#endif +- +-#ifndef AI_NUMERICSERV +-#define AI_NUMERICSERV (0) +-#endif +- +-#endif /* WSOCKET_H */ diff --git a/platform/ctr/libraries/thread/ctr_thread.h b/platform/ctr/libraries/thread/ctr_thread.h new file mode 100644 index 000000000..dfde5effc --- /dev/null +++ b/platform/ctr/libraries/thread/ctr_thread.h @@ -0,0 +1,382 @@ +#ifndef THREAD3DS_INC +#define THREAD3DS_INC + +/* + * Public domain C++ -ish implementation for libctru + * VER 20220902060500 + */ + +#include <3ds.h> +#include +#include +#include +#include +#include +#include + +namespace ctr +{ + + struct thread_id + { + friend class thread; + friend class this_thread; + + thread_id() = default; + + bool operator==(thread_id rhs) const noexcept + { + return m_id == rhs.m_id; + } + + bool operator!=(thread_id rhs) const noexcept + { + return m_id != rhs.m_id; + } + + bool operator<(thread_id rhs) const noexcept + { + return m_id < rhs.m_id; + } + + bool operator<=(thread_id rhs) const noexcept + { + return m_id <= rhs.m_id; + } + + bool operator>(thread_id rhs) const noexcept + { + return m_id > rhs.m_id; + } + + bool operator>=(thread_id rhs) const noexcept + { + return m_id >= rhs.m_id; + } + + private: + Thread m_id {}; + }; + + struct thread_base + { + template + static void sleep_for(const std::chrono::duration& length) + { + svcSleepThread(std::chrono::duration_cast(length).count()); + } + }; + + struct this_thread : public thread_base + { + using id = thread_id; + + static thread_id get_id() + { + thread_id out; + out.m_id = native_handle(); + return out; + } + + static Thread native_handle() + { + return threadGetCurrent(); + } + + static void yield() noexcept + { + svcSleepThread(1); + } + }; + + class thread + { + public: + struct meta + { + std::size_t stack_size; + int prio; + int core_id; + }; + + private: + Thread m_th {}; + + template + static void trampoline(void* v_arg) + { + auto const thread_arg = std::unique_ptr(static_cast(v_arg)); + std::apply(thread_arg->first, + std::forward(thread_arg->second)); + } + + public: + using id = thread_id; + + static constexpr inline meta basic_meta { + 64 * 1024, + 0x30, + 0, + }; + + template + thread(const meta& info, F&& func, Args&&... args) + { + static_assert(std::is_invocable_v...>, + "Can not call this function with these argument."); + using P_t = std::pair...>>; + auto arg = std::make_unique(std::forward(func), + std::forward_as_tuple(std::forward(args)...)); + m_th = threadCreate(&thread::trampoline, arg.get(), info.stack_size, info.prio, + info.core_id, false); + if (m_th) + { + svcSleepThread(1000 * 1000); + arg.release(); + } + } + + template>>> + thread(F&& func, Args&&... args) : + thread(basic_meta, std::forward(func), std::forward(args)...) + {} + + thread() = default; + + thread(thread&& other) : m_th(std::exchange(other.m_th, nullptr)) + {} + + thread(const thread&) = delete; + + thread& operator=(thread&& other) + { + if (this != &other) + { + if (joinable()) + throw std::runtime_error("Moving to a thread already running."); + m_th = std::exchange(other.m_th, nullptr); + } + return *this; + } + + thread& operator=(const thread&) = delete; + + ~thread() + { + if (m_th) + { + svcBreak(USERBREAK_ASSERT); + } + } + + operator bool() const + { + return m_th != nullptr; + } + + bool joinable() const noexcept + { + return m_th != threadGetCurrent(); + } + + thread_id get_id() + { + thread_id out; + out.m_id = m_th; + return out; + } + + Thread native_handle() const + { + return m_th; + } + + void join_timeout(const u64 timeout) + { + if (R_SUCCEEDED(threadJoin(m_th, timeout))) + { + threadFree(m_th); + m_th = nullptr; + } + } + + void join() + { + join_timeout(U64_MAX); + } + + void swap(thread& other) noexcept + { + std::swap(m_th, other.m_th); + } + + static unsigned int hardware_concurrency() noexcept + { + return 1; + } + }; + + class mutex + { + LightLock m_lock; + + public: + mutex() noexcept + { + LightLock_Init(&m_lock); + } + + mutex(const mutex&) = delete; + mutex& operator=(const mutex&) = delete; + mutex(mutex&&) = delete; + mutex& operator=(mutex&&) = delete; + + void lock() noexcept + { + LightLock_Lock(&m_lock); + } + + bool try_lock() noexcept + { + return LightLock_TryLock(&m_lock) == 0; + } + + void unlock() noexcept + { + LightLock_Unlock(&m_lock); + } + + LightLock* native_handle() + { + return &m_lock; + } + }; + + class recursive_mutex + { + RecursiveLock m_lock; + + public: + recursive_mutex() noexcept + { + RecursiveLock_Init(&m_lock); + } + + recursive_mutex(const recursive_mutex&) = delete; + recursive_mutex& operator=(const recursive_mutex&) = delete; + recursive_mutex(recursive_mutex&&) = delete; + recursive_mutex& operator=(recursive_mutex&&) = delete; + + void lock() noexcept + { + RecursiveLock_Lock(&m_lock); + } + + bool try_lock() noexcept + { + return RecursiveLock_TryLock(&m_lock) == 0; + } + + void unlock() noexcept + { + RecursiveLock_Unlock(&m_lock); + } + + RecursiveLock* native_handle() + { + return &m_lock; + } + }; + + enum class cv_status + { + no_timeout, + timeout + }; + + class condition_variable + { + CondVar m_cv; + + public: + condition_variable() + { + CondVar_Init(&m_cv); + } + + condition_variable(const condition_variable&) = delete; + + void notify_one() noexcept + { + CondVar_Signal(&m_cv); + } + + void notify_all() noexcept + { + CondVar_Broadcast(&m_cv); + } + + void wait(std::unique_lock& lock) + { + CondVar_Wait(&m_cv, lock.mutex()->native_handle()); + } + + template + void wait(std::unique_lock& lock, Predicate stop_waiting) + { + while (!stop_waiting()) + { + wait(lock); + } + } + + template + cv_status wait_for(std::unique_lock& lock, + const std::chrono::duration& rel_time) + { + const int r = CondVar_WaitTimeout( + &m_cv, lock.mutex()->native_handle(), + std::chrono::duration_cast(rel_time).count()); + return r == 0 ? cv_status::no_timeout : cv_status::timeout; + } + + template + bool wait_for(std::unique_lock& lock, + const std::chrono::duration& rel_time, Predicate stop_waiting) + { + return wait_until(lock, std::chrono::steady_clock::now() + rel_time, + std::move(stop_waiting)); + } + + template + cv_status wait_until(std::unique_lock& lock, + const std::chrono::duration& abs_time) + { + return wait_for(lock, abs_time - std::chrono::steady_clock::now()); + } + + template + bool wait_until(std::unique_lock& lock, + const std::chrono::time_point& timeout_time, + Predicate stop_waiting) + { + while (!stop_waiting()) + { + if (wait_until(lock, timeout_time) == cv_status::timeout) + { + return stop_waiting(); + } + } + return true; + } + + CondVar* native_handle() + { + return &m_cv; + } + }; + +} // namespace ctr + +#endif diff --git a/platform/ctr/pkglist.txt b/platform/ctr/pkglist.txt new file mode 100644 index 000000000..ff0bd3777 --- /dev/null +++ b/platform/ctr/pkglist.txt @@ -0,0 +1,18 @@ +3ds-box2d +3ds-curl +3ds-flac +3ds-libconfig +3ds-libjpeg-turbo +3ds-libmodplug +3ds-libogg +3ds-libpng +3ds-libtheora +3ds-libvorbisidec +3ds-liblua51 +3ds-lz4 +3ds-mbedtls +3ds-physfs +3ds-tinyxml2 +3ds-zlib +3ds-cmake +3dstools \ No newline at end of file diff --git a/platform/ctr/romfs/shaders/main_v_pica.shbin b/platform/ctr/romfs/shaders/main_v_pica.shbin new file mode 100644 index 000000000..0a1c13868 Binary files /dev/null and b/platform/ctr/romfs/shaders/main_v_pica.shbin differ diff --git a/platform/ctr/source/common/matrix_ext.cpp b/platform/ctr/source/common/matrix_ext.cpp new file mode 100644 index 000000000..c6f734daf --- /dev/null +++ b/platform/ctr/source/common/matrix_ext.cpp @@ -0,0 +1,214 @@ +#include + +#include + +using namespace love; + +void Matrix4::Multiply(const Matrix4& a, const Matrix4& b, C3D_Mtx& c) +{ + Mtx_Multiply(&c, &a.matrix, &b.matrix); +} + +void Matrix4::Multiply(const Matrix4& a, const Matrix4& b, Matrix4& c) +{ + Multiply(a, b, c.matrix); +} + +Matrix4::Matrix4() +{ + this->SetIdentity(); +} + +Matrix4::Matrix4(const C3D_Mtx& a) +{ + Mtx_Copy(&this->matrix, &a); +} + +/* credit to @mtheall */ +static bool fuzzy(float const a_, float const b_) +{ + float const EPSILON = 0.00001f; + return fabs(a_ - b_) < EPSILON; +} + +bool Matrix4::IsAffine2DTransform() const +{ + // z-vector must remain unchanged + if (this->matrix.r[0].z != 0.0f || this->matrix.r[1].z != 0.0f || this->matrix.r[2].x != 0.0f || + this->matrix.r[2].y != 0.0f || this->matrix.r[2].z != 1.0f || this->matrix.r[2].w != 0.0f) + return false; + + // last row must be {0, 0, 0, 1} + if (this->matrix.r[3].x != 0.0f || this->matrix.r[3].y != 0.0f || this->matrix.r[3].z != 0.0f || + this->matrix.r[3].w != 1.0f) + return false; + + float const a = this->matrix.r[0].x; + float const b = this->matrix.r[0].y; + float const c = this->matrix.r[1].x; + float const d = this->matrix.r[1].y; + + // M * transpose(M) must be identity matrix, where M is upper-left 2x2 matrix + if (!fuzzy(a * a + b * b, 1.0f) || !fuzzy(c * c + d * d, 1.0f) || !fuzzy(a * c + b * d, 0.0f)) + return false; + + return true; +} + +bool Matrix4::IsAffine3DTransform() const +{ + // last row must be {0, 0, 0, 1} + if (this->matrix.r[3].x != 0.0f || this->matrix.r[3].y != 0.0f || this->matrix.r[3].z != 0.0f || + this->matrix.r[3].w != 1.0f) + return false; + + float const a = this->matrix.r[0].x; + float const b = this->matrix.r[0].y; + float const c = this->matrix.r[0].z; + float const d = this->matrix.r[1].x; + float const e = this->matrix.r[1].y; + float const f = this->matrix.r[1].z; + float const g = this->matrix.r[2].x; + float const h = this->matrix.r[2].y; + float const i = this->matrix.r[2].z; + + // M * transpose(M) must be identity matrix, where M is upper-left 3x3 matrix + if (!fuzzy(a * a + b * b + c * c, 1.0f) || !fuzzy(d * d + e * e + f * f, 1.0f) || + !fuzzy(g * g + h * h + i * i, 1.0f) || !fuzzy(a * d + b * e + c * f, 0.0f) || + !fuzzy(a * g + b * h + c * i, 0.0f) || !fuzzy(d * g + e * h + g * i, 0.0f)) + return false; + + return true; +} +/* ----------------- */ + +Matrix4::Matrix4(const Matrix4& a, const Matrix4& b) +{ + Matrix4::Multiply(a, b, this->matrix); +} + +Matrix4::Matrix4(float x, float y, float angle, float sx, float sy, float ox, + float oy, float kx, float ky) +{ + this->SetTransformation(x, y, angle, sx, sy, ox, oy, ky, ky); +} + +void Matrix4::operator*=(const Matrix4& m) +{ + Matrix4::Multiply(*this, m, this->matrix); +} + +Matrix4 Matrix4::operator*(const Matrix4& m) const +{ + return Matrix4(*this, m); +} + +void Matrix4::SetIdentity() +{ + Mtx_Identity(&this->matrix); +} + +void Matrix4::SetRawTransformation(float t00, float t10, float t01, float t11, + float x, float y) +{ + Mtx_Zeros(&this->matrix); + + this->matrix.r[2].z = this->matrix.r[3].w = 1.0f; + + this->matrix.r[0].x = t00; + this->matrix.r[1].x = t10; + + this->matrix.r[0].y = t01; + this->matrix.r[1].y = t11; + + this->matrix.r[0].w = x; + this->matrix.r[1].w = y; +} + +void Matrix4::SetTransformation(float x, float y, float angle, float sx, float sy, + float ox, float oy, float kx, float ky) +{ + Mtx_Zeros(&this->matrix); + + /* + ** Note that the offsets are applied before rotation, scaling, or shearing; + ** scaling and shearing are applied before rotation. + */ + float c = cosf(angle), s = sinf(angle); + + this->matrix.r[2].z = this->matrix.r[3].w = 1.0f; + + this->matrix.r[0].x = c * sx - ky * s * sy; // = a + this->matrix.r[1].x = s * sx + ky * c * sy; // = b + + this->matrix.r[0].y = kx * c * sx - s * sy; // = c + this->matrix.r[1].y = kx * s * sx + c * sy; // = d + + this->matrix.r[0].w = x - ox * this->matrix.r[0].x - oy * this->matrix.r[0].y; // = c + this->matrix.r[1].w = y - ox * this->matrix.r[1].x - oy * this->matrix.r[1].y; // = d +} + +void Matrix4::Translate(float x, float y) +{ + Mtx_Translate(&this->matrix, x, y, 0.0f, true); +} + +void Matrix4::Rotate(float r) +{ + Mtx_RotateZ(&this->matrix, r, true); +} + +void Matrix4::Scale(float sx, float sy) +{ + Mtx_Scale(&this->matrix, sx, sy, 1.0f); +} + +void Matrix4::GetApproximateScale(float& sx, float& sy) const +{ + // clang-format off + sx = sqrtf(this->matrix.r[0].x * this->matrix.r[0].x + this->matrix.r[0].y * this->matrix.r[0].y); + sy = sqrtf(this->matrix.r[1].x * this->matrix.r[1].x + this->matrix.r[1].y * this->matrix.r[1].y); + // clang-format on +} + +void Matrix4::Shear(float kx, float ky) +{ + C3D_Mtx mtx; + Mtx_Identity(&mtx); + + mtx.r[0].y = kx; + mtx.r[1].x = ky; + + Mtx_Multiply(&this->matrix, &this->matrix, &mtx); +} + +void Matrix4::TransformXY() +{ + auto instance = Renderer::Instance(); + Mtx_Copy(&instance.GetModelView(), &this->matrix); +} + +void Matrix4::TransformXY(const C3D_Mtx& elements) +{ + auto instance = Renderer::Instance(); + Mtx_Copy(&instance.GetModelView(), &elements); +} + +Matrix4 Matrix4::Inverse() const +{ + Matrix4 inv; + Mtx_Copy(&inv.matrix, &this->matrix); + + Mtx_Inverse(&inv.matrix); + + return inv; +} + +Matrix4 Matrix4::Ortho(float left, float right, float bottom, float top, + float near, float far) +{ + Matrix4 ortho; + Mtx_Ortho(&ortho.matrix, left, right, bottom, top, near, far, true); + + return ortho; +} diff --git a/platform/ctr/source/common/screen_ext.cpp b/platform/ctr/source/common/screen_ext.cpp new file mode 100644 index 000000000..8018b5422 --- /dev/null +++ b/platform/ctr/source/common/screen_ext.cpp @@ -0,0 +1,23 @@ +#include + +#include <3ds.h> + +namespace love +{ + std::span GetScreenInfo() + { + if (!gfxIs3D()) + return { altScreenInfo }; + else if (gfxIsWide()) + return { wideScreenInfo }; + + return { screenInfo }; + } + + const ScreenInfo& GetScreenInfo(Screen id) + { + const auto& info = GetScreenInfo(); + + return info[id]; + } +} // namespace love diff --git a/platform/ctr/source/modules/fontmodule_ext.cpp b/platform/ctr/source/modules/fontmodule_ext.cpp new file mode 100644 index 000000000..5bab0ee17 --- /dev/null +++ b/platform/ctr/source/modules/fontmodule_ext.cpp @@ -0,0 +1,162 @@ +#include + +#include + +#include +#include + +using namespace love; + +SystemFont::SystemFont(CFG_Region region) +{ + this->font = FontModule::LoadSystemFont(region, this->size); +} + +static CFNT_s* loadFromArchive(uint64_t title, const char* path, size_t& outSize) +{ + std::unique_ptr fontData; + long size = 0; + + Result result = 0; + CFNT_s* font = nullptr; + + result = romfsMountFromTitle(title, MEDIATYPE_NAND, "font"); + + if (R_FAILED(result)) + { + throw love::Exception("Failed to mount 'font:/' from NAND: %x", result); + return nullptr; + } + + auto* file = std::fopen(path, "rb"); + + if (!file) + { + std::fclose(file); + romfsUnmount("font"); + + throw love::Exception("Failed to open '%s'", path); + return nullptr; + } + + std::fseek(file, 0, SEEK_END); + size = std::ftell(file); + std::rewind(file); + + try + { + fontData = std::make_unique(size); + } + catch (std::bad_alloc&) + { + throw love::Exception("Not enough memory."); + } + + std::fread(fontData.get(), 1, size, file); + std::fclose(file); + + romfsUnmount("font"); + + uint32_t fontSize = *(uint32_t*)fontData.get() >> 0x08; + + font = (CFNT_s*)linearAlloc(fontSize); + + if (font && !decompress_LZ11(font, fontSize, nullptr, fontData.get() + 4, size - 4)) + { + linearFree(font); + throw love::Exception("Failed to decompress '%s'", path); + } + + outSize = fontSize; + + return font; +} + +static size_t getFontIndex(CFG_Region region) +{ + switch (region) + { + default: + case CFG_REGION_JPN ... CFG_REGION_AUS: + return 0; + case CFG_REGION_CHN: + return 1; + case CFG_REGION_KOR: + return 2; + case CFG_REGION_TWN: + return 3; + } +} + +CFNT_s* FontModule::LoadSystemFont(CFG_Region region, size_t& size) +{ + size_t index = getFontIndex(region); + + const auto archive = FontModule::FONT_ARCHIVE | (index << 8); + return loadFromArchive(archive, FontModule::fontPaths[index], size); +} + +FontModule::FontModule() +{ + size_t size = 0; + void* data = FontModule::LoadSystemFont(CFG_REGION_USA, size); + + this->defaultFontData.Set(new ByteData(data, size, true), Acquire::NORETAIN); +} + +Rasterizer* FontModule::NewImageRasterizer(ImageData* data, + const std::string& text, int extraSpacing, + float dpiScale) const +{ + std::vector glyphs {}; + glyphs.reserve(text.size()); + + try + { + utf8::iterator it(text.begin(), text.begin(), text.end()); + utf8::iterator end(text.end(), text.begin(), text.end()); + + while (it != end) + glyphs.push_back(*it++); + } + catch (utf8::exception& e) + { + throw love::Exception("UTF-8 decoding error: %s", e.what()); + } + + return this->NewImageRasterizer(data, &glyphs[0], (int)glyphs.size(), extraSpacing, dpiScale); +} + +Rasterizer* FontModule::NewImageRasterizer(ImageData* data, + uint32_t* glyphs, int glyphCount, + int extraSpacing, float dpiScale) const +{ + return new ImageRasterizer(data, glyphs, glyphCount, extraSpacing, dpiScale); +} + +Rasterizer* FontModule::NewRasterizer(FileData* data) const +{ + if (TrueTypeRasterizer::Accepts(this->library, data)) + return this->NewTrueTypeRasterizer(data, 12, TrueTypeRasterizer<>::HINTING_NORMAL); + + throw love::Exception("Invalid font file: %s", data->GetFilename().c_str()); + return nullptr; +} + +Rasterizer* FontModule::NewTrueTypeRasterizer( + Data* data, int size, TrueTypeRasterizer<>::Hinting hinting) const +{ + float dpiScale = 1.0f; + auto window = Module::GetInstance>(Module::M_WINDOW); + + if (window != nullptr) + dpiScale = window->GetDPIScale(); + + return this->NewTrueTypeRasterizer(data, size, dpiScale, hinting); +} + +Rasterizer* FontModule::NewTrueTypeRasterizer( + Data* data, int size, float dpiScale, TrueTypeRasterizer<>::Hinting hinting) const +{ + return new TrueTypeRasterizer(this->library, data, size, dpiScale, hinting); +} \ No newline at end of file diff --git a/platform/ctr/source/modules/graphics_ext.cpp b/platform/ctr/source/modules/graphics_ext.cpp new file mode 100644 index 000000000..f85a5dad5 --- /dev/null +++ b/platform/ctr/source/modules/graphics_ext.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +#include + +using Renderer = love::Renderer; +using namespace love; + +Graphics::Graphics() +{ + auto* window = Module::GetInstance>(Module::M_WINDOW); + + if (window != nullptr) + { + window->SetGraphics(this); + + if (window->IsOpen()) + window->SetWindow(); + } + + this->CheckSetDefaultFont(); +} + +/* objects */ + +void Graphics::SetShader() +{ + Shader::AttachDefault(Shader<>::STANDARD_DEFAULT); + this->states.back().shader.Set(nullptr); +} + +void Graphics::SetShader(Shader* shader) +{ + if (shader == nullptr) + return this->SetShader(); + + shader->Attach(); + this->states.back().shader.Set(shader); +} + +void Graphics::Draw(Drawable* drawable, const Matrix4& matrix) +{ + drawable->Draw(*this, matrix); +} + +void Graphics::Draw(Texture* texture, Quad* quad, + const Matrix4& matrix) +{ + texture->Draw(*this, quad, matrix); +} + +Texture* Graphics::NewTexture(const Texture<>::Settings& settings, + const Texture<>::Slices* slices) const +{ + return new Texture(this, settings, slices); +} + +void Graphics::Set3D(bool enabled) +{ + return ::Renderer::Instance().Set3D(enabled); +} + +bool Graphics::Get3D() +{ + return ::Renderer::Instance().Get3D(); +} + +void Graphics::SetMode(int x, int y, int width, int height) +{ + ::Renderer::Instance().CreateFramebuffers(); + this->RestoreState(this->states.back()); + + const auto type = Shader<>::STANDARD_DEFAULT; + + try + { + if (!Shader::defaults[type]) + { + auto* shader = new Shader(); + shader->LoadDefaults(type); + + Shader::defaults[type] = shader; + } + } + catch (love::Exception&) + { + throw; + } + + if (!Shader::current) + Shader::defaults[Shader<>::STANDARD_DEFAULT]->Attach(); + + this->created = true; +} diff --git a/platform/ctr/source/modules/imagemodule_ext.cpp b/platform/ctr/source/modules/imagemodule_ext.cpp new file mode 100644 index 000000000..2cf59a93c --- /dev/null +++ b/platform/ctr/source/modules/imagemodule_ext.cpp @@ -0,0 +1,8 @@ +#include + +using namespace love; + +ImageModule::ImageModule() +{ + this->formatHandlers.push_back(new T3XHandler()); +} diff --git a/platform/ctr/source/modules/joystickmodule_ext.cpp b/platform/ctr/source/modules/joystickmodule_ext.cpp new file mode 100644 index 000000000..464aae6ff --- /dev/null +++ b/platform/ctr/source/modules/joystickmodule_ext.cpp @@ -0,0 +1,61 @@ +#include + +using namespace love; + +JoystickModule::JoystickModule() +{ + for (int index = 0; index < this->GetCurrentJoystickCount(); index++) + this->AddJoystick(index); +} + +Joystick* JoystickModule::AddJoystick(int index) +{ + if (index < 0 || index >= this->GetCurrentJoystickCount()) + return nullptr; + + std::string guid = love::guid::GetGamepadGUID(guid::GAMEPAD_TYPE_NINTENDO_3DS); + Joystick* joystick = nullptr; + bool reused = false; + + for (auto stick : this->joysticks) + { + if (!stick->IsConnected() && stick->GetGUID() == guid) + { + joystick = stick; + reused = true; + break; + } + } + + if (!joystick) + { + joystick = new Joystick((int)this->joysticks.size()); + this->joysticks.push_back(joystick); + } + + this->RemoveJoystick(joystick); + + if (!joystick->Open(index)) + return nullptr; + + for (auto activeStick : this->active) + { + if (joystick->GetHandle() == activeStick->GetHandle()) + { + joystick->Close(); + + if (!reused) + { + this->joysticks.remove(joystick); + joystick->Release(); + } + + return activeStick; + } + } + + this->recentGUIDs[joystick->GetGUID()] = true; + this->active.push_back(joystick); + + return joystick; +} diff --git a/platform/ctr/source/modules/keyboard_ext.cpp b/platform/ctr/source/modules/keyboard_ext.cpp new file mode 100644 index 000000000..d61f5b680 --- /dev/null +++ b/platform/ctr/source/modules/keyboard_ext.cpp @@ -0,0 +1,32 @@ +#include +#include + +using namespace love; + +Keyboard::Keyboard() : + Keyboard(this->GetMaxEncodingLength(MAX_INPUT_LENGTH * 3) + 1), + state {}, + showing(false) +{} + +void Keyboard::SetTextInput(const KeyboardOptions& options) +{ + uint32_t maxLength = this->GetMaxEncodingLength(options.maxLength); + this->text = std::make_unique(maxLength); + + const auto type = (SwkbdType)options.type; + + swkbdInit(&this->state, type, 2, maxLength); + swkbdSetInitialText(&this->state, this->text.get()); + + swkbdSetHintText(&this->state, options.hint.data()); + + if (options.isPassword) + swkbdSetPasswordMode(&this->state, SWKBD_PASSWORD_HIDE_DELAY); + + this->showing = true; + const auto button = swkbdInputText(&this->state, this->text.get(), maxLength); + + if (button != SWKBD_BUTTON_LEFT) + HID::Instance().SendTextInput(this->GetText()); +} diff --git a/platform/ctr/source/modules/love_ext.cpp b/platform/ctr/source/modules/love_ext.cpp new file mode 100644 index 000000000..c5d8180e0 --- /dev/null +++ b/platform/ctr/source/modules/love_ext.cpp @@ -0,0 +1,32 @@ +#include "common/luax.hpp" + +#include "modules/love/love.hpp" + +#include <3ds.h> + +using namespace love; + +static constexpr int SOC_BUFFER_SIZE = 0x100000; +static constexpr int SOC_BUFFER_ALIGN = 0x1000; + +// static inline uint32_t* socBuffer = nullptr; + +template<> +void love::PreInit() +{ + // socBuffer = (uint32_t*)aligned_alloc(SOC_BUFFER_ALIGN, SOC_BUFFER_SIZE); + // socInit(socBuffer, SOC_BUFFER_SIZE); +} + +template<> +bool love::MainLoop(lua_State* L, int numArgs) +{ + return (luax::Resume(L, numArgs) == LUA_YIELD && aptMainLoop()); +} + +template<> +void love::OnExit() +{ + // socExit(); + // free(socBuffer); +} diff --git a/platform/ctr/source/modules/system_ext.cpp b/platform/ctr/source/modules/system_ext.cpp new file mode 100644 index 000000000..e197b81eb --- /dev/null +++ b/platform/ctr/source/modules/system_ext.cpp @@ -0,0 +1,229 @@ +#include <3ds.h> + +#include + +#include + +using namespace love; + +System<>::PowerState System::GetPowerInfo(uint8_t& percent) const +{ + uint8_t batteryState = 0; + PowerState state = PowerState::POWER_UNKNOWN; + + uint8_t percentRaw = 0; + MCUHWC_GetBatteryLevel(&percentRaw); + + percent = (percentRaw / 0xFF) * 100; + PTMU_GetBatteryChargeState(&batteryState); + + state = (batteryState) ? PowerState::POWER_CHARGING : PowerState::POWER_BATTERY; + + if (percent == 100 && !batteryState) + state = PowerState::POWER_CHARGED; + + return state; +} + +System<>::NetworkState System::GetNetworkInfo(uint8_t& signal) const +{ + uint32_t status = 0; + ACU_GetWifiStatus(&status); + + NetworkState state = NetworkState::NETWORK_UNKNOWN; + + signal = osGetWifiStrength(); + state = (status > 0) ? NetworkState::NETWORK_CONNECTED : NetworkState::NETWORK_DISCONNECTED; + + return state; +} + +int System::GetProcessorCount() +{ + if (this->info.processors != 0) + return this->info.processors; + + uint8_t model = 0; + CFGU_GetSystemModel(&model); + + int count = 2; + switch (model) + { + default: + break; + case CFG_MODEL_N3DS: + case CFG_MODEL_N3DSXL: + case CFG_MODEL_N2DSXL: + { + count = 4; + break; + } + } + + this->info.processors = count; + + return count; +} + +std::string_view System::GetSystemTheme() +{ + return "light"; +} + +std::string_view System::GetPreferredLocales() +{ + if (!this->info.locale.empty()) + return this->info.locale; + + uint8_t language = 0; + R_UNLESS(CFGU_GetSystemLanguage(&language), std::string {}); + + if (auto lang = System::languages.ReverseFind((CFG_Language)language)) + this->info.locale = *lang; + else + this->info.locale = "Unknown"; + + this->info.locale += "_"; + + uint8_t region = 0; + R_UNLESS(CFGU_SecureInfoGetRegion(®ion), std::string {}); + + if (auto r = System::countryCodes.ReverseFind((CFG_Region)region)) + this->info.locale += *r; + else + this->info.locale += "Unknown"; + + return this->info.locale; +} + +std::string_view System::GetVersion() +{ + if (!this->info.version.empty()) + return this->info.version; + + char version[256] { 0 }; + + R_UNLESS(osGetSystemVersionDataString(NULL, NULL, version, 256), std::string {}); + + this->info.version = version; + + return version; +} + +std::string_view System::GetModel() +{ + if (!this->info.model.empty()) + return this->info.model; + + uint8_t model = 0; + R_UNLESS(CFGU_GetSystemModel(&model), std::string {}); + + if (auto modelName = System::models.ReverseFind((CFG_SystemModel)model)) + this->info.model = *modelName; + else + this->info.model = "Unknown"; + + return this->info.model; +} + +std::string_view System::GetUsername() +{ + if (!this->info.username.empty()) + return this->info.username; + + char username[0x1C] { 0 }; + + R_UNLESS(FRD_GetMyScreenName(username, 0x1C), std::string {}); + + this->info.username = username; + + return username; +} + +static inline std::string MAKE_FRIEND_CODE(uint64_t friendCode) +{ + std::string result(0x0F, '\0'); + + const auto first = (int)((friendCode / 100000000) % 10000); + const auto second = (int)((friendCode / 10000) % 10000); + const auto third = (int)((friendCode % 10000)); + + snprintf(result.data(), result.size(), "%04i-%04i-%04i", first, second, third); + + return result; +} + +std::string_view System::GetFriendInfo() +{ + if (!this->info.friendCode.empty()) + return this->info.friendCode; + + FriendKey friendKey {}; + uint64_t friendCode = 0; + + /* Get the Friend Key for the user */ + R_UNLESS(FRD_GetMyFriendKey(&friendKey), std::string {}); + + /* Convert the principalId to friendCode */ + R_UNLESS(FRD_PrincipalIdToFriendCode(friendKey.principalId, &friendCode), std::string {}); + + this->info.friendCode = MAKE_FRIEND_CODE(friendCode); + + return this->info.friendCode; +} + +static Handle openPlayCoinsFile() +{ + Handle playCoinsFile; + const uint32_t path[3] = { MEDIATYPE_NAND, 0xF000000B, 0x00048000 }; + + const FS_Path archivePath = { PATH_BINARY, 0xC, path }; + const FS_Path filePath = fsMakePath(PATH_UTF16, u"/gamecoin.dat"); + + Result res = FSUSER_OpenFileDirectly(&playCoinsFile, ARCHIVE_SHARED_EXTDATA, archivePath, + filePath, FS_OPEN_READ | FS_OPEN_WRITE, 0); + + if (R_FAILED(res)) + throw love::Exception("Failed to open gamecoin.dat!"); + + return playCoinsFile; +} + +void System::SetPlayCoins(int amount) +{ + if (amount < 0 || amount > 300) + throw love::Exception("Cannot set Play Coin count to %d! Must be within [0, 300].", amount); + + Handle playCoinsFile = openPlayCoinsFile(); + + const uint8_t buffer[2] = { (uint8_t)amount, (uint8_t)(amount >> 8) }; + + Result res = FSFILE_Write(playCoinsFile, nullptr, 4, buffer, 2, 0); + + if (R_FAILED(res)) + { + FSFILE_Close(playCoinsFile); + throw love::Exception("Failed to write to gamecoin.dat!"); + } + + FSFILE_Close(playCoinsFile); +} + +int System::GetPlayCoins() const +{ + Handle playCoinsFile = openPlayCoinsFile(); + + uint8_t buffer[2] = { 0 }; + + Result res = FSFILE_Read(playCoinsFile, nullptr, 4, buffer, 2); + + if (R_FAILED(res)) + { + FSFILE_Close(playCoinsFile); + throw love::Exception("Failed to read gamecoin.dat!"); + } + + FSFILE_Close(playCoinsFile); + + return ((int)buffer[1] << 8) | buffer[0]; +} diff --git a/platform/ctr/source/modules/timer_ext.cpp b/platform/ctr/source/modules/timer_ext.cpp new file mode 100644 index 000000000..4553b88c4 --- /dev/null +++ b/platform/ctr/source/modules/timer_ext.cpp @@ -0,0 +1,53 @@ +#include <3ds.h> + +#include + +using namespace love; + +TickCounter Timer::counter {}; + +Timer::Timer() +{ + osTickCounterStart(&counter); + this->previousFpsUpdate = this->currentTime = Timer::GetTime(); +} + +double Timer::GetTime() +{ + counter.elapsed = svcGetSystemTick() - counter.reference; + return osTickCounterRead(&counter) / 1000.0; +} + +void Timer::Sleep(double seconds) const +{ + if (seconds >= 0) + { + auto time = std::chrono::duration(seconds); + svcSleepThread(std::chrono::duration(time).count()); + } +} + +double Timer::Step() +{ + this->frames++; + + this->previousTime = this->currentTime; + this->currentTime = Timer::GetTime(); + + this->delta = this->currentTime - this->previousTime; + + if (this->delta < 0) + this->delta = 0; + + double timeSinceLast = (this->currentTime - this->previousFpsUpdate); + + if (timeSinceLast > this->fpsUpdateFrequency) + { + this->fps = int((this->frames / timeSinceLast) + 0.5); + this->averageDelta = timeSinceLast / frames; + this->previousFpsUpdate = this->currentTime; + this->frames = 0; + } + + return this->delta; +} diff --git a/platform/ctr/source/modules/window_ext.cpp b/platform/ctr/source/modules/window_ext.cpp new file mode 100644 index 000000000..a1a63f496 --- /dev/null +++ b/platform/ctr/source/modules/window_ext.cpp @@ -0,0 +1,98 @@ +#include + +#include +#include + +#include <3ds.h> + +using namespace love; + +Window::Window() +{ + this->sleepAllowed = this->IsDisplaySleepEnabled(); +} + +Window::~Window() +{ + this->Close(); + + this->SetDisplaySleepEnabled(this->sleepAllowed); + this->graphics.Set(nullptr); +} + +bool Window::SetWindow(int width, int height, WindowSettings* settings) +{ + if (!this->graphics.Get()) + this->graphics.Set((Module::GetInstance>(Module::M_GRAPHICS))); + + this->Close(); + + /* handled internally */ + if (!this->CreateWindowAndContext(0, 0, 0, 0)) + return false; + + if (this->graphics.Get()) + this->graphics->SetMode(400, 240, 400, 240); + + return true; +} + +bool Window::CreateWindowAndContext(int x, int y, int width, int height) +{ + this->open = true; + return true; +} + +void Window::GetWindow(int& width, int& height, WindowSettings& settings) +{} + +void Window::Close() +{ + this->open = false; +} + +bool Window::OnSizeChanged(int width, int height) +{ + return true; +} + +std::string_view Window::GetDisplayName(int displayIndex) const +{ + switch (displayIndex) + { + case 0: + return "left"; + case 1: + return "right"; + case 2: + return "bottom"; + default: + break; + } + + return std::string_view {}; +} + +std::vector::WindowSize> Window::GetFullscreenSizes(int displayIndex) +{ + return {}; +} + +void Window::GetDesktopDimensions(int displayIndex, int& width, int& height) +{} + +void Window::SetPosition(int x, int y, int displayIndex) +{} + +void Window::GetPosition(int& x, int& y, int& displayIndex) +{} + +void Window::SetDisplaySleepEnabled(bool enabled) +{ + aptSetSleepAllowed(enabled); +} + +bool Window::IsDisplaySleepEnabled() const +{ + return aptIsSleepAllowed(); +} diff --git a/platform/ctr/source/modules/wrap_graphics_ext.cpp b/platform/ctr/source/modules/wrap_graphics_ext.cpp new file mode 100644 index 000000000..c67e84daf --- /dev/null +++ b/platform/ctr/source/modules/wrap_graphics_ext.cpp @@ -0,0 +1,39 @@ +#include +#include + +using namespace love; + +#define instance() (Module::GetInstance>(Module::M_GRAPHICS)) + +int Wrap_Graphics::Get3D(lua_State* L) +{ + luax::PushBoolean(L, instance()->Get3D()); + + return 1; +} + +int Wrap_Graphics::Set3D(lua_State* L) +{ + bool enabled = luax::CheckBoolean(L, 1); + + instance()->Set3D(enabled); + + return 0; +} + +int Wrap_Graphics::GetDepth(lua_State* L) +{ + lua_pushnumber(L, osGet3DSliderState()); + + return 1; +} + +// clang-format off +static constexpr luaL_Reg functions[] = +{ + { "get3D", Wrap_Graphics::Get3D }, + { "set3D", Wrap_Graphics::Set3D }, + { "getDepth", Wrap_Graphics::GetDepth } +}; +// clang-format on +std::span Wrap_Graphics::extensions = functions; diff --git a/platform/ctr/source/modules/wrap_system_ext.cpp b/platform/ctr/source/modules/wrap_system_ext.cpp new file mode 100644 index 000000000..b5c539253 --- /dev/null +++ b/platform/ctr/source/modules/wrap_system_ext.cpp @@ -0,0 +1,36 @@ +#include +#include + +using namespace love; + +#define instance() (Module::GetInstance>(Module::M_SYSTEM)) + +int Wrap_System::SetPlayCoins(lua_State* L) +{ + int amount = luaL_checknumber(L, 1); + + luax::CatchException(L, [&]() { instance()->SetPlayCoins(amount); }); + + return 0; +} + +int Wrap_System::GetPlayCoins(lua_State* L) +{ + int amount = 0; + + luax::CatchException(L, [&]() { amount = instance()->GetPlayCoins(); }); + + lua_pushnumber(L, amount); + + return 1; +} + +// clang-format off +static constexpr luaL_Reg functions[] = +{ + { "getPlayCoins", Wrap_System::GetPlayCoins }, + { "setPlayCoins", Wrap_System::SetPlayCoins } +}; +// clang-format on + +std::span Wrap_System::extensions = functions; diff --git a/platform/ctr/source/objects/imagedata_ext.cpp b/platform/ctr/source/objects/imagedata_ext.cpp new file mode 100644 index 000000000..a719edc02 --- /dev/null +++ b/platform/ctr/source/objects/imagedata_ext.cpp @@ -0,0 +1,56 @@ +#include +#include + +using namespace love; + +void ImageData::Paste(ImageData* source, int x, int y, Rect& sourceRect) +{ + PixelFormat destFormat = this->GetFormat(); + PixelFormat srcFormat = source->GetFormat(); + + if (destFormat != srcFormat) + throw love::Exception("Pixel formats do not match."); + + if (srcFormat != PIXELFORMAT_RGBA8_UNORM && destFormat != PIXELFORMAT_RGBA8_UNORM) + throw love::Exception("Both source and destination formats must be RGBA8."); + + const auto srcWidth = source->GetWidth(); + const auto srcHeight = source->GetHeight(); + + const auto destWidth = this->GetWidth(); + const auto destHeight = this->GetHeight(); + + this->AdjustPaste(source, x, y, destWidth, destHeight, sourceRect); + + std::unique_lock lock(source->mutex); + std::unique_lock other(this->mutex); + + uint8_t* srcData = (uint8_t*)source->GetData(); + uint8_t* dstData = (uint8_t*)this->GetData(); + + auto getFunction = source->pixelGetFunction; + auto setFunction = this->pixelSetFunction; + + const auto _srcWidth = NextPo2(srcWidth); + const auto _dstWidth = NextPo2(destWidth); + + for (int _y = 0; _y < std::min(sourceRect.h, destHeight - y); _y++) + { + for (int _x = 0; _x < std::min(sourceRect.w, destWidth - x); _x++) + { + Color color {}; + + // clang-format off + Vector2 srcPosition { (sourceRect.x + _x), (sourceRect.y + _y) }; + const auto* sourcePixel = Color::FromTile(srcData, _srcWidth, srcPosition); + + getFunction((const Pixel*)sourcePixel, color); + + Vector2 dstPosition { (x + _x), (y + _y) }; + auto* destinationPixel = Color::FromTile(dstData, _dstWidth, dstPosition); + + setFunction(color, (Pixel*)destinationPixel); + // clang-format on + } + } +} \ No newline at end of file diff --git a/platform/ctr/source/objects/joystick_ext.cpp b/platform/ctr/source/objects/joystick_ext.cpp new file mode 100644 index 000000000..43e3448a3 --- /dev/null +++ b/platform/ctr/source/objects/joystick_ext.cpp @@ -0,0 +1,313 @@ +#include +#include + +#include +#include + +using namespace love; + +// clang-format off +constexpr BidirectionalMap buttons = { + Joystick<>::GAMEPAD_BUTTON_A, KEY_A, + Joystick<>::GAMEPAD_BUTTON_B, KEY_B, + Joystick<>::GAMEPAD_BUTTON_X, KEY_X, + Joystick<>::GAMEPAD_BUTTON_Y, KEY_Y, + Joystick<>::GAMEPAD_BUTTON_BACK, KEY_SELECT, + Joystick<>::GAMEPAD_BUTTON_START, KEY_START, + Joystick<>::GAMEPAD_BUTTON_LEFTSHOULDER, KEY_L, + Joystick<>::GAMEPAD_BUTTON_RIGHTSHOULDER, KEY_R, + Joystick<>::GAMEPAD_BUTTON_DPAD_UP, KEY_DUP, + Joystick<>::GAMEPAD_BUTTON_DPAD_DOWN, KEY_DDOWN, + Joystick<>::GAMEPAD_BUTTON_DPAD_RIGHT, KEY_DRIGHT, + Joystick<>::GAMEPAD_BUTTON_DPAD_LEFT, KEY_DLEFT +}; + +constexpr BidirectionalMap axes = { + Joystick<>::GAMEPAD_AXIS_LEFTX, KEY_CPAD_LEFT | KEY_CPAD_RIGHT, + Joystick<>::GAMEPAD_AXIS_LEFTY, KEY_CPAD_UP | KEY_CPAD_DOWN, + Joystick<>::GAMEPAD_AXIS_RIGHTX, KEY_CSTICK_LEFT | KEY_CSTICK_RIGHT, + Joystick<>::GAMEPAD_AXIS_RIGHTY, KEY_CSTICK_UP | KEY_CSTICK_DOWN +}; +// clang-format on + +Joystick::Joystick(int id) : buttonStates {} +{ + this->instanceId = -1; + this->id = id; + + this->handle = std::make_unique(1); +} + +Joystick::Joystick(int id, int index) : Joystick(id) +{ + this->Open(index); +} + +Joystick::~Joystick() +{ + this->Close(); +} + +bool Joystick::Open(int index) +{ + this->Close(); + + this->name = guid::GetGamepadName(this->GetGamepadType()); + this->guid = guid::GetGamepadGUID(this->GetGamepadType()); + + this->instanceId = index; + + return this->IsConnected(); +} + +void Joystick::Close() +{ + this->instanceId = -1; +} + +void Joystick::GetDeviceInfo(int& vendor, int& product, int& version) +{ + guid::DeviceInfo info {}; + + if (!guid::GetDeviceInfo(this->GetGamepadType(), info)) + return; + + vendor = info.vendorId; + product = info.productId; + version = info.productVersion; +} + +int Joystick::GetAxisCount() const +{ + return guid::GetGamepadAxisCount(this->GetGamepadType()); +} + +int Joystick::GetButtonCount() const +{ + return guid::GetGamepadButtonCount(this->GetGamepadType()); +} + +void Joystick::Update() +{ + this->buttonStates.pressed = hidKeysDown(); + this->buttonStates.released = hidKeysUp(); + this->buttonStates.held = hidKeysHeld(); +} + +bool Joystick::IsDown(JoystickInput& result) +{ + if (!this->IsConnected()) + return false; + + if (!this->buttonStates.pressed) + return false; + + uint32_t button = 0; + + const auto entries = buttons.GetEntries(); + + for (size_t index = 0; index < entries.size(); index++) + { + button = (uint32_t)entries[index].second; + + if ((int)entries[index].second == -1) + continue; + + if (button & this->buttonStates.pressed) + { + this->buttonStates.pressed ^= button; + result = { .type = InputType::INPUT_TYPE_BUTTON, + .button = entries[index].first, + .buttonNumber = (int)index }; + + return true; + } + } + + return false; +} + +bool Joystick::IsUp(JoystickInput& result) +{ + uint32_t button = 0; + + if (!this->buttonStates.released) + return false; + + const auto entries = buttons.GetEntries(); + + for (size_t index = 0; index < entries.size(); index++) + { + button = (uint32_t)entries[index].second; + + if ((int)entries[index].second == -1) + continue; + + if (button & this->buttonStates.released) + { + this->buttonStates.released ^= button; + result = { .type = InputType::INPUT_TYPE_BUTTON, + .button = entries[index].first, + .buttonNumber = (int)index }; + + return true; + } + } + + return false; +} + +float Joystick::GetAxis(int index) +{ + if (!this->IsConnected() || index < 0 || index >= this->GetAxisCount()) + return 0.0f; + + if (index == 0 || index == 1) + { + circlePosition leftStick {}; + hidCircleRead(&leftStick); + + float value = (index == 1) ? leftStick.dx : leftStick.dy; + return std::clamp(value / Joystick::JoystickMax, -1.0f, 1.0f); + } + else if (index == 2 || index == 3) + { + circlePosition rightStick {}; + irrstCstickRead(&rightStick); + + float value = (index == 3) ? rightStick.dx : rightStick.dy; + return std::clamp(value / Joystick::JoystickMax, -1.0f, 1.0f); + } + else if (index == 4) + { + if (hidKeysHeld() & KEY_ZL) + return 1.0f; + + return 0.0f; + } + else if (index == 5) + { + if (hidKeysHeld() & KEY_ZR) + return 1.0f; + + return 0.0f; + } + + return 0.0f; +} + +std::vector Joystick::GetAxes() +{ + std::vector axes; + int count = this->GetAxisCount(); + + if (!this->IsConnected() || count <= 0) + return axes; + + axes.reserve(count); + + for (int index = 0; index < count; index++) + axes.push_back(this->GetAxis(index)); + + return axes; +} + +bool Joystick::IsAxisChanged(GamepadAxis axis) +{ + auto dsAxis = *axes.Find(axis); + + if (dsAxis & this->buttonStates.held) + { + this->buttonStates.held ^= dsAxis; + return true; + } + + if (dsAxis & this->buttonStates.released) + { + this->buttonStates.released ^= dsAxis; + return true; + } + + return false; +} + +bool Joystick::HasSensor(Sensor::SensorType type) const +{ + return true; +} + +bool Joystick::IsSensorEnabled(Sensor::SensorType type) +{ + return this->sensors[type]; +} + +void Joystick::SetSensorEnabled(Sensor::SensorType type, bool enabled) +{ + if (this->sensors[type] && !enabled) + this->sensors[type] = nullptr; + else if (this->sensors[type] == nullptr && enabled) + { + SensorBase* sensor = nullptr; + + if (type == Sensor::SENSOR_ACCELEROMETER) + sensor = new Accelerometer(); + else if (type == Sensor::SENSOR_GYROSCOPE) + sensor = new Gyroscope(); + + sensor->SetEnabled(enabled); + this->sensors[type] = sensor; + } +} + +std::vector Joystick::GetSensorData(Sensor::SensorType type) +{ + if (!this->IsSensorEnabled(type)) + { + auto name = Sensor::sensorTypes.ReverseFind(type); + throw love::Exception("\"%s\" sensor is not enabled", *name); + } + + return this->sensors[type]->GetData(); +} + +bool Joystick::IsDown(const std::vector& buttons) const +{ + if (!this->IsConnected()) + return false; + + int count = this->GetButtonCount(); + auto records = ::buttons.GetEntries(); + + for (int button : buttons) + { + if (button < 0 || button >= count) + continue; + + if (hidKeysHeld() & records[button].second) + return true; + } + + return false; +} + +float Joystick::GetGamepadAxis(GamepadAxis axis) +{ + if (!this->IsConnected()) + return 0.0f; + + int getAxis = (int)axis; + return this->GetAxis(getAxis - 1); +} + +bool Joystick::IsGamepadDown(const std::vector& buttons) const +{ + uint32_t heldSet = hidKeysHeld(); + + for (auto button : buttons) + { + if (auto found = ::buttons.Find(button)) + return heldSet & (uint32_t)*found; + } + + return false; +} diff --git a/platform/ctr/source/objects/shader_ext.cpp b/platform/ctr/source/objects/shader_ext.cpp new file mode 100644 index 000000000..f801b5365 --- /dev/null +++ b/platform/ctr/source/objects/shader_ext.cpp @@ -0,0 +1,124 @@ +#include + +#include + +#include + +using namespace love; + +#define SHADERS_DIR "romfs:/shaders/" + +#define DEFAULT_SHADER (SHADERS_DIR "main_v_pica.shbin") + +Shader::Shader() : uniforms {} +{} + +Shader::Shader(Data* data) : uniforms {} +{ + std::string error; + + if (!this->Validate(data, error)) + throw love::Exception("Invalid shader: %s", error.c_str()); +} + +Shader::~Shader() +{ + for (int i = 0; i < STANDARD_MAX_ENUM; i++) + { + if (this == Shader::defaults[i]) + Shader::defaults[i] = nullptr; + } + + if (Shader::current == this) + Shader::AttachDefault(STANDARD_DEFAULT); + + shaderProgramFree(&this->program); + DVLB_Free(this->binary); +} + +void Shader::LoadDefaults(StandardShader) +{ + std::string error {}; + + if (!this->Validate(DEFAULT_SHADER, error)) + throw love::Exception("Failed to load shader: %s.", error.c_str()); + + shaderProgramInit(&this->program); + shaderProgramSetVsh(&this->program, &this->binary->DVLE[0]); + + this->uniforms.uLocMdlView = + shaderInstanceGetUniformLocation(this->program.vertexShader, "mdlvMtx"); + this->uniforms.uLocProjMtx = + shaderInstanceGetUniformLocation(this->program.vertexShader, "projMtx"); +} + +void Shader::AttachDefault(StandardShader type) +{ + Shader* defaultShader = Shader::defaults[type]; + + if (defaultShader == nullptr) + { + current = nullptr; + return; + } + + if (current != defaultShader) + defaultShader->Attach(); +} + +void Shader::Attach() +{ + if (Shader::current != this) + { + C3D_BindProgram(&this->program); + Shader::current = this; + } +} + +bool Shader::Validate(const char* filepath, std::string& error) +{ + FILE* file = std::fopen(filepath, "r"); + + if (!file) + { + error = "File does not exist."; + std::fclose(file); + return false; + } + + std::fseek(file, 0, SEEK_END); + long size = std::ftell(file); + std::rewind(file); + + try + { + this->data = std::make_unique(size / sizeof(uint32_t)); + } + catch (std::bad_alloc&) + { + error = "Not enough memory."; + return false; + } + + long readSize = (long)std::fread(this->data.get(), 1, size, file); + + if (readSize != size) + { + error = "Failed to read whole file."; + std::fclose(file); + return false; + } + + std::fclose(file); + this->binary = DVLB_ParseFile(this->data.get(), size); + + return true; +} + +bool Shader::Validate(Data* data, std::string& error) +{ + this->data = std::make_unique(data->GetSize() / 4); + std::memcpy(this->data.get(), data->GetData(), data->GetSize()); + + return true; +} \ No newline at end of file diff --git a/platform/ctr/source/objects/source_ext.cpp b/platform/ctr/source/objects/source_ext.cpp new file mode 100644 index 000000000..a3dd887bd --- /dev/null +++ b/platform/ctr/source/objects/source_ext.cpp @@ -0,0 +1,687 @@ +#include +#include + +#include + +using namespace love; + +using DSP = love::DSP; + +template<> +Source::DataBuffer::DataBuffer(const void* data, size_t size) : size(size) +{ + this->buffer = (int16_t*)linearAlloc(size); + std::memcpy(this->buffer, (int16_t*)data, size); + + DSP_FlushDataCache(this->buffer, this->size); +} + +template<> +Source::DataBuffer::~DataBuffer() +{ + linearFree(this->buffer); +} + +Source::Source(AudioPool* pool, SoundData* soundData) : + Source<>(TYPE_STATIC), + pool(pool) +{ + this->sampleRate = soundData->GetSampleRate(); + this->channels = soundData->GetChannelCount(); + this->bitDepth = soundData->GetBitDepth(); + this->samplesOffset = 0; + + std::fill_n(this->buffers, 2, ndspWaveBuf {}); + + this->staticBuffer = std::make_shared(soundData->GetData(), soundData->GetSize()); +} + +Source::Source(AudioPool* pool, Decoder* decoder) : Source<>(TYPE_STREAM), pool(pool) +{ + this->decoder = decoder; + this->sampleRate = decoder->GetSampleRate(); + this->channels = decoder->GetChannelCount(); + this->bitDepth = decoder->GetBitDepth(); + this->bufferCount = MAX_BUFFERS; + this->samplesOffset = 0; + + std::fill_n(this->buffers, this->bufferCount, ndspWaveBuf {}); + + for (auto& buffer : this->buffers) + { + buffer.data_pcm16 = (int16_t*)linearAlloc(decoder->GetSize()); + buffer.status = NDSP_WBUF_DONE; + } +} + +Source::Source(AudioPool* pool, int sampleRate, int bitDepth, int channels, + int buffers) : + Source<>(TYPE_QUEUE), + pool(pool) +{ + this->sampleRate = sampleRate; + this->channels = channels; + this->bitDepth = bitDepth; + this->bufferCount = buffers; + this->samplesOffset = 0; + + if (buffers < 1 || (size_t)buffers > Source::MAX_BUFFERS) + buffers = MAX_BUFFERS; + + std::fill_n(this->buffers, this->bufferCount, ndspWaveBuf {}); + + for (size_t index = 0; index < (size_t)this->bufferCount; index++) + this->buffers[index].status = NDSP_WBUF_DONE; +} + +Source::Source(const Source& other) : Source<>(other.sourceType), pool(other.pool) +{ + this->staticBuffer = other.staticBuffer; + this->decoder = nullptr; + this->sampleRate = other.sampleRate; + this->channels = other.channels; + this->bitDepth = other.bitDepth; + this->bufferCount = other.bufferCount; + this->samplesOffset = other.samplesOffset; + + if (this->sourceType == TYPE_STREAM) + { + if (other.decoder.Get()) + this->decoder.Set(other.decoder->Clone(), Acquire::NORETAIN); + } + + std::fill_n(this->buffers, this->bufferCount, ndspWaveBuf {}); + + for (size_t index = 0; index < this->bufferCount; index++) + { + if (this->sourceType == TYPE_STREAM) + this->buffers[index].data_pcm16 = (int16_t*)linearAlloc(this->decoder->GetSize()); + + this->buffers[index].status = NDSP_WBUF_DONE; + } +} + +Source::~Source() +{ + this->Stop(); + + for (auto& buffer : this->buffers) + { + if (buffer.data_pcm16) + linearFree(buffer.data_pcm16); + } +} + +Source* Source::Clone() +{ + return new Source(*this); +} + +bool Source::Play() +{ + uint8_t wasPlaying = false; + + { + auto lock = this->pool->Lock(); + if (!this->pool->AssignSource(this, this->channel, wasPlaying)) + return this->valid = false; + } + + if (!wasPlaying) + { + if (!(this->valid = this->PlayAtomic(this->buffers[0]))) + return false; + + this->ResumeAtomic(); + + { + auto lock = this->pool->Lock(); + this->pool->AddSource(this, this->channel); + } + + return this->valid; + } + + this->ResumeAtomic(); + + return this->valid = true; +} + +void Source::Reset() +{ + ::DSP::Instance().ChannelReset(this->channel, this->channels, this->bitDepth, this->sampleRate); +} + +void Source::Stop() +{ + if (!this->valid) + return; + + auto lock = this->pool->Lock(); + this->pool->ReleaseSource(this); +} + +void Source::Pause() +{ + auto lock = this->pool->Lock(); + + if (this->pool->IsPlaying(this)) + this->PauseAtomic(); +} + +bool Source::IsPlaying() const +{ + return this->valid && !::DSP::Instance().IsChannelPaused(this->channel); +} + +bool Source::IsFinished() const +{ + if (!this->valid) + return false; + + if (this->sourceType == TYPE_STREAM && (this->IsLooping() || !this->decoder->IsFinished())) + return false; + + if (this->sourceType == TYPE_STATIC) + return this->buffers[0].status == NDSP_WBUF_DONE; + + return ::DSP::Instance().IsChannelPlaying(this->channel) == false; +} + +bool Source::Update() +{ + if (!this->valid) + return false; + + switch (this->sourceType) + { + case TYPE_STATIC: + return !this->IsFinished(); + case TYPE_STREAM: + { + if (this->IsFinished()) + return false; + + bool other = !this->current; + if (this->buffers[other].status == NDSP_WBUF_DONE) + { + int decoded = this->StreamAtomic(this->buffers[other], this->decoder.Get()); + + if (decoded == 0) + return false; + + ::DSP::Instance().ChannelAddBuffer(this->channel, &this->buffers[other]); + this->samplesOffset += this->buffers[other].nsamples; + + this->current = !this->current; + } + return true; + } + case TYPE_QUEUE: + break; + default: + break; + } + + return false; +} + +void Source::SetVolume(float volume) +{ + if (volume < this->GetMinVolume() || volume > this->GetMaxVolume()) + return; + + if (this->valid) + ::DSP::Instance().ChannelSetVolume(this->channel, volume); + + this->volume = volume; +} + +float Source::GetVolume() const +{ + if (this->valid) + return ::DSP::Instance().ChannelGetVolume(this->channel); + + return this->volume; +} + +/* todo */ +void Source::Seek(double offset, Unit unit) +{ + // auto lock = this->pool->Lock(); + + int offsetSamples = 0; + double offsetSeconds = 0.0f; + + switch (unit) + { + case UNIT_SAMPLES: + { + offsetSamples = (int)offset; + offsetSeconds = offset / ((double)this->sampleRate / this->channels); + break; + } + case UNIT_SECONDS: + default: + { + offsetSeconds = offset; + offsetSamples = (int)(offset * sampleRate * this->channels); + } + } + + bool wasPlaying = this->IsPlaying(); + + switch (this->sourceType) + { + case TYPE_STATIC: + { + if (this->valid) + this->Stop(); + + this->samplesOffset = offsetSamples; + + if (wasPlaying) + this->Play(); + + break; + } + case TYPE_STREAM: + { + if (this->valid) + this->Stop(); + + this->decoder->Seek(offsetSeconds); + + if (wasPlaying) + this->Play(); + + break; + } + case TYPE_QUEUE: + { + /* todo */ + } + default: + break; + } + + if (wasPlaying && (this->sourceType == TYPE_STREAM && !this->IsPlaying())) + { + this->Stop(); + + if (this->IsLooping()) + this->Play(); + + return; + } + + this->samplesOffset = offsetSamples; +} + +/* todo */ +double Source::Tell(Unit unit) +{ + auto lock = this->pool->Lock(); + + int offset = 0; + + if (this->valid) + { + if (this->sourceType == TYPE_STATIC) + offset += ::DSP::Instance().ChannelGetSampleOffset(this->channel); + else + offset = this->samplesOffset; + } + + if (unit == UNIT_SECONDS) + return offset / (double)sampleRate / this->channels; + + return offset; +} + +double Source::GetDuration(Unit unit) +{ + auto lock = this->pool->Lock(); + + switch (this->sourceType) + { + case TYPE_STATIC: + { + size_t size = this->staticBuffer->GetSize(); + size_t samples = (size / this->channels) / (this->bitDepth / 8); + + if (unit == UNIT_SAMPLES) + return (double)samples; + + return (double)samples / (double)sampleRate; + } + case TYPE_STREAM: + { + /* vorbisidec 1.2.1 uses ms, not sec, convert */ + double seconds = this->decoder->GetDuration() / 1000.0; + + if (unit == UNIT_SECONDS) + return seconds; + + return seconds * decoder->GetSampleRate(); + } + case TYPE_QUEUE: + { + /* todo */ + break; + } + default: + return 0.0; + } + + return 0.0; +} + +void Source::SetLooping(bool loop) +{ + if (this->sourceType == TYPE_QUEUE) + throw QueueLoopingException(); + + if (this->valid && this->sourceType == TYPE_STATIC) + this->buffers[0].looping = loop; + + this->looping = loop; +} + +/* todo */ +bool Source::Queue(void* data, size_t length, int sampleRate, int bitDepth, + int channels) +{ + if (this->sourceType != TYPE_QUEUE) + throw QueueTypeMismatchException(); + + if (sampleRate != this->sampleRate || bitDepth != this->bitDepth || channels != this->channels) + throw QueueFormatMismatchException(); + + if (length % (bitDepth / 8 * channels) != 0) + throw QueueMalformedLengthException(bitDepth / 8 * channels); + + if (length == 0) + return true; + + // ndspWaveBuf buffer {}; + // buffer.data_pcm16 = (int16_t*)linearAlloc(length); + // std::memcpy(buffer.data_pcm16, data, length); + + // buffer.nsamples = (int)((length / this->channels) / (this->bitDepth / 8)); + // this->queue.push(buffer); + + return true; +} + +int Source::GetFreeBufferCount() const +{ + if (this->sourceType == TYPE_STATIC) + return 0; + + size_t count = 0; + for (auto& buffer : this->buffers) + count += (buffer.status == NDSP_WBUF_DONE) ? 1 : 0; + + return count; +} + +void Source::PrepareAtomic() +{ + this->Reset(); + + switch (this->sourceType) + { + case TYPE_STATIC: + { + const auto size = this->staticBuffer->GetSize(); + + // clang-format off + this->buffers[0].nsamples = (int)((size / this->channels) / (this->bitDepth / 8)) - (this->samplesOffset / this->channels); + this->buffers[0].data_pcm16 = this->staticBuffer->GetBuffer() + (size_t)this->samplesOffset; + // clang-format on + + this->buffers[0].looping = this->looping; + + break; + } + case TYPE_STREAM: + { + if (this->StreamAtomic(this->buffers[0], this->decoder.Get()) == 0) + break; + + if (this->decoder->IsFinished()) + break; + + break; + } + case TYPE_QUEUE: + break; /* todo */ + default: + break; + } +} + +int Source::StreamAtomic(ndspWaveBuf& buffer, Decoder* decoder) +{ + int decoded = std::max(decoder->Decode(), 0); + + if (decoded > 0) + { + std::memcpy(buffer.data_pcm16, (int16_t*)decoder->GetBuffer(), decoded); + buffer.nsamples = (int)((decoded / this->channels) / (this->bitDepth / 8)); + + DSP_FlushDataCache(buffer.data_pcm16, decoded); + } + + if (decoder->IsFinished() && this->IsLooping()) + decoder->Rewind(); + + return decoded; +} + +/* todo */ +void Source::TeardownAtomic() +{ + ::DSP::Instance().ChannelStop(this->channel); + + switch (this->sourceType) + { + case TYPE_STATIC: + break; + case TYPE_STREAM: + { + this->decoder->Rewind(); + + for (auto& buffer : this->buffers) + buffer.status = NDSP_WBUF_DONE; + + break; + } + case TYPE_QUEUE: + break; /* todo */ + default: + break; + } + + this->valid = false; + this->samplesOffset = 0; +} + +bool Source::PlayAtomic(ndspWaveBuf& waveBuffer) +{ + this->PrepareAtomic(); + + ::DSP::Instance().ChannelAddBuffer(this->channel, &waveBuffer); + + if (this->sourceType != TYPE_STREAM) + this->samplesOffset = 0; + + if (this->sourceType == TYPE_STREAM) + this->valid = true; + + return true; +} + +void Source::StopAtomic() +{ + if (!this->valid) + return; + + this->TeardownAtomic(); +} + +void Source::PauseAtomic() +{ + if (this->valid) + ::DSP::Instance().ChannelPause(this->channel); +} + +void Source::ResumeAtomic() +{ + if (this->valid) + ::DSP::Instance().ChannelPause(this->channel, false); +} + +bool Source::Play(const std::vector& sources) +{ + if (sources.size() == 0) + return true; + + auto* pool = ((Source*)sources[0])->pool; + auto lock = pool->Lock(); + + std::vector wasPlaying(sources.size()); + std::vector channels(sources.size()); + + for (size_t index = 0; index < sources.size(); index++) + { + if (!pool->AssignSource((Source*)sources[index], channels[index], wasPlaying[index])) + { + for (size_t j = 0; j < index; j++) + { + if (!wasPlaying[j]) + pool->ReleaseSource((Source*)sources[index], false); + } + + return false; + } + } + + std::vector toPlay; + toPlay.reserve(sources.size()); + + for (size_t index = 0; index < sources.size(); index++) + { + if (wasPlaying[index] && sources[index]->IsPlaying()) + continue; + + if (!wasPlaying[index]) + { + auto* source = (Source*)sources[index]; + source->channel = channels[index]; + + source->PrepareAtomic(); + } + + toPlay.push_back(sources[index]); + } + + for (auto& _source : toPlay) + { + auto* source = (Source*)_source; + + if (source->sourceType != TYPE_STREAM) + source->samplesOffset = 0; + + if (!(_source->valid = _source->Play())) + return false; + + pool->AddSource(_source, source->channel); + } + + return true; +} + +void Source::Stop(const std::vector& sources) +{ + if (sources.size() == 0) + return; + + auto* pool = ((Source*)sources[0])->pool; + auto lock = pool->Lock(); + + std::vector toStop; + toStop.reserve(sources.size()); + + for (auto& _source : sources) + { + auto* source = (Source*)_source; + + if (source->valid) + toStop.push_back(source); + } + + for (auto& _source : toStop) + { + auto* source = (Source*)_source; + + if (source->valid) + source->TeardownAtomic(); + + pool->ReleaseSource(source, false); + } +} + +void Source::Pause(const std::vector& sources) +{ + if (sources.size() == 0) + return; + + auto lock = ((Source*)sources[0])->pool->Lock(); + + for (auto& _source : sources) + { + auto* source = (Source*)_source; + + if (source->valid) + source->PauseAtomic(); + } +} + +/* todo */ +std::vector*> Source::Pause(AudioPool* pool) +{ + std::vector sources; + + { + auto lock = pool->Lock(); + sources = pool->GetPlayingSources(); + + auto end = std::remove_if(sources.begin(), sources.end(), + [](Source* source) { return !source->IsPlaying(); }); + + sources.erase(end, sources.end()); + } + + Source::Pause(sources); + + return sources; +} + +void Source::Stop(AudioPool* pool) +{ + std::vector sources; + + { + auto lock = pool->Lock(); + sources = pool->GetPlayingSources(); + } + + Source::Stop(sources); +} + +int Source::GetChannelCount() const +{ + return this->channels; +} diff --git a/platform/ctr/source/objects/texture_ext.cpp b/platform/ctr/source/objects/texture_ext.cpp new file mode 100644 index 000000000..b53f089c2 --- /dev/null +++ b/platform/ctr/source/objects/texture_ext.cpp @@ -0,0 +1,350 @@ +#include + +#include + +using namespace love; + +static void createFramebufferObject(C3D_RenderTarget*& target, C3D_Tex*& texture, uint16_t width, + uint16_t height) +{ + const auto _width = NextPo2(width); + const auto _height = NextPo2(height); + + texture = new C3D_Tex(); + + if (!C3D_TexInitVRAM(texture, _width, _height, GPU_RGBA8)) + throw love::Exception("Failed to create framebuffer Texture"); + + target = C3D_RenderTargetCreateFromTex(texture, GPU_TEXFACE_2D, 0, GPU_RB_DEPTH16); +} + +static void createTextureObject(C3D_Tex*& texture, PixelFormat format, uint16_t width, + uint16_t height) +{ + const auto _width = NextPo2(width); + const auto _height = NextPo2(height); + + texture = new C3D_Tex(); + + std::optional color; + if (!(color = Renderer::pixelFormats.Find(format))) + throw love::Exception("Invalid color format: %s", love::GetPixelFormatName(format)); + + if (!C3D_TexInit(texture, _width, _height, *color)) + throw love::Exception("Failed to create Texture!"); +} + +Texture::Texture(const Graphics* graphics, const Settings& settings, + const Slices* data) : + Texture(settings, data), + framebuffer(nullptr) +{ + this->format = graphics->GetSizedFormat(format, this->renderTarget, this->readable); + + if (this->mipmapMode == MIPMAPS_AUTO && this->IsCompressed()) + this->mipmapMode = MIPMAPS_MANUAL; + + if (this->mipmapMode != MIPMAPS_NONE) + this->mipmapCount = + Texture<>::GetTotalMipmapCount(this->pixelWidth, this->pixelHeight, this->depth); + + bool invalidDimensions = this->pixelWidth <= 0 || this->pixelHeight <= 0; + if (invalidDimensions || this->layers <= 0 || this->depth <= 0) + throw love::Exception("Texture dimensions must be greater than zero."); + + if (this->textureType != TEXTURE_2D && this->requestedMSAA > 1) + throw love::Exception("MSAA is only supported for 2D textures."); + + if (!this->renderTarget && this->requestedMSAA > 1) + throw love::Exception("MSAA is only supported with render target textures."); + + bool isDepthStencilFormat = love::IsPixelFormatDepthStencil(this->format); + if (this->readable && isDepthStencilFormat && settings.msaa > 1) + throw love::Exception("Readable depth/stencil textures with MSAA are not supported."); + + if ((!this->readable || settings.msaa > 1) && this->mipmapMode != MIPMAPS_NONE) + throw love::Exception("Non-readable and MSAA textures cannot have mipmaps."); + + if (!this->readable && this->textureType != TEXTURE_2D) + throw love::Exception("Non-readable pixel formats are only supported for 2D textures."); + + if (this->IsCompressed() && this->renderTarget) + throw love::Exception("Compressed textures cannot be render targets."); + + this->state = graphics->GetDefaultSamplerState(); + if (this->GetMipmapCount() == 1) + this->state.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE; + + this->state = graphics->GetDefaultSamplerState(); + + Quad::Viewport view { 0, 0, (double)this->width, (double)this->height }; + this->quad.Set(new Quad(view, this->width, this->height), Acquire::NORETAIN); + + ++textureCount; + + if (data != nullptr) + slices = *data; + + this->LoadVolatile(); + + slices.Clear(); +} + +Texture::~Texture() +{ + this->UnloadVolatile(); +} + +bool Texture::LoadVolatile() +{ + if (this->IsReadable()) + this->CreateTexture(); + + int64_t memorySize = 0; + + for (int mipmap = 0; mipmap < this->GetMipmapCount(); mipmap++) + { + const auto width = this->GetPixelWidth(mipmap); + const auto height = this->GetPixelHeight(mipmap); + + const auto faceCount = this->textureType == TEXTURE_CUBE ? 6 : 1; + const auto slices = this->GetDepth(mipmap) * this->layers * faceCount; + + memorySize += love::GetPixelFormatSliceSize(this->format, width, height) * slices; + } + + this->SetGraphicsMemorySize(memorySize); + + return true; +} + +void Texture::UnloadVolatile() +{ + if (this->IsRenderTarget() && this->framebuffer != nullptr) + { + C3D_RenderTargetDelete(this->framebuffer); + C3D_TexDelete(this->texture); + + delete this->texture; + } + else + { + C3D_TexDelete(this->texture); + delete this->texture; + } +} + +void Texture::SetSamplerState(const SamplerState& state) +{ + Texture<>::SetSamplerState(state); + + this->state.magFilter = this->state.minFilter = SamplerState::FILTER_NEAREST; + + if (this->state.mipmapFilter == SamplerState::MIPMAP_FILTER_LINEAR) + this->state.mipmapFilter = SamplerState::MIPMAP_FILTER_NEAREST; + + Renderer::Instance().SetSamplerState(this, this->state); +} + +void Texture::CreateTexture() +{ + Texture::CreateTexture(); + bool hasData = this->slices.Get(0, 0) != nullptr; + + int _width = this->pixelWidth; + int _height = this->pixelHeight; + + if (this->IsRenderTarget()) + { + createFramebufferObject(this->framebuffer, this->texture, _width, _height); + + if (!hasData) + { + Renderer::Instance().BindFramebuffer(this); + Renderer::Instance().Clear({ 0, 0, 0, 0 }); + Renderer::Instance().BindFramebuffer(); + } + } + else + { + createTextureObject(this->texture, this->format, _width, _height); + const auto copySize = love::GetPixelFormatSliceSize(this->format, _width, _height); + + if (!hasData) + std::memset(this->texture->data, 0, copySize); + else + std::memcpy(this->texture->data, this->slices.Get(0, 0)->GetData(), copySize); + + C3D_TexFlush(this->texture); + } + + this->SetSamplerState(this->state); +} + +void Texture::ReplacePixels(ImageData* data, int slice, int mipmap, + int x, int y, bool reloadMipmaps) +{ + if (!this->IsReadable()) + throw love::Exception("replacePixels can only be called on a readable Texture."); + + if (this->GetMSAA() > 1) + throw love::Exception("replacePixels cannot be called on a MSAA Texture."); + + auto* graphics = Module::GetInstance>(Module::M_GRAPHICS); + if (graphics == nullptr && graphics->IsRenderTargetActive(this)) + throw love::Exception( + "replacePixels cannot be called on this Texture while it's an active rendertarget."); + + if (this->GetHandle() == nullptr) + return; + + if (data->GetFormat() != this->GetPixelFormat()) + throw love::Exception("Pixel formats must match."); + + if (mipmap < 0 || mipmap >= this->GetMipmapCount()) + throw love::Exception("Invalid Texture mipmap index: %d", mipmap + 1); + + // clang-format off + bool isInvalidCubeslice = this->textureType == TEXTURE_CUBE && slice >= 6; + bool isInvalidVolumeSlice = this->textureType == TEXTURE_VOLUME && slice >= this->GetDepth(mipmap); + bool isInvalidArraySlice = this->textureType == TEXTURE_2D_ARRAY && slice >= this->GetLayerCount(); + // clang-format on + + if (slice < 0 || isInvalidCubeslice || isInvalidVolumeSlice || isInvalidArraySlice) + throw love::Exception("Invalid texture slice index %d.", slice + 1); + + Rect rect = { x, y, data->GetWidth(), data->GetHeight() }; + + int mipWidth = this->GetPixelWidth(mipmap); + int mipHeight = this->GetPixelHeight(mipmap); + + if (rect.x < 0 || rect.y < 0 || rect.w <= 0 || rect.h <= 0 || (rect.x + rect.w) > mipWidth || + (rect.y + rect.h) > mipHeight) + { + throw love::Exception("Invalid rectangle dimensions (x = %d, y = %d, w = %d, h = %d) " + "for a %dx%d Texture.", + rect.x, rect.y, rect.w, rect.h, mipWidth, mipHeight); + } + + this->ReplacePixels(data->GetData(), data->GetSize(), slice, mipmap, rect, reloadMipmaps); +} + +template +void _replacePixels(const void* source, void* texture, const Rect& rect, const int width, + const int height) +{ + const auto sourcePowTwo = NextPo2(rect.w); + const auto destPowTwo = NextPo2(width); + + for (int _y = 0; _y < std::min(rect.h, height - rect.y); _y++) + { + for (int _x = 0; _x < std::min(rect.w, width - rect.x); _x++) + { + Vector2 srcPosition { _x, _y }; + const auto* srcPixel = Color::FromTile(source, sourcePowTwo, srcPosition); + + Vector2 destPosition { (rect.x + _x), (rect.y + _y) }; + auto* destPixel = Color::FromTile(texture, destPowTwo, destPosition); + *destPixel = *srcPixel; + } + } +} + +void Texture::ReplacePixels(const void* data, size_t size, int slice, int mipmap, + const Rect& rect, bool reloadMipmaps) +{ + switch (this->GetPixelFormat()) + { + case PIXELFORMAT_RGB565_UNORM: + { + _replacePixels(data, this->texture->data, rect, this->width, this->height); + break; + } + default: + { + _replacePixels(data, this->texture->data, rect, this->width, this->height); + break; + } + } + + C3D_TexFlush(this->texture); +} + +void Texture::Draw(Graphics& graphics, + const Matrix4& matrix) +{ + this->Draw(graphics, this->quad, matrix); +} + +static Vector2 getVertex(const float x, const float y, const Vector2& virtualDim, + const Vector2& physicalDim) +{ + const auto u = x / physicalDim.x; + const auto v = (virtualDim.y - y) / physicalDim.y; + + return Vector2(u, v); +} + +static void refreshQuad(StrongReference quad, const Quad::Viewport& viewport, + const Vector2& virtualDim, const Vector2& physicalDim, bool isRenderTarget) +{ + quad->Refresh(viewport, physicalDim.x, physicalDim.y); + const auto* texCoords = quad->GetVertexTextureCoords(); + + if (isRenderTarget) + { + auto coord = getVertex(0, 0, virtualDim, physicalDim); + quad->SetVertexTextureCoord(0, coord); + + coord = getVertex(0, virtualDim.y, virtualDim, physicalDim); + quad->SetVertexTextureCoord(1, coord); + + coord = getVertex(virtualDim.x, virtualDim.y, virtualDim, physicalDim); + quad->SetVertexTextureCoord(2, coord); + + coord = getVertex(virtualDim.x, 0.0f, virtualDim, physicalDim); + quad->SetVertexTextureCoord(3, coord); + + return; + } + + quad->SetVertexTextureCoord(0, Vector2(texCoords[0].x, 1.0f - texCoords[0].y)); + quad->SetVertexTextureCoord(1, Vector2(texCoords[1].x, 1.0f - texCoords[1].y)); + quad->SetVertexTextureCoord(2, Vector2(texCoords[2].x, 1.0f - texCoords[2].y)); + quad->SetVertexTextureCoord(3, Vector2(texCoords[3].x, 1.0f - texCoords[3].y)); +} + +void Texture::Draw(Graphics& graphics, Quad* quad, + const Matrix4& matrix) +{ + if (!this->readable) + throw love::Exception("Textures with non-readable formats cannot be drawn."); + + if (this->renderTarget && graphics.IsRenderTargetActive(this)) + throw love::Exception("Cannot render a Texture to itself."); + + const Quad::Viewport& viewport = quad->GetViewport(); + + Vector2 physicalDim = { (double)this->texture->width, (double)this->texture->height }; + Vector2 virtualDim = { (double)this->pixelWidth, (double)this->pixelHeight }; + + refreshQuad(quad, viewport, virtualDim, physicalDim, this->renderTarget); + + const auto& transform = graphics.GetTransform(); + bool is2D = transform.IsAffine2DTransform(); + + Matrix4 translated(graphics.GetTransform(), matrix); + + DrawCommand command(0x04, vertex::PRIMITIVE_TRIANGLE_FAN); + command.handles = { this->texture }; + command.format = CommonFormat::TEXTURE; + + if (is2D) + translated.TransformXY(command.Positions().get(), quad->GetVertexPositions(), + command.count); + + const auto* coords = quad->GetVertexTextureCoords(); + command.FillVertices(graphics.GetColor(), coords); + + Renderer::Instance().Render(command); +} diff --git a/platform/ctr/source/objects/truetyperasterizer_ext.cpp b/platform/ctr/source/objects/truetyperasterizer_ext.cpp new file mode 100644 index 000000000..fbc6d7ba2 --- /dev/null +++ b/platform/ctr/source/objects/truetyperasterizer_ext.cpp @@ -0,0 +1,150 @@ +#include + +#include + +#include + +#include + +#include + +using namespace love; + +static float scaleMetric(uint8_t value, float scale) +{ + return value * scale; +} + +template<> +TrueTypeRasterizer::TrueTypeRasterizer(FT_Library, Data* data, int size, + float dpiScale, + TrueTypeRasterizer<>::Hinting hinting) +{ + this->dpiScale = dpiScale; + + this->face = (CFNT_s*)linearAlloc(data->GetSize()); + std::memcpy((uint8_t*)this->face, data->GetData(), data->GetSize()); + fontFixPointers(this->face); + + auto* fontInfo = fontGetInfo(this->face); + auto* sheetInfo = fontInfo->tglp; + + this->scale = std::floor(size * dpiScale + 0.5f) / sheetInfo->cellHeight; + + if (size <= 0 || scale <= 0) + throw Exception("Invalid TrueType font size: %d", size); + + this->data.Set(data); + this->hinting = hinting; + + this->metrics.advance = scaleMetric(sheetInfo->maxCharWidth, scale); + this->metrics.ascent = scaleMetric(fontInfo->ascent, scale); + this->metrics.descent = scaleMetric((fontInfo->height - fontInfo->ascent), scale); + this->metrics.height = scaleMetric(sheetInfo->cellHeight, scale); +} + +template<> +TrueTypeRasterizer::~TrueTypeRasterizer() +{ + if (this->face) + linearFree(this->face); +} + +template<> +TextShaper* TrueTypeRasterizer::NewTextShaper() +{ + return new GenericShaper(this); +} + +template<> +int TrueTypeRasterizer::GetLineHeight() const +{ + return 1; +} + +template<> +bool TrueTypeRasterizer::HasGlyph(uint32_t glyph) const +{ + int index = fontGlyphIndexFromCodePoint(this->face, glyph); + const auto* info = fontGetInfo(this->face); + + return index != info->alterCharIndex; +} + +template<> +int TrueTypeRasterizer::GetGlyphSpacing(uint32_t glyph) const +{ + fontGlyphPos_s out {}; + + int index = fontGlyphIndexFromCodePoint(this->face, glyph); + fontCalcGlyphPos(&out, this->face, index, GLYPH_POS_CALC_VTXCOORD, this->scale, this->scale); + + return out.xAdvance; +} + +template<> +int TrueTypeRasterizer::GetGlyphIndex(uint32_t glyph) const +{ + const auto index = fontGlyphIndexFromCodePoint(this->face, glyph); + this->glyphMap[glyph] = index; + + return index; +} + +template<> +GlyphData* TrueTypeRasterizer::GetGlyphDataForIndex(int index) const +{ + fontGlyphPos_s out {}; + fontCalcGlyphPos(&out, this->face, index, GLYPH_POS_CALC_VTXCOORD, this->scale, this->scale); + + GlyphData::GlyphMetrics metrics {}; + metrics.height = this->metrics.height; + metrics.width = out.width; + metrics.advance = out.xAdvance; + metrics.bearingX = out.xOffset; + metrics.bearingY = this->metrics.ascent; + + GlyphData::GlyphSheetInfo sheetInfo {}; + sheetInfo.index = out.sheetIndex; + + sheetInfo.top = out.texcoord.top; + sheetInfo.left = out.texcoord.left; + sheetInfo.right = out.texcoord.right; + sheetInfo.bottom = out.texcoord.bottom; + + return new GlyphData(0, metrics, sheetInfo, PIXELFORMAT_RGBA8_UNORM); +} + +template<> +int TrueTypeRasterizer::GetGlyphCount() const +{ + if (this->glyphCount != -1) + return this->glyphCount; + + /* cache this data, as it's slow and stupid */ + FINF_s* info = fontGetInfo(this->face); + int count = 0; + + for (auto map = info->cmap; map; map = map->next) + count += (map->codeEnd - map->codeBegin) + 1; + + return this->glyphCount = count; +} + +template<> +float TrueTypeRasterizer::GetKerning(uint32_t, uint32_t) const +{ + return 0.0f; +} + +template<> +Rasterizer::DataType TrueTypeRasterizer::GetDataType() const +{ + return Rasterizer::DataType::DATA_BCFNT; +} + +template<> +bool TrueTypeRasterizer::Accepts(FT_Library, Data* data) +{ + return (!std::memcmp(data->GetData(), "CFNT", 4) || !std::memcmp(data->GetData(), "CFNU", 4)); +} \ No newline at end of file diff --git a/platform/ctr/source/objects/wrap_imagedata_ext.cpp b/platform/ctr/source/objects/wrap_imagedata_ext.cpp new file mode 100644 index 000000000..b6d5a388b --- /dev/null +++ b/platform/ctr/source/objects/wrap_imagedata_ext.cpp @@ -0,0 +1,82 @@ +#include + +using namespace love; +using ImageData = love::ImageData; + +int Wrap_ImageData::__MapPixelUnsafe(lua_State* L) +{ + auto* self = Wrap_ImageData::CheckImageData(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + + int sourceX = lua_tonumber(L, 3); + int sourceY = lua_tonumber(L, 4); + int width = lua_tonumber(L, 5); + int height = lua_tonumber(L, 6); + + if (!(self->Inside(sourceX, sourceY) && + self->Inside(sourceX + width - 1, sourceY + height - 1))) + { + return luaL_error(L, "Invalid rectangle dimensions."); + } + + int imageWidth = self->GetWidth(); + + PixelFormat format = self->GetFormat(); + int components = love::GetPixelFormatColorComponents(format); + + auto pixelSetFunction = self->GetPixelSetFunction(); + auto pixelGetFunction = self->GetPixelGetFunction(); + + uint32_t* data = (uint32_t*)self->GetData(); + size_t pixelSize = self->GetPixelSize(); + unsigned _width = NextPo2(imageWidth); + + for (int y = sourceY; y < sourceY + height; y++) + { + for (int x = sourceX; x < sourceX + width; x++) + { + auto* pixelData = (::ImageData::Pixel*)Color::FromTile(data, _width, { x, y }); + + Color color {}; + pixelGetFunction(pixelData, color); + + lua_pushvalue(L, 2); + + lua_pushnumber(L, x); + lua_pushnumber(L, y); + + lua_pushnumber(L, color.r); + lua_pushnumber(L, color.g); + lua_pushnumber(L, color.b); + lua_pushnumber(L, color.a); + + lua_call(L, 6, 4); + + color.r = luaL_checknumber(L, -4); + + if (components > 1) + color.g = luaL_checknumber(L, -3); + + if (components > 2) + color.b = luaL_checknumber(L, -2); + + if (components > 3) + color.a = luaL_checknumber(L, -1); + + pixelSetFunction(color, pixelData); + + lua_pop(L, 4); + } + } + + return 0; +} + +// clang-format off +static constexpr luaL_Reg functions[] = +{ + { "_mapPixelUnsafe", Wrap_ImageData::__MapPixelUnsafe } +}; +// clang-forma ton + +std::span Wrap_ImageData::extensions = functions; diff --git a/platform/ctr/source/runtime.cpp b/platform/ctr/source/runtime.cpp new file mode 100644 index 000000000..96604e34e --- /dev/null +++ b/platform/ctr/source/runtime.cpp @@ -0,0 +1,83 @@ +#include <3ds.h> + +#include + +#include +#include +#include + +extern "C" +{ + static void tryInit(std::function initFunction, love::AbortCode code) + { + if (!initFunction || love::g_EarlyExit) + return; + + love::ResultCode result; + if ((result = initFunction()); result.Success()) + return; + + errorConf conf {}; + + errorInit(&conf, ERROR_TEXT_WORD_WRAP, CFG_LANGUAGE_EN); + errorCode(&conf, result); + + static char message[0x100] {}; + + std::optional header; + if ((header = love::abortTypes.Find(code))) + snprintf(message, sizeof(message), love::ABORT_FORMAT_KNOWN, *header, (int32_t)result, + R_LEVEL(result), R_SUMMARY(result), R_DESCRIPTION(result)); + + errorText(&conf, message); + errorDisp(&conf); + + love::g_EarlyExit = true; + } + + void userAppInit() + { + osSetSpeedupEnable(true); + + tryInit(std::bind_front(romfsInit), love::ABORT_ROMFS); + +#if !defined(__EMULATION__) + /* raw battery info */ + tryInit(std::bind_front(mcuHwcInit), love::ABORT_MCU_HWC); +#endif + + /* charging state */ + tryInit(std::bind_front(ptmuInit), love::ABORT_PTMU); + + /* region information and fonts */ + tryInit(std::bind_front(cfguInit), love::ABORT_CFGU); + + /* network state */ + tryInit(std::bind_front(acInit), love::ABORT_AC); + + /* friend code */ + tryInit(std::bind_front(frdInit), love::ABORT_FRD); + + /* theora video conversion */ + tryInit(std::bind_front(y2rInit), love::ABORT_Y2R); + } + + void userAppExit() + { + y2rExit(); + + frdExit(); + + acExit(); + + cfguExit(); + + ptmuExit(); + + romfsExit(); + +#if !defined(__EMULATION__) + mcuHwcExit(); +#endif + } +} diff --git a/platform/ctr/source/utilities/driver/dsp_ext.cpp b/platform/ctr/source/utilities/driver/dsp_ext.cpp new file mode 100644 index 000000000..10fc4969e --- /dev/null +++ b/platform/ctr/source/utilities/driver/dsp_ext.cpp @@ -0,0 +1,136 @@ +#include + +#include + +using namespace love; + +static void audioCallback(void* data) +{ + auto event = (LightEvent*)data; + LightEvent_Signal(event); +} + +void DSP::Initialize() +{ + if (Result result; R_FAILED(result = ndspInit())) + { + if ((uint32_t)result == 0xD880A7FA) + throw love::Exception("Failed to initialize ndsp (dspfirm.cdc not found)"); + + throw love::Exception("Failed to initialize ndsp: %x", result); + } + + this->initialized = true; + + LightEvent_Init(&this->event, RESET_ONESHOT); + ndspSetCallback(audioCallback, &this->event); +} + +DSP::~DSP() +{ + if (!this->initialized) + return; + + ndspExit(); +} + +void DSP::Update() +{ + LightEvent_Wait(&this->event); +} + +void DSP::SetMasterVolume(float volume) +{ + ndspSetMasterVol(volume); +} + +float DSP::GetMasterVolume() const +{ + return ndspGetMasterVol(); +} + +bool DSP::ChannelReset(size_t id, int channels, int bitDepth, int sampleRate) +{ + ndspChnReset(id); + + uint8_t ndspFormat = 0; + if ((ndspFormat = DSP::GetFormat(bitDepth, channels)) < 0) + return false; + + std::optional interpType; + if (!(interpType = DSP::interpTypes.Find(channels))) + return false; + + ndspChnSetFormat(id, ndspFormat); + ndspChnSetRate(id, sampleRate); + ndspChnSetInterp(id, *interpType); + + this->ChannelSetVolume(id, this->ChannelGetVolume(id)); + + return true; +} + +void DSP::ChannelSetVolume(size_t id, float volume) +{ + float mix[12] { 0.0f }; + mix[0] = mix[1] = volume; + + ndspChnSetMix(id, mix); +} + +float DSP::ChannelGetVolume(size_t id) const +{ + float mix[12] { 0.0f }; + ndspChnGetMix(id, mix); + + return mix[0]; +} + +size_t DSP::ChannelGetSampleOffset(size_t id) const +{ + return ndspChnGetSamplePos(id); +} + +bool DSP::ChannelAddBuffer(size_t id, ndspWaveBuf* buffer) +{ + ndspChnWaveBufAdd(id, buffer); + + return true; +} + +void DSP::ChannelPause(size_t id, bool paused) +{ + ndspChnSetPaused(id, paused); +} + +bool DSP::IsChannelPaused(size_t id) const +{ + return ndspChnIsPaused(id); +} + +bool DSP::IsChannelPlaying(size_t id) const +{ + return ndspChnIsPlaying(id); +} + +void DSP::ChannelStop(size_t id) +{ + ndspChnWaveBufClear(id); +} + +int8_t DSP::GetFormat(int bitDepth, int channels) +{ + /* invalid bitDepth */ + if (bitDepth != 8 && bitDepth != 16) + return -1; + + /* invalid channel count */ + if (channels < 0 || channels > 2) + return -2; + + /* grab the encoding */ + uint8_t encoding = *DSP::audioFormats.Find(bitDepth); + + /* https://github.com/devkitPro/libctru/blob/master/libctru/include/3ds/ndsp/channel.h#L25 */ + return NDSP_CHANNELS(channels) | NDSP_ENCODING(encoding); +} diff --git a/platform/ctr/source/utilities/driver/hid_ext.cpp b/platform/ctr/source/utilities/driver/hid_ext.cpp new file mode 100644 index 000000000..f29da9335 --- /dev/null +++ b/platform/ctr/source/utilities/driver/hid_ext.cpp @@ -0,0 +1,130 @@ +#include +#include + +#include + +using namespace love; + +#define Module() (Module::GetInstance>(Module::M_JOYSTICK)) +#define Sensor() (Module::GetInstance>(Module::M_SENSOR)) + +static aptHookCookie s_aptHookCookie; + +static void aptEventHook(const APT_HookType type, void* parameter) +{ + auto driver = HID::Instance(); + auto* graphics = Module::GetInstance>(Module::M_GRAPHICS); + + switch (type) + { + case APTHOOK_ONRESTORE: + case APTHOOK_ONWAKEUP: + { + driver.SendFocus(true); + + if (graphics) + graphics->SetActive(true); + + break; + } + case APTHOOK_ONSUSPEND: + case APTHOOK_ONSLEEP: + { + driver.SendFocus(false); + + if (graphics) + graphics->SetActive(false); + + break; + } + case APTHOOK_ONEXIT: + { + driver.SendQuit(); + break; + } + default: + break; + } +} + +HID::HID() : touchState {} +{ + aptHook(&s_aptHookCookie, aptEventHook, nullptr); +} + +HID::~HID() +{ + aptUnhook(&s_aptHookCookie); +} + +void HID::_Poll() +{ + hidScanInput(); + + const auto touchDown = hidKeysDown(); + const auto touchHeld = hidKeysHeld(); + const auto touchReleased = hidKeysUp(); + + if (touchDown & KEY_TOUCH || touchHeld & KEY_TOUCH) + hidTouchRead(&this->touchState.current); + + if (touchDown & KEY_TOUCH) + { + float x = this->touchState.current.px, y = this->touchState.current.py; + this->SendTouchEvent(SUBTYPE_TOUCHPRESS, 0, x, y, 0.0f, 0.0f, 1.0f); + + this->touchState.previous = this->touchState.current; + } + else if (touchHeld & KEY_TOUCH) + { + float x = this->touchState.current.px, y = this->touchState.current.py; + + float dx = this->touchState.current.px - this->touchState.previous.px; + float dy = this->touchState.current.py - this->touchState.previous.py; + + if (dx != 0.0f || dy != 0.0f) + this->SendTouchEvent(SUBTYPE_TOUCHMOVED, 0, x, y, dx, dy, 1.0f); + + this->touchState.previous = this->touchState.current; + } + + if (touchReleased & KEY_TOUCH) + { + float x = this->touchState.previous.px, y = this->touchState.previous.py; + this->SendTouchEvent(SUBTYPE_TOUCHRELEASE, 0, x, y, 0.0f, 0.0f, 0.0f); + } + + Joystick* joystick = nullptr; + + if (Module()) + joystick = Module()->GetJoystickFromId(0); + + if (joystick) + { + joystick->Update(); + Joystick<>::JoystickInput input {}; + + for (int index = 0; index < Sensor::SENSOR_MAX_ENUM; index++) + { + const auto sensor = (Sensor::SensorType)index; + + if (joystick->IsSensorEnabled(sensor)) + this->SendJoystickSensorUpdated(0, sensor, joystick->GetSensorData(sensor)); + } + + if (joystick->IsDown(input)) + this->SendGamepadPress(true, joystick->GetID(), input.button, input.buttonNumber); + + if (joystick->IsUp(input)) + this->SendGamepadPress(false, joystick->GetID(), input.button, input.buttonNumber); + + /* handle trigger and stick inputs */ + for (size_t index = 0; index < Joystick<>::GAMEPAD_AXIS_MAX_ENUM; index++) + { + const auto axis = (Joystick<>::GamepadAxis)index; + + if (joystick->IsAxisChanged(axis)) + this->SendGamepadAxis(0, axis, index, joystick->GetAxis(index)); + } + } +} diff --git a/platform/ctr/source/utilities/driver/renderer/framebuffer_ext.cpp b/platform/ctr/source/utilities/driver/renderer/framebuffer_ext.cpp new file mode 100644 index 000000000..406145b74 --- /dev/null +++ b/platform/ctr/source/utilities/driver/renderer/framebuffer_ext.cpp @@ -0,0 +1,102 @@ +#include + +#include + +using namespace love; + +Framebuffer::Framebuffer() : target(nullptr) +{} + +void Framebuffer::Create(Screen screen) +{ + this->id = screen; + Mtx_Identity(&this->modelView); + + switch (screen) + { + case Screen::LEFT: + case Screen::RIGHT: + { + const auto side = (screen == Screen::LEFT) ? GFX_LEFT : GFX_RIGHT; + this->SetSize(400, 240, GFX_TOP, side); + break; + } + case Screen::BOTTOM: + { + this->SetSize(320, 240, GFX_BOTTOM, GFX_LEFT); + break; + } + default: + break; // shouldn't happen + } +} + +void Framebuffer::Destroy() +{ + if (this->target) + C3D_RenderTargetDelete(this->target); + + this->target = nullptr; +} + +void Framebuffer::SetSize(int width, int height, gfxScreen_t screen, gfx3dSide_t side) +{ + this->target = C3D_RenderTargetCreate(height, width, GPU_RB_RGBA8, GPU_RB_DEPTH16); + + if (this->target) + C3D_RenderTargetSetOutput(this->target, screen, side, Framebuffer::DISPLAY_FLAGS); + else + { + const auto name = std::string(love::GetScreenName(this->id)); + throw love::Exception("Failed to allocate framebuffer %s", name.c_str()); + } + + this->width = width; + this->height = height; + + this->viewport = { 0, 0, width, height }; + this->scissor = { 0, 0, width, height }; + + Mtx_OrthoTilt(&this->projView, 0, width, height, 0, Z_NEAR, Z_FAR, true); + this->SetScissor(); +} + +const Rect Framebuffer::CalculateBounds(const Rect& bounds) +{ + // clang-format off + const uint32_t left = this->height > (bounds.y + bounds.h) ? this->height - (bounds.y + bounds.h) : 0; + const uint32_t top = this->width > (bounds.x + bounds.w) ? this->width - (bounds.x + bounds.w) : 0; + const uint32_t right = this->height - bounds.y; + const uint32_t bottom = this->width - bounds.x; + // clang-format on + + return { (int)left, (int)top, (int)right, (int)bottom }; +} + +void Framebuffer::SetViewport(const Rect& viewport, bool canvasActive) +{ + Rect newViewport = viewport; + if (viewport == Rect::EMPTY) + newViewport = this->viewport; + + Mtx_OrthoTilt(&this->projView, newViewport.x, newViewport.w, newViewport.h, newViewport.y, + Z_NEAR, Z_FAR, true); +} + +void Framebuffer::SetScissor(const Rect& scissor, bool canvasActive) +{ + const bool enable = (scissor != Rect::EMPTY); + GPU_SCISSORMODE mode = enable ? GPU_SCISSOR_NORMAL : GPU_SCISSOR_DISABLE; + + Rect newScissor = this->CalculateBounds(scissor); + if (viewport == Rect::EMPTY) + newScissor = this->CalculateBounds(this->scissor); + + C3D_SetScissor(mode, newScissor.y, newScissor.x, newScissor.h, newScissor.w); +} + +void Framebuffer::UseProjection(Shader::Uniforms uniforms) +{ + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uniforms.uLocProjMtx, &this->projView); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uniforms.uLocMdlView, &this->modelView); +} \ No newline at end of file diff --git a/platform/ctr/source/utilities/driver/renderer/renderer_ext.cpp b/platform/ctr/source/utilities/driver/renderer/renderer_ext.cpp new file mode 100644 index 000000000..387065323 --- /dev/null +++ b/platform/ctr/source/utilities/driver/renderer/renderer_ext.cpp @@ -0,0 +1,336 @@ +#include +#include + +#include +#include + +#include + +#include +#include + +using namespace love; + +Renderer::Renderer() : targets {}, currentTexture(nullptr) +{ + gfxInitDefault(); + gfxSet3D(true); + + if (!C3D_Init(C3D_DEFAULT_CMDBUF_SIZE)) + throw love::Exception("Failed to initialize citro3d!"); + + C3D_CullFace(GPU_CULL_NONE); + C3D_DepthTest(true, GPU_GEQUAL, GPU_WRITE_ALL); + + C3D_AttrInfo* attributes = C3D_GetAttrInfo(); + AttrInfo_Init(attributes); + + AttrInfo_AddLoader(attributes, 0, GPU_FLOAT, 3); // position + AttrInfo_AddLoader(attributes, 1, GPU_FLOAT, 4); // color + AttrInfo_AddLoader(attributes, 2, GPU_FLOAT, 2); // texcoord + + BufInfo_Init(&this->bufferInfo); + m_vertices = (Vertex*)linearAlloc(VERTEX_BUFFER_SIZE * VERTEX_SIZE); + + if (!m_vertices) + throw love::Exception("Out of memory."); + + int result = BufInfo_Add(&this->bufferInfo, (void*)m_vertices, VERTEX_SIZE, 0x03, 0x210); + C3D_SetBufInfo(&this->bufferInfo); + + if (result < 0) + throw love::Exception("Failed to add C3D_BufInfo."); + + Mtx_Identity(&this->context.projection); + Mtx_Identity(&this->context.modelView); +} + +Renderer::~Renderer() +{ + linearFree(m_vertices); + + C3D_Fini(); + gfxExit(); +} + +Renderer::Info Renderer::GetRendererInfo() +{ + if (this->info.filled) + return this->info; + + this->info.device = Renderer::RENDERER_DEVICE; + this->info.name = Renderer::RENDERER_NAME; + this->info.vendor = Renderer::RENDERER_VENDOR; + this->info.version = Renderer::RENDERER_VERSION; + + this->info.filled = true; + + return this->info; +} + +void Renderer::CreateFramebuffers() +{ + for (uint8_t index = 0; index < this->targets.size(); index++) + this->targets[index].Create((Screen)index); +} + +void Renderer::DestroyFramebuffers() +{ + for (uint8_t index = 0; index < this->targets.size(); index++) + this->targets[index].Destroy(); +} + +void Renderer::Clear(const Color& color) +{ + C3D_FrameSplit(0); + C3D_RenderTargetClear(this->context.target, C3D_CLEAR_ALL, color.abgr(), 0); +} + +/* todo */ +void Renderer::ClearDepthStencil(int stencil, uint8_t mask, double depth) +{} + +/* kept track of in Graphics */ +void Renderer::SetBlendColor(const Color& color) +{} + +void Renderer::EnsureInFrame() +{ + if (!this->inFrame) + { + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + this->inFrame = true; + } +} + +void Renderer::BindFramebuffer(Texture* texture) +{ + if (!IsActiveScreenValid()) + return; + + this->EnsureInFrame(); + FlushVertices(); + + this->context.target = this->targets[love::GetActiveScreen()].GetTarget(); + Rect viewport = this->targets[love::GetActiveScreen()].GetViewport(); + + if (texture != nullptr && texture->IsRenderTarget()) + { + auto* _texture = (Texture*)texture; + this->context.target = _texture->GetRenderTargetHandle(); + + viewport = { 0, 0, _texture->GetPixelWidth(), _texture->GetPixelHeight() }; + } + + C3D_FrameDrawOn(this->context.target); + this->SetViewport(viewport, this->context.target->linked); +} + +void Renderer::FlushVertices() +{ + for (const auto& command : m_commands) + { + if (command.count + m_vertexOffset > MAX_OBJECTS) + m_vertexOffset = 0; + + std::memcpy(m_vertices + m_vertexOffset, command.Vertices().get(), command.size); + + if (m_format != command.format) + { + const auto setTexEnvFunction = vertex::attributes::GetTexEnvFunction(command.format); + setTexEnvFunction(); + + m_format = command.format; + } + + std::optional primitive; + if (!(primitive = primitiveModes.Find(command.type))) + throw love::Exception("Invalid primitive mode"); + + ++drawCallsBatched; + C3D_DrawArrays(*primitive, m_vertexOffset, command.count); + m_vertexOffset += command.count; + } + + m_commands.clear(); +} + +bool Renderer::Render(DrawCommand& command) +{ + { + Shader::defaults[command.shader]->Attach(); + + auto uniforms = Shader::current->GetUniformLocations(); + + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uniforms.uLocProjMtx, &this->context.projection); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uniforms.uLocMdlView, &this->context.modelView); + } + + // check if texture is the same, or no texture at all + if (command.handles.empty() || + (command.handles.size() > 0 && this->currentTexture == command.handles.back())) + { + ++drawCalls; + m_commands.push_back(command.Clone()); + return true; + } + else + { + FlushVertices(); + + if (!command.handles.empty()) + { + if (this->currentTexture != command.handles.back()) + this->currentTexture = command.handles.back(); + + C3D_TexBind(0, command.handles.back()); + } + ++drawCalls; + m_commands.push_back(command.Clone()); + return true; + } + + return false; +} + +void Renderer::Present() +{ + if (this->inFrame) + { + FlushVertices(); + C3D_FrameEnd(0); + + m_vertexOffset = 0; + + this->inFrame = false; + } + + Renderer<>::cpuTime = C3D_GetProcessingTime(); + Renderer<>::gpuTime = C3D_GetDrawingTime(); + + for (size_t i = this->deferred.size(); i > 0; i--) + { + this->deferred[i - 1](); + this->deferred.erase(deferred.begin() + i - 1); + } +} + +void Renderer::SetViewport(const Rect& rect, bool tilt) +{ + Rect newView = rect; + + if (newView.h == GSP_SCREEN_WIDTH && tilt) + { + if (newView.w == GSP_SCREEN_HEIGHT_TOP || newView.w == GSP_SCREEN_HEIGHT_TOP_2X) + { + Mtx_Copy(&this->context.projection, &this->targets[0].GetProjView()); + return; + } + else if (newView.w == GSP_SCREEN_HEIGHT_BOTTOM) + { + Mtx_Copy(&this->context.projection, &this->targets[2].GetProjView()); + return; + } + } + + auto* ortho = tilt ? Mtx_OrthoTilt : Mtx_Ortho; + ortho(&this->context.projection, 0.0f, rect.w, rect.h, 0.0f, Z_NEAR, Z_FAR, true); + + C3D_SetViewport(0, 0, rect.w, rect.h); +} + +void Renderer::SetScissor(const Rect& scissor, bool canvasActive) +{ + this->targets[love::GetActiveScreen()].SetScissor(scissor, canvasActive); +} + +void Renderer::SetStencil(RenderState::CompareMode mode, int value) +{ + bool enabled = (mode == RenderState::COMPARE_ALWAYS) ? false : true; + + std::optional compareOp; + if (!(compareOp = Renderer::compareModes.Find(mode))) + return; + + C3D_StencilTest(enabled, *compareOp, value, 0xFFFFFFFF, 0xFFFFFFFF); + C3D_StencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); +} + +void Renderer::SetMeshCullMode(vertex::CullMode mode) +{ + std::optional cullMode; + if (!(cullMode = Renderer::cullModes.Find(mode))) + return; + + C3D_CullFace(*cullMode); +} + +/* ??? */ +void Renderer::SetVertexWinding(vertex::Winding winding) +{} + +void Renderer::SetSamplerState(Texture* texture, SamplerState& state) +{ + /* set the min and mag filters */ + + auto* handle = texture->GetHandle(); + + std::optional mag; + if (!(mag = Renderer::filterModes.Find(state.magFilter))) + return; + + std::optional min; + if (!(min = Renderer::filterModes.Find(state.minFilter))) + return; + + C3D_TexSetFilter(handle, *mag, *min); + + /* set the wrapping modes */ + + std::optional wrapU; + if (!(wrapU = Renderer::wrapModes.Find(state.wrapU))) + return; + + std::optional wrapV; + if (!(wrapV = Renderer::wrapModes.Find(state.wrapV))) + return; + + C3D_TexSetWrap(handle, *wrapU, *wrapV); +} + +void Renderer::SetColorMask(const RenderState::ColorMask& mask) +{ + uint8_t writeMask = GPU_WRITE_DEPTH; + writeMask |= mask.GetColorMask(); + + C3D_DepthTest(true, GPU_GEQUAL, (GPU_WRITEMASK)writeMask); +} + +void Renderer::SetBlendMode(const RenderState::BlendState& state) +{ + std::optional opRGB; + if (!(opRGB = Renderer::blendEquations.Find(state.operationRGB))) + return; + + std::optional opAlpha; + if (!(opAlpha = Renderer::blendEquations.Find(state.operationA))) + return; + + std::optional srcColor; + if (!(srcColor = Renderer::blendFactors.Find(state.srcFactorRGB))) + return; + + std::optional dstColor; + if (!(dstColor = Renderer::blendFactors.Find(state.dstFactorRGB))) + return; + + std::optional srcAlpha; + if (!(srcAlpha = Renderer::blendFactors.Find(state.srcFactorA))) + return; + + std::optional dstAlpha; + if (!(dstAlpha = Renderer::blendFactors.Find(state.dstFactorA))) + return; + + C3D_AlphaBlend(*opRGB, *opAlpha, *srcColor, *dstColor, *srcAlpha, *dstAlpha); +} diff --git a/platform/ctr/source/utilities/formathandler/types/t3xhandler.cpp b/platform/ctr/source/utilities/formathandler/types/t3xhandler.cpp new file mode 100644 index 000000000..74f323a79 --- /dev/null +++ b/platform/ctr/source/utilities/formathandler/types/t3xhandler.cpp @@ -0,0 +1,69 @@ +#include + +#include + +#include + +#include + +#include <3ds.h> + +using namespace love; + +bool T3XHandler::CanDecode(Data* data) +{ + Tex3DSHeader header {}; + std::memcpy(&header, data->GetData(), sizeof(header)); + + if (header.numSubTextures != 1) + return false; + else if (header.type != GPU_TEX_2D) + return false; + + return true; +} + +T3XHandler::DecodedImage T3XHandler::Decode(Data* data) +{ + Tex3DSHeader header {}; + std::memcpy(&header, data->GetData(), sizeof(header)); + + if (header.numSubTextures != 1) + throw love::Exception("t3x does not contain exactly one subtexture"); + + if (header.type != GPU_TEX_2D) + throw love::Exception("t3x texture type is not GPU_TEX_2D"); + + std::optional pixelFormat; + const auto format = (GPU_TEXCOLOR)header.format; + + if (!(pixelFormat = Renderer::pixelFormats.ReverseFind(format))) + throw love::Exception("PixelFormat %u is not compatible", header.format); + + const auto width = 1 << (header.width_log2 + 3); + const auto height = 1 << (header.height_log2 + 3); + + if (width > 1024 || height > 1024) + throw love::Exception("t3x dimensions too large"); + + DecodedImage decoded {}; + + decoded.width = header.width; + decoded.height = header.height; + decoded.format = *pixelFormat; + + const auto size = data->GetSize() - sizeof(header); + const auto compressed = ((uint8_t*)data->GetData() + sizeof(header)); + + size_t outSize = 0; + if (decompressHeader(nullptr, &outSize, nullptr, compressed, size) < 0) + throw love::Exception("Failed to decompress t3x header!"); + + decoded.size = (header.width * header.height * love::GetPixelFormatBlockSize(*pixelFormat)); + decoded.data = std::make_unique(outSize); + + if (!decompress(decoded.data.get(), outSize, nullptr, compressed, size)) + throw love::Exception("Failed to decompress t3x data!"); + + return decoded; +} diff --git a/platform/ctr/source/utilities/sensor/accelerometer.cpp b/platform/ctr/source/utilities/sensor/accelerometer.cpp new file mode 100644 index 000000000..684b3b52a --- /dev/null +++ b/platform/ctr/source/utilities/sensor/accelerometer.cpp @@ -0,0 +1,28 @@ +#include + +using namespace love; + +Accelerometer::Accelerometer() : data {} +{} + +Accelerometer::~Accelerometer() +{ + this->SetEnabled(false); +} + +std::vector Accelerometer::GetData() +{ + hidAccelRead(&this->data); + + return { (float)this->data.x, (float)this->data.y, (float)this->data.z }; +} + +void Accelerometer::SetEnabled(bool enable) +{ + if (enable) + HIDUSER_EnableAccelerometer(); + else + HIDUSER_DisableAccelerometer(); + + SensorBase::SetEnabled(enable); +} diff --git a/platform/ctr/source/utilities/sensor/gyroscope.cpp b/platform/ctr/source/utilities/sensor/gyroscope.cpp new file mode 100644 index 000000000..78cfc2016 --- /dev/null +++ b/platform/ctr/source/utilities/sensor/gyroscope.cpp @@ -0,0 +1,28 @@ +#include + +using namespace love; + +Gyroscope::Gyroscope() : data {} +{} + +Gyroscope::~Gyroscope() +{ + this->SetEnabled(false); +} + +std::vector Gyroscope::GetData() +{ + hidGyroRead(&this->data); + + return { (float)this->data.x, (float)this->data.y, (float)this->data.z }; +} + +void Gyroscope::SetEnabled(bool enabled) +{ + if (enabled) + HIDUSER_EnableGyroscope(); + else + HIDUSER_DisableGyroscope(); + + SensorBase::SetEnabled(enabled); +} diff --git a/platform/hac/CMakeLists.txt b/platform/hac/CMakeLists.txt new file mode 100644 index 000000000..9feac8fe8 --- /dev/null +++ b/platform/hac/CMakeLists.txt @@ -0,0 +1,62 @@ +# declare an asset target for the executable's RomFS (optional) +dkp_add_asset_target(${PROJECT_NAME}_hac_romfs romfs) + +# make our shader targets +nx_add_shader_program(transform_vsh "shaders/transform_vsh.glsl" "vert") +nx_add_shader_program(color_fsh "shaders/color_fsh.glsl" "frag") +nx_add_shader_program(texture_fsh "shaders/texture_fsh.glsl" "frag") +nx_add_shader_program(video_fsh "shaders/video_fsh.glsl" "frag") + +# install shaders to RomFS +dkp_install_assets (${PROJECT_NAME}_hac_romfs + DESTINATION "shaders" + TARGETS + transform_vsh + color_fsh + texture_fsh + video_fsh +) + +dkp_track_assets(${PROJECT_NAME}_hac_romfs + FOLDER "nogame" + FILES + cartridge.png + nogame.png +) + +target_include_directories(${PROJECT_NAME} PRIVATE + include +) + +# find source -type f | grep "\.cpp$" | clip +target_sources(${PROJECT_NAME} PRIVATE + source/common/matrix_ext.cpp + source/common/screen_ext.cpp + source/modules/fontmodule_ext.cpp + source/modules/graphics_ext.cpp + source/modules/imagemodule_ext.cpp + source/modules/joystickmodule_ext.cpp + source/modules/keyboard_ext.cpp + source/modules/love_ext.cpp + source/modules/system_ext.cpp + source/modules/timer_ext.cpp + source/modules/window_ext.cpp + source/objects/joystick_ext.cpp + ${PROJECT_SOURCE_DIR}/source/objects/truetyperasterizer/truetyperasterizer.cpp + source/objects/shader_ext.cpp + source/objects/source_ext.cpp + source/objects/texture_ext.cpp + source/objects/wrap_imagedata_ext.cpp + source/objects/wrap_joystick_ext.cpp + source/runtime.cpp + source/utilities/driver/CIntrusiveTree.cpp + source/utilities/driver/CMemPool.cpp + source/utilities/driver/dsp_ext.cpp + source/utilities/driver/dsp_mem.cpp + source/utilities/driver/hid_ext.cpp + source/utilities/driver/renderer_ext.cpp + source/utilities/haptics/vibration_ext.cpp + source/utilities/npad.cpp + source/utilities/sensor/accelerometer.cpp + source/utilities/sensor/gyroscope.cpp +) diff --git a/platform/hac/icon-dev.jpg b/platform/hac/icon-dev.jpg new file mode 100644 index 000000000..b92575258 Binary files /dev/null and b/platform/hac/icon-dev.jpg differ diff --git a/platform/hac/icon.jpg b/platform/hac/icon.jpg new file mode 100644 index 000000000..6a3cb8da4 Binary files /dev/null and b/platform/hac/icon.jpg differ diff --git a/platform/hac/include/common/matrix_ext.hpp b/platform/hac/include/common/matrix_ext.hpp new file mode 100644 index 000000000..172cd5538 --- /dev/null +++ b/platform/hac/include/common/matrix_ext.hpp @@ -0,0 +1,155 @@ +#pragma once + +#include + +namespace love +{ + template<> + class Matrix4 : public Matrix4 + { + public: + Matrix4(); + + Matrix4(const float elements[16]); + + Matrix4(const Matrix4& a, const Matrix4& b); + + Matrix4(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, + float ky); + + Matrix4(float t00, float t10, float t01, float t11, float x, float y); + + void SetIdentity(); + + void Transpose() + {} + + void SetTranslation(float x, float y); + + void Translate(float x, float y); + + void Rotate(float r); + + void Scale(float sx, float sy); + + void Shear(float kx, float ky); + + bool IsAffine2DTransform() const; + + bool IsAffine3DTransform() const; + + Matrix4 Inverse() const; + + void SetRawTransformation(float t00, float t10, float t01, float t11, float x, float y); + + void SetTransformation(float x, float y, float angle, float sx, float sy, float ox, + float oy, float kx, float ky); + + void Translation(float x, float y); + + void SetRotation(float r); + + void SetScale(float x, float y); + + void SetShear(float kx, float ky); + + void GetApproximateScale(float& sx, float& sy) const; + + Matrix4 operator*(const Matrix4& m) const; + + void operator*=(const Matrix4& m); + + static Matrix4 Ortho(float left, float right, float bottom, float top, float near, + float far); + + static void Multiply(const Matrix4& a, const Matrix4& b, Matrix4& result); + + float Get(const unsigned row, const unsigned column) const + { + return this->elements[column * 4 + row]; + } + + void Set(const unsigned row, const unsigned column, const float value) + { + this->elements[column * 4 + row] = value; + } + + void TransformXY(float elements[16]); + + void TransformXY(); + + /** + * Transforms an array of 2-component vertices by this Matrix. The source + * and destination arrays may be the same. + **/ + template + void TransformXY(Vdst* dst, const Vsrc* src, int size) const; + + template + void TransformXYVert(Vdst* dst, const Vsrc* src, int size) const + { + for (int i = 0; i < size; i++) + { + // Store in temp variables in case src = dst + float x = (this->elements[0] * src[i].position[0]) + + (this->elements[4] * src[i].position[1]) + (0) + (this->elements[12]); + + float y = (this->elements[1] * src[i].position[0]) + + (this->elements[5] * src[i].position[1]) + (0) + (this->elements[13]); + + dst[i].x = x; + dst[i].y = y; + } + } + + template + void TransformXYVertPure(Vdst* dst, const Vsrc* src, int size) const; + + /** + * Transforms an array of 2-component vertices by this Matrix, and stores + * them in an array of 3-component vertices. + **/ + template + void TransformXY0(Vdst* dst, const Vsrc* src, int size) const; + + private: + static void Multiply(const Matrix4& a, const Matrix4& b, float elements[16]); + + float elements[16]; + }; + + /* use with Vector2 */ + template + void Matrix4::TransformXY(Vdst* dst, const Vsrc* src, int size) const + { + for (int i = 0; i < size; i++) + { + // Store in temp variables in case src = dst + float x = (this->elements[0] * src[i].x) + (this->elements[4] * src[i].y) + (0) + + (this->elements[12]); + float y = (this->elements[1] * src[i].x) + (this->elements[5] * src[i].y) + (0) + + (this->elements[13]); + + dst[i].x = x; + dst[i].y = y; + } + } + + /* use with Vertex */ + template + void Matrix4::TransformXYVertPure(Vdst* dst, const Vsrc* src, int size) const + { + for (int i = 0; i < size; i++) + { + // Store in temp variables in case src = dst + float x = (this->elements[0] * src[i].position[0]) + + (this->elements[4] * src[i].position[1]) + (0) + (this->elements[12]); + + float y = (this->elements[1] * src[i].position[0]) + + (this->elements[5] * src[i].position[1]) + (0) + (this->elements[13]); + + dst[i].position[0] = x; + dst[i].position[1] = y; + } + } +} // namespace love diff --git a/platform/hac/include/common/screen_ext.hpp b/platform/hac/include/common/screen_ext.hpp new file mode 100644 index 000000000..3742e9433 --- /dev/null +++ b/platform/hac/include/common/screen_ext.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace love +{ + enum Screen : int8_t + { + DEFAULT + }; + + // clang-format off + inline constinit ScreenInfo screenInfo[0x01] = + { + { Screen::DEFAULT, "default", -1, -1 }, + }; + // clang-format on + + inline void SetScreenSize(int width, int height) + { + auto& info = screenInfo[0]; + + info.width = width; + info.height = height; + } +} // namespace love diff --git a/platform/hac/include/modules/fontmodule_ext.hpp b/platform/hac/include/modules/fontmodule_ext.hpp new file mode 100644 index 000000000..9fde70ac3 --- /dev/null +++ b/platform/hac/include/modules/fontmodule_ext.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include + +#include + +#include + +#include + +namespace love +{ + using SystemFontType = PlSharedFontType; + + class SystemFont : public Data + { + public: + SystemFont(PlSharedFontType type = PlSharedFontType_Standard) : type(type) + { + plGetSharedFontByType(&this->data, type); + } + + Data* Clone() const override + { + return new SystemFont(this->type); + } + + void* GetData() const override + { + return this->data.address; + } + + size_t GetSize() const override + { + return this->data.size; + } + + private: + PlFontData data; + PlSharedFontType type; + }; + + template<> + class FontModule : public FontModule + { + public: + FontModule(); + + using FontModule::NewTrueTypeRasterizer; + + Rasterizer* NewImageRasterizer(ImageData* data, const std::string& text, + int extraSpacing, float dpiScale) const override; + + Rasterizer* NewImageRasterizer(ImageData* data, uint32_t* glyphs, + int glyphCount, int extraSpacing, + float dpiScale) const override; + + Rasterizer* NewTrueTypeRasterizer(Data* data, int size, + TrueTypeRasterizer<>::Hinting hinting) const override; + + Rasterizer* NewTrueTypeRasterizer(Data* data, int size, float dpiScale, + TrueTypeRasterizer<>::Hinting hinting) const override; + + Rasterizer* NewRasterizer(FileData* data) const; + + // clang-format off + static constexpr BidirectionalMap systemFonts = { + "standard", PlSharedFontType_Standard, + "chinese simplified", PlSharedFontType_ChineseSimplified, + "chinese simplified extended", PlSharedFontType_ExtChineseSimplified, + "chinese traditional", PlSharedFontType_ChineseTraditional, + "korean", PlSharedFontType_KO, + "nintendo extended", PlSharedFontType_NintendoExt + }; + // clang-format on + }; +} // namespace love diff --git a/platform/hac/include/modules/graphics_ext.hpp b/platform/hac/include/modules/graphics_ext.hpp new file mode 100644 index 000000000..2896faa37 --- /dev/null +++ b/platform/hac/include/modules/graphics_ext.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace love +{ + template<> + class Graphics : public Graphics + { + public: + Graphics(); + + virtual ~Graphics(); + + bool SetMode(int x, int y, int width, int height); + + void SetShader(); + + void SetShader(Shader* shader); + + Texture* NewTexture(const Texture<>::Settings& settings, + const Texture<>::Slices* slices = nullptr) const; + + void Draw(Texture* texture, Quad* quad, const Matrix4& matrix); + + void Draw(Drawable* drawable, const Matrix4& matrix); + + void SetViewportSize(int width, int height); + }; // namespace love +} // namespace love diff --git a/platform/hac/include/modules/joystickmodule_ext.hpp b/platform/hac/include/modules/joystickmodule_ext.hpp new file mode 100644 index 000000000..49d9340c6 --- /dev/null +++ b/platform/hac/include/modules/joystickmodule_ext.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include +#include + +#include +#include + +namespace love +{ + template<> + class JoystickModule : public JoystickModule + { + public: + JoystickModule(); + + virtual ~JoystickModule(); + + std::vector AcquireCurrentJoystickIds(); + + std::vector GetActiveStyleSets(); + + Joystick* AddJoystick(int index); + + void AddVibration(::Vibration* vibration); + + private: + VibrationPool* pool; + PoolThread* thread; + }; +} // namespace love diff --git a/platform/hac/include/modules/keyboard_ext.hpp b/platform/hac/include/modules/keyboard_ext.hpp new file mode 100644 index 000000000..391cc1d20 --- /dev/null +++ b/platform/hac/include/modules/keyboard_ext.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +#include + +namespace love +{ + template<> + class Keyboard : public Keyboard + { + public: + static constexpr uint32_t MAX_INPUT_LENGTH = 0x1F4; + + const uint32_t GetMaxEncodingLength(const uint32_t in) + { + return in * 0x04; + } + + Keyboard(); + + void SetTextInput(const KeyboardOptions& options); + + const bool HasTextInput() const + { + return this->showing; + } + + // clang-format off + static constexpr BidirectionalMap keyboardTypes = { + "normal", SwkbdType_Normal, + "qwerty", SwkbdType_QWERTY, + "numpad", SwkbdType_NumPad + }; + // clang-format on + + private: + SwkbdConfig config; + bool showing; + }; +} // namespace love diff --git a/platform/hac/include/modules/system_ext.hpp b/platform/hac/include/modules/system_ext.hpp new file mode 100644 index 000000000..d3be64db9 --- /dev/null +++ b/platform/hac/include/modules/system_ext.hpp @@ -0,0 +1,76 @@ +#pragma once + +#include + +#include + +#include + +namespace love +{ + template<> + class System : public System + { + public: + System(); + + PowerState GetPowerInfo(uint8_t& percent) const; + + NetworkState GetNetworkInfo(uint8_t& signal) const; + + int GetProcessorCount(); + + std::string_view GetUsername(); + + std::string_view GetSystemTheme(); + + std::string_view GetPreferredLocales(); + + std::string_view GetVersion(); + + std::string_view GetModel(); + + std::string_view GetFriendInfo(); + + // clang-format off + static constexpr BidirectionalMap languages = { + "jp", SetLanguage_JA, + "en_US", SetLanguage_ENUS, + "fr", SetLanguage_FR, + "de", SetLanguage_DE, + "it", SetLanguage_IT, + "es", SetLanguage_ES, + "zh_CN", SetLanguage_ZHCN, + "ko", SetLanguage_KO, + "nl", SetLanguage_NL, + "pt", SetLanguage_PT, + "ru", SetLanguage_RU, + "zh_TW", SetLanguage_ZHTW, + "en_GB", SetLanguage_ENGB, + "fr_CA", SetLanguage_FRCA, + "es_419", SetLanguage_ES419, + "zh_HANS", SetLanguage_ZHHANS, + "zh_HANT", SetLanguage_ZHHANT, + "pt_BR", SetLanguage_PTBR + }; + + static constexpr BidirectionalMap models = { + "Invalid", SetSysProductModel_Invalid, + "Erista", SetSysProductModel_Nx, + "Erista Simulation", SetSysProductModel_Copper, + "Mariko", SetSysProductModel_Iowa, + "Mariko Lite", SetSysProductModel_Hoag, + "Mariko Simulation", SetSysProductModel_Calcio, + "Mariko Pro", SetSysProductModel_Aula + }; + + static constexpr BidirectionalMap themes = { + "dark", ColorSetId_Dark, + "light", ColorSetId_Light + }; + // clang-format on + + private: + AccountUid account; + }; +} // namespace love diff --git a/platform/hac/include/modules/timer_ext.hpp b/platform/hac/include/modules/timer_ext.hpp new file mode 100644 index 000000000..84d208b49 --- /dev/null +++ b/platform/hac/include/modules/timer_ext.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace love +{ + template<> + class Timer : public Timer + { + public: + Timer(); + + double Step(); + + void Sleep(double seconds) const; + + static double GetTime(); + + private: + static constexpr double NANOSECONDS_TO_SECONDS = 1000000000.0; + static double reference; + }; +} // namespace love diff --git a/platform/hac/include/modules/window_ext.hpp b/platform/hac/include/modules/window_ext.hpp new file mode 100644 index 000000000..3f85fecdb --- /dev/null +++ b/platform/hac/include/modules/window_ext.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include + +#include + +#include + +namespace love +{ + template<> + class Window : public Window + { + public: + Window(); + + virtual ~Window(); + + void SetGraphics(Graphics* graphics) + { + this->graphics.Set(graphics); + } + + bool CreateWindowAndContext(int x, int y, int width, int height); + + bool SetWindow(int width = 800, int height = 600, WindowSettings* settings = nullptr); + + void GetWindow(int& width, int& height, WindowSettings& settings); + + void Close(); + + bool SetFullscreen(bool fullscreen, FullscreenType fullScreenType) + { + return true; + } + + bool SetFullscreen(bool fullscreen) + { + return true; + } + + bool OnSizeChanged(int width, int height); + + int GetDisplayCount() const + { + return 1; + } + + std::string_view GetDisplayName(int displayIndex) const; + + std::vector GetFullscreenSizes(int displayIndex); + + void GetDesktopDimensions(int displayIndex, int& width, int& height); + + void SetPosition(int x, int y, int displayIndex); + + void GetPosition(int& x, int& y, int& displayIndex); + + void SetDisplaySleepEnabled(bool enable); + + bool IsDisplaySleepEnabled() const; + + private: + StrongReference> graphics; + }; +} // namespace love diff --git a/platform/hac/include/objects/imagedata_ext.hpp b/platform/hac/include/objects/imagedata_ext.hpp new file mode 100644 index 000000000..cf14536c1 --- /dev/null +++ b/platform/hac/include/objects/imagedata_ext.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace love +{ + template<> + class ImageData : public ImageData + { + public: + using ImageData::ImageData; + + ImageData* Clone() const override + { + return new ImageData(*this); + } + }; +} // namespace love diff --git a/platform/hac/include/objects/joystick_ext.hpp b/platform/hac/include/objects/joystick_ext.hpp new file mode 100644 index 000000000..da619bb2c --- /dev/null +++ b/platform/hac/include/objects/joystick_ext.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include + +#include + +#include + +using Vibration = love::Vibration; + +namespace love +{ + template<> + class Joystick : public Joystick + { + public: + Joystick(int id); + + Joystick(int id, int index); + + virtual ~Joystick(); + + bool Open(int index); + + void Close(); + + bool IsConnected() const; + + bool IsDown(JoystickInput& result); + + bool IsUp(JoystickInput& result); + + void GetDeviceInfo(int& vendor, int& product, int& version); + + int GetAxisCount() const; + + int GetButtonCount() const; + + void Update(); + + float GetAxis(int index); + + std::vector GetAxes(); + + bool IsDown(const std::vector& buttons) const; + + void SetPlayerIndex(int index); + + int GetPlayerIndex() const + { + return (int)this->playerId; + } + + bool IsGamepad() const + { + return true; + } + + bool IsAxisChanged(GamepadAxis axis); + + guid::GamepadType GetGamepadType() const; + + float GetGamepadAxis(GamepadAxis axis); + + bool IsGamepadDown(const std::vector& buttons) const; + + bool IsVibrationSupported() + { + return true; + } + + bool SetVibration(float left, float right, float duration = -1.0f); + + bool SetVibration(); + + void GetVibration(float& left, float& right); + + bool HasSensor(Sensor::SensorType type) const; + + bool IsSensorEnabled(Sensor::SensorType type); + + void SetSensorEnabled(Sensor::SensorType type, bool enabled); + + std::vector GetSensorData(Sensor::SensorType type); + + private: + PadState state; + + HidNpadStyleTag style; + HidNpadIdType playerId; + + struct + { + uint64_t pressed; + uint64_t released; + } buttonStates; + + ::Vibration vibration; + }; +} // namespace love diff --git a/platform/hac/include/objects/shader_ext.hpp b/platform/hac/include/objects/shader_ext.hpp new file mode 100644 index 000000000..b32e89226 --- /dev/null +++ b/platform/hac/include/objects/shader_ext.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include + +#include + +#include + +namespace love +{ + template<> + class Shader : public Shader + { + public: + struct DekoStage + { + dk::Shader shader; + CMemPool::Handle codeMemory; + }; + + struct Program + { + std::optional vertex; + std::optional fragment; + }; + + struct DkshHeader + { + uint32_t magic; + uint32_t header_sz; + uint32_t control_sz; + uint32_t code_sz; + uint32_t programs_off; + uint32_t num_programs; + }; + + static constexpr size_t HEADER_SIZE = sizeof(DkshHeader); + + Shader(); + + Shader(Data* vertex, Data* fragment); + + virtual ~Shader(); + + void Attach(); + + std::optional Vertex() + { + return this->program.vertex; + } + + std::optional Fragment() + { + return this->program.fragment; + } + + static void AttachDefault(StandardShader type); + + bool Validate(const char* filepath, DekoStage& stage, std::string& error) const; + + bool Validate(Data* data, DekoStage& stage, std::string& error) const; + + void LoadDefaults(StandardShader type); + + private: + Program program; + }; + +} // namespace love diff --git a/platform/hac/include/objects/source_ext.hpp b/platform/hac/include/objects/source_ext.hpp new file mode 100644 index 000000000..175bca93b --- /dev/null +++ b/platform/hac/include/objects/source_ext.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include +#include + +#include +#include + +namespace love +{ + class Audio; + class AudioPool; + + template<> + class Source : public Source + { + public: + Source(AudioPool* pool, SoundData* soundData); + + Source(AudioPool* pool, Decoder* decoder); + + Source(AudioPool* pool, int sampleRate, int bitDepth, int channels, int buffers); + + Source(const Source& other); + + virtual ~Source(); + + virtual Source* Clone(); + + /* normal stuff */ + + bool Play(); + + void Stop(); + + void Pause(); + + bool IsPlaying() const; + + bool IsFinished() const; + + bool Update(); + + void SetVolume(float volume); + + float GetVolume() const; + + void Seek(double offset, Unit unit); + + double Tell(Unit unit); + + double GetDuration(Unit unit); + + void SetLooping(bool looping); + + int GetChannelCount() const; + + int GetFreeBufferCount() const; + + bool Queue(void* data, size_t length, int sampleRate, int bitDepth, int channels); + + /* atomic things */ + + void PrepareAtomic(); + + void TeardownAtomic(); + + bool PlayAtomic(AudioDriverWaveBuf& waveBuffer); + + void StopAtomic(); + + void PauseAtomic(); + + void ResumeAtomic(); + + /* global state stuff */ + + static bool Play(const std::vector*>& sources); + + static void Stop(const std::vector*>& sources); + + static void Pause(const std::vector*>& sources); + + static std::vector*> Pause(AudioPool* pool); + + static void Stop(AudioPool* pool); + + private: + static constexpr size_t MAX_BUFFERS = 2; + + void Reset(); + + int StreamAtomic(AudioDriverWaveBuf& buffer, Decoder* decoder); + + AudioPool* pool; + + struct WaveInfo + { + AudioDriverWaveBuf buffer; + size_t alignedSize; + } buffers[2]; + }; +} // namespace love diff --git a/platform/hac/include/objects/texture_ext.hpp b/platform/hac/include/objects/texture_ext.hpp new file mode 100644 index 000000000..868dfbaa5 --- /dev/null +++ b/platform/hac/include/objects/texture_ext.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include + +#include + +namespace love +{ + template<> + class Texture : public Texture + { + public: + Texture(const Graphics* graphics, const Settings& settings, + const Slices* data); + + virtual ~Texture(); + + virtual void Draw(Graphics& graphics, + const Matrix4& matrix) override; + + virtual void Draw(Graphics& graphics, Quad* quad, + const Matrix4& transform) override; + + void ReplacePixels(ImageDataBase* data, int slice, int mipmap, int x, int y, + bool reloadMipmaps); + + void ReplacePixels(const void* data, size_t size, int slice, int mipmap, const Rect& rect, + bool reloadMipmaps); + + void SetSamplerState(const SamplerState& state); + + void GenerateMipmaps() + {} + + bool LoadVolatile(); + + void UnloadVolatile(); + + DkResHandle GetHandle() const + { + return this->textureHandle; + } + + dk::Image GetImage() + { + return this->image; + } + + dk::ImageDescriptor& GetDescriptor() + { + return this->descriptor; + } + + dk::Sampler& GetSampler() + { + return this->sampler; + } + + private: + void CreateTexture(); + + DkResHandle textureHandle; + + dk::Image image; + dk::ImageDescriptor descriptor; + CMemPool::Handle memory; + + dk::Sampler sampler; + }; +} // namespace love diff --git a/platform/hac/include/scripts/wrap_window.lua b/platform/hac/include/scripts/wrap_window.lua new file mode 100644 index 000000000..b9c0ac5c3 --- /dev/null +++ b/platform/hac/include/scripts/wrap_window.lua @@ -0,0 +1,4 @@ +R"luastring"--( +function love.window.showMessageBox(title, text, buttons) +end +--)luastring"--" diff --git a/platform/hac/include/scripts/wrap_window_messagebox.lua b/platform/hac/include/scripts/wrap_window_messagebox.lua new file mode 100644 index 000000000..9385054bf --- /dev/null +++ b/platform/hac/include/scripts/wrap_window_messagebox.lua @@ -0,0 +1,6 @@ +R"luastring"--( +local empty = {} +empty.__index = empty + +return empty +--)luastring"--" diff --git a/platform/hac/include/utilities/abort.hpp b/platform/hac/include/utilities/abort.hpp new file mode 100644 index 000000000..3ee3d3e73 --- /dev/null +++ b/platform/hac/include/utilities/abort.hpp @@ -0,0 +1,39 @@ +#include + +namespace love +{ + enum AbortCode + { + ABORT_PLU, + ABORT_NIFM, + ABORT_ACC, + ABORT_SET, + ABORT_SETSYS, + ABORT_PSM, + ABORT_SOCKETS, + ABORT_FRIENDV, + ABORT_APPLET + }; + + static constexpr const char* ABORT_FORMAT = + "Failed to initalize %s!\n\n" + "Result: %ld\n" + "Description: %ld\n\n" + "Please visit https://switchbrew.org/wiki/Error_codes for more information.\n"; + + static constexpr const char* TITLE_TAKEOVER_ERROR = "Please run LÖVE Potion under " + "Atmosphère title takeover."; + // clang-format off + static constexpr BidirectionalMap abortTypes = + { + ABORT_PLU, "pl:u", + ABORT_NIFM, "nifm:u", + ABORT_ACC, "acc:a", + ABORT_SET, "set", + ABORT_SETSYS, "set:sys", + ABORT_PSM, "psm", + ABORT_SOCKETS, "sockets", + ABORT_FRIENDV, "friend:v" + }; + // clang-format on +} // namespace love diff --git a/platform/switch/include/deko3d/CCmdMemRing.h b/platform/hac/include/utilities/deko3d/CCmdMemRing.h similarity index 94% rename from platform/switch/include/deko3d/CCmdMemRing.h rename to platform/hac/include/utilities/deko3d/CCmdMemRing.h index dda6a2111..989241eb4 100644 --- a/platform/switch/include/deko3d/CCmdMemRing.h +++ b/platform/hac/include/utilities/deko3d/CCmdMemRing.h @@ -19,6 +19,10 @@ class CCmdMemRing CCmdMemRing() : m_mem {}, m_curSlice {}, m_fences {} {} + CCmdMemRing(const CCmdMemRing&) = delete; + + CCmdMemRing& operator=(const CCmdMemRing&) = delete; + ~CCmdMemRing() { m_mem.destroy(); diff --git a/platform/switch/include/deko3d/CCmdVtxRing.h b/platform/hac/include/utilities/deko3d/CCmdVtxRing.h similarity index 83% rename from platform/switch/include/deko3d/CCmdVtxRing.h rename to platform/hac/include/utilities/deko3d/CCmdVtxRing.h index 3668f2660..8be6cb071 100644 --- a/platform/switch/include/deko3d/CCmdVtxRing.h +++ b/platform/hac/include/utilities/deko3d/CCmdVtxRing.h @@ -4,10 +4,10 @@ */ #pragma once -#include "deko3d/CMemPool.h" -#include "deko3d/common.h" +#include +#include -#include "vertex.h" +#include template class CCmdVtxRing @@ -22,6 +22,10 @@ class CCmdVtxRing CCmdVtxRing() : m_mem {}, m_curSlice {}, m_sliceSize {} {} + CCmdVtxRing(const CCmdVtxRing&) = delete; + + CCmdVtxRing& operator=(const CCmdVtxRing&) = delete; + ~CCmdVtxRing() { m_mem.destroy(); @@ -30,7 +34,7 @@ class CCmdVtxRing bool allocate(CMemPool& pool, uint32_t size) { m_sliceSize = (size + DK_CMDMEM_ALIGNMENT - 1) & ~(DK_CMDMEM_ALIGNMENT - 1); - m_mem = pool.allocate(NumSlices * m_sliceSize, alignof(vertex::Vertex)); + m_mem = pool.allocate(NumSlices * m_sliceSize, alignof(love::vertex::Vertex)); return m_mem; } diff --git a/platform/switch/include/deko3d/CDescriptorSet.h b/platform/hac/include/utilities/deko3d/CDescriptorSet.h similarity index 95% rename from platform/switch/include/deko3d/CDescriptorSet.h rename to platform/hac/include/utilities/deko3d/CDescriptorSet.h index 542701dd5..395876ccd 100644 --- a/platform/switch/include/deko3d/CDescriptorSet.h +++ b/platform/hac/include/utilities/deko3d/CDescriptorSet.h @@ -21,6 +21,11 @@ class CDescriptorSet public: CDescriptorSet() : m_mem {} {} + + CDescriptorSet(const CDescriptorSet&) = delete; + + CDescriptorSet& operator=(const CDescriptorSet&) = delete; + ~CDescriptorSet() { m_mem.destroy(); diff --git a/platform/switch/include/deko3d/CIntrusiveList.h b/platform/hac/include/utilities/deko3d/CIntrusiveList.h similarity index 100% rename from platform/switch/include/deko3d/CIntrusiveList.h rename to platform/hac/include/utilities/deko3d/CIntrusiveList.h diff --git a/platform/switch/include/deko3d/CIntrusiveTree.h b/platform/hac/include/utilities/deko3d/CIntrusiveTree.h similarity index 100% rename from platform/switch/include/deko3d/CIntrusiveTree.h rename to platform/hac/include/utilities/deko3d/CIntrusiveTree.h diff --git a/platform/switch/include/deko3d/CMemPool.h b/platform/hac/include/utilities/deko3d/CMemPool.h similarity index 93% rename from platform/switch/include/deko3d/CMemPool.h rename to platform/hac/include/utilities/deko3d/CMemPool.h index 2b82feb94..324b27720 100644 --- a/platform/switch/include/deko3d/CMemPool.h +++ b/platform/hac/include/utilities/deko3d/CMemPool.h @@ -20,6 +20,10 @@ class CMemPool void* m_cpuAddr; DkGpuAddr m_gpuAddr; + Block(const Block&) = delete; + + Block& operator=(const Block&) = delete; + constexpr void* cpuOffset(uint32_t offset) const { return m_cpuAddr ? ((u8*)m_cpuAddr + offset) : nullptr; @@ -42,6 +46,10 @@ class CMemPool uint32_t m_start; uint32_t m_end; + Slice(const Slice&) = delete; + + Slice& operator=(const Slice&) = delete; + constexpr uint32_t getSize() const { return m_end - m_start; @@ -73,7 +81,6 @@ class CMemPool public: static constexpr uint32_t DefaultBlockSize = 0x800000; - class Handle { Slice* m_slice; @@ -148,9 +155,14 @@ class CMemPool m_sliceHeap {}, m_freeList {} {} + ~CMemPool(); Handle allocate(uint32_t size, uint32_t alignment = DK_CMDMEM_ALIGNMENT); + + CMemPool(const CMemPool&) = delete; + + CMemPool& operator=(const CMemPool&) = delete; }; constexpr bool operator<(uint32_t lhs, CMemPool::Slice const& rhs) diff --git a/platform/switch/include/deko3d/LICENSE b/platform/hac/include/utilities/deko3d/LICENSE similarity index 100% rename from platform/switch/include/deko3d/LICENSE rename to platform/hac/include/utilities/deko3d/LICENSE diff --git a/platform/hac/include/utilities/deko3d/bitalloc.hpp b/platform/hac/include/utilities/deko3d/bitalloc.hpp new file mode 100644 index 000000000..f1c9a3c59 --- /dev/null +++ b/platform/hac/include/utilities/deko3d/bitalloc.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +template +class BitwiseAlloc : std::bitset +{ + public: + size_t Allocate() + { + size_t index = 0; + while (index < this->size() && this->test(index)) + ++index; + + this->set(index); + return index; + } + + void DeAllocate(uint32_t handle) + { + auto index = 0; + + if (this->Find(handle, index)) + this->reset(index); + } + + bool Find(uint32_t handle, int& out) + { + size_t index = (handle & ((1U << 20) - 1)); + + if (this->test(index)) + { + out = index; + return true; + } + + out = -1; + return false; + } +}; diff --git a/platform/hac/include/utilities/deko3d/common.h b/platform/hac/include/utilities/deko3d/common.h new file mode 100644 index 000000000..ee5babc59 --- /dev/null +++ b/platform/hac/include/utilities/deko3d/common.h @@ -0,0 +1,13 @@ +/* +** Sample Framework for deko3d Applications +** common.h: Common includes +*/ +#pragma once +#include +#include +#include + +#include + +#define DK_HPP_SUPPORT_VECTOR +#include diff --git a/platform/hac/include/utilities/driver/dsp_ext.hpp b/platform/hac/include/utilities/driver/dsp_ext.hpp new file mode 100644 index 000000000..43bd3adcf --- /dev/null +++ b/platform/hac/include/utilities/driver/dsp_ext.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include + +#include + +namespace love +{ + template<> + class DSP : public DSP + { + public: + static DSP& Instance() + { + static DSP instance; + return instance; + } + + DSP(); + + ~DSP(); + + void Initialize(); + + void Update(); + + void SetMasterVolume(float volume); + + float GetMasterVolume() const; + + bool ChannelReset(size_t id, int channels, int bitDepth, int sampleRate); + + void ChannelSetVolume(size_t id, float volume); + + float ChannelGetVolume(size_t id) const; + + size_t ChannelGetSampleOffset(size_t id); + + bool ChannelAddBuffer(size_t id, AudioDriverWaveBuf* buffer); + + void ChannelPause(size_t id, bool paused = true); + + bool IsChannelPaused(size_t id); + + bool IsChannelPlaying(size_t id); + + void ChannelStop(size_t id); + + // clang-format off + static constexpr BidirectionalMap audioFormats = { + 0x08, PcmFormat_Int8, + 0x10, PcmFormat_Int16 + }; + // clang-format on + + static int8_t GetFormat(int bitDepth, int channels); + + private: + love::mutex mutex; + bool channelReset; + AudioDriver driver; + }; +} // namespace love diff --git a/platform/hac/include/utilities/driver/dsp_mem.hpp b/platform/hac/include/utilities/driver/dsp_mem.hpp new file mode 100644 index 000000000..799d32f4d --- /dev/null +++ b/platform/hac/include/utilities/driver/dsp_mem.hpp @@ -0,0 +1,139 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace AudioMemory +{ + extern void* POOL_BASE; + inline uint64_t POOL_SIZE = 0x1000000; + + struct MemoryChunk + { + uint8_t* address; + size_t size; + }; + + struct MemoryBlock + { + MemoryBlock* prev; + MemoryBlock* next; + + uint8_t* base; + size_t size; + + static MemoryBlock* Create(uint8_t* base, size_t size) + { + auto block = (MemoryBlock*)malloc(sizeof(MemoryBlock)); + + if (!block) + return nullptr; + + block->prev = nullptr; + block->next = nullptr; + + block->base = base; + block->size = size; + + return block; + } + }; + + struct MemoryPool + { + MemoryBlock* first; + MemoryBlock* last; + + bool Ready() + { + return first != nullptr; + } + + void AddBlock(MemoryBlock* block) + { + block->prev = last; + + if (last) + last->next = block; + + if (!first) + first = block; + + last = block; + } + + void DeleteBlock(MemoryBlock* block) + { + auto prev = block->prev; + auto& pNext = (prev) ? prev->next : first; + + auto next = block->next; + auto& nPrev = (next) ? next->prev : last; + + pNext = next; + nPrev = prev; + + free(block); + } + + void InsertBefore(MemoryBlock* b, MemoryBlock* p) + { + auto prev = b->prev; + auto& pNext = (prev) ? prev->next : first; + + b->prev = p; + p->next = b; + + p->prev = prev; + + pNext = p; + } + + void InsertAfter(MemoryBlock* b, MemoryBlock* n) + { + auto next = b->next; + auto& nPrev = (next) ? next->prev : last; + + b->next = n; + n->prev = b; + + n->next = next; + + nPrev = n; + } + + void CoalesceRight(MemoryBlock* block); + + bool Allocate(MemoryChunk& chunk, size_t size); + void DeAllocate(uint8_t* address, size_t size); + + void Destroy() + { + MemoryBlock* next = nullptr; + + for (auto block = first; block; block = next) + { + next = block->next; + free(block); + } + + first = nullptr; + last = nullptr; + } + }; + + extern MemoryPool audioPool; + + bool Initialize(); + + bool InitMemPool(); + + void* Align(size_t size, size_t& aligned); + + void Free(const void* chunk, size_t size); +} // namespace AudioMemory diff --git a/platform/hac/include/utilities/driver/hid_ext.hpp b/platform/hac/include/utilities/driver/hid_ext.hpp new file mode 100644 index 000000000..63da4071e --- /dev/null +++ b/platform/hac/include/utilities/driver/hid_ext.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include + +#include +#include + +namespace love +{ + template<> + class HID : public HID + { + public: + static HID& Instance() + { + static HID instance; + return instance; + } + + ~HID(); + + private: + HID(); + + static constexpr int MAX_TOUCHES = 16; + + virtual void _Poll() override; + + void CheckFocus(); + + HidTouchScreenState touchState; + + std::array stateTouches; + std::array oldStateTouches; + + int previousTouchCount; + + std::vector previousJoystickState; + std::vector previousGamepadTypes; + + std::array<::Event, npad::MAX_JOYSTICKS> statusEvents; + }; +} // namespace love diff --git a/platform/hac/include/utilities/driver/renderer_ext.hpp b/platform/hac/include/utilities/driver/renderer_ext.hpp new file mode 100644 index 000000000..7a5390f45 --- /dev/null +++ b/platform/hac/include/utilities/driver/renderer_ext.hpp @@ -0,0 +1,332 @@ +#pragma once + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +/* Enforces GLSL std140/std430 alignment rules for glm types */ +#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES +/* Enables usage of SIMD CPU instructions (requiring the above as well) */ +#define GLM_FORCE_INTRINSICS +#include +#include +#include +#include + +namespace love +{ + template<> + class Renderer : public Renderer + { + private: + static constexpr const char* RENDERER_NAME = "deko3d"; + static constexpr const char* RENDERER_VERSION = "0.4.0"; + static constexpr const char* RENDERER_VENDOR = "devkitPro"; + static constexpr const char* RENDERER_DEVICE = "NVIDIA Tegra X1"; + + static constexpr uint8_t MAX_RENDERTARGETS = 0x02; + static constexpr int MAX_ANISOTROPY = 0x10; + + static constexpr int COMMAND_SIZE = 0x100000; + static constexpr int VERTEX_COMMAND_SIZE = 0x100000; + + static constexpr int MAX_OBJECTS = 0x250; + + // clang-format off + static constexpr int GPU_POOL_SIZE = 0x4000000; + static constexpr int GPU_USE_FLAGS = (DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image); + + static constexpr int CPU_POOL_SIZE = 0x100000; + static constexpr int CPU_USE_FLAGS = (DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached); + + static constexpr int SHADER_POOL_SIZE = 0x20000; + static constexpr int SHADER_USE_FLAGS = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | + DkMemBlockFlags_Code; + + static constexpr int RENDERTARGET_USE_FLAGS = DkImageFlags_UsageRender | DkImageFlags_UsagePresent | + DkImageFlags_HwCompression; + // clang-format on + + Renderer(); + + public: + enum MemPoolType + { + IMAGE, + CODE, + DATA + }; + + enum QueueType + { + QUEUE_MAIN, + QUEUE_IMAGES + }; + + static Renderer& Instance() + { + static Renderer instance; + return instance; + } + + virtual ~Renderer(); + + Info GetRendererInfo(); + + void DestroyFramebuffers(); + + void CreateFramebuffers(); + + void Clear(const Color& color); + + void ClearDepthStencil(int stencil, uint8_t mask, double depth); + + void SetBlendColor(const Color& color); + + void SetBlendMode(const RenderState::BlendState& state); + + void BindFramebuffer(Texture* texture = nullptr); + + void Present(); + + void SetViewport(const Rect& viewport); + + void SetScissor(const Rect& scissor, bool canvasActive); + + void SetStencil(RenderState::CompareMode mode, int value); + + void SetMeshCullMode(vertex::CullMode mode); + + void SetVertexWinding(vertex::Winding winding); + + void SetSamplerState(Texture* texture, SamplerState& state); + + void SetColorMask(const RenderState::ColorMask& mask); + + void SetLineWidth(float lineWidth); + + void SetLineStyle(RenderState::LineStyle style); + + void SetPointSize(float size); + + void CheckDescriptorsDirty(const std::vector& handles); + + bool Render(const DrawCommand& command); + + void UseProgram(Shader::Program program); + + void Register(Texture* texture, DkResHandle& handle); + + void UnRegister(Texture* texture); + + void SetAttributes(const vertex::attributes::Attribs& attributes); + + bool IsHandheldMode() const; + + CMemPool::Handle Allocate(MemPoolType type, size_t size, uint32_t alignment = 0); + + CMemPool& GetMemPool(MemPoolType type); + + dk::Queue GetQueue(QueueType type); + + dk::Device GetDevice() + { + return this->device; + } + + static void FlushVertices() + {} + + // clang-format off + static constexpr BidirectionalMap pixelFormats = { + PIXELFORMAT_R8_UNORM, DkImageFormat_R8_Unorm, + PIXELFORMAT_R16_UNORM, DkImageFormat_R16_Unorm, + PIXELFORMAT_RG8_UNORM, DkImageFormat_RG8_Unorm, + PIXELFORMAT_RGBA8_UNORM, DkImageFormat_RGBA8_Unorm, + PIXELFORMAT_RGB565_UNORM, DkImageFormat_RGB565_Unorm, + PIXELFORMAT_RGBA8_UNORM_SRGB, DkImageFormat_RGBA8_Unorm_sRGB, + PIXELFORMAT_DXT1_UNORM, DkImageFormat_RGBA_BC1, + PIXELFORMAT_DXT3_UNORM, DkImageFormat_RGBA_BC2, + PIXELFORMAT_DXT5_UNORM, DkImageFormat_RGBA_BC3, + PIXELFORMAT_ETC1_UNORM, DkImageFormat_RGB_ETC2, + PIXELFORMAT_ETC2_RGB_UNORM, DkImageFormat_RGB_ETC2, + PIXELFORMAT_ETC2_RGBA1_UNORM, DkImageFormat_RGBA_ETC2, + PIXELFORMAT_ETC2_RGBA_UNORM, DkImageFormat_RGBA_ETC2, + PIXELFORMAT_BC4_UNORM, DkImageFormat_R_BC4_Unorm, + PIXELFORMAT_BC5_UNORM, DkImageFormat_RG_BC5_Unorm, + PIXELFORMAT_BC7_UNORM, DkImageFormat_RGBA_BC7_Unorm, + PIXELFORMAT_BC7_UNORM_SRGB, DkImageFormat_RGBA_BC7_Unorm_sRGB, + PIXELFORMAT_ASTC_4x4, DkImageFormat_RGBA_ASTC_4x4, + PIXELFORMAT_ASTC_5x4, DkImageFormat_RGBA_ASTC_5x4, + PIXELFORMAT_ASTC_6x5, DkImageFormat_RGBA_ASTC_6x5, + PIXELFORMAT_ASTC_6x6, DkImageFormat_RGBA_ASTC_6x6, + PIXELFORMAT_ASTC_8x5, DkImageFormat_RGBA_ASTC_8x5, + PIXELFORMAT_ASTC_8x6, DkImageFormat_RGBA_ASTC_8x6, + PIXELFORMAT_ASTC_8x8, DkImageFormat_RGBA_ASTC_8x8, + PIXELFORMAT_ASTC_10x5, DkImageFormat_RGBA_ASTC_10x5, + PIXELFORMAT_ASTC_10x6, DkImageFormat_RGBA_ASTC_10x6, + PIXELFORMAT_ASTC_10x8, DkImageFormat_RGBA_ASTC_10x8, + PIXELFORMAT_ASTC_10x10, DkImageFormat_RGBA_ASTC_10x10, + PIXELFORMAT_ASTC_12x10, DkImageFormat_RGBA_ASTC_12x10, + PIXELFORMAT_ASTC_12x12, DkImageFormat_RGBA_ASTC_12x12 + }; + + static constexpr BidirectionalMap blendEquations = { + RenderState::BLENDOP_ADD, DkBlendOp_Add, + RenderState::BLENDOP_SUBTRACT, DkBlendOp_Sub, + RenderState::BLENDOP_REVERSE_SUBTRACT, DkBlendOp_RevSub, + RenderState::BLENDOP_MIN, DkBlendOp_Min, + RenderState::BLENDOP_MAX, DkBlendOp_Max + }; + + static constexpr BidirectionalMap blendFactors = { + RenderState::BLENDFACTOR_ZERO, DkBlendFactor_Zero, + RenderState::BLENDFACTOR_ONE, DkBlendFactor_One, + RenderState::BLENDFACTOR_SRC_COLOR, DkBlendFactor_SrcColor, + RenderState::BLENDFACTOR_ONE_MINUS_SRC_COLOR, DkBlendFactor_InvSrcColor, + RenderState::BLENDFACTOR_SRC_ALPHA, DkBlendFactor_SrcAlpha, + RenderState::BLENDFACTOR_ONE_MINUS_SRC_ALPHA, DkBlendFactor_InvSrcAlpha, + RenderState::BLENDFACTOR_DST_COLOR, DkBlendFactor_DstColor, + RenderState::BLENDFACTOR_ONE_MINUS_DST_COLOR, DkBlendFactor_InvDstColor, + RenderState::BLENDFACTOR_DST_ALPHA, DkBlendFactor_DstAlpha, + RenderState::BLENDFACTOR_ONE_MINUS_DST_ALPHA, DkBlendFactor_InvDstAlpha, + RenderState::BLENDFACTOR_SRC_ALPHA_SATURATED, DkBlendFactor_SrcAlphaSaturate + }; + + static constexpr BidirectionalMap filterModes = { + SamplerState::FILTER_LINEAR, DkFilter_Linear, + SamplerState::FILTER_NEAREST, DkFilter_Nearest + }; + + static constexpr BidirectionalMap mipmapFilterModes = { + SamplerState::MIPMAP_FILTER_NONE, DkMipFilter_None, + SamplerState::MIPMAP_FILTER_LINEAR, DkMipFilter_Linear, + SamplerState::MIPMAP_FILTER_NEAREST, DkMipFilter_Nearest + }; + + static constexpr BidirectionalMap wrapModes = { + SamplerState::WRAP_CLAMP, DkWrapMode_ClampToEdge, + SamplerState::WRAP_CLAMP_ZERO, DkWrapMode_ClampToBorder, + SamplerState::WRAP_REPEAT, DkWrapMode_Repeat, + SamplerState::WRAP_MIRRORED_REPEAT, DkWrapMode_MirroredRepeat + }; + + static constexpr BidirectionalMap cullModes = { + vertex::CULL_NONE, DkFace_None, + vertex::CULL_BACK, DkFace_Back, + vertex::CULL_FRONT, DkFace_Front + }; + + static constexpr BidirectionalMap windingModes = { + vertex::WINDING_CW, DkFrontFace_CW, + vertex::WINDING_CCW, DkFrontFace_CCW + }; + + static constexpr BidirectionalMap compareModes = { + RenderState::COMPARE_LESS, DkCompareOp_Less, + RenderState::COMPARE_LEQUAL, DkCompareOp_Lequal, + RenderState::COMPARE_EQUAL, DkCompareOp_Equal, + RenderState::COMPARE_GEQUAL, DkCompareOp_Gequal, + RenderState::COMPARE_GREATER, DkCompareOp_Greater, + RenderState::COMPARE_NOTEQUAL, DkCompareOp_NotEqual, + RenderState::COMPARE_ALWAYS, DkCompareOp_Always, + RenderState::COMPARE_NEVER, DkCompareOp_Never + }; + + static constexpr BidirectionalMap primitiveModes = { + vertex::PRIMITIVE_TRIANGLES, DkPrimitive_Triangles, + vertex::PRIMITIVE_TRIANGLE_FAN, DkPrimitive_TriangleFan, + vertex::PRIMITIVE_TRIANGLE_STRIP, DkPrimitive_TriangleStrip, + vertex::PRIMITIVE_QUADS, DkPrimitive_Quads, + vertex::PRIMITIVE_POINTS, DkPrimitive_Points + }; + + private: + struct Transform + { + glm::mat4 modelView; + glm::mat4 projection; + } transform; + static constexpr int TRANSFORM_SIZE = sizeof(Transform); + + CMemPool::Handle uniformBuffer; + + uint32_t firstVertex; + vertex::Vertex* data; + + dk::UniqueDevice device; + + dk::UniqueQueue mainQueue; + dk::UniqueQueue textureQueue; + + dk::UniqueCmdBuf commandBuffer; + dk::UniqueSwapchain swapchain; + + struct MemPools + { + CMemPool image; + CMemPool data; + CMemPool code; + } pools; + + struct DeviceState + { + dk::RasterizerState rasterizer; + dk::ColorState color; + dk::ColorWriteState colorWrite; + dk::BlendState blend; + dk::DepthStencilState depthStencil; + } state; + + struct Framebuffer + { + dk::Image images[MAX_RENDERTARGETS]; + CMemPool::Handle memory[MAX_RENDERTARGETS]; + dk::ImageLayout layout; + + dk::Image depthImage; + CMemPool::Handle depthMemory; + dk::ImageLayout depthLayout; + + int slot = -1; + bool dirty; + } framebuffers; + + struct DescriptorSets + { + CDescriptorSet image; + CDescriptorSet sampler; + bool dirty = false; + } descriptors; + + std::unordered_map descriptorList; + + void EnsureInFrame(); + + CCmdMemRing commands; + CCmdVtxRing vertices; + + std::array rendertargets; + + BitwiseAlloc allocator; + }; +} // namespace love diff --git a/platform/hac/include/utilities/driver/vertex_ext.hpp b/platform/hac/include/utilities/driver/vertex_ext.hpp new file mode 100644 index 000000000..f364c29b5 --- /dev/null +++ b/platform/hac/include/utilities/driver/vertex_ext.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include + +#include + +#include + +#include + +namespace love +{ + namespace vertex + { + namespace attributes + { + struct Attribs + { + dk::detail::ArrayProxy attributeState; + dk::detail::ArrayProxy bufferState; + }; + + // clang-format off + /* Primitives */ + constexpr std::array PrimitiveBufferState = { + DkVtxBufferState { sizeof(Vertex), 0 }, + }; + + constexpr std::array PrimitiveAttribState = { + DkVtxAttribState { 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 }, + DkVtxAttribState { 0, 0, offsetof(Vertex, color), DkVtxAttribSize_4x32, DkVtxAttribType_Float, 0 } + }; + + /* Textures*/ + constexpr std::array TextureBufferState = { + DkVtxBufferState { sizeof(Vertex), 0 }, + }; + + constexpr std::array TextureAttribState = { + DkVtxAttribState { 0, 0, offsetof(Vertex, position), DkVtxAttribSize_3x32, DkVtxAttribType_Float, 0 }, + DkVtxAttribState { 0, 0, offsetof(Vertex, color), DkVtxAttribSize_4x32, DkVtxAttribType_Float, 0 }, + DkVtxAttribState { 0, 0, offsetof(Vertex, texcoord), DkVtxAttribSize_2x32, DkVtxAttribType_Float, 0 } + }; + // clang-format on + + static inline void GetAttributes(vertex::CommonFormat format, Attribs& out) + { + + switch (format) + { + case CommonFormat::PRIMITIVE: + default: + { + out.attributeState = PrimitiveAttribState; + out.bufferState = PrimitiveBufferState; + break; + } + case CommonFormat::TEXTURE: + { + out.attributeState = TextureAttribState; + out.bufferState = TextureBufferState; + break; + } + } + } + } // namespace attributes + } // namespace vertex +} // namespace love diff --git a/platform/hac/include/utilities/haptics/vibration_ext.hpp b/platform/hac/include/utilities/haptics/vibration_ext.hpp new file mode 100644 index 000000000..6ea810ff9 --- /dev/null +++ b/platform/hac/include/utilities/haptics/vibration_ext.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include + +namespace love +{ + template<> + class Vibration : public Vibration + { + public: + Vibration() + {} + + Vibration(HidNpadIdType playerId, HidNpadStyleTag style); + + Vibration& operator=(Vibration&& other); + + virtual ~Vibration(); + + bool SendValues(float left, float right); + + bool Stop(); + + private: + std::unique_ptr handles; + + HidNpadIdType playerId; + HidNpadStyleTag style; + }; +} // namespace love diff --git a/platform/hac/include/utilities/npad.hpp b/platform/hac/include/utilities/npad.hpp new file mode 100644 index 000000000..bd54e009e --- /dev/null +++ b/platform/hac/include/utilities/npad.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +namespace love::npad +{ + static constexpr size_t MAX_JOYSTICKS = 0x08; + static constexpr HidNpadStyleTag INVALID_STYLE_TAG = (HidNpadStyleTag)-1; + static constexpr HidNpadIdType INVALID_PLAYER_ID = (HidNpadIdType)-1; + + HidNpadStyleTag GetStyleTag(PadState* state); + + // clang-format off + constexpr BidirectionalMap styleTypes = { + npad::INVALID_STYLE_TAG, guid::GAMEPAD_TYPE_UNKNOWN, + HidNpadStyleTag_NpadFullKey, guid::GAMEPAD_TYPE_NINTENDO_SWITCH_PRO, + HidNpadStyleTag_NpadHandheld, guid::GAMEPAD_TYPE_NINTENDO_SWITCH_HANDHELD, + HidNpadStyleTag_NpadJoyLeft, guid::GAMEPAD_TYPE_JOYCON_LEFT, + HidNpadStyleTag_NpadJoyRight, guid::GAMEPAD_TYPE_JOYCON_RIGHT, + HidNpadStyleTag_NpadJoyDual, guid::GAMEPAD_TYPE_JOYCON_PAIR + }; + // clang-format on +} // namespace love::npad diff --git a/platform/hac/include/utilities/sensor/accelerometer.hpp b/platform/hac/include/utilities/sensor/accelerometer.hpp new file mode 100644 index 000000000..be1213e10 --- /dev/null +++ b/platform/hac/include/utilities/sensor/accelerometer.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include + +#include + +namespace love +{ + class Accelerometer : public SensorBase + { + public: + Accelerometer() + {} + + Accelerometer(HidNpadIdType playerId, HidNpadStyleTag style); + + ~Accelerometer(); + + Accelerometer& operator=(Accelerometer&& other); + + void SetEnabled(bool enabled); + + std::vector GetData() override; + + private: + std::unique_ptr handles; + + HidNpadIdType playerId; + HidNpadStyleTag style; + + int handleCount; + }; +} // namespace love diff --git a/platform/hac/include/utilities/sensor/gyroscope.hpp b/platform/hac/include/utilities/sensor/gyroscope.hpp new file mode 100644 index 000000000..c3d38b45e --- /dev/null +++ b/platform/hac/include/utilities/sensor/gyroscope.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include + +#include +#include + +namespace love +{ + class Gyroscope : public SensorBase + { + public: + Gyroscope() + {} + + Gyroscope(HidNpadIdType playerId, HidNpadStyleTag style); + + ~Gyroscope(); + + Gyroscope& operator=(Gyroscope&& other); + + void SetEnabled(bool enabled); + + std::vector GetData() override; + + int GetHandleCount() + { + return this->handleCount; + } + + private: + std::unique_ptr handles; + + HidNpadIdType playerId; + HidNpadStyleTag style; + + int handleCount; + }; +} // namespace love diff --git a/platform/hac/include/utilities/threads/threads.hpp b/platform/hac/include/utilities/threads/threads.hpp new file mode 100644 index 000000000..47d538f4b --- /dev/null +++ b/platform/hac/include/utilities/threads/threads.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +namespace love +{ + using mutex = std::mutex; + using conditional = std::condition_variable; + using thread = std::thread; +} // namespace love diff --git a/platform/hac/libraries/luasocket.patch b/platform/hac/libraries/luasocket.patch new file mode 100644 index 000000000..6ba9fa36a --- /dev/null +++ b/platform/hac/libraries/luasocket.patch @@ -0,0 +1,2133 @@ +diff --git a/inet.c b/inet.c +index 138c9ab..1cb1cda 100755 +--- a/inet.c ++++ b/inet.c +@@ -371,7 +371,11 @@ const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) + } + case AF_INET6: { + struct sockaddr_in6 sin6; ++ #if !defined(__SWITCH__) + struct in6_addr addrany = IN6ADDR_ANY_INIT; ++ #else ++ struct in6_addr addrany = in6addr_any; ++ #endif + memset((char *) &sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_UNSPEC; + sin6.sin6_addr = addrany; +diff --git a/makefile b/makefile +deleted file mode 100755 +index 06f4d19..0000000 +--- a/makefile ++++ /dev/null +@@ -1,461 +0,0 @@ +-# luasocket src/makefile +-# +-# Definitions in this section can be overriden on the command line or in the +-# environment. +-# +-# These are equivalent: +-# +-# export PLAT=linux DEBUG=DEBUG LUAV=5.2 prefix=/sw +-# make +-# +-# and +-# +-# make PLAT=linux DEBUG=DEBUG LUAV=5.2 prefix=/sw +- +-# PLAT: linux macosx win32 win64 mingw +-# platform to build for +-PLAT?=linux +- +-# LUAV: 5.1 5.2 5.3 5.4 +-# lua version to build against +-LUAV?=5.1 +- +-# MYCFLAGS: to be set by user if needed +-MYCFLAGS?= +- +-# MYLDFLAGS: to be set by user if needed +-MYLDFLAGS?= +- +-# DEBUG: NODEBUG DEBUG +-# debug mode causes luasocket to collect and returns timing information useful +-# for testing and debugging luasocket itself +-DEBUG?=NODEBUG +- +-# where lua headers are found for macosx builds +-# LUAINC_macosx: +-# /opt/local/include +-LUAINC_macosx_base?=/opt/local/include +-LUAINC_macosx?=$(LUAINC_macosx_base)/lua/$(LUAV) $(LUAINC_macosx_base)/lua$(LUAV) $(LUAINC_macosx_base)/lua-$(LUAV) +- +-# FIXME default should this default to fink or to macports? +-# What happens when more than one Lua version is installed? +-LUAPREFIX_macosx?=/opt/local +-CDIR_macosx?=lib/lua/$(LUAV) +-LDIR_macosx?=share/lua/$(LUAV) +- +-# LUAINC_linux: +-# /usr/include/lua$(LUAV) +-# /usr/local/include +-# /usr/local/include/lua$(LUAV) +-# where lua headers are found for linux builds +-LUAINC_linux_base?=/usr/include +-LUAINC_linux?=$(LUAINC_linux_base)/lua/$(LUAV) $(LUAINC_linux_base)/lua$(LUAV) +-LUAPREFIX_linux?=/usr/local +-CDIR_linux?=lib/lua/$(LUAV) +-LDIR_linux?=share/lua/$(LUAV) +- +-# LUAINC_freebsd: +-# /usr/local/include/lua$(LUAV) +-# where lua headers are found for freebsd builds +-LUAINC_freebsd_base?=/usr/local/include/ +-LUAINC_freebsd?=$(LUAINC_freebsd_base)/lua/$(LUAV) $(LUAINC_freebsd_base)/lua$(LUAV) +-LUAPREFIX_freebsd?=/usr/local/ +-CDIR_freebsd?=lib/lua/$(LUAV) +-LDIR_freebsd?=share/lua/$(LUAV) +- +-# where lua headers are found for mingw builds +-# LUAINC_mingw: +-# /opt/local/include +-LUAINC_mingw_base?=/usr/include +-LUAINC_mingw?=$(LUAINC_mingw_base)/lua/$(LUAV) $(LUAINC_mingw_base)/lua$(LUAV) +-LUALIB_mingw_base?=/usr/bin +-LUALIB_mingw?=$(LUALIB_mingw_base)/lua/$(LUAV)/lua$(subst .,,$(LUAV)).dll +-LUAPREFIX_mingw?=/usr +-CDIR_mingw?=lua/$(LUAV) +-LDIR_mingw?=lua/$(LUAV)/lua +- +- +-# LUAINC_win32: +-# LUALIB_win32: +-# where lua headers and libraries are found for win32 builds +-LUAPREFIX_win32?= +-LUAINC_win32?=$(LUAPREFIX_win32)/include/lua/$(LUAV) $(LUAPREFIX_win32)/include/lua$(LUAV) +-PLATFORM_win32?=Release +-CDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32) +-LDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32)/lua +-LUALIB_win32?=$(LUAPREFIX_win32)/lib/lua/$(LUAV)/$(PLATFORM_win32) +-LUALIBNAME_win32?=lua$(subst .,,$(LUAV)).lib +- +-# LUAINC_win64: +-# LUALIB_win64: +-# where lua headers and libraries are found for win64 builds +-LUAPREFIX_win64?= +-LUAINC_win64?=$(LUAPREFIX_win64)/include/lua/$(LUAV) $(LUAPREFIX_win64)/include/lua$(LUAV) +-PLATFORM_win64?=x64/Release +-CDIR_win64?=bin/lua/$(LUAV)/$(PLATFORM_win64) +-LDIR_win64?=bin/lua/$(LUAV)/$(PLATFORM_win64)/lua +-LUALIB_win64?=$(LUAPREFIX_win64)/lib/lua/$(LUAV)/$(PLATFORM_win64) +-LUALIBNAME_win64?=lua$(subst .,,$(LUAV)).lib +- +- +-# LUAINC_solaris: +-LUAINC_solaris_base?=/usr/include +-LUAINC_solaris?=$(LUAINC_solaris_base)/lua/$(LUAV) $(LUAINC_solaris_base)/lua$(LUAV) +-LUAPREFIX_solaris?=/usr/local +-CDIR_solaris?=lib/lua/$(LUAV) +-LDIR_solaris?=share/lua/$(LUAV) +- +-# prefix: /usr/local /usr /opt/local /sw +-# the top of the default install tree +-prefix?=$(LUAPREFIX_$(PLAT)) +- +-CDIR?=$(CDIR_$(PLAT)) +-LDIR?=$(LDIR_$(PLAT)) +- +-# DESTDIR: (no default) +-# used by package managers to install into a temporary destination +-DESTDIR?= +- +-#------ +-# Definitions below can be overridden on the make command line, but +-# shouldn't have to be. +- +- +-#------ +-# Install directories +-# +- +-INSTALL_DIR=install -d +-INSTALL_DATA=install -m644 +-INSTALL_EXEC=install +-INSTALL_TOP=$(DESTDIR)$(prefix) +- +-INSTALL_TOP_LDIR=$(INSTALL_TOP)/$(LDIR) +-INSTALL_TOP_CDIR=$(INSTALL_TOP)/$(CDIR) +- +-INSTALL_SOCKET_LDIR=$(INSTALL_TOP_LDIR)/socket +-INSTALL_SOCKET_CDIR=$(INSTALL_TOP_CDIR)/socket +-INSTALL_MIME_LDIR=$(INSTALL_TOP_LDIR)/mime +-INSTALL_MIME_CDIR=$(INSTALL_TOP_CDIR)/mime +- +-print: +- @echo PLAT=$(PLAT) +- @echo LUAV=$(LUAV) +- @echo DEBUG=$(DEBUG) +- @echo prefix=$(prefix) +- @echo LUAINC_$(PLAT)=$(LUAINC_$(PLAT)) +- @echo LUALIB_$(PLAT)=$(LUALIB_$(PLAT)) +- @echo INSTALL_TOP_CDIR=$(INSTALL_TOP_CDIR) +- @echo INSTALL_TOP_LDIR=$(INSTALL_TOP_LDIR) +- @echo CFLAGS=$(CFLAGS) +- @echo LDFLAGS=$(LDFLAGS) +- +-#------ +-# Supported platforms +-# +-PLATS= macosx linux win32 win64 mingw solaris +- +-#------ +-# Compiler and linker settings +-# for Mac OS X +-SO_macosx=so +-O_macosx=o +-CC_macosx=gcc +-DEF_macosx= -DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN +-CFLAGS_macosx=$(LUAINC:%=-I%) $(DEF) -Wall -O2 -fno-common +-LDFLAGS_macosx= -bundle -undefined dynamic_lookup -o +-LD_macosx=gcc +-SOCKET_macosx=usocket.o +- +-#------ +-# Compiler and linker settings +-# for Linux +-SO_linux=so +-O_linux=o +-CC_linux=gcc +-DEF_linux=-DLUASOCKET_$(DEBUG) +-CFLAGS_linux=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ +- -Wimplicit -O2 -ggdb3 -fpic +-LDFLAGS_linux=-O -shared -fpic -o +-LD_linux=gcc +-SOCKET_linux=usocket.o +- +-#------ +-# Compiler and linker settings +-# for FreeBSD +-SO_freebsd=so +-O_freebsd=o +-CC_freebsd=gcc +-DEF_freebsd=-DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN +-CFLAGS_freebsd=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ +- -Wimplicit -O2 -ggdb3 -fpic +-LDFLAGS_freebsd=-O -shared -fpic -o +-LD_freebsd=gcc +-SOCKET_freebsd=usocket.o +- +-#------ +-# Compiler and linker settings +-# for Solaris +-SO_solaris=so +-O_solaris=o +-CC_solaris=gcc +-DEF_solaris=-DLUASOCKET_$(DEBUG) +-CFLAGS_solaris=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ +- -Wimplicit -O2 -ggdb3 -fpic +-LDFLAGS_solaris=-lnsl -lsocket -lresolv -O -shared -fpic -o +-LD_solaris=gcc +-SOCKET_solaris=usocket.o +- +-#------ +-# Compiler and linker settings +-# for MingW +-SO_mingw=dll +-O_mingw=o +-CC_mingw=gcc +-DEF_mingw= -DLUASOCKET_$(DEBUG) \ +- -DWINVER=0x0501 +-CFLAGS_mingw=$(LUAINC:%=-I%) $(DEF) -Wall -O2 -fno-common +-LDFLAGS_mingw= $(LUALIB) -shared -Wl,-s -lws2_32 -o +-LD_mingw=gcc +-SOCKET_mingw=wsocket.o +- +- +-#------ +-# Compiler and linker settings +-# for Win32 +-SO_win32=dll +-O_win32=obj +-CC_win32=cl +-DEF_win32= //D "WIN32" //D "NDEBUG" //D "_WINDOWS" //D "_USRDLL" \ +- //D "_CRT_SECURE_NO_WARNINGS" \ +- //D "_WINDLL" \ +- //D "LUASOCKET_$(DEBUG)" +-CFLAGS_win32=$(LUAINC:%=//I "%") $(DEF) //O2 //Ot //MD //W3 //nologo +-LDFLAGS_win32= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \ +- //MANIFEST //MANIFESTFILE:"intermediate.manifest" \ +- /MANIFESTUAC:"level='asInvoker' uiAccess='false'" \ +- //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \ +- //MACHINE:X86 /LIBPATH:"$(LUALIB)" \ +- $(LUALIBNAME_win32) ws2_32.lib //OUT: +- +-LD_win32=cl +-SOCKET_win32=wsocket.obj +- +-#------ +-# Compiler and linker settings +-# for Win64 +-SO_win64=dll +-O_win64=obj +-CC_win64=cl +-DEF_win64= //D "WIN32" //D "NDEBUG" //D "_WINDOWS" //D "_USRDLL" \ +- //D "_CRT_SECURE_NO_WARNINGS" \ +- //D "_WINDLL" \ +- //D "LUASOCKET_$(DEBUG)" +-CFLAGS_win64=$(LUAINC:%=//I "%") $(DEF) //O2 //Ot //MD //W3 //nologo +-LDFLAGS_win64= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \ +- //MANIFEST //MANIFESTFILE:"intermediate.manifest" \ +- /MANIFESTUAC:"level='asInvoker' uiAccess='false'" \ +- //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \ +- /LIBPATH:"$(LUALIB)" \ +- $(LUALIBNAME_win64) ws2_32.lib //OUT: +- +-LD_win64=cl +-SOCKET_win64=wsocket.obj +- +-.SUFFIXES: .obj +- +-.c.obj: +- $(CC) $(CFLAGS) //Fo"$@" //c $< +- +-#------ +-# Output file names +-# +-SO=$(SO_$(PLAT)) +-O=$(O_$(PLAT)) +-SOCKET_V=3.0.0 +-MIME_V=1.0.3 +-SOCKET_SO=socket-$(SOCKET_V).$(SO) +-MIME_SO=mime-$(MIME_V).$(SO) +-UNIX_SO=unix.$(SO) +-SERIAL_SO=serial.$(SO) +-SOCKET=$(SOCKET_$(PLAT)) +- +-#------ +-# Settings selected for platform +-# +-CC=$(CC_$(PLAT)) +-DEF=$(DEF_$(PLAT)) +-CFLAGS=$(MYCFLAGS) $(CFLAGS_$(PLAT)) +-LDFLAGS=$(MYLDFLAGS) $(LDFLAGS_$(PLAT)) +-LD=$(LD_$(PLAT)) +-LUAINC= $(LUAINC_$(PLAT)) +-LUALIB= $(LUALIB_$(PLAT)) +- +-#------ +-# Modules belonging to socket-core +-# +-SOCKET_OBJS= \ +- luasocket.$(O) \ +- timeout.$(O) \ +- buffer.$(O) \ +- io.$(O) \ +- auxiliar.$(O) \ +- compat.$(O) \ +- options.$(O) \ +- inet.$(O) \ +- $(SOCKET) \ +- except.$(O) \ +- select.$(O) \ +- tcp.$(O) \ +- udp.$(O) +- +-#------ +-# Modules belonging mime-core +-# +-MIME_OBJS= \ +- mime.$(O) \ +- compat.$(O) +- +-#------ +-# Modules belonging unix (local domain sockets) +-# +-UNIX_OBJS=\ +- buffer.$(O) \ +- auxiliar.$(O) \ +- options.$(O) \ +- timeout.$(O) \ +- io.$(O) \ +- usocket.$(O) \ +- unixstream.$(O) \ +- unixdgram.$(O) \ +- compat.$(O) \ +- unix.$(O) +- +-#------ +-# Modules belonging to serial (device streams) +-# +-SERIAL_OBJS=\ +- buffer.$(O) \ +- compat.$(O) \ +- auxiliar.$(O) \ +- options.$(O) \ +- timeout.$(O) \ +- io.$(O) \ +- usocket.$(O) \ +- serial.$(O) +- +-#------ +-# Files to install +-# +-TO_SOCKET_LDIR= \ +- http.lua \ +- url.lua \ +- tp.lua \ +- ftp.lua \ +- headers.lua \ +- smtp.lua +- +-TO_TOP_LDIR= \ +- ltn12.lua \ +- socket.lua \ +- mime.lua +- +-#------ +-# Targets +-# +-default: $(PLAT) +- +- +-freebsd: +- $(MAKE) all-unix PLAT=freebsd +- +-macosx: +- $(MAKE) all-unix PLAT=macosx +- +-win32: +- $(MAKE) all PLAT=win32 +- +-win64: +- $(MAKE) all PLAT=win64 +- +-linux: +- $(MAKE) all-unix PLAT=linux +- +-mingw: +- $(MAKE) all PLAT=mingw +- +-solaris: +- $(MAKE) all-unix PLAT=solaris +- +-none: +- @echo "Please run" +- @echo " make PLATFORM" +- @echo "where PLATFORM is one of these:" +- @echo " $(PLATS)" +- +-all: $(SOCKET_SO) $(MIME_SO) +- +-$(SOCKET_SO): $(SOCKET_OBJS) +- $(LD) $(SOCKET_OBJS) $(LDFLAGS)$@ +- +-$(MIME_SO): $(MIME_OBJS) +- $(LD) $(MIME_OBJS) $(LDFLAGS)$@ +- +-all-unix: all $(UNIX_SO) $(SERIAL_SO) +- +-$(UNIX_SO): $(UNIX_OBJS) +- $(LD) $(UNIX_OBJS) $(LDFLAGS)$@ +- +-$(SERIAL_SO): $(SERIAL_OBJS) +- $(LD) $(SERIAL_OBJS) $(LDFLAGS)$@ +- +-install: +- $(INSTALL_DIR) $(INSTALL_TOP_LDIR) +- $(INSTALL_DATA) $(TO_TOP_LDIR) $(INSTALL_TOP_LDIR) +- $(INSTALL_DIR) $(INSTALL_SOCKET_LDIR) +- $(INSTALL_DATA) $(TO_SOCKET_LDIR) $(INSTALL_SOCKET_LDIR) +- $(INSTALL_DIR) $(INSTALL_SOCKET_CDIR) +- $(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET_CDIR)/core.$(SO) +- $(INSTALL_DIR) $(INSTALL_MIME_CDIR) +- $(INSTALL_EXEC) $(MIME_SO) $(INSTALL_MIME_CDIR)/core.$(SO) +- +-install-unix: install +- $(INSTALL_EXEC) $(UNIX_SO) $(INSTALL_SOCKET_CDIR)/$(UNIX_SO) +- $(INSTALL_EXEC) $(SERIAL_SO) $(INSTALL_SOCKET_CDIR)/$(SERIAL_SO) +- +-local: +- $(MAKE) install INSTALL_TOP_CDIR=.. INSTALL_TOP_LDIR=.. +- +-clean: +- rm -f $(SOCKET_SO) $(SOCKET_OBJS) $(SERIAL_OBJS) +- rm -f $(MIME_SO) $(UNIX_SO) $(SERIAL_SO) $(MIME_OBJS) $(UNIX_OBJS) +- +-.PHONY: all $(PLATS) default clean echo none +- +-#------ +-# List of dependencies +-# +-compat.$(O): compat.c compat.h +-auxiliar.$(O): auxiliar.c auxiliar.h +-buffer.$(O): buffer.c buffer.h io.h timeout.h +-except.$(O): except.c except.h +-inet.$(O): inet.c inet.h socket.h io.h timeout.h usocket.h +-io.$(O): io.c io.h timeout.h +-luasocket.$(O): luasocket.c luasocket.h auxiliar.h except.h \ +- timeout.h buffer.h io.h inet.h socket.h usocket.h tcp.h \ +- udp.h select.h +-mime.$(O): mime.c mime.h +-options.$(O): options.c auxiliar.h options.h socket.h io.h \ +- timeout.h usocket.h inet.h +-select.$(O): select.c socket.h io.h timeout.h usocket.h select.h +-serial.$(O): serial.c auxiliar.h socket.h io.h timeout.h usocket.h \ +- options.h unix.h buffer.h +-tcp.$(O): tcp.c auxiliar.h socket.h io.h timeout.h usocket.h \ +- inet.h options.h tcp.h buffer.h +-timeout.$(O): timeout.c auxiliar.h timeout.h +-udp.$(O): udp.c auxiliar.h socket.h io.h timeout.h usocket.h \ +- inet.h options.h udp.h +-unix.$(O): unix.c auxiliar.h socket.h io.h timeout.h usocket.h \ +- options.h unix.h buffer.h +-usocket.$(O): usocket.c socket.h io.h timeout.h usocket.h +-wsocket.$(O): wsocket.c socket.h io.h timeout.h usocket.h +diff --git a/options.c b/options.c +index 3280c51..7c88d01 100644 +--- a/options.c ++++ b/options.c +@@ -22,6 +22,22 @@ static int opt_set(lua_State *L, p_socket ps, int level, int name, + static int opt_get(lua_State *L, p_socket ps, int level, int name, + void *val, int* len); + ++static int set_opt_error(lua_State* L) ++{ ++ lua_pushnil(L); ++ lua_pushstring(L, "setsockopt failed: not supported"); ++ ++ return 2; ++} ++ ++static int get_opt_error(lua_State* L) ++{ ++ lua_pushnil(L); ++ lua_pushstring(L, "getsockopt failed: not supported"); ++ ++ return 2; ++} ++ + /*=========================================================================*\ + * Exported functions + \*=========================================================================*/ +@@ -393,7 +409,7 @@ static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) + luaL_argerror(L, 3, "invalid 'interface' ip address"); + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); + } +- ++#if !defined(__SWITCH__) + static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) + { + struct ipv6_mreq val; /* obj, opt-name, table */ +@@ -419,6 +435,9 @@ static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) + } + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); + } ++#else ++static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) { return set_opt_error(L); } ++#endif + + static + int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len) +diff --git a/serial.c b/serial.c +deleted file mode 100644 +index 21485d3..0000000 +--- a/serial.c ++++ /dev/null +@@ -1,171 +0,0 @@ +-/*=========================================================================*\ +-* Serial stream +-* LuaSocket toolkit +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "auxiliar.h" +-#include "socket.h" +-#include "options.h" +-#include "unix.h" +- +-#include +-#include +- +-/* +-Reuses userdata definition from unix.h, since it is useful for all +-stream-like objects. +- +-If we stored the serial path for use in error messages or userdata +-printing, we might need our own userdata definition. +- +-Group usage is semi-inherited from unix.c, but unnecessary since we +-have only one object type. +-*/ +- +-/*=========================================================================*\ +-* Internal function prototypes +-\*=========================================================================*/ +-static int global_create(lua_State *L); +-static int meth_send(lua_State *L); +-static int meth_receive(lua_State *L); +-static int meth_close(lua_State *L); +-static int meth_settimeout(lua_State *L); +-static int meth_getfd(lua_State *L); +-static int meth_setfd(lua_State *L); +-static int meth_dirty(lua_State *L); +-static int meth_getstats(lua_State *L); +-static int meth_setstats(lua_State *L); +- +-/* serial object methods */ +-static luaL_Reg serial_methods[] = { +- {"__gc", meth_close}, +- {"__tostring", auxiliar_tostring}, +- {"close", meth_close}, +- {"dirty", meth_dirty}, +- {"getfd", meth_getfd}, +- {"getstats", meth_getstats}, +- {"setstats", meth_setstats}, +- {"receive", meth_receive}, +- {"send", meth_send}, +- {"setfd", meth_setfd}, +- {"settimeout", meth_settimeout}, +- {NULL, NULL} +-}; +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-LUASOCKET_API int luaopen_socket_serial(lua_State *L) { +- /* create classes */ +- auxiliar_newclass(L, "serial{client}", serial_methods); +- /* create class groups */ +- auxiliar_add2group(L, "serial{client}", "serial{any}"); +- lua_pushcfunction(L, global_create); +- return 1; +-} +- +-/*=========================================================================*\ +-* Lua methods +-\*=========================================================================*/ +-/*-------------------------------------------------------------------------*\ +-* Just call buffered IO methods +-\*-------------------------------------------------------------------------*/ +-static int meth_send(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_send(L, &un->buf); +-} +- +-static int meth_receive(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_receive(L, &un->buf); +-} +- +-static int meth_getstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_getstats(L, &un->buf); +-} +- +-static int meth_setstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); +- return buffer_meth_setstats(L, &un->buf); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Select support methods +-\*-------------------------------------------------------------------------*/ +-static int meth_getfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- lua_pushnumber(L, (int) un->sock); +- return 1; +-} +- +-/* this is very dangerous, but can be handy for those that are brave enough */ +-static int meth_setfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- un->sock = (t_socket) luaL_checknumber(L, 2); +- return 0; +-} +- +-static int meth_dirty(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- lua_pushboolean(L, !buffer_isempty(&un->buf)); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Closes socket used by object +-\*-------------------------------------------------------------------------*/ +-static int meth_close(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- socket_destroy(&un->sock); +- lua_pushnumber(L, 1); +- return 1; +-} +- +- +-/*-------------------------------------------------------------------------*\ +-* Just call tm methods +-\*-------------------------------------------------------------------------*/ +-static int meth_settimeout(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- return timeout_meth_settimeout(L, &un->tm); +-} +- +-/*=========================================================================*\ +-* Library functions +-\*=========================================================================*/ +- +- +-/*-------------------------------------------------------------------------*\ +-* Creates a serial object +-\*-------------------------------------------------------------------------*/ +-static int global_create(lua_State *L) { +- const char* path = luaL_checkstring(L, 1); +- +- /* allocate unix object */ +- p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); +- +- /* open serial device */ +- t_socket sock = open(path, O_NOCTTY|O_RDWR); +- +- /*printf("open %s on %d\n", path, sock);*/ +- +- if (sock < 0) { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(errno)); +- lua_pushnumber(L, errno); +- return 3; +- } +- /* set its type as client object */ +- auxiliar_setclass(L, "serial{client}", -1); +- /* initialize remaining structure fields */ +- socket_setnonblocking(&sock); +- un->sock = sock; +- io_init(&un->io, (p_send) socket_write, (p_recv) socket_read, +- (p_error) socket_ioerror, &un->sock); +- timeout_init(&un->tm, -1, -1); +- buffer_init(&un->buf, &un->io, &un->tm); +- return 1; +-} +diff --git a/unix.c b/unix.c +deleted file mode 100644 +index 268d8b2..0000000 +--- a/unix.c ++++ /dev/null +@@ -1,69 +0,0 @@ +-/*=========================================================================*\ +-* Unix domain socket +-* LuaSocket toolkit +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "unixstream.h" +-#include "unixdgram.h" +- +-/*-------------------------------------------------------------------------*\ +-* Modules and functions +-\*-------------------------------------------------------------------------*/ +-static const luaL_Reg mod[] = { +- {"stream", unixstream_open}, +- {"dgram", unixdgram_open}, +- {NULL, NULL} +-}; +- +-static void add_alias(lua_State *L, int index, const char *name, const char *target) +-{ +- lua_getfield(L, index, target); +- lua_setfield(L, index, name); +-} +- +-static int compat_socket_unix_call(lua_State *L) +-{ +- /* Look up socket.unix.stream in the socket.unix table (which is the first +- * argument). */ +- lua_getfield(L, 1, "stream"); +- +- /* Replace the stack entry for the socket.unix table with the +- * socket.unix.stream function. */ +- lua_replace(L, 1); +- +- /* Call socket.unix.stream, passing along any arguments. */ +- int n = lua_gettop(L); +- lua_call(L, n-1, LUA_MULTRET); +- +- /* Pass along the return values from socket.unix.stream. */ +- n = lua_gettop(L); +- return n; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-LUASOCKET_API int luaopen_socket_unix(lua_State *L) +-{ +- int i; +- lua_newtable(L); +- int socket_unix_table = lua_gettop(L); +- +- for (i = 0; mod[i].name; i++) +- mod[i].func(L); +- +- /* Add backwards compatibility aliases "tcp" and "udp" for the "stream" and +- * "dgram" functions. */ +- add_alias(L, socket_unix_table, "tcp", "stream"); +- add_alias(L, socket_unix_table, "udp", "dgram"); +- +- /* Add a backwards compatibility function and a metatable setup to call it +- * for the old socket.unix() interface. */ +- lua_pushcfunction(L, compat_socket_unix_call); +- lua_setfield(L, socket_unix_table, "__call"); +- lua_pushvalue(L, socket_unix_table); +- lua_setmetatable(L, socket_unix_table); +- +- return 1; +-} +diff --git a/unix.h b/unix.h +deleted file mode 100644 +index c203561..0000000 +--- a/unix.h ++++ /dev/null +@@ -1,26 +0,0 @@ +-#ifndef UNIX_H +-#define UNIX_H +-/*=========================================================================*\ +-* Unix domain object +-* LuaSocket toolkit +-* +-* This module is just an example of how to extend LuaSocket with a new +-* domain. +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "buffer.h" +-#include "timeout.h" +-#include "socket.h" +- +-typedef struct t_unix_ { +- t_socket sock; +- t_io io; +- t_buffer buf; +- t_timeout tm; +-} t_unix; +-typedef t_unix *p_unix; +- +-LUASOCKET_API int luaopen_socket_unix(lua_State *L); +- +-#endif /* UNIX_H */ +diff --git a/unixdgram.c b/unixdgram.c +deleted file mode 100644 +index 69093d7..0000000 +--- a/unixdgram.c ++++ /dev/null +@@ -1,405 +0,0 @@ +-/*=========================================================================*\ +-* Unix domain socket dgram submodule +-* LuaSocket toolkit +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "auxiliar.h" +-#include "socket.h" +-#include "options.h" +-#include "unix.h" +- +-#include +-#include +- +-#include +- +-#define UNIXDGRAM_DATAGRAMSIZE 8192 +- +-/* provide a SUN_LEN macro if sys/un.h doesn't (e.g. Android) */ +-#ifndef SUN_LEN +-#define SUN_LEN(ptr) \ +- ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ +- + strlen ((ptr)->sun_path)) +-#endif +- +-/*=========================================================================*\ +-* Internal function prototypes +-\*=========================================================================*/ +-static int global_create(lua_State *L); +-static int meth_connect(lua_State *L); +-static int meth_bind(lua_State *L); +-static int meth_send(lua_State *L); +-static int meth_receive(lua_State *L); +-static int meth_close(lua_State *L); +-static int meth_setoption(lua_State *L); +-static int meth_settimeout(lua_State *L); +-static int meth_gettimeout(lua_State *L); +-static int meth_getfd(lua_State *L); +-static int meth_setfd(lua_State *L); +-static int meth_dirty(lua_State *L); +-static int meth_receivefrom(lua_State *L); +-static int meth_sendto(lua_State *L); +-static int meth_getsockname(lua_State *L); +- +-static const char *unixdgram_tryconnect(p_unix un, const char *path); +-static const char *unixdgram_trybind(p_unix un, const char *path); +- +-/* unixdgram object methods */ +-static luaL_Reg unixdgram_methods[] = { +- {"__gc", meth_close}, +- {"__tostring", auxiliar_tostring}, +- {"bind", meth_bind}, +- {"close", meth_close}, +- {"connect", meth_connect}, +- {"dirty", meth_dirty}, +- {"getfd", meth_getfd}, +- {"send", meth_send}, +- {"sendto", meth_sendto}, +- {"receive", meth_receive}, +- {"receivefrom", meth_receivefrom}, +- {"setfd", meth_setfd}, +- {"setoption", meth_setoption}, +- {"setpeername", meth_connect}, +- {"setsockname", meth_bind}, +- {"getsockname", meth_getsockname}, +- {"settimeout", meth_settimeout}, +- {"gettimeout", meth_gettimeout}, +- {NULL, NULL} +-}; +- +-/* socket option handlers */ +-static t_opt optset[] = { +- {"reuseaddr", opt_set_reuseaddr}, +- {NULL, NULL} +-}; +- +-/* functions in library namespace */ +-static luaL_Reg func[] = { +- {"dgram", global_create}, +- {NULL, NULL} +-}; +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-int unixdgram_open(lua_State *L) +-{ +- /* create classes */ +- auxiliar_newclass(L, "unixdgram{connected}", unixdgram_methods); +- auxiliar_newclass(L, "unixdgram{unconnected}", unixdgram_methods); +- /* create class groups */ +- auxiliar_add2group(L, "unixdgram{connected}", "unixdgram{any}"); +- auxiliar_add2group(L, "unixdgram{unconnected}", "unixdgram{any}"); +- auxiliar_add2group(L, "unixdgram{connected}", "select{able}"); +- auxiliar_add2group(L, "unixdgram{unconnected}", "select{able}"); +- +- luaL_setfuncs(L, func, 0); +- return 0; +-} +- +-/*=========================================================================*\ +-* Lua methods +-\*=========================================================================*/ +-static const char *unixdgram_strerror(int err) +-{ +- /* a 'closed' error on an unconnected means the target address was not +- * accepted by the transport layer */ +- if (err == IO_CLOSED) return "refused"; +- else return socket_strerror(err); +-} +- +-static int meth_send(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{connected}", 1); +- p_timeout tm = &un->tm; +- size_t count, sent = 0; +- int err; +- const char *data = luaL_checklstring(L, 2, &count); +- timeout_markstart(tm); +- err = socket_send(&un->sock, data, count, &sent, tm); +- if (err != IO_DONE) { +- lua_pushnil(L); +- lua_pushstring(L, unixdgram_strerror(err)); +- return 2; +- } +- lua_pushnumber(L, (lua_Number) sent); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Send data through unconnected unixdgram socket +-\*-------------------------------------------------------------------------*/ +-static int meth_sendto(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); +- size_t count, sent = 0; +- const char *data = luaL_checklstring(L, 2, &count); +- const char *path = luaL_checkstring(L, 3); +- p_timeout tm = &un->tm; +- int err; +- struct sockaddr_un remote; +- size_t len = strlen(path); +- +- if (len >= sizeof(remote.sun_path)) { +- lua_pushnil(L); +- lua_pushstring(L, "path too long"); +- return 2; +- } +- +- memset(&remote, 0, sizeof(remote)); +- strcpy(remote.sun_path, path); +- remote.sun_family = AF_UNIX; +- timeout_markstart(tm); +-#ifdef UNIX_HAS_SUN_LEN +- remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) +- + len + 1; +- err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, remote.sun_len, tm); +-#else +- err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, +- sizeof(remote.sun_family) + len, tm); +-#endif +- if (err != IO_DONE) { +- lua_pushnil(L); +- lua_pushstring(L, unixdgram_strerror(err)); +- return 2; +- } +- lua_pushnumber(L, (lua_Number) sent); +- return 1; +-} +- +-static int meth_receive(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- char buf[UNIXDGRAM_DATAGRAMSIZE]; +- size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); +- char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; +- int err; +- p_timeout tm = &un->tm; +- timeout_markstart(tm); +- if (!dgram) { +- lua_pushnil(L); +- lua_pushliteral(L, "out of memory"); +- return 2; +- } +- err = socket_recv(&un->sock, dgram, wanted, &got, tm); +- /* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */ +- if (err != IO_DONE && err != IO_CLOSED) { +- lua_pushnil(L); +- lua_pushstring(L, unixdgram_strerror(err)); +- if (wanted > sizeof(buf)) free(dgram); +- return 2; +- } +- lua_pushlstring(L, dgram, got); +- if (wanted > sizeof(buf)) free(dgram); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Receives data and sender from a DGRAM socket +-\*-------------------------------------------------------------------------*/ +-static int meth_receivefrom(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); +- char buf[UNIXDGRAM_DATAGRAMSIZE]; +- size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); +- char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; +- struct sockaddr_un addr; +- socklen_t addr_len = sizeof(addr); +- int err; +- p_timeout tm = &un->tm; +- timeout_markstart(tm); +- if (!dgram) { +- lua_pushnil(L); +- lua_pushliteral(L, "out of memory"); +- return 2; +- } +- addr.sun_path[0] = '\0'; +- err = socket_recvfrom(&un->sock, dgram, wanted, &got, (SA *) &addr, +- &addr_len, tm); +- /* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */ +- if (err != IO_DONE && err != IO_CLOSED) { +- lua_pushnil(L); +- lua_pushstring(L, unixdgram_strerror(err)); +- if (wanted > sizeof(buf)) free(dgram); +- return 2; +- } +- +- lua_pushlstring(L, dgram, got); +- /* the path may be empty, when client send without bind */ +- lua_pushstring(L, addr.sun_path); +- if (wanted > sizeof(buf)) free(dgram); +- return 2; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Just call option handler +-\*-------------------------------------------------------------------------*/ +-static int meth_setoption(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- return opt_meth_setoption(L, optset, &un->sock); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Select support methods +-\*-------------------------------------------------------------------------*/ +-static int meth_getfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- lua_pushnumber(L, (int) un->sock); +- return 1; +-} +- +-/* this is very dangerous, but can be handy for those that are brave enough */ +-static int meth_setfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- un->sock = (t_socket) luaL_checknumber(L, 2); +- return 0; +-} +- +-static int meth_dirty(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- (void) un; +- lua_pushboolean(L, 0); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Binds an object to an address +-\*-------------------------------------------------------------------------*/ +-static const char *unixdgram_trybind(p_unix un, const char *path) { +- struct sockaddr_un local; +- size_t len = strlen(path); +- if (len >= sizeof(local.sun_path)) return "path too long"; +- memset(&local, 0, sizeof(local)); +- strcpy(local.sun_path, path); +- local.sun_family = AF_UNIX; +- size_t addrlen = SUN_LEN(&local); +-#ifdef UNIX_HAS_SUN_LEN +- local.sun_len = addrlen + 1; +-#endif +- int err = socket_bind(&un->sock, (SA *) &local, addrlen); +- if (err != IO_DONE) socket_destroy(&un->sock); +- return socket_strerror(err); +-} +- +-static int meth_bind(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); +- const char *path = luaL_checkstring(L, 2); +- const char *err = unixdgram_trybind(un, path); +- if (err) { +- lua_pushnil(L); +- lua_pushstring(L, err); +- return 2; +- } +- lua_pushnumber(L, 1); +- return 1; +-} +- +-static int meth_getsockname(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- struct sockaddr_un peer = {0}; +- socklen_t peer_len = sizeof(peer); +- +- if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(errno)); +- return 2; +- } +- +- lua_pushstring(L, peer.sun_path); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Turns a master unixdgram object into a client object. +-\*-------------------------------------------------------------------------*/ +-static const char *unixdgram_tryconnect(p_unix un, const char *path) +-{ +- struct sockaddr_un remote; +- size_t len = strlen(path); +- if (len >= sizeof(remote.sun_path)) return "path too long"; +- memset(&remote, 0, sizeof(remote)); +- strcpy(remote.sun_path, path); +- remote.sun_family = AF_UNIX; +- timeout_markstart(&un->tm); +- size_t addrlen = SUN_LEN(&remote); +-#ifdef UNIX_HAS_SUN_LEN +- remote.sun_len = addrlen + 1; +-#endif +- int err = socket_connect(&un->sock, (SA *) &remote, addrlen, &un->tm); +- if (err != IO_DONE) socket_destroy(&un->sock); +- return socket_strerror(err); +-} +- +-static int meth_connect(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- const char *path = luaL_checkstring(L, 2); +- const char *err = unixdgram_tryconnect(un, path); +- if (err) { +- lua_pushnil(L); +- lua_pushstring(L, err); +- return 2; +- } +- /* turn unconnected object into a connected object */ +- auxiliar_setclass(L, "unixdgram{connected}", 1); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Closes socket used by object +-\*-------------------------------------------------------------------------*/ +-static int meth_close(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- socket_destroy(&un->sock); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Just call tm methods +-\*-------------------------------------------------------------------------*/ +-static int meth_settimeout(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- return timeout_meth_settimeout(L, &un->tm); +-} +- +-static int meth_gettimeout(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); +- return timeout_meth_gettimeout(L, &un->tm); +-} +- +-/*=========================================================================*\ +-* Library functions +-\*=========================================================================*/ +-/*-------------------------------------------------------------------------*\ +-* Creates a master unixdgram object +-\*-------------------------------------------------------------------------*/ +-static int global_create(lua_State *L) +-{ +- t_socket sock; +- int err = socket_create(&sock, AF_UNIX, SOCK_DGRAM, 0); +- /* try to allocate a system socket */ +- if (err == IO_DONE) { +- /* allocate unixdgram object */ +- p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); +- /* set its type as master object */ +- auxiliar_setclass(L, "unixdgram{unconnected}", -1); +- /* initialize remaining structure fields */ +- socket_setnonblocking(&sock); +- un->sock = sock; +- io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, +- (p_error) socket_ioerror, &un->sock); +- timeout_init(&un->tm, -1, -1); +- buffer_init(&un->buf, &un->io, &un->tm); +- return 1; +- } else { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(err)); +- return 2; +- } +-} +diff --git a/unixdgram.h b/unixdgram.h +deleted file mode 100644 +index a1a0166..0000000 +--- a/unixdgram.h ++++ /dev/null +@@ -1,28 +0,0 @@ +-#ifndef UNIXDGRAM_H +-#define UNIXDGRAM_H +-/*=========================================================================*\ +-* DGRAM object +-* LuaSocket toolkit +-* +-* The dgram.h module provides LuaSocket with support for DGRAM protocol +-* (AF_INET, SOCK_DGRAM). +-* +-* Two classes are defined: connected and unconnected. DGRAM objects are +-* originally unconnected. They can be "connected" to a given address +-* with a call to the setpeername function. The same function can be used to +-* break the connection. +-\*=========================================================================*/ +- +-#include "unix.h" +- +-#ifndef _WIN32 +-#pragma GCC visibility push(hidden) +-#endif +- +-int unixdgram_open(lua_State *L); +- +-#ifndef _WIN32 +-#pragma GCC visibility pop +-#endif +- +-#endif /* UNIXDGRAM_H */ +diff --git a/unixstream.c b/unixstream.c +deleted file mode 100644 +index 02aced9..0000000 +--- a/unixstream.c ++++ /dev/null +@@ -1,355 +0,0 @@ +-/*=========================================================================*\ +-* Unix domain socket stream sub module +-* LuaSocket toolkit +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include "auxiliar.h" +-#include "socket.h" +-#include "options.h" +-#include "unixstream.h" +- +-#include +-#include +- +-/*=========================================================================*\ +-* Internal function prototypes +-\*=========================================================================*/ +-static int global_create(lua_State *L); +-static int meth_connect(lua_State *L); +-static int meth_listen(lua_State *L); +-static int meth_bind(lua_State *L); +-static int meth_send(lua_State *L); +-static int meth_shutdown(lua_State *L); +-static int meth_receive(lua_State *L); +-static int meth_accept(lua_State *L); +-static int meth_close(lua_State *L); +-static int meth_setoption(lua_State *L); +-static int meth_settimeout(lua_State *L); +-static int meth_getfd(lua_State *L); +-static int meth_setfd(lua_State *L); +-static int meth_dirty(lua_State *L); +-static int meth_getstats(lua_State *L); +-static int meth_setstats(lua_State *L); +-static int meth_getsockname(lua_State *L); +- +-static const char *unixstream_tryconnect(p_unix un, const char *path); +-static const char *unixstream_trybind(p_unix un, const char *path); +- +-/* unixstream object methods */ +-static luaL_Reg unixstream_methods[] = { +- {"__gc", meth_close}, +- {"__tostring", auxiliar_tostring}, +- {"accept", meth_accept}, +- {"bind", meth_bind}, +- {"close", meth_close}, +- {"connect", meth_connect}, +- {"dirty", meth_dirty}, +- {"getfd", meth_getfd}, +- {"getstats", meth_getstats}, +- {"setstats", meth_setstats}, +- {"listen", meth_listen}, +- {"receive", meth_receive}, +- {"send", meth_send}, +- {"setfd", meth_setfd}, +- {"setoption", meth_setoption}, +- {"setpeername", meth_connect}, +- {"setsockname", meth_bind}, +- {"getsockname", meth_getsockname}, +- {"settimeout", meth_settimeout}, +- {"shutdown", meth_shutdown}, +- {NULL, NULL} +-}; +- +-/* socket option handlers */ +-static t_opt optset[] = { +- {"keepalive", opt_set_keepalive}, +- {"reuseaddr", opt_set_reuseaddr}, +- {"linger", opt_set_linger}, +- {NULL, NULL} +-}; +- +-/* functions in library namespace */ +-static luaL_Reg func[] = { +- {"stream", global_create}, +- {NULL, NULL} +-}; +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-int unixstream_open(lua_State *L) +-{ +- /* create classes */ +- auxiliar_newclass(L, "unixstream{master}", unixstream_methods); +- auxiliar_newclass(L, "unixstream{client}", unixstream_methods); +- auxiliar_newclass(L, "unixstream{server}", unixstream_methods); +- +- /* create class groups */ +- auxiliar_add2group(L, "unixstream{master}", "unixstream{any}"); +- auxiliar_add2group(L, "unixstream{client}", "unixstream{any}"); +- auxiliar_add2group(L, "unixstream{server}", "unixstream{any}"); +- +- luaL_setfuncs(L, func, 0); +- return 0; +-} +- +-/*=========================================================================*\ +-* Lua methods +-\*=========================================================================*/ +-/*-------------------------------------------------------------------------*\ +-* Just call buffered IO methods +-\*-------------------------------------------------------------------------*/ +-static int meth_send(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- return buffer_meth_send(L, &un->buf); +-} +- +-static int meth_receive(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- return buffer_meth_receive(L, &un->buf); +-} +- +-static int meth_getstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- return buffer_meth_getstats(L, &un->buf); +-} +- +-static int meth_setstats(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- return buffer_meth_setstats(L, &un->buf); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Just call option handler +-\*-------------------------------------------------------------------------*/ +-static int meth_setoption(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- return opt_meth_setoption(L, optset, &un->sock); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Select support methods +-\*-------------------------------------------------------------------------*/ +-static int meth_getfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- lua_pushnumber(L, (int) un->sock); +- return 1; +-} +- +-/* this is very dangerous, but can be handy for those that are brave enough */ +-static int meth_setfd(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- un->sock = (t_socket) luaL_checknumber(L, 2); +- return 0; +-} +- +-static int meth_dirty(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- lua_pushboolean(L, !buffer_isempty(&un->buf)); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Waits for and returns a client object attempting connection to the +-* server object +-\*-------------------------------------------------------------------------*/ +-static int meth_accept(lua_State *L) { +- p_unix server = (p_unix) auxiliar_checkclass(L, "unixstream{server}", 1); +- p_timeout tm = timeout_markstart(&server->tm); +- t_socket sock; +- int err = socket_accept(&server->sock, &sock, NULL, NULL, tm); +- /* if successful, push client socket */ +- if (err == IO_DONE) { +- p_unix clnt = (p_unix) lua_newuserdata(L, sizeof(t_unix)); +- auxiliar_setclass(L, "unixstream{client}", -1); +- /* initialize structure fields */ +- socket_setnonblocking(&sock); +- clnt->sock = sock; +- io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv, +- (p_error) socket_ioerror, &clnt->sock); +- timeout_init(&clnt->tm, -1, -1); +- buffer_init(&clnt->buf, &clnt->io, &clnt->tm); +- return 1; +- } else { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(err)); +- return 2; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Binds an object to an address +-\*-------------------------------------------------------------------------*/ +-static const char *unixstream_trybind(p_unix un, const char *path) { +- struct sockaddr_un local; +- size_t len = strlen(path); +- int err; +- if (len >= sizeof(local.sun_path)) return "path too long"; +- memset(&local, 0, sizeof(local)); +- strcpy(local.sun_path, path); +- local.sun_family = AF_UNIX; +-#ifdef UNIX_HAS_SUN_LEN +- local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) +- + len + 1; +- err = socket_bind(&un->sock, (SA *) &local, local.sun_len); +- +-#else +- err = socket_bind(&un->sock, (SA *) &local, +- sizeof(local.sun_family) + len); +-#endif +- if (err != IO_DONE) socket_destroy(&un->sock); +- return socket_strerror(err); +-} +- +-static int meth_bind(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); +- const char *path = luaL_checkstring(L, 2); +- const char *err = unixstream_trybind(un, path); +- if (err) { +- lua_pushnil(L); +- lua_pushstring(L, err); +- return 2; +- } +- lua_pushnumber(L, 1); +- return 1; +-} +- +-static int meth_getsockname(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- struct sockaddr_un peer = {0}; +- socklen_t peer_len = sizeof(peer); +- +- if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(errno)); +- return 2; +- } +- +- lua_pushstring(L, peer.sun_path); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Turns a master unixstream object into a client object. +-\*-------------------------------------------------------------------------*/ +-static const char *unixstream_tryconnect(p_unix un, const char *path) +-{ +- struct sockaddr_un remote; +- int err; +- size_t len = strlen(path); +- if (len >= sizeof(remote.sun_path)) return "path too long"; +- memset(&remote, 0, sizeof(remote)); +- strcpy(remote.sun_path, path); +- remote.sun_family = AF_UNIX; +- timeout_markstart(&un->tm); +-#ifdef UNIX_HAS_SUN_LEN +- remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) +- + len + 1; +- err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm); +-#else +- err = socket_connect(&un->sock, (SA *) &remote, +- sizeof(remote.sun_family) + len, &un->tm); +-#endif +- if (err != IO_DONE) socket_destroy(&un->sock); +- return socket_strerror(err); +-} +- +-static int meth_connect(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); +- const char *path = luaL_checkstring(L, 2); +- const char *err = unixstream_tryconnect(un, path); +- if (err) { +- lua_pushnil(L); +- lua_pushstring(L, err); +- return 2; +- } +- /* turn master object into a client object */ +- auxiliar_setclass(L, "unixstream{client}", 1); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Closes socket used by object +-\*-------------------------------------------------------------------------*/ +-static int meth_close(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- socket_destroy(&un->sock); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Puts the sockt in listen mode +-\*-------------------------------------------------------------------------*/ +-static int meth_listen(lua_State *L) +-{ +- p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); +- int backlog = (int) luaL_optnumber(L, 2, 32); +- int err = socket_listen(&un->sock, backlog); +- if (err != IO_DONE) { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(err)); +- return 2; +- } +- /* turn master object into a server object */ +- auxiliar_setclass(L, "unixstream{server}", 1); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Shuts the connection down partially +-\*-------------------------------------------------------------------------*/ +-static int meth_shutdown(lua_State *L) +-{ +- /* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */ +- static const char* methods[] = { "receive", "send", "both", NULL }; +- p_unix stream = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); +- int how = luaL_checkoption(L, 2, "both", methods); +- socket_shutdown(&stream->sock, how); +- lua_pushnumber(L, 1); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Just call tm methods +-\*-------------------------------------------------------------------------*/ +-static int meth_settimeout(lua_State *L) { +- p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); +- return timeout_meth_settimeout(L, &un->tm); +-} +- +-/*=========================================================================*\ +-* Library functions +-\*=========================================================================*/ +-/*-------------------------------------------------------------------------*\ +-* Creates a master unixstream object +-\*-------------------------------------------------------------------------*/ +-static int global_create(lua_State *L) { +- t_socket sock; +- int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0); +- /* try to allocate a system socket */ +- if (err == IO_DONE) { +- /* allocate unixstream object */ +- p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); +- /* set its type as master object */ +- auxiliar_setclass(L, "unixstream{master}", -1); +- /* initialize remaining structure fields */ +- socket_setnonblocking(&sock); +- un->sock = sock; +- io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, +- (p_error) socket_ioerror, &un->sock); +- timeout_init(&un->tm, -1, -1); +- buffer_init(&un->buf, &un->io, &un->tm); +- return 1; +- } else { +- lua_pushnil(L); +- lua_pushstring(L, socket_strerror(err)); +- return 2; +- } +-} +diff --git a/unixstream.h b/unixstream.h +deleted file mode 100644 +index 7916aff..0000000 +--- a/unixstream.h ++++ /dev/null +@@ -1,29 +0,0 @@ +-#ifndef UNIXSTREAM_H +-#define UNIXSTREAM_H +-/*=========================================================================*\ +-* UNIX STREAM object +-* LuaSocket toolkit +-* +-* The unixstream.h module is basicly a glue that puts together modules buffer.h, +-* timeout.h socket.h and inet.h to provide the LuaSocket UNIX STREAM (AF_UNIX, +-* SOCK_STREAM) support. +-* +-* Three classes are defined: master, client and server. The master class is +-* a newly created unixstream object, that has not been bound or connected. Server +-* objects are unixstream objects bound to some local address. Client objects are +-* unixstream objects either connected to some address or returned by the accept +-* method of a server object. +-\*=========================================================================*/ +-#include "unix.h" +- +-#ifndef _WIN32 +-#pragma GCC visibility push(hidden) +-#endif +- +-int unixstream_open(lua_State *L); +- +-#ifndef _WIN32 +-#pragma GCC visibility pop +-#endif +- +-#endif /* UNIXSTREAM_H */ +diff --git a/wsocket.c b/wsocket.c +deleted file mode 100755 +index 6cb1e41..0000000 +--- a/wsocket.c ++++ /dev/null +@@ -1,434 +0,0 @@ +-/*=========================================================================*\ +-* Socket compatibilization module for Win32 +-* LuaSocket toolkit +-* +-* The penalty of calling select to avoid busy-wait is only paid when +-* the I/O call fail in the first place. +-\*=========================================================================*/ +-#include "luasocket.h" +- +-#include +- +-#include "socket.h" +-#include "pierror.h" +- +-/* WinSock doesn't have a strerror... */ +-static const char *wstrerror(int err); +- +-/*-------------------------------------------------------------------------*\ +-* Initializes module +-\*-------------------------------------------------------------------------*/ +-int socket_open(void) { +- WSADATA wsaData; +- WORD wVersionRequested = MAKEWORD(2, 0); +- int err = WSAStartup(wVersionRequested, &wsaData ); +- if (err != 0) return 0; +- if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) && +- (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) { +- WSACleanup(); +- return 0; +- } +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Close module +-\*-------------------------------------------------------------------------*/ +-int socket_close(void) { +- WSACleanup(); +- return 1; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Wait for readable/writable/connected socket with timeout +-\*-------------------------------------------------------------------------*/ +-#define WAITFD_R 1 +-#define WAITFD_W 2 +-#define WAITFD_E 4 +-#define WAITFD_C (WAITFD_E|WAITFD_W) +- +-int socket_waitfd(p_socket ps, int sw, p_timeout tm) { +- int ret; +- fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL; +- struct timeval tv, *tp = NULL; +- double t; +- if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ +- if (sw & WAITFD_R) { +- FD_ZERO(&rfds); +- FD_SET(*ps, &rfds); +- rp = &rfds; +- } +- if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } +- if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(*ps, &efds); ep = &efds; } +- if ((t = timeout_get(tm)) >= 0.0) { +- tv.tv_sec = (int) t; +- tv.tv_usec = (int) ((t-tv.tv_sec)*1.0e6); +- tp = &tv; +- } +- ret = select(0, rp, wp, ep, tp); +- if (ret == -1) return WSAGetLastError(); +- if (ret == 0) return IO_TIMEOUT; +- if (sw == WAITFD_C && FD_ISSET(*ps, &efds)) return IO_CLOSED; +- return IO_DONE; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Select with int timeout in ms +-\*-------------------------------------------------------------------------*/ +-int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, +- p_timeout tm) { +- struct timeval tv; +- double t = timeout_get(tm); +- tv.tv_sec = (int) t; +- tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); +- if (n <= 0) { +- Sleep((DWORD) (1000*t)); +- return 0; +- } else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Close and inutilize socket +-\*-------------------------------------------------------------------------*/ +-void socket_destroy(p_socket ps) { +- if (*ps != SOCKET_INVALID) { +- socket_setblocking(ps); /* close can take a long time on WIN32 */ +- closesocket(*ps); +- *ps = SOCKET_INVALID; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* +-\*-------------------------------------------------------------------------*/ +-void socket_shutdown(p_socket ps, int how) { +- socket_setblocking(ps); +- shutdown(*ps, how); +- socket_setnonblocking(ps); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Creates and sets up a socket +-\*-------------------------------------------------------------------------*/ +-int socket_create(p_socket ps, int domain, int type, int protocol) { +- *ps = socket(domain, type, protocol); +- if (*ps != SOCKET_INVALID) return IO_DONE; +- else return WSAGetLastError(); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Connects or returns error message +-\*-------------------------------------------------------------------------*/ +-int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { +- int err; +- /* don't call on closed socket */ +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- /* ask system to connect */ +- if (connect(*ps, addr, len) == 0) return IO_DONE; +- /* make sure the system is trying to connect */ +- err = WSAGetLastError(); +- if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) return err; +- /* zero timeout case optimization */ +- if (timeout_iszero(tm)) return IO_TIMEOUT; +- /* we wait until something happens */ +- err = socket_waitfd(ps, WAITFD_C, tm); +- if (err == IO_CLOSED) { +- int elen = sizeof(err); +- /* give windows time to set the error (yes, disgusting) */ +- Sleep(10); +- /* find out why we failed */ +- getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &elen); +- /* we KNOW there was an error. if 'why' is 0, we will return +- * "unknown error", but it's not really our fault */ +- return err > 0? err: IO_UNKNOWN; +- } else return err; +- +-} +- +-/*-------------------------------------------------------------------------*\ +-* Binds or returns error message +-\*-------------------------------------------------------------------------*/ +-int socket_bind(p_socket ps, SA *addr, socklen_t len) { +- int err = IO_DONE; +- socket_setblocking(ps); +- if (bind(*ps, addr, len) < 0) err = WSAGetLastError(); +- socket_setnonblocking(ps); +- return err; +-} +- +-/*-------------------------------------------------------------------------*\ +-* +-\*-------------------------------------------------------------------------*/ +-int socket_listen(p_socket ps, int backlog) { +- int err = IO_DONE; +- socket_setblocking(ps); +- if (listen(*ps, backlog) < 0) err = WSAGetLastError(); +- socket_setnonblocking(ps); +- return err; +-} +- +-/*-------------------------------------------------------------------------*\ +-* Accept with timeout +-\*-------------------------------------------------------------------------*/ +-int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, +- p_timeout tm) { +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- for ( ;; ) { +- int err; +- /* try to get client socket */ +- if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; +- /* find out why we failed */ +- err = WSAGetLastError(); +- /* if we failed because there was no connectoin, keep trying */ +- if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err; +- /* call select to avoid busy wait */ +- if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Send with timeout +-* On windows, if you try to send 10MB, the OS will buffer EVERYTHING +-* this can take an awful lot of time and we will end up blocked. +-* Therefore, whoever calls this function should not pass a huge buffer. +-\*-------------------------------------------------------------------------*/ +-int socket_send(p_socket ps, const char *data, size_t count, +- size_t *sent, p_timeout tm) +-{ +- int err; +- *sent = 0; +- /* avoid making system calls on closed sockets */ +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- /* loop until we send something or we give up on error */ +- for ( ;; ) { +- /* try to send something */ +- int put = send(*ps, data, (int) count, 0); +- /* if we sent something, we are done */ +- if (put > 0) { +- *sent = put; +- return IO_DONE; +- } +- /* deal with failure */ +- err = WSAGetLastError(); +- /* we can only proceed if there was no serious error */ +- if (err != WSAEWOULDBLOCK) return err; +- /* avoid busy wait */ +- if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Sendto with timeout +-\*-------------------------------------------------------------------------*/ +-int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, +- SA *addr, socklen_t len, p_timeout tm) +-{ +- int err; +- *sent = 0; +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- for ( ;; ) { +- int put = sendto(*ps, data, (int) count, 0, addr, len); +- if (put > 0) { +- *sent = put; +- return IO_DONE; +- } +- err = WSAGetLastError(); +- if (err != WSAEWOULDBLOCK) return err; +- if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Receive with timeout +-\*-------------------------------------------------------------------------*/ +-int socket_recv(p_socket ps, char *data, size_t count, size_t *got, +- p_timeout tm) +-{ +- int err, prev = IO_DONE; +- *got = 0; +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- for ( ;; ) { +- int taken = recv(*ps, data, (int) count, 0); +- if (taken > 0) { +- *got = taken; +- return IO_DONE; +- } +- if (taken == 0) return IO_CLOSED; +- err = WSAGetLastError(); +- /* On UDP, a connreset simply means the previous send failed. +- * So we try again. +- * On TCP, it means our socket is now useless, so the error passes. +- * (We will loop again, exiting because the same error will happen) */ +- if (err != WSAEWOULDBLOCK) { +- if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; +- prev = err; +- } +- if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Recvfrom with timeout +-\*-------------------------------------------------------------------------*/ +-int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, +- SA *addr, socklen_t *len, p_timeout tm) +-{ +- int err, prev = IO_DONE; +- *got = 0; +- if (*ps == SOCKET_INVALID) return IO_CLOSED; +- for ( ;; ) { +- int taken = recvfrom(*ps, data, (int) count, 0, addr, len); +- if (taken > 0) { +- *got = taken; +- return IO_DONE; +- } +- if (taken == 0) return IO_CLOSED; +- err = WSAGetLastError(); +- /* On UDP, a connreset simply means the previous send failed. +- * So we try again. +- * On TCP, it means our socket is now useless, so the error passes. +- * (We will loop again, exiting because the same error will happen) */ +- if (err != WSAEWOULDBLOCK) { +- if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; +- prev = err; +- } +- if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; +- } +-} +- +-/*-------------------------------------------------------------------------*\ +-* Put socket into blocking mode +-\*-------------------------------------------------------------------------*/ +-void socket_setblocking(p_socket ps) { +- u_long argp = 0; +- ioctlsocket(*ps, FIONBIO, &argp); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Put socket into non-blocking mode +-\*-------------------------------------------------------------------------*/ +-void socket_setnonblocking(p_socket ps) { +- u_long argp = 1; +- ioctlsocket(*ps, FIONBIO, &argp); +-} +- +-/*-------------------------------------------------------------------------*\ +-* DNS helpers +-\*-------------------------------------------------------------------------*/ +-int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { +- *hp = gethostbyaddr(addr, len, AF_INET); +- if (*hp) return IO_DONE; +- else return WSAGetLastError(); +-} +- +-int socket_gethostbyname(const char *addr, struct hostent **hp) { +- *hp = gethostbyname(addr); +- if (*hp) return IO_DONE; +- else return WSAGetLastError(); +-} +- +-/*-------------------------------------------------------------------------*\ +-* Error translation functions +-\*-------------------------------------------------------------------------*/ +-const char *socket_hoststrerror(int err) { +- if (err <= 0) return io_strerror(err); +- switch (err) { +- case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; +- default: return wstrerror(err); +- } +-} +- +-const char *socket_strerror(int err) { +- if (err <= 0) return io_strerror(err); +- switch (err) { +- case WSAEADDRINUSE: return PIE_ADDRINUSE; +- case WSAECONNREFUSED : return PIE_CONNREFUSED; +- case WSAEISCONN: return PIE_ISCONN; +- case WSAEACCES: return PIE_ACCESS; +- case WSAECONNABORTED: return PIE_CONNABORTED; +- case WSAECONNRESET: return PIE_CONNRESET; +- case WSAETIMEDOUT: return PIE_TIMEDOUT; +- default: return wstrerror(err); +- } +-} +- +-const char *socket_ioerror(p_socket ps, int err) { +- (void) ps; +- return socket_strerror(err); +-} +- +-static const char *wstrerror(int err) { +- switch (err) { +- case WSAEINTR: return "Interrupted function call"; +- case WSAEACCES: return PIE_ACCESS; /* "Permission denied"; */ +- case WSAEFAULT: return "Bad address"; +- case WSAEINVAL: return "Invalid argument"; +- case WSAEMFILE: return "Too many open files"; +- case WSAEWOULDBLOCK: return "Resource temporarily unavailable"; +- case WSAEINPROGRESS: return "Operation now in progress"; +- case WSAEALREADY: return "Operation already in progress"; +- case WSAENOTSOCK: return "Socket operation on nonsocket"; +- case WSAEDESTADDRREQ: return "Destination address required"; +- case WSAEMSGSIZE: return "Message too long"; +- case WSAEPROTOTYPE: return "Protocol wrong type for socket"; +- case WSAENOPROTOOPT: return "Bad protocol option"; +- case WSAEPROTONOSUPPORT: return "Protocol not supported"; +- case WSAESOCKTNOSUPPORT: return PIE_SOCKTYPE; /* "Socket type not supported"; */ +- case WSAEOPNOTSUPP: return "Operation not supported"; +- case WSAEPFNOSUPPORT: return "Protocol family not supported"; +- case WSAEAFNOSUPPORT: return PIE_FAMILY; /* "Address family not supported by protocol family"; */ +- case WSAEADDRINUSE: return PIE_ADDRINUSE; /* "Address already in use"; */ +- case WSAEADDRNOTAVAIL: return "Cannot assign requested address"; +- case WSAENETDOWN: return "Network is down"; +- case WSAENETUNREACH: return "Network is unreachable"; +- case WSAENETRESET: return "Network dropped connection on reset"; +- case WSAECONNABORTED: return "Software caused connection abort"; +- case WSAECONNRESET: return PIE_CONNRESET; /* "Connection reset by peer"; */ +- case WSAENOBUFS: return "No buffer space available"; +- case WSAEISCONN: return PIE_ISCONN; /* "Socket is already connected"; */ +- case WSAENOTCONN: return "Socket is not connected"; +- case WSAESHUTDOWN: return "Cannot send after socket shutdown"; +- case WSAETIMEDOUT: return PIE_TIMEDOUT; /* "Connection timed out"; */ +- case WSAECONNREFUSED: return PIE_CONNREFUSED; /* "Connection refused"; */ +- case WSAEHOSTDOWN: return "Host is down"; +- case WSAEHOSTUNREACH: return "No route to host"; +- case WSAEPROCLIM: return "Too many processes"; +- case WSASYSNOTREADY: return "Network subsystem is unavailable"; +- case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range"; +- case WSANOTINITIALISED: +- return "Successful WSAStartup not yet performed"; +- case WSAEDISCON: return "Graceful shutdown in progress"; +- case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; /* "Host not found"; */ +- case WSATRY_AGAIN: return "Nonauthoritative host not found"; +- case WSANO_RECOVERY: return PIE_FAIL; /* "Nonrecoverable name lookup error"; */ +- case WSANO_DATA: return "Valid name, no data record of requested type"; +- default: return "Unknown error"; +- } +-} +- +-const char *socket_gaistrerror(int err) { +- if (err == 0) return NULL; +- switch (err) { +- case EAI_AGAIN: return PIE_AGAIN; +- case EAI_BADFLAGS: return PIE_BADFLAGS; +-#ifdef EAI_BADHINTS +- case EAI_BADHINTS: return PIE_BADHINTS; +-#endif +- case EAI_FAIL: return PIE_FAIL; +- case EAI_FAMILY: return PIE_FAMILY; +- case EAI_MEMORY: return PIE_MEMORY; +- case EAI_NONAME: return PIE_NONAME; +-#ifdef EAI_OVERFLOW +- case EAI_OVERFLOW: return PIE_OVERFLOW; +-#endif +-#ifdef EAI_PROTOCOL +- case EAI_PROTOCOL: return PIE_PROTOCOL; +-#endif +- case EAI_SERVICE: return PIE_SERVICE; +- case EAI_SOCKTYPE: return PIE_SOCKTYPE; +-#ifdef EAI_SYSTEM +- case EAI_SYSTEM: return strerror(errno); +-#endif +- default: return LUA_GAI_STRERROR(err); +- } +-} +diff --git a/wsocket.h b/wsocket.h +deleted file mode 100644 +index 3986640..0000000 +--- a/wsocket.h ++++ /dev/null +@@ -1,33 +0,0 @@ +-#ifndef WSOCKET_H +-#define WSOCKET_H +-/*=========================================================================*\ +-* Socket compatibilization module for Win32 +-* LuaSocket toolkit +-\*=========================================================================*/ +- +-/*=========================================================================*\ +-* WinSock include files +-\*=========================================================================*/ +-#include +-#include +- +-typedef int socklen_t; +-typedef SOCKADDR_STORAGE t_sockaddr_storage; +-typedef SOCKET t_socket; +-typedef t_socket *p_socket; +- +-#ifndef IPV6_V6ONLY +-#define IPV6_V6ONLY 27 +-#endif +- +-#define SOCKET_INVALID (INVALID_SOCKET) +- +-#ifndef SO_REUSEPORT +-#define SO_REUSEPORT SO_REUSEADDR +-#endif +- +-#ifndef AI_NUMERICSERV +-#define AI_NUMERICSERV (0) +-#endif +- +-#endif /* WSOCKET_H */ diff --git a/platform/hac/pkglist.txt b/platform/hac/pkglist.txt new file mode 100644 index 000000000..e7ac1d4b9 --- /dev/null +++ b/platform/hac/pkglist.txt @@ -0,0 +1,20 @@ +switch-box2d +switch-bzip2 +switch-curl +switch-flac +switch-freetype +switch-glm +switch-libjpeg-turbo +switch-libmodplug +switch-libogg +switch-libpng +switch-libtheora +switch-libvorbis +switch-libvorbisidec +switch-liblua51 +switch-lz4 +switch-physfs +switch-zlib +switch-cmake +switch-tools +uam diff --git a/platform/hac/romfs/nogame/cartridge.png b/platform/hac/romfs/nogame/cartridge.png new file mode 100644 index 000000000..cf0529bd9 Binary files /dev/null and b/platform/hac/romfs/nogame/cartridge.png differ diff --git a/platform/hac/romfs/nogame/nogame.png b/platform/hac/romfs/nogame/nogame.png new file mode 100644 index 000000000..ed4892350 Binary files /dev/null and b/platform/hac/romfs/nogame/nogame.png differ diff --git a/platform/switch/shaders/color_fsh.glsl b/platform/hac/shaders/color_fsh.glsl similarity index 94% rename from platform/switch/shaders/color_fsh.glsl rename to platform/hac/shaders/color_fsh.glsl index b148143eb..d391db098 100644 --- a/platform/switch/shaders/color_fsh.glsl +++ b/platform/hac/shaders/color_fsh.glsl @@ -1,9 +1,9 @@ -#version 460 - -layout (location = 0) in vec4 inColor; -layout (location = 0) out vec4 outColor; - -void main() -{ - outColor = inColor; +#version 460 + +layout (location = 0) in vec4 inColor; +layout (location = 0) out vec4 outColor; + +void main() +{ + outColor = inColor; } \ No newline at end of file diff --git a/platform/switch/shaders/texture_fsh.glsl b/platform/hac/shaders/texture_fsh.glsl similarity index 95% rename from platform/switch/shaders/texture_fsh.glsl rename to platform/hac/shaders/texture_fsh.glsl index 087e56776..0b4d3624d 100644 --- a/platform/switch/shaders/texture_fsh.glsl +++ b/platform/hac/shaders/texture_fsh.glsl @@ -1,13 +1,13 @@ -#version 460 - -layout (location = 0) in vec4 inColor; -layout (location = 1) in vec2 inTexCoord; - -layout (location = 0) out vec4 outColor; - -layout (binding = 0) uniform sampler2D texture0; - -void main() -{ - outColor = inColor * texture(texture0, inTexCoord); -} +#version 460 + +layout (location = 0) in vec4 inColor; +layout (location = 1) in vec2 inTexCoord; + +layout (location = 0) out vec4 outColor; + +layout (binding = 0) uniform sampler2D texture0; + +void main() +{ + outColor = inColor * texture(texture0, inTexCoord); +} diff --git a/platform/switch/shaders/transform_vsh.glsl b/platform/hac/shaders/transform_vsh.glsl similarity index 78% rename from platform/switch/shaders/transform_vsh.glsl rename to platform/hac/shaders/transform_vsh.glsl index ea58a6c0d..360c720eb 100644 --- a/platform/switch/shaders/transform_vsh.glsl +++ b/platform/hac/shaders/transform_vsh.glsl @@ -1,26 +1,23 @@ -#version 460 - -layout (location = 0) in vec3 inPos; - -// in attributes -- nothing happens if not used -layout (location = 1) in vec4 inColor; -layout (location = 2) in vec2 inTexCoord; - -// out attributes -- nothing happens if not used -layout (location = 0) out vec4 outColor; -layout (location = 1) out vec2 outTexCoord; - -layout (std140, binding = 0) uniform Transformation -{ - mat4 mdlvMtx; - mat4 projMtx; -} u; - -void main() -{ - vec4 pos = u.mdlvMtx * vec4(inPos, 1.0); - gl_Position = u.projMtx * pos; - - outColor = inColor; - outTexCoord = inTexCoord; -} +#version 460 + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec4 inColor; +layout (location = 2) in vec2 inTexCoord; + +layout (location = 0) out vec4 outColor; +layout (location = 1) out vec2 outTexCoord; + +layout (std140, binding = 0) uniform Transformation +{ + mat4 mdlvMtx; + mat4 projMtx; +} u; + +void main() +{ + vec4 pos = u.mdlvMtx * vec4(inPos, 1.0); + gl_Position = u.projMtx * pos; + + outColor = inColor; + outTexCoord = inTexCoord; +} diff --git a/platform/switch/shaders/video_fsh.glsl b/platform/hac/shaders/video_fsh.glsl similarity index 100% rename from platform/switch/shaders/video_fsh.glsl rename to platform/hac/shaders/video_fsh.glsl diff --git a/platform/hac/source/common/matrix_ext.cpp b/platform/hac/source/common/matrix_ext.cpp new file mode 100644 index 000000000..6c4293f53 --- /dev/null +++ b/platform/hac/source/common/matrix_ext.cpp @@ -0,0 +1,338 @@ +#include + +#include +#include + +using namespace love; + +void Matrix4::Multiply(const Matrix4& a, const Matrix4& b, float elements[16]) +{ + float32x4_t cola1 = vld1q_f32(&a.elements[0]); + float32x4_t cola2 = vld1q_f32(&a.elements[4]); + float32x4_t cola3 = vld1q_f32(&a.elements[8]); + float32x4_t cola4 = vld1q_f32(&a.elements[12]); + + float32x4_t col1 = vmulq_n_f32(cola1, b.elements[0]); + col1 = vmlaq_n_f32(col1, cola2, b.elements[1]); + col1 = vmlaq_n_f32(col1, cola3, b.elements[2]); + col1 = vmlaq_n_f32(col1, cola4, b.elements[3]); + + float32x4_t col2 = vmulq_n_f32(cola1, b.elements[4]); + col2 = vmlaq_n_f32(col2, cola2, b.elements[5]); + col2 = vmlaq_n_f32(col2, cola3, b.elements[6]); + col2 = vmlaq_n_f32(col2, cola4, b.elements[7]); + + float32x4_t col3 = vmulq_n_f32(cola1, b.elements[8]); + col3 = vmlaq_n_f32(col3, cola2, b.elements[9]); + col3 = vmlaq_n_f32(col3, cola3, b.elements[10]); + col3 = vmlaq_n_f32(col3, cola4, b.elements[11]); + + float32x4_t col4 = vmulq_n_f32(cola1, b.elements[12]); + col4 = vmlaq_n_f32(col4, cola2, b.elements[13]); + col4 = vmlaq_n_f32(col4, cola3, b.elements[14]); + col4 = vmlaq_n_f32(col4, cola4, b.elements[15]); + + vst1q_f32(&elements[0], col1); + vst1q_f32(&elements[4], col2); + vst1q_f32(&elements[8], col3); + vst1q_f32(&elements[12], col4); +} + +void Matrix4::Multiply(const Matrix4& a, const Matrix4& b, Matrix4& t) +{ + Matrix4::Multiply(a, b, t.elements); +} + +Matrix4::Matrix4() +{ + this->SetIdentity(); +} + +Matrix4::Matrix4(const float elements[16]) +{ + memcpy(this->elements, elements, sizeof(float) * 16); +} + +Matrix4::Matrix4(const Matrix4& a, const Matrix4& b) +{ + Matrix4::Multiply(a, b, this->elements); +} + +Matrix4::Matrix4(float t00, float t10, float t01, float t11, float x, float y) +{ + this->SetRawTransformation(t00, t10, t01, t11, x, y); +} + +Matrix4::Matrix4(float x, float y, float angle, float sx, float sy, float ox, + float oy, float kx, float ky) +{ + this->SetTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); +} + +bool Matrix4::IsAffine2DTransform() const +{ + return fabsf(this->elements[2] + this->elements[3] + this->elements[6] + this->elements[7] + + this->elements[8] + this->elements[9] + this->elements[11] + this->elements[14]) < + 0.00001f && + fabsf(this->elements[10] + this->elements[15] - 2.0f) < 0.00001f; +} + +Matrix4 Matrix4::operator*(const Matrix4& m) const +{ + return Matrix4(*this, m); +} + +void Matrix4::operator*=(const Matrix4& m) +{ + float elements[16]; + + Matrix4::Multiply(*this, m, elements); + memcpy(this->elements, elements, sizeof(float) * 16); +} + +void Matrix4::SetIdentity() +{ + memset(this->elements, 0, sizeof(float) * 16); + this->elements[15] = this->elements[10] = this->elements[5] = this->elements[0] = 1.0f; +} + +void Matrix4::SetTranslation(float x, float y) +{ + this->SetIdentity(); + + this->elements[12] = x; + this->elements[13] = y; +} + +void Matrix4::Translate(float x, float y) +{ + Matrix4 t; + t.SetTranslation(x, y); + this->operator*=(t); +} + +void Matrix4::SetRotation(float rad) +{ + this->SetIdentity(); + float c = cosf(rad), s = sinf(rad); + + this->elements[0] = c; + this->elements[4] = -s; + this->elements[1] = s; + this->elements[5] = c; +} + +void Matrix4::Rotate(float rad) +{ + Matrix4 t; + t.SetRotation(rad); + this->operator*=(t); +} + +void Matrix4::SetScale(float sx, float sy) +{ + this->SetIdentity(); + + this->elements[0] = sx; + this->elements[5] = sy; +} + +void Matrix4::Scale(float sx, float sy) +{ + Matrix4 t; + t.SetScale(sx, sy); + this->operator*=(t); +} + +void Matrix4::SetShear(float kx, float ky) +{ + this->SetIdentity(); + + this->elements[1] = ky; + this->elements[4] = kx; +} + +void Matrix4::Shear(float kx, float ky) +{ + Matrix4 t; + t.SetShear(kx, ky); + this->operator*=(t); +} + +void Matrix4::GetApproximateScale(float& sx, float& sy) const +{ + sx = sqrtf(this->elements[0] * this->elements[0] + this->elements[4] * this->elements[4]); + sy = sqrtf(this->elements[1] * this->elements[1] + this->elements[5] * this->elements[5]); +} + +void Matrix4::SetRawTransformation(float t00, float t10, float t01, float t11, + float x, float y) +{ + memset(this->elements, 0, sizeof(float) * 16); // zero out matrix + + this->elements[10] = this->elements[15] = 1.0f; + this->elements[0] = t00; + this->elements[1] = t10; + this->elements[4] = t01; + this->elements[5] = t11; + this->elements[12] = x; + this->elements[13] = y; +} + +void Matrix4::SetTransformation(float x, float y, float angle, float sx, float sy, + float ox, float oy, float kx, float ky) +{ + memset(this->elements, 0, sizeof(float) * 16); // zero out matrix + float c = cosf(angle), s = sinf(angle); + + this->elements[10] = this->elements[15] = 1.0f; + this->elements[0] = c * sx - ky * s * sy; // = a + this->elements[1] = s * sx + ky * c * sy; // = b + this->elements[4] = kx * c * sx - s * sy; // = c + this->elements[5] = kx * s * sx + c * sy; // = d + this->elements[12] = x - ox * this->elements[0] - oy * this->elements[4]; + this->elements[13] = y - ox * this->elements[1] - oy * this->elements[5]; +} + +Matrix4 Matrix4::Inverse() const +{ + Matrix4 inv; + + inv.elements[0] = this->elements[5] * this->elements[10] * this->elements[15] - + this->elements[5] * this->elements[11] * this->elements[14] - + this->elements[9] * this->elements[6] * this->elements[15] + + this->elements[9] * this->elements[7] * this->elements[14] + + this->elements[13] * this->elements[6] * this->elements[11] - + this->elements[13] * this->elements[7] * this->elements[10]; + + inv.elements[4] = -this->elements[4] * this->elements[10] * this->elements[15] + + this->elements[4] * this->elements[11] * this->elements[14] + + this->elements[8] * this->elements[6] * this->elements[15] - + this->elements[8] * this->elements[7] * this->elements[14] - + this->elements[12] * this->elements[6] * this->elements[11] + + this->elements[12] * this->elements[7] * this->elements[10]; + + inv.elements[8] = this->elements[4] * this->elements[9] * this->elements[15] - + this->elements[4] * this->elements[11] * this->elements[13] - + this->elements[8] * this->elements[5] * this->elements[15] + + this->elements[8] * this->elements[7] * this->elements[13] + + this->elements[12] * this->elements[5] * this->elements[11] - + this->elements[12] * this->elements[7] * this->elements[9]; + + inv.elements[12] = -this->elements[4] * this->elements[9] * this->elements[14] + + this->elements[4] * this->elements[10] * this->elements[13] + + this->elements[8] * this->elements[5] * this->elements[14] - + this->elements[8] * this->elements[6] * this->elements[13] - + this->elements[12] * this->elements[5] * this->elements[10] + + this->elements[12] * this->elements[6] * this->elements[9]; + + inv.elements[1] = -this->elements[1] * this->elements[10] * this->elements[15] + + this->elements[1] * this->elements[11] * this->elements[14] + + this->elements[9] * this->elements[2] * this->elements[15] - + this->elements[9] * this->elements[3] * this->elements[14] - + this->elements[13] * this->elements[2] * this->elements[11] + + this->elements[13] * this->elements[3] * this->elements[10]; + + inv.elements[5] = this->elements[0] * this->elements[10] * this->elements[15] - + this->elements[0] * this->elements[11] * this->elements[14] - + this->elements[8] * this->elements[2] * this->elements[15] + + this->elements[8] * this->elements[3] * this->elements[14] + + this->elements[12] * this->elements[2] * this->elements[11] - + this->elements[12] * this->elements[3] * this->elements[10]; + + inv.elements[9] = -this->elements[0] * this->elements[9] * this->elements[15] + + this->elements[0] * this->elements[11] * this->elements[13] + + this->elements[8] * this->elements[1] * this->elements[15] - + this->elements[8] * this->elements[3] * this->elements[13] - + this->elements[12] * this->elements[1] * this->elements[11] + + this->elements[12] * this->elements[3] * this->elements[9]; + + inv.elements[13] = this->elements[0] * this->elements[9] * this->elements[14] - + this->elements[0] * this->elements[10] * this->elements[13] - + this->elements[8] * this->elements[1] * this->elements[14] + + this->elements[8] * this->elements[2] * this->elements[13] + + this->elements[12] * this->elements[1] * this->elements[10] - + this->elements[12] * this->elements[2] * this->elements[9]; + + inv.elements[2] = this->elements[1] * this->elements[6] * this->elements[15] - + this->elements[1] * this->elements[7] * this->elements[14] - + this->elements[5] * this->elements[2] * this->elements[15] + + this->elements[5] * this->elements[3] * this->elements[14] + + this->elements[13] * this->elements[2] * this->elements[7] - + this->elements[13] * this->elements[3] * this->elements[6]; + + inv.elements[6] = -this->elements[0] * this->elements[6] * this->elements[15] + + this->elements[0] * this->elements[7] * this->elements[14] + + this->elements[4] * this->elements[2] * this->elements[15] - + this->elements[4] * this->elements[3] * this->elements[14] - + this->elements[12] * this->elements[2] * this->elements[7] + + this->elements[12] * this->elements[3] * this->elements[6]; + + inv.elements[10] = this->elements[0] * this->elements[5] * this->elements[15] - + this->elements[0] * this->elements[7] * this->elements[13] - + this->elements[4] * this->elements[1] * this->elements[15] + + this->elements[4] * this->elements[3] * this->elements[13] + + this->elements[12] * this->elements[1] * this->elements[7] - + this->elements[12] * this->elements[3] * this->elements[5]; + + inv.elements[14] = -this->elements[0] * this->elements[5] * this->elements[14] + + this->elements[0] * this->elements[6] * this->elements[13] + + this->elements[4] * this->elements[1] * this->elements[14] - + this->elements[4] * this->elements[2] * this->elements[13] - + this->elements[12] * this->elements[1] * this->elements[6] + + this->elements[12] * this->elements[2] * this->elements[5]; + + inv.elements[3] = -this->elements[1] * this->elements[6] * this->elements[11] + + this->elements[1] * this->elements[7] * this->elements[10] + + this->elements[5] * this->elements[2] * this->elements[11] - + this->elements[5] * this->elements[3] * this->elements[10] - + this->elements[9] * this->elements[2] * this->elements[7] + + this->elements[9] * this->elements[3] * this->elements[6]; + + inv.elements[7] = this->elements[0] * this->elements[6] * this->elements[11] - + this->elements[0] * this->elements[7] * this->elements[10] - + this->elements[4] * this->elements[2] * this->elements[11] + + this->elements[4] * this->elements[3] * this->elements[10] + + this->elements[8] * this->elements[2] * this->elements[7] - + this->elements[8] * this->elements[3] * this->elements[6]; + + inv.elements[11] = -this->elements[0] * this->elements[5] * this->elements[11] + + this->elements[0] * this->elements[7] * this->elements[9] + + this->elements[4] * this->elements[1] * this->elements[11] - + this->elements[4] * this->elements[3] * this->elements[9] - + this->elements[8] * this->elements[1] * this->elements[7] + + this->elements[8] * this->elements[3] * this->elements[5]; + + inv.elements[15] = this->elements[0] * this->elements[5] * this->elements[10] - + this->elements[0] * this->elements[6] * this->elements[9] - + this->elements[4] * this->elements[1] * this->elements[10] + + this->elements[4] * this->elements[2] * this->elements[9] + + this->elements[8] * this->elements[1] * this->elements[6] - + this->elements[8] * this->elements[2] * this->elements[5]; + + float det = this->elements[0] * inv.elements[0] + this->elements[1] * inv.elements[4] + + this->elements[2] * inv.elements[8] + this->elements[3] * inv.elements[12]; + + float invdet = 1.0f / det; + + for (int i = 0; i < 16; i++) + inv.elements[i] *= invdet; + + return inv; +} + +Matrix4 Matrix4::Ortho(float left, float right, float bottom, float top, + float near, float far) +{ + Matrix4 matrix; + + matrix.elements[0] = 2.0f / (right - left); + matrix.elements[5] = 2.0f / (top - bottom); + matrix.elements[10] = -2.0f / (far - near); + + matrix.elements[12] = -(right + left) / (right - left); + matrix.elements[13] = -(top + bottom) / (top - bottom); + matrix.elements[14] = -(far + near) / (far - near); + + return matrix; +} diff --git a/platform/hac/source/common/screen_ext.cpp b/platform/hac/source/common/screen_ext.cpp new file mode 100644 index 000000000..af050b476 --- /dev/null +++ b/platform/hac/source/common/screen_ext.cpp @@ -0,0 +1,16 @@ +#include + +namespace love +{ + std::span GetScreenInfo() + { + return { screenInfo }; + } + + const ScreenInfo& GetScreenInfo(Screen id) + { + const auto& info = GetScreenInfo(); + + return info[id]; + } +} // namespace love diff --git a/platform/hac/source/modules/fontmodule_ext.cpp b/platform/hac/source/modules/fontmodule_ext.cpp new file mode 100644 index 000000000..fc4d498ef --- /dev/null +++ b/platform/hac/source/modules/fontmodule_ext.cpp @@ -0,0 +1,72 @@ +#include + +#include + +#include +#include + +using namespace love; + +FontModule::FontModule() : FontModule() +{ + this->defaultFontData.Set(new SystemFont(), Acquire::NORETAIN); +} + +Rasterizer* FontModule::NewImageRasterizer(ImageData* data, + const std::string& text, int extraSpacing, + float dpiScale) const +{ + std::vector glyphs {}; + glyphs.reserve(text.size()); + + try + { + utf8::iterator it(text.begin(), text.begin(), text.end()); + utf8::iterator end(text.end(), text.begin(), text.end()); + + while (it != end) + glyphs.push_back(*it++); + } + catch (utf8::exception& e) + { + throw love::Exception("UTF-8 decoding error: %s", e.what()); + } + + return this->NewImageRasterizer(data, &glyphs[0], (int)glyphs.size(), extraSpacing, dpiScale); +} + +Rasterizer* FontModule::NewImageRasterizer(ImageData* data, + uint32_t* glyphs, int glyphCount, + int extraSpacing, float dpiScale) const +{ + return new ImageRasterizer(data, glyphs, glyphCount, extraSpacing, dpiScale); +} + +Rasterizer* FontModule::NewRasterizer(FileData* data) const +{ + if (TrueTypeRasterizer::Accepts(this->library, data)) + return this->NewTrueTypeRasterizer(data, 12, TrueTypeRasterizer<>::HINTING_NORMAL); + else if (BMFontRasterizer::Accepts(data)) + return this->NewBMFontRasterizer(data, {}, 1.0f); + + throw love::Exception("Invalid font file: %s", data->GetFilename().c_str()); + return nullptr; +} + +Rasterizer* FontModule::NewTrueTypeRasterizer( + Data* data, int size, TrueTypeRasterizer<>::Hinting hinting) const +{ + float dpiScale = 1.0f; + auto window = Module::GetInstance>(Module::M_WINDOW); + + if (window != nullptr) + dpiScale = window->GetDPIScale(); + + return this->NewTrueTypeRasterizer(data, size, dpiScale, hinting); +} + +Rasterizer* FontModule::NewTrueTypeRasterizer( + Data* data, int size, float dpiScale, TrueTypeRasterizer<>::Hinting hinting) const +{ + return new TrueTypeRasterizer(this->library, data, size, dpiScale, hinting); +} \ No newline at end of file diff --git a/platform/hac/source/modules/graphics_ext.cpp b/platform/hac/source/modules/graphics_ext.cpp new file mode 100644 index 000000000..f5b3e41bc --- /dev/null +++ b/platform/hac/source/modules/graphics_ext.cpp @@ -0,0 +1,118 @@ +#include + +#include + +#include + +#include + +#include + +using Renderer = love::Renderer; +using namespace love; + +Graphics::Graphics() +{ + auto* window = Module::GetInstance>(Module::M_WINDOW); + + if (window != nullptr) + { + window->SetGraphics(this); + + if (window->IsOpen()) + { + int width, height; + Window<>::WindowSettings settings {}; + + window->GetWindow(width, height, settings); + window->SetWindow(width, height, &settings); + } + } +} + +Graphics::~Graphics() +{ + for (size_t index = 0; index < Shader<>::STANDARD_MAX_ENUM; index++) + { + if (Shader::defaults[index]) + { + Shader::defaults[index]->Release(); + Shader::defaults[index] = nullptr; + } + } +} + +void Graphics::SetShader() +{ + Shader::AttachDefault(Shader<>::STANDARD_DEFAULT); + this->states.back().shader.Set(nullptr); +} + +void Graphics::SetShader(Shader* shader) +{ + if (shader == nullptr) + return this->SetShader(); + + shader->Attach(); + this->states.back().shader.Set(shader); +} + +bool Graphics::SetMode(int x, int y, int width, int height) +{ + this->width = width; + this->height = height; + + ::Renderer::Instance().CreateFramebuffers(); + this->SetViewportSize(width, height); + + this->RestoreState(this->states.back()); + + for (int index = 0; index < Shader<>::STANDARD_MAX_ENUM; index++) + { + const auto type = (Shader<>::StandardShader)index; + + try + { + if (!Shader::defaults[index]) + { + auto* shader = new Shader(); + shader->LoadDefaults(type); + + Shader::defaults[index] = shader; + } + } + catch (love::Exception&) + { + throw; + } + } + + if (!Shader::current) + Shader::defaults[Shader<>::STANDARD_DEFAULT]->Attach(); + + this->created = true; + return true; +} + +Texture* Graphics::NewTexture(const Texture<>::Settings& settings, + const Texture<>::Slices* slices) const +{ + return new Texture(this, settings, slices); +} + +void Graphics::Draw(Drawable* drawable, const Matrix4& matrix) +{ + drawable->Draw(*this, matrix); +} + +void Graphics::Draw(Texture* texture, Quad* quad, + const Matrix4& matrix) +{ + texture->Draw(*this, quad, matrix); +} + +void Graphics::SetViewportSize(int width, int height) +{ + ::Renderer::Instance().SetViewport({ 0, 0, width, height }); + this->SetScissor({ 0, 0, width, height }); +} diff --git a/platform/hac/source/modules/imagemodule_ext.cpp b/platform/hac/source/modules/imagemodule_ext.cpp new file mode 100644 index 000000000..eaed52f26 --- /dev/null +++ b/platform/hac/source/modules/imagemodule_ext.cpp @@ -0,0 +1,9 @@ +#include + +using namespace love; + +ImageModule::ImageModule() +{ + this->formatHandlers = { new JPGHandler(), new PNGHandler(), new DDSHandler(), + new PKMHandler(), new ASTCHandler(), new KTXHandler() }; +} diff --git a/platform/hac/source/modules/joystickmodule_ext.cpp b/platform/hac/source/modules/joystickmodule_ext.cpp new file mode 100644 index 000000000..c99d38b06 --- /dev/null +++ b/platform/hac/source/modules/joystickmodule_ext.cpp @@ -0,0 +1,149 @@ +#include + +#include +#include + +#include + +using namespace love; + +JoystickModule::JoystickModule() : pool(nullptr), thread(nullptr) +{ + for (int index = 0; index < (int)this->AcquireCurrentJoystickIds().size(); index++) + this->AddJoystick(index); + + try + { + this->pool = new VibrationPool(); + } + catch (love::Exception&) + { + throw; + } + + this->thread = new PoolThread(this->pool); + this->thread->Start(); +} + +JoystickModule::~JoystickModule() +{ + this->thread->SetFinish(); + this->thread->Wait(); + + delete this->thread; + delete this->pool; +} + +void JoystickModule::AddVibration(::Vibration* vibration) +{ + this->pool->AddVibration(vibration); +} + +std::vector JoystickModule::AcquireCurrentJoystickIds() +{ + std::vector info {}; + + for (size_t index = 0; index < npad::MAX_JOYSTICKS; index++) + { + PadState state {}; + + if (index == 0) + padInitializeDefault(&state); + else + padInitialize(&state, (HidNpadIdType)index); + + padUpdate(&state); + + if (padIsConnected(&state)) + info.push_back((HidNpadIdType)index); + } + + return info; +} + +std::vector JoystickModule::GetActiveStyleSets() +{ + std::vector info {}; + HidNpadStyleTag tag; + + for (size_t index = 0; index < npad::MAX_JOYSTICKS; index++) + { + PadState state {}; + + if (index == 0) + padInitializeDefault(&state); + else + padInitialize(&state, (HidNpadIdType)index); + + padUpdate(&state); + + if (padIsConnected(&state)) + { + tag = npad::GetStyleTag(&state); + + if (auto found = npad::styleTypes.Find(tag)) + info.push_back(*found); + } + } + + return info; +} + +Joystick* JoystickModule::AddJoystick(int index) +{ + if (index < 0 || index >= (int)this->AcquireCurrentJoystickIds().size()) + return nullptr; + + PadState state {}; + + padInitialize(&state, (HidNpadIdType)index); + padUpdate(&state); + + auto styleTag = npad::GetStyleTag(&state); + + std::string guid = love::guid::GetGamepadGUID(*npad::styleTypes.Find(styleTag)); + Joystick* joystick = nullptr; + bool reused = false; + + for (auto stick : this->joysticks) + { + if (!stick->IsConnected() && stick->GetGUID() == guid) + { + joystick = stick; + reused = true; + break; + } + } + + if (!joystick) + { + joystick = new Joystick((int)this->joysticks.size()); + this->joysticks.push_back(joystick); + } + + this->RemoveJoystick(joystick); + + if (!joystick->Open(index)) + return nullptr; + + // for (auto activeStick : this->active) + // { + // if (joystick->GetHandle() == activeStick->GetHandle()) + // { + // joystick->Close(); + + // if (!reused) + // { + // this->joysticks.remove(joystick); + // joystick->Release(); + // } + + // return activeStick; + // } + // } + + this->recentGUIDs[joystick->GetGUID()] = true; + this->active.push_back(joystick); + + return joystick; +} diff --git a/platform/hac/source/modules/keyboard_ext.cpp b/platform/hac/source/modules/keyboard_ext.cpp new file mode 100644 index 000000000..77caf8637 --- /dev/null +++ b/platform/hac/source/modules/keyboard_ext.cpp @@ -0,0 +1,37 @@ +#include +#include + +using namespace love; + +Keyboard::Keyboard() : + Keyboard<>(this->GetMaxEncodingLength(MAX_INPUT_LENGTH) + 1), + config {}, + showing(false) +{} + +void Keyboard::SetTextInput(const KeyboardOptions& options) +{ + if (R_FAILED(swkbdCreate(&this->config, 0))) + return; + + uint32_t maxLength = this->GetMaxEncodingLength(options.maxLength); + this->text = std::make_unique(maxLength); + + auto type = (SwkbdType)options.type; + swkbdConfigSetType(&this->config, type); + + swkbdConfigSetInitialCursorPos(&this->config, 1); + swkbdConfigSetBlurBackground(&this->config, 1); + + swkbdConfigSetPasswordFlag(&this->config, options.isPassword); + swkbdConfigSetStringLenMax(&this->config, maxLength); + swkbdConfigSetDicFlag(&this->config, 1); + + swkbdConfigSetGuideText(&this->config, options.hint.data()); + + this->showing = true; + if (R_SUCCEEDED(swkbdShow(&this->config, this->text.get(), maxLength))) + HID::Instance().SendTextInput(this->GetText()); + + swkbdClose(&this->config); +} diff --git a/platform/hac/source/modules/love_ext.cpp b/platform/hac/source/modules/love_ext.cpp new file mode 100644 index 000000000..067bb77ed --- /dev/null +++ b/platform/hac/source/modules/love_ext.cpp @@ -0,0 +1,20 @@ +#include "common/luax.hpp" +#include "modules/love/love.hpp" + +#include + +using namespace love; + +template<> +void love::PreInit() +{} + +template<> +void love::OnExit() +{} + +template<> +bool love::MainLoop(lua_State* L, int numArgs) +{ + return luax::Resume(L, numArgs) == LUA_YIELD; +} diff --git a/platform/hac/source/modules/system_ext.cpp b/platform/hac/source/modules/system_ext.cpp new file mode 100644 index 000000000..5c8f35f5f --- /dev/null +++ b/platform/hac/source/modules/system_ext.cpp @@ -0,0 +1,163 @@ +#include + +#include + +using namespace love; + +System::System() +{ + /* Check if there's a pre-selected user first */ + if (R_FAILED(accountGetPreselectedUser(&this->account))) + { + /* Create player selection UI settings */ + PselUiSettings settings; + pselUiCreate(&settings, PselUiMode_UserSelector); + + /* Ask for a user account */ + pselUiShow(&settings, &this->account); + } +} + +System<>::PowerState System::GetPowerInfo(uint8_t& percent) const +{ + uint32_t batteryPercent = 100; + PsmChargerType type = PsmChargerType_Unconnected; + + PowerState state = PowerState::POWER_UNKNOWN; + + psmGetBatteryChargePercentage(&batteryPercent); + psmGetChargerType(&type); + + state = (type > 0 && type != PsmChargerType_NotSupported) ? PowerState::POWER_CHARGING + : PowerState::POWER_BATTERY; + + if (percent == 100 && type != PsmChargerType_Unconnected) + state = PowerState::POWER_CHARGED; + + return state; +} + +System<>::NetworkState System::GetNetworkInfo(uint8_t& signal) const +{ + NetworkState state = NetworkState::NETWORK_UNKNOWN; + + uint32_t wifiStrength = 0; + Result res = nifmGetInternetConnectionStatus(NULL, &wifiStrength, NULL); + + signal = static_cast(wifiStrength); + state = R_SUCCEEDED(res) ? NetworkState::NETWORK_CONNECTED : NetworkState::NETWORK_DISCONNECTED; + + return state; +} + +int System::GetProcessorCount() +{ + return 0x04; +} + +std::string_view System::GetSystemTheme() +{ + if (!this->info.colorTheme.empty()) + return this->info.colorTheme; + + ColorSetId colorID; + + R_UNLESS(setsysGetColorSetId(&colorID), std::string {}); + + if (auto thm = System::themes.ReverseFind(colorID)) + this->info.colorTheme = *thm; + else + this->info.colorTheme = "Unknown"; + + return this->info.colorTheme; +} + +std::string_view System::GetPreferredLocales() +{ + if (!this->info.locale.empty()) + return this->info.locale; + + uint64_t languageCode = 0; + SetLanguage language; + + /* Get the System Language Code */ + R_UNLESS(setGetSystemLanguage(&languageCode), std::string {}); + + /* Convert the Language Code to SetLanguage */ + R_UNLESS(setMakeLanguage(languageCode, &language), std::string {}); + + if (auto found = System::languages.ReverseFind(language)) + this->info.locale = *found; + else + this->info.locale = "Unknown"; + + return this->info.locale; +} + +std::string_view System::GetVersion() +{ + if (!this->info.version.empty()) + return this->info.version; + + SetSysFirmwareVersion firmwareVersion; + + /* Get the System Firmware Version */ + R_UNLESS(setsysGetFirmwareVersion(&firmwareVersion), std::string {}); + + this->info.version = firmwareVersion.display_version; + + return this->info.version; +} + +std::string_view System::GetModel() +{ + if (!this->info.model.empty()) + return this->info.model; + + SetSysProductModel model = SetSysProductModel_Invalid; + + /* Get the Product Model */ + R_UNLESS(setsysGetProductModel(&model), std::string {}); + + if (auto name = System::models.ReverseFind(model)) + this->info.model = *name; + else + this->info.model = "Unknown"; + + return this->info.model; +} + +std::string_view System::GetFriendInfo() +{ + if (!this->info.friendCode.empty()) + return this->info.friendCode; + + FriendsUserSetting settings; + + R_UNLESS(friendsGetUserSetting(this->account, &settings), std::string {}); + + this->info.friendCode = settings.friend_code; + + return this->info.friendCode; +} + +std::string_view System::GetUsername() +{ + if (!this->info.username.empty()) + return this->info.username; + + AccountProfile profile {}; + AccountProfileBase base {}; + + /* Get the profile from the System's userID we selected */ + R_UNLESS(accountGetProfile(&profile, this->account), std::string {}); + + /* Get the base profile */ + R_UNLESS(accountProfileGet(&profile, NULL, &base), std::string {}); + + this->info.username = base.nickname; + + accountProfileClose(&profile); + + return this->info.username; +} diff --git a/platform/hac/source/modules/timer_ext.cpp b/platform/hac/source/modules/timer_ext.cpp new file mode 100644 index 000000000..f24f4c3b0 --- /dev/null +++ b/platform/hac/source/modules/timer_ext.cpp @@ -0,0 +1,56 @@ +#include + +#include +using namespace std::chrono_literals; + +#include + +using namespace love; + +double Timer::reference = 0; + +Timer::Timer() +{ + Timer::reference = armGetSystemTick(); + this->previousFpsUpdate = this->currentTime = Timer::GetTime(); +} + +double Timer::GetTime() +{ + uint64_t nanoseconds = armTicksToNs(armGetSystemTick() - Timer::reference); + return nanoseconds / Timer::NANOSECONDS_TO_SECONDS; +} + +void Timer::Sleep(double seconds) const +{ + if (seconds >= 0) + { + auto time = std::chrono::duration(seconds); + svcSleepThread(std::chrono::duration(time).count()); + } +} + +double Timer::Step() +{ + this->frames++; + + this->previousTime = this->currentTime; + this->currentTime = Timer::GetTime(); + + this->delta = this->currentTime - this->previousTime; + + if (this->delta < 0) + this->delta = 0; + + double timeSinceLast = (this->currentTime - this->previousFpsUpdate); + + if (timeSinceLast > this->fpsUpdateFrequency) + { + this->fps = int((this->frames / timeSinceLast) + 0.5); + this->averageDelta = timeSinceLast / frames; + this->previousFpsUpdate = this->currentTime; + this->frames = 0; + } + + return this->delta; +} diff --git a/platform/hac/source/modules/window_ext.cpp b/platform/hac/source/modules/window_ext.cpp new file mode 100644 index 000000000..8cc9f832f --- /dev/null +++ b/platform/hac/source/modules/window_ext.cpp @@ -0,0 +1,105 @@ +#include + +#include + +using namespace love; + +Window::Window() +{ + this->sleepAllowed = this->IsDisplaySleepEnabled(); +} + +Window::~Window() +{ + this->Close(); + this->SetDisplaySleepEnabled(this->sleepAllowed); + + this->graphics.Set(nullptr); +} + +bool Window::SetWindow(int width, int height, WindowSettings* settings) +{ + if (!this->graphics.Get()) + this->graphics.Set((Module::GetInstance>(Module::M_GRAPHICS))); + + bool setMode = false; + + /* handled internally */ + if (!this->IsOpen()) + { + if (!this->CreateWindowAndContext(0, 0, width, height)) + return false; + + setMode = true; + } + + if (this->graphics.Get()) + { + if (setMode) + this->graphics->SetMode(0, 0, width, height); + else + this->graphics->SetViewportSize(width, height); + } + + return true; +} + +bool Window::CreateWindowAndContext(int x, int y, int width, int height) +{ + this->open = true; + return true; +} + +void Window::GetWindow(int& width, int& height, WindowSettings& settings) +{ + width = 1280; + height = 720; + + if (appletGetOperationMode() == AppletOperationMode_Console) + { + width = 1920; + height = 1080; + } +} + +void Window::Close() +{ + this->open = false; +} + +bool Window::OnSizeChanged(int width, int height) +{ + return true; +} + +std::string_view Window::GetDisplayName(int displayIndex) const +{ + return "default"; +} + +std::vector::WindowSize> Window::GetFullscreenSizes(int displayIndex) +{ + return {}; +} + +void Window::GetDesktopDimensions(int displayIndex, int& width, int& height) +{} + +void Window::SetPosition(int x, int y, int displayIndex) +{} + +void Window::GetPosition(int& x, int& y, int& displayIndex) +{} + +void Window::SetDisplaySleepEnabled(bool enabled) +{ + appletSetAutoSleepDisabled(!enabled); +} + +bool Window::IsDisplaySleepEnabled() const +{ + bool sleepDisabled = false; + appletIsAutoSleepDisabled(&sleepDisabled); + + return !sleepDisabled; +} diff --git a/platform/hac/source/objects/joystick_ext.cpp b/platform/hac/source/objects/joystick_ext.cpp new file mode 100644 index 000000000..8c0a0ca74 --- /dev/null +++ b/platform/hac/source/objects/joystick_ext.cpp @@ -0,0 +1,413 @@ +#include +#include + +#include +#include + +#include + +using namespace love; + +#define Module() (Module::GetInstance>(Module::M_JOYSTICK)) + +// clang-format off +constexpr BidirectionalMap buttons = { + Joystick<>::GAMEPAD_BUTTON_A, HidNpadButton_A, + Joystick<>::GAMEPAD_BUTTON_B, HidNpadButton_B, + Joystick<>::GAMEPAD_BUTTON_X, HidNpadButton_X, + Joystick<>::GAMEPAD_BUTTON_Y, HidNpadButton_Y, + Joystick<>::GAMEPAD_BUTTON_BACK, HidNpadButton_Minus, + Joystick<>::GAMEPAD_BUTTON_START, HidNpadButton_Plus, + Joystick<>::GAMEPAD_BUTTON_LEFTSHOULDER, HidNpadButton_L, + Joystick<>::GAMEPAD_BUTTON_RIGHTSHOULDER, HidNpadButton_R, + Joystick<>::GAMEPAD_BUTTON_LEFTSTICK, HidNpadButton_StickL, + Joystick<>::GAMEPAD_BUTTON_RIGHTSTICK, HidNpadButton_StickR, + Joystick<>::GAMEPAD_BUTTON_DPAD_UP, HidNpadButton_Up, + Joystick<>::GAMEPAD_BUTTON_DPAD_DOWN, HidNpadButton_Down, + Joystick<>::GAMEPAD_BUTTON_DPAD_RIGHT, HidNpadButton_Right, + Joystick<>::GAMEPAD_BUTTON_DPAD_LEFT, HidNpadButton_Left +}; +// clang-format on + +Joystick::Joystick(int id) : state {}, buttonStates {} +{ + this->instanceId = -1; + this->id = id; +} + +Joystick::Joystick(int id, int index) : Joystick(id) +{ + this->Open(index); +} + +Joystick::~Joystick() +{ + this->Close(); +} + +bool Joystick::Open(int index) +{ + this->Close(); + + if (index == 0) + padInitializeDefault(&this->state); + else + padInitialize(&this->state, (HidNpadIdType)index); + + padUpdate(&this->state); + this->style = npad::GetStyleTag(&this->state); + + this->instanceId = index; + + if (this->style == npad::INVALID_STYLE_TAG) + return false; + + this->playerId = (HidNpadIdType)index; + + this->guid = guid::GetGamepadGUID(this->GetGamepadType()); + this->name = guid::GetGamepadName(this->GetGamepadType()); + + this->vibration = std::move(::Vibration(this->playerId, this->style)); + + return this->IsConnected(); +} + +void Joystick::Close() +{ + this->instanceId = -1; + this->playerId = npad::INVALID_PLAYER_ID; + this->state = PadState {}; + + this->vibration.SendValues(0, 0); +} + +guid::GamepadType Joystick::GetGamepadType() const +{ + return *npad::styleTypes.Find(this->style); +} + +void Joystick::GetDeviceInfo(int& vendor, int& product, int& version) +{ + guid::DeviceInfo info {}; + + if (!guid::GetDeviceInfo(this->GetGamepadType(), info)) + return; + + vendor = info.vendorId; + product = info.productId; + version = info.productVersion; +} + +bool Joystick::IsConnected() const +{ + return padIsConnected(&this->state); +} + +int Joystick::GetAxisCount() const +{ + if (!this->IsConnected()) + return 0; + + return guid::GetGamepadAxisCount(this->GetGamepadType()); +} + +int Joystick::GetButtonCount() const +{ + if (!this->IsConnected()) + return 0; + + return guid::GetGamepadButtonCount(this->GetGamepadType()); +} + +void Joystick::Update() +{ + padUpdate(&this->state); + + this->buttonStates.pressed = padGetButtonsDown(&this->state); + this->buttonStates.released = padGetButtonsUp(&this->state); +} + +bool Joystick::IsDown(JoystickInput& result) +{ + if (!this->IsConnected()) + return false; + + if (!this->buttonStates.pressed) + return false; + + HidNpadButton button = (HidNpadButton)-1; + + const auto entries = buttons.GetEntries(); + + for (size_t index = 0; index < entries.size(); index++) + { + button = (HidNpadButton)entries[index].second; + + if (entries[index].second == -1) + continue; + + if (button & this->buttonStates.pressed) + { + this->buttonStates.pressed ^= button; + result = { .type = InputType::INPUT_TYPE_BUTTON, + .button = entries[index].first, + .buttonNumber = (int)index }; + + return true; + } + } + + return false; +} + +bool Joystick::IsUp(JoystickInput& result) +{ + if (!this->IsConnected()) + return false; + + HidNpadButton button = (HidNpadButton)-1; + + if (!this->buttonStates.released) + return false; + + const auto entries = buttons.GetEntries(); + + for (size_t index = 0; index < entries.size(); index++) + { + button = (HidNpadButton)entries[index].second; + + if (entries[index].second == -1) + continue; + + if (button & this->buttonStates.released) + { + this->buttonStates.released ^= button; + result = { .type = InputType::INPUT_TYPE_BUTTON, + .button = entries[index].first, + .buttonNumber = (int)index }; + + return true; + } + } + + return false; +} + +/* helper functionality */ + +static float getStickPosition(PadState& state, bool horizontal, bool isLeft) +{ + auto stickState = padGetStickPos(&state, isLeft); + + float value = (horizontal) ? stickState.x : stickState.y; + return std::clamp(value / Joystick<>::JoystickMax, -1.0f, 1.0f); +} + +static float getTrigger(uint64_t held, HidNpadButton trigger) +{ + if (held & trigger) + return 1.0f; + + return 0.0f; +} + +float Joystick::GetAxis(int index) +{ + if (!this->IsConnected() || index < 0 || index >= this->GetAxisCount()) + return 0.0f; + + // Buttons and sticks need separate code for each + if (index < 6) + { + switch (this->GetGamepadType()) + { + case guid::GAMEPAD_TYPE_JOYCON_LEFT: + { + if (index / 4 == 0) // No left stick, yes right stick + return (index / 2) ? 0 : getStickPosition(this->state, index % 2, false); + else + return getTrigger(padGetButtons(&this->state), + (index % 2) ? HidNpadButton_LeftSR : HidNpadButton_LeftSL); + + break; + } + case guid::GAMEPAD_TYPE_JOYCON_RIGHT: + { + if (index / 4 == 0) // No left stick, yes right stick + return (index / 2) ? 0 : getStickPosition(this->state, index % 2, true); + else + return getTrigger(padGetButtons(&this->state), + (index % 2) ? HidNpadButton_RightSR : HidNpadButton_RightSL); + break; + } + default: + { + if (index / 4 == 0) + return getStickPosition(this->state, index % 2, (index / 2) == 0); + else + return getTrigger(padGetButtons(&this->state), + (index % 2) ? HidNpadButton_ZR : HidNpadButton_ZL); + + break; + } + } + } + + return 0.0f; +} + +std::vector Joystick::GetAxes() +{ + std::vector axes {}; + int count = this->GetAxisCount(); + + if (!this->IsConnected() || count <= 0) + return axes; + + axes.reserve(count); + + for (int index = 0; index < count; index++) + axes.push_back(this->GetAxis(index)); + + return axes; +} + +bool Joystick::IsDown(const std::vector& buttons) const +{ + if (!this->IsConnected()) + return false; + + int count = this->GetButtonCount(); + auto records = ::buttons.GetEntries(); + + for (int button : buttons) + { + if (button < 0 || button >= count) + continue; + + if (records[button].second == -1) + continue; + + if (padGetButtons(&this->state) && records[button].second) + return true; + } + + return false; +} + +float Joystick::GetGamepadAxis(GamepadAxis axis) +{ + if (!this->IsConnected()) + return 0.0f; + + return this->GetAxis(axis); +} + +bool Joystick::IsGamepadDown(const std::vector& buttons) const +{ + for (auto button : buttons) + { + if (auto gamepadButton = ::buttons.Find(button); + gamepadButton && (padGetButtons(&this->state) & (uint32_t)*gamepadButton)) + return true; + } + + return false; +} + +void Joystick::SetPlayerIndex(int index) +{ + if (!this->IsConnected()) + return; + + if (index < 0 || index > npad::MAX_JOYSTICKS) + return; + + if (R_SUCCEEDED(hidSwapNpadAssignment(this->playerId, (HidNpadIdType)index))) + this->playerId = (HidNpadIdType)index; +} + +bool Joystick::SetVibration(float left, float right, float duration) +{ + left = std::clamp(left, 0.0f, 1.0f); + right = std::clamp(right, 0.0f, 1.0f); + + uint32_t length = Vibration<>::MAX; + + if (left == 0.0f && right == 0.0f) + return this->SetVibration(); + + if (!this->IsConnected()) + { + this->SetVibration(); + return false; + } + + if (duration >= 0.0f) + length = std::min(duration, Vibration<>::MAX / 1000.0f); + + if (length == Vibration<>::HAPTYIC_INFINITY) + this->vibration.SetDuration(length); + else + this->vibration.SetDuration(Timer::GetTime() + length); + + bool success = this->vibration.SendValues(left, right); + + if (success) + Module()->AddVibration(&this->vibration); + + return success; +} + +bool Joystick::SetVibration() +{ + return this->vibration.SendValues(0.0f, 0.0f); +} + +void Joystick::GetVibration(float& left, float& right) +{ + this->vibration.GetValues(left, right); +} + +bool Joystick::HasSensor(Sensor::SensorType type) const +{ + return true; +} + +bool Joystick::IsSensorEnabled(Sensor::SensorType type) +{ + return this->sensors[type]; +} + +void Joystick::SetSensorEnabled(Sensor::SensorType type, bool enabled) +{ + if (this->sensors[type] && !enabled) + this->sensors[type] = nullptr; + else if (this->sensors[type] == nullptr && enabled) + { + SensorBase* sensor = nullptr; + + HidNpadIdType idType = this->playerId; + if (padIsHandheld(&this->state)) + idType = HidNpadIdType_Handheld; + + if (type == Sensor::SENSOR_ACCELEROMETER) + sensor = new Accelerometer(idType, this->style); + else if (type == Sensor::SENSOR_GYROSCOPE) + sensor = new Gyroscope(idType, this->style); + + sensor->SetEnabled(enabled); + this->sensors[type] = sensor; + } +} + +std::vector Joystick::GetSensorData(Sensor::SensorType type) +{ + if (!this->IsSensorEnabled(type)) + { + auto name = Sensor::sensorTypes.ReverseFind(type); + throw love::Exception("\"%s\" sensor is not enabled", *name); + } + + return this->sensors[type]->GetData(); +} diff --git a/platform/hac/source/objects/shader_ext.cpp b/platform/hac/source/objects/shader_ext.cpp new file mode 100644 index 000000000..e69ec65fd --- /dev/null +++ b/platform/hac/source/objects/shader_ext.cpp @@ -0,0 +1,222 @@ +#include + +#include + +using namespace love; + +#define SHADERS_DIR "romfs:/shaders/" + +#define DEFAULT_VERTEX_SHADER (SHADERS_DIR "transform_vsh.dksh") +#define DEFAULT_FRAGMENT_SHADER (SHADERS_DIR "color_fsh.dksh") +#define DEFAULT_TEXTURE_SHADER (SHADERS_DIR "texture_fsh.dksh") +#define DEFAULT_VIDEO_SHADER (SHADERS_DIR "video_fsh.dksh") + +Shader::Shader() : program {} +{} + +Shader::Shader(Data* vertex, Data* fragment) : program {} +{ + std::string error; + + if (!this->Validate(vertex, *this->program.vertex, error)) + throw love::Exception("Invalid vertex shader: %s", error.c_str()); + + error.clear(); + + if (!this->Validate(fragment, *this->program.fragment, error)) + throw love::Exception("Invalid fragment shader: %s", error.c_str()); +} + +Shader::~Shader() +{ + for (int i = 0; i < STANDARD_MAX_ENUM; i++) + { + if (this == Shader::defaults[i]) + Shader::defaults[i] = nullptr; + } + + if (current == this) + Shader::AttachDefault(STANDARD_DEFAULT); + + this->program.vertex->codeMemory.destroy(); + this->program.fragment->codeMemory.destroy(); +} + +void Shader::LoadDefaults(StandardShader type) +{ + const char* fragmentStage = nullptr; + + switch (type) + { + case STANDARD_DEFAULT: + { + fragmentStage = DEFAULT_FRAGMENT_SHADER; + break; + } + case STANDARD_TEXTURE: + { + fragmentStage = DEFAULT_TEXTURE_SHADER; + break; + } + case STANDARD_VIDEO: + { + fragmentStage = DEFAULT_VIDEO_SHADER; + break; + } + default: + break; + } + + std::string error; + if (!this->Validate(DEFAULT_VERTEX_SHADER, *this->program.vertex, error)) + throw love::Exception("Invalid vertex shader: %s", error.c_str()); + + error.clear(); + + if (!this->Validate(fragmentStage, *this->program.fragment, error)) + throw love::Exception("Invalid fragment shader: %s", error.c_str()); +} + +void Shader::AttachDefault(StandardShader type) +{ + Shader* defaultshader = Shader::defaults[type]; + + if (defaultshader == nullptr) + { + current = nullptr; + return; + } + + if (current != defaultshader) + defaultshader->Attach(); +} + +void Shader::Attach() +{ + if (Shader::current != this) + { + Renderer::Instance().UseProgram(this->program); + ++shaderSwitches; + + Shader::current = this; + } +} + +static bool loadFile(Shader::DekoStage& stage, const uint8_t* buffer, + const size_t size, std::string& error) +{ + Shader::DkshHeader header {}; + std::unique_ptr controlMemory; + + stage.codeMemory.destroy(); + + if (!buffer || size == 0) + { + error = "Buffer size is zero."; + return false; + } + + const auto headerSize = Shader::HEADER_SIZE; + std::memcpy(&header, buffer, headerSize); + + if (header.header_sz != headerSize) + { + error = "Invalid dksh header size: expected " + std::to_string(headerSize) + ", got " + + std::to_string(header.header_sz); + return false; + } + + try + { + controlMemory = std::make_unique(header.control_sz); + } + catch (std::bad_alloc&) + { + error = "Failed to allocate control memory."; + return false; + } + + std::memcpy(controlMemory.get(), buffer, header.control_sz); + + const auto poolId = Renderer::CODE; + auto& pool = Renderer::Instance().GetMemPool(poolId); + + stage.codeMemory = pool.allocate(header.code_sz, DK_SHADER_CODE_ALIGNMENT); + + if (!stage.codeMemory) + { + error = "Failed to allocate code memory."; + return false; + } + + std::memcpy(stage.codeMemory.getCpuAddr(), buffer + header.control_sz, header.code_sz); + + dk::ShaderMaker { stage.codeMemory.getMemBlock(), stage.codeMemory.getOffset() } + .setControl(controlMemory.get()) + .setProgramId(0) + .initialize(stage.shader); + + if (!stage.shader.isValid()) + { + error = "Shader code is invalid."; + stage.codeMemory.destroy(); + return false; + } + + return true; +} + +bool Shader::Validate(const char* filepath, DekoStage& stage, + std::string& error) const +{ + if (!filepath) + { + error = "No filepath provided."; + return false; + } + + FILE* file = std::fopen(filepath, "rb"); + + if (!file) + { + error = "File '" + std::string(filepath) + "' does not exist."; + std::fclose(file); + return false; + } + + std::fseek(file, 0, SEEK_END); + + size_t size = std::ftell(file); + + if (size == 0) + { + error = "File size is zero."; + std::fclose(file); + return false; + } + + std::rewind(file); + + std::unique_ptr buffer = nullptr; + + try + { + buffer = std::make_unique(size); + } + catch (std::bad_alloc&) + { + error = "Failed to allocate buffer."; + std::fclose(file); + return false; + } + + std::fread(buffer.get(), size, 1, file); + fclose(file); + + return loadFile(stage, buffer.get(), size, error); +} + +bool Shader::Validate(Data* data, DekoStage& stage, std::string& error) const +{ + return loadFile(stage, (uint8_t*)data->GetData(), data->GetSize(), error); +} diff --git a/platform/hac/source/objects/source_ext.cpp b/platform/hac/source/objects/source_ext.cpp new file mode 100644 index 000000000..8a7a78edb --- /dev/null +++ b/platform/hac/source/objects/source_ext.cpp @@ -0,0 +1,683 @@ +#include +#include + +#include +#include + +using namespace love; + +using DSP = love::DSP; + +template<> +Source::DataBuffer::DataBuffer(const void* data, size_t size) : size(size) +{ + this->buffer = (int16_t*)AudioMemory::Align(size, this->alignSize); + std::memcpy(this->buffer, (int16_t*)data, size); + + armDCacheFlush(this->buffer, this->size); +} + +template<> +Source::DataBuffer::~DataBuffer() +{ + AudioMemory::Free(this->buffer, this->alignSize); +} + +Source::Source(AudioPool* pool, SoundData* soundData) : + Source<>(TYPE_STATIC), + pool(pool) +{ + this->sampleRate = soundData->GetSampleRate(); + this->channels = soundData->GetChannelCount(); + this->bitDepth = soundData->GetBitDepth(); + this->samplesOffset = 0; + + std::fill_n(this->buffers, 2, WaveInfo {}); + + this->staticBuffer = std::make_shared(soundData->GetData(), soundData->GetSize()); +} + +Source::Source(AudioPool* pool, Decoder* decoder) : Source<>(TYPE_STREAM), pool(pool) +{ + this->decoder = decoder; + this->sampleRate = decoder->GetSampleRate(); + this->channels = decoder->GetChannelCount(); + this->bitDepth = decoder->GetBitDepth(); + this->bufferCount = MAX_BUFFERS; + this->samplesOffset = 0; + + std::fill_n(this->buffers, this->bufferCount, WaveInfo {}); + + for (auto& info : this->buffers) + { + info.buffer.data_pcm16 = (int16_t*)AudioMemory::Align(decoder->GetSize(), info.alignedSize); + info.buffer.state = AudioDriverWaveBufState_Done; + } +} + +Source::Source(AudioPool* pool, int sampleRate, int bitDepth, int channels, + int buffers) : + Source<>(TYPE_QUEUE), + pool(pool) +{ + this->sampleRate = sampleRate; + this->channels = channels; + this->bitDepth = bitDepth; + this->bufferCount = buffers; + this->samplesOffset = 0; + + if (buffers < 1 || buffers > Source::MAX_BUFFERS) + buffers = MAX_BUFFERS; + + std::fill_n(this->buffers, this->bufferCount, WaveInfo {}); + + for (auto& info : this->buffers) + info.buffer.state = AudioDriverWaveBufState_Done; +} + +Source::Source(const Source& other) : Source<>(other.sourceType), pool(other.pool) +{ + this->staticBuffer = other.staticBuffer; + this->decoder = nullptr; + this->sampleRate = other.sampleRate; + this->channels = other.channels; + this->bitDepth = other.bitDepth; + this->bufferCount = other.bufferCount; + this->samplesOffset = other.samplesOffset; + + if (this->sourceType == TYPE_STREAM) + { + if (other.decoder.Get()) + this->decoder.Set(other.decoder->Clone(), Acquire::NORETAIN); + } + + std::fill_n(this->buffers, this->bufferCount, WaveInfo {}); + + for (size_t index = 0; index < this->bufferCount; index++) + { + if (this->sourceType == TYPE_STREAM) + { + this->buffers[index].buffer.data_pcm16 = + (int16_t*)AudioMemory::Align(decoder->GetSize(), this->buffers[index].alignedSize); + } + + this->buffers[index].buffer.state = AudioDriverWaveBufState_Done; + } +} + +Source::~Source() +{ + this->Stop(); + + for (auto& info : this->buffers) + { + if (info.buffer.data_pcm16) + AudioMemory::Free(info.buffer.data_pcm16, info.alignedSize); + } +} + +Source* Source::Clone() +{ + return new Source(*this); +} + +bool Source::Play() +{ + uint8_t wasPlaying = false; + + { + auto lock = this->pool->Lock(); + if (!this->pool->AssignSource(this, this->channel, wasPlaying)) + return this->valid = false; + } + + if (!wasPlaying) + { + if (!(this->valid = this->PlayAtomic(this->buffers[0].buffer))) + return false; + + this->ResumeAtomic(); + + { + auto lock = this->pool->Lock(); + this->pool->AddSource(this, this->channel); + } + + return this->valid; + } + + this->ResumeAtomic(); + + return this->valid = true; +} + +void Source::Reset() +{ + ::DSP::Instance().ChannelReset(this->channel, this->channels, this->bitDepth, this->sampleRate); +} + +void Source::Stop() +{ + if (!this->valid) + return; + + auto lock = this->pool->Lock(); + this->pool->ReleaseSource(this); +} + +void Source::Pause() +{ + auto lock = this->pool->Lock(); + + if (this->pool->IsPlaying(this)) + this->PauseAtomic(); +} + +bool Source::IsPlaying() const +{ + return this->valid && !::DSP::Instance().IsChannelPaused(this->channel); +} + +bool Source::IsFinished() const +{ + if (!this->valid) + return false; + + if (this->sourceType == TYPE_STREAM && (this->IsLooping() || !this->decoder->IsFinished())) + return false; + + if (this->sourceType == TYPE_STATIC) + return this->buffers[0].buffer.state == AudioDriverWaveBufState_Done; + + return ::DSP::Instance().IsChannelPlaying(this->channel) == false; +} + +bool Source::Update() +{ + if (!this->valid) + return false; + + switch (this->sourceType) + { + case TYPE_STATIC: + return !this->IsFinished(); + case TYPE_STREAM: + { + if (this->IsFinished()) + return false; + + bool other = !this->current; + if (this->buffers[other].buffer.state == AudioDriverWaveBufState_Done) + { + int decoded = this->StreamAtomic(this->buffers[other].buffer, this->decoder.Get()); + + if (decoded == 0) + return false; + + ::DSP::Instance().ChannelAddBuffer(this->channel, &this->buffers[other].buffer); + this->samplesOffset += this->buffers[other].buffer.start_sample_offset; + + this->current = !this->current; + } + return true; + } + case TYPE_QUEUE: + break; + default: + break; + } + + return false; +} + +void Source::SetVolume(float volume) +{ + if (volume < this->GetMinVolume() || volume > this->GetMaxVolume()) + return; + + if (this->valid) + ::DSP::Instance().ChannelSetVolume(this->channel, volume); + + this->volume = volume; +} + +float Source::GetVolume() const +{ + if (this->valid) + return ::DSP::Instance().ChannelGetVolume(this->channel); + + return this->volume; +} + +void Source::Seek(double offset, Unit unit) +{ + int offsetSamples = 0; + double offsetSeconds = 0.0f; + + switch (unit) + { + case UNIT_SAMPLES: + { + offsetSamples = (int)offset; + offsetSeconds = offset / ((double)this->sampleRate / this->channels); + break; + } + case UNIT_SECONDS: + default: + { + offsetSeconds = offset; + offsetSamples = (int)(offset * sampleRate * this->channels); + } + } + + bool wasPlaying = this->IsPlaying(); + + switch (this->sourceType) + { + case TYPE_STATIC: + { + if (this->valid) + this->Stop(); + + this->samplesOffset = offsetSamples; + + if (wasPlaying) + this->Play(); + + break; + } + case TYPE_STREAM: + { + if (this->valid) + this->Stop(); + + this->decoder->Seek(offsetSeconds); + + if (wasPlaying) + this->Play(); + + break; + } + case TYPE_QUEUE: + { + /* todo */ + } + default: + break; + } + + if (wasPlaying && (this->sourceType == TYPE_STREAM && !this->IsPlaying())) + { + this->Stop(); + + if (this->IsLooping()) + this->Play(); + + return; + } + + this->samplesOffset = offsetSamples; +} + +double Source::Tell(Unit unit) +{ + auto lock = this->pool->Lock(); + + int offset = 0; + + if (this->valid) + { + if (this->sourceType == TYPE_STATIC) + offset += ::DSP::Instance().ChannelGetSampleOffset(this->channel); + else + offset = this->samplesOffset; + } + + if (unit == UNIT_SECONDS) + return offset / (double)sampleRate / this->channels; + + return offset; +} + +double Source::GetDuration(Unit unit) +{ + auto lock = this->pool->Lock(); + + switch (this->sourceType) + { + case TYPE_STATIC: + { + size_t size = this->staticBuffer->GetSize(); + size_t samples = (size / this->channels) / (this->bitDepth / 8); + + if (unit == UNIT_SAMPLES) + return (double)samples; + + return (double)samples / (double)sampleRate; + } + case TYPE_STREAM: + { + /* vorbisidec 1.2.1 uses ms, not sec, convert */ + double seconds = this->decoder->GetDuration() / 1000.0; + + if (unit == UNIT_SECONDS) + return seconds; + + return seconds * decoder->GetSampleRate(); + } + case TYPE_QUEUE: + { + /* todo */ + break; + } + default: + return 0.0; + } + + return 0.0; +} + +void Source::SetLooping(bool loop) +{ + if (this->sourceType == TYPE_QUEUE) + throw QueueLoopingException(); + + if (this->valid && this->sourceType == TYPE_STATIC) + this->buffers[0].buffer.is_looping = loop; + + this->looping = loop; +} + +/* todo */ +bool Source::Queue(void* data, size_t length, int sampleRate, int bitDepth, + int channels) +{ + if (this->sourceType != TYPE_QUEUE) + throw QueueTypeMismatchException(); + + if (sampleRate != this->sampleRate || bitDepth != this->bitDepth || channels != this->channels) + throw QueueFormatMismatchException(); + + if (length % (bitDepth / 8 * channels) != 0) + throw QueueMalformedLengthException(bitDepth / 8 * channels); + + if (length == 0) + return true; + + return true; +} + +int Source::GetFreeBufferCount() const +{ + if (this->sourceType == TYPE_STATIC) + return 0; + + size_t count = 0; + for (auto& info : this->buffers) + count += (info.buffer.state == AudioDriverWaveBufState_Done) ? 1 : 0; + + return count; +} + +void Source::PrepareAtomic() +{ + this->Reset(); + + switch (this->sourceType) + { + case TYPE_STATIC: + { + const auto size = this->staticBuffer->GetSize(); + + // clang-format off + this->buffers[0].buffer.size = size; + this->buffers[0].buffer.end_sample_offset = (int)((size / this->channels) / (this->bitDepth / 8)) - (this->samplesOffset / this->channels); + this->buffers[0].buffer.data_pcm16 = this->staticBuffer->GetBuffer() + (size_t)this->samplesOffset; + // clang-format on + + this->buffers[0].buffer.is_looping = this->looping; + + break; + } + case TYPE_STREAM: + { + if (this->StreamAtomic(this->buffers[0].buffer, this->decoder.Get()) == 0) + break; + + if (this->decoder->IsFinished()) + break; + + break; + } + case TYPE_QUEUE: + break; /* todo */ + default: + break; + } +} + +int Source::StreamAtomic(AudioDriverWaveBuf& buffer, Decoder* decoder) +{ + int decoded = std::max(decoder->Decode(), 0); + + if (decoded > 0) + { + std::memcpy(buffer.data_pcm16, (int16_t*)decoder->GetBuffer(), decoded); + + buffer.size = decoded; + buffer.end_sample_offset = (int)((decoded / this->channels) / (this->bitDepth / 8)); + + armDCacheFlush(buffer.data_pcm16, decoded); + } + + if (decoder->IsFinished() && this->IsLooping()) + decoder->Rewind(); + + return decoded; +} + +/* todo */ +void Source::TeardownAtomic() +{ + ::DSP::Instance().ChannelStop(this->channel); + + switch (this->sourceType) + { + case TYPE_STATIC: + break; + case TYPE_STREAM: + { + this->decoder->Rewind(); + + for (auto& info : this->buffers) + info.buffer.state = AudioDriverWaveBufState_Done; + + break; + } + case TYPE_QUEUE: + break; /* todo */ + default: + break; + } + + this->valid = false; + this->samplesOffset = 0; +} + +bool Source::PlayAtomic(AudioDriverWaveBuf& waveBuffer) +{ + this->PrepareAtomic(); + + ::DSP::Instance().ChannelAddBuffer(this->channel, &waveBuffer); + + if (this->sourceType != TYPE_STREAM) + this->samplesOffset = 0; + + if (this->sourceType == TYPE_STREAM) + this->valid = true; + + return true; +} + +void Source::StopAtomic() +{ + if (!this->valid) + return; + + this->TeardownAtomic(); +} + +void Source::PauseAtomic() +{ + if (this->valid) + ::DSP::Instance().ChannelPause(this->channel); +} + +void Source::ResumeAtomic() +{ + if (this->valid) + ::DSP::Instance().ChannelPause(this->channel, false); +} + +bool Source::Play(const std::vector& sources) +{ + if (sources.size() == 0) + return true; + + auto* pool = ((Source*)sources[0])->pool; + auto lock = pool->Lock(); + + std::vector wasPlaying(sources.size()); + std::vector channels(sources.size()); + + for (size_t index = 0; index < sources.size(); index++) + { + if (!pool->AssignSource((Source*)sources[index], channels[index], wasPlaying[index])) + { + for (size_t j = 0; j < index; j++) + { + if (!wasPlaying[j]) + pool->ReleaseSource((Source*)sources[index], false); + } + + return false; + } + } + + std::vector toPlay; + toPlay.reserve(sources.size()); + + for (size_t index = 0; index < sources.size(); index++) + { + if (wasPlaying[index] && sources[index]->IsPlaying()) + continue; + + if (!wasPlaying[index]) + { + auto* source = (Source*)sources[index]; + source->channel = channels[index]; + + source->PrepareAtomic(); + } + + toPlay.push_back(sources[index]); + } + + for (auto& _source : toPlay) + { + auto* source = (Source*)_source; + + if (source->sourceType != TYPE_STREAM) + source->samplesOffset = 0; + + if (!(_source->valid = _source->Play())) + return false; + + pool->AddSource(_source, source->channel); + } + + return true; +} + +void Source::Stop(const std::vector& sources) +{ + if (sources.size() == 0) + return; + + auto* pool = ((Source*)sources[0])->pool; + auto lock = pool->Lock(); + + std::vector toStop; + toStop.reserve(sources.size()); + + for (auto& _source : sources) + { + auto* source = (Source*)_source; + + if (source->valid) + toStop.push_back(source); + } + + for (auto& _source : toStop) + { + auto* source = (Source*)_source; + + if (source->valid) + source->TeardownAtomic(); + + pool->ReleaseSource(source, false); + } +} + +void Source::Pause(const std::vector& sources) +{ + if (sources.size() == 0) + return; + + auto lock = ((Source*)sources[0])->pool->Lock(); + + for (auto& _source : sources) + { + auto* source = (Source*)_source; + + if (source->valid) + source->PauseAtomic(); + } +} + +/* todo */ +std::vector*> Source::Pause(AudioPool* pool) +{ + std::vector sources; + + { + auto lock = pool->Lock(); + sources = pool->GetPlayingSources(); + + auto end = std::remove_if(sources.begin(), sources.end(), + [](Source* source) { return !source->IsPlaying(); }); + + sources.erase(end, sources.end()); + } + + Source::Pause(sources); + + return sources; +} + +void Source::Stop(AudioPool* pool) +{ + std::vector sources; + + { + auto lock = pool->Lock(); + sources = pool->GetPlayingSources(); + } + + Source::Stop(sources); +} + +int Source::GetChannelCount() const +{ + return this->channels; +} diff --git a/platform/hac/source/objects/texture_ext.cpp b/platform/hac/source/objects/texture_ext.cpp new file mode 100644 index 000000000..b6b717ca5 --- /dev/null +++ b/platform/hac/source/objects/texture_ext.cpp @@ -0,0 +1,421 @@ +#include + +#include + +#include + +using namespace love; + +static void createFramebufferObject(dk::Image& image, CMemPool::Handle& memory, + dk::ImageDescriptor& descriptor, uint32_t width, + uint32_t height) + +{ + auto device = Renderer::Instance().GetDevice(); + + dk::ImageLayout layout; + dk::ImageLayoutMaker { device } + .setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression) + .setFormat(DkImageFormat_RGBA8_Unorm) + .setDimensions(width, height) + .initialize(layout); + + auto poolId = Renderer::IMAGE; + auto& scratch = Renderer::Instance().GetMemPool(poolId); + + memory = scratch.allocate(layout.getSize(), layout.getAlignment()); + image.initialize(layout, memory.getMemBlock(), memory.getOffset()); + + dk::ImageView imageView { image }; + descriptor.initialize(imageView); +} + +static void dkImageRectFromRect(const Rect& rectangle, DkImageRect& out) +{ + out.x = (uint32_t)rectangle.x; + out.y = (uint32_t)rectangle.y; + out.z = (uint32_t)0; + + out.width = (uint32_t)rectangle.w; + out.height = (uint32_t)rectangle.h; + out.depth = (uint32_t)1; +} + +static void createTextureObject(dk::Image& image, CMemPool::Handle& memory, + dk::ImageDescriptor& descriptor, const void* data, size_t realSize, + PixelFormat format, Rect rectangle) +{ + if (data == nullptr) + throw love::Exception("No data for Texture."); + + std::optional imageFormat; + if (!(imageFormat = Renderer::pixelFormats.Find(format))) + throw love::Exception("Invalid image format."); + + auto size = love::GetPixelFormatSliceSize(format, rectangle.w, rectangle.h); + + if (realSize != 0) + size = realSize; + + if (size <= 0) + throw love::Exception("Invalid PixelFormat slice size."); + + auto poolId = Renderer::DATA; + auto& scratchPool = Renderer::Instance().GetMemPool(poolId); + + auto tempImageMemory = scratchPool.allocate(size, DK_IMAGE_LINEAR_STRIDE_ALIGNMENT); + + if (!tempImageMemory) + throw love::Exception("Failed to allocate temporary memory."); + + /* copy the data into the temp image memory */ + std::memcpy(tempImageMemory.getCpuAddr(), data, size); + + auto device = Renderer::Instance().GetDevice(); + auto tempCmdBuf = dk::CmdBufMaker { device }.create(); + + /* make some memory for the command buffer */ + auto tempCmdMemory = scratchPool.allocate(DK_MEMBLOCK_ALIGNMENT); + + const auto& memBlock = tempCmdMemory.getMemBlock(); + const auto offset = tempCmdMemory.getOffset(); + const auto memSize = tempCmdMemory.getSize(); + + /* add the memory to the command buffer */ + tempCmdBuf.addMemory(memBlock, offset, memSize); + + /* set the image layout */ + dk::ImageLayout layout; + dk::ImageLayoutMaker { device } + .setFlags(0) + .setFormat(*imageFormat) + .setDimensions(rectangle.w, rectangle.h) + .initialize(layout); + + poolId = Renderer::IMAGE; + auto& imagePool = Renderer::Instance().GetMemPool(poolId); + + memory = imagePool.allocate(layout.getSize(), layout.getAlignment()); + + if (!memory) + throw love::Exception("Failed to allocate image memory handle"); + + image.initialize(layout, memory.getMemBlock(), memory.getOffset()); + descriptor.initialize(image); + + dk::ImageView view { image }; + + DkImageRect dkRectangle {}; + dkImageRectFromRect(rectangle, dkRectangle); + + tempCmdBuf.copyBufferToImage({ tempImageMemory.getGpuAddr() }, view, dkRectangle); + + const auto queueId = Renderer::QUEUE_IMAGES; + auto transferQueue = Renderer::Instance().GetQueue(queueId); + + transferQueue.submitCommands(tempCmdBuf.finishList()); + transferQueue.waitIdle(); + + tempCmdMemory.destroy(); + tempImageMemory.destroy(); +} + +Texture::Texture(const Graphics* graphics, const Settings& settings, + const Slices* data) : + Texture(settings, data), + textureHandle(0), + image {}, + descriptor {}, + memory {} +{ + this->format = graphics->GetSizedFormat(format, this->renderTarget, this->readable); + + if (this->mipmapMode == MIPMAPS_AUTO && this->IsCompressed()) + this->mipmapMode = MIPMAPS_MANUAL; + + if (this->mipmapMode != MIPMAPS_NONE) + this->mipmapCount = + Texture<>::GetTotalMipmapCount(this->pixelWidth, this->pixelHeight, this->depth); + + bool invalidDimensions = this->pixelWidth <= 0 || this->pixelHeight <= 0; + if (invalidDimensions || this->layers <= 0 || this->depth <= 0) + throw love::Exception("Texture dimensions must be greater than zero."); + + if (this->textureType != TEXTURE_2D && this->requestedMSAA > 1) + throw love::Exception("MSAA is only supported for 2D textures."); + + if (!this->renderTarget && this->requestedMSAA > 1) + throw love::Exception("MSAA is only supported with render target textures."); + + bool isDepthStencilFormat = love::IsPixelFormatDepthStencil(this->format); + if (this->readable && isDepthStencilFormat && settings.msaa > 1) + throw love::Exception("Readable depth/stencil textures with MSAA are not supported."); + + if ((!this->readable || settings.msaa > 1) && this->mipmapMode != MIPMAPS_NONE) + throw love::Exception("Non-readable and MSAA textures cannot have mipmaps."); + + if (!this->readable && this->textureType != TEXTURE_2D) + throw love::Exception("Non-readable pixel formats are only supported for 2D textures."); + + if (this->IsCompressed() && this->renderTarget) + throw love::Exception("Compressed textures cannot be render targets."); + + this->state = graphics->GetDefaultSamplerState(); + + if (this->GetMipmapCount() == 1) + this->state.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE; + + Quad::Viewport view { 0, 0, (double)this->width, (double)this->height }; + this->quad.Set(new Quad(view, this->width, this->height), Acquire::NORETAIN); + + ++textureCount; + + if (data != nullptr) + this->slices = *data; + + this->LoadVolatile(); + + this->slices.Clear(); +} + +Texture::~Texture() +{ + this->UnloadVolatile(); +} + +bool Texture::LoadVolatile() +{ + if (this->IsReadable()) + this->CreateTexture(); + + int64_t memorySize = 0; + + for (int mipmap = 0; mipmap < this->GetMipmapCount(); mipmap++) + { + const auto width = this->GetPixelWidth(mipmap); + const auto height = this->GetPixelHeight(mipmap); + + const auto faceCount = this->textureType == TEXTURE_CUBE ? 6 : 1; + const auto slices = this->GetDepth(mipmap) * this->layers * faceCount; + + memorySize += love::GetPixelFormatSliceSize(this->format, width, height) * slices; + } + + this->SetGraphicsMemorySize(memorySize); + + return true; +} + +void Texture::CreateTexture() +{ + Texture<>::CreateTexture(); + bool hasData = this->slices.Get(0, 0) != nullptr; + + int _width = this->pixelWidth; + int _height = this->pixelHeight; + + Rect rectangle { 0, 0, _width, _height }; + + if (this->IsRenderTarget()) + { + bool clear = !hasData; + + auto& instance = Renderer::Instance(); + + createFramebufferObject(this->image, this->memory, this->descriptor, _width, _height); + + instance.BindFramebuffer(this); + instance.Clear({ 0, 0, 0, 0 }); + instance.BindFramebuffer(nullptr); + } + else + { + if (!hasData) + { + std::vector empty(_width * _height, 0); + createTextureObject(this->image, this->memory, this->descriptor, empty.data(), + empty.size(), this->format, rectangle); + } + else + { + createTextureObject(this->image, this->memory, this->descriptor, + this->slices.Get(0, 0)->GetData(), + this->slices.Get(0, 0)->GetSize(), this->format, rectangle); + } + } + + Renderer::Instance().Register(this, this->textureHandle); + this->SetSamplerState(this->state); +} + +void Texture::SetSamplerState(const SamplerState& state) +{ + Texture<>::SetSamplerState(state); + + this->state.magFilter = this->state.minFilter = SamplerState::FILTER_NEAREST; + + if (this->state.mipmapFilter == SamplerState::MIPMAP_FILTER_LINEAR) + this->state.mipmapFilter = SamplerState::MIPMAP_FILTER_NEAREST; + + Renderer::Instance().SetSamplerState(this, this->state); +} + +void Texture::UnloadVolatile() +{ + Renderer::Instance().UnRegister(this); + this->memory.destroy(); +} + +void Texture::ReplacePixels(ImageDataBase* data, int slice, int mipmap, int x, int y, + bool reloadMipmaps) +{ + if (!this->IsReadable()) + throw love::Exception("replacePixels can only be called on readable Textures."); + + if (this->GetMSAA() > 1) + throw love::Exception("replacePixels cannot be called on an MSAA Texture."); + + auto* graphics = Module::GetInstance>(Module::M_GRAPHICS); + + if (graphics != nullptr && graphics->IsRenderTargetActive(this)) + throw love::Exception( + "replacePixels cannot be called on this Texture while it's an active render target."); + + if (!this->memory) + return; + + if (data->GetFormat() != this->GetPixelFormat()) + throw love::Exception("Pixel formats must match."); + + if (mipmap < 0 || mipmap >= this->GetMipmapCount()) + throw love::Exception("Invalid texture mipmap index %d.", mipmap + 1); + + const bool isCubeType = this->textureType == TEXTURE_CUBE; + const bool isVolumeType = this->textureType == TEXTURE_VOLUME; + const bool isArrayType = this->textureType == TEXTURE_2D_ARRAY; + + if (slice < 0 || (isCubeType && slice >= 6) || + (isVolumeType && slice >= this->GetDepth(mipmap)) || + (isArrayType && slice >= this->GetLayerCount())) + { + throw love::Exception("Invalid texture slice index %d", slice + 1); + } + + Rect rectangle = { x, y, data->GetWidth(), data->GetHeight() }; + + int mipWidth = this->GetPixelWidth(mipmap); + int mipHeight = this->GetPixelHeight(mipmap); + + if (rectangle.x < 0 || rectangle.y < 0 || rectangle.w <= 0 || rectangle.h <= 0 || + (rectangle.x + rectangle.w) > mipWidth || (rectangle.y + rectangle.h) > mipHeight) + { + throw love::Exception( + "Invalid rectangle dimensions (x = %d, y = %d, w = %d, h = %d) for %dx%d Texture.", + rectangle.x, rectangle.y, rectangle.w, rectangle.h, mipWidth, mipHeight); + } + + if (love::IsPixelFormatCompressed(data->GetFormat()) && + (rectangle.x != 0 || rectangle.y != 0 || rectangle.w != mipWidth || + rectangle.h != mipHeight)) + { + const auto& info = love::GetPixelFormatInfo(data->GetFormat()); + + int blockWidth = info.blockWidth; + int blockHeight = info.blockHeight; + + if (rectangle.x % blockWidth != 0 || rectangle.y % blockHeight != 0 || + rectangle.w % blockWidth != 0 || rectangle.h % blockHeight != 0) + { + const char* name = love::GetPixelFormatName(data->GetFormat()); + + throw love::Exception( + "Compressed texture format %s only supports replacing a sub-rectangle with offset " + "and dimensions that are a multiple of %d x %d.", + name, blockWidth, blockHeight); + } + } + + this->ReplacePixels(data->GetData(), data->GetSize(), slice, mipmap, rectangle, false); +} + +void Texture::ReplacePixels(const void* data, size_t size, int slice, int mipmap, + const Rect& rectangle, bool reloadMipmaps) +{ + if (!data || size == 0) + throw love::Exception("No data for replacement."); + + auto poolId = Renderer::DATA; + auto& scratchPool = Renderer::Instance().GetMemPool(poolId); + + auto tempImageMemory = scratchPool.allocate(size, DK_IMAGE_LINEAR_STRIDE_ALIGNMENT); + + if (!tempImageMemory) + throw love::Exception("Failed to allocate temporary memory."); + + /* copy the data into the temp image memory */ + std::memcpy(tempImageMemory.getCpuAddr(), data, size); + + auto device = Renderer::Instance().GetDevice(); + auto tempCmdBuf = dk::CmdBufMaker { device }.create(); + + /* make some memory for the command buffer */ + auto tempCmdMemory = scratchPool.allocate(DK_MEMBLOCK_ALIGNMENT); + + const auto& memBlock = tempCmdMemory.getMemBlock(); + const auto offset = tempCmdMemory.getOffset(); + const auto memSize = tempCmdMemory.getSize(); + + /* add the memory to the command buffer */ + tempCmdBuf.addMemory(memBlock, offset, memSize); + + dk::ImageView view { this->image }; + + DkImageRect dkRectangle {}; + dkImageRectFromRect(rectangle, dkRectangle); + + tempCmdBuf.copyBufferToImage({ tempImageMemory.getGpuAddr() }, view, dkRectangle); + + const auto queueId = Renderer::QUEUE_IMAGES; + auto transferQueue = Renderer::Instance().GetQueue(queueId); + + transferQueue.submitCommands(tempCmdBuf.finishList()); + transferQueue.waitIdle(); + + tempCmdMemory.destroy(); + tempImageMemory.destroy(); +} + +void Texture::Draw(Graphics& graphics, + const Matrix4& matrix) +{ + this->Draw(graphics, this->quad, matrix); +} + +void Texture::Draw(Graphics& graphics, Quad* quad, + const Matrix4& matrix) +{ + if (!this->readable) + throw love::Exception("Textures with non-readable formats cannot be drawn."); + + if (this->renderTarget && graphics.IsRenderTargetActive(this)) + throw love::Exception("Cannot render a Texture to itself."); + + const auto& stateTransform = graphics.GetTransform(); + bool is2D = stateTransform.IsAffine2DTransform(); + + Matrix4 transform(stateTransform, matrix); + + DrawCommand command(4); + command.shader = Shader<>::STANDARD_TEXTURE; + command.format = vertex::CommonFormat::TEXTURE; + command.type = vertex::PRIMITIVE_QUADS; + command.handles = { this }; + + if (is2D) + transform.TransformXY(command.Positions().get(), quad->GetVertexPositions(), command.count); + + const auto* textureCoords = quad->GetVertexTextureCoords(); + command.FillVertices(graphics.GetColor(), textureCoords); + + Renderer::Instance().Render(command); +} diff --git a/platform/hac/source/objects/wrap_imagedata_ext.cpp b/platform/hac/source/objects/wrap_imagedata_ext.cpp new file mode 100644 index 000000000..6e65e6871 --- /dev/null +++ b/platform/hac/source/objects/wrap_imagedata_ext.cpp @@ -0,0 +1,81 @@ +#include + +using namespace love; +using ImageData = love::ImageData; + +int Wrap_ImageData::__MapPixelUnsafe(lua_State* L) +{ + auto* self = Wrap_ImageData::CheckImageData(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + + int sourceX = lua_tonumber(L, 3); + int sourceY = lua_tonumber(L, 4); + int width = lua_tonumber(L, 5); + int height = lua_tonumber(L, 6); + + if (!(self->Inside(sourceX, sourceY) && + self->Inside(sourceX + width - 1, sourceY + height - 1))) + { + return luaL_error(L, "Invalid rectangle dimensions."); + } + + int imageWidth = self->GetWidth(); + + PixelFormat format = self->GetFormat(); + int components = love::GetPixelFormatColorComponents(format); + + auto pixelSetFunction = self->GetPixelSetFunction(); + auto pixelGetFunction = self->GetPixelGetFunction(); + + uint8_t* data = (uint8_t*)self->GetData(); + size_t pixelSize = self->GetPixelSize(); + + for (int y = sourceY; y < sourceY + height; y++) + { + for (int x = sourceX; x < sourceX + width; x++) + { + auto pixelData = (::ImageData::Pixel*)(data + (y * imageWidth + x) * pixelSize); + + Color color {}; + pixelGetFunction(pixelData, color); + + lua_pushvalue(L, 2); + + lua_pushnumber(L, x); + lua_pushnumber(L, y); + + lua_pushnumber(L, color.r); + lua_pushnumber(L, color.g); + lua_pushnumber(L, color.b); + lua_pushnumber(L, color.a); + + lua_call(L, 6, 4); + + color.r = luaL_checknumber(L, -4); + + if (components > 1) + color.g = luaL_checknumber(L, -3); + + if (components > 2) + color.b = luaL_checknumber(L, -2); + + if (components > 3) + color.a = luaL_checknumber(L, -1); + + pixelSetFunction(color, pixelData); + + lua_pop(L, 4); + } + } + + return 0; +} + +// clang-format off +static constexpr luaL_Reg functions[] = +{ + { "_mapPixelUnsafe", Wrap_ImageData::__MapPixelUnsafe } +}; +// clang-forma ton + +std::span Wrap_ImageData::extensions = functions; diff --git a/platform/hac/source/objects/wrap_joystick_ext.cpp b/platform/hac/source/objects/wrap_joystick_ext.cpp new file mode 100644 index 000000000..0721f5439 --- /dev/null +++ b/platform/hac/source/objects/wrap_joystick_ext.cpp @@ -0,0 +1,89 @@ +#include +#include + +using namespace love; + +#define instance() (Module::GetInstance>(Module::M_JOYSTICK)) + +static HidNpadIdType luaL_checkIdType(lua_State* L, int index) +{ + int playerId = luaL_checknumber(L, index); + + if (playerId < 0 || playerId >= npad::MAX_JOYSTICKS) + luaL_error(L, "invalid player id %d", playerId); + + return (HidNpadIdType)playerId; +} + +int Wrap_Joystick::Split(lua_State* L) +{ + auto* self = Wrap_Joystick::CheckJoystick(L, 1); + + HidNpadIdType id = (HidNpadIdType)self->GetPlayerIndex(); + + HidNpadIdType left = luaL_checkIdType(L, 2); + HidNpadIdType right = luaL_checkIdType(L, 3); + + bool setOutput = true; + + /* set the left */ + auto device = HidNpadJoyDeviceType_Left; + auto result = hidSetNpadJoyAssignmentModeSingleWithDestination(id, device, &setOutput, &left); + + if (R_FAILED(result)) + return 0; + + /* set the right */ + device = HidNpadJoyDeviceType_Right; + hidSetNpadJoyAssignmentModeSingleWithDestination(right, device, &setOutput, &right); + + return 0; +} + +int Wrap_Joystick::Join(lua_State* L) +{ + auto* self = Wrap_Joystick::CheckJoystick(L, 1); + Joystick* other = nullptr; + + if (luax::IsType(L, 2, Joystick<>::type)) + other = Wrap_Joystick::CheckJoystick(L, 2); + else if (lua_isnumber(L, 2)) + other = instance()->GetJoystickFromId(luaL_checknumber(L, 2) - 1); + else + luax::TypeError(L, 2, "Joystick or number"); + + HidNpadIdType first = (HidNpadIdType)self->GetPlayerIndex(); + HidNpadIdType second; + + if (other != nullptr) + second = (HidNpadIdType)other->GetPlayerIndex(); + else + return luaL_error(L, "Joystick does not exist."); + + /* mark left to be merged */ + auto result = hidSetNpadJoyAssignmentModeDual(first); + + if (R_FAILED(result)) + return 0; + + /* mark right to merge */ + result = hidSetNpadJoyAssignmentModeDual(second); + + if (R_FAILED(result)) + return 0; + + /* merge both of the controllers now -- they become the id of "first" */ + result = hidMergeSingleJoyAsDualJoy(first, second); + + return 0; +} + +// clang-format off +static constexpr luaL_Reg functions[] = +{ + { "split", Wrap_Joystick::Split }, + { "join", Wrap_Joystick::Join } +}; +// clang-format on + +std::span Wrap_Joystick::extension; diff --git a/platform/hac/source/runtime.cpp b/platform/hac/source/runtime.cpp new file mode 100644 index 000000000..e74963fd6 --- /dev/null +++ b/platform/hac/source/runtime.cpp @@ -0,0 +1,106 @@ +#include + +#include + +#include +#include + +extern "C" +{ + static void tryInit(std::function serviceInit, love::AbortCode code) + { + if (!serviceInit || love::g_EarlyExit) + return; + + love::ResultCode result; + if ((result = serviceInit()); result.Success()) + return; + + std::optional header; + if ((header = love::abortTypes.Find(code)) && code != love::ABORT_APPLET) + { + static char message[0x100] {}; + int32_t info = R_DESCRIPTION(result); + + snprintf(message, sizeof(message), love::ABORT_FORMAT, *header, (int32_t)result, info); + + ErrorApplicationConfig config {}; + errorApplicationCreate(&config, message, nullptr); + errorApplicationSetNumber(&config, result); + errorApplicationShow(&config); + } + else + { + ErrorSystemConfig config {}; + errorSystemCreate(&config, love::TITLE_TAKEOVER_ERROR, nullptr); + errorSystemShow(&config); + } + + love::g_EarlyExit = true; + } + + void userAppInit() + { + { + const auto appletType = appletGetAppletType(); + const bool isValid = (appletType == AppletType_Application || + appletType == AppletType_SystemApplication); + + tryInit(std::bind_front([&]() { return (isValid) ? 0 : -1; }), love::ABORT_APPLET); + } + + romfsInit(); + + /* system fonts */ + tryInit(std::bind_front(plInitialize, PlServiceType_User), love::ABORT_PLU); + + /* wireless */ + tryInit(std::bind_front(nifmInitialize, NifmServiceType_User), love::ABORT_NIFM); + + /* accounts */ + tryInit(std::bind_front(accountInitialize, AccountServiceType_Application), + love::ABORT_ACC); + + /* settings */ + tryInit(std::bind_front(setInitialize), love::ABORT_SET); + + /* system settings */ + tryInit(std::bind_front(setsysInitialize), love::ABORT_SETSYS); + + /* battery charge and state */ + tryInit(std::bind_front(psmInitialize), love::ABORT_PSM); + + /* wireless */ + tryInit(std::bind_front(socketInitializeDefault), love::ABORT_SOCKETS); + + /* friends */ + tryInit(std::bind_front(friendsInitialize, FriendsServiceType_Viewer), love::ABORT_FRIENDV); + + /* initialize controllers -- 4 players max */ + padConfigureInput(0x04, HidNpadStyleSet_NpadStandard); + + /* initialize touch screen */ + hidInitializeTouchScreen(); + } + + void userAppExit() + { + friendsExit(); + + socketExit(); + + psmExit(); + + setsysExit(); + + setExit(); + + accountExit(); + + nifmExit(); + + romfsExit(); + + plExit(); + } +} diff --git a/platform/switch/source/deko3d/CIntrusiveTree.cpp b/platform/hac/source/utilities/driver/CIntrusiveTree.cpp similarity index 99% rename from platform/switch/source/deko3d/CIntrusiveTree.cpp rename to platform/hac/source/utilities/driver/CIntrusiveTree.cpp index 667ac808e..b3d634b57 100644 --- a/platform/switch/source/deko3d/CIntrusiveTree.cpp +++ b/platform/hac/source/utilities/driver/CIntrusiveTree.cpp @@ -2,7 +2,7 @@ ** Sample Framework for deko3d Applications ** CIntrusiveTree.cpp: Intrusive red-black tree helper class */ -#include "deko3d/CIntrusiveTree.h" +#include // This red-black tree implementation is mostly based on mtheall's work, // which can be found here: diff --git a/platform/switch/source/deko3d/CMemPool.cpp b/platform/hac/source/utilities/driver/CMemPool.cpp similarity index 99% rename from platform/switch/source/deko3d/CMemPool.cpp rename to platform/hac/source/utilities/driver/CMemPool.cpp index 489dfbf80..41851c4b5 100644 --- a/platform/switch/source/deko3d/CMemPool.cpp +++ b/platform/hac/source/utilities/driver/CMemPool.cpp @@ -2,7 +2,7 @@ ** Sample Framework for deko3d Applications ** CMemPool.cpp: Pooled dynamic memory allocation manager class */ -#include "deko3d/CMemPool.h" +#include inline auto CMemPool::_newSlice() -> Slice* { diff --git a/platform/hac/source/utilities/driver/dsp_ext.cpp b/platform/hac/source/utilities/driver/dsp_ext.cpp new file mode 100644 index 000000000..8e2368f83 --- /dev/null +++ b/platform/hac/source/utilities/driver/dsp_ext.cpp @@ -0,0 +1,188 @@ +#include + +#include +#include + +using namespace love; + +static constexpr AudioRendererConfig config = { + .output_rate = AudioRendererOutputRate_48kHz, + .num_voices = 24, + .num_effects = 0, + .num_sinks = 1, + .num_mix_objs = 1, + .num_mix_buffers = 2, +}; + +static constexpr uint8_t sinkChannels[2] = { 0, 1 }; + +DSP::DSP() : channelReset(false), driver {} +{} + +void DSP::Initialize() +{ + if (bool result = AudioMemory::InitMemPool(); !result) + throw love::Exception("Failed to create audio memory pool!"); + + if (Result result = audrenInitialize(&config); R_FAILED(result)) + throw love::Exception("Failed to initialize audren: %x", result); + + if (Result result = audrvCreate(&this->driver, &config, 2); R_FAILED(result)) + throw love::Exception("Failed to create audio driver: %x", result); + + this->initialized = true; + + int poolId = audrvMemPoolAdd(&this->driver, AudioMemory::POOL_BASE, AudioMemory::POOL_SIZE); + + if (poolId == -1) + throw love::Exception("Failed to add memory pool!"); + + bool attached = audrvMemPoolAttach(&this->driver, poolId); + + if (!attached) + throw love::Exception("Failed to attach memory pool!"); + + int sinkId = audrvDeviceSinkAdd(&this->driver, AUDREN_DEFAULT_DEVICE_NAME, 2, sinkChannels); + + if (sinkId == -1) + throw love::Exception("Failed to add sink to driver!"); + + if (Result result = audrvUpdate(&this->driver); R_FAILED(result)) + throw love::Exception("Failed to update audio driver: %x", result); + + if (Result result = audrenStartAudioRenderer(); R_FAILED(result)) + throw love::Exception("Failed to start audio renderer: %x", result); +} + +DSP::~DSP() +{ + if (!this->initialized) + return; + + audrvClose(&this->driver); + audrenExit(); +} + +void DSP::Update() +{ + { + std::unique_lock lock(this->mutex); + + audrvUpdate(&this->driver); + } + + audrenWaitFrame(); +} + +void DSP::SetMasterVolume(float volume) +{ + std::unique_lock lock(this->mutex); + + for (int mix = 0; mix < 2; mix++) + audrvMixSetVolume(&this->driver, mix, volume); +} + +float DSP::GetMasterVolume() const +{ + return this->driver.in_mixes[0].volume; +} + +bool DSP::ChannelReset(size_t channel, int channels, int bitDepth, int sampleRate) +{ + std::unique_lock lock(this->mutex); + + PcmFormat format = PcmFormat_Invalid; + if (!(format = (PcmFormat)DSP::GetFormat(bitDepth, channels))) + return false; + + this->channelReset = audrvVoiceInit(&this->driver, channel, channels, format, sampleRate); + + if (this->channelReset) + { + audrvVoiceSetDestinationMix(&this->driver, channel, AUDREN_FINAL_MIX_ID); + audrvVoiceSetMixFactor(&this->driver, channel, 1.0f, 0, 0); + + if (channels == 2) + audrvVoiceSetMixFactor(&this->driver, channel, 1.0f, 0, 1); + } + + return this->channelReset; +} + +void DSP::ChannelSetVolume(size_t channel, float volume) +{ + std::unique_lock lock(this->mutex); + + audrvVoiceSetVolume(&this->driver, channel, volume); +} + +float DSP::ChannelGetVolume(size_t channel) const +{ + return this->driver.in_voices[channel].volume; +} + +size_t DSP::ChannelGetSampleOffset(size_t channel) +{ + std::unique_lock lock(this->mutex); + + return audrvVoiceGetPlayedSampleCount(&this->driver, channel); +} + +bool DSP::ChannelAddBuffer(size_t channel, AudioDriverWaveBuf* buffer) +{ + if (this->channelReset) + { + std::unique_lock lock(this->mutex); + + bool success = audrvVoiceAddWaveBuf(&this->driver, channel, buffer); + + if (success) + audrvVoiceStart(&this->driver, channel); + + return success; + } + + return false; +} + +void DSP::ChannelPause(size_t channel, bool pause) +{ + std::unique_lock lock(this->mutex); + + audrvVoiceSetPaused(&this->driver, channel, pause); +} + +bool DSP::IsChannelPaused(size_t channel) +{ + std::unique_lock lock(this->mutex); + + return audrvVoiceIsPaused(&this->driver, channel); +} + +bool DSP::IsChannelPlaying(size_t channel) +{ + std::unique_lock lock(this->mutex); + + return audrvVoiceIsPlaying(&this->driver, channel); +} + +void DSP::ChannelStop(size_t channel) +{ + std::unique_lock lock(this->mutex); + + audrvVoiceStop(&this->driver, channel); + audrvVoiceDrop(&this->driver, channel); +} + +int8_t DSP::GetFormat(int bitDepth, int channels) +{ + /* invalid bitDepth */ + if (bitDepth != 8 && bitDepth != 16) + return PcmFormat_Invalid; + + /* invalid channel count */ + if (channels < 0 || channels > 2) + return PcmFormat_Invalid; + + return *DSP::audioFormats.Find(bitDepth); +} diff --git a/platform/hac/source/utilities/driver/dsp_mem.cpp b/platform/hac/source/utilities/driver/dsp_mem.cpp new file mode 100644 index 000000000..19f489bee --- /dev/null +++ b/platform/hac/source/utilities/driver/dsp_mem.cpp @@ -0,0 +1,175 @@ +#include + +/* Audio Pool */ +void* AudioMemory::POOL_BASE = nullptr; +AudioMemory::MemoryPool AudioMemory::audioPool; + +bool AudioMemory::InitMemPool() +{ + if (!AudioMemory::POOL_BASE) + AudioMemory::POOL_BASE = memalign(AUDREN_MEMPOOL_ALIGNMENT, AudioMemory::POOL_SIZE); + + if (!AudioMemory::POOL_BASE) + return false; + + return true; +} + +bool AudioMemory::Initialize() +{ + auto block = MemoryBlock::Create((uint8_t*)AudioMemory::POOL_BASE, AudioMemory::POOL_SIZE); + + if (!block) + return false; + + audioPool.AddBlock(block); + return true; +} + +void* AudioMemory::Align(size_t size, size_t& aligned) +{ + if (!audioPool.Ready() && !Initialize()) + return nullptr; + + MemoryChunk chunk; + if (!audioPool.Allocate(chunk, size)) + return nullptr; + + aligned = chunk.size; + return chunk.address; +} + +void AudioMemory::Free(const void* chunk, const size_t size) +{ + audioPool.DeAllocate((uint8_t*)chunk, size); +} + +/* Audio Pool's Memory Pool */ + +void AudioMemory::MemoryPool::CoalesceRight(MemoryBlock* block) +{ + auto current = block->base + block->size; + auto next = block->next; + + for (auto n = next; n; n = next) + { + next = n->next; + + if (n->base != current) + break; + + block->size += n->size; + current += n->size; + + AudioMemory::MemoryPool::DeleteBlock(n); + } +} + +bool AudioMemory::MemoryPool::Allocate(MemoryChunk& chunk, size_t size) +{ + size_t alignMask = (AUDREN_BUFFER_ALIGNMENT - 1); + + if (size && alignMask) + { + if (size > UINTPTR_MAX - alignMask) + return false; + + size = (size + alignMask) & ~alignMask; + } + + for (auto block = first; block; block = block->next) + { + auto address = block->base; + + size_t waste = (size_t)address & alignMask; + + if (waste > 0) + waste = alignMask + 1 - waste; + + if (waste > block->size) + continue; + + address += waste; + + size_t blockSize = block->size - waste; + + if (blockSize < size) + continue; + + // found space + chunk.address = address; + chunk.size = size; + + if (!waste) + { + block->base += size; + block->size -= size; + + if (!block->size) + AudioMemory::MemoryPool::DeleteBlock(block); + } + else + { + auto nextAddress = address + size; + auto nextSize = blockSize - size; + + block->size = waste; + + if (nextSize) + { + auto next = MemoryBlock::Create(nextAddress, nextSize); + + if (next) + AudioMemory::MemoryPool::InsertAfter(block, next); + else + chunk.size += nextSize; + } + } + + return true; + } + + return false; +} + +void AudioMemory::MemoryPool::DeAllocate(uint8_t* chunkAddress, size_t chunkSize) +{ + bool done = false; + + for (auto block = first; !done && block; block = block->next) + { + auto address = block->base; + if (address > chunkAddress) + { + if ((chunkAddress + chunkSize) == address) + { + block->base = chunkAddress; + block->size += chunkSize; + } + else + { + auto chunk = MemoryBlock::Create(chunkAddress, chunkSize); + + if (chunk) + AudioMemory::MemoryPool::InsertBefore(block, chunk); + } + + done = true; + } + else if ((block->base + block->size) == chunkAddress) + { + block->size += chunkSize; + AudioMemory::MemoryPool::CoalesceRight(block); + + done = true; + } + } + + if (!done) + { + auto block = MemoryBlock::Create(chunkAddress, chunkSize); + + if (block) + AudioMemory::MemoryPool::AddBlock(block); + } +} diff --git a/platform/hac/source/utilities/driver/hid_ext.cpp b/platform/hac/source/utilities/driver/hid_ext.cpp new file mode 100644 index 000000000..17b426d0e --- /dev/null +++ b/platform/hac/source/utilities/driver/hid_ext.cpp @@ -0,0 +1,214 @@ +#include +#include + +#include + +using namespace love; + +#define Module() Module::GetInstance>(Module::M_JOYSTICK) + +HID::HID() : + touchState {}, + stateTouches {}, + oldStateTouches {}, + previousTouchCount(0), + previousJoystickState {}, + previousGamepadTypes {} +{ + for (size_t index = 0; index < npad::MAX_JOYSTICKS; index++) + { + HidNpadIdType id = (HidNpadIdType)index; + hidAcquireNpadStyleSetUpdateEventHandle(id, &this->statusEvents[index], true); + } + + this->previousJoystickState = Module()->AcquireCurrentJoystickIds(); + this->previousGamepadTypes = Module()->GetActiveStyleSets(); +} + +HID::~HID() +{ + for (auto event : this->statusEvents) + eventClose(&event); +} + +void HID::CheckFocus() +{ + bool focused = (appletGetFocusState() == AppletFocusState_InFocus); + + uint32_t message = 0; + Result res = appletGetMessage(&message); + + if (R_SUCCEEDED(res)) + { + bool shouldClose = !appletProcessMessage(message); + + if (shouldClose) + { + this->SendQuit(); + return; + } + + auto* graphics = Module::GetInstance>(Module::M_GRAPHICS); + + switch (message) + { + case AppletMessage_FocusStateChanged: + { + + bool oldFocus = focused; + AppletFocusState state = appletGetFocusState(); + + focused = (state == AppletFocusState_InFocus); + + this->SendFocus(focused); + + if (graphics) + graphics->SetActive(focused); + + if (focused == oldFocus) + break; + + if (focused) + appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend); + else + appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleepNotify); + break; + } + case AppletMessage_OperationModeChanged: + { + std::pair size; + // ::deko3d::Instance().OnOperationMode(size); + + this->SendResize(size.first, size.second); + + break; + } + default: + break; + } + } +} + +void HID::_Poll() +{ + this->CheckFocus(); + + hidGetTouchScreenStates(&this->touchState, 1); + int touchCount = this->touchState.count; + + if (touchCount > 0) + { + for (int id = 0; id < touchCount; id++) + { + auto touchType = SUBTYPE_TOUCHPRESS; + + if (touchCount > this->previousTouchCount && id >= this->previousTouchCount) + { + this->stateTouches[id] = this->touchState.touches[id]; + this->oldStateTouches[id] = this->stateTouches[id]; + + touchType = SUBTYPE_TOUCHPRESS; + } + else + { + this->oldStateTouches[id] = this->stateTouches[id]; + this->stateTouches[id] = this->touchState.touches[id]; + + touchType = SUBTYPE_TOUCHMOVED; + } + + float x = this->stateTouches[id].x, y = this->stateTouches[id].y; + + int32_t dx = this->stateTouches[id].x - this->oldStateTouches[id].x; + int32_t dy = (int32_t)this->stateTouches[id].y - this->oldStateTouches[id].y; + + this->SendTouchEvent(touchType, id, x, y, dx, dy, 1.0f); + + if (touchType == SUBTYPE_TOUCHMOVED && !dx && !dy) + { + this->events.pop_back(); + continue; + } + } + } + + if (touchCount < this->previousTouchCount) + { + for (int id = 0; id < this->previousTouchCount; ++id) + { + float x = this->stateTouches[id].x, y = this->stateTouches[id].y; + this->SendTouchEvent(SUBTYPE_TOUCHRELEASE, id, x, y, 0.0f, 0.0f, 0.0f); + } + } + + this->previousTouchCount = touchCount; + + if (!Module()) + return; + + for (auto event : this->statusEvents) + { + /* a controller was updated! */ + if (R_SUCCEEDED(eventWait(&event, 0))) + { + auto types = Module()->GetActiveStyleSets(); + + this->previousGamepadTypes = types; + + auto ids = Module()->AcquireCurrentJoystickIds(); + + /* joystick removed */ + if (ids.size() < this->previousJoystickState.size()) + { + for (auto id : this->previousJoystickState) + { + if (std::find(ids.begin(), ids.end(), id) == ids.end()) + { + this->SendJoystickStatus((size_t)id, false); + break; + } + } + } /* joystick added */ + else if (ids.size() > this->previousJoystickState.size()) + this->SendJoystickStatus((size_t)ids.back(), true); + + this->previousJoystickState = ids; + } + } + + const auto joystickCount = Module()->GetJoystickCount(); + + for (size_t index = 0; index < joystickCount; index++) + { + auto* joystick = Module()->GetJoystick(index); + + if (joystick) + { + joystick->Update(); + Joystick<>::JoystickInput input {}; + + for (int index = 0; index < Sensor::SENSOR_MAX_ENUM; index++) + { + const auto sensor = (Sensor::SensorType)index; + + if (joystick->IsSensorEnabled(sensor)) + this->SendJoystickSensorUpdated(index, sensor, joystick->GetSensorData(sensor)); + } + + if (joystick->IsDown(input)) + this->SendGamepadPress(true, joystick->GetID(), input.button, input.buttonNumber); + + if (joystick->IsUp(input)) + this->SendGamepadPress(false, joystick->GetID(), input.button, input.buttonNumber); + + /* handle trigger and stick inputs */ + for (size_t index = 0; index < Joystick<>::GAMEPAD_AXIS_MAX_ENUM; index++) + { + const auto axis = (Joystick<>::GamepadAxis)index; + const auto value = joystick->GetAxis(index); + + this->SendGamepadAxis(joystick->GetID(), axis, index, value); + } + } + } +} diff --git a/platform/hac/source/utilities/driver/renderer_ext.cpp b/platform/hac/source/utilities/driver/renderer_ext.cpp new file mode 100644 index 000000000..ee1bdb418 --- /dev/null +++ b/platform/hac/source/utilities/driver/renderer_ext.cpp @@ -0,0 +1,561 @@ +#include + +#include + +#include + +using namespace love; + +Renderer::Renderer() : + transform {}, + firstVertex(0), + data(nullptr), + device(dk::DeviceMaker {}.setFlags(DkDeviceFlags_DepthMinusOneToOne).create()), + mainQueue(dk::QueueMaker { this->device }.setFlags(DkQueueFlags_Graphics).create()), + textureQueue(dk::QueueMaker { this->device }.setFlags(DkQueueFlags_Graphics).create()), + commandBuffer {}, + swapchain {}, + pools { .image = CMemPool(this->device, GPU_USE_FLAGS, GPU_POOL_SIZE), + .data = CMemPool(this->device, CPU_USE_FLAGS, CPU_POOL_SIZE), + .code = CMemPool(this->device, SHADER_USE_FLAGS, SHADER_POOL_SIZE) }, + state {}, + framebuffers {}, + descriptors {} +{ + /* create our Transform information */ + this->uniformBuffer = this->pools.data.allocate(TRANSFORM_SIZE, DK_UNIFORM_BUF_ALIGNMENT); + this->transform.modelView = glm::mat4(1.0f); + + /* allocate descriptors */ + this->descriptors.image.allocate(this->pools.data); + this->descriptors.sampler.allocate(this->pools.data); + + /* allocate our rings */ + this->commands.allocate(this->pools.data, COMMAND_SIZE); + this->vertices.allocate(this->pools.data, VERTEX_COMMAND_SIZE / 2); + + /* set up the device depth state */ + this->state.depthStencil.setDepthTestEnable(true); + this->state.depthStencil.setDepthWriteEnable(true); + this->state.depthStencil.setDepthCompareOp(DkCompareOp_Always); + + this->state.rasterizer.setCullMode(DkFace_None); + this->state.rasterizer.setFrontFace(DkFrontFace_CCW); + + /* set up the device color state */ + this->state.color.setBlendEnable(0, true); + this->commandBuffer = dk::CmdBufMaker { this->device }.create(); + + this->EnsureInFrame(); + + this->descriptors.image.bindForImages(this->commandBuffer); + this->descriptors.sampler.bindForSamplers(this->commandBuffer); +} + +Renderer::~Renderer() +{ + this->DestroyFramebuffers(); + this->uniformBuffer.destroy(); +} + +Renderer::Info Renderer::GetRendererInfo() +{ + if (this->info.filled) + return this->info; + + this->info.device = Renderer::RENDERER_DEVICE; + this->info.name = Renderer::RENDERER_NAME; + this->info.vendor = Renderer::RENDERER_VENDOR; + this->info.version = Renderer::RENDERER_VERSION; + + this->info.filled = true; + + return this->info; +} + +CMemPool& Renderer::GetMemPool(MemPoolType type) +{ + switch (type) + { + case MemPoolType::DATA: + return this->pools.data; + case MemPoolType::CODE: + return this->pools.code; + case MemPoolType::IMAGE: + default: + return this->pools.image; + } +} + +dk::Queue Renderer::GetQueue(QueueType type) +{ + switch (type) + { + case QueueType::QUEUE_IMAGES: + return this->textureQueue; + case QueueType::QUEUE_MAIN: + default: + return this->mainQueue; + } +} + +CMemPool::Handle Renderer::Allocate(MemPoolType type, size_t size, uint32_t alignment) +{ + auto& pool = this->GetMemPool(type); + return pool.allocate(size, alignment); +} + +bool Renderer::IsHandheldMode() const +{ + return appletGetOperationMode() == AppletOperationMode_Handheld; +} + +void Renderer::CreateFramebuffers() +{ + int width = 1280; + int height = 720; + + if (!this->IsHandheldMode()) + { + width = 1920; + height = 1080; + } + + /* create layout for the depth buffer */ + dk::ImageLayoutMaker { this->device } + .setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression) + .setFormat(DkImageFormat_Z24S8) + .setDimensions(width, height) + .initialize(this->framebuffers.depthLayout); + + // /* create the depth buffer */ + // const auto poolId = MemPoolType::IMAGE; + + const auto depthLayoutSize = this->framebuffers.depthLayout.getSize(); + const auto depthLayoutAlign = this->framebuffers.depthLayout.getAlignment(); + + this->framebuffers.depthMemory = this->Allocate(IMAGE, depthLayoutSize, depthLayoutAlign); + + const auto& depthMemBlock = this->framebuffers.depthMemory.getMemBlock(); + auto depthMemOffset = this->framebuffers.depthMemory.getOffset(); + + this->framebuffers.depthImage.initialize(this->framebuffers.depthLayout, depthMemBlock, + depthMemOffset); + + /* initialize framebuffer layout */ + dk::ImageLayoutMaker { this->device } + .setFlags(RENDERTARGET_USE_FLAGS) + .setFormat(DkImageFormat_RGBA8_Unorm) + .setDimensions(width, height) + .initialize(this->framebuffers.layout); + + const auto size = this->framebuffers.layout.getSize(); + const auto alignment = this->framebuffers.layout.getAlignment(); + + for (size_t index = 0; index < MAX_RENDERTARGETS; index++) + { + this->framebuffers.memory[index] = this->Allocate(IMAGE, size, alignment); + + const auto memory = this->framebuffers.memory[index]; + + const auto& memBlock = memory.getMemBlock(); + auto offset = memory.getOffset(); + + this->framebuffers.images[index].initialize(this->framebuffers.layout, memBlock, offset); + this->rendertargets[index] = &this->framebuffers.images[index]; + } + + this->swapchain = + dk::SwapchainMaker { this->device, nwindowGetDefault(), this->rendertargets }.create(); + + this->viewport = { 0, 0, width, height }; + love::SetScreenSize(width, height); +} + +void Renderer::DestroyFramebuffers() +{ + if (!this->swapchain) + return; + + this->mainQueue.waitIdle(); + this->textureQueue.waitIdle(); + + this->commandBuffer.clear(); + + this->swapchain.destroy(); + + for (auto& framebuffer : this->framebuffers.memory) + framebuffer.destroy(); + + this->framebuffers.depthMemory.destroy(); +} + +void Renderer::EnsureInFrame() +{ + if (!this->inFrame) + { + this->firstVertex = 0; + this->descriptors.dirty = false; + + this->commands.begin(this->commandBuffer); + this->inFrame = true; + } +} + +void Renderer::Clear(const Color& color) +{ + this->EnsureInFrame(); + this->commandBuffer.clearColor(0, DkColorMask_RGBA, color.r, color.g, color.b, color.a); +} + +void Renderer::ClearDepthStencil(int stencil, uint8_t mask, double depth) +{ + this->EnsureInFrame(); + this->commandBuffer.clearDepthStencil(true, depth, mask, stencil); +} + +void Renderer::SetBlendColor(const Color& color) +{ + this->EnsureInFrame(); + this->commandBuffer.setBlendConst(color.r, color.g, color.b, color.a); +} + +void Renderer::BindFramebuffer(Texture* texture) +{ + if (!this->swapchain) + return; + + this->EnsureInFrame(); + + if (this->framebuffers.slot < 0) + this->framebuffers.slot = this->mainQueue.acquireImage(this->swapchain); + + if (this->framebuffers.dirty) + this->commandBuffer.barrier(DkBarrier_Fragments, 0); + + dk::ImageView target { this->framebuffers.images[this->framebuffers.slot] }; + dk::ImageView depth { this->framebuffers.depthImage }; + + if (texture != nullptr && texture->IsRenderTarget()) + { + auto* _texture = (Texture*)texture; + target = { _texture->GetImage() }; + + this->SetViewport({ 0, 0, texture->GetPixelWidth(), texture->GetPixelHeight() }); + this->framebuffers.dirty = true; + } + else + { + if (this->framebuffers.dirty) + { + this->SetViewport(this->viewport); + this->framebuffers.dirty = false; + } + } + + this->commandBuffer.bindRenderTargets(&target, &depth); + + this->commandBuffer.pushConstants(this->uniformBuffer.getGpuAddr(), + this->uniformBuffer.getSize(), 0, TRANSFORM_SIZE, + &this->transform); + + /* begin vertex ring */ + auto ring = this->vertices.begin(); + this->data = (vertex::Vertex*)ring.first; + + this->commandBuffer.bindRasterizerState(this->state.rasterizer); + // this->commandBuffer.bindDepthStencilState(this->state.depthStencil); + this->commandBuffer.bindColorState(this->state.color); + this->commandBuffer.bindColorWriteState(this->state.colorWrite); + this->commandBuffer.bindBlendStates(0, this->state.blend); + + this->commandBuffer.bindVtxBuffer(0, ring.second, this->vertices.getSize()); +} + +void Renderer::Present() +{ + if (!this->swapchain) + return; + + if (this->inFrame) + { + this->vertices.end(); + + this->mainQueue.submitCommands(this->commands.end(this->commandBuffer)); + this->mainQueue.presentImage(this->swapchain, this->framebuffers.slot); + + this->inFrame = false; + } + + this->framebuffers.slot = -1; +} + +void Renderer::Register(Texture* texture, DkResHandle& handle) +{ + this->EnsureInFrame(); + + const auto index = this->allocator.Allocate(); + + handle = dkMakeTextureHandle(index, index); +} + +void Renderer::UnRegister(Texture* texture) +{ + const auto handle = texture->GetHandle(); + this->allocator.DeAllocate(handle); +} + +void Renderer::CheckDescriptorsDirty(const std::vector& handles) +{ + if (this->descriptors.dirty) + { + this->commandBuffer.barrier(DkBarrier_Primitives, DkInvalidateFlags_Descriptors); + this->descriptors.dirty = false; + } + + this->commandBuffer.bindTextures(DkStage_Fragment, 0, handles); +} + +void Renderer::SetAttributes(const vertex::attributes::Attribs& attributes) +{ + this->commandBuffer.bindVtxAttribState(attributes.attributeState); + this->commandBuffer.bindVtxBufferState(attributes.bufferState); +} + +bool Renderer::Render(const DrawCommand& command) +{ + if (command.count > (this->vertices.getSize() - this->firstVertex)) + return false; + + { + Shader::defaults[command.shader]->Attach(); + + vertex::attributes::Attribs attributes {}; + vertex::attributes::GetAttributes(command.format, attributes); + + this->SetAttributes(attributes); + } + + std::optional primitive; + if (!(primitive = primitiveModes.Find(command.type))) + return false; + + if (!command.handles.empty()) + { + std::vector handles {}; + for (size_t index = 0; index < command.handles.size(); index++) + handles.push_back(command.handles[index]->GetHandle()); + + this->CheckDescriptorsDirty(handles); + } + + std::memcpy(this->data + this->firstVertex, command.vertices.get(), command.size); + this->commandBuffer.draw(*primitive, command.count, 1, this->firstVertex, 0); + + this->firstVertex += command.count; + ++drawCalls; + + return true; +} + +void Renderer::UseProgram(Shader::Program program) +{ + this->EnsureInFrame(); + + // clang-format off + this->commandBuffer.bindShaders(DkStageFlag_GraphicsMask, { &program.vertex->shader, &program.fragment->shader }); + this->commandBuffer.bindUniformBuffer(DkStage_Vertex, 0, this->uniformBuffer.getGpuAddr(), this->uniformBuffer.getSize()); + // clang-format on +} + +void Renderer::SetBlendMode(const RenderState::BlendState& state) +{ + std::optional opRGB; + if (!(opRGB = Renderer::blendEquations.Find(state.operationRGB))) + return; + + std::optional opAlpha; + if (!(opAlpha = Renderer::blendEquations.Find(state.operationA))) + return; + + std::optional srcColor; + if (!(srcColor = Renderer::blendFactors.Find(state.srcFactorRGB))) + return; + + std::optional dstColor; + if (!(dstColor = Renderer::blendFactors.Find(state.dstFactorRGB))) + return; + + std::optional srcAlpha; + if (!(srcAlpha = Renderer::blendFactors.Find(state.srcFactorA))) + return; + + std::optional dstAlpha; + if (!(dstAlpha = Renderer::blendFactors.Find(state.dstFactorA))) + return; + + this->state.blend.setColorBlendOp(*opRGB); + this->state.blend.setAlphaBlendOp(*opAlpha); + + // Blend factors + this->state.blend.setSrcColorBlendFactor(*srcColor); + this->state.blend.setSrcAlphaBlendFactor(*srcAlpha); + + this->state.blend.setDstColorBlendFactor(*dstColor); + this->state.blend.setDstAlphaBlendFactor(*dstAlpha); +} + +void Renderer::SetColorMask(const RenderState::ColorMask& mask) +{ + auto writeMask = uint32_t(DkColorMask_R * mask.r + DkColorMask_G * mask.g + + DkColorMask_B * mask.b + DkColorMask_A * mask.a); + + this->state.colorWrite.setMask(0, writeMask); +} + +void Renderer::SetSamplerState(Texture* texture, SamplerState& state) +{ + this->EnsureInFrame(); + + auto index = -1; + + if (!this->allocator.Find(texture->GetHandle(), index)) + index = this->allocator.Allocate(); + + auto& descriptor = texture->GetDescriptor(); + auto& sampler = texture->GetSampler(); + + /* filter modes */ + + std::optional mag; + if (!(mag = Renderer::filterModes.Find(state.magFilter))) + return; + + std::optional min; + if (!(min = Renderer::filterModes.Find(state.minFilter))) + return; + + std::optional mipmap; + if (!(mipmap = Renderer::mipmapFilterModes.Find(state.mipmapFilter))) + return; + + sampler.setFilter(*min, *mag, *mipmap); + + /* wrapping modes */ + + std::optional wrapU; + if (!(wrapU = Renderer::wrapModes.Find(state.wrapU))) + return; + + std::optional wrapV; + if (!(wrapV = Renderer::wrapModes.Find(state.wrapV))) + return; + + std::optional wrapW; + if (!(wrapW = Renderer::wrapModes.Find(state.wrapW))) + return; + + sampler.setWrapMode(*wrapU, *wrapV, *wrapW); + + this->descriptors.image.update(this->commandBuffer, index, descriptor); + + dk::SamplerDescriptor samplerDescriptor {}; + samplerDescriptor.initialize(sampler); + + this->descriptors.sampler.update(this->commandBuffer, index, samplerDescriptor); + + this->descriptors.dirty = true; +} + +void Renderer::SetStencil(RenderState::CompareMode mode, int value) +{ + bool enabled = (mode == RenderState::COMPARE_ALWAYS) ? false : true; + + std::optional operation; + if (!(operation = Renderer::compareModes.Find(mode))) + return; + + this->state.depthStencil.setDepthTestEnable(enabled); + this->state.depthStencil.setDepthCompareOp(*operation); +} + +void Renderer::SetMeshCullMode(vertex::CullMode mode) +{ + std::optional cullFace; + if (!(cullFace = Renderer::cullModes.Find(mode))) + return; + + this->state.rasterizer.setCullMode(*cullFace); +} + +void Renderer::SetVertexWinding(vertex::Winding winding) +{ + std::optional frontFace; + if (!(frontFace = Renderer::windingModes.Find(winding))) + return; + + this->state.rasterizer.setFrontFace(*frontFace); +} + +void Renderer::SetLineWidth(float width) +{ + this->EnsureInFrame(); + this->commandBuffer.setLineWidth(width); +} + +void Renderer::SetLineStyle(RenderState::LineStyle style) +{ + // bool smooth = (style == RenderState::LINE_SMOOTH); + // this->state.rasterizer.setPolygonSmoothEnable(smooth); +} + +void Renderer::SetPointSize(float size) +{ + this->EnsureInFrame(); + this->commandBuffer.setPointSize(size); +} + +static void dkScissorFromRect(const Rect& scissor, DkScissor& out) +{ + out.x = (uint32_t)scissor.x; + out.y = (uint32_t)scissor.y; + out.width = (uint32_t)scissor.w; + out.height = (uint32_t)scissor.h; +} + +void Renderer::SetScissor(const Rect& scissor, bool canvasActive) +{ + this->EnsureInFrame(); + DkScissor dkScissor {}; + + if (scissor == Rect::EMPTY) + dkScissorFromRect(this->viewport, dkScissor); + else + dkScissorFromRect(scissor, dkScissor); + + this->commandBuffer.setScissors(0, { dkScissor }); +} + +static void dkViewportFromRect(const Rect& viewport, DkViewport& out) +{ + out.x = (float)viewport.x; + out.y = (float)viewport.y; + out.width = (float)viewport.w; + out.height = (float)viewport.h; + + out.near = Renderer<>::Z_NEAR; + out.far = Renderer<>::Z_FAR; +} + +void Renderer::SetViewport(const Rect& viewport) +{ + this->EnsureInFrame(); + + DkViewport dkViewport {}; + dkViewportFromRect(viewport, dkViewport); + + this->commandBuffer.setViewports(0, { dkViewport }); + + const auto ortho = glm::ortho(0.0f, (float)viewport.w, (float)viewport.h, 0.0f, Z_NEAR, Z_FAR); + this->transform.projection = ortho; +} diff --git a/platform/hac/source/utilities/haptics/vibration_ext.cpp b/platform/hac/source/utilities/haptics/vibration_ext.cpp new file mode 100644 index 000000000..cb9f7d6f7 --- /dev/null +++ b/platform/hac/source/utilities/haptics/vibration_ext.cpp @@ -0,0 +1,71 @@ +#include + +using namespace love; + +Vibration::Vibration(HidNpadIdType playerId, HidNpadStyleTag style) : + playerId(playerId), + style(style) +{ + this->handleCount = 1; + + if (style == HidNpadStyleTag_NpadJoyDual || style == HidNpadStyleTag_NpadHandheld) + this->handleCount = 2; + + this->handles = std::make_unique(this->handleCount); + hidInitializeVibrationDevices(this->handles.get(), this->handleCount, playerId, style); +} + +Vibration::~Vibration() +{ + if (!this->handles) + return; + + this->Stop(); +} + +Vibration& Vibration::operator=(Vibration&& other) +{ + Vibration<>::operator=(std::move(other)); + this->handles = std::move(other.handles); + this->playerId = other.playerId; + this->style = other.style; + + return *this; +} + +bool Vibration::Stop() +{ + return this->SendValues(0, 0); +} + +bool Vibration::SendValues(float left, float right) +{ + if (!this->handles) + return false; + + HidVibrationValue values[this->handleCount] {}; + + for (auto& value : values) + { + value.freq_low = 160.0f; + value.freq_high = 320.0f; + + value.amp_low = left; + value.amp_high = right; + } + + Result result = hidSendVibrationValues(this->handles.get(), values, this->handleCount); + + if (R_SUCCEEDED(result)) + { + this->vibrationInfo.left = left; + this->vibrationInfo.right = right; + } + else + { + this->vibrationInfo.left = this->vibrationInfo.right = 0.0f; + this->vibrationInfo.endTime = -1.0f; + } + + return R_SUCCEEDED(result); +} diff --git a/platform/hac/source/utilities/npad.cpp b/platform/hac/source/utilities/npad.cpp new file mode 100644 index 000000000..e71eea893 --- /dev/null +++ b/platform/hac/source/utilities/npad.cpp @@ -0,0 +1,30 @@ +#include + +#include + +using namespace love; + +HidNpadStyleTag love::npad::GetStyleTag(PadState* state) +{ + uint32_t styleSet = padGetStyleSet(state); + uint32_t attributes = padGetAttributes(state); + + if (styleSet & HidNpadStyleTag_NpadFullKey) + return HidNpadStyleTag_NpadFullKey; + else if (styleSet & HidNpadStyleTag_NpadHandheld) + return HidNpadStyleTag_NpadHandheld; + + /* this won't work with PadState split and join at the moment */ + if (styleSet & HidNpadStyleTag_NpadJoyLeft) + return HidNpadStyleTag_NpadJoyLeft; + + if (styleSet & HidNpadStyleTag_NpadJoyDual) + { + if (!(attributes & HidNpadAttribute_IsLeftConnected)) + return HidNpadStyleTag_NpadJoyRight; + else + return HidNpadStyleTag_NpadJoyDual; + } + + return INVALID_STYLE_TAG; +} diff --git a/platform/hac/source/utilities/sensor/accelerometer.cpp b/platform/hac/source/utilities/sensor/accelerometer.cpp new file mode 100644 index 000000000..15eb98e29 --- /dev/null +++ b/platform/hac/source/utilities/sensor/accelerometer.cpp @@ -0,0 +1,67 @@ +#include + +using namespace love; + +Accelerometer::Accelerometer(HidNpadIdType playerId, HidNpadStyleTag style) : + playerId(playerId), + style(style) +{ + this->handleCount = 1; + + if (style == HidNpadStyleTag_NpadJoyDual) + this->handleCount = 2; + + this->handles = std::make_unique(this->handleCount); + hidGetSixAxisSensorHandles(this->handles.get(), this->handleCount, playerId, style); +} + +Accelerometer& Accelerometer::operator=(Accelerometer&& other) +{ + Accelerometer::operator=(std::move(other)); + this->handles = std::move(other.handles); + this->playerId = other.playerId; + this->style = other.style; + + return *this; +} + +Accelerometer::~Accelerometer() +{ + this->SetEnabled(false); +} + +void Accelerometer::SetEnabled(bool enabled) +{ + if (enabled) + { + for (int index = 0; index < this->handleCount; index++) + hidStartSixAxisSensor(this->handles[index]); + } + else + { + if (!this->handles) + return; + + for (int index = 0; index < this->handleCount; index++) + hidStopSixAxisSensor(this->handles[index]); + } + + SensorBase::SetEnabled(enabled); +} + +std::vector Accelerometer::GetData() +{ + std::vector results {}; + + // clang-format off + for (int index = 0; index < this->handleCount; index++) + { + HidSixAxisSensorState state {}; + hidGetSixAxisSensorStates(this->handles[index], &state, 1); + + results.insert(results.end(), { state.acceleration.x, state.acceleration.y, state.acceleration.z }); + } + // clang-format on + + return results; +} diff --git a/platform/hac/source/utilities/sensor/gyroscope.cpp b/platform/hac/source/utilities/sensor/gyroscope.cpp new file mode 100644 index 000000000..4c5b296cd --- /dev/null +++ b/platform/hac/source/utilities/sensor/gyroscope.cpp @@ -0,0 +1,68 @@ +#include + +#include + +using namespace love; + +Gyroscope::Gyroscope(HidNpadIdType playerId, HidNpadStyleTag style) : + playerId(playerId), + style(style) +{ + this->handleCount = 1; + + if (style == HidNpadStyleTag_NpadJoyDual) + this->handleCount = 2; + + this->handles = std::make_unique(this->handleCount); + hidGetSixAxisSensorHandles(this->handles.get(), this->handleCount, playerId, style); +} + +Gyroscope& Gyroscope::operator=(Gyroscope&& other) +{ + this->handles = std::move(other.handles); + this->playerId = other.playerId; + this->style = other.style; + + return *this; +} + +Gyroscope::~Gyroscope() +{ + this->SetEnabled(false); +} + +void Gyroscope::SetEnabled(bool enabled) +{ + if (enabled) + { + Result results[this->handleCount]; + for (int index = 0; index < this->handleCount; index++) + results[index] = hidStartSixAxisSensor(this->handles[index]); + } + else + { + if (!this->handles) + return; + + for (int index = 0; index < this->handleCount; index++) + hidStopSixAxisSensor(this->handles[index]); + } + + SensorBase::SetEnabled(enabled); +} + +std::vector Gyroscope::GetData() +{ + std::vector results {}; + + // clang-format off + for (size_t index = 0; index < this->handleCount; index++) + { + HidSixAxisSensorState state {}; + hidGetSixAxisSensorStates(this->handles[index], &state, 1); + + results.insert(results.end(), { state.angular_velocity.x, state.angular_velocity.y, state.angular_velocity.z }); + } + + return results; +} diff --git a/platform/switch/Makefile b/platform/switch/Makefile deleted file mode 100644 index d5fecca7d..000000000 --- a/platform/switch/Makefile +++ /dev/null @@ -1,298 +0,0 @@ -#--------------------------------------------------------------------------------- -.SUFFIXES: -#--------------------------------------------------------------------------------- - -ifeq ($(strip $(DEVKITPRO)),) -$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") -endif - -TOPDIR ?= $(CURDIR) -include $(DEVKITPRO)/libnx/switch_rules - -#--------------------------------------------------------------------------------- -# TARGET is the name of the output -# BUILD is the directory where object files & intermediate files will be placed -# SOURCES is a list of directories containing source code -# DATA is a list of directories containing data files -# INCLUDES is a list of directories containing header files -# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) -# -# NO_ICON: if set to anything, do not use icon. -# NO_NACP: if set to anything, no .nacp file is generated. -# APP_TITLE is the name of the app stored in the .nacp file (Optional) -# APP_AUTHOR is the author of the app stored in the .nacp file (Optional) -# APP_VERSION is the version of the app stored in the .nacp file (Optional) -# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) -# ICON is the filename of the icon (.jpg), relative to the project folder. -# If not set, it attempts to use one of the following (in this order): -# - .jpg -# - icon.jpg -# - /default_icon.jpg -# -# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder. -# If not set, it attempts to use one of the following (in this order): -# - .json -# - config.json -# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead -# of a homebrew executable (.nro). This is intended to be used for sysmodules. -# NACP building is skipped as well. -#--------------------------------------------------------------------------------- -TARGET := LOVEPotion -BUILD := build - -CONSOLE_INCLUDE := include $(foreach d, $(wildcard include/*), $(if $(wildcard $d/.), $(call DIR_WILDCARD, $d) $d,)) -CONSOLE_SOURCES := source $(foreach d, $(wildcard source/*), $(if $(wildcard $d/.), $(call DIR_WILDCARD, $d) $d,)) - -SOURCES := ${LOVE_SOURCES} \ - ${LOVE_LIBRARIES} \ - ${CONSOLE_SOURCES} \ - shaders - -DATA := ${LOVE_DATA_FILES} \ - source/scripts - -INCLUDES := ${LOVE_INCLUDES} \ - ${LOVE_LIBRARIES} \ - ${LOVE_MAIN_DATA_FILES} \ - ${CONSOLE_INCLUDE} - -APP_TITLEID := 1043 -ROMFS := romfs - -# Output folders for autogenerated files in romfs -OUT_SHADERS := shaders - -#--------------------------------------------------------------------------------- -# options for code generation -#--------------------------------------------------------------------------------- -ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE - -CFLAGS := -g -Wall -ffunction-sections \ - $(ARCH) $(DEFINES) - -CFLAGS += $(INCLUDE) -D__SWITCH__ $(DEFINES) - -# Custom defines -CFLAGS += `freetype-config --cflags` - -CXXFLAGS := $(CFLAGS) -fno-rtti -fexceptions -std=gnu++20 - -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) - -LIBS := ${LOVE_PORTLIBS} `freetype-config --libs` `$(PREFIX)pkg-config libmpg123 --libs` - -ifeq ($(strip $(DEBUG)),) - CFLAGS += -O2 - ICON := icon.jpg - LIBS += -ldeko3d -lnx -else - CFLAGS += -Og - ICON := icon-dev.jpg - LIBS += -ldeko3dd -lnxd -endif - -#--------------------------------------------------------------------------------- -# list of directories containing libraries, this must be the top level containing -# include and lib -#--------------------------------------------------------------------------------- -LIBDIRS := $(PORTLIBS) $(LIBNX) - - -#--------------------------------------------------------------------------------- -# no real need to edit anything past this point unless you need to add additional -# rules for different file extensions -#--------------------------------------------------------------------------------- -ifneq ($(BUILD),$(notdir $(CURDIR))) -#--------------------------------------------------------------------------------- - -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) - -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) - -export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir, $(SOURCES), $(notdir $(wildcard $(dir)/*.c))) -CPPFILES := $(foreach dir, $(SOURCES), $(notdir $(wildcard $(dir)/*.cpp))) -SFILES := $(foreach dir, $(SOURCES), $(notdir $(wildcard $(dir)/*.s))) -GLSLFILES := $(foreach dir, $(SOURCES), $(notdir $(wildcard $(dir)/*.glsl))) -BINFILES := $(foreach dir, $(DATA), $(notdir $(wildcard $(dir)/*.*))) - -#--------------------------------------------------------------------------------- -# use CXX for linking C++ projects, CC for standard C -#--------------------------------------------------------------------------------- -ifeq ($(strip $(CPPFILES)),) -#--------------------------------------------------------------------------------- - export LD := $(CC) -#--------------------------------------------------------------------------------- -else -#--------------------------------------------------------------------------------- - export LD := $(CXX) -#--------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------- - -export OFILES_BIN := $(addsuffix .o,$(BINFILES)) -export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) -export OFILES := $(OFILES_BIN) $(OFILES_SRC) -export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) - -ifneq ($(strip $(ROMFS)),) - ROMFS_TARGETS := - ROMFS_FOLDERS := - ifneq ($(strip $(OUT_SHADERS)),) - ROMFS_SHADERS := $(ROMFS)/$(OUT_SHADERS) - ROMFS_TARGETS += $(patsubst %.glsl, $(ROMFS_SHADERS)/%.dksh, $(GLSLFILES)) - ROMFS_FOLDERS += $(ROMFS_SHADERS) - endif - - export ROMFS_DEPS := $(foreach file,$(ROMFS_TARGETS),$(CURDIR)/$(file)) -endif - -export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ - $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ - -I$(CURDIR)/$(BUILD) - -export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) - -ifeq ($(strip $(CONFIG_JSON)),) - jsons := $(wildcard *.json) - ifneq (,$(findstring $(TARGET).json,$(jsons))) - export APP_JSON := $(TOPDIR)/$(TARGET).json - else - ifneq (,$(findstring config.json,$(jsons))) - export APP_JSON := $(TOPDIR)/config.json - endif - endif -else - export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) -endif - -ifeq ($(strip $(ICON)),) - icons := $(wildcard *.jpg) - ifneq (,$(findstring $(TARGET).jpg,$(icons))) - export APP_ICON := $(TOPDIR)/$(TARGET).jpg - else - ifneq (,$(findstring icon.jpg,$(icons))) - export APP_ICON := $(TOPDIR)/icon.jpg - endif - endif -else - export APP_ICON := $(TOPDIR)/$(ICON) -endif - -ifeq ($(strip $(NO_ICON)),) - export NROFLAGS += --icon=$(APP_ICON) -endif - -ifeq ($(strip $(NO_NACP)),) - export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp -endif - -ifneq ($(APP_TITLEID),) - export NACPFLAGS += --titleid=$(APP_TITLEID) -endif - -ifneq ($(ROMFS),) - export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) -endif - -.PHONY: clean all - -#--------------------------------------------------------------------------------- -all: $(ROMFS_TARGETS) | $(BUILD) - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -$(BUILD): - @mkdir -p $@ - -ifneq ($(strip $(ROMFS_TARGETS)),) - -$(ROMFS_TARGETS): | $(ROMFS_FOLDERS) - -$(ROMFS_FOLDERS): - @mkdir -p $@ - -$(ROMFS_SHADERS)/%_vsh.dksh: %_vsh.glsl - @echo {vert} $(notdir $<) - @uam -s vert -o $@ $< - -$(ROMFS_SHADERS)/%_tcsh.dksh: %_tcsh.glsl - @echo {tess_ctrl} $(notdir $<) - @uam -s tess_ctrl -o $@ $< - -$(ROMFS_SHADERS)/%_tesh.dksh: %_tesh.glsl - @echo {tess_eval} $(notdir $<) - @uam -s tess_eval -o $@ $< - -$(ROMFS_SHADERS)/%_gsh.dksh: %_gsh.glsl - @echo {geom} $(notdir $<) - @uam -s geom -o $@ $< - -$(ROMFS_SHADERS)/%_fsh.dksh: %_fsh.glsl - @echo {frag} $(notdir $<) - @uam -s frag -o $@ $< - -$(ROMFS_SHADERS)/%.dksh: %.glsl - @echo {comp} $(notdir $<) - @uam -s comp -o $@ $< - -endif - -#--------------------------------------------------------------------------------- -clean: - @echo clean ... -ifeq ($(strip $(APP_JSON)),) - @rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nro $(TARGET).nacp $(TARGET).elf -else - @rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf -endif - - -#--------------------------------------------------------------------------------- -else -.PHONY: all - -DEPENDS := $(OFILES:.o=.d) - -#--------------------------------------------------------------------------------- -# main targets -#--------------------------------------------------------------------------------- -ifeq ($(strip $(APP_JSON)),) - -all : $(OUTPUT).nro - -ifeq ($(strip $(NO_NACP)),) -$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp $(ROMFS_DEPS) -else -$(OUTPUT).nro : $(OUTPUT).elf $(ROMFS_DEPS) -endif - -else - -all : $(OUTPUT).nsp - -$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm - -$(OUTPUT).nso : $(OUTPUT).elf - -endif - -$(OUTPUT).elf : $(OFILES) - -$(OFILES_SRC) : $(HFILES_BIN) - -#--------------------------------------------------------------------------------- -# you need a rule like this for each extension you use as binary data -#--------------------------------------------------------------------------------- -%.lua.o %_lua.h : %.lua - @echo $(notdir $<) - @$(bin2o) - --include $(DEPENDS) - -#--------------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------------- diff --git a/platform/switch/icon-dev.jpg b/platform/switch/icon-dev.jpg deleted file mode 100644 index 615b9e6b0..000000000 Binary files a/platform/switch/icon-dev.jpg and /dev/null differ diff --git a/platform/switch/icon.jpg b/platform/switch/icon.jpg deleted file mode 100644 index 14d15e93d..000000000 Binary files a/platform/switch/icon.jpg and /dev/null differ diff --git a/platform/switch/include/common/bitalloc.h b/platform/switch/include/common/bitalloc.h deleted file mode 100644 index e35d10e22..000000000 --- a/platform/switch/include/common/bitalloc.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "common/exception.h" -#include -#include - -template -class BitwiseAlloc : std::bitset -{ - public: - size_t Allocate() - { - size_t index = 0; - while (index < this->size() && this->test(index)) - ++index; - - this->set(index); - return index; - } - - size_t Find(DkResHandle handle) - { - size_t index = (handle & ((1U << 20) - 1)); - -#if defined(__DEBUG__) - if (!this->test(index)) - throw love::Exception("Texture allocator bit %zu not set!", index); -#endif - - return index; - } - - void DeAllocate(DkResHandle handle) - { - size_t index = this->Find(handle); - this->reset(index); - } -}; \ No newline at end of file diff --git a/platform/switch/include/common/debugger.h b/platform/switch/include/common/debugger.h deleted file mode 100644 index 2b706c70d..000000000 --- a/platform/switch/include/common/debugger.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "common/debug/debuggerc.h" - -namespace love -{ - class Debugger : public common::Debugger - { - public: - static Debugger& Instance() - { - static Debugger instance; - return instance; - } - - bool Initialize() override; - - ~Debugger(); - - private: - int sockfd; - - Debugger(); - }; -} // namespace love diff --git a/platform/switch/include/common/screen.h b/platform/switch/include/common/screen.h deleted file mode 100644 index 1a23b835a..000000000 --- a/platform/switch/include/common/screen.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "common/screenc.h" - -namespace love -{ - class Screen : common::Screen - { - public: - static Screen& Instance() - { - static Screen screen; - return screen; - }; - - static constexpr int HANDHELD_WIDTH = 0x500; - static constexpr int HANDHELD_HEIGHT = 0x2D0; - - static constexpr int DOCKED_WIDTH = 0x780; - static constexpr int DOCKED_HEIGHT = 0x438; - - enum class HacScreen : RenderScreen - { - HAC_SCREEN_DEFAULT, - HAC_SCREEN_MAX_ENUM - }; - - int GetWidth(RenderScreen screen = 0) override; - - int GetHeight() override; - - static bool GetConstant(const char* in, RenderScreen& out); - static bool GetConstant(RenderScreen in, const char*& out); - static std::vector GetConstants(RenderScreen); - }; -} // namespace love diff --git a/platform/switch/include/deko3d/CImage.h b/platform/switch/include/deko3d/CImage.h deleted file mode 100644 index 5af899d45..000000000 --- a/platform/switch/include/deko3d/CImage.h +++ /dev/null @@ -1,69 +0,0 @@ -/* -** Sample Framework for deko3d Applications -** CExternalImage.h: Utility class for loading images from the filesystem -*/ -#pragma once - -#include "deko3d/CMemPool.h" -#include "deko3d/common.h" - -#include "common/lmath.h" -#include - -#include "common/pixelformat.h" - -class CImage -{ - dk::Image m_image; - dk::ImageDescriptor m_descriptor; - CMemPool::Handle m_mem; - - public: - CImage() : m_image {}, m_descriptor {}, m_mem {} - {} - - CImage(CImage const&) = delete; - - CImage(CImage&&) = delete; - - ~CImage() - { - m_mem.destroy(); - } - - constexpr operator bool() const - { - return m_mem; - } - - constexpr dk::Image& get() - { - return m_image; - } - - constexpr dk::ImageDescriptor const& getDescriptor() const - { - return m_descriptor; - } - - bool load(love::PixelFormat format, bool isSRGB, void* buffer, size_t size, int width, - int height, bool empty = false); - - bool loadEmptyPixels(CMemPool& imagePool, CMemPool& scratchPool, dk::Device device, - dk::Queue queue, uint32_t width, uint32_t height, DkImageFormat format, - uint32_t flags = 0); - - bool replacePixels(CMemPool& scratchPool, dk::Device device, const void* data, size_t size, - dk::Queue transferQueue, const love::Rect& rect); - - bool loadMemory(CMemPool& imagePool, CMemPool& scratchPool, dk::Device device, - dk::Queue transferQueue, const void* data, uint32_t width, uint32_t height, - DkImageFormat format, uint32_t flags = 0); - - size_t getFormatSize(DkImageFormat format); - - private: - std::unique_ptr loadPNG(const void* buffer, const size_t size, int& width, int& height); - - std::unique_ptr loadJPG(const void* buffer, const size_t size, int& width, int& height); -}; diff --git a/platform/switch/include/deko3d/CShader.h b/platform/switch/include/deko3d/CShader.h deleted file mode 100644 index e1ac0833a..000000000 --- a/platform/switch/include/deko3d/CShader.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -** Sample Framework for deko3d Applications -** CShader.h: Utility class for loading shaders from the filesystem -*/ -#pragma once -#include "deko3d/CMemPool.h" -#include "deko3d/common.h" - -#include "common/debug/logger.h" - -class CShader -{ - dk::Shader m_shader; - CMemPool::Handle m_codemem; - - public: - CShader() : m_shader {}, m_codemem {} - {} - - ~CShader() - { - m_codemem.destroy(); - } - - constexpr operator bool() const - { - return m_codemem; - } - - constexpr operator dk::Shader const *() const - { - return &m_shader; - } - - DkStage getStage() - { - return m_shader.getStage(); - } - - bool isValid() const - { - return m_shader.isValid(); - } - - bool load(CMemPool& pool, const char* path); - - bool load(CMemPool& pool, const void* buffer, size_t size); - - private: - struct DkshHeader - { - uint32_t magic; // DKSH_MAGIC - uint32_t header_sz; // sizeof(DkshHeader) - uint32_t control_sz; - uint32_t code_sz; - uint32_t programs_off; - uint32_t num_programs; - }; -}; diff --git a/platform/switch/include/deko3d/common.h b/platform/switch/include/deko3d/common.h deleted file mode 100644 index 814e49928..000000000 --- a/platform/switch/include/deko3d/common.h +++ /dev/null @@ -1,12 +0,0 @@ -/* -** Sample Framework for deko3d Applications -** common.h: Common includes -*/ -#pragma once -#include -#include -#include - -#include - -#include diff --git a/platform/switch/include/deko3d/deko.h b/platform/switch/include/deko3d/deko.h deleted file mode 100644 index 12263fa90..000000000 --- a/platform/switch/include/deko3d/deko.h +++ /dev/null @@ -1,268 +0,0 @@ -#pragma once - -#include "deko3d/common.h" - -#include "common/bitalloc.h" -#include "deko3d/CCmdMemRing.h" -#include "deko3d/CCmdVtxRing.h" -#include "deko3d/CImage.h" -#include "deko3d/CMemPool.h" -#include "deko3d/CShader.h" -#include "deko3d/shader.h" - -#include "objects/canvas/canvas.h" -#include "objects/font/font.h" -#include "objects/texture/texture.h" - -#include "common/lmath.h" -#include "deko3d/vertex.h" -#include "graphics/graphics.h" - -#include "deko3d/CDescriptorSet.h" - -#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES // Enforces GLSL std140/std430 alignment rules for glm - // types -#define GLM_FORCE_INTRINSICS // Enables usage of SIMD CPU instructions (requiring the above as well) -#include -#include -#include -#include - -#define MAX_ANISOTROPY 16 - -namespace love -{ - class Graphics; -} - -class deko3d -{ - private: - deko3d(); - - public: - static constexpr unsigned MAX_FRAMEBUFFERS = 2; - - static constexpr unsigned COMMAND_SIZE = 0x100000; - static constexpr size_t VERTEX_COMMAND_SIZE = 0x100000; - - static constexpr size_t MAX_OBJECTS = 0x250; - - static deko3d& Instance(); - - ~deko3d(); - - void CreateFramebufferResources(); - - void DestroyFramebufferResources(); - - void BindFramebuffer(love::Canvas* canvas = nullptr); - - void SetTextureFilter(const love::Texture::Filter& filter); - - void SetTextureFilter(love::Texture* texture, const love::Texture::Filter& filter); - - void SetTextureWrap(const love::Texture::Wrap& wrap); - - void SetTextureWrap(love::Texture* texture, const love::Texture::Wrap& filter); - - void ClearColor(const Colorf& color); - - void ClearDepthStencil(double depth, int stencil); - - void BeginFrame(); - - void SetViewport(const love::Rect& view); - - void SetDepthWrites(bool enable); - - void SetBlendMode(DkBlendOp func, DkBlendFactor srcColor, DkBlendFactor srcAlpha, - DkBlendFactor dstColor, DkBlendFactor dstAlpha); - - void UseProgram(const love::Shader::Program& program); - - void SetColorMask(const love::Graphics::ColorMask& mask); - - float GetPointSize(); - - void SetPointSize(float size); - - void SetLineWidth(float width); - - void SetLineStyle(bool smooth); - - void SetFrontFaceWinding(DkFrontFace face); - - void SetCullMode(DkFace face); - - love::Rect GetViewport(); - - void SetScissor(const love::Rect& scissor, bool canvasActive); - - void SetScissor(); - - void Present(); - - void SetBlendColor(const Colorf& color); - - void SetStencil(DkStencilOp op, DkCompareOp compare, int value); - - void OnOperationMode(std::pair& size); - - bool IsHandheldMode() - { - AppletOperationMode mode = appletGetOperationMode(); - - return mode == AppletOperationMode::AppletOperationMode_Handheld; - } - - dk::Device GetDevice() - { - return this->device; - } - - dk::Queue GetTextureQueue() - { - return this->textureQueue; - } - - CMemPool& GetImages() - { - return this->pool.images; - } - - CMemPool& GetCode() - { - return this->pool.code; - } - - CMemPool& GetData() - { - return this->pool.data; - } - - DkResHandle RegisterResHandle(const dk::ImageDescriptor& descriptor); - - void UnRegisterResHandle(DkResHandle handle); - - bool RenderTexture(const DkResHandle handle, const vertex::Vertex* points, size_t count); - - bool RenderVideo(const DkResHandle handles[3], const vertex::Vertex* points, size_t count); - - /* Primitives Rendering */ - - bool RenderPolygon(const vertex::Vertex* points, size_t count); - - bool RenderPolyline(DkPrimitive mode, const vertex::Vertex* points, size_t count); - - bool RenderPoints(const vertex::Vertex* points, size_t count); - - static DkWrapMode GetDekoWrapMode(love::Texture::WrapMode wrap); - - static bool GetConstant(PixelFormat in, DkImageFormat& out); - - static bool GetConstant(DkImageFormat in, PixelFormat& out); - - void SetDekoBarrier(DkBarrier barrier, uint32_t flags); - - private: - vertex::Vertex* vertexData; - - uint32_t firstVertex = 0; - BitwiseAlloc allocator; - - enum State - { - STATE_PRIMITIVE, - STATE_TEXTURE, - STATE_VIDEO, - STATE_MAX_ENUM - }; - - static constexpr float Z_NEAR = -10.0f; - static constexpr float Z_FAR = 10.0f; - - State renderState; - - void EnsureInState(State state); - - struct - { - CDescriptorSet image; - CDescriptorSet sampler; - bool dirty = false; - } descriptors; - - struct Transformation - { - glm::mat4 mdlvMtx; - glm::mat4 projMtx; - }; - - dk::UniqueDevice device; - dk::UniqueQueue queue; - dk::UniqueCmdBuf cmdBuf; - - struct - { - CMemPool images; - CMemPool data; - CMemPool code; - } pool; - - struct - { - dk::RasterizerState rasterizer; - dk::ColorState color; - dk::ColorWriteState colorWrite; - dk::BlendState blendState; - dk::DepthStencilState depthStencil; - - float pointSize; - } state; - - dk::UniqueQueue textureQueue; - - CCmdMemRing cmdRing; - CCmdVtxRing vtxRing; - - love::Rect viewport; - love::Rect scissor; - - struct - { - dk::Sampler sampler; - dk::SamplerDescriptor descriptor; - } filter; - - struct - { - dk::Image images[MAX_FRAMEBUFFERS]; - CMemPool::Handle memory[MAX_FRAMEBUFFERS]; - - bool inFrame = false; - int slot = -1; - bool dirty = false; - } framebuffers; - - bool descriptorsDirty; - - struct - { - dk::ImageLayout layout; - dk::Image image; - CMemPool::Handle memory; - } depthBuffer; - - Transformation transformState; - CMemPool::Handle transformUniformBuffer; - - dk::ImageLayout layoutFramebuffer; - std::array framebufferArray; - - dk::UniqueSwapchain swapchain; - - void EnsureInFrame(); - - void EnsureHasSlot(); -}; diff --git a/platform/switch/include/deko3d/graphics.h b/platform/switch/include/deko3d/graphics.h deleted file mode 100644 index 0b51e8ab2..000000000 --- a/platform/switch/include/deko3d/graphics.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include "modules/graphics/graphics.h" - -#include "deko3d/deko.h" - -#define RENDERER_NAME "deko3d" -#define RENDERER_VERSION "0.4.0" -#define RENDERER_VENDOR "devkitPro" -#define RENDERER_DEVICE "NVIDIA Tegra X1" - -namespace love::deko3d -{ - class Graphics : public love::Graphics - { - public: - Graphics(); - - virtual ~Graphics(); - - void Clear(std::optional color, std::optional stencil, - std::optional depth) override; - - void Clear(std::vector>& colors, std::optional stencil, - std::optional depth) override; - - void Present() override; - - void SetScissor(const Rect& scissor) override; - - void SetScissor() override; - - void SetColor(Colorf color) override; - - /* Primitives */ - - void Rectangle(DrawMode mode, float x, float y, float width, float height) override; - - void Rectangle(DrawMode mode, float x, float y, float width, float height, float rx, - float ry) override; - - void Rectangle(DrawMode mode, float x, float y, float width, float height, float rx, - float ry, int points) override; - - void Ellipse(DrawMode mode, float x, float y, float a, float b) override; - - void Ellipse(DrawMode mode, float x, float y, float a, float b, int points) override; - - void Circle(DrawMode mode, float x, float y, float radius) override; - - void Circle(DrawMode mode, float x, float y, float radius, int points) override; - - void Polyline(const Vector2* points, size_t count); - - void Polygon(DrawMode mode, const Vector2* points, size_t size, - bool skipLastFilledVertex = true) override; - - void Arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, float angle1, - float angle2) override; - - void Arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, float angle1, - float angle2, int points) override; - - void Points(const Vector2* points, size_t count, const Colorf* colors, - size_t colorCount) override; - - void SetPointSize(float size) override; - - void Line(const Vector2* points, int count) override; - - void SetLineWidth(float width) override; - - void SetDefaultFilter(const Texture::Filter& filter); - - /* End Primitives */ - - void SetBlendMode(BlendMode mode, BlendAlpha alpha) override; - - void SetColorMask(ColorMask mask) override; - - void SetMeshCullMode(vertex::CullMode cull) override; - - void SetFrontFaceWinding(vertex::Winding winding) override; - - Font* NewDefaultFont(int size, TrueTypeRasterizer::Hinting hinting, - const Texture::Filter& filter = Texture::defaultFilter) override; - - Font* NewFont(Rasterizer* rasterizer, const Texture::Filter& filter) override; - - RendererInfo GetRendererInfo() const override; - - // Internal? - Shader* NewShader(Shader::StandardShader type); - - private: - int CalculateEllipsePoints(float rx, float ry) const; - }; -} // namespace love::deko3d diff --git a/platform/switch/include/deko3d/shader.h b/platform/switch/include/deko3d/shader.h deleted file mode 100644 index c09f62af6..000000000 --- a/platform/switch/include/deko3d/shader.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include "deko3d/CShader.h" - -#include "common/data.h" - -#include "objects/object.h" - -#include - -namespace love -{ - class Shader : public Object - { - public: - static love::Type type; - - Shader(); - - Shader(love::Data* vertex, love::Data* pixel); - - virtual ~Shader(); - - enum StandardShader - { - STANDARD_DEFAULT, - STANDARD_TEXTURE, - STANDARD_VIDEO, - STANDARD_MAX_ENUM - }; - - struct Program - { - std::optional vertex; - std::optional fragment; - }; - - // Pointer to currently active Shader. - static Shader* current; - - // Pointer to the default Shader. - static Shader* standardShaders[STANDARD_MAX_ENUM]; - - void Attach(); - - bool Validate(const CShader& vertex, const CShader& pixel, std::string& error); - - void LoadDefaults(StandardShader defaultType); - - static void AttachDefault(StandardShader defaultType); - - static const char* GetStageName(CShader& shader); - - static bool GetConstant(const char* in, StandardShader& out); - static bool GetConstant(StandardShader in, const char*& out); - - static bool IsDefaultActive(); - - private: - Program program; - }; -} // namespace love diff --git a/platform/switch/include/deko3d/vertex.h b/platform/switch/include/deko3d/vertex.h deleted file mode 100644 index 5f7a5b5b7..000000000 --- a/platform/switch/include/deko3d/vertex.h +++ /dev/null @@ -1,132 +0,0 @@ -#pragma once - -#include "deko3d/CMemPool.h" -#include "deko3d/common.h" - -#include "common/colors.h" -#include "common/vector.h" - -#include -#include -#include - -using namespace love; - -namespace vertex -{ - struct Vertex - { - float position[3]; - float color[4]; - uint16_t texcoord[2]; - }; - - struct GlyphVertex - { - float x, y; - uint16_t s, t; - - Colorf color; - }; - - enum CullMode - { - CULL_NONE, - CULL_BACK, - CULL_FRONT, - CULL_MAX_ENUM - }; - - enum Winding - { - WINDING_CW, - WINDING_CCW, - WINDING_MAX_ENUM - }; - - enum class TriangleIndexMode - { - NONE, - STRIP, - FAN, - QUADS - }; - - static inline uint16_t normto16t(float in) - { - return uint16_t(in * 0xFFFF); - } - - namespace attributes - { - /* Primitives */ - - constexpr std::array PrimitiveBufferState = { - DkVtxBufferState { sizeof(vertex::Vertex), 0 }, - }; - - constexpr std::array PrimitiveAttribState = { - DkVtxAttribState { 0, 0, offsetof(vertex::Vertex, position), DkVtxAttribSize_3x32, - DkVtxAttribType_Float, 0 }, - DkVtxAttribState { 0, 0, offsetof(vertex::Vertex, color), DkVtxAttribSize_4x32, - DkVtxAttribType_Float, 0 } - }; - - /* Textures */ - - constexpr std::array TextureBufferState = { - DkVtxBufferState { sizeof(vertex::Vertex), 0 }, - }; - - constexpr std::array TextureAttribState = { - DkVtxAttribState { 0, 0, offsetof(vertex::Vertex, position), DkVtxAttribSize_3x32, - DkVtxAttribType_Float, 0 }, - DkVtxAttribState { 0, 0, offsetof(vertex::Vertex, color), DkVtxAttribSize_4x32, - DkVtxAttribType_Float, 0 }, - DkVtxAttribState { 0, 0, offsetof(vertex::Vertex, texcoord), DkVtxAttribSize_2x16, - DkVtxAttribType_Unorm, 0 } - }; - } // namespace attributes - - [[nodiscard]] static inline std::unique_ptr GeneratePrimitiveFromVectors( - std::span points, std::span colors) - { - Colorf color = colors[0]; - - size_t pointCount = points.size(); - auto result = std::make_unique(pointCount); - - size_t colorCount = colors.size(); - - for (size_t index = 0; index < pointCount; index++) - { - const Vector2 point = points[index]; - - if (index < colorCount) - color = colors[index]; - - Vertex append = { .position = { point.x, point.y, 0.0f }, - .color = { color.r, color.g, color.b, color.a }, - .texcoord = { 0, 0 } }; - - result[index] = append; - } - - return result; - } - - std::vector GenerateTextureFromVectors(const love::Vector2* points, - const love::Vector2* texcoord, size_t count, - Colorf color); - - std::vector GenerateTextureFromGlyphs(const vertex::GlyphVertex* verts, - size_t count); - - bool GetConstant(const char* in, CullMode& out); - bool GetConstant(CullMode in, const char*& out); - std::vector GetConstants(CullMode); - - bool GetConstant(const char* in, Winding& out); - bool GetConstant(Winding in, const char*& out); - std::vector GetConstants(Winding); -}; // namespace vertex diff --git a/platform/switch/include/driver/audiodrv.h b/platform/switch/include/driver/audiodrv.h deleted file mode 100644 index 629512d50..000000000 --- a/platform/switch/include/driver/audiodrv.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "common/driver/audiodrvc.h" -#include "modules/thread/types/mutex.h" - -#include - -#include - -namespace love::driver -{ - class Audrv : public common::driver::Audrv - { - public: - ~Audrv(); - - static Audrv& Instance() - { - static Audrv instance; - return instance; - } - - bool ResetChannel(size_t channel, int channels, PcmFormat format, int sampleRate); - - void SetMixVolume(int mix, float volume); - - void SetChannelVolume(size_t channel, float volume); - - bool IsChannelPlaying(size_t channel); - - bool IsChannelPaused(size_t channel); - - bool AddWaveBuf(size_t channel, AudioDriverWaveBuf* waveBuf); - - void PauseChannel(size_t channel, bool pause); - - void StopChannel(size_t channel); - - u32 GetSampleOffset(size_t channel); - - void Update(); - - private: - Audrv(); - - thread::MutexRef mutex; - - bool audioInitialized; - bool channelReset; - - AudioDriver driver; - }; -} // namespace love::driver diff --git a/platform/switch/include/driver/hidrv.h b/platform/switch/include/driver/hidrv.h deleted file mode 100644 index d08b7a292..000000000 --- a/platform/switch/include/driver/hidrv.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "common/driver/hidrvc.h" - -/* -** HID backend class for Nintendo Switch -*/ - -namespace love::driver -{ - class Hidrv : public common::driver::Hidrv - { - public: - Hidrv(); - - bool Poll(LOVE_Event* event) override; - - private: - void CheckFocus(); - - static constexpr int MAX_TOUCHES = 16; - - HidTouchScreenState touchState; - - std::array stateTouches; - std::array oldStateTouches; - - size_t currentPadIndex; - - int prevTouchCount; - }; -} // namespace love::driver diff --git a/platform/switch/include/modules/event/event.h b/platform/switch/include/modules/event/event.h deleted file mode 100644 index d79bb31bb..000000000 --- a/platform/switch/include/modules/event/event.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "modules/event/eventc.h" - -namespace love -{ - class Event : public common::Event - { - public: - Event(); - - virtual ~Event(); - }; -} // namespace love \ No newline at end of file diff --git a/platform/switch/include/modules/font/fontmodule.h b/platform/switch/include/modules/font/fontmodule.h deleted file mode 100644 index 469ec2482..000000000 --- a/platform/switch/include/modules/font/fontmodule.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include "modules/font/fontmodulec.h" -#include "objects/truetyperasterizer/truetyperasterizer.h" - -namespace love -{ - class DefaultFontData : public love::Data - { - public: - DefaultFontData(Font::SystemFontType type = Font::SystemFontType::TYPE_STANDARD) : - type(type) - { - plGetSharedFontByType(&this->fontData, (PlSharedFontType)type); - } - - Data* Clone() const override - { - return new DefaultFontData(this->type); - } - - void* GetData() const override - { - return this->fontData.address; - } - - size_t GetSize() const override - { - return this->fontData.size; - } - - private: - PlFontData fontData; - Font::SystemFontType type; - }; - - class FontModule : public common::FontModule - { - public: - FontModule(); - - virtual ~FontModule(); - - Data* GetSystemFont(Font::SystemFontType type); - - Rasterizer* NewRasterizer(FileData* data) override; - - Rasterizer* NewTrueTypeRasterizer(Font::SystemFontType fontType, int size, - TrueTypeRasterizer::Hinting hinting); - - Rasterizer* NewTrueTypeRasterizer(Font::SystemFontType fontType, int size, float dpiScale, - TrueTypeRasterizer::Hinting hinting); - - Rasterizer* NewTrueTypeRasterizer(int size, TrueTypeRasterizer::Hinting hinting); - - Rasterizer* NewTrueTypeRasterizer(int size, float dpiScale, - TrueTypeRasterizer::Hinting hinting); - - Rasterizer* NewTrueTypeRasterizer(Data* data, int size, - TrueTypeRasterizer::Hinting hinting); - - Rasterizer* NewTrueTypeRasterizer(Data* data, int size, float dpiScale, - TrueTypeRasterizer::Hinting hinting); - - private: - FT_Library library; - }; -} // namespace love diff --git a/platform/switch/include/modules/joystick/joystick.h b/platform/switch/include/modules/joystick/joystick.h deleted file mode 100644 index d1a52d41c..000000000 --- a/platform/switch/include/modules/joystick/joystick.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "modules/joystick/joystickc.h" -#include "modules/thread/types/threadable.h" - -#include "pools/vibration.h" - -namespace love -{ - class Joystick : public common::Joystick - { - public: - Joystick(); - - virtual ~Joystick(); - - bool AddVibration(Gamepad* gamepad, size_t id); - - protected: - size_t GetActiveControllerCount(); - - private: - class VibrationThread : public Threadable - { - public: - VibrationThread(VibrationPool* pool); - - virtual ~VibrationThread(); - - void SetFinish(); - - void ThreadFunction(); - - protected: - VibrationPool* pool; - std::atomic finish; - }; - - std::vector vibrations; - - VibrationPool* pool; - VibrationThread* poolThread; - }; -} // namespace love diff --git a/platform/switch/include/modules/keyboard/keyboard.h b/platform/switch/include/modules/keyboard/keyboard.h deleted file mode 100644 index 6068464aa..000000000 --- a/platform/switch/include/modules/keyboard/keyboard.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "modules/keyboard/keyboardc.h" -#include - -namespace love -{ - enum class common::Keyboard::KeyboardType : uint8_t - { - TYPE_NORMAL = SwkbdType_Normal, - TYPE_QWERTY = SwkbdType_QWERTY, - TYPE_NUMPAD = SwkbdType_NumPad - }; - - class Keyboard : public common::Keyboard - { - public: - static constexpr uint32_t MAX_INPUT_LENGTH = 0x1F4; - - constexpr uint32_t ENCODING_MULTIPLIER() override - { - return 0x04; - } - - Keyboard(); - - virtual ~Keyboard() - {} - - std::string SetTextInput(const SwkbdOpt& options) override; - - static bool GetConstant(const char* in, KeyboardType& out); - static bool GetConstant(KeyboardType in, const char*& out); - static std::vector GetConstants(KeyboardType); - - private: - SwkbdConfig keyboard; - }; -} // namespace love diff --git a/platform/switch/include/modules/system/system.h b/platform/switch/include/modules/system/system.h deleted file mode 100644 index 18789b207..000000000 --- a/platform/switch/include/modules/system/system.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include "modules/system/systemc.h" -#include - -#define USERNAME_LENGTH 0x21 -#define TEGRA_CPU_COUNT 0x04 - -namespace love -{ - class System : public common::System - { - public: - System(); - - virtual ~System() {}; - - using common::System::GetPowerInfo; - - using common::System::GetNetworkInfo; - - int GetProcessorCount() override; - - const std::string& GetUsername() override; - - PowerState GetPowerInfo(uint8_t& percent) const override; - - NetworkState GetNetworkInfo(uint8_t& signal) const override; - - const std::string& GetSystemTheme() override; - - const std::string& GetPreferredLocales() override; - - const std::string& GetModel() override; - - const std::string& GetRegion() override; - - const std::string& GetVersion() override; - - const std::string& GetFriendCode() override; - - static constexpr uint8_t MAX_REGIONS = 6; - static constexpr uint8_t MAX_THEMES = 2; - static constexpr uint8_t MAX_MODELS = 7; - - static bool GetConstant(const char* in, ColorSetId& out); - static bool GetConstant(ColorSetId in, const char*& out); - - static bool GetConstant(const char* in, SetLanguage& out); - static bool GetConstant(SetLanguage in, const char*& out); - static std::vector GetConstants(SetLanguage); - - static bool GetConstant(const char* in, SetSysProductModel& out); - static bool GetConstant(SetSysProductModel in, const char*& out); - static std::vector GetConstants(SetSysProductModel); - - static bool GetConstant(const char* in, SetRegion& out); - static bool GetConstant(SetRegion in, const char*& out); - static std::vector GetConstants(SetRegion); - - private: - AccountUid userID; - }; -} // namespace love diff --git a/platform/switch/include/modules/timer/timer.h b/platform/switch/include/modules/timer/timer.h deleted file mode 100644 index 03229882d..000000000 --- a/platform/switch/include/modules/timer/timer.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "modules/timer/timerc.h" - -namespace love -{ - class Timer : public common::Timer - { - public: - Timer(); - - virtual ~Timer() - {} - }; -} // namespace love diff --git a/platform/switch/include/modules/window/window.h b/platform/switch/include/modules/window/window.h deleted file mode 100644 index 8c615db0c..000000000 --- a/platform/switch/include/modules/window/window.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "modules/window/windowc.h" - -namespace love -{ - class Window : public common::Window - { - public: - Window(); - - virtual ~Window() - {} - - void OnSizeChanged(int width, int height) override; - - bool CreateWindowAndContext() override; - - DisplaySize GetDesktopSize() override; - - void GetWindow(int& width, int& height) override; - - int GetDisplayCount() override; - }; -} // namespace love diff --git a/platform/switch/include/objects/canvas/canvas.h b/platform/switch/include/objects/canvas/canvas.h deleted file mode 100644 index 0ad03ed92..000000000 --- a/platform/switch/include/objects/canvas/canvas.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "objects/canvas/canvasc.h" - -namespace love -{ - class Canvas : public common::Canvas - { - public: - Canvas(const Settings& settings); - - virtual ~Canvas(); - - void Draw(Graphics* gfx, Quad* quad, const Matrix4& localTransform) override; - - constexpr dk::Image& GetImage() - { - return this->colorBuffer; - } - - constexpr dk::ImageDescriptor const& GetDescriptor() const - { - return this->descriptor; - } - - private: - dk::Image colorBuffer; - CMemPool::Handle colorMemory; - - dk::ImageDescriptor descriptor; - }; -} // namespace love \ No newline at end of file diff --git a/platform/switch/include/objects/font/font.h b/platform/switch/include/objects/font/font.h deleted file mode 100644 index 5615d50aa..000000000 --- a/platform/switch/include/objects/font/font.h +++ /dev/null @@ -1,163 +0,0 @@ -#pragma once - -#include "deko3d/vertex.h" -#include "objects/font/fontc.h" - -#include "objects/image/image.h" -#include "objects/texture/texture.h" - -#include "objects/glyphdata/glyphdata.h" -#include "objects/truetyperasterizer/truetyperasterizer.h" - -enum class love::common::Font::SystemFontType : uint8_t -{ - TYPE_STANDARD = PlSharedFontType_Standard, - TYPE_CHINESE_SIMPLIFIED = PlSharedFontType_ChineseSimplified, - TYPE_CHINESE_TRADITIONAL = PlSharedFontType_ChineseTraditional, - TYPE_CHINESE_SIMPLIFIED_EXT = PlSharedFontType_ExtChineseSimplified, - TYPE_KOREAN = PlSharedFontType_KO, - TYPE_NINTENDO_EXTENDED = PlSharedFontType_NintendoExt, - TYPE_MAX_ENUM -}; - -namespace love -{ - class Font : public love::common::Font - { - public: - typedef std::vector Codepoints; - - struct IndexedColor - { - Colorf color; - int index; - }; - - struct ColoredCodepoints - { - std::vector codes; - std::vector colors; - }; - - struct TextInfo - { - int width; - int height; - }; - - static void GetCodepointsFromString(const std::string& string, Codepoints& points); - - static void GetCodepointsFromString(const std::vector& strings, - ColoredCodepoints& codepoints); - - struct DrawCommand - { - int startVertex; - int vertexCount; - - love::Texture* texture; - }; - - std::vector GenerateVertices(const ColoredCodepoints& codepoints, - const Colorf& constantcolor, - std::vector& glyphVertices, - float extra_spacing = 0.0f, Vector2 offset = {}, - TextInfo* info = nullptr); - - std::vector GenerateVerticesFormatted( - const ColoredCodepoints& text, const Colorf& constantColor, float wrap, AlignMode align, - std::vector& vertices, TextInfo* info = nullptr); - - void Print(Graphics* gfx, const std::vector& text, - const Matrix4& localTransform, const Colorf& color) override; - - void Printf(Graphics* gfx, const std::vector& text, float wrap, - AlignMode align, const Matrix4& localTransform, const Colorf& color) override; - - void PrintV(Graphics* gfx, const Matrix4& t, const std::vector& drawcommands, - const std::vector& vertices); - - void GetWrap(const std::vector& text, float wraplimit, - std::vector& lines, std::vector* lineWidths = nullptr); - - void GetWrap(const ColoredCodepoints& codepoints, float wraplimit, - std::vector& lines, std::vector* lineWidths = nullptr); - - int GetWidth(uint32_t prevGlyph, uint32_t codepoint) override; - - using common::Font::GetWidth; - - float GetHeight() const override; - - struct Glyph - { - love::Texture* texture; - int spacing; - vertex::GlyphVertex vertices[4]; - }; - - uint32_t GetTextureCacheID(); - - Font(Rasterizer* r, const Texture::Filter& filter); - - virtual ~Font(); - - const Font::Glyph& FindGlyph(uint32_t glyph); - - const Font::Glyph& AddGlyph(uint32_t glyph); - - bool HasGlyph(uint32_t glyph) const override; - - love::GlyphData* GetRasterizerGlyphData(uint32_t glyph); - - float GetKerning(uint32_t leftGlyph, uint32_t rightGlyph) override; - - float GetKerning(const std::string& leftChar, const std::string& rightChar) override; - - float GetDPIScale() const override; - - void SetFallbacks(const std::vector& fallbacks) override; - - float GetAscent() const override; - - float GetBaseline() const override; - - float GetDescent() const override; - - void SetFilter(const Texture::Filter& filter); - - private: - struct TextureSize - { - int width; - int height; - }; - - TextureSize GetNextTextureSize() const; - - std::vector> rasterizers; - - int textureWidth; - int textureHeight; - - bool useSpacesAsTab; - - uint32_t textureCacheID; - - int textureX; - int textureY; - int rowHeight; - - static const int TEXTURE_PADDING = 2; - - static const int SPACES_PER_TAB = 4; - - std::unordered_map glyphs; - - std::vector> images; - - std::unordered_map kerning; - - void CreateTexture(); - }; -} // namespace love diff --git a/platform/switch/include/objects/gamepad/gamepad.h b/platform/switch/include/objects/gamepad/gamepad.h deleted file mode 100644 index 13d02a3a3..000000000 --- a/platform/switch/include/objects/gamepad/gamepad.h +++ /dev/null @@ -1,153 +0,0 @@ -#pragma once - -#include "objects/gamepad/gamepadc.h" - -#include "common/bidirectionalmap.h" -#include "common/bitalloc.h" - -#include -#include - -enum class love::common::Gamepad::GamepadAxis : uint64_t -{ - GAMEPAD_AXIS_LEFTX = HidNpadButton_StickLLeft | HidNpadButton_StickLRight, - GAMEPAD_AXIS_LEFTY = HidNpadButton_StickLUp | HidNpadButton_StickLDown, - GAMEPAD_AXIS_RIGHTX = HidNpadButton_StickRLeft | HidNpadButton_StickRRight, - GAMEPAD_AXIS_RIGHTY = HidNpadButton_StickRUp | HidNpadButton_StickRDown, - GAMEPAD_AXIS_TRIGGERLEFT = HidNpadButton_ZL, - GAMEPAD_AXIS_TRIGGERRIGHT = HidNpadButton_ZR, -}; - -enum class love::common::Gamepad::GamepadButton : uint64_t -{ - GAMEPAD_BUTTON_A = HidNpadButton_A, - GAMEPAD_BUTTON_B = HidNpadButton_B, - GAMEPAD_BUTTON_X = HidNpadButton_X, - GAMEPAD_BUTTON_Y = HidNpadButton_Y, - GAMEPAD_BUTTON_BACK = HidNpadButton_Minus, - GAMEPAD_BUTTON_START = HidNpadButton_Plus, - GAMEPAD_BUTTON_LEFTSTICK = HidNpadButton_StickL, - GAMEPAD_BUTTON_RIGHTSTICK = HidNpadButton_StickR, - GAMEPAD_BUTTON_LEFT_SHOULDER = HidNpadButton_L, - GAMEPAD_BUTTON_RIGHT_SHOULDER = HidNpadButton_R, - GAMEPAD_BUTTON_DPAD_UP = HidNpadButton_Up, - GAMEPAD_BUTTON_DPAD_RIGHT = HidNpadButton_Right, - GAMEPAD_BUTTON_DPAD_DOWN = HidNpadButton_Down, - GAMEPAD_BUTTON_DPAD_LEFT = HidNpadButton_Left -}; - -namespace love -{ - class Gamepad : public common::Gamepad - { - public: - Gamepad(size_t id); - - Gamepad(size_t id, size_t index); - - virtual ~Gamepad(); - - bool Open(size_t id) override; - - void Close() override; - - bool IsConnected() const override; - - const char* GetName() const override; - - size_t GetAxisCount() const override; - - size_t GetButtonCount() const override; - - float GetAxis(size_t axis) const override; - - std::vector GetAxes() const override; - - bool IsDown(const std::vector& buttons) const override; - - float GetGamepadAxis(GamepadAxis axis) const override; - - bool IsGamepadDown(const std::vector& buttons) const override; - - bool IsVibrationSupported() override; - - bool SetVibration(float left, float right, float duration = -1.0f) override; - - bool SetVibration() override; - - void GetVibration(float& left, float& right) override; - - const Vibration& GetVibration() const; - - void UpdatePadState(); - - bool IsDown(size_t index, ButtonMapping& mapping) override; - - bool IsHeld(size_t index, ButtonMapping& mapping) const override; - - bool IsUp(size_t index, ButtonMapping& mapping) override; - - static constexpr uint8_t GAMEPAD_MAX_BUTTONS = 15; - - static bool GetConstant(const char* in, GamepadAxis& out); - static bool GetConstant(GamepadAxis in, const char*& out); - - static bool GetConstant(const char* in, GamepadButton& out); - static bool GetConstant(GamepadButton in, const char*& out); - - static constexpr uint8_t MAX_BUTTONS = 15; - static constexpr uint8_t MAX_AXES = 6; - - const static auto& GetButtonMapping() - { - return Gamepad::buttons; - } - - private: - PadState pad; - u32 style; - - std::unique_ptr sixAxisHandles; - - std::unique_ptr vibrationHandles; - HidVibrationValue vibrationValues[2]; - - HidNpadStyleTag GetStyleTag(); - - HidNpadIdType GetNpadIdType(); - - // clang-format off - static constexpr auto axes = BidirectionalMap<>::Create( - "leftx", Gamepad::GamepadAxis::GAMEPAD_AXIS_LEFTX, - "lefty", Gamepad::GamepadAxis::GAMEPAD_AXIS_LEFTY, - "rightx", Gamepad::GamepadAxis::GAMEPAD_AXIS_RIGHTX, - "righty", Gamepad::GamepadAxis::GAMEPAD_AXIS_RIGHTY, - "triggerleft", Gamepad::GamepadAxis::GAMEPAD_AXIS_TRIGGERLEFT, - "triggerright", Gamepad::GamepadAxis::GAMEPAD_AXIS_TRIGGERRIGHT - ); - - static constexpr auto buttons = BidirectionalMap<>::Create( - "a", Gamepad::GamepadButton::GAMEPAD_BUTTON_A, - "b", Gamepad::GamepadButton::GAMEPAD_BUTTON_B, - "x", Gamepad::GamepadButton::GAMEPAD_BUTTON_X, - "y", Gamepad::GamepadButton::GAMEPAD_BUTTON_Y, - "back", Gamepad::GamepadButton::GAMEPAD_BUTTON_BACK, - "start", Gamepad::GamepadButton::GAMEPAD_BUTTON_START, - "leftstick", Gamepad::GamepadButton::GAMEPAD_BUTTON_LEFTSTICK, - "rightstick", Gamepad::GamepadButton::GAMEPAD_BUTTON_RIGHTSTICK, - "leftshoulder", Gamepad::GamepadButton::GAMEPAD_BUTTON_LEFT_SHOULDER, - "rightshoulder", Gamepad::GamepadButton::GAMEPAD_BUTTON_RIGHT_SHOULDER, - "dpup", Gamepad::GamepadButton::GAMEPAD_BUTTON_DPAD_UP, - "dpdown", Gamepad::GamepadButton::GAMEPAD_BUTTON_DPAD_DOWN, - "dpleft", Gamepad::GamepadButton::GAMEPAD_BUTTON_DPAD_LEFT, - "dpright", Gamepad::GamepadButton::GAMEPAD_BUTTON_DPAD_RIGHT - ); - // clang-format on - - struct - { - uint64_t pressed; - uint64_t released; - } buttonStates; - }; -} // namespace love diff --git a/platform/switch/include/objects/glyphdata/glyphdata.h b/platform/switch/include/objects/glyphdata/glyphdata.h deleted file mode 100644 index 1796ebcc6..000000000 --- a/platform/switch/include/objects/glyphdata/glyphdata.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "objects/glyphdata/glyphdatac.h" - -namespace love -{ - class GlyphData : public common::GlyphData - { - public: - GlyphData(uint32_t glyph, GlyphMetrics metrics); - - GlyphData(const GlyphData& c); - - virtual ~GlyphData(); - - GlyphData* Clone() const override; - - size_t GetPixelSize() const; - - void* GetData() const; - - size_t GetSize() const; - - void* GetData(int x, int y) const; - - private: - uint8_t* data; - }; -} // namespace love diff --git a/platform/switch/include/objects/quad/quad.h b/platform/switch/include/objects/quad/quad.h deleted file mode 100644 index 77cd85e3d..000000000 --- a/platform/switch/include/objects/quad/quad.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "objects/quad/quadc.h" - -namespace love -{ - class Quad : public common::Quad - { - public: - Quad(const Viewport& viewport, double sw, double sh); - - virtual ~Quad() - {} - - void Refresh(const Viewport& viewport, double sw, double sh) override; - - const Vector2* GetVertexPositions() const; - const Vector2* GetVertexTexCoords() const; - - private: - Vector2 vertexPositions[4]; - Vector2 vertexTexCoords[4]; - }; -} // namespace love \ No newline at end of file diff --git a/platform/switch/include/objects/shader/wrap_shader.h b/platform/switch/include/objects/shader/wrap_shader.h deleted file mode 100644 index 38d73ea59..000000000 --- a/platform/switch/include/objects/shader/wrap_shader.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "common/luax.h" -#include "deko3d/shader.h" -namespace Wrap_Shader -{ - love::Shader* CheckShader(lua_State* L, int index); - - int Register(lua_State* L); -} // namespace Wrap_Shader diff --git a/platform/switch/include/objects/source/source.h b/platform/switch/include/objects/source/source.h deleted file mode 100644 index df6ad545c..000000000 --- a/platform/switch/include/objects/source/source.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include "objects/source/sourcec.h" - -namespace love -{ - class Source : public common::Source - { - public: - Source(Pool* pool, SoundData* sound); - - Source(Pool* pool, Decoder* decoder); - - Source(const Source& other); - - virtual ~Source(); - - Source* Clone(); - - void SetLooping(bool should) override; - - bool Update() override; - - bool IsPlaying() const override; - - bool IsFinished() const override; - - void SetVolume(float volume) override; - - void StopAtomic() override; - - protected: - double GetSampleOffset() override; - - void ClearChannel() override; - - private: - AudioDriverWaveBuf sources[Source::MAX_BUFFERS]; - - void Reset() override; - - void InitializeStreamBuffers(Decoder* decoder) override; - - void PrepareAtomic() override; - - int StreamAtomic(size_t which) override; - - bool PlayAtomic() override; - - void PauseAtomic() override; - - void ResumeAtomic() override; - - void FreeBuffer() override; - }; -} // namespace love diff --git a/platform/switch/include/objects/text/text.h b/platform/switch/include/objects/text/text.h deleted file mode 100644 index 44d21c146..000000000 --- a/platform/switch/include/objects/text/text.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include "deko3d/vertex.h" -#include "objects/text/textc.h" - -namespace love -{ - class Text : public common::Text - { - public: - Text(Font* font, const std::vector& text = {}); - - virtual ~Text(); - - void SetFont(Font* font); - - void Set(const std::vector& text) override; - - void Set(const std::vector& text, float wrap, - Font::AlignMode align) override; - - int Add(const std::vector& text, - const Matrix4& localTransform) override; - - int Addf(const std::vector& text, float wrap, Font::AlignMode align, - const Matrix4& localTransform) override; - - int GetWidth(int index = 0) const override; - - int GetHeight(int index = 0) const override; - - void Draw(Graphics* gfx, const Matrix4& localTransform) override; - - void Clear() override; - - private: - struct TextData - { - Font::ColoredCodepoints codepoints; - float wrap; - Font::AlignMode align; - Font::TextInfo textInfo; - bool useMatrix; - bool appendVertices; - Matrix4 matrix; - }; - - std::vector drawCommands; - - std::vector textData; - - std::vector vertexBuffer; - - size_t vertexOffset; - - uint32_t textureCacheId; - - void CopyVertices(const std::vector& vertices, size_t vertoffset); - - void RegenerateVertices(); - - void AddTextData(const TextData& textData); - }; -} // namespace love diff --git a/platform/switch/include/objects/texture/texture.h b/platform/switch/include/objects/texture/texture.h deleted file mode 100644 index 3f966818a..000000000 --- a/platform/switch/include/objects/texture/texture.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "deko3d/CImage.h" -#include "objects/texture/texturec.h" - -namespace love -{ - class Texture : public common::Texture - { - public: - Texture(TextureType type); - - virtual ~Texture(); - - void SetHandle(DkResHandle handle); - - DkResHandle GetHandle(); - - static constexpr int TEXTURE_QUAD_POINT_COUNT = 4; - - void Draw(Graphics* gfx, const Matrix4& localTransform) override; - - void Draw(Graphics* gfx, love::Quad* quad, const Matrix4& localTransform) override; - - bool SetWrap(const Wrap& wrap) override; - - void SetFilter(const Filter& filter) override; - - protected: - DkResHandle handle; - CImage texture; - }; -} // namespace love diff --git a/platform/switch/include/objects/thread/thread.h b/platform/switch/include/objects/thread/thread.h deleted file mode 100644 index 4f8a0aa38..000000000 --- a/platform/switch/include/objects/thread/thread.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "thread/threadc.h" - -namespace love -{ - class Thread : public common::Thread - { - public: - Thread(Threadable* t); - - /* Detach the thread */ - virtual ~Thread(); - - virtual bool Start() override; - - /* - ** In SDL, SDL_WaitThread will wait - ** for the thread to be finished and then - ** SDL frees the pointer to the thread - */ - virtual void Wait() override; - }; - - inline Thread* newThread(Threadable* t) - { - return new Thread(t); - } -} // namespace love diff --git a/platform/switch/include/objects/truetyperasterizer/truetyperasterizer.h b/platform/switch/include/objects/truetyperasterizer/truetyperasterizer.h deleted file mode 100644 index 97c002e10..000000000 --- a/platform/switch/include/objects/truetyperasterizer/truetyperasterizer.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include "objects/rasterizer/rasterizer.h" - -#include "common/strongref.h" - -// FreeType2 -#include -#include FT_FREETYPE_H -#include FT_GLYPH_H - -#include - -namespace love -{ - class TrueTypeRasterizer : public Rasterizer - { - public: - /* - ** Types of hinting - ** for TrueType font glyphs. - */ - enum Hinting - { - HINTING_NORMAL, - HINTING_LIGHT, - HINTING_MONO, - HINTING_NONE, - HINTING_MAX_ENUM - }; - - TrueTypeRasterizer(FT_Library library, love::Data* data, int size, Hinting hinting); - - virtual ~TrueTypeRasterizer(); - - // Implement Rasterizer - int GetLineHeight() const override; - - GlyphData* GetGlyphData(uint32_t glyph) const override; - - int GetGlyphCount() const override; - - bool HasGlyph(uint32_t glyph) const override; - - float GetKerning(uint32_t leftglyph, uint32_t rightglyph) const override; - - DataType GetDataType() const override; - - static bool Accepts(FT_Library library, love::Data* data); - - static bool GetConstant(const char* in, Hinting& out); - static bool GetConstant(Hinting in, const char*& out); - static std::vector GetConstants(Hinting); - - private: - FT_Face face; - - StrongReference data; - - Hinting hinting; - - static FT_UInt HintingToLoadOption(Hinting hinting); - }; -} // namespace love diff --git a/platform/switch/include/objects/video/video.h b/platform/switch/include/objects/video/video.h deleted file mode 100644 index edc5150a6..000000000 --- a/platform/switch/include/objects/video/video.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "deko3d/vertex.h" -#include "objects/video/videoc.h" - -namespace love -{ - class Video : public common::Video - { - public: - Video(Graphics* graphics, VideoStream* stream, float dpiScale = 1.0f); - - virtual ~Video(); - - void Draw(Graphics* graphics, const Matrix4& matrix) override; - - protected: - void Update() override; - - private: - vertex::Vertex vertices[4]; - }; -} // namespace love diff --git a/platform/switch/include/objects/videostream/theorastream.h b/platform/switch/include/objects/videostream/theorastream.h deleted file mode 100644 index f9a4169a9..000000000 --- a/platform/switch/include/objects/videostream/theorastream.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "objects/videostream/theora/theorastreamc.h" - -namespace love -{ - class TheoraStream : public common::TheoraStream - { - public: - TheoraStream(File* file); - - struct Frame : VideoStream::IFrame - { - Frame(); - - ~Frame(); - - int yw, yh; - uint8_t* yPlane; - - int cw, ch; - uint8_t* cbPlane; - uint8_t* crPlane; - }; - - virtual size_t GetSize() const override; - - virtual void SetupBuffers() override; - - virtual void FillBufferData(th_ycbcr_buffer bufferInfo) override; - - private: - unsigned yPlaneXOffset; - unsigned cPlaneXOffset; - unsigned yPlaneYOffset; - unsigned cPlaneYOffset; - }; -} // namespace love diff --git a/platform/switch/include/polyline/beveljoin.h b/platform/switch/include/polyline/beveljoin.h deleted file mode 100644 index f5a5b9fd7..000000000 --- a/platform/switch/include/polyline/beveljoin.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "polyline/polyline.h" - -namespace love -{ - /* - ** A Polyline whose segments - ** are connected by a flat edge. - */ - class BevelJoinPolyline : public Polyline - { - public: - void Render(const Vector2* coords, size_t count, float halfWidth, float pixelSize, - bool drawOverdraw) - { - Polyline::Render(coords, count, 4 * count - 4, halfWidth, pixelSize, drawOverdraw); - } - - protected: - void RenderEdge(std::vector& anchors, std::vector& normals, Vector2& s, - float& len_s, Vector2& ns, const Vector2& q, const Vector2& r, - float hw) override; - }; -} // namespace love diff --git a/platform/switch/include/polyline/common.h b/platform/switch/include/polyline/common.h deleted file mode 100644 index 7dd55c4a7..000000000 --- a/platform/switch/include/polyline/common.h +++ /dev/null @@ -1,11 +0,0 @@ -/* -** This code is mostly from LÖVE's actual source code for the Polyline header -** and sources. However, it has been adapted to work with deko3d. -** -** Original code for Polyline, etc by Matthias Richter -*/ - -#include "polyline/beveljoin.h" -#include "polyline/miterjoin.h" -#include "polyline/nonejoin.h" -#include "polyline/polyline.h" diff --git a/platform/switch/include/polyline/miterjoin.h b/platform/switch/include/polyline/miterjoin.h deleted file mode 100644 index 1524390f0..000000000 --- a/platform/switch/include/polyline/miterjoin.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "polyline/polyline.h" - -namespace love -{ - /* - ** A Polyline whose segments - ** are connected by a sharp edge. - */ - class MiterJoinPolyline : public Polyline - { - public: - void Render(const Vector2* coords, size_t count, float halfWidth, float pixelSize, - bool drawOverdraw) - { - Polyline::Render(coords, count, 2 * count, halfWidth, pixelSize, drawOverdraw); - } - - protected: - void RenderEdge(std::vector& anchors, std::vector& normals, Vector2& s, - float& len_s, Vector2& ns, const Vector2& q, const Vector2& r, - float hw) override; - }; -} // namespace love diff --git a/platform/switch/include/polyline/nonejoin.h b/platform/switch/include/polyline/nonejoin.h deleted file mode 100644 index 2bda8ad14..000000000 --- a/platform/switch/include/polyline/nonejoin.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "polyline/polyline.h" - -namespace love -{ - /* - ** A Polyline whose segments - ** are not connected. - */ - class NoneJoinPolyline : public Polyline - { - public: - NoneJoinPolyline() : Polyline(vertex::TriangleIndexMode::QUADS) - { - this->triangleMode = DkPrimitive_Quads; - } - - void Render(const Vector2* vertices, size_t count, float halfWidth, float pixelSize, - bool drawOverdraw) - { - Polyline::Render(vertices, count, 4 * count - 4, halfWidth, pixelSize, drawOverdraw); - - // discard the first and last two vertices. (these are redundant) - for (size_t i = 0; i < this->vertexCount - 4; ++i) - this->vertices[i] = this->vertices[i + 2]; - - // The last quad is now garbage, so zero it out to make sure it doesn't - // get rasterized. These vertices are in between the core line vertices - // and the overdraw vertices in the combined vertex array, so they still - // get "rendered" since we draw everything with one draw call. - std::fill_n(this->vertices + (this->vertexCount - 4), 4, Vector2 {}); - - this->vertexCount -= 4; - } - - protected: - void CalculateOverdrawVertexCount(bool /* isLooping */) override; - - void RenderOverdraw(const std::vector& normals, float pixelSize, - bool isLooping) override; - - void FillColorArray(Colorf constantColor, Colorf* colors, int count) override; - - void RenderEdge(std::vector& anchors, std::vector& normals, Vector2& s, - float& lengthS, Vector2& normalS, const Vector2& q, const Vector2& r, - float halfWidth) override; - }; -} // namespace love diff --git a/platform/switch/include/polyline/polyline.h b/platform/switch/include/polyline/polyline.h deleted file mode 100644 index 6b81771b3..000000000 --- a/platform/switch/include/polyline/polyline.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include "deko3d/vertex.h" -#include - -namespace love -{ - class Graphics; - - /* - ** Abstract base class - ** for a chain of segments. - */ - class Polyline - { - public: - Polyline(vertex::TriangleIndexMode mode = vertex::TriangleIndexMode::STRIP) : - vertices(nullptr), - overdraw(nullptr), - vertexCount(0), - overdrawVertexCount(0), - triangleIndexMode(mode), - overdrawVertexStart(0) - {} - - virtual ~Polyline(); - - /** - * @param coords Vertices defining the core line segments - * @param count Number of vertices - * @param size_hint Expected number of vertices of the rendering sleeve around the core - * line. - * @param halfwidth linewidth / 2. - * @param pixel_size Dimension of one pixel on the screen in world coordinates. - * @param draw_overdraw Fake antialias the line. - */ - void Render(const Vector2* coords, size_t count, size_t sizeHint, float halfWidth, - float pixelSize, bool drawOverdraw); - - void Draw(Graphics* graphics); - - protected: - virtual void CalculateOverdrawVertexCount(bool isLooping); - - virtual void RenderOverdraw(const std::vector& normals, float pixelSize, - bool isLooping); - - /* Enables a "fake" anti-aliasing line border */ - virtual void FillColorArray(Colorf constant, Colorf* colors, int count); - - /** Calculate line boundary points. - * - * @param[out] anchors Anchor points defining the core line. - * @param[out] normals Normals defining the edge of the sleeve. - * @param[in,out] s Direction of segment pq (updated to the segment qr). - * @param[in,out] len_s Length of segment pq (updated to the segment qr). - * @param[in,out] ns Normal on the segment pq (updated to the segment qr). - * @param[in] q Current point on the line. - * @param[in] r Next point on the line. - * @param[in] hw Half line width (see Polyline.render()). - */ - virtual void RenderEdge(std::vector& anchors, std::vector& normals, - Vector2& s, float& len_s, Vector2& ns, const Vector2& q, - const Vector2& r, float hw) = 0; - - static constexpr float LINES_PARALLEL_EPS = 0.05f; - - Vector2* vertices; - Vector2* overdraw; - - size_t vertexCount; - size_t overdrawVertexCount; - - vertex::TriangleIndexMode triangleIndexMode; - size_t overdrawVertexStart; - - DkPrimitive triangleMode = DkPrimitive_TriangleStrip; - }; -} // namespace love diff --git a/platform/switch/include/pools/audiopool.h b/platform/switch/include/pools/audiopool.h deleted file mode 100644 index 1f52b8839..000000000 --- a/platform/switch/include/pools/audiopool.h +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include - -namespace AudioPool -{ - // Initialize BASE during audio module load? - extern void* AUDIO_POOL_BASE; - inline u64 AUDIO_POOL_SIZE = 0x1000000; - - struct MemoryChunk - { - u8* address; - size_t size; - }; - - struct MemoryBlock - { - MemoryBlock* prev; - MemoryBlock* next; - - u8* base; - size_t size; - - static MemoryBlock* Create(u8* base, size_t size) - { - auto block = (MemoryBlock*)malloc(sizeof(MemoryBlock)); - - if (!block) - return nullptr; - - block->prev = nullptr; - block->next = nullptr; - - block->base = base; - block->size = size; - - return block; - } - }; - - struct MemoryPool - { - MemoryBlock* first; - MemoryBlock* last; - - bool Ready() - { - return first != nullptr; - } - - void AddBlock(MemoryBlock* block) - { - block->prev = last; - - if (last) - last->next = block; - - if (!first) - first = block; - - last = block; - } - - void DeleteBlock(MemoryBlock* block) - { - auto prev = block->prev; - auto& pNext = (prev) ? prev->next : first; - - auto next = block->next; - auto& nPrev = (next) ? next->prev : last; - - pNext = next; - nPrev = prev; - - free(block); - } - - void InsertBefore(MemoryBlock* b, MemoryBlock* p) - { - auto prev = b->prev; - auto& pNext = (prev) ? prev->next : first; - - b->prev = p; - p->next = b; - - p->prev = prev; - - pNext = p; - } - - void InsertAfter(MemoryBlock* b, MemoryBlock* n) - { - auto next = b->next; - auto& nPrev = (next) ? next->prev : last; - - b->next = n; - n->prev = b; - - n->next = next; - - nPrev = n; - } - - void CoalesceRight(MemoryBlock* block); - - bool Allocate(MemoryChunk& chunk, size_t size); - void DeAllocate(u8* address, size_t size); - - void Destroy() - { - MemoryBlock* next = nullptr; - - for (auto block = first; block; block = next) - { - next = block->next; - free(block); - } - - first = nullptr; - last = nullptr; - } - }; - - extern MemoryPool audioPool; - - bool Initialize(); - - std::pair MemoryAlign(size_t size); - - void MemoryFree(const std::pair& chunk); -} // namespace AudioPool diff --git a/platform/switch/include/pools/vibration.h b/platform/switch/include/pools/vibration.h deleted file mode 100644 index dffaa65d5..000000000 --- a/platform/switch/include/pools/vibration.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "objects/gamepad/gamepad.h" - -#include "thread/types/lock.h" -#include "thread/types/mutex.h" - -#include - -namespace love -{ - class VibrationPool - { - public: - ~VibrationPool() - {} - - bool IsRunning(); - - bool FindGamepad(Gamepad* gamepad); - - bool AssignGamepad(Gamepad* gamepad, size_t id); - - bool ReleaseGamepad(Gamepad* gamepad, bool stop = true); - - void Finish(); - - void Update(); - - thread::Lock Lock(); - - private: - std::atomic running = true; - std::map vibrating; - thread::MutexRef mutex; - }; -} // namespace love \ No newline at end of file diff --git a/platform/switch/romfs/graphics/messagebox_dark.png b/platform/switch/romfs/graphics/messagebox_dark.png deleted file mode 100644 index 64db6ab4e..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_dark.png and /dev/null differ diff --git a/platform/switch/romfs/graphics/messagebox_dark_pressed.png b/platform/switch/romfs/graphics/messagebox_dark_pressed.png deleted file mode 100644 index 026253010..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_dark_pressed.png and /dev/null differ diff --git a/platform/switch/romfs/graphics/messagebox_dark_two_left.png b/platform/switch/romfs/graphics/messagebox_dark_two_left.png deleted file mode 100644 index f69292c0b..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_dark_two_left.png and /dev/null differ diff --git a/platform/switch/romfs/graphics/messagebox_dark_two_left_pressed.png b/platform/switch/romfs/graphics/messagebox_dark_two_left_pressed.png deleted file mode 100644 index b9a256c35..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_dark_two_left_pressed.png and /dev/null differ diff --git a/platform/switch/romfs/graphics/messagebox_dark_two_none.png b/platform/switch/romfs/graphics/messagebox_dark_two_none.png deleted file mode 100644 index c0896b077..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_dark_two_none.png and /dev/null differ diff --git a/platform/switch/romfs/graphics/messagebox_dark_two_right.png b/platform/switch/romfs/graphics/messagebox_dark_two_right.png deleted file mode 100644 index f232cb916..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_dark_two_right.png and /dev/null differ diff --git a/platform/switch/romfs/graphics/messagebox_dark_two_right_pressed.png b/platform/switch/romfs/graphics/messagebox_dark_two_right_pressed.png deleted file mode 100644 index 787d9043c..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_dark_two_right_pressed.png and /dev/null differ diff --git a/platform/switch/romfs/graphics/messagebox_light.png b/platform/switch/romfs/graphics/messagebox_light.png deleted file mode 100644 index 8c790054d..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_light.png and /dev/null differ diff --git a/platform/switch/romfs/graphics/messagebox_light_pressed.png b/platform/switch/romfs/graphics/messagebox_light_pressed.png deleted file mode 100644 index 0b9a56d75..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_light_pressed.png and /dev/null differ diff --git a/platform/switch/romfs/graphics/messagebox_light_two_left.png b/platform/switch/romfs/graphics/messagebox_light_two_left.png deleted file mode 100644 index 3e2ed1e89..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_light_two_left.png and /dev/null differ diff --git a/platform/switch/romfs/graphics/messagebox_light_two_left_pressed.png b/platform/switch/romfs/graphics/messagebox_light_two_left_pressed.png deleted file mode 100644 index f27a5a652..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_light_two_left_pressed.png and /dev/null differ diff --git a/platform/switch/romfs/graphics/messagebox_light_two_none.png b/platform/switch/romfs/graphics/messagebox_light_two_none.png deleted file mode 100644 index 7cae63585..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_light_two_none.png and /dev/null differ diff --git a/platform/switch/romfs/graphics/messagebox_light_two_right.png b/platform/switch/romfs/graphics/messagebox_light_two_right.png deleted file mode 100644 index a97c562d3..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_light_two_right.png and /dev/null differ diff --git a/platform/switch/romfs/graphics/messagebox_light_two_right_pressed.png b/platform/switch/romfs/graphics/messagebox_light_two_right_pressed.png deleted file mode 100644 index ff798f4b6..000000000 Binary files a/platform/switch/romfs/graphics/messagebox_light_two_right_pressed.png and /dev/null differ diff --git a/platform/switch/source/common/matrix.cpp b/platform/switch/source/common/matrix.cpp deleted file mode 100644 index 8b1046eab..000000000 --- a/platform/switch/source/common/matrix.cpp +++ /dev/null @@ -1,340 +0,0 @@ -#include "common/matrix.h" - -#include -#include - -using namespace love; - -void Matrix4::Multiply(const Matrix4& a, const Matrix4& b, Elements& t) -{ - float32x4_t cola1 = vld1q_f32(&a.matrix[0]); - float32x4_t cola2 = vld1q_f32(&a.matrix[4]); - float32x4_t cola3 = vld1q_f32(&a.matrix[8]); - float32x4_t cola4 = vld1q_f32(&a.matrix[12]); - - float32x4_t col1 = vmulq_n_f32(cola1, b.matrix[0]); - col1 = vmlaq_n_f32(col1, cola2, b.matrix[1]); - col1 = vmlaq_n_f32(col1, cola3, b.matrix[2]); - col1 = vmlaq_n_f32(col1, cola4, b.matrix[3]); - - float32x4_t col2 = vmulq_n_f32(cola1, b.matrix[4]); - col2 = vmlaq_n_f32(col2, cola2, b.matrix[5]); - col2 = vmlaq_n_f32(col2, cola3, b.matrix[6]); - col2 = vmlaq_n_f32(col2, cola4, b.matrix[7]); - - float32x4_t col3 = vmulq_n_f32(cola1, b.matrix[8]); - col3 = vmlaq_n_f32(col3, cola2, b.matrix[9]); - col3 = vmlaq_n_f32(col3, cola3, b.matrix[10]); - col3 = vmlaq_n_f32(col3, cola4, b.matrix[11]); - - float32x4_t col4 = vmulq_n_f32(cola1, b.matrix[12]); - col4 = vmlaq_n_f32(col4, cola2, b.matrix[13]); - col4 = vmlaq_n_f32(col4, cola3, b.matrix[14]); - col4 = vmlaq_n_f32(col4, cola4, b.matrix[15]); - - vst1q_f32(&t[0], col1); - vst1q_f32(&t[4], col2); - vst1q_f32(&t[8], col3); - vst1q_f32(&t[12], col4); -} - -void Matrix4::Multiply(const Matrix4& a, const Matrix4& b, Matrix4& t) -{ - Matrix4::Multiply(a, b, t.matrix); -} - -Matrix4::Matrix4() -{ - this->SetIdentity(); -} - -Matrix4::Matrix4(const Elements& elements) -{ - memcpy(this->matrix, elements, sizeof(float) * 16); -} - -Matrix4::Matrix4(const Matrix4& a, const Matrix4& b) -{ - Matrix4::Multiply(a, b, this->matrix); -} - -Matrix4::Matrix4(float t00, float t10, float t01, float t11, float x, float y) -{ - this->SetRawTransformation(t00, t10, t01, t11, x, y); -} - -Matrix4::Matrix4(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, - float ky) -{ - this->SetTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); -} - -bool Matrix4::IsAffine2DTransform() const -{ - return fabsf(this->matrix[2] + this->matrix[3] + this->matrix[6] + this->matrix[7] + - this->matrix[8] + this->matrix[9] + this->matrix[11] + this->matrix[14]) < - 0.00001f && - fabsf(this->matrix[10] + this->matrix[15] - 2.0f) < 0.00001f; -} - -Matrix4 Matrix4::operator*(const Matrix4& m) const -{ - return Matrix4(*this, m); -} - -void Matrix4::operator*=(const Matrix4& m) -{ - Elements matrix; - Matrix4::Multiply(*this, m, matrix); - memcpy(this->matrix, matrix, sizeof(float) * 16); -} - -const Elements& Matrix4::GetElements() const -{ - return this->matrix; -} - -void Matrix4::SetIdentity() -{ - memset(this->matrix, 0, sizeof(Elements)); - this->matrix[15] = this->matrix[10] = this->matrix[5] = this->matrix[0] = 1.0f; -} - -void Matrix4::SetTranslation(float x, float y) -{ - this->SetIdentity(); - - this->matrix[12] = x; - this->matrix[13] = y; -} - -void Matrix4::Translate(float x, float y) -{ - Matrix4 t; - t.SetTranslation(x, y); - this->operator*=(t); -} - -void Matrix4::SetRotation(float rad) -{ - this->SetIdentity(); - float c = cosf(rad), s = sinf(rad); - - this->matrix[0] = c; - this->matrix[4] = -s; - this->matrix[1] = s; - this->matrix[5] = c; -} - -void Matrix4::Rotate(float rad) -{ - Matrix4 t; - t.SetRotation(rad); - this->operator*=(t); -} - -void Matrix4::SetScale(float sx, float sy) -{ - this->SetIdentity(); - - this->matrix[0] = sx; - this->matrix[5] = sy; -} - -void Matrix4::Scale(float sx, float sy) -{ - Matrix4 t; - t.SetScale(sx, sy); - this->operator*=(t); -} - -void Matrix4::SetShear(float kx, float ky) -{ - this->SetIdentity(); - - this->matrix[1] = ky; - this->matrix[4] = kx; -} - -void Matrix4::Shear(float kx, float ky) -{ - Matrix4 t; - t.SetShear(kx, ky); - this->operator*=(t); -} - -void Matrix4::GetApproximateScale(float& sx, float& sy) const -{ - sx = sqrtf(this->matrix[0] * this->matrix[0] + this->matrix[4] * this->matrix[4]); - sy = sqrtf(this->matrix[1] * this->matrix[1] + this->matrix[5] * this->matrix[5]); -} - -void Matrix4::SetRawTransformation(float t00, float t10, float t01, float t11, float x, float y) -{ - memset(this->matrix, 0, sizeof(float) * 16); // zero out matrix - - this->matrix[10] = this->matrix[15] = 1.0f; - this->matrix[0] = t00; - this->matrix[1] = t10; - this->matrix[4] = t01; - this->matrix[5] = t11; - this->matrix[12] = x; - this->matrix[13] = y; -} - -void Matrix4::SetTransformation(float x, float y, float angle, float sx, float sy, float ox, - float oy, float kx, float ky) -{ - memset(this->matrix, 0, sizeof(float) * 16); // zero out matrix - float c = cosf(angle), s = sinf(angle); - - this->matrix[10] = this->matrix[15] = 1.0f; - this->matrix[0] = c * sx - ky * s * sy; // = a - this->matrix[1] = s * sx + ky * c * sy; // = b - this->matrix[4] = kx * c * sx - s * sy; // = c - this->matrix[5] = kx * s * sx + c * sy; // = d - this->matrix[12] = x - ox * this->matrix[0] - oy * this->matrix[4]; - this->matrix[13] = y - ox * this->matrix[1] - oy * this->matrix[5]; -} - -Matrix4 Matrix4::Inverse() const -{ - Matrix4 inv; - - inv.matrix[0] = this->matrix[5] * this->matrix[10] * this->matrix[15] - - this->matrix[5] * this->matrix[11] * this->matrix[14] - - this->matrix[9] * this->matrix[6] * this->matrix[15] + - this->matrix[9] * this->matrix[7] * this->matrix[14] + - this->matrix[13] * this->matrix[6] * this->matrix[11] - - this->matrix[13] * this->matrix[7] * this->matrix[10]; - - inv.matrix[4] = -this->matrix[4] * this->matrix[10] * this->matrix[15] + - this->matrix[4] * this->matrix[11] * this->matrix[14] + - this->matrix[8] * this->matrix[6] * this->matrix[15] - - this->matrix[8] * this->matrix[7] * this->matrix[14] - - this->matrix[12] * this->matrix[6] * this->matrix[11] + - this->matrix[12] * this->matrix[7] * this->matrix[10]; - - inv.matrix[8] = this->matrix[4] * this->matrix[9] * this->matrix[15] - - this->matrix[4] * this->matrix[11] * this->matrix[13] - - this->matrix[8] * this->matrix[5] * this->matrix[15] + - this->matrix[8] * this->matrix[7] * this->matrix[13] + - this->matrix[12] * this->matrix[5] * this->matrix[11] - - this->matrix[12] * this->matrix[7] * this->matrix[9]; - - inv.matrix[12] = -this->matrix[4] * this->matrix[9] * this->matrix[14] + - this->matrix[4] * this->matrix[10] * this->matrix[13] + - this->matrix[8] * this->matrix[5] * this->matrix[14] - - this->matrix[8] * this->matrix[6] * this->matrix[13] - - this->matrix[12] * this->matrix[5] * this->matrix[10] + - this->matrix[12] * this->matrix[6] * this->matrix[9]; - - inv.matrix[1] = -this->matrix[1] * this->matrix[10] * this->matrix[15] + - this->matrix[1] * this->matrix[11] * this->matrix[14] + - this->matrix[9] * this->matrix[2] * this->matrix[15] - - this->matrix[9] * this->matrix[3] * this->matrix[14] - - this->matrix[13] * this->matrix[2] * this->matrix[11] + - this->matrix[13] * this->matrix[3] * this->matrix[10]; - - inv.matrix[5] = this->matrix[0] * this->matrix[10] * this->matrix[15] - - this->matrix[0] * this->matrix[11] * this->matrix[14] - - this->matrix[8] * this->matrix[2] * this->matrix[15] + - this->matrix[8] * this->matrix[3] * this->matrix[14] + - this->matrix[12] * this->matrix[2] * this->matrix[11] - - this->matrix[12] * this->matrix[3] * this->matrix[10]; - - inv.matrix[9] = -this->matrix[0] * this->matrix[9] * this->matrix[15] + - this->matrix[0] * this->matrix[11] * this->matrix[13] + - this->matrix[8] * this->matrix[1] * this->matrix[15] - - this->matrix[8] * this->matrix[3] * this->matrix[13] - - this->matrix[12] * this->matrix[1] * this->matrix[11] + - this->matrix[12] * this->matrix[3] * this->matrix[9]; - - inv.matrix[13] = this->matrix[0] * this->matrix[9] * this->matrix[14] - - this->matrix[0] * this->matrix[10] * this->matrix[13] - - this->matrix[8] * this->matrix[1] * this->matrix[14] + - this->matrix[8] * this->matrix[2] * this->matrix[13] + - this->matrix[12] * this->matrix[1] * this->matrix[10] - - this->matrix[12] * this->matrix[2] * this->matrix[9]; - - inv.matrix[2] = this->matrix[1] * this->matrix[6] * this->matrix[15] - - this->matrix[1] * this->matrix[7] * this->matrix[14] - - this->matrix[5] * this->matrix[2] * this->matrix[15] + - this->matrix[5] * this->matrix[3] * this->matrix[14] + - this->matrix[13] * this->matrix[2] * this->matrix[7] - - this->matrix[13] * this->matrix[3] * this->matrix[6]; - - inv.matrix[6] = -this->matrix[0] * this->matrix[6] * this->matrix[15] + - this->matrix[0] * this->matrix[7] * this->matrix[14] + - this->matrix[4] * this->matrix[2] * this->matrix[15] - - this->matrix[4] * this->matrix[3] * this->matrix[14] - - this->matrix[12] * this->matrix[2] * this->matrix[7] + - this->matrix[12] * this->matrix[3] * this->matrix[6]; - - inv.matrix[10] = this->matrix[0] * this->matrix[5] * this->matrix[15] - - this->matrix[0] * this->matrix[7] * this->matrix[13] - - this->matrix[4] * this->matrix[1] * this->matrix[15] + - this->matrix[4] * this->matrix[3] * this->matrix[13] + - this->matrix[12] * this->matrix[1] * this->matrix[7] - - this->matrix[12] * this->matrix[3] * this->matrix[5]; - - inv.matrix[14] = -this->matrix[0] * this->matrix[5] * this->matrix[14] + - this->matrix[0] * this->matrix[6] * this->matrix[13] + - this->matrix[4] * this->matrix[1] * this->matrix[14] - - this->matrix[4] * this->matrix[2] * this->matrix[13] - - this->matrix[12] * this->matrix[1] * this->matrix[6] + - this->matrix[12] * this->matrix[2] * this->matrix[5]; - - inv.matrix[3] = -this->matrix[1] * this->matrix[6] * this->matrix[11] + - this->matrix[1] * this->matrix[7] * this->matrix[10] + - this->matrix[5] * this->matrix[2] * this->matrix[11] - - this->matrix[5] * this->matrix[3] * this->matrix[10] - - this->matrix[9] * this->matrix[2] * this->matrix[7] + - this->matrix[9] * this->matrix[3] * this->matrix[6]; - - inv.matrix[7] = this->matrix[0] * this->matrix[6] * this->matrix[11] - - this->matrix[0] * this->matrix[7] * this->matrix[10] - - this->matrix[4] * this->matrix[2] * this->matrix[11] + - this->matrix[4] * this->matrix[3] * this->matrix[10] + - this->matrix[8] * this->matrix[2] * this->matrix[7] - - this->matrix[8] * this->matrix[3] * this->matrix[6]; - - inv.matrix[11] = -this->matrix[0] * this->matrix[5] * this->matrix[11] + - this->matrix[0] * this->matrix[7] * this->matrix[9] + - this->matrix[4] * this->matrix[1] * this->matrix[11] - - this->matrix[4] * this->matrix[3] * this->matrix[9] - - this->matrix[8] * this->matrix[1] * this->matrix[7] + - this->matrix[8] * this->matrix[3] * this->matrix[5]; - - inv.matrix[15] = this->matrix[0] * this->matrix[5] * this->matrix[10] - - this->matrix[0] * this->matrix[6] * this->matrix[9] - - this->matrix[4] * this->matrix[1] * this->matrix[10] + - this->matrix[4] * this->matrix[2] * this->matrix[9] + - this->matrix[8] * this->matrix[1] * this->matrix[6] - - this->matrix[8] * this->matrix[2] * this->matrix[5]; - - float det = this->matrix[0] * inv.matrix[0] + this->matrix[1] * inv.matrix[4] + - this->matrix[2] * inv.matrix[8] + this->matrix[3] * inv.matrix[12]; - - float invdet = 1.0f / det; - - for (int i = 0; i < 16; i++) - inv.matrix[i] *= invdet; - - return inv; -} - -Matrix4 Matrix4::Ortho(float left, float right, float bottom, float top, float near, float far) -{ - Matrix4 m; - - m.matrix[0] = 2.0f / (right - left); - m.matrix[5] = 2.0f / (top - bottom); - m.matrix[10] = -2.0f / (far - near); - - m.matrix[12] = -(right + left) / (right - left); - m.matrix[13] = -(top + bottom) / (top - bottom); - m.matrix[14] = -(far + near) / (far - near); - - return m; -} \ No newline at end of file diff --git a/platform/switch/source/common/screen.cpp b/platform/switch/source/common/screen.cpp deleted file mode 100644 index 436ea0a85..000000000 --- a/platform/switch/source/common/screen.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "common/screen.h" - -#include "common/bidirectionalmap.h" -#include "deko3d/deko.h" -#include "modules/graphics/graphics.h" - -using namespace love; - -int Screen::GetWidth(RenderScreen) -{ - if (::deko3d::Instance().IsHandheldMode()) - return Screen::HANDHELD_WIDTH; - - return Screen::DOCKED_WIDTH; -} - -int Screen::GetHeight() -{ - if (::deko3d::Instance().IsHandheldMode()) - return Screen::HANDHELD_HEIGHT; - - return Screen::DOCKED_HEIGHT; -} - -// clang-format off -constexpr auto ScreenTypes = BidirectionalMap<>::Create( - "default", Screen::HacScreen::HAC_SCREEN_DEFAULT -); -// clang-format on - -bool Screen::GetConstant(const char* in, RenderScreen& out) -{ - return Screen::FindSetCast(ScreenTypes, in, out); -} - -bool Screen::GetConstant(RenderScreen in, const char*& out) -{ - return Screen::ReverseFindSetCast(ScreenTypes, in, out); -} - -std::vector Screen::GetConstants(RenderScreen) -{ - return ScreenTypes.GetNames(); -} diff --git a/platform/switch/source/conditional.cpp b/platform/switch/source/conditional.cpp deleted file mode 100644 index 9e536103d..000000000 --- a/platform/switch/source/conditional.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "modules/thread/types/conditional.h" - -using namespace love::thread; - -Conditional::Conditional() -{ - condvarInit(&this->condVar); -} - -Conditional::~Conditional() -{} - -void Conditional::Signal() -{ - condvarWakeOne(&this->condVar); -} - -void Conditional::Broadcast() -{ - condvarWakeAll(&this->condVar); -} - -bool Conditional::Wait(thread::Mutex* _mutex, s64 timeout) -{ - if (timeout < 0) - condvarWait(&this->condVar, &_mutex->mutex); - else if (R_FAILED(condvarWaitTimeout(&this->condVar, &_mutex->mutex, timeout))) - return false; - - return true; -} diff --git a/platform/switch/source/deko3d/CImage.cpp b/platform/switch/source/deko3d/CImage.cpp deleted file mode 100644 index 15fae296c..000000000 --- a/platform/switch/source/deko3d/CImage.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/* -** Sample Framework for deko3d Applications -** CExternalImage.cpp: Utility class for loading images from the filesystem -*/ -#include "deko3d/CImage.h" -#include "deko3d/deko.h" - -#include "common/lmath.h" -#include "common/pixelformat.h" - -#include - -bool CImage::load(love::PixelFormat pixelFormat, bool isSRGB, void* buffer, size_t size, int width, - int height, bool empty) -{ - DkImageFormat format; - if (!::deko3d::GetConstant(pixelFormat, format)) - return false; - - if (!empty) - return this->loadMemory(::deko3d::Instance().GetImages(), ::deko3d::Instance().GetData(), - ::deko3d::Instance().GetDevice(), - ::deko3d::Instance().GetTextureQueue(), buffer, width, height, - format); - else - return this->loadEmptyPixels(::deko3d::Instance().GetImages(), - ::deko3d::Instance().GetData(), - ::deko3d::Instance().GetDevice(), - ::deko3d::Instance().GetTextureQueue(), width, height, format); -} - -/* replace the pixels at a location */ -bool CImage::replacePixels(CMemPool& scratchPool, dk::Device device, const void* data, size_t size, - dk::Queue transferQueue, const love::Rect& rect) -{ - if (data == nullptr) - return false; - - CMemPool::Handle tempImageMemory = scratchPool.allocate(size, DK_IMAGE_LINEAR_STRIDE_ALIGNMENT); - - if (!tempImageMemory) - return false; - - memcpy(tempImageMemory.getCpuAddr(), data, size); - - /* - ** We need to have a command buffer and some more memory for it - ** so allocate both and add the memory to the temporary command buffer - */ - dk::UniqueCmdBuf tempCmdBuff = dk::CmdBufMaker { device }.create(); - CMemPool::Handle tempCmdMem = scratchPool.allocate(DK_MEMBLOCK_ALIGNMENT); - tempCmdBuff.addMemory(tempCmdMem.getMemBlock(), tempCmdMem.getOffset(), tempCmdMem.getSize()); - - dk::ImageView imageView { m_image }; - tempCmdBuff.copyBufferToImage( - { tempImageMemory.getGpuAddr() }, imageView, - { uint32_t(rect.x), uint32_t(rect.y), 0, uint32_t(rect.w), uint32_t(rect.h), 1 }); - - // Submit the commands to the transfer queue - transferQueue.submitCommands(tempCmdBuff.finishList()); - transferQueue.waitIdle(); - - // Destroy the memory we don't need - tempCmdMem.destroy(); - tempImageMemory.destroy(); - - return true; -} - -/* load a CImage with transparent black pixels */ -bool CImage::loadEmptyPixels(CMemPool& imagePool, CMemPool& scratchPool, dk::Device device, - dk::Queue transferQueue, uint32_t width, uint32_t height, - DkImageFormat dkFormat, uint32_t flags) -{ - PixelFormat format; - if (!::deko3d::GetConstant(dkFormat, format)) - return false; - - size_t size = width * height * love::GetPixelFormatSize(format); - - if (size <= 0) - return false; - - CMemPool::Handle tempImageMemory = scratchPool.allocate(size, DK_IMAGE_LINEAR_STRIDE_ALIGNMENT); - - if (!tempImageMemory) - return false; - - /* memcpy for transparent black pixels */ - std::vector empty(size, 0); - memcpy(tempImageMemory.getCpuAddr(), empty.data(), sizeof(uint8_t) * empty.size()); - - /* - ** We need to have a command buffer and some more memory for it - ** so allocate both and add the memory to the temporary command buffer - */ - dk::UniqueCmdBuf tempCmdBuff = dk::CmdBufMaker { device }.create(); - CMemPool::Handle tempCmdMem = scratchPool.allocate(DK_MEMBLOCK_ALIGNMENT); - tempCmdBuff.addMemory(tempCmdMem.getMemBlock(), tempCmdMem.getOffset(), tempCmdMem.getSize()); - - // Set the image layout for the image - dk::ImageLayout layout; - dk::ImageLayoutMaker { device } - .setFlags(flags) - .setFormat(dkFormat) - .setDimensions(width, height) - .initialize(layout); - - // Create the image - m_mem = imagePool.allocate(layout.getSize(), layout.getAlignment()); - m_image.initialize(layout, m_mem.getMemBlock(), m_mem.getOffset()); - m_descriptor.initialize(m_image); - - dk::ImageView imageView { m_image }; - tempCmdBuff.copyBufferToImage({ tempImageMemory.getGpuAddr() }, imageView, - { 0, 0, 0, width, height, 1 }); - - // Submit the commands to the transfer queue - transferQueue.submitCommands(tempCmdBuff.finishList()); - transferQueue.waitIdle(); - - // Destroy the memory we don't need - tempCmdMem.destroy(); - tempImageMemory.destroy(); - - return true; -} - -bool CImage::loadMemory(CMemPool& imagePool, CMemPool& scratchPool, dk::Device device, - dk::Queue transferQueue, const void* data, uint32_t width, uint32_t height, - DkImageFormat dkFormat, uint32_t flags) -{ - if (data == nullptr) - return false; - - // Allocate temporary memory for the image - PixelFormat format; - if (!::deko3d::GetConstant(dkFormat, format)) - return false; - - size_t size = width * height * love::GetPixelFormatSize(format); - - if (size <= 0) - return false; - - CMemPool::Handle tempImageMemory = scratchPool.allocate(size, DK_IMAGE_LINEAR_STRIDE_ALIGNMENT); - - if (!tempImageMemory) - return false; - - memcpy(tempImageMemory.getCpuAddr(), data, size); - - /* - ** We need to have a command buffer and some more memory for it - ** so allocate both and add the memory to the temporary command buffer - */ - dk::UniqueCmdBuf tempCmdBuff = dk::CmdBufMaker { device }.create(); - CMemPool::Handle tempCmdMem = scratchPool.allocate(DK_MEMBLOCK_ALIGNMENT); - tempCmdBuff.addMemory(tempCmdMem.getMemBlock(), tempCmdMem.getOffset(), tempCmdMem.getSize()); - - // Set the image layout for the image - dk::ImageLayout layout; - dk::ImageLayoutMaker { device } - .setFlags(flags) - .setFormat(dkFormat) - .setDimensions(width, height) - .initialize(layout); - - // Create the image - m_mem = imagePool.allocate(layout.getSize(), layout.getAlignment()); - m_image.initialize(layout, m_mem.getMemBlock(), m_mem.getOffset()); - m_descriptor.initialize(m_image); - - /* - ** Create the image's view and copy the data - ** to the temporary image memory - */ - dk::ImageView imageView { m_image }; - tempCmdBuff.copyBufferToImage({ tempImageMemory.getGpuAddr() }, imageView, - { 0, 0, 0, width, height, 1 }, 0); - - // Submit the commands to the transfer queue - transferQueue.submitCommands(tempCmdBuff.finishList()); - transferQueue.waitIdle(); - - // Destroy the memory we don't need - tempCmdMem.destroy(); - tempImageMemory.destroy(); - - return true; -} diff --git a/platform/switch/source/deko3d/CShader.cpp b/platform/switch/source/deko3d/CShader.cpp deleted file mode 100644 index 5526b8612..000000000 --- a/platform/switch/source/deko3d/CShader.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* -** Sample Framework for deko3d Applications -** CShader.cpp: Utility class for loading shaders from the filesystem -*/ -#include "deko3d/CShader.h" - -bool CShader::load(CMemPool& pool, const void* buffer, size_t size) -{ - return false; -} - -bool CShader::load(CMemPool& pool, const char* path) -{ - FILE* file; - DkshHeader header; - void* controlMemory; - - m_codemem.destroy(); - - if (path == nullptr) - return false; - - file = fopen(path, "rb"); - if (!file) - return false; - - if (!fread(&header, sizeof(header), 1, file)) - goto _fail0; - - controlMemory = malloc(header.control_sz); - if (!controlMemory) - goto _fail0; - - rewind(file); - - if (!fread(controlMemory, header.control_sz, 1, file)) - goto _fail1; - - m_codemem = pool.allocate(header.code_sz, DK_SHADER_CODE_ALIGNMENT); - if (!m_codemem) - goto _fail1; - - if (!fread(m_codemem.getCpuAddr(), header.code_sz, 1, file)) - goto _fail2; - - dk::ShaderMaker { m_codemem.getMemBlock(), m_codemem.getOffset() } - .setControl(controlMemory) - .setProgramId(0) - .initialize(m_shader); - - free(controlMemory); - fclose(file); - - return true; - -_fail2: - m_codemem.destroy(); -_fail1: - free(controlMemory); -_fail0: - fclose(file); - return false; -} diff --git a/platform/switch/source/deko3d/deko.cpp b/platform/switch/source/deko3d/deko.cpp deleted file mode 100644 index 4a6e54416..000000000 --- a/platform/switch/source/deko3d/deko.cpp +++ /dev/null @@ -1,692 +0,0 @@ -#include "deko3d/deko.h" - -#include "common/bidirectionalmap.h" -#include "common/pixelformat.h" -#include "deko3d/vertex.h" - -#include "common/screen.h" -namespace -{ - /* GPU & CPU Memory Pools */ - constexpr auto gpuPoolSize = 64 * 1024 * 1024; - constexpr auto gpuFlags = (DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image); - - constexpr auto cpuPoolSize = 1 * 1024 * 1024; - constexpr auto cpuFlags = (DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached); - - /* Used for Shader code */ - constexpr auto shaderPoolSize = 128 * 1024; - constexpr auto shaderFlags = - (DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code); - - constexpr auto framebufferLayoutFlags = - (DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression); -} // namespace - -deko3d::deko3d() : - firstVertex(0), - renderState(STATE_MAX_ENUM), - /* - ** Create GPU device - ** default origin is top left - ** Unique -- destroys automatically - */ - device(dk::DeviceMaker {}.setFlags(DkDeviceFlags_DepthMinusOneToOne).create()), - /* - ** Render Queue - ** Unique -- destroys automatically - */ - queue(dk::QueueMaker { this->device }.setFlags(DkQueueFlags_Graphics).create()), - pool { .images = CMemPool(this->device, gpuFlags, gpuPoolSize), - .data = CMemPool(this->device, cpuFlags, cpuPoolSize), - .code = CMemPool(this->device, shaderFlags, shaderPoolSize) }, - state(), - textureQueue(dk::QueueMaker { this->device }.setFlags(DkQueueFlags_Graphics).create()), - viewport { 0, 0, static_cast(Screen::HANDHELD_WIDTH), - static_cast(Screen::HANDHELD_HEIGHT) }, - framebuffers(), - descriptorsDirty(false), - depthBuffer() -{ - this->transformUniformBuffer = - this->pool.data.allocate(sizeof(this->transformState), DK_UNIFORM_BUF_ALIGNMENT); - this->transformState.mdlvMtx = glm::mat4(1.0f); - - this->descriptors.image.allocate(this->pool.data); - this->descriptors.sampler.allocate(this->pool.data); - - this->cmdRing.allocate(this->pool.data, COMMAND_SIZE); - this->vtxRing.allocate(this->pool.data, VERTEX_COMMAND_SIZE / 2); - - this->state.depthStencil.setDepthTestEnable(true); - this->state.depthStencil.setDepthWriteEnable(true); - this->state.depthStencil.setDepthCompareOp(DkCompareOp_Always); - - this->state.color.setBlendEnable(0, true); - - love::Texture::Filter filter; - filter.min = filter.mag = love::Texture::FILTER_NEAREST; - this->SetTextureFilter(filter); - - love::Texture::Wrap wrap; - wrap.s = wrap.t = wrap.r = love::Texture::WRAP_CLAMP; - this->SetTextureWrap(wrap); - - // Create the dynamic command buffer - this->cmdBuf = dk::CmdBufMaker { this->device }.create(); - this->EnsureInFrame(); - - this->descriptors.image.bindForImages(this->cmdBuf); - this->descriptors.sampler.bindForSamplers(this->cmdBuf); -} - -deko3d::~deko3d() -{ - this->DestroyFramebufferResources(); - this->transformUniformBuffer.destroy(); -} - -deko3d& deko3d::Instance() -{ - static deko3d instance; - return instance; -} - -void deko3d::CreateFramebufferResources() -{ - /* initialize depth buffer */ - dk::ImageLayout layout_depthbuffer; - dk::ImageLayoutMaker { this->device } - .setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression) - .setFormat(DkImageFormat_Z24S8) - .setDimensions(Screen::Instance().GetWidth(), Screen::Instance().GetHeight()) - .initialize(layout_depthbuffer); - - this->depthBuffer.memory = - this->pool.images.allocate(layout_depthbuffer.getSize(), layout_depthbuffer.getAlignment()); - this->depthBuffer.image.initialize(layout_depthbuffer, this->depthBuffer.memory.getMemBlock(), - this->depthBuffer.memory.getOffset()); - - /* Create a layout for the normal framebuffers */ - dk::ImageLayoutMaker { this->device } - .setFlags(framebufferLayoutFlags) - .setFormat(DkImageFormat_RGBA8_Unorm) - .setDimensions(Screen::Instance().GetWidth(), Screen::Instance().GetHeight()) - .initialize(this->layoutFramebuffer); - - uint64_t framebufferSize = this->layoutFramebuffer.getSize(); - uint32_t framebufferAlign = this->layoutFramebuffer.getAlignment(); - - for (unsigned i = 0; i < MAX_FRAMEBUFFERS; i++) - { - // Allocate a framebuffer - this->framebuffers.memory[i] = - this->pool.images.allocate(framebufferSize, framebufferAlign); - this->framebuffers.images[i].initialize(this->layoutFramebuffer, - this->framebuffers.memory[i].getMemBlock(), - this->framebuffers.memory[i].getOffset()); - - // Fill in the array for use later by the swapchain creation code - this->framebufferArray[i] = &this->framebuffers.images[i]; - } - - // Create the swapchain using the framebuffers - this->swapchain = - dk::SwapchainMaker { this->device, nwindowGetDefault(), this->framebufferArray }.create(); -} - -void deko3d::OnOperationMode(std::pair& size) -{ - /* Destroy resources */ - this->DestroyFramebufferResources(); - - /* Recreate them, Screem will auto-determine the sizes */ - this->CreateFramebufferResources(); - - size = { Screen::Instance().GetWidth(), Screen::Instance().GetHeight() }; -} - -void deko3d::DestroyFramebufferResources() -{ - // Return early if we have nothing to destroy - if (!this->swapchain) - return; - - // Make sure the queue is idle before destroying anything - this->queue.waitIdle(); - - this->textureQueue.waitIdle(); - - // Clear the cmdbuf - this->cmdBuf.clear(); - - // Destroy the swapchain - this->swapchain.destroy(); - - // Destroy the framebuffers - for (unsigned i = 0; i < MAX_FRAMEBUFFERS; i++) - this->framebuffers.memory[i].destroy(); - - this->depthBuffer.memory.destroy(); -} - -// Ensure we have begun our frame -void deko3d::EnsureInFrame() -{ - if (!this->framebuffers.inFrame) - { - this->firstVertex = 0; - this->descriptorsDirty = false; - this->cmdRing.begin(this->cmdBuf); - this->framebuffers.inFrame = true; - } -} - -void deko3d::EnsureInState(State state) -{ - if (this->renderState != state && state != State::STATE_MAX_ENUM) - this->renderState = state; - - if (this->renderState == STATE_PRIMITIVE) - { - love::Shader::standardShaders[love::Shader::STANDARD_DEFAULT]->Attach(); - - this->cmdBuf.bindVtxAttribState(vertex::attributes::PrimitiveAttribState); - this->cmdBuf.bindVtxBufferState(vertex::attributes::PrimitiveBufferState); - } - else if (this->renderState == STATE_TEXTURE || this->renderState == STATE_VIDEO) - { - if (this->renderState == STATE_TEXTURE) - love::Shader::standardShaders[love::Shader::STANDARD_TEXTURE]->Attach(); - else - love::Shader::standardShaders[love::Shader::STANDARD_VIDEO]->Attach(); - - this->cmdBuf.bindVtxAttribState(vertex::attributes::TextureAttribState); - this->cmdBuf.bindVtxBufferState(vertex::attributes::TextureBufferState); - } -} - -/* -** Acquire a framebuffer from the swapchain -** (and wait for it to be available) -*/ -void deko3d::EnsureHasSlot() -{ - if (this->framebuffers.slot < 0) - this->framebuffers.slot = this->queue.acquireImage(this->swapchain); -} - -void deko3d::SetBlendColor(const Colorf& color) -{ - this->cmdBuf.setBlendConst(color.r, color.g, color.b, color.a); -} - -/* -** First thing that happens to start the frame -** Clear the screen to a specified color -*/ -void deko3d::ClearColor(const Colorf& color) -{ - this->EnsureInFrame(); - - this->cmdBuf.clearColor(0, DkColorMask_RGBA, color.r, color.g, color.b, color.a); -} - -void deko3d::ClearDepthStencil(double depth, int stencil) -{ - // this->cmdBuf.clearDepthStencil(true, depth, 0xFF, stencil); -} - -void deko3d::SetDekoBarrier(DkBarrier barrier, uint32_t flags) -{ - this->EnsureInFrame(); - this->cmdBuf.barrier(barrier, flags); -} - -/* -** Binds a Framebuffer we have allocated -** It ensures that there's a "slot" from @EnsureHasSlot -** This is used to access the current Framebuffer image -** TODO: Add depth/stencil images -*/ -void deko3d::BindFramebuffer(love::Canvas* canvas) -{ - if (!this->swapchain) - return; - - this->EnsureInFrame(); - this->EnsureHasSlot(); - - if (this->framebuffers.dirty) - this->SetDekoBarrier(DkBarrier_Fragments, 0); - - dk::ImageView target { this->framebuffers.images[this->framebuffers.slot] }; - - if (canvas != nullptr) - { - target = { canvas->GetImage() }; - this->SetViewport({ 0, 0, canvas->GetWidth(), canvas->GetHeight() }); - - this->framebuffers.dirty = true; - } - else - { - this->framebuffers.dirty = false; - this->SetViewport({ 0, 0, this->viewport.w, this->viewport.h }); - } - - this->cmdBuf.bindRenderTargets(&target); - - this->cmdBuf.pushConstants(this->transformUniformBuffer.getGpuAddr(), - this->transformUniformBuffer.getSize(), 0, sizeof(transformState), - &transformState); - - this->BeginFrame(); -} - -void deko3d::BeginFrame() -{ - std::pair data = this->vtxRing.begin(); - - this->vertexData = (vertex::Vertex*)data.first; - - this->cmdBuf.bindRasterizerState(this->state.rasterizer); - this->cmdBuf.bindColorState(this->state.color); - this->cmdBuf.bindColorWriteState(this->state.colorWrite); - this->cmdBuf.bindBlendStates(0, this->state.blendState); - // this->cmdBuf.bindDepthStencilState(this->state.depthStencil); - - // Bind the current slice's GPU address to the buffer - this->cmdBuf.bindVtxBuffer(0, data.second, this->vtxRing.getSize()); -} - -/* -** Presents the current Framebuffer to the screen -** and submits all our commands from the buffer -** to the queue -*/ -void deko3d::Present() -{ - if (!this->swapchain) - return; - - if (this->framebuffers.inFrame) - { - this->vtxRing.end(); - this->queue.submitCommands(this->cmdRing.end(this->cmdBuf)); - this->queue.presentImage(this->swapchain, this->framebuffers.slot); - - this->framebuffers.inFrame = false; - } - - this->framebuffers.slot = -1; -} - -void deko3d::SetStencil(DkStencilOp op, DkCompareOp compare, int value) -{ - bool enabled = (compare == DkCompareOp_Always) ? false : true; - - this->state.depthStencil.setStencilTestEnable(enabled); - - // Front - - this->state.depthStencil.setStencilFrontCompareOp(compare); - - this->state.depthStencil.setStencilFrontDepthFailOp(DkStencilOp_Keep); - this->state.depthStencil.setStencilFrontFailOp(DkStencilOp_Keep); - this->state.depthStencil.setStencilFrontPassOp(DkStencilOp_Keep); - - // Back - - this->state.depthStencil.setStencilBackCompareOp(compare); - - this->state.depthStencil.setStencilBackDepthFailOp(DkStencilOp_Keep); - this->state.depthStencil.setStencilBackFailOp(DkStencilOp_Keep); - this->state.depthStencil.setStencilBackPassOp(DkStencilOp_Keep); - - this->cmdBuf.setStencil(DkFace_FrontAndBack, 0xFF, value, 0xFF); -} - -void deko3d::UnRegisterResHandle(DkResHandle handle) -{ - this->allocator.DeAllocate(handle); -} - -DkResHandle deko3d::RegisterResHandle(const dk::ImageDescriptor& descriptor) -{ - this->EnsureInFrame(); - - uint32_t index = this->allocator.Allocate(); - - this->descriptors.image.update(this->cmdBuf, index, descriptor); - this->descriptors.sampler.update(this->cmdBuf, index, this->filter.descriptor); - - this->descriptorsDirty = true; - - return dkMakeTextureHandle(index, index); -} - -bool deko3d::RenderTexture(const DkResHandle handle, const vertex::Vertex* points, size_t count) -{ - if (count > (this->vtxRing.getSize() - this->firstVertex) || points == nullptr) - return false; - - this->EnsureInState(STATE_TEXTURE); - - if (this->descriptorsDirty) - { - this->cmdBuf.barrier(DkBarrier_Primitives, DkInvalidateFlags_Descriptors); - this->descriptorsDirty = false; - } - - this->cmdBuf.bindTextures(DkStage_Fragment, 0, handle); - - memcpy(this->vertexData + this->firstVertex, points, count * sizeof(vertex::Vertex)); - - this->cmdBuf.draw(DkPrimitive_Quads, count, 1, this->firstVertex, 0); - - this->firstVertex += count; - - return true; -} - -bool deko3d::RenderVideo(const DkResHandle handles[3], const vertex::Vertex* points, size_t count) -{ - if (count > (this->vtxRing.getSize() - this->firstVertex) || points == nullptr) - return false; - - this->EnsureInState(STATE_VIDEO); - - if (this->descriptorsDirty) - { - this->cmdBuf.barrier(DkBarrier_Primitives, DkInvalidateFlags_Descriptors); - this->descriptorsDirty = false; - } - - this->cmdBuf.bindTextures(DkStage_Fragment, 0, { handles[0], handles[1], handles[2] }); - - memcpy(this->vertexData + this->firstVertex, points, count * sizeof(vertex::Vertex)); - - this->cmdBuf.draw(DkPrimitive_Quads, count, 1, this->firstVertex, 0); - - this->firstVertex += count; - - return true; -} - -bool deko3d::RenderPolyline(DkPrimitive mode, const vertex::Vertex* points, size_t count) -{ - if (count > (this->vtxRing.getSize() - this->firstVertex) || points == nullptr) - return false; - - this->EnsureInState(STATE_PRIMITIVE); - - memcpy(this->vertexData + this->firstVertex, points, count * sizeof(vertex::Vertex)); - - this->cmdBuf.draw(mode, count, 1, this->firstVertex, 0); - - this->firstVertex += count; - - return true; -} - -bool deko3d::RenderPolygon(const vertex::Vertex* points, size_t count) -{ - if (count > (this->vtxRing.getSize() - this->firstVertex) || points == nullptr) - return false; - - this->EnsureInState(STATE_PRIMITIVE); - - memcpy(this->vertexData + this->firstVertex, points, count * sizeof(vertex::Vertex)); - - this->cmdBuf.draw(DkPrimitive_TriangleFan, count, 1, this->firstVertex, 0); - - this->firstVertex += count; - - return true; -} - -bool deko3d::RenderPoints(const vertex::Vertex* points, size_t count) -{ - if (count > (this->vtxRing.getSize() - this->firstVertex) || points == nullptr) - return false; - - this->EnsureInState(STATE_PRIMITIVE); - - memcpy(this->vertexData + this->firstVertex, points, count * sizeof(vertex::Vertex)); - - this->cmdBuf.draw(DkPrimitive_Points, count, 1, this->firstVertex, 0); - - this->firstVertex += count; - - return true; -} - -void deko3d::SetPointSize(float size) -{ - this->EnsureInFrame(); - this->cmdBuf.setPointSize(size); -} - -void deko3d::SetLineWidth(float width) -{ - this->EnsureInFrame(); - this->cmdBuf.setLineWidth(width); -} - -void deko3d::SetLineStyle(bool smooth) -{ - this->state.rasterizer.setPolygonSmoothEnable(smooth); -} - -float deko3d::GetPointSize() -{ - return this->state.pointSize; -} - -void deko3d::SetColorMask(const love::Graphics::ColorMask& mask) -{ - this->state.colorWrite.setMask(0, mask.GetColorMask()); -} - -void deko3d::SetBlendMode(DkBlendOp func, DkBlendFactor srcColor, DkBlendFactor srcAlpha, - DkBlendFactor dstColor, DkBlendFactor dstAlpha) -{ - this->state.blendState.setColorBlendOp(func); - this->state.blendState.setAlphaBlendOp(func); - - // Blend factors - this->state.blendState.setSrcColorBlendFactor(srcColor); - this->state.blendState.setSrcAlphaBlendFactor(srcAlpha); - - this->state.blendState.setDstColorBlendFactor(dstColor); - this->state.blendState.setDstAlphaBlendFactor(dstAlpha); -} - -void deko3d::SetFrontFaceWinding(DkFrontFace face) -{ - this->state.rasterizer.setFrontFace(face); -} - -void deko3d::SetCullMode(DkFace face) -{ - this->state.rasterizer.setCullMode(face); -} - -/* Encapsulation and Abstraction - fincs */ - -/* -** Equivalent to LÖVE's OpenGL::UseProgram function -** We need to make sure that we're *in* a frame before attaching the Shader -** LÖVE attaches a default Shader on Graphics creation, which is not in a frame -** although OpenGL *hides* this from the user, so we have to deal with it in deko3d -*/ -void deko3d::UseProgram(const love::Shader::Program& program) -{ - this->EnsureInFrame(); - - this->cmdBuf.bindShaders(DkStageFlag_GraphicsMask, { *program.vertex, *program.fragment }); - this->cmdBuf.bindUniformBuffer(DkStage_Vertex, 0, this->transformUniformBuffer.getGpuAddr(), - this->transformUniformBuffer.getSize()); -} - -void deko3d::SetDepthWrites(bool enable) -{ - this->state.rasterizer.setDepthClampEnable(enable); -} - -// Set the global filter mode for textures -void deko3d::SetTextureFilter(const love::Texture::Filter& filter) -{ - DkFilter min = - (filter.min == love::Texture::FILTER_NEAREST) ? DkFilter_Nearest : DkFilter_Linear; - DkFilter mag = - (filter.min == love::Texture::FILTER_NEAREST) ? DkFilter_Nearest : DkFilter_Linear; - - DkMipFilter mipFilter = DkMipFilter_Linear; - if (filter.mipmap != love::Texture::FILTER_NONE) - { - if (filter.min == love::Texture::FILTER_NEAREST && - filter.mipmap == love::Texture::FILTER_NEAREST) - mipFilter = DkMipFilter_Nearest; - else if (filter.min == love::Texture::FILTER_NEAREST && - filter.mipmap == love::Texture::FILTER_LINEAR) - mipFilter = DkMipFilter_Linear; - else if (filter.min == love::Texture::FILTER_LINEAR && - filter.mipmap == love::Texture::FILTER_NEAREST) - mipFilter = DkMipFilter_Nearest; - else if (filter.min == love::Texture::FILTER_LINEAR && - filter.mipmap == love::Texture::FILTER_LINEAR) - mipFilter = DkMipFilter_Linear; - else - mipFilter = DkMipFilter_Linear; - } - - this->filter.sampler.setFilter(min, mag, mipFilter); - - float anisotropy = std::clamp(filter.anisotropy, 1.0f, (float)MAX_ANISOTROPY); - this->filter.sampler.setMaxAnisotropy(anisotropy); - - this->filter.descriptor.initialize(this->filter.sampler); -} - -void deko3d::SetTextureFilter(love::Texture* texture, const love::Texture::Filter& filter) -{ - this->EnsureInFrame(); - - this->SetTextureFilter(filter); - - uint32_t handleID = this->allocator.Find(texture->GetHandle()); - this->descriptors.sampler.update(this->cmdBuf, handleID, this->filter.descriptor); - - this->descriptorsDirty = true; -} - -void deko3d::SetTextureWrap(const love::Texture::Wrap& wrap) -{ - DkWrapMode u = deko3d::GetDekoWrapMode(wrap.s); - DkWrapMode v = deko3d::GetDekoWrapMode(wrap.t); - - this->filter.sampler.setWrapMode(u, v); - - this->filter.descriptor.initialize(this->filter.sampler); -} - -void deko3d::SetTextureWrap(love::Texture* texture, const love::Texture::Wrap& wrap) -{ - this->EnsureInFrame(); - - this->SetTextureWrap(wrap); - - uint32_t handleID = this->allocator.Find(texture->GetHandle()); - this->descriptors.sampler.update(this->cmdBuf, handleID, this->filter.descriptor); - - this->descriptorsDirty = true; -} - -DkWrapMode deko3d::GetDekoWrapMode(love::Texture::WrapMode wrap) -{ - switch (wrap) - { - case love::Texture::WRAP_CLAMP: - default: - return DkWrapMode_ClampToEdge; - case love::Texture::WRAP_CLAMP_ZERO: - return DkWrapMode_ClampToBorder; - case love::Texture::WRAP_REPEAT: - return DkWrapMode_Repeat; - case love::Texture::WRAP_MIRRORED_REPEAT: - return DkWrapMode_MirroredRepeat; - } -} - -// clang-format off -constexpr auto pixelFormats = BidirectionalMap<>::Create( - PIXELFORMAT_R8, DkImageFormat_R8_Unorm, - PIXELFORMAT_RGBA8, DkImageFormat_RGBA8_Unorm, - PIXELFORMAT_DXT1, DkImageFormat_RGBA_BC1, - PIXELFORMAT_DXT3, DkImageFormat_RGBA_BC2, - PIXELFORMAT_DXT5, DkImageFormat_RGBA_BC3, - PIXELFORMAT_ETC1, DkImageFormat_RGB_ETC2, - PIXELFORMAT_ETC2_RGB, DkImageFormat_RGB_ETC2, - PIXELFORMAT_ETC2_RGBA1, DkImageFormat_RGBA_ETC2, - PIXELFORMAT_ETC2_RGBA, DkImageFormat_RGBA_ETC2, - PIXELFORMAT_ASTC_4x4, DkImageFormat_RGBA_ASTC_4x4, - PIXELFORMAT_ASTC_5x4, DkImageFormat_RGBA_ASTC_5x4, - PIXELFORMAT_ASTC_6x5, DkImageFormat_RGBA_ASTC_6x5, - PIXELFORMAT_ASTC_6x6, DkImageFormat_RGBA_ASTC_6x6, - PIXELFORMAT_ASTC_8x5, DkImageFormat_RGBA_ASTC_8x5, - PIXELFORMAT_ASTC_8x6, DkImageFormat_RGBA_ASTC_8x6, - PIXELFORMAT_ASTC_8x8, DkImageFormat_RGBA_ASTC_8x8, - PIXELFORMAT_ASTC_10x5, DkImageFormat_RGBA_ASTC_10x5, - PIXELFORMAT_ASTC_10x6, DkImageFormat_RGBA_ASTC_10x6, - PIXELFORMAT_ASTC_10x8, DkImageFormat_RGBA_ASTC_10x8, - PIXELFORMAT_ASTC_10x10, DkImageFormat_RGBA_ASTC_10x10, - PIXELFORMAT_ASTC_12x10, DkImageFormat_RGBA_ASTC_12x10, - PIXELFORMAT_ASTC_12x12, DkImageFormat_RGBA_ASTC_12x12 -); -// clang-format on - -bool deko3d::GetConstant(PixelFormat in, DkImageFormat& out) -{ - return pixelFormats.Find(in, out); -} - -bool deko3d::GetConstant(DkImageFormat in, PixelFormat& out) -{ - return pixelFormats.ReverseFind(in, out); -} - -/* -** Set the Scissor region to clip -** Anything drawn outside of this will not be rendered -*/ -void deko3d::SetScissor(const love::Rect& scissor, bool canvasActive) -{ - this->EnsureInFrame(); - - this->scissor = scissor; - this->cmdBuf.setScissors(0, { { (uint32_t)scissor.x, (uint32_t)scissor.y, (uint32_t)scissor.w, - (uint32_t)scissor.h } }); -} - -/* -** Set the viewing screen space for rendering -** This sets up the actual bounds we can see -*/ -void deko3d::SetViewport(const love::Rect& view) -{ - this->EnsureInFrame(); - - this->viewport = view; - this->cmdBuf.setViewports( - 0, { { (float)view.x, (float)view.y, (float)view.w, (float)view.h, Z_NEAR, Z_FAR } }); - - this->transformState.projMtx = - glm::ortho(0.0f, (float)view.w, (float)view.h, 0.0f, Z_NEAR, Z_FAR); -} - -love::Rect deko3d::GetViewport() -{ - return this->viewport; -} diff --git a/platform/switch/source/deko3d/graphics.cpp b/platform/switch/source/deko3d/graphics.cpp deleted file mode 100644 index 38aaf5356..000000000 --- a/platform/switch/source/deko3d/graphics.cpp +++ /dev/null @@ -1,616 +0,0 @@ -#include "deko3d/graphics.h" - -#include "common/bidirectionalmap.h" -#include "polyline/common.h" - -using namespace love; - -love::deko3d::Graphics::Graphics() -{ - /* quick hax */ - - this->width = 1280; - this->height = 720; - - this->RestoreState(this->states.back()); - - try - { - for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) - { - if (!Shader::standardShaders[i]) - Shader::standardShaders[i] = this->NewShader(Shader::StandardShader(i)); - } - } - catch (love::Exception&) - { - throw; - } - - // A shader should always be active, but the default shader shouldn't be - // returned by getShader(), so we don't do setShader(defaultShader). - if (!Shader::current) - Shader::standardShaders[Shader::STANDARD_DEFAULT]->Attach(); -} - -love::deko3d::Graphics::~Graphics() -{} - -void Graphics::SetCanvas(Canvas* canvas) -{ - DisplayState& state = this->states.back(); - state.canvas.Set(canvas); - - ::deko3d::Instance().BindFramebuffer(canvas); - - if (this->states.back().scissor) - this->SetScissor(this->states.back().scissorRect); -} - -void love::deko3d::Graphics::Clear(std::optional color, std::optional stencil, - std::optional depth) -{ - if (this->IsCanvasActive() == false) - ::deko3d::Instance().BindFramebuffer(); - - if (color.has_value()) - { - Graphics::GammaCorrectColor(color.value()); - ::deko3d::Instance().ClearColor(color.value()); - } - - if (stencil.has_value()) - ::deko3d::Instance().ClearDepthStencil(depth.value(), stencil.value()); -} - -void love::deko3d::Graphics::Clear(std::vector>& colors, - std::optional stencil, std::optional depth) -{ - int numColors = colors.size(); - - if (numColors == 0 || !stencil.has_value() || !depth.has_value()) - return; - - if (numColors <= 1) - this->Clear(numColors > 0 ? colors[0] : std::optional(), stencil, depth); -} - -void love::deko3d::Graphics::Present() -{ - if (this->IsCanvasActive()) - throw love::Exception("present cannot be called while a Canvas is active."); - - ::deko3d::Instance().Present(); -} - -Graphics::RendererInfo love::deko3d::Graphics::GetRendererInfo() const -{ - RendererInfo info {}; - - info.name = RENDERER_NAME; - info.device = RENDERER_DEVICE; - info.vendor = RENDERER_VENDOR; - info.version = RENDERER_VERSION; - - return info; -} - -void love::deko3d::Graphics::SetColor(Colorf color) -{ - love::Graphics::SetColor(color); - ::deko3d::Instance().SetBlendColor(color); -} - -void love::deko3d::Graphics::SetMeshCullMode(vertex::CullMode mode) -{ - DkFace face = DkFace_None; - - switch (mode) - { - case vertex::CULL_BACK: - face = DkFace_Back; - break; - case vertex::CULL_FRONT: - face = DkFace_Front; - break; - case vertex::CULL_NONE: - face = DkFace_None; - break; - default: - break; - } - - ::deko3d::Instance().SetCullMode(face); -} - -void love::deko3d::Graphics::SetFrontFaceWinding(vertex::Winding winding) -{ - DkFrontFace face = DkFrontFace_CW; - - switch (winding) - { - case vertex::WINDING_CW: - face = DkFrontFace_CW; - break; - case vertex::WINDING_CCW: - face = DkFrontFace_CCW; - break; - default: - break; - } - - ::deko3d::Instance().SetFrontFaceWinding(face); -} - -void love::deko3d::Graphics::SetDefaultFilter(const Texture::Filter& filter) -{ - love::Graphics::SetDefaultFilter(filter); -} - -void love::deko3d::Graphics::SetBlendMode(BlendMode mode, BlendAlpha alphamode) -{ - if (mode != this->states.back().blendMode || alphamode != this->states.back().blendAlphaMode) - {} // flush stream draws - - if (alphamode != BLENDALPHA_PREMULTIPLIED) - { - const char* modestr = "unknown"; - switch (mode) - { - case BLEND_LIGHTEN: - case BLEND_DARKEN: - case BLEND_MULTIPLY: - Graphics::GetConstant(mode, modestr); - throw love::Exception("The '%s' blend mode must be used with premultiplied alpha.", - modestr); - break; - default: - break; - } - } - - DkBlendOp func = DkBlendOp_Add; - - DkBlendFactor srcColor = DkBlendFactor_One; - DkBlendFactor srcAlpha = DkBlendFactor_One; - - DkBlendFactor dstColor = DkBlendFactor_Zero; - DkBlendFactor dstAlpha = DkBlendFactor_Zero; - - switch (mode) - { - case love::Graphics::BLEND_ALPHA: - srcColor = srcAlpha = DkBlendFactor_One; - dstColor = dstAlpha = DkBlendFactor_InvSrcAlpha; - - break; - case love::Graphics::BLEND_MULTIPLY: - srcColor = srcAlpha = DkBlendFactor_DstColor; - dstColor = dstAlpha = DkBlendFactor_Zero; - - break; - case love::Graphics::BLEND_SUBTRACT: - func = DkBlendOp_RevSub; - - break; - case love::Graphics::BLEND_ADD: - srcColor = DkBlendFactor_One; - srcAlpha = DkBlendFactor_Zero; - - dstColor = dstAlpha = DkBlendFactor_One; - - break; - case love::Graphics::BLEND_LIGHTEN: - func = DkBlendOp_Max; - - break; - case love::Graphics::BLEND_DARKEN: - func = DkBlendOp_Min; - - break; - case love::Graphics::BLEND_SCREEN: - srcColor = srcAlpha = DkBlendFactor_One; - dstColor = dstAlpha = DkBlendFactor_InvSrcColor; - - break; - case love::Graphics::BLEND_REPLACE: - case love::Graphics::BLEND_NONE: - default: - srcColor = srcAlpha = DkBlendFactor_One; - dstColor = dstAlpha = DkBlendFactor_Zero; - - break; - } - - // We can only do alpha-multiplication when srcRGB would have been unmodified. - if (srcColor == DkBlendFactor_One && alphamode == BLENDALPHA_MULTIPLY && mode != BLEND_NONE) - srcColor = DkBlendFactor_SrcAlpha; - - ::deko3d::Instance().SetBlendMode(func, srcColor, srcAlpha, dstColor, dstAlpha); - - this->states.back().blendMode = mode; - this->states.back().blendAlphaMode = alphamode; -} - -Font* love::deko3d::Graphics::NewFont(Rasterizer* data, const Texture::Filter& filter) -{ - return new Font(data, filter); -} - -/* Primitives */ - -void love::deko3d::Graphics::Polyline(const Vector2* points, size_t count) -{ - float halfWidth = this->GetLineWidth() * 0.5f; - float pixelSize = 1.0f / std::max((float)pixelScaleStack.back(), 0.000001f); - - LineJoin lineJoin = this->GetLineJoin(); - LineStyle lineStyle = this->GetLineStyle(); - - bool drawOverdraw = (lineStyle == LINE_SMOOTH); - - if (lineJoin == LINE_JOIN_NONE) - { - NoneJoinPolyline line; - line.Render(points, count, halfWidth, pixelSize, drawOverdraw); - - line.Draw(this); - } - else if (lineJoin == LINE_JOIN_BEVEL) - { - BevelJoinPolyline line; - line.Render(points, count, halfWidth, pixelSize, drawOverdraw); - - line.Draw(this); - } - else if (lineJoin == LINE_JOIN_MITER) - { - MiterJoinPolyline line; - line.Render(points, count, halfWidth, pixelSize, drawOverdraw); - - line.Draw(this); - } -} - -void love::deko3d::Graphics::Polygon(DrawMode mode, const Vector2* points, size_t count, - bool skipLastVertex) -{ - if (mode == DRAW_LINE) - this->Polyline(points, count); - else - { - Colorf color[1] = { this->GetColor() }; - - const Matrix4& t = this->GetTransform(); - bool is2D = t.IsAffine2DTransform(); - - int vertexCount = (int)count - ((skipLastVertex) ? 1 : 0); - - Vector2 transformed[vertexCount]; - std::fill_n(transformed, vertexCount, Vector2 {}); - - if (is2D) - t.TransformXY(transformed, points, vertexCount); - - auto vertices = vertex::GeneratePrimitiveFromVectors(std::span(transformed, vertexCount), - std::span(color, 1)); - - ::deko3d::Instance().RenderPolygon(vertices.get(), vertexCount); - } -} - -void love::deko3d::Graphics::SetLineWidth(float width) -{ - ::Graphics::SetLineWidth(width); - ::deko3d::Instance().SetLineWidth(width); -} - -void love::deko3d::Graphics::Line(const Vector2* points, int count) -{ - this->Polyline(points, count); -} - -void love::deko3d::Graphics::Rectangle(DrawMode mode, float x, float y, float width, float height) -{ - Vector2 coords[5] = { Vector2(x, y), Vector2(x, y + height), Vector2(x + width, y + height), - Vector2(x + width, y), Vector2(x, y) }; - - this->Polygon(mode, coords, 5); -} - -void love::deko3d::Graphics::Rectangle(DrawMode mode, float x, float y, float width, float height, - float rx, float ry, int points) -{ - if (rx == 0 || ry == 0) - { - this->Rectangle(mode, x, y, width, height); - return; - } - - // Radius values that are more than half the rectangle's size aren't handled - // correctly (for now)... - - if (width >= 0.02f) - rx = std::min(rx, width / 2.0f - 0.01f); - - if (height >= 0.02f) - ry = std::min(ry, height / 2.0f - 0.01f); - - points = std::max(points / 4, 1); - - const float half_pi = static_cast(LOVE_M_PI / 2); - float angle_shift = half_pi / ((float)points + 1.0f); - - int num_coords = (points + 2) * 4; - - Vector2 coords[num_coords + 1] = {}; - float phi = .0f; - - for (int i = 0; i <= points + 2; ++i, phi += angle_shift) - { - coords[i].x = x + rx * (1 - cosf(phi)); - coords[i].y = y + ry * (1 - sinf(phi)); - } - - phi = half_pi; - - for (int i = points + 2; i <= 2 * (points + 2); ++i, phi += angle_shift) - { - coords[i].x = x + width - rx * (1 + cosf(phi)); - coords[i].y = y + ry * (1 - sinf(phi)); - } - - phi = 2 * half_pi; - - for (int i = 2 * (points + 2); i <= 3 * (points + 2); ++i, phi += angle_shift) - { - coords[i].x = x + width - rx * (1 + cosf(phi)); - coords[i].y = y + height - ry * (1 + sinf(phi)); - } - - phi = 3 * half_pi; - - for (int i = 3 * (points + 2); i <= 4 * (points + 2); ++i, phi += angle_shift) - { - coords[i].x = x + rx * (1 - cosf(phi)); - coords[i].y = y + height - ry * (1 + sinf(phi)); - } - - coords[num_coords] = coords[0]; - - this->Polygon(mode, coords, num_coords + 1); -} - -void love::deko3d::Graphics::Rectangle(DrawMode mode, float x, float y, float width, float height, - float rx, float ry) -{ - int points = this->CalculateEllipsePoints(std::min(rx, std::abs(width / 2)), - std::min(ry, std::abs(height / 2))); - - this->Rectangle(mode, x, y, width, height, rx, ry, points); -} - -void love::deko3d::Graphics::Ellipse(DrawMode mode, float x, float y, float a, float b, int points) -{ - float two_pi = (float)(LOVE_M_PI * 2); - if (points <= 0) - points = 1; - - float angle_shift = (two_pi / points); - float phi = .0f; - - // 1 extra point at the end for a closed loop, and 1 extra point at the - // start in filled mode for the vertex in the center of the ellipse. - int extrapoints = 1 + (mode == DRAW_FILL ? 1 : 0); - - Vector2 coords[points + extrapoints] = {}; - - if (mode == DRAW_FILL) - { - coords[0].x = x; - coords[0].y = y; - } - - for (int i = 0; i < points; ++i, phi += angle_shift) - { - coords[i].x = x + a * cosf(phi); - coords[i].y = y + b * sinf(phi); - } - - coords[points] = coords[0]; - - // Last argument to polygon(): don't skip the last vertex in fill mode. - this->Polygon(mode, coords, points + extrapoints, false); -} - -void love::deko3d::Graphics::Circle(DrawMode mode, float x, float y, float radius) -{ - this->Ellipse(mode, x, y, radius, radius); -} - -void love::deko3d::Graphics::Circle(DrawMode mode, float x, float y, float radius, int points) -{ - this->Ellipse(mode, x, y, radius, radius, points); -} - -void love::deko3d::Graphics::Ellipse(DrawMode mode, float x, float y, float a, float b) -{ - this->Ellipse(mode, x, y, a, b, this->CalculateEllipsePoints(a, b)); -} - -void love::deko3d::Graphics::Arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, - float angle1, float angle2, int points) -{ - /* - ** Nothing to display with no points or equal angles. - ** (Or is there with line mode?) - */ - if (points <= 0 || angle1 == angle2) - return; - - // Oh, you want to draw a circle? - if (fabs(angle1 - angle2) >= 2.0f * (float)LOVE_M_PI) - { - this->Circle(drawmode, x, y, radius, points); - return; - } - - float angle_shift = (angle2 - angle1) / points; - - // Bail on precision issues. - if (angle_shift == 0.0f) - return; - - /* - ** Prevent the connecting line from being drawn if a closed line arc has a - ** small angle. Avoids some visual issues when connected lines are at sharp - ** angles, due to the miter line join drawing code. - */ - if (drawmode == DRAW_LINE && arcmode == ARC_CLOSED && fabsf(angle1 - angle2) < LOVE_TORAD(4)) - arcmode = ARC_OPEN; - - /* - ** Quick fix for the last part of a filled open arc not being drawn (because - ** polygon(DRAW_FILL, ...) doesn't work without a closed loop of vertices.) - */ - if (drawmode == DRAW_FILL && arcmode == ARC_OPEN) - arcmode = ARC_CLOSED; - - float phi = angle1; - - int num_coords = 0; - Vector2* coords = nullptr; - - const auto createPoints = [&](Vector2* coordinates) { - for (int i = 0; i <= points; ++i, phi += angle_shift) - { - coordinates[i].x = x + radius * cosf(phi); - coordinates[i].y = y + radius * sinf(phi); - } - }; - - if (arcmode == ARC_PIE) - { - num_coords = points + 3; - coords = new Vector2[num_coords]; - - coords[0] = coords[num_coords - 1] = Vector2(x, y); - - createPoints(coords + 1); - } - else if (arcmode == ARC_OPEN) - { - num_coords = points + 1; - coords = new Vector2[num_coords]; - - createPoints(coords); - } - else // ARC_CLOSED - { - num_coords = points + 2; - coords = new Vector2[num_coords]; - - createPoints(coords); - - // Connect the ends of the arc. - coords[num_coords - 1] = coords[0]; - } - - this->Polygon(drawmode, coords, num_coords); - delete[] coords; -} - -void love::deko3d::Graphics::Points(const Vector2* points, size_t count, const Colorf* colors, - size_t colorCount) -{ - const Matrix4& t = this->GetTransform(); - bool is2D = t.IsAffine2DTransform(); - - Vector2 transformed[count]; - std::fill_n(transformed, count, Vector2 {}); - - if (is2D) - t.TransformXY(transformed, points, count); - - Colorf colorList[colorCount]; - memcpy(colorList, colors, colorCount); - - auto vertices = vertex::GeneratePrimitiveFromVectors(std::span(transformed, count), - std::span(colorList, colorCount)); - - ::deko3d::Instance().RenderPoints(vertices.get(), count); -} - -void love::deko3d::Graphics::SetPointSize(float size) -{ - ::deko3d::Instance().SetPointSize(size); - this->states.back().pointSize = size; -} - -void love::deko3d::Graphics::Arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, - float angle1, float angle2) -{ - float points = (float)this->CalculateEllipsePoints(radius, radius); - - // The amount of points is based on the fraction of the circle created by the arc. - float angle = fabsf(angle1 - angle2); - if (angle < 2.0f * (float)LOVE_M_PI) - points *= angle / (2.0f * (float)LOVE_M_PI); - - this->Arc(drawmode, arcmode, x, y, radius, angle1, angle2, (int)(points + 0.5f)); -} - -int love::deko3d::Graphics::CalculateEllipsePoints(float rx, float ry) const -{ - int points = (int)sqrtf(((rx + ry) / 2.0f) * 20.0f * (float)this->pixelScaleStack.back()); - return std::max(points, 8); -} - -/* Primitives */ - -void love::deko3d::Graphics::SetScissor(const Rect& scissor) -{ - DisplayState& state = this->states.back(); - - ::deko3d::Instance().SetScissor(scissor, this->IsCanvasActive()); - - state.scissor = true; - state.scissorRect = scissor; -} - -void love::deko3d::Graphics::SetColorMask(ColorMask mask) -{ - ::deko3d::Instance().SetColorMask(mask); - states.back().colorMask = mask; -} - -void love::deko3d::Graphics::SetScissor() -{ - int width = this->GetWidth(this->GetActiveScreen()); - int height = this->GetHeight(); - - ::deko3d::Instance().SetScissor({ 0, 0, width, height }, this->IsCanvasActive()); - states.back().scissor = false; -} - -Shader* love::deko3d::Graphics::NewShader(Shader::StandardShader type) -{ - Shader* s = new Shader(); - s->LoadDefaults(type); - - return s; -} - -Font* love::deko3d::Graphics::NewDefaultFont(int size, TrueTypeRasterizer::Hinting hinting, - const Texture::Filter& filter) -{ - auto fontModule = Module::GetInstance(M_FONT); - if (!fontModule) - throw love::Exception("Font module has not been loaded."); - - StrongReference r( - fontModule->NewTrueTypeRasterizer(size, TrueTypeRasterizer::HINTING_NORMAL), - Acquire::NORETAIN); - - return new Font(r.Get(), filter); -} diff --git a/platform/switch/source/deko3d/shader.cpp b/platform/switch/source/deko3d/shader.cpp deleted file mode 100644 index cb2fd9e5a..000000000 --- a/platform/switch/source/deko3d/shader.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "deko3d/shader.h" - -#include "common/bidirectionalmap.h" -#include "deko3d/deko.h" - -using namespace love; - -Type love::Shader::type("Shader", &love::Object::type); - -Shader* love::Shader::current = nullptr; -Shader* love::Shader::standardShaders[love::Shader::STANDARD_MAX_ENUM] = { nullptr }; - -#define SHADERS_DIR "romfs:/shaders/" - -#define DEFAULT_VERTEX_SHADER (SHADERS_DIR "transform_vsh.dksh") -#define DEFAULT_FRAGMENT_SHADER (SHADERS_DIR "color_fsh.dksh") -#define DEFAULT_TEXTURE_SHADER (SHADERS_DIR "texture_fsh.dksh") -#define DEFAULT_VIDEO_SHADER (SHADERS_DIR "video_fsh.dksh") - -Shader::Shader() : program() -{} - -Shader::Shader(Data* vertex, Data* pixel) : program() -{ - std::string error; - - this->program.vertex->load(::deko3d::Instance().GetCode(), vertex->GetData(), - vertex->GetSize()); - this->program.fragment->load(::deko3d::Instance().GetCode(), pixel->GetData(), - pixel->GetSize()); - - if (!this->Validate(*this->program.vertex, *this->program.fragment, error)) - throw love::Exception(error.c_str()); -} - -Shader::~Shader() -{ - for (int i = 0; i < STANDARD_MAX_ENUM; i++) - { - if (this == standardShaders[i]) - standardShaders[i] = nullptr; - } - - if (current == this) - Shader::AttachDefault(STANDARD_DEFAULT); -} - -void Shader::LoadDefaults(StandardShader type) -{ - switch (type) - { - case STANDARD_DEFAULT: - this->program.vertex->load(::deko3d::Instance().GetCode(), DEFAULT_VERTEX_SHADER); - this->program.fragment->load(::deko3d::Instance().GetCode(), DEFAULT_FRAGMENT_SHADER); - break; - case STANDARD_TEXTURE: - this->program.vertex->load(::deko3d::Instance().GetCode(), DEFAULT_VERTEX_SHADER); - this->program.fragment->load(::deko3d::Instance().GetCode(), DEFAULT_TEXTURE_SHADER); - break; - case STANDARD_VIDEO: - this->program.vertex->load(::deko3d::Instance().GetCode(), DEFAULT_VERTEX_SHADER); - this->program.fragment->load(::deko3d::Instance().GetCode(), DEFAULT_VIDEO_SHADER); - default: - break; - } - - std::string error; - if (!this->Validate(*this->program.vertex, *this->program.fragment, error)) - throw love::Exception(error.c_str()); -} - -const char* Shader::GetStageName(CShader& shader) -{ - switch (shader.getStage()) - { - case DkStage::DkStage_Vertex: - return "Vertex"; - case DkStage::DkStage_Fragment: - return "Fragment"; - default: - break; - } - - return NULL; -} - -bool Shader::Validate(const CShader& vertex, const CShader& pixel, std::string& error) -{ - if (!vertex.isValid()) - { - error = "Invalid vertex shader."; - return false; - } - - if (!pixel.isValid()) - { - error = "Invalid fragment shader."; - return false; - } - - return true; -} - -void Shader::AttachDefault(StandardShader defaultType) -{ - Shader* defaultshader = standardShaders[defaultType]; - - if (defaultshader == nullptr) - { - current = nullptr; - return; - } - - if (current != defaultshader) - defaultshader->Attach(); -} - -bool Shader::IsDefaultActive() -{ - for (int i = 0; i < STANDARD_MAX_ENUM; i++) - { - if (current == standardShaders[i]) - return true; - } - - return false; -} - -void Shader::Attach() -{ - if (Shader::current != this) - { - ::deko3d::Instance().UseProgram(this->program); - - Shader::current = this; - } -} - -// clang-format off -constexpr auto shaderNames = BidirectionalMap<>::Create( - "default", Shader::StandardShader::STANDARD_DEFAULT, - "texture", Shader::StandardShader::STANDARD_TEXTURE, - "video", Shader::StandardShader::STANDARD_VIDEO -); -// clang-format on - -bool Shader::GetConstant(const char* in, StandardShader& out) -{ - return shaderNames.Find(in, out); -} - -bool Shader::GetConstant(StandardShader in, const char*& out) -{ - return shaderNames.ReverseFind(in, out); -} diff --git a/platform/switch/source/deko3d/vertex.cpp b/platform/switch/source/deko3d/vertex.cpp deleted file mode 100644 index 9351ea80c..000000000 --- a/platform/switch/source/deko3d/vertex.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "deko3d/vertex.h" -#include "objects/font/font.h" - -using namespace love; - -std::vector vertex::GenerateTextureFromVectors(const love::Vector2* points, - const love::Vector2* texcoord, - size_t count, Colorf color) -{ - std::vector verts(count); - - for (size_t currentVertex = 0; currentVertex < count; currentVertex++) - { - const Vector2 point = points[currentVertex]; - const Vector2 texCoord = texcoord[currentVertex]; - - vertex::Vertex vert = { .position = { point.x, point.y, 0.0f }, - .color = { 1, 1, 1, 1 }, - .texcoord = { normto16t(texCoord.x), normto16t(texCoord.y) } }; - - color.CopyTo(vert.color); - - verts[currentVertex] = vert; - } - - return verts; -} - -std::vector vertex::GenerateTextureFromGlyphs(const vertex::GlyphVertex* data, - size_t count) -{ - std::vector verts(count); - - for (size_t currentVertex = 0; currentVertex < count; currentVertex++) - { - const GlyphVertex vertex = data[currentVertex]; - Colorf color = vertex.color; - - vertex::Vertex vert = { .position = { vertex.x, vertex.y, 0.0f }, - .color = { 1, 1, 1, 1 }, - .texcoord = { vertex.s, vertex.t } }; - - color.CopyTo(vert.color); - - verts[currentVertex] = vert; - } - - return verts; -} diff --git a/platform/switch/source/driver/audiodrv.cpp b/platform/switch/source/driver/audiodrv.cpp deleted file mode 100644 index 0c5a57e7d..000000000 --- a/platform/switch/source/driver/audiodrv.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include "driver/audiodrv.h" -#include "pools/audiopool.h" - -using namespace love::driver; - -static constexpr AudioRendererConfig config = { - .output_rate = AudioRendererOutputRate_48kHz, - .num_voices = 24, - .num_effects = 0, - .num_sinks = 1, - .num_mix_objs = 1, - .num_mix_buffers = 2, -}; - -Audrv::Audrv() : audioInitialized(false) -{ - AudioPool::AUDIO_POOL_BASE = memalign(AUDREN_MEMPOOL_ALIGNMENT, AudioPool::AUDIO_POOL_SIZE); - - Result res = audrenInitialize(&config); - - this->audioInitialized = R_SUCCEEDED(res); - this->channelReset = false; - - if (!this->audioInitialized) - return; - - this->driver = AudioDriver(); - - res = audrvCreate(&this->driver, &config, 2); - - if (R_SUCCEEDED(res)) - this->initialized = true; - - int mempoolID = - audrvMemPoolAdd(&this->driver, AudioPool::AUDIO_POOL_BASE, AudioPool::AUDIO_POOL_SIZE); - - audrvMemPoolAttach(&this->driver, mempoolID); - - static const u8 sink_channels[] = { 0, 1 }; - - audrvDeviceSinkAdd(&this->driver, AUDREN_DEFAULT_DEVICE_NAME, 2, sink_channels); - - audrvUpdate(&this->driver); - - audrenStartAudioRenderer(); -} - -/* -** Sets the master volume out -*/ -void Audrv::SetMixVolume(int mix, float volume) -{ - thread::Lock lock(this->mutex); - - audrvMixSetVolume(&this->driver, mix, volume); -} - -/* -** Reset a channel to a given format and sample rate -** Also needs to Mix Factor reset.. for some reason -*/ -bool Audrv::ResetChannel(size_t channel, int channels, PcmFormat format, int sampleRate) -{ - thread::Lock lock(this->mutex); - - this->channelReset = audrvVoiceInit(&this->driver, channel, channels, format, sampleRate); - - if (this->channelReset) - { - audrvVoiceSetDestinationMix(&this->driver, channel, AUDREN_FINAL_MIX_ID); - - audrvVoiceSetMixFactor(&this->driver, channel, 1.0f, 0, 0); - - if (channels == 2) - audrvVoiceSetMixFactor(&this->driver, channel, 1.0f, 0, 1); - } - - return this->channelReset; -} - -/* -** Set a channel's volume -*/ -void Audrv::SetChannelVolume(size_t channel, float volume) -{ - thread::Lock lock(this->mutex); - - audrvVoiceSetVolume(&this->driver, channel, volume); -} - -/* -** Check if a channel is playing -*/ -bool Audrv::IsChannelPlaying(size_t channel) -{ - thread::Lock lock(this->mutex); - - return audrvVoiceIsPlaying(&this->driver, channel); -} - -/* -** Check if a channel is paused -*/ -bool Audrv::IsChannelPaused(size_t channel) -{ - thread::Lock lock(this->mutex); - - return audrvVoiceIsPaused(&this->driver, channel); -} - -/* -** Adds an AudioDriverWaveBuf to the Audio Driver channel -** NOTE: Stop the Voice before Adding -** However, this is done when released from the audio pool -*/ -bool Audrv::AddWaveBuf(size_t channel, AudioDriverWaveBuf* waveBuf) -{ - if (this->channelReset) - { - thread::Lock lock(this->mutex); - - bool success = audrvVoiceAddWaveBuf(&this->driver, channel, waveBuf); - - if (success) - audrvVoiceStart(&this->driver, channel); - - return success; - } - - return false; -} - -/* -** Pause a channel -*/ -void Audrv::PauseChannel(size_t channel, bool pause) -{ - thread::Lock lock(this->mutex); - - audrvVoiceSetPaused(&this->driver, channel, pause); -} - -/* -** Stop a channel -** This is done on audio pool release -*/ -void Audrv::StopChannel(size_t channel) -{ - thread::Lock lock(this->mutex); - - audrvVoiceStop(&this->driver, channel); - audrvVoiceDrop(&this->driver, channel); -} - -/* -** Get the sample offset of the audio -** Unlike 3DS, this is continguous data -** So just return the offset and it's fine -*/ -u32 Audrv::GetSampleOffset(size_t channel) -{ - thread::Lock lock(this->mutex); - - return audrvVoiceGetPlayedSampleCount(&this->driver, channel); -} - -Audrv::~Audrv() -{ - if (this->initialized) - audrvClose(&this->driver); - - if (this->audioInitialized) - audrenExit(); -} - -void Audrv::Update() -{ - thread::Lock lock(this->mutex); - - audrvUpdate(&this->driver); -} diff --git a/platform/switch/source/driver/hidrv.cpp b/platform/switch/source/driver/hidrv.cpp deleted file mode 100644 index dd49f4be5..000000000 --- a/platform/switch/source/driver/hidrv.cpp +++ /dev/null @@ -1,271 +0,0 @@ -#include "driver/hidrv.h" -#include "objects/gamepad/gamepad.h" - -#include "modules/joystick/joystick.h" - -#include - -#include "debug/logger.h" -#include "deko3d/deko.h" - -using namespace love::driver; - -#define MODULE() love::Module::GetInstance(love::Module::M_JOYSTICK) - -Hidrv::Hidrv() : touchState {}, oldStateTouches {}, currentPadIndex(0), prevTouchCount(0) -{} - -void Hidrv::CheckFocus() -{ - bool focused = (appletGetFocusState() == AppletFocusState_InFocus); - - uint32_t message = 0; - Result res = appletGetMessage(&message); - - if (R_SUCCEEDED(res)) - { - bool shouldClose = !appletProcessMessage(message); - - if (shouldClose) - { - this->SendQuit(); - return; - } - - switch (message) - { - case AppletMessage_FocusStateChanged: - { - - bool oldFocus = focused; - AppletFocusState state = appletGetFocusState(); - - focused = (state == AppletFocusState_InFocus); - - this->SendFocus(focused); - - if (focused == oldFocus) - break; - - if (focused) - appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend); - else - appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleepNotify); - - break; - } - case AppletMessage_OperationModeChanged: - { - std::pair size; - ::deko3d::Instance().OnOperationMode(size); - - this->SendResize(size.first, size.second); - - break; - } - default: - break; - } - } -} - -bool Hidrv::Poll(LOVE_Event* event) -{ - if (!this->events.empty()) - { - *event = this->events.front(); - this->events.pop_front(); - - return true; - } - - if (this->hysteresis) - { - this->hysteresis = false; - return false; - } - - /* applet focus handling */ - this->CheckFocus(); - - /* touch screen */ - hidGetTouchScreenStates(&touchState, 1); - int touchCount = touchState.count; - - if (touchCount > 0) - { - for (int id = 0; id < touchCount; id++) - { - auto& newEvent = this->events.emplace_back(); - - if (touchCount > this->prevTouchCount && id >= this->prevTouchCount) - { - this->stateTouches[id] = this->touchState.touches[id]; //< read the touches - this->oldStateTouches[id] = - this->stateTouches[id]; //< set old touches to newly read ones - - newEvent.type = TYPE_TOUCHPRESS; - } - else - { - this->oldStateTouches[id] = - this->stateTouches[id]; //< set old touches to currently read ones - this->stateTouches[id] = this->touchState.touches[id]; //< read the touches - - newEvent.type = TYPE_TOUCHMOVED; - } - - newEvent.touch.id = id; - - newEvent.touch.x = this->stateTouches[id].x; - newEvent.touch.y = this->stateTouches[id].y; - - newEvent.touch.dx = (s32)this->stateTouches[id].x - this->oldStateTouches[id].x; - newEvent.touch.dy = (s32)this->stateTouches[id].y - this->oldStateTouches[id].y; - - newEvent.touch.pressure = 1.0f; - - if (newEvent.type == TYPE_TOUCHMOVED && !newEvent.touch.dx && !newEvent.touch.dy) - { - this->events.pop_back(); - continue; - } - } - } - - if (touchCount < this->prevTouchCount) - { - for (int id = 0; id < prevTouchCount; ++id) - { - auto& newEvent = this->events.emplace_back(); - - newEvent.type = TYPE_TOUCHRELEASE; - - newEvent.touch.id = id; - newEvent.touch.x = this->stateTouches[id].x; - newEvent.touch.y = this->stateTouches[id].y; - newEvent.touch.dx = 0.0f; - newEvent.touch.dy = 0.0f; - newEvent.touch.pressure = 0.0f; - } - } - - this->prevTouchCount = touchCount; - - /* shouldn't happen, but eh */ - if (!MODULE()) - return false; - - int newIndex = -1; - if ((newIndex = MODULE()->CheckGamepadAdded()) != -1) - { - auto& newEvent = this->events.emplace_back(); - newEvent.type = TYPE_GAMEPADADDED; - newEvent.padStatus.which = newIndex; - } - - if ((newIndex = MODULE()->CheckGamepadRemoved()) != -1) - { - auto& newEvent = this->events.emplace_back(); - newEvent.type = TYPE_GAMEPADREMOVED; - newEvent.padStatus.which = newIndex; - } - - /* this iterates the "active" joysticks only */ - for (size_t index = 0; index < MODULE()->GetJoystickCount(); index++) - { - Gamepad* gamepad = MODULE()->GetJoystickFromID(index); - - if (gamepad) - { - gamepad->UpdatePadState(); - - /* handle button inputs */ - - Gamepad::ButtonMapping button; - - const auto& mappings = gamepad->GetButtonMapping(); - const auto entries = mappings.GetEntries(); - - for (size_t index = 0; index < entries.second; index++) - { - if (gamepad->IsDown(index, button)) - { - auto& newEvent = this->events.emplace_back(); - - newEvent.type = TYPE_GAMEPADDOWN; - - newEvent.button.name = button.first; - newEvent.button.which = gamepad->GetID(); - newEvent.button.button = button.second; - } - } - - for (size_t index = 0; index < entries.second; index++) - { - if (gamepad->IsUp(index, button)) - { - auto& newEvent = this->events.emplace_back(); - - newEvent.type = TYPE_GAMEPADUP; - - newEvent.button.name = button.first; - newEvent.button.which = gamepad->GetID(); - newEvent.button.button = button.second; - } - } - - /* handle trigger inputs */ - for (size_t i = 5; i <= 6; i++) - { - auto& newEvent = this->events.emplace_back(); - - newEvent.type = TYPE_GAMEPADAXIS; - newEvent.axis.which = gamepad->GetID(); - - newEvent.axis.axis = (i == 5) ? "triggerleft" : "triggerright"; - newEvent.axis.value = gamepad->GetAxis(i); - newEvent.axis.number = i; - } - - /* handle stick inputs */ - for (size_t i = 1; i <= 4; i++) - { - auto& newEvent = this->events.emplace_back(); - - newEvent.type = TYPE_GAMEPADAXIS; - newEvent.axis.which = gamepad->GetID(); - - const char* axis = nullptr; - if (i < 3) // left - { - if ((i % 2) != 0) - axis = "leftx"; - else - axis = "lefty"; - } - else - { - if ((i % 2) != 0) - axis = "rightx"; - else - axis = "righty"; - } - - newEvent.axis.axis = axis; - newEvent.axis.value = gamepad->GetAxis(i); - newEvent.axis.number = i; - } - } - } - - if (this->events.empty()) - return false; - - *event = this->events.front(); - this->events.pop_front(); - - this->hysteresis = true; - - return true; -} diff --git a/platform/switch/source/modules/audio.cpp b/platform/switch/source/modules/audio.cpp deleted file mode 100644 index ab460efe4..000000000 --- a/platform/switch/source/modules/audio.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "modules/audio/audio.h" -#include "driver/audiodrv.h" - -using namespace love; - -void Audio::SetVolume(float volume) -{ - for (int mix = 0; mix < 2; mix++) - driver::Audrv::Instance().SetMixVolume(mix, volume); -} diff --git a/platform/switch/source/modules/event.cpp b/platform/switch/source/modules/event.cpp deleted file mode 100644 index 01b57da96..000000000 --- a/platform/switch/source/modules/event.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "modules/event/event.h" - -love::Event::Event() -{ - appletLockExit(); - appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend); -} - -love::Event::~Event() -{ - appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleep); - appletUnlockExit(); -} \ No newline at end of file diff --git a/platform/switch/source/modules/fontmodule.cpp b/platform/switch/source/modules/fontmodule.cpp deleted file mode 100644 index c65eac616..000000000 --- a/platform/switch/source/modules/fontmodule.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "modules/font/fontmodule.h" - -#include "common/data.h" -#include "utf8/utf8.h" - -using namespace love; - -FontModule::FontModule() -{ - if (FT_Init_FreeType(&this->library)) - throw love::Exception("TrueTypeFont Loading error: FT_Init_FreeType failed"); -} - -FontModule::~FontModule() -{ - FT_Done_FreeType(this->library); -} - -/* Create new System Font with Size and Hinting */ -Rasterizer* FontModule::NewTrueTypeRasterizer(Font::SystemFontType fontType, int size, - TrueTypeRasterizer::Hinting hinting) -{ - love::StrongReference data(new DefaultFontData(fontType), Acquire::NORETAIN); - - return this->NewTrueTypeRasterizer(data.Get(), size, hinting); -} - -/* Create new System Font with Size, DPI Scale, and Hinting */ -Rasterizer* FontModule::NewTrueTypeRasterizer(Font::SystemFontType fontType, int size, - float dpiScale, TrueTypeRasterizer::Hinting hinting) -{ - love::StrongReference data(new DefaultFontData(fontType), Acquire::NORETAIN); - - return this->NewTrueTypeRasterizer(data.Get(), size, dpiScale, hinting); -} - -/* Create new Standard System Font with Size */ -Rasterizer* FontModule::NewTrueTypeRasterizer(int size, TrueTypeRasterizer::Hinting hinting) -{ - love::StrongReference data(new DefaultFontData, Acquire::NORETAIN); - - return this->NewTrueTypeRasterizer(data.Get(), size, hinting); -} - -/* Create new Standard System Font with Size and DPI Scale */ -Rasterizer* FontModule::NewTrueTypeRasterizer(int size, float dpiScale, - TrueTypeRasterizer::Hinting hinting) -{ - StrongReference data(new DefaultFontData, Acquire::NORETAIN); - - return this->NewTrueTypeRasterizer(data.Get(), size, dpiScale, hinting); -} - -/* Create from FileData */ -Rasterizer* FontModule::NewRasterizer(FileData* data) -{ - if (TrueTypeRasterizer::Accepts(this->library, data)) - return this->NewTrueTypeRasterizer(data, 12, TrueTypeRasterizer::HINTING_NORMAL); - - throw love::Exception("Invalid font file: %s", data->GetFilename().c_str()); -} - -/* Create from Data with Size and Hinting */ -Rasterizer* FontModule::NewTrueTypeRasterizer(Data* data, int size, - TrueTypeRasterizer::Hinting hinting) -{ - return this->NewTrueTypeRasterizer(data, size, 1.0f, hinting); -} - -/* Create from Data with Size, DPI Scaling, and Hinting */ -Rasterizer* FontModule::NewTrueTypeRasterizer(Data* data, int size, float dpiScale, - love::TrueTypeRasterizer::Hinting hinting) -{ - return new TrueTypeRasterizer(this->library, data, size, hinting); -} diff --git a/platform/switch/source/modules/joystick.cpp b/platform/switch/source/modules/joystick.cpp deleted file mode 100644 index 23ac49ca4..000000000 --- a/platform/switch/source/modules/joystick.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "modules/joystick/joystick.h" - -#include "modules/timer/timer.h" - -using namespace love; - -static constexpr size_t MAX_GAMEPADS = 4; - -Joystick::VibrationThread::VibrationThread(VibrationPool* pool) : pool(pool), finish(false) -{ - this->threadName = "VibrationPool"; -} - -Joystick::VibrationThread::~VibrationThread() -{} - -void Joystick::VibrationThread::ThreadFunction() -{ - while (!this->finish) - { - this->pool->Update(); - svcSleepThread(5000000); - } -} - -void Joystick::VibrationThread::SetFinish() -{ - this->finish = true; -} - -Joystick::Joystick() : pool(nullptr), poolThread(nullptr) -{ - - this->pool = new VibrationPool(); - - this->poolThread = new VibrationThread(pool); - this->poolThread->Start(); -} - -Joystick::~Joystick() -{ - this->poolThread->SetFinish(); - this->poolThread->Wait(); - - delete this->poolThread; - delete this->pool; -} - -size_t Joystick::GetActiveControllerCount() -{ - size_t active = 0; - - for (size_t index = 0; index < MAX_GAMEPADS; index++) - { - HidNpadIdType id = static_cast(HidNpadIdType_No1 + index); - uint32_t styleSet = hidGetNpadStyleSet(id); - - /* check for handheld */ - if (styleSet == 0) - styleSet = hidGetNpadStyleSet(HidNpadIdType_Handheld); - - if (styleSet != 0) - active++; - } - - return active; -} - -bool Joystick::AddVibration(Gamepad* gamepad, size_t id) -{ - return this->pool->AssignGamepad(gamepad, id); -} diff --git a/platform/switch/source/modules/keyboard.cpp b/platform/switch/source/modules/keyboard.cpp deleted file mode 100644 index 473bf7460..000000000 --- a/platform/switch/source/modules/keyboard.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "modules/keyboard/keyboard.h" -#include "common/bidirectionalmap.h" -#include - -using namespace love; - -Keyboard::Keyboard() : common::Keyboard((MAX_INPUT_LENGTH * 4) + 1) -{} - -std::string Keyboard::SetTextInput(const Keyboard::SwkbdOpt& options) -{ - Result res = swkbdCreate(&this->keyboard, 0); - - if (R_FAILED(res)) - return std::string(); - - uint32_t maxLength = this->CalculateEncodingMaxLength(options.maxLength); - memset(this->text, 0, maxLength); - - /* set the software keyboard type */ - - swkbdConfigSetType(&this->keyboard, static_cast(options.type)); - - /* generic config things */ - - swkbdConfigSetInitialCursorPos(&this->keyboard, 1); - swkbdConfigSetBlurBackground(&this->keyboard, 1); - - /* password flag */ - - swkbdConfigSetPasswordFlag(&this->keyboard, options.isPassword); - - /* allow the dictionary to be used */ - - swkbdConfigSetDicFlag(&this->keyboard, 1); - - /* set max input length */ - - swkbdConfigSetStringLenMax(&this->keyboard, maxLength); - - /* set the hint text */ - - swkbdConfigSetGuideText(&this->keyboard, options.hint.c_str()); - - /* show the software keyboard with the text */ - - res = swkbdShow(&this->keyboard, text, maxLength); - - if (R_SUCCEEDED(res)) - swkbdClose(&this->keyboard); - - return text; -} - -// clang-format off -constexpr auto keyboardTypes = BidirectionalMap<>::Create( - "normal", Keyboard::KeyboardType::TYPE_NORMAL, - "qwerty", Keyboard::KeyboardType::TYPE_QWERTY, - "numpad", Keyboard::KeyboardType::TYPE_NUMPAD -); -// clang-format on - -bool Keyboard::GetConstant(const char* in, KeyboardType& out) -{ - return keyboardTypes.Find(in, out); -} - -bool Keyboard::GetConstant(KeyboardType in, const char*& out) -{ - return keyboardTypes.ReverseFind(in, out); -} - -std::vector Keyboard::GetConstants(KeyboardType) -{ - return keyboardTypes.GetNames(); -} diff --git a/platform/switch/source/modules/system.cpp b/platform/switch/source/modules/system.cpp deleted file mode 100644 index dd1594b2e..000000000 --- a/platform/switch/source/modules/system.cpp +++ /dev/null @@ -1,292 +0,0 @@ -#include "modules/system/system.h" -#include - -#include "common/bidirectionalmap.h" -#include "common/results.h" - -using namespace love; - -System::System() -{ - /* Check if there's a pre-selected user first */ - if (R_FAILED(accountGetPreselectedUser(&this->userID))) - { - /* Create player selection UI settings */ - PselUiSettings settings; - pselUiCreate(&settings, PselUiMode_UserSelector); - - /* Ask for a user account */ - pselUiShow(&settings, &this->userID); - } -} - -/* https://tinyurl.com/yyh7tnml */ -int System::GetProcessorCount() -{ - return TEGRA_CPU_COUNT; -} - -const std::string& System::GetUsername() -{ - if (!this->systemInfo.username.empty()) - return this->systemInfo.username; - - AccountProfile profile {}; - AccountProfileBase base {}; - - /* Get the profile from the System's userID we selected */ - R_UNLESS(accountGetProfile(&profile, this->userID), LOVE_STRING_EMPTY); - - /* Get the base profile */ - R_UNLESS(accountProfileGet(&profile, NULL, &base), LOVE_STRING_EMPTY); - - this->systemInfo.username = base.nickname; - - accountProfileClose(&profile); - - return this->systemInfo.username; -} - -System::PowerState System::GetPowerInfo(uint8_t& percent) const -{ - uint32_t batteryPercent = 100; - PsmChargerType type = PsmChargerType_Unconnected; - - PowerState state = PowerState::POWER_UNKNOWN; - - psmGetBatteryChargePercentage(&batteryPercent); - psmGetChargerType(&type); - - state = (type > 0 && type != PsmChargerType_NotSupported) ? PowerState::POWER_CHARGING - : PowerState::POWER_BATTERY; - - if (percent == 100 && type != PsmChargerType_Unconnected) - state = PowerState::POWER_CHARGED; - - return state; -} - -System::NetworkState System::GetNetworkInfo(uint8_t& signal) const -{ - NetworkState state = NetworkState::NETWORK_UNKNOWN; - - uint32_t wifiStrength = 0; - Result res = nifmGetInternetConnectionStatus(NULL, &wifiStrength, NULL); - - signal = static_cast(wifiStrength); - state = R_SUCCEEDED(res) ? NetworkState::NETWORK_CONNECTED : NetworkState::NETWORK_DISCONNECTED; - - return state; -} - -const std::string& System::GetPreferredLocales() -{ - if (!this->systemInfo.language.empty()) - return this->systemInfo.language; - - uint64_t languageCode = 0; - SetLanguage language; - - /* Get the System Language Code */ - R_UNLESS(setGetSystemLanguage(&languageCode), LOVE_STRING_EMPTY); - - /* Convert the Language Code to SetLanguage */ - R_UNLESS(setMakeLanguage(languageCode, &language), LOVE_STRING_EMPTY); - - const char* name = nullptr; - if (!System::GetConstant(language, name)) - name = "Unknown"; - - this->systemInfo.language = name; - - return this->systemInfo.language; -} - -const std::string& System::GetModel() -{ - if (!this->systemInfo.model.empty()) - return this->systemInfo.model; - - SetSysProductModel model = SetSysProductModel_Invalid; - - /* Get the Product Model */ - R_UNLESS(setsysGetProductModel(&model), LOVE_STRING_EMPTY); - - const char* name = nullptr; - if (!System::GetConstant(model, name)) - name = "Unknown"; - - this->systemInfo.model = name; - - return this->systemInfo.model; -} - -const std::string& System::GetRegion() -{ - if (!this->systemInfo.region.empty()) - return this->systemInfo.region; - - SetRegion region; - - /* Get the System Region */ - R_UNLESS(setGetRegionCode(®ion), LOVE_STRING_EMPTY); - - const char* name = nullptr; - if (!System::GetConstant(region, name)) - name = "Unknown"; - - this->systemInfo.region = name; - - return this->systemInfo.region; -} - -const std::string& System::GetVersion() -{ - if (!this->systemInfo.version.empty()) - return this->systemInfo.version; - - SetSysFirmwareVersion firmwareVersion; - - /* Get the System Firmware Version */ - R_UNLESS(setsysGetFirmwareVersion(&firmwareVersion), LOVE_STRING_EMPTY); - - this->systemInfo.version = firmwareVersion.display_version; - return this->systemInfo.version; -} - -const std::string& System::GetFriendCode() -{ - if (!this->systemInfo.friendCode.empty()) - return this->systemInfo.friendCode; - - FriendsUserSetting settings; - - R_UNLESS(friendsGetUserSetting(this->userID, &settings), LOVE_STRING_EMPTY); - - this->systemInfo.friendCode = settings.friend_code; - - return this->systemInfo.friendCode; -} - -const std::string& System::GetSystemTheme() -{ - const char* theme = nullptr; - ColorSetId colorID; - - R_UNLESS(setsysGetColorSetId(&colorID), LOVE_STRING_EMPTY); - - if (!System::GetConstant(colorID, theme)) - theme = "Unknown"; - - this->systemInfo.colorTheme = theme; - return this->systemInfo.colorTheme; -} - -// clang-format off -constexpr auto languages = BidirectionalMap<>::Create( - "jp", SetLanguage_JA, - "en_US", SetLanguage_ENUS, - "fr", SetLanguage_FR, - "de", SetLanguage_DE, - "it", SetLanguage_IT, - "es", SetLanguage_ES, - "zh_CN", SetLanguage_ZHCN, - "ko", SetLanguage_KO, - "nl", SetLanguage_NL, - "pt", SetLanguage_PT, - "ru", SetLanguage_RU, - "zh_TW", SetLanguage_ZHTW, - "en_GB", SetLanguage_ENGB, - "fr_CA", SetLanguage_FRCA, - "es_419", SetLanguage_ES419, - "zh_HANS", SetLanguage_ZHHANS, - "zh_HANT", SetLanguage_ZHHANT, - "pt_BR", SetLanguage_PTBR -); - -constexpr auto models = BidirectionalMap<>::Create( - "Invalid", SetSysProductModel_Invalid, - "Erista", SetSysProductModel_Nx, - "Erista Simulation", SetSysProductModel_Copper, - "Mariko", SetSysProductModel_Iowa, - "Mariko Lite", SetSysProductModel_Hoag, - "Mariko Simulation", SetSysProductModel_Calcio, - "Mariko Pro", SetSysProductModel_Aula -); - -constexpr auto regions = BidirectionalMap<>::Create( - "Japan", SetRegion_JPN, - "United States", SetRegion_USA, - "Europe", SetRegion_EUR, - "Australia/New Zealand", SetRegion_AUS, - "Hong Kong/Taiwan/Korea", SetRegion_HTK, - "China", SetRegion_CHN -); - -constexpr auto themes = BidirectionalMap<>::Create( - "dark", ColorSetId_Dark, - "light", ColorSetId_Light -); -// clang-format on - -/* THEME CONSTANTS */ -bool System::GetConstant(const char* in, ColorSetId& out) -{ - return themes.Find(in, out); -} - -bool System::GetConstant(ColorSetId in, const char*& out) -{ - return themes.ReverseFind(in, out); -} - -/* LANGUAGE CONSTANTS */ - -bool System::GetConstant(const char* in, SetLanguage& out) -{ - return languages.Find(in, out); -} - -bool System::GetConstant(SetLanguage in, const char*& out) -{ - return languages.ReverseFind(in, out); -} - -std::vector System::GetConstants(SetLanguage) -{ - return languages.GetNames(); -} - -/* MODEL CONSTANTS */ - -bool System::GetConstant(const char* in, SetSysProductModel& out) -{ - return models.Find(in, out); -} - -bool System::GetConstant(SetSysProductModel in, const char*& out) -{ - return models.ReverseFind(in, out); -} - -std::vector System::GetConstants(SetSysProductModel) -{ - return models.GetNames(); -} - -/* REGION CONSTANTS */ - -bool System::GetConstant(const char* in, SetRegion& out) -{ - return regions.Find(in, out); -} - -bool System::GetConstant(SetRegion in, const char*& out) -{ - return regions.ReverseFind(in, out); -} - -std::vector System::GetConstants(SetRegion) -{ - return regions.GetNames(); -} diff --git a/platform/switch/source/modules/timer.cpp b/platform/switch/source/modules/timer.cpp deleted file mode 100644 index cc8f4e8cb..000000000 --- a/platform/switch/source/modules/timer.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "modules/timer/timer.h" -#include - -using namespace love; - -static constexpr auto NS_TO_SEC = 1000000000.0; - -Timer::Timer() -{ - Timer::reference = armGetSystemTick(); - this->prevFPSUpdate = currentTime = this->GetTime(); -} - -double common::Timer::GetTime() -{ - return armTicksToNs(armGetSystemTick() - Timer::reference) / NS_TO_SEC; -} \ No newline at end of file diff --git a/platform/switch/source/modules/window.cpp b/platform/switch/source/modules/window.cpp deleted file mode 100644 index fc3821868..000000000 --- a/platform/switch/source/modules/window.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "modules/window/window.h" - -using namespace love; - -#include "common/exception.h" -#include "deko3d/deko.h" - -#include "common/screen.h" - -Window::Window() -{ - this->fullscreenModes = { { Screen::HANDHELD_WIDTH, Screen::HANDHELD_HEIGHT }, - { Screen::DOCKED_WIDTH, Screen::DOCKED_HEIGHT } }; -} - -void Window::GetWindow(int& width, int& height) -{ - std::pair size; - ::deko3d::Instance().OnOperationMode(size); - - width = size.first; - height = size.second; - - this->OnSizeChanged(width, height); -} - -bool Window::CreateWindowAndContext() -{ - std::pair size; - ::deko3d::Instance().OnOperationMode(size); - - this->OnSizeChanged(size.first, size.second); - - return true; -} - -Window::DisplaySize Window::GetDesktopSize() -{ - auto operationMode = appletGetOperationMode(); - - return this->fullscreenModes[operationMode]; -} - -void Window::OnSizeChanged(int width, int height) -{ - if (this->graphics.Get()) - this->graphics->SetMode(width, height); - - const Rect newViewport = { 0, 0, width, height }; - - ::deko3d::Instance().SetViewport(newViewport); - ::deko3d::Instance().SetScissor(newViewport, false); -} - -int Window::GetDisplayCount() -{ - return 1; -} diff --git a/platform/switch/source/objects/canvas.cpp b/platform/switch/source/objects/canvas.cpp deleted file mode 100644 index 510508f28..000000000 --- a/platform/switch/source/objects/canvas.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "objects/canvas/canvas.h" - -#include "deko3d/deko.h" - -#include "modules/graphics/graphics.h" - -using namespace love; - -Canvas::Canvas(const Canvas::Settings& settings) : common::Canvas(settings), descriptor {} -{ - // Create layout for the (multisampled) color buffer - dk::ImageLayout layoutColorBuffer; - dk::ImageLayoutMaker { ::deko3d::Instance().GetDevice() } - .setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression) - .setFormat(DkImageFormat_RGBA8_Unorm) - .setDimensions(this->width, this->height) - .initialize(layoutColorBuffer); - - // Create the color buffer - this->colorMemory = ::deko3d::Instance().GetImages().allocate(layoutColorBuffer.getSize(), - layoutColorBuffer.getAlignment()); - this->colorBuffer.initialize(layoutColorBuffer, this->colorMemory.getMemBlock(), - this->colorMemory.getOffset()); - - // Clear to transparent black - ::deko3d::Instance().BindFramebuffer(this); - ::deko3d::Instance().ClearColor({ 0, 0, 0, 0 }); - ::deko3d::Instance().BindFramebuffer(); - - dk::ImageView view { this->colorBuffer }; - this->descriptor.initialize(view); - - // Register the texture handle for the descriptor - this->handle = ::deko3d::Instance().RegisterResHandle(this->descriptor); -} - -Canvas::~Canvas() -{ - this->colorMemory.destroy(); -} - -void Canvas::Draw(Graphics* gfx, Quad* quad, const Matrix4& localTransform) -{ - if (gfx->IsCanvasActive(this)) - throw love::Exception("Cannot render a Canvas to itself!"); - - Texture::Draw(gfx, quad, localTransform); -} diff --git a/platform/switch/source/objects/font.cpp b/platform/switch/source/objects/font.cpp deleted file mode 100644 index f18fe8d9f..000000000 --- a/platform/switch/source/objects/font.cpp +++ /dev/null @@ -1,940 +0,0 @@ -#include "objects/font/font.h" - -#include "modules/font/fontmodule.h" -#include "modules/graphics/graphics.h" - -#include "common/bidirectionalmap.h" -#include "common/matrix.h" -#include "deko3d/graphics.h" - -#include "deko3d/deko.h" -#include "utf8/utf8.h" - -using namespace vertex; - -using namespace love; - -#define FONT_MODULE() (Module::GetInstance(Module::M_FONT)) - -Font::Font(Rasterizer* r, const Texture::Filter& filter) : - common::Font(filter), - rasterizers({ r }), - textureWidth(128), - textureHeight(128), - useSpacesAsTab(false), - textureCacheID(0) -{ - this->dpiScale = rasterizers[0]->GetDPIScale(); - this->height = rasterizers[0]->GetHeight(); - - this->lineHeight = 1.0f; - - this->filter = filter; - this->filter.mipmap = Texture::FILTER_NONE; - - while (true) - { - if ((this->height * 0.8) * this->height * 30 <= this->textureWidth * this->textureHeight) - break; - - TextureSize nextsize = this->GetNextTextureSize(); - - if (nextsize.width <= textureWidth && nextsize.height <= textureHeight) - break; - - this->textureWidth = nextsize.width; - this->textureHeight = nextsize.height; - } - - /* No tab character in the Rasterizer. */ - if (!r->HasGlyph(9)) - this->useSpacesAsTab = true; - - this->textureCacheID++; - - this->glyphs.clear(); - this->images.clear(); - - this->CreateTexture(); -} - -Font::~Font() -{ - this->glyphs.clear(); - this->images.clear(); -} - -void Font::SetFilter(const Texture::Filter& filter) -{ - for (const auto& image : this->images) - image->SetFilter(filter); - - this->filter = filter; -} - -Font::TextureSize Font::GetNextTextureSize() const -{ - TextureSize size = { this->textureWidth, this->textureHeight }; - - int maxSize = 2048; - - int maxWidth = std::min(8192, maxSize); - int maxHeight = std::min(4096, maxSize); - - if (size.width * 2 <= maxWidth || size.height * 2 <= maxHeight) - { - // {128, 128} -> {256, 128} -> {256, 256} -> {512, 256} -> etc. - if (size.width == size.height) - size.width *= 2; - else - size.height *= 2; - } - - return size; -} - -void Font::CreateTexture() -{ - auto gfx = Module::GetInstance(Module::M_GRAPHICS); - - TextureSize size = { this->textureWidth, this->textureHeight }; - TextureSize nextSize = this->GetNextTextureSize(); - - love::Image* texture = nullptr; - bool recreatetexture = false; - - /* - ** If we have an existing texture already, we'll try replacing it with a - ** larger-sized one rather than creating a second one. Having a single - ** texture reduces texture switches and draw calls when rendering. - */ - if ((nextSize.width > size.width || nextSize.height > size.height) && !this->images.empty()) - { - recreatetexture = true; - size = nextSize; - images.pop_back(); - } - - texture = - gfx->NewImage(love::Texture::TEXTURE_2D, PIXELFORMAT_RGBA8, size.width, size.height, 1); - texture->SetFilter(this->filter); - - { - size_t bytesPerPixel = love::GetPixelFormatSize(PIXELFORMAT_RGBA8); - size_t pixelCount = size.width * size.height; - - std::vector empty(pixelCount * bytesPerPixel, 0); - Rect rect = { 0, 0, size.width, size.height }; - - texture->ReplacePixels(empty.data(), empty.size(), rect); - } - - this->images.emplace_back(texture, Acquire::NORETAIN); - - this->textureWidth = size.width; - this->textureHeight = size.height; - - this->rowHeight = this->textureX = this->textureY = this->TEXTURE_PADDING; - - if (recreatetexture) - { - this->textureCacheID++; - - std::vector glyphsToAdd; - - for (const auto& glyphPair : this->glyphs) - glyphsToAdd.push_back(glyphPair.first); - - this->glyphs.clear(); - - for (uint32_t glyph : glyphsToAdd) - this->AddGlyph(glyph); - } -} - -uint32_t Font::GetTextureCacheID() -{ - return this->textureCacheID; -} - -love::GlyphData* Font::GetRasterizerGlyphData(uint32_t glyph) -{ - /* Use spaces for the tab 'glyph' */ - if (glyph == 9 && useSpacesAsTab) - { - love::GlyphData* spacegd = this->rasterizers[0]->GetGlyphData(32); - - love::GlyphData::GlyphMetrics gm = {}; - - gm.advance = spacegd->GetAdvance() * SPACES_PER_TAB; - gm.bearingX = spacegd->GetBearingX(); - gm.bearingY = spacegd->GetBearingY(); - - spacegd->Release(); - - return new love::GlyphData(glyph, gm); - } - - for (const auto& r : rasterizers) - { - if (r->HasGlyph(glyph)) - return r->GetGlyphData(glyph); - } - - return rasterizers[0]->GetGlyphData(glyph); -} - -float Font::GetDPIScale() const -{ - return this->dpiScale; -} - -const Font::Glyph& Font::FindGlyph(uint32_t glyph) -{ - const auto it = this->glyphs.find(glyph); - - if (it != this->glyphs.end()) - return it->second; - - return this->AddGlyph(glyph); -} - -bool Font::HasGlyph(uint32_t glyph) const -{ - for (const StrongReference& r : rasterizers) - { - if (r->HasGlyph(glyph)) - return true; - } - - return false; -} - -static inline uint16_t norm16(double n) -{ - return uint16_t(n * 0xFFFF); -} - -const Font::Glyph& Font::AddGlyph(uint32_t glyph) -{ - love::StrongReference gd(this->GetRasterizerGlyphData(glyph), - Acquire::NORETAIN); - - int width = gd->GetWidth(); - int height = gd->GetHeight(); - - if (width + this->TEXTURE_PADDING * 2 < this->textureWidth && - height + this->TEXTURE_PADDING * 2 < this->textureHeight) - { - if (this->textureX + width + this->TEXTURE_PADDING > this->textureWidth) - { - // Out of space - new row! - this->textureX = this->TEXTURE_PADDING; - this->textureY += this->rowHeight; - this->rowHeight = this->TEXTURE_PADDING; - } - - if (this->textureY + height + this->TEXTURE_PADDING > this->textureHeight) - { - /* Totally out of space - new texture! */ - this->CreateTexture(); - - /* Ensure last glyph is re-added */ - return this->AddGlyph(glyph); - } - } - - Glyph g; - - g.texture = 0; - g.spacing = floorf(gd->GetAdvance() / this->dpiScale + 0.5f); - - std::fill_n(g.vertices, 4, GlyphVertex {}); - - /* Don't waste space on empty glyphs */ - if (width > 0 && height > 0) - { - Image* image = images.back(); - g.texture = image; - - Rect rect = { this->textureX, this->textureY, gd->GetWidth(), gd->GetHeight() }; - - image->ReplacePixels(gd->GetData(), gd->GetSize(), rect); - - double tX = (double)this->textureX, tY = (double)this->textureY; - double tWidth = (double)this->textureWidth, tHeight = (double)this->textureHeight; - - Colorf c(1.0f, 1.0f, 1.0f, 1.0f); - - // Extrude the quad borders by 1 pixel. We have an extra pixel of - // transparent padding in the texture atlas, so the quad extrusion will - // add some antialiasing at the edges of the quad. - int o = 1; - - // 0---3 - // | | - // 1---2 - const GlyphVertex verts[4] = { - { float(-o), float(-o), norm16((tX - o) / tWidth), norm16((tY - o) / tHeight), c }, - { float(-o), (height + o) / this->dpiScale, norm16((tX - o) / tWidth), - norm16((tY + height + o) / tHeight), c }, - { (width + o) / this->dpiScale, (height + o) / this->dpiScale, - norm16((tX + width + o) / tWidth), norm16((tY + height + o) / tHeight), c }, - { (width + o) / this->dpiScale, float(-o), norm16((tX + width + o) / tWidth), - norm16((tY - o) / tHeight), c }, - }; - - // Copy vertex data to the glyph - // and set proper bearing. - for (int i = 0; i < 4; i++) - { - g.vertices[i] = verts[i]; - - g.vertices[i].x += gd->GetBearingX() / this->dpiScale; - g.vertices[i].y -= gd->GetBearingY() / this->dpiScale; - } - - this->textureX += width + this->TEXTURE_PADDING; - this->rowHeight = std::max(this->rowHeight, height + this->TEXTURE_PADDING); - } - - this->glyphs[glyph] = g; - return this->glyphs[glyph]; -} - -void Font::GetCodepointsFromString(const std::string& text, Codepoints& codepoints) -{ - codepoints.reserve(text.size()); - - try - { - utf8::iterator i(text.begin(), text.begin(), text.end()); - utf8::iterator end(text.end(), text.begin(), text.end()); - - while (i != end) - { - uint32_t g = *i++; - codepoints.push_back(g); - } - } - catch (utf8::exception& e) - { - throw love::Exception("UTF-8 decoding error: %s", e.what()); - } -} - -void Font::GetCodepointsFromString(const std::vector& strings, - ColoredCodepoints& codepoints) -{ - if (strings.empty()) - return; - - codepoints.codes.reserve(strings[0].string.size()); - - for (const ColoredString& coloredString : strings) - { - if (coloredString.string.size() == 0) - continue; - - IndexedColor iColor = { coloredString.color, (int)codepoints.codes.size() }; - codepoints.colors.push_back(iColor); - - Font::GetCodepointsFromString(coloredString.string, codepoints.codes); - } - - if (codepoints.colors.size() == 1) - { - IndexedColor iColor = codepoints.colors[0]; - - if (iColor.index == 0 && iColor.color == Colorf(1.0f, 1.0f, 1.0f, 1.0f)) - codepoints.colors.pop_back(); - } -} - -std::vector Font::GenerateVertices(const ColoredCodepoints& codepoints, - const Colorf& constantColor, - std::vector& glyphVertices, - float extra_spacing, Vector2 offset, - TextInfo* info) -{ - /* - ** Spacing counter and - ** newline handling. - */ - float dx = offset.x; - float dy = offset.y; - - float heightoffset = 0.0f; - - if (this->rasterizers[0]->GetDataType() == Rasterizer::DATA_TRUETYPE) - heightoffset = this->GetBaseline(); - - int maxwidth = 0; - std::vector commands; - - // Pre-allocate space for the maximum possible number of vertices. - size_t vertstartsize = glyphVertices.size(); - glyphVertices.reserve(vertstartsize + codepoints.codes.size() * 4); - - uint32_t prevGlyph = 0; - - Colorf linearConstant = Graphics::GammaCorrectColor(constantColor); - Colorf currentColor = constantColor; - - int currentColorIndex = -1; - int numColors = (int)codepoints.colors.size(); - - for (int i = 0; i < (int)codepoints.codes.size(); i++) - { - uint32_t glyphChar = codepoints.codes[i]; - - if (currentColorIndex + 1 < numColors && - codepoints.colors[currentColorIndex + 1].index == i) - { - Colorf c = codepoints.colors[++currentColorIndex].color; - - c.r = std::min(std::max(c.r, 0.0f), 1.0f); - c.g = std::min(std::max(c.g, 0.0f), 1.0f); - c.b = std::min(std::max(c.b, 0.0f), 1.0f); - c.a = std::min(std::max(c.a, 0.0f), 1.0f); - - Graphics::GammaCorrectColor(c); - c *= linearConstant; - Graphics::UnGammaCorrectColor(c); - - currentColor = c; - } - - if (glyphChar == '\n') - { - if (dx > maxwidth) - maxwidth = (int)dx; - - // Wrap newline, but do not print it. - dy += floorf(this->GetHeight() * this->GetLineHeight() + 0.5f); - dx = offset.x; - - prevGlyph = 0; - - continue; - } - - /* Ignore carriage returns */ - if (glyphChar == '\r') - continue; - - uint32_t cacheID = this->textureCacheID; - - const Glyph& glyph = this->FindGlyph(glyphChar); - - /* - ** If findGlyph invalidates the texture cache - ** restart the loop. - */ - if (cacheID != this->textureCacheID) - { - i = -1; // The next iteration will increment this to 0. - maxwidth = 0; - - dx = offset.x; - dy = offset.y; - - commands.clear(); - - glyphVertices.resize(vertstartsize); - prevGlyph = 0; - - currentColorIndex = -1; - currentColor = constantColor; - - continue; - } - - /* Add kerning to the current horizontal offset. */ - dx += this->GetKerning(prevGlyph, glyphChar); - - if (glyph.texture != nullptr) - { - /* - ** Copy the vertices and set their colors - ** and relative positions. - */ - for (int j = 0; j < 4; j++) - { - glyphVertices.push_back(glyph.vertices[j]); - glyphVertices.back().x += dx; - glyphVertices.back().y += dy + heightoffset; - - glyphVertices.back().color = currentColor; - } - - /* Check if glyph texture has changed since the last iteration. */ - if (commands.empty() || commands.back().texture != glyph.texture) - { - /* Add a new draw command if the texture has changed. */ - DrawCommand cmd; - cmd.startVertex = (int)glyphVertices.size() - 4; - cmd.vertexCount = 0; - cmd.texture = glyph.texture; - - commands.push_back(cmd); - } - - commands.back().vertexCount += 4; - } - - dx += glyph.spacing; - - // Account for extra spacing given to space characters. - if (glyphChar == ' ' && extra_spacing != 0.0f) - dx = floorf(dx + extra_spacing); - - prevGlyph = glyphChar; - } - - const auto drawsort = [](const DrawCommand& a, const DrawCommand& b) -> bool { - /* - ** Texture binds are expensive - ** so we should sort by that first. - */ - if (a.texture != b.texture) - return a.texture < b.texture; - else - return a.startVertex < b.startVertex; - }; - - std::sort(commands.begin(), commands.end(), drawsort); - - if (dx > maxwidth) - maxwidth = (int)dx; - - if (info != nullptr) - { - info->width = maxwidth - offset.x; - info->height = (int)dy + - (dx > 0.0f ? floorf(this->GetHeight() * this->GetLineHeight() + 0.5f) : 0) - - offset.y; - } - - return commands; -} - -std::vector Font::GenerateVerticesFormatted( - const ColoredCodepoints& text, const Colorf& constantColor, float wrap, AlignMode align, - std::vector& vertices, TextInfo* info) -{ - wrap = std::max(wrap, 0.0f); - - uint32_t cacheid = this->textureCacheID; - - std::vector drawcommands; - vertices.reserve(text.codes.size() * 4); - - std::vector widths; - std::vector lines; - - this->GetWrap(text, wrap, lines, &widths); - - float y = 0.0f; - float maxwidth = 0.0f; - - for (int i = 0; i < (int)lines.size(); i++) - { - const auto& line = lines[i]; - - float width = (float)widths[i]; - love::Vector2 offset(0.0f, floorf(y)); - float extraspacing = 0.0f; - - maxwidth = std::max(width, maxwidth); - - switch (align) - { - case ALIGN_RIGHT: - offset.x = floorf(wrap - width); - break; - case ALIGN_CENTER: - offset.x = floorf((wrap - width) / 2.0f); - break; - case ALIGN_JUSTIFY: - { - float numspaces = (float)std::count(line.codes.begin(), line.codes.end(), ' '); - if (width < wrap && numspaces >= 1) - extraspacing = (wrap - width) / numspaces; - else - extraspacing = 0.0f; - break; - } - case ALIGN_LEFT: - default: - break; - } - - std::vector newcommands = - this->GenerateVertices(line, constantColor, vertices, extraspacing, offset); - - if (!newcommands.empty()) - { - auto firstcmd = newcommands.begin(); - - // If the first draw command in the new list has the same texture - // as the last one in the existing list we're building and its - // vertices are in-order, we can combine them (saving a draw call.) - if (!drawcommands.empty()) - { - auto prevcmd = drawcommands.back(); - if (prevcmd.texture == firstcmd->texture && - (prevcmd.startVertex + prevcmd.vertexCount) == firstcmd->startVertex) - { - drawcommands.back().vertexCount += firstcmd->vertexCount; - ++firstcmd; - } - } - - // Append the new draw commands to the list we're building. - drawcommands.insert(drawcommands.end(), firstcmd, newcommands.end()); - } - - y += this->GetHeight() * this->GetLineHeight(); - } - - if (info != nullptr) - { - info->width = (int)maxwidth; - info->height = (int)y; - } - - if (cacheid != textureCacheID) - { - vertices.clear(); - drawcommands = this->GenerateVerticesFormatted(text, constantColor, wrap, align, vertices); - } - - return drawcommands; -} - -float Font::GetKerning(uint32_t leftGlyph, uint32_t rightGlyph) -{ - uint64_t packed = ((uint64_t)leftGlyph << 32) | (uint64_t)rightGlyph; - const auto iterator = this->kerning.find(packed); - - if (iterator != this->kerning.end()) - return iterator->second; - - float kern = rasterizers[0]->GetKerning(leftGlyph, rightGlyph); - - for (const auto& rasterizer : this->rasterizers) - { - if (rasterizer->HasGlyph(leftGlyph) && rasterizer->HasGlyph(rightGlyph)) - { - kern = floorf(rasterizer->GetKerning(leftGlyph, rightGlyph) / this->dpiScale + 0.5f); - break; - } - } - - this->kerning[packed] = kern; - - return kern; -} - -void Font::Print(Graphics* gfx, const std::vector& text, - const Matrix4& localTransform, const Colorf& color) -{ - ColoredCodepoints codepoints; - Font::GetCodepointsFromString(text, codepoints); - - std::vector vertices; - std::vector drawCommands = this->GenerateVertices(codepoints, color, vertices); - - this->PrintV(gfx, localTransform, drawCommands, vertices); -} - -void Font::Printf(Graphics* gfx, const std::vector& text, float wrap, - Font::AlignMode align, const Matrix4& localTransform, const Colorf& color) -{ - ColoredCodepoints codepoints; - Font::GetCodepointsFromString(text, codepoints); - - std::vector vertices; - std::vector drawCommands = - this->GenerateVerticesFormatted(codepoints, color, wrap, align, vertices); - - this->PrintV(gfx, localTransform, drawCommands, vertices); -} - -void Font::PrintV(Graphics* gfx, const Matrix4& t, const std::vector& drawCommands, - const std::vector& vertices) -{ - if (vertices.empty() || drawCommands.empty()) - return; - - Matrix4 m(gfx->GetTransform(), t); - - for (const DrawCommand& cmd : drawCommands) - { - vertex::GlyphVertex vertexData[cmd.vertexCount]; - - memcpy(vertexData, &vertices[cmd.startVertex], sizeof(GlyphVertex) * cmd.vertexCount); - m.TransformXY(vertexData, &vertices[cmd.startVertex], cmd.vertexCount); - - std::vector verts = vertex::GenerateTextureFromGlyphs(vertexData, cmd.vertexCount); - - ::deko3d::Instance().RenderTexture(cmd.texture->GetHandle(), verts.data(), cmd.vertexCount); - } -} - -void Font::GetWrap(const std::vector& text, float wraplimit, - std::vector& lines, std::vector* linewidths) -{ - ColoredCodepoints cps; - Font::GetCodepointsFromString(text, cps); - - std::vector codepointlines; - this->GetWrap(cps, wraplimit, codepointlines, linewidths); - - std::string line; - - for (const ColoredCodepoints& codepoints : codepointlines) - { - line.clear(); - line.reserve(codepoints.codes.size()); - - for (uint32_t codepoint : codepoints.codes) - { - char character[5] = { '\0' }; - char* end = utf8::unchecked::append(codepoint, character); - - line.append(character, end - character); - } - - lines.push_back(line); - } -} - -void Font::GetWrap(const ColoredCodepoints& codepoints, float wraplimit, - std::vector& lines, std::vector* linewidths) -{ - // Per-line info. - float width = 0.0f; - - float widthbeforelastspace = 0.0f; - float widthoftrailingspace = 0.0f; - - uint32_t prevglyph = 0; - - int lastspaceindex = -1; - - // Keeping the indexed colors "in sync" is a bit tricky, since we split - // things up and we might skip some glyphs but we don't want to skip any - // color which starts at those indices. - Colorf curcolor(1.0f, 1.0f, 1.0f, 1.0f); - bool addcurcolor = false; - int curcolori = -1; - int endcolori = (int)codepoints.colors.size() - 1; - - // A wrapped line of text. - ColoredCodepoints wline; - - int i = 0; - while (i < (int)codepoints.codes.size()) - { - uint32_t c = codepoints.codes[i]; - - // Determine the current color before doing anything else, to make sure - // it's still applied to future glyphs even if this one is skipped. - if (curcolori < endcolori && codepoints.colors[curcolori + 1].index == i) - { - curcolor = codepoints.colors[curcolori + 1].color; - curcolori++; - addcurcolor = true; - } - - // Split text at newlines. - if (c == '\n') - { - lines.push_back(wline); - - // Ignore the width of any trailing spaces, for individual lines. - if (linewidths) - linewidths->push_back(width - widthoftrailingspace); - - // Make sure the new line keeps any color that was set previously. - addcurcolor = true; - - width = widthbeforelastspace = widthoftrailingspace = 0.0f; - prevglyph = 0; // Reset kerning information. - lastspaceindex = -1; - wline.codes.clear(); - wline.colors.clear(); - i++; - - continue; - } - - // Ignore carriage returns - if (c == '\r') - { - i++; - continue; - } - - const Glyph& g = this->FindGlyph(c); - float charwidth = g.spacing + this->GetKerning(prevglyph, c); - float newwidth = width + charwidth; - - // Wrap the line if it exceeds the wrap limit. Don't wrap yet if we're - // processing a newline character, though. - if (c != ' ' && newwidth > wraplimit) - { - // If this is the first character in the line and it exceeds the - // limit, skip it completely. - if (wline.codes.empty()) - i++; - else if (lastspaceindex != -1) - { - // 'Rewind' to the last seen space, if the line has one. - // FIXME: This could be more efficient... - while (!wline.codes.empty() && wline.codes.back() != ' ') - wline.codes.pop_back(); - - while (!wline.colors.empty() && - wline.colors.back().index >= (int)wline.codes.size()) - wline.colors.pop_back(); - - // Also 'rewind' to the color that the last character is using. - for (int colori = curcolori; colori >= 0; colori--) - { - if (codepoints.colors[colori].index <= lastspaceindex) - { - curcolor = codepoints.colors[colori].color; - curcolori = colori; - break; - } - } - - // Ignore the width of trailing spaces in wrapped lines. - width = widthbeforelastspace; - - i = lastspaceindex; - i++; // Start the next line after the space. - } - - lines.push_back(wline); - - if (linewidths) - linewidths->push_back(width); - - addcurcolor = true; - - prevglyph = 0; - width = widthbeforelastspace = widthoftrailingspace = 0.0f; - wline.codes.clear(); - wline.colors.clear(); - lastspaceindex = -1; - - continue; - } - - if (prevglyph != ' ' && c == ' ') - widthbeforelastspace = width; - - width = newwidth; - prevglyph = c; - - if (addcurcolor) - { - wline.colors.push_back({ curcolor, (int)wline.codes.size() }); - addcurcolor = false; - } - - wline.codes.push_back(c); - - // Keep track of the last seen space, so we can "rewind" to it when - // wrapping. - if (c == ' ') - { - lastspaceindex = i; - widthoftrailingspace += charwidth; - } - else if (c != '\n') - widthoftrailingspace = 0.0f; - - i++; - } - - // Push the last line. - if (!wline.codes.empty()) - { - lines.push_back(wline); - - // Ignore the width of any trailing spaces, for individual lines. - if (linewidths) - linewidths->push_back(width - widthoftrailingspace); - } -} - -float Font::GetAscent() const -{ - return floorf(this->rasterizers[0]->GetAscent() / this->dpiScale + 0.5f); -} - -float Font::GetDescent() const -{ - return floorf(this->rasterizers[0]->GetDescent() / this->dpiScale + 0.5f); -} - -float Font::GetHeight() const -{ - return floorf(this->height / this->dpiScale + 0.5f); -} - -float Font::GetBaseline() const -{ - float ascent = this->GetAscent(); - - if (ascent != 0.0f) - return ascent; - else if (this->rasterizers[0]->GetDataType() == love::Rasterizer::DATA_TRUETYPE) - return floorf(this->GetHeight() / 1.25f + 0.5f); - else - return 0.0f; -} - -int Font::GetWidth(uint32_t prevGlyph, uint32_t current) -{ - const Glyph& g = this->FindGlyph(current); - return g.spacing + this->GetKerning(prevGlyph, current); -} - -void Font::SetFallbacks(const std::vector& fallbacks) -{ - for (const Font* font : fallbacks) - { - if (font->rasterizers[0]->GetDataType() != this->rasterizers[0]->GetDataType()) - throw love::Exception("Font fallbacks must be of the same font type."); - } - - this->rasterizers.resize(1); - - // NOTE: this won't invalidate already-rasterized glyphs. - for (const Font* font : fallbacks) - this->rasterizers.push_back(font->rasterizers[0]); -} - -float Font::GetKerning(const std::string& leftChar, const std::string& rightChar) -{ - uint32_t left = 0; - uint32_t right = 0; - - try - { - left = utf8::peek_next(leftChar.begin(), leftChar.end()); - right = utf8::peek_next(rightChar.begin(), rightChar.end()); - } - catch (utf8::exception& e) - { - throw love::Exception("UTF-8 decoding error: %s", e.what()); - } - - return this->GetKerning(left, right); -} diff --git a/platform/switch/source/objects/gamepad.cpp b/platform/switch/source/objects/gamepad.cpp deleted file mode 100644 index a529e1da5..000000000 --- a/platform/switch/source/objects/gamepad.cpp +++ /dev/null @@ -1,544 +0,0 @@ -#include "objects/gamepad/gamepad.h" - -#include "driver/hidrv.h" -#include "modules/timer/timer.h" - -#include "common/bidirectionalmap.h" -#include "modules/event/event.h" -#include "modules/joystick/joystick.h" - -using namespace love; - -#define JOYSTICK_MODULE() (Module::GetInstance(Module::M_JOYSTICK)) - -#define INVALID_GAMEPAD_BUTTON static_cast(-1) -#define INVALID_NPAD_BUTTON static_cast(0) -static constexpr HidNpadStyleTag INVALID_STYLE_TAG = static_cast(-1); - -Gamepad::Gamepad(size_t id) : - common::Gamepad(id), - sixAxisHandles(nullptr), - vibrationHandles(nullptr), - buttonStates() -{} - -Gamepad::Gamepad(size_t id, size_t index) : - common::Gamepad(id), - sixAxisHandles(nullptr), - vibrationHandles(nullptr), - buttonStates() -{ - this->Open(index); -} - -Gamepad::~Gamepad() -{ - this->Close(); -} - -void Gamepad::UpdatePadState() -{ - padUpdate(&this->pad); - - this->buttonStates.pressed = padGetButtonsDown(&this->pad); - this->buttonStates.released = padGetButtonsUp(&this->pad); -} - -HidNpadStyleTag Gamepad::GetStyleTag() -{ - uint32_t styleSet = padGetStyleSet(&this->pad); - - if (styleSet & HidNpadStyleTag_NpadFullKey) - return HidNpadStyleTag_NpadFullKey; - else if (styleSet & HidNpadStyleTag_NpadHandheld) - return HidNpadStyleTag_NpadHandheld; - else if (styleSet & HidNpadStyleTag_NpadJoyDual) - return HidNpadStyleTag_NpadJoyDual; - else if (styleSet & HidNpadStyleTag_NpadJoyLeft) - return HidNpadStyleTag_NpadJoyLeft; - else if (styleSet & HidNpadStyleTag_NpadJoyRight) - return HidNpadStyleTag_NpadJoyRight; - - return INVALID_STYLE_TAG; -} - -HidNpadIdType Gamepad::GetNpadIdType() -{ - return static_cast(HidNpadIdType_No1 + this->id); -} - -bool Gamepad::Open(size_t index) -{ - this->Close(); - - if (index == 0) - padInitializeDefault(&this->pad); - else - padInitialize(&this->pad, this->GetNpadIdType()); - - padUpdate(&this->pad); - - HidNpadStyleTag styleTag = this->GetStyleTag(); - - if (styleTag == INVALID_STYLE_TAG) - return false; - - this->style = padGetStyleSet(&this->pad); - - switch (styleTag) - { - case HidNpadStyleTag_NpadFullKey: - this->name = "Pro Controller"; - - this->sixAxisHandles = std::make_unique(1); - hidGetSixAxisSensorHandles(&this->sixAxisHandles[0], 1, - static_cast(this->id), - HidNpadStyleTag_NpadFullKey); - - this->vibrationHandles = std::make_unique(1); - - break; - case HidNpadStyleTag_NpadJoyLeft: - this->name = "Left Joy-Con"; - - this->sixAxisHandles = std::make_unique(1); - hidGetSixAxisSensorHandles(&this->sixAxisHandles[0], 1, - static_cast(this->id), - HidNpadStyleTag_NpadFullKey); - - this->vibrationHandles = std::make_unique(1); - - break; - case HidNpadStyleTag_NpadJoyRight: - this->name = "Right Joy-Con"; - - this->sixAxisHandles = std::make_unique(1); - hidGetSixAxisSensorHandles(&this->sixAxisHandles[0], 1, - static_cast(this->id), - HidNpadStyleTag_NpadFullKey); - - this->vibrationHandles = std::make_unique(1); - - break; - case HidNpadStyleTag_NpadHandheld: - this->name = "Nintendo Switch"; - - this->sixAxisHandles = std::make_unique(1); - hidGetSixAxisSensorHandles(&this->sixAxisHandles[0], 1, HidNpadIdType_Handheld, - HidNpadStyleTag_NpadHandheld); - - this->vibrationHandles = std::make_unique(2); - - break; - case HidNpadStyleTag_NpadJoyDual: - this->name = "Dual Joy-Con"; - - this->sixAxisHandles = std::make_unique(2); - hidGetSixAxisSensorHandles(&this->sixAxisHandles[0], 2, - static_cast(this->id), - HidNpadStyleTag_NpadJoyDual); - - this->vibrationHandles = std::make_unique(2); - - break; - default: - /* not supported */ - return false; - } - - memset(this->vibrationValues, 0, sizeof(this->vibrationValues)); - - size_t handleCount = (styleTag == HidNpadStyleTag_NpadFullKey) ? 1 : 2; - hidInitializeVibrationDevices(this->vibrationHandles.get(), handleCount, this->GetNpadIdType(), - styleTag); - - hidStartSixAxisSensor(this->sixAxisHandles[0]); - - u32 attributes = padGetAttributes(&this->pad); - if ((this->style & HidNpadStyleTag_NpadJoyDual) && - (attributes & HidNpadAttribute_IsRightConnected)) - hidStartSixAxisSensor(this->sixAxisHandles[1]); - - this->guid = std::to_string(this->style); - - return this->IsConnected(); -} - -void Gamepad::Close() -{ - this->instanceID = -1; - - /* stop vibration, just in case */ - if (this->vibrationHandles != nullptr) - this->SetVibration(0, 0); - - this->vibration = Vibration(); - - if (this->sixAxisHandles != nullptr) - { - hidStopSixAxisSensor(this->sixAxisHandles[0]); - - u32 attributes = padGetAttributes(&this->pad); - if ((this->style & HidNpadStyleTag_NpadJoyDual) && - (attributes & HidNpadAttribute_IsRightConnected)) - hidStopSixAxisSensor(this->sixAxisHandles[1]); - } - - this->sixAxisHandles = nullptr; - this->vibrationHandles = nullptr; -} - -bool Gamepad::IsConnected() const -{ - return padIsConnected(&this->pad); -} - -const char* Gamepad::GetName() const -{ - return this->name.c_str(); -} - -size_t Gamepad::GetAxisCount() const -{ - return this->IsConnected() ? 12 : 0; -} - -size_t Gamepad::GetButtonCount() const -{ - return this->IsConnected() ? 16 - 6 : 0; -} - -float Gamepad::GetAxis(size_t axis) const -{ - if (!this->IsConnected()) - return 0.0f; - - float value = 0.0f; - - if (axis == 1 || axis == 2) - { - HidAnalogStickState left = padGetStickPos(&this->pad, 0); - - if (axis == 1) - value = left.x; - else if (axis == 2) - value = -left.y; - - return Gamepad::ClampValue(value / JOYSTICK_MAX); - } - else if (axis == 3 || axis == 4) - { - HidAnalogStickState right = padGetStickPos(&this->pad, 1); - - if (axis == 3) - value = right.x; - else if (axis == 4) - value = -right.y; - - return Gamepad::ClampValue(value / JOYSTICK_MAX); - } - else if (axis == 5) - { - if (padGetButtons(&this->pad) & HidNpadButton_ZL) - return 1.0f; - - return 0.0f; - } - else if (axis == 6) - { - if (padGetButtons(&this->pad) & HidNpadButton_ZR) - return 1.0f; - - return 0.0f; - } - else - { - HidSixAxisSensorState sixAxisState = { 0 }; - - if (this->style & HidNpadStyleTag_NpadFullKey) - hidGetSixAxisSensorStates(this->sixAxisHandles[0], &sixAxisState, 1); - else if (this->style & HidNpadStyleTag_NpadHandheld) - hidGetSixAxisSensorStates(this->sixAxisHandles[0], &sixAxisState, 1); - else if (this->style & HidNpadStyleTag_NpadJoyDual) - { - /* - ** For JoyDual, read from either the Left or Right Joy-Con - ** depending on which is/are connected - */ - - u32 attributes = padGetAttributes(&pad); - if (attributes & HidNpadAttribute_IsLeftConnected) - hidGetSixAxisSensorStates(sixAxisHandles[0], &sixAxisState, 1); - else if (attributes & HidNpadAttribute_IsRightConnected) - hidGetSixAxisSensorStates(sixAxisHandles[1], &sixAxisState, 1); - } - - if (axis >= 7 and axis < 10) - { - if (axis == 7) - return sixAxisState.angular_velocity.x; - else if (axis == 8) - return sixAxisState.angular_velocity.y; - - return sixAxisState.angular_velocity.z; - } - else if (axis >= 10 and axis < 13) - { - if (axis == 7) - return sixAxisState.acceleration.x; - else if (axis == 8) - return sixAxisState.acceleration.y; - - return sixAxisState.acceleration.z; - } - } - - return 0.0f; -} - -std::vector Gamepad::GetAxes() const -{ - std::vector axes; - size_t count = this->GetAxisCount(); - - if (count <= 0) - return axes; - - axes.reserve(count); - - for (size_t index = 0; index < count; index++) - axes.push_back(this->GetAxis(index)); - - return axes; -} - -/* helper functions */ -bool Gamepad::IsDown(size_t index, ButtonMapping& button) -{ - if (!this->IsConnected()) - return false; - - HidNpadButton hidButton = INVALID_NPAD_BUTTON; - - if (!this->buttonStates.pressed) - return false; - - const auto records = buttons.GetEntries().first; - - hidButton = static_cast(records[index].second); - - if (hidButton & this->buttonStates.pressed) - { - this->buttonStates.pressed ^= hidButton; - button = std::make_pair(records[index].first, index); - - return true; - } - - return false; -} - -bool Gamepad::IsUp(size_t index, ButtonMapping& button) -{ - if (!this->IsConnected()) - return false; - - HidNpadButton hidButton = INVALID_NPAD_BUTTON; - - if (!this->buttonStates.released) - return false; - - const auto records = buttons.GetEntries().first; - - hidButton = static_cast(records[index].second); - - if (hidButton & this->buttonStates.released) - { - this->buttonStates.released ^= hidButton; - button = std::make_pair(records[index].first, index); - - return true; - } - - return false; -} - -bool Gamepad::IsHeld(size_t index, ButtonMapping& button) const -{ - if (!this->IsConnected()) - return false; - - uint64_t heldSet = padGetButtons(&this->pad); - HidNpadButton hidButton; - - auto recordPair = buttons.GetEntries(); - auto records = recordPair.first; - - for (size_t i = 0; i < recordPair.second; i++) - { - if ((hidButton = static_cast(records[i].second)) & heldSet) - { - button = { records[i].first, i }; - break; - } - } - - return (heldSet & hidButton); -} - -bool Gamepad::IsDown(const std::vector& buttonsVector) const -{ - auto recordPair = buttons.GetEntries(); - - uint32_t heldSet = padGetButtons(&this->pad); - auto records = recordPair.first; - - for (size_t button : buttonsVector) - { - if (button < 0 || button >= recordPair.second) - continue; - - if (heldSet & static_cast(records[button].second)) - return true; - } - - return false; -} - -bool Gamepad::IsGamepadDown(const std::vector& buttonsVector) const -{ - uint64_t heldSet = padGetButtons(&this->pad); - - GamepadButton consoleButton = INVALID_GAMEPAD_BUTTON; - const char* name = nullptr; - - for (GamepadButton button : buttonsVector) - { - /* make sure our out button isn't invalid */ - if (!GetConstant(button, name)) - continue; - - /* convert to the proper button */ - if (!GetConstant(name, consoleButton)) - continue; - - if (heldSet & static_cast(consoleButton)) - return true; - } - - return false; -} - -float Gamepad::GetGamepadAxis(GamepadAxis axis) const -{ - const char* name = nullptr; - if (!Gamepad::GetConstant(axis, name)) - return 0.0f; - - switch (axis) - { - case GamepadAxis::GAMEPAD_AXIS_LEFTX: - return this->GetAxis(1); - case GamepadAxis::GAMEPAD_AXIS_LEFTY: - return this->GetAxis(2); - case GamepadAxis::GAMEPAD_AXIS_RIGHTX: - return this->GetAxis(3); - case GamepadAxis::GAMEPAD_AXIS_RIGHTY: - return this->GetAxis(4); - case GamepadAxis::GAMEPAD_AXIS_TRIGGERLEFT: - return this->GetAxis(5); - case GamepadAxis::GAMEPAD_AXIS_TRIGGERRIGHT: - return this->GetAxis(6); - default: - break; - } - - return 0.0f; -} - -bool Gamepad::IsVibrationSupported() -{ - return true; -} - -bool Gamepad::SetVibration(float left, float right, float duration) -{ - left = std::clamp(left, 0.0f, 1.0f); - right = std::clamp(right, 0.0f, 1.0f); - - uint32_t length = Vibration::max; - if (duration >= 0) - length = (uint32_t)std::min(duration, Vibration::max / 1000.0f); - - for (auto& vibrationValue : this->vibrationValues) - { - vibrationValue.freq_low = 160.0f; - vibrationValue.freq_high = 320.0f; - } - - this->vibrationValues[0].amp_low = left; - this->vibrationValues[0].amp_high = right; - - this->vibrationValues[1].amp_low = left; - this->vibrationValues[1].amp_high = right; - - Result res = hidSendVibrationValues(this->vibrationHandles.get(), this->vibrationValues, 2); - bool success = R_SUCCEEDED(res); - - if (success && (left != 0.0f && right != 0.0f)) - { - this->vibration.left = left; - this->vibration.right = right; - - if (length == Vibration::max) - this->vibration.endTime = Vibration::max; - else - this->vibration.endTime = love::Timer::GetTime() + length; - - JOYSTICK_MODULE()->AddVibration(this, this->id); - } - else - { - this->vibration.left = this->vibration.right = 0.0f; - this->vibration.endTime = Vibration::max; - } - - return R_SUCCEEDED(success); -} - -bool Gamepad::SetVibration() -{ - this->vibration.id = -1; - return this->SetVibration(0, 0); -} - -void Gamepad::GetVibration(float& left, float& right) -{ - left = this->vibration.left; - right = this->vibration.right; -} - -const Gamepad::Vibration& Gamepad::GetVibration() const -{ - return this->vibration; -} - -bool Gamepad::GetConstant(const char* in, Gamepad::GamepadAxis& out) -{ - return axes.Find(in, out); -} - -bool Gamepad::GetConstant(Gamepad::GamepadAxis in, const char*& out) -{ - return axes.ReverseFind(in, out); -} - -bool Gamepad::GetConstant(const char* in, GamepadButton& out) -{ - return buttons.Find(in, out); -} - -bool Gamepad::GetConstant(GamepadButton in, const char*& out) -{ - return buttons.ReverseFind(in, out); -} diff --git a/platform/switch/source/objects/glyphdata.cpp b/platform/switch/source/objects/glyphdata.cpp deleted file mode 100644 index 73605d383..000000000 --- a/platform/switch/source/objects/glyphdata.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "objects/glyphdata/glyphdata.h" - -#include "utf8/utf8.h" - -using namespace love; - -GlyphData::GlyphData(uint32_t glyph, GlyphMetrics metrics) : - common::GlyphData(glyph, metrics), - data(nullptr) -{ - size_t pixelSize = this->GetPixelSize(); - if (this->metrics.width > 0 && this->metrics.height > 0) - this->data = new uint8_t[(metrics.width * metrics.height) * pixelSize]; -} - -GlyphData::GlyphData(const GlyphData& other) : - common::GlyphData(other.glyph, other.metrics), - data(nullptr) -{ - size_t pixelSize = this->GetPixelSize(); - if (this->metrics.width > 0 && this->metrics.height > 0) - { - data = new uint8_t[(metrics.width * metrics.height) * pixelSize]; - memcpy(data, other.data, other.GetSize()); - } -} - -GlyphData* GlyphData::Clone() const -{ - return new GlyphData(*this); -} - -size_t GlyphData::GetPixelSize() const -{ - return 4; -} - -GlyphData::~GlyphData() -{ - delete[] this->data; -} - -void* GlyphData::GetData() const -{ - return this->data; -} - -void* GlyphData::GetData(int x, int y) const -{ - size_t offset = (y * this->GetWidth() + x) * this->GetPixelSize(); - return this->data + offset; -} - -size_t GlyphData::GetSize() const -{ - return size_t(this->GetWidth() * this->GetHeight()) * this->GetPixelSize(); -} diff --git a/platform/switch/source/objects/image.cpp b/platform/switch/source/objects/image.cpp deleted file mode 100644 index adf9362ef..000000000 --- a/platform/switch/source/objects/image.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "objects/image/image.h" -#include "modules/image/imagemodule.h" - -#include "deko3d/deko.h" - -#include "modules/graphics/graphics.h" - -using namespace love; - -Image::Image(const Slices& slices, bool validate) : - Texture(slices.GetTextureType()), - data(slices), - mipmapsType(MIPMAPS_NONE), - sRGB(Graphics::IsGammaCorrect()) -{ - if (validate && data.Validate() == MIPMAPS_DATA) - this->mipmapsType = MIPMAPS_DATA; -} - -Image::Image(TextureType type, PixelFormat format, int width, int height, int slices) : - Image(Slices(type), false) -{ - if (love::IsPixelFormatCompressed(format)) - throw love::Exception("This constructor only supports non-compressesd pixel formats."); - - this->Init(format, width, height); -} - -Image::Image(const Slices& slices) : Image(slices, true) -{ - this->Init(slices.Get(0, 0)); -} - -Image::~Image() -{} - -void Image::Init(ImageDataBase* data) -{ - this->Init(data->GetFormat(), data->GetWidth(), data->GetHeight()); -} - -void Image::Init(PixelFormat pixelFormat, int width, int height) -{ - PixelFormat format = pixelFormat; - - if (this->data.Get(0, 0)) - { - bool success = this->texture.load(format, this->sRGB, this->data.Get(0, 0)->GetData(), - this->data.Get(0, 0)->GetSize(), width, height); - - if (!success) - { - const char* formatName = nullptr; - ImageModule::GetConstant(format, formatName); - - throw love::Exception("Failed to upload image data: format %s not supported", - formatName); - } - } - else - { - this->texture.load(pixelFormat, this->sRGB, nullptr, 0, width, height, true); - } - - this->width = width; - this->height = height; - - this->handle = ::deko3d::Instance().RegisterResHandle(this->texture.getDescriptor()); - - this->InitQuad(); - - this->SetFilter(this->filter); - this->SetWrap(this->wrap); -} - -void Image::ReplacePixels(const void* data, size_t size, const Rect& rect) -{ - this->texture.replacePixels(::deko3d::Instance().GetData(), ::deko3d::Instance().GetDevice(), - data, size, ::deko3d::Instance().GetTextureQueue(), rect); -} diff --git a/platform/switch/source/objects/quad.cpp b/platform/switch/source/objects/quad.cpp deleted file mode 100644 index 1d89bdda3..000000000 --- a/platform/switch/source/objects/quad.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "objects/quad/quad.h" - -using namespace love; - -Quad::Quad(const Viewport& viewport, double sw, double sh) : common::Quad(sw, sh) -{ - this->Refresh(viewport, sw, sh); -} - -void Quad::Refresh(const Viewport& viewport, double sw, double sh) -{ - this->RefreshViewport(viewport, sw, sh); - - // zero out vertex positions - std::fill_n(this->vertexPositions, 4, Vector2 {}); - - this->vertexPositions[0] = Vector2(0.0f, 0.0f); - this->vertexPositions[1] = Vector2(0.0f, (float)viewport.h); - this->vertexPositions[2] = Vector2((float)viewport.w, (float)viewport.h); - this->vertexPositions[3] = Vector2((float)viewport.w, 0.0f); - - // zero out texcoords - std::fill_n(this->vertexTexCoords, 4, Vector2 {}); - - this->vertexTexCoords[0] = Vector2((float)(viewport.x / sw), (float)(viewport.y / sh)); - this->vertexTexCoords[1] = - Vector2((float)(viewport.x / sw), (float)((viewport.y + viewport.h) / sh)); - this->vertexTexCoords[2] = - Vector2((float)((viewport.x + viewport.w) / sw), (float)((viewport.y + viewport.h) / sh)); - this->vertexTexCoords[3] = - Vector2((float)((viewport.x + viewport.w) / sw), (float)(viewport.y / sh)); -} - -const Vector2* Quad::GetVertexPositions() const -{ - return this->vertexPositions; -} - -const Vector2* Quad::GetVertexTexCoords() const -{ - return this->vertexTexCoords; -} diff --git a/platform/switch/source/objects/source.cpp b/platform/switch/source/objects/source.cpp deleted file mode 100644 index 62ce44e17..000000000 --- a/platform/switch/source/objects/source.cpp +++ /dev/null @@ -1,257 +0,0 @@ -#include "objects/source/source.h" -#include "modules/audio/audio.h" - -#include "driver/audiodrv.h" -#include "pools/audiopool.h" - -using namespace love; - -#define AudioModule() (Module::GetInstance